当前位置: 首页 > article >正文

USB Hub 检测设备

在这里插入图片描述

系列文章目录


xHCI 简单分析
USB Root Hub 分析
USB Hub 检测设备


文章目录

  • 系列文章目录
  • 一、引言
  • 二、hub_events
    • hub_port_connect_change
    • usb_alloc_dev
    • usb_set_device_state
    • hub_port_init
    • usb_new_device


一、引言

    USB Hub 检测设备 一文中讲到,当有 USB 插入时,它会激活 hub_events 函数。

static int hub_thread(void *__unused)
{
	do {
		hub_events();
		wait_event_interruptible(khubd_wait,
				!list_empty(&hub_event_list) ||
				kthread_should_stop());
		try_to_freeze();
	} while (!kthread_should_stop() || !list_empty(&hub_event_list));

	pr_debug("%s: khubd exiting\n", usbcore_name);
	return 0;
}

二、hub_events

在这里插入图片描述

static void hub_events(void)
{
	// ...
	while (1) {
		// ...
		tmp = hub_event_list.next;
		list_del_init(tmp);

		hub = list_entry(tmp, struct usb_hub, event_list);
		// 描述 usb 设备(Hub,整体,不是接口)
		hdev = hub->hdev;
		intf = to_usb_interface(hub->intfdev);
		// hub 接口设备
		hub_dev = &intf->dev;
		// ...
		/* Lock the device, then check to see if we were
		 * disconnected while waiting for the lock to succeed. */
		if (locktree(hdev) < 0) {
		// ...
		/* Autoresume */
		ret = usb_autopm_get_interface(intf);
		// ...
		/* deal with port status changes */
		for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
			// ...
			ret = hub_port_status(hub, i,
					&portstatus, &portchange);
			// ...
			if (connect_change)
				hub_port_connect_change(hub, i,
						portstatus, portchange);
		} /* end for i */
		// ...
		if (!hdev->parent && !hub->busy_bits[0])
			usb_enable_root_hub_irq(hdev->bus);

loop_autopm:
		/* Allow autosuspend if we're not going to run again */
		if (list_empty(&hub->event_list))
			usb_autopm_enable(intf);
loop:
		usb_unlock_device(hdev);
		usb_put_intf(intf);

        } /* end while (1) */
}

    在这个循环中,检测 Hub 的每个端口是否有变化,有变化则调用 hub_port_connect_change 进行处理。

hub_port_connect_change

static void hub_port_connect_change(struct usb_hub *hub, int port1,
					u16 portstatus, u16 portchange)
{
	struct usb_device *hdev = hub->hdev;
	struct device *hub_dev = hub->intfdev;
	// ...
	/* Disconnect any existing devices under this port */
	if (hdev->children[port1-1])
		usb_disconnect(&hdev->children[port1-1]);
	clear_bit(port1, hub->change_bits);
	// ...
	for (i = 0; i < SET_CONFIG_TRIES; i++) {
		struct usb_device *udev;
		// ...
		udev = usb_alloc_dev(hdev, hdev->bus, port1);
		// ...
		usb_set_device_state(udev, USB_STATE_POWERED);
		udev->speed = USB_SPEED_UNKNOWN;
 		udev->bus_mA = hub->mA_per_port;
		udev->level = hdev->level + 1;
		// ...
		choose_address(udev);
		// ...
		/* reset and get descriptor */
		status = hub_port_init(hub, udev, port1, i);
		// ...
		if (!status) {
			status = usb_new_device(udev);
			// ...
		}
		// ...
		status = hub_power_remaining(hub);
		// ...
}

    在这个循环中,主要涉及 8 个重量级函数,先点明它们的角色分工。

    第一个函数,usb_alloc_dev(),一个 struct usb_device 结构体指针,申请内存,这个结构体指针可不是为 Hub 准备的,它正是为了 Hub 这个端口所接的设备而申请的,别忘了我们此时此刻的上下文,之所以进入这个循环,是因为我们的 Hub 检测到某个端口有设备连接,所以,Hub 驱动就义不容辞地要为该设备做点什么。

    第二个函数,usb_set_device_state(),这个函数用来设置设备的状态,在 struct usb_device 结构体中,有一个成员 enum usb_device_state state,这一刻会把这个设备的状态设置为 USB_STATE_POWERED,即上电状态。

    第三个函数,choose_address(),为设备选择一个地址。后面会用实例来查看效果。

    第四个函数,hub_port_init(),端口初始化,主要就是前面所讲的获取设备的描述符。

    第五个函数,usb_get_status(),这个函数是专门为 Hub 准备的,不是为当前的这个 Hub,而是说当前 Hub 的这个端口上连接的如果又是 Hub,那么和连接普通设备就不一样。

    第六个函数,check_highspeed(),不同速度的设备,当然待遇不一样。

    第七个函数,usb_new_device()。寻找驱动程序,调用驱动程序的 probe,跟踪这个函数就能一直到设备驱动程序的 probe() 函数的调用。

    第八个函数,hub_power_remaining(),电源管理。

usb_alloc_dev

struct usb_device *
usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
{
	struct usb_device *dev;
	// ...
	device_initialize(&dev->dev);
	dev->dev.bus = &usb_bus_type;
	dev->dev.type = &usb_device_type;
	dev->dev.dma_mask = bus->controller->dma_mask;
	dev->state = USB_STATE_ATTACHED;

	INIT_LIST_HEAD(&dev->ep0.urb_list);
	dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
	dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
	/* ep0 maxpacket comes later, from device descriptor */
	dev->ep_in[0] = dev->ep_out[0] = &dev->ep0;
	// ...
	dev->portnum = port1;
	dev->bus = bus;
	dev->parent = parent;
	INIT_LIST_HEAD(&dev->filelist);

#ifdef	CONFIG_PM
	mutex_init(&dev->pm_mutex);
	INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
	dev->autosuspend_delay = usb_autosuspend_delay * HZ;
#endif
	return dev;
}

usb_set_device_state

void usb_set_device_state(struct usb_device *udev,
		enum usb_device_state new_state)
{
	unsigned long flags;

	spin_lock_irqsave(&device_state_lock, flags);
	if (udev->state == USB_STATE_NOTATTACHED)
		;	/* do nothing */
	else if (new_state != USB_STATE_NOTATTACHED) {

		/* root hub wakeup capabilities are managed out-of-band
		 * and may involve silicon errata ... ignore them here.
		 */
		if (udev->parent) {
			if (udev->state == USB_STATE_SUSPENDED
					|| new_state == USB_STATE_SUSPENDED)
				;	/* No change to wakeup settings */
			else if (new_state == USB_STATE_CONFIGURED)
				device_init_wakeup(&udev->dev,
					(udev->actconfig->desc.bmAttributes
					 & USB_CONFIG_ATT_WAKEUP));
			else
				device_init_wakeup(&udev->dev, 0);
		}
		udev->state = new_state;
	} else
		recursively_mark_NOTATTACHED(udev);
	spin_unlock_irqrestore(&device_state_lock, flags);
}

hub_port_init

static int
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
		int retry_counter)
{
	// ...
	/* Reset the device; full speed may morph to high speed */
	retval = hub_port_reset(hub, port1, udev, delay);
	// ...
	for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
		if (USE_NEW_SCHEME(retry_counter)) {
			struct usb_device_descriptor *buf;
			int r = 0;

#define GET_DESCRIPTOR_BUFSIZE	64
			buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
			// ...
			for (j = 0; j < 3; ++j) {
				buf->bMaxPacketSize0 = 0;
				r = usb_control_msg(udev, usb_rcvaddr0pipe(),
					USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
					USB_DT_DEVICE << 8, 0,
					buf, GET_DESCRIPTOR_BUFSIZE,
					USB_CTRL_GET_TIMEOUT);
				// ...
			}
			udev->descriptor.bMaxPacketSize0 =
					buf->bMaxPacketSize0;
			kfree(buf);

			retval = hub_port_reset(hub, port1, udev, delay);
			// ...
		}

		for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
			retval = hub_set_address(udev);
			if (retval >= 0)
				break;
			msleep(200);
		}
		// ...
		retval = usb_get_device_descriptor(udev, 8);
		// ...
}

usb_new_device

int usb_new_device(struct usb_device *udev)
{
	// ...
	usb_detect_quirks(udev);
	err = usb_get_configuration(udev);
	// ...
	/* read the standard strings and cache them if present */
	udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
	udev->manufacturer = usb_cache_string(udev,
			udev->descriptor.iManufacturer);
	udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
	// ...
	/* export the usbdev device-node for libusb */
	udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
			(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
	// ...
	err = device_add(&udev->dev);
	// ...
	if (udev->parent)
		usb_autoresume_device(udev->parent);
	// ...			
}

   
 


http://www.kler.cn/a/453562.html

相关文章:

  • benchmarksql5.0 测试工具如何在达梦上能够跑起来?
  • 依图科技简介
  • 19_HTML5 Web Workers --[HTML5 API 学习之旅]
  • 【C语言】库函数常见的陷阱与缺陷(二):字符串转化函数[3]--strtol
  • JVM实战—2.JVM内存设置与对象分配流转
  • Xshell 和 Xftp 更新提示问题的解决方法及分析
  • Android APP 集成本地大模型 LLM
  • datax ubuntu安装
  • 华为 AI Agent:企业内部管理的智能变革引擎(11/30)
  • E-commerce .net+React(一)——项目初始化
  • Mac上Stable Diffusion的环境搭建(还算比较简单)
  • DevOps 中的 AI:测试始终是一个关键领域
  • 【ES6复习笔记】Promise对象详解(12)
  • cad学习 day7-9
  • 数据结构 C/C++(实验六:查找)
  • 35. TCP网络编程
  • 2024国赛A题第一问
  • 【ubuntu基础软件安装】
  • Weex购物车长列表横滑操作优化“编年史”
  • 成本高,周期长,家电行业如何突破重实验轻仿真的瓶颈?
  • 整合语音命令与大型语言模型 (LLM) 及传感器在人类和机器人之间进行有效的自然语言交流 以简化装配操作并提高生产车间的安全性
  • Redis可视化工具 RDM mac安装使用
  • 【C语言】判断素数
  • 电商平台能挡住恶意网络爬虫的攻击吗?
  • 一键自动创建删除磁盘的逻辑卷信息
  • 大模型日报 2024-12-20