linux 设备驱动

http://blog.csdn.net/yangjin_unique/article/details/8222465

目的:将内核内存的一块作为字符设备,用户可通过这些调用来读写这段内存。

总结:

1、设备号

主设备号标识设备对应的驱动程序,次设备号由内核使用,用于确定设备文件所指的设备。

通过次设备号获得一个指向内核设备的直接指针,也可将此设备号当作设备本地数组的索引。

设备编号用dev_t表示(linux/types.h  32位,其中12位表示主设备号,20位表示次设备号)。

dev_t获得主设备号或次设备号:MAJOR(dev_t dev); MINOR(dev_t dev)

已知主设备号和次设备号来获取dev_t类型:MKDEV(int  major,  int  minor)

获取一个或多个设备编号int  register_chrdev_region(dev_t first,  unsigned int  count,  char  *name);(静态分配,事先已知道设备号)

动态分配设备编号int  alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name); 调用成功后dev会保存已分配的第一个编号。

释放设备编号:void unregister_chrdev_region(dev_t first, unsigned int count);

接下来,驱动程序需要将设备编号和内部函数连接起来。

注:(下一步可尝试采用动态分配设备号

 

动态分配设备号缺点:不能预先创建设备节点(因为分配的设备号不能保证始终一致)。

2、文件操作file_operations:

这些操作将与设备编号连接起来。

__user:用于文档,表明该指针是一个用户空间地址。

主要成员:open,  ioctl,  read,  write,  llseek

3struct file结构  linux/fs.h 文件描述符

每打开一个文件,内核就会创建一个对应的file结构,在open()时创建,同时会传递给在该文件上进行操作的所有函数(因为file结构中包含file_operations结构,而该结构包含了所有驱动操作的函数)。

内核中用filp作为执行file结构的指针。

主要成员:

Mode_t  f_mode;  loff_t  f_pos;   struct file_operations *f_pos;  void  private_data; 

4inode结构

对单个文件只有一个inode,而可能有多个file(由于forkdup操作)

主要成员:

dev_t  i_rdev; 对表示设备文件的inode结构,该字段包含真正的设备编号。

struct cdev  *i_cdev;   该结构表示字符设备的内核的内部结构,当inode指向一个字符设备文件时,该字段包含指向struct cdev结构的指针。

inode中获取设备号: iminor(struct inode *inode);  imajor(inode);

5、字符设备注册  /linux/cdev.h

内核使用struct cdev结构表示字符设备,所以在内核调用该设备操作之前,需要分配并注册一个或者多个该结构。

注册有两种方式:

新方法:

1)定义字节的结构:

struct my_dev {

         struct  cdev  cdev; //此处如果定义的是指针类型,则需要申请分配内存

}my_dev;

//my_dev ->cdev = cdev_alloc();  //如果cdev是指针则需要这一步

my_dev->cdev.ops = &my_fops;

my_dev->cdev.owner = THIS_MODULE;

2)再调用cdev_init(struct cdev *cdev,  struct file_operations *fops);

3)调用cdev_add(struct cdev *dev,  dev_t num,  unsigned int count);

上面每一步都要判断函数调用是否出错。

旧办法(老接口):

注册:int register_chrdev(); 移除:int unregister_chrdev()

 

6、各操作函数实现

1open   int (*open) (struct  inode  *inode,  struct  file  *filp)

完成以下工作:传入一个inode,创建一个file结构

n  检查设备特定错误(如未就绪);

n  如果设备首次打开,则进行初始化;

n  必要时更新f_op指针;

n  分配并填写filp->private_data

注:inode结构是传入的参数,对应一个特定的设备(这就是为什么要在/devmknodde 原因),而file结构的filp是要修改的参数(传出),对应该设备的一个文件描述符,也就是一个inode可能有多个file描述符,而每个描述符需要保存inode的信息,即存放在filp->private_data中。

2release  int (*release) (struct inode *inode,  struct file *filp)

完成工作:传入一个inode,释放这个file结构

n  释放由open分配的、保存在filp->private_data中的内容;

n  在最后一次close时关闭设备

dup fork都会在不调用open时创建新的file结构(对应同一个inode)。

注:并不是每个close调用都会调用release;只有真正释放设备数据结构的close调用才会调用release。内核对每个file结构维护其被使用次数的计数器,无论是fork还是dup,都不会创建新的数据结构(只会有open创建),它们只是增加已有结构中的计数器而已。只有在file结构的计数为0时,close才会调用release

3read  ssize_t  read(struct  file  *filp,  char __user *buf,  count,  loff_t *offp)

完成工作:传入file,将count个字节数据写入用户地址buf,修改loff_t

copy_to_user()实现
返回值说明:

n  等于count:所请求的字节数读取成功;

n  返回值为正,但小于count:只读取了部分数据;

n  0:已经达到文件尾;

n  负值:出错

