源代码:
/****************************************************************************** *Name: memdev.c *Desc: 字符设备驱动程序的框架结构,该字符设备并不是一个真实的物理设备, * 而是使用内存来模拟一个字符设备 *Parameter: *Return: *Author: derek *Date: 2013-6-4 ********************************************************************************/ #include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> #include <linux/spinlock.h> #include <linux/blkdev.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/cdev.h> #define MEMDEV_MAJOR 260 /*预设的mem的主设备号*/ #define MEMDEV_NR_DEVS 2 /*设备数*/ #define MEMDEV_SIZE 4096 #define CHAR_DEV_NAME "memdev" /*mem设备描述结构体*/ struct mem_dev { char *data; unsigned long size; }; static int mem_major = MEMDEV_MAJOR; module_param(mem_major, int, S_IRUGO); struct mem_dev *mem_devp; /*设备结构体指针*/ struct cdev cdev; /*文件打开函数*/ int mem_open(struct inode *inode, struct file *filp) { struct mem_dev *dev; /*获取次设备号*/ int num = MINOR(inode->i_rdev); if (num >= MEMDEV_NR_DEVS) return -ENODEV; dev = &mem_devp[num]; /*将设备描述结构指针赋值给文件私有数据指针*/ filp->private_data = dev; return 0; } /*文件释放函数*/ int mem_release(struct inode *inode, struct file *filp) { return 0; } /*读函数*/ static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/ /*判断读位置是否有效*/ if (p >= MEMDEV_SIZE) return 0; if (count > MEMDEV_SIZE - p) count = MEMDEV_SIZE - p; /*读数据到用户空间*/ if (copy_to_user(buf, (void*)(dev->data + p), count)) { ret = - EFAULT; } else { *ppos += count; ret = count; printk(KERN_INFO "read %d bytes(s) from %d\n", count, p); } return ret; } /*写函数*/ static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/ /*分析和获取有效的写长度*/ if (p >= MEMDEV_SIZE) return 0; if (count > MEMDEV_SIZE - p) count = MEMDEV_SIZE - p; /*从用户空间写入数据*/ if (copy_from_user(dev->data + p, buf, count)) ret = - EFAULT; else { *ppos += count; ret = count; printk(KERN_INFO "written %d bytes(s) from %d\n", count, p); } return ret; } /* seek文件定位函数 */ static loff_t mem_llseek(struct file *filp, loff_t offset, int whence) { loff_t newpos; switch(whence) { case 0: /* SEEK_SET */ newpos = offset; break; case 1: /* SEEK_CUR */ newpos = filp->f_pos + offset; break; case 2: /* SEEK_END */ newpos = MEMDEV_SIZE -1 + offset; break; default: /* can't happen */ return -EINVAL; } if ((newpos<0) || (newpos>MEMDEV_SIZE)) return -EINVAL; filp->f_pos = newpos; return newpos; } /*文件操作结构体*/ static const struct file_operations mem_fops = { .owner = THIS_MODULE, .llseek = mem_llseek, .read = mem_read, .write = mem_write, .open = mem_open, .release = mem_release, }; struct class *pclass; /*设备驱动模块加载函数*/ static int memdev_init(void) { int result; int i; dev_t devno = MKDEV(mem_major, 0); if (mem_major) { /* 静态申请设备号*/ result = register_chrdev_region(devno, 2, CHAR_DEV_NAME); } else {/* 动态分配设备号 */ result = alloc_chrdev_region(&devno, 0, 2, CHAR_DEV_NAME); mem_major = MAJOR(devno); } if (result < 0) return result; /*初始化cdev结构*/ cdev_init(&cdev, &mem_fops); cdev.owner = THIS_MODULE; cdev.ops = &mem_fops; /* 注册字符设备 */ cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS); /* 为设备描述结构分配内存*/ mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL); if (!mem_devp) /*申请失败*/ { result = - ENOMEM; goto fail_malloc; } memset(mem_devp, 0, sizeof(struct mem_dev)); /*为设备分配内存*/ for (i=0; i < MEMDEV_NR_DEVS; i++) { mem_devp[i].size = MEMDEV_SIZE; mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL); memset(mem_devp[i].data, 0, MEMDEV_SIZE); } pclass = class_create(THIS_MODULE, CHAR_DEV_NAME); if (IS_ERR(pclass)) { printk("class_create failed!/n"); kfree(mem_devp); goto fail_malloc; } device_create(pclass, NULL, devno, NULL, CHAR_DEV_NAME); return 0; fail_malloc: unregister_chrdev_region(devno, 1); return result; } /*模块卸载函数*/ static void memdev_exit(void) { device_destroy(pclass, MKDEV(mem_major, 0)); class_destroy(pclass); cdev_del(&cdev); /*注销设备*/ kfree(mem_devp); /*释放设备结构体内存*/ unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/ } MODULE_AUTHOR("derek yi"); MODULE_LICENSE("GPL"); module_init(memdev_init); module_exit(memdev_exit);
makefile:
ifneq ($(KERNELRELEASE),) obj-m:=memdev.o else KERNELDIR:=/lib/modules/$(shell uname -r)/build PWD:=$(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -rf *.o *.mod.c *.mod.o *.ko endif
测试代码:
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <linux/fcntl.h> int main() { int fd; char buf[]="this is a example for character devices driver by derek!"; char buf_read[4096]; //memdev设备的内容读入到该buf中 if((fd = open("/dev/memdev",O_RDWR))==-1) //打开memdev设备 printf("open memdev WRONG!\n"); else printf("open memdev SUCCESS!\n"); printf("write: %s\n", buf); write(fd, buf, sizeof(buf)); //把buf中的内容写入memdev设备 lseek(fd, 0, SEEK_SET); //把文件指针重新定位到文件开始的位置 read(fd, buf_read, sizeof(buf)); //把memdev设备中的内容读入到buf_read中 printf("read: %s\n",buf_read); close(fd); return 0; }