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

📄 lh7a40x_udc.c

📁 ARM S3C2410 USB SLAVE LINUX驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
	}

	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 + -