程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

linux驱动移植-USB总线设备驱动

我们之前在设备总线模型中介绍过,linux包含I2C、SPI、USB、UART、platform等总线,其中platform总线我们已经学习过了,这一些我们将会介绍USB总线。

一、usb总线

1.1 usb总线定义

在linux 设备模型中,总线由bus_type 结构表示,我们所用的 I2C、SPI、USB 都是用这个结构体来定义的。该结构体定义在 include/linux/device.h文件中:

复制代码
/**
 * struct bus_type - The bus type of the device
 *
 * @name:       The name of the bus.
 * @dev_name:   Used for subsystems to enumerate devices like ("foo%u", dev->id).
 * @dev_root:   Default device to use as the parent.
 * @bus_groups: Default attributes of the bus.
 * @dev_groups: Default attributes of the devices on the bus.
 * @drv_groups: Default attributes of the device drivers on the bus.
 * @match:      Called, perhaps multiple times, whenever a new device or driver
 *              is added for this bus. It should return a positive value if the
 *              given device can be handled by the given driver and zero
 *              otherwise. It may also return error code if determining that
 *              the driver supports the device is not possible. In case of
 *              -EPROBE_DEFER it will queue the device for deferred probing.
 * @uevent:     Called when a device is added, removed, or a few other things
 *              that generate uevents to add the environment variables.
 * @probe:      Called when a new device or driver add to this bus, and callback
 *              the specific driver's probe to initial the matched device.
 * @remove:     Called when a device removed from this bus.
 * @shutdown:   Called at shut-down time to quiesce the device.
 *
 * @online:     Called to put the device back online (after offlining it).
 * @offline:    Called to put the device offline for hot-removal. May fail.
 *
 * @suspend:    Called when a device on this bus wants to go to sleep mode.
 * @resume:     Called to bring a device on this bus out of sleep mode.
 * @num_vf:     Called to find out how many virtual functions a device on this
 *              bus supports.
 * @dma_configure:      Called to setup DMA configuration on a device on
 *                      this bus.
 * @pm:         Power management operations of this bus, callback the specific
 *              device driver's pm-ops.
 * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU
 *              driver implementations to a bus and allow the driver to do
 *              bus-specific setup
 * @p:          The private data of the driver core, only the driver core can
 *              touch this.
 * @lock_key:   Lock class key for use by the lock validator
 * @need_parent_lock:   When probing or removing a device on this bus, the
 *                      device core should lock the device's parent.
 *
 * A bus is a channel between the processor and one or more devices. For the
 * purposes of the device model, all devices are connected via a bus, even if
 * it is an internal, virtual, "platform" bus. Buses can plug into each other.
 * A USB controller is usually a PCI device, for example. The device model
 * represents the actual connections between buses and the devices they control.
 * A bus is represented by the bus_type structure. It contains the name, the
 * default attributes, the bus' methods, PM operations, and the driver core's
 * private data.
 */
struct bus_type {
        const char              *name;
        const char              *dev_name;
        struct device           *dev_root;
        const struct attribute_group **bus_groups;
        const struct attribute_group **dev_groups;
        const struct attribute_group **drv_groups;

        int (*match)(struct device *dev, struct device_driver *drv);
        int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
        int (*probe)(struct device *dev);
        int (*remove)(struct device *dev);
        void (*shutdown)(struct device *dev);

        int (*online)(struct device *dev);
        int (*offline)(struct device *dev);

        int (*suspend)(struct device *dev, pm_message_t state);
        int (*resume)(struct device *dev);

        int (*num_vf)(struct device *dev);

        int (*dma_configure)(struct device *dev);

        const struct dev_pm_ops *pm;

        const struct iommu_ops *iommu_ops;

        struct subsys_private *p;
        struct lock_class_key lock_key;

        bool need_parent_lock;
};
复制代码

其中部分字段的含义如下:

  • name:总线名称;
  • bus_groups:总线属性;
  • dev_groups:该总线上所有设备的默认属性;
  • drv_groups:该总线上所有驱动的默认属性;
  • match:当有新的设备或驱动添加到总线上时match函数被调用,如果设备和驱动可以匹配,返回0;
  • uevent:当一个设备添加、移除或添加环境变量时,函数调用;
  • probe:当有新设备或驱动添加时,probe函数调用,并且回调该驱动的probe函数来初始化相关联的设备;
  • remove:设备移除时调用remove函数;
  • shutdown:设备关机时调用shutdown函数;
  • suspend:设备进入睡眠时调用suspend函数;
  • resume:设备唤醒时调用resume函数;
  • pm:总线的电源管理选项,并回调设备驱动的电源管理模块;

usb总线是 bus_type 类型的全局变量,这个变量已经被 linux 内核赋值好了,其结构体成员对应的函数也已经在内核里面写好,定义在drivers/usb/core/driver.c:

struct bus_type usb_bus_type = {
        .name =         "usb",
        .match =        usb_device_match,
        .uevent =       usb_uevent,
        .need_parent_lock =     true,
};

这里我们重点关注BUS匹配函数usb_device_match即可。

1.2 usb设备和驱动匹配

1.2.1 usb_device_match

usb_bus_type 中的usb_device_match就是用来进行驱动和设备匹配的函数,不同的总线对应的match 函数肯定不一样,这个我们不用管,内核都会写好。

我们所用的BUS总线对应的 match 函数是 usb_device_match函数,该函数定义在drivers/usb/core/driver.c:

复制代码
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
        /* devices and interfaces are handled separately */
        if (is_usb_device(dev)) {

                /* interface drivers never match devices */
                if (!is_usb_device_driver(drv))
                        return 0;

                /* TODO: Add real matching code */
                return 1;

        } else if (is_usb_interface(dev)) {
                struct usb_interface *intf;
                struct usb_driver *usb_drv;
                const struct usb_device_id *id;

                /* device drivers never match interfaces */
                if (is_usb_device_driver(drv))
                        return 0;

                intf = to_usb_interface(dev);
                usb_drv = to_usb_driver(drv);

                id = usb_match_id(intf, usb_drv->id_table);
                if (id)
                        return 1;

                id = usb_match_dynamic_id(intf, usb_drv);
                if (id)
                        return 1;
        }

        return 0;
}
复制代码

在介绍这段之前,我们先来介绍一下usb设备和usb接口的区别:

  • 对于usb设备,使用struct usb_device类型表示,dev成员的type成员会设置成usb_device_type;
  • 对于usb接口,使用struct usb_interface类型表示,dev成员的type成员则会设成usb_if_device_type;

接着我们再来说一下usb设备驱动和usb接口驱动的区别:

  • 对于usb设备驱动,使用struct usb_device_driver类型表示,是通过usb_register_device_driver函数来注册的,这个后面会介绍;
  • 对于usb接口驱动,使用struct usb_driver类型表示,是通过usb_register函数来注册的,这个后面会介绍;

不管是设备驱动usb_device_driver,还是接口驱动usb_driver数据结构中都包含了struct usbdrv_wrap项,其定义如下:

struct usbdrv_wrap {
    struct device_driver driver;
    int for_devices;
}

数据结构中的for_devices用来表示该驱动是设备驱动还是接口驱动,如果为设备驱动,则在用usb_register_device_driver注册时,会将该变量for_devices设置成1,而接口驱动则设为0。usb_device_match中的is_usb_device_driver函数就是通过获取上而结构中的for_devices来进行判断是设备还是接口驱动的。

接着我们再来分析一下上面的代码,该函数有两个参数:设备和设备驱动,该函数可以分为两部分,一部分用于匹配usb设备,另一部分用于匹配usb接口;

  • 对于usb设备,当匹配的是usb设备驱动时,将会匹配成功,实际上所有的usb设备对应的设备驱动都是usb_generic_driver,该设备驱动在usb子系统初始化时注册;
  • 对于usb接口,先用is_usb_device_driver来进行判断,如果不是usb设备驱动则继续判断,否则退出;然后再通过usb_match_id函数来判断接口和接口驱动中的usb_device_id是否匹配。

