您的位置:首页 > 运维架构 > Linux

Linux设备驱动程序第三版学习(2)-字符设备驱动程序源码分析(续)

2010-12-24 18:41 537 查看
前段时间一直在搞GPS导航系统的应用软件,最近告一段落,继续捡起linux设备驱动,啃之!

上次记录了模块初始化代码的学习,今次看看卸载模块的代码。

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);  /*系统调用。从系统中移除一个字符设备。该函数同cdev_alloc, cdev_init, cdev_add三个函数构成了字符设备注册所需的函数 */

}

kfree(scull_devices); /*此次和前面的kmalloc函数相对应,释放设备的内存空间(这里设备本身就是一段内存,哈哈) */

}

}


看一下scull_trim

/*

* 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*/

for(dptr = dev->data; dptr; dptr = next)  //遍历整个链表

{

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;

}


SCULL设备驱动程序实现了几个最重要的设备方法。如下file_operations结构
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_real,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};

这些方法都要驱动程序设计者自己来实现的,每个设备驱动程序都需要设计者来实现这几个方法。
例如驱动程序使用者对open的调用将会调用scull_open函数。我认为这里可以理解为所有驱动程序的接口都是相同的,只是内部实现不同。
关于open和release方法的作用参考书中page62, page63。
read和write方法的原型如下:
ssize_t read(stuct file *filp, char _ _user *buff, size_t count, loff_t offp);
ssize_t write(struct file * filp, const char _ _user *buff, size_t count, loff_t *offp);
这两个方法的实质就是数据拷贝。read方法拷贝数据到用户地址空间,write方法拷贝数据到内核地址空间。实现这两种拷贝用到的内核函数如下(是大多数read和write方法实现的核心部分):
unsigned long copy_to_user(void _ _user *to, const void *from, unsigned long count);
unsigned long copy_from_user(void *to, const void _ _user *from, unsigned long count);

分析一下scull的open方法:
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); /*由于inode的i_cdev字段只是包含了指向struct cdev结构的指针,而我们要使用的是scull_dev结构指针,所有这里使用了container_of宏,用来取得包含cdev结构的scull_dev结构*/
filp->private_data = dev; /* 将得到的scull_dev结构指针保存起来,方便以后的访问。*/

/*这里只是当设备以“写”打开时,长度截为0*/
if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
if (down_interruptible(&dev->sem)) /*获得信号量,成功获得则继续,没成功则返回-ERESTARSYS */
return -ERESTARTSYS;
scull_trim(dev); /* ignore errors */
up(&dev->sem); /*释放信号量*/
}
return 0;          /* success */
}


分析一下scull的release方法:基本的scull没有需要关闭的硬件,因此直接返回。
int scull_release(struct inode *inode, struct file *filp)
{
return 0;
}


分析一下scull的read方法:
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时分配的保存在filp->private_data中的scull_dev结构指针。*/
struct scull_qset *dptr;    /* the first listitem */
int quantum = dev->quantum, qset = dev->qset; /*取得量子大小(4000bytes), 取得量子集大小(1000个量子)*/
int itemsize = quantum * qset; /* how many bytes in the listitem,链表中每个item的大小(4 000 000bytes) */
int item, s_pos, q_pos, rest;
ssize_t retval = 0;

if (down_interruptible(&dev->sem)) /*下面要进行的是一个互斥的操作,所以这里要获得信号量*/
return -ERESTARTSYS;
if (*f_pos >= dev->size) /*如果要读取的偏移位置超过了设备文件的大小,则不能读取,直接跳到out*/
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;
s_pos = rest / quantum; 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;

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