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

linux设备驱动之 i2c设备驱动 at24c08驱动程序分析【全部地址的操作】

2014-03-06 15:05 579 查看
linux-2.6.22源码分析\linux-2.6.22\drivers\i2c\chips\eeprom.c为例,分析i2c设备驱动程序的原理

1.从驱动的入口函数开始分析

eeprom_init

>i2c_add_driver(&eeprom_driver)

>i2c_register_driver(THIS_MODULE, driver)

>is_newstyle_driver(driver) //这是一处宏定义:#define is_newstyle_driver(d) ((d)->probe || (d)->remove)

/*用于判断是否是新风格的驱动,新风格的驱动i2c_driver成员的probe与remove函数指针都为空*/

接下来填充i2c_driver的owner与bus成员

>driver_register(&driver->driver)

>bus_add_driver(&driver->driver)也就是(struct device_driver * drv)//添加成功返回0

>list_add_tail(&driver->list,&drivers)//把该i2c_driver添加到链表drivers全局变量中

>list_for_each_entry(adapter, &adapters, list) //遍历adapters适配器链表的所有成员

{driver->attach_adapter(adapter);} //调用i2c_driver->attach_adapter

在eeprom.c中定义了一个全局变量:

static struct i2c_driver eeprom_driver = {

.driver = {

.name = "eeprom",

},

.id = I2C_DRIVERID_EEPROM,

.attach_adapter = eeprom_attach_adapter,

.detach_client = eeprom_detach_client,

};

2.i2c设备的探测过程分析

在1中遍历调用了适配器链表中的每一个成员的attach_adapter成员函数,来完成i2c设备的探测

driver->attach_adapter(adapter)

等效于

eeprom_attach_adapter

>i2c_probe(adapter, &addr_data, eeprom_detect)

//当探测到i2c设备后会调用第三个参数所指的函数:也就是函数eeprom_detect。

/*在i2c.h头文件中定义了addr_data全局变量

*static struct i2c_client_address_data addr_data = { \

* .normal_i2c = normal_i2c, \

* .probe = probe, \

* .ignore = ignore, \

* .forces = forces, \

*}

*/

函数原型为:

i2c_probe(struct i2c_adapter *adapter,

struct i2c_client_address_data *address_data,

int (*found_proc) (struct i2c_adapter *, int, int))

>判断是否强制识别 if(address_data->forces),如果设置了i2c_client_address_data的

成员.forces则强制识别,采取相应的措施。

>i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)//检查是否支持SMBUS_QUICK方式

>for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2){

判断if(address_data->probe[i] || address_data->probe[i] == ANY_I2C_BUS)成立则:

>i2c_probe_address(adapter,address_data->probe[i + 1],-1, found_proc);

>i2c_check_addr(adapter, addr)

>__i2c_check_addr(adapter, addr)//检查地址是否可用

>i2c_smbus_xfer(adapter, addr, 0, 0, 0,I2C_SMBUS_QUICK, NULL)

>如果定义了adapter->algo->smbus_xfer则调用:

adapter->algo->smbus_xfer(adapter,addr,flags,read_write,command,size,data)

>found_proc(adapter, addr, kind)//也就是调用i2c_probe函数的第三个参数

(该参数为一个函数)

>否则调用:

i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data);

}

由以上的程序分析可知:

在编写i2c设备驱动时需要手动 设置i2c_client_address_data结构体的成员变量。使驱动能探测到设备

3.i2c_probe的第三个参数(函数)分析

在事例代码中是eeprom_detect,下面分析这个函数都是做了什么工作。

如果通过第2项的程序调用后,说明i2c设备(i2c_client)已经找到能配合工作的适配器(adapter),接下来

就会调用用户自定义的一个函数,该函数为 eeprom_driver结构体成员函数指针:attach_adapter中调用

的i2c_probe(adapter, &addr_data, xxxx)的第三个参数xxxx

xxxx <==>eeprom_detect(struct i2c_adapter *adapter, int address, int kind)

>struct i2c_client *new_client //在这里填充一个和欲驱动的设备相应的i2c_client结构体

