linux驱动入门
0赞有足够的理由来说服自己来学习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
- staticmem_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;
- intnum=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 longp=*ppos;
- unsigned intcount=size;
- intret=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 longp=*ppos;
- unsigned intcount=size;
- intret=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_operationsmem_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_tdevno=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