驱动工程师的工作基本上集中在接口驱动上,所以通常是通过usb_register来注册usb接口驱动的。 

1.2.2 usb_match_id

usb_match_id定义如下:

复制代码
/**
 * usb_match_id - find first usb_device_id matching device or interface
 * @interface: the interface of interest
 * @id: array of usb_device_id structures, terminated by zero entry
 *
 * usb_match_id searches an array of usb_device_id's and returns
 * the first one matching the device or interface, or null.
 * This is used when binding (or rebinding) a driver to an interface.
 * Most USB device drivers will use this indirectly, through the usb core,
 * but some layered driver frameworks use it directly.
 * These device tables are exported with MODULE_DEVICE_TABLE, through
 * modutils, to support the driver loading functionality of USB hotplugging.
 *
 * Return: The first matching usb_device_id, or %NULL.
 *
 * What Matches:
 *
 * The "match_flags" element in a usb_device_id controls which
 * members are used.  If the corresponding bit is set, the
 * value in the device_id must match its corresponding member
 * in the device or interface descriptor, or else the device_id
 * does not match.
 *
 * "driver_info" is normally used only by device drivers,
 * but you can create a wildcard "matches anything" usb_device_id
 * as a driver's "modules.usbmap" entry if you provide an id with
 * only a nonzero "driver_info" field.  If you do this, the USB device
 * driver's probe() routine should use additional intelligence to
 * decide whether to bind to the specified interface.
 *
 * What Makes Good usb_device_id Tables:
 *
 * The match algorithm is very simple, so that intelligence in
 * driver selection must come from smart driver id records.
 * Unless you have good reasons to use another selection policy,
 * provide match elements only in related groups, and order match
 * specifiers from specific to general.  Use the macros provided
 * for that purpose if you can.
 *
 * The most specific match specifiers use device descriptor
 * data.  These are commonly used with product-specific matches;
 * the USB_DEVICE macro lets you provide vendor and product IDs,
 * and you can also match against ranges of product revisions.
 * These are widely used for devices with application or vendor
 * specific bDeviceClass values.
 *
 * Matches based on device class/subclass/protocol specifications
 * are slightly more general; use the USB_DEVICE_INFO macro, or
 * its siblings.  These are used with single-function devices
 * where bDeviceClass doesn't specify that each interface has
 * its own class.
 *
 * Matches based on interface class/subclass/protocol are the
 * most general; they let drivers bind to any interface on a
 * multiple-function device.  Use the USB_INTERFACE_INFO
 * macro, or its siblings, to match class-per-interface style
 * devices (as recorded in bInterfaceClass).
 *
 * Note that an entry created by USB_INTERFACE_INFO won't match
 * any interface if the device class is set to Vendor-Specific.
 * This is deliberate; according to the USB spec the meanings of
 * the interface class/subclass/protocol for these devices are also
 * vendor-specific, and hence matching against a standard product
 * class wouldn't work anyway.  If you really want to use an
 * interface-based match for such a device, create a match record
 * that also specifies the vendor ID.  (Unforunately there isn't a
 * standard macro for creating records like this.)
 *
 * Within those groups, remember that not all combinations are
 * meaningful.  For example, don't give a product version range
 * without vendor and product IDs; or specify a protocol without
 * its associated class and subclass.
 */
const struct usb_device_id *usb_match_id(struct usb_interface *interface,
                                         const struct usb_device_id *id)
{
        /* proc_connectinfo in devio.c may call us with id == NULL. */
        if (id == NULL)
                return NULL;

        /* It is important to check that id->driver_info is nonzero,
           since an entry that is all zeroes except for a nonzero
           id->driver_info is the way to create an entry that
           indicates that the driver want to examine every
           device and interface. */
        for (; id->idVendor || id->idProduct || id->bDeviceClass ||
               id->bInterfaceClass || id->driver_info; id++) {
                if (usb_match_one_id(interface, id))
                        return id;
        }

        return NULL;
}
View Code
复制代码

遍历接口驱动中的usb_device_id列表项,只要usb_device_id结构中的idVendor,idProduct,DeviceClass,binterfaceClass,driver_info项有效就调用usb_match_one_id进行判断,如找到匹配项则函数返回1,否则返回0 。

1.2.3 usb_match_one_id
复制代码
/* returns 0 if no match, 1 if match */
int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
{
        if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
            id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
                return 0;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
            id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
                return 0;

        /* No need to test id->bcdDevice_lo != 0, since 0 is never
           greater than any unsigned number. */
        if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
            (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
                return 0;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
            (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
                return 0;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
            (id->bDeviceClass != dev->descriptor.bDeviceClass))
                return 0;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
            (id->bDeviceSubClass != dev->descriptor.bDeviceSubClass))
                return 0;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
            (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
                return 0;

        return 1;
}
/* returns 0 if no match, 1 if match */
int usb_match_one_id_intf(struct usb_device *dev,
                          struct usb_host_interface *intf,
                          const struct usb_device_id *id)
{
        /* The interface class, subclass, protocol and number should never be
         * checked for a match if the device class is Vendor Specific,
         * unless the match record specifies the Vendor ID. */
        if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&
                        !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
                        (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
                                USB_DEVICE_ID_MATCH_INT_SUBCLASS |
                                USB_DEVICE_ID_MATCH_INT_PROTOCOL |
                                USB_DEVICE_ID_MATCH_INT_NUMBER)))
                return 0;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
            (id->bInterfaceClass != intf->desc.bInterfaceClass))
                return 0;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
            (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
                return 0;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
            (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
                return 0;

        if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&
            (id->bInterfaceNumber != intf->desc.bInterfaceNumber))
                return 0;

        return 1;
}
/* returns 0 if no match, 1 if match */
int usb_match_one_id(struct usb_interface *interface,
                     const struct usb_device_id *id)
{
        struct usb_host_interface *intf;
        struct usb_device *dev;

        /* proc_connectinfo in devio.c may call us with id == NULL. */
        if (id == NULL)
                return 0;

        intf = interface->cur_altsetting;
        dev = interface_to_usbdev(interface);

        if (!usb_match_device(dev, id))
                return 0;

        return usb_match_one_id_intf(dev, intf, id);
}
View Code
复制代码

实际上这里就是将usb设备(usb设备的设备描述符信息、以及接口描述符信息)的信息和usb_driver驱动id_table信息进行匹配,如果匹配成功,则返回1,否则返回0.

1.3 usb总线注册

usb的总线注册是由usb_init函数完成,该函数也是usb子系统的入口函数,代码实现在drivers/usb/core/usb.c文件中,模块入口/出口函数如下:

subsys_initcall(usb_init);
module_exit(usb_exit);

usb_init函数代码:

复制代码
/*
 * Init
 */
static int __init usb_init(void)
{
        int retval;
        if (usb_disabled()) {
                pr_info("%s: USB support disabled\n", usbcore_name);
                return 0;
        }
        usb_init_pool_max();

        usb_debugfs_init();

        usb_acpi_register();
        retval = bus_register(&usb_bus_type);
        if (retval)
                goto bus_register_failed;
        retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
        if (retval)
                goto bus_notifier_failed;
        retval = usb_major_init();
        if (retval)
                goto major_init_failed;
        retval = usb_register(&usbfs_driver);
        if (retval)
                goto driver_register_failed;
        retval = usb_devio_init();
        if (retval)
                goto usb_devio_init_failed;
        retval = usb_hub_init();
        if (retval)
                goto hub_init_failed;
        retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
        if (!retval)
                goto out;

        usb_hub_cleanup();
hub_init_failed:
        usb_devio_cleanup();
usb_devio_init_failed:
        usb_deregister(&usbfs_driver);
driver_register_failed:
        usb_major_cleanup();
major_init_failed:
        bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
bus_notifier_failed:
        bus_unregister(&usb_bus_type);
bus_register_failed:
        usb_acpi_unregister();
        usb_debugfs_cleanup();
out:
        return retval;
}
复制代码

