嵌入式内核及驱动开发之学习笔记(四) 规范化代码

举报
王建峰 发表于 2021/11/19 04:06:18 2021/11/19
【摘要】 前面笔者已实现了用户程序控制led灯闪烁的驱动代码,但是由于代码不规范,显得乱七八糟的,因此需要规范化。如果比较大一点的工程没有规范的话,也不利于后期的跟新与维护。分析先前的程序不规范点有二: 定义的变量多而且散乱程序没有错误处理机制 C语言虽然是面向过程的语言,但是可以利用结构体来实现面向对象的思想。通过引入面向对象的思想,来解决第...

前面笔者已实现了用户程序控制led灯闪烁的驱动代码,但是由于代码不规范,显得乱七八糟的,因此需要规范化。如果比较大一点的工程没有规范的话,也不利于后期的跟新与维护。分析先前的程序不规范点有二:

  1. 定义的变量多而且散乱
  2. 程序没有错误处理机制

C语言虽然是面向过程的语言,但是可以利用结构体来实现面向对象的思想。通过引入面向对象的思想,来解决第一个问题。通过结构体将将相关的变量类型进行一次封装,构造出一个对象;而对于错误处理, 可以通过prink提示错误信息,然后goto语句跳转到错误处理的过程来处理。

 

面向对象

在面向对象的思想中,一切皆是对象。将led设备抽象成为一个对象,那它的主设备号、寄存器基地址等信息都可以看做它的属性,我们用结构体将这样一些数据类型进行封装。


  
  1. struct led_desc{
  2. //声明结构体类型,描述led信息
  3. unsigned int dev_major; //描述主设备号
  4. struct class *cls;
  5. struct device *dev;
  6. void *reg_virt_base; //寄存器基地址
  7. };

定义结构体对象 ,led_dev明显是一个结构体指针

struct led_desc *led_dev;
 

为led_dev分配空间,对象的实例化

led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
 

 

如果要引用这个对象的属性(结构体访问成员变量),例如

led_dev->dev_major
 

 

因为使用malloc分配的是块空间(堆),结束时需要手动释放资源(空间)

kfree(led_dev);
 

 

 

错误处理机制

当程序申请资源失败,我们不仅要分析判断这个错误,还要打印错误提示


  
  1. led_dev->dev_major = register_chrdev(0, "led_dev_test", &my_fops);
  2. if(led_dev->dev_major < 0)
  3. {//打印错误
  4. printk(KERN_ERR "register_chrdev error\n");
  5. }

但是,,这样的做法是不对的。我们程序不能直接抛出错误信息,然后退出,给系统留下一个烂摊子啊!因为之前可能存在,已申请但是没有被释放的资源。正确的做法

错误源 ---> 程序判断 ---> (打印错误信息) ---> 设置错误码 ---> 跳转到错误处理 ---> 退出


  
  1. int ret;
  2. //led_dev分配空间,对象的实例化
  3. led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
  4. if(led_dev == NULL)
  5. {
  6. printk(KERN_ERR "malloc error\n");
  7. return -ENOMEM;
  8. }
  9. //动态向系统申请设备号
  10. led_dev->dev_major = register_chrdev(0, "led_dev_test", &my_fops);
  11. if(led_dev->dev_major < 0)
  12. {
  13. printk(KERN_ERR "register_chrdev error\n");
  14. ret = -ENODEV;
  15. goto err_0;
  16. }
  17. err_0:
  18. kfree(led_dev);
  19. return ret;

总之,错误处理是要处理那些 在错误出现之前申请的资源,将其回收。

 

驱动代码

