📄 s3c2410_udc.c
字号:
}/*------------------------- s3c2410_ep_ops ----------------------------------*/static inline struct s3c2410_ep *to_s3c2410_ep(struct usb_ep *ep){ return container_of(ep, struct s3c2410_ep, ep);}static inline struct s3c2410_udc *to_s3c2410_udc(struct usb_gadget *gadget){ return container_of(gadget, struct s3c2410_udc, gadget);}static inline struct s3c2410_request *to_s3c2410_req(struct usb_request *req){ return container_of(req, struct s3c2410_request, req);}/* * s3c2410_udc_ep_enable */static int s3c2410_udc_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc){ struct s3c2410_udc *dev; struct s3c2410_ep *ep; u32 max, tmp; unsigned long flags; u32 csr1,csr2; u32 int_en_reg; ep = to_s3c2410_ep(_ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; dev = ep->dev; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; local_irq_save (flags); _ep->maxpacket = max & 0x7ff; ep->desc = desc; ep->halted = 0; ep->bEndpointAddress = desc->bEndpointAddress; /* set max packet */ udc_write(ep->num, S3C2410_UDC_INDEX_REG); udc_write(max >> 3, S3C2410_UDC_MAXP_REG); /* set type, direction, address; reset fifo counters */ if (desc->bEndpointAddress & USB_DIR_IN) { csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT; csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN; udc_write(ep->num, S3C2410_UDC_INDEX_REG); udc_write(csr1, S3C2410_UDC_IN_CSR1_REG); udc_write(ep->num, S3C2410_UDC_INDEX_REG); udc_write(csr2, S3C2410_UDC_IN_CSR2_REG); } else { /* don't flush in fifo or it will cause endpoint interrupt */ csr1 = S3C2410_UDC_ICSR1_CLRDT; csr2 = S3C2410_UDC_ICSR2_DMAIEN; udc_write(ep->num, S3C2410_UDC_INDEX_REG); udc_write(csr1, S3C2410_UDC_IN_CSR1_REG); udc_write(ep->num, S3C2410_UDC_INDEX_REG); udc_write(csr2, S3C2410_UDC_IN_CSR2_REG); csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT; csr2 = S3C2410_UDC_OCSR2_DMAIEN; udc_write(ep->num, S3C2410_UDC_INDEX_REG); udc_write(csr1, S3C2410_UDC_OUT_CSR1_REG); udc_write(ep->num, S3C2410_UDC_INDEX_REG); udc_write(csr2, S3C2410_UDC_OUT_CSR2_REG); } /* enable irqs */ int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG); udc_write(int_en_reg | (1 << ep->num), S3C2410_UDC_EP_INT_EN_REG); /* print some debug message */ tmp = desc->bEndpointAddress; dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n", _ep->name,ep->num, tmp, desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max); local_irq_restore (flags); s3c2410_udc_set_halt(_ep, 0); return 0;}/* * s3c2410_udc_ep_disable */static int s3c2410_udc_ep_disable(struct usb_ep *_ep){ struct s3c2410_ep *ep = to_s3c2410_ep(_ep); unsigned long flags; u32 int_en_reg; if (!_ep || !ep->desc) { dprintk(DEBUG_NORMAL, "%s not enabled\n", _ep ? ep->ep.name : NULL); return -EINVAL; } local_irq_save(flags); dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name); ep->desc = NULL; ep->halted = 1; s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN); /* disable irqs */ int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG); udc_write(int_en_reg & ~(1<<ep->num), S3C2410_UDC_EP_INT_EN_REG); local_irq_restore(flags); dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name); return 0;}/* * s3c2410_udc_alloc_request */static struct usb_request *s3c2410_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags){ struct s3c2410_request *req; dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags); if (!_ep) return NULL; req = kzalloc (sizeof(struct s3c2410_request), mem_flags); if (!req) return NULL; INIT_LIST_HEAD (&req->queue); return &req->req;}/* * s3c2410_udc_free_request */static voids3c2410_udc_free_request(struct usb_ep *_ep, struct usb_request *_req){ struct s3c2410_ep *ep = to_s3c2410_ep(_ep); struct s3c2410_request *req = to_s3c2410_req(_req); dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req); if (!ep || !_req || (!ep->desc && _ep->name != ep0name)) return; WARN_ON (!list_empty (&req->queue)); kfree(req);}/* * s3c2410_udc_queue */static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags){ struct s3c2410_request *req = to_s3c2410_req(_req); struct s3c2410_ep *ep = to_s3c2410_ep(_ep); struct s3c2410_udc *dev; u32 ep_csr = 0; int fifo_count = 0; unsigned long flags; if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__); return -EINVAL; } dev = ep->dev; if (unlikely (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { return -ESHUTDOWN; } local_irq_save (flags); if (unlikely(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) { if (!_req) dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__); else { dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n", __func__, !_req->complete,!_req->buf, !list_empty(&req->queue)); } local_irq_restore(flags); return -EINVAL; } _req->status = -EINPROGRESS; _req->actual = 0; dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n", __func__, ep->bEndpointAddress, _req->length); if (ep->bEndpointAddress) { udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG); ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN) ? S3C2410_UDC_IN_CSR1_REG : S3C2410_UDC_OUT_CSR1_REG); fifo_count = s3c2410_udc_fifo_count_out(); } else { udc_write(0, S3C2410_UDC_INDEX_REG); ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG); fifo_count = s3c2410_udc_fifo_count_out(); } /* kickstart this i/o queue? */ if (list_empty(&ep->queue) && !ep->halted) { if (ep->bEndpointAddress == 0 /* ep0 */) { switch (dev->ep0state) { case EP0_IN_DATA_PHASE: if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY) && s3c2410_udc_write_fifo(ep, req)) { dev->ep0state = EP0_IDLE; req = NULL; } break; case EP0_OUT_DATA_PHASE: if ((!_req->length) || ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) && s3c2410_udc_read_fifo(ep, req))) { dev->ep0state = EP0_IDLE; req = NULL; } break; default: local_irq_restore(flags); return -EL2HLT; } } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 && (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) && s3c2410_udc_write_fifo(ep, req)) { req = NULL; } else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) && fifo_count && s3c2410_udc_read_fifo(ep, req)) { req = NULL; } } /* pio or dma irq handler advances the queue. */ if (likely (req != 0)) list_add_tail(&req->queue, &ep->queue); local_irq_restore(flags); dprintk(DEBUG_VERBOSE, "%s ok\n", __func__); return 0;}/* * s3c2410_udc_dequeue */static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req){ struct s3c2410_ep *ep = to_s3c2410_ep(_ep); struct s3c2410_udc *udc; int retval = -EINVAL; unsigned long flags; struct s3c2410_request *req = NULL; dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req); if (!the_controller->driver) return -ESHUTDOWN; if (!_ep || !_req) return retval; udc = to_s3c2410_udc(ep->gadget); local_irq_save (flags); list_for_each_entry (req, &ep->queue, queue) { if (&req->req == _req) { list_del_init (&req->queue); _req->status = -ECONNRESET; retval = 0; break; } } if (retval == 0) { dprintk(DEBUG_VERBOSE, "dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); s3c2410_udc_done(ep, req, -ECONNRESET); } local_irq_restore (flags); return retval;}/* * s3c2410_udc_set_halt */static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value){ struct s3c2410_ep *ep = to_s3c2410_ep(_ep); u32 ep_csr = 0; unsigned long flags; u32 idx; if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { dprintk(DEBUG_NORMAL, "%s: inval 2\n", __func__); return -EINVAL; } local_irq_save (flags); idx = ep->bEndpointAddress & 0x7F; if (idx == 0) { s3c2410_udc_set_ep0_ss(base_addr); s3c2410_udc_set_ep0_de_out(base_addr); } else { udc_write(idx, S3C2410_UDC_INDEX_REG); ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN) ? S3C2410_UDC_IN_CSR1_REG : S3C2410_UDC_OUT_CSR1_REG); if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { if (value) udc_write(ep_csr | S3C2410_UDC_ICSR1_SENDSTL, S3C2410_UDC_IN_CSR1_REG); else { ep_csr &= ~S3C2410_UDC_ICSR1_SENDSTL; udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG); ep_csr |= S3C2410_UDC_ICSR1_CLRDT; udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG); } } else { if (value) udc_write(ep_csr | S3C2410_UDC_OCSR1_SENDSTL, S3C2410_UDC_OUT_CSR1_REG); else { ep_csr &= ~S3C2410_UDC_OCSR1_SENDSTL; udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG); ep_csr |= S3C2410_UDC_OCSR1_CLRDT; udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG); } } } ep->halted = value ? 1 : 0; local_irq_restore (flags); return 0;}static const struct usb_ep_ops s3c2410_ep_ops = { .enable = s3c2410_udc_ep_enable, .disable = s3c2410_udc_ep_disable, .alloc_request = s3c2410_udc_alloc_request, .free_request = s3c2410_udc_free_request, .queue = s3c2410_udc_queue, .dequeue = s3c2410_udc_dequeue, .set_halt = s3c2410_udc_set_halt,};/*------------------------- usb_gadget_ops ----------------------------------*//* * s3c2410_udc_get_frame */static int s3c2410_udc_get_frame(struct usb_gadget *_gadget){ int tmp; dprintk(DEBUG_VERBOSE, "%s()\n", __func__); tmp = udc_read(S3C2410_UDC_FRAME_NUM2_REG) << 8; tmp |= udc_read(S3C2410_UDC_FRAME_NUM1_REG); return tmp;}/* * s3c2410_udc_wakeup */static int s3c2410_udc_wakeup(struct usb_gadget *_gadget){ dprintk(DEBUG_NORMAL, "%s()\n", __func__); return 0;}/* * s3c2410_udc_set_selfpowered */static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value){ struct s3c2410_udc *udc = to_s3c2410_udc(gadget); dprintk(DEBUG_NORMAL, "%s()\n", __func__); if (value) udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED); else udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); return 0;}static void s3c2410_udc_disable(struct s3c2410_udc *dev);static void s3c2410_udc_enable(struct s3c2410_udc *dev);static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on){ dprintk(DEBUG_NORMAL, "%s()\n", __func__); if (udc_info && udc_info->udc_command) { if (is_on) s3c2410_udc_enable(udc); else { if (udc->gadget.speed != USB_SPEED_UNKNOWN) { if (udc->driver && udc->driver->disconnect) udc->driver->disconnect(&udc->gadget); } s3c2410_udc_disable(udc); } } else return -EOPNOTSUPP; return 0;}static int s3c2410_udc_vbus_session(struct usb_gadget *gadget, int is_active){ struct s3c2410_udc *udc = to_s3c2410_udc(gadget); dprintk(DEBUG_NORMAL, "%s()\n", __func__); udc->vbus = (is_active != 0); s3c2410_udc_set_pullup(udc, is_active); return 0;}static int s3c2410_udc_pullup(struct usb_gadget *gadget, int is_on){ struct s3c2410_udc *udc = to_s3c2410_udc(gadget); dprintk(DEBUG_NORMAL, "%s()\n", __func__); s3c2410_udc_set_pullup(udc, is_on ? 0 : 1); return 0;}static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev){ struct s3c2410_udc *dev = _dev; unsigned int value; dprintk(DEBUG_NORMAL, "%s()\n", __func__); /* some cpus cannot read from an line configured to IRQ! */ s3c2410_gpio_cfgpin(udc_info->vbus_pin, S3C2410_GPIO_INPUT); value = s3c2410_gpio_getpin(udc_info->vbus_pin); s3c2410_gpio_cfgpin(udc_info->vbus_pin, S3C2410_GPIO_SFN2); if (udc_info->vbus_pin_inverted) value = !value; if (value != dev->vbus) s3c2410_udc_vbus_session(&dev->gadget, value); return IRQ_HANDLED;}static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma){ dprintk(DEBUG_NORMAL, "%s()\n", __func__); if (udc_info && udc_info->vbus_draw) { udc_info->vbus_draw(ma); return 0; } return -ENOTSUPP;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -