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

📄 hub.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
int usb_new_device(struct usb_device *udev){	int err;	usb_detect_quirks(udev);		/* Determine quirks */	err = usb_configure_device(udev);	/* detect & probe dev/intfs */	if (err < 0)		goto fail;	/* export the usbdev device-node for libusb */	udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,			(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));	/* Increment the parent's count of unsuspended children */	if (udev->parent)		usb_autoresume_device(udev->parent);	/* Register the device.  The device driver is responsible	 * for adding the device files to sysfs and for configuring	 * the device.	 */	err = device_add(&udev->dev);	if (err) {		dev_err(&udev->dev, "can't device_add, error %d\n", err);		goto fail;	}	/* Tell the world! */	dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, "		"SerialNumber=%d\n",		udev->descriptor.iManufacturer,		udev->descriptor.iProduct,		udev->descriptor.iSerialNumber);	show_string(udev, "Product", udev->product);	show_string(udev, "Manufacturer", udev->manufacturer);	show_string(udev, "SerialNumber", udev->serial);	return err;fail:	usb_set_device_state(udev, USB_STATE_NOTATTACHED);	return err;}/** * usb_deauthorize_device - deauthorize a device (usbcore-internal) * @usb_dev: USB device * * Move the USB device to a very basic state where interfaces are disabled * and the device is in fact unconfigured and unusable. * * We share a lock (that we have) with device_del(), so we need to * defer its call. */int usb_deauthorize_device(struct usb_device *usb_dev){	unsigned cnt;	usb_lock_device(usb_dev);	if (usb_dev->authorized == 0)		goto out_unauthorized;	usb_dev->authorized = 0;	usb_set_configuration(usb_dev, -1);	usb_dev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);	usb_dev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);	usb_dev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);	kfree(usb_dev->config);	usb_dev->config = NULL;	for (cnt = 0; cnt < usb_dev->descriptor.bNumConfigurations; cnt++)		kfree(usb_dev->rawdescriptors[cnt]);	usb_dev->descriptor.bNumConfigurations = 0;	kfree(usb_dev->rawdescriptors);out_unauthorized:	usb_unlock_device(usb_dev);	return 0;}int usb_authorize_device(struct usb_device *usb_dev){	int result = 0, c;	usb_lock_device(usb_dev);	if (usb_dev->authorized == 1)		goto out_authorized;	kfree(usb_dev->product);	usb_dev->product = NULL;	kfree(usb_dev->manufacturer);	usb_dev->manufacturer = NULL;	kfree(usb_dev->serial);	usb_dev->serial = NULL;	result = usb_autoresume_device(usb_dev);	if (result < 0) {		dev_err(&usb_dev->dev,			"can't autoresume for authorization: %d\n", result);		goto error_autoresume;	}	result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor));	if (result < 0) {		dev_err(&usb_dev->dev, "can't re-read device descriptor for "			"authorization: %d\n", result);		goto error_device_descriptor;	}	usb_dev->authorized = 1;	result = usb_configure_device(usb_dev);	if (result < 0)		goto error_configure;	/* Choose and set the configuration.  This registers the interfaces	 * with the driver core and lets interface drivers bind to them.	 */	c = usb_choose_configuration(usb_dev);	if (c >= 0) {		result = usb_set_configuration(usb_dev, c);		if (result) {			dev_err(&usb_dev->dev,				"can't set config #%d, error %d\n", c, result);			/* This need not be fatal.  The user can try to			 * set other configurations. */		}	}	dev_info(&usb_dev->dev, "authorized to connect\n");error_configure:error_device_descriptor:error_autoresume:out_authorized:	usb_unlock_device(usb_dev);	// complements locktree	return result;}static int hub_port_status(struct usb_hub *hub, int port1,			       u16 *status, u16 *change){	int ret;	mutex_lock(&hub->status_mutex);	ret = get_port_status(hub->hdev, port1, &hub->status->port);	if (ret < 4) {		dev_err (hub->intfdev,			"%s failed (err = %d)\n", __FUNCTION__, ret);		if (ret >= 0)			ret = -EIO;	} else {		*status = le16_to_cpu(hub->status->port.wPortStatus);		*change = le16_to_cpu(hub->status->port.wPortChange); 		ret = 0;	}	mutex_unlock(&hub->status_mutex);	return ret;}/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */static unsigned hub_is_wusb(struct usb_hub *hub){	struct usb_hcd *hcd;	if (hub->hdev->parent != NULL)  /* not a root hub? */		return 0;	hcd = container_of(hub->hdev->bus, struct usb_hcd, self);	return hcd->wireless;}#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 the connection bounced */		if ((portchange & USB_PORT_STAT_C_CONNECTION))			return -ENOTCONN;		/* if we`ve finished resetting, then break out of the loop */		if (!(portstatus & USB_PORT_STAT_RESET) &&		    (portstatus & USB_PORT_STAT_ENABLE)) {			if (hub_is_wusb(hub))				udev->speed = USB_SPEED_VARIABLE;			else 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;	/* Block EHCI CF initialization during the port reset.	 * Some companion controllers don't like it when they mix.	 */	down_read(&ehci_cf_port_reset_rwsem);	/* 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);		  	udev->devnum = 0;	/* Device now at address 0 */			/* 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);			goto done;		}		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); done:	up_read(&ehci_cf_port_reset_rwsem);	return status;}#ifdef	CONFIG_PM#ifdef	CONFIG_USB_SUSPEND/* * usb_port_suspend - suspend a usb device's upstream port * @udev: device that's no longer in active use, not a root hub * 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_port_resume().  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. * * 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. * * 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. * * 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. * * Returns 0 on success, else negative errno. */int usb_port_suspend(struct usb_device *udev){	struct usb_hub	*hub = hdev_to_hub(udev->parent);	int		port1 = udev->portnum;	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->do_remote_wakeup) {		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 %ssuspend\n",				udev->auto_pm ? "auto-" : "");		usb_set_device_state(udev, USB_STATE_SUSPENDED);		msleep(10);	}	return status;}/* * 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.  This routine kicks 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. * * If @udev->reset_resume is set then the device is reset before the * status check is done. */static int finish_port_resume(struct usb_device *udev){	int	status = 0;	u16	devstatus;	/* caller owns the udev device lock */	dev_dbg(&udev->dev, "finish %sresume\n",			udev->reset_resume ? "reset-" : "");	/* 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 not to reset a suspended port if the attached	 * device is enabled for remote wakeup.  Hence the reset	 * operation is carried out here, after the port has been	 * resumed.	 */	if (udev->reset_resume)		status = usb_reset_device(udev); 	/* 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.	 */	if (status == 0) {		devstatus = 0;		status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);		if (status >= 0)			status = (status > 0 ? 0 : -ENODEV);	}	if (status) {		dev_dbg(&udev->dev, "gone after usb resume? status %d\n",				status);	} else if (udev->actconfig) {		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;	}	return status;}/* * usb_port_resume - re-activate a suspended usb device's upstream port * @udev: device to re-activate, not a root hub * 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.

⌨️ 快捷键说明

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