您的位置:首页 > 运维架构 > Linux

基于S3C2440的Linux-3.6.6移植——sysfs文件系统的IIC应用

2013-07-23 19:43 489 查看
上一篇文章我们介绍了使用devfs访问IIC设备,下面再来介绍应用sysfs方法访问IIC。



在这里,系统要用到三个很重要的结构——i2c_client、i2c_driver和i2c_adapter。i2c_client指的是IIC设备,我的开发板上的IIC设备就是AT24C02的eeprom;i2c_driver指的是IIC的设备驱动;i2c_adapter指的是IIC的适配器,也就是2440集成的IIC总线。要形成IIC的sysfs文件系统,就必须创建这三个结构,并把它们关联起来。



一、i2c_client、i2c_driver和i2c_adapter结构的创建



我们先来看看这三个结构是如何被定义的。



在mach-zhaocj2440.c文件中的zhaocj2440_init函数内,有下面一句:

i2c_register_board_info(0,zhaocj2440_i2c_devs,ARRAY_SIZE(zhaocj2440_i2c_devs));



i2c_register_board_info函数是在drivers/i2c/i2c-boardinfo.c中被定义,作用是静态注册声明开发板上的IIC信息。其中该函数的参数zhaocj2440_i2c_devs的作用是定义板上的IIC设备信息,它是在mach-zhaocj2440.c内定义的。由于我的开发板上的IIC设备为AT24C02的eeprom,因此zhaocj2440_i2c_devs应该重新定义为:

static struct i2c_board_info zhaocj2440_i2c_devs[] __initdata = {

{

I2C_BOARD_INFO("24c02", 0x50), //宏定义:.type=24c02, .addr=0x50,

.platform_data= &at24c02,

},

};



而at24c02定义为:

static struct at24_platform_data at24c02 = {

.byte_len = SZ_2K / 8, //容量大小

.page_size = 8, //每页的字节数

};



i2c_board_info.type就是i2c_client.name变量,i2c_board_info.addr就是i2c_client.addr变量,所以只要定义了zhaocj2440_i2c_devs,也就是定义了i2c_client结构。关于这一点,我们在后面会讲到。



我们再来看drivers/misc/eeprom/at24.c文件中的模块初始化函数at24_init:

static int __init at24_init(void)

{

if(!io_limit) {

pr_err("at24:io_limit must not be 0!\n");

return -EINVAL;

}



io_limit= rounddown_pow_of_two(io_limit);

//为IIC添加驱动

return i2c_add_driver(&at24_driver);

}



i2c_add_driver函数通过宏定义为i2c_register_driver函数,它的作用是注册IIC驱动,这里的驱动是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,

};



IIC适配器是通过i2c-s3c2410.c文件内的s3c24xx_i2c_probe函数创建的。该函数调用了i2c_add_numbered_adapter函数,它又调用了i2c_register_adapter函数,从而完成了对i2c_adapter的创建和注册。



二、i2c_client、i2c_driver和i2c_adapter三者的绑定



这样,i2c_client、i2c_driver和i2c_adapter都被创建了,并都各自放到了总线上,那它们是如何关联在一起的呢?



在s3c24xx_i2c_probe函数内调用了of_i2c_register_devices函数,该函数提取出了i2c_board_info设备信息,并且又调用了i2c_new_device函数,该函数主要完成了两项工作,一是通过把i2c_board_info赋值给i2c_client,从而创建了i2c_client,因此正如我们在前面介绍过的,定义了i2c_board_info,也就是定义了i2c_client;二是把刚刚创建的IIC设备与在s3c24xx_i2c_probe函数创建的IIC适配器绑定在了一起,如下面这句:

client->adapter= adap;



如前文所述,i2c_register_adapter函数用于注册i2c_adapter,i2c_register_driver函数用于注册i2c_driver。i2c_register_adapter函数有下面一句:

adap->dev.bus= &i2c_bus_type;

i2c_register_driver函数有下面一句:

driver->driver.bus= &i2c_bus_type;



总之,都定义了IIC的总线类型——i2c_bus_type:

struct bus_type i2c_bus_type = {

.name ="i2c",

.match =i2c_device_match,

.probe =i2c_device_probe,

.remove =i2c_device_remove,

.shutdown = i2c_device_shutdown,

.pm = &i2c_device_pm_ops,

};



当设备或驱动添加到总线上时,会调用match指向的函数,用于找到是否有设备和驱动相匹配;当设备或驱动添加到总线上时,也会调用probe指向的函数,用于初始化与驱动相匹配的设备。因此无论是在创建i2c_adapter,还是i2c_driver的时候,都会调用这两个函数。

static int i2c_device_match(struct device *dev, structdevice_driver *drv)

