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

📄 hub.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * USB hub driver. * * (C) Copyright 1999 Linus Torvalds * (C) Copyright 1999 Johannes Erdfelt * (C) Copyright 1999 Gregory P. Smith * (C) Copyright 2001 Brad Hards (bhards@bigpond.net.au) * */#include <linux/config.h>#ifdef CONFIG_USB_DEBUG	#define DEBUG#else	#undef DEBUG#endif#include <linux/kernel.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/completion.h>#include <linux/sched.h>#include <linux/list.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/ioctl.h>#include <linux/usb.h>#include <linux/usbdevice_fs.h>#include <linux/suspend.h>#include <asm/semaphore.h>#include <asm/uaccess.h>#include <asm/byteorder.h>#include "usb.h"#include "hcd.h"#include "hub.h"/* Protect all struct usb_device state members */static spinlock_t device_state_lock = SPIN_LOCK_UNLOCKED;/* Wakes up khubd */static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;static LIST_HEAD(hub_event_list);	/* List of hubs needing servicing */static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);static pid_t khubd_pid = 0;			/* PID of khubd */static DECLARE_COMPLETION(khubd_exited);/* cycle leds on hubs that aren't blinking for attention */static int blinkenlights = 0;module_param (blinkenlights, bool, S_IRUGO);MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");#ifdef	DEBUGstatic inline char *portspeed (int portstatus){	if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))    		return "480 Mb/s";	else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))		return "1.5 Mb/s";	else		return "12 Mb/s";}#endif/* for dev_info, dev_dbg, etc */static inline struct device *hubdev (struct usb_device *hdev){	return &hdev->actconfig->interface[0]->dev;}/* USB 2.0 spec Section 11.24.4.5 */static int get_hub_descriptor(struct usb_device *hdev, void *data, int size){	return usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),		USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,		USB_DT_HUB << 8, 0, data, size, HZ * USB_CTRL_GET_TIMEOUT);}/* * USB 2.0 spec Section 11.24.2.1 */static int clear_hub_feature(struct usb_device *hdev, int feature){	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),		USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, HZ);}/* * USB 2.0 spec Section 11.24.2.2 */static int clear_port_feature(struct usb_device *hdev, int port, int feature){	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),		USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);}/* * USB 2.0 spec Section 11.24.2.13 */static int set_port_feature(struct usb_device *hdev, int port, int feature){	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),		USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);}/* * USB 2.0 spec Section 11.24.2.7.1.10 and table 11-7 * for info about using port indicators */static void set_port_led(	struct usb_device *hdev,	int port,	int selector){	int status = set_port_feature(hdev, (selector << 8) | port,			USB_PORT_FEAT_INDICATOR);	if (status < 0)		dev_dbg (hubdev (hdev),			"port %d indicator %s status %d\n",			port,			({ char *s; switch (selector) {			case HUB_LED_AMBER: s = "amber"; break;			case HUB_LED_GREEN: s = "green"; break;			case HUB_LED_OFF: s = "off"; break;			case HUB_LED_AUTO: s = "auto"; break;			default: s = "??"; break;			}; s; }),			status);}#define	LED_CYCLE_PERIOD	((2*HZ)/3)static void led_work (void *__hub){	struct usb_hub		*hub = __hub;	struct usb_device	*hdev = hub->hdev;	unsigned		i;	unsigned		changed = 0;	int			cursor = -1;	if (hdev->state != USB_STATE_CONFIGURED)		return;	for (i = 0; i < hub->descriptor->bNbrPorts; i++) {		unsigned	selector, mode;		/* 30%-50% duty cycle */		switch (hub->indicator[i]) {		/* cycle marker */		case INDICATOR_CYCLE:			cursor = i;			selector = HUB_LED_AUTO;			mode = INDICATOR_AUTO;			break;		/* blinking green = sw attention */		case INDICATOR_GREEN_BLINK:			selector = HUB_LED_GREEN;			mode = INDICATOR_GREEN_BLINK_OFF;			break;		case INDICATOR_GREEN_BLINK_OFF:			selector = HUB_LED_OFF;			mode = INDICATOR_GREEN_BLINK;			break;		/* blinking amber = hw attention */		case INDICATOR_AMBER_BLINK:			selector = HUB_LED_AMBER;			mode = INDICATOR_AMBER_BLINK_OFF;			break;		case INDICATOR_AMBER_BLINK_OFF:			selector = HUB_LED_OFF;			mode = INDICATOR_AMBER_BLINK;			break;		/* blink green/amber = reserved */		case INDICATOR_ALT_BLINK:			selector = HUB_LED_GREEN;			mode = INDICATOR_ALT_BLINK_OFF;			break;		case INDICATOR_ALT_BLINK_OFF:			selector = HUB_LED_AMBER;			mode = INDICATOR_ALT_BLINK;			break;		default:			continue;		}		if (selector != HUB_LED_AUTO)			changed = 1;		set_port_led(hdev, i + 1, selector);		hub->indicator[i] = mode;	}	if (!changed && blinkenlights) {		cursor++;		cursor %= hub->descriptor->bNbrPorts;		set_port_led(hdev, cursor + 1, HUB_LED_GREEN);		hub->indicator[cursor] = INDICATOR_CYCLE;		changed++;	}	if (changed)		schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);}/* * USB 2.0 spec Section 11.24.2.6 */static int get_hub_status(struct usb_device *hdev,		struct usb_hub_status *data){	return usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),		USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,		data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT);}/* * USB 2.0 spec Section 11.24.2.7 */static int get_port_status(struct usb_device *hdev, int port,		struct usb_port_status *data){	return usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),		USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port,		data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT);}/* completion function, fires on port status changes and various faults */static void hub_irq(struct urb *urb, struct pt_regs *regs){	struct usb_hub *hub = (struct usb_hub *)urb->context;	int status;	int i;	unsigned long bits;	switch (urb->status) {	case -ENOENT:		/* synchronous unlink */	case -ECONNRESET:	/* async unlink */	case -ESHUTDOWN:	/* hardware going away */		return;	default:		/* presumably an error */		/* Cause a hub reset after 10 consecutive errors */		dev_dbg (&hub->intf->dev, "transfer --> %d\n", urb->status);		if ((++hub->nerrors < 10) || hub->error)			goto resubmit;		hub->error = urb->status;		/* FALL THROUGH */		/* let khubd handle things */	case 0:			/* we got data:  port status changed */		bits = 0;		for (i = 0; i < urb->actual_length; ++i)			bits |= ((unsigned long) ((*hub->buffer)[i]))					<< (i*8);		hub->event_bits[0] = bits;		break;	}	hub->nerrors = 0;	/* Something happened, let khubd figure it out */	spin_lock(&hub_event_lock);	if (list_empty(&hub->event_list)) {		list_add_tail(&hub->event_list, &hub_event_list);		wake_up(&khubd_wait);	}	spin_unlock(&hub_event_lock);resubmit:	if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0			&& status != -ENODEV && status != -EPERM)		dev_err (&hub->intf->dev, "resubmit --> %d\n", status);}/* USB 2.0 spec Section 11.24.2.3 */static inline inthub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt){	return usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),			       HUB_CLEAR_TT_BUFFER, USB_RT_PORT, devinfo,			       tt, NULL, 0, HZ);}/* * enumeration blocks khubd for a long time. we use keventd instead, since * long blocking there is the exception, not the rule.  accordingly, HCDs * talking to TTs must queue control transfers (not just bulk and iso), so * both can talk to the same hub concurrently. */static void hub_tt_kevent (void *arg){	struct usb_hub		*hub = arg;	unsigned long		flags;	spin_lock_irqsave (&hub->tt.lock, flags);	while (!list_empty (&hub->tt.clear_list)) {		struct list_head	*temp;		struct usb_tt_clear	*clear;		struct usb_device	*hdev = hub->hdev;		int			status;		temp = hub->tt.clear_list.next;		clear = list_entry (temp, struct usb_tt_clear, clear_list);		list_del (&clear->clear_list);		/* drop lock so HCD can concurrently report other TT errors */		spin_unlock_irqrestore (&hub->tt.lock, flags);		status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt);		spin_lock_irqsave (&hub->tt.lock, flags);		if (status)			dev_err (&hdev->dev,				"clear tt %d (%04x) error %d\n",				clear->tt, clear->devinfo, status);		kfree (clear);	}	spin_unlock_irqrestore (&hub->tt.lock, flags);}/** * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub * @dev: the device whose split transaction failed * @pipe: identifies the endpoint of the failed transaction * * High speed HCDs use this to tell the hub driver that some split control or * bulk transaction failed in a way that requires clearing internal state of * a transaction translator.  This is normally detected (and reported) from * interrupt context. * * It may not be possible for that hub to handle additional full (or low) * speed transactions until that state is fully cleared out. */void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe){	struct usb_tt		*tt = udev->tt;	unsigned long		flags;	struct usb_tt_clear	*clear;	/* we've got to cope with an arbitrary number of pending TT clears,	 * since each TT has "at least two" buffers that can need it (and	 * there can be many TTs per hub).  even if they're uncommon.	 */	if ((clear = kmalloc (sizeof *clear, SLAB_ATOMIC)) == 0) {		dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n");		/* FIXME recover somehow ... RESET_TT? */		return;	}	/* info that CLEAR_TT_BUFFER needs */	clear->tt = tt->multi ? udev->ttport : 1;	clear->devinfo = usb_pipeendpoint (pipe);	clear->devinfo |= udev->devnum << 4;	clear->devinfo |= usb_pipecontrol (pipe)			? (USB_ENDPOINT_XFER_CONTROL << 11)			: (USB_ENDPOINT_XFER_BULK << 11);	if (usb_pipein (pipe))		clear->devinfo |= 1 << 15;		/* tell keventd to clear state for this TT */	spin_lock_irqsave (&tt->lock, flags);	list_add_tail (&clear->clear_list, &tt->clear_list);	schedule_work (&tt->kevent);	spin_unlock_irqrestore (&tt->lock, flags);}static void hub_power_on(struct usb_hub *hub){	int i;	/* if hub supports power switching, enable power on each port */	if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) < 2) {		dev_dbg(&hub->intf->dev, "enabling power on all ports\n");		for (i = 0; i < hub->descriptor->bNbrPorts; i++)			set_port_feature(hub->hdev, i + 1,					USB_PORT_FEAT_POWER);	}	/* Wait for power to be enabled */	msleep(hub->descriptor->bPwrOn2PwrGood * 2);}static int hub_hub_status(struct usb_hub *hub,		u16 *status, u16 *change){	int ret;	ret = get_hub_status(hub->hdev, &hub->status->hub);	if (ret < 0)		dev_err (&hub->intf->dev,			"%s failed (err = %d)\n", __FUNCTION__, ret);	else {		*status = le16_to_cpu(hub->status->hub.wHubStatus);		*change = le16_to_cpu(hub->status->hub.wHubChange); 		ret = 0;	}	return ret;}static int hub_configure(struct usb_hub *hub,	struct usb_endpoint_descriptor *endpoint){	struct usb_device *hdev = hub->hdev;	struct device *hub_dev = &hub->intf->dev;	u16 hubstatus, hubchange;	unsigned int pipe;	int maxp, ret;	char *message;	hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,			&hub->buffer_dma);	if (!hub->buffer) {		message = "can't allocate hub irq buffer";		ret = -ENOMEM;		goto fail;	}	hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);	if (!hub->status) {		message = "can't kmalloc hub status buffer";		ret = -ENOMEM;		goto fail;	}	hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);	if (!hub->descriptor) {		message = "can't kmalloc hub descriptor";		ret = -ENOMEM;		goto fail;	}	/* Request the entire hub descriptor.	 * hub->descriptor can handle USB_MAXCHILDREN ports,	 * but the hub can/will return fewer bytes here.	 */	ret = get_hub_descriptor(hdev, hub->descriptor,			sizeof(*hub->descriptor));	if (ret < 0) {		message = "can't read hub descriptor";		goto fail;	} else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {		message = "hub has too many ports!";		ret = -ENODEV;		goto fail;	}	hdev->maxchild = hub->descriptor->bNbrPorts;	dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,		(hdev->maxchild == 1) ? "" : "s");	le16_to_cpus(&hub->descriptor->wHubCharacteristics);	if (hub->descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) {		int	i;		char	portstr [USB_MAXCHILDREN + 1];		for (i = 0; i < hdev->maxchild; i++)			portstr[i] = hub->descriptor->DeviceRemovable				    [((i + 1) / 8)] & (1 << ((i + 1) % 8))				? 'F' : 'R';		portstr[hdev->maxchild] = 0;		dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);	} else		dev_dbg(hub_dev, "standalone hub\n");	switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) {		case 0x00:			dev_dbg(hub_dev, "ganged power switching\n");			break;		case 0x01:			dev_dbg(hub_dev, "individual port power switching\n");			break;		case 0x02:		case 0x03:			dev_dbg(hub_dev, "no power switching (usb 1.0)\n");			break;	}	switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) {		case 0x00:			dev_dbg(hub_dev, "global over-current protection\n");			break;		case 0x08:			dev_dbg(hub_dev, "individual port over-current protection\n");			break;		case 0x10:		case 0x18:			dev_dbg(hub_dev, "no over-current protection\n");                        break;	}	spin_lock_init (&hub->tt.lock);	INIT_LIST_HEAD (&hub->tt.clear_list);	INIT_WORK (&hub->tt.kevent, hub_tt_kevent, hub);	switch (hdev->descriptor.bDeviceProtocol) {		case 0:			break;		case 1:			dev_dbg(hub_dev, "Single TT\n");			hub->tt.hub = hdev;			break;		case 2:			ret = usb_set_interface(hdev, 0, 1);			if (ret == 0) {				dev_dbg(hub_dev, "TT per port\n");				hub->tt.multi = 1;			} else				dev_err(hub_dev, "Using single TT (err %d)\n",					ret);			hub->tt.hub = hdev;			break;		default:			dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",				hdev->descriptor.bDeviceProtocol);			break;	}	switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) {		case 0x00:			if (hdev->descriptor.bDeviceProtocol != 0)				dev_dbg(hub_dev, "TT requires at most 8 FS bit times\n");			break;		case 0x20:			dev_dbg(hub_dev, "TT requires at most 16 FS bit times\n");			break;		case 0x40:			dev_dbg(hub_dev, "TT requires at most 24 FS bit times\n");			break;		case 0x60:			dev_dbg(hub_dev, "TT requires at most 32 FS bit times\n");			break;	}	/* probe() zeroes hub->indicator[] */	if (hub->descriptor->wHubCharacteristics & HUB_CHAR_PORTIND) {		hub->has_indicators = 1;		dev_dbg(hub_dev, "Port indicators are supported\n");	}	dev_dbg(hub_dev, "power on to power good time: %dms\n",		hub->descriptor->bPwrOn2PwrGood * 2);

⌨️ 快捷键说明

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