📄 atmel_usba_udc.c
字号:
.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 + -