{

structi2c_client *client = i2c_verify_client(dev);

structi2c_driver *driver;



if(!client)

return0;



/*Attempt an OF style match */

//设备和驱动是否匹配

if(of_driver_match_device(dev, drv))

return1;



driver= to_i2c_driver(drv);

/*match on an id table if there is one */

//如果驱动有设备列表,则用下面判断设备和驱动是否匹配

if(driver->id_table)

returni2c_match_id(driver->id_table,client) != NULL;



return0;

}



i2c_device_match函数返回非零值,表示设备和驱动匹配上了。at24_driver中有一个设备列表项——at24_ids:

static const struct i2c_device_id at24_ids[] = {

/*needs 8 addresses as A0-A2 are ignored */

{"24c00",AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },

/*old variants can't be handled with this generic entry! */

{"24c01",AT24_DEVICE_MAGIC(1024 / 8, 0) },

{"24c02",AT24_DEVICE_MAGIC(2048 / 8, 0) },

/*spd is a 24c02 in memory DIMMs */

{"spd", AT24_DEVICE_MAGIC(2048 / 8,

AT24_FLAG_READONLY| AT24_FLAG_IRUGO) },

{"24c04",AT24_DEVICE_MAGIC(4096 / 8, 0) },

/*24rf08 quirk is handled at i2c-core*/

{"24c08",AT24_DEVICE_MAGIC(8192 / 8, 0) },

{"24c16",AT24_DEVICE_MAGIC(16384 / 8, 0) },

{"24c32",AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },

{"24c64",AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },

{"24c128",AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },

{"24c256",AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },

{"24c512",AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },

{"24c1024",AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },

{"at24", 0 },

{/* END OF LIST */ }

};



上面结构中的一项"24c02",与我们前面定义的i2c_client.name一致,因此i2c_client和i2c_driver匹配上了。



static int i2c_device_probe(struct device *dev)

{

structi2c_client *client = i2c_verify_client(dev);

structi2c_driver *driver;

intstatus;



if(!client)

return0;



//提取IIC驱动

driver = to_i2c_driver(dev->driver);

if(!driver->probe || !driver->id_table)

return-ENODEV;

//i2c_client和i2c_driver绑定在了一起

client->driver= driver;

if(!device_can_wakeup(&client->dev))

device_init_wakeup(&client->dev,

client->flags& I2C_CLIENT_WAKE);

dev_dbg(dev,"probe\n");



//调用probe函数,也就是at24_probe函数

status= driver->probe(client, i2c_match_id(driver->id_table,client));

if(status) {

client->driver= NULL;

i2c_set_clientdata(client, NULL);

}

returnstatus;

}



经过上面的分析,可以看出IIC设备中的驱动指向了IIC驱动,IIC设备中的适配器指向了IIC适配器,这样三者就都绑定在了一起,成为了一个整体。



三、IIC设备的操作



系统对IIC设备的操作是在at24_probe函数完成的:

static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)

{

structat24_platform_data chip;

boolwritable;

intuse_smbus = 0;

structat24_data *at24;

interr;

unsignedi, num_addresses;

kernel_ulong_tmagic;



if(client->dev.platform_data) {

chip= *(struct at24_platform_data *)client->dev.platform_data;

}else {

if(!id->driver_data) {

err= -ENODEV;

gotoerr_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-typesvia platform_data

* is recommended anyhow.

*/

chip.page_size= 1;



/*update chipdata if OF is present */

at24_get_ofdata(client,&chip);



chip.setup= NULL;

chip.context= NULL;

}



//判断IIC设备(这里指的就是eeprom)容量是否为2的次幂

if(!is_power_of_2(chip.byte_len))

dev_warn(&client->dev,

"byte_lenlooks suspicious (no power of 2)!\n");

//判断eeprom的每页的大小

if(!chip.page_size) {

dev_err(&client->dev,"page_size must not be 0!\n");

err= -EINVAL;

gotoerr_out;

}

//判断eeprom每页的大小是否为2的次幂

if(!is_power_of_2(chip.page_size))

dev_warn(&client->dev,

"page_sizelooks suspicious (no power of 2)!\n");



/*Use I2C operationsunless we're stuck with SMBus extensions. */

if(!i2c_check_functionality(client->adapter,I2C_FUNC_I2C)) {

if(chip.flags & AT24_FLAG_ADDR16) {

err= -EPFNOSUPPORT;

gotoerr_out;

}

if(i2c_check_functionality(client->adapter,

I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {

use_smbus= I2C_SMBUS_I2C_BLOCK_DATA;

}else if (i2c_check_functionality(client->adapter,

I2C_FUNC_SMBUS_READ_WORD_DATA)) {

use_smbus= I2C_SMBUS_WORD_DATA;

}else if (i2c_check_functionality(client->adapter,

I2C_FUNC_SMBUS_READ_BYTE_DATA)) {

use_smbus= I2C_SMBUS_BYTE_DATA;

}else {

err= -EPFNOSUPPORT;

gotoerr_out;

}

}



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;

gotoerr_out;

}



mutex_init(&at24->lock);

at24->use_smbus= use_smbus;

at24->chip= chip;

at24->num_addresses= num_addresses;



/*

* Export the EEPROM bytes through sysfs, sincethat's convenient.

* By default, only root should see the data(maybe passwords etc)

*/

//下面是定义一些关于sysfs的属性

sysfs_bin_attr_init(&at24->bin);

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)) {



unsignedwrite_max = chip.page_size;



at24->macc.write= at24_macc_write;



at24->bin.write =at24_bin_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;

gotoerr_struct;

}

}else {

dev_warn(&client->dev,

"cannotwrite 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 0x%02x unavailable\n",

client->addr+ i);

err= -EADDRINUSE;

gotoerr_clients;

}

}



