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

static int
hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
		int retry_counter)
{
	struct usb_device	*hdev = hub->hdev;
	struct usb_hcd		*hcd = bus_to_hcd(hdev->bus);
	int			retries, operations, retval, i;
	unsigned		delay = HUB_SHORT_RESET_TIME;
	enum usb_device_speed	oldspeed = udev->speed;
	const char		*speed;
	int			devnum = udev->devnum;
	const char		*driver_name;

	/* root hub ports have a slightly longer reset period
	 * (from USB 2.0 spec, section 7.1.7.5)
	 */
	if (!hdev->parent) {
		delay = HUB_ROOT_RESET_TIME;
		if (port1 == hdev->bus->otg_port)
			hdev->bus->b_hnp_enable = 0;
	}

	/* Some low speed devices have problems with the quick delay, so */
	/*  be a bit pessimistic with those devices. RHbug #23670 */
	if (oldspeed == USB_SPEED_LOW)        //根据速度设置重启延时时间
		delay = HUB_LONG_RESET_TIME;

	mutex_lock(hcd->address0_mutex);

	/* Reset the device; full speed may morph to high speed */
	/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
	retval = hub_port_reset(hub, port1, udev, delay, false);     //重启设备
	if (retval < 0)		/* error or disconnect */
		goto fail;
	/* success, speed is known */

	retval = -ENODEV;

	/* Don't allow speed changes at reset, except usb 3.0 to faster */
	if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed &&       //重启前后速度不能改变,若改变,则出错了
	    !(oldspeed == USB_SPEED_SUPER && udev->speed > oldspeed)) {
		dev_dbg(&udev->dev, "device reset changed speed!\n");
		goto fail;
	}
	oldspeed = udev->speed;

	/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
	 * it's fixed size except for full speed devices.
	 * For Wireless USB devices, ep0 max packet is always 512 (tho
	 * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
	 */
	switch (udev->speed) {                                 //根据速度预置0号端点package的最大字节数,后期读取到设备描述符后会修正
	case USB_SPEED_SUPER_PLUS:
	case USB_SPEED_SUPER:
	case USB_SPEED_WIRELESS:	/* fixed at 512 */
		udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
		break;
	case USB_SPEED_HIGH:		/* fixed at 64 */
		udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
		break;
	case USB_SPEED_FULL:		/* 8, 16, 32, or 64 */
		/* to determine the ep0 maxpacket size, try to read
		 * the device descriptor to get bMaxPacketSize0 and
		 * then correct our initial guess.
		 */
		udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
		break;
	case USB_SPEED_LOW:		/* fixed at 8 */
		udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
		break;
	default:
		goto fail;
	}

	if (udev->speed == USB_SPEED_WIRELESS)
		speed = "variable speed Wireless";
	else
		speed = usb_speed_string(udev->speed);

	/*
	 * The controller driver may be NULL if the controller device
	 * is the middle device between platform device and roothub.
	 * This middle device may not need a device driver due to
	 * all hardware control can be at platform device driver, this
	 * platform device is usually a dual-role USB controller device.
	 */
	if (udev->bus->controller->driver)
		driver_name = udev->bus->controller->driver->name;
	else
		driver_name = udev->bus->sysdev->driver->name;

	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, driver_name);

	/* Set up TT records, if needed  */
	if (hdev->tt) {                 //如果需要tt转换电路,则设置,一般只有高速hub才有
		udev->tt = hdev->tt;
		udev->ttport = hdev->ttport;
	} else if (udev->speed != USB_SPEED_HIGH
			&& hdev->speed == USB_SPEED_HIGH) {
		if (!hub->tt.hub) {
			dev_err(&udev->dev, "parent hub has no TT\n");
			retval = -EINVAL;
			goto fail;
		}
		udev->tt = &hub->tt;
		udev->ttport = port1;
	}

	/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
	 * Because device hardware and firmware is sometimes buggy in
	 * this area, and this is how Linux has done it for ages.
	 * Change it cautiously.
	 *
	 * NOTE:  If use_new_scheme() is true we will start by issuing
	 * a 64-byte GET_DESCRIPTOR request.  This is what Windows does,
	 * so it may help with some non-standards-compliant devices.
	 * Otherwise we start with SET_ADDRESS and then try to read the
	 * first 8 bytes of the device descriptor to get the ep0 maxpacket
	 * value.
	 */

    //读取设备描述符,两种方式选一个,一种是windows的,new_scheme,一种是linux里面的
	for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
		bool did_new_scheme = false;
  /**********************************************************************
       windows中的new_scheme方式读取,总体思路:
    for循环,尝试三次,每次读取64字节任意一次读取成功,r = 0,退出for循环,并修改ep0 的 max size。
    读取成功的标志是:类型为设备描述符、bMaxPacketSize0在USB协议范围内。
         *****************************************************************/
		if (use_new_scheme(udev, retry_counter)) {
			struct usb_device_descriptor *buf;
			int r = 0;
		
			did_new_scheme = true;
			retval = hub_enable_device(udev);   //使能设备
			if (retval < 0) {
				dev_err(&udev->dev,
					"hub failed to enable device, error %d\n",
					retval);
				goto fail;
			}

#define GET_DESCRIPTOR_BUFSIZE	64
			buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
			if (!buf) {
				retval = -ENOMEM;
				continue;
			}

			/* Retry on all errors; some devices are flakey.
			 * 255 is for WUSB devices, we actually need to use
			 * 512 (WUSB1.0[4.8.1]).
			 */
			for (operations = 0; operations < 3; ++operations) {    //预或设备描述符,并检查其合法性,尝试三次获取,其中一次成功后
				buf->bMaxPacketSize0 = 0;    
				printk(KERN_EMERG"pompey hub: %s get desc in new %d\n", _func__,__LINE__);                       //r = 0,则退出for,如果压根儿就没有获取到描述符,则 r = -EPROTO; 
				r = usb_control_msg(udev, usb_rcvaddr0pipe(),
					USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
					USB_DT_DEVICE << 8, 0,
					buf, GET_DESCRIPTOR_BUFSIZE,
					initial_descriptor_timeout);      //获取设备描述符
				switch (buf->bMaxPacketSize0) {       // 查看描述符的合法性  
				case 8: case 16: case 32: case 64: case 255:            //读取到设备描述符bMaxPacketSize0 在协议范围内
					if (buf->bDescriptorType ==
							USB_DT_DEVICE) {                   //描述符类型检查,必须为设备描述符
						r = 0;                                    //退出for循环的条件
						break;
					}
					/* FALL THROUGH */
				default:                           //压根儿就没有读到设备描述符的情况
					if (r == 0)
						r = -EPROTO;
					break;
				}
				/*
				 * Some devices time out if they are powered on
				 * when already connected. They need a second
				 * reset. But only on the first attempt,
				 * lest we get into a time out/reset loop
				 */
				if (r == 0 || (r == -ETIMEDOUT &&
						retries == 0 &&
						udev->speed > USB_SPEED_FULL))    //结束该层for循环的两个条件 1. r = 0; 2.外层for也是第一次,且读取超时,且不是低速/全速设备。
					break;
			}
			udev->descriptor.bMaxPacketSize0 =            //修正端点0的最大包size,但是这里如果设备描述没有获取成功怎么办?
					buf->bMaxPacketSize0;
			kfree(buf);

			retval = hub_port_reset(hub, port1, udev, delay, false);  
			if (retval < 0)		/* error or disconnect */
				goto fail;
			if (oldspeed != udev->speed) {
				dev_dbg(&udev->dev,
					"device reset changed speed!\n");
				retval = -ENODEV;
				goto fail;
			}
			if (r) {
				if (r != -ENODEV)
					dev_err(&udev->dev, "device descriptor read/64, error %d\n",
							r);
				retval = -EMSGSIZE;
				continue;
			}
#undef GET_DESCRIPTOR_BUFSIZE
		}  