4write  ssize_t  write(struct  file  *filp,  char  __user *buf,  count,  offp);

copy_from_user()实现

返回值同上。

 

[cpp] view plain copy print ?
  1. 1)驱动代码  
  2. Demo.h  
  3. #ifndef _DEMO_H_   
  4. #define _DEMO_H_   
  5. #include <linux/ioctl.h>   
  6. /*Macros to help debuging*/  
  7. #undef PDEBUG   
  8. #ifdef DEMO_DEBUG   
  9.     #ifdef __KERNEL__   
  10.         #define PDEBUG(fmt, args...) printk(KERN_DEBUG "DEMO:" fmt,## args)    
  11.     #else   
  12.         #define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)   
  13.     #endif   
  14. #else   
  15. #define PDEBUG(fmt, args...)    
  16. #endif   
  17.   
  18. #define DEMO_MAJOR 224   
  19. #define DEMO_MINOR 0   
  20. #define COMMAND1 1   
  21. #define COMMAND2 2   
  22.   
  23. struct demo_dev {  
  24.     struct cdev cdev;  
  25. };  
  26.   
  27. ssize_t demo_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);  
  28. ssize_t demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);  
  29. loff_t demo_llseek(struct file *filp, loff_t off, int whence);  
  30. int demo_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);  
  31.   
  32. #endif   
  33. demo.c  
  34. #include <linux/module.h>   
  35. #include <linux/kernel.h>   
  36. #include <linux/fs.h>   
  37. #include <linux/errno.h>   
  38. #include <linux/types.h>   
  39. #include <linux/fcntl.h>   
  40. #include <linux/cdev.h>   
  41. #include <linux/version.h>   
  42. #include <linux/vmalloc.h>   
  43. #include <linux/ctype.h>   
  44. #include <linux/pagemap.h>   
  45. #include "demo.h"   
  46.   
  47. MODULE_AUTHOR("Yangjin");  
  48. MODULE_LICENSE("Dual BSD/GPL");  
  49.   
  50. struct demo_dev *demo_devices;  
  51.   
  52. static unsigned char demo_inc = 0;//全局变量,每次只能打开一个设备   
  53.   
  54. static u8 demo_buffer[256];  
  55.   
  56. int demo_open(struct inode *inode, struct file *filp)  
  57. {  
  58.     struct demo_dev *dev;  
  59.       
  60.     if (demo_inc > 0) return -ERESTARTSYS;  
  61.     demo_inc++;  
  62.     dev = container_of(inode->i_cdev, struct demo_dev, cdev);  
  63.     filp->private_data = dev;  
  64.   
  65.     return 0;  
  66. }  
  67.   
  68. int demo_release(struct inode *inode, struct file *filp)  
  69. {     
  70.     demo_inc--;  
  71.     return 0;  
  72. }  
  73.   
  74. ssize_t demo_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)  
  75. {  
  76.     int result;  
  77.     loff_t pos = *f_pos; //pos: offset   
  78.   
  79.     if (pos >= 256) {  
  80.         result = 0;  
  81.         goto out;                                                                                                
  82.     }  
  83.     if (count > (256 - pos))  
  84.         count = 256 - pos;  
  85.     pos += count;  
  86.   
  87.     if (copy_to_user(buf, demo_buffer+*f_pos, count)) {  
  88.         count = -EFAULT;  
  89.         goto out;     
  90.     }  
  91.       
  92.     *f_pos = pos;  
  93. out:  
  94.     return count;  
  95. }  
  96.   
  97. ssize_t  demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)  
  98. {  
  99.     ssize_t retval = -ENOMEM;  
  100.     loff_t pos = *f_pos;  
  101.   
  102.     if (pos > 256)  
  103.         goto out;  
  104.     if (count > (256 - pos))   
  105.         count = 256 - pos;    
  106.     pos += count;  
  107.     if (copy_from_user(demo_buffer+*f_pos, buf, count)) {  
  108.         retval = -EFAULT;  
  109.         goto out;     
  110.     }  
  111.       
  112.     *f_pos = pos;  
  113.     retval = count;  
  114. out:  
  115.     return retval;  
  116. }  
  117.   
  118. int  demo_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)  
  119. {  
  120.     if (cmd == COMMAND1) {  
  121.         printk("ioctl command 1 successfully\n");  
  122.         return 0;     
  123.     }  
  124.     if (cmd == COMMAND2) {  
  125.         printk("ioctl command 2 successfully\n");  
  126.         return 0;     
  127.     }  
  128.     printk("ioctl error\n");  
  129.     return -EFAULT;  
  130. }  
  131.   
  132. loff_t demo_llseek(struct file *filp, loff_t off, int whence)  
  133. {  
  134.     loff_t pos;  
  135.       
  136.     pos = filp->f_pos;  
  137.     switch (whence) {  
  138.     case 0:  
  139.         pos = off;  
  140.         break;  
  141.     case 1:  
  142.         pos += off;  
  143.         break;  
  144.     case 2:  
  145.     default:  
  146.         return -EINVAL;   
  147.     }  
  148.       
  149.     if ((pos > 256) || (pos < 0))  
  150.         return -EINVAL;  
  151.       
  152.     return filp->f_pos = pos;  
  153. }  
  154.   
  155. struct file_operations demo_fops = {  
  156.     .owner = THIS_MODULE,  
  157.     .llseek = demo_llseek,  
  158.     .read = demo_read,  
  159.     .write = demo_write,  
  160.     .ioctl = demo_ioctl,  
  161.     .open = demo_open,  
  162.     .release = demo_release,  
  163. };  
  164.   
  165. void demo_cleanup_module(void)  
  166. {  
  167.     dev_t devno = MKDEV(DEMO_MAJOR, DEMO_MINOR);  
  168.       
  169.     if (demo_devices) {  
  170.         cdev_del(&demo_devices->cdev);  
  171.         kfree(demo_devices);  
  172.     }  
  173.     unregister_chrdev_region(devno, 1);  
  174. }  
  175.   
  176. Init module流程:  
  177. 1)注册设备号MKDEV;  
  178. 2)注册设备驱动程序,即初始化cdev结构(嵌入到demo_devices结构中)  
  179. int demo_init_module(void)  
  180. {  
  181.     int result;  
  182.     dev_t dev = 0;  
  183.       
  184.     dev = MKDEV(DEMO_MAJOR, DEMO_MINOR);  
  185.     result = register_chrdev_region(dev, 1, "DEMO");  
  186.     if (result < 0) {  
  187.         printk(KERN_WARNING "DEMO: can't get major %d\n", DEMO_MAJOR);  
  188.         return result;  
  189.     }  
  190.     demo_devices = kmalloc(sizeof(struct demo_dev), GFP_KERNEL);  
  191.     if (!demo_devices) {  
  192.         result = -ENOMEM;  
  193.         goto fail;  
  194.     }  
  195.     memset(demo_devices, 0, sizeof(struct demo_dev));  
  196.     cdev_init(&demo_devices->cdev, &demo_fops);    
  197. demo_devices->cdev.owner = THIS_MODULE;  
  198.     demo_devices->cdev.ops = &demo_fops; //将创建的字符设备与file_operations中各函数操作连接起来   
  199.   
  200.     result = cdev_add(&demo_devices->cdev, dev, 1);  
  201.     if (result) {  
  202.         printk(KERN_NOTICE "error %d adding demo\n", result);  
  203.         goto fail;  
  204.     }  
  205.     return 0;  
  206. fail:  
  207.     demo_cleanup_module();  
  208.     return result;  
  209. }  
  210.   
  211. module_init(demo_init_module);  
  212. module_exit(demo_cleanup_module);  


 

