I2C子系统_设备驱动移植
用户程序通过I2C设备驱动程序访问步骤是这样的...
用户的操作函数---/sys/bus/i2c/devices/....->设备驱动的操作函数---->最终回调i2c-core的函数实现数据的通信。
AT24.C驱动追踪:(Linux内核已经帮我们实现大部分驱动,所以驱动开发移植比较多..)
模块入口:每个i2c设备都需要使用该函数向i2c总线注册。
i2c_add_driver(&at24_driver); /*添加设备驱动,将驱动挂载在I2C总线上*/
/*i2c_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, /*设备驱动所支持的设备型号列表*/
};
继续查看 prob 函数:
struct at24_platform_data chip; /*用这个结构获取定义在/arch/arm/match-xxx/...平台初始化设备*/
之后对选取芯片进一步检测
接着:sysfs_bin_attr_init(&at24->bin); 初始化属性文件
at24->bin.attr.name = "eeprom";
sysfs_create_bin_file; 在 sys/.../eeprom二进制文件,并绑定相关的操作函数
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR; /*文件权限*/
at24->bin.read = at24_bin_read; /*对文件进行读操作*/
at24->bin.write = at24_bin_write; /*对文件写*/
从这里可以看出,通过读写sys/.../eeprom 设备文件从而达到通讯的目的
具体在进来写看看
/*
* Note that if the hardware write-protect pin is pulled high, the whole
* chip is normally write protected. But there are plenty of product
* variants here, including OTP fuses and partial chip protect.
*
* We only use page mode writes; the alternative is sloooow. This routine
* writes at most one page.
*/
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 ,构造消息,和用户驱动一样的*/
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-CORE*/
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设备注册
在TQ210上移植AT24C02设备驱动:需要用到的....
1、每个开发板在 /arch/arm/mach-xxx 中有一个 mach-xxx.c 这个文件记录了开发板上存在的设备资源!
2、因为内核集成了AT24驱动,平台驱动要对应平台设备,所以使用驱动前应该注册平台设备。I2C平台设备注册需要用到函数:
i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)
参数说明:
busnum:指明用到那条i2c总线,TQ210支持4条I2C总线
struct i2c_board_info const *info: i2c设备描述符
len: 设备描述符的大小
详细注册信息:
照着别人的来改
/* I2C0 */
//add by hntea
#include<linux/i2c/at24.h>
static struct at24_platform_data at24c02=
{
.byte_len = 2048/8,
.page_size = 8,
.flags = 0
};
static struct i2c_board_info i2c_devs0[] __initdata = {
#ifdef CONFIG_SND_SOC_WM8960_TQ210
{
I2C_BOARD_INFO("wm8960", 0x1a),
.platform_data = &wm8960_pdata,
},
#endif
#ifdef CONFIG_SND_SOC_WM8580
{
I2C_BOARD_INFO("wm8580", 0x1b),
},
#endif
//add by hntea
{
I2C_BOARD_INFO("24c02", 0x50), /*这个文件名要和驱动对应*/
.platform_data = &at24c02,
},
};
修改完后将驱动编入内核
从新编译一下内核就好了.
注册使用
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
以上这幅图很明显没有产生eeprom 文件....
原因在于I2C_BOARD_INFO("at24c02", 0x50), /*这个文件名要和驱动对应*/
设备名写错了,导致驱动在列表中找不到对应的设备号
3、测试驱动是否有效....
驱动测试
#include<stdio.h>
#include<sys/fcntl.h>
#include<sys/types.h>
#define PATH "/sys/bus/i2c/devices/0-0050/eeprom"
int main(void)
{
int fd = 0;
int ret = 0;
unsigned char i=0;
char readData[12],wrData[12];
/*1、打开设备文件*/
if( (fd = open(PATH,O_RDWR) )<0 )
printf("open err!\n fd = %d \n",fd);
/*2、文件定位*/
lseek(fd,0,SEEK_SET);
/*3、写入数据*/
for(i=0;i<12;i++)
{
wrData[i]=i;
}
if ((ret = write(fd,wrData,12))<0)
printf("wr err!\n");
lseek(fd,0,SEEK_SET);
/*4、读取数据*/
if ((ret = read(fd,readData,12))<0)
printf("rd err!\n");
/*5、打印数据*/
for(i=0;i<12;i++)
{
printf("%d\n",readData[i]);
}
/*5、关闭文件*/
close(fd);
}
大功告成!