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

📄 s3c2410_udc.c

📁 ARM S3C2410 USB SLAVE LINUX驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
	struct s3c2410_ep	*ep = &dev->ep [0];
	struct s3c2410_request	*req;
	struct usb_ctrlrequest	crq;

	if (list_empty(&ep->queue))
    		req = NULL;
	else
		req = list_entry(ep->queue.next, struct s3c2410_request, queue);


	udc_writel(0, S3C2410_UDC_INDEX_REG);
	ep0csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
	dprintk(DEBUG_NORMAL,"ep0csr %x ep0state %s\n",ep0csr,ep0states[dev->ep0state]);

	/* clear stall status */
	if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
		/* FIXME */
		nuke(dev, ep);
	    	dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
	    	clear_ep0_sst(base_addr);
		dev->ep0state = EP0_IDLE;
		return;
	}

	/* clear setup end */
	if (ep0csr & S3C2410_UDC_EP0_CSR_SE
	    	/* && dev->ep0state != EP0_IDLE */) {
	    	dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
		nuke(dev, ep);
	    	clear_ep0_se(base_addr);
		dev->ep0state = EP0_IDLE;
	}


	switch (dev->ep0state) {
	case EP0_IDLE:
		/* start control request? */
		if (ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) {
			int len, ret, tmp;

			nuke (dev, ep);

			len = read_fifo_crq(&crq);
			if (len != sizeof(crq)) {
			  	dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR"
				    	" wanted %d bytes got %d. Stalling out...\n",
					sizeof(crq), len);
 				set_ep0_ss(base_addr);
				return;
			}

			dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n", crq.bRequest,crq.bRequestType,  crq.wLength);

			/* cope with automagic for some standard requests. */
			dev->req_std = (crq.bRequestType & USB_TYPE_MASK)
						== USB_TYPE_STANDARD;
			dev->req_config = 0;
			dev->req_pending = 1;
			switch (crq.bRequest) {
				case USB_REQ_SET_CONFIGURATION:		// 9
				    	dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n");
					if (crq.bRequestType == USB_RECIP_DEVICE) {
config_change:
						dev->req_config = 1;
						clear_ep_state(dev);
						set_ep0_de_out(base_addr);
					}
					break;
				case USB_REQ_SET_INTERFACE:			// b
				    	dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n");
					if (crq.bRequestType == USB_RECIP_INTERFACE) {
						goto config_change;
					}
					break;

				case USB_REQ_SET_ADDRESS:				// 5
				    	dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n");
					if (crq.bRequestType == USB_RECIP_DEVICE) {
						tmp = crq.wValue & 0x7F;
						dev->address = tmp;
 						udc_writel((tmp | 0x80), S3C2410_UDC_FUNC_ADDR_REG);
						set_ep0_de_out(base_addr);
						return;
					}
					break;
					
				case USB_REQ_GET_STATUS:				// 0
					dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n");
					clear_ep0_opr(base_addr);
					if (!s3c2410_get_status(dev, &crq)) {
						return;
					}
					break;

				default:
					clear_ep0_opr(base_addr);
					break;
			}

			if (crq.bRequestType & USB_DIR_IN)
				dev->ep0state = EP0_IN_DATA_PHASE;
			else
				dev->ep0state = EP0_OUT_DATA_PHASE;
			spin_unlock(&dev->lock);
			ret = dev->driver->setup(&dev->gadget, &crq);
			spin_lock(&dev->lock);
			if (ret < 0) {
				if (dev->req_config) {
					dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n",
						crq.bRequest, ret);
					return;
				}
				if (ret == -EOPNOTSUPP)
					dprintk(DEBUG_NORMAL, "Operation not supported\n");
				else
					dprintk(DEBUG_NORMAL, "dev->driver->setup failed. (%d)\n",ret);

				set_ep0_ss(base_addr);
				set_ep0_de_out(base_addr);
				dev->ep0state = EP0_IDLE;
			/* deferred i/o == no response yet */
			} else if (dev->req_pending) {
			    	dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n");
				dev->req_pending=0;
			}
			dprintk(DEBUG_VERBOSE, "ep0state %s\n",ep0states[dev->ep0state]);
		}
		break;
	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */
	    	dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
		if (!(ep0csr & 2) && req)
		{
			write_fifo(ep, req);
		}
		break;
	case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR etc */
	    	dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
		if ((ep0csr & 1) && req ) {
			read_fifo(ep,req);
		}
		break;
	case EP0_END_XFER:
	    	dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
		dev->ep0state=EP0_IDLE;
		break;
	case EP0_STALL:
	    	dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
	    	dev->ep0state=EP0_IDLE;
		break;
	}
}
/*
 * 	handle_ep - Manage I/O endpoints
 */
