4934|1

1234

帖子

4

TA的资源

纯净的硅(高级)

楼主
 

[记录]ARM,Linux,搭建一个字符设备驱动程序的框架(慢慢改进) [复制链接]

本帖最后由 ywlzh 于 2016-7-23 21:41 编辑

  在我看来,Linux内核开发,有两步,第一步,不管是字符设备驱动程序,还是块设备驱动程序,还是网络设备驱动程序,都需要一个空的框架,第二步,在这个空的框架下再填充对硬件的控制。然后编译,下载到板子上运行。
  这里有一篇讲内核开发入门编写hello world 模块,我觉得很好的文章,值得一看,由简入深。
  http://mp.weixin.qq.com/s?__biz= ... 85559127&scene=0#rd
先写一个最简单的字符设备驱动:

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/fs.h>
  4. #include <linux/init.h>
  5. #include <linux/delay.h>
  6. #include <asm/uaccess.h>
  7. #include <asm/irq.h>
  8. #include <asm/io.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>
  11. #include <linux/slab.h>
  12. #include <asm/uaccess.h>

  13. #define DEVICE_NAME     "kong"   /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
  14. #define kong_MAJOR       250     /* 主设备号 */


  15. static int kong_open(struct inode *inode, struct file *file)
  16. {
  17.   printk("open ok\n");
  18.   return 0;
  19. }

  20. static kong_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
  21. {
  22.   printk("write ok\n");
  23.   char val;

  24.   copy_from_user(&val,buf,1);

  25.   printk("write data:%d \n",val);

  26.   return 0;
  27.   
  28. }
  29. static struct file_operations kong_fops = {
  30.     .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
  31.     .open   =   kong_open,     
  32.         .write        =        kong_write,           
  33. };


  34. static int __init kong_init(void)
  35. {
  36.     int ret;
  37.     ret = register_chrdev(kong_MAJOR, DEVICE_NAME, &kong_fops);
  38.     if (ret < 0) {
  39.       printk(" can't register major number\n");
  40.       return ret;
  41.     }
  42.         printk("register sucess /dev/kong OK!\n");
  43.     printk(DEVICE_NAME " initialized\n");
  44.         return 0;
  45. }

  46. static void __exit kong_exit(void)
  47. {
  48.    unregister_chrdev(kong_MAJOR, DEVICE_NAME);
  49.    
  50.    printk("rmmod OK!\n");
  51. }

  52. module_init(kong_init);
  53. module_exit(kong_exit);

  54. MODULE_LICENSE("GPL");
复制代码
可以看到,这个代码中,入口函数就这个register_chrdev函数,这个函数做了什么?注册了设备号250,这个设备号是通过终端cat /proc/devices 发现250号没用用到,于是就用上了,提交了一个结构体kong_fops,这里只对open,write,做了处理,后续可以根据情况添加。将文件传到虚拟机Linux系统里,make,在板子上insmod
再写一个测试.c文件


  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>

  5. int main(int argc, char **argv)
  6. {

  7.   int fd;
  8.   char val;
  9.   val = 13;
  10.   fd=open("/dev/xxx",O_RDWR);
  11.   if (fd < 0)
  12.   {
  13.         printf("error, can't open \n");
  14.         return 0;
  15.   }
  16.   write(fd, &val, 1);
  17.   printf("write :%d \n",val);

  18.   return 0;
  19.   
  20. }


复制代码
如果注意到了,会发现驱动文件是注册的"/dev/kong" ,而测试文件是打开的“/dev/xxx”,这能打开吗,答案是能,为什么?因为......太多的解释,我也不清楚,我就知道内核之所以能找上这个驱动因为主设备号,来看结果,我们为/dev/xxx创建一个设备节点,驱动的主设备号是250,那就创建250号,好让测试文件可以打开。在板子终端输入  mknod /dev/xxx c 250 0
运行测试文件,需要先交叉编译

