at91_udc.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,774 行 · 第 1/3 页

C
1,774
字号
	INIT_LIST_HEAD(&req->queue);	return &req->req;}static void at91_ep_free_request(struct usb_ep *_ep, struct usb_request *_req){	struct at91_request *req;	req = container_of(_req, struct at91_request, req);	BUG_ON(!list_empty(&req->queue));	kfree(req);}static void *at91_ep_alloc_buffer(	struct usb_ep *_ep,	unsigned bytes,	dma_addr_t *dma,	gfp_t gfp_flags){	*dma = ~0;	return kmalloc(bytes, gfp_flags);}static void at91_ep_free_buffer(	struct usb_ep *ep,	void *buf,	dma_addr_t dma,	unsigned bytes){	kfree(buf);}static int at91_ep_queue(struct usb_ep *_ep,			struct usb_request *_req, gfp_t gfp_flags){	struct at91_request	*req;	struct at91_ep		*ep;	struct at91_udc		*dev;	int			status;	unsigned long		flags;	req = container_of(_req, struct at91_request, req);	ep = container_of(_ep, struct at91_ep, ep);	if (!_req || !_req->complete			|| !_req->buf || !list_empty(&req->queue)) {		DBG("invalid request\n");		return -EINVAL;	}	if (!_ep || (!ep->desc && ep->ep.name != ep0name)) {		DBG("invalid ep\n");		return -EINVAL;	}	dev = ep->udc;	if (!dev || !dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {		DBG("invalid device\n");		return -EINVAL;	}	_req->status = -EINPROGRESS;	_req->actual = 0;	local_irq_save(flags);	/* try to kickstart any empty and idle queue */	if (list_empty(&ep->queue) && !ep->stopped) {		int	is_ep0;		/*		 * If this control request has a non-empty DATA stage, this		 * will start that stage.  It works just like a non-control		 * request (until the status stage starts, maybe early).		 *		 * If the data stage is empty, then this starts a successful		 * IN/STATUS stage.  (Unsuccessful ones use set_halt.)		 */		is_ep0 = (ep->ep.name == ep0name);		if (is_ep0) {			u32	tmp;			if (!dev->req_pending) {				status = -EINVAL;				goto done;			}			/*			 * defer changing CONFG until after the gadget driver			 * reconfigures the endpoints.			 */			if (dev->wait_for_config_ack) {				tmp = at91_udp_read(AT91_UDP_GLB_STAT);				tmp ^= AT91_UDP_CONFG;				VDBG("toggle config\n");				at91_udp_write(AT91_UDP_GLB_STAT, tmp);			}			if (req->req.length == 0) {ep0_in_status:				PACKET("ep0 in/status\n");				status = 0;				tmp = __raw_readl(ep->creg);				tmp &= ~SET_FX;				tmp |= CLR_FX | AT91_UDP_TXPKTRDY;				__raw_writel(tmp, ep->creg);				dev->req_pending = 0;				goto done;			}		}		if (ep->is_in)			status = write_fifo(ep, req);		else {			status = read_fifo(ep, req);			/* IN/STATUS stage is otherwise triggered by irq */			if (status && is_ep0)				goto ep0_in_status;		}	} else		status = 0;	if (req && !status) {		list_add_tail (&req->queue, &ep->queue);		at91_udp_write(AT91_UDP_IER, ep->int_mask);	}done:	local_irq_restore(flags);	return (status < 0) ? status : 0;}static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req){	struct at91_ep	*ep;	struct at91_request	*req;	ep = container_of(_ep, struct at91_ep, ep);	if (!_ep || ep->ep.name == ep0name)		return -EINVAL;	/* 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)		return -EINVAL;	done(ep, req, -ECONNRESET);	return 0;}static int at91_ep_set_halt(struct usb_ep *_ep, int value){	struct at91_ep	*ep = container_of(_ep, struct at91_ep, ep);	u32 __iomem	*creg;	u32		csr;	unsigned long	flags;	int		status = 0;	if (!_ep || ep->is_iso || !ep->udc->clocked)		return -EINVAL;	creg = ep->creg;	local_irq_save(flags);	csr = __raw_readl(creg);	/*	 * fail with still-busy IN endpoints, ensuring correct sequencing	 * of data tx then stall.  note that the fifo rx bytecount isn't	 * completely accurate as a tx bytecount.	 */	if (ep->is_in && (!list_empty(&ep->queue) || (csr >> 16) != 0))		status = -EAGAIN;	else {		csr |= CLR_FX;		csr &= ~SET_FX;		if (value) {			csr |= AT91_UDP_FORCESTALL;			VDBG("halt %s\n", ep->ep.name);		} else {			at91_udp_write(AT91_UDP_RST_EP, ep->int_mask);			at91_udp_write(AT91_UDP_RST_EP, 0);			csr &= ~AT91_UDP_FORCESTALL;		}		__raw_writel(csr, creg);	}	local_irq_restore(flags);	return status;}static struct usb_ep_ops at91_ep_ops = {	.enable		= at91_ep_enable,	.disable	= at91_ep_disable,	.alloc_request	= at91_ep_alloc_request,	.free_request	= at91_ep_free_request,	.alloc_buffer	= at91_ep_alloc_buffer,	.free_buffer	= at91_ep_free_buffer,	.queue		= at91_ep_queue,	.dequeue	= at91_ep_dequeue,	.set_halt	= at91_ep_set_halt,	// there's only imprecise fifo status reporting};/*-------------------------------------------------------------------------*/static int at91_get_frame(struct usb_gadget *gadget){	if (!to_udc(gadget)->clocked)		return -EINVAL;	return at91_udp_read(AT91_UDP_FRM_NUM) & AT91_UDP_NUM;}static int at91_wakeup(struct usb_gadget *gadget){	struct at91_udc	*udc = to_udc(gadget);	u32		glbstate;	int		status = -EINVAL;	unsigned long	flags;	DBG("%s\n", __FUNCTION__ );	local_irq_save(flags);	if (!udc->clocked || !udc->suspended)		goto done;	/* NOTE:  some "early versions" handle ESR differently ... */	glbstate = at91_udp_read(AT91_UDP_GLB_STAT);	if (!(glbstate & AT91_UDP_ESR))		goto done;	glbstate |= AT91_UDP_ESR;	at91_udp_write(AT91_UDP_GLB_STAT, glbstate);done:	local_irq_restore(flags);	return status;}/* reinit == restore inital software state */static void udc_reinit(struct at91_udc *udc){	u32 i;	INIT_LIST_HEAD(&udc->gadget.ep_list);	INIT_LIST_HEAD(&udc->gadget.ep0->ep_list);	for (i = 0; i < NUM_ENDPOINTS; i++) {		struct at91_ep *ep = &udc->ep[i];		if (i != 0)			list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);		ep->desc = NULL;		ep->stopped = 0;		ep->fifo_bank = 0;		ep->ep.maxpacket = ep->maxpacket;		// initialiser une queue par endpoint		INIT_LIST_HEAD(&ep->queue);	}}static void stop_activity(struct at91_udc *udc){	struct usb_gadget_driver *driver = udc->driver;	int i;	if (udc->gadget.speed == USB_SPEED_UNKNOWN)		driver = NULL;	udc->gadget.speed = USB_SPEED_UNKNOWN;	for (i = 0; i < NUM_ENDPOINTS; i++) {		struct at91_ep *ep = &udc->ep[i];		ep->stopped = 1;		nuke(ep, -ESHUTDOWN);	}	if (driver)		driver->disconnect(&udc->gadget);	udc_reinit(udc);}static void clk_on(struct at91_udc *udc){	if (udc->clocked)		return;	udc->clocked = 1;	clk_enable(udc->iclk);	clk_enable(udc->fclk);}static void clk_off(struct at91_udc *udc){	if (!udc->clocked)		return;	udc->clocked = 0;	udc->gadget.speed = USB_SPEED_UNKNOWN;	clk_disable(udc->iclk);	clk_disable(udc->fclk);}/* * activate/deactivate link with host; minimize power usage for * inactive links by cutting clocks and transceiver power. */static void pullup(struct at91_udc *udc, int is_on){	if (!udc->enabled || !udc->vbus)		is_on = 0;	DBG("%sactive\n", is_on ? "" : "in");	if (is_on) {		clk_on(udc);		at91_udp_write(AT91_UDP_TXVC, 0);		at91_set_gpio_value(udc->board.pullup_pin, 1);	} else  {		stop_activity(udc);		at91_udp_write(AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);		at91_set_gpio_value(udc->board.pullup_pin, 0);		clk_off(udc);		// REVISIT:  with transceiver disabled, will D- float		// so that a host would falsely detect a device?	}}/* 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);	pullup(udc, is_active);	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(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(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(AT91_UDP_GLB_STAT);		tmp |= AT91_UDP_ESR;		at91_udp_write(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(AT91_UDP_GLB_STAT);		tmp &= ~AT91_UDP_ESR;		at91_udp_write(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)

⌨️ 快捷键说明

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