一、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);