开始改进:
每次驱动程序写完,都需要自己看下哪个这设备号没有用到,然后在选择没有用到的设备号,需要改进为动态注册设备号,让系统自动分配,写完后还需要手动创建设备节点,需要改进为让系统自己去为这个驱动创建设备节点,这样应用层就更好的测试和开发了
上面的驱动程序不变,刚才需要改进的地方都是可以在入口函数做的
  1. static int __init kong_init(void)
  2. {
  3.     int ret;
  4.     ret = register_chrdev(kong_MAJOR, DEVICE_NAME, &kong_fops);
  5.     if (ret < 0) {
  6.       printk(" can't register major number\n");
  7.       return ret;
  8.     }
  9.         printk("register sucess /dev/kong OK!\n");
  10.         kong_class = class_create(THIS_MODULE, "kong");
  11.         if (IS_ERR(kong_class))
  12.                 return PTR_ERR(kong_class);
  13.         
  14.         kong_class_devs = device_create(kong_class, NULL, MKDEV(kong_MAJOR, 0), NULL, "kong");
  15.         if (unlikely(IS_ERR(kong_class_devs)))
  16.                         return PTR_ERR(kong_class_devs);
  17.                
  18.     printk(DEVICE_NAME " initialized\n");
  19.         return 0;
  20. }
复制代码
整个函数先不要被if语句给看误导了,就三个函数
   register_chrdev
   class_create
   device_create
既然入口函数变了,出口也要相应的变

  1. static void __exit kong_exit(void)
  2. {
  3.    unregister_chrdev(kong_MAJOR, DEVICE_NAME);
  4.    
  5.    device_unregister(kong_class_devs);
  6.    
  7.    class_destroy(kong_class);
  8.    
  9.    printk("rmmod OK!\n");
  10. }
复制代码
这样,测试函数就不要写open "/dev/xxx",可以直接“dev/kong”,系统可以直接找到这个驱动的

还可以改进
要是注册没有成功怎么办?创建设备节点没有成功怎么办?怎么申请?多个功能一样的硬件比如GPIO一类,串口一类,是不是对应每一个
GPIO都要写一个驱动程序,肯定不是,可以在一个驱动设备程序里写上,怎么区分?用次设备号,次设备号多了需不需要申请内存?失败了怎么办?
这里的改进,就不一一细说了,还是分享网上别人写的挺好的文章。
当中有完整的字符设备驱动程序
http://blog.chinaunix.net/uid-26833883-id-4371047.html
此帖出自ARM技术论坛
点赞 关注
个人签名天地庄周马;江湖范蠡船。
个性签名还是放QQ号吧,2060347305,添加说明EEworld好友
 

回复
举报

1234

帖子

4

TA的资源

纯净的硅(高级)

沙发
 
本帖最后由 ywlzh 于 2016-7-29 13:16 编辑

混杂驱动程序
这里可以参照《宋宝华_精通LINUX设备驱动程序》中第5章第7节



所以在写这种驱动程序的时候,保留字符设备驱动程序的file_operations结构,
程序入口(出口)函数做了简化操作,为开发者做了便捷,有利便有弊,自己在开发的时候注意一下就可以了
总的来说,写起来还是挺便捷的
  1. static struct miscdevice xxx_miscdev = {
  2.         .minor  = MISC_DYNAMIC_MINOR,
  3.         .name        = DEVICE_NAME,
  4.         .fops        = &xxx_fops,
  5. };        
  6. static int __init xxx_init(void)
  7. {

  8.         misc_register(&xxx_miscdev);
  9.         printk(DEVICE_NAME" up. \n");

  10.         return 0;
  11. }

  12. static void __exit xxx_exit(void)
  13. {
  14.         misc_deregister(&xxx_miscdev);
  15.         printk(DEVICE_NAME " down.\n");
  16. }

  17. module_init(xxx_init);
  18. module_exit(xxx_exit);

  19. MODULE_LICENSE("Dual BSD/GPL");
复制代码
有个不同的地方,普通的字符设备驱动程序,在注册的时候可以提交file_operations结构而混杂驱动程序,需要在miscdevice结构提交就可以了。

此帖出自ARM技术论坛
 
个人签名天地庄周马;江湖范蠡船。
个性签名还是放QQ号吧,2060347305,添加说明EEworld好友
 
 

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

查找数据手册?

EEWorld Datasheet 技术支持

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

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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

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

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

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