• 设计和编写设备驱动的一般方法


    http://zqwt.012.blog.163.com/blog/static/1204468420128291156143/

    我的上一篇博客《设备驱动调试和移植的一般方法》详细介绍了调试和移植的几个步骤,依照那几个步骤,按部就班,外加不屑的努力,你就可以完成驱动的调试和移植。但是当你面临的是一块崭新的芯片XXXX,当前并没有现有的芯片相近驱动代码,更没有芯片原厂提供给你的驱动代码,只有一份该芯片的数据手册,此时,你就不得不FROM SCRATCH。

    这时候你是不是有点不知所措呢?其实你不用着急,根据可知论,世界是可以被认知的,linux设备驱动的一般性的设计和编写方法也是存在的,所以,你大可不必惊慌。Linux设备驱动的设计一般可以遵循以下几个步骤。

    一、深刻理解datasheet和原理图

    这里讲的datasheet包括主控的datasheet和设备芯片的datasheet,原理图就指的是开发板的的原理图了,

    如果有pads版本的原理图和贴片图,那是最好的了,方便以后测量引脚的各项参数。阅读和深刻理解设备芯片的datasheet对第一个步骤里面最重要的组成部分,设备驱动就是datasheet的最直观表达,只有在深刻理解的datasheet的基础之上,才有可能设计和开发出优秀的设备驱动。另外,datasheet在一定程度上就是你做驱动软件的需求文档。你做软件总该有个需求吧,那么这里谈到的datasheet就是驱动软件的最好的、最清楚的需求分析。所以,在没有理解datasheet之前就着手做驱动,那结果是可想而知的。

    二、搭建起设备驱动的框架

    理解了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 ", 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目录)获得。总之,你要编写什么类型的驱动,首先根据实际情况搭建好框架。

    三、填充框架

    搭建好设备驱动程序的框架之后,你应该做的就是根据实际情况往框架里面填充内容了。填充内容可以分为两个层次的填充,分别是:

    l  和上层应用打交道的内容

    主要包括:

    ?  基于devfs实现文件操作结构体(file_operations)中的函数,目的是给上层提供系统调用(system call)

    ?  通过procfs或者sysfs给上层应用提供不同种类的接口

    ?  向虚拟文件系统(VFS)注册或者注销(添加或者删除)设备驱动,等等。

    l  和主控以及设备芯片打交道的内容

    依据主控、设备芯片的datasheet以及开发板原理图,做以下填充:

    ?  定义设备驱动的头文件

    ?  填充系统调用中和设备芯片直接打交道的部分

    ?  编写设备驱动程序电源管理模块

    ?  配置好I/O口的功能,等等。

    四、开始设备驱动的调试

    这里就得回头谈谈我的上一篇博客《设备驱动调试和移植的一般方法》。它详细介绍了调试和移植的几个步骤。

    这里的第四步从《设备驱动调试和移植的一般方法》这篇文章里面讲的第一步往下一步一步走,按部就班,外加你不懈的努力,最终你会发现被别人吹的像神一样的设备驱动会一个一个被你征服。

  • 相关阅读:
    [洛谷P1484] 种树
    Codeforces Round #505 Div. 1 + Div. 2
    [NOIp2015] 斗地主
    ☆ [NOIp2016] 天天爱跑步 「树上差分」
    [NOI2010] 超级钢琴
    [POI2000] 病毒
    [SCOI2010] 股票交易
    [NOI2002] 贪吃的九头龙
    [ZJOI2008] 骑士
    LeetCode 笔记系列 18 Maximal Rectangle [学以致用]
  • 原文地址:https://www.cnblogs.com/welhzh/p/4786685.html
Copyright © 2020-2023  润新知