一个简单的字符驱动程序

       最近在学习Linux下设备驱动程序,从头开始吧!慢慢总结,先入手,后入门,再提高!

       下面是一个简单的字符设备驱动程序,主要是一个模拟设备,使用了系统的内存,相信大家都能看懂,毕竟简单,而且注释清楚,编译测试的方法大家都知道吧,这里要说的就是习惯的测试方法是通过echo向设备文件写入内容,然后通过cat显示内容,但是希望大家看到,该设备只有4字节的内存,测试的时候建议大家可以试一试写入的内容小于4字节和大于4字节,大家自己看看出现什么事,呵呵:

#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/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>

#define MEMSIZE		4

#define MAJORNUM	2538

static int major = MAJORNUM;

/******定义自己的字符设备******/
struct char_mem_t {
	struct cdev memdev;
	int device[MEMSIZE];
}char_mem;


static ssize_t char_mem_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
	/*******取得文件当前偏移量********/
	unsigned long p = *ppos;
	int ret = 0;
	
	/*****分析当前要读取操作的读取位置和读取量是否满足要求*****/
	if(p > MEMSIZE)
		return 0;
	if(count > MEMSIZE - p)
		count = MEMSIZE - p;
	
	/*******读取操作,调用copy_to_user()函数*********/
	if(copy_to_user(buf, (void*)(char_mem.device+p), count))
		ret = -EFAULT;
	else  //读取成功则需要调整文件指针位置
	{
		*ppos += count;
		ret = count;
		
		printk(KERN_INFO "read %d bytes from %d", count, p);
	}
	
	return ret;
}

static ssize_t char_mem_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
	/*******取得文件当前偏移量********/
	unsigned long p = *ppos;
	int ret = 0;
	
	/*****分析当前要写入操作的写入位置和写入量是否满足要求*****/
	if(p >= MEMSIZE)
		return 0;
	if(count > MEMSIZE - p)
		count = MEMSIZE - p;
	
	/*******写入操作,调用copy_from_user()函数*********/
	if(copy_from_user((void*)(char_mem.device+p), buf, count))
		ret = -EFAULT;
	else  //写入成功则需要调整文件指针位置
	{
		*ppos += count;
		ret = count;
		
		printk(KERN_INFO "write %d bytes to %d", count, p);
	}
	
	return ret;
}

static loff_t char_mem_llseek(struct file *filp, loff_t offset, int orig)
{
	loff_t ret = 0;
	
	switch(orig) {
	/*****从文件头开始偏移*****/
	case 0:
		if(offset < 0)
		{
			ret = -EINVAL;
			break;
		}
		if((unsigned int)offset > MEMSIZE)
		{
			ret = -EINVAL;
			break;
		}
		filp->f_pos = (unsigned int) offset;
		ret = filp->f_pos;
		break;
	/*******从当前位置偏移*************/
	case 1:
		if((filp->f_pos + offset) > MEMSIZE)
		{
			ret = -EINVAL;
			break;
		}
		if((filp->f_pos + offset) < 0)
		{
			ret = -EINVAL;
			break;
		}
		filp->f_pos += offset;
		ret = filp->f_pos;
		break;
	default:
		ret = -EINVAL;
	}
	
	return ret;
}


static const struct file_operations char_mem_ops = {
	.owner = THIS_MODULE,
	.llseek = char_mem_llseek,
	.read = char_mem_read,
	.write = char_mem_write,
//	.ioctl = char_mem_ioctl,
};


static int charmem_init(void)
{
	int result = 0;
	int err = 0;
	dev_t dev = MKDEV(major, 0);
	
	if(major)
	{
		result = register_chrdev_region(dev, 1, "charmem");
	}
	else
	{
		result = alloc_chrdev_region(&dev, 0, 1, "charmem");
		major = MAJOR(dev);
	}
	
	/******申请设备号失败*******/
	if(result < 0) 
		return result;
	
	/********注册设备********/
	cdev_init(&char_mem.memdev, &char_mem_ops);
	char_mem.memdev.owner = THIS_MODULE;
	err = cdev_add(&char_mem.memdev, dev, 1);
	
	/******注册失败********/
	if(err)
		printk(KERN_INFO "Error %d adding char_mem device", err);
	
}

static void charmem_exit(void)
{
	/****删除设备***/
	cdev_del(&char_mem.memdev);
	/*****释放设备编号******/
	unregister_chrdev_region(MKDEV(major, 0), 1);
}


MODULE_AUTHOR("chenlong12580");
MODULE_LICENSE("Dual BSD/GPL");


module_init(charmem_init);
module_exit(charmem_exit);


posted on 2012-03-08 23:13  哼哼唧唧  阅读(216)  评论(0编辑  收藏  举报

导航