Linux内核学习笔记之使用ioctl函数实现用户态命令

本文介绍了一个基于GPIO库的驱动程序设计,该程序通过ioctl函数实现用户态命令,支持四个GPIO接口的高低电平控制,并详细展示了从设备初始化到用户态测试程序的完整流程。

驱动程序:


[cpp]  view plain  copy
  1. /******************************** 
  2.  * GPIO驱动程序控制GPIO接口高低电平 
  3.  * 基于gpio库,四个GPIO识别为一个设备 
  4.  * 使用miscdevice结构体动态分配设备号,自动创建/dev/文件 
  5.  * 使用ioctl函数实现用户态命令 
  6.  * 更多内容见于笔记05 
  7.  * 开发板:Tiny 4412 
  8.  * 主控芯片:Exynos 4412 
  9.  * author: zhangn 
  10.  * date: 2016-1-12 
  11.  ********************************/  
  12.   
  13. #include <linux/module.h>  
  14. #include <linux/fs.h>  
  15. #include <linux/uaccess.h>  
  16. #include <linux/miscdevice.h>  
  17. //包含gpio库头文件  
  18. #include <linux/gpio.h>//所有处理器通用  
  19. #include <plat/gpio-cfg.h>//三棒子芯片公用  
  20. #include <mach/gpio.h>//只针对4412芯片  
  21.   
  22. #define GPIO_NUM    4  
  23.   
  24. //0.定义ioctl命令,本例我们定义两个带一个参数的命令  
  25. /**************************************************************** 
  26.   关于_IOW,这是内核中定义的一个用于向内核写数据的一个宏 
  27.   其参数是_IOW(幻数, 命令序号, 命令所占内存大小) 
  28.   对于第三个字段,这里要使用参数的数据类型,内核里会使用sizeof获得 
  29.   这里具体说明详见ldd3的第六章关于ioctl的说明 
  30.   最后要注意的是这的宏定义要和用户态程序完全相同 
  31.  *****************************************************************/  
  32. #define GPIO_TYPE   'Z'//区分不同命令而取的8位幻数,为简便随便使用一个字母  
  33. #define GPIO_ON     _IOW(GPIO_TYPE, 1, int)//用户态定义命令参数  
  34. #define GPIO_OFF    _IOW(GPIO_TYPE, 2, int)  
  35.   
  36. //四个设备,定义四个gpio号  
  37. static int gpios[GPIO_NUM];  
  38.   
  39. //7.实现ioctl函数  
  40. //这里书上说函数实参应该在file前面还有个inode参数  
  41. //但加上该参数后用户态传进来的命令和参数错位,  
  42. //去掉该参数后程序正常,不晓得什么原因,是版本问题?  
  43. static long gpio_ioctl(struct file *filp,  
  44.                         unsigned int req, unsigned long arg)  
  45. {  
  46.     printk("req = %d, arg = %ld\n", req, arg);  
  47. /*  if(arg>3 || arg<0) 
  48.     { 
  49.         printk("Only support gpio 0~3\n"); 
  50.         return -1; 
  51.     }*/  
  52.     //req为命令代码,代表开关.arg为命令参数,代表gpio序号  
  53.     switch(req)  
  54.     {  
  55.         case GPIO_ON:  
  56.             gpio_set_value(gpios[arg], 0);  
  57.             printk("arg_on = %ld\n", arg);  
  58.             break;  
  59.         case GPIO_OFF:  
  60.             gpio_set_value(gpios[arg], 1);  
  61.             printk("arg_off = %ld\n", arg);  
  62.             break;  
  63.         default:  
  64.             printk("arg_err = %ld\n", arg);  
  65.             break;  
  66.     }  
  67.     return 0;  
  68. }  
  69.   
  70. //1.准备file_operations  
  71. static struct file_operations fops =   
  72. {  
  73.     //本例中使用了ioctl函数,不需要open,read等函数  
  74.     .owner = THIS_MODULE,  
  75.     .unlocked_ioctl = gpio_ioctl,  
  76. };  
  77.   
  78. //2.准备miscdevice  
  79. //如果将4个GPIO看成一个设备,推荐使用miscdevice代替cdev  
  80. //如果一个驱动程序要驱动多个设备,那么就不应该使用miscdevice  
  81. //miscdevice会在内核中使用同一个主设备号,设备间使用次设备号进行区分  
  82. //内核中会为所有使用misc的设备维护一个链表,具体请自行查阅misc相关信息  
  83. //使用misc的好处是内核会自动创建/dev/文件,自动分配设备号  
  84. static struct miscdevice misc =   
  85. {  
  86.     .minor = MISC_DYNAMIC_MINOR,//次设备号,使用MISC_DYNAMIC_MINOR系统会自动分配  
  87.     .name  = "gpios",//设备名称,自动创建的/dev/文件也使用这个名字  
  88.     .fops  = &fops,  
  89. };  
  90.   
  91. static int __init my_init(void)  
  92. {  
  93.     int i, ret, gpio_num;  
  94.     //以下工作,每个设备完成一次  
  95.     for(i = 0; i < GPIO_NUM; ++i)  
  96.     {  
  97.         //3.在头文件中找到寄存器的宏定义  
  98.         gpios[i] = EXYNOS4_GPX3(i+2);  
  99.         gpio_num = gpios[i];  
  100.         printk("i = %d, gpios[%d] = %d, num = %d\n", i, i, gpios[i], gpio_num);  
  101.         //4.申请gpio  
  102.         ret = gpio_request(gpio_num, "myio");  
  103.         if(ret)  
  104.         {  
  105.             printk("Request failed...%d\n", ret);  
  106.             for(; i-1 > 0; --i)  
  107.                 gpio_free(gpios[i-1]);  
  108.             return ret;  
  109.         }  
  110.         //5.对io进行配置  
  111.         s3c_gpio_cfgpin(gpio_num, S3C_GPIO_OUTPUT);  
  112.         //写入数据,这里是设置默认值  
  113.         gpio_set_value(gpio_num, 0);  
  114.     }  
  115.   
  116.     //6.注册misc  
  117.     ret = misc_register(&misc);  
  118.     if(ret)  
  119.         for(i = 0; i < GPIO_NUM; ++i)  
  120.             gpio_free(gpios[i]);  
  121.   
  122.     return ret;  
  123. }  
  124.   
  125. static void __exit my_exit(void)  
  126. {  
  127.     int i;  
  128.     misc_deregister(&misc);  
  129.     for(i = 0; i < GPIO_NUM; ++i)  
  130.         gpio_free(gpios[i]);  
  131.   
  132. }  
  133.   
  134.   
  135. module_init(my_init);  
  136. module_exit(my_exit);  
  137.   
  138. MODULE_LICENSE("GPL");  
  139. MODULE_AUTHOR("ZhangN");  


