Linux USB驱动分析(三)

环境

  • Linux Kernel 5.0
  • Source Insight 3.5
  • USB Spec 2.0

前言

  • USB驱动分析(二)中,我们分析了hub_probe()函数。
  • 接下来我们分析USB设备接入系统后的整个流程。

1 背景知识

The Hub Class defines one additional endpoint beyond Default Control Pipe, which is required for all hubs: the Status Change endpoint. The host system receives port and hub status change notifications through the Status Change endpoint. The Status Change endpoint is an interrupt endpoint. The USB System Software uses the interrupt pipe associated with the Status Change endpoint to detect changes in hub and port status.

结论:从上面Spec资料中,我们可以得出当设备接入系统后,就会产生产生中断传输,然后会调用hub_irq(). 接下来,我们分析下hub_irq()函数。

usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval);
INIT_WORK(&hub->events, hub_event);

2 hub_irq()

static void hub_irq(struct urb *urb)
{
	struct usb_hub *hub = urb->context;
	int status = urb->status;
	unsigned i;
	unsigned long bits;

	switch (status) {
	/* 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;
	}

	/* Something happened, let hub_wq figure it out */
	//queue_work(hub_wq, &hub->events)
	kick_hub_wq(hub);

resubmit:
	status = usb_submit_urb(hub->urb, GFP_ATOMIC);
}
  • 首先获取urb状态,然后从hub->buffer中获取数据,并放在hub->event_bits[0]中,然后让hub->events工作。
  • 重新提交interrupt urb,然后等待端口的变化。

2.1 hub_event()

  • 当hub_irq()处理完后,就会调用hub_event()去处理
static void hub_event(struct work_struct *work)
{
	struct usb_device *hdev;
	struct usb_interface *intf;
	struct usb_hub *hub;
	struct device *hub_dev;
	u16 hubstatus;
	u16 hubchange;
	int i, ret;

	hub = container_of(work, struct usb_hub, events);
	hdev = hub->hdev;
	hub_dev = hub->intfdev;
	intf = to_usb_interface(hub_dev);



	/* 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)) {
			...
			port_event(hub, i);
			...
		}
	}

	/* deal with hub status changes */
	if (test_and_clear_bit(0, hub->event_bits) == 0)
		;	/* do nothing */
	else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
		dev_err(hub_dev, "get_hub_status failed\n");
	else {
		if (hubchange & HUB_CHANGE_LOCAL_POWER) {
			clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
		}
		if (hubchange & HUB_CHANGE_OVERCURRENT) {
			clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
			hub_power_on(hub, true);
			hub_hub_status(hub, &status, &unused);
		}
	}
}
  • 这个函数,首先处理变化的端口,然后处理hub。
  • 为什么要清理hub? 从Spec中可以发现答案

When set, these bits remain set until cleared directly by the USB System Software through a ClearPortFeature() request or by a hub reset.
While a change bit is set, the hub continues to report a status change when polled until all change bits have been cleared by the USB System Software.

2.2 port_event()

static void port_event(struct usb_hub *hub, int port1)
		__must_hold(&port_dev->status_lock)
{
	int connect_change;
	struct usb_port *port_dev = hub->ports[port1 - 1];
	struct usb_device *udev = port_dev->child;
	struct usb_device *hdev = hub->hdev;
	u16 portstatus, portchange;

	connect_change = test_bit(port1, hub->change_bits);
	clear_bit(port1, hub->event_bits);
	clear_bit(port1, hub->wakeup_bits);

	if (hub_port_status(hub, port1, &portstatus, &portchange) < 0)
		return;

	if (portchange & USB_PORT_STAT_C_CONNECTION) {
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
		connect_change = 1;
	}

	if (portchange & USB_PORT_STAT_C_ENABLE) {
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
		if (!(portstatus & USB_PORT_STAT_ENABLE)
		    && !connect_change && udev) {
			dev_err(&port_dev->dev, "disabled by hub (EMI?), re-enabling...\n");
			connect_change = 1;
		}
	}
	if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
		u16 status = 0, unused;
		port_dev->over_current_count++;
		port_over_current_notify(port_dev);

		usb_clear_port_feature(hdev, port1,
				USB_PORT_FEAT_C_OVER_CURRENT);
		hub_power_on(hub, true);
		hub_port_status(hub, port1, &status, &unused);
	}

	if (hub_handle_remote_wakeup(hub, port1, portstatus, portchange))
		connect_change = 1;

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

2.3 hub_port_connect_change()

static void hub_port_connect_change(struct usb_hub *hub, int port1,
					u16 portstatus, u16 portchange)
		__must_hold(&port_dev->status_lock)
{
	struct usb_port *port_dev = hub->ports[port1 - 1];
	struct usb_device *udev = port_dev->child;
	int status = -ENODEV;

	clear_bit(port1, hub->change_bits);

	hub_port_connect(hub, port1, portstatus, portchange);
}

2.4 hub_port_connect()

static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
		u16 portchange)
{
	int status = -ENODEV;
	int i;
	unsigned unit_load;
	struct usb_device *hdev = hub->hdev;
	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
	struct usb_port *port_dev = hub->ports[port1 - 1];
	struct usb_device *udev = port_dev->child;
	static int unreliable_port = -1;


	if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
			(portchange & USB_PORT_STAT_C_CONNECTION))
		clear_bit(port1, hub->removed_bits);


	for (i = 0; i < SET_CONFIG_TRIES; i++) {
		udev = usb_alloc_dev(hdev, hdev->bus, port1);

		usb_set_device_state(udev, USB_STATE_POWERED);
		udev->bus_mA = hub->mA_per_port;
		udev->level = hdev->level + 1;
		udev->wusb = hub_is_wusb(hub);

		/* Devices connected to SuperSpeed hubs are USB 3.0 or later */
		if (hub_is_superspeed(hub->hdev))
			udev->speed = USB_SPEED_SUPER;
		else
			udev->speed = USB_SPEED_UNKNOWN;

		choose_devnum(udev);

		status = hub_port_init(hub, udev, port1, i);
		port_dev->child = udev;
		
		if (!status) {
			status = usb_new_device(udev);
		}
		return;
   }
}
  • 最终调用usb_alloc_dev()和usb_new_device(udev),分配并初始化,注册设备.
  • 设备的初始化以及调用设备接口驱动的过程就和Root Hub一样了.
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值