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

📄 lh7a40x_udc.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
		DEBUG("Hmm.. %d ep FIFO is not empty!\n", ep_index(ep));	}	return 0;}/** Read to request from FIFO (max read == bytes in fifo) *  Return:  0 = still running, 1 = completed, negative = errno *  NOTE: INDEX register must be set for EP */static int read_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req){	u32 csr;	u8 *buf;	unsigned bufferspace, count, is_short;	volatile u32 *fifo = (volatile u32 *)ep->fifo;	/* make sure there's a packet in the FIFO. */	csr = usb_read(ep->csr1);	if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY)) {		DEBUG("%s: Packet NOT ready!\n", __FUNCTION__);		return -EINVAL;	}	buf = req->req.buf + req->req.actual;	prefetchw(buf);	bufferspace = req->req.length - req->req.actual;	/* read all bytes from this packet */	count = usb_read(USB_OUT_FIFO_WC1);	req->req.actual += min(count, bufferspace);	is_short = (count < ep->ep.maxpacket);	DEBUG("read %s %02x, %d bytes%s req %p %d/%d\n",	      ep->ep.name, csr, count,	      is_short ? "/S" : "", req, req->req.actual, req->req.length);	while (likely(count-- != 0)) {		u8 byte = (u8) (*fifo & 0xff);		if (unlikely(bufferspace == 0)) {			/* this happens when the driver's buffer			 * is smaller than what the host sent.			 * discard the extra data.			 */			if (req->req.status != -EOVERFLOW)				printk("%s overflow %d\n", ep->ep.name, count);			req->req.status = -EOVERFLOW;		} else {			*buf++ = byte;			bufferspace--;		}	}	usb_clear(USB_OUT_CSR1_OUT_PKT_RDY, ep->csr1);	/* completion */	if (is_short || req->req.actual == req->req.length) {		done(ep, req, 0);		usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1);		if (list_empty(&ep->queue))			pio_irq_disable(ep_index(ep));		return 1;	}	/* finished that packet.  the next one may be waiting... */	return 0;}/* *	done - retire a request; caller blocked irqs *  INDEX register is preserved to keep same */static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req, int status){	unsigned int stopped = ep->stopped;	u32 index;	DEBUG("%s, %p\n", __FUNCTION__, ep);	list_del_init(&req->queue);	if (likely(req->req.status == -EINPROGRESS))		req->req.status = status;	else		status = req->req.status;	if (status && status != -ESHUTDOWN)		DEBUG("complete %s req %p stat %d len %u/%u\n",		      ep->ep.name, &req->req, status,		      req->req.actual, req->req.length);	/* don't modify queue heads during completion callback */	ep->stopped = 1;	/* Read current index (completion may modify it) */	index = usb_read(USB_INDEX);	spin_unlock(&ep->dev->lock);	req->req.complete(&ep->ep, &req->req);	spin_lock(&ep->dev->lock);	/* Restore index */	usb_set_index(index);	ep->stopped = stopped;}/** Enable EP interrupt */static void pio_irq_enable(int ep){	DEBUG("%s: %d\n", __FUNCTION__, ep);	switch (ep) {	case 1:		usb_set(USB_IN_INT_EP1, USB_IN_INT_EN);		break;	case 2:		usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN);		break;	case 3:		usb_set(USB_IN_INT_EP3, USB_IN_INT_EN);		break;	default:		DEBUG("Unknown endpoint: %d\n", ep);		break;	}}/** Disable EP interrupt */static void pio_irq_disable(int ep){	DEBUG("%s: %d\n", __FUNCTION__, ep);	switch (ep) {	case 1:		usb_clear(USB_IN_INT_EP1, USB_IN_INT_EN);		break;	case 2:		usb_clear(USB_OUT_INT_EP2, USB_OUT_INT_EN);		break;	case 3:		usb_clear(USB_IN_INT_EP3, USB_IN_INT_EN);		break;	default:		DEBUG("Unknown endpoint: %d\n", ep);		break;	}}/* * 	nuke - dequeue ALL requests */void nuke(struct lh7a40x_ep *ep, int status){	struct lh7a40x_request *req;	DEBUG("%s, %p\n", __FUNCTION__, ep);	/* Flush FIFO */	flush(ep);	/* called with irqs blocked */	while (!list_empty(&ep->queue)) {		req = list_entry(ep->queue.next, struct lh7a40x_request, queue);		done(ep, req, status);	}	/* Disable IRQ if EP is enabled (has descriptor) */	if (ep->desc)		pio_irq_disable(ep_index(ep));}/*void nuke_all(struct lh7a40x_udc *dev){	int n;	for(n=0; n<UDC_MAX_ENDPOINTS; n++) {		struct lh7a40x_ep *ep = &dev->ep[n];		usb_set_index(n);		nuke(ep, 0);	}}*//*static void flush_all(struct lh7a40x_udc *dev){	int n;    for (n = 0; n < UDC_MAX_ENDPOINTS; n++)    {		struct lh7a40x_ep *ep = &dev->ep[n];		flush(ep);    }}*//** Flush EP * NOTE: INDEX register must be set before this call */static void flush(struct lh7a40x_ep *ep){	DEBUG("%s, %p\n", __FUNCTION__, ep);	switch (ep->ep_type) {	case ep_control:		/* check, by implication c.f. 15.1.2.11 */		break;	case ep_bulk_in:	case ep_interrupt:		/* if(csr & USB_IN_CSR1_IN_PKT_RDY) */		usb_set(USB_IN_CSR1_FIFO_FLUSH, ep->csr1);		break;	case ep_bulk_out:		/* if(csr & USB_OUT_CSR1_OUT_PKT_RDY) */		usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1);		break;	}}/** * lh7a40x_in_epn - handle IN interrupt */static void lh7a40x_in_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr){	u32 csr;	struct lh7a40x_ep *ep = &dev->ep[ep_idx];	struct lh7a40x_request *req;	usb_set_index(ep_idx);	csr = usb_read(ep->csr1);	DEBUG("%s: %d, csr %x\n", __FUNCTION__, ep_idx, csr);	if (csr & USB_IN_CSR1_SENT_STALL) {		DEBUG("USB_IN_CSR1_SENT_STALL\n");		usb_set(USB_IN_CSR1_SENT_STALL /*|USB_IN_CSR1_SEND_STALL */ ,			ep->csr1);		return;	}	if (!ep->desc) {		DEBUG("%s: NO EP DESC\n", __FUNCTION__);		return;	}	if (list_empty(&ep->queue))		req = 0;	else		req = list_entry(ep->queue.next, struct lh7a40x_request, queue);	DEBUG("req: %p\n", req);	if (!req)		return;	write_fifo(ep, req);}/* ********************************************************************************************* *//* Bulk OUT (recv) */static void lh7a40x_out_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr){	struct lh7a40x_ep *ep = &dev->ep[ep_idx];	struct lh7a40x_request *req;	DEBUG("%s: %d\n", __FUNCTION__, ep_idx);	usb_set_index(ep_idx);	if (ep->desc) {		u32 csr;		csr = usb_read(ep->csr1);		while ((csr =			usb_read(ep->				 csr1)) & (USB_OUT_CSR1_OUT_PKT_RDY |					   USB_OUT_CSR1_SENT_STALL)) {			DEBUG("%s: %x\n", __FUNCTION__, csr);			if (csr & USB_OUT_CSR1_SENT_STALL) {				DEBUG("%s: stall sent, flush fifo\n",				      __FUNCTION__);				/* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */				flush(ep);			} else if (csr & USB_OUT_CSR1_OUT_PKT_RDY) {				if (list_empty(&ep->queue))					req = 0;				else					req =					    list_entry(ep->queue.next,						       struct lh7a40x_request,						       queue);				if (!req) {					printk("%s: NULL REQ %d\n",					       __FUNCTION__, ep_idx);					flush(ep);					break;				} else {					read_fifo(ep, req);				}			}		}	} else {		/* Throw packet away.. */		printk("%s: No descriptor?!?\n", __FUNCTION__);		flush(ep);	}}static void stop_activity(struct lh7a40x_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 < UDC_MAX_ENDPOINTS; i++) {		struct lh7a40x_ep *ep = &dev->ep[i];		ep->stopped = 1;		usb_set_index(i);		nuke(ep, -ESHUTDOWN);	}	/* report disconnect; the driver is already quiesced */	if (driver) {		spin_unlock(&dev->lock);		driver->disconnect(&dev->gadget);		spin_lock(&dev->lock);	}	/* re-init driver-visible data structures */	udc_reinit(dev);}/** Handle USB RESET interrupt */static void lh7a40x_reset_intr(struct lh7a40x_udc *dev){#if 0				/* def CONFIG_ARCH_LH7A404 */	/* Does not work always... */	DEBUG("%s: %d\n", __FUNCTION__, dev->usb_address);	if (!dev->usb_address) {		/*usb_set(USB_RESET_IO, USB_RESET);		   mdelay(5);		   usb_clear(USB_RESET_IO, USB_RESET); */		return;	}	/* Put the USB controller into reset. */	usb_set(USB_RESET_IO, USB_RESET);	/* Set Device ID to 0 */	udc_set_address(dev, 0);	/* Let PLL2 settle down */	mdelay(5);	/* Release the USB controller from reset */	usb_clear(USB_RESET_IO, USB_RESET);	/* Re-enable UDC */	udc_enable(dev);#endif	dev->gadget.speed = USB_SPEED_FULL;}/* *	lh7a40x usb client interrupt handler. */static irqreturn_t lh7a40x_udc_irq(int irq, void *_dev, struct pt_regs *r){	struct lh7a40x_udc *dev = _dev;	DEBUG("\n\n");	spin_lock(&dev->lock);	for (;;) {		u32 intr_in = usb_read(USB_IN_INT);		u32 intr_out = usb_read(USB_OUT_INT);		u32 intr_int = usb_read(USB_INT);		/* Test also against enable bits.. (lh7a40x errata).. Sigh.. */		u32 in_en = usb_read(USB_IN_INT_EN);		u32 out_en = usb_read(USB_OUT_INT_EN);		if (!intr_out && !intr_in && !intr_int)			break;		DEBUG("%s (on state %s)\n", __FUNCTION__,		      state_names[dev->ep0state]);		DEBUG("intr_out = %x\n", intr_out);		DEBUG("intr_in  = %x\n", intr_in);		DEBUG("intr_int = %x\n", intr_int);		if (intr_in) {			usb_write(intr_in, USB_IN_INT);			if ((intr_in & USB_IN_INT_EP1)			    && (in_en & USB_IN_INT_EP1)) {				DEBUG("USB_IN_INT_EP1\n");				lh7a40x_in_epn(dev, 1, intr_in);			}			if ((intr_in & USB_IN_INT_EP3)			    && (in_en & USB_IN_INT_EP3)) {				DEBUG("USB_IN_INT_EP3\n");				lh7a40x_in_epn(dev, 3, intr_in);			}			if (intr_in & USB_IN_INT_EP0) {				DEBUG("USB_IN_INT_EP0 (control)\n");				lh7a40x_handle_ep0(dev, intr_in);			}		}		if (intr_out) {			usb_write(intr_out, USB_OUT_INT);			if ((intr_out & USB_OUT_INT_EP2)			    && (out_en & USB_OUT_INT_EP2)) {				DEBUG("USB_OUT_INT_EP2\n");				lh7a40x_out_epn(dev, 2, intr_out);			}		}		if (intr_int) {			usb_write(intr_int, USB_INT);			if (intr_int & USB_INT_RESET_INT) {				lh7a40x_reset_intr(dev);			}			if (intr_int & USB_INT_RESUME_INT) {				DEBUG("USB resume\n");				if (dev->gadget.speed != USB_SPEED_UNKNOWN				    && dev->driver				    && dev->driver->resume				    && is_usb_connected()) {					dev->driver->resume(&dev->gadget);				}			}			if (intr_int & USB_INT_SUSPEND_INT) {				DEBUG("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);				}			}		}	}	spin_unlock(&dev->lock);	return IRQ_HANDLED;}static int lh7a40x_ep_enable(struct usb_ep *_ep,			     const struct usb_endpoint_descriptor *desc){	struct lh7a40x_ep *ep;	struct lh7a40x_udc *dev;	unsigned long flags;	DEBUG("%s, %p\n", __FUNCTION__, _ep);	ep = container_of(_ep, struct lh7a40x_ep, ep);	if (!_ep || !desc || ep->desc || _ep->name == ep0name	    || desc->bDescriptorType != USB_DT_ENDPOINT	    || ep->bEndpointAddress != desc->bEndpointAddress	    || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) {		DEBUG("%s, bad ep or descriptor\n", __FUNCTION__);		return -EINVAL;	}	/* xfer types must match, except that interrupt ~= bulk */	if (ep->bmAttributes != desc->bmAttributes	    && ep->bmAttributes != USB_ENDPOINT_XFER_BULK	    && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {		DEBUG("%s, %s type mismatch\n", __FUNCTION__, _ep->name);		return -EINVAL;	}	/* hardware _could_ do smaller, but driver doesn't */	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK	     && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep))	    || !desc->wMaxPacketSize) {		DEBUG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name);		return -ERANGE;	}	dev = ep->dev;	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {		DEBUG("%s, bogus device state\n", __FUNCTION__);		return -ESHUTDOWN;	}	spin_lock_irqsave(&ep->dev->lock, flags);	ep->stopped = 0;	ep->desc = desc;	ep->pio_irqs = 0;	ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);	/* Reset halt state (does flush) */	lh7a40x_set_halt(_ep, 0);	spin_unlock_irqrestore(&ep->dev->lock, flags);	DEBUG("%s: enabled %s\n", __FUNCTION__, _ep->name);	return 0;}/** Disable EP *  NOTE: Sets INDEX register */static int lh7a40x_ep_disable(struct usb_ep *_ep){	struct lh7a40x_ep *ep;	unsigned long flags;	DEBUG("%s, %p\n", __FUNCTION__, _ep);	ep = container_of(_ep, struct lh7a40x_ep, ep);	if (!_ep || !ep->desc) {		DEBUG("%s, %s not enabled\n", __FUNCTION__,

⌨️ 快捷键说明

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