或者在该函数中分配一个与设备相关的结构体

data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL)

>i2c_set_clientdata(new_client, data);//将指针new_client->dev->driver_data(=data)指向data

>填充i2c_client结构体成员:

new_client->addr = address;

new_client->adapter = adapter;

new_client->driver = &eeprom_driver;

new_client->flags = 0;

/* Fill in the remaining client fields */

strlcpy(new_client->name, "eeprom", I2C_NAME_SIZE);

>i2c_attach_client(new_client) //告诉i2c协议层已经生成了一个i2c_client

>struct i2c_adapter *adapter = client->adapter;//取出client中的适配器adapter

>list_add_tail(&client->list,&adapter->clients);//把client加入到适配器adapter的链表尾

>现对client的成员进行进一步地相应设置

>device_register(&client->dev)

>device_initialize(dev);

>device_add(dev)

>sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr)//创建sysfs

至此i2c设备驱动程序分析完毕,那么,如何写一个i2c设备驱动呢?比如at24c08

a.在入口函数中调用i2c_add_driver(struct i2c_driver *driver)

所以需要定义一个全局变量 i2c_driver ,并对一些必要的成员进行初始化

b.编写 i2c_driver成员函数: attach_adapter 与 detach_client

int (*attach_adapter)(struct i2c_adapter *);

>i2c_probe

所以需要提供 i2c_client_address_data 结构体,并提供一个探测函数:gq_at24cxx_detect

int (*detach_adapter)(struct i2c_adapter *);

c.定义struct i2c_client *new_client,并填充

i2c_attach_client(at24cxx_client);

d.至此,其实该设备的i2c驱动框架已经完成了,下来要做的就是提供一个字符设备或块设备驱动的接口

e.读写i2c设备时的接口函数设计:

读过程:

copy_from_user(&dev_addr,buf,1);//拷贝来数据地址

需要构造两个msg消息结构,第一个用来写欲读取的数据地址,第二个才是用来读取数据值

/* 数据传输:源地址,目的地址,数据长度 */

/* 先把要读的数据地址写入 */

msg[0].addr = at24cxx_client->addr;//distination

msg[0].len = 1; //length 地址长度=1byte

msg[0].buf = &dev_addr; //source

msg[0].flags = 0; //write flag

/* 再把数据读出 */

msg[1].addr = at24cxx_client->addr;//source

msg[1].len = 1; //length 数据长度=1byte

msg[1].buf = &read_data; //distination

msg[1].flags = I2C_M_RD; //read flag

构造完成后,调用下面的这个函数,它会调用i2c-core等提供的适配器算法的相关函数来 发送/接收 数据

i2c_transfer(at24cxx_client->adapter, msg, 2);

copy_to_user(buf,&read_data,1);

把读到的数据拷贝给应用程序

写过程:

copy_from_user(dev_addr,buf,2);//把写的地址和数据拷贝过来

构造一个msg消息,用于发送地址和数据

/* 数据传输:源地址,目的地址,数据长度 */

msg[0].addr = at24cxx_client->addr;//distination

msg[0].len = 2; //length 地址+数据 长度=2byte

msg[0].buf = dev_addr; //source

msg[0].flags = 0; //write flag

ret = i2c_transfer(at24cxx_client->adapter, msg, 1);//同读过程

f.卸载

在gq_at24cxx_detach_client 中完成卸载所需要的操作,释放内存,反注册等。

源码如下:

/*

* 在字符驱动中的读写操作:

* 全部页的读写操作

* 读: copy_from_user(write_buf,buf,2) write_buf数组的0元素为该页中的地址,1元素为页数

* 写: copy_from_user(dev_addr,buf,3) dev_addr数组的0元素为页数,1元素为该页中的地址,2元素为写入的数据

*/

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/i2c.h>

#include <linux/fs.h>

#include <linux/uaccess.h>

#include <linux/types.h>

static int gq_at24cxx_detect(struct i2c_adapter *adapter, int addrss, int kind);

static unsigned short normal_gq[] = {I2C_CLIENT_END};

