• 驱动开发day4


    通过字符设备驱动的分步实现编写LED驱动,另外实现设备文件和驱动的绑定

    head.h

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

    test.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include "head.h"
    10. int main(int argc, char const *argv[])
    11. {
    12. char buf[128]={0};
    13. int a,b;
    14. int fd=open("/dev/mycdev1",O_RDWR);
    15. if(fd<0)
    16. {
    17. printf("打开设备文件失败\n");
    18. exit(-1);
    19. }
    20. while(1)
    21. {
    22. //从终端读取
    23. printf("请输入要实现的功能 ");
    24. printf("0(关灯) 1(开灯)\n");
    25. printf("请输入>");
    26. scanf("%d",&a);
    27. switch(a)
    28. {
    29. case 1:
    30. ioctl(fd,LED_ON);
    31. break;
    32. case 0:
    33. ioctl(fd,LED_OFF);
    34. break;
    35. }
    36. }
    37. close(fd);
    38. return 0;
    39. }

    mycdev.c

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

  • 相关阅读:
    Linux安装redis数据库
    温敏性N-异丙基丙烯酰胺(NIPA)和pH敏感性丙烯酸(AA)接枝纳米聚苯乙烯微球相关研究
    机械工程师和电气工程师方向哪个前景比较好?
    LeetCode 230.二叉搜索树中第K小的元素
    C#基础笔记
    深度学习推荐系统--协同过滤推荐算法+实现代码
    Java经典问题解答(9题)
    git everything up-to-date解决方法
    【无标题】
    【保姆级】lookup-method标签实践与分析
  • 原文地址:https://blog.csdn.net/Meng_fighting/article/details/133999305