#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h>
#include "memdev.h"
static 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); //inode->i_rdev中存放了设备号
//这里要注意struct inode,open close read write等函数的参数是不一样的
//在struct inode中存放有该设备的设备号,struct file 中存放有设备文件相关操作的信息。
if (num >= MEMDEV_NR_DEVS) //这里做检测,
return -ENODEV; dev = &mem_devp[num]; /*将设备描述结构指针赋值给文件私有数据指针*/ filp->private_data = dev;
// 为什么这里要这么操作?
//在read write中的参数没有struct inode,只有struct file ,而设备号在struct inode,
//要获取设备号对应的内存空间,只能在open中这样操作。 //这里的filp->private_data与dev之间的数据类型是一致的吗/
//还是filp->private_data的数据类型是
// void *类型
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; /*获得设备结构体指针*/ // 这里要练习OPEN中的操作
/*判断读位置是否有效*/ if (p >= MEMDEV_SIZE) return 0; if (count > MEMDEV_SIZE - p) count = MEMDEV_SIZE - p;
/*读数据到用户空间*/ // copy_to_user() 如果读取正确,返回0,错误返回大于0
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, };
/*设备驱动模块加载函数*/ static int memdev_init(void) { int result; int i;
// dev_t 设备号类型,MKDEV是宏定义 用于构造设备号类型
dev_t devno = MKDEV(mem_major, 0);
//MKDEV宏操作时构造一个设备号,第一个参数是主设备号,第二个参数是从设备号 // dev_t类型实质是一个unsigned int 类型的数据,包含了主设备号
// 从设备号,数据的前12位表示主设备号 /* 静态申请设备号*/ if (mem_major) result = register_chrdev_region(devno, 2, "memdev"); else /* 动态分配设备号 */ { result = alloc_chrdev_region(&devno, 0, 2, "memdev"); mem_major = MAJOR(devno); // 从dev_t 设备类型中获取主设备好, MAJOR 宏的作用 }
//上边可以看出,获得设备号的两种方法:静态注册与动态获取,各有优劣。
// MAJOR() 宏操作的作用是从已知的设备号中获得主设备号。
//相应由MINOR()宏获得从设备号 if (result < 0) return result;
/*初始化cdev结构*/ cdev_init(&cdev, &mem_fops); //用mem_fops填充cdev中fileoperation
cdev.owner = THIS_MODULE; //控制模块引用计数
// 这里的 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));]
//以上代码不严谨,mem_devp申请的大小是MEMDEV_NR_DEVS * sizeof(struct mem_dev),而memset的时候大小却为sizeof(struct mem_dev),应为2*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); } return 0;
fail_malloc: unregister_chrdev_region(devno, 1); return result; }
/*模块卸载函数*/ static void memdev_exit(void) { cdev_del(&cdev); /*注销设备*/ kfree(mem_devp); /*释放设备结构体内存*/ unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/ }
MODULE_AUTHOR("David Xie"); MODULE_LICENSE("GPL");
module_init(memdev_init); module_exit(memdev_exit); |