• 通过inode结构体取到次设备号,实现LED灯的亮灭


    对Linux来说,设备驱动也是文件。驱动控制硬件的过程,实际上是对驱动文件的读写操作。

    对内核来说,如何获取唯一的文件标识呢?当然是通过file结构体中的,inode结构体识别应用层打开的到底是哪一个设备文件。

     实验操作及要求:

    串口工具进行输入:
    echo 1 > /dev/myled0 ---->led1灯点亮
    echo 0 > /dev/myled0 ---->led1灯熄灭
    echo 1 > /dev/myled1 ---->led1灯点亮
    echo 0 > /dev/myled1 ---->led1灯熄灭
    echo 1 > /dev/myled2 ---->led1灯点亮
    echo 0 > /dev/myled2 ---->led1灯熄灭
    linux@ubuntu:/sys/class/myled$ ls /dev/myled* -ll

    crw------- 1 root root 236, 0 Nov 18 14:55 /dev/myled0 ----->控制PE10(LED1)
    crw------- 1 root root 236, 1 Nov 18 14:55 /dev/myled1----->控制PF10(LED2)
    crw------- 1 root root 236, 2 Nov 18 14:55 /dev/myled2----->控制PE8(LED3)

    2.驱动:
    open:在open函数中获取到次设备号,用私有数据传参,传递给write函数
    write:在write函数,判断次设备号,就知道操作的是哪盏灯

    3.要求:
    1)分部实现注册字符设备驱动
    2)自动创建设备节点
    3)通过结构体对led灯地址进行映射
    4)次设备号完成私有数据传参
     

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include "led.h"
    10. #define CNAME "myled"
    11. unsigned int major=0;
    12. int minor=0;
    13. struct class *cls;
    14. //指向字符设备号
    15. struct device *dev;
    16. const int count=3;
    17. //指向字符设备驱动结构体
    18. struct cdev *cdev;
    19. volatile unsigned int* virt_rcc;
    20. volatile gpio_t* virt_gpioe;
    21. volatile gpio_t* virt_gpiof;
    22. int mycdev_open(struct inode *inode,struct file *file)
    23. {
    24. //取出次设备号
    25. int mino;
    26. mino=MINOR(inode->i_rdev);
    27. //将次设备号放到file结构体私有数据中
    28. file->private_data = (void*)mino;
    29. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    30. return 0;
    31. }
    32. ssize_t mycdev_read(struct file*file,char __user *ubuf,size_t size,loff_t *loffs)
    33. {
    34. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    35. return 0;
    36. }
    37. //ssize_t mycdev_write(struct file *file,char __user *ubuf,size_t size,loff_t *loff)错误写法
    38. ssize_t mycdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
    39. {
    40. //私有数据传参
    41. //读取用户空间数据到内核(传输数据以内核空间大小为准)
    42. char kbuf[128]="0";
    43. int ret=0;
    44. if(size>sizeof(kbuf)) size= sizeof(kbuf);
    45. ret=copy_from_user(kbuf,ubuf,size);
    46. if(ret)
    47. {
    48. printk("copy_from_user is failed\n");
    49. return -EIO;
    50. }
    51. //取出次设备号
    52. minor = (int)file->private_data;
    53. switch(minor)
    54. {
    55. case LED1:
    56. if(kbuf[0]=='0')
    57. {
    58. virt_gpioe->ODR &=(~(0x1<<10));
    59. printk("LED1 on\n");
    60. }else
    61. {
    62. virt_gpioe->ODR |=(0x1<<10);
    63. printk("LED1 off\n");
    64. }
    65. break;
    66. case LED2:
    67. if(kbuf[0]=='0')
    68. {
    69. virt_gpiof->ODR &=(~(0x1<<10));
    70. printk("LED2 on\n");
    71. }else{
    72. virt_gpiof->ODR |=(0x1<<10);
    73. printk("LED2 off\n");
    74. }
    75. break;
    76. case LED3:
    77. if(kbuf[0]=='0')
    78. {
    79. virt_gpioe->ODR &=(~(0x1<<8));
    80. printk("LED3 on\n");
    81. }
    82. else
    83. virt_gpioe->ODR |=(0x1<<8);
    84. break;
    85. }
    86. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    87. //此处返回从用户空间获取到的大小,如果获取为0;copy_from_user会一直等待
    88. return size;
    89. }
    90. int mycdev_close(struct inode* inode,struct file *file)
    91. {
    92. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    93. return 0;
    94. }
    95. const struct file_operations fops={
    96. .open=mycdev_open,
    97. .read=mycdev_read,
    98. .write=mycdev_write,
    99. .release=mycdev_close,
    100. };
    101. static int __init mycdev_init(void)
    102. {
    103. int ret;
    104. dev_t devno;
    105. int i;
    106. //1.分步注册字符设备驱动
    107. //1)分配dev结构体
    108. cdev=cdev_alloc();
    109. if(NULL==cdev)
    110. {
    111. printk("cdev alloc is failed\n");
    112. return -EIO;
    113. }
    114. //2)初始化cdev结构体
    115. cdev_init(cdev,&fops);
    116. //3)动态申请设备号,成功返回0,失败返回错误码
    117. ret=alloc_chrdev_region(&devno,0,count,CNAME);
    118. if(ret)
    119. {
    120. printk("alloc_chrdev_region is failed\n");
    121. return -ENOMEM;
    122. }
    123. //成功申请到设备号,调函数获取主次设备号(12+20)
    124. major=MAJOR(devno);
    125. minor=MINOR(devno);
    126. //3)驱动注册
    127. ret=cdev_add(cdev,MKDEV(major,minor),count);
    128. if(ret)
    129. {
    130. printk("dev add is failed\n");
    131. return -EIO;
    132. }
    133. //2.自动创建设备节点
    134. //1)向上层提交目录信息
    135. cls=class_create(THIS_MODULE,CNAME);
    136. if(IS_ERR(cls))
    137. {
    138. return PTR_ERR(cls);
    139. }
    140. //2.向上层提交设备节点
    141. for(i=0;i
    142. {
    143. dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
    144. if(IS_ERR(dev))
    145. {
    146. return PTR_ERR(dev);
    147. }
    148. }
    149. //3.通过结构体对LED灯进行映射
    150. //将GPIOE和GPIOF物理地址映射为虚拟地址
    151. //1)将rcc地址进行映射
    152. virt_rcc=ioremap(PHY_RCC,4);
    153. if(NULL==virt_rcc)
    154. {
    155. printk("rcc ioremap failed\n");
    156. return -ENOMEM;
    157. }
    158. //2)将gpioe地址进行映射
    159. virt_gpioe=ioremap(PHY_GPIOE,sizeof(gpio_t));
    160. if(NULL==virt_gpioe)
    161. {
    162. printk("virt_gpioe ioremap failed\n");
    163. return -ENOMEM;
    164. }
    165. //2)将gpiof地址进行映射
    166. virt_gpiof=ioremap(PHY_GPIOF,sizeof(gpio_t));
    167. if(NULL==virt_gpiof)
    168. {
    169. printk("virt_gpiof ioremap failed\n");
    170. return -ENOMEM;
    171. }
    172. //灯的初始化
    173. //LED1--PE10
    174. *virt_rcc |= (0x1<<4);
    175. virt_gpioe->MODER &= (~(0x3<<20));
    176. virt_gpioe->MODER |= (0x1<<20);
    177. //设置默认输出低电平
    178. virt_gpioe->ODR |= (~(0x1<<10));
    179. //LED2--PF10
    180. *virt_rcc |= (0x1<<5);
    181. virt_gpiof->MODER &= (~(0x3<<20));
    182. virt_gpiof->MODER |= (0x1<<20);
    183. virt_gpiof->ODR &= (~(0x1<<10));
    184. //LED3--PE8
    185. virt_gpioe->MODER &= (~(0x3<<16));
    186. virt_gpioe->MODER |= (0x1<<16);
    187. virt_gpioe->ODR &= (~(0x1<<8));
    188. return 0;
    189. }
    190. static void __exit mycdev_exit(void)
    191. {
    192. int i;
    193. iounmap(virt_rcc);
    194. iounmap(virt_gpioe);
    195. iounmap(virt_gpiof);
    196. //销毁节点
    197. for(i=0;i
    198. {
    199. device_destroy(cls,MKDEV(major,i));
    200. }
    201. //销毁目录信息
    202. class_destroy(cls);
    203. //驱动注销
    204. cdev_del(cdev);
    205. //注销设备号
    206. unregister_chrdev_region(MKDEV(major,minor),count);
    207. //5.释放dev结构体
    208. kfree(cdev);
    209. }
    210. module_init(mycdev_init);
    211. module_exit(mycdev_exit);
    212. MODULE_LICENSE("GPL");

     测试:

    1.查看到3个设备节点

     case判断条件写反了,所以结果显示是反的。

  • 相关阅读:
    Python入坑系列-pyside6桌面编程之认识并设置理想字体效果
    PHP生成图形验证码
    Spring项目整合 XXL-JOB分布式任务调度平台
    Java SE 9 多版本兼容 JAR 包示例
    web安全最亲密的战友Burp Suite—网络攻防常用工具介绍--burp suit工具初体验一
    掌握AI助手的魔法工具:解密`Prompt`(提示)在AIGC时代的应用(下篇)
    错字修改 | 布署1个中文文文本拼蟹纠错模型
    【Java杂谈】#1 【MCA JAVA后端架构师】
    玩机教程:阿里云无影云电脑怎么使用?
    企业级大数据平台智能运维好帮手——星环科技多模数据平台监控软件Aquila Insight
  • 原文地址:https://blog.csdn.net/m0_68004652/article/details/127955728