i2c的platformlinux i2c设备驱动什么时候注册的

I2C设备与驱动的关联_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
I2C设备与驱动的关联
上传于||文档简介
&&I2C设备与驱动的关联
阅读已结束,如果下载本文需要使用1下载券
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,查找使用更方便
还剩14页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢下次自动登录
现在的位置:
& 综合 & 正文
linux I2C设备驱动流程
一、I2C设备驱动流程
1) i2c_register_board_info定义I2C器件信息(Name,Address,etc.)
static struct i2c_board_info __initdata pi2c_board_info[] = {
I2C_BOARD_INFO("max1586", 0x14),
.platform_data = &max1587a_info,
i2c_register_board_info(1, ARRAY_AND_SIZE(pi2c_board_info));
/* 1表示该I2C设备挂在I2C-1上,注册I2C
adapt时相应的id = 1 */
static struct i2c_gpio_platform_data i2c_bus_data = {
.sda_pin = VIPER_RTC_I2C_SDA_GPIO,
.scl_pin = VIPER_RTC_I2C_SCL_GPIO,
.timeout = HZ,
static struct platform_device i2c_bus_device = {
= "i2c-gpio",
/* /sys/class/i2c-adapt/i2c-1
.platform_data = &i2c_bus_data,
将I2C器件信息注册到I2C的器件列表中:
struct i2c_devinfo {
struct list_head
/* 双向链表
struct i2c_board_info
/* i2c器件信息 */
int __init
i2c_register_board_info(int busnum,
struct i2c_board_info const *info, unsigned len)
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--, info++) {
struct i2c_devinfo
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
if (!devinfo) {
pr_debug("i2c-core: can't register boardinfo!/n");
status = -ENOMEM;
devinfo-&busnum =
devinfo-&board_info = *
list_add_tail(&devinfo-&list,
&__i2c_board_list);
/* 加入__i2c_board_list
up_write(&__i2c_board_lock);
2) 注册I2C device
i2c_register_adapter -& i2c_scan_static_board_info -& i2c_new_device
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
struct i2c_client
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client-&adapter =
client-&dev.platform_data = info-&platform_
if (info-&archdata)
client-&dev.archdata = *info-&
client-&flags = info-&
client-&addr = info-&
/* 取得I2C器件地址 */
client-&irq = info-&
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_
/* Check for address business */
status = i2c_check_addr_busy(adap, client-&addr);
if (status)
client-&dev.parent = &client-&adapter-&
client-&dev.bus = &i2c_bus_
client-&dev.type = &i2c_client_
dev_set_name(&client-&dev, "%d-%04x", i2c_adapter_id(adap),
client-&addr);
status = device_register(&client-&dev);
/* 注册器件 */
if (status)
3) I2C读写
static inline int pm860x_read_device(struct i2c_client *i2c,
int reg, int bytes, void *dest)
data = (unsigned char)
ret = i2c_master_send(i2c, &data, 1);
/* 写地址 */
if (ret & 0)
ret = i2c_master_recv(i2c, dest, bytes);
/* 读数据 */
if (ret & 0)
static inline int pm860x_write_device(struct i2c_client *i2c,
int reg, int bytes, void *src)
unsigned char buf[bytes + 1];
buf[0] = (unsigned char)
memcpy(&buf[1], src, bytes);
ret = i2c_master_send(i2c, buf, bytes + 1);
/* 写地址&数据 */
if (ret & 0)
/* i2c-core.c 函数 */
int i2c_master_send(struct i2c_client *client, const char *buf, int count)
struct i2c_adapter *adap = client-&
struct i2c_
msg.addr = client-&
I2C器件地址 */
msg.flags = client-&flags & I2C_M_TEN;
msg.buf = (char *)
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count :
int i2c_master_recv(struct i2c_client *client, char *buf, int count)
struct i2c_adapter *adap = client-&
struct i2c_
msg.addr = client-&
msg.flags = client-&flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count :
二、I2C时序
START/STOP时序
I2C器件地址结构(bit7~Bit1)
FIXME:”1” Read, “0” Write
START + ( Device address +W ) + (Register Address) + (Data) + STOP
4) 多字节写
5) *读操作
START + (Device Address + w) + (Register Address) +RESTART + (Device Address + R) +(Data) +STOP
一、I2C设备驱动流程
1) i2c_register_board_info定义I2C器件信息(Name,Address,etc.)
static struct i2c_board_info __initdata pi2c_board_info[] = {
I2C_BOARD_INFO("max1586", 0x14),
.platform_data = &max1587a_info,
i2c_register_board_info(1, ARRAY_AND_SIZE(pi2c_board_info));
/* 1表示该I2C设备挂在I2C-1上,注册I2C
adapt时相应的id = 1 */
static struct i2c_gpio_platform_data i2c_bus_data = {
.sda_pin = VIPER_RTC_I2C_SDA_GPIO,
.scl_pin = VIPER_RTC_I2C_SCL_GPIO,
.timeout = HZ,
static struct platform_device i2c_bus_device = {
= "i2c-gpio",
/* /sys/class/i2c-adapt/i2c-1
.platform_data = &i2c_bus_data,
将I2C器件信息注册到I2C的器件列表中:
struct i2c_devinfo {
struct list_head
/* 双向链表
struct i2c_board_info
/* i2c器件信息 */
int __init
i2c_register_board_info(int busnum,
struct i2c_board_info const *info, unsigned len)
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--, info++) {
struct i2c_devinfo
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
if (!devinfo) {
pr_debug("i2c-core: can't register boardinfo!/n");
status = -ENOMEM;
devinfo-&busnum =
devinfo-&board_info = *
list_add_tail(&devinfo-&list,
&__i2c_board_list);
/* 加入__i2c_board_list
up_write(&__i2c_board_lock);
2) 注册I2C device
i2c_register_adapter -& i2c_scan_static_board_info -& i2c_new_device
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
struct i2c_client
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client-&adapter =
client-&dev.platform_data = info-&platform_
if (info-&archdata)
client-&dev.archdata = *info-&
client-&flags = info-&
client-&addr = info-&
/* 取得I2C器件地址 */
client-&irq = info-&
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_
/* Check for address business */
status = i2c_check_addr_busy(adap, client-&addr);
if (status)
client-&dev.parent = &client-&adapter-&
client-&dev.bus = &i2c_bus_
client-&dev.type = &i2c_client_
dev_set_name(&client-&dev, "%d-%04x", i2c_adapter_id(adap),
client-&addr);
status = device_register(&client-&dev);
/* 注册器件 */
if (status)
3) I2C读写
static inline int pm860x_read_device(struct i2c_client *i2c,
int reg, int bytes, void *dest)
data = (unsigned char)
ret = i2c_master_send(i2c, &data, 1);
/* 写地址 */
if (ret & 0)
ret = i2c_master_recv(i2c, dest, bytes);
/* 读数据 */
if (ret & 0)
static inline int pm860x_write_device(struct i2c_client *i2c,
int reg, int bytes, void *src)
unsigned char buf[bytes + 1];
buf[0] = (unsigned char)
memcpy(&buf[1], src, bytes);
ret = i2c_master_send(i2c, buf, bytes + 1);
/* 写地址&数据 */
if (ret & 0)
/* i2c-core.c 函数 */
int i2c_master_send(struct i2c_client *client, const char *buf, int count)
struct i2c_adapter *adap = client-&
struct i2c_
msg.addr = client-&
I2C器件地址 */
msg.flags = client-&flags & I2C_M_TEN;
msg.buf = (char *)
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count :
int i2c_master_recv(struct i2c_client *client, char *buf, int count)
struct i2c_adapter *adap = client-&
struct i2c_
msg.addr = client-&
msg.flags = client-&flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count :
二、I2C时序
START/STOP时序
I2C器件地址结构(bit7~Bit1)
FIXME:”1” Read, “0” Write
START + ( Device address +W ) + (Register Address) + (Data) + STOP
4) 多字节写
5) *读操作
START + (Device Address + w) + (Register Address) +RESTART + (Device Address + R) +(Data) +STOP
&&&&推荐文章:
【上篇】【下篇】platform驱动小结_ubuntu驱动开发吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0可签7级以上的吧50个
本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:18贴子:
platform驱动小结
Platform led驱动最简单的了解platform平台的例子,可以理解为3部分,由驱动层,系统核心层,设备驱动三部分组成:驱动层:硬件设备注册部分。系统核心层:无设备驱动层:设备端的实现,如led闪烁等实际上就只有之所以这里分成3部分,是为了与后面的设备驱动程序对应起来。使用步骤示例:(1)platform_device_register()注册平台led设备(2)platform_driver_register()注册平台led驱动。 Platform input驱动Linux系统提供了input子系统,按键、触摸屏、键盘、鼠标等输入都可以利用input接口函数来实现设备驱动。在linux主要由驱动层,系统核心层(Input Core)和事件处理层(Event Handler)三部份组成。驱动层:硬件设备注册部分,只是把输入设备注册到input子系统中,在驱动层的代码本身并不创建结点。对应文件如gpio_key.cInput core:向系统报告按键、触摸屏、键盘、鼠标等输入事件(event,通过input_event结构体描述),使得驱动层不需要关心文件操作接口。对应文件如Input.cEvent Handler:提供input设备接口。 对应文件如evdev.c,mousedev.c等。 一般来说,如果要使用input子系统,只需要更改驱动层部分就可以了。 Platform i2c驱动Linux系统中,i2c驱动由3部分组成,即i2c总线驱动、i2c core、i2c设备驱动。I2c总线驱动:对i2c硬件体系结构中适配器端的实现,适配器可由CPU控制,或集成在CPU内部。对应文件如:i2c-at91.cI2c core:提供了i2c总线驱动和设备驱动的注册、注销方法,i2c algorithm。与具体适配器无关的代码以及探测设备、检测设备地址的上层代码。对应文件如:i2c-core.cI2c设备驱动:i2c体系硬件结构中设备端的实现,设备一般挂在受CPU控制的i2c适配器上,通过i2c适配器与CPU交换数据。对应文件如:at24.c,i2c-dev.c等。 对于常见的开发板来说,主芯片已经带了i2c总线,i2c总线驱动基本上提供了,不用怎么动。即使不带i2c总线,基本上也会提供io模拟的i2c,也就是说i2c总线驱动部分一般情况下不需要自己写或者更改。I2c core部分就更不用动了,呵呵。因此,写一个i2c设备的驱动,只需要写i2c设备驱动(这里对应于上面说的i2c驱动的3部分之一)就可以了,貌似有点拗口,希望把意思表达清楚了。大多数i2c设备驱动,内核已经提供了。而且简单的应用还可以利用i2c-dev.c来实现。 Platform spi驱动
Linux系统中,spi驱动由3部分组成,即spi总线驱动、spi core、spi设备驱动。
Spi总线驱动:硬件spi驱动的实现,spi可为主芯片内部集成,也可以io口模拟。对应文件如:atmel_spi.c
Spi core:提供了spi总线驱动和设备驱动的注册、注销方法。
Spi 设备驱动:spi体系结构中,spi设备端的实现。 综合上述几个比较简单的驱动可以看出一个共性:这几个驱动基本都是由3部分组成:(1)
总线驱动:与所选用的主芯片相关联,一般都有提供。(2)
总线core:与具体的硬件无关,内核已经提供。(3)
总线设备驱动:所操作的具体设备。根据实际应用需要,使用或更改内核已经提供的驱动,或者自己重新写一个驱动。实际上,写一个设备驱动,我们所要做的工作基本上集中在第3部分,而这部分,内核也提供了大多数设备的驱动,即使没有提供,我们也可以根据已有的设备自己更改。
贴吧热议榜
使用签名档&&
保存至快速回贴温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
本文转自:从&Linux 2.6&起引入了一套新的驱动管理和注册机制&:Platform_device&和&Platform_driver&。Linux&中大部分的设备驱动,都可以使用这套机制&,&&设备用&Platform_device&表示,驱动用&Platform_driver&进行注册。&Linux platform_driver&机制和传统的&&&机制&(&通过&&函数进行注册&)&相比,一个十分明显的优势在于&platform&机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过&platform_device&提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性&(&这些标准接口是安全的&)&。&Platform&机制的本身使用并不复杂,由两部分组成:&platform_device&和&platfrom_driver&。通过&Platform&机制开发发底层驱动的大致流程为&:&&&定义&&注册platform_device,&定义&&&platform_add_driver&注册&&platform_driver&。&1、platform_device注册过程:首先要确认的就是设备的资源信息,例如设备的地址,中断号等。在&2.6&内核中&platform&设备用结构体&platform_device&来描述,该结构体定义在&kernel/include/linux/platform_device.h&中,struct platform_device {&&& const char&&& *&&& int&&& &&&&&& struct device&&&&&& u32&&& &&& num_&&& struct resource&&& *&&resource&;&&& struct platform_device_id&&& *id_};该结构一个重要的元素是&resource&,该元素存入了最为重要的设备资源信息,定义在&kernel/include/linux/ioport.h&中,struct resource {&& &resource_size_&& &resource_size_&& &const char *&& &&& &struct resource *parent, *sibling, *};&下面举&s3c6410&平台的&i2c&驱动作为例子来说明:static struct platform_device *smdk6410_devices&[] __initdata = {#ifdef CONFIG_SMDK6410_SD_CH0&& &&s3c_device_hsmmc0,#endif#ifdef CONFIG_SMDK6410_SD_CH1&& &&s3c_device_hsmmc1,#endif&& &&s3c_device_i2c0&,&& &&s3c_device_i2c1,&& &&s3c_device_fb,&& &&s3c_device_usb,&& &&s3c_device_usb_hsotg,&& &&smdk6410_lcd_powerdev,&& &&smdk6410_smsc911x,};把一个或几个设备资源放在一起,便于集中管理,其中IIC设备&platform_device如下:struct platform_device&&s3c_device_i2c0&&&= {&&& .name&&& &&& & = "s3c2410-i2c",#ifdef CONFIG_S3C_DEV_I2C1&&& .id&&& &&& & = 0,#else&&& .id&&& &&& & = -1,#endif&&& .num_resources&&& & =&(s3c_i2c_resource&),&&& .resource&&& & = s3c_i2c_resource,};具体resource如下:static struct resource&&s3c_i2c_resource&[] = {&&& [0] = {&&& &&& .start = S3C_PA_IIC,&&& &&& .end&& = S3C_PA_IIC + SZ_4K – 1,&&& &&& .flags = IORESOURCE_MEM,&&& },&&& [1] = {&&& &&& .start = IRQ_IIC,&&& &&& .end&& = IRQ_IIC,&&& &&& .flags = IORESOURCE_IRQ,&&& },};&这里定义了两组&resource&,它描述了一个&I2C&设备的资源,第&1&组描述了这个&I2C&设备所占用的总线地址范围,&IORESOURCE_MEM&表示第&1&组描述的是内存类型的资源信息,第&2&组描述了这个&I2C&设备的中断号,&IORESOURCE_IRQ&表示第&2&组描述的是中断资源信息。设备驱动会根据&flags&来获取相应的资源信息。定义好了&platform_device&结构体后就可以调用函数&向系统中添加该设备了,之后可以调用&platform_driver_register()&进行设备注册。&s3c6410-i2c的platform_device是在系统启动时,在mach-smdk6410.c里的smdk6410_machine_init()函数里进行注册的,这个函数申明为arch_initcall的函数调用,arch_initcall的优先级高于module_init。所以会在Platform驱动注册之前调用。(详细参考imach-smdk6410.c)static void __init smdk6410_machine_init(void){&&& s3c_i2c0_set_platdata(NULL);&&& s3c_i2c1_set_platdata(NULL);&&& s3c_fb_set_platdata(&smdk6410_lcd_pdata);&&& gpio_request(S3C64XX_GPN(5), "LCD power");&&& gpio_request(S3C64XX_GPF(13), "LCD power");&&& gpio_request(S3C64XX_GPF(15), "LCD power");&&& i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));&&& i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));&&& platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));&&&& //添加多设备&}int&(struct platform_device **devs, int num){&&& int i, ret = 0;&&& for (i = 0; i & i++) {&&& &&&&&ret = platform_device_register(devs[i]);&&&& &&& if (ret) {&&& &&& &&& while (–i &= 0)&&& &&& &&& &&& platform_device_unregister(devs[i]);&&& &&& &&&&&& &&& }&&& }&&&}int platform_device_register(struct platform_device *pdev){&&& device_initialize(&pdev-&dev);&&&&&return platform_device_add(pdev);&}int platform_device_add(struct platform_device *pdev){&&& int i, ret = 0;&&& if (!pdev)&&& &&& return -EINVAL;&&& if (!pdev-&dev.parent)&&& &&& pdev-&dev.parent = &platform_&&& pdev-&dev.bus = &platform_bus_&&& if (pdev-&id != -1)&&& &&& dev_set_name(&pdev-&dev, "%s.%d", pdev-&name,& pdev-&id);//如果有id 表示有多个同类设备用&pdev-&name和&pdev-&id标识该设备&&&& else&&& &&& dev_set_name(&pdev-&dev, "%s", pdev-&name);&//否则,只用&pdev-&name标识该设备&&&& for (i = 0; i & pdev-&num_ i++) {&&& &&& struct resource *p, *r = &pdev-&resource[i];&&& &&& if (r-&name == NULL)&&& &&& &&& r-&name = dev_name(&pdev-&dev);&&& &&& p = r-&&&& &&& if (!p) {&&& &&& &&& if (resource_type(r) == IORESOURCE_MEM)&&& &&& &&& &&&&&p = &iomem_&//&&&作为&IOMEM&资源分配&&&&&& &&& &&& else if (resource_type(r) == IORESOURCE_IO)&&& &&& &&& &&&&&p = &ioport_&//&&&作为&IO&PORT资源分配&&&&&& &&& }&&& &&&&&if (p && insert_resource(p, r)) {&//&将新的&resource&插入内核&resource tree&&&& &&& &&& printk(KERN_ERR&&& &&& &&& &&&&&& "%s: failed to claim resource %d/n",&&& &&& &&& &&&&&& dev_name(&pdev-&dev), i);&&& &&& &&& ret = -EBUSY;&&& &&& &&&&&& &&& }&&& }&&& pr_debug("Registering platform device '%s'. Parent at %s/n",&&& &&& &dev_name(&pdev-&dev), dev_name(pdev-&dev.parent));&&&&&ret = device_add(&pdev-&dev);//添加设备到设备树&&&& if (ret == 0)&&& &&&&failed:&&& while (–i &= 0) {&&& &&& struct resource *r = &pdev-&resource[i];&&& &&& unsigned long type = resource_type(r);&&& &&& if (type == IORESOURCE_MEM || type == IORESOURCE_IO)&&& &&& &&& release_resource(r);&&& }&&&}
阅读(1591)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'如何使用platform_device和platform_driver注册驱动',
blogAbstract:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}arm学习(2)
linux(3)
OMAP3630 I2C device驱动
&&& 在Linux内核中,I2C device驱动位于drivers/i2c/chips目录下,可以看到该目录下有很多相关的device驱动,这里以xxxx项目的mma7455为例介绍device驱动的注册过程,对应的device驱动程序为mma7455.c。
&&& 既然有device驱动,那么必定有相应的device,I2C的device是什么呢?其实就是我们在1.3节中提到的i2c_client,所以在device驱动注册之前先来了解下i2c_client的注册过程。
4.1 Mma7455 device注册
&&& Mma7455 device即i2c_client的创建以及注册分为两步。
4.1.1 将mma7455设备信息加入到设备链表
&&& 在板级初始化时将I2C device的名称,地址和相关的信息加入到链表__i2c_board_list中,该链表记录了具体开发板上的I2C设备信息。
&&& 在board-xxxx.c中,定义了mma7455的设备信息定义如下:
static struct i2c_board_info __initdata xxxx_i2c_bus3_info[] = {&&&&
#ifdef CONFIG_SENSORS_MMA7455&&&&
&&&&&&& {&&&&
&&&&&&& I2C_BOARD_INFO(&mma7455&, 0x1D),&&&&
&&&&&&& .platform_data = &xxxx_mma7455_platform_data,&&&&
&&&&&&& },&&&&
#endif&&&&
&&& Mma7455加入到设备链表__i2c_board_list的流程图如下图:
图4.1 mma7455加入到I2C设备链表的过程
&&& i2c_register_board_info()函数的定义如下:
int __init i2c_register_board_info(int busnum,&&&&
&&& struct i2c_board_info const *info, unsigned len)&&&&
&&& ……&&&&
&&& for (status = 0; len--, info++) {&&&&
&&&&&&& struct i2c_devinfo& *&&&&
&&&&&&& devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);&&&&
&&&&&&& ……&&&&
&&&&&&& devinfo-&busnum =&&&&
&&&&&&& devinfo-&board_info = *&&&&
&&&&&&& list_add_tail(&devinfo-&list, &__i2c_board_list);&&&&
&&& ……&&&&
4.1.2 创建并注册i2c_client
&&& i2c_client的创建和注册在I2C adapter驱动注册过程中完成,I2C adapter驱动的注册可以参考3.2.2节,i2c_add_numbered_adapter()函数在注册I2C adapter驱动的同时会扫描4.1.1中提到的I2C设备链表__i2c_board_list,如果该总线上有对应的I2C设备,则创建相应的i2c_client,并将其注册到I2C core中。流程图如下所示:
图4.2创建并注册i2c_client
&&& 相应的代码位于i2c-core.c如下:
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)&&&&
&&& ……&&&&
&&& list_for_each_entry(devinfo, &__i2c_board_list, list) {&&&&
&&&&&&& if (devinfo-&busnum == adapter-&nr&&&&
&&&&&&&&&&&&&&& && !i2c_new_device(adapter,&&&&
&&&&&&&&&&&&&&&&&&&&&&& &devinfo-&board_info))&&&&
&&&&&&&&&&& ……&&&&
&&& ……&&&&
&&& 在i2c_scan_static_board_info()函数中遍历I2C设备链表__i2c_board_list,设备的总线号和adapter的总线号相等,则使用函数i2c_new_device()创建该设备。
struct i2c_client *&&&&
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)&&&&
&&& ……&&&&
&&& client = kzalloc(sizeof *client, GFP_KERNEL);&&&&
&&& if (!client)&&&&
&&&&&&& return NULL;&&&&
&&& client-&adapter =&&&&
&&& client-&dev.platform_data = info-&platform_&&&&
&&& if (info-&archdata)&&&&
&&&&&&& client-&dev.archdata = *info-&&&&&
&&& client-&flags = info-&&&&&
&&& client-&addr = info-&&&&&
&&& client-&irq = info-&&&&&
&&& strlcpy(client-&name, info-&type, sizeof(client-&name));&&&&
&&& ……&&&&
&&& status = i2c_attach_client(client);&&&&
&&& ……&&&&
&&&& 在函数i2c_new_device()中创建一个i2c_client,初始化该结构体的adapter,addr,name等变量,这里的client-&name被初始化为info-&type,在4.1.1中,info-&type初始化为“mma7455”, client-&name后面会用于I2C device和I2C driver匹配时使用,最后调用i2c_attach_client()将该client注册到I2C core。
int i2c_attach_client(struct i2c_client *client)&&&&
&&& struct i2c_adapter *adapter = client-&&&&&
&&& ……&&&&
&&& client-&dev.parent = &client-&adapter-&&&&&
&&& client-&dev.bus = &i2c_bus_&&&&
&&& ……&&&&
&&& res = device_register(&client-&dev);&&&&
&&& ……&&&&
&&& 函数i2c_attach_client()进一步初始化i2c_client结构体,将该设备的总线初始化为i2c_bus_type,说明该设备被放在I2C总线上,用于后面跟I2C driver匹配时使用,最后使用device_register(&client-&dev)注册该i2c_client设备。
4.2 Mma7455 device驱动注册
&&& 在mma7455.c中,定义了mma7455的device驱动,代码如下:
static struct i2c_driver mma7455_driver = {&&&&
&&& .driver&&&& = {&&&&
&&&&&&&&&&&&&&& .name = &mma7455&,&&&&
&&&&&&&&&&& },&&&&
&&& .class&&&&& = I2C_CLASS_HWMON,&&&&
&&& .probe&&&&& = mma7455_probe,&&&&
&&& .remove&&&& = mma7455_remove,&&&&
&&& .id_table&& = mma7455_id,&&&&
&&& ……&&&&
&&& 注册的简要示意图如下:
图4.3 device驱动的注册
&&& 相应的代码位于mma7455.c和i2c-core.c。
static int __init init_mma7455(void)&&&&
&&& ……&&&&
&&& res = i2c_add_driver(&mma7455_driver);&&&&
&&& ……&&&&
&&& return (res);&&&&
&&& 在模块加载的时候首先调用init_mma7455(),然后init_mma7455()调用函数i2c_add_driver()注册mma7455_driver结构体。
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)&&&&
&&& …….&&&&
&&& /* add the driver to the list of i2c drivers in the driver core */&&&
&&& driver-&driver.owner =&&&&
&&& driver-&driver.bus = &i2c_bus_&&&&
&&& ……&&&&
&&& res = driver_register(&driver-&driver);&&&&
&&& if (res)&&&&
&&&&&&&&&&&
&&& ……&&&&
&&& 函数i2c_register_driver()初始化该驱动的总线为i2c_bus_type,然后使用函数driver_register(&driver-&driver)注册该驱动,因此内核会在I2C总线上遍历所有I2C设备,由于该mma7455 device驱动的匹配因子name变量为“mma7455”,因此正好和在4.1.2里创建的name也为“mma7455”的i2c client匹配。因此总线的probe函数将会被调用,I2C总线的probe函数为i2c_device_probe(),具体代码如下:
static int i2c_device_probe(struct device *dev)&&&&
&&& struct i2c_client&& *client = to_i2c_client(dev);&&&&
&&& struct i2c_driver&& *driver = to_i2c_driver(dev-&driver);&&&&
&&& if (!driver-&probe || !driver-&id_table)&&&&
&&&&&&& return -ENODEV;&&&&
&&& client-&driver =&&&&
&&& …….&&&&
&&& status = driver-&probe(client, i2c_match_id(driver-&id_table, client));&&&&
&&& ……&&&&
&&& 在i2c_device_probe()函数中,语句client-&driver = driver将I2C device和I2C driver绑定,然后直接调用具体设备的probe函数,这里即mma7455的probe函数mma7455_probe()。
&&& 在mma7455_probe()函数会完成一些具体I2C设备相关的初始化等操作,这边就不再详述。
5 用户空间的支持
&&& 图1.1Linux I2C的架构图中的i2c-dev部份是一个通用的I2C设备的驱动程序,通过一个带有操作集file_operations的标准字符设备驱动为用户空间提供了访问接口,使用户空间可以通过I2C core,进而访问I2C adapter。
5.1 I2c-dev的注册
&&& 该部分的源代码位于drivers/i2c/i2c-dev.c,首先定义了I2C device驱动i2cdev_driver:
static struct i2c_driver i2cdev_driver = {&&&&
&&& .driver = {&&&&
&&&&&&& .name&& = &dev_driver&,&&&&
&&& },&&&&
&&& .attach_adapter = i2cdev_attach_adapter,&&&&
&&& .detach_adapter = i2cdev_detach_adapter,&&&&
&&& i2cdev_driver注册代码如下:
static int __init i2c_dev_init(void)&&&&
&&& ……&&&&
&&& res = register_chrdev(I2C_MAJOR, &i2c&, &i2cdev_fops);&&&&
&&& ……&&&&
&&& i2c_dev_class = class_create(THIS_MODULE, &i2c-dev&);&&&&
&&& ……&&&&
&&& res = i2c_add_driver(&i2cdev_driver);&&&&
&&& ……&&&&
&&& 首先注册了一个主设备号为I2C_MAJOR,操作集为i2cdev_fops,名字为“i2c”的字符设备。在文件drivers/i2c/i2c-dev.h中,I2C_MAJOR被定义为89。在i2c-dev.c中i2cdev_fops的定义如下:
static const struct file_operations i2cdev_fops = {&&&&
&&& .owner&&&&& = THIS_MODULE,&&&&
&&& .llseek&&&& = no_llseek,&&&&
&&& .read&&&&&& = i2cdev_read,&&&&
&&& .write&&&&& = i2cdev_write,&&&&
&&& .unlocked_ioctl = i2cdev_ioctl,&&&&
&&& .open&&&&&& = i2cdev_open,&&&&
&&& .release&&& = i2cdev_release,&&&&
&&& 该操作集是用户空间访问该字符设备的接口。
&& 然后调用函数i2c_add_driver(&i2cdev_driver)将i2cdev_driver驱动注册到i2c core中,i2cdev_driver驱动注册的流程图如下:
图5.1 i2cdev_driver注册过程
&注册i2c_driver时,会将驱动和adapter绑定起来,然后将调用i2c_driver 的attach_adapter 方法,即i2cdev_attach_adapter()函数,建立dev设备节点,每个adapter都会对应一个dev设备节点,并维护了一个i2c_dev链表保存设备节点和adapter的关系。
&&& i2cdev_attach_adapter()函数的代码如下:&
static int i2cdev_attach_adapter(struct i2c_adapter *adap)&&&&
&&& ……&&&&
&&& /* register this i2c device with the driver core */&&&
&&& i2c_dev-&dev = device_create(i2c_dev_class, &adap-&dev,&&&&
&&&&&&&&&&&&&&&&&&&& MKDEV(I2C_MAJOR, adap-&nr), NULL,&&&&
&&&&&&&&&&&&&&&&&&&& &i2c-%d&, adap-&nr);&&&&
&&& ……&&&&
&&& res = device_create_file(i2c_dev-&dev, &dev_attr_name);&&&&
&&& ……&&&&
&&& 以I2C_MAJOR和adap-&nr为主从设备号创建并注册设备节点,如果系统有udev或者是hotplug,那么就会在/dev下自动创建相关的设备节点了。
5.2 I2c-dev的打开
&&& I2c-dev的open函数如下:
static int i2cdev_open(struct inode *inode, struct file *file)&&&&
&&& ……&&&&
&&& i2c_dev = i2c_dev_get_by_minor(minor);&&&&
&&& if (!i2c_dev) {&&&&
&&&&&&& ret = -ENODEV;&&&&
&&&&&&&&&&&
&&& adap = i2c_get_adapter(i2c_dev-&adap-&nr);&&&&
&&& ……&&&&
&&& client = kzalloc(sizeof(*client), GFP_KERNEL);&&&&
&&& if (!client) {&&&&
&&&&&&& i2c_put_adapter(adap);&&&&
&&&&&&& ret = -ENOMEM;&&&&
&&&&&&&&&&&
&&& snprintf(client-&name, I2C_NAME_SIZE, &i2c-dev %d&, adap-&nr);&&&&
&&& client-&driver = &i2cdev_&&&&
&&& client-&adapter =&&&&
&&& file-&private_data =&&&&
&&& ……&&&&
&&& Open操作是用户空间程序和内核驱动交换的第一步,最终返回给用户空间的就是struct file结构体。对于I2C 驱动来说,用户空间所获得的就是client这个关键信息,在其中可以找到所有有关的信息如client所在的adapter及i2c_driver。
&&& 用open函数将i2c-dev设备打开以后,就可以通过ioctl函数的各种命令来设定要访问从设备的地址,I2C设备读写等操作,也可以通过 read和write函数完成对I2C设备的读写。
&&& 对I2C设备的具体操作在这里不再具体阐述,可以参看i2c-dev.c源代码。
6 I2C数据收发的框架
&&& I2C架构的读写支持两种协议类型,I2C协议与SMBUS协议。
&&& I2C协议和SMBUS协议不完成等同,SMBUS是I2C的子集,SMBUS由I2C衍生而来。SMBUS总线上传输的数据一定是I2C的格式的,但是SMBUS上传输的数据不一定能满足具体某个I2C从设备的通信要求。
6.1 SMBUS协议的数据收发
&&& 如果控制器不支持SMBUS协议,框架层可以用i2c_transfer模拟SMBUS协议的实现,系统默认的I2C传输函数一般都是基于I2C模拟的SMBUS方法传输的,如i2c_smbus_write_byte_data(),i2c_smbus_read_byte_data()等。
&&& 在设备mma7455的驱动程序中,使用了SMBUS协议,而OMAP3630控制器使用的是I2C协议,因此在mma7455的驱动程序中就用到了基于I2C模拟的SMBUS方法。
&&& 下面以函数i2c_smbus_write_byte_data()为例来说明SMBUS协议下的数据发送的过程。
&&& 在i2c-core.c中,函数i2c_smbus_write_byte_data()的定义如下:&
&* i2c_smbus_write_byte_data - SMBus &write byte& protocol&&&
&* @client: Handle to slave device&&&
&* @command: Byte interpreted by slave&&&
&* @value: Byte being written&&&
&* This executes the SMBus &write byte& protocol, returning negative errno&&&
&* else zero on success.&&&
s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value)&&&&
&&& union i2c_smbus_&&&&
&&& data.byte =&&&&
&&& return i2c_smbus_xfer(client-&adapter,client-&addr,client-&flags,&&&&
&&&&&&&&&&&&&&&&&&&&&&&&& I2C_SMBUS_WRITE,command,&&&&
&&&&&&&&&&&&&&&&&&&&&&&&& I2C_SMBUS_BYTE_DATA,&data);&&&&
&&& 函数i2c_smbus_write_byte_data()的调用流程图如下:
图6.1 SMBUS协议下的数据发送过程
&&& 从图6.1可以看到,走哪条分支取决于I2C控制器的i2c_algorithm算法,当定义了方法smbus_xfer,则直接调用该方法,如果没有则通过先调用i2c_smbus_xfer_emulated(),进而通过i2c_transfer()最终调用I2C协议下的master_xfer方法,所以我们说SMBUS总线上传输的数据一定是I2C的格式的。
6.2 I2C协议的数据收发
&&& I2C协议下的数据收发函数就是常用的I2C传输函数:i2c_master_send()和i2c_master_recv()。
&&& 下面以函数i2c_master_send ()为例来说明I2C协议下的数据发送的过程。
&&& 在i2c-core.c中,函数i2c_master_send()定义如下:
&* i2c_master_send - issue a single I2C message in master transmit mode&&&
&* @client: Handle to slave device&&&
&* @buf: Data that will be written to the slave&&&
&* @count: How many bytes to write&&&
&* Returns negative errno, or else the number of bytes written.&&&
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)&&&&
&&& struct i2c_adapter *adap=client-&&&&&
&&& struct i2c_&&&&
&&& msg.addr = client-&&&&&
&&& msg.flags = client-&flags & I2C_M_TEN;&&&&
&&& msg.len =&&&&
&&& msg.buf = (char *)&&&&
&&& ret = i2c_transfer(adap, &msg, 1);&&&&
&&& /* If everything went ok (i.e. 1 msg transmitted), return #bytes&&&
&&&&&& transmitted, else error code. */&&&
&&& return (ret == 1) ? count :&&&&
&&& 从源代码中可以看出,函数i2c_master_send()先构造i2c_msg结构体,然后直接调用函数i2c_transfer。简单的示意图如下:
图6-2 I2C协议下的数据发送过程
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2981次
排名:千里之外
转载:14篇
(1)(2)(1)(1)(1)(8)

我要回帖

更多关于 i2c设备驱动 的文章

 

随机推荐