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

📄 atmel_usba_udc.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	.fifo_size	= maxpkt,				\	.nr_banks	= maxbk,				\	.index		= idx,					\	.can_dma	= dma,					\	.can_isoc	= isoc,					\}static struct usba_ep usba_ep[] = {	EP("ep0", 0, 64, 1, 0, 0),	EP("ep1in-bulk", 1, 512, 2, 1, 1),	EP("ep2out-bulk", 2, 512, 2, 1, 1),	EP("ep3in-int", 3, 64, 3, 1, 0),	EP("ep4out-int", 4, 64, 3, 1, 0),	EP("ep5in-iso", 5, 1024, 3, 1, 1),	EP("ep6out-iso", 6, 1024, 3, 1, 1),};#undef EPstatic struct usb_endpoint_descriptor usba_ep0_desc = {	.bLength = USB_DT_ENDPOINT_SIZE,	.bDescriptorType = USB_DT_ENDPOINT,	.bEndpointAddress = 0,	.bmAttributes = USB_ENDPOINT_XFER_CONTROL,	.wMaxPacketSize = __constant_cpu_to_le16(64),	/* FIXME: I have no idea what to put here */	.bInterval = 1,};static void nop_release(struct device *dev){}static struct usba_udc the_udc = {	.gadget	= {		.ops		= &usba_udc_ops,		.ep0		= &usba_ep[0].ep,		.ep_list	= LIST_HEAD_INIT(the_udc.gadget.ep_list),		.is_dualspeed	= 1,		.name		= "atmel_usba_udc",		.dev	= {			.bus_id		= "gadget",			.release	= nop_release,		},	},	.lock	= SPIN_LOCK_UNLOCKED,};/* * Called with interrupts disabled and udc->lock held. */static void reset_all_endpoints(struct usba_udc *udc){	struct usba_ep *ep;	struct usba_request *req, *tmp_req;	usba_writel(udc, EPT_RST, ~0UL);	ep = to_usba_ep(udc->gadget.ep0);	list_for_each_entry_safe(req, tmp_req, &ep->queue, queue) {		list_del_init(&req->queue);		request_complete(ep, req, -ECONNRESET);	}	list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {		if (ep->desc) {			spin_unlock(&udc->lock);			usba_ep_disable(&ep->ep);			spin_lock(&udc->lock);		}	}}static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex){	struct usba_ep *ep;	if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)		return to_usba_ep(udc->gadget.ep0);	list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) {		u8 bEndpointAddress;		if (!ep->desc)			continue;		bEndpointAddress = ep->desc->bEndpointAddress;		if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)			continue;		if ((bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)				== (wIndex & USB_ENDPOINT_NUMBER_MASK))			return ep;	}	return NULL;}/* Called with interrupts disabled and udc->lock held */static inline void set_protocol_stall(struct usba_udc *udc, struct usba_ep *ep){	usba_ep_writel(ep, SET_STA, USBA_FORCE_STALL);	ep->state = WAIT_FOR_SETUP;}static inline int is_stalled(struct usba_udc *udc, struct usba_ep *ep){	if (usba_ep_readl(ep, STA) & USBA_FORCE_STALL)		return 1;	return 0;}static inline void set_address(struct usba_udc *udc, unsigned int addr){	u32 regval;	DBG(DBG_BUS, "setting address %u...\n", addr);	regval = usba_readl(udc, CTRL);	regval = USBA_BFINS(DEV_ADDR, addr, regval);	usba_writel(udc, CTRL, regval);}static int do_test_mode(struct usba_udc *udc){	static const char test_packet_buffer[] = {		/* JKJKJKJK * 9 */		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,		/* JJKKJJKK * 8 */		0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,		/* JJKKJJKK * 8 */		0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,		/* JJJJJJJKKKKKKK * 8 */		0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,		/* JJJJJJJK * 8 */		0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD,		/* {JKKKKKKK * 10}, JK */		0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E	};	struct usba_ep *ep;	struct device *dev = &udc->pdev->dev;	int test_mode;	test_mode = udc->test_mode;	/* Start from a clean slate */	reset_all_endpoints(udc);	switch (test_mode) {	case 0x0100:		/* Test_J */		usba_writel(udc, TST, USBA_TST_J_MODE);		dev_info(dev, "Entering Test_J mode...\n");		break;	case 0x0200:		/* Test_K */		usba_writel(udc, TST, USBA_TST_K_MODE);		dev_info(dev, "Entering Test_K mode...\n");		break;	case 0x0300:		/*		 * Test_SE0_NAK: Force high-speed mode and set up ep0		 * for Bulk IN transfers		 */		ep = &usba_ep[0];		usba_writel(udc, TST,				USBA_BF(SPEED_CFG, USBA_SPEED_CFG_FORCE_HIGH));		usba_ep_writel(ep, CFG,				USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64)				| USBA_EPT_DIR_IN				| USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK)				| USBA_BF(BK_NUMBER, 1));		if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) {			set_protocol_stall(udc, ep);			dev_err(dev, "Test_SE0_NAK: ep0 not mapped\n");		} else {			usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);			dev_info(dev, "Entering Test_SE0_NAK mode...\n");		}		break;	case 0x0400:		/* Test_Packet */		ep = &usba_ep[0];		usba_ep_writel(ep, CFG,				USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64)				| USBA_EPT_DIR_IN				| USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK)				| USBA_BF(BK_NUMBER, 1));		if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) {			set_protocol_stall(udc, ep);			dev_err(dev, "Test_Packet: ep0 not mapped\n");		} else {			usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);			usba_writel(udc, TST, USBA_TST_PKT_MODE);			copy_to_fifo(ep->fifo, test_packet_buffer,					sizeof(test_packet_buffer));			usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);			dev_info(dev, "Entering Test_Packet mode...\n");		}		break;	default:		dev_err(dev, "Invalid test mode: 0x%04x\n", test_mode);		return -EINVAL;	}	return 0;}/* Avoid overly long expressions */static inline bool feature_is_dev_remote_wakeup(struct usb_ctrlrequest *crq){	if (crq->wValue == __constant_cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP))		return true;	return false;}static inline bool feature_is_dev_test_mode(struct usb_ctrlrequest *crq){	if (crq->wValue == __constant_cpu_to_le16(USB_DEVICE_TEST_MODE))		return true;	return false;}static inline bool feature_is_ep_halt(struct usb_ctrlrequest *crq){	if (crq->wValue == __constant_cpu_to_le16(USB_ENDPOINT_HALT))		return true;	return false;}static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,		struct usb_ctrlrequest *crq){	int retval = 0;;	switch (crq->bRequest) {	case USB_REQ_GET_STATUS: {		u16 status;		if (crq->bRequestType == (USB_DIR_IN | USB_RECIP_DEVICE)) {			status = cpu_to_le16(udc->devstatus);		} else if (crq->bRequestType				== (USB_DIR_IN | USB_RECIP_INTERFACE)) {			status = __constant_cpu_to_le16(0);		} else if (crq->bRequestType				== (USB_DIR_IN | USB_RECIP_ENDPOINT)) {			struct usba_ep *target;			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));			if (!target)				goto stall;			status = 0;			if (is_stalled(udc, target))				status |= __constant_cpu_to_le16(1);		} else			goto delegate;		/* Write directly to the FIFO. No queueing is done. */		if (crq->wLength != __constant_cpu_to_le16(sizeof(status)))			goto stall;		ep->state = DATA_STAGE_IN;		__raw_writew(status, ep->fifo);		usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);		break;	}	case USB_REQ_CLEAR_FEATURE: {		if (crq->bRequestType == USB_RECIP_DEVICE) {			if (feature_is_dev_remote_wakeup(crq))				udc->devstatus					&= ~(1 << USB_DEVICE_REMOTE_WAKEUP);			else				/* Can't CLEAR_FEATURE TEST_MODE */				goto stall;		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) {			struct usba_ep *target;			if (crq->wLength != __constant_cpu_to_le16(0)					|| !feature_is_ep_halt(crq))				goto stall;			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));			if (!target)				goto stall;			usba_ep_writel(target, CLR_STA, USBA_FORCE_STALL);			if (target->index != 0)				usba_ep_writel(target, CLR_STA,						USBA_TOGGLE_CLR);		} else {			goto delegate;		}		send_status(udc, ep);		break;	}	case USB_REQ_SET_FEATURE: {		if (crq->bRequestType == USB_RECIP_DEVICE) {			if (feature_is_dev_test_mode(crq)) {				send_status(udc, ep);				ep->state = STATUS_STAGE_TEST;				udc->test_mode = le16_to_cpu(crq->wIndex);				return 0;			} else if (feature_is_dev_remote_wakeup(crq)) {				udc->devstatus |= 1 << USB_DEVICE_REMOTE_WAKEUP;			} else {				goto stall;			}		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) {			struct usba_ep *target;			if (crq->wLength != __constant_cpu_to_le16(0)					|| !feature_is_ep_halt(crq))				goto stall;			target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex));			if (!target)				goto stall;			usba_ep_writel(target, SET_STA, USBA_FORCE_STALL);		} else			goto delegate;		send_status(udc, ep);		break;	}	case USB_REQ_SET_ADDRESS:		if (crq->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE))			goto delegate;		set_address(udc, le16_to_cpu(crq->wValue));		send_status(udc, ep);		ep->state = STATUS_STAGE_ADDR;		break;	default:delegate:		spin_unlock(&udc->lock);		retval = udc->driver->setup(&udc->gadget, crq);		spin_lock(&udc->lock);	}	return retval;stall:	printk(KERN_ERR		"udc: %s: Invalid setup request: %02x.%02x v%04x i%04x l%d, "		"halting endpoint...\n",		ep->ep.name, crq->bRequestType, crq->bRequest,		le16_to_cpu(crq->wValue), le16_to_cpu(crq->wIndex),		le16_to_cpu(crq->wLength));	set_protocol_stall(udc, ep);	return -1;}static void usba_control_irq(struct usba_udc *udc, struct usba_ep *ep){	struct usba_request *req;	u32 epstatus;	u32 epctrl;restart:	epstatus = usba_ep_readl(ep, STA);	epctrl = usba_ep_readl(ep, CTL);	DBG(DBG_INT, "%s [%d]: s/%08x c/%08x\n",			ep->ep.name, ep->state, epstatus, epctrl);	req = NULL;	if (!list_empty(&ep->queue))		req = list_entry(ep->queue.next,				 struct usba_request, queue);	if ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) {		if (req->submitted)			next_fifo_transaction(ep, req);		else			submit_request(ep, req);		if (req->last_transaction) {			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);			usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);		}		goto restart;	}	if ((epstatus & epctrl) & USBA_TX_COMPLETE) {		usba_ep_writel(ep, CLR_STA, USBA_TX_COMPLETE);		switch (ep->state) {		case DATA_STAGE_IN:			usba_ep_writel(ep, CTL_ENB, USBA_RX_BK_RDY);			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);			ep->state = STATUS_STAGE_OUT;			break;		case STATUS_STAGE_ADDR:			/* Activate our new address */			usba_writel(udc, CTRL, (usba_readl(udc, CTRL)						| USBA_FADDR_EN));			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);			ep->state = WAIT_FOR_SETUP;			break;		case STATUS_STAGE_IN:			if (req) {				list_del_init(&req->queue);				request_complete(ep, req, 0);				submit_next_request(ep);			}			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);			ep->state = WAIT_FOR_SETUP;			break;		case STATUS_STAGE_TEST:			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);			ep->state = WAIT_FOR_SETUP;			if (do_test_mode(udc))				set_protocol_stall(udc, ep);			break;		default:			printk(KERN_ERR				"udc: %s: TXCOMP: Invalid endpoint state %d, "				"halting endpoint...\n",				ep->ep.name, ep->state);			set_protocol_stall(udc, ep);			break;		}		goto restart;	}	if ((epstatus & epctrl) & USBA_RX_BK_RDY) {		switch (ep->state) {		case STATUS_STAGE_OUT:			usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);			if (req) {				list_del_init(&req->queue);				request_complete(ep, req, 0);			}			ep->state = WAIT_FOR_SETUP;			break;		case DATA_STAGE_OUT:			receive_data(ep);			break;		default:			usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);			printk(KERN_ERR				"udc: %s: RXRDY: Invalid endpoint state %d, "				"halting endpoint...\n",				ep->ep.name, ep->state);			set_protocol_stall(udc, ep);			break;		}		goto restart;	}	if (epstatus & USBA_RX_SETUP) {		union {			struct usb_ctrlrequest crq;			unsigned long data[2];		} crq;		unsigned int pkt_len;		int ret;		if (ep->state != WAIT_FOR_SETUP) {			/*			 * Didn't expect a SETUP packet at this			 * point. Clean up any pending requests (which			 * may be successful).			 */			int status = -EPROTO;			/*			 * RXRDY and TXCOMP are dropped when SETUP			 * packets arrive.  Just pretend we received			 * the status packet.			 */			if (ep->state == STATUS_STAGE_OUT					|| ep->state == STATUS_STAGE_IN) {				usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);				status = 0;			}			if (req) {				list_del_init(&req->queue);				request_complete(ep, req, status);			}		}		pkt_len = USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA));		DBG(DBG_HW, "Packet length: %u\n", pkt_len);		if (pkt_len != sizeof(crq)) {			printk(KERN_WARNING "udc: Invalid packet length %u "				"(expected %lu)\n", pkt_len, sizeof(crq));			set_protocol_stall(udc, ep);			return;		}		DBG(DBG_FIFO, "Copying ctrl request from 0x%p:\n", ep->fifo);		copy_from_fifo(crq.data, ep->fifo, sizeof(crq));		/* Free up one bank in the FIFO so that we can		 * generate or receive a reply right away. */		usba_ep_writel(ep, CLR_STA, USBA_RX_SETUP);		/* printk(KERN_DEBUG "setup: %d: %02x.%02x\n",			ep->state, crq.crq.bRequestType,			crq.crq.bRequest); */		if (crq.crq.bRequestType & USB_DIR_IN) {			/*			 * The USB 2.0 spec states that "if wLength is			 * zero, there is no data transfer phase."			 * However, testusb #14 seems to actually			 * expect a data phase even if wLength = 0...			 */			ep->state = DATA_STAGE_IN;		} else {

⌨️ 快捷键说明

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