一、自编设备驱动模型
at24.c:
static int __init at24_init(void) { io_limit
= rounddown_pow_of_two
(io_limit
); return i2c_add_driver
(&at24_driver
); //注册i2c驱动设备 }
at24_driver:
static struct i2c_driver at24_driver = { .driver
= { .name
= "at24", .owner
= THIS_MODULE
, }, .probe
= at24_probe
, //找到驱动对应的设备调用的函数 .remove
= __devexit_p
(at24_remove
), .id_table
= at24_ids
, //这个表里的设备都支持 };
at24_probe:
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct at24_platform_data chip
; bool writable
; bool use_smbus
= false; struct at24_data
*at24
; int err; unsigned i
, num_addresses
; kernel_ulong_t magic
; if (client
->dev
.platform_data
) { chip
= *(struct at24_platform_data
*)client
->dev
.platform_data
; } else { if (!id
->driver_data
) { err = -ENODEV
; goto err_out
; } magic
= id
->driver_data
; chip
.byte_len
= BIT
(magic
& AT24_BITMASK
(AT24_SIZE_BYTELEN
)); magic
>>= AT24_SIZE_BYTELEN
; chip
.flags
= magic
& AT24_BITMASK
(AT24_SIZE_FLAGS
); /* * This
is slow
, but we can
't know all eeproms
, so we better
* play safe
. Specifying custom eeprom
-types via platform_data
* is recommended anyhow
. */ chip
.page_size
= 1
; chip
.setup
= NULL; chip
.context
= NULL; } if (!is_power_of_2
(chip
.byte_len
)) dev_warn
(&client
->dev
, "byte_len looks suspicious (no power of 2)!\n"); if (!is_power_of_2
(chip
.page_size
)) dev_warn
(&client
->dev
, "page_size looks suspicious (no power of 2)!\n"); /* Use I2C operations unless we
're stuck with SMBus extensions
. */ if (!i2c_check_functionality
(client
->adapter
, I2C_FUNC_I2C
)) { if (chip
.flags
& AT24_FLAG_ADDR16
) { err = -EPFNOSUPPORT
; goto err_out
; } if (!i2c_check_functionality
(client
->adapter
, I2C_FUNC_SMBUS_READ_I2C_BLOCK
)) { err = -EPFNOSUPPORT
; goto err_out
; } use_smbus
= true; } if (chip
.flags
& AT24_FLAG_TAKE8ADDR
) num_addresses
= 8
; else num_addresses
= DIV_ROUND_UP
(chip
.byte_len
, (chip
.flags
& AT24_FLAG_ADDR16
) ? 65536
: 256
); at24
= kzalloc
(sizeof
(struct at24_data
) + num_addresses
* sizeof
(struct i2c_client
*), GFP_KERNEL
); if (!at24
) { err = -ENOMEM
; goto err_out
; } mutex_init
(&at24
->lock
); at24
->use_smbus
= use_smbus
; at24
->chip
= chip
; at24
->num_addresses
= num_addresses
; /* * Export the EEPROM bytes through sysfs
, since that
's convenient
. * By default
, only root should see the data
(maybe passwords etc
) */ at24
->bin
.attr
.name
= "eeprom"; at24
->bin
.attr
.mode
= chip
.flags
& AT24_FLAG_IRUGO
? S_IRUGO
: S_IRUSR
; at24
->bin
.read
= at24_bin_read
; at24
->bin
.size
= chip
.byte_len
; at24
->macc
.read
= at24_macc_read
; writable
= !(chip
.flags
& AT24_FLAG_READONLY
); if (writable
) { if (!use_smbus
|| i2c_check_functionality
(client
->adapter
, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
)) { unsigned write_max
= chip
.page_size
; at24
->macc
.write
= at24_macc_write
; at24
->bin
.write
= at24_bin_write
; //这里注册了at24_bin_write,用户的write函数的调用接口 at24
->bin
.attr
.mode
|= S_IWUSR
; if (write_max
> io_limit
) write_max
= io_limit
; if (use_smbus
&& write_max
> I2C_SMBUS_BLOCK_MAX
) write_max
= I2C_SMBUS_BLOCK_MAX
; at24
->write_max
= write_max
; /* buffer
(data
+ address at the beginning
) */ at24
->writebuf
= kmalloc
(write_max
+ 2
, GFP_KERNEL
); if (!at24
->writebuf
) { err = -ENOMEM
; goto err_struct
; } } else { dev_warn
(&client
->dev
, "cannot write due to controller restrictions."); } } at24
->client
[0
] = client
; /* use dummy devices
for multiple
-address chips
*/ for (i
= 1
; i
< num_addresses
; i
++) { at24
->client
[i
] = i2c_new_dummy
(client
->adapter
, client
->addr
+ i
); if (!at24
->client
[i
]) { dev_err
(&client
->dev
, "address 0xx unavailable\n", client
->addr
+ i
); err = -EADDRINUSE
; goto err_clients
; } } err = sysfs_create_bin_file
(&client
->dev
.kobj
, &at24
->bin
); //创建一个文件应用程序实际上是在/sys目录下的文件,而这个文件就是它函数创建的。 if (err) goto err_clients
; i2c_set_clientdata
(client
, at24
); dev_info
(&client
->dev
, "%zu byte %s EEPROM %s\n", at24
->bin
.size
, client
->name
, writable
? "(writable)" : "(read-only)"); dev_dbg
(&client
->dev
, "page_size %d, num_addresses %d, write_max %d%s\n", chip
.page_size
, num_addresses
, at24
->write_max
, use_smbus
? ", use_smbus" : ""); /* export data
to kernel code
*/ if (chip
.setup
) chip
.setup
(&at24
->macc
, chip
.context
); return 0
; err_clients
: for (i
= 1
; i
< num_addresses
; i
++) if (at24
->client
[i
]) i2c_unregister_device
(at24
->client
[i
]); kfree
(at24
->writebuf
); err_struct
: kfree
(at24
); err_out
: dev_dbg
(&client
->dev
, "probe error %d\n", err); return
err; }
at24_bin_write:
static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr, char
*buf
, loff_t off
, size_t count
) { struct at24_data
*at24
; at24
= dev_get_drvdata
(container_of
(kobj
, struct device
, kobj
)); return at24_write(at24, buf, off, count); //调用at24_write }
at24_write:
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off, size_t count
) { ssize_t retval
= 0
; if (unlikely
(!count
)) return count
; mutex_lock
(&at24
->lock
); while (count
) { ssize_t status
; status
= at24_eeprom_write
(at24
, buf
, off
, count
); //调用at24_eeprom_write if (status
<= 0
) { if (retval
== 0
) retval
= status
; break
; } buf
+= status
; off
+= status
; count
-= status
; retval
+= status
; } mutex_unlock
(&at24
->lock
); return retval
; }
at24_eeprom_write:
static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf, unsigned offset
, size_t count
) { struct i2c_client
*client
; struct i2c_msg msg
; ssize_t status
; unsigned long timeout
, write_time
; unsigned next_page
; /* Get corresponding I2C address
and adjust offset
*/ client
= at24_translate_offset
(at24
, &offset
); /* write_max
is at most a page
*/ if (count
> at24
->write_max
) count
= at24
->write_max
; /* Never roll over backwards
, to the start of this page
*/ next_page
= roundup
(offset
+ 1
, at24
->chip
.page_size
); if (offset
+ count
> next_page
) count
= next_page
- offset
; /* If we
'll use I2C calls
for I
/O
, set up the message
*/ //I2C的消息 if (!at24
->use_smbus
) { int i
= 0
; msg
.addr
= client
->addr
; msg
.flags
= 0
; /* msg
.buf
is u8
and casts will mask the values
*/ msg
.buf
= at24
->writebuf
; if (at24
->chip
.flags
& AT24_FLAG_ADDR16
) msg
.buf
[i
++] = offset
>> 8
; msg
.buf
[i
++] = offset
; //提供偏移地址 memcpy
(&msg
.buf
[i
], buf
, count
); //拷贝用户发送数据 msg
.len = i
+ count
; //设置长度 } /* * Writes fail
if the previous one didn
't complete yet
. We may
* loop a few times
until this one succeeds
, waiting at least
* long enough
for one entire page write
to work
. */ timeout
= jiffies
+ msecs_to_jiffies
(write_timeout
); do { write_time
= jiffies
; if (at24
->use_smbus
) { status
= i2c_smbus_write_i2c_block_data
(client
, offset
, count
, buf
); if (status
== 0
) status
= count
; } else { status
= i2c_transfer
(client
->adapter
, &msg
, 1
); //交给I2C控制器完成 if (status
== 1
) status
= count
; } dev_dbg
(&client
->dev
, "write %zu@%d --> %zd (%ld)\n", count
, offset
, status
, jiffies
); if (status
== count
) return count
; /* REVISIT
: at HZ
=100
, this
is sloooow
*/ msleep
(1
); } while (time_before
(write_time
, timeout
)); return
-ETIMEDOUT
; }
i2c_transfer:
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { int ret
; if (adap
->algo
->master_xfer
) { #ifdef DEBUG
for (ret
= 0
; ret
< num
; ret
++) { dev_dbg
(&adap
->dev
, "master_xfer[%d] %c, addr=0xx, " "len=%d%s\n", ret
, (msgs
[ret
].flags
& I2C_M_RD
) ? 'R
' : 'W
', msgs
[ret
].addr
, msgs
[ret
].len, (msgs
[ret
].flags
& I2C_M_RECV_LEN
) ? "+" : ""); } #endif
if (in_atomic
() || irqs_disabled
()) { ret
= mutex_trylock
(&adap
->bus_lock
); if (!ret
) /* I2C activity
is ongoing
. */ return
-EAGAIN
; } else { mutex_lock_nested
(&adap
->bus_lock
, adap
->level
); } ret
= adap
->algo
->master_xfer
(adap
,msgs
,num
); //调用控制器中的算法 mutex_unlock
(&adap
->bus_lock
); return ret
; } else { dev_dbg
(&adap
->dev
, "I2C level transfers not supported\n"); return
-EOPNOTSUPP
; } }
然后看一下i2c_bin_read:
static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr, char
*buf
, loff_t off
, size_t count
) { struct at24_data
*at24
; at24
= dev_get_drvdata
(container_of
(kobj
, struct device
, kobj
)); return at24_read
(at24
, buf
, off
, count
); //调用at24_read }
at24_read:
static ssize_t at24_read(struct at24_data *at24, char
*buf
, loff_t off
, size_t count
) { ssize_t retval
= 0
; if (unlikely
(!count
)) return count
; mutex_lock
(&at24
->lock
); while (count
) { ssize_t status
; status
= at24_eeprom_read
(at24
, buf
, off
, count
); //继续调用at24_eeprom_read if (status
<= 0
) { if (retval
== 0
) retval
= status
; break
; } buf
+= status
; off
+= status
; count
-= status
; retval
+= status
; } mutex_unlock
(&at24
->lock
); return retval
; }
at24_eeprom_read:
static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, unsigned offset
, size_t count
) { struct i2c_msg msg
[2
]; u8 msgbuf
[2
]; struct i2c_client
*client
; int status
, i
; memset
(msg
, 0
, sizeof
(msg
)); client
= at24_translate_offset
(at24
, &offset
); if (count
> io_limit
) count
= io_limit
; /* Smaller eeproms can work given some SMBus extension calls
*/ if (at24
->use_smbus
) { if (count
> I2C_SMBUS_BLOCK_MAX
) count
= I2C_SMBUS_BLOCK_MAX
; status
= i2c_smbus_read_i2c_block_data
(client
, offset
, count
, buf
); dev_dbg
(&client
->dev
, "smbus read %zu@%d --> %d\n", count
, offset
, status
); return
(status
< 0
) ? -EIO
: status
; } i
= 0
; if (at24
->chip
.flags
& AT24_FLAG_ADDR16
) msgbuf
[i
++] = offset
>> 8
; msgbuf
[i
++] = offset
; //设置偏移 msg
[0
].addr
= client
->addr
; //第一条消息,提供从设备地址 msg
[0
].buf
= msgbuf
; msg
[0
].len = i
; msg
[1
].addr
= client
->addr
; //第二条消息 msg
[1
].flags
= I2C_M_RD
; //flags是读 msg
[1
].buf
= buf
; //提供数据量 msg
[1
].len = count
; status
= i2c_transfer
(client
->adapter
, msg
, 2
); dev_dbg
(&client
->dev
, "i2c read %zu@%d --> %d\n", count
, offset
, status
); if (status
== 2
) return count
; else if (status
>= 0
) return
-EIO
; else return status
; }
二、对驱动程序的修改和移植
I2C设备的注册:
static void __init tq2440_machine_init(void) { s3c24xx_fb_set_platdata
(&tq2440_fb_info
); s3c_i2c0_set_platdata
(NULL); platform_add_devices
(tq2440_devices
, ARRAY_SIZE
(tq2440_devices
)); EmbedSky_machine_init
(); s3c2410_gpio_setpin
(S3C2410_GPG12
, 0
); s3c2410_gpio_cfgpin
(S3C2410_GPG12
, S3C2410_GPIO_OUTPUT
); s3c24xx_udc_set_platdata
(&EmbedSky_udc_cfg
); }
增加设备:
static struct at24_platform_data at24c02 = { .byte_len
= 2048
/ 8
, .page_size
= 8
, .flags
= 0
, }; static struct i2c_board_info __initdata tq2440_i2c_devices
[] = { { I2C_BOARD_INFO
("24c02", 0x50
), .platform_data
= &at24c02
, }, };
然后在tq2440_machine_init中添加:
i2c_register_board_info(0, tq2440_i2c_devices, ARRAY_SIZE(tq2440_i2c_devices));
然后添加头文件:
#include <linux/i2c.h> #include
<linux
/i2c
/at24
.h
>
然后会在开发板/sys/bus/i2c/devices/有一个0-0050的目录,有一个eeprom文件。
编写i2c-app.c文件:
#include <stdio.h> #include
<sys
/types
.h
> #include
<sys
/stat
.h
> #include
<fcntl
.h
> #include
<unistd
.h
> int main
() { char write_data
[256
],read_data
[256
]; int fd
; int i
= 0
; //打开at24c02对应的sys文件 fd
= open
("/sys/bus/i2c/devices/0-0050/eeprom", O_RDWR
); //写入数据
for(i
=0
;i
<256
;i
++) write_data
[i
] = i
; lseek
(fd
, 0
, SEEK_SET
); write
(fd
, write_data
, 256
); //读出数据 lseek
(fd
, 0
, SEEK_SET
); read
(fd
, read_data
, 256
); //打印对比
for(i
=0
;i
<256
;i
++) { if(i
%16
== 0
) printf
("\r\n"); printf
("= ",read_data
[i
]); } printf
("\n"); close(fd); }
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(30) | 评论(0) | 转发(0) |
0
上一篇:I2C用户态驱动设计
下一篇:SPI总线介绍和裸机编程分析
相关热门文章
SHTML是什么_SSI有什么用...
查看linux中某个端口(port)...
卡尔曼滤波的原理说明...
shell中字符串操作
关于java中的“错误:找不到或...
给主人留下些什么吧!~~
评论热议