您的位置:首页 > 运维架构 > 网站架构

从总线模型看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适配器层:底层实现
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: