这一节没有新的内容,只是将前面(一)(二)中的代码整合起来,形成一个让我们可以运行测试的scull驱动程序(补充了scull_unload脚本)。经过测试,按照如下流程可以在ubuntu14.04上正常运行。
1.scull_load和scull_unload
- scull_load
#!/bin/sh module="scull" device="scull" mode="664" #invoke insmod with all arguments we got #and use a pathname,as newer modutils don't look in . by default /sbin/insmod ./$module.ko $* || exit 1 major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices) #remove stale nodes rm -f /dev/${device}[0-3] mknod /dev/${device}0 c $major 0 mknod /dev/${device}1 c $major 1 mknod /dev/${device}2 c $major 2 mknod /dev/${device}3 c $major 3 #give appropriate group/permissions, and change the group. #Not all distributions have staff, some have "wheel" instead. group="staff" grep -q '^staff:' /etc/group || group="wheel" chgrp $group /dev/${device}[0-3] chmod $mode /dev/${device}[0-3]
- scull_unload
#!/bin/sh module="scull" device="scull" # invoke rmmod with all arguments we got /sbin/rmmod $module $* || exit 1 # Remove stale nodes rm -f /dev/${device} /dev/${device}[0-3]
2.scull.c和scull.h
- scull.h
#ifndef _SCULL_H #define _SCULL_H #ifndef SCULL_MAJOR #define SCULL_MAJOR 0 /* dynamic major by default */ #endif #ifndef SCULL_NR_DEVS #define SCULL_NR_DEVS 4 /* scull0 through scull3 */ #endif #ifndef SCULL_QUANTUM #define SCULL_QUANTUM 4000 #endif #ifndef SCULL_QSET #define SCULL_QSET 1000 #endif struct scull_qset{ void **data; struct scull_qset *next; }; struct scull_dev{ struct scull_qset *data; int quantum; int qset; unsigned long size; unsigned int access_key; struct semaphore sem; struct cdev cdev; }; #endif
- scull.c
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <asm/uaccess.h> #include <linux/semaphore.h> #include <linux/slab.h> #include "scull.h" int scull_major = SCULL_MAJOR; // 主设备号 int scull_minor = 0; // 次设备号 int scull_nr_devs = SCULL_NR_DEVS; // scull设备数量 int scull_quantum = SCULL_QUANTUM; // 量子大小(字节) int scull_qset = SCULL_QSET; // 量子集大小 module_param(scull_major, int, S_IRUGO); module_param(scull_minor, int, S_IRUGO); module_param(scull_nr_devs, int, S_IRUGO); module_param(scull_quantum, int, S_IRUGO); module_param(scull_qset, int, S_IRUGO); struct scull_dev *scull_devices; /*scull长度裁剪为0*/ int scull_trim(struct scull_dev *dev) { struct scull_qset *next,*dptr; int qset = dev->qset; int i; for(dptr = dev->data;dptr;dptr = next)/*遍历链表,访问每个scull_qset节点*/ { if(dptr->data) { for(i=0;i<qset;i++) /*遍历量子集,释放每个量子*/ kfree(dptr->data[i]); kfree(dptr->data); /*释放量子集*/ dptr->data = NULL; } next = dptr->next; kfree(dptr); /*释放scull_qset节点*/ } /*初始化scull_dev*/ dev->size = 0; dev->quantum = scull_quantum; dev->qset = scull_qset; dev->data = NULL; return 0; } /*返回链表中第n个scull_qset*/ struct scull_qset *scull_follow(struct scull_dev *dev, int n) { struct scull_qset *qs = dev->data; // 指向第0个scull_qset /* 第0个为空的话,则分配 */ if (! qs) { qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL); if (qs == NULL) return NULL; /* Never mind */ memset(qs, 0, sizeof(struct scull_qset)); } /* 遍历列表,指向第N个scull_qset */ while (n--) { if (!qs->next) { qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL); if (qs->next == NULL) return NULL; memset(qs->next, 0, sizeof(struct scull_qset)); } qs = qs->next; continue; } return qs; } int scull_open(struct inode *inode,struct file *filp) { struct scull_dev *dev; dev = container_of(inode->i_cdev,struct scull_dev,cdev); filp->private_data = dev; /*保存到private_data域,以便其他方法使用*/ /*如果是只写打开,长度裁剪为0*/ if((filp->f_flags & O_ACCMODE) == O_WRONLY){ scull_trim(dev); /*ignore errors*/ } return 0; /*success*/ } int scull_release(struct inode *inode,struct file *filp) { return 0; } ssize_t scull_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos) { struct scull_dev *dev = filp->private_data; /*在open中,我们把dev保存在了file的private_data域*/ struct scull_qset *dptr; int quantum = dev->quantum, qset = dev->qset; /*quantum是量子大小,qset是一个量子集有多少量子*/ int itemsize = quantum * qset; /*计算链表中一个scull_qset节点 指向的空间大小*/ int item,s_pos,q_pos,rest; ssize_t retval = 0; if(down_interruptible(&dev->sem)) return -ERESTARTSYS; if(*f_pos >= dev->size) /*判断文件当前位置是否超出设备区域*/ goto out; if(*f_pos + count > dev->size) /*判断当前位置+写入的字节是否会超出设备区域*/ count = dev->size - *f_pos; item = (long)*f_pos / itemsize; /*计算是第几个scull_qset节点*/ rest = (long)*f_pos % itemsize; s_pos = rest / quantum; /*计算是量子集中第几个量子*/ q_pos = rest % quantum; /*计算在量子中的偏移*/ dptr = scull_follow(dev,item); /*返回指针指向相应scull_qset节点*/ if(dptr == NULL || !dptr->data || !dptr->data[s_pos]) goto out; if(count > quantum - q_pos) /*一个量子中放不下要写入的数据,那么只写入能写下的*/ count = quantum-q_pos; if(copy_to_user(buf,dptr->data[s_pos] + q_pos,count)) /*copy_to_user*/ { retval = -EFAULT; goto out; } *f_pos += count; /*更新文件位置指示*/ retval = count; /*更新实际写入字节*/ out: up(&dev->sem); return retval; } ssize_t scull_write(struct file *flip,const char __user *buf,size_t count,loff_t *f_pos) { struct scull_dev *dev = flip->private_data; struct scull_qset *dptr; int quantum = dev->quantum,qset = dev->qset; int itemsize = quantum * qset; int item,s_pos,q_pos,rest; ssize_t retval = -ENOMEM ; if(down_interruptible(&dev->sem)) return -ERESTARTSYS; item = (long)*f_pos / itemsize; rest = (long)*f_pos % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; /*在此之前都与read相同, 不同的地方在于,写数据的时候要为量子集和量子开辟空间 */ dptr = scull_follow(dev,item); if(dptr == NULL ) goto out; if(!dptr->data)/*为量子集开辟空间*/ { dptr->data = kmalloc(qset * sizeof(char *),GFP_KERNEL); if(!dptr->data) goto out; memset(dptr->data,0,qset * sizeof(char *)); } if(!dptr->data[s_pos]) /*为量子开辟空间*/ { dptr->data[s_pos] = kmalloc(quantum,GFP_KERNEL); if(!dptr->data[s_pos]) goto out; } if(count > quantum - q_pos) count = quantum - q_pos; if(copy_from_user(dptr->data[s_pos]+q_pos,buf,count)) { retval = -EFAULT; goto out; } *f_pos += count; retval = count; /*更新设备size大小*/ if(dev->size < *f_pos) dev->size = *f_pos; out: up(&dev->sem); return retval; } struct file_operations scull_fops = { .owner = THIS_MODULE, .open = scull_open, .release = scull_release, .read = scull_read, .write = scull_write, }; void scull_cleanup_module(void) { int i; dev_t devno = MKDEV(scull_major, scull_minor); if (scull_devices) { for (i = 0; i < scull_nr_devs; i++) { scull_trim(scull_devices + i); cdev_del(&scull_devices[i].cdev); } kfree(scull_devices); } unregister_chrdev_region(devno, scull_nr_devs); printk(KERN_DEBUG "scull_cleanup_module\n"); } static void scull_setup_cdev(struct scull_dev *dev, int index) { int err,devno = MKDEV(scull_major,scull_minor+index); cdev_init(&dev->cdev,&scull_fops); dev->cdev.owner = THIS_MODULE; //dev->cdev.ops = &scull_fops; /*cdev_init中明明完成了这一条代码,为何要重复???*/ err = cdev_add(&dev->cdev,devno,1); if(err) printk(KERN_NOTICE "Error %d adding scull%d",err,index); } int scull_init_module(void) { int result,i; dev_t dev = 0; /*注册设备号*/ if(scull_major) //scull_major如果是默认值0,就跳转else,动态分配设备号 { dev = MKDEV(scull_major,scull_minor); result = register_chrdev_region(dev,scull_nr_devs,"scull"); }else{ result = alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull"); scull_major = MAJOR(dev); } if(result < 0) { printk(KERN_WARNING"scull:cant't get major %d\n",scull_major); return result; } scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL); if (!scull_devices) { result = -ENOMEM; goto fail; /* Make this more graceful */ } memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev)); /*初始化字符设备(共4个)*/ for (i = 0; i < scull_nr_devs; i++) { scull_devices[i].quantum = scull_quantum; scull_devices[i].qset = scull_qset; sema_init(&scull_devices[i].sem,1); scull_setup_cdev(&scull_devices[i], i); } printk(KERN_DEBUG "scull_init_module succeed\n"); return 0; fail: scull_cleanup_module(); return result; } module_init(scull_init_module); module_exit(scull_cleanup_module); MODULE_LICENSE("Dual BSD/GPL");
3.scull测试
-
将Makefile和scull.c scull.h放在同一个文件夹下,运行make命令编译生成ko文件。
-
执行scull_load脚本文件
sudo ./scull_load
-
使用ls和cat命令进行测试(可能需要执行 sudo -i 进入root用户才能正确运行,只是sudo可能不行)
执行
ls -l > /dev/scull0
执行cat /dev/scull0
查看结果
成功~!用scull_unload卸载吧!