下面是对之前led驱动程序的一些改进。

  • 使用了结构体来描述设备信息
  • 使用goto进行错误处理
  • 修改申请主设备号为动态方式
  • 使用readl writel 接口函数读写地址

  
  1. //led_drv.c
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/fs.h>
  5. #include <linux/device.h>
  6. #include <linux/slab.h>
  7. #include <asm/uaccess.h>
  8. #include <asm/io.h>
  9. ssize_t led_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos);
  10. ssize_t led_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos);
  11. int led_drv_open(struct inode *inode, struct file *filp);
  12. int led_drv_close(struct inode *inode, struct file *filp);
  13. #define GPX2_CON 0x11000C40
  14. #define GPX2_SIZE 8
  15. //volatile unsigned long *gpx2conf;
  16. //volatile unsigned long *gpx2dat;
  17. //static unsigned int dev_major = 250;
  18. //static struct class *devcls;
  19. //static struct device *dev;
  20. const struct file_operations my_fops = {
  21. .open = led_drv_open,
  22. .read = led_drv_read,
  23. .write = led_drv_write,
  24. .release = led_drv_close,
  25. };
  26. struct led_desc{
  27. //声明结构体类型,描述led信息
  28. unsigned int dev_major; //描述主设备号
  29. struct class *cls;
  30. struct device *dev;
  31. void *reg_virt_base; //寄存器基地址
  32. };
  33. //定义一个结构体变量,创建对象
  34. struct led_desc *led_dev;
  35. static int __init led_drv_init(void)
  36. {
  37. int ret;
  38. printk("-------%s-------------\n", __FUNCTION__);
  39. //led_dev分配空间,对象的实例化
  40. led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
  41. if(led_dev == NULL)
  42. {
  43. printk(KERN_ERR "malloc error\n");
  44. return -ENOMEM;
  45. }
  46. //动态向系统申请设备号
  47. led_dev->dev_major = register_chrdev(0, "led_dev_test", &my_fops);
  48. if(led_dev->dev_major < 0)
  49. {
  50. printk(KERN_ERR "register_chrdev error\n");
  51. ret = -ENODEV;
  52. goto err_0;
  53. }
  54. //创建设备结点
  55. led_dev->cls = class_create(THIS_MODULE, "led_cls");
  56. if(IS_ERR(led_dev->cls))
  57. {
  58. printk(KERN_ERR "class_create error\n");
  59. ret = PTR_ERR(led_dev->cls); //½«Ö¸Õë³ö´íµÄ¾ßÌåÔ­Òòת»»³ÉÒ»¸ö³ö´íÂë
  60. goto err_1;
  61. }
  62. led_dev->dev = device_create(led_dev->cls, NULL,
  63. MKDEV(led_dev->dev_major, 0), NULL, "led%d", 0);
  64. if(IS_ERR(led_dev->dev))
  65. {
  66. printk(KERN_ERR "device_create error\n");
  67. ret = PTR_ERR(led_dev->dev); //½«Ö¸Õë³ö´íµÄ¾ßÌåÔ­Òòת»»³ÉÒ»¸ö³ö´íÂë
  68. goto err_2;
  69. }
  70. //将物理地址映射成为虚拟地址,用指针指向这个地址
  71. led_dev->reg_virt_base = ioremap(GPX2_CON, GPX2_SIZE);
  72. if(led_dev->reg_virt_base == NULL)
  73. {
  74. printk(KERN_ERR "ioremap error\n");
  75. ret = -ENOMEM;
  76. goto err_3;
  77. }
  78. //GPX2_7设置成输出模式
  79. u32 value = readl(led_dev->reg_virt_base);
  80. value &= ~(0xf<<28);
  81. value |= (0x1<<28);
  82. writel(value, led_dev->reg_virt_base);
  83. return 0;
  84. err_3:
  85. device_destroy(led_dev->cls, MKDEV(led_dev->dev_major, 0));
  86. err_2:
  87. class_destroy(led_dev->cls);
  88. err_1:
  89. unregister_chrdev(led_dev->dev_major, "led_dev_test");
  90. err_0:
  91. kfree(led_dev);
  92. return ret;
  93. }
  94. static void __exit led_drv_exit(void)
  95. {
  96. printk("-------%s-------------\n", __FUNCTION__);
  97. //取消地址映射
  98. iounmap(led_dev->reg_virt_base);
  99. //销毁这个设备结点
  100. device_destroy(led_dev->cls, MKDEV(led_dev->dev_major, 0));
  101. class_destroy(led_dev->cls);
  102. //释放这个设备号
  103. unregister_chrdev(led_dev->dev_major, "led_dev_test");
  104. //释放结构体空间
  105. kfree(led_dev);
  106. }
  107. module_init(led_drv_init);
  108. module_exit(led_drv_exit);
  109. MODULE_LICENSE("GPL");
  110. static int kernel_val = 555;
  111. // read(fd, buf, size);
  112. ssize_t led_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
  113. {
  114. //printk("-------%s-------\n", __FUNCTION__);
  115. int ret;
  116. ret = copy_to_user(buf, &kernel_val, count);
  117. if(ret > 0)
  118. {
  119. printk("copy_to_user error\n");
  120. return -EFAULT;
  121. }
  122. return 0;
  123. }
  124. ssize_t led_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
  125. {
  126. //printk("-------%s-------\n", __FUNCTION__);
  127. int ret;
  128. int value;
  129. ret = copy_from_user(&value, buf, count);
  130. if(ret > 0)
  131. {
  132. printk("copy_to_user error\n");
  133. return -EFAULT;
  134. }
  135. if(value){
  136. writel( readl(led_dev->reg_virt_base + 4) | (1<<7), led_dev->reg_virt_base + 4 );
  137. }else{
  138. writel( readl(led_dev->reg_virt_base + 4) & ~(1<<7), led_dev->reg_virt_base + 4 );
  139. }
  140. return 0;
  141. }
  142. int led_drv_open(struct inode *inode, struct file *filp)
  143. {
  144. printk("-------%s-------\n", __FUNCTION__);
  145. return 0;
  146. }
  147. int led_drv_close(struct inode *inode, struct file *filp)
  148. {
  149. printk("-------%s-------\n", __FUNCTION__);
  150. return 0;
  151. }

 

查看实验结果

修改应用程序,识别设备结点为 /dev/led0

编译并移动文件到nfs根目录


  
  1. root@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make
  2. root@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make install

开发板加载模块,执行应用程序

[root@farsight drv_module]# ls
chr_drv.ko  chr_test    led_drv.ko  led_test
[root@farsight drv_module]# insmod led_drv.ko
[ 5097.315000] -------led_drv_init-------------
[root@farsight drv_module]# ./led_test
[ 5104.010000] -------led_drv_open-------

 

又可以观察开发板上led是闪烁状态了。

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/84072823

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。