xzy610030

一起探讨,一起进步,一起分享!

linux驱动入门

0
阅读(2928)

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

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

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

memdev.c

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/types.h>  
  3. #include <linux/fs.h>  
  4. #include <linux/errno.h>  
  5. #include <linux/mm.h>  
  6. #include <linux/sched.h>  
  7. #include <linux/init.h>  
  8. #include <linux/cdev.h>  
  9. #include <asm/io.h>  
  10. #include <linux/slab.h>  
  11. //#include <asm/system.h>  
  12. #include <asm/uaccess.h>  
  13.   
  14. #define MEMDEV_MAJOR  250  
  15. static mem_major=MEMDEV_MAJOR;  
  16. struct cdev cdev;  
  17. struct mem_dev  
  18. {  
  19.     char *data;  
  20.     unsigned long size;  
  21. };  
  22. struct mem_dev *mem_devp;  
  23.   
  24. // open function  
  25. int mem_open(struct inode *inode,struct file *filp)  
  26. {  
  27.     struct mem_dev *dev;  
  28.     int num=MINOR(inode->i_rdev);  
  29.     if(num>=2)  
  30.         return -ENODEV;  
  31.     dev=&mem_devp[num];  
  32.     filp->private_data=dev;  
  33.     return 0;  
  34. }  
  35. //release function  
  36. int mem_release(struct inode *inode,struct file *filp)  
  37. {  
  38.     return 0;  
  39. }  
  40.   
  41. //read function  
  42. static ssize_t  mem_read(struct file *filp,char __user *buf,size_t size,loff_t *ppos)  
  43. {  
  44.     unsigned long p=*ppos;  
  45.     unsigned int count=size;  
  46.     int ret=0;  
  47.     struct mem_dev *dev=filp->private_data;  
  48.     if(p>=4096)  
  49.         return 0;  
  50.     if(count>4096-p)  
  51.         count=4096-p;  
  52.     if(copy_to_user(buf,(void *)(dev->data+p),count))  
  53.     {  
  54.         ret=-EFAULT;  
  55.     }  
  56.     else  
  57.     {  
  58.         *ppos+=count;  
  59.         ret=count;  
  60.         printk(KERN_INFO "read %d bytes from %d\n",count,p);  
  61.     }  
  62.     return ret;  
  63. }  
  64.   
  65. // write function  
  66. static ssize_t mem_write(struct file *filp,const char  __user *buf,size_t size,loff_t *ppos)  
  67. {  
  68.     unsigned long p=*ppos;  
  69.     unsigned int count=size;  
  70.     int ret=0;  
  71.     struct mem_dev  *dev=filp->private_data;  
  72.     if(p>=4096)  
  73.     {  
  74.         return 0;  
  75.     }  
  76.     if(count > 4096-p)  
  77.     count=4096-p;  
  78.     if(copy_from_user(dev->data+p,buf,count))  
  79.         ret=-EFAULT;  
  80.     else  
  81.     {  
  82.         *ppos+=count;  
  83.         ret=count;  
  84.         printk(KERN_INFO "written %d bytes from %d\n",count,p);  
  85.     }  
  86.     return ret;  
  87. }  
  88.    
  89. //seek function  
  90. static loff_t mem_llseek(struct file *filp,loff_t offset,int whence)  
  91. {  
  92.     loff_t newpos;  
  93.     switch(whence)  
  94.     {  
  95.         case 0: newpos=offset; break;  
  96.         case 1: newpos=filp->f_pos+offset;break;  
  97.         case 2: newpos=4095+offset;break;  
  98.         default : return -EINVAL;  
  99.     }  
  100.     if((newpos<0)||(newpos>4096))  
  101.         return -EINVAL;  
  102.     filp->f_pos=newpos;  
  103.     return newpos;  
  104. }  
  105. //  
  106. static const struct file_operations mem_fops=  
  107. {  
  108.     .owner=THIS_MODULE,  
  109.     .llseek=mem_llseek,  
  110.     .read=mem_read,  
  111.     .write=mem_write,  
  112.     .open=mem_open,  
  113.     .release=mem_release,  
  114. };  
  115. //init function  
  116. static int memdev_init(void)  
  117. {  
  118.     int result;  
  119.     dev_t devno=MKDEV(mem_major,0);  
  120.     //get major  
  121.     if(mem_major)     
  122.     {  
  123.         result=register_chrdev_region(devno,2,"memdev");  
  124.     }  
  125.     else  
  126.     {  
  127.         result=alloc_chrdev_region(&devno,0,2,"memdev");  
  128.         mem_major=MAJOR(devno);  
  129.     }  
  130.     if(result<0)  
  131.         return result;  
  132.     //init cdev   
  133.     cdev_init(&cdev,&mem_fops);  
  134.     cdev.owner=THIS_MODULE;  
  135.     //register memdev  
  136.     cdev_add(&cdev,MKDEV(mem_major,0),2);  
  137.     mem_devp=kmalloc(2*sizeof(struct mem_dev),GFP_KERNEL);  
  138.     if(!mem_devp)  
  139.     {  
  140.         result= -ENOMEM;  
  141.         goto fail_malloc;  
  142.     }  
  143.     memset(mem_devp,0,2*sizeof(struct mem_dev));  
  144.     mem_devp[0].size=4096;  
  145.     mem_devp[0].data=kmalloc(4096,GFP_KERNEL);  
  146.     memset(mem_devp[0].data,0,4096);  
  147.     mem_devp[1].size=4096;  
  148.     mem_devp[1].data=kmalloc(4096,GFP_KERNEL);  
  149.     memset(mem_devp[1].data,0,4096);  
  150.     return 0;  
  151.     fail_malloc:unregister_chrdev_region(devno,1);  
  152.     return result;  
  153. }  
  154. //exit function  
  155. static int memdev_exit(void)  
  156. {  
  157.     cdev_del(&cdev);  
  158.     kfree(mem_devp);  
  159.     unregister_chrdev_region(MKDEV(mem_major,0),2);  
  160. }  
  161. module_init(memdev_init);  
  162. module_exit(memdev_exit);  
  163. MODULE_AUTHOR("by xzy 214");  
  164. MODULE_LICENSE("GPL");  

