Linux I2C设备驱动编写
来源:互联网 发布:网站源代码怎么修改seo 编辑:程序博客网 时间:2024/05/16 23:39
Linux I2C设备驱动编写(一)
Linux定义了系统的I2C驱动体系结构,在Linux系统中,I2C驱动由3部分组成,即I2C核心、I2C总线驱动和I2C设备驱动。这3部分相互协作,形成了非常通用、可适应性很强的I2C框架。Linux的I2C体系结构分为3个组成部分:I2C核心、I2C总线驱动、I2C设备驱动,如下图所示。I2C核心提供总线驱动和设备驱动的注册、注销方法,algorithm;I2C总线驱动对硬件体系结构中适配器的实现,主要包括适配器i2c_adapter、适配器通信算法i2c_algorithm,如果CPU集成了I2C控制器并且linux内核支持这个CPU,那么总线驱动就不用管,比如S3C2440就属于这类情况,在后文中我们将分析它的总线驱动;I2C设备驱动是具体的一个设备(如AT24C02),挂接在CPU控制的I2C适配器的设备驱动,有了这部分驱动才能使用控制器操作该设备,设备驱动主要包括i2c_driver和i2c_client数据结构。
Linux的I2C体系结构分为3个组成部分:
·I2C核心:
I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(即“algorithm”)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。这部分是与平台无关的。
·I2C总线驱动:
I2C总线驱动是对I2C硬件体系结构中适配器端的实现。I2C总线驱动主要包含了I2C适配器数据结构i2c_adapter、I2C适配器的algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数。经由I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。不同的CPU平台对应着不同的I2C总线驱动。
总线驱动的职责,是为系统中每个I2C总线增加相应的读写方法。但是总线驱动本身并不会进行任何的通讯,它只是存在在那里,等待设备驱动调用其函数。
这部分在MTK 6516中是由MTK已经帮我们实现了的,不需要我们更改。
· I2C设备驱动:
I2C设备驱动是对I2C硬件体系结构中设备端的实现。设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。I2C设备驱动主要包含了数据结构i2c_driver和i2c_client,我们需要根据具体设备实现其中的成员函数。在Linux内核源代码中的drivers目录下的
Alps\kernel3.10\driver\i2c
i2c_dev.c文件,实现了I2C适配器设备文件的功能,应用程序通过“i2c-%d”文件名并使用文件操作接口open()、write()、read()、ioctl()和close()等来访问这个设备。应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器并控制I2C设备的工作方式。
设备驱动则是与挂在I2C总线上的具体的设备通讯的驱动。通过I2C总线驱动提供的函数,设备驱动可以忽略不同总线控制器的差异,不考虑其实现细节地与硬件设备通讯。
这部分在MTK 6516中是由具体的设备实现的。(比如camera)
struct i2c_client:
代表一个挂载到i2c总线上的i2c从设备,该设备所需要的数据结构,其中包括该i2c从设备所依附的i2c主设备struct i2c_adapter *adapter 该i2c从设备的驱动程序struct i2c_driver *driver 作为i2c从设备所通用的成员变量,比如addr, name等 该i2c从设备驱动所特有的数据,依附于dev->driver_data下
struct i2c_adapter:
代表主芯片所支持的一个i2c主设备。
struct i2c_algorithm *algo:
是该i2c主设备传输数据的一种算法,或者说是在i2c总线上完成主从设备间数据通信的一种能力。
在Linux驱动中I2C系统中主要包含以下几个成员:
I2C adapter 即I2C适配器I2C driver 某个I2C设备的设备驱动,可以以driver理解。I2C client 某个I2C设备的设备声明,可以以device理解。
I2C adapter
是CPU集成或外接的I2C适配器,用来控制各种I2C从设备,其驱动需要完成对适配器的完整描述,最主要的工作是需要完成i2c_algorithm结构体。这个结构体包含了此I2C控制器的数据传输具体实现,以及对外上报此设备所支持的功能类型。i2c_algorithm结构体如下:
struct i2c_algorithm { 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); u32 (*functionality) (struct i2c_adapter *);};
如果一个I2C适配器不支持I2C通道,那么就将master_xfer成员设为NULL。如果适配器支持SMBUS协议,那么需要去实现smbus_xfer,如果smbus_xfer指针被设为NULL,那么当使用SMBUS协议的时候将会通过I2C通道进行仿真。master_xfer指向的函数的返回值应该是已经成功处理的消息数,或者返回负数表示出错了。functionality指针很简单,告诉询问着这个I2C主控器都支持什么功能。
在内核的drivers/i2c/i2c-stub.c中实现了一个i2c adapter的例子,其中实现的是更为复杂的SMBUS。
SMBus 与 I2C的区别
通常情况下,I2C和SMBus是兼容的,但是还是有些微妙的区别的。
时钟速度对比:
在电气特性上他们也有所不同,SMBus要求的电压范围更低。
I2C driver
具体的I2C设备驱动,如相机、传感器、触摸屏、背光控制器常见硬件设备大多都有或都是通过I2C协议与主机进行数据传输、控制。结构体如下:
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, it will be removed in a * near future. */ 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 *); //从挂起状态恢复 /* Alert callback, for example for the SMBus alert protocol. * The format and meaning of the data value depends on the protocol. * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). */ void (*alert)(struct i2c_client *, unsigned int data); /* 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; //I2C设备的驱动模型 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;};#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver) //一般编写驱动过程中对象常是driver类型,可以通过to_i2c_driver找到其父类型i2c_driver
如同普通设备的驱动能够驱动多个设备一样,一个I2C driver也可以对应多个I2C client。
以重力传感器AXLL34X为例,其实现的I2C驱动为:
static const struct i2c_device_id adxl34x_id[] = { { "adxl34x", 0 }, //匹配i2c client名为adxl34x的设备 { } }; MODULE_DEVICE_TABLE(i2c, adxl34x_id); static struct i2c_driver adxl34x_driver = { .driver = { .name = "adxl34x", .owner = THIS_MODULE, .pm = &adxl34x_i2c_pm, //指定设备驱动的电源管理接口,包含suspend、resume }, .probe = adxl34x_i2c_probe, //组装设备匹配时候的匹配动作 .remove = adxl34x_i2c_remove, //组装设备移除接口 .id_table = adxl34x_id, //制定匹配设备列表 }; module_i2c_driver(adxl34x_driver);
这里要说明一下module_i2c_driver宏定义(i2c.h):
#define module_i2c_driver(__i2c_driver) \ module_driver(__i2c_driver, i2c_add_driver, \ i2c_del_driver)#define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver)
module_driver():
#define module_driver(__driver, __register, __unregister, ...) \static int __init __driver##_init(void) \{ \ return __register(&(__driver) , ##__VA_ARGS__); \} \module_init(__driver##_init); \static void __exit __driver##_exit(void) \{ \ __unregister(&(__driver) , ##__VA_ARGS__); \} \module_exit(__driver##_exit);
理解上述宏定义后,将module_i2c_driver(adxl34x_driver)展开就可以得到:
static int __int adxl34x_driver_init(void){ return i2c_register_driver(&adxl34x_driver);}module_init(adxl34x_driver_init);static void __exit adxl34x_driver_exit(void){ return i2c_del_driver(&adxl34x_driver);}module_exit(adxl34x_driver_exit);
这一句宏就解决了模块module安装卸载的复杂代码。这样驱动开发者在实现I2C驱动时只要将i2c_driver结构体填充进来就可以了,无需关心设备的注册与反注册过程。
I2C client
即I2C设备。I2C设备的注册一般在板级代码中,在解析实例前还是先熟悉几个定义:
struct i2c_client { unsigned short flags; //I2C_CLIENT_TEN表示设备使用10bit从地址,I2C_CLIENT_PEC表示设备使用SMBus检错 unsigned short addr; //设备从地址,7bit。这里说一下为什么是7位,因为最后以为0表示写,1表示读,通过对这个7bit地址移位处理即可。addr<<1 & 0x0即写,addr<<1 | 0x01即读。 char name[I2C_NAME_SIZE]; //从设备名称 struct i2c_adapter *adapter; //此从设备依附于哪个adapter上 struct i2c_driver *driver; // 此设备对应的I2C驱动指针 struct device dev; // 设备模型 int irq; // 设备使用的中断号 struct list_head detected; //用于链表操作};#define to_i2c_client(d) container_of(d, struct i2c_client, dev) //通常使用device设备模型进行操作,可以通过to_i2c_client找到对应client指针struct i2c_board_info { char type[I2C_NAME_SIZE]; //设备名,最长20个字符,最终安装到client的name上 unsigned short flags; //最终安装到client.flags unsigned short addr; //设备从地址slave address,最终安装到client.addr上 void *platform_data; //设备数据,最终存储到i2c_client.dev.platform_data上 struct dev_archdata *archdata; struct device_node *of_node; //OpenFirmware设备节点指针 struct acpi_dev_node acpi_node; int irq; //设备采用的中断号,最终存储到i2c_client.irq上};//可以看到,i2c_board_info基本是与i2c_client对应的。#define I2C_BOARD_INFO(dev_type, dev_addr) \ .type = dev_type, .addr = (dev_addr)//通过这个宏定义可以方便的定义I2C设备的名称和从地址(别忘了是7bit的)
下面还是以adxl34x为例:
static struct i2c_board_info i2c0_devices[] = { { I2C_BOARD_INFO("ak4648", 0x12), }, { I2C_BOARD_INFO("r2025sd", 0x32), }, { I2C_BOARD_INFO("ak8975", 0x0c), .irq = intcs_evt2irq(0x3380), /* IRQ28 */ }, { I2C_BOARD_INFO("adxl34x", 0x1d), .irq = intcs_evt2irq(0x3340), /* IRQ26 */ }, };...i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices));
这样ADXL34X的i2c设备就被注册到了系统中,当名字与i2c_driver中的id_table中的成员匹配时就能够出发probe匹配函数了。
转载:http://blog.csdn.net/airk000/article/details/21345457
- linux I2C设备驱动编写
- Linux I2C设备驱动编写
- Linux I2C设备驱动编写
- Linux I2C设备驱动编写
- Linux I2C设备驱动编写
- LINUX I2C设备驱动的编写方法
- Linux I2C设备驱动编写(一)
- Linux I2C设备驱动编写(二)
- Linux i2c设备驱动编写(一)
- Linux i2c设备驱动编写(二)
- Linux I2C设备驱动编写(一)
- Linux I2C设备驱动编写(二)
- Linux I2C设备驱动编写(一)
- Linux I2C设备驱动编写(二)
- Linux系统I2C设备驱动编写方法
- Linux I2C设备驱动编写(二)
- I2C设备驱动编写
- I2C设备驱动编写
- httpd反向代理
- java环境变量
- [BZOJ3238][Ahoi2013]差异
- 编写android HAL代码
- UVA216 ——dfs
- Linux I2C设备驱动编写
- Java导论(一)
- 总结_高效能人士的七个习惯
- eightQueen1.0
- 数据库设计(4)-高级ER模型构建
- 第十章 方差分析
- UIday0603:UIImageView的属性和用法 Tom猫举例
- 免费自学Cocos2d-x3.0final2014原创视频教程(56集)(适用于Cocos2d-x3.1 Cocos2d-x3.2版本全)
- Binary Tree Paths