理解了datasheet之后,先不要急着去编写代码,你首先应该做的就是给你将要写的驱动程序设计一个框架。
那么这里的框架应该依据什么搭建呢?具体怎么搭建呢?
一般的,从USB驱动到I2C驱动,从SPI驱动到串口驱动,从PCI驱动到DMA驱动,等等,不管是什么类型的驱动,它总有一种或者几种基本固定的套路供你选择。如果你打算写一个touch驱动,而这个touch挂接在I2C上,那么你就依据I2C设备编写的一一种固定套路先搭建框架。具体如何搭建,这里就给出I2C设备驱动编写的一种固定套路给大家。
I2C设备驱动的新式编写方法为probe方法,这里就讲编写I2C设备驱动的probe方法的框架的搭建。
l 创建一个i2c_driver
static struct i2c_driver XXXX_driver = {
.driver = {
.name= "XXXX",
},
.probe = XXXX_probe, /* 当存在i2c_client和i2c_driver匹配时调用 */
.remove = XXXX_remove,
.id_table = XXXX_id,/* 匹配规则 */
};
l 注册i2c_driver
static int __init XXXX_init(void)
{
return i2c_add_driver(&XXXX_driver);
}
module_init(XXXX_init);
注册i2c_driver的过程,实际上是将driver注册到i2c_bus_type的总线上。此总线的匹配规则是:
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_client的名称和id_table中的名称做匹配的。这里的id_table为:
static const struct i2c_device_id XXXX_id[] = {
{ "XXXX346", 8, },
{ "XXXX353", 16, },
{ "XXXX388", 8, },
{ "XXXX399", 16, },
{ "XXXX542", 8, },
{ "XXXX551", 16, },
{ "XXXX572", 8, },
{ }
};
l 注册i2c_board_info
对于Probe方法,都要在平台代码中要完成i2c_board_info的注册。办法如下:
static struct i2c_board_info __initdata tmp_i2c_devices[] = {
{
I2C_BOARD_INFO("XXXX9555", 0x27),//XXXX9555为芯片名称,0x27为芯片地址
.platform_data = &XXXX9555_data,
},
{
I2C_BOARD_INFO("mt9v022", 0x48),
.platform_data = &iclink[0], /* With extender */
},
{
I2C_BOARD_INFO("mt9m001", 0x5d),
.platform_data = &iclink[0], /* With extender */
},
};
i2c_register_board_info(0,tmp_i2c_devices,
ARRAY_SIZE(tmp_i2c_devices)); /* 注册 */
i2c_client是在注册过程中创建的。
l 调用i2c适配器来完成数据传输
int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,int num);
master_xfer要通过调用i2c_transfer来完成传输。
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);
l 字符驱动注册
I2C设备驱动的Probe方法,字符驱动的添加位置在XXXX_probe中。
static int __devinit XXXX_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
/* … 省略 …*/
if(XXXX_major){
Result = register_chrdev_region(XXXX_dev,1,"XXXX");
} else {
result = alloc_chrdev_region(&XXXX_dev,0,1,"XXXX");
XXXX_major=MAJOR(XXXX_dev);
}
if (result < 0) {
printk(KERN_NOTICE "Unable to get XXXX region, error %d\n", result);
return result;
}
XXXX_setup_cdev(chip,0); /* 注册字符设备, 具体的请google一下 */
/* … 省略 …*/
}
l 字符设备驱动的构建
struct file_operations XXXX_fops = {
.owner = THIS_MODULE,
.ioctl = XXXX_ioctl,
.open = XXXX_open,
.release = XXXX_release,
};
l 删除i2c_driver
static void __exit XXXX_exit(void)
{
i2c_del_driver(&XXXX_driver);
}
module_exit(XXXX_exit);
l 删除字符设备驱动
static int __devinit XXXX_remove (struct i2c_client *client)
{
/*…省略…*/
}
你看到这里,就应该明白上面讲到的所谓设备驱动程序的框架是什么意思了吧。当然,这里只写出了I2C设备驱动的一种框架,SPI、USB、DMA 驱动编程都有其各自的框架,具体的信息可以从内核文档(kernel顶层目录下的Documentation目录)获得。总之,你要编写什么类型的驱动,首先根据实际情况搭建好框架。