如何解决自定义usb驱动被hid驱动抢占的问题

文章详细阐述了USB设备如何触发与驱动的匹配过程,特别是涉及到USB设备初始化、hub初始化以及hid设备的匹配规则。同时,解释了如何屏蔽usbhid驱动的方法,包括模块参数和内核黑名单。文章还讨论了自定义驱动如何在系统中卸载和手动触发hid驱动的probe过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、usb如何触发一次设备匹配

(1)usb_init 初始化

subsys_initcall(usb_init);
static int __init usb_init(void) 
{
    ...
    retval = usb_debugfs_init();
    ...
    retval = bus_register(&usb_bus_type); 
    ...
    // 注册了BUS_NOTIFY_ADD_DEVICE和BUS_NOTIFY_DEL_DEVICE的处理
    retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb); 
    ...
    retval = usb_major_init();
    ...
    // 注册usbfs驱动
    retval = usb_register(&usbfs_driver)
    ...
    // 主要创建/dev/bus/usb节点
    retval = usb_devio_init();
    ...
    // 详见下面的描述
    retval = usb_hub_init();  
    ...
    retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
    ...
}
struct bus_type usb_bus_type = {
    .name = "usb", 
    .match = usb_device_match, // usb设备和驱动匹配的函数
    .uevent = usb_uevent,
}

static int usb_device_match(struct device *dev, struct device_driver *drv)
{
    if (is_usb_device(dev)) {
        if (!is_usb_device_driver(drv))
            return 0;
        return 1;
    } else if (is_usb_interface(dev)) {
        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;
}


const struct usb_device_id *usb_match_id(struct usb_interface *interface, const struct usb_device_id *id)
{
    ...
    for (; id->idVendor || id->idProduct || id->bDeviceClass || 
        id->bInterfaceClass || id->driver_info; id++) { 
        if (usb_match_one_id(interface, id)) 
            return id;
}

int usb_match_one_id(struct usb_interface *interface, const struct usb_device_id *id)
{
    ...
    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);
}


/* 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;

    // usbhid驱动会匹配成功
	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;
}

struct usb_device_driver usb_generic_driver = {
    .name = "usb",
    .probe = generic_probe,
}

static int generic_probe(struct usb_device *udev)
{
    ...
    usb_notify_add_device(udev);
    ...
}
int usb_hub_init(void)                                                          
{
    if (usb_register(&hub_driver) < 0) { 
    ...
    hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0); 
    ...
    
}

static struct usb_driver hub_driver = { 
    .name = "hub",
    .probe = hub_probe, 
    ...
    .id_table = hub_id_table,
    ...
}

static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    ...
    desc = intf->cur_altsetting; 
    ...
    INIT_WORK(&hub->events, hub_event); 
    ...
    if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) 
    ...
}

static void hub_event(struct work_struct *work)
{
    ...
    for (i = 1; i <= hdev->maxchild; i++) { 
        struct usb_port *port_dev = hub->ports[i - 1];
        if (test_bit(i, hub->event_bits) 
            || test_bit(i, hub->change_bits)
            || test_bit(i, hub->wakeup_bits)) {  
            ...
            port_event(hub, i); 
            ...
        }
    }
}


static void port_event(struct usb_hub *hub, int port1)
{
    ...
    ...
    ...
    if (connect_change)
        hub_port_connect_change(hub, port1, portstatus, portchange); 
}

static void hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange)
{
    ...
    hub_port_connect(hub, port1, portstatus, portchange);
    ...
}

static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange)
{
   ...
    /* Disconnect any existing devices under this port */
    if (udev) { 
        if (hcd->usb_phy && !hdev->parent)
            usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
        usb_disconnect(&port_dev->child);
    }
    
    ...
    ...
    for (i = 0; i < SET_CONFIG_TRIES; i++) {
        ...
        udev = usb_alloc_dev(hdev, hdev->bus, port1);
        ...
        // 在该函数中进行枚举
        status = hub_port_init(hub, udev, port1, i);  
        ...
    }
    
    status = usb_new_device(udev);
    
    if (hcd->usb_phy && !hdev->parent) 
        usb_phy_notify_connect(hcd->usb_phy, udev->speed); 
   ... 
}