static void handle_ep(struct s3c2410_ep *ep)
{
	struct s3c2410_request	*req;
	int			is_in = ep->bEndpointAddress & USB_DIR_IN;
	u32			ep_csr1;
	u32			idx;

	if (likely (!list_empty(&ep->queue)))
		req = list_entry(ep->queue.next,
				struct s3c2410_request, queue);
	else
		req = NULL;

	idx = (u32)(ep->bEndpointAddress&0x7F);

	if (is_in) {
		udc_writel(idx, S3C2410_UDC_INDEX_REG);
		ep_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
		dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n",idx,ep_csr1,req ? 1 : 0);

		if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL)
		{
			dprintk(DEBUG_VERBOSE, "st\n");
			udc_writel(idx, S3C2410_UDC_INDEX_REG);
			udc_writel(0x00,S3C2410_UDC_IN_CSR1_REG);
			return;
		}

		if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
		{
			write_fifo(ep,req);
		}
	}
	else {
		udc_writel(idx, S3C2410_UDC_INDEX_REG);
		ep_csr1 = udc_readl(S3C2410_UDC_OUT_CSR1_REG);
		dprintk(DEBUG_VERBOSE, "ep%01d read csr:%02x\n",idx,ep_csr1);

		if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL)
		{
			udc_writel(idx, S3C2410_UDC_INDEX_REG);
			udc_writel(0x00,S3C2410_UDC_OUT_CSR1_REG);
			return;
		}
		if( (ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
		{
			read_fifo(ep,req);
		}
	}
}

#include <asm/arch/regs-irq.h>
/*
 *      s3c2410_udc_irq - interrupt handler
 */
static irqreturn_t
s3c2410_udc_irq(int irq, void *_dev, struct pt_regs *r)
{
	struct s3c2410_udc      *dev = _dev;
	int usb_status;
	int usbd_status;
	int pwr_reg;
	int ep0csr;
	int     i;
	u32	idx;
	unsigned long flags;


	/* Driver connected ? */
	if (!dev->driver) {
		/* Clear interrupts */
		udc_writel( \
				udc_readl(S3C2410_UDC_USB_INT_REG), \
				S3C2410_UDC_USB_INT_REG \
			    );
		udc_writel( \
				udc_readl(S3C2410_UDC_EP_INT_REG), \
				S3C2410_UDC_EP_INT_REG \
			    );
	}
	spin_lock_irqsave(&dev->lock,flags);

	/* Save index */
	idx = udc_readl(S3C2410_UDC_INDEX_REG);

	/* Read status registers */
	usb_status = udc_readl(S3C2410_UDC_USB_INT_REG);
	usbd_status = udc_readl(S3C2410_UDC_EP_INT_REG);
	pwr_reg = udc_readl(S3C2410_UDC_PWR_REG);

	S3C2410_UDC_SETIX(base_addr,EP0);
	ep0csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);

	// dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", usb_status, usbd_status, pwr_reg,ep0csr);

	/*
	 * Now, handle interrupts. There's two types :
	 * - Reset, Resume, Suspend coming -> usb_int_reg
	 * - EP -> ep_int_reg
	 */

	/* RESET */
	if (usb_status & S3C2410_UDC_USBINT_RESET )
	{
		dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",ep0csr,pwr_reg);
		
		udc_writel(0x00, S3C2410_UDC_INDEX_REG);
		udc_writel((dev->ep[0].ep.maxpacket&0x7ff)>>3,S3C2410_UDC_MAXP_REG);

		dev->gadget.speed = USB_SPEED_FULL;
		dev->ep0state = EP0_IDLE;
		nuke(dev,&dev->ep[0]);

		/* clear interrupt */
		udc_writel(S3C2410_UDC_USBINT_RESET,
			S3C2410_UDC_USB_INT_REG);

		udc_writel(idx,S3C2410_UDC_INDEX_REG);
		spin_unlock_irqrestore(&dev->lock,flags);
		return IRQ_HANDLED;
	}

	/* RESUME */
	if (usb_status & S3C2410_UDC_USBINT_RESUME)
	{
		dprintk(DEBUG_NORMAL, "USB resume\n");

		/* clear interrupt */
		udc_writel(S3C2410_UDC_USBINT_RESUME,
			S3C2410_UDC_USB_INT_REG);

		if (dev->gadget.speed != USB_SPEED_UNKNOWN
			&& dev->driver
			&& dev->driver->resume)
			dev->driver->resume(&dev->gadget);
	}

	/* SUSPEND */
	if (usb_status & S3C2410_UDC_USBINT_SUSPEND)
	{
		dprintk(DEBUG_NORMAL, "USB suspend\n");

		/* clear interrupt */
		udc_writel(S3C2410_UDC_USBINT_SUSPEND,
			S3C2410_UDC_USB_INT_REG);

		if (dev->gadget.speed != USB_SPEED_UNKNOWN
				&& dev->driver
				&& dev->driver->suspend)
			dev->driver->suspend(&dev->gadget);

		dev->ep0state = EP0_IDLE;
	}

	/* EP */
	/* control traffic */
	/* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
	 * generate an interrupt
	 */
	if (usbd_status & S3C2410_UDC_INT_EP0)
	{
		dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
		/* Clear the interrupt bit by setting it to 1 */
		udc_writel(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
		handle_ep0(dev);
	}
	/* endpoint data transfers */
	for (i = 1; i < S3C2410_ENDPOINTS; i++) {
		u32 tmp = 1 << i;
		if (usbd_status & tmp) {
			dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);

			/* Clear the interrupt bit by setting it to 1 */
			udc_writel(tmp, S3C2410_UDC_EP_INT_REG);
			handle_ep(&dev->ep[i]);
		}
	}


	dprintk(DEBUG_VERBOSE,"irq: %d done.\n", irq);

	/* Restore old index */
	udc_writel(idx,S3C2410_UDC_INDEX_REG);

	spin_unlock_irqrestore(&dev->lock,flags);

	return IRQ_HANDLED;
}
/*------------------------- s3c2410_ep_ops ----------------------------------*/

/*
 * 	s3c2410_ep_enable
 */
static int
s3c2410_ep_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
{
	struct s3c2410_udc	*dev;
	struct s3c2410_ep	*ep;
	u32			max, tmp;
	unsigned long		flags;
	u32			csr1,csr2;
	u32			int_en_reg;


	ep = container_of (_ep, struct s3c2410_ep, ep);
	if (!_ep || !desc || ep->desc || _ep->name == ep0name
			|| desc->bDescriptorType != USB_DT_ENDPOINT)
		return -EINVAL;
	dev = ep->dev;
	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
		return -ESHUTDOWN;

	max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff;

	spin_lock_irqsave (&dev->lock, flags);
	_ep->maxpacket = max & 0x7ff;
	ep->desc = desc;
	ep->bEndpointAddress = desc->bEndpointAddress;

	/* set max packet */
	udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
	udc_writel(max>>3,S3C2410_UDC_MAXP_REG);


	/* set type, direction, address; reset fifo counters */
	if (desc->bEndpointAddress & USB_DIR_IN)
	{
		csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
		csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;

		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
		udc_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
		udc_writel(csr2,S3C2410_UDC_IN_CSR2_REG);
	}
	else
	{
		/* don't flush he in fifo or there will be an interrupt for that
		 * endpoint */
		csr1 = S3C2410_UDC_ICSR1_CLRDT;
		csr2 = S3C2410_UDC_ICSR2_DMAIEN;

		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
		udc_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
		udc_writel(csr2,S3C2410_UDC_IN_CSR2_REG);

		csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
		csr2 = S3C2410_UDC_OCSR2_DMAIEN;

		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
		udc_writel(csr1,S3C2410_UDC_OUT_CSR1_REG);
		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
		udc_writel(csr2,S3C2410_UDC_OUT_CSR2_REG);
	}


	/* enable irqs */
	int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
	udc_writel(int_en_reg | (1<<ep->num),S3C2410_UDC_EP_INT_EN_REG);


	/* print some debug message */
	tmp = desc->bEndpointAddress;
	dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
		_ep->name,ep->num, tmp, desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);

	spin_unlock_irqrestore (&dev->lock, flags);

	return 0;
}

/*
 * s3c2410_ep_disable
 */
static int s3c2410_ep_disable (struct usb_ep *_ep)
{
	struct s3c2410_ep	*ep = container_of(_ep, struct s3c2410_ep, ep);
	unsigned long	flags;
	u32			int_en_reg;


	if (!_ep || !ep->desc) {
		dprintk(DEBUG_NORMAL, "%s not enabled\n",
			_ep ? ep->ep.name : NULL);
		return -EINVAL;
	}

	spin_lock_irqsave(&ep->dev->lock, flags);
	ep->desc = NULL;

	nuke (ep->dev, ep);

	/* disable irqs */
	int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
	udc_writel(int_en_reg & ~(1<<ep->num),S3C2410_UDC_EP_INT_EN_REG);

	spin_unlock_irqrestore(&ep->dev->lock, flags);

	dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);

	return 0;
}

