从总线模型看IIC架构
2013-09-29 21:03
323 查看
1、IIC总线:
struct bus_type i2c_bus_type = {
.name= "i2c",
.match= i2c_device_match,
.probe
= i2c_device_probe,
.remove
= i2c_device_remove,
.shutdown
= i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
调用位置iic-core.c:
i2c_init(void)
retval = bus_register(&i2c_bus_type);
IIC设备模型:
i2c_new_device(i2c_client结构体)
i2c_register_adapter(i2c_adapter结构体)
IIC驱动模型:
i2c_register_driver(i2c_driver结构体)
前两者是设备模型,后一个是驱动模型。
2、i2c_register_adapter是适配器,对应我们开发板的IIC控制器:
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
而i2c_new_device是我们的IIC设备了,如IIC芯片:
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
最后i2c_register_driver是我们的IIC驱动,一般由i2c_add_driver进行包装,使用的结构体是i2c_driver,这个主要用来实现IIC协议。
3、现在我们来看框架:
i2c_register_driver驱动注册过程:
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = __devexit_p(at24_remove),
.id_table = at24_ids,
};
然后通过i2c_add_driver(&at24_driver)注册:
i2c_register_driver
res = driver_register(&driver->driver); //挂到IIC总线驱动链表,从IIC设备链表进行匹配,注意这里只针对IIC设备,对IIC适配器没有用,这点看IIC总线匹配函数就知道。匹配成功的话,调用驱动probe函数,由于当前没有IIC设备,所以不会调用。
2、IIC设备注册过程:
首先是适配器,因为适配器是IIC控制器,是核心所在。注意适配器有编号的adapter->nr,IIC设备是根据编号来挂到适配器的。
这部分是通过平台总线模型来实现的,所以这部分是驱动工程师负责的地方,具体如下:
平台设备:
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C_PA_IIC1,
.end = S3C_PA_IIC1 + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC1,
.end = IRQ_IIC1,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device s3c_device_i2c1 = {
.name = "s3c-i2c",
.id = 1,
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource
= s3c_i2c_resource,
};
而平台驱动:
static struct platform_driver s3c24xx_i2c_driver = {
.probe
= s3c24xx_i2c_probe,
.remove
= s3c24xx_i2c_remove,
.id_table
= s3c24xx_driver_ids,
.driver
= {
.owner
= THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
},
};
这样以匹配就调用s3c24xx_i2c_probe函数:
s3c24xx_i2c_probe函数任务:
1、设置s3c24xx_i2c结构体:
i2c->adap.algo = &s3c24xx_i2c_algorithm; //底层操作函数,主要包括传输函数等。
获取硬件信息填充结构体,注册中断
platform_set_drvdata(pdev, i2c);
2、ret = i2c_add_numbered_adapter(&i2c->adap); //通过i2c_register_adapter注册适配器,
注意i2c_add_numbered_adapter里的i2c_scan_static_board_info函数,这个函数回去__i2c_board_list链表找是否有IIC设备使用了本适配器,如果有要挂到本适配器,而该链表是通过i2c_register_board_info函数来实现的!所以对于IIC设备的注册有多种方式,一种直接i2c_new_device,一种就是这种情况,通过i2c_register_board_info函数实现,不过还有其他方式,这里就略去。再者i2c_add_numbered_adapter会调用device_register进而和IIC总线驱动匹配,不过不会匹配成功的,原因如下:
i2c_device_match
i2c_verify_client(dev);
(dev->type == &i2c_client_type)? to_i2c_client(dev): NULL; //适配器dev->type =i2c_adapter_type,返回NULL,不进行匹配!!
接着就是IIC设备注册,针对于IIC芯片之类的:
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
1、设置struct i2c_client*client;结构体
client->adapter = adap; //适配器
client->addr = info->addr;//地址
status = i2c_check_client_addr_validity(client); //检查地址是否有效,手段:发一个开始信号看看对方是否回应
strlcpy(client->name, info->type, sizeof(client->name)); //设备名字
status = device_register(&client->dev); 这个就重要啦,又来匹配了,并且IIC驱动前面已经有了,所以匹配成功,调用驱动的probe函数!
2、要先填充i2c_board_info结构体,然后实现第1的赋值。
现在看看驱动的probe函数:
at24_probe:
1、struct at24_data *at24设置
at24->bin.read = at24_bin_read; //最终将会调用底层的读函数
at24->bin.write = at24_bin_write;
err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin); //通过创建文件实现读写,这个例子比较特殊,我们可以实现为file_operation来读写(创建设备文件)。
i2c_set_clientdata(client, at24); //at24放到clien结构体里的私有数据。
到这里就OK了。
总结一下:
IIC适配器注册:
适配器对应于我们的IIC控制器,通过平台总线实现的,主要就是硬件相关的操作,尤其是底层的读写操作之类的,这些操作供给IIC驱动层的操作函数调用。
IIC的设备注册:
strlcpy(info.type, "wm8990", I2C_NAME_SIZE); //设置i2c_board_info
adapter = i2c_get_adapter(setup->i2c_bus); //这点说明适配器必须先注册,设备是挂在适配器上
client = i2c_new_device(adapter, &info); //注册设备,挂到适配器,匹配IIC总线的驱动,如果存在调用驱动probe
i2c_put_adapter(adapter)
IIC驱动注册:
static int __init at24_init(void)
{
io_limit = rounddown_pow_of_two(io_limit);
return i2c_add_driver(&at24_driver); //i2c_driver结构体
}
实现了协议层的操作:
static ssize_t at24_read(struct at24_data *at24,
char *buf, loff_t off, size_t count)
{
}
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
size_t count)
{
}
这些操作基底要靠适配器的底层来实现传输
最后一点:设备节点也是交给驱动层来实现
工作流程:
1、IIC驱动和IIC设备匹配,获取设备地址和名字等;
2、操作设备节点从IIC驱动函数集开始,这些最终要依靠适配器的底层操作来支撑完成!!
IIC设备层:地址和名字等
IIC驱动层:IIC协议
IIC适配器层:底层实现
struct bus_type i2c_bus_type = {
.name= "i2c",
.match= i2c_device_match,
.probe
= i2c_device_probe,
.remove
= i2c_device_remove,
.shutdown
= i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
调用位置iic-core.c:
i2c_init(void)
retval = bus_register(&i2c_bus_type);
IIC设备模型:
i2c_new_device(i2c_client结构体)
i2c_register_adapter(i2c_adapter结构体)
IIC驱动模型:
i2c_register_driver(i2c_driver结构体)
前两者是设备模型,后一个是驱动模型。
2、i2c_register_adapter是适配器,对应我们开发板的IIC控制器:
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
而i2c_new_device是我们的IIC设备了,如IIC芯片:
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
最后i2c_register_driver是我们的IIC驱动,一般由i2c_add_driver进行包装,使用的结构体是i2c_driver,这个主要用来实现IIC协议。
3、现在我们来看框架:
i2c_register_driver驱动注册过程:
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = __devexit_p(at24_remove),
.id_table = at24_ids,
};
然后通过i2c_add_driver(&at24_driver)注册:
i2c_register_driver
res = driver_register(&driver->driver); //挂到IIC总线驱动链表,从IIC设备链表进行匹配,注意这里只针对IIC设备,对IIC适配器没有用,这点看IIC总线匹配函数就知道。匹配成功的话,调用驱动probe函数,由于当前没有IIC设备,所以不会调用。
2、IIC设备注册过程:
首先是适配器,因为适配器是IIC控制器,是核心所在。注意适配器有编号的adapter->nr,IIC设备是根据编号来挂到适配器的。
这部分是通过平台总线模型来实现的,所以这部分是驱动工程师负责的地方,具体如下:
平台设备:
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C_PA_IIC1,
.end = S3C_PA_IIC1 + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC1,
.end = IRQ_IIC1,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device s3c_device_i2c1 = {
.name = "s3c-i2c",
.id = 1,
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource
= s3c_i2c_resource,
};
而平台驱动:
static struct platform_driver s3c24xx_i2c_driver = {
.probe
= s3c24xx_i2c_probe,
.remove
= s3c24xx_i2c_remove,
.id_table
= s3c24xx_driver_ids,
.driver
= {
.owner
= THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
},
};
这样以匹配就调用s3c24xx_i2c_probe函数:
s3c24xx_i2c_probe函数任务:
1、设置s3c24xx_i2c结构体:
i2c->adap.algo = &s3c24xx_i2c_algorithm; //底层操作函数,主要包括传输函数等。
获取硬件信息填充结构体,注册中断
platform_set_drvdata(pdev, i2c);
2、ret = i2c_add_numbered_adapter(&i2c->adap); //通过i2c_register_adapter注册适配器,
注意i2c_add_numbered_adapter里的i2c_scan_static_board_info函数,这个函数回去__i2c_board_list链表找是否有IIC设备使用了本适配器,如果有要挂到本适配器,而该链表是通过i2c_register_board_info函数来实现的!所以对于IIC设备的注册有多种方式,一种直接i2c_new_device,一种就是这种情况,通过i2c_register_board_info函数实现,不过还有其他方式,这里就略去。再者i2c_add_numbered_adapter会调用device_register进而和IIC总线驱动匹配,不过不会匹配成功的,原因如下:
i2c_device_match
i2c_verify_client(dev);
(dev->type == &i2c_client_type)? to_i2c_client(dev): NULL; //适配器dev->type =i2c_adapter_type,返回NULL,不进行匹配!!
接着就是IIC设备注册,针对于IIC芯片之类的:
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
1、设置struct i2c_client*client;结构体
client->adapter = adap; //适配器
client->addr = info->addr;//地址
status = i2c_check_client_addr_validity(client); //检查地址是否有效,手段:发一个开始信号看看对方是否回应
strlcpy(client->name, info->type, sizeof(client->name)); //设备名字
status = device_register(&client->dev); 这个就重要啦,又来匹配了,并且IIC驱动前面已经有了,所以匹配成功,调用驱动的probe函数!
2、要先填充i2c_board_info结构体,然后实现第1的赋值。
现在看看驱动的probe函数:
at24_probe:
1、struct at24_data *at24设置
at24->bin.read = at24_bin_read; //最终将会调用底层的读函数
at24->bin.write = at24_bin_write;
err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin); //通过创建文件实现读写,这个例子比较特殊,我们可以实现为file_operation来读写(创建设备文件)。
i2c_set_clientdata(client, at24); //at24放到clien结构体里的私有数据。
到这里就OK了。
总结一下:
IIC适配器注册:
适配器对应于我们的IIC控制器,通过平台总线实现的,主要就是硬件相关的操作,尤其是底层的读写操作之类的,这些操作供给IIC驱动层的操作函数调用。
IIC的设备注册:
strlcpy(info.type, "wm8990", I2C_NAME_SIZE); //设置i2c_board_info
adapter = i2c_get_adapter(setup->i2c_bus); //这点说明适配器必须先注册,设备是挂在适配器上
client = i2c_new_device(adapter, &info); //注册设备,挂到适配器,匹配IIC总线的驱动,如果存在调用驱动probe
i2c_put_adapter(adapter)
IIC驱动注册:
static int __init at24_init(void)
{
io_limit = rounddown_pow_of_two(io_limit);
return i2c_add_driver(&at24_driver); //i2c_driver结构体
}
实现了协议层的操作:
static ssize_t at24_read(struct at24_data *at24,
char *buf, loff_t off, size_t count)
{
}
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
size_t count)
{
}
这些操作基底要靠适配器的底层来实现传输
最后一点:设备节点也是交给驱动层来实现
工作流程:
1、IIC驱动和IIC设备匹配,获取设备地址和名字等;
2、操作设备节点从IIC驱动函数集开始,这些最终要依靠适配器的底层操作来支撑完成!!
IIC设备层:地址和名字等
IIC驱动层:IIC协议
IIC适配器层:底层实现
相关文章推荐
- 从总线模型看USB架构
- <2012 12 17> linux驱动中的platform总线架构(含具体IIC设备驱动)
- Exynos4412 IIC总线驱动开发(一)—— IIC 基础概念及驱动架构分析
- 从总线模型谈SD/MMC架构
- IIC总线驱动架构
- linux驱动中的platform总线架构(含具体IIC设备驱动)
- Exynos4412 IIC总线驱动开发(一)—— IIC 基础概念及驱动架构分析
- 4、struts2异常处理及全局异常与结果剖析、struts2模型驱动及分层体系架构
- jvm内存架构模型
- HDMI EDID, PC EDID, DDC, IIC总线四者之间的关系
- 途牛原创|基于EAV模型的运营系统架构实践
- 微软业务生产力平台基础架构优化(BPIO)中企业项目管理(EPM)模型概述
- Linux设备驱动程序学习-Linux设备模型(总线、设备、驱动程序和类)
- 【Spark亚太研究院系列丛书】Spark实战高手之路-第3章Spark架构设计与编程模型第1节:为什么Spark是大数据必然的现在和未来?(1)
- 从经典架构项目中透析微服务架构的核心概念和充血模型
- Linux平台总线驱动设备模型
- PowerDesigner(三)-企业架构模型
- 有骨有肉的模型驱动架构-iModel模型驱动开发平台
- Django框架MTV架构之 Models(模型)
- 插件式设计的架构模型与实例