、《linux device driver 3》读书笔记(-)

来源:互联网 发布:数据草堂 编辑:程序博客网 时间:2024/05/16 14:19

 一、概念

1)内核空间和用户空间

2)Kernel Symbot Table

A:The table contains the address of global kernel itmes---functions and variables---that are needed to implement modularized drivers.

B:When a module is loaded, any symbol exported by the module becomes part of the kernel symbo table. Using this technology,we can stack new modules on top of other modules.

C:If your modules needs to export symbol for other modules to use, the following macros should be used, EXPORT_SYMBOL(name),or EXPORT_SYMBOL_GPL(name).

 

二、内核模块hello world模型

1)必须的头文件(第三版)

#include <linux/init.h> (lld2提供的例子并没包含该头文件)
#include <linux/module.h>

2)init/clean接口

  1. #include <linux/module.h>
  2. #include <linux/sched.h>
  3. int init_module(void)
  4. {
  5.         EXPORT_NO_SYMBOLS;
  6.         printk("The process is /"%s/" (pid %i)/n",current->comm,current->pid);
  7.         return 0;
  8. }
  9. void cleanup_module(void)  { printk("<1>Goodbye cruel world/n"); }

3)编译

使用例子里的Makefile文件,或者直接用GCC命令gcc -D __KERNEL__  -D MODULE -I /usr/src/linux/include/ -c hello.c - o hello.o 特别的是没有包含宏MODULE时,能编译过,但加载时老提示与现有内核版本不一致。书上说只用包含了头文件<linux/module.h>包含了该宏,但2.4.34内核该文件似乎没包含该宏。

4)必要的考虑(或技巧)

A:初始化过程中出错处理

初始化过程中出错,则需仔细回滚已初始化过的步骤,通常goto语句是个不错的选择。(没亲手体验过)

B:使用资源

申请、释放I/O端口和I/O内存(/proc/ioports, /proc/iomem),使用request_region,release_region,request_mem_region,release_mem_regon,其中错误处理机制可采用goto语句。

C:竞争机制

使用信号量

 

三、简单字符型设备驱动模型(SCULL)

 

1)主、次设备号

主设备号标识设备对应的驱动程序,内核使用主设备号在open操作中将设备与相应驱动程序(提供方法)对应起来。次设备号,内核不使用,交由驱动程序区分所管理下的“设备们”。几个相关的宏:MAJOR(dev_t dev),MINOR(dev_t dev),MKDEV(int major, int minor)。

 

2)分配主设备号

int register_chrdev(unsinged int major,const char *name);

int unregister_chrdev(unsinged int major,const char *name)

当注册函数的major参数为0时,是指由内核动态分配主设备号(似乎从255开始递减地分配)。动态分配的缺点是事先无法得知设备的major号,从而难创建设备节点文件,但这个可通过读/proc/device解决。

 

3)三个重要的结构

A:struct file_operations

Linux内核面向对象化编程的例证,它将文件看作一个“对象”,操作它的函数是“方法”。f_op就是方法的集合。

B:struct file

它代表一个打开的文件,由内核在open时创建,并传递给在该文件上进行操作的所有函数(方法),直到最后的close函数,内核才会释放该数据结构。该结构包含*file_operations,f_pos(文件当前位置)和*private_data(open系统调用在调研驱动程序的open方法前,将这个指针置为NULL,留待驱动程序分配。)

C:struct inode

It is different from the file structure that represents an open file descriptor.There can be numerous file structures representing multiple open descriptors on a single file,but they all point to a signle inode structure.

dev_t i_rdev; sturct cdev *i_cdev;

 

4)open/close方法

引用《linux device driver》(本文全部摘自该书,呵呵),The open method is provided for a driver to do any initialization in preparation for later operations。由于scull是个全局持久的内存设备,因此它在open方法是没有检查硬件就绪等常规检查,而是识别次设备号,更新f_op指针,并配置filp->private_data。

虽然在scull_init_module中的 result = register_chrdev(scull_major, "scull", &scull_fops);注册的方法函数集是scull_fops,但在open方法中,根据次设备号更新了fops。

  1. int scull_open(struct inode *inode, struct file *filp)
  2. {
  3.     Scull_Dev *dev; /* device information */
  4.     int num = NUM(inode->i_rdev);
  5.     int type = TYPE(inode->i_rdev);
  6.     /*
  7.      * the type and num values are only valid if we are not using devfs.
  8.      * However, since we use them to retrieve the device pointer, we
  9.      * don't need them with devfs as filp->private_data is already
  10.      * initialized
  11.      */
  12.     /*
  13.      * If private data is not valid, we are not using devfs
  14.      * so use the type (from minor nr.) to select a new f_op
  15.      */
  16.     if (!filp->private_data && type) {
  17.         if (type > SCULL_MAX_TYPE) return -ENODEV;
  18.         filp->f_op = scull_fop_array[type];
  19.         return filp->f_op->open(inode, filp); /* dispatch to specific open */
  20.     }
  21.     /* type 0, check the device number (unless private_data valid) */
  22.     dev = (Scull_Dev *)filp->private_data;
  23.     if (!dev) {
  24.         if (num >= scull_nr_devs) return -ENODEV;
  25.         dev = &scull_devices[num];
  26.         filp->private_data = dev; /* for other methods */
  27.     }
  28.     MOD_INC_USE_COUNT;  /* Before we maybe sleep */
  29.     /* now trim to 0 the length of the device if open was write-only */
  30.     if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
  31.         if (down_interruptible(&dev->sem)) {
  32.             MOD_DEC_USE_COUNT;
  33.             return -ERESTARTSYS;
  34.         }
  35.         scull_trim(dev); /* ignore errors */
  36.         up(&dev->sem);
  37.     }
  38.     return 0;          /* success */
  39. }
  40. int scull_release(struct inode *inode, struct file *filp)
  41. {
  42.     MOD_DEC_USE_COUNT;
  43.     return 0;
  44. }

5)write/read方法

没看太懂。

6)测试程序

编写了一个小测试程序用于验证scull模块中的open,write,read方法。

  1. #include<fcntl.h>
  2. #include<sys/stat.h>
  3. #include<string.h>
  4. void main()
  5. {
  6.         int file,file2,ret;
  7.         char *pp="hello pp";
  8.         char recv[100];
  9.         int len = strlen(pp)+1;
  10.         file = open("/dev/scull0",O_RDWR);
  11.         if(file < 0)
  12.                 printf("open fail/n");
  13.         else
  14.                 printf("open success/n");
  15.         ret = write(file,pp,len);
  16.         if(ret < 0)
  17.                 printf("write fail/n");
  18.         else
  19.                 printf("write success/n");
  20.         ret = close(file);
  21.         if(ret < 0)
  22.                 printf("close fail/n");
  23.         #if 1
  24.         file2 = open("/dev/scull0",O_RDWR);
  25.         ret = read(file2,recv,len);
  26.         if(ret < 0)
  27.                 printf("read fail/n");
  28.         else
  29.                 printf("%s/n",recv);
  30.         #endif
  31.         ret = close(file2);
  32.         if(ret < 0)
  33.                 printf("close fail/n");
  34. }

 

 

 

 

原创粉丝点击