📄 s3c2410_udc.c
字号:
if (fifo_count != avail) req->req.status = -EOVERFLOW; } else { is_last = (req->req.length <= req->req.actual) ? 1 : 0; } udc_write(idx, S3C2410_UDC_INDEX_REG); fifo_count = s3c2410_udc_fifo_count_out(); /* Only ep0 debug messages are interesting */ if (idx == 0) dprintk(DEBUG_VERBOSE, "%s fifo count : %d [last %d]\n", __func__, fifo_count,is_last); if (is_last) { if (idx == 0) { s3c2410_udc_set_ep0_de_out(base_addr); ep->dev->ep0state = EP0_IDLE; } else { udc_write(idx, S3C2410_UDC_INDEX_REG); ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG); udc_write(idx, S3C2410_UDC_INDEX_REG); udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY, S3C2410_UDC_OUT_CSR1_REG); } s3c2410_udc_done(ep, req, 0); } else { if (idx == 0) { s3c2410_udc_clear_ep0_opr(base_addr); } else { udc_write(idx, S3C2410_UDC_INDEX_REG); ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG); udc_write(idx, S3C2410_UDC_INDEX_REG); udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY, S3C2410_UDC_OUT_CSR1_REG); } } return is_last;}static int s3c2410_udc_read_fifo_crq(struct usb_ctrlrequest *crq){ unsigned char *outbuf = (unsigned char*)crq; int bytes_read = 0; udc_write(0, S3C2410_UDC_INDEX_REG); bytes_read = s3c2410_udc_fifo_count_out(); dprintk(DEBUG_NORMAL, "%s: fifo_count=%d\n", __func__, bytes_read); if (bytes_read > sizeof(struct usb_ctrlrequest)) bytes_read = sizeof(struct usb_ctrlrequest); readsb(S3C2410_UDC_EP0_FIFO_REG + base_addr, outbuf, bytes_read); dprintk(DEBUG_VERBOSE, "%s: len=%d %02x:%02x {%x,%x,%x}\n", __func__, bytes_read, crq->bRequest, crq->bRequestType, crq->wValue, crq->wIndex, crq->wLength); return bytes_read;}static int s3c2410_udc_get_status(struct s3c2410_udc *dev, struct usb_ctrlrequest *crq){ u16 status = 0; u8 ep_num = crq->wIndex & 0x7F; u8 is_in = crq->wIndex & USB_DIR_IN; switch (crq->bRequestType & USB_RECIP_MASK) { case USB_RECIP_INTERFACE: break; case USB_RECIP_DEVICE: status = dev->devstatus; break; case USB_RECIP_ENDPOINT: if (ep_num > 4 || crq->wLength > 2) return 1; if (ep_num == 0) { udc_write(0, S3C2410_UDC_INDEX_REG); status = udc_read(S3C2410_UDC_IN_CSR1_REG); status = status & S3C2410_UDC_EP0_CSR_SENDSTL; } else { udc_write(ep_num, S3C2410_UDC_INDEX_REG); if (is_in) { status = udc_read(S3C2410_UDC_IN_CSR1_REG); status = status & S3C2410_UDC_ICSR1_SENDSTL; } else { status = udc_read(S3C2410_UDC_OUT_CSR1_REG); status = status & S3C2410_UDC_OCSR1_SENDSTL; } } status = status ? 1 : 0; break; default: return 1; } /* Seems to be needed to get it working. ouch :( */ udelay(5); udc_write(status & 0xFF, S3C2410_UDC_EP0_FIFO_REG); udc_write(status >> 8, S3C2410_UDC_EP0_FIFO_REG); s3c2410_udc_set_ep0_de_in(base_addr); return 0;}/*------------------------- usb state machine -------------------------------*/static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value);static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev, struct s3c2410_ep *ep, struct usb_ctrlrequest *crq, u32 ep0csr){ int len, ret, tmp; /* start control request? */ if (!(ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY)) return; s3c2410_udc_nuke(dev, ep, -EPROTO); len = s3c2410_udc_read_fifo_crq(crq); if (len != sizeof(*crq)) { dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR" " wanted %d bytes got %d. Stalling out...\n", sizeof(*crq), len); s3c2410_udc_set_ep0_ss(base_addr); return; } dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n", crq->bRequest, crq->bRequestType, crq->wLength); /* cope with automagic for some standard requests. */ dev->req_std = (crq->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD; dev->req_config = 0; dev->req_pending = 1; switch (crq->bRequest) { case USB_REQ_SET_CONFIGURATION: dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n"); if (crq->bRequestType == USB_RECIP_DEVICE) { dev->req_config = 1; s3c2410_udc_set_ep0_de_out(base_addr); } break; case USB_REQ_SET_INTERFACE: dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n"); if (crq->bRequestType == USB_RECIP_INTERFACE) { dev->req_config = 1; s3c2410_udc_set_ep0_de_out(base_addr); } break; case USB_REQ_SET_ADDRESS: dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n"); if (crq->bRequestType == USB_RECIP_DEVICE) { tmp = crq->wValue & 0x7F; dev->address = tmp; udc_write((tmp | S3C2410_UDC_FUNCADDR_UPDATE), S3C2410_UDC_FUNC_ADDR_REG); s3c2410_udc_set_ep0_de_out(base_addr); return; } break; case USB_REQ_GET_STATUS: dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n"); s3c2410_udc_clear_ep0_opr(base_addr); if (dev->req_std) { if (!s3c2410_udc_get_status(dev, crq)) { return; } } break; case USB_REQ_CLEAR_FEATURE: s3c2410_udc_clear_ep0_opr(base_addr); if (crq->bRequestType != USB_RECIP_ENDPOINT) break; if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0) break; s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 0); s3c2410_udc_set_ep0_de_out(base_addr); return; case USB_REQ_SET_FEATURE: s3c2410_udc_clear_ep0_opr(base_addr); if (crq->bRequestType != USB_RECIP_ENDPOINT) break; if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0) break; s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 1); s3c2410_udc_set_ep0_de_out(base_addr); return; default: s3c2410_udc_clear_ep0_opr(base_addr); break; } if (crq->bRequestType & USB_DIR_IN) dev->ep0state = EP0_IN_DATA_PHASE; else dev->ep0state = EP0_OUT_DATA_PHASE; ret = dev->driver->setup(&dev->gadget, crq); if (ret < 0) { if (dev->req_config) { dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n", crq->bRequest, ret); return; } if (ret == -EOPNOTSUPP) dprintk(DEBUG_NORMAL, "Operation not supported\n"); else dprintk(DEBUG_NORMAL, "dev->driver->setup failed. (%d)\n", ret); udelay(5); s3c2410_udc_set_ep0_ss(base_addr); s3c2410_udc_set_ep0_de_out(base_addr); dev->ep0state = EP0_IDLE; /* deferred i/o == no response yet */ } else if (dev->req_pending) { dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n"); dev->req_pending=0; } dprintk(DEBUG_VERBOSE, "ep0state %s\n", ep0states[dev->ep0state]);}static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev){ u32 ep0csr; struct s3c2410_ep *ep = &dev->ep[0]; struct s3c2410_request *req; struct usb_ctrlrequest crq; if (list_empty(&ep->queue)) req = NULL; else req = list_entry(ep->queue.next, struct s3c2410_request, queue); /* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to * S3C2410_UDC_EP0_CSR_REG when index is zero */ udc_write(0, S3C2410_UDC_INDEX_REG); ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG); dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n", ep0csr, ep0states[dev->ep0state]); /* clear stall status */ if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) { s3c2410_udc_nuke(dev, ep, -EPIPE); dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n"); s3c2410_udc_clear_ep0_sst(base_addr); dev->ep0state = EP0_IDLE; return; } /* clear setup end */ if (ep0csr & S3C2410_UDC_EP0_CSR_SE) { dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n"); s3c2410_udc_nuke(dev, ep, 0); s3c2410_udc_clear_ep0_se(base_addr); dev->ep0state = EP0_IDLE; } switch (dev->ep0state) { case EP0_IDLE: s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr); break; case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n"); if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) { s3c2410_udc_write_fifo(ep, req); } break; case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n"); if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) { s3c2410_udc_read_fifo(ep,req); } break; case EP0_END_XFER: dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n"); dev->ep0state = EP0_IDLE; break; case EP0_STALL: dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n"); dev->ep0state = EP0_IDLE; break; }}/* * handle_ep - Manage I/O endpoints */static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep){ struct s3c2410_request *req; int is_in = ep->bEndpointAddress & USB_DIR_IN; u32 ep_csr1; u32 idx; if (likely (!list_empty(&ep->queue))) req = list_entry(ep->queue.next, struct s3c2410_request, queue); else req = NULL; idx = ep->bEndpointAddress & 0x7F; if (is_in) { udc_write(idx, S3C2410_UDC_INDEX_REG); ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n", idx, ep_csr1, req ? 1 : 0); if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) { dprintk(DEBUG_VERBOSE, "st\n"); udc_write(idx, S3C2410_UDC_INDEX_REG); udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL, S3C2410_UDC_IN_CSR1_REG); return; } if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) { s3c2410_udc_write_fifo(ep,req); } } else { udc_write(idx, S3C2410_UDC_INDEX_REG); ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG); dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1); if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) { udc_write(idx, S3C2410_UDC_INDEX_REG); udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL, S3C2410_UDC_OUT_CSR1_REG); return; } if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) { s3c2410_udc_read_fifo(ep,req); } }}#include <asm/arch/regs-irq.h>/* * s3c2410_udc_irq - interrupt handler */static irqreturn_t s3c2410_udc_irq(int irq, void *_dev){ struct s3c2410_udc *dev = _dev; int usb_status; int usbd_status; int pwr_reg; int ep0csr; int i; u32 idx; unsigned long flags; spin_lock_irqsave(&dev->lock, flags); /* Driver connected ? */ if (!dev->driver) { /* Clear interrupts */ udc_write(udc_read(S3C2410_UDC_USB_INT_REG), S3C2410_UDC_USB_INT_REG); udc_write(udc_read(S3C2410_UDC_EP_INT_REG), S3C2410_UDC_EP_INT_REG); } /* Save index */ idx = udc_read(S3C2410_UDC_INDEX_REG); /* Read status registers */ usb_status = udc_read(S3C2410_UDC_USB_INT_REG); usbd_status = udc_read(S3C2410_UDC_EP_INT_REG); pwr_reg = udc_read(S3C2410_UDC_PWR_REG); udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG); dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", usb_status, usbd_status, pwr_reg, ep0csr); /* * Now, handle interrupts. There's two types : * - Reset, Resume, Suspend coming -> usb_int_reg * - EP -> ep_int_reg */ /* RESET */ if (usb_status & S3C2410_UDC_USBINT_RESET) { /* two kind of reset : * - reset start -> pwr reg = 8 * - reset end -> pwr reg = 0 **/ dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n", ep0csr, pwr_reg); dev->gadget.speed = USB_SPEED_UNKNOWN; udc_write(0x00, S3C2410_UDC_INDEX_REG); udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3, S3C2410_UDC_MAXP_REG); dev->address = 0; dev->ep0state = EP0_IDLE; dev->gadget.speed = USB_SPEED_FULL; /* clear interrupt */ udc_write(S3C2410_UDC_USBINT_RESET, S3C2410_UDC_USB_INT_REG); udc_write(idx, S3C2410_UDC_INDEX_REG); spin_unlock_irqrestore(&dev->lock, flags); return IRQ_HANDLED; } /* RESUME */ if (usb_status & S3C2410_UDC_USBINT_RESUME) { dprintk(DEBUG_NORMAL, "USB resume\n"); /* clear interrupt */ udc_write(S3C2410_UDC_USBINT_RESUME, S3C2410_UDC_USB_INT_REG); if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->resume) dev->driver->resume(&dev->gadget); } /* SUSPEND */ if (usb_status & S3C2410_UDC_USBINT_SUSPEND) { dprintk(DEBUG_NORMAL, "USB suspend\n"); /* clear interrupt */ udc_write(S3C2410_UDC_USBINT_SUSPEND, S3C2410_UDC_USB_INT_REG); if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->suspend) dev->driver->suspend(&dev->gadget); dev->ep0state = EP0_IDLE; } /* EP */ /* control traffic */ /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready * generate an interrupt */ if (usbd_status & S3C2410_UDC_INT_EP0) { dprintk(DEBUG_VERBOSE, "USB ep0 irq\n"); /* Clear the interrupt bit by setting it to 1 */ udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG); s3c2410_udc_handle_ep0(dev); } /* endpoint data transfers */ for (i = 1; i < S3C2410_ENDPOINTS; i++) { u32 tmp = 1 << i; if (usbd_status & tmp) { dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i); /* Clear the interrupt bit by setting it to 1 */ udc_write(tmp, S3C2410_UDC_EP_INT_REG); s3c2410_udc_handle_ep(&dev->ep[i]); } } dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", irq); /* Restore old index */ udc_write(idx, S3C2410_UDC_INDEX_REG); spin_unlock_irqrestore(&dev->lock, flags); return IRQ_HANDLED;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -