您的位置:首页 > 编程语言

ldd3代码分析(高级字符驱动)

2007-02-08 00:31 513 查看



/**//*


* main.c -- the bare scull char module


*


* 此代码为ldd3例子,自己加了些注释;希望可以和更多和我同样有兴趣的鸟儿们一块学习讨论。


* 哪有注释的不对的地方请发mail给我,或留言;


*


* author : liyangth@gmail.com


*


* date: 2007-2-7


*


* Note:注释的每一个关键的段都以[tag00]作了标签,大家可以按照tag的顺序阅读;


* e.g: 搜索 "Tag000"


*/




#include <linux/config.h>


#include <linux/module.h>


#include <linux/moduleparam.h>


#include <linux/init.h>






#include <linux/kernel.h> /**//* printk() */




#include <linux/slab.h> /**//* kmalloc() */




#include <linux/fs.h> /**//* everything... */




#include <linux/errno.h> /**//* error codes */




#include <linux/types.h> /**//* size_t */


#include <linux/proc_fs.h>




#include <linux/fcntl.h> /**//* O_ACCMODE */


#include <linux/seq_file.h>


#include <linux/cdev.h>






#include <asm/system.h> /**//* cli(), *_flags */




#include <asm/uaccess.h> /**//* copy_*_user */






#include "scull.h" /**//* local definitions */






/**//*


* Our parameters which can be set at load time.


*/




int scull_major = SCULL_MAJOR;


int scull_minor = 0;




int scull_nr_devs = SCULL_NR_DEVS; /**//* number of bare scull devices */


int scull_quantum = SCULL_QUANTUM;


int scull_qset = SCULL_QSET;






/**//*


* 模块参数,可在模块转载时赋值,很灵活方便;


* e.g:


* insmod scull.ko scull_major=111 scull_nr_devs=3 scull_quantum=1000


*


*[形参说明]


* 1 -- 变量名;


* 2 -- 变量类型;


* 3 -- sysfs入口项的访问许可掩码(一般用S_IRUGO就成);


*/


module_param(scull_major, 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);




MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");


MODULE_LICENSE("Dual BSD/GPL");






struct scull_dev *scull_devices; /**//* allocated in scull_init_module */




/**//* Note: 不要把它理解成一个指向scull_dev结构的指针, 它其实是一个scull_dev结构数组,等待下面kmalloc分配多个我们scull设备空间 */








/**//*


* Empty out the scull device; 就像销毁链表,和理解如何编写一个字符驱动没有关系,可以不看;


*


* must be called with the device semaphore held. 要注意一下了,肯定是要同步的;


*


*/


int scull_trim(struct scull_dev *dev)




...{


struct scull_qset *next, *dptr;




int qset = dev->qset; /**//* "dev" is not-null */


int i;






for (dptr = dev->data; dptr; dptr = next) ...{ /**//* all the list items */




if (dptr->data) ...{


for (i = 0; i < qset; i++)


kfree(dptr->data[i]);


kfree(dptr->data);


dptr->data = NULL;


}


next = dptr->next;


kfree(dptr);


}


dev->size = 0;


dev->quantum = scull_quantum;


dev->qset = scull_qset;


dev->data = NULL;


return 0;


}




//Start: [Tag003] proc的实现,可以先不看;




#ifdef SCULL_DEBUG /**//* use proc only if debugging */




/**//*


* The proc filesystem: function to read and entry


*/




int scull_read_procmem(char *buf, char **start, off_t offset,


int count, int *eof, void *data)




...{


int i, j, len = 0;




int limit = count - 80; /**//* Don't print more than this */






for (i = 0; i < scull_nr_devs && len <= limit; i++) ...{


struct scull_dev *d = &scull_devices[i];


struct scull_qset *qs = d->data;


if (down_interruptible(&d->sem))


return -ERESTARTSYS;


len += sprintf(buf+len," Device %i: qset %i, q %i, sz %li ",


i, d->qset, d->quantum, d->size);




for (; qs && len <= limit; qs = qs->next) ...{ /**//* scan the list */


len += sprintf(buf + len, " item at %p, qset at %p ",


qs, qs->data);




if (qs->data && !qs->next) /**//* dump only the last item */




for (j = 0; j < d->qset; j++) ...{


if (qs->data[j])


len += sprintf(buf + len,


" % 4i: %8p ",


j, qs->data[j]);


}


}


up(&scull_devices[i].sem);


}


*eof = 1;


return len;


}








/**//*


* For now, the seq_file implementation will exist in parallel. The


* older read_procmem function should maybe go away, though.


*/






/**//*


* Here are our sequence iteration methods. Our "position" is


* simply the device number.


*/


static void *scull_seq_start(struct seq_file *s, loff_t *pos)




...{


if (*pos >= scull_nr_devs)




return NULL; /**//* No more to read */


return scull_devices + *pos;


}




static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos)




...{


(*pos)++;


if (*pos >= scull_nr_devs)


return NULL;


return scull_devices + *pos;


}




static void scull_seq_stop(struct seq_file *s, void *v)




...{




/**//* Actually, there's nothing to do here */


}




static int scull_seq_show(struct seq_file *s, void *v)




...{


struct scull_dev *dev = (struct scull_dev *) v;


struct scull_qset *d;


int i;




if (down_interruptible(&dev->sem))


return -ERESTARTSYS;


seq_printf(s, " Device %i: qset %i, q %i, sz %li ",


(int) (dev - scull_devices), dev->qset,


dev->quantum, dev->size);




for (d = dev->data; d; d = d->next) ...{ /**//* scan the list */


seq_printf(s, " item at %p, qset at %p ", d, d->data);




if (d->data && !d->next) /**//* dump only the last item */




for (i = 0; i < dev->qset; i++) ...{


if (d->data[i])


seq_printf(s, " % 4i: %8p ",


i, d->data[i]);


}


}


up(&dev->sem);


return 0;


}






/**//*


* Tie the sequence operators up.


*/




static struct seq_operations scull_seq_ops = ...{


.start = scull_seq_start,


.next = scull_seq_next,


.stop = scull_seq_stop,


.show = scull_seq_show


};






/**//*


* Now to implement the /proc file we need only make an open


* method which sets up the sequence operators.


*/


static int scull_proc_open(struct inode *inode, struct file *file)




...{


return seq_open(file, &scull_seq_ops);


}






/**//*


* Create a set of file operations for our proc file.


*/




static struct file_operations scull_proc_ops = ...{


.owner = THIS_MODULE,


.open = scull_proc_open,


.read = seq_read,


.llseek = seq_lseek,


.release = seq_release


};








/**//*


* Actually create (and remove) the /proc file(s).


*/




static void scull_create_proc(void)




...{


struct proc_dir_entry *entry;




create_proc_read_entry("scullmem", 0 /**//* default mode */,




NULL /**//* parent dir */, scull_read_procmem,




NULL /**//* client data */);


entry = create_proc_entry("scullseq", 0, NULL);


if (entry)


entry->proc_fops = &scull_proc_ops;


}




static void scull_remove_proc(void)




...{




/**//* no problem if it was not registered */




remove_proc_entry("scullmem", NULL /**//* parent dir */);


remove_proc_entry("scullseq", NULL);


}






#endif /* SCULL_DEBUG */


//End










/**//* 开始实现对设备操作的方法集了,关键!!! */




/**//*


* Open and close


*/


//[Tag004]




/**//*


open应完成的工作有:


1.检查设备特定的错误(诸如设备未就绪或类似的硬件问题)


2.如果设备是首次打开,则对其进行初始化;


3.如有必要,更新f_op指针;


4.分配并填写filp->private_data;(在这里我们只实现这项即可)


*/






/**//*


[形参说明]


struct inode *inode -- 用它的i_cdev成员得到dev;


struct file *filp -- 将得到的dev存放到他的成员private_data中;


*/


int scull_open(struct inode *inode, struct file *filp)




...{




struct scull_dev *dev; /**//* device information */




dev = container_of(inode->i_cdev, struct scull_dev, cdev);




/**//*


[说明]


1.我们要填充的应该是我们自己的特殊设备,而不是钳在他里面的字符设备结构;


2.inode结构的i_cdev成员这能提供基本字符设备结构;


3.这里利用了定义在<linux/kernel.h>中的宏来实现通过cdev得到dev;


*/






/**//*


以后read , write ,等操作的实现中就靠他来得到dev了;


*/




filp->private_data = dev; /**//* for other methods */








/**//* now trim to 0 the length of the device if open was write-only */




if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) ...{


if (down_interruptible(&dev->sem))


return -ERESTARTSYS;




scull_trim(dev); /**//* ignore errors */


up(&dev->sem);


}




return 0; /**//* success */


}






/**//* close device file, in here we do nothing */




/**//*


* [Tag005]


* close应完成的工作有:


* 1.释放由open分配的,保存在filp->private_data中的所有内容;


* 2.在最后一次关闭操作时关闭设备;


* [注意:]并不是每次的close系统调用都会去调用到release. 在open时,也仅在open时才会创建


* 一个新的数据结构;在fork, dup时只是增加了这个结构中维护的一个引用计数;


* 所以当这个引用计数为0时,调用的close才意味着要释放设备数据结构,此时release才会被调用;


*/


int scull_release(struct inode *inode, struct file *filp)




...{


return 0;


}








/**//*


* Follow the list


*


* 第一次调用时用于创建链表;


* 然后就是找到第n个节点;


* 对编写驱动程序关系不大;


*/


struct scull_qset *scull_follow(struct scull_dev *dev, int n)




...{


struct scull_qset *qs = dev->data;






/**//* Allocate first qset explicitly if need be */




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));


}






/**//* Then follow the list */




while (n--) ...{




if (!qs->next) ...{


qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);


if (qs->next == NULL)




return NULL; /**//* Never mind */


memset(qs->next, 0, sizeof(struct scull_qset));


}


qs = qs->next;


continue;


}


return qs;


}






/**//*[Tag006]


* Data management: read and write


* [read和write的参数]


* 1] filp -- 文件指针;用它的成员filp->private_data得到dev;


* 2] buf -- 都是来自用户空间的指针;


* 3] count -- 缓冲区大小;(希望传输的字节数目)


* 4] f_pos -- 指向一个长偏移量对象的指针,这个对象指明了用户在文件中进行存取


* 操作的位置;


*


*[返回值]


* 1]如果返回值等于count,则完成了所请求数目的字节传输;


* 2]如果返回值是正,但小于count,则继续读或写余下的数据;


* 3]如果为0,则证明已经到了文件尾;


* 4]如果为负,则发生了错误。会返回一个错误码,该值指明了发生了什么错误。


* 错误码在<linux/errno.h>中定义;


* 例如:-EINTR (系统调用被中断)


* -EFAULT (无效地址)


*/






ssize_t scull_read(struct file *filp, char __user *buf, size_t count,


loff_t *f_pos)




...{


struct scull_dev *dev = filp->private_data;




struct scull_qset *dptr; /**//* the first listitem */


int quantum = dev->quantum, qset = dev->qset;




int itemsize = quantum * qset; /**//* how many bytes in the listitem */


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; //减小这次的期望读取数目






/**//* find listitem, qset index, and offset in the quantum */


item = (long)*f_pos / itemsize; //确定是哪个链表项下,即哪个节点下;


rest = (long)*f_pos % itemsize; //在这个链表项的什么位置(偏移量),用于下面找qset索引和偏移量;


s_pos = rest / quantum; //在这个节点里**data这个指针数组的第几行;


q_pos = rest % quantum; //在这行,即这个量子里的偏移量;






/**//* follow the list up to the right position (defined elsewhere) */


dptr = scull_follow(dev, item); //找到这个链表项




if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])




goto out; /**//* don't fill holes */




//以一个量子为单位传,简化了代码;




/**//* read only up to the end of this quantum */


if (count > quantum - q_pos)


count = quantum - q_pos;






/**//*


* 上面为这步准备了具体在哪个链表项的指针数组的第几行的第几列(即dptr->data[s_pos] + q_pos)


* 从这个位置的内核态的buf中拷给用户态


*/




//关键一步,将数据拷给用户空间




if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) ...{


retval = -EFAULT;


goto out;


}


*f_pos += count; //更新文件指针


retval = count;




out:


up(&dev->sem);


return retval;


}




//与read的实现类似


ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,


loff_t *f_pos)




...{


struct scull_dev *dev = filp->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; /**//* value used in "goto out" statements */




if (down_interruptible(&dev->sem))


return -ERESTARTSYS;






/**//* find listitem, qset index and offset in the quantum */


item = (long)*f_pos / itemsize;


rest = (long)*f_pos % itemsize;


s_pos = rest / quantum; q_pos = rest % quantum;






/**//* follow the list up to the right position */


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;


}




/**//* write only up to the end of this quantum */


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;






/**//* update the size */


if (dev->size < *f_pos)


dev->size = *f_pos;




out:


up(&dev->sem);


return retval;


}






/**//*


* The ioctl() implementation


*/




int scull_ioctl(struct inode *inode, struct file *filp,


unsigned int cmd, unsigned long arg)




...{




int err = 0, tmp;


int retval = 0;






/**//*


* extract the type and number bitfields, and don't decode


* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()


*/


if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;


if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;






/**//*


* the direction is a bitmask, and VERIFY_WRITE catches R/W


* transfers. `Type' is user-oriented, while


* access_ok is kernel-oriented, so the concept of "read" and


* "write" is reversed


*/


if (_IOC_DIR(cmd) & _IOC_READ)


err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));


else if (_IOC_DIR(cmd) & _IOC_WRITE)


err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));


if (err) return -EFAULT;






switch(cmd) ...{




case SCULL_IOCRESET:


scull_quantum = SCULL_QUANTUM;


scull_qset = SCULL_QSET;


break;






case SCULL_IOCSQUANTUM: /**//* Set: arg points to the value */


if (! capable (CAP_SYS_ADMIN))


return -EPERM;


retval = __get_user(scull_quantum, (int __user *)arg);


break;






case SCULL_IOCTQUANTUM: /**//* Tell: arg is the value */


if (! capable (CAP_SYS_ADMIN))


return -EPERM;


scull_quantum = arg;


break;






case SCULL_IOCGQUANTUM: /**//* Get: arg is pointer to result */


retval = __put_user(scull_quantum, (int __user *)arg);


break;






case SCULL_IOCQQUANTUM: /**//* Query: return it (it's positive) */


return scull_quantum;






case SCULL_IOCXQUANTUM: /**//* eXchange: use arg as pointer */


if (! capable (CAP_SYS_ADMIN))


return -EPERM;


tmp = scull_quantum;


retval = __get_user(scull_quantum, (int __user *)arg);


if (retval == 0)


retval = __put_user(tmp, (int __user *)arg);


break;






case SCULL_IOCHQUANTUM: /**//* sHift: like Tell + Query */


if (! capable (CAP_SYS_ADMIN))


return -EPERM;


tmp = scull_quantum;


scull_quantum = arg;


return tmp;




case SCULL_IOCSQSET:


if (! capable (CAP_SYS_ADMIN))


return -EPERM;


retval = __get_user(scull_qset, (int __user *)arg);


break;




case SCULL_IOCTQSET:


if (! capable (CAP_SYS_ADMIN))


return -EPERM;


scull_qset = arg;


break;




case SCULL_IOCGQSET:


retval = __put_user(scull_qset, (int __user *)arg);


break;




case SCULL_IOCQQSET:


return scull_qset;




case SCULL_IOCXQSET:


if (! capable (CAP_SYS_ADMIN))


return -EPERM;


tmp = scull_qset;


retval = __get_user(scull_qset, (int __user *)arg);


if (retval == 0)


retval = put_user(tmp, (int __user *)arg);


break;




case SCULL_IOCHQSET:


if (! capable (CAP_SYS_ADMIN))


return -EPERM;


tmp = scull_qset;


scull_qset = arg;


return tmp;






/**//*


* The following two change the buffer size for scullpipe.


* The scullpipe device uses this same ioctl method, just to


* write less code. Actually, it's the same driver, isn't it?


*/




case SCULL_P_IOCTSIZE:


scull_p_buffer = arg;


break;




case SCULL_P_IOCQSIZE:


return scull_p_buffer;








default: /**//* redundant, as cmd was checked against MAXNR */


return -ENOTTY;


}


return retval;




}










/**//*


* The "extended" operations -- only seek


*/




loff_t scull_llseek(struct file *filp, loff_t off, int whence)




...{


struct scull_dev *dev = filp->private_data;


loff_t newpos;






switch(whence) ...{




case 0: /**//* SEEK_SET */


newpos = off;


break;






case 1: /**//* SEEK_CUR */


newpos = filp->f_pos + off;


break;






case 2: /**//* SEEK_END */


newpos = dev->size + off;


break;






default: /**//* can't happen */


return -EINVAL;


}


if (newpos < 0) return -EINVAL;


filp->f_pos = newpos;


return newpos;


}






//[Tag007]将这组操作打包为一个对象;




struct file_operations scull_fops = ...{


.owner = THIS_MODULE,


.llseek = scull_llseek,


.read = scull_read,


.write = scull_write,


.ioctl = scull_ioctl,


.open = scull_open,


.release = scull_release,


};






/**//*


* Finally, the module stuff


*/






//[Tag008]模块卸载或goto fail时;




/**//*


* The cleanup function is used to handle initialization failures as well.


* Thefore, it must be careful to work correctly even if some of the items


* have not been initialized


*/


void scull_cleanup_module(void)




...{


int i;


dev_t devno = MKDEV(scull_major, scull_minor);






/**//* Get rid of our char dev entries */




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);


}






#ifdef SCULL_DEBUG /**//* use proc only if debugging */


scull_remove_proc();


#endif






/**//* cleanup_module is never called if registering failed */


unregister_chrdev_region(devno, scull_nr_devs);






/**//* and call the cleanup functions for friend devices */


scull_p_cleanup();


scull_access_cleanup();




}








/**//* [Tag002]


这里主要干了2件事;


在内核内部使用struct cdev结构来表示字符设备;


[1]在这里因为我们将cdev结构嵌入到自己的scull_dev设备下了,所以我们用下面这个方法来


初始化已分配的结构;


cdev_init(&dev->cdev, &scull_fops);




[2]告诉内核我们新结构的信息;


*/




/**//*


* Set up the char_dev structure for this device.


*/


static void scull_setup_cdev(struct scull_dev *dev, int index)




...{


int err, devno = MKDEV(scull_major, scull_minor + index);




// [1]




cdev_init(&dev->cdev, &scull_fops); /**//* 初始化, 字符设备和给它一组在它上面操作的方法集 */






/**//* 填充基本字符设备的成员 */


dev->cdev.owner = THIS_MODULE; //模块计数


dev->cdev.ops = &scull_fops; //附上一组操作自己的方法集




// [2]


err = cdev_add (&dev->cdev, devno, 1);




/**//*


函数说明:


cdev -- 字符设备的结构指针,我们就是要把他告诉给内核;


devno -- 设备编号,用MKDEV利用全局的主设备号和次设备号生成的;


1 -- 是应该和该设备关联的设备编号的数量, 一般情况下都为1;


一般我们都是一个设备编号对应一个设备;


*/




/**//*


注意:


在调用cdev_add后,我们的设备就被添加到系统了,他"活"了. 附加的操作集也就可以被内核调用了


,因此,在驱动程序还没有完全准备好处理设备上的操作时,就不能调用cdev_add!


*/




/**//* Fail gracefully if need be */


if (err)


printk(KERN_NOTICE "Error %d adding scull%d", err, index);


}






/**//*[Tag000]


* 当模块加载时,调用;但是为什么要放在最后来实现他呢,看到Tag002时,你应该就明白了;


*/


int scull_init_module(void)




...{


int result, i;


dev_t dev = 0;






/**//* [Tag001] */




/**//* [1]分配设备编号 */




/**//*


* Get a range of minor numbers to work with, asking for a dynamic


* major unless directed otherwise at load time.


*/




if (scull_major) ...{ /**//* 预先自己指定了主设备号 */




dev = MKDEV(scull_major, scull_minor); /**//* 利用主设备号,找到设备编号给方法1用 */


result = register_chrdev_region(dev, scull_nr_devs, "scull");




} else ...{ /**//* 动态自己生成设备编号,然后再利用设备编号得到主设备号;


记住如果用这个方法那么就要后建设备文件了,因为不能提前知道主号


当然也可以利用ldd3书中提供的脚本,巨方便&&通用 */


result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,


"scull");


scull_major = MAJOR(dev);


}




if (result < 0) ...{


printk(KERN_WARNING "scull: can't get major %d ", scull_major);


return result;


}






/**//*[2]设备对象实例化*/




/**//*


* allocate the devices -- we can't have them static, as the number


* can be specified at load time


*/


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));






/**//* [3]在这里初始化设备用了2.6的新方法,在scull_setup_cdev里完成 */




/**//* Initialize each device. */




for (i = 0; i < scull_nr_devs; i++) ...{




scull_devices[i].quantum = scull_quantum; /**//* 可以根据自己insmod时传参


来自己改变量子和量子集(指针数组)的大小 */


scull_devices[i].qset = scull_qset;


init_MUTEX(&scull_devices[i].sem);




scull_setup_cdev(&scull_devices[i], i); /**//* 在分别完主设备编号后goto Tag002 设备注册 */


}






/**//* At this point call the init function for any friend device */


dev = MKDEV(scull_major, scull_minor + scull_nr_devs);


dev += scull_p_init(dev);


dev += scull_access_init(dev);






#ifdef SCULL_DEBUG /**//* only when debugging */


scull_create_proc();


#endif






return 0; /**//* succeed */




fail:


scull_cleanup_module();


return result;


}




module_init(scull_init_module); //insmod


module_exit(scull_cleanup_module); //rmmod






/**//*


* scull.h -- definitions for the char module


*


* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet


* Copyright (C) 2001 O'Reilly & Associates


*


* The source code in this file can be freely used, adapted,


* and redistributed in source or binary form, so long as an


* acknowledgment appears in derived source files. The citation


* should list that the code comes from the book "Linux Device


* Drivers" by Alessandro Rubini and Jonathan Corbet, published


* by O'Reilly & Associates. No warranty is attached;


* we cannot take responsibility for errors or fitness for use.


*


* $Id: scull.h,v 1.15 2004/11/04 17:51:18 rubini Exp $


*/




#ifndef _SCULL_H_


#define _SCULL_H_






#include <linux/ioctl.h> /**//* needed for the _IOW etc stuff used later */






/**//*


* Macros to help debugging


*/




#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




# define PDEBUG(fmt, args...) /**//* not debugging: nothing */


#endif




#undef PDEBUGG


#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */




#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_P_NR_DEVS


#define SCULL_P_NR_DEVS 4 /* scullpipe0 through scullpipe3 */


#endif






/**//*


* The bare device is a variable-length region of memory.


* Use a linked list of indirect blocks.


*


* "scull_dev->data" points to an array of pointers, each


* pointer refers to a memory area of SCULL_QUANTUM bytes.


*


* The array (quantum-set) is SCULL_QSET long.


*/


#ifndef SCULL_QUANTUM


#define SCULL_QUANTUM 4000 /* 每个指针(量子)指向一个4000字节的区域 */


#endif




#ifndef SCULL_QSET


#define SCULL_QSET 1000 /* 一个有1000个(量子)的指针数组 */


#endif






/**//*


* The pipe device is a simple circular buffer. Here its default size


*/


#ifndef SCULL_P_BUFFER


#define SCULL_P_BUFFER 4000


#endif






/**//*


* Representation of scull quantum sets.


* 一个链表项


*/




struct scull_qset ...{


void **data;




struct scull_qset *next; /**//* 下一个链表节点(链表项) */


};






/**//* 我们自己的设备(包含了基本的cdev字符设备结构) */




struct scull_dev ...{




struct scull_qset *data; /**//* Pointer to first quantum set (链表的头)*/




int quantum; /**//* the current quantum size */




int qset; /**//* the current array size */




unsigned long size; /**//* amount of data stored here (保存在其中的数据总量)*/




unsigned int access_key; /**//* used by sculluid and scullpriv */




struct semaphore sem; /**//* mutual exclusion semaphore */




struct cdev cdev; /**//* Char device structure */


};






/**//*


* Split minors in two parts


*/


#define TYPE(minor) (((minor) >> 4) & 0xf) /* high nibble */


#define NUM(minor) ((minor) & 0xf) /* low nibble */








/**//*


* The different configurable parameters


*/




extern int scull_major; /**//* main.c */


extern int scull_nr_devs;


extern int scull_quantum;


extern int scull_qset;






extern int scull_p_buffer; /**//* pipe.c */








/**//*


* Prototypes for shared functions


*/




int scull_p_init(dev_t dev);


void scull_p_cleanup(void);


int scull_access_init(dev_t dev);


void scull_access_cleanup(void);




int scull_trim(struct scull_dev *dev);




ssize_t scull_read(struct file *filp, char __user *buf, size_t count,


loff_t *f_pos);


ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,


loff_t *f_pos);


loff_t scull_llseek(struct file *filp, loff_t off, int whence);


int scull_ioctl(struct inode *inode, struct file *filp,


unsigned int cmd, unsigned long arg);








/**//*


* Ioctl definitions


*/






/**//* Use 'k' as magic number */


#define SCULL_IOC_MAGIC 'k'




/**//* Please use a different 8-bit number in your code */




#define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0)






/**//*


* S means "Set" through a ptr,


* T means "Tell" directly with the argument value


* G means "Get": reply by setting through a pointer


* Q means "Query": response is on the return value


* X means "eXchange": switch G and S atomically


* H means "sHift": switch T and Q atomically


*/


#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int)


#define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int)


#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3)


#define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4)


#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int)


#define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int)


#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7)


#define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8)


#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)


#define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int)


#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11)


#define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12)






/**//*


* The other entities only have "Tell" and "Query", because they're


* not printed in the book, and there's no need to have all six.


* (The previous stuff was only there to show different ways to do it.


*/


#define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC, 13)


#define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC, 14)




/**//* ... more to come */




#define SCULL_IOC_MAXNR 14




#endif /* _SCULL_H_ */



linux设备驱动(第三版)的例子里还提供了几个非常通用和灵活的脚本,还有一个标准的Makefile.

大家可以利用下面的文件,修改一下运行试试效果。如果想了解读写的具体过程可以试试用strace命令来追踪;

e.g : strace ls -l >/dev/scull0


# Comment/uncomment the following line to disable/enable debugging


#非常标准的Makefile,稍加修改就可以用在很多驱动上


#将这个开关打开,看proc的输出。在这个例子分别有二个用于输出的proc文件。一个是用老方法实现的


#/proc/scullmem


#新方法 /proc/scullseq


DEBUG = y






# Add your debugging flag (or not) to CFLAGS


ifeq ($(DEBUG),y)


DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines


else


DEBFLAGS = -O2


endif




CFLAGS += $(DEBFLAGS)


CFLAGS += -I$(LDDINC)




ifneq ($(KERNELRELEASE),)


# call from kernel build system




scull-objs := main.o pipe.o access.o




obj-m := scull.o




else




KERNELDIR ?= /lib/modules/$(shell uname -r)/build


PWD := $(shell pwd)




modules:


$(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules




endif








clean:


rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions




depend .depend dep:


$(CC) $(CFLAGS) -M *.c > .depend






ifeq (.depend,$(wildcard .depend))


include .depend


endif



下面这个是load,因为在2.6的新方法中我们是先动态分配主设备号,然后再根据/proc/modules来建立设备文件的

看下面这个脚本的实现就知道了。


#!/bin/sh


# $Id: scull_load,v 1.4 2004/11/03 06:19:49 rubini Exp $


module="scull"


device="scull"


mode="664"




# Group: since distributions do it differently, look for wheel or use staff


if grep -q '^staff:' /etc/group; then


group="staff"


else


group="wheel"


fi




# invoke insmod with all arguments we got


# and use a pathname, as insmod doesn't look in . by default


/sbin/insmod ./$module.ko $* || exit 1




# retrieve major number


major=$(awk "/$2=="$module" {print /$1}" /proc/devices)




# Remove stale nodes and replace them, then give gid and perms


# Usually the script is shorter, it's scull that has several devices in it.






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




ln -sf $...{device}0 /dev/$...{device}




chgrp $group /dev/$...{device}[0-3]




chmod $mode /dev/$...{device}[0-3]






rm -f /dev/$...{device}pipe[0-3]




mknod /dev/$...{device}pipe0 c $major 4




mknod /dev/$...{device}pipe1 c $major 5




mknod /dev/$...{device}pipe2 c $major 6




mknod /dev/$...{device}pipe3 c $major 7




ln -sf $...{device}pipe0 /dev/$...{device}pipe




chgrp $group /dev/$...{device}pipe[0-3]




chmod $mode /dev/$...{device}pipe[0-3]






rm -f /dev/$...{device}single




mknod /dev/$...{device}single c $major 8




chgrp $group /dev/$...{device}single




chmod $mode /dev/$...{device}single






rm -f /dev/$...{device}uid




mknod /dev/$...{device}uid c $major 9




chgrp $group /dev/$...{device}uid




chmod $mode /dev/$...{device}uid






rm -f /dev/$...{device}wuid




mknod /dev/$...{device}wuid c $major 10




chgrp $group /dev/$...{device}wuid




chmod $mode /dev/$...{device}wuid






rm -f /dev/$...{device}priv




mknod /dev/$...{device}priv c $major 11




chgrp $group /dev/$...{device}priv




chmod $mode /dev/$...{device}priv















下面这个用于卸载


#!/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]




rm -f /dev/$...{device}priv




rm -f /dev/$...{device}pipe /dev/$...{device}pipe[0-3]




rm -f /dev/$...{device}single




rm -f /dev/$...{device}uid




rm -f /dev/$...{device}wuid











内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: