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

📄 hub.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 5 页
字号:
	/* 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;	/* 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, pm_message_t state){	struct usb_hub		*hub = usb_get_intfdata (intf);	struct usb_device	*hdev = hub->hdev;	unsigned		port1;	int			status;	/* stop khubd and related activity */	hub_quiesce(hub);	/* then suspend every port */	for (port1 = 1; port1 <= hdev->maxchild; port1++) {		struct usb_device	*udev;		udev = hdev->children [port1-1];		if (!udev)			continue;		down(&udev->serialize);		status = __usb_suspend_device(udev, port1, state);		up(&udev->serialize);		if (status < 0)			dev_dbg(&intf->dev, "suspend port %d --> %d\n",				port1, 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		port1;	int			status;	if (intf->dev.power.power_state.event == PM_EVENT_ON)		return 0;	for (port1 = 1; port1 <= hdev->maxchild; port1++) {		struct usb_device	*udev;		u16			portstat, portchange;		udev = hdev->children [port1-1];		status = hub_port_status(hub, port1, &portstat, &portchange);		if (status == 0) {			if (portchange & USB_PORT_STAT_C_SUSPEND) {				clear_port_feature(hdev, port1,					USB_PORT_FEAT_C_SUSPEND);				portchange &= ~USB_PORT_STAT_C_SUSPEND;			}			/* let khubd handle disconnects etc */			if (portchange)				continue;		}		if (!udev || status < 0)			continue;		down (&udev->serialize);		if (portstat & USB_PORT_STAT_SUSPEND)			status = hub_port_resume(hub, port1, udev);		else {			status = finish_port_resume(udev);			if (status < 0) {				dev_dbg(&intf->dev, "resume port %d --> %d\n",					port1, status);				hub_port_logical_disconnect(hub, port1);			}		}		up(&udev->serialize);	}	intf->dev.power.power_state = PMSG_ON;	hub->resume_root_hub = 0;	hub_activate(hub);	return 0;}void usb_resume_root_hub(struct usb_device *hdev){	struct usb_hub *hub = hdev_to_hub(hdev);	hub->resume_root_hub = 1;	kick_khubd(hub);}#else	/* !CONFIG_USB_SUSPEND */int usb_suspend_device(struct usb_device *udev, pm_message_t 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_hub *hub, int port1){	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(hub, port1, &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(hub->hdev, port1,					USB_PORT_FEAT_C_CONNECTION);		}		if (total_time >= HUB_DEBOUNCE_TIMEOUT)			break;		msleep(HUB_DEBOUNCE_STEP);	}	dev_dbg (hub->intfdev,		"debounce: port %d: total %dms stable %dms status 0x%x\n",		port1, total_time, stable_time, portstatus);	if (stable_time < HUB_DEBOUNCE_STABLE)		return -ETIMEDOUT;	return portstatus;}static void ep0_reinit(struct usb_device *udev){	usb_disable_endpoint(udev, 0 + USB_DIR_IN);	usb_disable_endpoint(udev, 0 + USB_DIR_OUT);	udev->ep_in[0] = udev->ep_out[0] = &udev->ep0;}#define usb_sndaddr0pipe()	(PIPE_CONTROL << 30)#define usb_rcvaddr0pipe()	((PIPE_CONTROL << 30) | USB_DIR_IN)static int hub_set_address(struct usb_device *udev){	int retval;	if (udev->devnum == 0)		return -EINVAL;	if (udev->state == USB_STATE_ADDRESS)		return 0;	if (udev->state != USB_STATE_DEFAULT)		return -EINVAL;	retval = usb_control_msg(udev, usb_sndaddr0pipe(),		USB_REQ_SET_ADDRESS, 0, udev->devnum, 0,		NULL, 0, USB_CTRL_SET_TIMEOUT);	if (retval == 0) {		usb_set_device_state(udev, USB_STATE_ADDRESS);		ep0_reinit(udev);	}	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_hub *hub, struct usb_device *udev, int port1,		int retry_counter){	static DECLARE_MUTEX(usb_address0_sem);	struct usb_device	*hdev = hub->hdev;	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 (port1 == 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(hub, port1, 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;	}	oldspeed = udev->speed;  	/* 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 */		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);		break;	case USB_SPEED_FULL:		/* 8, 16, 32, or 64 */		/* to determine the ep0 maxpacket size, try to read		 * the device descriptor to get bMaxPacketSize0 and		 * then correct our initial guess.		 */		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);		break;	case USB_SPEED_LOW:		/* fixed at 8 */		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(8);		break;	default:		goto fail;	} 	dev_info (&udev->dev,			"%s %s speed USB device using %s and 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->bus->controller->driver->name,			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) {		udev->tt = &hub->tt;		udev->ttport = port1;	} 	/* 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:  If USE_NEW_SCHEME() is true we will start by issuing	 * a 64-byte GET_DESCRIPTOR request.  This is what Windows does,	 * so it may help with some non-standards-compliant devices.	 * Otherwise we start with SET_ADDRESS and then try to read the	 * first 8 bytes of the device descriptor to get the ep0 maxpacket	 * value.	 */	for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {		if (USE_NEW_SCHEME(retry_counter)) {			struct usb_device_descriptor *buf;			int r = 0;#define GET_DESCRIPTOR_BUFSIZE	64			buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);			if (!buf) {				retval = -ENOMEM;				continue;			}			/* Use a short timeout the first time through,			 * so that recalcitrant full-speed devices with			 * 8- or 16-byte ep0-maxpackets won't slow things			 * down tremendously by NAKing the unexpectedly			 * early status stage.  Also, retry on all errors;			 * some devices are flakey.			 */			for (j = 0; j < 3; ++j) {				buf->bMaxPacketSize0 = 0;				r = usb_control_msg(udev, usb_rcvaddr0pipe(),					USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,					USB_DT_DEVICE << 8, 0,					buf, GET_DESCRIPTOR_BUFSIZE,					(i ? USB_CTRL_GET_TIMEOUT : 1000));				switch (buf->bMaxPacketSize0) {				case 8: case 16: case 32: case 64:					if (buf->bDescriptorType ==							USB_DT_DEVICE) {						r = 0;						break;					}					/* FALL THROUGH */				default:					if (r == 0)						r = -EPROTO;					break;				}				if (r == 0)					break;			}			udev->descriptor.bMaxPacketSize0 =					buf->bMaxPacketSize0;			kfree(buf);			retval = hub_port_reset(hub, port1, udev, delay);			if (retval < 0)		/* error or disconnect */				goto fail;			if (oldspeed != udev->speed) {				dev_dbg(&udev->dev,					"device reset changed speed!\n");				retval = -ENODEV;				goto fail;			}			if (r) {				dev_err(&udev->dev, "device descriptor "						"read/%s, error %d\n",						"64", r);				retval = -EMSGSIZE;				continue;			}#undef GET_DESCRIPTOR_BUFSIZE		}		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);		if (USE_NEW_SCHEME(retry_counter))			break;		retval = usb_get_device_descriptor(udev, 8);		if (retval < 8) {			dev_err(&udev->dev, "device descriptor "					"read/%s, error %d\n",					"8", retval);			if (retval >= 0)				retval = -EMSGSIZE;		} else {			retval = 0;			break;		}	}	if (retval)		goto fail;	i = udev->descriptor.bMaxPacketSize0;	if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {		if (udev->speed != USB_SPEED_FULL ||				!(i == 8 || i == 16 || i == 32 || i == 64)) {			dev_err(&udev->dev, "ep0 maxpacket = %d\n", i);			retval = -EMSGSIZE;			goto fail;		}		dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);		udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);		ep0_reinit(udev);	}  	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:	if (retval)		hub_port_disable(hub, port1, 0);	up(&usb_address0_sem);	return retval;}static voidcheck_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1){	struct usb_qualifier_descriptor	*qual;	int				status;	qual = kmalloc (sizeof *qual, SLAB_KERNEL);	if (qual == NULL)		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 L

⌨️ 快捷键说明

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