Linux驱动开发之USB设备检测流程

一、引言

最近工作中,因为工作需要,特地学习了一下USB设备的检测流程,做个笔记。

二、大致流程

从中断响应到probe对应的usb设备驱动:

hub_irq -> kick_hub_wq -> queue_work -> hub_event -> port_event -> hub_port_connect_change -> hub_port_connect -> usb_alloc_dev -> choose_devnum -> hub_port_init -> hub_set_address -> usb_get_device_descriptor -> usb_new_device -> usb_enumerate_autosuspend -> announce_device -> device_add ->bus_probe_device。

三、源码概览

1、hub_irq

源码路径:kernel/drviers/usb/core/hub.c。

/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb)
{
	struct usb_hub *hub = urb->context;
	int status = urb->status;
	unsigned i;
	unsigned long bits;

	switch (status) {
	case -ENOENT:		/* synchronous unlink */
	case -ECONNRESET:	/* async unlink */
	case -ESHUTDOWN:	/* hardware going away */
		return;

	default:		/* presumably an error */
		/* Cause a hub reset after 10 consecutive errors */
		dev_dbg(hub->intfdev, "transfer --> %d\n", status);
		if ((++hub->nerrors < 10) || hub->error)
			goto resubmit;
		hub->error = status;
		/* FALL THROUGH */

	/* let hub_wq handle things */
	case 0:			/* we got data:  port status changed */
		bits = 0;
		for (i = 0; i < urb->actual_length; ++i)
			bits |= ((unsigned long) ((*hub->buffer)[i]))
					<< (i*8);
		hub->event_bits[0] = bits;
		break;
	}

	hub->nerrors = 0;

	/* Something happened, let hub_wq figure it out */
	kick_hub_wq(hub);

resubmit:
	if (hub->quiescing)
		return;

	status = usb_submit_urb(hub->urb, GFP_ATOMIC);
	if (status != 0 && status != -ENODEV && status != -EPERM)
		dev_err(hub->intfdev, "resubmit --> %d\n", status);
}

2、kick_hub_wq

源码路径:kernel/drviers/usb/core/hub.c。

static void kick_hub_wq(struct usb_hub *hub)
{
	struct usb_interface *intf;

	if (hub->disconnected || work_pending(&hub->events))
		return;

	/*
	 * Suppress autosuspend until the event is proceed.
	 *
	 * Be careful and make sure that the symmetric operation is
	 * always called. We are here only when there is no pending
	 * work for this hub. Therefore put the interface either when
	 * the new work is called or when it is canceled.
	 */
	intf = to_usb_interface(hub->intfdev);
	usb_autopm_get_interface_no_resume(intf);
	kref_get(&hub->kref);

	if (queue_work(hub_wq, &hub->events))
		return;

	/* the work has already been scheduled */
	usb_autopm_put_interface_async(intf);
	kref_put(&hub->kref, hub_release);
}

3、hub_event

源码路径:kernel/drviers/usb/core/hub.c。

static void hub_event(struct work_struct *work)
{
    ……
	/* deal with port status changes */
	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)) {
			/*
			 * The get_noresume and barrier ensure that if
			 * the port was in the process of resuming, we
			 * flush that work and keep the port active for
			 * the duration of the port_event().  However,
			 * if the port is runtime pm suspended
			 * (powered-off), we leave it in that state, run
			 * an abbreviated port_event(), and move on.
			 */
			pm_runtime_get_noresume(&port_dev->dev);
			pm_runtime_barrier(&port_dev->dev);
			usb_lock_port(port_dev);
			port_event(hub, i);
			usb_unlock_port(port_dev);
			pm_runtime_put_sync(&port_dev->dev);
		}
	}
    ……
}

4、port_event

源码路径:kernel/drviers/usb/core/hub.c。

static void port_event(struct usb_hub *hub, int port1)__must_hold(&port_dev->status_lock)
{
    ……
#ifdef CONFIG_ATC_AOSP_ENHANCEMENT
	dev_info(&port_dev->dev, "[USB] connect_change = %d\n", connect_change);
#endif

	if (connect_change)
		hub_port_connect_change(hub, port1, portstatus, portchange);
}

5、hub_port_connect_change

源码路径:kernel/drviers/usb/core/hub.c。

static void hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange)__must_hold(&port_dev->status_lock)
{
    ……
	usb_unlock_port(port_dev);
	hub_port_connect(hub, port1, portstatus, portchange);
	usb_lock_port(port_dev);
}

6、hub_port_connect

源码路径:kernel/drviers/usb/core/hub.c。

static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange)
{
    ……
    for (i = 0; i < SET_CONFIG_TRIES; i++) {
		/* reallocate for each attempt, since references
		 * to the previous one can escape in various ways
		 */
		udev = usb_alloc_dev(hdev, hdev->bus, port1);
		if (!udev) {
			dev_err(&port_dev->dev,
					"couldn't allocate usb_device\n");
			goto done;
		}
        ……
        /* reset (non-USB 3.0 devices) and get descriptor */
		usb_lock_port(port_dev);
		status = hub_port_init(hub, udev, port1, i);
		usb_unlock_port(port_dev);
        ……
        /* Run it through the hoops (find a driver, etc) */
		if (!status) {
			    status = usb_new_device(udev);
			}
		}
        ……
    }
}

7、usb_alloc_dev

源码路径:kernel/drviers/usb/core/usb.c。

8、hub_port_init

源码路径:kernel/drviers/usb/core/hub.c。

static int hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, int retry_counter)
{
    ……
	if (udev->speed < USB_SPEED_SUPER)
		dev_info(&udev->dev,
				"%s %s USB device number %d using %s\n",
				(udev->config) ? "reset" : "new", speed,
				devnum, udev->bus->controller->driver->name);
    ……
    if (udev->wusb == 0) {
			for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
				retval = hub_set_address(udev, devnum);
				if (retval >= 0)
					break;
				msleep(200);
			}
    ……
    }
    retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
}

9、usb_new_device

源码路径:kernel/drviers/usb/core/hub.c。

int usb_new_device(struct usb_device *udev)
{
    ……
    err = usb_enumerate_device(udev);	/* Read descriptors */
    ……
    /* Tell the world! */
	announce_device(udev);
    ……
    /* Register the device.  The device driver is responsible
	 * for configuring the device and invoking the add-device
	 * notifier chain (used by usbfs and possibly others).
	 */
	err = device_add(&udev->dev);
    ……
}

10、announce_device

源码路径:kernel/drviers/usb/core/hub.c。

static void announce_device(struct usb_device *udev)
{
	dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
		le16_to_cpu(udev->descriptor.idVendor),
		le16_to_cpu(udev->descriptor.idProduct));
	dev_info(&udev->dev,
		"New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
		udev->descriptor.iManufacturer,
		udev->descriptor.iProduct,
		udev->descriptor.iSerialNumber);
	show_string(udev, "Product", udev->product);
	show_string(udev, "Manufacturer", udev->manufacturer);
	show_string(udev, "SerialNumber", udev->serial);
}

 

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值