• 【驱动开发】创建设备节点、ioctl函数的使用


    一、控制三盏灯的亮灭

    头文件:
    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. #endif
    驱动程序:
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include"head.h"
    6. int major;
    7. char kbuf[128]={0};
    8. gpio_t *vir_led1;
    9. gpio_t *vir_led2;
    10. gpio_t *vir_led3;
    11. unsigned int *vir_rcc;
    12. int mycdev_open(struct inode *inode, struct file *file)
    13. {
    14. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    15. return 0;
    16. }
    17. ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
    18. {
    19. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    20. unsigned long ret;
    21. //向用户空间读取拷贝
    22. if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
    23. size=sizeof(kbuf);
    24. ret=copy_to_user(ubuf,kbuf,size);
    25. if(ret)//拷贝失败
    26. {
    27. printk("copy_to_user filed\n");
    28. return ret;
    29. }
    30. return 0;
    31. }
    32. ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
    33. {
    34. unsigned long ret;
    35. //从用户空间读取数据
    36. if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
    37. size=sizeof(kbuf);
    38. ret=copy_from_user(kbuf,ubuf,size);
    39. if(ret)//拷贝失败
    40. {
    41. printk("copy_to_user filed\n");
    42. return ret;
    43. }
    44. switch(kbuf[0]){
    45. case '1'://LED1
    46. if(kbuf[1]=='0')//关灯
    47. vir_led1->ODR &= (~(1<<10));
    48. else//开灯
    49. vir_led1->ODR |= 1<<10;
    50. break;
    51. case '2'://LED2
    52. if(kbuf[1]=='0')//关灯
    53. vir_led2->ODR &= (~(1<<10));
    54. else//开灯
    55. vir_led2->ODR |= 1<<10;
    56. break;
    57. case '3'://LED3
    58. if(kbuf[1]=='0')//关灯
    59. vir_led3->ODR &= (~(1<<8));
    60. else//开灯
    61. vir_led3->ODR |= 1<<8;
    62. break;
    63. }
    64. return 0;
    65. }
    66. int mycdev_close(struct inode *inode, struct file *file)
    67. {
    68. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    69. return 0;
    70. }
    71. //定义操作方法结构体变量并赋值
    72. struct file_operations fops={
    73. .open=mycdev_open,
    74. .read=mycdev_read,
    75. .write=mycdev_write,
    76. .release=mycdev_close,
    77. };
    78. int all_led_init(void)
    79. {
    80. //寄存器地址的映射
    81. vir_led1=ioremap(PHY_LED1_ADDR,sizeof(gpio_t));
    82. if(vir_led1==NULL)
    83. {
    84. printk("ioremap filed:%d\n",__LINE__);
    85. return -ENOMEM;
    86. }
    87. vir_led2=ioremap(PHY_LED2_ADDR,sizeof(gpio_t));
    88. if(vir_led2==NULL)
    89. {
    90. printk("ioremap filed:%d\n",__LINE__);
    91. return -ENOMEM;
    92. }
    93. vir_led3=vir_led1;
    94. vir_rcc=ioremap(PHY_RCC_ADDR,4);
    95. if(vir_rcc==NULL)
    96. {
    97. printk("ioremap filed:%d\n",__LINE__);
    98. return -ENOMEM;
    99. }
    100. printk("物理地址映射成功\n");
    101. //寄存器的初始化
    102. //rcc
    103. (*vir_rcc) |= (3<<4);
    104. //led1
    105. vir_led1->MODER &= (~(3<<20));
    106. vir_led1->MODER |= (1<<20);
    107. vir_led1->ODR &= (~(1<<10));
    108. //led2
    109. vir_led2->MODER &= (~(3<<20));
    110. vir_led2->MODER |= (1<<20);
    111. vir_led2->ODR &= (~(1<<10));
    112. //led3
    113. vir_led3->MODER &= (~(3<<16));
    114. vir_led1->MODER |= (1<<16);
    115. vir_led1->ODR &= (~(1<<8));
    116. printk("寄存器初始化成功\n");
    117. return 0;
    118. }
    119. static int __init mycdev_init(void)
    120. {
    121. //字符设备驱动注册
    122. major=register_chrdev(0,"mychrdev",&fops);
    123. if(major<0)
    124. {
    125. printk("字符设备驱动注册失败\n");
    126. return major;
    127. }
    128. printk("字符设备驱动注册成功:major=%d\n",major);
    129. //寄存器映射以及初始化
    130. all_led_init();
    131. return 0;
    132. }
    133. static void __exit mycdev_exit(void)
    134. {
    135. //取消地址映射
    136. iounmap(vir_led1);
    137. iounmap(vir_led2);
    138. iounmap(vir_rcc);
    139. //注销字符设备驱动
    140. unregister_chrdev(major,"mychrdev");
    141. }
    142. module_init(mycdev_init);
    143. module_exit(mycdev_exit);
    144. MODULE_LICENSE("GPL");
    应用程序:
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. int main(int argc, char const *argv[])
    9. {
    10. char buf[128]={0};
    11. int fd=open("/dev/mychrdev",O_RDWR);
    12. if(fd<0)
    13. {
    14. printf("打开设备文件失败\n");
    15. exit(-1);
    16. }
    17. while(1)
    18. {
    19. //从终端读取
    20. printf("请输入两个字符\n");
    21. printf("第一个字符:1(LED1) 2(LED2) 3(LED3)\n");
    22. printf("第二个字符:0(关灯) 1(开灯)\n");
    23. printf("请输入>");
    24. fgets(buf,sizeof(buf),stdin);
    25. buf[strlen(buf)-1]='\0';
    26. //向设备文件中写
    27. write(fd,buf,sizeof(buf));
    28. }
    29. close(fd);
    30. return 0;
    31. }

    二、自动创建设备节点

    1.创建设备节点的机制

    • mknod:手动创建设备节点的命令
    • devfs:linux早期创建设备节点的机制,如今由于各自原因不再使用,它创建设备节点的逻辑存在于内核空间(2.4版本之前)
    • udev机制:目前使用的自动创建设备节点的机制,创建设备节点的逻辑是在用户空间(2.6版本)
    • mdev机制:可以将mdev理解为轻量级的udev,mdev主要应用于一些嵌入式系统中

    2.udev创建设备节点的原理

    3.创建设备节点的相关API

    1. **************创建设备文件相关**********
    2. #include
    3. 1.向上提交目录
    4. struct class *class_create(struct module *owner, const char *name)
    5. 功能:向上提交目录信息,申请了一个struct class对象并初始化
    6. 参数:
    7. owner:指向当前模块自身的一个指针,填写THIS_MODULE
    8. name:向上提交的目录名
    9. 返回值:成功返回申请到的class对象的首地址,失败返回一个指向内核顶层4K空间的指针
    10. /*
    11. 关关于返回值判断问题:
    12. 只要判断指针的数值>4K预留空间起始值就说明函数调用失败
    13. bool __must_check IS_ERR(__force const void *ptr)
    14. //#define IS_ERR_VALUE(x) (unsigned long)(void *)(x) >= 0XFFFFFFF-4095
    15. 功能:判断指针是否指向内核4K预留空间,如果指针指向4K预留空间返回真,否则返回假
    16. long __must_check PTR_ERR(__force const void *ptr)
    17. 功能:将一个指针转换成long类型错误码返回
    18. */
    19. ex:
    20. struct class *cls=class_create(THIS_MODULE,"mychrdev");
    21. if(IS_ERR(cls))
    22. {
    23. printk("向上提交目录失败\n");
    24. return -PTR_ERR(cls);
    25. }
    26. 2.向上提交设备节点信息
    27. struct device *device_create(struct class *class, struct device *parent,
    28. dev_t devt, void *drvdata, const char *fmt, ...)
    29. 功能:向上提交设备节点信息,申请一个struct device对象并初始化
    30. 参数:
    31. class:class_create()得到的对象指针
    32. parent:父节点指针,不知道就填NULL
    33. devt:设备号 主设备号<<20|次设备号
    34. /*
    35. MKDEV(主设备号,次设备号):根据主设备号和次设备号得到设备号
    36. MAJOR(dev):根据设备号得到主设备号
    37. MINOR(dev):根据设备号得到次设备号
    38. */
    39. drvdata:当前对象的一个私有数据,填NULL
    40. fmt:填要创建的设备节点名 video%d
    41. ...:不定长参数 i
    42. 返回值:成功返回创建成功的struct device对象指针,失败返回指针指向4K预留空间
    43. ************删除设备文件相关**********
    44. 销毁节点信息:
    45. void device_destroy(struct class *class, dev_t devt)
    46. 功能:销毁节点信息
    47. 参数:
    48. class:class_create()得到的对象指针
    49. devt:向上提交设备节点是填写的设备号
    50. 销毁目录
    51. void class_destroy(struct class *cls)
    52. 参数:class_create()得到的对象指针
    53. 返回值:无

    4.实例

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include"head.h"
    7. int major;
    8. char kbuf[128]={0};
    9. gpio_t *vir_led1;
    10. gpio_t *vir_led2;
    11. gpio_t *vir_led3;
    12. unsigned int *vir_rcc;
    13. struct class *cls;
    14. struct device *dev;
    15. int mycdev_open(struct inode *inode, struct file *file)
    16. {
    17. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    18. return 0;
    19. }
    20. ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
    21. {
    22. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    23. unsigned long ret;
    24. //向用户空间读取拷贝
    25. if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
    26. size=sizeof(kbuf);
    27. ret=copy_to_user(ubuf,kbuf,size);
    28. if(ret)//拷贝失败
    29. {
    30. printk("copy_to_user filed\n");
    31. return ret;
    32. }
    33. return 0;
    34. }
    35. ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
    36. {
    37. unsigned long ret;
    38. //从用户空间读取数据
    39. if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
    40. size=sizeof(kbuf);
    41. ret=copy_from_user(kbuf,ubuf,size);
    42. if(ret)//拷贝失败
    43. {
    44. printk("copy_to_user filed\n");
    45. return ret;
    46. }
    47. switch(kbuf[0]){
    48. case '1'://LED1
    49. if(kbuf[1]=='0')//关灯
    50. vir_led1->ODR &= (~(1<<10));
    51. else//开灯
    52. vir_led1->ODR |= 1<<10;
    53. break;
    54. case '2'://LED2
    55. if(kbuf[1]=='0')//关灯
    56. vir_led2->ODR &= (~(1<<10));
    57. else//开灯
    58. vir_led2->ODR |= 1<<10;
    59. break;
    60. case '3'://LED3
    61. if(kbuf[1]=='0')//关灯
    62. vir_led3->ODR &= (~(1<<8));
    63. else//开灯
    64. vir_led3->ODR |= 1<<8;
    65. break;
    66. }
    67. return 0;
    68. }
    69. int mycdev_close(struct inode *inode, struct file *file)
    70. {
    71. printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    72. return 0;
    73. }
    74. //定义操作方法结构体变量并赋值
    75. struct file_operations fops={
    76. .open=mycdev_open,
    77. .read=mycdev_read,
    78. .write=mycdev_write,
    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. //字符设备驱动注册
    125. major=register_chrdev(0,"mychrdev",&fops);
    126. if(major<0)
    127. {
    128. printk("字符设备驱动注册失败\n");
    129. return major;
    130. }
    131. printk("字符设备驱动注册成功:major=%d\n",major);
    132. //向上提交目录
    133. cls=class_create(THIS_MODULE,"mychrdev");
    134. if(IS_ERR(cls))
    135. {
    136. printk("向上提交目录失败\n");
    137. return -PTR_ERR(cls);
    138. }
    139. printk("向上提交目录成功\n");
    140. //向上提交设备节点信息
    141. int i;//向上提交三次设备节点信息
    142. for(i=0;i<3;i++)
    143. {
    144. dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
    145. if(IS_ERR(dev))
    146. {
    147. printk("向上提交设备节点失败\n");
    148. return -PTR_ERR(dev);
    149. }
    150. }
    151. printk("向上提交设备节点成功\n");
    152. //寄存器映射以及初始化
    153. all_led_init();
    154. return 0;
    155. }
    156. static void __exit mycdev_exit(void)
    157. {
    158. //取消地址映射
    159. iounmap(vir_led1);
    160. iounmap(vir_led2);
    161. iounmap(vir_rcc);
    162. //销毁设备节点信息
    163. int i;
    164. for(i=0;i<3;i++)
    165. {
    166. device_destroy(cls,MKDEV(major,i));
    167. }
    168. //销毁目录
    169. class_destroy(cls);
    170. //注销字符设备驱动
    171. unregister_chrdev(major,"mychrdev");
    172. }
    173. module_init(mycdev_init);
    174. module_exit(mycdev_exit);
    175. MODULE_LICENSE("GPL");

    三、ioctl函数的使用

    1.引入ioctl函数的意义

    linux操作系统中有意将数据的读写和读写功能的选择分别交给不同的函数去完成。就让read/write函数只进行数据的读写即可,让一些其他功能的设置和选择交给ioctl函数来实现。比如,串口通信时,需要设置波特率,需要设置数据格式,也需要最终选择数据收发,让这些都由ioctl函数来完成。让read()write()只进行串口数据收发即可。

    2.ioctl函数的分析

    1. *********系统调用函数的分析**********
    2. #include
    3. int ioctl(int fd, unsigned long request, ...);
    4. 功能:进行io功能的设置
    5. 参数:
    6. fd:文件描述符
    7. request:io控制的功能码
    8. ...:可以加,也可以不加。如果第三个参数传递数值,只能传递整型数据和指针
    9. 返回值:成功返回0,失败返回错误码
    10. *********驱动中操作方法的分析********
    11. long (*unlocked_ioctl) (struct file *file, unsigned int cmd, unsigned long arg)
    12. {
    13. 参数分析:
    14. file:文件指针
    15. cmd:应用程序中的ioctl第二个参数传递过来
    16. arg:应用程序中的ioctl第三个参数传递过来
    17. }

    3.ioctl功能码的构建

    为了让实现不同功能的功能码尽量不一样,我们对功能码进行了编码

    查询内核的说明手册:~/linux-5.10.61/Documentation/userspace-api/ioctl

    vi ioctl-decoding.rst//功能码的编码说明文档

    1. ====== ==================================
    2. bits meaning
    3. ====== ==================================
    4. 31-30 00 - no parameters: uses _IO macro
    5. 10 - read: _IOR
    6. 01 - write: _IOW
    7. 11 - read/write: _IOWR
    8. 29-16 size of arguments
    9. 15-8 ascii character supposedly
    10. unique to each driver
    11. 7-0 function #
    12. ====== ==================================

    1. #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
    2. #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
    3. #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
    4. #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)
    5. #define _IOC(dir,type,nr,size) \
    6. ((unsigned int) \
    7. (((dir) << _IOC_DIRSHIFT) | \
    8. ((type) << _IOC_TYPESHIFT) | \
    9. ((nr) << _IOC_NRSHIFT) | \
    10. ((size) << _IOC_SIZESHIFT)))
    11. ex:构建LED开关的功能码:ioctl函数无第三个参数
    12. 开灯 #define LED_ON _IO('l',1)
    13. 关灯 #define LED_OFF _IO('l',0)
    14. ex:构建LED开关的功能码:ioctl函数有第三个参数
    15. 开灯 #define LED_ON _IOW('l',1,int)
    16. 关灯 #define LED_OFF _IOW('l',0,int)

    4.ioctl实例----不传递第三个参数

    头文件:
    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
    驱动程序:
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include "head.h"
    7. int major;
    8. char kbuf[128] = {0};
    9. gpio_t *vir_led1;
    10. gpio_t *vir_led2;
    11. gpio_t *vir_led3;
    12. unsigned int *vir_rcc;
    13. struct class *cls;
    14. struct device *dev;
    15. int mycdev_open(struct inode *inode, struct file *file)
    16. {
    17. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    18. return 0;
    19. }
    20. long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    21. {
    22. switch (cmd)
    23. {
    24. case LED_ON: // 开灯
    25. vir_led1->ODR |= (0X1 << 10);
    26. vir_led2->ODR |= (0X1 << 10);
    27. vir_led3->ODR |= (0X1 << 8);
    28. break;
    29. case LED_OFF: // 关灯
    30. vir_led1->ODR &= (~(0X1 << 10));
    31. vir_led2->ODR &= (~(0X1 << 10));
    32. vir_led3->ODR &= (~(0X1 << 8));
    33. break;
    34. }
    35. return 0;
    36. }
    37. int mycdev_close(struct inode *inode, struct file *file)
    38. {
    39. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    40. return 0;
    41. }
    42. // 定义操作方法结构体变量并赋值
    43. struct file_operations fops = {
    44. .open = mycdev_open,
    45. .unlocked_ioctl = mycdev_ioctl,
    46. .release = mycdev_close,
    47. };
    48. int all_led_init(void)
    49. {
    50. // 寄存器地址的映射
    51. vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
    52. if (vir_led1 == NULL)
    53. {
    54. printk("ioremap filed:%d\n", __LINE__);
    55. return -ENOMEM;
    56. }
    57. vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
    58. if (vir_led2 == NULL)
    59. {
    60. printk("ioremap filed:%d\n", __LINE__);
    61. return -ENOMEM;
    62. }
    63. vir_led3 = vir_led1;
    64. vir_rcc = ioremap(PHY_RCC_ADDR, 4);
    65. if (vir_rcc == NULL)
    66. {
    67. printk("ioremap filed:%d\n", __LINE__);
    68. return -ENOMEM;
    69. }
    70. printk("物理地址映射成功\n");
    71. // 寄存器的初始化
    72. // rcc
    73. (*vir_rcc) |= (3 << 4);
    74. // led1
    75. vir_led1->MODER &= (~(3 << 20));
    76. vir_led1->MODER |= (1 << 20);
    77. vir_led1->ODR &= (~(1 << 10));
    78. // led2
    79. vir_led2->MODER &= (~(3 << 20));
    80. vir_led2->MODER |= (1 << 20);
    81. vir_led2->ODR &= (~(1 << 10));
    82. // led3
    83. vir_led3->MODER &= (~(3 << 16));
    84. vir_led1->MODER |= (1 << 16);
    85. vir_led1->ODR &= (~(1 << 8));
    86. printk("寄存器初始化成功\n");
    87. return 0;
    88. }
    89. static int __init mycdev_init(void)
    90. {
    91. // 字符设备驱动注册
    92. major = register_chrdev(0, "mychrdev", &fops);
    93. if (major < 0)
    94. {
    95. printk("字符设备驱动注册失败\n");
    96. return major;
    97. }
    98. printk("字符设备驱动注册成功:major=%d\n", major);
    99. // 向上提交目录
    100. cls = class_create(THIS_MODULE, "mychrdev");
    101. if (IS_ERR(cls))
    102. {
    103. printk("向上提交目录失败\n");
    104. return -PTR_ERR(cls);
    105. }
    106. printk("向上提交目录成功\n");
    107. // 向上提交设备节点信息
    108. int i; // 向上提交三次设备节点信息
    109. for (i = 0; i < 3; i++)
    110. {
    111. dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
    112. if (IS_ERR(dev))
    113. {
    114. printk("向上提交设备节点失败\n");
    115. return -PTR_ERR(dev);
    116. }
    117. }
    118. printk("向上提交设备节点成功\n");
    119. // 寄存器映射以及初始化
    120. all_led_init();
    121. return 0;
    122. }
    123. static void __exit mycdev_exit(void)
    124. {
    125. // 取消地址映射
    126. iounmap(vir_led1);
    127. iounmap(vir_led2);
    128. iounmap(vir_rcc);
    129. // 销毁设备节点信息
    130. int i;
    131. for (i = 0; i < 3; i++)
    132. {
    133. device_destroy(cls, MKDEV(major, i));
    134. }
    135. // 销毁目录
    136. class_destroy(cls);
    137. // 注销字符设备驱动
    138. unregister_chrdev(major, "mychrdev");
    139. }
    140. module_init(mycdev_init);
    141. module_exit(mycdev_exit);
    142. MODULE_LICENSE("GPL");
    应用程序:
    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;
    14. int fd = open("/dev/myled0", 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. }

    5.ioctl实例----传递第三个参数(传递整型)

    头文件:
    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 _IOW('l', 1, int)
    18. #define LED_OFF _IOW('l', 0, int)
    19. #endif
    驱动程序:
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include "head.h"
    7. int major;
    8. char kbuf[128] = {0};
    9. gpio_t *vir_led1;
    10. gpio_t *vir_led2;
    11. gpio_t *vir_led3;
    12. unsigned int *vir_rcc;
    13. struct class *cls;
    14. struct device *dev;
    15. int mycdev_open(struct inode *inode, struct file *file)
    16. {
    17. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    18. return 0;
    19. }
    20. long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    21. {
    22. switch (cmd)
    23. {
    24. case LED_ON: // 开灯
    25. switch (arg)
    26. {
    27. case 1: // LED1
    28. vir_led1->ODR |= (0X1 << 10);
    29. break;
    30. case 2:
    31. vir_led2->ODR |= (0X1 << 10);
    32. break;
    33. case 3:
    34. vir_led3->ODR |= (0X1 << 8);
    35. break;
    36. }
    37. break;
    38. case LED_OFF: // 关灯
    39. switch (arg)
    40. {
    41. case 1: // LED1
    42. vir_led1->ODR &= (~(0X1 << 10));
    43. break;
    44. case 2:
    45. vir_led2->ODR &= (~(0X1 << 10));
    46. break;
    47. case 3:
    48. vir_led3->ODR &= (~(0X1 << 8));
    49. break;
    50. }
    51. break;
    52. }
    53. return 0;
    54. }
    55. int mycdev_close(struct inode *inode, struct file *file)
    56. {
    57. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    58. return 0;
    59. }
    60. // 定义操作方法结构体变量并赋值
    61. struct file_operations fops = {
    62. .open = mycdev_open,
    63. .unlocked_ioctl = mycdev_ioctl,
    64. .release = mycdev_close,
    65. };
    66. int all_led_init(void)
    67. {
    68. // 寄存器地址的映射
    69. vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
    70. if (vir_led1 == NULL)
    71. {
    72. printk("ioremap filed:%d\n", __LINE__);
    73. return -ENOMEM;
    74. }
    75. vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
    76. if (vir_led2 == NULL)
    77. {
    78. printk("ioremap filed:%d\n", __LINE__);
    79. return -ENOMEM;
    80. }
    81. vir_led3 = vir_led1;
    82. vir_rcc = ioremap(PHY_RCC_ADDR, 4);
    83. if (vir_rcc == NULL)
    84. {
    85. printk("ioremap filed:%d\n", __LINE__);
    86. return -ENOMEM;
    87. }
    88. printk("物理地址映射成功\n");
    89. // 寄存器的初始化
    90. // rcc
    91. (*vir_rcc) |= (3 << 4);
    92. // led1
    93. vir_led1->MODER &= (~(3 << 20));
    94. vir_led1->MODER |= (1 << 20);
    95. vir_led1->ODR &= (~(1 << 10));
    96. // led2
    97. vir_led2->MODER &= (~(3 << 20));
    98. vir_led2->MODER |= (1 << 20);
    99. vir_led2->ODR &= (~(1 << 10));
    100. // led3
    101. vir_led3->MODER &= (~(3 << 16));
    102. vir_led1->MODER |= (1 << 16);
    103. vir_led1->ODR &= (~(1 << 8));
    104. printk("寄存器初始化成功\n");
    105. return 0;
    106. }
    107. static int __init mycdev_init(void)
    108. {
    109. // 字符设备驱动注册
    110. major = register_chrdev(0, "mychrdev", &fops);
    111. if (major < 0)
    112. {
    113. printk("字符设备驱动注册失败\n");
    114. return major;
    115. }
    116. printk("字符设备驱动注册成功:major=%d\n", major);
    117. // 向上提交目录
    118. cls = class_create(THIS_MODULE, "mychrdev");
    119. if (IS_ERR(cls))
    120. {
    121. printk("向上提交目录失败\n");
    122. return -PTR_ERR(cls);
    123. }
    124. printk("向上提交目录成功\n");
    125. // 向上提交设备节点信息
    126. int i; // 向上提交三次设备节点信息
    127. for (i = 0; i < 3; i++)
    128. {
    129. dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
    130. if (IS_ERR(dev))
    131. {
    132. printk("向上提交设备节点失败\n");
    133. return -PTR_ERR(dev);
    134. }
    135. }
    136. printk("向上提交设备节点成功\n");
    137. // 寄存器映射以及初始化
    138. all_led_init();
    139. return 0;
    140. }
    141. static void __exit mycdev_exit(void)
    142. {
    143. // 取消地址映射
    144. iounmap(vir_led1);
    145. iounmap(vir_led2);
    146. iounmap(vir_rcc);
    147. // 销毁设备节点信息
    148. int i;
    149. for (i = 0; i < 3; i++)
    150. {
    151. device_destroy(cls, MKDEV(major, i));
    152. }
    153. // 销毁目录
    154. class_destroy(cls);
    155. // 注销字符设备驱动
    156. unregister_chrdev(major, "mychrdev");
    157. }
    158. module_init(mycdev_init);
    159. module_exit(mycdev_exit);
    160. MODULE_LICENSE("GPL");
    应用程序:
    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/myled0", 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. printf("请选择要控制的灯:1(LED1)2(LED2) 3(LED3)\n");
    28. printf("请输入>>>");
    29. scanf("%d", &b);
    30. switch (a)
    31. {
    32. case 1:
    33. ioctl(fd, LED_ON, b);
    34. break;
    35. case 0:
    36. ioctl(fd, LED_OFF, b);
    37. break;
    38. }
    39. }
    40. close(fd);
    41. return 0;
    42. }

    6.ioctl实例----传递第三个参数(传递地址)

    头文件:
    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 _IOW('l', 1,int)
    18. #define LED_OFF _IOW('l', 0,int)
    19. #endif
    驱动程序:
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include "head.h"
    7. int major;
    8. char kbuf[128] = {0};
    9. gpio_t *vir_led1;
    10. gpio_t *vir_led2;
    11. gpio_t *vir_led3;
    12. unsigned int *vir_rcc;
    13. struct class *cls;
    14. struct device *dev;
    15. int mycdev_open(struct inode *inode, struct file *file)
    16. {
    17. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    18. return 0;
    19. }
    20. long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    21. {
    22. int which;
    23. //获取应用程序中b的值
    24. int ret= copy_from_user(&which,(void *)arg,4);
    25. if(ret)
    26. {
    27. printk("copy_from_user filed\n");
    28. return-EIO;
    29. }
    30. switch (cmd)
    31. {
    32. case LED_ON: // 开灯
    33. switch (which)
    34. {
    35. case 1: // LED1
    36. vir_led1->ODR |= (0X1 << 10);
    37. break;
    38. case 2:
    39. vir_led2->ODR |= (0X1 << 10);
    40. break;
    41. case 3:
    42. vir_led3->ODR |= (0X1 << 8);
    43. break;
    44. }
    45. break;
    46. case LED_OFF: // 关灯
    47. switch (which)
    48. {
    49. case 1: // LED1
    50. vir_led1->ODR &= (~(0X1 << 10));
    51. break;
    52. case 2:
    53. vir_led2->ODR &= (~(0X1 << 10));
    54. break;
    55. case 3:
    56. vir_led3->ODR &= (~(0X1 << 8));
    57. break;
    58. }
    59. break;
    60. }
    61. return 0;
    62. }
    63. int mycdev_close(struct inode *inode, struct file *file)
    64. {
    65. printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    66. return 0;
    67. }
    68. // 定义操作方法结构体变量并赋值
    69. struct file_operations fops = {
    70. .open = mycdev_open,
    71. .unlocked_ioctl = mycdev_ioctl,
    72. .release = mycdev_close,
    73. };
    74. int all_led_init(void)
    75. {
    76. // 寄存器地址的映射
    77. vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
    78. if (vir_led1 == NULL)
    79. {
    80. printk("ioremap filed:%d\n", __LINE__);
    81. return -ENOMEM;
    82. }
    83. vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
    84. if (vir_led2 == NULL)
    85. {
    86. printk("ioremap filed:%d\n", __LINE__);
    87. return -ENOMEM;
    88. }
    89. vir_led3 = vir_led1;
    90. vir_rcc = ioremap(PHY_RCC_ADDR, 4);
    91. if (vir_rcc == NULL)
    92. {
    93. printk("ioremap filed:%d\n", __LINE__);
    94. return -ENOMEM;
    95. }
    96. printk("物理地址映射成功\n");
    97. // 寄存器的初始化
    98. // rcc
    99. (*vir_rcc) |= (3 << 4);
    100. // led1
    101. vir_led1->MODER &= (~(3 << 20));
    102. vir_led1->MODER |= (1 << 20);
    103. vir_led1->ODR &= (~(1 << 10));
    104. // led2
    105. vir_led2->MODER &= (~(3 << 20));
    106. vir_led2->MODER |= (1 << 20);
    107. vir_led2->ODR &= (~(1 << 10));
    108. // led3
    109. vir_led3->MODER &= (~(3 << 16));
    110. vir_led1->MODER |= (1 << 16);
    111. vir_led1->ODR &= (~(1 << 8));
    112. printk("寄存器初始化成功\n");
    113. return 0;
    114. }
    115. static int __init mycdev_init(void)
    116. {
    117. // 字符设备驱动注册
    118. major = register_chrdev(0, "mychrdev", &fops);
    119. if (major < 0)
    120. {
    121. printk("字符设备驱动注册失败\n");
    122. return major;
    123. }
    124. printk("字符设备驱动注册成功:major=%d\n", major);
    125. // 向上提交目录
    126. cls = class_create(THIS_MODULE, "mychrdev");
    127. if (IS_ERR(cls))
    128. {
    129. printk("向上提交目录失败\n");
    130. return -PTR_ERR(cls);
    131. }
    132. printk("向上提交目录成功\n");
    133. // 向上提交设备节点信息
    134. int i; // 向上提交三次设备节点信息
    135. for (i = 0; i < 3; i++)
    136. {
    137. dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
    138. if (IS_ERR(dev))
    139. {
    140. printk("向上提交设备节点失败\n");
    141. return -PTR_ERR(dev);
    142. }
    143. }
    144. printk("向上提交设备节点成功\n");
    145. // 寄存器映射以及初始化
    146. all_led_init();
    147. return 0;
    148. }
    149. static void __exit mycdev_exit(void)
    150. {
    151. // 取消地址映射
    152. iounmap(vir_led1);
    153. iounmap(vir_led2);
    154. iounmap(vir_rcc);
    155. // 销毁设备节点信息
    156. int i;
    157. for (i = 0; i < 3; i++)
    158. {
    159. device_destroy(cls, MKDEV(major, i));
    160. }
    161. // 销毁目录
    162. class_destroy(cls);
    163. // 注销字符设备驱动
    164. unregister_chrdev(major, "mychrdev");
    165. }
    166. module_init(mycdev_init);
    167. module_exit(mycdev_exit);
    168. MODULE_LICENSE("GPL");
    应用程序:
    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/myled0",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. printf("请选择要控制的灯:1(LED1)2(LED2) 3(LED3)\n");
    28. printf("请输入>");
    29. scanf("%d",&b);
    30. switch(a)
    31. {
    32. case 1:
    33. ioctl(fd,LED_ON,&b);
    34. break;
    35. case 0:
    36. ioctl(fd,LED_OFF,&b);
    37. break;
    38. }
    39. }
    40. close(fd);
    41. return 0;
    42. }
  • 相关阅读:
    Git学习笔记(四)远程仓库
    DocCMS keyword SQL注入
    2334. 元素值大于变化阈值的子数组-单调栈法和暴力求解法
    【Spring】Spring MVC
    DataX二次开发——(9)新增s3reader和s3writer模块
    【注解和反射】获取类运行时结构
    06-集合
    机器人中的数值优化|【三】无约束优化,拟牛顿法,共轭梯度法理论与推导
    openGauss/MogDB脚本源码浅析(2)—— gs_install_plugin/gs_install_plugin_local
    目标检测(4)—— 经典算法和常用指标
  • 原文地址:https://blog.csdn.net/m0_68542867/article/details/133977628