1.在字符设备驱动模块加载函数中应该实现设备号的申请和cdev 的注册,而在卸载函数中应实现设备号的释放和cdev 的注销。
1//设备结构体 2 struct xxx_dev_t 3 { 4 struct cdev cdev; 5 ... 6 } xxx_dev; 7 //设备驱动模块加载函数 8 static int _ _init xxx_init(void) 9 { 10 ... 11 cdev_init(&xxx_dev.cdev, &xxx_fops); //初始化cdev 12 xxx_dev.cdev.owner = THIS_MODULE; 13 //获取字符设备号 14 if (xxx_major) 15 { 16 register_chrdev_region(xxx_dev_no, 1, DEV_NAME); 17 } 18 else 19 { 20 alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME); 21 } 22 23 ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); //注册设备 24 ... 25 } 26 /*设备驱动模块卸载函数*/ 27 static void _ _exit xxx_exit(void) 28 { 29 unregister_chrdev_region(xxx_dev_no, 1); //释放占用的设备号 30 cdev_del(&xxx_dev.cdev); //注销设备 31 ... 32 }
2.file_operations 结构体中成员函数是字符设备驱动与内核的接口,是用户空间对Linux 进行系统调用最终的落实者。大多数字符设备驱动会实现read()、write()和ioctl()函数。
1 struct file_operations xxx_fops = 2 { 3 .owner = THIS_MODULE, 4 .read = xxx_read, 5 .write = xxx_write, 6 .ioctl = xxx_ioctl, 7 ... 8 };
下面将基于虚拟的 globalmem 设备进行字符设备驱动。globalmem 意味着“全局内存”,在globalmem 字符设备驱动中会分配一片大小为GLOBALMEM_ SIZE的内存空
间,并在驱动中提供针对该片内存的读写、控制和定位函数,以供用户空间的进程能通过Linux 系统调用访问这片内存。
1 #include<linux/module.h> 2 #include<linux/types.h> 3 #include<linux/fs.h> 4 #include<linux/errno.h> 5 #include<linux/mm.h> 6 #include<linux/sched.h> 7 #include<linux/init.h> 8 #include<linux/cdev.h> 9 #include<linux/slab.h> 10 #include<linux/kdev_t.h> 11 #include<linux/device.h> 12 #include <asm/io.h> 13 #include<asm/system.h> 14 #include<asm/uaccess.h> 15 16 #define GLOBALMEM_SIZE 0x1000 17 #define MEM_CLEAR 0x1 18 #define GLOBALMEM_MAJOR 254 /*预设的globalmem 的主设备号*/ 19 20 static int globalmem_major = GLOBALMEM_MAJOR; 21 /*globalmem 设备结构体*/ 22 struct globalmem_dev 23 { 24 struct cdev cdev; 25 unsigned char mem[GLOBALMEM_SIZE]; 26 }; 27 struct globalmem_dev *globalmem_devp; /*设备结构体指针*/ 28 /*文件打开函数*/ 29 int globalmem_open(struct inode *inode, struct file *filp) 30 { 31 filp->private_data = globalmem_devp;/*将设备结构体指针赋值给文件私有数据指针*/ 32 return 0; 33 } 34 /*文件释放函数*/ 35 int globalmem_release(struct inode *inode, struct file *filp) 36 { 37 return 0; 38 } 39 /* ioctl 设备控制函数 */ 40 static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg) 41 { 42 struct globalmem_dev *dev = filp->private_data;/*获得设备结构体指针*/ 43 switch (cmd) 44 { 45 case MEM_CLEAR: 46 memset(dev->mem, 0, GLOBALMEM_SIZE); 47 printk(KERN_INFO "globalmem is set to zero "); 48 break; 49 default:return - EINVAL; 50 } 51 return 0; 52 } 53 /*读函数*/ 54 static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) 55 { 56 unsigned long p = *ppos; 57 unsigned int count = size; 58 int ret = 0; 59 struct globalmem_dev *dev = filp->private_data; 60 if (p >= GLOBALMEM_SIZE) 61 return count ? - ENXIO: 0; 62 if (count > GLOBALMEM_SIZE - p) 63 count = GLOBALMEM_SIZE - p; 64 /*内核空间→用户空间*/ 65 if (copy_to_user(buf, (void*)(dev->mem + p), count)) 66 { 67 ret = - EFAULT; 68 } 69 else 70 { 71 *ppos += count; 72 ret = count; 73 printk(KERN_INFO "read %d bytes(s) from %d ",count,p); 74 } 75 return ret; 76 } 77 /*写函数*/ 78 static ssize_t globalmem_write(struct file *filp, const char __user *buf,size_t size, loff_t *ppos) 79 { 80 unsigned long p = *ppos; 81 unsigned int count = size; 82 int ret = 0; 83 struct globalmem_dev *dev = filp->private_data; 84 if (p >= GLOBALMEM_SIZE) 85 return count ? - ENXIO: 0; 86 if (count > GLOBALMEM_SIZE - p) 87 count = GLOBALMEM_SIZE - p; 88 /*用户空间→内核空间*/ 89 if (copy_from_user(dev->mem + p, buf, count)) 90 { 91 ret = - EFAULT; 92 } 93 else 94 { 95 *ppos += count; 96 ret = count; 97 printk(KERN_INFO "written %d bytes(s) from %d ", count, p); 98 } 99 return ret; 100 } 101 /* seek 文件定位函数 */ 102 static loff_t globalmem_llseek(struct file *filp,loff_t offset,int orig) 103 { 104 loff_t ret = 0; 105 switch(orig) 106 { 107 case 0: /*相对文件开始位置偏移*/ 108 if(offset < 0) 109 { 110 ret = -EINVAL;// 111 break; 112 } 113 if((unsigned int)offset > GLOBALMEM_SIZE) 114 { 115 ret = -EINVAL; 116 break; 117 } 118 filp->f_pos = (unsigned int)offset; 119 ret = filp->f_pos; 120 break; 121 case 1: /*相对文件当前位置偏移*/ 122 if((filp->f_pos + offset) > GLOBALMEM_SIZE) 123 { 124 ret = -EINVAL; 125 break; 126 } 127 if((filp->f_pos + offset) < 0) 128 { 129 ret = -EINVAL; 130 break; 131 } 132 filp->f_pos +=offset; 133 ret = filp->f_pos; 134 break; 135 default: 136 ret = -EINVAL; 137 break; 138 } 139 return ret; 140 } 141 /*文件操作结构体*/ 142 static const struct file_operations globalmem_fops= 143 { 144 .owner = THIS_MODULE, 145 .llseek = globalmem_llseek, 146 .read = globalmem_read, 147 .write = globalmem_write, 148 .ioctl = globalmem_ioctl, 149 .open = globalmem_open, 150 .release = globalmem_release, 151 }; 152 /*初始化并注册cdev*/ 153 static void globalmem_setup_cdev(struct globalmem_dev *dev,int index) 154 { 155 int err,devno = MKDEV(globalmem_major,index); 156 cdev_init(&dev->cdev,&globalmem_fops); 157 dev->cdev.owner = THIS_MODULE; 158 err = cdev_add(&dev->cdev,devno,1); 159 if(err) 160 printk(KERN_INFO"error %d ading globalmem %d",err,index); 161 } 162 /*设备驱动模块加载函数*/ 163 int globalmem_init(void) 164 { 165 int result; 166 dev_t devno = MKDEV(globalmem_major,0); 167 if(globalmem_major) /* 申请设备号*/ 168 { 169 result = register_chrdev_region(devno,1,"globalmem"); 170 printk("register_chrdev_region "); 171 } 172 else /* 动态申请设备号 */ 173 { 174 result = alloc_chrdev_region(&devno,0,1,"globalmem"); 175 globalmem_major = MAJOR(devno); 176 printk("alloc_chrdev_region "); 177 } 178 if(result < 0) 179 return result; 180 /* 动态申请设备结构体的内存*/ 181 globalmem_devp = kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL); 182 if(!globalmem_devp) 183 { 184 result = -ENOMEM;//no memory 185 goto fail_malloc; 186 } 187 memset(globalmem_devp,0,sizeof(struct globalmem_dev)); 188 globalmem_setup_cdev(globalmem_devp,0); 189 printk("init ok.. "); 190 return 0; 191 fail_malloc: 192 unregister_chrdev_region(devno,1); 193 return result; 194 } 195 /*模块卸载函数*/ 196 void globalmem_exit(void) 197 { 198 cdev_del(&globalmem_devp->cdev); /*注销cdev*/ 199 kfree(globalmem_devp); 200 unregister_chrdev_region(MKDEV(globalmem_major,0),1); /*释放设备号*/ 201 printk("exit.. "); 202 } 203 MODULE_AUTHOR("Huang"); 204 MODULE_LICENSE("Dual BSD/GPL"); 205 206 module_param(globalmem_major, int, S_IRUGO);/*内核模块通过module_param()来传递参数*/ 207 module_init(globalmem_init); 208 module_exit(globalmem_exit);
Makefile:
1 ifneq ($(KERNELRELEASE),) 2 obj-m:= globalmem.o 3 else 4 PWD:=$(shell pwd) 5 KVER?=$(shell uname -r) 6 KERNELDIR:= /usr/src/linux-2.6.32.2 //编译好的内核 7 default: 8 make -C $(KERNELDIR) M=$(PWD) modules 9 endif 10 clean: 11 rm-f *.ko *.mod.c *.mod.o *.o
运行 insmod globalmem.ko 命令加载模块,通过 lsmod 命令,发现globalmem 模块已被加载。再通过 cat /proc/devices 命令查看,发现多出了主设备号为254的globalmem字符设备驱动。