首页 > 代码库 > [Linux驱动]字符设备驱动学习笔记(二)———实例

[Linux驱动]字符设备驱动学习笔记(二)———实例

一,注册字符设备

 

[cpp] view plaincopy技术分享技术分享
 
  1. #define GLOBALMEM_MAJOR 256  
  2. #define GLOBALMEM_SIZE 0X1000 //4k  
  3. static int char_major=GLOBALMEM_MAJOR;//主设备号  
  4. struct chartest_dev  
  5. {  
  6.    struct cdev cdev;  
  7.    unsigned char mem[GLOBALMEM_SIZE];  
  8. };  
  9. struct chartest_dev glob_char_dev;//全局设备结构  
  10. static struct class *char_class=NULL;  
  11. static struct class_device *char_class_dev=NULL;  
  12. //-------------------------------------设备打开函数,匹配上层函数调用  
  13. static int char_open(struct inode *inode, struct file *filp)  
  14. {  
  15.   filp->private_data = &glob_char_dev;  
  16.   return 0;  
  17. }  
  18. //-------------------------------------从内核往用户空间拷贝读取数据  
  19. static  ssize_t  char_read(struct file* filp,char __user *buf,size_t count,loff_t *ppos)  
  20. {  
  21.         unsigned long p=*ppos;  
  22.         int ret=0;  
  23.         struct chartest_dev *char_dev=filp->private_data;  
  24.         if(p>GLOBALMEM_SIZE)//作越界检查  
  25.         return 0;  
  26.     if(count>GLOBALMEM_SIZE-p)// 读取数据较多  
  27.         count=GLOBALMEM_SIZE-p;  
  28.     if(copy_to_user(buf,(void*)(char_dev->mem+p),count))  
  29.             ret=-EFAULT;  
  30.         else  
  31.     {  
  32.         *ppos=*ppos+count;  
  33.                 ret=count;  
  34.                 printk(KERN_INFO "READ %d BYTES(S) FROM %d",count,p);  
  35.     }  
  36.         return ret;  
  37. }  
  38. //-------------------------------------从用户控件往内核写数据  
  39. static ssize_t  char_write(struct file* filp,const char __user *buf,size_t count,loff_t *ppos)  
  40. {  
  41.         unsigned long p=*ppos;  
  42.         int ret=0;  
  43.     struct chartest_dev *char_dev=filp->private_data;  
  44.         if(p>GLOBALMEM_SIZE)  
  45.         return 0;  
  46.     if(count>GLOBALMEM_SIZE-p)  
  47.         count=GLOBALMEM_SIZE-p;  
  48.     if(copy_from_user(char_dev->mem+p,buf,count))  
  49.         ret=-EFAULT;  
  50.     else  
  51.     {  
  52.         *ppos=*ppos+count;  
  53.                 ret=count;  
  54.                 printk(KERN_INFO "WRITTEN %d BYTES(S) FROM %d",count,p);  
  55.     }  
  56.         return ret;  
  57. }  
  58. static loff_t char_llseek(struct file *filp, loff_t offset, int orig)  
  59. {  
  60.   loff_t ret = 0;  
  61.   switch (orig)  
  62.   {  
  63.     case 0:     
  64.       if (offset < 0)  
  65.       {  
  66.         ret =  - EINVAL;  
  67.         break;  
  68.       }  
  69.       if ((unsigned int)offset > GLOBALMEM_SIZE)  
  70.       {  
  71.         ret =  - EINVAL;  
  72.         break;  
  73.       }  
  74.       filp->f_pos = (unsigned int)offset;  
  75.       ret = filp->f_pos;  
  76.       break;  
  77.     case 1:    
  78.       if ((filp->f_pos + offset) > GLOBALMEM_SIZE)  
  79.       {  
  80.         ret =  - EINVAL;  
  81.         break;  
  82.       }  
  83.       if ((filp->f_pos + offset) < 0)  
  84.       {  
  85.         ret =  - EINVAL;  
  86.         break;  
  87.       }  
  88.       filp->f_pos += offset;  
  89.       ret = filp->f_pos;  
  90.       break;  
  91.     default:  
  92.       ret =  - EINVAL;  
  93.       break;  
  94.   }  
  95.   return ret;  
  96. }  
  97. static const struct file_operations char_fops = {  
  98.     .owner      = THIS_MODULE,  
  99.     .llseek     =char_llseek,  
  100.     .open       = char_open,  
  101.     .read       = char_read,  
  102.     .write      = char_write,  
  103.     };  
  104.   
  105. static void chartest_setup_cdev()  
  106. {  
  107.     int err,devno=MKDEV(char_major,0);  
  108.     cdev_init(&glob_char_dev.cdev,&char_fops);/*初始化*/  
  109.     glob_char_dev.cdev.owner=THIS_MODULE;  
  110.     err=cdev_add(&glob_char_dev.cdev,devno,1);  
  111.     if(err<0)  
  112.     printk(KERN_NOTICE "ERROR %d ADDING chartest",err);  
  113.     char_class=class_create(THIS_MODULE,"chartest");  
  114.     char_class_dev=device_create(char_class,NULL,devno,NULL,"chartest",0);  
  115. }  
  116. static int  char_init(void)  
  117. {  
  118.  int result;  
  119.  unsigned long char_dev_no=MKDEV(char_major,0);  
  120.  if(char_major){  
  121.     result=register_chrdev_region(char_dev_no,1,"chartest");  
  122.   }  
  123.   else{  
  124.         result=alloc_chrdev_region(&char_dev_no,0,1,"chartest");  
  125.     char_major=MAJOR(char_dev_no);  
  126.   }  
  127.  printk(KERN_ALERT "Hello,,We succeed\n");  
  128.  if(result<0)  
  129.     return result;  
  130.   chartest_setup_cdev();  
  131.   printk(KERN_ALERT "Hello,,We succeed\n");  
  132.   return 0;  
  133. }  
  134. static void  char_exit(void)  
  135. {  
  136.     cdev_del(&glob_char_dev.cdev);  
  137.     unregister_chrdev_region( MKDEV(char_major,0),1);//ZHU XIAO  
  138.     device_unregister(char_class_dev);  
  139.     class_destroy(char_class);  
  140.     printk(KERN_ALERT "720 unregister success\n");  
  141. }  
  142. MODULE_LICENSE("Dual BSD/GPL");  
  143. module_init(char_init);  

 

二,注意点:

read和write函数的参数 buff是用户空间的指针,因此内核代码不能直接引用其中的内容,主要原因如下:
1,在内核模式运行时,用户空间的指针可能是无效的
2,用户空间是分页的,涉及的内容可能不在RAM中,对用户空间的内存直接引用可能导致页错误
3.指针是用户空间提供的,可能存在缺陷。

copy_to_user()和copy_from_user()两个函数除了在内核空间和用户空间进行数据拷贝,还会检查用户空间的指针是否有效,如果指针无效则不拷贝,如果拷贝过程中遇到无效地址,则仅仅会复制部分数据,,在这两种情况下返回值是还需要拷贝内存数量的值。

[Linux驱动]字符设备驱动学习笔记(二)———实例