用户态测试程序


[cpp]  view plain  copy
  1. /******************************* 
  2.  * GPIO的用户态测试 
  3.  * &> test /dev/gpios 0|1|2|3 on|off 
  4.  * author:zhangn 
  5.  * date:2016-1-12 
  6.  *******************************/  
  7. #include <stdio.h>  
  8. #include <stdlib.h>  
  9. #include <unistd.h>  
  10. #include <fcntl.h>  
  11. #include <sys/ioctl.h>  
  12.   
  13. #define GPIO_TYPE   'Z'  
  14. #define GPIO_ON     _IOW(GPIO_TYPE, 1, int)  
  15. #define GPIO_OFF    _IOW(GPIO_TYPE, 2, int)  
  16.   
  17. int main(int argc, char **argv)  
  18. {  
  19.     int fd;  
  20.     int gpio_num;  
  21.     char *endp;  
  22.       
  23.     fd = open(argv[1], O_RDWR);  
  24.     if(fd < 0)  
  25.     {  
  26.         printf("Cannot open %s\n", argv[1]);  
  27.         exit(1);  
  28.     }  
  29.   
  30.     gpio_num = strtol(argv[2], &endp, 10);  
  31.     if(gpio_num > 3)  
  32.     {  
  33.         close(fd);  
  34.         printf("Only support gpio 0~3\n");  
  35.         exit(1);  
  36.     }  
  37.   
  38.     printf("arg = %d\n", gpio_num);  
  39.   
  40.     if(strncmp(argv[3], "on", 2) == 0)  
  41.     {  
  42.         ioctl(fd, GPIO_ON, gpio_num);  
  43.     }  
  44.     else if(strncmp(argv[3], "off", 3) == 0)  
  45.     {  
  46.         ioctl(fd, GPIO_OFF, gpio_num);  
  47.     }  
  48.     else  
  49.     {  
  50.         close(fd);  
  51.         exit(1);  
  52.     }  
  53.   
  54.     close(fd);  
  55.     exit(0);  
  56.   
  57. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值