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

📄 hub.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
		udev = usb_alloc_dev(hdev, hdev->bus, port);		if (!udev) {			dev_err (hub_dev,				"couldn't allocate port %d usb_device\n", port+1);			goto done;		}		usb_set_device_state(udev, USB_STATE_POWERED);		udev->speed = USB_SPEED_UNKNOWN; 		/* set the address */		choose_address(udev);		if (udev->devnum <= 0) {			status = -ENOTCONN;	/* Don't retry */			goto loop;		}		/* reset and get descriptor */		status = hub_port_init(hdev, udev, port);		if (status < 0)			goto loop;		/* consecutive bus-powered hubs aren't reliable; they can		 * violate the voltage drop budget.  if the new child has		 * a "powered" LED, users should notice we didn't enable it		 * (without reading syslog), even without per-port LEDs		 * on the parent.		 */		if (udev->descriptor.bDeviceClass == USB_CLASS_HUB				&& hub->power_budget) {			u16	devstat;			status = usb_get_status(udev, USB_RECIP_DEVICE, 0,					&devstat);			if (status < 0) {				dev_dbg(&udev->dev, "get status %d ?\n", status);				goto loop;			}			cpu_to_le16s(&devstat);			if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {				dev_err(&udev->dev,					"can't connect bus-powered hub "					"to this port\n");				if (hub->has_indicators) {					hub->indicator[port] =						INDICATOR_AMBER_BLINK;					schedule_work (&hub->leds);				}				status = -ENOTCONN;	/* Don't retry */				goto loop;			}		} 		/* check for devices running slower than they could */		if (udev->descriptor.bcdUSB >= 0x0200				&& udev->speed == USB_SPEED_FULL				&& highspeed_hubs != 0)			check_highspeed (hub, udev, port);		/* Store the parent's children[] pointer.  At this point		 * udev becomes globally accessible, although presumably		 * no one will look at it until hdev is unlocked.		 */		down (&udev->serialize);		status = 0;		/* We mustn't add new devices if the parent hub has		 * been disconnected; we would race with the		 * recursively_mark_NOTATTACHED() routine.		 */		spin_lock_irq(&device_state_lock);		if (hdev->state == USB_STATE_NOTATTACHED)			status = -ENOTCONN;		else			hdev->children[port] = udev;		spin_unlock_irq(&device_state_lock);		/* Run it through the hoops (find a driver, etc) */		if (!status) {			status = usb_new_device(udev);			if (status) {				spin_lock_irq(&device_state_lock);				hdev->children[port] = NULL;				spin_unlock_irq(&device_state_lock);			}		}		up (&udev->serialize);		if (status)			goto loop;		status = hub_power_remaining(hub);		if (status)			dev_dbg(hub_dev,				"%dmA power budget left\n",				2 * status);		return;loop:		hub_port_disable(hdev, port);		usb_disable_endpoint(udev, 0 + USB_DIR_IN);		usb_disable_endpoint(udev, 0 + USB_DIR_OUT);		release_address(udev);		usb_put_dev(udev);		if (status == -ENOTCONN)			break;	} done:	hub_port_disable(hdev, port);}static void hub_events(void){	struct list_head *tmp;	struct usb_device *hdev;	struct usb_hub *hub;	struct device *hub_dev;	u16 hubstatus;	u16 hubchange;	u16 portstatus;	u16 portchange;	int i, ret;	int connect_change;	/*	 *  We restart the list every time to avoid a deadlock with	 * deleting hubs downstream from this one. This should be	 * safe since we delete the hub from the event list.	 * Not the most efficient, but avoids deadlocks.	 */	while (1) {		/* Grab the first entry at the beginning of the list */		spin_lock_irq(&hub_event_lock);		if (list_empty(&hub_event_list)) {			spin_unlock_irq(&hub_event_lock);			break;		}		tmp = hub_event_list.next;		list_del_init(tmp);		hub = list_entry(tmp, struct usb_hub, event_list);		hdev = hub->hdev;		hub_dev = &hub->intf->dev;		usb_get_dev(hdev);		spin_unlock_irq(&hub_event_lock);		/* Lock the device, then check to see if we were		 * disconnected while waiting for the lock to succeed. */		down(&hdev->serialize);		if (hdev->state != USB_STATE_CONFIGURED ||				!hdev->actconfig ||				hub != usb_get_intfdata(					hdev->actconfig->interface[0]))			goto loop;		if (hub->error) {			dev_dbg (hub_dev, "resetting for error %d\n",				hub->error);			if (hub_reset(hub)) {				dev_dbg (hub_dev,					"can't reset; disconnecting\n");				hub_start_disconnect(hdev);				goto loop;			}			hub->nerrors = 0;			hub->error = 0;		}		/* deal with port status changes */		for (i = 0; i < hub->descriptor->bNbrPorts; i++) {			connect_change = test_bit(i, hub->change_bits);			if (!test_and_clear_bit(i+1, hub->event_bits) &&					!connect_change)				continue;			ret = hub_port_status(hdev, i,					&portstatus, &portchange);			if (ret < 0)				continue;			if (portchange & USB_PORT_STAT_C_CONNECTION) {				clear_port_feature(hdev,					i + 1, USB_PORT_FEAT_C_CONNECTION);				connect_change = 1;			}			if (portchange & USB_PORT_STAT_C_ENABLE) {				if (!connect_change)					dev_dbg (hub_dev,						"port %d enable change, "						"status %08x\n",						i + 1, portstatus);				clear_port_feature(hdev,					i + 1, USB_PORT_FEAT_C_ENABLE);				/*				 * EM interference sometimes causes badly				 * shielded USB devices to be shutdown by				 * the hub, this hack enables them again.				 * Works at least with mouse driver. 				 */				if (!(portstatus & USB_PORT_STAT_ENABLE)				    && !connect_change				    && hdev->children[i]) {					dev_err (hub_dev,					    "port %i "					    "disabled by hub (EMI?), "					    "re-enabling...\n",						i + 1);					connect_change = 1;				}			}			if (portchange & USB_PORT_STAT_C_SUSPEND) {				clear_port_feature(hdev, i + 1,					USB_PORT_FEAT_C_SUSPEND);				if (hdev->children[i])					ret = remote_wakeup(hdev->children[i]);				else					ret = -ENODEV;				dev_dbg (hub_dev,					"resume on port %d, status %d\n",					i + 1, ret);				if (ret < 0)					ret = hub_port_disable(hdev, i);			}						if (portchange & USB_PORT_STAT_C_OVERCURRENT) {				dev_err (hub_dev,					"over-current change on port %d\n",					i + 1);				clear_port_feature(hdev,					i + 1, USB_PORT_FEAT_C_OVER_CURRENT);				hub_power_on(hub);			}			if (portchange & USB_PORT_STAT_C_RESET) {				dev_dbg (hub_dev,					"reset change on port %d\n",					i + 1);				clear_port_feature(hdev,					i + 1, USB_PORT_FEAT_C_RESET);			}			if (connect_change)				hub_port_connect_change(hub, i,						portstatus, portchange);		} /* end for i */		/* deal with hub status changes */		if (test_and_clear_bit(0, hub->event_bits) == 0)			;	/* do nothing */		else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)			dev_err (hub_dev, "get_hub_status failed\n");		else {			if (hubchange & HUB_CHANGE_LOCAL_POWER) {				dev_dbg (hub_dev, "power change\n");				clear_hub_feature(hdev, C_HUB_LOCAL_POWER);			}			if (hubchange & HUB_CHANGE_OVERCURRENT) {				dev_dbg (hub_dev, "overcurrent change\n");				msleep(500);	/* Cool down */				clear_hub_feature(hdev, C_HUB_OVER_CURRENT);                        	hub_power_on(hub);			}		}loop:		up(&hdev->serialize);		usb_put_dev(hdev);        } /* end while (1) */}static int hub_thread(void *__unused){	/*	 * This thread doesn't need any user-level access,	 * so get rid of all our resources	 */	daemonize("khubd");	allow_signal(SIGKILL);	/* Send me a signal to get me die (for debugging) */	do {		hub_events();		wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list)); 		if (current->flags & PF_FREEZE)			refrigerator(PF_FREEZE);	} while (!signal_pending(current));	pr_debug ("%s: khubd exiting\n", usbcore_name);	complete_and_exit(&khubd_exited, 0);}static struct usb_device_id hub_id_table [] = {    { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,      .bDeviceClass = USB_CLASS_HUB},    { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,      .bInterfaceClass = USB_CLASS_HUB},    { }						/* Terminating entry */};MODULE_DEVICE_TABLE (usb, hub_id_table);static struct usb_driver hub_driver = {	.owner =	THIS_MODULE,	.name =		"hub",	.probe =	hub_probe,	.disconnect =	hub_disconnect,	.suspend =	hub_suspend,	.resume =	hub_resume,	.ioctl =	hub_ioctl,	.id_table =	hub_id_table,};int usb_hub_init(void){	pid_t pid;	if (usb_register(&hub_driver) < 0) {		printk(KERN_ERR "%s: can't register hub driver\n",			usbcore_name);		return -1;	}	pid = kernel_thread(hub_thread, NULL, CLONE_KERNEL);	if (pid >= 0) {		khubd_pid = pid;		return 0;	}	/* Fall through if kernel_thread failed */	usb_deregister(&hub_driver);	printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);	return -1;}void usb_hub_cleanup(void){	int ret;	/* Kill the thread */	ret = kill_proc(khubd_pid, SIGKILL, 1);	wait_for_completion(&khubd_exited);	/*	 * Hub resources are freed for us by usb_deregister. It calls	 * usb_driver_purge on every device which in turn calls that	 * devices disconnect function if it is using this driver.	 * The hub_disconnect function takes care of releasing the	 * individual hub resources. -greg	 */	usb_deregister(&hub_driver);} /* usb_hub_cleanup() */static int config_descriptors_changed(struct usb_device *udev){	unsigned			index;	unsigned			len = 0;	struct usb_config_descriptor	*buf;	for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {		if (len < udev->config[index].desc.wTotalLength)			len = udev->config[index].desc.wTotalLength;	}	buf = kmalloc (len, SLAB_KERNEL);	if (buf == 0) {		dev_err(&udev->dev, "no mem to re-read configs after reset\n");		/* assume the worst */		return 1;	}	for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {		int length;		int old_length = udev->config[index].desc.wTotalLength;		length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf,				old_length);		if (length < old_length) {			dev_dbg(&udev->dev, "config index %d, error %d\n",					index, length);			break;		}		if (memcmp (buf, udev->rawdescriptors[index], old_length)				!= 0) {			dev_dbg(&udev->dev, "config index %d changed (#%d)\n",				index, buf->bConfigurationValue);			break;		}	}	kfree(buf);	return index != udev->descriptor.bNumConfigurations;}/** * usb_reset_devce - perform a USB port reset to reinitialize a device * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) * * WARNING - don't reset any device unless drivers for all of its * interfaces are expecting that reset!  Maybe some driver->reset() * method should eventually help ensure sufficient cooperation. * * Do a port reset, reassign the device's address, and establish its * former operating configuration.  If the reset fails, or the device's * descriptors change from their values before the reset, or the original * configuration and altsettings cannot be restored, a flag will be set * telling khubd to pretend the device has been disconnected and then * re-connected.  All drivers will be unbound, and the device will be * re-enumerated and probed all over again. * * Returns 0 if the reset succeeded, -ENODEV if the device has been * flagged for logical disconnection, or some other negative error code * if the reset wasn't even attempted. * * The caller must own the device lock.  For example, it's safe to use * this from a driver probe() routine after downloading new firmware. */int __usb_reset_device(struct usb_device *udev){	struct usb_device *parent = udev->parent;	struct usb_device_descriptor descriptor = udev->descriptor;	int i, ret, port = -1;	struct usb_hub *hub;	if (udev->state == USB_STATE_NOTATTACHED ||			udev->state == USB_STATE_SUSPENDED) {		dev_dbg(&udev->dev, "device reset not allowed in state %d\n",				udev->state);		return -EINVAL;	}	/* FIXME: This should be legal for regular hubs.  Root hubs may	 * have special requirements. */	if (udev->maxchild) {		/* this requires hub- or hcd-specific logic;		 * see hub_reset() and OHCI hc_restart()		 */		dev_dbg(&udev->dev, "%s for hub!\n", __FUNCTION__);		return -EISDIR;	}	for (i = 0; i < parent->maxchild; i++)		if (parent->children[i] == udev) {			port = i;			break;		}	if (port < 0) {		/* If this ever happens, it's very bad */		dev_err(&udev->dev, "Can't locate device's port!\n");		return -ENOENT;	}	ret = hub_port_init(parent, udev, port);	if (ret < 0)		goto re_enumerate; 	/* Device might have changed firmware (DFU or similar) */	if (memcmp(&udev->descriptor, &descriptor, sizeof descriptor)			|| config_descriptors_changed (udev)) {		dev_info(&udev->dev, "device firmware changed\n");		udev->descriptor = descriptor;	/* for disconnect() calls */		goto re_enumerate;  	}  	if (!udev->actconfig)		return 0;	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),			USB_REQ_SET_CONFIGURATION, 0,			udev->actconfig->desc.bConfigurationValue, 0,			NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);	if (ret < 0) {		dev_err(&udev->dev,			"can't restore configuration #%d (error=%d)\n",			udev->actconfig->desc.bConfigurationValue, ret);		goto re_enumerate;  	}	usb_set_device_state(udev, USB_STATE_CONFIGURED);	for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {		struct usb_interface *intf = udev->actconfig->interface[i];		struct usb_interface_descriptor *desc;		/* set_interface resets host side toggle and halt status even		 * for altsetting zero.  the interface may have no driver.		 */		desc = &intf->cur_altsetting->desc;		ret = usb_set_interface(udev, desc->bInterfaceNumber,			desc->bAlternateSetting);		if (ret < 0) {			dev_err(&udev->dev, "failed to restore interface %d "				"altsetting %d (error=%d)\n",				desc->bInterfaceNumber,				desc->bAlternateSetting,				ret);			goto re_enumerate;		}	}	return 0; re_enumerate:	hub_port_disable(parent, port);	hub = usb_get_intfdata(parent->actconfig->interface[0]);	set_bit(port, hub->change_bits);	spin_lock_irq(&hub_event_lock);	if (list_empty(&hub->event_list)) {		list_add_tail(&hub->event_list, &hub_event_list);		wake_up(&khubd_wait);	}	spin_unlock_irq(&hub_event_lock);	return -ENODEV;}EXPORT_SYMBOL(__usb_reset_device);int usb_reset_device(struct usb_device *udev){	int r;		down(&udev->serialize);	r = __usb_reset_device(udev);	up(&udev->serialize);	return r;}

⌨️ 快捷键说明

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