linux驱动初探之杂项设备(控制两个GPIO口)
关键字:linux驱动、杂项设备、GPIO
此驱动程序控制了外接的两个二极管,二极管是低电平有效。
上一篇博客中已经介绍了linux驱动程序的编写流程,这篇博客算是前一篇的提高篇,也是下一篇博客(JNI)的底层程序
一样的在平台文件中配置设备信息
1 #ifdef CONFIG_HELLO_CTL 2 struct platform_device s3c_device_hello_ctl = { 3 .name = "jni", 4 .id = -1, 5 }; 6 #endif
1 #ifdef CONFIG_HELLO_CTL 2 &s3c_device_hello_ctl, 3 #endif
在编写驱动程序之前要确定需要控制哪个GPIO接口,同时要保证该GPIO口没有被其他程序占用,若被占用则需要取消编译那个驱动程序。
经过查找开发板原理图准备使用CAM_VSYNC和CAM_HREF两个端口
这两个端口对应于平台文件的EXYNOS4212_GPIO(1)与EXYNOS4212_GPIO(2)两个脚,也就是说只要控制这两个脚,就是控制了硬件上的两个脚。
使用杂项设备编写驱动会比字符类设备简单,因为杂项设备的主设备号规定了为10,他能够挂255个从设备号。
同样的,从init函数开始:
1 static void jni_exit(void){ 2 3 printk("jni_exit ...\n"); 4 5 platform_driver_unregister(&jni_driver); 6 7 } 8 9 static int jni_init(void){ 10 11 int err; 12 13 printk("jni_init start...\n"); 14 15 err = platform_driver_register(&jni_driver); 16 17 printk("state is %d\n",err); 18 19 return 0; 20 } 21 22 module_init(jni_init); 23 module_exit(jni_exit);
通过platform_driver进行注册,然后申明一个platform_driver结构体,在这里要注意!!这里的.name与我们刚开始时在平台文件中的.name必须要一致,否则会注册失败!也就是说内核会自动进行匹配
1 struct platform_driver jni_driver = { 2 .probe = jni_probe, 3 .remove = jni_remove, 4 .shutdown = jni_shutdown, 5 .suspend = jni_suspend, 6 .resume = jni_resume, 7 .driver = { 8 .name = "jni", 9 .owner = THIS_MODULE, 10 } 11 };
如果匹配成功,那么会进入驱动的probe函数中,所以一般初始化操作都写在了probe函数中,而不像字符类设备是写在init函数中!
1 static int jni_probe(struct platform_device *pdv){ 2 int ret,i; 3 printk("jni_probe start..\n"); 4 5 for(i=0; i<GPIO_NUM; i++) 6 { 7 ret = gpio_request(led_gpios[i], "LED"); 8 if (ret < 0) { 9 printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,i, ret); 10 } 11 else{ 12 printk("%s: request GPIO %d for LED success, ret = %d\n", DEVICE_NAME,i, ret); 13 s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT); 14 gpio_set_value(led_gpios[i], 0); 15 //gpio_free(led_gpios[i]); 16 } 17 } 18 19 ret = misc_register(&jni_dev); 20 if(ret<0) 21 { 22 printk("jni:register device failed!\n"); 23 goto exit; 24 } 25 26 return 0; 27 28 exit: 29 misc_deregister(&jni_dev); 30 return ret; 31 } 32 33 static int jni_remove(struct platform_device *pdv){ 34 //int i; 35 printk("jni_remove...\n"); 36 37 misc_deregister(&jni_dev); 38 39 40 return 0; 41 } 42 43 static void jni_shutdown(struct platform_device *pdv){ 44 45 return ; 46 } 47 48 static int jni_suspend(struct platform_device *pdv,pm_message_t pmt){ 49 50 return 0; 51 } 52 53 static int jni_resume(struct platform_device *pdv){ 54 55 return 0; 56 }
在probe函数中会请求gpio口,即gpio_request,如果请求失败,那肯定是某个驱动占用了该gpio口,需要手动取消。
然后设置为输出。最后调用杂项设备注册函数misc_register进行注册。
1 static struct miscdevice jni_dev = { 2 .minor = MISC_DYNAMIC_MINOR, 3 .name = DEVICE_NAME, 4 .fops = &jni_ops, 5 };
这里终于出现久违的fops函数了,也就是驱动操作函数。
1 static struct file_operations jni_ops = { 2 .owner = THIS_MODULE, 3 .open = jni_open, 4 .release = jni_release, 5 .unlocked_ioctl = jni_ioctl, 6 };
这里的函数接口就是为上层应用提供啦。
1 static long jni_ioctl( struct file *files, unsigned int cmd, unsigned long arg){ 2 int ret; 3 printk("Hello JNI and cmd is %d,arg is %d\n",cmd,arg); 4 5 switch(cmd) 6 { 7 case 0: 8 case 1: 9 if (arg > 2) { 10 return -EINVAL; 11 } 12 gpio_set_value(led_gpios[arg], cmd); 13 14 break; 15 16 default: 17 return -EINVAL; 18 } 19 20 return 0; 21 } 22 23 static int jni_release(struct inode *inode, struct file *file){ 24 25 printk("jni release\n"); 26 27 return 0; 28 } 29 30 31 static int jni_open(struct inode *inode, struct file *file){ 32 33 printk("jni open\n"); 34 35 36 return nonseekable_open(inode,file); 37 }
最后附上完整的驱动代码
1 #include <linux/init.h> 2 #include <linux/module.h> 3 4 /*驱动注册的头文件,platform结构体和驱动注册与注销*/ 5 #include <linux/platform_device.h> 6 7 /*杂项设备头文件*/ 8 #include <linux/miscdevice.h> 9 10 /*设备节点头文件*/ 11 #include <linux/fs.h> 12 13 14 /*Linux中申请GPIO的头文件*/ 15 #include <linux/gpio.h> 16 /*三星平台的GPIO配置函数头文件*/ 17 /*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/ 18 #include <plat/gpio-cfg.h> 19 #include <mach/gpio.h> 20 /*三星平台4412平台,GPIO宏定义头文件*/ 21 #include <mach/gpio-exynos4.h> 22 23 #include <linux/delay.h> 24 25 26 //设备节点 27 #define DEVICE_NAME "jni" 28 //匹配项 29 #define DRIVER_NAME "jni" 30 31 32 33 34 MODULE_LICENSE("Dual BSD/GPL"); 35 MODULE_AUTHOR("PNGCUI"); 36 37 38 static int led_gpios[] = { 39 EXYNOS4212_GPJ0(1),EXYNOS4212_GPJ0(2), 40 }; 41 42 #define GPIO_NUM ARRAY_SIZE(led_gpios) 43 44 45 static long jni_ioctl( struct file *files, unsigned int cmd, unsigned long arg){ 46 int ret; 47 printk("Hello JNI and cmd is %d,arg is %d\n",cmd,arg); 48 49 switch(cmd) 50 { 51 case 0: 52 case 1: 53 if (arg > 2) { 54 return -EINVAL; 55 } 56 gpio_set_value(led_gpios[arg], cmd); 57 58 break; 59 60 default: 61 return -EINVAL; 62 } 63 64 return 0; 65 } 66 67 static int jni_release(struct inode *inode, struct file *file){ 68 69 printk("jni release\n"); 70 71 return 0; 72 } 73 74 75 static int jni_open(struct inode *inode, struct file *file){ 76 77 printk("jni open\n"); 78 79 80 return nonseekable_open(inode,file); 81 } 82 83 static struct file_operations jni_ops = { 84 .owner = THIS_MODULE, 85 .open = jni_open, 86 .release = jni_release, 87 .unlocked_ioctl = jni_ioctl, 88 }; 89 90 static struct miscdevice jni_dev = { 91 .minor = MISC_DYNAMIC_MINOR, 92 .name = DEVICE_NAME, 93 .fops = &jni_ops, 94 }; 95 96 static int jni_probe(struct platform_device *pdv){ 97 int ret,i; 98 printk("jni_probe start..\n"); 99 100 for(i=0; i<GPIO_NUM; i++) 101 { 102 ret = gpio_request(led_gpios[i], "LED"); 103 if (ret < 0) { 104 printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,i, ret); 105 } 106 else{ 107 printk("%s: request GPIO %d for LED success, ret = %d\n", DEVICE_NAME,i, ret); 108 s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT); 109 gpio_set_value(led_gpios[i], 0); 110 //gpio_free(led_gpios[i]); 111 } 112 } 113 114 ret = misc_register(&jni_dev); 115 if(ret<0) 116 { 117 printk("jni:register device failed!\n"); 118 goto exit; 119 } 120 121 return 0; 122 123 exit: 124 misc_deregister(&jni_dev); 125 return ret; 126 } 127 128 static int jni_remove(struct platform_device *pdv){ 129 //int i; 130 printk("jni_remove...\n"); 131 132 misc_deregister(&jni_dev); 133 134 135 return 0; 136 } 137 138 static void jni_shutdown(struct platform_device *pdv){ 139 140 return ; 141 } 142 143 static int jni_suspend(struct platform_device *pdv,pm_message_t pmt){ 144 145 return 0; 146 } 147 148 static int jni_resume(struct platform_device *pdv){ 149 150 return 0; 151 } 152 153 struct platform_driver jni_driver = { 154 .probe = jni_probe, 155 .remove = jni_remove, 156 .shutdown = jni_shutdown, 157 .suspend = jni_suspend, 158 .resume = jni_resume, 159 .driver = { 160 .name = DRIVER_NAME, 161 .owner = THIS_MODULE, 162 } 163 }; 164 165 static void jni_exit(void){ 166 167 printk("jni_exit ...\n"); 168 169 platform_driver_unregister(&jni_driver); 170 171 } 172 173 static int jni_init(void){ 174 175 int err; 176 177 printk("jni_init start...\n"); 178 179 err = platform_driver_register(&jni_driver); 180 181 printk("state is %d\n",err); 182 183 return 0; 184 } 185 186 module_init(jni_init); 187 module_exit(jni_exit);
作者:pngcui
博客园:http://www.cnblogs.com/pngcui/
github:https://github.com/pngcui
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明。