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

📄 goku_udc.c

📁 LINUX2.4.18内核下的usb GADGET驱动程序
💻 C
📖 第 1 页 / 共 4 页
字号:
	u8			*buf;	unsigned		bufferspace, is_short, dbuff;	regs = ep->dev->regs;top:	buf = req->req.buf + req->req.actual;	prefetchw(buf);	if (unlikely(ep->num == 0 && ep->dev->ep0state != EP0_OUT))		return -EL2HLT;	dbuff = (ep->num == 1 || ep->num == 2);	do {		/* ack dataset irq matching the status we'll handle */		if (ep->num != 0)			writel(~INT_EPxDATASET(ep->num), &regs->int_status);		set = readl(&regs->DataSet) & DATASET_AB(ep->num);		size = readl(&regs->EPxSizeLA[ep->num]);		bufferspace = req->req.length - req->req.actual;		/* usually do nothing without an OUT packet */		if (likely(ep->num != 0 || bufferspace != 0)) {			if (unlikely(set == 0))				break;			/* use ep1/ep2 double-buffering for OUT */			if (!(size & PACKET_ACTIVE))				size = readl(&regs->EPxSizeLB[ep->num]);			if (!(size & PACKET_ACTIVE)) 	// "can't happen"				break;			size &= DATASIZE;	/* EPxSizeH == 0 */		/* ep0out no-out-data case for set_config, etc */		} else			size = 0;		/* read all bytes from this packet */		req->req.actual += size;		is_short = (size < ep->ep.maxpacket);#ifdef USB_TRACE		VDBG(ep->dev, "read %s %u bytes%s OUT req %p %u/%u\n",			ep->ep.name, size, is_short ? "/S" : "",			req, req->req.actual, req->req.length);#endif		while (likely(size-- != 0)) {			u8	byte = (u8) readl(ep->reg_fifo);			if (unlikely(bufferspace == 0)) {				/* this happens when the driver's buffer				 * is smaller than what the host sent.				 * discard the extra data in this packet.				 */				if (req->req.status != -EOVERFLOW)					DBG(ep->dev, "%s overflow %u\n",						ep->ep.name, size);				req->req.status = -EOVERFLOW;			} else {				*buf++ = byte;				bufferspace--;			}		}		/* completion */		if (unlikely(is_short || req->req.actual == req->req.length)) {			if (unlikely(ep->num == 0)) {				/* non-control endpoints now usable? */				if (ep->dev->req_config)					writel(ep->dev->configured							? USBSTATE_CONFIGURED							: 0,						&regs->UsbState);				/* ep0out status stage */				writel(~(1<<0), &regs->EOP);				ep->stopped = 1;				ep->dev->ep0state = EP0_STATUS;			}			done(ep, req, 0);			/* empty the second buffer asap */			if (dbuff && !list_empty(&ep->queue)) {				req = list_entry(ep->queue.next,						struct goku_request, queue);				goto top;			}			return 1;		}	} while (dbuff);	return 0;}static inline voidpio_irq_enable(struct goku_udc *dev, struct goku_udc_regs *regs, int epnum){	dev->int_enable |= INT_EPxDATASET (epnum);	writel(dev->int_enable, &regs->int_enable);	/* write may still be posted */}static inline voidpio_irq_disable(struct goku_udc *dev, struct goku_udc_regs *regs, int epnum){	dev->int_enable &= ~INT_EPxDATASET (epnum);	writel(dev->int_enable, &regs->int_enable);	/* write may still be posted */}static inline voidpio_advance(struct goku_ep *ep){	struct goku_request	*req;	if (unlikely(list_empty (&ep->queue)))		return;	req = list_entry(ep->queue.next, struct goku_request, queue);	(ep->is_in ? write_fifo : read_fifo)(ep, req);}/*-------------------------------------------------------------------------*/// return:  0 = q running, 1 = q stopped, negative = errnostatic int start_dma(struct goku_ep *ep, struct goku_request *req){	struct goku_udc_regs	*regs = ep->dev->regs;	u32			master;	u32			start = req->req.dma;	u32			end = start + req->req.length - 1;	master = readl(&regs->dma_master) & MST_RW_BITS;	/* re-init the bits affecting IN dma; careful with zlps */	if (likely(ep->is_in)) {		if (unlikely(master & MST_RD_ENA)) {			DBG (ep->dev, "start, IN active dma %03x!!\n",				master);//			return -EL2HLT;		}		writel(end, &regs->in_dma_end);		writel(start, &regs->in_dma_start);		master &= ~MST_R_BITS;		if (unlikely(req->req.length == 0))			master = MST_RD_ENA | MST_RD_EOPB;		else if ((req->req.length % ep->ep.maxpacket) != 0					|| req->req.zero)			master = MST_RD_ENA | MST_EOPB_ENA;		else			master = MST_RD_ENA | MST_EOPB_DIS;		ep->dev->int_enable |= INT_MSTRDEND;	/* Goku DMA-OUT merges short packets, which plays poorly with	 * protocols where short packets mark the transfer boundaries.	 * The chip supports a nonstandard policy with INT_MSTWRTMOUT,	 * ending transfers after 3 SOFs; we don't turn it on.	 */	} else {		if (unlikely(master & MST_WR_ENA)) {			DBG (ep->dev, "start, OUT active dma %03x!!\n",				master);//			return -EL2HLT;		}		writel(end, &regs->out_dma_end);		writel(start, &regs->out_dma_start);		master &= ~MST_W_BITS;		master |= MST_WR_ENA | MST_TIMEOUT_DIS;		ep->dev->int_enable |= INT_MSTWREND|INT_MSTWRTMOUT;	}	writel(master, &regs->dma_master);	writel(ep->dev->int_enable, &regs->int_enable);	return 0;}static void dma_advance(struct goku_udc *dev, struct goku_ep *ep){	struct goku_request	*req;	struct goku_udc_regs	*regs = ep->dev->regs;	u32			master;	master = readl(&regs->dma_master);	if (unlikely(list_empty(&ep->queue))) {stop:		if (ep->is_in)			dev->int_enable &= ~INT_MSTRDEND;		else			dev->int_enable &= ~(INT_MSTWREND|INT_MSTWRTMOUT);		writel(dev->int_enable, &regs->int_enable);		return;	}	req = list_entry(ep->queue.next, struct goku_request, queue);	/* normal hw dma completion (not abort) */	if (likely(ep->is_in)) {		if (unlikely(master & MST_RD_ENA))			return;		req->req.actual = readl(&regs->in_dma_current);	} else {		if (unlikely(master & MST_WR_ENA))			return;		/* hardware merges short packets, and also hides packet		 * overruns.  a partial packet MAY be in the fifo here.		 */		req->req.actual = readl(&regs->out_dma_current);	}	req->req.actual -= req->req.dma;	req->req.actual++;#ifdef USB_TRACE	VDBG(dev, "done %s %s dma, %u/%u bytes, req %p\n",		ep->ep.name, ep->is_in ? "IN" : "OUT",		req->req.actual, req->req.length, req);#endif	done(ep, req, 0);	if (list_empty(&ep->queue))		goto stop;	req = list_entry(ep->queue.next, struct goku_request, queue);	(void) start_dma(ep, req);}static void abort_dma(struct goku_ep *ep, int status){	struct goku_udc_regs	*regs = ep->dev->regs;	struct goku_request	*req;	u32			curr, master;	/* NAK future host requests, hoping the implicit delay lets the	 * dma engine finish reading (or writing) its latest packet and	 * empty the dma buffer (up to 16 bytes).	 *	 * This avoids needing to clean up a partial packet in the fifo;	 * we can't do that for IN without side effects to HALT and TOGGLE.	 */	command(regs, COMMAND_FIFO_DISABLE, ep->num);	req = list_entry(ep->queue.next, struct goku_request, queue);	master = readl(&regs->dma_master) & MST_RW_BITS;	/* FIXME using these resets isn't usably documented. this may	 * not work unless it's followed by disabling the endpoint.	 *	 * FIXME the OUT reset path doesn't even behave consistently.	 */	if (ep->is_in) {		if (unlikely((readl(&regs->dma_master) & MST_RD_ENA) == 0))			goto finished;		curr = readl(&regs->in_dma_current);		writel(curr, &regs->in_dma_end);		writel(curr, &regs->in_dma_start);		master &= ~MST_R_BITS;		master |= MST_RD_RESET;		writel(master, &regs->dma_master);		if (readl(&regs->dma_master) & MST_RD_ENA)			DBG(ep->dev, "IN dma active after reset!\n");	} else {		if (unlikely((readl(&regs->dma_master) & MST_WR_ENA) == 0))			goto finished;		curr = readl(&regs->out_dma_current);		writel(curr, &regs->out_dma_end);		writel(curr, &regs->out_dma_start);		master &= ~MST_W_BITS;		master |= MST_WR_RESET;		writel(master, &regs->dma_master);		if (readl(&regs->dma_master) & MST_WR_ENA)			DBG(ep->dev, "OUT dma active after reset!\n");	}	req->req.actual = (curr - req->req.dma) + 1;	req->req.status = status;	VDBG(ep->dev, "%s %s %s %d/%d\n", __FUNCTION__, ep->ep.name,		ep->is_in ? "IN" : "OUT",		req->req.actual, req->req.length);	command(regs, COMMAND_FIFO_ENABLE, ep->num);	return;finished:	/* dma already completed; no abort needed */	command(regs, COMMAND_FIFO_ENABLE, ep->num);	req->req.actual = req->req.length;	req->req.status = 0;}/*-------------------------------------------------------------------------*/static intgoku_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags){	struct goku_request	*req;	struct goku_ep		*ep;	struct goku_udc		*dev;	unsigned long		flags;	int			status;	/* always require a cpu-view buffer so pio works */	req = container_of(_req, struct goku_request, req);	if (unlikely(!_req || !_req->complete			|| !_req->buf || !list_empty(&req->queue)))		return -EINVAL;	ep = container_of(_ep, struct goku_ep, ep);	if (unlikely(!_ep || (!ep->desc && ep->num != 0)))		return -EINVAL;	dev = ep->dev;	if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))		return -ESHUTDOWN;	/* can't touch registers when suspended */	if (dev->ep0state == EP0_SUSPEND)		return -EBUSY;	/* set up dma mapping in case the caller didn't */	if (ep->dma && _req->dma == DMA_ADDR_INVALID) {		_req->dma = pci_map_single(dev->pdev, _req->buf, _req->length,			ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);		req->mapped = 1;	}#ifdef USB_TRACE	VDBG(dev, "%s queue req %p, len %u buf %p\n",			_ep->name, _req, _req->length, _req->buf);#endif	spin_lock_irqsave(&dev->lock, flags);	_req->status = -EINPROGRESS;	_req->actual = 0;	/* for ep0 IN without premature status, zlp is required and	 * writing EOP starts the status stage (OUT).	 */	if (unlikely(ep->num == 0 && ep->is_in))		_req->zero = 1;	/* kickstart this i/o queue? */	status = 0;	if (list_empty(&ep->queue) && likely(!ep->stopped)) {		/* dma:  done after dma completion IRQ (or error)		 * pio:  done after last fifo operation		 */		if (ep->dma)			status = start_dma(ep, req);		else			status = (ep->is_in ? write_fifo : read_fifo)(ep, req);		if (unlikely(status != 0)) {			if (status > 0)				status = 0;			req = 0;		}	} /* else pio or dma irq handler advances the queue. */	if (likely(req != 0))		list_add_tail(&req->queue, &ep->queue);	if (likely(!list_empty(&ep->queue))			&& likely(ep->num != 0)			&& !ep->dma			&& !(dev->int_enable & INT_EPxDATASET (ep->num)))		pio_irq_enable(dev, dev->regs, ep->num);	spin_unlock_irqrestore(&dev->lock, flags);	/* pci writes may still be posted */	return status;}/* dequeue ALL requests */static void nuke(struct goku_ep *ep, int status){	struct goku_request	*req;	ep->stopped = 1;	if (list_empty(&ep->queue))		return;	if (ep->dma)		abort_dma(ep, status);	while (!list_empty(&ep->queue)) {		req = list_entry(ep->queue.next, struct goku_request, queue);		done(ep, req, status);	}}/* dequeue JUST ONE request */static int goku_dequeue(struct usb_ep *_ep, struct usb_request *_req){	struct goku_request	*req;	struct goku_ep		*ep;	struct goku_udc		*dev;	unsigned long		flags;	ep = container_of(_ep, struct goku_ep, ep);	if (!_ep || !_req || (!ep->desc && ep->num != 0))		return -EINVAL;	dev = ep->dev;	if (!dev->driver)		return -ESHUTDOWN;	/* we can't touch (dma) registers when suspended */	if (dev->ep0state == EP0_SUSPEND)		return -EBUSY;	VDBG(dev, "%s %s %s %s %p\n", __FUNCTION__, _ep->name,		ep->is_in ? "IN" : "OUT",		ep->dma ? "dma" : "pio",		_req);	spin_lock_irqsave(&dev->lock, flags);	/* make sure it's actually queued on this endpoint */	list_for_each_entry (req, &ep->queue, queue) {		if (&req->req == _req)			break;	}	if (&req->req != _req) {		spin_unlock_irqrestore (&dev->lock, flags);		return -EINVAL;	}	if (ep->dma && ep->queue.next == &req->queue && !ep->stopped) {		abort_dma(ep, -ECONNRESET);		done(ep, req, -ECONNRESET);		dma_advance(dev, ep);	} else if (!list_empty(&req->queue))		done(ep, req, -ECONNRESET);	else		req = 0;	spin_unlock_irqrestore(&dev->lock, flags);	return req ? 0 : -EOPNOTSUPP;}/*-------------------------------------------------------------------------*/static void goku_clear_halt(struct goku_ep *ep){	// assert (ep->num !=0)	VDBG(ep->dev, "%s clear halt\n", ep->ep.name);	command(ep->dev->regs, COMMAND_SETDATA0, ep->num);	command(ep->dev->regs, COMMAND_STALL_CLEAR, ep->num);	if (ep->stopped) {		ep->stopped = 0;		if (ep->dma) {			struct goku_request	*req;			if (list_empty(&ep->queue))				return;			req = list_entry(ep->queue.next, struct goku_request,						queue);			(void) start_dma(ep, req);		} else			pio_advance(ep);	}}static int goku_set_halt(struct usb_ep *_ep, int value){	struct goku_ep	*ep;	unsigned long	flags;	int		retval = 0;	if (!_ep)		return -ENODEV;	ep = container_of (_ep, struct goku_ep, ep);	if (ep->num == 0) {		if (value) {			ep->dev->ep0state = EP0_STALL;			ep->dev->ep[0].stopped = 1;		} else			return -EINVAL;	/* don't change EPxSTATUS_EP_INVALID to READY */	} else if (!ep->desc) {		DBG(ep->dev, "%s %s inactive?\n", __FUNCTION__, ep->ep.name);		return -EINVAL;	}	spin_lock_irqsave(&ep->dev->lock, flags);	if (!list_empty(&ep->queue))		retval = -EAGAIN;	else if (ep->is_in && value			/* data in (either) packet buffer? */

⌨️ 快捷键说明

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