关键字:字符驱动、动态生成设备节点、helloworld
linux驱动编程,个人觉得第一件事就是配置好平台文件,这里以字符设备,也就是传说中的helloworld为例~
此驱动程序基于linux3.0的内核,exynos4412开发板。
首先,打开平台文件,此开发板的平台文件是archarmmach-exynosmach-itop4412.c,不同平台位置是不一样的。
申明一下设备信息,这里以编译进kernel为例
1 #ifdef CONFIG_HELLO_CHAR_CTL 2 struct platform_device s3c_device_hello_char_ctl = { 3 .name = "Hello_Jni_Char", 4 .id = -1, 5 }; 6 #endif
然后在struct platform_device *smdk4x12_devices[]数组里注册设备信息
1 #ifdef CONFIG_HELLO_CHAR_CTL 2 &s3c_device_hello_char_ctl, 3 #endif
至此,平台文件配置完毕。
接下来,开始大餐,驱动程序的编写!
注意:在linux的每一个驱动程序里面都需要加上如下信息,表明开源
1 MODULE_LICENSE("Dual BSD/GPL"); 2 MODULE_AUTHOR("pngcui");
每一个驱动程序都是从init函数开始的,同时也需要编写exit函数,在初始化函数中需要生成主设备号与从设备号,因为linux是根据主从设备号来寻找相应的硬件的
1 #define DEV_MAJOR 0 2 #define DEV_MINOR 0 3 4 int numdev_major = DEV_MAJOR; 5 int numdev_minor = DEV_MINOR; 6 7 8 /*输入主设备号*/ 9 module_param(numdev_major,int,S_IRUSR); 10 /*输入次设备号*/ 11 module_param(numdev_minor,int,S_IRUSR);
进入驱动程序中的init函数中,进行注册设备号,生成设备节点DEVICE_NAME
1 #define DEVICE_NAME "chardevnode"
注意:此驱动由于控制了两个gpio引脚所以出现了gpio_init(),在这里可以忽略,对于gpio的控制,在下一篇博客中会写到。
1 static int scdev_init(void) 2 { 3 int ret = 0,i; 4 dev_t num_dev; 5 6 7 printk(KERN_EMERG "numdev_major is %d! ",numdev_major); 8 printk(KERN_EMERG "numdev_minor is %d! ",numdev_minor); 9 10 if(numdev_major){ 11 num_dev = MKDEV(numdev_major,numdev_minor); 12 ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME); 13 } 14 else{ 15 /*动态注册设备号*/ 16 ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME); 17 /*获得主设备号*/ 18 numdev_major = MAJOR(num_dev); 19 printk(KERN_EMERG "adev_region req %d ! ",numdev_major); 20 } 21 if(ret<0){ 22 printk(KERN_EMERG "register_chrdev_region req %d is failed! ",numdev_major); 23 } 24 myclass = class_create(THIS_MODULE,DEVICE_NAME); 25 26 27 my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL); 28 if(!my_devices){ 29 ret = -ENOMEM; 30 goto fail; 31 } 32 memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev)); 33 34 /*设备初始化*/ 35 for(i=0;i<DEVICE_MINOR_NUM;i++){ 36 my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL); 37 memset(my_devices[i].data,0,REGDEV_SIZE); 38 /*设备注册到系统*/ 39 reg_init_cdev(&my_devices[i],i); 40 41 /*创建设备节点*/ 42 device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i); 43 } 44 45 ret = gpio_init(); 46 if(ret){ 47 printk(KERN_EMERG "gpio_init failed! "); 48 } 49 50 printk(KERN_EMERG "scdev_init! "); 51 return 0; 52 53 fail: 54 /*注销设备号*/ 55 unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM); 56 printk(KERN_EMERG "kmalloc is fail! "); 57 58 return ret; 59 } 60 61 static void scdev_exit(void) 62 { 63 int i; 64 printk(KERN_EMERG "scdev_exit! "); 65 66 /*除去字符设备*/ 67 for(i=0;i<DEVICE_MINOR_NUM;i++){ 68 cdev_del(&(my_devices[i].cdev)); 69 /*摧毁设备节点函数*/ 70 device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i)); 71 } 72 /*释放设备class*/ 73 class_destroy(myclass); 74 /*释放内存*/ 75 kfree(my_devices); 76 77 /*释放GPIO*/ 78 for(i=0;i<LED_NUM;i++){ 79 gpio_free(led_gpios[i]); 80 } 81 82 unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM); 83 } 84 85 86 module_init(scdev_init); 87 /*初始化函数*/ 88 module_exit(scdev_exit); 89 /*卸载函数*/
然后把设备注册到系统
1 static void reg_init_cdev(struct reg_dev *dev,int index){ 2 int err; 3 int devno = MKDEV(numdev_major,numdev_minor+index); 4 5 /*数据初始化*/ 6 cdev_init(&dev->cdev,&my_fops); 7 dev->cdev.owner = THIS_MODULE; 8 dev->cdev.ops = &my_fops; 9 10 /*注册到系统*/ 11 err = cdev_add(&dev->cdev,devno,1); 12 if(err){ 13 printk(KERN_EMERG "cdev_add %d is fail! %d ",index,err); 14 } 15 else{ 16 printk(KERN_EMERG "cdev_add %d is success! ",numdev_minor+index); 17 } 18 }
dev->cdev.ops = &my_fops;这一句就指明了此驱动程序的函数接口
1 struct file_operations my_fops = { 2 .owner = THIS_MODULE, 3 .open = chardevnode_open, 4 .release = chardevnode_release, 5 .unlocked_ioctl = chardevnode_ioctl, 6 .read = chardevnode_read, 7 .write = chardevnode_write, 8 .llseek = chardevnode_llseek, 9 };
在这里的所有函数接口必须都实现,否则会编译报错。
其中最重要的两个函数是open、ioctl这两个函数,open函数用来打开设备节点,然后才可以对设备进行驱动。
然后是ioctl函数,一般上层应用都是通过调用此函数来对设备进行操作的。
1 /*打开操作*/ 2 static int chardevnode_open(struct inode *inode, struct file *file){ 3 printk(KERN_EMERG "chardevnode_open is success! "); 4 5 return 0; 6 } 7 /*关闭操作*/ 8 static int chardevnode_release(struct inode *inode, struct file *file){ 9 printk(KERN_EMERG "chardevnode_release is success! "); 10 11 return 0; 12 } 13 /*IO操作*/ 14 static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ 15 16 switch(cmd) 17 { 18 case 0: 19 case 1: 20 if (arg > LED_NUM) { 21 return -EINVAL; 22 } 23 24 gpio_set_value(led_gpios[arg], cmd); 25 break; 26 27 default: 28 return -EINVAL; 29 } 30 31 printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d ",cmd,arg); 32 33 return 0; 34 } 35 36 ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){ 37 return 0; 38 } 39 40 ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){ 41 return 0; 42 } 43 44 loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){ 45 return 0; 46 }
到这里一个完整的字符驱动就编写完毕了。
最后附上完整的驱动程序
1 /*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/ 2 #include <linux/init.h> 3 /*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*/ 4 #include <linux/module.h> 5 /*定义module_param module_param_array的头文件*/ 6 #include <linux/moduleparam.h> 7 /*定义module_param module_param_array中perm的头文件*/ 8 #include <linux/stat.h> 9 /*三个字符设备函数*/ 10 #include <linux/fs.h> 11 /*MKDEV转换设备号数据类型的宏定义*/ 12 #include <linux/kdev_t.h> 13 /*定义字符设备的结构体*/ 14 #include <linux/cdev.h> 15 /*分配内存空间函数头文件*/ 16 #include <linux/slab.h> 17 /*包含函数device_create 结构体class等头文件*/ 18 #include <linux/device.h> 19 20 /*自定义头文件*/ 21 #include "char_driver_leds.h" 22 23 /*Linux中申请GPIO的头文件*/ 24 #include <linux/gpio.h> 25 /*三星平台的GPIO配置函数头文件*/ 26 /*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/ 27 #include <plat/gpio-cfg.h> 28 /*三星平台4412平台,GPIO宏定义头文件*/ 29 #include <mach/gpio-exynos4.h> 30 31 32 MODULE_LICENSE("Dual BSD/GPL"); 33 /*声明是开源的,没有内核版本限制*/ 34 MODULE_AUTHOR("iTOPEET_dz"); 35 /*声明作者*/ 36 37 static int led_gpios[] = { 38 EXYNOS4_GPL2(0),EXYNOS4_GPK1(1), 39 }; 40 #define LED_NUM ARRAY_SIZE(led_gpios) 41 42 #define DEVICE_NAME "chardevnode" 43 44 #define DEVICE_MINOR_NUM 1 45 46 #define DEV_MAJOR 0 47 #define DEV_MINOR 0 48 #define REGDEV_SIZE 3000 49 50 51 struct reg_dev 52 { 53 char *data; 54 unsigned long size; 55 56 struct cdev cdev; 57 }; 58 59 60 int numdev_major = DEV_MAJOR; 61 int numdev_minor = DEV_MINOR; 62 63 /*输入主设备号*/ 64 module_param(numdev_major,int,S_IRUSR); 65 /*输入次设备号*/ 66 module_param(numdev_minor,int,S_IRUSR); 67 68 static struct class *myclass; 69 struct reg_dev *my_devices; 70 71 /*打开操作*/ 72 static int chardevnode_open(struct inode *inode, struct file *file){ 73 printk(KERN_EMERG "chardevnode_open is success! "); 74 75 return 0; 76 } 77 /*关闭操作*/ 78 static int chardevnode_release(struct inode *inode, struct file *file){ 79 printk(KERN_EMERG "chardevnode_release is success! "); 80 81 return 0; 82 } 83 /*IO操作*/ 84 static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ 85 86 switch(cmd) 87 { 88 case 0: 89 case 1: 90 if (arg > LED_NUM) { 91 return -EINVAL; 92 } 93 94 gpio_set_value(led_gpios[arg], cmd); 95 break; 96 97 default: 98 return -EINVAL; 99 } 100 101 printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d ",cmd,arg); 102 103 return 0; 104 } 105 106 ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){ 107 return 0; 108 } 109 110 ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){ 111 return 0; 112 } 113 114 loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){ 115 return 0; 116 } 117 struct file_operations my_fops = { 118 .owner = THIS_MODULE, 119 .open = chardevnode_open, 120 .release = chardevnode_release, 121 .unlocked_ioctl = chardevnode_ioctl, 122 .read = chardevnode_read, 123 .write = chardevnode_write, 124 .llseek = chardevnode_llseek, 125 }; 126 127 128 /*设备注册到系统*/ 129 static void reg_init_cdev(struct reg_dev *dev,int index){ 130 int err; 131 int devno = MKDEV(numdev_major,numdev_minor+index); 132 133 /*数据初始化*/ 134 cdev_init(&dev->cdev,&my_fops); 135 dev->cdev.owner = THIS_MODULE; 136 dev->cdev.ops = &my_fops; 137 138 /*注册到系统*/ 139 err = cdev_add(&dev->cdev,devno,1); 140 if(err){ 141 printk(KERN_EMERG "cdev_add %d is fail! %d ",index,err); 142 } 143 else{ 144 printk(KERN_EMERG "cdev_add %d is success! ",numdev_minor+index); 145 } 146 } 147 148 static int gpio_init(void){ 149 int i=0,ret; 150 151 for(i=0;i<LED_NUM;i++){ 152 ret = gpio_request(led_gpios[i], "LED"); 153 if (ret) { 154 printk("%s: request GPIO %d for LED failed, ret = %d ", DEVICE_NAME,i,ret); 155 return -1; 156 } 157 else{ 158 s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT); 159 gpio_set_value(led_gpios[i], 1); 160 } 161 } 162 163 return 0; 164 } 165 166 167 static int scdev_init(void) 168 { 169 int ret = 0,i; 170 dev_t num_dev; 171 172 173 printk(KERN_EMERG "numdev_major is %d! ",numdev_major); 174 printk(KERN_EMERG "numdev_minor is %d! ",numdev_minor); 175 176 if(numdev_major){ 177 num_dev = MKDEV(numdev_major,numdev_minor); 178 ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME); 179 } 180 else{ 181 /*动态注册设备号*/ 182 ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME); 183 /*获得主设备号*/ 184 numdev_major = MAJOR(num_dev); 185 printk(KERN_EMERG "adev_region req %d ! ",numdev_major); 186 } 187 if(ret<0){ 188 printk(KERN_EMERG "register_chrdev_region req %d is failed! ",numdev_major); 189 } 190 myclass = class_create(THIS_MODULE,DEVICE_NAME); 191 192 193 my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL); 194 if(!my_devices){ 195 ret = -ENOMEM; 196 goto fail; 197 } 198 memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev)); 199 200 /*设备初始化*/ 201 for(i=0;i<DEVICE_MINOR_NUM;i++){ 202 my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL); 203 memset(my_devices[i].data,0,REGDEV_SIZE); 204 /*设备注册到系统*/ 205 reg_init_cdev(&my_devices[i],i); 206 207 /*创建设备节点*/ 208 device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i); 209 } 210 211 return 0; 212 213 fail: 214 /*注销设备号*/ 215 unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM); 216 printk(KERN_EMERG "kmalloc is fail! "); 217 218 return ret; 219 } 220 221 static void scdev_exit(void) 222 { 223 int i; 224 printk(KERN_EMERG "scdev_exit! "); 225 226 /*除去字符设备*/ 227 for(i=0;i<DEVICE_MINOR_NUM;i++){ 228 cdev_del(&(my_devices[i].cdev)); 229 /*摧毁设备节点函数d*/ 230 device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i)); 231 } 232 /*释放设备class*/ 233 class_destroy(myclass); 234 /*释放内存*/ 235 kfree(my_devices); 236 237 /*释放GPIO*/ 238 for(i=0;i<LED_NUM;i++){ 239 gpio_free(led_gpios[i]); 240 } 241 242 unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM); 243 } 244 245 246 module_init(scdev_init); 247 /*初始化函数*/ 248 module_exit(scdev_exit); 249 /*卸载函数*/
以及完整的测试程序
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <unistd.h> 5 #include <sys/ioctl.h> 6 #include <stdio.h> 7 8 int main(){ 9 10 int fd; 11 12 if((fd = open("/dev/chardevnode0",O_RDWR|O_NDELAY)) < 0) 13 printf("Hello_Jni open failed! "); 14 15 ioctl(fd,0,0); 16 ioctl(fd,0,1); 17 18 close(fd); 19 20 return 0; 21 }