• 9、Linux驱动的杂项设备


        杂项设备,是字符设备中的特殊,它的主设备号,是 10,不同的杂项设备,通过次设备号进行区分。

    1、注册与注销

    int misc_register(struct miscdevice * misc)

        完成杂项设备的注册,

    int misc_deregister(struct miscdevice *misc)

        可见,设备的注册和注销,都是设置到 struct miscdevice  结构体

    2、struct miscdevice  结构体

    struct miscdevice  { 
        int minor; 
        const char *name;      // 名字 
        const struct file_operations *fops;  // 文件操作结构体 
        struct list_head list; 
        struct device *parent; 
        struct device *this_device; 
        const char *nodename; 
        mode_t mode; 
    };

        结构体中,name 是注册的名字,以后将会在 /dev 目录下,进行显示的 name,里面最主要的是 struct file_operations ,在注册杂项设备的时候,字符设备的结构体与杂项设备进行绑定,

        minor 为 MISC_DYNAMIC_MINOR 的时候,miscdevice 核心层会自动寻找一个空闲的次设备号,

    3、杂项设备与字符设备驱动

        杂项设备,本质上就是字符设备驱动,只不过是一个特殊一点的。杂项设备的主设备号,被固定在 10,通过次设备号进行区分设备。杂项设备注册之后,在 /dev/ 目录下就有 name 设备节点,在 /sys/clas/misc 下面,也会自动生成的类信息,因此,一定程度上,比标准的的字符设备驱动简单了一丢丢。

    4、流程

        struct const file_operations xxxx_fops = {
    
        .unlock_ioctl = xxx_ioctl,
    
         xxxx
    
    };
    
        struct miscdevice xxx_dev = {
    
        .minor = MISC_DYNAMIC_MINOR,
    
        .name = “xxx”,
    
        .fops = xxxx_fops
    
    };
    
    static int __init xxx_init()
    
    {
    
        return misc_register(&xxx_dev );
    
    }
    
    void __exit xxx_exit()
    
    {
    
        return misc_deregister(&xxx_dev) ;
    
    }

       当调用 misc_register(&xxx_dev ); 的时候,会在 misc_open 的帮助下,实现将 xxx_dev 成为 file 的private_data,也就是 系统会帮助我们实现 file->private_data = xxx_dev .

    二、总线平、设备、驱动

        在实际的编程中,最希望的是,一套支持,可以支持一类所有的设备,比如 同一套 DM9000 驱动,可最好是满足在不同板子上运行,所以为了达到目标,就提出了总线、设备、驱动软件架构。

        驱动编写代码中,有一些信息是关于设备的相关信息,比如 IO 地址,内存资源地址,其余的是对这些设备的操作,所以,就将这些对应的设备的信息进行提取到一个文件,设备文件,它只做对不同设备的资源进行定义,不同的平台就单独设置不同的设备文件。

        驱动则只管对设备的操作。

        总线,主要是完成 设备 与 驱动的配合。总线、设备、驱动 的 软件架构,就可以使得驱动四海皆准了。

        在系统注册设备的时候,总线就会根据设备的信息去匹配对应的设备;同理,驱动注册的时候,总线也会根据驱动的信息进行运行匹配的设备,当找到对应的驱动或者设备的时候,就会调用驱动文件的 probe 函数。

    2.1、platform_device 与 platform_driver

        因为提出了总线、设备、驱动这种软件架构,Linux就虚构了虚拟的总线,称之为 platform 总线,设备称之为 platform_device,驱动则成为 platform_driver。

        platform_device  有对应的结构体,

    struct platform_device { 
        const char    * name;     // 设备名字 
        int        id; 
        struct device    dev;    // 设备 
        u32        num_resources; 
        struct resource    * resource;   // 资源
    
        const struct platform_device_id    *id_entry;
    
        /* MFD cell pointer */ 
        struct mfd_cell *mfd_cell;
    
        /* arch specific additions */ 
        struct pdev_archdata    archdata; 
    };

        platform_driver  结构体,我们只需要注意的是 设备的名字,以及设备的资源。

    struct platform_driver { 
        int (*probe)(struct platform_device *);     // probe 
        int (*remove)(struct platform_device *);   // 删除 
        void (*shutdown)(struct platform_device *); 
        int (*suspend)(struct platform_device *, pm_message_t state); 
        int (*resume)(struct platform_device *); 
        struct device_driver driver; 
        const struct platform_device_id *id_table;     // 平台设备的 ID, 
    };

        platform_driver  结构体的地位 和 i2c_driver、spi_driver 、usb_driver, pci_driver 相等。所以说,在实现 I2C 驱动的时候,就可以使用 i2c_driver,当然可以是 platform_driver 。

        设备与驱动的匹配主要是通过设备名字的方式实现的:

    struct platform_device  里面的注册一个 一个 name,指定的是 device 的名字;struct platform_driver 里面的const struct platform_device_id *id_table 表,则是注册了驱动支持的不同设备的名字,正是对两个名字进行了匹配,匹配成功之后,就会调用 driver 端 的probe 函数。一般是在里面是  probe 里面,完成新的字符设备驱动的操作。

    2.2、device 的注册

        当 新建了platform_device  结构体(也可以是新建了一个指针, 再通过platform_device_alloc 接口实现 ),完成了资源和名字的设置之后,需要将这个 device 注册到内核。

    设备注册进内核:

        

    platform_device_add(struct platform_device *pdev)

    设备从内核取消:

       

     platform_device_unregister(struct platform_device * pdev)

    2.3、driver 的注册

        struct platform_driver  里面的 id_table 指定了设备的名字,当总线完成名字的匹配之后,就会调用 driver 端的 probe 函数,一般是在 probe 函数里面完成注册,注册的实现,一般也是注册为字符设备驱动,或者杂项设备。

    3.、device 资源、driver 获取资源

    struct resource { 
        resource_size_t start; 
        resource_size_t end; 
        const char *name; 
        unsigned long flags; 
        struct resource *parent, *sibling, *child; 
    };

        device 结构体 里面,的resource 结构体实现了对设备资源的描述。一般上,只需要关心 start、end、flags。

    start: 设备资源的开始值

    end : 设备资源的结束值

    flags : 设备资源类型的标志。IORESOURCE_IO   IORESOURCE_MEM IORESOURCE_IRQ IORESOURCE_DMA

        当 flag 是 IORESOURCE_MEM  的时候,start 就是内存的开始地址,end 就是内存资源的结束地址。

        flag 是 IORESOURCE_IRQ  的时候,start 和 end 就是中断号的开始值和结束值。如果只是一个中断的话,那么开始值和结束值就是相同的。

        device 定了硬件相关的板级资源,所以 driver 端,就应该在需要获取板级资源的时候,去获取相关的设备资源,driver 是通过这个接口去实现 相匹配的 device 端的资源:

       

     platform_get_resource(struct platform_device * dev,unsigned int type,unsigned int num)

        去 device 获取 type 类型指定的资源。

    如果是获取 irq 的资源的话,也可以使用直接封装的接口:

      

      platform_get_irq(struct platform_device * dev,unsigned int num) 

    4、代码

         借助宋宝华的 Linux设备驱动开发详解的代码,

    4.1、device

    static struct platform_device *globalfifo_pdev;     // 指针
    
    static int __init globalfifodev_init(void) 
    { 
        int ret;
    
        globalfifo_pdev = platform_device_alloc("globalfifo", -1);   // 分配一个  device ,指定了名字 
        if (!globalfifo_pdev) 
            return -ENOMEM;
    
        ret = platform_device_add(globalfifo_pdev);   // 将 device 注册进 总线 
        if (ret) { 
            platform_device_put(globalfifo_pdev); 
            return ret; 
        }
    
        return 0;
    
    } 
    module_init(globalfifodev_init);
    
    static void __exit globalfifodev_exit(void) 
    { 
        platform_device_unregister(globalfifo_pdev);      // 将 device 从总线进行注销
    
    } 
    module_exit(globalfifodev_exit);

    4.3、driver

    static int globalfifo_probe(struct platform_device *pdev) 
    {
    
        ret = misc_register(miscdev);    // 杂项设备的注册 
        if (ret < 0) 
            goto err; 
    }
    
    static int globalfifo_remove(struct platform_device *pdev) 
    { 
        
        misc_deregister(miscdev);   // 注销
    
        return 0; 
    }
    
    static struct platform_driver globalfifo_driver = { 
        .driver = { 
            .name = "globalfifo",   // 名字 
            .owner = THIS_MODULE, 
        }, 
        .probe = globalfifo_probe,   // 名字匹配成功,就调用 probe 函数 
        .remove = globalfifo_remove, 
    };
    
    module_platform_driver(globalfifo_driver);   // 平台设备 driver 入口
  • 相关阅读:
    【Lua】LuaForWindows_v5.1.4-46安装失败解决方案
    【C++】指针引发的bug
    【C++】指针引发的bug
    【C++】位操作(3)-获取某位的值
    bzoj1444
    bzoj1758
    bzoj3091
    poj1741 bzoj2152
    bzoj2125 3047
    bzoj3669
  • 原文地址:https://www.cnblogs.com/qxj511/p/5514271.html
Copyright © 2020-2023  润新知