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

📄 hub.c

📁 usb driver for 2.6.17
💻 C
📖 第 1 页 / 共 5 页
字号:
	return 0;fail:	usb_set_device_state(udev, USB_STATE_NOTATTACHED);	return err;}static int hub_port_status(struct usb_hub *hub, int port1,			       u16 *status, u16 *change){	int ret;	ret = get_port_status(hub->hdev, port1, &hub->status->port);	if (ret < 0)		dev_err (hub->intfdev,			"%s failed (err = %d)\n", __FUNCTION__, ret);	else {		*status = le16_to_cpu(hub->status->port.wPortStatus);		*change = le16_to_cpu(hub->status->port.wPortChange); 		ret = 0;	}	return ret;}#define PORT_RESET_TRIES	5#define SET_ADDRESS_TRIES	2#define GET_DESCRIPTOR_TRIES	2#define SET_CONFIG_TRIES	(2 * (use_both_schemes + 1))#define USE_NEW_SCHEME(i)	((i) / 2 == old_scheme_first)#define HUB_ROOT_RESET_TIME	50	/* times are in msec */#define HUB_SHORT_RESET_TIME	10#define HUB_LONG_RESET_TIME	200#define HUB_RESET_TIMEOUT	500static int hub_port_wait_reset(struct usb_hub *hub, int port1,				struct usb_device *udev, unsigned int delay){	int delay_time, ret;	u16 portstatus;	u16 portchange;	for (delay_time = 0;			delay_time < HUB_RESET_TIMEOUT;			delay_time += delay) {		/* wait to give the device a chance to reset */		msleep(delay);		/* read and decode port status */		ret = hub_port_status(hub, port1, &portstatus, &portchange);		if (ret < 0)			return ret;		/* Device went away? */		if (!(portstatus & USB_PORT_STAT_CONNECTION))			return -ENOTCONN;		/* bomb out completely if something weird happened */		if ((portchange & USB_PORT_STAT_C_CONNECTION))			return -EINVAL;		/* if we`ve finished resetting, then break out of the loop */		if (!(portstatus & USB_PORT_STAT_RESET) &&		    (portstatus & USB_PORT_STAT_ENABLE)) {			if (portstatus & USB_PORT_STAT_HIGH_SPEED)				udev->speed = USB_SPEED_HIGH;			else if (portstatus & USB_PORT_STAT_LOW_SPEED)				udev->speed = USB_SPEED_LOW;			else				udev->speed = USB_SPEED_FULL;			return 0;		}		/* switch to the long delay after two short delay failures */		if (delay_time >= 2 * HUB_SHORT_RESET_TIME)			delay = HUB_LONG_RESET_TIME;		dev_dbg (hub->intfdev,			"port %d not reset yet, waiting %dms\n",			port1, delay);	}	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	 *  - ... 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	if (udev->state == USB_STATE_NOTATTACHED)		return -ENODEV;	return __usb_suspend_device(udev, udev->portnum);#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);	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 < 2)		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++) {			struct device *dev =					&udev->actconfig->interface[i]->dev;			down(&dev->sem);			(void) resume(dev);			up(&dev->sem);		}		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	status;	if (udev->state == USB_STATE_NOTATTACHED)		return -ENODEV;	/* selective resume of one downstream hub-to-device port */	if (udev->parent) {#ifdef	CONFIG_USB_SUSPEND		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),					udev->portnum, udev);		} else#endif

⌨️ 快捷键说明

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