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

📄 hub.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 5 页
字号:
	}	return -EBUSY;}static int hub_port_reset(struct usb_hub *hub, int port1,				struct usb_device *udev, unsigned int delay){	int i, status;	/* Reset the port */	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	 *  - usb_suspend_device(dev, PMSG_SUSPEND)	 * 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 (udev->actconfig			// && FIXME (remote wakeup enabled on this bus)			// ... currently assuming it's always appropriate			&& (udev->actconfig->desc.bmAttributes				& USB_CONFIG_ATT_WAKEUP) != 0) {		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. */static int __usb_suspend_device (struct usb_device *udev, int port1,				 pm_message_t state){	int	status;	/* caller owns the udev device lock */	if (port1 < 0)		return port1;	if (udev->state == USB_STATE_SUSPENDED			|| udev->state == USB_STATE_NOTATTACHED) {		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.event <= intf->dev.power.power_state.event)				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.event != state.event						|| status)					dev_err(&intf->dev,						"suspend %d fail, code %d\n",						state.event, 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.event > PM_EVENT_FREEZE) {#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.event > PM_EVENT_FREEZE) {		dev_warn(&udev->dev, "no poweroff yet, suspending instead\n");	}	/* "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);			if (status == 0) {				dev_dbg(&udev->dev, "usb suspend\n");				usb_set_device_state(udev,						USB_STATE_SUSPENDED);			}		} else			status = -EOPNOTSUPP;	} else		status = hub_port_suspend(hdev_to_hub(udev->parent), port1,				udev);	if (status == 0)		udev->dev.power.power_state = state;	return status;}/** * usb_suspend_device - suspend a usb device * @udev: device that's no longer in active use * @state: PMSG_SUSPEND 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, pm_message_t state){	int	port1, status;	port1 = locktree(udev);	if (port1 < 0)		return port1;	status = __usb_suspend_device(udev, port1, state);	usb_unlock_device(udev);	return status;}/* * 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 the udev device lock */	dev_dbg(&udev->dev, "usb 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);	udev->dev.power.power_state = PMSG_ON; 	/* 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.event == PM_EVENT_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.event != PM_EVENT_ON					|| status)				dev_dbg(&intf->dev,					"resume fail, state %d code %d\n",					intf->dev.power.power_state.event, 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_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_port_resume(udev);		}	}	if (status < 0)		hub_port_logical_disconnect(hub, port1);	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	port1, status;	port1 = locktree(udev);	if (port1 < 0)		return port1;	/* "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) {			dev_dbg(&udev->dev, "usb resume\n");			/* TRSMRCY = 10 msec */			msleep(10);			usb_set_device_state (udev, USB_STATE_CONFIGURED);			udev->dev.power.power_state = PMSG_ON;			status = hub_resume (udev					->actconfig->interface[0]);		}	} else if (udev->state == USB_STATE_SUSPENDED) {		// NOTE this fails if parent is also suspended...		status = hub_port_resume(hdev_to_hub(udev->parent),				port1, udev);	} else {		status = 0;	}	if (status < 0) {		dev_dbg(&udev->dev, "can't resume, status %d\n",			status);	}	usb_unlock_device(udev);

⌨️ 快捷键说明

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