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

📄 hub.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 5 页
字号:
 *   - suspend, disconnect ... VBUS lost * * Once VBUS drop breaks the circuit, the port it's using has to go through * normal re-enumeration procedures, starting with enabling VBUS power. * Other than re-initializing the hub (plug/unplug, except for root hubs), * Linux (2.6) currently has NO mechanisms to initiate that:  no khubd * timer, no SRP, no requests through sysfs. */static int __usb_suspend_device (struct usb_device *udev, int port, u32 state){	int	status;	if (port < 0)		return port;	/* NOTE:  udev->serialize released on all real returns! */	if (state <= udev->dev.power.power_state			|| state < PM_SUSPEND_MEM			|| udev->state == USB_STATE_SUSPENDED			|| udev->state == USB_STATE_NOTATTACHED) {		up(&udev->serialize);		return 0;	}	/* suspend interface drivers; if this is a hub, it	 * suspends the child devices	 */	if (udev->actconfig) {		int	i;		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {			struct usb_interface	*intf;			struct usb_driver	*driver;			intf = udev->actconfig->interface[i];			if (state <= intf->dev.power.power_state)				continue;			if (!intf->dev.driver)				continue;			driver = to_usb_driver(intf->dev.driver);			if (driver->suspend) {				status = driver->suspend(intf, state);				if (intf->dev.power.power_state != state						|| status)					dev_err(&intf->dev,						"suspend %d fail, code %d\n",						state, status);			}			/* only drivers with suspend() can ever resume();			 * and after power loss, even they won't.			 * bus_rescan_devices() can rebind drivers later.			 *			 * FIXME the PM core self-deadlocks when unbinding			 * drivers during suspend/resume ... everything grabs			 * dpm_sem (not a spinlock, ugh).  we want to unbind,			 * since we know every driver's probe/disconnect works			 * even for drivers that can't suspend.			 */			if (!driver->suspend || state > PM_SUSPEND_MEM) {#if 1				dev_warn(&intf->dev, "resume is unsafe!\n");#else				down_write(&usb_bus_type.rwsem);				device_release_driver(&intf->dev);				up_write(&usb_bus_type.rwsem);#endif			}		}	}	/*	 * FIXME this needs port power off call paths too, to help force	 * USB into the "generic" PM model.  At least for devices on	 * ports that aren't using ganged switching (usually root hubs).	 *	 * NOTE: SRP-capable links should adopt more aggressive poweroff	 * policies (when HNP doesn't apply) once we have mechanisms to	 * turn power back on!  (Likely not before 2.7...)	 */	if (state > PM_SUSPEND_MEM) {		dev_warn(&udev->dev, "no poweroff yet, suspending instead\n");		state = PM_SUSPEND_MEM;	}	/* "global suspend" of the HC-to-USB interface (root hub), or	 * "selective suspend" of just one hub-device link.	 */	if (!udev->parent) {		struct usb_bus	*bus = udev->bus;		if (bus && bus->op->hub_suspend)			status = bus->op->hub_suspend (bus);		else			status = -EOPNOTSUPP;	} else		status = hub_port_suspend(udev->parent, port + 1);	if (status == 0)		udev->dev.power.power_state = state;	up(&udev->serialize);	return status;}/** * usb_suspend_device - suspend a usb device * @udev: device that's no longer in active use * @state: PM_SUSPEND_MEM to suspend * Context: must be able to sleep; device not locked * * Suspends a USB device that isn't in active use, conserving power. * Devices may wake out of a suspend, if anything important happens, * using the remote wakeup mechanism.  They may also be taken out of * suspend by the host, using usb_resume_device().  It's also routine * to disconnect devices while they are suspended. * * Suspending OTG devices may trigger HNP, if that's been enabled * between a pair of dual-role devices.  That will change roles, such * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral. * * Returns 0 on success, else negative errno. */int usb_suspend_device(struct usb_device *udev, u32 state){	return __usb_suspend_device(udev, locktree(udev), state);}/* * hardware resume signaling is finished, either because of selective * resume (by host) or remote wakeup (by device) ... now see what changed * in the tree that's rooted at this device. */static int finish_port_resume(struct usb_device *udev){	int	status;	u16	devstatus;	/* caller owns udev->serialize */	dev_dbg(&udev->dev, "usb resume\n");	udev->dev.power.power_state = PM_SUSPEND_ON;	/* usb ch9 identifies four variants of SUSPENDED, based on what	 * state the device resumes to.  Linux currently won't see the	 * first two on the host side; they'd be inside hub_port_init()	 * during many timeouts, but khubd can't suspend until later.	 */	udev->state = udev->actconfig		? USB_STATE_CONFIGURED		: USB_STATE_ADDRESS; 	/* 10.5.4.5 says be sure devices in the tree are still there. 	 * For now let's assume the device didn't go crazy on resume,	 * and device drivers will know about any resume quirks.	 */	status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);	if (status < 0)		dev_dbg(&udev->dev,			"gone after usb resume? status %d\n",			status);	else if (udev->actconfig) {		unsigned	i;		le16_to_cpus(&devstatus);		if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {			status = usb_control_msg(udev,					usb_sndctrlpipe(udev, 0),					USB_REQ_CLEAR_FEATURE,						USB_RECIP_DEVICE,					USB_DEVICE_REMOTE_WAKEUP, 0,					NULL, 0,					USB_CTRL_SET_TIMEOUT);			if (status) {				dev_dbg(&udev->dev, "disable remote "					"wakeup, status %d\n", status);				status = 0;			}		}		/* resume interface drivers; if this is a hub, it		 * resumes the child devices		 */		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {			struct usb_interface	*intf;			struct usb_driver	*driver;			intf = udev->actconfig->interface[i];			if (intf->dev.power.power_state == PM_SUSPEND_ON)				continue;			if (!intf->dev.driver) {				/* FIXME maybe force to alt 0 */				continue;			}			driver = to_usb_driver(intf->dev.driver);			/* bus_rescan_devices() may rebind drivers */			if (!driver->resume)				continue;			/* can we do better than just logging errors? */			status = driver->resume(intf);			if (intf->dev.power.power_state != PM_SUSPEND_ON					|| status)				dev_dbg(&intf->dev,					"resume fail, state %d code %d\n",					intf->dev.power.power_state, status);		}		status = 0;	} else if (udev->devnum <= 0) {		dev_dbg(&udev->dev, "bogus resume!\n");		status = -EINVAL;	}	return status;}static inthub_port_resume(struct usb_device *hdev, int port){	int			status;	struct usb_device	*udev;	udev = hdev->children[port - 1];	// dev_dbg(hubdev(hdev), "resume port %d\n", port);	/* see 7.1.7.7; affects power usage, but not budgeting */	status = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND);	if (status) {		dev_dbg(&hdev->actconfig->interface[0]->dev,			"can't resume port %d, status %d\n",			port, status);	} else {		u16		devstatus;		u16		portchange;		/* drive resume for at least 20 msec */		dev_dbg(&udev->dev, "RESUME\n");		msleep(25);#define LIVE_FLAGS	( USB_PORT_STAT_POWER \			| USB_PORT_STAT_ENABLE \			| USB_PORT_STAT_CONNECTION)		/* Virtual root hubs can trigger on GET_PORT_STATUS to		 * stop resume signaling.  Then finish the resume		 * sequence.		 */		devstatus = portchange = 0;		status = hub_port_status(hdev, port - 1,				&devstatus, &portchange);		if (status < 0				|| (devstatus & LIVE_FLAGS) != LIVE_FLAGS				|| (devstatus & USB_PORT_STAT_SUSPEND) != 0				) {			dev_dbg(&hdev->actconfig->interface[0]->dev,				"port %d status %04x.%04x after resume, %d\n",				port, portchange, devstatus, status);		} else {			/* TRSMRCY = 10 msec */			msleep(10);			status = finish_port_resume(udev);		}	}	if (status < 0)		status = hub_port_disable(hdev, port);	return status;}static int hub_resume (struct usb_interface *intf);/** * usb_resume_device - re-activate a suspended usb device * @udev: device to re-activate * Context: must be able to sleep; device not locked * * This will re-activate the suspended device, increasing power usage * while letting drivers communicate again with its endpoints. * USB resume explicitly guarantees that the power session between * the host and the device is the same as it was when the device * suspended. * * Returns 0 on success, else negative errno. */int usb_resume_device(struct usb_device *udev){	int			port, status;	port = locktree(udev);	if (port < 0)		return port;	/* "global resume" of the HC-to-USB interface (root hub), or	 * selective resume of one hub-to-device port	 */	if (!udev->parent) {		struct usb_bus	*bus = udev->bus;		if (bus && bus->op->hub_resume)			status = bus->op->hub_resume (bus);		else			status = -EOPNOTSUPP;		if (status == 0) {			/* TRSMRCY = 10 msec */			msleep(10);			status = hub_resume (bus->root_hub					->actconfig->interface[0]);		}	} else if (udev->state == USB_STATE_SUSPENDED) {		status = hub_port_resume(udev->parent, port + 1);	} else {		status = 0;		udev->dev.power.power_state = PM_SUSPEND_ON;	}	if (status < 0) {		dev_dbg(&udev->dev, "can't resume, status %d\n",			status);	}	up(&udev->serialize);	/* rebind drivers that had no suspend() */	bus_rescan_devices(&usb_bus_type);	return status;}static int remote_wakeup(struct usb_device *udev){	int	status = 0;	/* don't repeat RESUME sequence if this device	 * was already woken up by some other task	 */	down(&udev->serialize);	if (udev->state == USB_STATE_SUSPENDED) {		dev_dbg(&udev->dev, "RESUME (wakeup)\n");		/* TRSMRCY = 10 msec */		msleep(10);		status = finish_port_resume(udev);	}	up(&udev->serialize);	return status;}static int hub_suspend(struct usb_interface *intf, u32 state){	struct usb_hub		*hub = usb_get_intfdata (intf);	struct usb_device	*hdev = hub->hdev;	unsigned		port;	int			status;	/* stop khubd and related activity */	hub_quiesce(hub);	/* then suspend every port */	for (port = 0; port < hdev->maxchild; port++) {		struct usb_device	*udev;		udev = hdev->children [port];		if (!udev)			continue;		down(&udev->serialize);		status = __usb_suspend_device(udev, port, state);		if (status < 0)			dev_dbg(&intf->dev, "suspend port %d --> %d\n",				port, status);	}	intf->dev.power.power_state = state;	return 0;}static int hub_resume(struct usb_interface *intf){	struct usb_device	*hdev = interface_to_usbdev(intf);	struct usb_hub		*hub = usb_get_intfdata (intf);	unsigned		port;	int			status;	for (port = 0; port < hdev->maxchild; port++) {		struct usb_device	*udev;		u16			portstat, portchange;		udev = hdev->children [port];		status = hub_port_status(hdev, port, &portstat, &portchange);		if (status == 0) {			if (portchange & USB_PORT_STAT_C_SUSPEND) {				clear_port_feature(hdev, port + 1,					USB_PORT_FEAT_C_SUSPEND);				portchange &= ~USB_PORT_STAT_C_SUSPEND;			}			/* let khubd handle disconnects etc */			if (portchange)				continue;		}		if (!udev)			continue;		down (&udev->serialize);		if (portstat & USB_PORT_STAT_SUSPEND)			status = hub_port_resume(hdev, port + 1);		else {			status = finish_port_resume(udev);			if (status < 0)				status = hub_port_disable(hdev, port);			if (status < 0)				dev_dbg(&intf->dev, "resume port %d --> %d\n",					port, status);		}		up(&udev->serialize);	}	intf->dev.power.power_state = PM_SUSPEND_ON;	hub_reactivate(hub);	return 0;}#else	/* !CONFIG_USB_SUSPEND */int usb_suspend_device(struct usb_device *udev, u32 state){	return 0;}int usb_resume_device(struct usb_device *udev){	return 0;}#define	hub_suspend		NULL#define	hub_resume		NULL#define	remote_wakeup(x)	0#endif	/* CONFIG_USB_SUSPEND */EXPORT_SYMBOL(usb_suspend_device);EXPORT_SYMBOL(usb_resume_device);/* 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_device *hdev, int port){	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(hdev, port, &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(hdev, port+1,					USB_PORT_FEAT_C_CONNECTION);		}		if (total_time >= HUB_DEBOUNCE_TIMEOUT)

⌨️ 快捷键说明

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