2)加载驱动insmod demo.ko,再使用lsmodcat /proc/modules查看驱动是否安装;

3)创建设备节点:mknod  /dev/yangjin c 224 0;注意:此处的节点设备号要与驱动程序中的注册的设备号相同。

4)再编写应用程序测试代码:

用户测试代码:

[cpp] view plain copy print ?
  1. #include <sys/types.h>   
  2. #include <unistd.h>   
  3. #include <fcntl.h>   
  4. #include <linux/rtc.h>   
  5. #include <linux/ioctl.h>   
  6. #include <stdio.h>   
  7. #include <stdlib.h>   
  8.   
  9. #define COMMAND1 1   
  10. #define COMMAND2 2   
  11.   
  12. int main()  
  13. {  
  14.     int fd;  
  15.     int i;  
  16.     char data[256] = {0};  
  17.     int retval;  
  18.       
  19.     fd = open("/dev/yangjin", O_RDWR);  
  20.     if (fd == 1) {  
  21.         perror("open error\n");  
  22.         exit(-1);  
  23.     }  
  24.     printf("open /dev/yangjin successfully\n");  
  25.     retval = ioctl(fd, COMMAND1, 0);  
  26.     if (retval == -1) {  
  27.         perror("ioctl error\n");  
  28.         exit(-1);  
  29.     }  
  30.     printf("ioctl command 1 successfully\n");  
  31.     retval = write(fd, "yangjin", 7);  
  32.     if (retval == -1) {  
  33.         perror("write error\n");  
  34.         exit(-1);  
  35.     }  
  36.     retval = lseek(fd, 0, 0);  
  37.     if (retval == -1) {  
  38.         perror("lseek error\n");  
  39.         exit(-1);  
  40.     }  
  41.     retval = read(fd, data, 10);  
  42.     if (retval == -1) {  
  43.         perror("read error\n");  
  44.         exit(-1);  
  45.     }  
  46.     printf("read successfully: %s\n", data);  
  47.     close(fd);  
  48.     return 0;  
  49. }  


 

你可能感兴趣的:(linux 设备驱动)