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;
}
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;
}
相关文章推荐
- uboot中ramdisk启动linux的两种方法
- windows 与Linux之间的文件传输
- Linux 安装MySQL
- Linux下iptables的用法
- LINUX 查看分区UUID的两种方法
- linux命令之uniq sort cut
- Linux操作系统下编辑保存退出命令
- ARM Linux 3.x的设备树(Device Tree)
- linux 安装ffmpeg
- Linux源地址路由
- linux 生成随机密码和wordlist常用方法
- 用 Python 脚本实现对 Linux 服务器的监控
- 11中操作网络的Linux命令
- (Mark) (Ubuntu)Ubuntu 12.04 LTS 通过ppa安装Fcitx 搜狗输入法 (Linux) (输入法)
- linux u盘启动工具unetbootin
- 开启linux命令行下的英汉词典
- linaro与linux kernel的关系
- Linux Mint下编译安装支持SPDY协议的Wireshark
- Linux rpm 命令参数使用详解[介绍和应用]
- linux 下查看机器是cpu是几核的