Linux 设备驱动 ====> 字符驱动

原创 2012年03月27日 21:14:10

开始从头学起linux 设备驱动,当然是先从字符驱动看起。

下面仿照着书上的例子,写了一个misc 字符驱动。

root@jay-LJ:/home/jay/globalmem# tree globalmem/
globalmem/
├── globalmem.c
└── Makefile

首先咱来看下Makefile, 其实这东西都一个模子,

KVERS	=	$(shell uname -r)

obj-m	+=	globalmem.o

build:	kernel_modules

kernel_modules:
	make -C /lib/modules/$(KVERS)/build/ M=$(CURDIR) modules

clean:
	make -C /lib/modules/$(KVERS)/build/ M=$(CURDIR) clean

然后是我们的驱动文件globalmem.c,我们从init函数看起

int globalmem_init(void)
{
	int result;
	globalmem_devp = kmalloc(sizeof(struct globalmem_dev) ,GFP_KERNEL);
	if(!globalmem_devp) {
		result = -ENOMEM;
		goto fail_malloc;
	}
	memset(globalmem_devp, 0, sizeof(struct globalmem_dev));
	
	globalmem_devp->mdev = mdev_struct;

	result = misc_register(&(globalmem_devp->mdev));
	if(result<0)
		return result;
	else
		return 0;

fail_malloc:
	return result;
}

首先是给我们的全局指针变量分配内存

	globalmem_devp = kmalloc(sizeof(struct globalmem_dev) ,GFP_KERNEL);
	if(!globalmem_devp) {
		result = -ENOMEM;
		goto fail_malloc;
	}
	memset(globalmem_devp, 0, sizeof(struct globalmem_dev));

看下这个指针的定义

struct globalmem_dev {
	struct miscdevice mdev;
	unsigned char mem[GLOBALMEM_SIZE];
};

struct globalmem_dev *globalmem_devp;

globalmem_dev结构体中内嵌了一个miscdevice结构体,这个结构体就是描述我们的misc字符驱动的结构体,若想注册一个misc 字符设备就必须包含有这样一个结构体,

struct miscdevice  {
	int minor;
	const char *name;
	const struct file_operations *fops;
	struct list_head list;
	struct device *parent;
	struct device *this_device;
};

这个结构体描述了这个驱动的信息,包含次设备号, 文件操作结构体,驱动名称等。

内存分配结束之后是调用misc_register来注册我们的misc驱动,使用misc字符驱动相对于标准的字符驱动写起来简单。

然后是exit函数,跟init相反:

void globalmem_exit(void)
{
	misc_deregister(&(globalmem_devp->mdev));
	if(globalmem_devp)
		kfree(globalmem_devp);
}

module_init(globalmem_init);
module_exit(globalmem_exit);

千万别忘了最后要释放我们分配的内存。

然后使我们的miscdevice和fops结构体

static const struct file_operations globalmem_fops = {
	.owner = THIS_MODULE,
	.open = globalmem_open,
	.release = globalmem_release,
	.unlocked_ioctl = globalmem_ioctl,
	.read = globalmem_read,
	.write = globalmem_write,
};

static struct miscdevice mdev_struct = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "globalmem",
	.fops = &globalmem_fops,
};

这里主要是这个file_operations接头体的定义,这里主要是把这个结构体中我们想要实现的读,写等方法与对应的回调函数挂钩起来。

然后就是我们的读写函数了

int globalmem_open(struct inode *inode, struct file *filp)
{
	printk(KERN_INFO "globalmem open!\n");
	filp->private_data = globalmem_devp;	
	return 0;
}

int globalmem_release(struct inode *inode ,struct file *filp)
{
	printk(KERN_INFO "globalmem release!\n");
	return 0;
}

static int globalmem_ioctl(struct inode *inode, struct file *filp, 
		unsigned int cmd, unsigned long arg)
{
	struct globalmem_dev *dev = filp->private_data;	//get global data pointer

	switch(cmd) {
		case	MEM_CLEAR:
			memset(dev->mem, 0, GLOBALMEM_SIZE);
			printk(KERN_INFO "clear globalmem!\n");\
			break;

		default:
			return -EINVAL;
	}

	return 0;
}

static ssize_t globalmem_read(struct file *filp, char __user *buf,size_t size,
		loff_t *ppos)
{
	unsigned long p = *ppos;
	unsigned int count = size;
	int ret = 0;
	struct globalmem_dev *dev = filp->private_data;	//get global data pointer
	
	if(p>=GLOBALMEM_SIZE)
		return 0;
	if(count > GLOBALMEM_SIZE-p)
		count = GLOBALMEM_SIZE-p;

	if(copy_to_user(buf, (void *)(dev->mem + p), count)) 
		ret = -EFAULT;
	else {
		*ppos += count;
		ret = count;
		
		printk(KERN_INFO "read %u bytes(s) from %lu\n",count,p);
	}

	return ret;
}

static ssize_t globalmem_write(struct file *filp, const char __user *buf,
		size_t size, loff_t *ppos)
{
	unsigned long p = *ppos;
	unsigned int count = size;
	int ret = 0;
	struct globalmem_dev *dev = filp->private_data;	//get global data pointer

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

	if(copy_from_user(dev->mem+p, buf, count)) {
		printk(KERN_INFO "copy from user error!!\n");
		ret = -EFAULT;
	}
	else {
		*ppos += count;
		ret = count;
		printk(KERN_INFO "written %u bytes(s) from %lu\n",count,p);
	}

	return ret;
}