在usb_init函数中主要有如下的三个主要函数:

  • 通过usb_debugfs_init初始化usb debugfs;
  • 通过bus_register注册usb总线usb_bus_type;
  • 通过bus_register_notifier注册usb总线通知链;
  • 通过usb_major_init注册usb控制器字符设备,主设备编号为181,字符设备名称为usb;
  • 通过usb_register注册usbfs驱动;
  • 通过usb_devio_init注册usb设备字符设备,主设备编号为189,字符设备名称为usb_device;
  • 通过usb_hub_init初始化usb hub接口驱动;
  • 通过usb_register_device_driver注册usb设备驱动.

二、usb子系统初始化

由于usb_init函数比较复杂,这里就单独一小节进行介绍。

2.1 usb_debugfs_init()

usb_debugfs_init函数定义在drivers/usb/core/usb.c文件中:

static void usb_debugfs_init(void)
{
        usb_debug_root = debugfs_create_dir("usb", NULL);
        debugfs_create_file("devices", 0444, usb_debug_root, NULL,
                            &usbfs_devices_fops);
}

其主要作用就是在debug文件系统中创建一个usb目录,然后在usb目录下面创建一个devices的文件。

要使用debugfs下面的usb功能,需要先挂着debug文件系统,具体初始化了啥调试接口,看下usbfs_devices_fops操作函数集。

mount -t debugfs none $(debugfs) 

2.2 bus_register(&usb_bus_type)

bus_rigister总线注册函数,传入的参数就是我们上面介绍的usb_bus_type。

struct bus_type usb_bus_type = {
        .name =         "usb",
        .match =        usb_device_match,
        .uevent =       usb_uevent,
        .need_parent_lock =     true,
};

bus_register中创建了两个链表,一个为设备链表,一个为驱动链表:

klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);

usb总线设备链表上挂载的是usb设备或者usb接口,而在usb总线驱动链表上挂在的是usb设备驱动或者是usb接口驱动。

无论是usb设备还是usb接口,其设备基类都是struct device,该类型包含一个成员是bus,类型为struct bus_type,会被初始化为usb_bus_type。

无论是usb设备驱动还是usb接口驱动,其驱动基类都是structdevice_driver,该类型包含一个成员是bus,类型为struct bus_type,会被初始化为usb_bus_type。

usb总线上设备链表上的设备和驱动链表上的驱动的匹配函数是usb_device_match,这个上面已经介绍过了。

 

bus_register函数调用后,就会在用户空间生成usb相关文件,执行如下命令:

root@zhengyang:/work/sambashare/linux-5.2.8# ls /sys/bus/usb
devices  drivers  drivers_autoprobe  drivers_probe  uevent

/sys/bus/usb/devices里用来存放的是usb设备,/sys/bus/usb/drivers里用来存放的是usb驱动:

root@zhengyang:/work/sambashare/linux-5.2.8# ls /sys/bus/usb/devices
1-0:1.0  2-0:1.0  2-1  2-1:1.0  2-2  2-2.1  2-2:1.0  2-2.1:1.0  2-2.1:1.1  usb1  usb2
root@zhengyang:/work/sambashare/linux-5.2.8# ls /sys/bus/usb/drivers
btusb  hub  usb  usbfs  usbhid

2.3 bus_register_notifier(&usb_bus_type,&usb_bus_nb)

bus_register_notifier函数定义在drivers/base/bus.c:

int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
{
        return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
}

第一个参数为总线类型,传入的是&usb_bus_type,第二个参数传入的是&usb_bus_nb。

usb_bus_nb定义在drivers/usb/core/usb.c:

static struct notifier_block usb_bus_nb = {
        .notifier_call = usb_bus_notify,
};

这里注册了usb总线通知链,当向usb_bus_type添加删除设备的时候会调用usb_bus_nb指定的notifier_cal方法,即usb_bus_notify:

复制代码
/*
 * Notifications of device and interface registration
 */
static int usb_bus_notify(struct notifier_block *nb, unsigned long action,
                void *data)
{
        struct device *dev = data;

        switch (action) {
        case BUS_NOTIFY_ADD_DEVICE:       // 添加设备
                if (dev->type == &usb_device_type)   // usb设备
                        (void) usb_create_sysfs_dev_files(to_usb_device(dev));
                else if (dev->type == &usb_if_device_type)   // usb接口
                        usb_create_sysfs_intf_files(to_usb_interface(dev));
                break;

        case BUS_NOTIFY_DEL_DEVICE:          // 删除设备
                if (dev->type == &usb_device_type)   // usb设备
                        usb_remove_sysfs_dev_files(to_usb_device(dev));
                else if (dev->type == &usb_if_device_type)  // usb接口
                        usb_remove_sysfs_intf_files(to_usb_interface(dev));
                break;
        }
        return 0;
}
复制代码

2.4 usb_major_init()

usb_major_init函数定义在drivers/usb/core/file.c:

复制代码
int usb_major_init(void)
{
        int error;

        error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
        if (error)
                printk(KERN_ERR "Unable to get major %d for usb devices\n",
                       USB_MAJOR);

        return error;
}
复制代码

这里使用register_chrdev向内核注册usb主控制器字符设备,字符设备的名称为usb,字符设备的主设备号为USB_MAJOR(180),设备操作为usb_fops。

register_chrdev封装了register_chrdev_region、cdev_alloc、cdev_add相关逻辑,这里不介绍了。

其中usb_fops定义为:

static const struct file_operations usb_fops = {
        .owner =        THIS_MODULE,
        .open =         usb_open,
        .llseek =       noop_llseek,
};

2.5 usb_register(&usbfs_driver)

用usb_register宏向linux内核注册usb接口驱动usbfs_driver,usbfs_driver定义在drivers/usb/core/devio.c:

struct usb_driver usbfs_driver = {
        .name =         "usbfs",
        .probe =        driver_probe,
        .disconnect =   driver_disconnect,
        .suspend =      driver_suspend,
        .resume =       driver_resume,
};

usb_rigister宏定义在include/linux/usb.h文件中:

/* use a define to avoid include chaining to get THIS_MODULE & friends */
#define usb_register(driver) \
        usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
复制代码
/**
 * usb_register_driver - register a USB interface driver
 * @new_driver: USB operations for the interface driver
 * @owner: module owner of this driver.
 * @mod_name: module name string
 *
 * Registers a USB interface driver with the USB core.  The list of
 * unattached interfaces will be rescanned whenever a new driver is
 * added, allowing the new driver to attach to any recognized interfaces.
 *
 * Return: A negative error code on failure and 0 on success.
 *
 * NOTE: if you want your driver to use the USB major number, you must call
 * usb_register_dev() to enable that functionality.  This function no longer
 * takes care of that.
 */
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
                        const char *mod_name)
{
        int retval = 0;

        if (usb_disabled())
                return -ENODEV;

        new_driver->drvwrap.for_devices = 0;
        new_driver->drvwrap.driver.name = new_driver->name;
        new_driver->drvwrap.driver.bus = &usb_bus_type;
        new_driver->drvwrap.driver.probe = usb_probe_interface;
        new_driver->drvwrap.driver.remove = usb_unbind_interface;
        new_driver->drvwrap.driver.owner = owner;
        new_driver->drvwrap.driver.mod_name = mod_name;
        spin_lock_init(&new_driver->dynids.lock);
        INIT_LIST_HEAD(&new_driver->dynids.list);

        retval = driver_register(&new_driver->drvwrap.driver);
        if (retval)
                goto out;

        retval = usb_create_newid_files(new_driver);
        if (retval)
                goto out_newid;

        pr_info("%s: registered new interface driver %s\n",
                        usbcore_name, new_driver->name);

out:
        return retval;

out_newid:
        driver_unregister(&new_driver->drvwrap.driver);

        printk(KERN_ERR "%s: error %d registering interface "
                        "       driver %s\n",
                        usbcore_name, retval, new_driver->name);
        goto out;
}
复制代码

