• linux驱动开发--day4(字符设备驱动注册内部流程、及实现备文件和设备的绑定下LED灯实验)


    一、字符设备驱动注册的内部过程

    1.分配struct cdev对象空间

    2.初始化struct cdev对象

    3.注册cdev对象

    二、注册字符设备驱动分步实现

    1.分配字符设备驱动对象

    2.字符设备驱动对象初始化

    3.设备号的申请

    4.根据申请的设备号和驱动对象注册驱动

    三、open函数回调驱动中操作方法open的路线

    1.应用层打开文件系统中的存在文件,会有inode号,且系统内核中就会存在一个inode对象(struct inode)保存文件相关信息

    2.inode对象里字符设备结构体成员(struct cdev)里,有操作方法结构体成员(struct file_operations *ops)

    3.操作方法结构体中会调用VFS(虚拟文件系统层)中sys_open()得到文件描述符,申请file对象并填充,根据路线再回调驱动中的open操作方法

    四、通过文件描述符回调驱动操作的路线

    1.因为文件描述符依赖进程存在,只要进程加载到系统上,系统内核就存在task_struct对象描述进程信息

    2.在进程里打开一个文件,内核就会存在一个struct file对象,用于保存打开文件的信息

    3.struct file对象的成员里fd_array数组保存的每个成员是打开的文件的相关信息

    4.数组每个成员都是struct file类型,存储的是打开文件的相关信息,里面有操作方法结构体(驱动中的操作方法)去驱使硬件实现特定功能

    任务:

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

    1. #include <linux/init.h>
    2. #include <linux/module.h>
    3. #include<linux/cdev.h>
    4. #include<linux/fs.h>
    5. #include<linux/device.h>
    6. #include<linux/uaccess.h>
    7. #include<linux/slab.h>
    8. #include<linux/io.h>
    9. #include"head.h"
    10. struct cdev* cdev;
    11. unsigned int major=0;
    12. unsigned int minor=0;
    13. dev_t devno;
    14. module_param(major,uint,0664);
    15. struct class* cls;
    16. struct device* dev;
    17. gpio_t *vir_led1;
    18. gpio_t *vir_led2;
    19. gpio_t *vir_led3;
    20. unsigned int *vir_rcc;
    21. //封装操作的方法
    22. int mycdev_open(struct inode *inode, struct file *file)
    23. {
    24. int min=MINOR(inode->i_rdev);
    25. file->private_data=(void *)min;
    26. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    27. return 0;
    28. }
    29. long mydev_ioctl(struct file* file,unsigned int cmd,unsigned long arg)
    30. {
    31. int min=(int)file->private_data;
    32. switch(min)
    33. {
    34. case 0: //控制LED1
    35. switch(cmd)
    36. {
    37. case LED_ON: //开灯
    38. vir_led1->ODR |= 1<<10;
    39. break;
    40. case LED_OFF: //关灯
    41. vir_led1->ODR &= (~(1<<10));
    42. break;
    43. }
    44. break;
    45. case 1: //控制LED2
    46. switch(cmd)
    47. {
    48. case LED_ON: //开灯
    49. vir_led2->ODR |= 1<<10;
    50. break;
    51. case LED_OFF: //关灯
    52. vir_led2->ODR &= (~(1<<10));
    53. break;
    54. }
    55. break;
    56. case 2: //控制LED3
    57. switch(cmd)
    58. {
    59. case LED_ON: //开灯
    60. vir_led3->ODR |= 1<<8;
    61. break;
    62. case LED_OFF: //关灯
    63. vir_led3->ODR &= (~(1<<8));
    64. break;
    65. }
    66. break;
    67. }
    68. return 0;
    69. }
    70. int mycdev_close(struct inode *inode, struct file *file)
    71. {
    72. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    73. return 0;
    74. }
    75. //定义操作方法结构体变量并赋值
    76. struct file_operations fops={
    77. .open=mycdev_open,
    78. .unlocked_ioctl=mydev_ioctl,
    79. .release=mycdev_close,
    80. };
    81. int all_led_init(void)
    82. {
    83. //寄存器地址的映射
    84. vir_led1=ioremap(PHY_LED1_ADDR,sizeof(gpio_t));
    85. if(vir_led1==NULL)
    86. {
    87. printk("ioremap filed:%d\n",__LINE__);
    88. return -ENOMEM;
    89. }
    90. vir_led2=ioremap(PHY_LED2_ADDR,sizeof(gpio_t));
    91. if(vir_led2==NULL)
    92. {
    93. printk("ioremap filed:%d\n",__LINE__);
    94. return -ENOMEM;
    95. }
    96. vir_led3=vir_led1;
    97. vir_rcc=ioremap(PHY_RCC_ADDR,4);
    98. if(vir_rcc==NULL)
    99. {
    100. printk("ioremap filed:%d\n",__LINE__);
    101. return -ENOMEM;
    102. }
    103. printk("物理地址映射成功\n");
    104. //寄存器的初始化
    105. //rcc
    106. (*vir_rcc) |= (3<<4);
    107. //led1
    108. vir_led1->MODER &= (~(3<<20));
    109. vir_led1->MODER |= (1<<20);
    110. vir_led1->ODR &= (~(1<<10));
    111. //led2
    112. vir_led2->MODER &= (~(3<<20));
    113. vir_led2->MODER |= (1<<20);
    114. vir_led2->ODR &= (~(1<<10));
    115. //led3
    116. vir_led3->MODER &= (~(3<<16));
    117. vir_led1->MODER |= (1<<16);
    118. vir_led1->ODR &= (~(1<<8));
    119. printk("寄存器初始化成功\n");
    120. return 0;
    121. }
    122. static int __init mycdev_init(void)
    123. {
    124. int ret;
    125. //为字符设备驱动对象申请空间
    126. cdev=cdev_alloc();
    127. if(cdev==NULL)
    128. {
    129. printk("字符设备驱动对象申请空间失败\n");
    130. ret=-EFAULT;
    131. goto out1;
    132. }
    133. printk("申请对象空间成功\n");
    134. //初始化字符设备驱动对象
    135. cdev_init(cdev,&fops);
    136. //申请设备号
    137. if(major>0)//静态指定设备号
    138. {
    139. ret=register_chrdev_region(MKDEV(major,minor),3,"myled");
    140. if(ret)
    141. {
    142. printk("静态申请设备号失败\n");
    143. goto out2;
    144. }
    145. }
    146. else if(major==0)//动态申请设备号
    147. {
    148. ret=alloc_chrdev_region(&devno,minor,3,"myled");
    149. if(ret)
    150. {
    151. printk("动态申请设备号失败\n");
    152. goto out2;
    153. }
    154. major=MAJOR(devno);//获取主设备号
    155. minor=MINOR(devno);//获取次设备号
    156. }
    157. printk("申请设备号成功\n");
    158. //注册字符设备驱动对象
    159. ret=cdev_add(cdev,MKDEV(major,minor),3);
    160. if(ret)
    161. {
    162. printk("注册字符设备驱动对象失败\n");
    163. goto out3;
    164. }
    165. printk("注册字符设备驱动对象成功\n");
    166. //寄存器映射以及初始化
    167. all_led_init();
    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. iounmap(vir_led1);
    211. iounmap(vir_led2);
    212. iounmap(vir_rcc);
    213. //释放节点信息
    214. int i;
    215. for(i=0;i<3;i++)
    216. {
    217. device_destroy(cls,MKDEV(major,i));
    218. }
    219. //销毁目录
    220. class_destroy(cls);
    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");
    test.c:
    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. #include <fcntl.h>
    5. #include <unistd.h>
    6. #include <stdlib.h>
    7. #include <string.h>
    8. #include <sys/ioctl.h>
    9. #include "head.h"
    10. int main(int argc, const char *argv[])
    11. {
    12. char buf[128] = "";
    13. int a;
    14. int fd;
    15. while (1)
    16. {
    17. printf("请选择要打开的灯(1,2,3)\n");
    18. scanf(" %d", &a);
    19. switch (a)
    20. {
    21. case 1:
    22. fd = open("/dev/myled0", O_RDWR);
    23. if (fd < 0)
    24. {
    25. printf("设备文件打开失败\n");
    26. exit(-1);
    27. }
    28. printf("打开文件myled0成功\n");
    29. break;
    30. case 2:
    31. fd = open("/dev/myled1", O_RDWR);
    32. if (fd < 0)
    33. {
    34. printf("设备文件打开失败\n");
    35. exit(-1);
    36. }
    37. printf("打开文件myled1成功\n");
    38. break;
    39. case 3:
    40. fd = open("/dev/myled2", O_RDWR);
    41. if (fd < 0)
    42. {
    43. printf("设备文件打开失败\n");
    44. exit(-1);
    45. }
    46. printf("打开文件myled2成功\n");
    47. break;
    48. default:
    49. printf("请输入范围内的数\n");
    50. }
    51. int b;
    52. printf("请开灯关灯(0/1)\n");
    53. scanf(" %d",&b);
    54. switch(b)
    55. {
    56. case 1:
    57. ioctl(fd,LED_ON);
    58. break;
    59. case 0:
    60. ioctl(fd,LED_OFF);
    61. break;
    62. default:
    63. printf("请输入'0'或'1'\n");
    64. }
    65. close(fd);
    66. printf("关闭文件\n");
    67. }
    68. return 0;
    69. }
    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开关功能码
    16. #define LED_ON _IO('l',1)
    17. #define LED_OFF _IO('l',0)
    18. #endif
    测试现象:

  • 相关阅读:
    springboot 引入nacos
    【计算机视觉(CV)】基于高层API实现宝石分类
    【java学习—八】==操作符与equals方法(2)
    网络编程与HTTP协议
    4创建iSCSI共享存储
    VoIP Push 在海外音视频业务中的应用
    【HDU No. 4902】 数据结构难题 Nice boat
    GMSL自学笔记
    react设置代理
    【服务器数据恢复】RAID6中3块磁盘离线崩溃的数据恢复案例
  • 原文地址:https://blog.csdn.net/m0_72852022/article/details/132858507