/*
 * s3c2410_alloc_request
 */
static struct usb_request *
s3c2410_alloc_request (struct usb_ep *_ep, unsigned int mem_flags)
{
	struct s3c2410_ep	*ep;
	struct s3c2410_request	*req;

    	dprintk(DEBUG_VERBOSE,"s3c2410_alloc_request(ep=%p,flags=%d)\n", _ep, mem_flags);

	ep = container_of (_ep, struct s3c2410_ep, ep);
	if (!_ep)
		return NULL;

	req = kmalloc (sizeof *req, mem_flags);
	if (!req)
		return NULL;
	memset (req, 0, sizeof *req);
	INIT_LIST_HEAD (&req->queue);
	return &req->req;
}

/*
 * s3c2410_free_request
 */
static void
s3c2410_free_request (struct usb_ep *_ep, struct usb_request *_req)
{
	struct s3c2410_ep	*ep;
	struct s3c2410_request	*req;

    	dprintk(DEBUG_VERBOSE, "s3c2410_free_request(ep=%p,req=%p)\n", _ep, _req);

	ep = container_of (_ep, struct s3c2410_ep, ep);
	if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
		return;

	req = container_of (_req, struct s3c2410_request, req);
	WARN_ON (!list_empty (&req->queue));
	kfree (req);
}

/*
 * 	s3c2410_alloc_buffer
 */
static void *
s3c2410_alloc_buffer (
	struct usb_ep *_ep,
	unsigned bytes,
	dma_addr_t *dma,
	unsigned int mem_flags)
{
	char *retval;

    	dprintk(DEBUG_VERBOSE,"s3c2410_alloc_buffer()\n");

	if (!the_controller->driver)
		return NULL;
	retval = kmalloc (bytes, mem_flags);
	*dma = (dma_addr_t) retval;
	return retval;
}

/*
 * s3c2410_free_buffer
 */
static void
s3c2410_free_buffer (
	struct usb_ep *_ep,
	void *buf,
	dma_addr_t dma,
	unsigned bytes)
{
    	dprintk(DEBUG_VERBOSE, "s3c2410_free_buffer()\n");

	if (bytes)
		kfree (buf);
}

/*
 * 	s3c2410_queue
 */
static int
s3c2410_queue(struct usb_ep *_ep, struct usb_request *_req, unsigned int gfp_flags)
{
	struct s3c2410_request	*req;
	struct s3c2410_ep	*ep;
	struct s3c2410_udc	*dev;
	u32			ep_csr=0;
	int 			fifo_count=0;
	unsigned long		flags;


	ep = container_of(_ep, struct s3c2410_ep, ep);
	if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
		dprintk(DEBUG_NORMAL, "s3c2410_queue: inval 2\n");
		return -EINVAL;
	}

	dev = ep->dev;
	if (unlikely (!dev->driver
			|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
		return -ESHUTDOWN;
	}

	spin_lock_irqsave (&dev->lock, flags);
	
	req = container_of(_req, struct s3c2410_request, req);
	if (unlikely (!_req || !_req->complete || !_req->buf
	|| !list_empty(&req->queue))) {
		if (!_req)
			dprintk(DEBUG_NORMAL, "s3c2410_queue: 1 X X X\n");
		else
		{
			dprintk(DEBUG_NORMAL, "s3c2410_queue: 0 %01d %01d %01d\n",!_req->complete,!_req->buf, !list_empty(&req->queue));
		}
		spin_unlock_irqrestore(&dev->lock,flags);
		return -EINVAL;
	}
#if 0
	/* iso is always one packet per request, that's the only way
	 * we can report per-packet status.  that also helps with dma.
	 */
	if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
			&& req->req.length > le16_to_cpu
						(ep->desc->wMaxPacketSize)))
		return -EMSGSIZE;
#endif
	_req->status = -EINPROGRESS;
	_req->actual = 0;

	dprintk(DEBUG_NORMAL,"s3c2410_queue: ep%x len %d\n",ep->bEndpointAddress,_req->length);
	
	if (ep->bEndpointAddress)
	{
		udc_writel(ep->bEndpointAddress&0x7F,S3C2410_UDC_INDEX_REG);
		ep_csr = udc_readl(ep->bEndpointAddress&USB_DIR_IN ? S3C2410_UDC_IN_CSR1_REG : S3C2410_UDC_OUT_CSR1_REG);
		fifo_count=fifo_count_out();
	}

⌨️ 快捷键说明

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