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

📄 hcd.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
	int		is_iso = usb_pipeisoc (pipe);	int		old_alloc = dev->bus->bandwidth_allocated;	int		new_alloc;	bustime = NS_TO_US (usb_calc_bus_time (dev->speed, is_in, is_iso,			usb_maxpacket (dev, pipe, !is_in)));	if (is_iso)		bustime /= urb->number_of_packets;	new_alloc = old_alloc + (int) bustime;	if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) {#ifdef	DEBUG		char	*mode = #ifdef CONFIG_USB_BANDWIDTH			"";#else			"would have ";#endif		dev_dbg (&dev->dev, "usb_check_bandwidth %sFAILED: %d + %ld = %d usec\n",			mode, old_alloc, bustime, new_alloc);#endif#ifdef CONFIG_USB_BANDWIDTH		bustime = -ENOSPC;	/* report error */#endif	}	return bustime;}EXPORT_SYMBOL (usb_check_bandwidth);/** * usb_claim_bandwidth - records bandwidth for a periodic transfer * @dev: source/target of request * @urb: request (urb->dev == dev) * @bustime: bandwidth consumed, in (average) microseconds per frame * @isoc: true iff the request is isochronous * * Bus bandwidth reservations are recorded purely for diagnostic purposes. * HCDs are expected not to overcommit periodic bandwidth, and to record such * reservations whenever endpoints are added to the periodic schedule. * * FIXME averaging per-frame is suboptimal.  Better to sum over the HCD's * entire periodic schedule ... 32 frames for OHCI, 1024 for UHCI, settable * for EHCI (256/512/1024 frames, default 1024) and have the bus expose how * large its periodic schedule is. */void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc){	dev->bus->bandwidth_allocated += bustime;	if (isoc)		dev->bus->bandwidth_isoc_reqs++;	else		dev->bus->bandwidth_int_reqs++;	urb->bandwidth = bustime;#ifdef USB_BANDWIDTH_MESSAGES	dev_dbg (&dev->dev, "bandwidth alloc increased by %d (%s) to %d for %d requesters\n",		bustime,		isoc ? "ISOC" : "INTR",		dev->bus->bandwidth_allocated,		dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);#endif}EXPORT_SYMBOL (usb_claim_bandwidth);/** * usb_release_bandwidth - reverses effect of usb_claim_bandwidth() * @dev: source/target of request * @urb: request (urb->dev == dev) * @isoc: true iff the request is isochronous * * This records that previously allocated bandwidth has been released. * Bandwidth is released when endpoints are removed from the host controller's * periodic schedule. */void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc){	dev->bus->bandwidth_allocated -= urb->bandwidth;	if (isoc)		dev->bus->bandwidth_isoc_reqs--;	else		dev->bus->bandwidth_int_reqs--;#ifdef USB_BANDWIDTH_MESSAGES	dev_dbg (&dev->dev, "bandwidth alloc reduced by %d (%s) to %d for %d requesters\n",		urb->bandwidth,		isoc ? "ISOC" : "INTR",		dev->bus->bandwidth_allocated,		dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);#endif	urb->bandwidth = 0;}EXPORT_SYMBOL (usb_release_bandwidth);/*-------------------------------------------------------------------------*//* * Generic HC operations. *//*-------------------------------------------------------------------------*/static void urb_unlink (struct urb *urb){	unsigned long		flags;	/* Release any periodic transfer bandwidth */	if (urb->bandwidth)		usb_release_bandwidth (urb->dev, urb,			usb_pipeisoc (urb->pipe));	/* clear all state linking urb to this dev (and hcd) */	spin_lock_irqsave (&hcd_data_lock, flags);	list_del_init (&urb->urb_list);	spin_unlock_irqrestore (&hcd_data_lock, flags);	usb_put_dev (urb->dev);}/* may be called in any context with a valid urb->dev usecount * caller surrenders "ownership" of urb * expects usb_submit_urb() to have sanity checked and conditioned all * inputs in the urb */static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags){	int			status;	struct usb_hcd		*hcd = urb->dev->bus->hcpriv;	struct usb_host_endpoint *ep;	unsigned long		flags;	if (!hcd)		return -ENODEV;	usbmon_urb_submit(&hcd->self, urb);	/*	 * Atomically queue the urb,  first to our records, then to the HCD.	 * Access to urb->status is controlled by urb->lock ... changes on	 * i/o completion (normal or fault) or unlinking.	 */	// FIXME:  verify that quiescing hc works right (RH cleans up)	spin_lock_irqsave (&hcd_data_lock, flags);	ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)			[usb_pipeendpoint(urb->pipe)];	if (unlikely (!ep))		status = -ENOENT;	else if (unlikely (urb->reject))		status = -EPERM;	else switch (hcd->state) {	case HC_STATE_RUNNING:	case HC_STATE_RESUMING:doit:		usb_get_dev (urb->dev);		list_add_tail (&urb->urb_list, &ep->urb_list);		status = 0;		break;	case HC_STATE_SUSPENDED:		/* HC upstream links (register access, wakeup signaling) can work		 * even when the downstream links (and DMA etc) are quiesced; let		 * usbcore talk to the root hub.		 */		if (hcd->self.controller->power.power_state.event == PM_EVENT_ON				&& urb->dev->parent == NULL)			goto doit;		/* FALL THROUGH */	default:		status = -ESHUTDOWN;		break;	}	spin_unlock_irqrestore (&hcd_data_lock, flags);	if (status) {		INIT_LIST_HEAD (&urb->urb_list);		usbmon_urb_submit_error(&hcd->self, urb, status);		return status;	}	/* increment urb's reference count as part of giving it to the HCD	 * (which now controls it).  HCD guarantees that it either returns	 * an error or calls giveback(), but not both.	 */	urb = usb_get_urb (urb);	atomic_inc (&urb->use_count);	if (urb->dev == hcd->self.root_hub) {		/* NOTE:  requirement on hub callers (usbfs and the hub		 * driver, for now) that URBs' urb->transfer_buffer be		 * valid and usb_buffer_{sync,unmap}() not be needed, since		 * they could clobber root hub response data.		 */		status = rh_urb_enqueue (hcd, urb);		goto done;	}	/* lower level hcd code should use *_dma exclusively,	 * unless it uses pio or talks to another transport.	 */	if (hcd->self.controller->dma_mask) {		if (usb_pipecontrol (urb->pipe)			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))			urb->setup_dma = dma_map_single (					hcd->self.controller,					urb->setup_packet,					sizeof (struct usb_ctrlrequest),					DMA_TO_DEVICE);		if (urb->transfer_buffer_length != 0			&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))			urb->transfer_dma = dma_map_single (					hcd->self.controller,					urb->transfer_buffer,					urb->transfer_buffer_length,					usb_pipein (urb->pipe)					    ? DMA_FROM_DEVICE					    : DMA_TO_DEVICE);	}	status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags);done:	if (unlikely (status)) {		urb_unlink (urb);		atomic_dec (&urb->use_count);		if (urb->reject)			wake_up (&usb_kill_urb_queue);		usb_put_urb (urb);		usbmon_urb_submit_error(&hcd->self, urb, status);	}	return status;}/*-------------------------------------------------------------------------*//* called in any context */static int hcd_get_frame_number (struct usb_device *udev){	struct usb_hcd	*hcd = (struct usb_hcd *)udev->bus->hcpriv;	if (!HC_IS_RUNNING (hcd->state))		return -ESHUTDOWN;	return hcd->driver->get_frame_number (hcd);}/*-------------------------------------------------------------------------*//* this makes the hcd giveback() the urb more quickly, by kicking it * off hardware queues (which may take a while) and returning it as * soon as practical.  we've already set up the urb's return status, * but we can't know if the callback completed already. */static intunlink1 (struct usb_hcd *hcd, struct urb *urb){	int		value;	if (urb->dev == hcd->self.root_hub)		value = usb_rh_urb_dequeue (hcd, urb);	else {		/* The only reason an HCD might fail this call is if		 * it has not yet fully queued the urb to begin with.		 * Such failures should be harmless. */		value = hcd->driver->urb_dequeue (hcd, urb);	}	if (value != 0)		dev_dbg (hcd->self.controller, "dequeue %p --> %d\n",				urb, value);	return value;}/* * called in any context * * caller guarantees urb won't be recycled till both unlink() * and the urb's completion function return */static int hcd_unlink_urb (struct urb *urb, int status){	struct usb_host_endpoint	*ep;	struct usb_hcd			*hcd = NULL;	struct device			*sys = NULL;	unsigned long			flags;	struct list_head		*tmp;	int				retval;	if (!urb)		return -EINVAL;	if (!urb->dev || !urb->dev->bus)		return -ENODEV;	ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)			[usb_pipeendpoint(urb->pipe)];	if (!ep)		return -ENODEV;	/*	 * we contend for urb->status with the hcd core,	 * which changes it while returning the urb.	 *	 * Caller guaranteed that the urb pointer hasn't been freed, and	 * that it was submitted.  But as a rule it can't know whether or	 * not it's already been unlinked ... so we respect the reversed	 * lock sequence needed for the usb_hcd_giveback_urb() code paths	 * (urb lock, then hcd_data_lock) in case some other CPU is now	 * unlinking it.	 */	spin_lock_irqsave (&urb->lock, flags);	spin_lock (&hcd_data_lock);	sys = &urb->dev->dev;	hcd = urb->dev->bus->hcpriv;	if (hcd == NULL) {		retval = -ENODEV;		goto done;	}	/* insist the urb is still queued */	list_for_each(tmp, &ep->urb_list) {		if (tmp == &urb->urb_list)			break;	}	if (tmp != &urb->urb_list) {		retval = -EIDRM;		goto done;	}	/* Any status except -EINPROGRESS means something already started to	 * unlink this URB from the hardware.  So there's no more work to do.	 */	if (urb->status != -EINPROGRESS) {		retval = -EBUSY;		goto done;	}	/* IRQ setup can easily be broken so that USB controllers	 * never get completion IRQs ... maybe even the ones we need to	 * finish unlinking the initial failed usb_set_address()	 * or device descriptor fetch.	 */	if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags)	    && hcd->self.root_hub != urb->dev) {		dev_warn (hcd->self.controller, "Unlink after no-IRQ?  "			"Controller is probably using the wrong IRQ."			"\n");		set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);	}	urb->status = status;	spin_unlock (&hcd_data_lock);	spin_unlock_irqrestore (&urb->lock, flags);	retval = unlink1 (hcd, urb);	if (retval == 0)		retval = -EINPROGRESS;	return retval;done:	spin_unlock (&hcd_data_lock);	spin_unlock_irqrestore (&urb->lock, flags);	if (retval != -EIDRM && sys && sys->driver)		dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);	return retval;}/*-------------------------------------------------------------------------*//* disables the endpoint: cancels any pending urbs, then synchronizes with * the hcd to make sure all endpoint state is gone from hardware. use for * set_configuration, set_interface, driver removal, physical disconnect. * * example:  a qh stored in ep->hcpriv, holding state related to endpoint * type, maxpacket size, toggle, halt status, and scheduling. */static voidhcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep){	struct usb_hcd		*hcd;	struct urb		*urb;	hcd = udev->bus->hcpriv;	WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT &&			udev->state != USB_STATE_NOTATTACHED);	local_irq_disable ();	/* FIXME move most of this into message.c as part of its	 * endpoint disable logic	 */	/* ep is already gone from udev->ep_{in,out}[]; no more submits */rescan:	spin_lock (&hcd_data_lock);	list_for_each_entry (urb, &ep->urb_list, urb_list) {		int	tmp;		/* another cpu may be in hcd, spinning on hcd_data_lock		 * to giveback() this urb.  the races here should be		 * small, but a full fix needs a new "can't submit"		 * urb state.		 * FIXME urb->reject should allow that...		 */		if (urb->status != -EINPROGRESS)			continue;		usb_get_urb (urb);		spin_unlock (&hcd_data_lock);		spin_lock (&urb->lock);		tmp = urb->status;		if (tmp == -EINPROGRESS)			urb->status = -ESHUTDOWN;		spin_unlock (&urb->lock);		/* kick hcd unless it's already returning this */		if (tmp == -EINPROGRESS) {			tmp = urb->pipe;			unlink1 (hcd, urb);			dev_dbg (hcd->self.controller,				"shutdown urb %p pipe %08x ep%d%s%s\n",				urb, tmp, usb_pipeendpoint (tmp),				(tmp & USB_DIR_IN) ? "in" : "out",				({ char *s; \				 switch (usb_pipetype (tmp)) { \				 case PIPE_CONTROL:	s = ""; break; \				 case PIPE_BULK:	s = "-bulk"; break; \				 case PIPE_INTERRUPT:	s = "-intr"; break; \				 default: 		s = "-iso"; break; \				}; s;}));		}		usb_put_urb (urb);		/* list contents may have changed */		goto rescan;	}	spin_unlock (&hcd_data_lock);	local_irq_enable ();	/* synchronize with the hardware, so old configuration state	 * clears out immediately (and will be freed).	 */	might_sleep ();	if (hcd->driver->endpoint_disable)		hcd->driver->endpoint_disable (hcd, ep);}/*-------------------------------------------------------------------------*/#ifdef	CONFIG_PMint hcd_bus_suspend (struct usb_bus *bus){	struct usb_hcd		*hcd;	int			status;	hcd = container_of (bus, struct usb_hcd, self);	if (!hcd->driver->bus_suspend)		return -ENOENT;	hcd->state = HC_STATE_QUIESCING;	status = hcd->driver->bus_suspend (hcd);	if (status == 0)		hcd->state = HC_STATE_SUSPENDED;	else		dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",				"suspend", status);	return status;}int hcd_bus_resume (struct usb_bus *bus){	struct usb_hcd		*hcd;	int			status;	hcd = container_of (bus, struct usb_hcd, self);	if (!hcd->driver->bus_resume)		return -ENOENT;	if (hcd->state == HC_STATE_RUNNING)		return 0;	hcd->state = HC_STATE_RESUMING;	status = hcd->driver->bus_resume (hcd);	if (status == 0)		hcd->state = HC_STATE_RUNNING;	else {

⌨️ 快捷键说明

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