//为sysfs创建二进制文件

err= sysfs_create_bin_file(&client->dev.kobj, &at24->bin);

if(err)

gotoerr_clients;



i2c_set_clientdata(client, at24);



dev_info(&client->dev,"%zu byte %s EEPROM, %s, %u bytes/write\n",

at24->bin.size,client->name,

writable? "writable" : "read-only", at24->write_max);

if(use_smbus == I2C_SMBUS_WORD_DATA||

use_smbus == I2C_SMBUS_BYTE_DATA) {

dev_notice(&client->dev,"Falling back to %s reads, "

"performance will suffer\n",use_smbus ==

I2C_SMBUS_WORD_DATA? "word" : "byte");

}



/*export data to kernel code */

if(chip.setup)

chip.setup(&at24->macc,chip.context);



return0;



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);

returnerr;

}



在这里,读文件的函数为at24_bin_read,它又调用了at24_read函数,最后又调用at24_eeprom_read函数;同样的,写文件的函数为at24_bin_write,它又调用了at24_write函数,最后又调用at24_eeprom_write函数。at24_eeprom_read和at24_eeprom_write最终都要调用i2c_transfer函数,来真正完成对AT24C02的读写操作。



i2c_transfer函数已经在上一篇文章分析过了,这里就不再重复。



四、应用程序



基于sysfs文件系统的IIC子系统的整个过程就介绍完了,我们来验证一下。



系统为IIC创建的sysfs文件系统在/sys/bus/i2c/devices/0-0050/目录下,该目录下有一个eeprom文件,只要对该文件进行操作,就可以实现对eeprom的读写,我们来测试一下:

[root@zhaocj /]#hexdump -C /sys/bus/i2c/devices/0-0050/eeprom

00000000 61 62 63 0a 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |abc.............|

00000010 19 95 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f |................|

00000020 ee 71 67 23 42 25 26 27 00 9f 03 5a 2e 67 89 10 |.qg#B%&'...Z.g..|

00000030 a9 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f |.123456789:;<=>?|

00000040 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f |@ABCDEFGHIJKLMNO|

00000050 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f |PQRSTUVWXYZ[\]^_|

00000060 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f |`abcdefghijklmno|

00000070 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f |pqrstuvwxyz{|}~.|

00000080 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f |................|

00000090 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f |................|

000000a0 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab acad ae af |................|

000000b0 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf |................|

000000c0 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cccd ce cf |................|

000000d0 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df |................|

000000e0 e0 e1 e2 e3 e4 e5 e6 e7 03 5a 2e 67 8910 ee 9f |.........Z.g....|

000000f0 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fcfd fe ff |................|



在命令行下输入上述命令,就会把eeprom中的地址的内容全部呈现出来,cat也具有同样的功能。我们也可以通过echo修改内容:

[root@zhaocj /]#echo "zcj" >> /sys/bus/i2c/devices/0-0050/eeprom
[root@zhaocj /]#cat /sys/bus/i2c/devices/0-0050/eeprom
zcj


��qg#B%&'�Z.g��123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~������������������������������������������������������������������������������������������������������Z.g�������������������


下面我们写一段应用程序来实现对eeprom的读写,完成的任务是先从地址为0x40处开始写入数据,然后再从相同的位置读取:



#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

int main(void){
intfd, size, len, i;
char buf[50]=;
char *bufw="Hi,this is an eepromtest!";

len=strlen(bufw);

fd= open("/sys/bus/i2c/devices/0-0050/eeprom",O_RDWR);
if(fd< 0){
printf("####i2c test device open failed####/n");
return(-1);
}
//写操作
lseek(fd,0x40,SEEK_SET); //地址是0x40
if((size=write(fd,bufw, len))<0){
printf("write error\n");
return 1;
}
printf("writeok\n");
//读操作
lseek(fd,0x40, SEEK_SET);
if((size=read(fd,buf,len))<0){
printf("readerror\n");
return 1;
}
printf("readok\n");
for(i=0; i< len; i++)

printf("buff[%d]=%x\n",i, buf[i]);


close(fd);
return 0;
}


仅从撰写IIC应用程序来看,sysfs的方法要更简单一些,而且不涉及硬件的知识;而devfs的方法要灵活一些。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