/*scull.h*/ /*main.c*/ /*=========================================*/ /*头文件相关定义*/ 1.定义scull_dev结构体用来描述scull设备储存结构 /*主函数流程分析*/ 1.在程序的开始处使用module_param定义模块参数 2.初始化模块module_init(scull_init_module){ scull_init_module分析: (1)获得内核识别的设备号若动态分配则调用alloc_chrdev_region,若已知则调用register_chrdev_region (2)使用kmalloc动态的给scull_dev设备结构体在普通内核内存空间分配内存空间,并使用memset初始化为0 (3)初始化每个设备的访问区块(即内存区块),设定量子和量子集,互斥锁,调用scull_setup_cdev向内核注册这个char device scull_setup_cdev注册char设备的流程: a.调用MKDEV由实参生成具体设备的设备号 b.初始化cdev,主要是指定其ops cdev_init(&dev->cdev, &scull_fops).dev->cdev在设备结构体中定义. 关于定义设备的file_operations fops.设备的访问等同与文件,所以这里需要具体指定打开文件时候的操作. struct file_operations scull_fops = { .owner = THIS_MODULE, .llseek = scull_llseek, .read = scull_read, ...};/*注意这里标记化结构体初始化的语法*/ c.cdev的初始化,指定所有者 dev->cdev.owner = THIS_MODULE; d.注册cdev cdev_add (&dev->cdev, devno, 1); (4)初始化其他相关友好的设备/*这里是指pipe和有open control机制的设备实现,本次不学习*/ (5)调用scull_create_proc建立通过proc文件的debug机制需要的proc文件 /*注意:这里使用goto的方式对每步骤出现的错误进行恢复*/ 调用scull_cleanup_module进行上述步骤的错误退出. } 3.退出并注销模块module_exit(scull_cleanup_module){ (1).获得设备号MKDEV (2).调用scull_trim来清除dev设备文件,对在初始化分配内存的所有步骤都作kfree释放操作,对设备结构体数组值重新设定. /*注意scull_trim必须被拥有旗帜的函数所调用,而且其中所以DATA区域在释放后依然赋值为NULL是从安全角度出发*/ (3).删除已经注册的char设备:cdev_del (4).释放设备结构体 (5).调用scull_remove_proc删除初始化阶段创立的proc文件调试机制 (6).调用unregister_chrdev_region注销注册的设备号 (7).注销其他相关友好的设备 } 4.关于对设备读写等的file-ops函数的定义(){ (1)open(){ 原型:int (*open)(struct inode *inode, struct file *filp) a.通过indode->i_cdev(其本身指向scull_dev的cdev)获得scull_dev的入口指针/*识别需要被打开的设备*/ dev = container_of(inode->i_cdev, struct scull_dev, cdev); b.将获得的dev保存到filp->private_data filp->private_data = dev c.如果是以只读模式打开则调用scull_trim()截短设备为0 filp->f_flags & O_ACCMODE) == O_WRONLY,判读其打开模式 对访问区域加锁down_interruptible(&dev->sem) 调用scull_trim(dev) 解锁up(&dev->sem); } (2)release(){/*作用和open相反*/ 原型:int (*release)(struct inode *inode, struct file *filp) } (3)read(){ 原型 ssize_t read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos) a.通过filp->private_data获得已经保存在其中的设备结构体入口指针:struct scull_dev *dev = filp->private_data; b.初始化一些结构体和变量.比如scull_qset结构体,量子和量子集等等. c.加锁down_interruptible(&dev->sem) d.判读off_t *f_pos参数是否超越了设备存储的范围:*f_pos >= dev->size 修正能够返回数据的长度(*f_pos count > dev->size) && (count = dev->size - *f_pos) e.从f_pos获得find listitem, qset index, and offset in the quantum f.调用scull_follow依据e步的item获得其设备结构体中的入口地址(即需要的qset的地址),即通过qset->next寻找item-1次即可,如果qset还没有被实体化,那么就调用kmalloc,memset即可. 找到后,判读qset地址,其指向的量子集,和量子集中的量子的正确性. g./*注意:我们在这里约定每次读和写只操作一个量子*/ 所以要根据实际量子中的off和量子长度再次调整count值 h.调用copy_to_user将内核数据复制到用户区域. i.f_pos处理加上实际读出来的count j.错误和结束:解锁即可. } (4)write(){ 原型 ssize_t write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos) 基本同read,只是h步骤使用copy_from_user. /*注意如果内核访问用户区域,由于user域存在分页机制,进程有可能被休眠以等待不再当前页中的数据,所以要求copy_from_user函数是可重入的.*/ } (5)llseek(){ 原型:(*llseek)(struct file *filp, loff_t off, int whence) a.通过filp->private_data获得已经保存在其中的设备结构体入口指针 b.根据llseek的第二个参数,返回不同的地址 c.指定filp->f_pos为b步的返回值 } 5.iotcl的实现(){ 具体间收获 } } /*=========================================*/ /*收获*/ /*=========================================*/ 1.如何用宏定义来实现DEBUG调试(){ 程序中: #undef PDEBUG /* undef it, just in case */ #ifdef SCULL_DEBUG # ifdef __KERNEL__ /* This one if debugging is on, and kernel space */ # define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull: " fmt, ## args) # else /* This one for user space */ # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) # endif #else /* not debugging: nothing */ # define PDEBUG(fmt, args...) #endif 编译时候: gcc -DSCULL_DEBUG/*即define SCULL_DEBUG*/ } /*--------------------------------*/ 2.ioctl向驱动程序发出请求在驱动中的实现(){ 1.定义ioctl的cmd参数 #define SCULL_IOC_MAGIC 'k'/* Use 'k' as magic number */ #define SCULL_XXX0 _IO(SCULL_IOC_MAGIC, 0)/*无参数的,即无数据传递*/ #define SCULL_XXX1 _IOW(SCULL_IOC_MAGIC, 1,int)/*无参数的,即无数据传递*/ #define SCULL_XXX2 _IOR(SCULL_IOC_MAGIC, 2,int)/*无参数的,即无数据传递*/ 2.驱动程序中如何实现ioctl 原型:int (*ioctl)(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg) a.判断cmd类型和个数是否正确,调用_IOC_TYPE,_IOC_NR b.如果有数据通过指针传递,首先检查传递指针是否有效:access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)) 调用_IOC_DIR,_IOC_SIZE分别得到cmd的方向和参数的长度 /*注意如果cmd是读,那么要检查user是否能被内核写,反之则反之*/ c.经典的switch结构,根据cmd分别做分别的事情. 其中使用指针传递参数的实现方法: 从用户空间传递到内核:__get_user(内核变量名,(int __user *)参数), 反之则用__put_user(内核变量名,(int __user *)参数) } /*--------------------------------*/ 3.设备访问区域使用semaphore实现mutual exclusion的流程(){ 相关头文件asm/semaphore.h 1.定义旗帜变量与设备结构体中:xxx_dev:struct semaphore sem 2.在设备访问区块初始时候,初始化旗帜变量init_MUTEX(xxx_dev.sem) 3.具体运用: 对访问区域加锁down_interruptible(&xxx_dev->sem) 解锁up(&xxx_dev->sem); } /*--------------------------------*/ 4.如何建立通过proc的debug机制(){ 方法一:通过一般的/proc文件 1.创建对/proc文件可读的方法函数 这个函数虽然是自己定义,但是有共同的type,如下 int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data);也就是说要把返回的信息写在kernel中的buf区域 然后可以根据自己的需要写对应的读函数,本例中是int scull_read_procmem(char *buf, char **start, off_t offset,int count, int *eof, void *data) 2.将上步骤的函数和具体的.proc文件关联 定义文件相关结构体:struct proc_dir_entry *entry; create_proc_read_entry,创建文件并使之关联read函数 方法二:通过seq_file文件接口 0.根据seq_file ops的原型定义自己的函数,这些主要用于open时寻找iterator scull_seq_start,scull_seq_next,scull_seq_stop,scull_seq_show /*在这些函数的创建中尽量使用seq对应的一些函数如seq_printf,seq_putc等*/ 1.创建seq文件的ops static struct seq_operations scull_seq_ops = { .start = scull_seq_start, .next = scull_seq_next,...}; 2.创建file-ops需要的函数open,同时也注册好上述seq的ops static int scull_proc_open(struct inode *inode, struct file *file) {return seq_open(file, &scull_seq_ops);}/*由于seq文件(iterator)需要特别的寻找方法,即seq ops提供的*/ 3.创建文件ops结构体,指定对该文件的相关操作的函数 static struct file_operations scull_proc_ops = { .owner = THIS_MODULE, .open = scull_proc_open,/*使用自己定的open*/ .read = seq_read,...};/*使用seq的函数*/ 4.创建真实的文件,并指定文件的ops entry = create_proc_entry("scullseq", 0, NULL); if (entry) entry->proc_fops = &scull_proc_ops; 关于注销对应文件两种方法的做法是一样的 remove_proc_entry("name", NULL /* parent dir */); /*注意对设备有互斥锁部分访问时候的down up机制的运用.*/ } /*=========================================*/ /*遗留*/ /*=========================================*/ 1. 主函数分析 2->(4)步骤涉及用pipe实现和有open control机制,本次暂不学习 类似的有3->(7) /*=========================================*/ |
|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )
GMT+8, 2024-9-30 11:25 , Processed in 0.095611 second(s), 12 queries , Gzip On, MemCache On.
Powered by Discuz! X3.5
© 2001-2023 Discuz! Team.