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

📄 hub.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
					c, err);			usb_remove_sysfs_dev_files(udev);			device_del(&udev->dev);			goto fail;		}	}	/* USB device state == configured ... usable */	/* add a /proc/bus/usb entry */	usbfs_add_device(udev);	return 0;fail:	usb_set_device_state(udev, USB_STATE_NOTATTACHED);	return err;}static int hub_port_status(struct usb_device *hdev, int port,			       u16 *status, u16 *change){	struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]);	int ret;	if (!hub)		return -ENODEV;	ret = get_port_status(hdev, port + 1, &hub->status->port);	if (ret < 0)		dev_err (&hub->intf->dev,			"%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#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_device *hdev, int port,				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(hdev, port, &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 (hubdev (hdev),			"port %d not reset yet, waiting %dms\n",			port + 1, delay);	}	return -EBUSY;}static int hub_port_reset(struct usb_device *hdev, int port,				struct usb_device *udev, unsigned int delay){	int i, status;	struct device *hub_dev = hubdev (hdev);	/* Reset the port */	for (i = 0; i < PORT_RESET_TRIES; i++) {		status = set_port_feature(hdev, port + 1, USB_PORT_FEAT_RESET);		if (status)			dev_err(hub_dev, "cannot reset port %d (err = %d)\n",					port + 1, status);		else			status = hub_port_wait_reset(hdev, port, udev, delay);		/* return on disconnect or reset */		if (status == -ENOTCONN || status == 0) {			clear_port_feature(hdev,				port + 1, USB_PORT_FEAT_C_RESET);			usb_set_device_state(udev, status					? USB_STATE_NOTATTACHED					: USB_STATE_DEFAULT);			return status;		}		dev_dbg (hub_dev,			"port %d not enabled, trying reset again...\n",			port + 1);		delay = HUB_LONG_RESET_TIME;	}	dev_err (hub_dev,		"Cannot enable port %i.  Maybe the USB cable is bad?\n",		port + 1);	return status;}static int hub_port_disable(struct usb_device *hdev, int port){	int ret;	if (hdev->children[port])		usb_set_device_state(hdev->children[port],				USB_STATE_NOTATTACHED);	ret = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_ENABLE);	if (ret)		dev_err(hubdev(hdev), "cannot disable port %d (err = %d)\n",			port + 1, ret);	return ret;}#ifdef	CONFIG_USB_SUSPEND	/* no USB_SUSPEND yet! */#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)			break;		msleep(HUB_DEBOUNCE_STEP);	}	dev_dbg (hubdev (hdev),		"debounce: port %d: total %dms stable %dms status 0x%x\n",		port + 1, total_time, stable_time, portstatus);	if (stable_time < HUB_DEBOUNCE_STABLE)		return -ETIMEDOUT;	return portstatus;}static int hub_set_address(struct usb_device *udev){	int retval;	if (udev->devnum == 0)		return -EINVAL;	if (udev->state != USB_STATE_DEFAULT &&			udev->state != USB_STATE_ADDRESS)		return -EINVAL;	retval = usb_control_msg(udev, (PIPE_CONTROL << 30) /* Address 0 */,		USB_REQ_SET_ADDRESS, 0, udev->devnum, 0,		NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);	if (retval == 0)		usb_set_device_state(udev, USB_STATE_ADDRESS);	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_device *hdev, struct usb_device *udev, int port){	static DECLARE_MUTEX(usb_address0_sem);	int			i, j, retval;	unsigned		delay = HUB_SHORT_RESET_TIME;	enum usb_device_speed	oldspeed = udev->speed;	/* 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 (port + 1 == 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;	down(&usb_address0_sem);	/* Reset the device; full speed may morph to high speed */	retval = hub_port_reset(hdev, port, 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;	}  	/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...	 * it's fixed size except for full speed devices.	 */	switch (udev->speed) {	case USB_SPEED_HIGH:		/* fixed at 64 */		i = 64;		break;	case USB_SPEED_FULL:		/* 8, 16, 32, or 64 */		/* to determine the ep0 maxpacket size, read the first 8		 * bytes from the device descriptor to get bMaxPacketSize0;		 * then correct our initial (small) guess.		 */		// FALLTHROUGH	case USB_SPEED_LOW:		/* fixed at 8 */		i = 8;		break;	default:		goto fail;	}	udev->epmaxpacketin [0] = i;	udev->epmaxpacketout[0] = i; 	dev_info (&udev->dev,			"%s %s speed USB device using address %d\n",			(udev->config) ? "reset" : "new",			({ char *speed; switch (udev->speed) {			case USB_SPEED_LOW:	speed = "low";	break;			case USB_SPEED_FULL:	speed = "full";	break;			case USB_SPEED_HIGH:	speed = "high";	break;			default: 		speed = "?";	break;			}; speed;}),			udev->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) {		struct usb_hub *hub;		hub = usb_get_intfdata(hdev->actconfig->interface[0]);		udev->tt = &hub->tt;		udev->ttport = port + 1;	} 	/* 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:  Windows gets the descriptor first, seemingly to help	 * work around device bugs like "can't use addresses with bit 3	 * set in certain configurations".  Yes, really.	 */	for (i = 0; i < GET_DESCRIPTOR_TRIES; ++i) {		for (j = 0; j < SET_ADDRESS_TRIES; ++j) {			retval = hub_set_address(udev);			if (retval >= 0)				break;			msleep(200);		}		if (retval < 0) {			dev_err(&udev->dev,				"device not accepting address %d, error %d\n",				udev->devnum, retval);			goto fail;		} 		/* cope with hardware quirkiness:		 *  - let SET_ADDRESS settle, some device hardware wants it		 *  - read ep0 maxpacket even for high and low speed,  		 */		msleep(10);		retval = usb_get_device_descriptor(udev, 8);		if (retval >= 8)			break;		msleep(100);	}	if (retval != 8) {		dev_err(&udev->dev, "device descriptor read/%s, error %d\n",				"8", retval);		if (retval >= 0)			retval = -EMSGSIZE;		goto fail;	}	if (udev->speed == USB_SPEED_FULL			&& (udev->epmaxpacketin [0]				!= udev->descriptor.bMaxPacketSize0)) {		usb_disable_endpoint(udev, 0 + USB_DIR_IN);		usb_disable_endpoint(udev, 0 + USB_DIR_OUT);		usb_endpoint_running(udev, 0, 1);		usb_endpoint_running(udev, 0, 0);		udev->epmaxpacketin [0] = udev->descriptor.bMaxPacketSize0;		udev->epmaxpacketout[0] = udev->descriptor.bMaxPacketSize0;	}  	retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);	if (retval < (signed)sizeof(udev->descriptor)) {		dev_err(&udev->dev, "device descriptor read/%s, error %d\n",			"all", retval);		if (retval >= 0)			retval = -ENOMSG;		goto fail;	}	retval = 0;fail:	up(&usb_address0_sem);	return retval;}static voidcheck_highspeed (struct usb_hub *hub, struct usb_device *udev, int port){	struct usb_qualifier_descriptor	*qual;	int				status;	qual = kmalloc (sizeof *qual, SLAB_KERNEL);	if (qual == 0)		return;	status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0,			qual, sizeof *qual);	if (status == sizeof *qual) {		dev_info(&udev->dev, "not running at top speed; "			"connect to a high speed hub\n");		/* hub LEDs are probably harder to miss than syslog */		if (hub->has_indicators) {			hub->indicator[port] = INDICATOR_GREEN_BLINK;			schedule_work (&hub->leds);		}	}	kfree (qual);}static unsignedhub_power_remaining (struct usb_hub *hub){	struct usb_device *hdev = hub->hdev;	int remaining;	unsigned i;	remaining = hub->power_budget;	if (!remaining)		/* self-powered */		return 0;	for (i = 0; i < hdev->maxchild; i++) {		struct usb_device	*udev = hdev->children[i];		int			delta, ceiling;		if (!udev)			continue;		/* 100mA per-port ceiling, or 8mA for OTG ports */		if (i != (udev->bus->otg_port - 1) || hdev->parent)			ceiling = 50;		else			ceiling = 4;		if (udev->actconfig)			delta = udev->actconfig->desc.bMaxPower;		else			delta = ceiling;		// dev_dbg(&udev->dev, "budgeted %dmA\n", 2 * delta);		if (delta > ceiling)			dev_warn(&udev->dev, "%dmA over %dmA budget!\n",				2 * (delta - ceiling), 2 * ceiling);		remaining -= delta;	}	if (remaining < 0) {		dev_warn(&hub->intf->dev,			"%dmA over power budget!\n",			-2 * remaining);		remaining = 0;	}	return remaining;}/* Handle physical or logical connection change events. * This routine is called when: * 	a port connection-change occurs; *	a port enable-change occurs (often caused by EMI); *	usb_reset_device() encounters changed descriptors (as from *		a firmware download) */static void hub_port_connect_change(struct usb_hub *hub, int port,					u16 portstatus, u16 portchange){	struct usb_device *hdev = hub->hdev;	struct device *hub_dev = &hub->intf->dev;	int status, i; 	dev_dbg (hub_dev,		"port %d, status %04x, change %04x, %s\n",		port + 1, portstatus, portchange, portspeed (portstatus));	if (hub->has_indicators) {		set_port_led(hdev, port + 1, HUB_LED_AUTO);		hub->indicator[port] = INDICATOR_AUTO;	} 	/* Disconnect any existing devices under this port */	if (hdev->children[port])		usb_disconnect(&hdev->children[port]);	clear_bit(port, hub->change_bits);	if (portchange & USB_PORT_STAT_C_CONNECTION) {		status = hub_port_debounce(hdev, port);		if (status < 0) {			dev_err (hub_dev,				"connect-debounce failed, port %d disabled\n",				port+1);			goto done;		}		portstatus = status;	}	/* Return now if nothing is connected */	if (!(portstatus & USB_PORT_STAT_CONNECTION)) {		/* maybe switch power back on (e.g. root hub was reset) */		if ((hub->descriptor->wHubCharacteristics					& HUB_CHAR_LPSM) < 2				&& !(portstatus & (1 << USB_PORT_FEAT_POWER)))			set_port_feature(hdev, port + 1, USB_PORT_FEAT_POWER); 		if (portstatus & USB_PORT_STAT_ENABLE)  			goto done;		return;	}	for (i = 0; i < SET_CONFIG_TRIES; i++) {		struct usb_device *udev;		/* reallocate for each attempt, since references		 * to the previous one can escape in various ways		 */

⌨️ 快捷键说明

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