这里首先初始化usb接口驱动里的驱动基类driver,然后调用driver_register进行驱动注册,关于驱动注册函数driver_register具体参考linux驱动移植-总线设备驱动

这里简单介绍一下driver_register函数执行流程:

  • 将usb接口驱动添加到usb总线上(usb_bus_type);
  • 遍历usb总线上的所有usb接口,然后调用usb_device_match进行usb接口驱动和usb接口的匹配;
  • 匹配成功后最终会执行usb接口驱动的probe函数,过程中的驱动基类driver的probe函数和 usb_probe_interface 函数都是达到这个目的的中转函数而已。

2.6 usb_devio_init()

usb_devio_init函数实现在drivers/usb/core/devio.c:

复制代码
int __init usb_devio_init(void)
{
        int retval;

        retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
                                        "usb_device");
        if (retval) {
                printk(KERN_ERR "Unable to register minors for usb_device\n");
                goto out;
        }
        cdev_init(&usb_device_cdev, &usbdev_file_operations);
        retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
        if (retval) {
                printk(KERN_ERR "Unable to get usb_device major %d\n",
                       USB_DEVICE_MAJOR);
                goto error_cdev;
        }
        usb_register_notify(&usbdev_nb);
out:
        return retval;

error_cdev:
        unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
        goto out;
}
复制代码

首先使用register_chrdev_region来静态注册一组字符设备编号,主设备编号为USB_DEVICE_DEV(189),次设备编号从0~USB_DEVICE_MAX,字符设备名称为usb_device。

:#define USB_DEVICE_MAX                      (USB_MAXBUS * 128)  // 一个USB主机控制器,最多接入128个usb设备

然后使用cdev_init初始化字符设备结构体cdev,usbdev_file_operations结构体放入cdev-> ops 里。

最后使用cdev_add将字符设备usb_device_cdev添加到系统,并将USB_DEVICE_DEV(定义为 MKDEV(USB_DEVICE_MAJOR, 0),即起始设备编号)放入cdev-> dev里,  USB_DEVICE_MAX放入cdev->count里。

函数的最后又使用usb_register_notify注册了usb设备通知链。

2.7 usb_register_notify(&usbdev_nb)

usbdev_nb定义在 drivers/usb/core/devio.c:

static struct notifier_block usbdev_nb = {
        .notifier_call =        usbdev_notify,
};

usb_register_notify函数定义在drivers/usb/core/notify.c:

复制代码
/**
 * usb_register_notify - register a notifier callback whenever a usb change happens
 * @nb: pointer to the notifier block for the callback events.
 *
 * These changes are either USB devices or busses being added or removed.
 */
void usb_register_notify(struct notifier_block *nb)
{
        blocking_notifier_chain_register(&usb_notifier_list, nb);
}
复制代码

usb通知链表头为usb_notifier_list:

static BLOCKING_NOTIFIER_HEAD(usb_notifier_list);

在drivers/usb/core/notify.c文件中,有四个函数对usb_notifier_list中发送通知,这四个函数如下:

复制代码
void usb_notify_add_device(struct usb_device *udev)
{
        blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev);
}

void usb_notify_remove_device(struct usb_device *udev)
{
        /* Protect against simultaneous usbfs open */
        mutex_lock(&usbfs_mutex);
        blocking_notifier_call_chain(&usb_notifier_list,
                        USB_DEVICE_REMOVE, udev);
        mutex_unlock(&usbfs_mutex);
}

void usb_notify_add_bus(struct usb_bus *ubus)
{
        blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_ADD, ubus);
}

void usb_notify_remove_bus(struct usb_bus *ubus)
{
        blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_REMOVE, ubus);
}
复制代码

当这些事件发生后会调用usbdev_nb指定的notifier_cal方法,即usbdev_notify:

