2.设备操作接口 (1).注册与注销 注册时,我们必须考虑到两种管理方式(传统方式与devfs方式)的兼容性。在这里, 我们用条件编译来解决这个问题。由于许多主设备号已经静态地分配给了公用设备,Linux 提供了动态分配机制以获取空闲的主设备号。传统方式下,如果调用devfs_register_chrdev( ) 时的major 为零的话,它所调用的register_chrdev( )函数就会选择一个空闲号码作为返回值 返回。主设备号总是正的,因此不会和错误码混淆。在devfs方式下,如果devfs_register( ) 的flags 参数值为DEVFS_FL_AUTO_DEVNUM,注册时就会自动生成设备号。 动态分配的缺点是:由于分配的主设备号不能保证总是一样的,无法事先创建设备节点。 但是这并不是什么问题,因为一旦分配了设备号,我们就可以从/proc/devices 读到。为了加 载一个设备驱动程序,我们可以用一个简单的脚本替换对insmod的调用,它通过/proc/devices 获得新分配的主设备号,并创建节点。加载动态分配主设备号驱动程序的脚本可以利用awk 这类工具从/proc/devices 中获取信息,并在/dev中创建文件。在我们的实例程序中,为了简 单起见,仍然使用静态分配的主设备号。 你也许会注意到我们并没有使用统一的函数名init_module( )和cleanup_module( ),这是 由于内核编程风格的变化。自从2.3.13 版的内核以来,Linux提供了两个宏module_init( )和 module_exit( )来显式地命名模块的注册和注销函数。通常在源文件的末尾写上这两个宏,例 如: module_init(vfifo_init_module); module_exit(vfifo_exit_module); 注意,在使用这两个宏之前必须先包含头文件<linux/init.h>。这样做的好处是,内核中 的每一个注册和注销函数都有一个唯一的名字,有助于调试。我们知道驱动程序既可以设计 成模块,又可以静态地编译进内核,用了这两个宏后就能更方便地支持这两种方式。实际上, 对于模块来说,它们所做的工作仅仅是把给出的函数名重新命名为 init_module( )和 cleanup_module( )。当然,如果你已使用了init_module( )和cleanup_module( )作为函数名, 那就没必要再使用这两个宏了。 在函数名之前,我们可以看到一个表示属性的词“__init”,加了这个属性之后,系统 会在初始化完成之后丢弃初始化函数,收回它所占用的内存。这样可以减小内核所占用的内 存空间。但它只对内建的驱动程序有用,对于模块则没有影响。
vfifo.c char vfifoname[8]; static int __init vfifo_init_module(void) { int result,i; SET_MODULE_OWNER(&vfifo_fops); #ifdef CONFIG_DEVFS_FS vfifo_devfs_dir=devfs_mk_dir(NULL,"vfifo",NULL); if(!vfifo_devfs_dir) return -EBUSY; #endif result=devfs_register_chrdev(vfifo_major,"vfifo",&vfifo_fops); if(result<0){ printk(KERN_WARNING "vfifo: can't get major %d",vfifo_major); return result; } if(vfifo_major==0) vfifo_major=result; vfifo_devices = kmalloc(vfifo_nr_devs*sizeof(Vfifo_Dev),GFP_KERNEL); if(!vfifo_devices){ return -ENOMEM; } memset(vfifo_devices,0,vfifo_nr_devs*sizeof(Vfifo_Dev)); for(i=0;i<vfifo_nr_devs;i++){ init_waitqueue_head(&vfifo_devices.rdq); init_waitqueue_head(&vfifo_devices.wrq); sema_init(&vfifo_devices.sem,1); #ifdef CONFIG_DEVFS_FS sprintf(vfifoname,"vfifo%d",2*i); vfifo_devices.w_handle= devfs_register(vfifo_devfs_dir,vfifoname, DEVFS_FL_NON, vfifo_major,2*i,S_IFCHR|S_IRUGO|S_IWUGO, &vfifo_fops,vfifo_device+i); sprintf(vfifoname,"vfifo%d",2*i+1); vfifo_devices.r_handle= devfs_register(vfifo_devfs_dir,vfifoname, DEVFS_FL_NON, vfifo_major,2*i+1,S_IFCHR|S_IRUGO|S_IWUGO, &vfifo_fops,vfifo_device+i); if(!vfifo_devices.r_handle||!vfifo_devices.w_handle){ printk(KERN_WARNING "vfifo: can't register vfifo device nr %i\n",i); } #endif } #ifdef VFIFO_DEBUG create_proc_read_entry("vfifo",0,NULL,vfifo_read_mem,NULL); #endif return 0; }
[此贴子已经被linuxarm于2006-7-20 15:17:57编辑过] |