• hid_info函数分析


    昨天博文《linux下无线鼠标驱动执行流程》中有一行输出信息很让我迷惑,如下所示:

    [ 3597.864715] generic-usb 0003:1D57:0016.0006: input,hidraw2: USB HID v1.10 Mouse [HID Wireless Mouse HID Wireless Mouse] on usb-0000:00:1d.7-1.2/input0


    该行信息中最后 "input,hidraw2: USB HID v1.10 Mouse [HID Wireless Mouse HID Wireless Mouse] on usb-0000:00:1d.7-1.2/input0"这一部分内容

    对应hid_info的第二个参数(位于drivers/hid/hid-core.c):

    hid_info(hdev, "%s: %s HID v%x.%02x %s [%s] on %s
    ", buf, bus, hdev->version >> 8, hdev->version & 0xff, type, hdev->name, hdev->phys)


    但是该行前面的"generic-usb 0003:1D57:0016.0006:"却不知道是从哪儿生成的,因此我想追踪一下hid_info函数的执行过程。

    hid_info定义如下(include/linux/hid.h):

    #define hid_info(hid, fmt, arg...)            
        dev_info(&(hid)->dev, fmt, ##arg)


    而dev_info函数定义如下(include/linux/device.h):

    #define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg)


    _dev_info函数定义如下(drivers/base/core.c):

    #ifdef CONFIG_PRINTK
    
    int __dev_printk(const char *level, const struct device *dev,
             struct va_format *vaf)
    {
        if (!dev)
            return printk("%s(NULL device *): %pV", level, vaf);
    
        return printk("%s%s %s: %pV",
                  level, dev_driver_string(dev), dev_name(dev), vaf);
    }
    EXPORT_SYMBOL(__dev_printk);
    
    int dev_printk(const char *level, const struct device *dev,
               const char *fmt, ...)
    {
        struct va_format vaf;
        va_list args;
        int r;
    
        va_start(args, fmt);
    
        vaf.fmt = fmt;
        vaf.va = &args;
    
        r = __dev_printk(level, dev, &vaf);
        va_end(args);
    
        return r;
    }
    EXPORT_SYMBOL(dev_printk);
    
    #define define_dev_printk_level(func, kern_level)        
    int func(const struct device *dev, const char *fmt, ...)    
    {                                
        struct va_format vaf;                    
        va_list args;                        
        int r;                            
                                    
        va_start(args, fmt);                    
                                    
        vaf.fmt = fmt;                        
        vaf.va = &args;                        
                                    
        r = __dev_printk(kern_level, dev, &vaf);        
        va_end(args);                        
                                    
        return r;                        
    }                                
    EXPORT_SYMBOL(func);
    
    define_dev_printk_level(dev_emerg, KERN_EMERG);
    define_dev_printk_level(dev_alert, KERN_ALERT);
    define_dev_printk_level(dev_crit, KERN_CRIT);
    define_dev_printk_level(dev_err, KERN_ERR);
    define_dev_printk_level(dev_warn, KERN_WARNING);
    define_dev_printk_level(dev_notice, KERN_NOTICE);
    define_dev_printk_level(_dev_info, KERN_INFO);
    
    #endif


    上面这部分函数定义只有在定义CONFIG_PRINTK时才有效,需要查看内核配置文件是否有其定义.

    查看当前内核配置:

    $uname -a
    Linux debian 3.2.0-4-686-pae #1 SMP Debian 3.2.54-2 i686 GNU/Linux


    查看 /boot/config-3.2.0-4-686-pae文件中确实定义了CONFIG_RPINTK:

    CONFIG_PRINTK=y


    上面的函数定义实际上就是将最终调用下面内容:

    printk("%s%s %s: %pV", KERN_INFO, dev_driver_string(dev), dev_name(dev), vaf);


    KERN_INFO定义如下(include/linux/printk.h):

    #define KERN_INFO    "<6>"    /* informational            */



    dev_driver_string定义如下(drivers/base/core.c):

    const char *dev_driver_string(const struct device *dev)
    {
        struct device_driver *drv;
    
        /* dev->driver can change to NULL underneath us because of unbinding,
         * so be careful about accessing it.  dev->bus and dev->class should
         * never change once they are set, so they don't need special care.
         */
        drv = ACCESS_ONCE(dev->driver);
        return drv ? drv->name :
                (dev->bus ? dev->bus->name :
                (dev->class ? dev->class->name : ""));
    }
    EXPORT_SYMBOL(dev_driver_string);


    dev_name函数定义如下(include/linux/device.h):

    static inline const char *dev_name(const struct device *dev)
    {
        /* Use the init name until the kobject becomes available */
        if (dev->init_name)
            return dev->init_name;
    
        return kobject_name(&dev->kobj);
    }



    dev_driver_string获取驱动的字符串,最终使用的驱动是usbhid,其定义如下(drivers/hid/usbhid/hid-core.c):

    static struct hid_driver hid_usb_driver = {
        .name = "generic-usb",
        .id_table = hid_usb_table,
    };

    所以驱动名称就是generic-usb。


    而dev_name中则查看其init_name的值是否为空,如果不为空返回init_name,如果为空,则返回其kobj的名称。
    此处init_name为空(默认值为NULL,在源代码中也没有找到相关的赋值),所以使用的是kobj的名称。
    在函数hid_add_device(drivers/hid/hid-core.c)中有下面的代码:

    dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus,
                 hdev->vendor, hdev->product, atomic_inc_return(&id));


    该行代码将kobj的值设定成总线、vendor、product以及id连接成的字符串。
    从我的前一篇博文中提到vendor和product分别是0x1d57和0x0016,此处使用%04X,那么中间的vendor和product
    应该分别是1D57和0016,这与上面的输出信息是一致的。

    hid_info函数大致执行流程就这样,但是我还是没弄清楚usbhid和hid模块之间是如何有机联系起来的,等到对usb模块
    有了更深入了解后再回头来看二者之间的联系。

  • 相关阅读:
    主机与虚拟机通信:以主机VS2010连接虚拟机MySql为例
    Json与类对象转换
    VS附加到进程调试的方法及应用场景
    地图API使用文档-以腾讯地图为例
    JS使用ActiveXObject读取数据库代码示例(只支持IE)
    css文件内引用外部资源文件的相对路径
    ::after,::before使用
    高德地图API应用
    LogNet4学习笔记
    MvcPager分页控件的使用
  • 原文地址:https://www.cnblogs.com/qiaoqiao2003/p/3792437.html
Copyright © 2020-2023  润新知