linux驱动入门

有足够的理由来说服自己来学习linux设备驱动!学习linux设备驱动,第一个就是helloworld驱动了,第二个应该是memdev这个驱动了。

今天通过学习国嵌的memdev这个设备驱动程序,简单的理解了下设备驱动程序运作过程,这个和前面的globalmem设备驱动是类似的。

我对源码有轻微的改动,如果学习的话,建议手动敲一遍代码,这样理解会深刻一些。

memdev.c

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

#define MEMDEV_MAJOR  250
static mem_major=MEMDEV_MAJOR;
struct cdev cdev;
struct mem_dev
{
	char *data;
	unsigned long size;
};
struct mem_dev *mem_devp;

// open function
int mem_open(struct inode *inode,struct file *filp)
{
	struct mem_dev *dev;
	int num=MINOR(inode->i_rdev);
	if(num>=2)
		return -ENODEV;
	dev=&mem_devp[num];
	filp->private_data=dev;
	return 0;
}
//release function
int mem_release(struct inode *inode,struct file *filp)
{
	return 0;
}

//read function
static ssize_t  mem_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 mem_dev *dev=filp->private_data;
	if(p>=4096)
		return 0;
	if(count>4096-p)
		count=4096-p;
	if(copy_to_user(buf,(void *)(dev->data+p),count))
	{
		ret=-EFAULT;
	}
	else
	{
		*ppos+=count;
		ret=count;
		printk(KERN_INFO "read %d bytes from %d\n",count,p);
	}
	return ret;
}

// write function
static ssize_t mem_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 mem_dev  *dev=filp->private_data;
	if(p>=4096)
	{
		return 0;
	}
	if(count > 4096-p)
	count=4096-p;
	if(copy_from_user(dev->data+p,buf,count))
		ret=-EFAULT;
	else
	{
		*ppos+=count;
		ret=count;
		printk(KERN_INFO "written %d bytes from %d\n",count,p);
	}
	return ret;
}
 
//seek function
static loff_t mem_llseek(struct file *filp,loff_t offset,int whence)
{
	loff_t newpos;
	switch(whence)
	{
		case 0: newpos=offset; break;
		case 1: newpos=filp->f_pos+offset;break;
		case 2: newpos=4095+offset;break;
		default : return -EINVAL;
	}
	if((newpos<0)||(newpos>4096))
		return -EINVAL;
	filp->f_pos=newpos;
	return newpos;
}
//
static const struct file_operations mem_fops=
{
	.owner=THIS_MODULE,
	.llseek=mem_llseek,
	.read=mem_read,
	.write=mem_write,
	.open=mem_open,
	.release=mem_release,
};
//init function
static int memdev_init(void)
{
	int result;
	dev_t devno=MKDEV(mem_major,0);
	//get major
	if(mem_major)	
	{
		result=register_chrdev_region(devno,2,"memdev");
	}
	else
	{
		result=alloc_chrdev_region(&devno,0,2,"memdev");
		mem_major=MAJOR(devno);
	}
	if(result<0)
		return result;
	//init cdev 
	cdev_init(&cdev,&mem_fops);
	cdev.owner=THIS_MODULE;
	//register memdev
	cdev_add(&cdev,MKDEV(mem_major,0),2);
	mem_devp=kmalloc(2*sizeof(struct mem_dev),GFP_KERNEL);
	if(!mem_devp)
	{
		result= -ENOMEM;
		goto fail_malloc;
	}
	memset(mem_devp,0,2*sizeof(struct mem_dev));
	mem_devp[0].size=4096;
	mem_devp[0].data=kmalloc(4096,GFP_KERNEL);
	memset(mem_devp[0].data,0,4096);
	mem_devp[1].size=4096;
	mem_devp[1].data=kmalloc(4096,GFP_KERNEL);
	memset(mem_devp[1].data,0,4096);
	return 0;
	fail_malloc:unregister_chrdev_region(devno,1);
	return result;
}
//exit function
static int memdev_exit(void)
{
	cdev_del(&cdev);
	kfree(mem_devp);
	unregister_chrdev_region(MKDEV(mem_major,0),2);
}
module_init(memdev_init);
module_exit(memdev_exit);
MODULE_AUTHOR("by xzy 214");
MODULE_LICENSE("GPL");

Makefile如下

ifneq ($(KERNELRELEASE),)
obj-m :=memdev.o
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR)  M=$(PWD) modules
clean:
	rm -rf *.o *.mod.c *.mod.o *.ko
endif


测试该驱动的应用程序

app_mem.c,源码是用c库函数的文件操作来做的。我这里用系统调用来完成。

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main()
{
	int fd;
	char buf[4096];
	strcpy(buf,"mem is char dev!!hello xzy");
	printf("buf:%s\n",buf);
	fd=open("/dev/memdev0",O_RDWR);
	printf("fd: %d\n",fd);
	if(fd<0)
	{
		printf("open memdev0 error!\n");
		return -1;
	}
	write(fd,buf,sizeof(buf));
	lseek(fd,0,SEEK_SET);
	strcpy(buf,"buf is null");
	printf("buf: %s\n",buf);
	read(fd,buf,sizeof(buf));
	printf("buf: %s\n",buf);
	close(fd);
	return 0;
}

驱动运行的过程:自己理解。应用程序来打开设备节点文件,这个设备节点文件是通过主设备号和驱动程序联系在一起的。打开这个设备文件的同时,在内核空间会相应的有一个关联的struct file结构和关于该设备的inode结构体,所以你必须要了解一下这两个结构,在file结构中保存着文件读写的信息,还有一个file_operation,这个可以认为是一个转化表,对应着驱动程序的一些文件操作的函数,这样就对应起来了,inode包括了设备号,这个在文件打开的时候,可以判断是次设备号。

系统调用open的执行过程:用户空间open,会执行的sys_open,这个就为打开的文件分配一个file结构体和从inode的节点中找到对应的file_operations

具体可以见:http://blog.csdn.net/xzyiverson/article/details/12676911

系统调用read的执行过程:先调用vfs_read,file->fop->read,我们可以看内核源码,我截下了一张图


上述内核源码见于read_write.c


了解了大致的过程,看看上面的memdev驱动程序,从init看起:

1.申请设备号2.初始化并添加cdev结构

然后就是该设备的一些操作函数的编写了,分析起来应该是不难的。


注明:出错误的地方,解决办法如下:

http://blog.csdn.net/xzyiverson/article/details/17315931


运行结果:

insmod   memdev.ko

mknod  memdev0  c  250 0

mknod  memdev1  c   250 1


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值