复制代码
static int usbdev_notify(struct notifier_block *self,
                               unsigned long action, void *dev)
{
        switch (action) {
        case USB_DEVICE_ADD:  // 设备添加
                break;
        case USB_DEVICE_REMOVE:  // 设备删除
                usbdev_remove(dev);
                break;
        }
        return NOTIFY_OK;
}
复制代码

2.8 usb_hub_init()

该函数定义在drivers/usb/core/hub.c:

复制代码
int usb_hub_init(void)
{
        if (usb_register(&hub_driver) < 0) {
                printk(KERN_ERR "%s: can't register hub driver\n",
                        usbcore_name);
                return -1;
        }

        /*
         * The workqueue needs to be freezable to avoid interfering with
         * USB-PERSIST port handover. Otherwise it might see that a full-speed
         * device was gone before the EHCI controller had handed its port
         * over to the companion full-speed controller.
         */
        hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
        if (hub_wq)
                return 0;

        /* Fall through if kernel_thread failed */
        usb_deregister(&hub_driver);
        pr_err("%s: can't allocate workqueue for usb hub\n", usbcore_name);

        return -1;
}
复制代码

该函数主要做了以下工作:

  • 通过usb_register向usb总线注册hub_driver接口驱动,指定了probe,disconnect,suspend,resume,id_table等相关函数。可以猜测,在根hub创建后,会执行此处的hub_probe函数。
  • 创建新的工作队列,工作队列名称为usb_hub_wq,标志位为WQ_FREEZABLE参数WQ_FREEZABLE表示工作线程在挂起时候,需要先完成当前队列的所有工作之后才能挂起。创建好队列后,需要定义一个工作用于完成实际的任务。更多工作队列的知识具体可以参考linux驱动移植-软中断

hub_driver全局变量定义如下:

复制代码
static struct usb_driver hub_driver = {
        .name =         "hub",
        .probe =        hub_probe,
        .disconnect =   hub_disconnect,
        .suspend =      hub_suspend,
        .resume =       hub_resume,
        .reset_resume = hub_reset_resume,
        .pre_reset =    hub_pre_reset,
        .post_reset =   hub_post_reset,
        .unlocked_ioctl = hub_ioctl,
        .id_table =     hub_id_table,
        .supports_autosuspend = 1,
};
复制代码

亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。

日期姓名金额
2023-09-06*源19
2023-09-11*朝科88
2023-09-21*号5
2023-09-16*真60
2023-10-26*通9.9
2023-11-04*慎0.66
2023-11-24*恩0.01
2023-12-30I*B1
2024-01-28*兴20
2024-02-01QYing20
2024-02-11*督6
2024-02-18一*x1
2024-02-20c*l18.88
2024-01-01*I5
2024-04-08*程150
2024-04-18*超20
2024-04-26.*V30
2024-05-08D*W5
2024-05-29*辉20
2024-05-30*雄10
2024-06-08*:10
2024-06-23小狮子666
2024-06-28*s6.66
2024-06-29*炼1
2024-06-30*!1
2024-07-08*方20
2024-07-18A*16.66
2024-07-31*北12
2024-08-13*基1
2024-08-23n*s2
2024-09-02*源50
2024-09-04*J2
2024-09-06*强8.8
2024-09-09*波1
2024-09-10*口1
2024-09-10*波1
2024-09-12*波10
2024-09-18*明1.68
2024-09-26B*h10
2024-09-3010
2024-10-02M*i1
2024-10-14*朋10
2024-10-22*海10
2024-10-23*南10
2024-10-26*节6.66
2024-10-27*o5
2024-10-28W*F6.66
2024-10-29R*n6.66
2024-11-02*球6
2024-11-021*鑫6.66
2024-11-25*沙5
2024-11-29C*n2.88
posted @   大奥特曼打小怪兽  阅读(1295)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤
历史上的今天:
2018-05-12 第二十一节,使用TensorFlow实现LSTM和GRU网络
如果有任何技术小问题,欢迎大家交流沟通,共同进步

公告 & 打赏

>>

欢迎打赏支持我 ^_^

最新公告

程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)。

了解更多

点击右上角即可分享
微信分享提示