USB 设备驱动之设备接入梳理(二)

USB提供了一套自顶向下的识别方法,从hub_event出发,检查是否真的有端口发生了改变,如果坐实是port被触发了,则进入port_event。

port_event()

先去获取hub的状态

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

最终也是调用get_port_stats()函数去获得想要的状态,hub也是一类usb设备,

static int get_port_status(struct usb_device *hdev, int port1,
			   void *data, u16 value, u16 length)
{
	int i, status = -ETIMEDOUT;

	for (i = 0; i < USB_STS_RETRIES &&
			(status == -ETIMEDOUT || status == -EPIPE); i++) {
		status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, value,
			port1, data, length, USB_STS_TIMEOUT);
	}
	return status;
}

根据spec对于port状态的请求

状态字段的定于

驱动也有相同的宏定义:

/*
  * wPortChange bit field
  * See USB 2.0 spec Table 11-22 and USB 2.0 LPM ECN Table-4.10
  * Bits 0 to 5 shown, bits 6 to 15 are reserved
  */
#define USB_PORT_STAT_C_CONNECTION	0x0001
#define USB_PORT_STAT_C_ENABLE		0x0002
#define USB_PORT_STAT_C_SUSPEND     0x0004
#define USB_PORT_STAT_C_OVERCURRENT 0x0008
#define USB_PORT_STAT_C_RESET		0x0010
#define USB_PORT_STAT_C_L1		    0x0020
/*
122   * wPortStatus bit field
123   * See USB 2.0 spec Table 11-21
124   */
125  #define USB_PORT_STAT_CONNECTION	0x0001
126  #define USB_PORT_STAT_ENABLE		0x0002
127  #define USB_PORT_STAT_SUSPEND		0x0004
128  #define USB_PORT_STAT_OVERCURRENT	0x0008
129  #define USB_PORT_STAT_RESET		0x0010
130  #define USB_PORT_STAT_L1		0x0020
131  /* bits 6 to 7 are reserved */
132  #define USB_PORT_STAT_POWER		0x0100
133  #define USB_PORT_STAT_LOW_SPEED		0x0200
134  #define USB_PORT_STAT_HIGH_SPEED        0x0400
135  #define USB_PORT_STAT_TEST              0x0800
136  #define USB_PORT_STAT_INDICATOR         0x1000

然后引发了一串 if 判断...

	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) {              //hub上port是否使能?
		if (!connect_change)
			dev_dbg(&port_dev->dev, "enable change, status %08x\n",
					portstatus);
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);

		/*
		 * EM interference sometimes causes badly shielded USB devices
		 * to be shutdown by the hub, this hack enables them again.
		 * Works at least with mouse driver.
		 */
		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;

		dev_dbg(&port_dev->dev, "over-current change\n");
		usb_clear_port_feature(hdev, port1,
				USB_PORT_FEAT_C_OVER_CURRENT);
		msleep(100);	/* Cool down */
		hub_power_on(hub, true);
		hub_port_status(hub, port1, &status, &unused);
		if (status & USB_PORT_STAT_OVERCURRENT)
			dev_err(&port_dev->dev, "over-current condition\n");
	}

	if (portchange & USB_PORT_STAT_C_RESET) {  //重启?
		dev_dbg(&port_dev->dev, "reset change\n");
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_RESET);
	}
	if ((portchange & USB_PORT_STAT_C_BH_RESET)     //以下是usb3.0相关的状态
	    && hub_is_superspeed(hdev)) {
		dev_dbg(&port_dev->dev, "warm reset change\n");
		usb_clear_port_feature(hdev, port1,
				USB_PORT_FEAT_C_BH_PORT_RESET);
	}
	if (portchange & USB_PORT_STAT_C_LINK_STATE) {
		dev_dbg(&port_dev->dev, "link state change\n");
		usb_clear_port_feature(hdev, port1,
				USB_PORT_FEAT_C_PORT_LINK_STATE);
	}
	if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
		dev_warn(&port_dev->dev, "config error\n");
		usb_clear_port_feature(hdev, port1,
				USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
	}

connect_change 标志,用来标志着是不是真的有端口改变了。如果真的改变了,需要处理调用hub_port_connect_change()函数。

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

 

总结一下,在那些情况下会使得 connect_change 标志位 为1?

答:1. 端口对应的hub->change_bits 被置1

      2.有设备接入的时候

     3. 远程唤醒hub端口

port_event() 函数都干了些啥?

答:判定hub的port发生了什么(设备接入、唤醒...),做出相应的响应。

 

hub_port_connect_change()

函数原型:

/* Handle physical or logical connection change events.
 * This routine is called when:
 *	a port connection-change occurs;
 *	a port enable-change occurs (often caused by EMI);
 *	usb_reset_and_verify_device() encounters changed descriptors (as from
 *		a firmware download)
 * caller already locked the hub
 */
static void hub_port_connect_change(struct usb_hub *hub, int port1,
					u16 portstatus, u16 portchange)
		__must_hold(&port_dev->status_lock)

功能:响应物理层或者逻辑层的port改变

适用于: 端口连接情况发生改变;端口使能情况发生改变等

若hub有指示灯,则开始工作

if (hub->has_indicators) {
		set_port_led(hub, port1, HUB_LED_AUTO);
		hub->indicator[port1-1] = INDICATOR_AUTO;
	}

如支持otg功能,设置标志位

#ifdef	CONFIG_USB_OTG
	/* during HNP, don't repeat the debounce */
	if (hub->hdev->bus->is_b_host)
		portchange &= ~(USB_PORT_STAT_C_CONNECTION |
				USB_PORT_STAT_C_ENABLE);
#endif

检查端口设备是不是已连接需要被唤醒

	if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
			udev->state != USB_STATE_NOTATTACHED) {
		if (portstatus & USB_PORT_STAT_ENABLE) {
			status = 0;		/* Nothing to do */
#ifdef CONFIG_PM
		} else if (udev->state == USB_STATE_SUSPENDED &&
				udev->persist_enabled) {
			/* For a suspended device, treat this as a
			 * remote wakeup event.
			 */
			usb_unlock_port(port_dev);
			status = usb_remote_wakeup(udev);
			usb_lock_port(port_dev);
#endif
		} else {
			/* Don't resuscitate */;
		}
	}
	clear_bit(port1, hub->change_bits);

	/* successfully revalidated the connection */
	if (status == 0)
		return;

如果之前没有不是只需要唤醒,就进入连接处理

	usb_unlock_port(port_dev);
	hub_port_connect(hub, port1, portstatus, portchange);
	usb_lock_port(port_dev);

hub_port_connect 函数接着处理连接相关事务。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值