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

📄 at91_udc.c

📁 linux下面gadget设备驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
		}		clk_off(udc);	}}/* vbus is here!  turn everything on that's ready */static int at91_vbus_session(struct usb_gadget *gadget, int is_active){	struct at91_udc	*udc = to_udc(gadget);	unsigned long	flags;	// VDBG("vbus %s\n", is_active ? "on" : "off");	local_irq_save(flags);	udc->vbus = (is_active != 0);	if (udc->driver)		pullup(udc, is_active);	else		pullup(udc, 0);	local_irq_restore(flags);	return 0;}static int at91_pullup(struct usb_gadget *gadget, int is_on){	struct at91_udc	*udc = to_udc(gadget);	unsigned long	flags;	local_irq_save(flags);	udc->enabled = is_on = !!is_on;	pullup(udc, is_on);	local_irq_restore(flags);	return 0;}static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on){	struct at91_udc	*udc = to_udc(gadget);	unsigned long	flags;	local_irq_save(flags);	udc->selfpowered = (is_on != 0);	local_irq_restore(flags);	return 0;}static const struct usb_gadget_ops at91_udc_ops = {	.get_frame		= at91_get_frame,	.wakeup			= at91_wakeup,	.set_selfpowered	= at91_set_selfpowered,	.vbus_session		= at91_vbus_session,	.pullup			= at91_pullup,	/*	 * VBUS-powered devices may also also want to support bigger	 * power budgets after an appropriate SET_CONFIGURATION.	 */	// .vbus_power		= at91_vbus_power,};/*-------------------------------------------------------------------------*/static int handle_ep(struct at91_ep *ep){	struct at91_request	*req;	u32 __iomem		*creg = ep->creg;	u32			csr = __raw_readl(creg);	if (!list_empty(&ep->queue))		req = list_entry(ep->queue.next,			struct at91_request, queue);	else		req = NULL;	if (ep->is_in) {		if (csr & (AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)) {			csr |= CLR_FX;			csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP);			__raw_writel(csr, creg);		}		if (req)			return write_fifo(ep, req);	} else {		if (csr & AT91_UDP_STALLSENT) {			/* STALLSENT bit == ISOERR */			if (ep->is_iso && req)				req->req.status = -EILSEQ;			csr |= CLR_FX;			csr &= ~(SET_FX | AT91_UDP_STALLSENT);			__raw_writel(csr, creg);			csr = __raw_readl(creg);		}		if (req && (csr & RX_DATA_READY))			return read_fifo(ep, req);	}	return 0;}union setup {	u8			raw[8];	struct usb_ctrlrequest	r;};static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr){	u32 __iomem	*creg = ep->creg;	u8 __iomem	*dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0));	unsigned	rxcount, i = 0;	u32		tmp;	union setup	pkt;	int		status = 0;	/* read and ack SETUP; hard-fail for bogus packets */	rxcount = (csr & AT91_UDP_RXBYTECNT) >> 16;	if (likely(rxcount == 8)) {		while (rxcount--)			pkt.raw[i++] = __raw_readb(dreg);		if (pkt.r.bRequestType & USB_DIR_IN) {			csr |= AT91_UDP_DIR;			ep->is_in = 1;		} else {			csr &= ~AT91_UDP_DIR;			ep->is_in = 0;		}	} else {		// REVISIT this happens sometimes under load; why??		ERR("SETUP len %d, csr %08x\n", rxcount, csr);		status = -EINVAL;	}	csr |= CLR_FX;	csr &= ~(SET_FX | AT91_UDP_RXSETUP);	__raw_writel(csr, creg);	udc->wait_for_addr_ack = 0;	udc->wait_for_config_ack = 0;	ep->stopped = 0;	if (unlikely(status != 0))		goto stall;#define w_index		le16_to_cpu(pkt.r.wIndex)#define w_value		le16_to_cpu(pkt.r.wValue)#define w_length	le16_to_cpu(pkt.r.wLength)	VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n",			pkt.r.bRequestType, pkt.r.bRequest,			w_value, w_index, w_length);	/*	 * A few standard requests get handled here, ones that touch	 * hardware ... notably for device and endpoint features.	 */	udc->req_pending = 1;	csr = __raw_readl(creg);	csr |= CLR_FX;	csr &= ~SET_FX;	switch ((pkt.r.bRequestType << 8) | pkt.r.bRequest) {	case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)			| USB_REQ_SET_ADDRESS:		__raw_writel(csr | AT91_UDP_TXPKTRDY, creg);		udc->addr = w_value;		udc->wait_for_addr_ack = 1;		udc->req_pending = 0;		/* FADDR is set later, when we ack host STATUS */		return;	case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)			| USB_REQ_SET_CONFIGURATION:		tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT) & AT91_UDP_CONFG;		if (pkt.r.wValue)			udc->wait_for_config_ack = (tmp == 0);		else			udc->wait_for_config_ack = (tmp != 0);		if (udc->wait_for_config_ack)			VDBG("wait for config\n");		/* CONFG is toggled later, if gadget driver succeeds */		break;	/*	 * Hosts may set or clear remote wakeup status, and	 * devices may report they're VBUS powered.	 */	case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)			| USB_REQ_GET_STATUS:		tmp = (udc->selfpowered << USB_DEVICE_SELF_POWERED);		if (at91_udp_read(udc, AT91_UDP_GLB_STAT) & AT91_UDP_ESR)			tmp |= (1 << USB_DEVICE_REMOTE_WAKEUP);		PACKET("get device status\n");		__raw_writeb(tmp, dreg);		__raw_writeb(0, dreg);		goto write_in;		/* then STATUS starts later, automatically */	case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)			| USB_REQ_SET_FEATURE:		if (w_value != USB_DEVICE_REMOTE_WAKEUP)			goto stall;		tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);		tmp |= AT91_UDP_ESR;		at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);		goto succeed;	case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)			| USB_REQ_CLEAR_FEATURE:		if (w_value != USB_DEVICE_REMOTE_WAKEUP)			goto stall;		tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);		tmp &= ~AT91_UDP_ESR;		at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);		goto succeed;	/*	 * Interfaces have no feature settings; this is pretty useless.	 * we won't even insist the interface exists...	 */	case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)			| USB_REQ_GET_STATUS:		PACKET("get interface status\n");		__raw_writeb(0, dreg);		__raw_writeb(0, dreg);		goto write_in;		/* then STATUS starts later, automatically */	case ((USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)			| USB_REQ_SET_FEATURE:	case ((USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8)			| USB_REQ_CLEAR_FEATURE:		goto stall;	/*	 * Hosts may clear bulk/intr endpoint halt after the gadget	 * driver sets it (not widely used); or set it (for testing)	 */	case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)			| USB_REQ_GET_STATUS:		tmp = w_index & USB_ENDPOINT_NUMBER_MASK;		ep = &udc->ep[tmp];		if (tmp > NUM_ENDPOINTS || (tmp && !ep->desc))			goto stall;		if (tmp) {			if ((w_index & USB_DIR_IN)) {				if (!ep->is_in)					goto stall;			} else if (ep->is_in)				goto stall;		}		PACKET("get %s status\n", ep->ep.name);		if (__raw_readl(ep->creg) & AT91_UDP_FORCESTALL)			tmp = (1 << USB_ENDPOINT_HALT);		else			tmp = 0;		__raw_writeb(tmp, dreg);		__raw_writeb(0, dreg);		goto write_in;		/* then STATUS starts later, automatically */	case ((USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)			| USB_REQ_SET_FEATURE:		tmp = w_index & USB_ENDPOINT_NUMBER_MASK;		ep = &udc->ep[tmp];		if (w_value != USB_ENDPOINT_HALT || tmp > NUM_ENDPOINTS)			goto stall;		if (!ep->desc || ep->is_iso)			goto stall;		if ((w_index & USB_DIR_IN)) {			if (!ep->is_in)				goto stall;		} else if (ep->is_in)			goto stall;		tmp = __raw_readl(ep->creg);		tmp &= ~SET_FX;		tmp |= CLR_FX | AT91_UDP_FORCESTALL;		__raw_writel(tmp, ep->creg);		goto succeed;	case ((USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8)			| USB_REQ_CLEAR_FEATURE:		tmp = w_index & USB_ENDPOINT_NUMBER_MASK;		ep = &udc->ep[tmp];		if (w_value != USB_ENDPOINT_HALT || tmp > NUM_ENDPOINTS)			goto stall;		if (tmp == 0)			goto succeed;		if (!ep->desc || ep->is_iso)			goto stall;		if ((w_index & USB_DIR_IN)) {			if (!ep->is_in)				goto stall;		} else if (ep->is_in)			goto stall;		at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask);		at91_udp_write(udc, AT91_UDP_RST_EP, 0);		tmp = __raw_readl(ep->creg);		tmp |= CLR_FX;		tmp &= ~(SET_FX | AT91_UDP_FORCESTALL);		__raw_writel(tmp, ep->creg);		if (!list_empty(&ep->queue))			handle_ep(ep);		goto succeed;	}#undef w_value#undef w_index#undef w_length	/* pass request up to the gadget driver */	if (udc->driver)		status = udc->driver->setup(&udc->gadget, &pkt.r);	else		status = -ENODEV;	if (status < 0) {stall:		VDBG("req %02x.%02x protocol STALL; stat %d\n",				pkt.r.bRequestType, pkt.r.bRequest, status);		csr |= AT91_UDP_FORCESTALL;		__raw_writel(csr, creg);		udc->req_pending = 0;	}	return;succeed:	/* immediate successful (IN) STATUS after zero length DATA */	PACKET("ep0 in/status\n");write_in:	csr |= AT91_UDP_TXPKTRDY;	__raw_writel(csr, creg);	udc->req_pending = 0;	return;}static void handle_ep0(struct at91_udc *udc){	struct at91_ep		*ep0 = &udc->ep[0];	u32 __iomem		*creg = ep0->creg;	u32			csr = __raw_readl(creg);	struct at91_request	*req;	if (unlikely(csr & AT91_UDP_STALLSENT)) {		nuke(ep0, -EPROTO);		udc->req_pending = 0;		csr |= CLR_FX;		csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_FORCESTALL);		__raw_writel(csr, creg);		VDBG("ep0 stalled\n");		csr = __raw_readl(creg);	}	if (csr & AT91_UDP_RXSETUP) {		nuke(ep0, 0);		udc->req_pending = 0;		handle_setup(udc, ep0, csr);		return;	}	if (list_empty(&ep0->queue))		req = NULL;	else		req = list_entry(ep0->queue.next, struct at91_request, queue);	/* host ACKed an IN packet that we sent */	if (csr & AT91_UDP_TXCOMP) {		csr |= CLR_FX;		csr &= ~(SET_FX | AT91_UDP_TXCOMP);		/* write more IN DATA? */		if (req && ep0->is_in) {			if (handle_ep(ep0))				udc->req_pending = 0;		/*		 * Ack after:		 *  - last IN DATA packet (including GET_STATUS)		 *  - IN/STATUS for OUT DATA		 *  - IN/STATUS for any zero-length DATA stage		 * except for the IN DATA case, the host should send		 * an OUT status later, which we'll ack.		 */		} else {			udc->req_pending = 0;			__raw_writel(csr, creg);			/*			 * SET_ADDRESS takes effect only after the STATUS			 * (to the original address) gets acked.			 */			if (udc->wait_for_addr_ack) {				u32	tmp;				at91_udp_write(udc, AT91_UDP_FADDR,						AT91_UDP_FEN | udc->addr);				tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);				tmp &= ~AT91_UDP_FADDEN;				if (udc->addr)					tmp |= AT91_UDP_FADDEN;				at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);				udc->wait_for_addr_ack = 0;				VDBG("address %d\n", udc->addr);			}		}	}	/* OUT packet arrived ... */	else if (csr & AT91_UDP_RX_DATA_BK0) {		csr |= CLR_FX;		csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0);		/* OUT DATA stage */		if (!ep0->is_in) {			if (req) {				if (handle_ep(ep0)) {					/* send IN/STATUS */					PACKET("ep0 in/status\n");					csr = __raw_readl(creg);					csr &= ~SET_FX;					csr |= CLR_FX | AT91_UDP_TXPKTRDY;					__raw_writel(csr, creg);					udc->req_pending = 0;				}			} else if (udc->req_pending) {				/*				 * AT91 hardware has a hard time with this				 * "deferred response" mode for control-OUT				 * transfers.  (For control-IN it's fine.)				 *				 * The normal solution leaves OUT data in the				 * fifo until the gadget driver is ready.				 * We couldn't do that here without disabling				 * the IRQ that tells about SETUP packets,				 * e.g. when the host gets impatient...				 *				 * Working around it by copying into a buffer				 * would almost be a non-deferred response,				 * except that it wouldn't permit reliable				 * stalling of the request.  Instead, demand				 * that gadget drivers not use this mode.				 */				DBG("no control-OUT deferred responses!\n");				__raw_writel(csr | AT91_UDP_FORCESTALL, creg);				udc->req_pending = 0;			}		/* STATUS stage for control-IN; ack.  */		} else {			PACKET("ep0 out/status ACK\n");			__raw_writel(csr, creg);			/* "early" status stage */			if (req)				done(ep0, req, 0);		}	}}static irqreturn_t at91_udc_irq (int irq, void *_udc){	struct at91_udc		*udc = _udc;	u32			rescans = 5;	while (rescans--) {		u32 status;		status = at91_udp_read(udc, AT91_UDP_ISR)			& at91_udp_read(udc, AT91_UDP_IMR);		if (!status)

⌨️ 快捷键说明

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