一个完整的字符设备驱动程序导读

#include <linux/module.h> 
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/slab.h>
/**void *kmalloc(size_t size,int flags);	*/
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
//#include <asm/semaphore.h>

#define MEMDEV_MAJOR 251
#define MEMDEV_NUM 2
#define MEMDEV_SIZE 1024

struct mem_dev
{
	unsigned int size;
	char *data;
	struct semaphore sem;
};


static int mem_major = MEMDEV_MAJOR;

struct cdev mem_cdev;
struct mem_dev *mem_devp;

static int mem_open(struct inode *inode,struct file *filp)
{
	struct mem_dev *dev;
	unsigned int num;

	printk("mem_open.\n");

	num = MINOR(inode->i_rdev);//获得次设备号
	if(num> (MEMDEV_NUM -1)) //检查次设备号有效性
	{
		return -ENODEV;
	}

	dev= &mem_devp[num];
	filp->private_data= dev; //将设备结构保存为私有数据

	return 0;
}

static int mem_release(struct inode *inode,struct file *filp)
{
	printk("mem_release.\n");
	return 0;
}

static ssize_t mem_read(struct file *filp,char __user *buf, size_t size, loff_t *ppos)
{
	int ret = 0;
	struct mem_dev *dev;
	unsigned long p;
	unsigned long count;

	printk("mem_read.\n");

	dev= filp->private_data;//获得设备结构
	count= size;
	p= *ppos;

	//检查偏移量和数据大小的有效性
	if(p> MEMDEV_SIZE)
		return 0;
	if(count> (MEMDEV_SIZE-p))
	count= MEMDEV_SIZE - p;

	if(down_interruptible(&dev->sem))//锁定互斥信号量
	return -ERESTARTSYS;

	//读取数据到用户空间
	if(copy_to_user(buf,dev->data+p, count)){
		ret= -EFAULT;
		printk("copyfrom user failed\n");
	}
	else{
		*ppos+= count;
		ret= count;
		printk("read %ld bytes from dev\n", count);
	}

	up(&dev->sem);//解锁互斥信号量

	return ret;
}

static ssize_t mem_write(struct file *filp,const char __user *buf, size_t size, loff_t *ppos)//注意:第二个参数和read方法不同
{
	int ret = 0;
	struct mem_dev *dev;
	unsigned long p;
	unsigned long count;

	printk("mem_write.\n");

	dev= filp->private_data;
	count= size;
	p= *ppos;

	if(p> MEMDEV_SIZE)
		return 0;
	if(count> (MEMDEV_SIZE-p))
		count= MEMDEV_SIZE - p;

	if(down_interruptible(&dev->sem))//锁定互斥信号量
		return -ERESTARTSYS;

	if(copy_from_user(dev->data+p,buf, count)){
		ret= -EFAULT;
		printk("copyfrom user failed\n");
	}
	else{
		*ppos+= count;
		ret= count;
		printk("write%ld bytes to dev\n", count);
	}

	up(&dev->sem);//解锁互斥信号量

	return ret;
}

static loff_t mem_llseek(struct file *filp,loff_t offset, int whence)
{
	int newpos;
	printk("mem_llseek.\n");
	switch(whence)
	{
		case 0:
			newpos= offset;
			break;
		case 1:
			newpos= filp->f_pos + offset;
			break;
		case 2:
			newpos= MEMDEV_SIZE - 1 + offset;
			break;
		default:
				return -EINVAL;
	}
	if((newpos<0)|| (newpos>(MEMDEV_SIZE - 1)))
		return -EINVAL;
	filp->f_pos= newpos;
	return newpos;
}

static const struct file_operations mem_fops = {
	.owner= THIS_MODULE,
	.open= mem_open,
	.write= mem_write,
	.read= mem_read,
	.release= mem_release,
	.llseek= mem_llseek,
};

static int __init memdev_init(void)
{ 
	int result;
	int err;
	int i;

	//申请设备号	
	dev_t devno = MKDEV(mem_major, 0);

	if(mem_major)
		result= register_chrdev_region(devno, MEMDEV_NUM, "memdev");
		//注意静态申请的dev_t参数和动态dev_t参数的区别
	else{ //静态直接传变量,动态传变量指针
		result= alloc_chrdev_region(&devno, 0, MEMDEV_NUM, "memdev");
		mem_major= MAJOR(devno);
	}

	if(result< 0){
		printk("can'tget major devno:%d\n", mem_major);
		return result;
	}

	//注册设备驱动
	cdev_init(&mem_cdev,&mem_fops);
	mem_cdev.owner= THIS_MODULE;

	err= cdev_add(&mem_cdev, MKDEV(mem_major, 0), MEMDEV_NUM);
	//如果有N个设备就要添加N个设备号
	if(err)
		printk("addcdev faild,err is %d\n", err);

	//分配设备内存
	mem_devp= kmalloc(MEMDEV_NUM*(sizeof(struct mem_dev)), GFP_KERNEL);
	if(!mem_devp){
		result = - ENOMEM;
		goto fail_malloc;
	}

	memset(mem_devp,0, MEMDEV_NUM*(sizeof(struct mem_dev)));

	for(i=0;i<MEMDEV_NUM; i++){	
		mem_devp[i].size= MEMDEV_SIZE;
		mem_devp[i].data= kmalloc(MEMDEV_SIZE, GFP_KERNEL);
		memset(mem_devp[i].data,0, MEMDEV_SIZE);
		sema_init(&mem_devp[i].sem,1);//初始化互斥锁
	}

	return result;

fail_malloc:
	unregister_chrdev_region(MKDEV(mem_major,0), MEMDEV_NUM);
	return result;
}

static void memdev_exit(void)
{		
	cdev_del(&mem_cdev);
	unregister_chrdev_region(MKDEV(mem_major,0), MEMDEV_NUM);//注意释放的设备号个数一定要和申请的设备号个数保存一致
	//否则会导致设备号资源流失
	printk("memdev_exit\n");
}

module_init(memdev_init);
module_exit(memdev_exit);

MODULE_AUTHOR("Y-Kee");
MODULE_LICENSE("GPL");



原文地址:https://www.cnblogs.com/xuddong/p/3071742.html