//结束windows new_sheme 获取设备描述符,即新方法获取设备描述符
		/*
		 * If device is WUSB, we already assigned an
		 * unauthorized address in the Connect Ack sequence;
		 * authorization will assign the final address.
		 */
		if (udev->wusb == 0) {                //如果不是非无线USB设备
			for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
				retval = hub_set_address(udev, devnum);                           //设置地址
				if (retval >= 0)
					break;
				msleep(200);
			}
			if (retval < 0) {
				if (retval != -ENODEV)
					dev_err(&udev->dev, "device not accepting address %d, error %d\n",
							devnum, retval);
				goto fail;
			}
			if (udev->speed >= USB_SPEED_SUPER) {
				devnum = udev->devnum;
				dev_info(&udev->dev,
						"%s SuperSpeed%s USB device number %d using %s\n",
						(udev->config) ? "reset" : "new",
					 (udev->speed == USB_SPEED_SUPER_PLUS) ? "Plus" : "",
					 devnum, driver_name);
			}

			/* cope with hardware quirkiness:
			 *  - let SET_ADDRESS settle, some device hardware wants it
			 *  - read ep0 maxpacket even for high and low speed,
			 */
			msleep(10);
			/* use_new_scheme() checks the speed which may have
			 * changed since the initial look so we cache the result
			 * in did_new_scheme
			 */
			if (did_new_scheme)   //即如果使用的是新方法获取,则彻底结束外层for循环,不然就用linux方法获取到描述符
				break;
		}

		retval = usb_get_device_descriptor(udev, 8);        //获取描述符前8个字节
		
		if (retval < 8) {
			if (retval != -ENODEV)
				dev_err(&udev->dev,
					"device descriptor read/8, error %d\n",
					retval);
			if (retval >= 0)
				retval = -EMSGSIZE;
		} else {
			retval = 0;           //获取到后退出最外层for循环,
			break;                  
		}
	}           //end for (retries = 0; retries < GET_DESCRIPTOR_TRIES;...
	if (retval)
		goto fail;

	/*
	 * Some superspeed devices have finished the link training process
	 * and attached to a superspeed hub port, but the device descriptor
	 * got from those devices show they aren't superspeed devices. Warm
	 * reset the port attached by the devices can fix them.
	 */
	if ((udev->speed >= USB_SPEED_SUPER) &&           // 设备描述符合法性校验
			(le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
		dev_err(&udev->dev, "got a wrong device descriptor, "
				"warm reset device\n");
		hub_port_reset(hub, port1, udev,
				HUB_BH_RESET_TIME, true);
		retval = -EINVAL;
		goto fail;
	}

	if (udev->descriptor.bMaxPacketSize0 == 0xff ||  //再次修正ep0 max size
			udev->speed >= USB_SPEED_SUPER)
		i = 512;
	else
		i = udev->descriptor.bMaxPacketSize0;
	if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
		if (udev->speed == USB_SPEED_LOW ||
				!(i == 8 || i == 16 || i == 32 || i == 64)) {
			dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
			retval = -EMSGSIZE;
			goto fail;
		}
		if (udev->speed == USB_SPEED_FULL)
			dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
		else
			dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i);
		udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
		usb_ep0_reinit(udev);
	}

	retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);  //获取全部的设备描述符
	if (retval < (signed)sizeof(udev->descriptor)) {
		if (retval != -ENODEV)
			dev_err(&udev->dev, "device descriptor read/all, error %d\n",
					retval);
		if (retval >= 0)
			retval = -ENOMSG;
		goto fail;
	}

	usb_detect_quirks(udev);

	if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {   //对于 USB3.0 获取设备的BOS描述符
		retval = usb_get_bos_descriptor(udev);
		if (!retval) {
			udev->lpm_capable = usb_device_supports_lpm(udev);
			usb_set_lpm_parameters(udev);
		}
	}

	retval = 0;
	/* notify HCD that we have a device connected and addressed */
	if (hcd->driver->update_device)
		hcd->driver->update_device(hcd, udev);
	hub_set_initial_usb2_lpm_policy(udev);
fail:
	if (retval) {
		hub_port_disable(hub, port1, 0);
		update_devnum(udev, devnum);	/* for disconnect processing */
	}
	mutex_unlock(hcd->address0_mutex);
	return retval;
}

整体来说,就是对设备描述符进行读取并校验,先用windows或者linux方法读取一次,其目的是获取ep0 的 max size,然后再读取全部设备描述符,如果是USB3.0以上,还有BOS描述符;对于BOS描述符是什么,还不是特别了解,USB3.0协议还没看。后续看了3.0的协议,再来补充。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值