• Linux驱动开发 通过字符设备驱动分步注册方式编写LED驱动


    通过字符设备驱动分步注册方式编写LED驱动,完成设备文件和设备的绑定:

    head.h:

    1. #ifndef __HEAD_H__
    2. #define __HEAD_H__
    3. typedef struct{
    4. unsigned int MODER;
    5. unsigned int OTYPER;
    6. unsigned int OSPEEDR;
    7. unsigned int PUPDR;
    8. unsigned int IDR;
    9. unsigned int ODR;
    10. }gpio_t;
    11. #define PHY_LED1_ADDR 0X50006000
    12. #define PHY_LED2_ADDR 0X50007000
    13. #define PHY_LED3_ADDR 0X50006000
    14. #define PHY_RCC_ADDR 0X50000A28
    15. //构建LED开关的功能码,不添加ioctl第三个参数
    16. #define LED_ON _IO('l',1)
    17. #define LED_OFF _IO('l',0)
    18. #endif

    Makefile:

    1. modname?=mychrdev
    2. arch?=arm
    3. ifeq ($(arch),arm)
    4. KERNELDIR:=/home/ubuntu/FSMP1A/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61 #用于编译生成ARM架构的模块
    5. else
    6. KERNELDIR :=/lib/modules/$(shell uname -r )/build #用于生产x86架构的模块
    7. endif
    8. #定义变量保存模块化编译的文件路径
    9. PWD:=$(shell pwd)
    10. all:
    11. #M=$(PWD)指定模块化编译的路径
    12. make -C $(KERNELDIR) M=$(PWD) modules
    13. clean: #编译清除
    14. make -C $(KERNELDIR) M=$(PWD) clean
    15. obj-m:=$(modname).o #指定将demo.o独立连接生产内核模块文件

    LED.c:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include"head.h"
    9. struct cdev *cdev;
    10. gpio_t *vir_led1;
    11. gpio_t *vir_led2;
    12. gpio_t *vir_led3;
    13. unsigned int *vir_rcc;
    14. char kbuf[128]={0};
    15. unsigned int major=0;
    16. unsigned int minor=0;
    17. dev_t devno;
    18. module_param(major,uint,0664); //方便在命令行传递major值
    19. struct class *cls;
    20. struct device *dev;
    21. int mycdev_open(struct inode *inode, struct file *file)
    22. {
    23. int min=MINOR(inode->i_rdev); //根据打开的文件对应的设备号获取次设备号
    24. file->private_data=(void *)min;
    25. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    26. return 0;
    27. }
    28. long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    29. {
    30. int min=(int)file->private_data;
    31. switch(min)
    32. {
    33. case 0: //控制LED1
    34. switch(cmd)
    35. {
    36. case LED_ON:
    37. //开灯
    38. vir_led1->ODR |= 1 << 10;
    39. break;
    40. case LED_OFF:
    41. //关灯
    42. vir_led1->ODR &= (~(1 << 10));
    43. break;
    44. }
    45. break;
    46. case 1: //控制LED2
    47. switch(cmd)
    48. {
    49. case LED_ON:
    50. //开灯
    51. vir_led2->ODR |= 1 << 10;
    52. break;
    53. case LED_OFF:
    54. //关灯
    55. vir_led2->ODR &= (~(1 << 10));
    56. break;
    57. }
    58. break;
    59. case 2: //控制LED3
    60. switch(cmd)
    61. {
    62. case LED_ON:
    63. //开灯
    64. vir_led3->ODR |= 1 << 8;
    65. break;
    66. case LED_OFF:
    67. //关灯
    68. vir_led3->ODR &= (~(1 << 8));
    69. break;
    70. }
    71. break;
    72. }
    73. return 0;
    74. }
    75. int mycdev_close(struct inode *inode, struct file *file)
    76. {
    77. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    78. return 0;
    79. }
    80. //定义一个操作方法结构体对象并且初始化
    81. struct file_operations fops = {
    82. .open=mycdev_open,
    83. .unlocked_ioctl = mycdev_ioctl,
    84. .release=mycdev_close,
    85. };
    86. int all_led_init(void)
    87. {
    88. //寄存器地址的映射
    89. vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
    90. if(vir_led1 == NULL)
    91. {
    92. printk("ioremap filed: %d\n",__LINE__);
    93. return -ENOMEM;
    94. }
    95. vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
    96. if(vir_led2 == NULL)
    97. {
    98. printk("ioremap filed: %d\n",__LINE__);
    99. return -ENOMEM;
    100. }
    101. vir_led3 = vir_led1;
    102. vir_rcc = ioremap(PHY_RCC_ADDR,4);
    103. if(vir_rcc == NULL)
    104. {
    105. printk("ioremap filed: %d\n",__LINE__);
    106. return -ENOMEM;
    107. }
    108. printk("物理地址映射成功\n");
    109. //寄存器的初始化
    110. //RCC
    111. (*vir_rcc) |= (0x3 << 4);
    112. //LED1
    113. vir_led1->MODER &= (~(3 << 20));
    114. vir_led1->MODER |= (1 << 20);
    115. vir_led1->ODR &= (~(1 << 10));
    116. //LED2
    117. vir_led2->MODER &= (~(3 << 20));
    118. vir_led2->MODER |= (1 << 20);
    119. vir_led2->ODR &= (~(1 << 10));
    120. //LED3
    121. vir_led3->MODER &= (~(3 << 16));
    122. vir_led3->MODER |= (1 << 16);
    123. vir_led3->ODR &= (~(1 << 8));
    124. printk("寄存器初始化成功\n");
    125. return 0;
    126. }
    127. static int __init mycdev_init(void)
    128. {
    129. int ret;
    130. cdev = cdev_alloc(); //申请一个字符设备驱动对象空间
    131. if(cdev==NULL)
    132. {
    133. printk("字符设备驱动对象申请空间失败\n");
    134. ret = -EFAULT;
    135. goto out1;
    136. }
    137. printk("申请对象空间成功\n");
    138. cdev_init(cdev, &fops); //字符设备驱动对象初始化
    139. if(major>0) //静态指定设备号
    140. {
    141. ret=register_chrdev_region(MKDEV(major,minor),3,"myled");
    142. if(ret)
    143. {
    144. printk("静态申请设备号失败\n");
    145. goto out2;
    146. }
    147. }
    148. else if(major==0) //动态申请设备号
    149. {
    150. ret=alloc_chrdev_region(&devno,minor,3,"myled");
    151. if(ret)
    152. {
    153. printk("动态申请设备号失败\n");
    154. goto out2;
    155. }
    156. major=MAJOR(devno); //获取主设备号
    157. minor=MINOR(devno); //获取次设备号
    158. }
    159. printk("申请设备号成功\n");
    160. //注册字符设备驱动对象
    161. ret=cdev_add(cdev,MKDEV(major,minor),3);
    162. if(ret)
    163. {
    164. printk("注册字符设备驱动对象失败\n");
    165. goto out3;
    166. }
    167. printk("注册字符设备驱动对象成功\n");
    168. //向上提交目录信息
    169. cls=class_create(THIS_MODULE,"myled");
    170. if(IS_ERR(cls))
    171. {
    172. printk("向上提交目录失败\n");
    173. ret=-PTR_ERR(cls);
    174. goto out4;
    175. }
    176. printk("向上提交目录成功\n");
    177. //向上提交设备节点信息
    178. int i;
    179. for(i=0;i<3;i++)
    180. {
    181. dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
    182. if(IS_ERR(dev))
    183. {
    184. printk("向上提交设备节点信息失败\n");
    185. ret=-PTR_ERR(dev);
    186. goto out5;
    187. }
    188. }
    189. printk("向上提交设备信息成功\n");
    190. return 0;
    191. out5:
    192. //释放前一次提交成功的设备信息
    193. for(--i;i>=0;i--)
    194. {
    195. device_destroy(cls,MKDEV(major,i));
    196. }
    197. class_destroy(cls);//释放目录
    198. out4:
    199. cdev_del(cdev);
    200. out3:
    201. unregister_chrdev_region(MKDEV(major,minor),3);
    202. out2:
    203. kfree(cdev);
    204. out1:
    205. return ret;
    206. }
    207. static void __exit mycdev_exit(void)
    208. {
    209. //释放节点信息
    210. int i;
    211. for(i=0;i<3;i++)
    212. {
    213. device_destroy(cls,MKDEV(major,i));
    214. }
    215. //销毁目录
    216. class_destroy(cls);
    217. //取消地址映射
    218. iounmap(vir_led1);
    219. iounmap(vir_led2);
    220. iounmap(vir_rcc);
    221. //注销驱动对象
    222. cdev_del(cdev);
    223. //释放设备号
    224. unregister_chrdev_region(MKDEV(major,minor),3);
    225. //释放对象空间
    226. kfree(cdev);
    227. }
    228. module_init(mycdev_init);
    229. module_exit(mycdev_exit);
    230. MODULE_LICENSE("GPL");

  • 相关阅读:
    重学SpringBoot3-内容协商机制
    文件下载Blob
    生产环境_移动目标轨迹压缩应用和算法处理-Douglas-Peucker轨迹压缩算法
    el-button禁用后,按钮上el-tooltip的内容无法触发的问题
    获取IP地址-根据IP获取位置信息
    iperf+natapp做4g模块网络带宽测试
    用于Python降维的线性判别分析
    网络面试-卧槽!牛皮了,居然把TCP三次握手四次挥手讲的这么详细
    九章云极DataCanvas大模型系列成果发布会重磅来袭,诚邀见证!
    wife_wife【web 攻防世界】
  • 原文地址:https://blog.csdn.net/Satellitex/article/details/132863384