int usb_new_device(struct usb_device *udev) 
{
    ...
    err = usb_enumerate_device(udev);>--/* Read descriptors */ 
    ...
    /* Tell the world! */ 
    announce_device(udev); 
    ...
    err = device_add(&udev->dev);
    ...
}

static int hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, int retry_counter) 
{
    ...
    retval = hub_port_reset(hub, port1, udev, delay, false);
    ...
    ...
    for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
        ...
        retval = hub_enable_device(udev); 
        
        buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
        for (operations = 0; operations < 3; ++operations) {
            r = usb_control_msg(udev, usb_rcvaddr0pipe(),
                    USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                    USB_DT_DEVICE << 8, 0,
                    buf, GET_DESCRIPTOR_BUFSIZE,
                    initial_descriptor_timeout);
        }
        free(buf);
        
        retval = hub_port_reset(hub, port1, udev, delay, false);
    }
    
    if (udev->wusb == 0) {
        for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
            retval = hub_set_address(udev, devnum);
            ...   
        }
    }
    
    retval = usb_get_device_descriptor(udev, 8);
    ...
    retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
    ...
    usb_detect_quirks(udev);
    
    ...
    if (hcd->driver->update_device)
        hcd->driver->update_device(hcd, udev);
    
}



static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) 
{
    ...
    hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
    ...
    INIT_WORK(&hub->tt.clear_work, hub_tt_work);
    ...
    pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
    maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
    hub->urb = usb_alloc_urb(0, GFP_KERNEL);
    usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval);
    ...
}

(2) xhci_plat_init初始化

drivers/usb/host/xhci-plat.c
static struct platform_driver usb_xhci_driver = {
	.probe	= xhci_plat_probe,
	.remove	= xhci_plat_remove,
	.shutdown	= usb_hcd_platform_shutdown,
	.driver	= {
		.name = "xhci-hcd",
		.pm = &xhci_plat_pm_ops,
		.of_match_table = of_match_ptr(usb_xhci_of_match),
		.acpi_match_table = ACPI_PTR(usb_xhci_acpi_match),
	},
};
MODULE_ALIAS("platform:xhci-hcd");

static int __init xhci_plat_init(void)
{
	xhci_init_driver(&xhci_plat_hc_driver, &xhci_plat_overrides);
	return platform_driver_register(&usb_xhci_driver);
}
module_init(xhci_plat_init);
static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
	.extra_priv_size = sizeof(struct xhci_plat_priv),
	.reset = xhci_plat_setup,
	.start = xhci_plat_start,
};
static const struct hc_driver xhci_hc_driver = {
    .irq =			xhci_irq,
    .start =		xhci_run,
    .stop =			xhci_stop,
	.shutdown =		xhci_shutdown,
	
	.urb_enqueue =		xhci_urb_enqueue,
	.urb_dequeue =		xhci_urb_dequeue,
	.alloc_dev =		xhci_alloc_dev,
	
	...
	.update_device =        xhci_update_device,
	...
}
void xhci_init_driver(struct hc_driver *drv,
		      const struct xhci_driver_overrides *over)
{
	BUG_ON(!over);

	/* Copy the generic table to drv then apply the overrides */
	*drv = xhci_hc_driver;

	if (over) {
		drv->hcd_priv_size += over->extra_priv_size;
		if (over->reset)
			drv->reset = over->reset;
		if (over->start)
			drv->start = over->start;
	}
}
EXPORT_SYMBOL_GPL(xhci_init_driver);

从xhci_init_driver可以看出start和reset会被覆盖掉,hc_driver主要是由xhci_hc_driver结构体赋值
xhci_init_driver函数会把xhci_plat_overrides的ops赋值给xhci_plat_hc_driver

由于xhci_plat_init调用的是platform_driver_register,对应的bus是platform_bus_type,该bus对应的match函数为:

static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}
static int xhci_plat_probe(struct platform_device *pdev)
{
   ...
   driver = &xhci_plat_hc_driver;
   ...
   irq = platform_get_irq(pdev, 0); 
   ...
   ...
   	hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
			       dev_name(&pdev->dev), NULL);
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	clk = devm_clk_get(&pdev->dev, NULL);
		ret = clk_prepare_enable(clk);
	...
	hcd->rsrc_start = res->start;
	...
	xhci = hcd_to_xhci(hcd);
	...
	xhci->clk = clk;
	xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
			dev_name(&pdev->dev), hcd);
	...		
	hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
	ret = usb_phy_init(hcd->usb_phy);	
	...
	ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
	...
	ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
	...
}
struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
		struct device *sysdev, struct device *dev, const char *bus_name,
		struct usb_hcd *primary_hcd)
{
    ...
    hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
    ...
    ...
    timer_setup(&hcd->rh_timer, rh_timer_func, 0);                              
    
}

static void rh_timer_func (struct timer_list *t)
{
    struct usb_hcd *_hcd = from_timer(_hcd, t, rh_timer)
    usb_hcd_poll_rh_status(_hcd);
}


void usb_hcd_poll_rh_status(struct usb_hcd *hcd) 
{
    ...
    length = hcd->driver->hub_status_data(hcd, buffer);
    urb = hcd->status_urb;
    if (urb) {  
        urb->actual_length = length;
        memcpy(urb->transfer_buffer, buffer, length);
        
        usb_hcd_unlink_urb_from_ep(hcd, urb); 
        usb_hcd_giveback_urb(hcd, urb, 0);
    }
}
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags)
{
    ...
    retval = usb_register_bus(&hcd->self);
    ...
    
    if (hcd->driver->reset) {  
        retval = hcd->driver->reset(hcd);
        ...
    }
    
    ...
    
    init_giveback_urb_bh(&hcd->high_prio_bh);
    init_giveback_urb_bh(&hcd->low_prio_bh);
    
    ...
    if (usb_hcd_is_primary_hcd(hcd) && irqnum) { 
        retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
    ...
    }
    
    ...
    retval = hcd->driver->start(hcd);
    ...
    retval = register_root_hub(hcd);
    ...
    if (hcd->uses_new_polling && HCD_POLL_RH(hcd))  {
        usb_hcd_poll_rh_status(hcd);
    }
    ...
}


static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
{
    tasklet_init(&bh->bh, usb_giveback_urb_bh, (unsigned long)bh);
}

static void usb_giveback_urb_bh(unsigned long param)
{
    list_replace_init(&bh->head, &local_list);
    while (!list_empty(&local_list)) { 
        urb = list_entry(local_list.next, struct urb, urb_list);
        list_del_init(&urb->urb_list); 
        bh->completing_ep = urb->ep; 
        __usb_hcd_giveback_urb(urb);
        ...
    }
    ...
}

static void __usb_hcd_giveback_urb(struct urb *urb)
{
    ...
    usb_unanchor_urb(urb);
    ...
    
    local_irq_save(flags); 
    urb->complete(urb);
    local_irq_restore(flags);
    ...
    usb_put_urb(urb);
    
}


static int usb_hcd_request_irqs(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags)
{
    if (hcd->driver->irq) { 
        retval = request_irq(irqnum, &usb_hcd_irq, irqflags, hcd->irq_descr, hcd);
        ...
         hcd->irq = irqnum; 
    }
    ...
}

irqreturn_t usb_hcd_irq (int irq, void *__hcd) 
{
    struct usb_hcd  *hcd = __hcd;
    ...
    if (hcd->driver->irq(hcd) == IRQ_NONE)
    ...
}
irqreturn_t xhci_irq(struct usb_hcd *hcd)
{
	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
	union xhci_trb *event_ring_deq;

	status = readl(&xhci->op_regs->status);
	if (status == ~(u32)0) {
		xhci_hc_died(xhci);
		ret = IRQ_HANDLED;
		goto out;
	}

	if (!(status & STS_EINT))
		goto out;

	if (status & STS_FATAL) {
		xhci_warn(xhci, "WARNING: Host System Error\n");
		xhci_halt(xhci);
		ret = IRQ_HANDLED;
		goto out;
	}

	/*
	 * Clear the op reg interrupt status first,
	 * so we can receive interrupts from other MSI-X interrupters.
	 * Write 1 to clear the interrupt status.
	 */
	status |= STS_EINT;
	writel(status, &xhci->op_regs->status);

	if (!hcd->msi_enabled) {
		u32 irq_pending;
		irq_pending = readl(&xhci->ir_set->irq_pending);
		irq_pending |= IMAN_IP;
		writel(irq_pending, &xhci->ir_set->irq_pending);
	}

	if (xhci->xhc_state & XHCI_STATE_DYING ||
	    xhci->xhc_state & XHCI_STATE_HALTED) {
		xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
				"Shouldn't IRQs be disabled?\n");
		/* Clear the event handler busy flag (RW1C);
		 * the event ring should be empty.
		 */
		temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
		xhci_write_64(xhci, temp_64 | ERST_EHB,
				&xhci->ir_set->erst_dequeue);
		ret = IRQ_HANDLED;
		goto out;
	}

	event_ring_deq = xhci->event_ring->dequeue;
	/* FIXME this should be a delayed service routine
	 * that clears the EHB.
	 */
	while (xhci_handle_event(xhci) > 0) {}

	temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
	/* If necessary, update the HW's version of the event ring deq ptr. */
	if (event_ring_deq != xhci->event_ring->dequeue) {
		deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
				xhci->event_ring->dequeue);
		if (deq == 0)
			xhci_warn(xhci, "WARN something wrong with SW event "
					"ring dequeue ptr.\n");
		/* Update HC event ring dequeue pointer */
		temp_64 &= ERST_PTR_MASK;
		temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK);
	}

	/* Clear the event handler busy flag (RW1C); event ring is empty. */
	temp_64 |= ERST_EHB;
	xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue);
	ret = IRQ_HANDLED;

out:
	spin_unlock_irqrestore(&xhci->lock, flags);

	return ret;
}



static int xhci_handle_event(struct xhci_hcd *xhci)
{
    union xhci_trb *event;
	int update_ptrs = 1;
	int ret;

	event = xhci->event_ring->dequeue;

	trace_xhci_handle_event(xhci->event_ring, &event->generic);

	switch (le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) {
	case TRB_TYPE(TRB_COMPLETION):
		handle_cmd_completion(xhci, &event->event_cmd);
		break;
	case TRB_TYPE(TRB_PORT_STATUS):
		handle_port_status(xhci, event);
		update_ptrs = 0;
		break;
	case TRB_TYPE(TRB_TRANSFER):
		ret = handle_tx_event(xhci, &event->trans_event);
		if (ret >= 0)
			update_ptrs = 0;
		break;
	case TRB_TYPE(TRB_DEV_NOTE):
		handle_device_notification(xhci, event);
		break;
	default:
		if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >=
		    TRB_TYPE(48))
			handle_vendor_event(xhci, event);
		else
			xhci_warn(xhci, "ERROR unknown event type %d\n",
				  TRB_FIELD_TO_TYPE(
				  le32_to_cpu(event->event_cmd.flags)));
	}

	if (xhci->xhc_state & XHCI_STATE_DYING) {
		xhci_dbg(xhci, "xHCI host dying, returning from "
				"event handler.\n");
		return 0;
	}

	if (update_ptrs)
		/* Update SW event ring dequeue pointer */
		inc_deq(xhci, xhci->event_ring);

	return 1;
}


