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

📄 pxa2xx_udc.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	dev->gadget.dev.driver = &driver->driver;	device_add (&dev->gadget.dev);	retval = driver->bind(&dev->gadget);	if (retval) {		DMSG("bind to driver %s --> error %d\n",				driver->driver.name, retval);		device_del (&dev->gadget.dev);		dev->driver = 0;		dev->gadget.dev.driver = 0;		return retval;	}	device_create_file(dev->dev, &dev_attr_function);	/* ... then enable host detection and ep0; and we're ready	 * for set_configuration as well as eventual disconnect.	 * NOTE:  this shouldn't power up until later.	 */	DMSG("registered gadget driver '%s'\n", driver->driver.name);	udc_enable(dev);	dump_state(dev);	return 0;}EXPORT_SYMBOL(usb_gadget_register_driver);static voidstop_activity(struct pxa2xx_udc *dev, struct usb_gadget_driver *driver){	int i;	/* don't disconnect drivers more than once */	if (dev->gadget.speed == USB_SPEED_UNKNOWN)		driver = 0;	dev->gadget.speed = USB_SPEED_UNKNOWN;	/* prevent new request submissions, kill any outstanding requests  */	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {		struct pxa2xx_ep *ep = &dev->ep[i];		ep->stopped = 1;		nuke(ep, -ESHUTDOWN);	}	del_timer_sync(&dev->timer);	/* report disconnect; the driver is already quiesced */	LED_CONNECTED_OFF;	if (driver)		driver->disconnect(&dev->gadget);	/* re-init driver-visible data structures */	udc_reinit(dev);}int usb_gadget_unregister_driver(struct usb_gadget_driver *driver){	struct pxa2xx_udc	*dev = the_controller;	if (!dev)		return -ENODEV;	if (!driver || driver != dev->driver)		return -EINVAL;	local_irq_disable();	udc_disable(dev);	stop_activity(dev, driver);	local_irq_enable();	driver->unbind(&dev->gadget);	dev->driver = 0;	device_del (&dev->gadget.dev);	device_remove_file(dev->dev, &dev_attr_function);	DMSG("unregistered gadget driver '%s'\n", driver->driver.name);	dump_state(dev);	return 0;}EXPORT_SYMBOL(usb_gadget_unregister_driver);/*-------------------------------------------------------------------------*/#ifdef CONFIG_ARCH_LUBBOCK#ifndef	CONFIG_ARCH_FS_PXA255/* Lubbock can report connect or disconnect irqs.  Likely more hardware * could support it as a timer callback. * * FIXME for better power management, keep the hardware powered down * until a host is powering the link.  means scheduling work later * in some task that can udc_enable(). */#define	enable_disconnect_irq() \	if (machine_is_lubbock()) { enable_irq(LUBBOCK_USB_DISC_IRQ); }#define	disable_disconnect_irq() \	if (machine_is_lubbock()) { disable_irq(LUBBOCK_USB_DISC_IRQ); }static irqreturn_tusb_connection_irq(int irq, void *_dev, struct pt_regs *r){	struct pxa2xx_udc	*dev = _dev;	dev->stats.irqs++;	HEX_DISPLAY(dev->stats.irqs);	if (!is_usb_connected()) {		LED_CONNECTED_OFF;		disable_disconnect_irq();		/* report disconnect just once */		if (dev->gadget.speed != USB_SPEED_UNKNOWN) {			DMSG("disconnect %s\n",				dev->driver ? dev->driver->driver.name : 0);			stop_activity(dev, dev->driver);			// udc_disable (dev);			// no more udc irqs			// maybe "ACTION=disconnect /sbin/hotplug gadget".		}	} else if (dev->gadget.speed == USB_SPEED_UNKNOWN) {		LED_CONNECTED_ON;		DMSG("?? connect irq ??\n");		// if there's no driver bound, ignore; else		// udc_enable (dev);		// UDC irqs drive the rest.		// maybe "ACTION=connect /sbin/hotplug gadget".	}	return IRQ_HANDLED;}#endif#endif#ifndef	enable_disconnect_irq#warning USB disconnect() is not yet reported.#define	enable_disconnect_irq()		do {} while (0)#define	disable_disconnect_irq()	do {} while (0)#endif/*-------------------------------------------------------------------------*/static inline void clear_ep_state (struct pxa2xx_udc *dev){	unsigned i;	/* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint	 * fifos, and pending transactions mustn't be continued in any case.	 */	for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++)		nuke(&dev->ep[i], -ECONNABORTED);}static void udc_watchdog(unsigned long _dev){	struct pxa2xx_udc	*dev = (void *)_dev;	local_irq_disable();	if (dev->ep0state == EP0_STALL			&& (UDCCS0 & UDCCS0_FST) == 0			&& (UDCCS0 & UDCCS0_SST) == 0) {		UDCCS0 = UDCCS0_FST|UDCCS0_FTF;		DBG(DBG_VERBOSE, "ep0 re-stall\n");		start_watchdog(dev);	}	local_irq_enable();}static void handle_ep0 (struct pxa2xx_udc *dev){	u32			udccs0 = UDCCS0;	struct pxa2xx_ep	*ep = &dev->ep [0];	struct pxa2xx_request	*req;	union {		struct usb_ctrlrequest	r;		u8			raw [8];		u32			word [2];	} u;	if (list_empty(&ep->queue))		req = 0;	else		req = list_entry(ep->queue.next, struct pxa2xx_request, queue);	/* clear stall status */	if (udccs0 & UDCCS0_SST) {		nuke(ep, -EPIPE);		UDCCS0 = UDCCS0_SST;		del_timer(&dev->timer);		ep0_idle(dev);	}	/* previous request unfinished?  non-error iff back-to-back ... */	if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) {		nuke(ep, 0);		del_timer(&dev->timer);		ep0_idle(dev);	}	switch (dev->ep0state) {	case EP0_IDLE:		/* late-breaking status? */		udccs0 = UDCCS0;		/* start control request? */		if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))				== (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) {			int i;			nuke (ep, -EPROTO);			/* read SETUP packet */			for (i = 0; i < 8; i++) {				if (unlikely(!(UDCCS0 & UDCCS0_RNE))) {bad_setup:					DMSG("SETUP %d!\n", i);					goto stall;				}				u.raw [i] = (u8) UDDR0;			}			if (unlikely((UDCCS0 & UDCCS0_RNE) != 0))				goto bad_setup;got_setup:			le16_to_cpus (&u.r.wValue);			le16_to_cpus (&u.r.wIndex);			le16_to_cpus (&u.r.wLength);			LED_EP0_ON;			DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n",				u.r.bRequestType, u.r.bRequest,				u.r.wValue, u.r.wIndex, u.r.wLength);			/* cope with automagic for some standard requests. */			dev->req_std = (u.r.bRequestType & USB_TYPE_MASK)						== USB_TYPE_STANDARD;			dev->req_config = 0;			dev->req_pending = 1;			switch (u.r.bRequest) {			/* hardware restricts gadget drivers here! */			case USB_REQ_SET_CONFIGURATION:				if (u.r.bRequestType == USB_RECIP_DEVICE) {					/* reflect hardware's automagic					 * up to the gadget driver.					 */config_change:					dev->req_config = 1;					clear_ep_state(dev);					/* if !has_cfr, there's no synch					 * else use AREN (later) not SA|OPR					 * USIR0_IR0 acts edge sensitive					 */				}				break;			/* ... and here, even more ... */			case USB_REQ_SET_INTERFACE:				if (u.r.bRequestType == USB_RECIP_INTERFACE) {					/* udc hardware is broken by design:					 *  - altsetting may only be zero;					 *  - hw resets all interfaces' eps;					 *  - ep reset doesn't include halt(?).					 */					DMSG("broken set_interface (%d/%d)\n",						u.r.wIndex, u.r.wValue);					goto config_change;				}				break;			/* hardware was supposed to hide this */			case USB_REQ_SET_ADDRESS:				if (u.r.bRequestType == USB_RECIP_DEVICE) {					ep0start(dev, 0, "address");					return;				}				break;			}			if (u.r.bRequestType & USB_DIR_IN)				dev->ep0state = EP0_IN_DATA_PHASE;			else				dev->ep0state = EP0_OUT_DATA_PHASE;			i = dev->driver->setup(&dev->gadget, &u.r);			if (i < 0) {				/* hardware automagic preventing STALL... */				if (dev->req_config) {					/* hardware sometimes neglects to tell					 * tell us about config change events,					 * so later ones may fail...					 */					WARN("config change %02x fail %d?\n",						u.r.bRequest, i);					return;					/* TODO experiment:  if has_cfr,					 * hardware didn't ACK; maybe we					 * could actually STALL!					 */				}				DBG(DBG_VERBOSE, "protocol STALL, "					"%02x err %d\n", UDCCS0, i);stall:				/* the watchdog timer helps deal with cases				 * where udc seems to clear FST wrongly, and				 * then NAKs instead of STALLing.				 */				ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall");				start_watchdog(dev);				dev->ep0state = EP0_STALL;				LED_EP0_OFF;			/* deferred i/o == no response yet */			} else if (dev->req_pending) {				if (likely(dev->ep0state == EP0_IN_DATA_PHASE						|| dev->req_std || u.r.wLength))					ep0start(dev, 0, "defer");				else					ep0start(dev, UDCCS0_IPR, "defer/IPR");			}			/* expect at least one data or status stage irq */			return;		} else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA))				== (UDCCS0_OPR|UDCCS0_SA))) {			unsigned i;			/* pxa210/250 erratum 131 for B0/B1 says RNE lies.			 * still observed on a pxa255 a0.			 */			DBG(DBG_VERBOSE, "e131\n");			nuke(ep, -EPROTO);			/* read SETUP data, but don't trust it too much */			for (i = 0; i < 8; i++)				u.raw [i] = (u8) UDDR0;			if ((u.r.bRequestType & USB_RECIP_MASK)					> USB_RECIP_OTHER)				goto stall;			if (u.word [0] == 0 && u.word [1] == 0)				goto stall;			goto got_setup;		} else {			/* some random early IRQ:			 * - we acked FST			 * - IPR cleared			 * - OPR got set, without SA (likely status stage)			 */			UDCCS0 = udccs0 & (UDCCS0_SA|UDCCS0_OPR);		}		break;	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */		if (udccs0 & UDCCS0_OPR) {			UDCCS0 = UDCCS0_OPR|UDCCS0_FTF;			DBG(DBG_VERBOSE, "ep0in premature status\n");			if (req)				done(ep, req, 0);			ep0_idle(dev);		} else /* irq was IPR clearing */ {			if (req) {				/* this IN packet might finish the request */				(void) write_ep0_fifo(ep, req);			} /* else IN token before response was written */		}		break;	case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR etc */		if (udccs0 & UDCCS0_OPR) {			if (req) {				/* this OUT packet might finish the request */				if (read_ep0_fifo(ep, req))					done(ep, req, 0);				/* else more OUT packets expected */			} /* else OUT token before read was issued */		} else /* irq was IPR clearing */ {			DBG(DBG_VERBOSE, "ep0out premature status\n");			if (req)				done(ep, req, 0);			ep0_idle(dev);		}		break;	case EP0_END_XFER:		if (req)			done(ep, req, 0);		/* ack control-IN status (maybe in-zlp was skipped)		 * also appears after some config change events.		 */		if (udccs0 & UDCCS0_OPR)			UDCCS0 = UDCCS0_OPR;		ep0_idle(dev);		break;	case EP0_STALL:		UDCCS0 = UDCCS0_FST;		break;	}	USIR0 = USIR0_IR0;}static void handle_ep(struct pxa2xx_ep *ep){	struct pxa2xx_request	*req;	int			is_in = ep->bEndpointAddress & USB_DIR_IN;	int			completed;	u32			udccs, tmp;	do {		completed = 0;		if (likely (!list_empty(&ep->queue)))			req = list_entry(ep->queue.next,					struct pxa2xx_request, queue);		else			req = 0;		// TODO check FST handling		udccs = *ep->reg_udccs;		if (unlikely(is_in)) {	/* irq from TPC, SST, or (ISO) TUR */			tmp = UDCCS_BI_TUR;			if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))				tmp |= UDCCS_BI_SST;			tmp &= udccs;			if (likely (tmp))				*ep->reg_udccs = tmp;			if (req && likely ((udccs & UDCCS_BI_TFS) != 0))				completed = write_fifo(ep, req);		} else {	/* irq from RPC (or for ISO, ROF) */			if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))				tmp = UDCCS_BO_SST | UDCCS_BO_DME;			else				tmp = UDCCS_IO_ROF | UDCCS_IO_DME;			tmp &= udccs;			if (likely(tmp))				*ep->reg_udccs = tmp;			/* fifos can hold packets, ready for reading... */			if (likely(req)) {#ifdef USE_OUT_DMA// TODO didn't yet debug out-dma.  this approach assumes// the worst about short packets and RPC; it might be better.				if (likely(ep->dma >= 0)) {					if (!(udccs & UDCCS_BO_RSP)) {						*ep->reg_udccs = UDCCS_BO_RPC;						ep->dma_irqs++;						return;					}				}#endif				completed = read_fifo(ep, req);			} else				pio_irq_disable (ep->bEndpointAddress);		}		ep->pio_irqs++;	} while (completed);}/* *	pxa2xx_udc_irq - interrupt handler * * avoid delays in ep0 processing. the control handshaking isn't always * under software control (pxa250c0 and the pxa255 are better), and delays * could cause usb protocol errors. */static irqreturn_tpxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r){	struct pxa2xx_udc	*dev = _dev;	int			handled;	dev->stats.irqs++;	HEX_DISPLAY(dev->stats.irqs);	do {		u32		udccr = UDCCR;		handled = 0;		/* SUSpend Interrupt Request */		if (unlikely(udccr & UDCCR_SUSIR)) {			udc_ack_int_UDCCR(UDCCR_SUSIR);			handled = 1;			DBG(DBG_VERBOSE, "USB suspend%s\n", is_usb_connected()				? "" : "+disconnect");			if (!is_usb_connected())				stop_activity(dev, dev->driver);			else if (dev->gadget.speed != USB_SPEED_UNKNOWN					&& dev->driver					&& dev->driver->suspend)				dev->driver->suspend(&dev->gadget);			ep0_idle (dev);		}		/* RESume Interrupt Request */		if (unlikely(udccr & UDCCR_RESIR)) {			udc_ack_int_UDCCR(UDCCR_RESIR);			handled = 1;			DBG(DBG_VERBOSE, "USB resume\n");			if (dev->gadget.speed != USB_SPEED_UNKNOWN					&& dev->driver					&& dev->driver->resume					&& is_usb_connected())				dev->driver->resume(&dev->gadget);		}		/* ReSeT Interrupt Request - USB reset */		if (unlikely(udccr & UDCCR_RSTIR)) {			udc_ack_int_UDCCR(UDCCR_RSTIR);			handled = 1;			if ((UDCCR & UDCCR_UDA) == 0) {				DBG(DBG_VERBOSE, "USB reset start\n");				if (dev->gadget.speed != USB_SPEED_UNKNOWN)					disable_disconnect_irq();				/* reset driver and endpoints,				 * in case that's not yet done

⌨️ 快捷键说明

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