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

📄 hub.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
 * 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, GFP_ATOMIC)) == NULL) {		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 port1;	unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;	u16 wHubCharacteristics =			le16_to_cpu(hub->descriptor->wHubCharacteristics);	/* Enable power on each port.  Some hubs have reserved values	 * of LPSM (> 2) in their descriptors, even though they are	 * USB 2.0 hubs.  Some hubs do not implement port-power switching	 * but only emulate it.  In all cases, the ports won't work	 * unless we send these messages to the hub.	 */	if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2)		dev_dbg(hub->intfdev, "enabling power on all ports\n");	else		dev_dbg(hub->intfdev, "trying to enable port power on "				"non-switchable hub\n");	for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)		set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);	/* Wait at least 100 msec for power to become stable */	msleep(max(pgood_delay, (unsigned) 100));}static void hub_quiesce(struct usb_hub *hub){	/* (nonblocking) khubd and related activity won't re-trigger */	hub->quiescing = 1;	hub->activating = 0;	/* (blocking) stop khubd and related activity */	usb_kill_urb(hub->urb);	if (hub->has_indicators)		cancel_delayed_work_sync(&hub->leds);	if (hub->tt.hub)		cancel_work_sync(&hub->tt.kevent);}static void hub_activate(struct usb_hub *hub){	int	status;	hub->quiescing = 0;	hub->activating = 1;	status = usb_submit_urb(hub->urb, GFP_NOIO);	if (status < 0)		dev_err(hub->intfdev, "activate --> %d\n", status);	if (hub->has_indicators && blinkenlights)		schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);	/* scan all ports ASAP */	kick_khubd(hub);}static int hub_hub_status(struct usb_hub *hub,		u16 *status, u16 *change){	int ret;	mutex_lock(&hub->status_mutex);	ret = get_hub_status(hub->hdev, &hub->status->hub);	if (ret < 0)		dev_err (hub->intfdev,			"%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;	}	mutex_unlock(&hub->status_mutex);	return ret;}static int hub_port_disable(struct usb_hub *hub, int port1, int set_state){	struct usb_device *hdev = hub->hdev;	int ret = 0;	if (hdev->children[port1-1] && set_state)		usb_set_device_state(hdev->children[port1-1],				USB_STATE_NOTATTACHED);	if (!hub->error)		ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);	if (ret)		dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",				port1, ret);	return ret;}/* * Disable a port and mark a logical connnect-change event, so that some * time later khubd will disconnect() any existing usb_device on the port * and will re-enumerate if there actually is a device attached. */static void hub_port_logical_disconnect(struct usb_hub *hub, int port1){	dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1);	hub_port_disable(hub, port1, 1);	/* FIXME let caller ask to power down the port:	 *  - some devices won't enumerate without a VBUS power cycle	 *  - SRP saves power that way	 *  - ... new call, TBD ...	 * That's easy if this hub can switch power per-port, and	 * khubd reactivates the port later (timer, SRP, etc).	 * Powerdown must be optional, because of reset/DFU.	 */	set_bit(port1, hub->change_bits); 	kick_khubd(hub);}/* caller has locked the hub device */static int hub_pre_reset(struct usb_interface *intf){	struct usb_hub *hub = usb_get_intfdata(intf);	struct usb_device *hdev = hub->hdev;	int i;	/* Disconnect all the children */	for (i = 0; i < hdev->maxchild; ++i) {		if (hdev->children[i])			usb_disconnect(&hdev->children[i]);	}	hub_quiesce(hub);	return 0;}/* caller has locked the hub device */static int hub_post_reset(struct usb_interface *intf){	struct usb_hub *hub = usb_get_intfdata(intf);	hub_power_on(hub);	hub_activate(hub);	return 0;}static int hub_configure(struct usb_hub *hub,	struct usb_endpoint_descriptor *endpoint){	struct usb_device *hdev = hub->hdev;	struct device *hub_dev = hub->intfdev;	u16 hubstatus, hubchange;	u16 wHubCharacteristics;	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;	}	mutex_init(&hub->status_mutex);	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");	wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);	if (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 (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 (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);	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;	}	/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */	switch (wHubCharacteristics & HUB_CHAR_TTTT) {		case HUB_TTTT_8_BITS:			if (hdev->descriptor.bDeviceProtocol != 0) {				hub->tt.think_time = 666;				dev_dbg(hub_dev, "TT requires at most %d "						"FS bit times (%d ns)\n",					8, hub->tt.think_time);			}			break;		case HUB_TTTT_16_BITS:			hub->tt.think_time = 666 * 2;			dev_dbg(hub_dev, "TT requires at most %d "					"FS bit times (%d ns)\n",				16, hub->tt.think_time);			break;		case HUB_TTTT_24_BITS:			hub->tt.think_time = 666 * 3;			dev_dbg(hub_dev, "TT requires at most %d "					"FS bit times (%d ns)\n",				24, hub->tt.think_time);			break;		case HUB_TTTT_32_BITS:			hub->tt.think_time = 666 * 4;			dev_dbg(hub_dev, "TT requires at most %d "					"FS bit times (%d ns)\n",				32, hub->tt.think_time);			break;	}	/* probe() zeroes hub->indicator[] */	if (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);	/* power budgeting mostly matters with bus-powered hubs,	 * and battery-powered root hubs (may provide just 8 mA).	 */	ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);	if (ret < 2) {		message = "can't get hub status";		goto fail;	}	le16_to_cpus(&hubstatus);	if (hdev == hdev->bus->root_hub) {		if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)			hub->mA_per_port = 500;		else {			hub->mA_per_port = hdev->bus_mA;			hub->limited_power = 1;		}	} else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {		dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",			hub->descriptor->bHubContrCurrent);		hub->limited_power = 1;		if (hdev->maxchild > 0) {			int remaining = hdev->bus_mA -					hub->descriptor->bHubContrCurrent;			if (remaining < hdev->maxchild * 100)				dev_warn(hub_dev,					"insufficient power available "					"to use all downstream ports\n");			hub->mA_per_port = 100;		/* 7.2.1.1 */		}	} else {	/* Self-powered external hub */		/* FIXME: What about battery-powered external hubs that		 * provide less current per port? */		hub->mA_per_port = 500;	}	if (hub->mA_per_port < 500)		dev_dbg(hub_dev, "%umA bus power budget for each child\n",				hub->mA_per_port);	ret = hub_hub_status(hub, &hubstatus, &hubchange);	if (ret < 0) {		message = "can't get hub status";		goto fail;	}	/* local power status reports aren't always correct */	if (hdev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER)		dev_dbg(hub_dev, "local power source is %s\n",			(hubstatus & HUB_STATUS_LOCAL_POWER)			? "lost (inactive)" : "good");	if ((wHubCharacteristics & HUB_CHAR_OCPM) == 0)		dev_dbg(hub_dev, "%sover-current condition exists\n",			(hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");	/* set up the interrupt endpoint	 * We use the EP's maxpacket size instead of (PORTS+1+7)/8	 * bytes as USB2.0[11.12.3] says because some hubs are known	 * to send more data (and thus cause overflow). For root hubs,	 * maxpktsize is defined in hcd.c's fake endpoint descriptors	 * to be big enough for at least USB_MAXCHILDREN ports. */	pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);	maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));	if (maxp > sizeof(*hub->buffer))		maxp = sizeof(*hub->buffer);	hub->urb = usb_alloc_urb(0, GFP_KERNEL);	if (!hub->urb) {		message = "couldn't allocate interrupt urb";		ret = -ENOMEM;		goto fail;	}	usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,		hub, endpoint->bInterval);	hub->urb->transfer_dma = hub->buffer_dma;	hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;	/* maybe cycle the hub leds */	if (hub->has_indicators && blinkenlights)		hub->indicator [0] = INDICATOR_CYCLE;	hub_power_on(hub);	hub_activate(hub);	return 0;fail:	dev_err (hub_dev, "config failed, %s (err %d)\n",			message, ret);	/* hub_disconnect() frees urb and descriptor */	return ret;}static void hub_release(struct kref *kref){	struct usb_hub *hub = container_of(kref, struct usb_hub, kref);	usb_put_intf(to_usb_interface(hub->intfdev));	kfree(hub);}static unsigned highspeed_hubs;static void hub_disconnect(struct usb_interface *intf){	struct usb_hub *hub = usb_get_intfdata (intf);	/* Take the hub off the event list and don't let it be added again */	spin_lock_irq(&hub_event_lock);	list_del_init(&hub->event_list);	hub->disconnected = 1;	spin_unlock_irq(&hub_event_lock);	/* Disconnect all children and quiesce the hub */	hub->error = 0;	hub_pre_reset(intf);	usb_set_intfdata (intf, NULL);	if (hub->hdev->speed == USB_SPEED_HIGH)

⌨️ 快捷键说明

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