static void handle_device_notification(struct xhci_hcd *xhci,
		union xhci_trb *event)
{
	u32 slot_id;
	struct usb_device *udev;

	slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->generic.field[3]));
	if (!xhci->devs[slot_id]) {
		xhci_warn(xhci, "Device Notification event for "
				"unused slot %u\n", slot_id);
		return;
	}

	xhci_dbg(xhci, "Device Wake Notification event for slot ID %u\n",
			slot_id);
	udev = xhci->devs[slot_id]->udev;
	if (udev && udev->parent)
		usb_wakeup_notification(udev->parent, udev->portnum);
}


void usb_wakeup_notification(struct usb_device *hdev, unsigned int portnum)
{
    ...
    hub = usb_hub_to_struct_hub(hdev);
    if (hub) { 
        set_bit(portnum, hub->wakeup_bits);
        kick_hub_wq(hub);
    }
}

static void kick_hub_wq(struct usb_hub *hub) 
{
    ...
    // 详见上面的hub_event函数
    if (queue_work(hub_wq, &hub->events))
    ...
}


(3)device_add

在usb_new_device函数中会调用device_add函数

int device_add(struct device *dev) 
{
    ...
    error = bus_add_device(dev);
    if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); 
    ...
    bus_probe_device(dev);
}

int bus_add_device(struct device *dev)
{
    struct bus_type *bus = bus_get(dev->bus);
    ...
    klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
    ...
}

void bus_probe_device(struct device *dev)
{
    ...
    if (bus->p->drivers_autoprobe)
        device_initial_probe(dev);
}

void device_initial_probe(struct device *dev)
{
    __device_attach(dev, true);
}

static int __device_attach(struct device *dev, bool allow_async)
{
    if (dev->driver) { 
        ...
        ret = device_bind_driver(dev); 
        ...    
    } else {
        ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
        if (!ret && allow_async && data.have_async) {
            ...
            async_schedule(__device_attach_async_helper, dev);
        }
    }
    ...
}

static int __device_attach_driver(struct device_driver *drv, void *_data)
{
    ret = driver_match_device(drv, dev);
    ...
    return driver_probe_device(drv, dev);
}

static inline int driver_match_device(struct device_driver *drv,struct device *dev)
{
    // 对应的match 是usb_device_match
    return drv->bus->match ? drv->bus->match(dev, drv) : 1; 
}

二、hid设备如何匹配

通过上面的描述可以看出,usbhid可以通过usb_match_one_id_intf进行匹配,所以导致usbhid可以检测到我们的设备
执行的流程如下:

usb_hcd_request_irqs
	usb_hcd_irq
		hcd->driver->irq(hcd) // xhci_irq
		|
		xhci_irq
			xhci_handle_event
				handle_device_notification
					usb_wakeup_notification
						kick_hub_wq
							queue_work(hub_wq, &hub->events) // 触发hub_event
							|
							hub_event 
								port_event
									hub_port_connect_change
										hub_port_connect
											usb_new_device
											    device_add
											        bus_probe_device
											            device_initial_probe
											                __device_attach
											                    __device_attach_driver
											                        driver_match_device
											                            usb_device_match
											                                usb_match_id
											                                	usb_match_one_id
													                                usb_match_one_id_intf
usb_match_one_id_intf
    if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
	    (id->bInterfaceClass != intf->desc.bInterfaceClass))
		return 0;

usb_match_one_id_intf这个匹配只需要判断bInterfaceClass是否正确就行,即该设备枚举出来是否是usbhid即可

static const struct usb_device_id hid_usb_ids[] = {
    { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
        .bInterfaceClass = USB_INTERFACE_CLASS_HID },
    { }
}
static struct usb_driver hid_driver = {
    .name = "usbhid",
    ...
    .id_table = hid_usb_ids,
    ...
}

三、如何屏蔽掉usbhid驱动

1、通过上面的分析,如果usbhid驱动编译进了内核,这样usbhid驱动的注册始终在自定义ko的前面,那么在调用
bus_for_each_drv接口的时候,优先匹配usbhid驱动
2、如果usbhid是ko模块,比如x86环境下,实际上只需要卸载usbhid驱动,然后加载自己的驱动,最后再加载usbhid驱动,这样使得自定义驱动在usbhid前面,那么在match驱动的时候,便可以优先加载自定义驱动
3、usbhid驱动支持dquirks,即给特定的pid和vid设备添加属性,可以忽略掉对应的设备

/* Quirks specified at module load time */
static char *quirks_param[MAX_USBHID_BOOT_QUIRKS];
module_param_array_named(quirks, quirks_param, charp, NULL, 0444);
MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying "
		" quirks=vendorID:productID:quirks"
		" where vendorID, productID, and quirks are all in"
		" 0x-prefixed hex");
		
static int __init hid_init(void)
{
    ...
    retval = usbhid_quirks_init(quirks_param);
    ...
}

/**
 * usbhid_quirks_init: apply USB HID quirks specified at module load time
 */
int usbhid_quirks_init(char **quirks_param)
{
	u16 idVendor, idProduct;
	u32 quirks;
	int n = 0, m;

	for (; n < MAX_USBHID_BOOT_QUIRKS && quirks_param[n]; n++) {

		m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
				&idVendor, &idProduct, &quirks);

		if (m != 3 ||
		    usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
			pr_warn("Could not parse HID quirk module param %s\n",
				quirks_param[n]);
		}
	}

	return 0;
}

从上面的流程可以看出,如果usbhid.ko是一个模块的话,那么在insmod的时候,可以传递参数进去,忽略掉特定的模块

#define HID_QUIRK_IGNORE    0x00000004
modprobe usbhid quirks=0xxxx:0xyyy:0x4   //0x4代表HID_QUIRK_IGNORE

如果对应的host的usbhid编译为一个模块,可以使用该方法,然后在开机比如/etc/rc.local中添加进去,这样可以实现开机自动重新insmod

4、usbhid驱动也支持squirks,即把的pid和vid和对应的属性(比如HID_QUIRK_IGNORE)静态的添加到黑名单中去

static const struct hid_blacklist {
	__u16 idVendor;
	__u16 idProduct;
	__u32 quirks;
} hid_blacklist[] = {
	{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
	...
	...
	{your_vid, your_pid, HID_QUIRK_IGNORE)}
	
	...
    { 0, 0 }
};

如果对应的host可以允许修改代码,可以使用该方法

5、如果有的设备不允许修改内核代码,且把usbhid编译进了内核,比如一些arm64的服务器。此时需要在自定义的驱动中通过代码去协助usbhid驱动,然后再加载自己的驱动的probe函数。具体方法详见下面的四和五章节

四、自定义驱动中如何卸载hid驱动

1、在libusb中,我们知道可以通过libusb_kernel_driver_active判断设备是否有驱动加载,然后通过libusb_detach_kernel_driver卸载掉对应的驱动
2、通过分析对应api,可以找到在内核中可以调用:

void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface)

但是调用该接口依赖struct usb_drive和struct usb_interface
其中struct usb_interface可以通过:

struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,unsigned ifnum)

其中ifnum是有自己的usb device确定的,目前默认是0,所以是固定的,也就是只需要获取到对应usb设备的struct usb_device,变可以获取到对应的struct usb_interface

而struct usb_driver可以通过bus_for_each_drv获取到:

int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
        void *data, int (*fn)(struct device_driver *, void *))

但是struct bus_type usb_bus_type我们无法获取到(因为没有export出来),但是可以通过struct usb_device *dev->dev.bus获取到

struct usb_device我们可以通过usb_for_each_dev函数获取到:

int usb_for_each_dev(void *data, int (*fn)(struct usb_device *, void *))

该函数会遍历整个usb_bus_type中的usb device,只需要在对应的fun中判断对应的idVendor和idProduct是否为自动usb设备,便key找到usb设备对应的struct usb_device
最后调用 usb_driver_release_interface获取到struct usb_interface
然后再调用bus_for_each_drv去遍历整个usb_bus_type中的usbdrvier,通过驱动名,可以找到usbhid和自定义的usb驱动的struct device_driver,再通过to_usb_driver宏找到struct usb_driver
最终调用usb_driver_release_interface接口便可以释放掉usbhid的驱动

伪代码如下:

int __print_usb_driver(struct device_driver *drv, void *_data)
{
	struct usb_device *dev = (struct usb_device *)_data;
	struct usb_driver *driver = to_usb_driver(drv);
	struct usb_interface    *intf = NULL;
	struct hid_device *hid;
	struct usbhid_device *usbhid; 

	if((container_of(drv, struct usbdrv_wrap, driver)->for_devices == 0) && (driver!=NULL) && (driver->name!=NULL) && (!memcmp(driver->name,"usbhid",6))) {
		intf = usb_ifnum_to_if(dev, 0);
		usb_driver_release_interface(driver, intf);
	}
	
	if((container_of(drv, struct usbdrv_wrap, driver)->for_devices == 0) && (driver!=NULL) && (driver->name!=NULL) && (!memcmp(driver->name,"your_driver_name",10))) {
		intf = usb_ifnum_to_if(dev, 0);
		usb_driver_claim_interface(driver, intf, NULL);
		driver->probe(intf, driver->id_table);
	}
	return 0;
}

int __print_usb(struct usb_device *dev, void *data)
{
	#if 1
	if(dev->descriptor.idVendor == 0xxxx && dev->descriptor.idProduct == 0xyyy)
		bus_for_each_drv(dev->dev.bus, NULL, dev, __print_usb_driver);
	#else
	if(dev->descriptor.idVendor == 0xxxx && dev->descriptor.idProduct == 0xyyy) {
		struct usb_interface    *intf = NULL;
		struct usb_driver *driver = NULL;
	
		intf = usb_ifnum_to_if(dev, 0);
		driver = to_usb_driver(intf->dev.driver);
		usb_driver_release_interface(driver, intf);
	}
	#endif 
}

usb_for_each_dev(NULL, __print_usb);

五、自定义驱动中如何手动触发驱动probe

1、通过上面第四章的分析由于通过找到自定义驱动的struct usb_driver然后手动触发probe函数实现
但是有一个新的问题,由于usb是热插拔设备,当出现热插拔的时候,需要有地触发一次usb_for_each_dev(NULL, __print_usb);否则当设备再次插入时,会自动被usbhid驱动匹配成功,而由于自定义的ko驱动无法再次执行usb_for_each_dev(NULL, __print_usb);

2、解决方法是注册一个usb通知,检测热插拔,如果出现设备插入,便主动调用usb_for_each_dev(NULL, __print_usb);
以下为伪代码:

int multi_port_ncb(struct notifier_block *nb, unsigned long val, void *priv)
{
	switch (val) {
		case USB_DEVICE_ADD:
			usb_for_each_dev(NULL, __print_usb);
			break;
		case USB_DEVICE_REMOVE:
			break;
		case USB_BUS_ADD:
			break;
		case USB_BUS_REMOVE:
			break;
		default:
			return NOTIFY_BAD;
	}
	
	return NOTIFY_OK;
}

static struct notifier_block multi_port_notifier = {
	.notifier_call = multi_port_ncb,
	.priority = INT_MAX/* Need to be called first of all */
};

usb_register_notify(&multi_port_notifier);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值