⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 m_p_cdevicepipe.c

📁 一个驱动程序开发 一个系统调用 内附文档 非常详细
💻 C
字号:
/* * m_p_cdevicepipe.c -- fifo driver for mdevice */ #include <linux/module.h>#include <linux/moduleparam.h>#include <linux/wait.h>		/*wait_queue_head_t*/#include <linux/kernel.h>	/* printk(), min() */#include <linux/slab.h>		/* kmalloc() */#include <linux/fs.h>		/* everything... */#include <linux/proc_fs.h>#include <linux/errno.h>	/* error codes */#include <linux/types.h>	/* size_t */#include <linux/fcntl.h>#include <linux/poll.h>		/*poll_table*/#include <linux/cdev.h>#include <asm/uaccess.h>#ifndef MDEVICE_P_MAJOR#define MDEVICE_P_MAJOR 245   /* 主设备号245 */#endif#ifndef MDEVICE_P_NR_DEVS#define MDEVICE_P_NR_DEVS 4  /* 次设备 数 为4*/#endif#ifndef MDEVICE_P_BUFFER#define MDEVICE_P_BUFFER 4000	/*管道设备是一个简单的环形缓冲区,定义初始大小为4000*/#endifstruct mdevice_pipe {        wait_queue_head_t inq, outq;       /* 读写队列*/        char *buffer, *end;                /* 缓冲起始指针,结尾指针 */        int buffersize;                    /* 用于指针计算 */        char *rp, *wp;                     /* 读写指针*/        int nreaders, nwriters;            /* 等待读和写的进程数 */        struct fasync_struct *async_queue; /* 协调读者*/        struct semaphore sem;              /* 互斥信号量*/        struct cdev cdev;                  /* 字符设备结构*/};/* 参数定义*/int mdevice_p_major=MDEVICE_P_MAJOR;static int mdevice_p_nr_devs = MDEVICE_P_NR_DEVS;	/* 管道设备数 */int mdevice_p_buffer =  MDEVICE_P_BUFFER;	/* 缓冲区大小 */module_param(mdevice_p_major, int, S_IRUGO);module_param(mdevice_p_nr_devs, int, 0);module_param(mdevice_p_buffer, int, 0);MODULE_LICENSE("Dual BSD/GPL");static struct mdevice_pipe *mdevice_p_devices;static int mdevice_p_fasync(int fd, struct file *filp, int mode);static int spacefree(struct mdevice_pipe *dev);/* * 设备的打开与关闭,当一个进程打开/关闭设备文件时,这两个函数会被调用 */static int mdevice_p_open(struct inode *inode, struct file *filp){	struct mdevice_pipe *dev;	dev = container_of(inode->i_cdev, struct mdevice_pipe, cdev);/*找到设备*/	filp->private_data = dev;	if (down_interruptible(&dev->sem))		/*防止一个设备被两个进程读*/		return -ERESTARTSYS;	if (!dev->buffer) {		/*初始化缓冲区,如果缓冲区设置失败,释放此设备的信号量并且返回一个错误代号*/		dev->buffer = kmalloc(mdevice_p_buffer, GFP_KERNEL);		if (!dev->buffer) {			up(&dev->sem);			return -ENOMEM;		}	}	dev->buffersize = mdevice_p_buffer;	dev->end = dev->buffer + dev->buffersize;	dev->rp = dev->wp = dev->buffer; /*将读写指针初始化为最前端 */	/*使用f_mode,不是f_flages判断是读进程还是写进程*/	if (filp->f_mode & FMODE_READ)		dev->nreaders++;		/*是读进程,设备的读进程数加一*/	if (filp->f_mode & FMODE_WRITE)		dev->nwriters++;		/*是写进程,设备的写进程数加一*/	up(&dev->sem);				/*释放信号量*/	return nonseekable_open(inode, filp);}static int mdevice_p_release(struct inode *inode, struct file *filp){	struct mdevice_pipe *dev = filp->private_data;	/*文件关闭时,从异步将文件从激活异步读者队列中除去*/	mdevice_p_fasync(-1, filp, 0);	down(&dev->sem);	if (filp->f_mode & FMODE_READ)		dev->nreaders--;	if (filp->f_mode & FMODE_WRITE)		dev->nwriters--;	if (dev->nreaders + dev->nwriters == 0) {		kfree(dev->buffer);		dev->buffer = NULL;	}	up(&dev->sem);	return 0;}/* *数据处理:读和写,当进程对设备进行读写时,这两个函数会被调用 */static ssize_t mdevice_p_read (struct file *filp, char __user *buf, size_t count,                loff_t *f_pos){	struct mdevice_pipe *dev = filp->private_data;	if (down_interruptible(&dev->sem))		return -ERESTARTSYS;	while (dev->rp == dev->wp) { /*没有数据读 */		up(&dev->sem); /* 释放信号量*/		if (filp->f_flags & O_NONBLOCK)   /*是非阻塞型的IO则立即结束*/			return -EAGAIN;				if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp)))/*进入等待队列*/			return -ERESTARTSYS;		/* 有数据,重新申请信号量 */		if (down_interruptible(&dev->sem))			return -ERESTARTSYS;	}	/*有数据开始读数据 */	if (dev->wp > dev->rp)		count = min(count, (size_t)(dev->wp - dev->rp));	else /*写指针回到开头写数据则读取度指针到缓冲区结束那一段的数据*/		count = min(count, (size_t)(dev->end - dev->rp));	if (copy_to_user(buf, dev->rp, count)) {		up (&dev->sem);		return -EFAULT;	}	dev->rp += count;/*写指针朝前推进*/	if (dev->rp == dev->end)		dev->rp = dev->buffer; /*回到开始,形成环形缓冲*/	up (&dev->sem);	/* 唤醒其他在等待的写者并且退出*/	wake_up_interruptible(&dev->outq);	return count;}/* 等待缓冲区的空闲空间以写入数据,调用着进入该程序时持有该设备的信号量*/static int mdevice_getwritespace(struct mdevice_pipe *dev, struct file *filp){	while (spacefree(dev) == 0) { /*如果缓冲区满则初始化一个队列*/		DEFINE_WAIT(wait);				up(&dev->sem);/*释放信号量*/		if (filp->f_flags & O_NONBLOCK)		/*如果设备文件的打开方式是非阻塞I/O,则立即退出*/			return -EAGAIN;		prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE);/*添加写进程等待队列入口到队列中*/		if (spacefree(dev) == 0)			schedule();/*通知内核该进程需得到所需资源*/		finish_wait(&dev->outq, &wait);/*清除写进程的等待队列*/		if (signal_pending(current))			return -ERESTARTSYS; /* 通知文件系统层处理*/		if (down_interruptible(&dev->sem))/*重新申请设备信号量*/			return -ERESTARTSYS;	}	return 0;}	/* 计算缓冲区中空闲空间的大小*/static int spacefree(struct mdevice_pipe *dev){	if (dev->rp == dev->wp)/*缓冲区已满*/		return dev->buffersize - 1;	return ((dev->rp + dev->buffersize - dev->wp) % dev->buffersize) - 1;}/*设备读操作*/static ssize_t mdevice_p_write(struct file *filp, const char __user *buf, size_t count,                loff_t *f_pos){	struct mdevice_pipe *dev = filp->private_data;	int result;	if (down_interruptible(&dev->sem))		return -ERESTARTSYS;	/* 确保缓冲区有空间写入数据*/	result = mdevice_getwritespace(dev, filp);	if (result)		return result; /*缓冲区已满 */	/* 有空间写则计算设备的空闲空间大小*/	count = min(count, (size_t)spacefree(dev));	if (dev->wp >= dev->rp)		count = min(count, (size_t)(dev->end - dev->wp)); /* 从当前位置写道缓冲区尾的数据*/	else /*写指针在读指针之前,则空闲空间为写指针到读指针的数据量*/		count = min(count, (size_t)(dev->rp - dev->wp - 1));	if (copy_from_user(dev->wp, buf, count)) {		up (&dev->sem);		return -EFAULT;	}	dev->wp += count;	if (dev->wp == dev->end)		dev->wp = dev->buffer; /*回到缓冲区开头*/	up(&dev->sem);	/* 唤醒在等待数据的写设备*/	wake_up_interruptible(&dev->inq);	/*有数据到达时,通知异步读者 */	if (dev->async_queue)		kill_fasync(&dev->async_queue, SIGIO, POLL_IN);	return count;}static unsigned int mdevice_p_poll(struct file *filp, poll_table *wait){	struct mdevice_pipe *dev = filp->private_data;	unsigned int mask = 0;	/*	 * 环形缓冲区满为wp==rp;空时spacefree(dev)为0;	 */	down(&dev->sem);	poll_wait(filp, &dev->inq,  wait);	poll_wait(filp, &dev->outq, wait);	if (dev->rp != dev->wp)		mask |= POLLIN | POLLRDNORM;	/* 可读*/	if (spacefree(dev))		mask |= POLLOUT | POLLWRNORM;	/* 可写 */	up(&dev->sem);	return mask;}/*文件关闭时,从异步将文件从激活异步读者队列中除去*/static int mdevice_p_fasync(int fd, struct file *filp, int mode){	struct mdevice_pipe *dev = filp->private_data;	/*从相关进程队列中添加或出去该文件*/	return fasync_helper(fd, filp, mode, &dev->async_queue);}/* 实现/proc文件 */#ifdef MDEVICE_DEBUGstatic void mdevicep_proc_offset(char *buf, char **start, off_t *offset, int *len){	if (*offset == 0)		return;	if (*offset >= *len) {		*offset -= *len;		*len = 0;	}	else {			/*设置文件偏移 */		*start = buf + *offset;		*offset = 0;	}}static int mdevice_read_p_mem(char *buf, char **start, off_t offset, int count,		int *eof, void *data){	int i, len;	struct mdevice_pipe *p;#define LIMIT (PAGE_SIZE-200)	/* don't print any more after this size */	*start = buf;	len = sprintf(buf, "Default buffersize is %i\n", mdevice_p_buffer);	for(i = 0; i<mdevice_p_nr_devs && len <= LIMIT; i++) {		p = &mdevice_p_devices[i];		if (down_interruptible(&p->sem))			return -ERESTARTSYS;		len += sprintf(buf+len, "\nDevice %i: %p\n", i, p);		len += sprintf(buf+len, "   Buffer: %p to %p (%i bytes)\n", p->buffer, p->end, p->buffersize);		len += sprintf(buf+len, "   rp %p   wp %p\n", p->rp, p->wp);		len += sprintf(buf+len, "   readers %i   writers %i\n", p->nreaders, p->nwriters);		up(&p->sem);		mdevicep_proc_offset(buf, start, &offset, &len);	}	*eof = (len <= LIMIT);	return len;}#endif/* * 管道字符设备的文件操作 */struct file_operations mdevice_pipe_fops = {	.owner =	THIS_MODULE,	.llseek =	no_llseek,	.read =		mdevice_p_read,	.write =	mdevice_p_write,	.poll =		mdevice_p_poll,	.open =		mdevice_p_open,	.release =	mdevice_p_release,	.fasync =	mdevice_p_fasync,};/* * 初始化cdev entry. */static void mdevice_p_setup_cdev(struct mdevice_pipe *dev, int index){	int err, devno = MKDEV(mdevice_p_major, index);    	cdev_init(&dev->cdev, &mdevice_pipe_fops);	dev->cdev.owner = THIS_MODULE;	err = cdev_add (&dev->cdev, devno, 1);	/* Fail gracefully if need be */	if (err)		printk(KERN_NOTICE "Error %d adding mdevicepipe%d", err, index);} /* * 模块加载时调用。初始化管道设备,返回初始化的数量. */int mdevice_p_init(void){	int i, result=0;	dev_t dev = 0;	if (mdevice_p_major) {		dev = MKDEV(mdevice_p_major, 0);		result = register_chrdev_region(dev, mdevice_p_nr_devs, "mdevicep");	}	if (result < 0) {		printk(KERN_WARNING "mdevicep: can't get major %d\n", mdevice_p_major);		return result;	}	mdevice_p_devices = kmalloc(mdevice_p_nr_devs * sizeof(struct mdevice_pipe), GFP_KERNEL);	if (mdevice_p_devices == NULL) {		unregister_chrdev_region(dev, mdevice_p_nr_devs);		return 0;	}	memset(mdevice_p_devices, 0, mdevice_p_nr_devs * sizeof(struct mdevice_pipe));	for (i = 0; i < mdevice_p_nr_devs; i++) {		init_waitqueue_head(&(mdevice_p_devices[i].inq));		init_waitqueue_head(&(mdevice_p_devices[i].outq));		init_MUTEX(&mdevice_p_devices[i].sem);		mdevice_p_setup_cdev(mdevice_p_devices + i, i);	}#ifdef MDEVICE_DEBUG	create_proc_read_entry("mdevicepipe", 0, NULL, mdevice_read_p_mem, NULL);#endif	return mdevice_p_nr_devs;}/* * 模块卸载时或者设备失败时调用. */void mdevice_p_cleanup(void){	int i;	dev_t mdevice_p_devno = MKDEV(mdevice_p_major, 0);#ifdef MDEVICE_DEBUG	remove_proc_entry("mdevicepipe", NULL);#endif	if (!mdevice_p_devices)		return; /* 没有内存需要释放*/	for (i = 0; i < mdevice_p_nr_devs; i++) {		cdev_del(&mdevice_p_devices[i].cdev);		kfree(mdevice_p_devices[i].buffer);	}	kfree(mdevice_p_devices);	unregister_chrdev_region(mdevice_p_devno, mdevice_p_nr_devs);	mdevice_p_devices = NULL; /* pedantic */}module_init(mdevice_p_init);module_exit(mdevice_p_cleanup);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -