USB 设备驱动之设备接入梳理_hub_port_connect()函数(三)

hub_port_connect()函数

断开该端口设备的连接:如果是root hub,挂接在控制器上的,则断开该端口下的设备。

	if (udev) {
		if (hcd->usb_phy && !hdev->parent)
			usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
		usb_disconnect(&port_dev->child);
	}

处理的是hub上有设备断开的情况。

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

	if (portchange & (USB_PORT_STAT_C_CONNECTION |
				USB_PORT_STAT_C_ENABLE)) {
		status = hub_port_debounce_be_stable(hub, port1);
		if (status < 0) {
			if (status != -ENODEV &&
				port1 != unreliable_port &&
				printk_ratelimit())
				dev_err(&port_dev->dev, "connect-debounce failed\n");
			portstatus &= ~USB_PORT_STAT_CONNECTION;
			unreliable_port = port1;
		} else {
			portstatus = status;
		}
	}

hub port 状态相关的宏定义如下:


/*
 * wPortStatus bit field
 * See USB 2.0 spec Table 11-21
 */
#define USB_PORT_STAT_CONNECTION	0x0001
#define USB_PORT_STAT_ENABLE		0x0002
#define USB_PORT_STAT_SUSPEND		0x0004
#define USB_PORT_STAT_OVERCURRENT	0x0008
#define USB_PORT_STAT_RESET		0x0010
#define USB_PORT_STAT_L1		0x0020
/* bits 6 to 7 are reserved */
#define USB_PORT_STAT_POWER		0x0100
#define USB_PORT_STAT_LOW_SPEED		0x0200
#define USB_PORT_STAT_HIGH_SPEED        0x0400
#define USB_PORT_STAT_TEST              0x0800
#define USB_PORT_STAT_INDICATOR         0x1000
/* bits 13 to 15 are reserved */

对应usb2.0  协议 Hub端口状态标志位如协议中的 表11-21

由上表可知道:

bit0表示连接状态,1 是有设备连接,0 是没有设备连接。bit1表示端口被使能还是禁止工作,1 是被使能,0 是禁止工作状态。bit 2表示挂起状态,0 是没有被挂起,1 是被挂起。bit3 表示端口过电流,0 是没有过电流,1 是发生了过电流。bit4 表示是否重启了,0没有收到重启命令,1 是收到了重启命令。bit5~bit7 未使用。bit8 表示端口供电情况,1 是处于power off 状态,0 是出于power on状态。bit9 表示是设备的速度类型,0 是全速或者高速设备接入该端口,1 是低速设备接入。bit10 表示设备速度类型,0 表示全速设备接入,1是高速设备接入。bit11 表示该端口是否处于测试模式,0 不是,1 处于测试模式。bit12 该端口指示灯控制,0是默认颜色,1是软件控制的颜色。bit13~bit15 未启用。

要读懂上面的程序,除了知道Hub 端口的状态标志位的含义还不行,还得知道Hub 端口发生改变的标志位,摘自协议中的表11-22

bit0 表示状态发生了改变,是什么改变,不知道,也许是断开,也许是设备接入,1 表示改变了,0是没有改变,结合协议中的表11-21中的bit0判定是设备接入还是断开。bit1 表示端口使能/禁用的改变,当端口由于错误引起禁用时被置1。bit2 挂起状态的改变,该位是以host的视角来看设备的状态,0 是没有改变,1是从挂起恢复了。bit3 过电流指示,如果过电流,在hub的描述符中表示出来,0 是过电流发生且没有改善,1过电流指示消除。bit4表示重启状态,0 表示没有改变端口的状态,1表示重启完成。其余bit保留。在代码里对应的code为:

/*
 * 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
/*

上面这段代码翻译过来就是:如果有设备断开或者设备连接情况发生了改变,都清除hub的remove bits。如果设备连接情况发生了改变且端口是被使能的,那么先去抖;消除抖动,接入或拔出时,会有物理抖动,导致状态不稳定,所以需要消抖。消抖后如果设备移除了,设置hub feature等清理工作。

根据速度设置hub设备总线电流的阈值

	if (hub_is_superspeed(hub->hdev))
		unit_load = 150;
	else
		unit_load = 100;

 

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);    //分配usb设备内存并初始化bus、type、group、设备在系统中的路径(dev->path)、ep0的属性并设置设备状态为attached。
//这里有一个疑问:设备中端点0是没有描述符的,但是这里有初始化ep0.desc的代码,是软件层面对ep0有单独的描述吗?
		if (!udev) {
			dev_err(&port_dev->dev,
					"couldn't allocate usb_device\n");
			goto done;
		}

		usb_set_device_state(udev, USB_STATE_POWERED);  //设置为 power状态,并设置电源等。
		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);                        //从USB总线的编号数组中找到一个合适的编号,为设备分配地址做好准备。
		if (udev->devnum <= 0) {
			status = -ENOTCONN;	/* Don't retry */
			goto loop;
		}

		/* 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);
		if (status < 0)
			goto loop;

		if (udev->quirks & USB_QUIRK_DELAY_INIT)
			msleep(2000);

		/* consecutive bus-powered hubs aren't reliable; they can
		 * violate the voltage drop budget.  if the new child has
		 * a "powered" LED, users should notice we didn't enable it
		 * (without reading syslog), even without per-port LEDs
		 * on the parent.
		 */
		if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
				&& udev->bus_mA <= unit_load) {
			u16	devstat;

			status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
					&devstat);
			if (status) {
				dev_dbg(&udev->dev, "get status %d ?\n", status);
				goto loop_disable;
			}
			if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
				dev_err(&udev->dev,
					"can't connect bus-powered hub "
					"to this port\n");
				if (hub->has_indicators) {
					hub->indicator[port1-1] =
						INDICATOR_AMBER_BLINK;
					queue_delayed_work(
						system_power_efficient_wq,
						&hub->leds, 0);
				}
				status = -ENOTCONN;	/* Don't retry */
				goto loop_disable;
			}
		}

		/* check for devices running slower than they could */
		if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
				&& udev->speed == USB_SPEED_FULL
				&& highspeed_hubs != 0)
			check_highspeed(hub, udev, port1);

		/* Store the parent's children[] pointer.  At this point
		 * udev becomes globally accessible, although presumably
		 * no one will look at it until hdev is unlocked.
		 */
		status = 0;

		mutex_lock(&usb_port_peer_mutex);

		/* We mustn't add new devices if the parent hub has
		 * been disconnected; we would race with the
		 * recursively_mark_NOTATTACHED() routine.
		 */
		spin_lock_irq(&device_state_lock);
		if (hdev->state == USB_STATE_NOTATTACHED)
			status = -ENOTCONN;
		else
			port_dev->child = udev;
		spin_unlock_irq(&device_state_lock);
		mutex_unlock(&usb_port_peer_mutex);

		/* Run it through the hoops (find a driver, etc) */
		if (!status) {
			status = usb_new_device(udev);            //读取、解析配置描述符,解析端点描述符
			if (status) {
				mutex_lock(&usb_port_peer_mutex);
				spin_lock_irq(&device_state_lock);
				port_dev->child = NULL;
				spin_unlock_irq(&device_state_lock);
				mutex_unlock(&usb_port_peer_mutex);
			} else {
				if (hcd->usb_phy && !hdev->parent)
					usb_phy_notify_connect(hcd->usb_phy,
							udev->speed);
			}
		}

		if (status)
			goto loop_disable;

		status = hub_power_remaining(hub);
		if (status)
			dev_dbg(hub->intfdev, "%dmA power budget left\n", status);

	printk(KERN_EMERG"pompey hub: %s %d status:%d\n",__func__,__FUNCTION__,status);
		return;

	printk(KERN_EMERG"pompey hub: %s %d status:%d\n",__func__,__FUNCTION__,status);
loop_disable:
		hub_port_disable(hub, port1, 1);
loop:
		usb_ep0_reinit(udev);
		release_devnum(udev);
		hub_free_dev(udev);
		usb_put_dev(udev);
		if ((status == -ENOTCONN) || (status == -ENOTSUPP))
			break;

		/* When halfway through our retry count, power-cycle the port */
		if (i == (SET_CONFIG_TRIES / 2) - 1) {
			dev_info(&port_dev->dev, "attempt power cycle\n");
			usb_hub_set_port_power(hdev, hub, port1, false);
			msleep(2 * hub_power_on_good_delay(hub));
			usb_hub_set_port_power(hdev, hub, port1, true);
			msleep(hub_power_on_good_delay(hub));
		}
	}

经过一系列的操作,设备就清清楚楚的被host了解了,是什么设备(设备描述符、配置描述符)、有什么功能(接口描述符)、怎么传数据(端点描述符)都明明白白了。

for (i = 0; i < SET_CONFIG_TRIES; i++) 

......

这里的目的就是多试几次,以防失败。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值