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

📄 hub.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
	for (i = 0; i < PORT_RESET_TRIES; i++) {		status = set_port_feature(hub->hdev,				port1, USB_PORT_FEAT_RESET);		if (status)			dev_err(hub->intfdev,					"cannot reset port %d (err = %d)\n",					port1, status);		else {			status = hub_port_wait_reset(hub, port1, udev, delay);			if (status && status != -ENOTCONN)				dev_dbg(hub->intfdev,						"port_wait_reset: err = %d\n",						status);		}		/* return on disconnect or reset */		switch (status) {		case 0:			/* TRSTRCY = 10 ms; plus some extra */			msleep(10 + 40);			/* FALL THROUGH */		case -ENOTCONN:		case -ENODEV:			clear_port_feature(hub->hdev,				port1, USB_PORT_FEAT_C_RESET);			/* FIXME need disconnect() for NOTATTACHED device */			usb_set_device_state(udev, status					? USB_STATE_NOTATTACHED					: USB_STATE_DEFAULT);			return status;		}		dev_dbg (hub->intfdev,			"port %d not enabled, trying reset again...\n",			port1);		delay = HUB_LONG_RESET_TIME;	}	dev_err (hub->intfdev,		"Cannot enable port %i.  Maybe the USB cable is bad?\n",		port1);	return status;}/* * Disable a port and mark a logical connnect-change event, so that some * time later khubd will disconnect() any existing usb_device on the port * and will re-enumerate if there actually is a device attached. */static void hub_port_logical_disconnect(struct usb_hub *hub, int port1){	dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1);	hub_port_disable(hub, port1, 1);	/* FIXME let caller ask to power down the port:	 *  - some devices won't enumerate without a VBUS power cycle	 *  - SRP saves power that way	 *  - ... new call, TBD ...	 * That's easy if this hub can switch power per-port, and	 * khubd reactivates the port later (timer, SRP, etc).	 * Powerdown must be optional, because of reset/DFU.	 */	set_bit(port1, hub->change_bits); 	kick_khubd(hub);}#ifdef	CONFIG_USB_SUSPEND/* * Selective port suspend reduces power; most suspended devices draw * less than 500 uA.  It's also used in OTG, along with remote wakeup. * All devices below the suspended port are also suspended. * * Devices leave suspend state when the host wakes them up.  Some devices * also support "remote wakeup", where the device can activate the USB * tree above them to deliver data, such as a keypress or packet.  In * some cases, this wakes the USB host. */static int hub_port_suspend(struct usb_hub *hub, int port1,		struct usb_device *udev){	int	status;	// dev_dbg(hub->intfdev, "suspend port %d\n", port1);	/* enable remote wakeup when appropriate; this lets the device	 * wake up the upstream hub (including maybe the root hub).	 *	 * NOTE:  OTG devices may issue remote wakeup (or SRP) even when	 * we don't explicitly enable it here.	 */	if (device_may_wakeup(&udev->dev)) {		status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),				USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,				USB_DEVICE_REMOTE_WAKEUP, 0,				NULL, 0,				USB_CTRL_SET_TIMEOUT);		if (status)			dev_dbg(&udev->dev,				"won't remote wakeup, status %d\n",				status);	}	/* see 7.1.7.6 */	status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);	if (status) {		dev_dbg(hub->intfdev,			"can't suspend port %d, status %d\n",			port1, status);		/* paranoia:  "should not happen" */		(void) 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);	} else {		/* device has up to 10 msec to fully suspend */		dev_dbg(&udev->dev, "usb suspend\n");		usb_set_device_state(udev, USB_STATE_SUSPENDED);		msleep(10);	}	return status;}/* * Devices on USB hub ports have only one "suspend" state, corresponding * to ACPI D2, "may cause the device to lose some context". * State transitions include: * *   - suspend, resume ... when the VBUS power link stays live *   - 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. * * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when * the root hub for their bus goes into global suspend ... so we don't * (falsely) update the device power state to say it suspended. */static int __usb_suspend_device (struct usb_device *udev, int port1){	int	status = 0;	/* caller owns the udev device lock */	if (port1 < 0)		return port1;	if (udev->state == USB_STATE_SUSPENDED			|| udev->state == USB_STATE_NOTATTACHED) {		return 0;	}	/* all interfaces must already be suspended */	if (udev->actconfig) {		int	i;		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {			struct usb_interface	*intf;			intf = udev->actconfig->interface[i];			if (is_active(intf)) {				dev_dbg(&intf->dev, "nyet suspended\n");				return -EBUSY;			}		}	}	/* we only change a device's upstream USB link.	 * root hubs have no upstream USB link.	 */	if (udev->parent)		status = hub_port_suspend(hdev_to_hub(udev->parent), port1,				udev);	if (status == 0)		udev->dev.power.power_state = PMSG_SUSPEND;	return status;}#endif/* * usb_suspend_device - suspend a usb device * @udev: device that's no longer in active use * Context: must be able to sleep; device not locked; pm locks held * * 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. * * This only affects the USB hardware for a device; its interfaces * (and, for hubs, child devices) must already have been 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){#ifdef	CONFIG_USB_SUSPEND	int	port1, status;	port1 = locktree(udev);	if (port1 < 0)		return port1;	status = __usb_suspend_device(udev, port1);	usb_unlock_device(udev);	return status;#else	/* NOTE:  udev->state unchanged, it's not lying ... */	udev->dev.power.power_state = PMSG_SUSPEND;	return 0;#endif}/* * If the USB "suspend" state is in use (rather than "global suspend"), * many devices will be individually taken out of suspend state using * special" resume" signaling.  These routines kick in shortly after * 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_device_resume(struct usb_device *udev){	int	status;	u16	devstatus;	/* caller owns the udev device lock */	dev_dbg(&udev->dev, "finish resume\n");	/* 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.	 */	usb_set_device_state(udev, 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;		int		(*resume)(struct device *);		le16_to_cpus(&devstatus);		if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)				&& udev->parent) {			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		 * may have a child resume event to deal with soon		 */		resume = udev->dev.bus->resume;		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++)			(void) resume(&udev->actconfig->interface[i]->dev);		status = 0;	} else if (udev->devnum <= 0) {		dev_dbg(&udev->dev, "bogus resume!\n");		status = -EINVAL;	}	return status;}#ifdef	CONFIG_USB_SUSPENDstatic inthub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev){	int	status;	// dev_dbg(hub->intfdev, "resume port %d\n", port1);	/* 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 {		u16		devstatus;		u16		portchange;		/* drive resume for at least 20 msec */		if (udev)			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(hub, port1,				&devstatus, &portchange);		if (status < 0				|| (devstatus & LIVE_FLAGS) != LIVE_FLAGS				|| (devstatus & USB_PORT_STAT_SUSPEND) != 0				) {			dev_dbg(hub->intfdev,				"port %d status %04x.%04x after resume, %d\n",				port1, portchange, devstatus, status);		} else {			/* TRSMRCY = 10 msec */			msleep(10);			if (udev)				status = finish_device_resume(udev);		}	}	if (status < 0)		hub_port_logical_disconnect(hub, port1);	return status;}#endif/* * usb_resume_device - re-activate a suspended usb device * @udev: device to re-activate * Context: must be able to sleep; device not locked; pm locks held * * 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	port1, status;	port1 = locktree(udev);	if (port1 < 0)		return port1;#ifdef	CONFIG_USB_SUSPEND	/* selective resume of one downstream hub-to-device port */	if (udev->parent) {		if (udev->state == USB_STATE_SUSPENDED) {			// NOTE swsusp may bork us, device state being wrong...			// NOTE this fails if parent is also suspended...			status = hub_port_resume(hdev_to_hub(udev->parent),					port1, udev);		} else			status = 0;	} else#endif		status = finish_device_resume(udev);	if (status < 0)		dev_dbg(&udev->dev, "can't resume, status %d\n",			status);	usb_unlock_device(udev);	/* rebind drivers that had no suspend() */	if (status == 0) {		usb_lock_all_devices();		bus_rescan_devices(&usb_bus_type);		usb_unlock_all_devices();	}	return status;}static int remote_wakeup(struct usb_device *udev){	int	status = 0;#ifdef	CONFIG_USB_SUSPEND	/* 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_device_resume(udev);	}	up(&udev->serialize);#endif	return status;}static 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->dev.power.power_state.event					== PM_EVENT_ON#ifdef	CONFIG_USB_SUSPEND				|| udev->state != USB_STATE_SUSPENDED#endif				)) {			dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);			return -EBUSY;		}	}	/* "global suspend" of the downstream HC-to-USB interface */	if (!hdev->parent) {		struct usb_bus	*bus = hdev->bus;		if (bus) {			int	status = hcd_bus_suspend (bus);			if (status != 0) {				dev_dbg(&hdev->dev, "'global' suspend %d\n",					status);				return status;			}		} else			return -EOPNOTSUPP;	}	/* stop khubd and related activity */	hub_quiesce(hub);	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);	int			status;	/* "global resume" of the downstream HC-to-USB interface */	if (!hdev->parent) {		struct usb_bus	*bus = hdev->bus;		if (bus) {			status = hcd_bus_resume (bus);			if (status) {				dev_dbg(&intf->dev, "'global' resume %d\n",					status);				return status;			}

⌨️ 快捷键说明

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