我这里实现了open release write read四个方法,open方法很简单,把我们的自己定义的全局的结构体指针保存到我们驱动中的私有指针,这样的话,在我们别的方法中就可以直接用这个私有指针来获取我们的全局结构体指针,

	filp->private_data = globalmem_devp;	

保存指针,

	struct globalmem_dev *dev = filp->private_data;	//get global data p

获取指针。

在我们的read/write中是与用户空间进行数据处理的过程,主要是调用了copy_from_user和copy_to_user这2个函数实现的,实现了从用户态到内核态的数据传递。

在应用层使用read/write来进行系统调用call到我们这边的read/write回调函数。

============================================================

接下来我们来测试一下我们的驱动,

编译

root@jay-LJ:/home/jay/globalmem/globalmem# make
make -C /lib/modules/2.6.35-22-generic/build/ M=/home/jay/globalmem/globalmem modules
make[1]: Entering directory `/usr/src/linux-headers-2.6.35-22-generic'
  CC [M]  /home/jay/globalmem/globalmem/globalmem.o
/home/jay/globalmem/globalmem/globalmem.c:110: warning: initialization from incompatible pointer type
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/jay/globalmem/globalmem/globalmem.mod.o
  LD [M]  /home/jay/globalmem/globalmem/globalmem.ko
make[1]: Leaving directory `/usr/src/linux-headers-2.6.35-22-generic'

加载

insmod globalmem.ko

查看驱动

root@jay-LJ:/home/jay/globalmem/globalmem# ls -l /dev/globalmem 
crw------- 1 root root 10, 53 2012-03-27 21:11 /dev/globalmem

进行读写测试

root@jay-LJ:/dev# chmod 666 globalmem 
root@jay-LJ:/dev# echo "Hello globalmem driver" > globalmem 
root@jay-LJ:/dev# cat globalmem 
Hello globalmem driver
root@jay-LJ:/dev# 

当然了,我们也可以自己写一个测试的应用程序来测试我们的驱动。

结束。

=========================================================

mail & MSN :zhangjie201412@live.com

=========================================================

Linux字符驱动详解

Linux中将设备分为三类 分别是字符设备,块设备,网络设备 应用程序通过open,read,write等系统调用访问相应的驱动程序,而字符驱动程序通过file_operations向上提供接口...
  • qq_31505483
  • qq_31505483
  • 2017年01月19日 02:14
  • 516

Linux字符驱动开发学习总结

linux驱动编写(虚拟字符设备编写)     昨天我们说了一些简单模块编写方法,但是终归没有涉及到设备的编写内容,今天我们就可以了解一下相关方面的内容,并且用一个实例来说明在linux上面...
  • bcbobo21cn
  • bcbobo21cn
  • 2016年03月19日 21:00
  • 1288

linux字符驱动之初见

学习驱动也有长达一年多的时间了,受益最深的就是看韦东山老师的视频,如今已经几乎将二期三期的视频全部看完,甚至已经将二期视频看过好几遍,为了再次加深印象,我将韦老师的源码自己全部编写一遍。将所有遇到的问...
  • lwj103862095
  • lwj103862095
  • 2013年12月21日 17:19
  • 3915

linux字符设备驱动开发模板及Makefile

linux2.6字符设备驱动开发模板 #include #include #include #include #include //=======================字符设备...
  • wdzxl198
  • wdzxl198
  • 2013年04月18日 09:14
  • 4169

linux设备驱动第三篇:写一个简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动。本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存...
  • HAOMCU
  • HAOMCU
  • 2015年03月28日 19:05
  • 23953

简单Linux字符型驱动

Linux驱动分为三种类型:字符型驱动、块设备驱动和杂项网络设备驱动。其结构图如下 首先学习一下最简单的字符型驱动的写法。 字符驱动是指只能一个字节一个字节读写的设备,不能随机读取设备内存中...
  • u010073981
  • u010073981
  • 2016年01月08日 23:29
  • 344

深入理解Linux字符设备驱动

文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次、组成框架和交互、如何编写驱动、设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解。...
  • yueqian_scut
  • yueqian_scut
  • 2016年03月20日 11:09
  • 3343

Linux 字符设备驱动开发基础(三)—— read()、write() 相关函数解析

我们在前面讲到了file_operations,其是一个函数指针的集合,用于存放我们定义的用于操作设备的函数的指针,如果我们不定义,它默认保留为NULL。其中有最重要的几个函数,分别是open()、r...
  • zqixiao_09
  • zqixiao_09
  • 2016年03月11日 22:16
  • 4575

Linux实现字符设备驱动的基础步骤

Linux应用层想要操作kernel层的API,比如想操作相关GPIO或寄存器,可以通过写一个字符设备驱动来实现。 1、先在rootfs中的 /dev/ 下生成一个字符设备。注意主设备号 和...
  • liukang325
  • liukang325
  • 2014年07月16日 15:57
  • 2076

linux高级字符设备驱动以及实例(TQ2440)

应用程序即用户空间中,ioctl 原型如下:int ioctl(int fd,unsignedlong cmd,...) 设备驱动方法中:int (*ioctl)(struct inode *inod...
  • zbffff
  • zbffff
  • 2013年09月22日 15:25
  • 1313
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux 设备驱动 ====> 字符驱动
举报原因:
原因补充:

(最多只允许输入30个字)