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

📄 hcd.c

📁 飞利浦isp1161 arm-linux驱动源码
💻 C
📖 第 1 页 / 共 3 页
字号:
	list_del_init (&urb->urb_list);	dev = urb->dev;	urb->dev = NULL;	usb_dec_dev_use (dev);	spin_unlock_irqrestore (&hcd_data_lock, flags);}/* may be called in any context with a valid urb->dev usecount *//* caller surrenders "ownership" of urb */static int hcd_submit_urb (struct urb *urb){	int			status;	struct usb_hcd		*hcd;	struct hcd_dev		*dev;	unsigned long		flags;	int			pipe, temp, max;	int			mem_flags;	if (!urb || urb->hcpriv || !urb->complete)		return -EINVAL;	urb->status = -EINPROGRESS;	urb->actual_length = 0;	urb->bandwidth = 0;	INIT_LIST_HEAD (&urb->urb_list);	if (!urb->dev || !urb->dev->bus || urb->dev->devnum <= 0)		return -ENODEV;	hcd = urb->dev->bus->hcpriv;	dev = urb->dev->hcpriv;	if (!hcd || !dev)		return -ENODEV;	/* can't submit new urbs when quiescing, halted, ... */	if (hcd->state == USB_STATE_QUIESCING || !HCD_IS_RUNNING (hcd->state))		return -ESHUTDOWN;	pipe = urb->pipe;	temp = usb_pipetype (urb->pipe);	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe),			usb_pipeout (pipe)))		return -EPIPE;	/* NOTE: 2.5 passes this value explicitly in submit() */	mem_flags = in_interrupt () ? GFP_ATOMIC : GFP_KERNEL;	/* FIXME there should be a sharable lock protecting us against	 * config/altsetting changes and disconnects, kicking in here.	 */	/* Sanity check, so HCDs can rely on clean data */	max = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));	if (max <= 0) {		err ("bogus endpoint (bad maxpacket)");		return -EINVAL;	}	/* "high bandwidth" mode, 1-3 packets/uframe? */	if (urb->dev->speed == USB_SPEED_HIGH) {		int	mult;		switch (temp) {		case PIPE_ISOCHRONOUS:		case PIPE_INTERRUPT:			mult = 1 + ((max >> 11) & 0x03);			max &= 0x03ff;			max *= mult;		}	}	/* periodic transfers limit size per frame/uframe */	switch (temp) {	case PIPE_ISOCHRONOUS: {		int	n, len;		if (urb->number_of_packets <= 0)		    			return -EINVAL;		for (n = 0; n < urb->number_of_packets; n++) {			len = urb->iso_frame_desc [n].length;			if (len < 0 || len > max) 				return -EINVAL;		}		}		break;	case PIPE_INTERRUPT:		if (urb->transfer_buffer_length > max)			return -EINVAL;	}	/* the I/O buffer must usually be mapped/unmapped */	if (urb->transfer_buffer_length < 0)		return -EINVAL;	if (urb->next) {		warn ("use explicit queuing not urb->next");		return -EINVAL;	}#ifdef DEBUG	/* stuff that drivers shouldn't do, but which shouldn't	 * cause problems in HCDs if they get it wrong.	 */	{	unsigned int	orig_flags = urb->transfer_flags;	unsigned int	allowed;	/* enforce simple/standard policy */	allowed = USB_ASYNC_UNLINK;	// affects later unlinks	allowed |= USB_NO_FSBR;		// only affects UHCI	switch (temp) {	case PIPE_CONTROL:		allowed |= USB_DISABLE_SPD;		break;	case PIPE_BULK:		allowed |= USB_DISABLE_SPD | USB_QUEUE_BULK				| USB_ZERO_PACKET | URB_NO_INTERRUPT;		break;	case PIPE_INTERRUPT:		allowed |= USB_DISABLE_SPD;		break;	case PIPE_ISOCHRONOUS:		allowed |= USB_ISO_ASAP;		break;	}	urb->transfer_flags &= allowed;	/* fail if submitter gave bogus flags */	if (urb->transfer_flags != orig_flags) {		err ("BOGUS urb flags, %x --> %x",			orig_flags, urb->transfer_flags);		return -EINVAL;	}	}#endif	/*	 * Force periodic transfer intervals to be legal values that are	 * a power of two (so HCDs don't need to).	 *	 * FIXME want bus->{intr,iso}_sched_horizon values here.  Each HC	 * supports different values... this uses EHCI/UHCI defaults (and	 * EHCI can use smaller non-default values).	 */	switch (temp) {	case PIPE_ISOCHRONOUS:	case PIPE_INTERRUPT:		/* too small? */		if (urb->interval <= 0)			return -EINVAL;		/* too big? */		switch (urb->dev->speed) {		case USB_SPEED_HIGH:	/* units are microframes */			// NOTE usb handles 2^15			if (urb->interval > (1024 * 8))				urb->interval = 1024 * 8;			temp = 1024 * 8;			break;		case USB_SPEED_FULL:	/* units are frames/msec */		case USB_SPEED_LOW:			if (temp == PIPE_INTERRUPT) {				if (urb->interval > 255)					return -EINVAL;				// NOTE ohci only handles up to 32				temp = 128;			} else {				if (urb->interval > 1024)					urb->interval = 1024;				// NOTE usb and ohci handle up to 2^15				temp = 1024;			}			break;		default:			return -EINVAL;		}		/* power of two? */		while (temp > urb->interval)			temp >>= 1;		urb->interval = temp;	}	/*	 * FIXME:  make urb timeouts be generic, keeping the HCD cores	 * as simple as possible.	 */	// NOTE:  a generic device/urb monitoring hook would go here.	// hcd_monitor_hook(MONITOR_URB_SUBMIT, urb)	// It would catch submission paths for all urbs.	/*	 * 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);	if (HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_QUIESCING) {		usb_inc_dev_use (urb->dev);		list_add (&urb->urb_list, &dev->urb_list);		status = 0;	} else {		INIT_LIST_HEAD (&urb->urb_list);		status = -ESHUTDOWN;	}	spin_unlock_irqrestore (&hcd_data_lock, flags);	if (status)		return status;	if (urb->dev == hcd->bus->root_hub)		status = rh_urb_enqueue (hcd, urb);	else		status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);	/* urb->dev got nulled if hcd called giveback for us	 * NOTE: ref to urb->dev is a race without (2.5) refcounting,	 * unless driver only returns status when it didn't giveback 	 */	if (status && urb->dev)		urb_unlink (urb);	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;	return hcd->driver->get_frame_number (hcd);}/*-------------------------------------------------------------------------*/struct completion_splice {		// modified urb context:	/* did we complete? */	struct completion	done;	/* original urb data */	void			(*complete)(struct urb *);	void			*context;};static void unlink_complete (struct urb *urb){	struct completion_splice	*splice;	splice = (struct completion_splice *) urb->context;	/* issue original completion call */	urb->complete = splice->complete;	urb->context = splice->context;	urb->complete (urb);	/* then let the synchronous unlink call complete */	complete (&splice->done);}/* * called in any context; note ASYNC_UNLINK restrictions * * 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){	struct hcd_dev			*dev;	struct usb_hcd			*hcd = 0;	unsigned long			flags;	struct completion_splice	splice;	int				retval;	if (!urb)		return -EINVAL;	/*	 * 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);	if (!urb->hcpriv || urb->transfer_flags & USB_TIMEOUT_KILLED) {		retval = -EINVAL;		goto done;	}	if (!urb->dev || !urb->dev->bus) {		retval = -ENODEV;		goto done;	}	/* giveback clears dev; non-null means it's linked at this level */	dev = urb->dev->hcpriv;	hcd = urb->dev->bus->hcpriv;	if (!dev || !hcd) {		retval = -ENODEV;		goto done;	}	/* For non-periodic transfers, any status except -EINPROGRESS means	 * the HCD has already started to unlink this URB from the hardware.	 * In that case, there's no more work to do.	 *	 * For periodic transfers, this is the only way to trigger unlinking	 * from the hardware.  Since we (currently) overload urb->status to	 * tell the driver to unlink, error status might get clobbered ...	 * unless that transfer hasn't yet restarted.  One such case is when	 * the URB gets unlinked from its completion handler.	 *	 * FIXME use an URB_UNLINKED flag to match URB_TIMEOUT_KILLED	 */	switch (usb_pipetype (urb->pipe)) {	case PIPE_CONTROL:	case PIPE_BULK:		if (urb->status != -EINPROGRESS) {			retval = -EINVAL;			goto done;		}	}	/* maybe set up to block on completion notification */	if ((urb->transfer_flags & USB_TIMEOUT_KILLED))		urb->status = -ETIMEDOUT;	else if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {		if (in_interrupt ()) {			dbg ("non-async unlink in_interrupt");			retval = -EWOULDBLOCK;			goto done;		}		/* synchronous unlink: block till we see the completion */		init_completion (&splice.done);		splice.complete = urb->complete;		splice.context = urb->context;		urb->complete = unlink_complete;		urb->context = &splice;		urb->status = -ENOENT;	} else {		/* asynchronous unlink */		urb->status = -ECONNRESET;	}	spin_unlock (&hcd_data_lock);	spin_unlock_irqrestore (&urb->lock, flags);	if (urb == (struct urb *) hcd->rh_timer.data) {		rh_status_dequeue (hcd, urb);		retval = 0;	} else {		retval = hcd->driver->urb_dequeue (hcd, urb);// FIXME:  if retval and we tried to splice, whoa!!if (retval && urb->status == -ENOENT) err ("whoa! retval %d", retval);	}    	/* block till giveback, if needed */	if (!(urb->transfer_flags & (USB_ASYNC_UNLINK|USB_TIMEOUT_KILLED))			&& HCD_IS_RUNNING (hcd->state)			&& !retval) {		dbg ("%s: wait for giveback urb %p",			hcd->bus_name, urb);		wait_for_completion (&splice.done);	} else if ((urb->transfer_flags & USB_ASYNC_UNLINK) && retval == 0) {		return -EINPROGRESS;	}	goto bye;done:	spin_unlock (&hcd_data_lock);	spin_unlock_irqrestore (&urb->lock, flags);bye:	if (retval)		dbg ("%s: hcd_unlink_urb fail %d",		    hcd ? hcd->bus_name : "(no bus?)",		    retval);	return retval;}/*-------------------------------------------------------------------------*//* called by khubd, rmmod, apmd, or other thread for hcd-private cleanup */// FIXME:  likely best to have explicit per-setting (config+alt)// setup primitives in the usbcore-to-hcd driver API, so nothing// is implicit.  kernel 2.5 needs a bunch of config cleanup...static int hcd_free_dev (struct usb_device *udev){	struct hcd_dev		*dev;	struct usb_hcd		*hcd;	unsigned long		flags;	if (!udev || !udev->hcpriv)		return -EINVAL;	if (!udev->bus || !udev->bus->hcpriv)		return -ENODEV;	// should udev->devnum == -1 ??	dev = udev->hcpriv;	hcd = udev->bus->hcpriv;	/* device driver problem with refcounts? */	if (!list_empty (&dev->urb_list)) {		dbg ("free busy dev, %s devnum %d (bug!)",			hcd->bus_name, udev->devnum);		return -EINVAL;	}	hcd->driver->free_config (hcd, udev);	spin_lock_irqsave (&hcd_data_lock, flags);	list_del (&dev->dev_list);	udev->hcpriv = NULL;	spin_unlock_irqrestore (&hcd_data_lock, flags);	kfree (dev);	return 0;}static struct usb_operations hcd_operations = {	allocate:		hcd_alloc_dev,	get_frame_number:	hcd_get_frame_number,	submit_urb:		hcd_submit_urb,	unlink_urb:		hcd_unlink_urb,	deallocate:		hcd_free_dev,};/*-------------------------------------------------------------------------*/static void hcd_irq (int irq, void *__hcd, struct pt_regs * r){	struct usb_hcd		*hcd = __hcd;	int			start = hcd->state;	if (unlikely (hcd->state == USB_STATE_HALT))	/* irq sharing? */		return;	hcd->driver->irq (hcd);	if (hcd->state != start && hcd->state == USB_STATE_HALT)		hc_died (hcd);}/*-------------------------------------------------------------------------*//** * usb_hcd_giveback_urb - return URB from HCD to device driver * @hcd: host controller returning the URB * @urb: urb being returned to the USB device driver. * Context: in_interrupt() * * This hands the URB from HCD to its USB device driver, using its * completion function.  The HCD has freed all per-urb resources * (and is done using urb->hcpriv).  It also released all HCD locks; * the device driver won't cause deadlocks if it resubmits this URB, * and won't confuse things by modifying and resubmitting this one. * Bandwidth and other resources will be deallocated. * * HCDs must not use this for periodic URBs that are still scheduled * and will be reissued.  They should just call their completion handlers * until the urb is returned to the device driver by unlinking. * * NOTE that no urb->next processing is done, even for isochronous URBs. * ISO streaming functionality can be achieved by having completion handlers * re-queue URBs.  Such explicit queuing doesn't discard error reports. */void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb){	urb_unlink (urb);	// NOTE:  a generic device/urb monitoring hook would go here.	// hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)	// It would catch exit/unlink paths for all urbs, but non-exit	// completions for periodic urbs need hooks inside the HCD.	// hcd_monitor_hook(MONITOR_URB_UPDATE, urb, dev)	if (urb->status)		dbg ("giveback urb %p status %d len %d",			urb, urb->status, urb->actual_length);	/* pass ownership to the completion handler */	urb->complete (urb);}EXPORT_SYMBOL (usb_hcd_giveback_urb);

⌨️ 快捷键说明

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