⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hub.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
 * USB resume explicitly guarantees that the power session between * the host and the device is the same as it was when the device * suspended. * * If CONFIG_USB_PERSIST and @udev->reset_resume are both set then this * routine won't check that the port is still enabled.  Furthermore, * if @udev->reset_resume is set then finish_port_resume() above will * reset @udev.  The end result is that a broken power session can be * recovered and @udev will appear to persist across a loss of VBUS power. * * For example, if a host controller doesn't maintain VBUS suspend current * during a system sleep or is reset when the system wakes up, all the USB * power sessions below it will be broken.  This is especially troublesome * for mass-storage devices containing mounted filesystems, since the * device will appear to have disconnected and all the memory mappings * to it will be lost.  Using the USB_PERSIST facility, the device can be * made to appear as if it had not disconnected. * * This facility is inherently dangerous.  Although usb_reset_device() * makes every effort to insure that the same device is present after the * reset as before, it cannot provide a 100% guarantee.  Furthermore it's * quite possible for a device to remain unaltered but its media to be * changed.  If the user replaces a flash memory card while the system is * asleep, he will have only himself to blame when the filesystem on the * new card is corrupted and the system crashes. * * Returns 0 on success, else negative errno. */int usb_port_resume(struct usb_device *udev){	struct usb_hub	*hub = hdev_to_hub(udev->parent);	int		port1 = udev->portnum;	int		status;	u16		portchange, portstatus;	unsigned	mask_flags, want_flags;	/* Skip the initial Clear-Suspend step for a remote wakeup */	status = hub_port_status(hub, port1, &portstatus, &portchange);	if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND))		goto SuspendCleared;	// dev_dbg(hub->intfdev, "resume port %d\n", port1);	set_bit(port1, hub->busy_bits);	/* see 7.1.7.7; affects power usage, but not budgeting */	status = clear_port_feature(hub->hdev,			port1, USB_PORT_FEAT_SUSPEND);	if (status) {		dev_dbg(hub->intfdev, "can't resume port %d, status %d\n",				port1, status);	} else {		/* drive resume for at least 20 msec */		dev_dbg(&udev->dev, "usb %sresume\n",				udev->auto_pm ? "auto-" : "");		msleep(25);		/* Virtual root hubs can trigger on GET_PORT_STATUS to		 * stop resume signaling.  Then finish the resume		 * sequence.		 */		status = hub_port_status(hub, port1, &portstatus, &portchange); SuspendCleared:		if (USB_PERSIST && udev->reset_resume)			want_flags = USB_PORT_STAT_POWER					| USB_PORT_STAT_CONNECTION;		else			want_flags = USB_PORT_STAT_POWER					| USB_PORT_STAT_CONNECTION					| USB_PORT_STAT_ENABLE;		mask_flags = want_flags | USB_PORT_STAT_SUSPEND;		if (status < 0 || (portstatus & mask_flags) != want_flags) {			dev_dbg(hub->intfdev,				"port %d status %04x.%04x after resume, %d\n",				port1, portchange, portstatus, status);			if (status >= 0)				status = -ENODEV;		} else {			if (portchange & USB_PORT_STAT_C_SUSPEND)				clear_port_feature(hub->hdev, port1,						USB_PORT_FEAT_C_SUSPEND);			/* TRSMRCY = 10 msec */			msleep(10);		}	}	clear_bit(port1, hub->busy_bits);	if (!hub->hdev->parent && !hub->busy_bits[0])		usb_enable_root_hub_irq(hub->hdev->bus);	if (status == 0)		status = finish_port_resume(udev);	if (status < 0) {		dev_dbg(&udev->dev, "can't resume, status %d\n", status);		hub_port_logical_disconnect(hub, port1);	}	return status;}static int remote_wakeup(struct usb_device *udev){	int	status = 0;	usb_lock_device(udev);	if (udev->state == USB_STATE_SUSPENDED) {		dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");		usb_mark_last_busy(udev);		status = usb_external_resume_device(udev);	}	usb_unlock_device(udev);	return status;}#else	/* CONFIG_USB_SUSPEND *//* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */int usb_port_suspend(struct usb_device *udev){	return 0;}int usb_port_resume(struct usb_device *udev){	int status = 0;	/* However we may need to do a reset-resume */	if (udev->reset_resume) {		dev_dbg(&udev->dev, "reset-resume\n");		status = usb_reset_device(udev);	}	return status;}static inline int remote_wakeup(struct usb_device *udev){	return 0;}#endifstatic int hub_suspend(struct usb_interface *intf, pm_message_t msg){	struct usb_hub		*hub = usb_get_intfdata (intf);	struct usb_device	*hdev = hub->hdev;	unsigned		port1;	/* fail if children aren't already suspended */	for (port1 = 1; port1 <= hdev->maxchild; port1++) {		struct usb_device	*udev;		udev = hdev->children [port1-1];		if (udev && udev->can_submit) {			if (!hdev->auto_pm)				dev_dbg(&intf->dev, "port %d nyet suspended\n",						port1);			return -EBUSY;		}	}	dev_dbg(&intf->dev, "%s\n", __FUNCTION__);	/* stop khubd and related activity */	hub_quiesce(hub);	return 0;}static int hub_resume(struct usb_interface *intf){	struct usb_hub		*hub = usb_get_intfdata (intf);	dev_dbg(&intf->dev, "%s\n", __FUNCTION__);	/* tell khubd to look for changes on this hub */	hub_activate(hub);	return 0;}static int hub_reset_resume(struct usb_interface *intf){	struct usb_hub *hub = usb_get_intfdata(intf);	struct usb_device *hdev = hub->hdev;	int port1;	hub_power_on(hub);	for (port1 = 1; port1 <= hdev->maxchild; ++port1) {		struct usb_device *child = hdev->children[port1-1];		if (child) {			/* For "USB_PERSIST"-enabled children we must			 * mark the child device for reset-resume and			 * turn off the connect-change status to prevent			 * khubd from disconnecting it later.			 */			if (USB_PERSIST && child->persist_enabled) {				child->reset_resume = 1;				clear_port_feature(hdev, port1,						USB_PORT_FEAT_C_CONNECTION);			/* Otherwise we must disconnect the child,			 * but as we may not lock the child device here			 * we have to do a "logical" disconnect.			 */			} else {				hub_port_logical_disconnect(hub, port1);			}		}	}	hub_activate(hub);	return 0;}/** * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power * @rhdev: struct usb_device for the root hub * * The USB host controller driver calls this function when its root hub * is resumed and Vbus power has been interrupted or the controller * has been reset.  The routine marks @rhdev as having lost power.  When * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST * is enabled then it will carry out power-session recovery, otherwise * it will disconnect all the child devices. */void usb_root_hub_lost_power(struct usb_device *rhdev){	dev_warn(&rhdev->dev, "root hub lost power or was reset\n");	rhdev->reset_resume = 1;}EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);#else	/* CONFIG_PM */static inline int remote_wakeup(struct usb_device *udev){	return 0;}#define hub_suspend		NULL#define hub_resume		NULL#define hub_reset_resume	NULL#endif/* USB 2.0 spec, 7.1.7.3 / fig 7-29: * * Between connect detection and reset signaling there must be a delay * of 100ms at least for debounce and power-settling.  The corresponding * timer shall restart whenever the downstream port detects a disconnect. *  * Apparently there are some bluetooth and irda-dongles and a number of * low-speed devices for which this debounce period may last over a second. * Not covered by the spec - but easy to deal with. * * This implementation uses a 1500ms total debounce timeout; if the * connection isn't stable by then it returns -ETIMEDOUT.  It checks * every 25ms for transient disconnects.  When the port status has been * unchanged for 100ms it returns the port status. */#define HUB_DEBOUNCE_TIMEOUT	1500#define HUB_DEBOUNCE_STEP	  25#define HUB_DEBOUNCE_STABLE	 100static int hub_port_debounce(struct usb_hub *hub, int port1){	int ret;	int total_time, stable_time = 0;	u16 portchange, portstatus;	unsigned connection = 0xffff;	for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {		ret = hub_port_status(hub, port1, &portstatus, &portchange);		if (ret < 0)			return ret;		if (!(portchange & USB_PORT_STAT_C_CONNECTION) &&		     (portstatus & USB_PORT_STAT_CONNECTION) == connection) {			stable_time += HUB_DEBOUNCE_STEP;			if (stable_time >= HUB_DEBOUNCE_STABLE)				break;		} else {			stable_time = 0;			connection = portstatus & USB_PORT_STAT_CONNECTION;		}		if (portchange & USB_PORT_STAT_C_CONNECTION) {			clear_port_feature(hub->hdev, port1,					USB_PORT_FEAT_C_CONNECTION);		}		if (total_time >= HUB_DEBOUNCE_TIMEOUT)			break;		msleep(HUB_DEBOUNCE_STEP);	}	dev_dbg (hub->intfdev,		"debounce: port %d: total %dms stable %dms status 0x%x\n",		port1, total_time, stable_time, portstatus);	if (stable_time < HUB_DEBOUNCE_STABLE)		return -ETIMEDOUT;	return portstatus;}static void ep0_reinit(struct usb_device *udev){	usb_disable_endpoint(udev, 0 + USB_DIR_IN);	usb_disable_endpoint(udev, 0 + USB_DIR_OUT);	usb_enable_endpoint(udev, &udev->ep0);}#define usb_sndaddr0pipe()	(PIPE_CONTROL << 30)#define usb_rcvaddr0pipe()	((PIPE_CONTROL << 30) | USB_DIR_IN)static int hub_set_address(struct usb_device *udev, int devnum){	int retval;	if (devnum <= 1)		return -EINVAL;	if (udev->state == USB_STATE_ADDRESS)		return 0;	if (udev->state != USB_STATE_DEFAULT)		return -EINVAL;	retval = usb_control_msg(udev, usb_sndaddr0pipe(),		USB_REQ_SET_ADDRESS, 0, devnum, 0,		NULL, 0, USB_CTRL_SET_TIMEOUT);	if (retval == 0) {		udev->devnum = devnum;	/* Device now using proper address */		usb_set_device_state(udev, USB_STATE_ADDRESS);		ep0_reinit(udev);	}	return retval;}/* Reset device, (re)assign address, get device descriptor. * Device connection must be stable, no more debouncing needed. * Returns device in USB_STATE_ADDRESS, except on error. * * If this is called for an already-existing device (as part of * usb_reset_device), the caller must own the device lock.  For a * newly detected device that is not accessible through any global * pointers, it's not necessary to lock the device. */static inthub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,		int retry_counter){	static DEFINE_MUTEX(usb_address0_mutex);	struct usb_device	*hdev = hub->hdev;	int			i, j, retval;	unsigned		delay = HUB_SHORT_RESET_TIME;	enum usb_device_speed	oldspeed = udev->speed;	char 			*speed, *type;	int			devnum = udev->devnum;	/* 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(&usb_address0_mutex);	/* Reset the device; full speed may morph to high speed */	retval = hub_port_reset(hub, port1, udev, delay);	if (retval < 0)		/* error or disconnect */		goto fail;				/* success, speed is known */	retval = -ENODEV;	if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {		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) {	case USB_SPEED_VARIABLE:	/* fixed at 512 */		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(512);		break;	case USB_SPEED_HIGH:		/* fixed at 64 */		udev->ep0.desc.wMaxPacketSize = __constant_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 = __constant_cpu_to_le16(64);		break;	case USB_SPEED_LOW:		/* fixed at 8 */		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(8);		break;	default:		goto fail;	} 	type = "";	switch (udev->speed) {	case USB_SPEED_LOW:	speed = "low";	break;	case USB_SPEED_FULL:	speed = "full";	break;	case USB_SPEED_HIGH:	speed = "high";	break;	case USB_SPEED_VARIABLE:				speed = "variable";				type = "Wireless ";				break;	default: 		speed = "?";	break;	}	dev_info (&udev->dev,		  "%s %s speed %sUSB device using %s and address %d\n",		  (udev->config) ? "reset" : "new", speed, type,		  udev->bus->controller->driver->name, devnum);	/* Set up TT records, if needed  */	if (hdev->tt) {		udev->tt = hdev->tt;		udev->ttport = hdev->ttport;	} else if (udev->speed != USB_SPEED_HIGH			&& hdev->speed == USB_SPEED_HIGH) {		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 descript

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -