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

📄 hub.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * 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/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/ioctl.h>#include <linux/usb.h>#include <linux/usbdevice_fs.h>#include <linux/kthread.h>#include <linux/mutex.h>#include <linux/freezer.h>#include <asm/semaphore.h>#include <asm/uaccess.h>#include <asm/byteorder.h>#include "usb.h"#include "hcd.h"#include "hub.h"#ifdef	CONFIG_USB_PERSIST#define	USB_PERSIST	1#else#define	USB_PERSIST	0#endifstruct usb_hub {	struct device		*intfdev;	/* the "interface" device */	struct usb_device	*hdev;	struct kref		kref;	struct urb		*urb;		/* for interrupt polling pipe */	/* buffer for urb ... with extra space in case of babble */	char			(*buffer)[8];	dma_addr_t		buffer_dma;	/* DMA address for buffer */	union {		struct usb_hub_status	hub;		struct usb_port_status	port;	}			*status;	/* buffer for status reports */	struct mutex		status_mutex;	/* for the status buffer */	int			error;		/* last reported error */	int			nerrors;	/* track consecutive errors */	struct list_head	event_list;	/* hubs w/data or errs ready */	unsigned long		event_bits[1];	/* status change bitmask */	unsigned long		change_bits[1];	/* ports with logical connect							status change */	unsigned long		busy_bits[1];	/* ports being reset or							resumed */#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */#error event_bits[] is too short!#endif	struct usb_hub_descriptor *descriptor;	/* class descriptor */	struct usb_tt		tt;		/* Transaction Translator */	unsigned		mA_per_port;	/* current for each child */	unsigned		limited_power:1;	unsigned		quiescing:1;	unsigned		activating:1;	unsigned		disconnected:1;	unsigned		has_indicators:1;	u8			indicator[USB_MAXCHILDREN];	struct delayed_work	leds;};/* Protect struct usb_device->state and ->children members * Note: Both are also protected by ->dev.sem, except that ->state can * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */static DEFINE_SPINLOCK(device_state_lock);/* khubd's worklist and its lock */static DEFINE_SPINLOCK(hub_event_lock);static LIST_HEAD(hub_event_list);	/* List of hubs needing servicing *//* Wakes up khubd */static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);static struct task_struct *khubd_task;/* 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");/* * As of 2.6.10 we introduce a new USB device initialization scheme which * closely resembles the way Windows works.  Hopefully it will be compatible * with a wider range of devices than the old scheme.  However some previously * working devices may start giving rise to "device not accepting address" * errors; if that happens the user can try the old scheme by adjusting the * following module parameters. * * For maximum flexibility there are two boolean parameters to control the * hub driver's behavior.  On the first initialization attempt, if the * "old_scheme_first" parameter is set then the old scheme will be used, * otherwise the new scheme is used.  If that fails and "use_both_schemes" * is set, then the driver will make another attempt, using the other scheme. */static int old_scheme_first = 0;module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);MODULE_PARM_DESC(old_scheme_first,		 "start with the old device initialization scheme");static int use_both_schemes = 1;module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR);MODULE_PARM_DESC(use_both_schemes,		"try the other device initialization scheme if the "		"first one fails");/* Mutual exclusion for EHCI CF initialization.  This interferes with * port reset on some companion controllers. */DECLARE_RWSEM(ehci_cf_port_reset_rwsem);EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);static 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";}/* Note that hdev or one of its children must be locked! */static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev){	return usb_get_intfdata(hdev->actconfig->interface[0]);}/* USB 2.0 spec Section 11.24.4.5 */static int get_hub_descriptor(struct usb_device *hdev, void *data, int size){	int i, ret;	for (i = 0; i < 3; i++) {		ret = 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,			USB_CTRL_GET_TIMEOUT);		if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))			return ret;	}	return -EINVAL;}/* * 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, 1000);}/* * USB 2.0 spec Section 11.24.2.2 */static int clear_port_feature(struct usb_device *hdev, int port1, int feature){	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),		USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,		NULL, 0, 1000);}/* * USB 2.0 spec Section 11.24.2.13 */static int set_port_feature(struct usb_device *hdev, int port1, int feature){	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),		USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,		NULL, 0, 1000);}/* * 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_hub *hub,	int port1,	int selector){	int status = set_port_feature(hub->hdev, (selector << 8) | port1,			USB_PORT_FEAT_INDICATOR);	if (status < 0)		dev_dbg (hub->intfdev,			"port %d indicator %s status %d\n",			port1,			({ 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 (struct work_struct *work){	struct usb_hub		*hub =		container_of(work, struct usb_hub, leds.work);	struct usb_device	*hdev = hub->hdev;	unsigned		i;	unsigned		changed = 0;	int			cursor = -1;	if (hdev->state != USB_STATE_CONFIGURED || hub->quiescing)		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(hub, i + 1, selector);		hub->indicator[i] = mode;	}	if (!changed && blinkenlights) {		cursor++;		cursor %= hub->descriptor->bNbrPorts;		set_port_led(hub, cursor + 1, HUB_LED_GREEN);		hub->indicator[cursor] = INDICATOR_CYCLE;		changed++;	}	if (changed)		schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);}/* use a short timeout for hub/port status fetches */#define	USB_STS_TIMEOUT		1000#define	USB_STS_RETRIES		5/* * USB 2.0 spec Section 11.24.2.6 */static int get_hub_status(struct usb_device *hdev,		struct usb_hub_status *data){	int i, status = -ETIMEDOUT;	for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {		status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,			data, sizeof(*data), USB_STS_TIMEOUT);	}	return status;}/* * USB 2.0 spec Section 11.24.2.7 */static int get_port_status(struct usb_device *hdev, int port1,		struct usb_port_status *data){	int i, status = -ETIMEDOUT;	for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {		status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,			data, sizeof(*data), USB_STS_TIMEOUT);	}	return status;}static void kick_khubd(struct usb_hub *hub){	unsigned long	flags;	/* Suppress autosuspend until khubd runs */	to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;	spin_lock_irqsave(&hub_event_lock, flags);	if (!hub->disconnected && list_empty(&hub->event_list)) {		list_add_tail(&hub->event_list, &hub_event_list);		wake_up(&khubd_wait);	}	spin_unlock_irqrestore(&hub_event_lock, flags);}void usb_kick_khubd(struct usb_device *hdev){	/* FIXME: What if hdev isn't bound to the hub driver? */	kick_khubd(hdev_to_hub(hdev));}/* completion function, fires on port status changes and various faults */static void hub_irq(struct urb *urb){	struct usb_hub *hub = urb->context;	int status = urb->status;	int i;	unsigned long bits;	switch (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->intfdev, "transfer --> %d\n", status);		if ((++hub->nerrors < 10) || hub->error)			goto resubmit;		hub->error = 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 */	kick_khubd(hub);resubmit:	if (hub->quiescing)		return;	if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0			&& status != -ENODEV && status != -EPERM)		dev_err (hub->intfdev, "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, 1000);}/* * 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 (struct work_struct *work){	struct usb_hub		*hub =		container_of(work, struct usb_hub, tt.kevent);	unsigned long		flags;	int			limit = 100;	spin_lock_irqsave (&hub->tt.lock, flags);	while (--limit && !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 * @udev: 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)

⌨️ 快捷键说明

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