设计和编写设备驱动的一般方法
来源:互联网 发布:欧盟进出口数据 编辑:程序博客网 时间:2024/05/04 01:01
转载:http://zqwt.012.blog.163.com/blog/static/1204468420128291156143/
Linux设备驱动的设计一般可以遵循以下几个步骤。
一、datasheet和原理图
阅读和深刻理解设备芯片的datasheet对第一个步骤里面最重要的组成部分,设备驱动就是datasheet的最直观表达,只有在深刻理解的datasheet的基础之上,才有可能设计和开发出优秀的设备驱动。另外,在一定程度上,datasheet是你做驱动软件的需求文档。你做软件总该有个需求吧,那么这里谈到的datasheet就是驱动软件的最好的、最清楚的需求分析。所以,在没有理解datasheet之前就着手做驱动,那结果是可想而知的。
二、搭建起设备驱动的框架
理解了datasheet之后,先不要急着去编写代码,你首先应该做的就是给你将要写的驱动程序设计一个框架。那么这里的框架应该依据什么搭建呢?具体怎么搭建呢?一般的,从USB驱动到I2C驱动,从SPI驱动到串口驱动,从PCI驱动到DMA驱动,等等,不管是什么类型的驱动,它总有一种或者几种基本固定的套路供你选择。如果你打算写一个touch驱动,而这个touch挂接在I2C上,那么你就依据I2C设备编写的一一种固定套路先搭建框架。具体如何搭建,这里就给出I2C设备驱动编写的一种固定套路给大家。
1、创建一个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,/* 匹配规则 */
};
2、注册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, },
{ }
};
3 、注册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是在注册过程中创建的。
4 、调用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);
5 、 字符驱动注册
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一下 */
/* … 省略 …*/
}
6 、字符设备驱动的构建
struct file_operations XXXX_fops = {
.owner = THIS_MODULE,
.ioctl = XXXX_ioctl,
.open = XXXX_open,
.release = XXXX_release,
};
7 、删除i2c_driver
static void __exit XXXX_exit(void)
{
i2c_del_driver(&XXXX_driver);
}
module_exit(XXXX_exit);
8 、 删除字符设备驱动
static int __devinit XXXX_remove (struct i2c_client *client)
{
/*…省略…*/
}
// 注:总之,你要编写什么类型的驱动,首先根据实际情况搭建好框架。
三、填充框架
搭建好设备驱动程序的框架之后,你应该做的就是根据实际情况往框架里面填充内容了。填充内容可以分为两个层次的填充,分别是:
1、和上层应用打交道的内容
主要包括:
1) 基于devfs实现文件操作结构体(file_operations)中的函数,目的是给上层提供系统调用(system call)
2) 通过procfs或者sysfs给上层应用提供不同种类的接口
3) 向虚拟文件系统(VFS)注册或者注销(添加或者删除)设备驱动,等等。
2、和主控以及设备芯片打交道的内容
1) 依据主控、设备芯片的datasheet以及开发板原理图,做以下填充:
2) 定义设备驱动的头文件
3) 填充系统调用中和设备芯片直接打交道的部分
4) 编写设备驱动程序电源管理模块
5) 配置好I/O口的功能,等等。
四、开始设备驱动的调试
一般的,设备驱动的调试都会经历如下几个步骤:
1、把设备驱动的代码添加进kernel (注意:不同的方案提供商给出的添加方法是不同的)。传统架构的话,就很简单了,一般都会涉及到以下几步:
(1)新建设备驱动目录 ; (2)添加头文件 ;(3)创建当前目录的Makefile
(4)创建上层目录的Makefile ;(5)在上层目录添加Kconfig选项 ; (6)在板文件添加对应项。
(7)如果不想每次都用menuconfig配置,就在kernel/arch/arm/configs/XXX_defconfig里面添加编译项
2、使得添加进去的设备驱动可以编译通过
设备驱动添加进来之后,一般的,都需要你添加、删除、修改一些内容,才能保证其编译通过。如果是调试,这里可能改动不大,因为
驱动文件一般是由芯片原厂提供的,他们已经修改的基本可以编译通过了。但如果是移植,在这里花费的精力一般是比较大的,这会在
后文中的附加说明里面提及到。
3、检查设备驱动是否正常初始化
进行到这一步,你需要检查设备驱动是否可以顺利初始化,这个很简单,你只需要在设备驱动的probe函数或者init函数里面添加一条打
印信息即可在串口观察是否有打印,如果没有打印,说明驱动根本就没有得到初始化。此时,你应该做两件事情:
(1)首先,确定你的板子以及贴了你要调试的设备驱动所对应的芯片。
(2)其次,如果芯片确实贴上了,马上检查设备驱动的添加是否有什么问题,是否是板文件没有修改好,是否驱动本身就没有注册好等等。
4、检查应用程序是否可以正常使用
如果驱动得到了正常的初始化,这时候你开始检查该设备驱动对应的应用程序是否可以正常使用。如果可以正常使用,那你太幸运了,这说明芯片
原厂为你考虑的很周到,你可以节省N多时间干别的事儿了。但遗憾的是,往往事情不会这么顺利,当你检查应用程序的时候,你发现应用程序没
有做出应有的反应。
5、设备驱动的“调”和“试”
所谓“调”,就是你依据芯片datasheet和观察与测量的结果,不断地对设备驱动 code进行添加、删除和修改。所谓“试”,就是你在添加、删除和
修改的同时要不断地通过串口对反馈信息进行观察,通过万用表对各电源引脚电压进行测量,通过示波器对芯片的时钟(输入的和输出的)频率、数
据信号的波形、某种总线信号的波形,等各个引脚进行测量(当然这里测量的参考标准是芯片的datasheet了)。这里应该养成一个好习惯,那就是
在测量各个引脚的同时,你最好创建一份excel表格,把各个引脚的电压、波形等情况记录下来,目的是在N多次修改之间作比较,也更有备忘的作用。
这里的“调”和“试”的过程是最耗时的,也是设备驱动调试的最关键的步骤,所以你应该非常有耐心的走好这一步。还应该注意的是,设备驱动的
调试不光是你一个人的事情,有时候你闷头苦干一个星期都不一定能搞定。所以,进行调试的同时,必须有一个硬件工程师配合你,因为驱动不能正常
运行也很有可能是硬件的设计纰漏导致的。此外,不要闭门造车,毕竟你在一个大的开发团队里面,你需要及时和同事进行有效的交流,避免走弯路。
最后,如果还是存在问题,你就得邀请芯片原厂的FAE过来喽。
6、自己对设备驱动的测试
测试不光是测试员的事情,你必须保证自己调试的驱动可以运行稳定才可以提交代码,发给测试员进行大量测试。测试自己调试的设备驱动有以下几种:
(1)利用系统里面现有的应用程序进行测试;这个是最直接的测试,也省去了你自己编写测试程序的时间。
(2)自己编写linux应用程序来测试驱动;如果你对系统现有的应用程序不放心,或者不满意,就自己编写测试驱动吧。
(3)利用proc或者sys文件系统的读和写函数
如果你在设备驱动里面添加了proc或者sys文件系统的读和写函数,你就可以在命令行通过cat或者echo命令来对设备驱动的各项参数进行手动测试。
- 设计和编写设备驱动的一般方法
- 设计和编写设备驱动的一般方法
- 设备驱动调试和移植的一般方法(一)
- 设备驱动调试和移植的一般方法(二)
- 设备驱动调试和移植的一般方法
- 设备驱动调试和移植的一般方法
- LINUX I2C设备驱动的编写方法
- Linux下SPI和IIC驱动免在设备树上添加设备信息的编写方法
- 字符设备驱动编写方法
- 两种方式的i2c设备驱动的编写方法
- I2C设备驱动的编写
- Linux系统I2C设备驱动编写方法
- 重用的设计一般方法
- LDD3linux设备驱动源码树的安装和第一个hello wolrd驱动的编写
- LDD3linux设备驱动源码树的安装和第一个hello wolrd驱动的编写
- 编写Ogre插件的一般方法
- C++编写函数模板的一般方法
- 驱动编写和编译方法
- onvif规范 中文介绍
- 排序算法的总结
- 蛇形填数
- 5.1 二叉树
- html的autocomplete属性如何使用
- 设计和编写设备驱动的一般方法
- 数据库基本操作
- 给初学者之九:收尾
- flex处理xml
- JEE , EJB概括
- 华为boss力荐公司高层看的一篇文章,真的很经典!!![转载]
- C++ Various String Types (char, wchar_t, string, wstring, cstring)
- 14.3.2 实现自己的拍照Activity(2)
- Linux知识积累:一些常用的Linux命令