static unsigned short ignore_gq[] = {ANY_I2C_BUS,0x50,I2C_CLIENT_END};

static struct i2c_client_address_data gq_address_data = {

.normal_i2c = normal_gq,

.probe = ignore_gq,

.ignore = ignore_gq,

//.forces = ,

};

static struct i2c_client *at24cxx_client;

static struct class *cls;

static struct class_device *cls_dev;

static int major;

static ssize_t at24cxx_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)

{

#if 0

/*现在驱动程序只能读一页的数据 256 byte*/

struct i2c_msg msg[2];

unsigned char dev_addr;

unsigned char read_data;

int ret;

copy_from_user(&dev_addr,buf,1);

/* 数据传输:源地址,目的地址,数据长度 */

/* 先把要读的数据地址写入 */

msg[0].addr = at24cxx_client->addr;//distination

msg[0].len = 1; //length 地址长度=1byte

msg[0].buf = &dev_addr; //source

msg[0].flags = 0; //write flag

/* 再把数据读出 */

msg[1].addr = at24cxx_client->addr;//source

msg[1].len = 1; //length 数据长度=1byte

msg[1].buf = &read_data; //distination

msg[1].flags = I2C_M_RD; //read flag

ret = i2c_transfer(at24cxx_client->adapter, msg, 2);

if(ret==2){

copy_to_user(buf,&read_data,1);

return 1;

}

else

return -1;

#endif

#if 1

/*使驱动程序能读e2prom全部的页*/

struct i2c_msg msg[2];

unsigned char write_buf[2];/*数组的0元素为该页中的地址,1元素为页数*/

unsigned char read_buf;

int ret;

if(size!=2){

printk("error param:write size is 2\n");

return -1;

}

copy_from_user(write_buf,buf,2);

/* 数据传输:源地址,目的地址,数据长度 */

/* 先把要读的数据地址写入 */

msg[0].addr = at24cxx_client->addr + write_buf[1]%4;//distination

msg[0].len = 1; //length 地址长度=1byte

msg[0].buf = &write_buf[0]; //source

msg[0].flags = 0; //write flag

/* 再把数据读出 */

msg[1].addr = at24cxx_client->addr + write_buf[1]%4;//source

msg[1].len = 1; //length 数据长度=1byte

msg[1].buf = &read_buf; //distination

msg[1].flags = I2C_M_RD; //read flag

ret = i2c_transfer(at24cxx_client->adapter, msg, 2);

if(ret==2){

copy_to_user(buf,&read_buf,1);

return 1;

}

else

return -1;

#endif

}

static ssize_t at24cxx_write(struct file *filp, char __user *buf, size_t size, loff_t *offset)

{

#if 0

/*现在驱动程序只能写一页的数据 256 byte*/

struct i2c_msg msg[1];

unsigned char dev_addr[2];/*第一个元素是数据地址,第二个元素是数据值*/

int ret;

copy_from_user(dev_addr,buf,2);

/* 数据传输:源地址,目的地址,数据长度 */

msg[0].addr = at24cxx_client->addr;//distination

msg[0].len = 2; //length 地址+数据 长度=2byte

msg[0].buf = dev_addr; //source

msg[0].flags = 0; //write flag

// printk("i2c dev write\n");

ret = i2c_transfer(at24cxx_client->adapter, msg, 1);

if(ret==1){

return 2;

}

else

return -1;

#endif

#if 1

/*使驱动程序能写e2prom全部的页*/

struct i2c_msg msg[1];

unsigned char dev_addr[3];/*第一二个元素是数据地址并认为数组的0为高位1为低位,第三个元素是数据值*/

/*dev_addr数组的0元素为页数,1元素为该页中的地址,2元素为写入的数据*/

int ret;

if(size!=3){

printk("error param:write size is 3\n");

return -1;

}

copy_from_user(dev_addr,buf,3);

/* 数据传输:源地址,目的地址,数据长度 */

msg[0].addr = at24cxx_client->addr+dev_addr[0]%4;//distination

msg[0].len = 2; //length 地址+数据 长度=2byte

msg[0].buf = &dev_addr[1]; /*source第一个元素是数据地址,第二个元素是数据值*/

msg[0].flags = 0; //write flag

ret = i2c_transfer(at24cxx_client->adapter, msg, 1);

if(ret==1){

return 2;

}

else{

printk("error:i2c_transfer failed!\n");

return -1;

}

#endif

}

static struct file_operations at24cxx_fops = {

.read = at24cxx_read,

.write = at24cxx_write,

.owner = THIS_MODULE,

};

static int gq_at24cxx_attach_adapter(struct i2c_adapter *adapter)

{

printk("attach_adapter\n");

return i2c_probe(adapter, &gq_address_data, gq_at24cxx_detect);

}

static int gq_at24cxx_detach_client(struct i2c_adapter *adapt)

{

printk("detach_client\n");

class_device_destroy(cls,MKDEV(major,0));

class_destroy(cls);

unregister_chrdev(major,"at24cxx");

/* detach the client */

i2c_detach_client(at24cxx_client);

kfree(at24cxx_client);

return 0;

}

static struct i2c_driver gq_at24cxx_driver = {

.driver ={

.name = "at24cxx",

},

.id = -1,

.attach_adapter = gq_at24cxx_attach_adapter,

.detach_client = gq_at24cxx_detach_client,

};

static int gq_at24cxx_detect(struct i2c_adapter *adapter, int addrss, int kind)

{

printk("detect the at24c08 device 0x%2x\n",addrss);

at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);

if(at24cxx_client==NULL)

{

printk("alloc client failed\n");

return -1;

}

/* 设置i2c_client */

at24cxx_client->adapter = adapter;

at24cxx_client->addr = addrss;

at24cxx_client->driver = &gq_at24cxx_driver;

at24cxx_client->flags = 0;

strcpy(at24cxx_client->name, "at24cxx");

/* 通知协议层已经生成一个client,并attach */

i2c_attach_client(at24cxx_client);

/* 下来就提供字符设备接口 */

major = register_chrdev(0, "at24cxx", &at24cxx_fops);

cls = class_create(THIS_MODULE, "gq_at24c08");

cls_dev = class_device_create(cls, NULL, MKDEV(major,0), NULL, "at24cxx");

return 0;

}

static int gq_at24cxx_init(void)

{

i2c_add_driver(&gq_at24cxx_driver);

return 0;

}

static void gq_at24cxx_exit(void)

{

i2c_del_driver(&gq_at24cxx_driver);

}

module_init(gq_at24cxx_init);

module_exit(gq_at24cxx_exit);

MODULE_LICENSE("GPL");

测试:

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int main(int argc,char **argv)

{

#if 0

/*测试读写at24c08第0页*/

int fd;

unsigned char buf[2];

unsigned char read_addr;

int i;

fd = open("/dev/at24cxx",O_RDWR);

if(fd<0)

{

printf("open error\n");

return -1;

}

for(i=0;i<256;i++){

buf[0] = i;

buf[1] = 255-i;

if(2==write(fd,buf,2))

{

printf("write %d : data:0x%2x \n",i,buf[1]);

}

}

for(i=0;i<256;i++){

read_addr = i;

if(1==read(fd,&read_addr,1))

{

printf("read %d : data:0x%2x \n",i,read_addr);

}

}

#endif

#if 1

/*测试读写at24c08的全部页*/

int fd;

unsigned char buf[3]={3,0xfe,0x01};

unsigned short read_addr=1000;

int i;

fd = open("/dev/at24cxx",O_RDWR);

if(fd<0)

{

printf("open error\n");

return -1;

}

for(i=0;i<1024;i++){

if(i%4==0)

printf("\n");

buf[0]=i/256;

buf[1]=i%256;

buf[2]=i%256;

if(2==write(fd,buf,3))

{

printf("write %d : data 0x%2x__",i,buf[2]);

}

}

for(i=0;i<1024;i++){

read_addr = i;

if(1==read(fd,&read_addr,2))

{

if(i%4==0)

printf("\n");

printf("read %d : data:0x%2x__",i,read_addr&0xff);

}

}

close(fd);

#endif

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: