📄 lh7a40x_udc.c
字号:
return 1;}static int lh7a40x_handle_get_status(struct lh7a40x_udc *dev, struct usb_ctrlrequest *ctrl){ struct lh7a40x_ep *ep0 = &dev->ep[0]; struct lh7a40x_ep *qep; int reqtype = (ctrl->bRequestType & USB_RECIP_MASK); u16 val = 0; if (reqtype == USB_RECIP_INTERFACE) { /* This is not supported. * And according to the USB spec, this one does nothing.. * Just return 0 */ DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n"); } else if (reqtype == USB_RECIP_DEVICE) { DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n"); val |= (1 << 0); /* Self powered */ /*val |= (1<<1); *//* Remote wakeup */ } else if (reqtype == USB_RECIP_ENDPOINT) { int ep_num = (ctrl->wIndex & ~USB_DIR_IN); DEBUG_SETUP ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n", ep_num, ctrl->wLength); if (ctrl->wLength > 2 || ep_num > 3) return -EOPNOTSUPP; qep = &dev->ep[ep_num]; if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0) && ep_index(qep) != 0) { return -EOPNOTSUPP; } usb_set_index(ep_index(qep)); /* Return status on next IN token */ switch (qep->ep_type) { case ep_control: val = (usb_read(qep->csr1) & EP0_SEND_STALL) == EP0_SEND_STALL; break; case ep_bulk_in: case ep_interrupt: val = (usb_read(qep->csr1) & USB_IN_CSR1_SEND_STALL) == USB_IN_CSR1_SEND_STALL; break; case ep_bulk_out: val = (usb_read(qep->csr1) & USB_OUT_CSR1_SEND_STALL) == USB_OUT_CSR1_SEND_STALL; break; } /* Back to EP0 index */ usb_set_index(0); DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num, ctrl->wIndex, val); } else { DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype); return -EOPNOTSUPP; } /* Clear "out packet ready" */ usb_set((EP0_CLR_OUT), USB_EP0_CSR); /* Put status to FIFO */ lh7a40x_fifo_write(ep0, (u8 *) & val, sizeof(val)); /* Issue "In packet ready" */ usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); return 0;}/* * WAIT_FOR_SETUP (OUT_PKT_RDY) * - read data packet from EP0 FIFO * - decode command * - if error * set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits * - else * set EP0_CLR_OUT | EP0_DATA_END bits */static void lh7a40x_ep0_setup(struct lh7a40x_udc *dev, u32 csr){ struct lh7a40x_ep *ep = &dev->ep[0]; struct usb_ctrlrequest ctrl; int i, bytes, is_in; DEBUG_SETUP("%s: %x\n", __FUNCTION__, csr); /* Nuke all previous transfers */ nuke(ep, -EPROTO); /* read control req from fifo (8 bytes) */ bytes = lh7a40x_fifo_read(ep, (unsigned char *)&ctrl, 8); DEBUG_SETUP("Read CTRL REQ %d bytes\n", bytes); DEBUG_SETUP("CTRL.bRequestType = %d (is_in %d)\n", ctrl.bRequestType, ctrl.bRequestType == USB_DIR_IN); DEBUG_SETUP("CTRL.bRequest = %d\n", ctrl.bRequest); DEBUG_SETUP("CTRL.wLength = %d\n", ctrl.wLength); DEBUG_SETUP("CTRL.wValue = %d (%d)\n", ctrl.wValue, ctrl.wValue >> 8); DEBUG_SETUP("CTRL.wIndex = %d\n", ctrl.wIndex); /* Set direction of EP0 */ if (likely(ctrl.bRequestType & USB_DIR_IN)) { ep->bEndpointAddress |= USB_DIR_IN; is_in = 1; } else { ep->bEndpointAddress &= ~USB_DIR_IN; is_in = 0; } dev->req_pending = 1; /* Handle some SETUP packets ourselves */ switch (ctrl.bRequest) { case USB_REQ_SET_ADDRESS: if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) break; DEBUG_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue); udc_set_address(dev, ctrl.wValue); usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); return; case USB_REQ_GET_STATUS:{ if (lh7a40x_handle_get_status(dev, &ctrl) == 0) return; case USB_REQ_CLEAR_FEATURE: case USB_REQ_SET_FEATURE: if (ctrl.bRequestType == USB_RECIP_ENDPOINT) { struct lh7a40x_ep *qep; int ep_num = (ctrl.wIndex & 0x0f); /* Support only HALT feature */ if (ctrl.wValue != 0 || ctrl.wLength != 0 || ep_num > 3 || ep_num < 1) break; qep = &dev->ep[ep_num]; if (ctrl.bRequest == USB_REQ_SET_FEATURE) { DEBUG_SETUP("SET_FEATURE (%d)\n", ep_num); lh7a40x_set_halt(&qep->ep, 1); } else { DEBUG_SETUP("CLR_FEATURE (%d)\n", ep_num); lh7a40x_set_halt(&qep->ep, 0); } usb_set_index(0); /* Reply with a ZLP on next IN token */ usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); return; } break; } default: break; } if (likely(dev->driver)) { /* device-2-host (IN) or no data setup command, process immediately */ spin_unlock(&dev->lock); i = dev->driver->setup(&dev->gadget, &ctrl); spin_lock(&dev->lock); if (i < 0) { /* setup processing failed, force stall */ DEBUG_SETUP (" --> ERROR: gadget setup FAILED (stalling), setup returned %d\n", i); usb_set_index(0); usb_set((EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL), USB_EP0_CSR); /* ep->stopped = 1; */ dev->ep0state = WAIT_FOR_SETUP; } }}/* * DATA_STATE_NEED_ZLP */static void lh7a40x_ep0_in_zlp(struct lh7a40x_udc *dev, u32 csr){ DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); /* c.f. Table 15-14 */ usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); dev->ep0state = WAIT_FOR_SETUP;}/* * handle ep0 interrupt */static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr){ struct lh7a40x_ep *ep = &dev->ep[0]; u32 csr; /* Set index 0 */ usb_set_index(0); csr = usb_read(USB_EP0_CSR); DEBUG_EP0("%s: csr = %x\n", __FUNCTION__, csr); /* * For overview of what we should be doing see c.f. Chapter 18.1.2.4 * We will follow that outline here modified by our own global state * indication which provides hints as to what we think should be * happening.. */ /* * if SENT_STALL is set * - clear the SENT_STALL bit */ if (csr & EP0_SENT_STALL) { DEBUG_EP0("%s: EP0_SENT_STALL is set: %x\n", __FUNCTION__, csr); usb_clear((EP0_SENT_STALL | EP0_SEND_STALL), USB_EP0_CSR); nuke(ep, -ECONNABORTED); dev->ep0state = WAIT_FOR_SETUP; return; } /* * if a transfer is in progress && IN_PKT_RDY and OUT_PKT_RDY are clear * - fill EP0 FIFO * - if last packet * - set IN_PKT_RDY | DATA_END * - else * set IN_PKT_RDY */ if (!(csr & (EP0_IN_PKT_RDY | EP0_OUT_PKT_RDY))) { DEBUG_EP0("%s: IN_PKT_RDY and OUT_PKT_RDY are clear\n", __FUNCTION__); switch (dev->ep0state) { case DATA_STATE_XMIT: DEBUG_EP0("continue with DATA_STATE_XMIT\n"); lh7a40x_ep0_in(dev, csr); return; case DATA_STATE_NEED_ZLP: DEBUG_EP0("continue with DATA_STATE_NEED_ZLP\n"); lh7a40x_ep0_in_zlp(dev, csr); return; default: /* Stall? */ DEBUG_EP0("Odd state!! state = %s\n", state_names[dev->ep0state]); dev->ep0state = WAIT_FOR_SETUP; /* nuke(ep, 0); */ /* usb_set(EP0_SEND_STALL, ep->csr1); */ break; } } /* * if SETUP_END is set * - abort the last transfer * - set SERVICED_SETUP_END_BIT */ if (csr & EP0_SETUP_END) { DEBUG_EP0("%s: EP0_SETUP_END is set: %x\n", __FUNCTION__, csr); usb_set(EP0_CLR_SETUP_END, USB_EP0_CSR); nuke(ep, 0); dev->ep0state = WAIT_FOR_SETUP; } /* * if EP0_OUT_PKT_RDY is set * - read data packet from EP0 FIFO * - decode command * - if error * set SERVICED_OUT_PKT_RDY | DATA_END bits | SEND_STALL * - else * set SERVICED_OUT_PKT_RDY | DATA_END bits */ if (csr & EP0_OUT_PKT_RDY) { DEBUG_EP0("%s: EP0_OUT_PKT_RDY is set: %x\n", __FUNCTION__, csr); switch (dev->ep0state) { case WAIT_FOR_SETUP: DEBUG_EP0("WAIT_FOR_SETUP\n"); lh7a40x_ep0_setup(dev, csr); break; case DATA_STATE_RECV: DEBUG_EP0("DATA_STATE_RECV\n"); lh7a40x_ep0_out(dev, csr); break; default: /* send stall? */ DEBUG_EP0("strange state!! 2. send stall? state = %d\n", dev->ep0state); break; } }}static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep){ u32 csr; usb_set_index(0); csr = usb_read(USB_EP0_CSR); DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); /* Clear "out packet ready" */ usb_set(EP0_CLR_OUT, USB_EP0_CSR); if (ep_is_in(ep)) { dev->ep0state = DATA_STATE_XMIT; lh7a40x_ep0_in(dev, csr); } else { dev->ep0state = DATA_STATE_RECV; lh7a40x_ep0_out(dev, csr); }}/* --------------------------------------------------------------------------- * device-scoped parts of the api to the usb controller hardware * --------------------------------------------------------------------------- */static int lh7a40x_udc_get_frame(struct usb_gadget *_gadget){ u32 frame1 = usb_read(USB_FRM_NUM1); /* Least significant 8 bits */ u32 frame2 = usb_read(USB_FRM_NUM2); /* Most significant 3 bits */ DEBUG("%s, %p\n", __FUNCTION__, _gadget); return ((frame2 & 0x07) << 8) | (frame1 & 0xff);}static int lh7a40x_udc_wakeup(struct usb_gadget *_gadget){ /* host may not have enabled remote wakeup */ /*if ((UDCCS0 & UDCCS0_DRWF) == 0) return -EHOSTUNREACH; udc_set_mask_UDCCR(UDCCR_RSM); */ return -ENOTSUPP;}static const struct usb_gadget_ops lh7a40x_udc_ops = { .get_frame = lh7a40x_udc_get_frame, .wakeup = lh7a40x_udc_wakeup, /* current versions must always be self-powered */};static void nop_release(struct device *dev){ DEBUG("%s %s\n", __FUNCTION__, dev->bus_id);}static struct lh7a40x_udc memory = { .usb_address = 0, .gadget = { .ops = &lh7a40x_udc_ops, .ep0 = &memory.ep[0].ep, .name = driver_name, .dev = { .bus_id = "gadget", .release = nop_release, }, }, /* control endpoint */ .ep[0] = { .ep = { .name = ep0name, .ops = &lh7a40x_ep_ops, .maxpacket = EP0_PACKETSIZE, }, .dev = &memory, .bEndpointAddress = 0, .bmAttributes = 0, .ep_type = ep_control, .fifo = io_p2v(USB_EP0_FIFO), .csr1 = USB_EP0_CSR, .csr2 = USB_EP0_CSR, }, /* first group of endpoints */ .ep[1] = { .ep = { .name = "ep1in-bulk", .ops = &lh7a40x_ep_ops, .maxpacket = 64, }, .dev = &memory, .bEndpointAddress = USB_DIR_IN | 1, .bmAttributes = USB_ENDPOINT_XFER_BULK, .ep_type = ep_bulk_in, .fifo = io_p2v(USB_EP1_FIFO), .csr1 = USB_IN_CSR1, .csr2 = USB_IN_CSR2, }, .ep[2] = { .ep = { .name = "ep2out-bulk", .ops = &lh7a40x_ep_ops, .maxpacket = 64, }, .dev = &memory, .bEndpointAddress = 2, .bmAttributes = USB_ENDPOINT_XFER_BULK, .ep_type = ep_bulk_out, .fifo = io_p2v(USB_EP2_FIFO), .csr1 = USB_OUT_CSR1, .csr2 = USB_OUT_CSR2, }, .ep[3] = { .ep = { .name = "ep3in-int", .ops = &lh7a40x_ep_ops, .maxpacket = 64, }, .dev = &memory, .bEndpointAddress = USB_DIR_IN | 3, .bmAttributes = USB_ENDPOINT_XFER_INT, .ep_type = ep_interrupt, .fifo = io_p2v(USB_EP3_FIFO), .csr1 = USB_IN_CSR1, .csr2 = USB_IN_CSR2, },};/* * probe - binds to the platform device */static int lh7a40x_udc_probe(struct platform_device *pdev){ struct lh7a40x_udc *dev = &memory; int retval; DEBUG("%s: %p\n", __FUNCTION__, pdev); spin_lock_init(&dev->lock); dev->dev = &pdev->dev; device_initialize(&dev->gadget.dev); dev->gadget.dev.parent = &pdev->dev; the_controller = dev; platform_set_drvdata(pdev, dev); udc_disable(dev); udc_reinit(dev); /* irq setup after old hardware state is cleaned up */ retval = request_irq(IRQ_USBINTR, lh7a40x_udc_irq, SA_INTERRUPT, driver_name, dev); if (retval != 0) { DEBUG(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, IRQ_USBINTR, retval); return -EBUSY; } create_proc_files(); return retval;}static int lh7a40x_udc_remove(struct platform_device *pdev){ struct lh7a40x_udc *dev = platform_get_drvdata(pdev); DEBUG("%s: %p\n", __FUNCTION__, pdev); udc_disable(dev); remove_proc_files(); usb_gadget_unregister_driver(dev->driver); free_irq(IRQ_USBINTR, dev); platform_set_drvdata(pdev, 0); the_controller = 0; return 0;}/*-------------------------------------------------------------------------*/static struct platform_driver udc_driver = { .probe = lh7a40x_udc_probe, .remove = lh7a40x_udc_remove /* FIXME power management support */ /* .suspend = ... disable UDC */ /* .resume = ... re-enable UDC */ .driver = { .name = (char *)driver_name, .owner = THIS_MODULE, },};static int __init udc_init(void){ DEBUG("%s: %s version %s\n", __FUNCTION__, driver_name, DRIVER_VERSION); return platform_driver_register(&udc_driver);}static void __exit udc_exit(void){ platform_driver_unregister(&udc_driver);}module_init(udc_init);module_exit(udc_exit);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_AUTHOR("Mikko Lahteenmaki, Bo Henriksen");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -