3661|4

603

帖子

1

TA的资源

纯净的硅(中级)

楼主
 

[BB方案]第二辑——Linux的设备驱动 [复制链接]

再次拜读了楼主的作品,于是就更加认为楼主是个山炮。 ——又是楼主 在Linux领域,可能再也找不到比楼主还矬的开发者了。 ——楼主 俗话说,万事开头难,有了第一辑的铺垫,完成了环境的搭建和基本模块的编写和加载, 那么,接下来的内容,就能进行的很顺利了……………… ……………………………………………… ……………………………………………… ……………………………………………… ……………………………………………… ………………………………吗? 显然,答案是否定的。 首先必须表明楼主其实已经无力吐槽了。根据计划,本月内要完成Linux驱动的各种学习。但是现在看来,有困难。 但是我不会放弃的。 距离上次吐槽,已经有两周过去了。真的不是楼主不想写,而是这两周里,楼主一直在苦心钻研,接下来的内容,我们来尝试通过文件式的访问来操作驱动。本期目标是实现一个假的字符设备驱动。 首先接上期,我们的模块搭建好了,再次基础上,只需要引入一个driver就OK了。楼主有一本教材,那么就首先根据教材的指导进行。 第一步是,在模块init里,通过driver_register函数来注册一个device_driver结构体的对象。 之所以这里说得如此简单,是因为,尼玛编过了执行不过呀! 我只写了个十分简单的device_driver,带几个简单的函数。这就不行了,根本没有办法insmod,报出一大堆错误……如下仅供参考吧。也方便有相关错误的童鞋通过搜索能找到这里。
  1. [ 127.605768] Hello!

    [ 127.611677] Unable to handle kernel NULL pointer dereference at virtual address 00000001

    [ 127.620207] pgd = dcce0000

    [ 127.623064] [00000001] *pgd=9ca81831, *pte=00000000, *ppte=00000000

    [ 127.629667] Internal error: Oops: 17 [#1] SMP THUMB2

    [ 127.634840] Modules linked in: hello(O+) ip_tables x_tables libcomposite ircomm_tty ircomm irda

    [ 127.643957] CPU: 0 Tainted: G O (3.8.13 #1)

    [ 127.649600] PC is at platform_match+0x2c/0x54

    [ 127.654140] LR is at platform_match+0xf/0x54

    [ 127.658587] pc : [] lr : [] psr: 60000133

    [ 127.658587] sp : dcdd1df0 ip : dcdd1d78 fp : 000006e0

    [ 127.670559] r10: 00000001 r9 : 00000000 r8 : c06d9d00

    [ 127.676005] r7 : df09d600 r6 : df09d610 r5 : bfbac358 r4 : 00000001

    [ 127.682816] r3 : c02017f9 r2 : 00000000 r1 : df09d610 r0 : 00000000

    [ 127.689618] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA Thumb Segment user

    [ 127.697240] Control: 50c5387d Table: 9cce0019 DAC: 00000015

    [ 127.703225] Process insmod (pid: 636, stack limit = 0xdcdd0240)

    [ 127.709390] Stack: (0xdcdd1df0 to 0xdcdd2000)

    [ 127.715476] 1de0: c02017f9 df09d610 c0200db5 bfbac358

    [ 127.728437] 1e00: 00000000 c0200dc3 bfbac358 c0200db5 00000000 c01ffeff df048478 df09c180

    [ 127.737063] 1e20: bfbac358 dcd3bcc0 c06b5310 c0200771 bfbac1ad bfbac1ae bfbac358 dcdd1f58

    [ 127.745647] 1e40: 00000001 bfbac0d9 c06d9d00 c02011c5 bfbac394 dcdd1f58 00000001 bfbac0d9

    [ 127.754515] 1e60: c06d9d00 00000000 00000001 bfbac0e7 dcdd0000 c0008661 c06a3b70 00000001

    [ 127.763090] 1e80: bfbac394 bfbac394 dcdd1f58 bfbac394 dcdd1f58 00000001 bfbac3a0 bfbac3dc

    [ 127.771662] 1ea0: dccd77c0 c005cd1d bfbac3a0 00007fff c005b2fd c008c421 00000010 bfbac3a0

    [ 127.780388] 1ec0: 00000000 c03a4054 bfbac3a0 bfbac4d0 e0ff00cc b6fa3cc4 6e72656b 00006c65

    [ 127.789638] 1ee0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

    [ 127.798226] 1f00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

    [ 127.806904] 1f20: 00000000 00000000 00000000 c05d5924 20000033 b6fb4000 00005d80 b6fa3cc4

    [ 127.815626] 1f40: 00000080 c000c6e4 dcdd0000 00000000 00000000 c005cfd3 e0fec000 00005d80

    [ 127.824362] 1f60: e0fefa3c e0fef88d e0ff1b04 00000530 000007a0 00000000 00000000 00000000

    [ 127.832953] 1f80: 0000002a 0000002b 00000010 0000000d 0000000b 00000000 00000000 00000000

    [ 127.841650] 1fa0: 00021d80 c000c541 00000000 00000000 b6fb4000 00005d80 b6fa3cc4 00000002

    [ 127.850224] 1fc0: 00000000 00000000 00021d80 00000080 00021088 00005d80 b6fa3cc4 00000000

    [ 127.858905] 1fe0: b6f09890 beccfba8 b6f9c734 b6f098a0 60000010 b6fb4000 00ffffff 00ffffff

    [ 127.867503] [] (platform_match+0x2c/0x54) from [] (__driver_attach+0xf/0x44)

    [ 127.876708] [] (__driver_attach+0xf/0x44) from [] (bus_for_each_dev+0x27/0x44)

    [ 127.886075] [] (bus_for_each_dev+0x27/0x44) from [] (bus_add_driver+0x65/0x158)

    [ 127.895530] [] (bus_add_driver+0x65/0x158) from [] (driver_register+0x4d/0xac)

    [ 127.904910] [] (driver_register+0x4d/0xac) from [] (hello_init+0xe/0x18 [hello])

    [ 127.914468] [] (hello_init+0xe/0x18 [hello]) from [] (do_one_initcall+0x65/0xf0)

    [ 127.924023] [] (do_one_initcall+0x65/0xf0) from [] (load_module+0x10cd/0x12fc)

    [ 127.933389] [] (load_module+0x10cd/0x12fc) from [] (sys_init_module+0x87/0x8a)

    [ 127.942757] [] (sys_init_module+0x87/0x8a) from [] (ret_fast_syscall+0x1/0x44)

    [ 127.952160] Code: f8c7 41c0 e004 3418 (7823) 2b00

    [ 127.957184] ---[ end trace ecc565211bab60ea ]---

复制代码
从dmesg的结果来看,可以看到,init过程转而最后跑到了platform_match。 我花了较长的时间在这部分的研究上。【毕竟计划里也提到了,本次重在学习,所以实际要实现的东西并不太复杂,重点是掌握相关开发技术先,之后再根据情况,争取实现得更好。所以时间基本都花在大篇大篇的学习linux上,这是有意义的事】 说得白些,好多说法我还没有掌握——注册一个驱动,如果只是把驱动挂到系统里,那就太没意思了。实际上从上面并进一步捋源代码可以看出,注册了驱动后,驱动进入相应的总线,注册成功后还要在总线上枚举设备,枚举到的每一个(我认为是还没有被驱动的)设备,检查其是否与这个新驱动匹配,如果匹配就扔到新驱动的probe过程中进一步的探测和驱动。注册设备也是一样,把新设备挂在总线上之后,会去枚举这条总线上的每一个已注册的驱动,匹配的就进一步probe。注意,这里的总线不是指物理上的总线,而是一个数据结构(可以在其下挂驱动和设备的数据结构),是一个虚拟的总线的概念。 这样的好处是什么呢,很简单,这就是热插拔呀。不但设备是热插拔的,驱动也可以啊。 事实上我所遇到的错误,就是把总线上的设备与我写的驱动进行匹配的时候,出问题了。 于是当然了,各路查找信息啊。找了一本权威的教材。《Linux设备驱动程序 第三版》在其第三章61页的地方,我发现了这样一段——早期的办法,其中提到不应该使用早期的过时的方式注册字符设备驱动:register_chrdev,因为这个方法会在将来从内核中消失。 可是我到网上一找driver_register,不对啊,好像几乎没有人在讨论driver_register,大家都是在register_chrdev。 郁闷的我又开始大篇的学习。通过网上获得的driver_register有关内容,基本上都是微软的技术支持:说的东西完全正确,可就是对用户来说一丁点儿价值都没有(此话参考前些日子坛子里的笑话)。 终于,我又开窍了,既然网上没有相关信息,我完全可以去找linux源码啊。 于是我悲催地发现,Linux源码里几乎没有在driver_register时使用device_driver的,都是使用device_driver的派生结构…… 算了,已经说了嘛,无力吐槽了啊。 于是知识结构又迈进了一步:platform_driver_register struct platform_driver 前面讲的device_driver,成了platform_driver中的一部分,这种情况就是派生啦。由device_driver派生出platform_driver类型。 进而利用platform_driver中的device_driver对象的地址,传入到driver_register中。 而这platform_driver中就有匹配时需要参考的内容,一个什么id_table。如果我原来没有这部分,而且很不幸的是参考地址的时候是一个不合理的值,那么,惨案就此发生了。 根据上面思想,楼主重新修改了代码,果然这次注册就成功了。所以只要接下来注册我的设备就OK了,设备注册会引起驱动的probe被调用,在probe中激活相应的设备不就妥了嘛。 不行啊……我似乎在这条路上越走越远了,后面到底还有多少沟啊…… 我突然懂了。 赶紧把《Linux设备驱动程序第三版》丢进厕所,我选择了register_chrdev,platform我们未来再见。 重新改造了代码,register_chrdev果然上手很快。但是又通过种种途径得知,/dev下的设备节点文件需要手动用命令创建,楼主觉得这个太不明智了。 于是到教材上继续寻找,得知新的办法是可以利用class_create和device_create来创建那个倒霉文件。好了,一个混搭而成的代码终于形成了,重要的是,它可以加载和被正确使用。 当然,头文件的问题,真是有点有病乱投医了。
  1. /* hello.c -- sjtitr */

  2. #include

    #include

    #include

  3. #include

    #include

    #include

  4. #include

    #include

    #include

  5. #include

    #include

  6. #define THIS_DRV_NAME "PracticeDriver"

  7. /* ------------------------- fileops --------------------------------- */

    #define FIXED_FILE_SIZE 8

  8. int practice_open(struct inode *inode, struct file *filp);

    int practice_release(struct inode *inode, struct file *filp);

    ssize_t practice_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos);

    ssize_t practice_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos);

  9. unsigned char vt_file[FIXED_FILE_SIZE];

    unsigned char vt_file_i = 0;

  10. int practice_open(struct inode *inode, struct file *filp)

    {

    printk(KERN_ERR "FOPS - Open\n");

    return 0;

    }

  11. int practice_release(struct inode *inode, struct file *filp)

    {

    printk(KERN_ERR "FOPS - Release\n");

    return 0;

    }

  12. ssize_t practice_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)

    {

    ssize_t size = (count < FIXED_FILE_SIZE) ? count : FIXED_FILE_SIZE;

    copy_to_user(buffer, vt_file, size);

    printk(KERN_ERR "FOPS - Read %d bytes\n", size);

    return size;

    }

  13. ssize_t practice_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos)

    {

    ssize_t size = (count < FIXED_FILE_SIZE) ? count : FIXED_FILE_SIZE;

    copy_from_user(vt_file, buffer, size);

    vt_file[FIXED_FILE_SIZE - 1] = 'A' + ((vt_file_i++)%10);

    printk(KERN_ERR "FOPS - Write %d bytes\n", size);

    return size;

    }

  14. static struct file_operations this_drv_fops =

    {

    .owner = THIS_MODULE,

    .open = practice_open,

    .release = practice_release,

    .read = practice_read,

    .write = practice_write,

    };

  15. static unsigned int this_dev_t;

    static struct class *this_class;

  16. /* ------------------------- Module ---------------------------------- */

    MODULE_AUTHOR("sjtitr");

    MODULE_LICENSE("Dual BSD/GPL");

  17. static int hello_init(void)

    {

    int ret = 0;

    int major;

    printk(KERN_ALERT "Hello!\n");

    if(!ret && (major = register_chrdev(0, THIS_DRV_NAME, &this_drv_fops))<0)

    {

    printk("Failed to register chrdev\n");

    ret = 1;

    }

  18. if(!ret)

    {

    this_dev_t = MKDEV(major, 0);

  19. printk(KERN_ERR "make driver fs\n");

  20. this_class = class_create(THIS_MODULE,THIS_DRV_NAME);

    if (IS_ERR(this_class))

    {

    printk(KERN_ERR "Failed to make driver fs\n");

    unregister_chrdev(MAJOR(this_dev_t), THIS_DRV_NAME);

    ret = 1;

    }

    else

    {

    device_create(this_class, NULL, this_dev_t, NULL, THIS_DRV_NAME"%d", 0);

    }

    }

    return 0;

    }

  21. static void hello_exit(void)

    {

    device_destroy(this_class, this_dev_t);

    class_destroy(this_class);

    unregister_chrdev(MAJOR(this_dev_t), THIS_DRV_NAME);

    printk(KERN_ALERT "Goodbye!\n");

    }

  22. module_init(hello_init);

    module_exit(hello_exit);

  23. /* ------------------ END ---------------------- */

复制代码
关于对设备节点文件的访问,就不多讲了,这个网上实在是太多太多,我随便找了个抄下来,打开我的PracticeDriver0文件,就可以读写啦。注意到的是,这个程序楼主是敲到BBB上直接在板上用gcc编译的。 也许故事到这里就该停止了,不过楼主的思想还没有停止。 妈蛋,好像又被骗了。看看这混搭的代码,其实把register_chrdev换成《Linux设备驱动程序第三版》所叙述的一组调用: alloc_chrdev_region cdev_alloc cdev_init cdev_add 不就成了书上所说的鼓励使用的新方法了嘛!貌似这个方式在网上也找得到,只是当时我觉得好凌乱,根本不像,于是直接pass掉了呀。 那么那个driver_register是个怎么回事儿?难道我又遇到了无良的教材?还是说教材也被频频变更的内核给毁了? 拜托了,我真的是无力吐槽了啊。 我到底是绕了多大的一个弯才搞明白的啊? ====================================================================== 后记: 本次学习的成果是实现了一个虚假的设备及其驱动。由于设备和驱动是绑定为一体的,而且设备还是个假设备,不存在热插拔的概念,所以根本不需要注册驱动注册设备这样的搞,显得麻烦而且反倒是不易理解。显然古董的调用register_chrdev最直接,直接体现了注册驱动和文件操作结构的对应性,文件结构读写中可能就直接操作设备了。 只是根据权威的指导,还是尽量不要使用register_chrdev为上策,尽量使用后面一连串的调用(为什么推荐的方法这么麻烦啊)。 对于需要热插拔支持的设备及驱动,当然就要利用driver_register来实现,这个方式还是很高大上的。日后一定要再好好研究,暂时先告别这个话题了。

最新回复

请问楼主如何把应用程序的部分功能编译到内核中去?  详情 回复 发表于 2014-4-20 15:37
点赞 关注(2)
 

回复
举报

5979

帖子

8

TA的资源

版主

沙发
 
呵呵,不错的开始!
个人签名生活就是油盐酱醋再加一点糖,快活就是一天到晚乐呵呵的忙
===================================
做一个简单的人,踏实而务实,不沉溺幻想,不庸人自扰
 
 

回复

954

帖子

0

TA的资源

纯净的硅(初级)

板凳
 
楼主高大上,完全不懂。。。。
 
 
 

回复

115

帖子

0

TA的资源

一粒金砂(中级)

4
 
请问楼主如何把应用程序的部分功能编译到内核中去?

点评

应用程序,不需要编译到内核中。  详情 回复 发表于 2014-4-20 19:07
 
 
 

回复

603

帖子

1

TA的资源

纯净的硅(中级)

5
 
jaccopa 发表于 2014-4-20 15:37
请问楼主如何把应用程序的部分功能编译到内核中去?

应用程序,不需要编译到内核中。
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表