Linux驱动之i2c

来源:互联网 发布:2016淘宝打假被关店 编辑:程序博客网 时间:2024/05/17 03:51

如果应聘Linux驱动的工作岗位,面试官一般就会问你i2c驱动程序框架,下面就以我个人的理解来谈谈i2c驱动程序框架。

Linux驱动中的 i2c驱动主要有 四个结构体,要弄清Linux下i2c驱动,必须理解一下四个结构体的关系,

struct i2c_adapter {struct module *owner;             //所属模块unsigned int id;                  //algorithm 类型,定义在i2c-id.h中,以 I2C_ALGO_开始unsigned int class;  /* classes to allow probing for */const struct i2c_algorithm *algo; //主要是i2c通信函数void *algo_data;                  //私有数据,自己定义的数据一般指向这里/* data fields that are valid for all devices*/u8 level; /* nesting level for lockdep */struct mutex bus_lock;int timeout;/* in jiffies */int retries;                    //重复传输的次数,一般是1,struct device dev;/* the adapter device */int nr;char name[48];struct completion dev_released;};

</pre><pre class="cpp" name="code">struct i2c_algorithm {/* If an adapter algorithm can't do I2C-level access, set master_xfer   to NULL. If an adapter algorithm can do SMBus access, set   smbus_xfer. If set to NULL, the SMBus protocol is simulated   using common I2C messages *//* master_xfer should return the number of messages successfully   processed, or a negative value on error */int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,   int num);int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,   unsigned short flags, char read_write,   u8 command, int size, union i2c_smbus_data *data);/* To determine what the adapter supports */u32 (*functionality) (struct i2c_adapter *);};


struct i2c_client {unsigned short flags;/* div., see below*/unsigned short addr;/* chip address - NOTE: 7bit低7位*//* addresses are stored in the*//* _LOWER_ 7 bits*/char name[I2C_NAME_SIZE];struct i2c_adapter *adapter;/* the adapter we sit on*/struct i2c_driver *driver;/* and our access routines*/struct device dev;/* the device structure*/int irq;/* irq issued by device*/struct list_head detected;};

struct i2c_driver {unsigned int class;/* Notifies the driver that a new bus has appeared or is about to be * removed. You should avoid using this if you can, it will probably * be removed in a near future. */int (*attach_adapter)(struct i2c_adapter *);int (*detach_adapter)(struct i2c_adapter *);/* Standard driver model interfaces */int (*probe)(struct i2c_client *, const struct i2c_device_id *);int (*remove)(struct i2c_client *);/* driver model interfaces that don't relate to enumeration  */void (*shutdown)(struct i2c_client *);int (*suspend)(struct i2c_client *, pm_message_t mesg);int (*resume)(struct i2c_client *);/* a ioctl like command that can be used to perform specific functions * with the device. */int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);struct device_driver driver;const struct i2c_device_id *id_table;/* Device detection callback for automatic device creation */int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);const struct i2c_client_address_data *address_data;struct list_head clients;};


理解以上四个结构体非常重要,i2c_adapter是适配器,即cpu里的i2c控制器,i2c_algorithm 是传输算法,即CPU中的I2C控制器怎样传输,i2c_client对应的是外设,比如e2prom,i2c_driver对应的则是外设驱动,就驱动client的方法,

这四个结构体里各指针指向关系如下图,


如下是AT24CXX的i2c驱动例子,以该例子为例来说明函数的调用关系

#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/jiffies.h>#include <linux/i2c.h>#include <linux/mutex.h>#include <linux/fs.h>#include <asm/uaccess.h>static unsigned short ignore[]      = { I2C_CLIENT_END };static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位 */                                        /* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};static unsigned short * forces[] = {force_addr, NULL};/*如果将.normal_i2c注释掉,将.force打开,则强制认为这个设备存在*//static struct i2c_client_address_data addr_data = {.normal_i2c= normal_addr,  /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */.probe= ignore,.ignore= ignore,//.forces     = forces, /* 强制认为存在这个设备 */};static struct i2c_driver at24cxx_driver;static int major;static struct class *cls;struct i2c_client *at24cxx_client;static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset){unsigned char address;unsigned char data;struct i2c_msg msg[2];int ret;/* address = buf[0]  * data    = buf[1] */if (size != 1)return -EINVAL;copy_from_user(&address, buf, 1);/* 数据传输三要素: 源,目的,长度 *//* 读AT24CXX时,要先把要读的存储空间的地址发给它 */msg[0].addr  = at24cxx_client->addr;  /* 目的 */msg[0].buf   = &address;              /* 源 */msg[0].len   = 1;                     /* 地址=1 byte */msg[0].flags = 0;                     /* 表示写 *//* 然后启动读操作 */msg[1].addr  = at24cxx_client->addr;  /* 源 */msg[1].buf   = &data;                 /* 目的 */msg[1].len   = 1;                     /* 数据=1 byte */msg[1].flags = I2C_M_RD;                     /* 表示读 */ret = i2c_transfer(at24cxx_client->adapter, msg, 2);if (ret == 2){copy_to_user(buf, &data, 1);return 1;}elsereturn -EIO;}static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset){unsigned char val[2];struct i2c_msg msg[1];int ret;/* address = buf[0]  * data    = buf[1] */if (size != 2)return -EINVAL;copy_from_user(val, buf, 2);/* 数据传输三要素: 源,目的,长度 */msg[0].addr  = at24cxx_client->addr;  /* 目的 */msg[0].buf   = val;                   /* 源 */msg[0].len   = 2;                     /* 地址+数据=2 byte */msg[0].flags = 0;                     /* 表示写 */ret = i2c_transfer(at24cxx_client->adapter, msg, 1);if (ret == 1)return 2;elsereturn -EIO;}static struct file_operations at24cxx_fops = {.owner = THIS_MODULE,.read  = at24cxx_read,.write = at24cxx_write,};static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind){printk("at24cxx_detect\n");/* 构构一个i2c_client结构体: 以后收改数据时会用到它 */at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);at24cxx_client->addr    = address;at24cxx_client->adapter = adapter;at24cxx_client->driver  = &at24cxx_driver;strcpy(at24cxx_client->name, "at24cxx");i2c_attach_client(at24cxx_client);major = register_chrdev(0, "at24cxx", &at24cxx_fops);cls = class_create(THIS_MODULE, "at24cxx");class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */return 0;}static int at24cxx_attach(struct i2c_adapter *adapter){return i2c_probe(adapter, &addr_data, at24cxx_detect);}static int at24cxx_detach(struct i2c_client *client){printk("at24cxx_detach\n");class_device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, "at24cxx");i2c_detach_client(client);kfree(i2c_get_clientdata(client));return 0;}/* 1. 分配一个i2c_driver结构体 *//* 2. 设置i2c_driver结构体 */static struct i2c_driver at24cxx_driver = {.driver = {.name= "at24cxx",},.attach_adapter = at24cxx_attach,.detach_client  = at24cxx_detach,};static int at24cxx_init(void){i2c_add_driver(&at24cxx_driver);return 0;}static void at24cxx_exit(void){i2c_del_driver(&at24cxx_driver);}module_init(at24cxx_init);module_exit(at24cxx_exit);MODULE_LICENSE("GPL");


i2c在Linux中是总线的形势来管理adapter和client的,如下


                                      

当调用i2c_add_adapter的时候,就会在i2c总线上挂载一个i2c_adapter,当注册一个client的时候就会在总线链表上挂一个i2c_client

每次注册一 i2c_driverr的时候就会遍历总线上的i2c_adapter链表,取每个adapter,用adapter中的传输函数来探测外设,如果有应答,则将该client中的adapter指针指向这个i2c_adapter结构体,


i2c_add_driver

    i2c_register_drvier

        driver->driver.owner = owner;
        driver->driver.bus = &i2c_bus_type;

        bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter); //遍历i2c上的adapter,取每个adapter,然后用该adapter中的传输函数来探测该adapter是否匹配该client

           __attach_adapter 

              adapter = to_i2c_adapter(dev);

              i2c_detect(adapter, driver);

                   temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); //构造 i2c_client

                   i2c_detect_address(temp_client, -1, driver);

                       i2c_smbus_xfer

                            adapter->algo->smbus_xfer //该传输函数对应i2c-s3c2410.c中的s3c24xx_i2c_xfer

我们编写i2c的时候,只需要定义i2c_driver就可以,当然前提是对应的处理器是已经定义了i2c_adapter并且已经注册了,比如s3c24xx处理器,在Linux里就已经有对应的adapter,所以我们只需要编写client端就可以,

注册i2c_adapter的时候也一样,会遍历i2c总线上的client,取每个client,然后探测该client是否能够利用该adapter中的传输函数来探测,


at24cxx的测试函数如下

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>/* i2c_test r addr * i2c_test w addr val */void print_usage(char *file){printf("%s r addr\n", file);printf("%s w addr val\n", file);}int main(int argc, char **argv){int fd;unsigned char buf[2];if ((argc != 3) && (argc != 4)){print_usage(argv[0]);return -1;}fd = open("/dev/at24cxx", O_RDWR);if (fd < 0){printf("can't open /dev/at24cxx\n");return -1;}if (strcmp(argv[1], "r") == 0){buf[0] = strtoul(argv[2], NULL, 0);read(fd, buf, 1);printf("data: %c, %d, 0x%2x\n", buf[0], buf[0], buf[0]);}else if (strcmp(argv[1], "w") == 0){buf[0] = strtoul(argv[2], NULL, 0);buf[1] = strtoul(argv[3], NULL, 0);write(fd, buf, 2);}else{print_usage(argv[0]);return -1;}return 0;}


                  






0 0
原创粉丝点击