Linux_7th_第1个linux驱动___给驱动模块上户口(二)

来源:互联网 发布:淘宝网第三方支付平台 编辑:程序博客网 时间:2024/05/01 15:29

从这篇博文开始,我们终于可以给我们的驱动模块first_drv一个正式的编制了,现在他已经填好了自己的户口登记信息表,距离拿户口本儿只有一步了!


内核内部有自己的一套安装驱动模块的方法,就像是民政局工作人员知道怎么给一个人办户口一样,对于一个普通公民来说,工作人员做了哪些操作办好了户口本,并不是普通公民需要关心的,我们最关心的是如何把我们填好的户口信息登记表交给工作人员,如何向工作人员表明我们有办户口的请求。


在linux系统中,对于字符设备驱动,我们通过register_chrdev这个函数来向内核注册驱动模块,我们希望在执行insmod first_drv.ko,也就是在调用first_drv_init函数的时候就完成驱动模块的注册,毕竟这就是驱动的初始化函数,在这个函数里“完成向内核注册驱动模块的任务”不正是它的职责所在吗。


我们来看一下之前的first_drv_init是怎样写的吧:


static int __init first_drv_init(void){       printk(KERN_INFO"hello world!\n");    return 0;}


我们知道将在里面添加一个register_chrdev函数,但是我们还不了解这个函数的用法,在Source Insight的linux-2.6.22.6内核源码工程里搜索一下,看看别人是怎么用的吧,如我搜到的例子:


int ret;

···

ret = register_chrdev(VFC_MAJOR, vfcstr, &vfc_fops);

if(ret) {

  printk(KERN_ERR "Unable to get major number %d\n", VFC_MAJOR);

  kfree(vfc_dev_lst);

  return -EIO;

}

···

return 0;


我们重点来看有颜色的那一行,VFC_MAJOR是一个宏定义,为60;vfcstr是数组vfcstr[]="vfc"的首地址,&vfc_fops是结构体vfc_fops的地址。


再来查找一下的定义:


int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)

{

···

}


果然如此,和我们的分析完全吻合,第一个参数major指主设备号,是unsigned int类型的数;第二个参数name是字符串类型数据的指针类型,而const修饰符说明它是输入型参数,其中这个字符串应该是设备的名字,;第三个参数fops是指向file_operations类型结构体变量的指针类型,并且也是输入型参数。 


我们在里面加上register_chrdev函数,改动如下:


static int __init first_drv_init(void){       int ret = -1;    register_chrdev(100, "first_drv", &first_drv_fops);    if(ret != 0)    {        printk(KERN_INFO"Register_chrdev failure!\n")        return -1;    }    printk(KERN_INFO"Register_chrdev success...\n");    return 0;}


我们的主设备号暂定为100,设备名字可以随便起,我们起为:"first_drv",我们之前定义了first_drv_fops结构体:


static const struct file_operations first_drv_fops = {

        .owner          = THIS_MODULE,

        .open           = first_drv_open,

        .release        = first_drv_release,

};


我们通过&first_drv_fops来将它的地址传进register_chrdev函数中去。


既然有了注册驱动模块的函数register_chrdev,那么也应该有卸载驱动函数,如果一个人死了几百年年,户籍系统出于方便管理的原因,可能会销毁这个人的数据,那么也就是说,一旦我们不再需要这个驱动模块在系统中工作时,我们要做的第一件事就是把这个驱动所占用的资源回收回来,回收资源的意思就是指:将file_operations这本书的第100面用橡皮擦擦干净,抹去该驱动模块的一切信息。


在字符设备驱动中,我们使用unregister_chrdev函数来完成驱动模块的卸载,理所应当地把unregister_chrdev函数放到first_drv_exit函数中:


static void __exit first_drv_exit(void)

{

     int ret = -1;

    ret = unregister_chrdev(100, "first_drv");

    if(ret != 0)

    {

        printk(KERN_INFO"Unregister_chrdev failure!\n");

    }

    printk(KERN_INFO"Unregister_chrdev success...\n");

}


不同于register_chrdev函数,unregister_chrdev只需要指定驱动模块的主设备号和名字就可以找到该驱动并卸载。


注意到不论是register_chrdev函数还是unregister_chrdev函数中,我们都使用了一个变量ret来记录这两个函数的返回值,当注册或卸载成功时,这两个函数就会返回0,当注册或卸载失败时,会返回一个负数,根据这个特点,我们可以打印出注册或卸载成功/失败的信息。


那么什么时候会注册、卸载失败呢,如果file_operations这本书的100面已经提前登记了别的驱动模块的信息,那么我们去申请100这个主设备号就会失败。


有的同学可能知道通过在最小根文件系统下执行cat /proc/devices可以查看当前系统中已经安装的驱动以及它们的主设备号,如果发现100这个主设备号已经有驱动模块在使用了,那就找一个空缺的、未被使用的主设备号,来把我们的驱动程序中的100改成这个主设备号,这个方法是可以,但是有些繁琐。


其实内核是可以自动帮我们分配主设备号的,我们在下一篇博文再来介绍。


下面附上我们完整的first_drv.c驱动程序:

#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>static int first_drv_open(struct inode *inode, struct file *file){    printk(KERN_INFO"It is in first_drv_open.\n");    return 0;}static int first_drv_release(struct inode *inode, struct file *file){    printk(KERN_INFO"It is in first_drv_release.\n");    return 0;}static const struct file_operations first_drv_fops = {        .owner          = THIS_MODULE,        .open           = first_drv_open,        .release        = first_drv_release,};static int __init first_drv_init(void){    int ret = -1;        ret = register_chrdev(100, "first_drv", &first_drv_fops);        if(ret < 0)    {        printk(KERN_ERR"Register_chrdev failure!\n");        return -1;    }    printk(KERN_INFO"Register_chrdev success...\n");    return 0;}static void __exit first_drv_exit(void){    int ret = -1;        ret = unregister_chrdev(100, "first_drv");        if(ret < 0)    {        printk(KERN_INFO"Unregister_chrdev failure!\n");    }    printk(KERN_INFO"Unregister_chrdev success...\n");}module_init(first_drv_init);module_exit(first_drv_exit);MODULE_LICENSE("GPL");




0 0
原创粉丝点击