Linux设备之I2C
来源:互联网 发布:centos网络配置 双网卡 编辑:程序博客网 时间:2024/04/29 13:02
在内核中已经提供I2C子系统,在linux系统中,I2C驱动结构如下图所示:上面包含i2c_algorithm,其对应一套通信方法i2c_driver代表i2c从设备驱动i2c_client代表i2c从设备那么内核怎么完成匹配呢?首先来看看i2c_register_board_info()这上面只做了一个非常重要的,将i2c_board_info放入到__i2c_board_list链表,而这个info中存放的是i2c通信非常重要的,设备名字和设备地址。那么链表何时使用呢?这个在i2c_scan_static_board_info的时候会调用该函数遍历挂载__i2c_board_list链表上面的i2c设备的信息,也就是我们在启动的时候指出的i2c设备的信息,如果指定设备位于adapter所在的i2c总线上,那么就调用i2c_new_device()。该函数只是device_register,但是上面出现了一个新的结构,i2c_client,其实它就是一个struct device的i2c设备的封装。在client里保存该设备的相关信息,client->adapter指向了它所在的adapter。clent->dev所在的bus为i2c_bus_type,在device_register注册的时候,会调用总线的match函数。上面是匹配driver的id_table的名字和client的名字是否相同,那么会调用驱动的probe函数。3.直接i2c_add_driver而i2c_add_diver只是调用i2c_register_driver了上面比较主要的是driver_register,同事遍历driver->client链表,会调用driver->detect的函数。
其中从上图可以I2C由三大部分组成:
1、I2C核心:I2C核心提供了总线驱动和设备驱动的注册、注销的方法,I2C通信方法,与具体适配器无关的代码以及检测设备地址的代码等。
2、I2C总线驱动:对I2C硬件体系结构中适配器的实现,控制I2C总线驱动的代码,控制I2C适配器以主控方式产生开始位,停止位,读写以及设备读写方式,产生ack等。
3、I2C客户驱动程序:是对I2C硬件体系结构设备端得实现。
我们在分析其子系统之前,还是来看看其数据结构
i2c_adapter结构体表示一个物理的i2c总线控制器
点击(此处)折叠或打开
- struct i2c_adapter {
- struct module *owner;
- unsigned int class; /* classes to allow probing for */
- const struct i2c_algorithm *algo; /* the algorithm to access the bus */
- void *algo_data;
- /* data fields that are valid for all devices */
- struct rt_mutex bus_lock;
- int timeout; /* in jiffies */
- int retries;
- struct device dev; /* the adapter device */
- int nr;
- char name[48]; //适配器名字
- struct completion dev_released; //用于同步
- struct mutex userspace_clients_lock;
- struct list_head userspace_clients;
- };
点击(此处)折叠或打开
- struct i2c_algorithm {
- int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
- int num);//I2C传输函数指针
- int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size, union i2c_smbus_data *data);//SMBUS传输函数指针
- u32 (*functionality) (struct i2c_adapter *);//返回适配器支持的功能
- };
一个i2c适配器需要i2c_algorithm中提供的通信函数来控制适配器产生特定的访问函数。其中提供了关键的函数master_xfer()用于产生i2c访问周期需要的信号,以i2c_msg为单位
点击(此处)折叠或打开
- struct i2c_msg {
- __u16 addr; /* slave address */
- __u16 flags;
- __u16 len; /* msg length */
- __u8 *buf; /* pointer to msg data */
- };
点击(此处)折叠或打开
- struct i2c_driver {
- unsigned int class;
- int (*attach_adapter)(struct i2c_adapter *) __deprecated;//适配器函数指针
- int (*detach_adapter)(struct i2c_adapter *) __deprecated;
- /* 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 *);
- void (*alert)(struct i2c_client *, unsigned int data);
- 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 *, struct i2c_board_info *);
- const unsigned short *address_list;
- struct list_head clients;
- };
点击(此处)折叠或打开
- struct i2c_client {
- unsigned short flags; /* div., see below */
- unsigned short addr; /* chip address - NOTE: 7bit */
- /* 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;
- };
i2c_driver与i2c_client是一对多的关系,一个i2c_driver上可以支持多个同等类型的i2c_client。i2c_adapter与i2c_client的关系与i2c硬件体系中适配器和从设备的关系一致,i2c_client依附在i2c_adapter。
下面来看看内核提供任何去注册一个i2c的设备驱动
1.直接使用i2c_register_board_unfo完成设备注册,这种方法适合于i2c总线上预先已知设备,因此可以预先声明i2c设备在哪条总线上,在通过数组结构i2c_board_info,内核实例为arch\arm\mach-omap2\board-h4.c。
点击(此处)折叠或打开
- static struct i2c_board_info __initdata h4_i2c_board_info[] = {
- {
- I2C_BOARD_INFO("isp1301_omap", 0x2d),
- .irq = OMAP_GPIO_IRQ(125),
- },
- { /* EEPROM on mainboard */
- I2C_BOARD_INFO("24c01", 0x52),
- .platform_data = &m24c01,
- },
- { /* EEPROM on cpu card */
- I2C_BOARD_INFO("24c01", 0x57),
- .platform_data = &m24c01,
- },
- };
点击(此处)折叠或打开
- static void __init omap_h4_init(void)
- {
- (...)
- i2c_register_board_info(1, h4_i2c_board_info,
- ARRAY_SIZE(h4_i2c_board_info));
- (...)
- }
点击(此处)折叠或打开
- int __init
- i2c_register_board_info(int busnum,
- struct i2c_board_info const *info, unsigned len)
- {
- int status;
- down_write(&__i2c_board_lock);
- /* dynamic bus numbers will be assigned after the last static one */
- if (busnum >= __i2c_first_dynamic_bus_num)
- __i2c_first_dynamic_bus_num = busnum + 1;
- for (status = 0; len; len--, info++) {
- struct i2c_devinfo *devinfo;
- devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
- if (!devinfo) {
- pr_debug("i2c-core: can't register boardinfo!\n");
- status = -ENOMEM;
- break;
- }
- devinfo->busnum = busnum;
- devinfo->board_info = *info;
- list_add_tail(&devinfo->list, &__i2c_board_list);
- }
- up_write(&__i2c_board_lock);
- return status;
- }
点击(此处)折叠或打开
- static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
- {
- struct i2c_devinfo *devinfo;
- down_read(&__i2c_board_lock);
- list_for_each_entry(devinfo, &__i2c_board_list, list) {
- if (devinfo->busnum == adapter->nr
- && !i2c_new_device(adapter,
- &devinfo->board_info))
- dev_err(&adapter->dev,
- "Can't create device at 0x%02x\n",
- devinfo->board_info.addr);
- }
- up_read(&__i2c_board_lock);
- }
2.直接使用2c_new_device, i2c_new_probed_device
点击(此处)折叠或打开
- struct i2c_client *
- i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
- {
- struct i2c_client *client;
- int status;
- client = kzalloc(sizeof *client, GFP_KERNEL);
- if (!client)
- return NULL;
- client->adapter = adap;
- client->dev.platform_data = info->platform_data;
- if (info->archdata)
- client->dev.archdata = *info->archdata;
- client->flags = info->flags;
- client->addr = info->addr;
- client->irq = info->irq;
- strlcpy(client->name, info->type, sizeof(client->name));
- /* Check for address validity */
- status = i2c_check_client_addr_validity(client);
- if (status) {
- dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
- client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
- goto out_err_silent;
- }
- /* Check for address business */
- status = i2c_check_addr_busy(adap, client->addr);
- if (status)
- goto out_err;
- client->dev.parent = &client->adapter->dev;
- client->dev.bus = &i2c_bus_type;
- client->dev.type = &i2c_client_type;
- client->dev.of_node = info->of_node;
- /* For 10-bit clients, add an arbitrary offset to avoid collisions */
- dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
- client->addr | ((client->flags & I2C_CLIENT_TEN)
- ? 0xa000 : 0));
- status = device_register(&client->dev);
- if (status)
- goto out_err;
- dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
- client->name, dev_name(&client->dev));
- return client;
- out_err:
- dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
- "(%d)\n", client->name, client->addr, status);
- out_err_silent:
- kfree(client);
- return NULL;
- }
点击(此处)折叠或打开
- static int i2c_device_match(struct device *dev, struct device_driver *drv)
- {
- struct i2c_client *client = i2c_verify_client(dev);
- struct i2c_driver *driver;
- if (!client)
- return 0;
- /* Attempt an OF style match */
- if (of_driver_match_device(dev, drv))
- return 1;
- driver = to_i2c_driver(drv);
- /* match on an id table if there is one */
- if (driver->id_table)
- return i2c_match_id(driver->id_table, client) != NULL;
- return 0;
- }
点击(此处)折叠或打开
- static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
- const struct i2c_client *client)
- {
- while (id->name[0]) {
- if (strcmp(client->name, id->name) == 0)
- return id;
- id++;
- }
- return NULL;
- }
下面在分析下i2c_new_probed_device有啥不一样,前面一个认为设备肯定存在,而后面的是对于已经识别出来的设备,才会创建。
点击(此处)折叠或打开
- for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {
- /* Check address validity */
- if (i2c_check_addr_validity(addr_list[i]) < 0) {
- dev_warn(&adap->dev, "Invalid 7-bit address "
- "0x%02x\n", addr_list[i]);
- continue;
- }
- /* Check address availability */
- if (i2c_check_addr_busy(adap, addr_list[i])) {
- dev_dbg(&adap->dev, "Address 0x%02x already in "
- "use, not probing\n", addr_list[i]);
- continue;
- }
- /* Test address responsiveness */
- if (probe(adap, addr_list[i]))
- break;
- }
前面的2中方法,都要实现确定适配器,如果我们不知道这个i2c设备在那个适配器上,怎么办?内核提供了一种去class表示在所有的适配器上查找一些i2c设备的地址。
点击(此处)折叠或打开
- static struct i2c_driver at24_driver = {
- .driver = {
- .name = "at24",
- .owner = THIS_MODULE,
- },
- .probe = at24_probe,
- .remove = __devexit_p(at24_remove),
- .id_table = at24_ids,
- };
点击(此处)折叠或打开
- 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);
- return i2c_add_driver(&at24_driver);
- }
点击(此处)折叠或打开
- int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
- {
- int res;
- /* Can't register until after driver model init */
- if (unlikely(WARN_ON(!i2c_bus_type.p)))
- return -EAGAIN;
- /* add the driver to the list of i2c drivers in the driver core */
- driver->driver.owner = owner;
- driver->driver.bus = &i2c_bus_type;
- /* When registration returns, the driver core
- * will have called probe() for all matching-but-unbound devices.
- */
- res = driver_register(&driver->driver);
- if (res)
- return res;
- /* Drivers should switch to dev_pm_ops instead. */
- if (driver->suspend)
- pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
- driver->driver.name);
- if (driver->resume)
- pr_warn("i2c-core: driver [%s] using legacy resume method\n",
- driver->driver.name);
- pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
- INIT_LIST_HEAD(&driver->clients);
- /* Walk the adapters that are already present */
- i2c_for_each_dev(driver, __process_new_driver);
- return 0;
- }
0
上一篇:照亮职场前途的10大经典故事
下一篇:andorid之摄像头驱动流程
相关热门文章
- GNU C __attribute__ 机制简...
- linux的磁盘的分区
- linux内核编译简单过程...
- 网站在移动终端(手机)显示错...
- 直销促销:Agilent E4402b E44...
- linux 常见服务端口
- 【ROOTFS搭建】busybox的httpd...
- 什么是shell
- linux socket的bug??
- linux的线程是否受到了保护?...
- 2013李宗盛北京演唱会订票电话...
- oracle11gr2 rac on aix6l安装...
- 通过oracle 10g 导出11g数据...
- 关于ethtool DEVNAME 打印的条...
- rsync安装时make install出错...
给主人留下些什么吧!~~
评论热议
- Linux设备之I2C
- Linux设备之I2C
- Linux之I2C设备总结
- linux设备驱动之I2C
- linux之i2c设备注册
- Linux设备之I2C体系
- Linux Kernel 设备驱动之I2C之i2c设备文件
- Linux设备驱动之I2C架构分析
- Linux设备驱动之I2C架构分析
- Linux设备驱动之I2C架构分析
- Linux设备驱动之I2C架构分析
- Linux设备驱动之I2C架构分析
- Linux设备驱动之I2C架构分析
- linux设备模型之I2C子系统
- linux设备模型之i2c子系统
- Linux设备驱动之I2C架构分析
- Linux设备驱动之I2C架构分析
- Linux设备驱动之I2C架构分析
- c# 多网卡获取ipv4 ip地址办法列举
- 总线设备驱动模型--设备篇
- 总线设备驱动模型----驱动篇
- 总线设备驱动模型---platform篇
- Linux的LCD驱动
- Linux设备之I2C
- andorid之摄像头驱动流程
- Andriod系统的Input设备分析二
- linux之块设备驱动
- linux块设备之nand flash
- 高性能的MySQL(5)创建高性能的索引一B-Tree索引
- 把JDesktopPane里面设置层
- 关于C++中const修饰的一些备忘.....【2013.10.23】
- 高性能的MySQL(5)创建高性能的索引一哈希索引
原创粉丝点击
热门IT博客
热门问题
老师的惩罚
人脸识别
我在镇武司摸鱼那些年
重生之率土为王
我在大康的咸鱼生活
盘龙之生命进化
天生仙种
凡人之先天五行
春回大明朝
姑娘不必设防,我是瞎子
马镫钢筋规范
镭
镭雕
居里夫人镭
镭怎么读
镭的作用
镭是什么东西
硫酸镭
镭威视
镭元素
镭图片
镭克音响
镭多少钱一克
镭威视下载
镭风hd6570
7750显卡
镭风显卡怎么样
镭风显卡
镭的拼音
镭是谁发明的
镭是谁发现的
radium
镭组词
镭的价格
镭读音是什么
镭怎么读音
镭的图片
镭波
镭的读音
镭是什么
镭射笔
镭射眼
镭射灯
镭射切割机
快乐镭射铺
防伪镭射膜
镭射膜
镭射不干胶
aj4黑镭射
什么是镭射
镭射机