Makefile如下

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ifneq ($(KERNELRELEASE),)  
  2. obj-m :=memdev.o  
  3. else  
  4. KERNELDIR:=/lib/modules/$(shell uname -r)/build  
  5. PWD:=$(shell pwd)  
  6. default:  
  7.     $(MAKE) -C $(KERNELDIR)  M=$(PWD) modules  
  8. clean:  
  9.     rm -rf *.o *.mod.c *.mod.o *.ko  
  10. endif  


测试该驱动的应用程序

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

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include <unistd.h>  
  2. #include <fcntl.h>  
  3. #include <stdio.h>  
  4. #include <string.h>  
  5. int main()  
  6. {  
  7.     int fd;  
  8.     char buf[4096];  
  9.     strcpy(buf,"mem is char dev!!hello xzy");  
  10.     printf("buf:%s\n",buf);  
  11.     fd=open("/dev/memdev0",O_RDWR);  
  12.     printf("fd: %d\n",fd);  
  13.     if(fd<0)  
  14.     {  
  15.         printf("open memdev0 error!\n");  
  16.         return -1;  
  17.     }  
  18.     write(fd,buf,sizeof(buf));  
  19.     lseek(fd,0,SEEK_SET);  
  20.     strcpy(buf,"buf is null");  
  21.     printf("buf: %s\n",buf);  
  22.     read(fd,buf,sizeof(buf));  
  23.     printf("buf: %s\n",buf);  
  24.     close(fd);  
  25.     return 0;  
  26. }  

驱动运行的过程:自己理解。应用程序来打开设备节点文件,这个设备节点文件是通过主设备号和驱动程序联系在一起的。打开这个设备文件的同时,在内核空间会相应的有一个关联的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