设为首页 加入收藏

TOP

Linux字符设备驱动入门(二)
2014-11-24 13:24:42 来源: 作者: 【 】 浏览:3
Tags:Linux 字符 设备驱动 入门
当其指向一个字符设备时,其中的i_cdev成员既包含了指向cdev结构的指针。而file表示打开的文件描述符,对一个文件,若打开多次,则会有多个file结构,但只有一个inode与之对应。


因为驱动工作在内核空间,不能使用用户空间的libc函数,所以程序中打印语句为内核提供的printk,而非printf,KERN_NOTICE宏其实标记的是日志级别(共有八个)不同级别的消息会记录到不同的地方。如果你运行本模块,可能会发现printk语句并没有输出到控制台,这是正常的,控制台只显示一定级别的消息。当日志级别小于console_loglevel时,消息才能显示出来。你可以通过dmsg命令看到这些信息,也可以通过修改日志级别使之输出到你的虚拟终端。


作好以上准备工作后,接下来就可以开始进行向内核申请主设备号了。设备号是干什么吃的?据LDD记载,对字符设备的访问是通过文件系统内的设备名称进行的。那些被称为特殊文件、设备文件的节点,通常位于/dev目录,如果ls -l 查看该目录,第一列中带有c标志的即为字符设备,有b标志的为块设备。而第5、6列所示的两个数字分别为设备的主、次设备号。通常,主设备号标识设备所用的驱动程序(现在大多设备仍然采用“一个主设备号对应一个驱动程序”的规则),次设备号用于确定设备,比如你有两块网卡,使用同一驱动,主设备号相同,那么他们将由次设备号区分。
/* Module housekeeping */
static int hello_init(void){
int result;
dev_t dev = MKDEV( hello_major, 0 );/*to transfer major as dev_t type*/

/* alloc the major device number dynamicly */
result = alloc_chrdev_region(&dev, 0 ,1, "hello" );
if( result < 0 ){
printk( KERN_NOTICE"Hello: unable to get major %d\n",hello_major );
return result;
}
hello_major = MAJOR(dev);
/* set up devices, in this case, there is only one device */
printk( KERN_NOTICE"hello init. major:%d, minor:%d\n",hello_major,0 );
//printk( KERN_ALERT"hello init: %d, %d\n",hello_major,0 );
hello_setup_cdev(&hellow, 0 , &hello_ops );

return 0;

}

/* Exit routine */
static void hello_exit(void){
/* remove the cdev from kernel */
cdev_del(&hellow );
/* release the device numble alloced earlier */
unregister_chrdev_region( MKDEV( hello_major, 0 ), 1 );
printk( KERN_NOTICE"hello exit. major:%d,minor %d\n",hello_major,0 );
}


这里主设备号的分配由alloc_chrdev_region(第一个参数为dev_t 指针,用来存放设备编号,第二个参数为要使用的第一个次设备号,通常为0,第三个参数为请求的连续设备编号个数)动态分配,当然也可以静态指定一个未被使用的主设备号,相应函数为register_chrdev_region,但不推荐这样做。在模块被卸载时(hello_exit),通过unregister_chrdev_region释放设备号。MKDEV宏将给出的主、次设备号转换成dev_t类型,MAJOR,MINOR分别从dev_t中析取主次设备号。


这里几个函数的原型为:


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);


void unregister_chrdev_region(dev_t first, unsigned int count);




/* set up the cdev stucture for a device */
static void hello_setup_cdev( struct cdev *dev, int minor, struct
file_operations *fops ){
int err;
int devno = MKDEV( hello_major, minor );
/* initialize the cdev struct */
cdev_init( dev,fops );
dev->owner = THIS_MODULE;
err = cdev_add( dev, devno, 1 ); /* register the cdev in the kernel */
if( err )
printk( KERN_NOTICE"Error %d adding hello%d\n",err ,minor );
}


最后module_init( hello_init ); module_exit( hello_exit );指定了模块初始化和关闭函数。MODULE_LICENSE( "Dual BSD/GPL" ); 指定模块使用的许可证能被内核识别的许可证有GPL、GPL v2、 Dual BSD/GPL、 Dual MPL/GPL、Proprietary(专有)等,如果模块没有显式标记许可证,则会被认定为“专有”,内核加载这样的模块会被“污染”。


/* register the init and exit routine of the module */
module_init( hello_init );
module_exit( hello_exit );


MODULE_AUTHOR( "jabenwang" );
MODULE_LICENSE( "Dual BSD/GPL" );


到这里,这个字符设备驱动已经完成,接下来就是编译它。


首页 上一页 1 2 下一页 尾页 2/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇G870 Uboot启动流程 下一篇Uboot下的DRAM的初始化

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: