📄 at91_udc.c
字号:
while (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct at91_request, queue); done(ep, req, status); }}/*-------------------------------------------------------------------------*/static int at91_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc){ struct at91_ep *ep = container_of(_ep, struct at91_ep, ep); struct at91_udc *dev = ep->udc; u16 maxpacket; u32 tmp; unsigned long flags; if (!_ep || !ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || (maxpacket = le16_to_cpu(desc->wMaxPacketSize)) == 0 || maxpacket > ep->maxpacket) { DBG("bad ep or descriptor\n"); return -EINVAL; } if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { DBG("bogus device state\n"); return -ESHUTDOWN; } tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; switch (tmp) { case USB_ENDPOINT_XFER_CONTROL: DBG("only one control endpoint\n"); return -EINVAL; case USB_ENDPOINT_XFER_INT: if (maxpacket > 64) goto bogus_max; break; case USB_ENDPOINT_XFER_BULK: switch (maxpacket) { case 8: case 16: case 32: case 64: goto ok; }bogus_max: DBG("bogus maxpacket %d\n", maxpacket); return -EINVAL; case USB_ENDPOINT_XFER_ISOC: if (!ep->is_pingpong) { DBG("iso requires double buffering\n"); return -EINVAL; } break; }ok: local_irq_save(flags); /* initialize endpoint to match this descriptor */ ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0; ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); ep->stopped = 0; if (ep->is_in) tmp |= 0x04; tmp <<= 8; tmp |= AT91_UDP_EPEDS; __raw_writel(tmp, ep->creg); ep->desc = desc; ep->ep.maxpacket = maxpacket; /* * reset/init endpoint fifo. NOTE: leaves fifo_bank alone, * since endpoint resets don't reset hw pingpong state. */ at91_udp_write(dev, AT91_UDP_RST_EP, ep->int_mask); at91_udp_write(dev, AT91_UDP_RST_EP, 0); local_irq_restore(flags); return 0;}static int at91_ep_disable (struct usb_ep * _ep){ struct at91_ep *ep = container_of(_ep, struct at91_ep, ep); struct at91_udc *udc = ep->udc; unsigned long flags; if (ep == &ep->udc->ep[0]) return -EINVAL; local_irq_save(flags); nuke(ep, -ESHUTDOWN); /* restore the endpoint's pristine config */ ep->desc = NULL; ep->ep.maxpacket = ep->maxpacket; /* reset fifos and endpoint */ if (ep->udc->clocked) { at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask); at91_udp_write(udc, AT91_UDP_RST_EP, 0); __raw_writel(0, ep->creg); } local_irq_restore(flags); return 0;}/* * this is a PIO-only driver, so there's nothing * interesting for request or buffer allocation. */static struct usb_request *at91_ep_alloc_request(struct usb_ep *_ep, unsigned int gfp_flags){ struct at91_request *req; req = kzalloc(sizeof (struct at91_request), gfp_flags); if (!req) return NULL; INIT_LIST_HEAD(&req->queue); return &req->req;}static void at91_ep_free_request(struct usb_ep *_ep, struct usb_request *_req){ struct at91_request *req; req = container_of(_req, struct at91_request, req); BUG_ON(!list_empty(&req->queue)); kfree(req);}static int at91_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags){ struct at91_request *req; struct at91_ep *ep; struct at91_udc *dev; int status; unsigned long flags; req = container_of(_req, struct at91_request, req); ep = container_of(_ep, struct at91_ep, ep); if (!_req || !_req->complete || !_req->buf || !list_empty(&req->queue)) { DBG("invalid request\n"); return -EINVAL; } if (!_ep || (!ep->desc && ep->ep.name != ep0name)) { DBG("invalid ep\n"); return -EINVAL; } dev = ep->udc; if (!dev || !dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { DBG("invalid device\n"); return -EINVAL; } _req->status = -EINPROGRESS; _req->actual = 0; local_irq_save(flags); /* try to kickstart any empty and idle queue */ if (list_empty(&ep->queue) && !ep->stopped) { int is_ep0; /* * If this control request has a non-empty DATA stage, this * will start that stage. It works just like a non-control * request (until the status stage starts, maybe early). * * If the data stage is empty, then this starts a successful * IN/STATUS stage. (Unsuccessful ones use set_halt.) */ is_ep0 = (ep->ep.name == ep0name); if (is_ep0) { u32 tmp; if (!dev->req_pending) { status = -EINVAL; goto done; } /* * defer changing CONFG until after the gadget driver * reconfigures the endpoints. */ if (dev->wait_for_config_ack) { tmp = at91_udp_read(dev, AT91_UDP_GLB_STAT); tmp ^= AT91_UDP_CONFG; VDBG("toggle config\n"); at91_udp_write(dev, AT91_UDP_GLB_STAT, tmp); } if (req->req.length == 0) {ep0_in_status: PACKET("ep0 in/status\n"); status = 0; tmp = __raw_readl(ep->creg); tmp &= ~SET_FX; tmp |= CLR_FX | AT91_UDP_TXPKTRDY; __raw_writel(tmp, ep->creg); dev->req_pending = 0; goto done; } } if (ep->is_in) status = write_fifo(ep, req); else { status = read_fifo(ep, req); /* IN/STATUS stage is otherwise triggered by irq */ if (status && is_ep0) goto ep0_in_status; } } else status = 0; if (req && !status) { list_add_tail (&req->queue, &ep->queue); at91_udp_write(dev, AT91_UDP_IER, ep->int_mask); }done: local_irq_restore(flags); return (status < 0) ? status : 0;}static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req){ struct at91_ep *ep; struct at91_request *req; ep = container_of(_ep, struct at91_ep, ep); if (!_ep || ep->ep.name == ep0name) return -EINVAL; /* make sure it's actually queued on this endpoint */ list_for_each_entry (req, &ep->queue, queue) { if (&req->req == _req) break; } if (&req->req != _req) return -EINVAL; done(ep, req, -ECONNRESET); return 0;}static int at91_ep_set_halt(struct usb_ep *_ep, int value){ struct at91_ep *ep = container_of(_ep, struct at91_ep, ep); struct at91_udc *udc = ep->udc; u32 __iomem *creg; u32 csr; unsigned long flags; int status = 0; if (!_ep || ep->is_iso || !ep->udc->clocked) return -EINVAL; creg = ep->creg; local_irq_save(flags); csr = __raw_readl(creg); /* * fail with still-busy IN endpoints, ensuring correct sequencing * of data tx then stall. note that the fifo rx bytecount isn't * completely accurate as a tx bytecount. */ if (ep->is_in && (!list_empty(&ep->queue) || (csr >> 16) != 0)) status = -EAGAIN; else { csr |= CLR_FX; csr &= ~SET_FX; if (value) { csr |= AT91_UDP_FORCESTALL; VDBG("halt %s\n", ep->ep.name); } else { at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask); at91_udp_write(udc, AT91_UDP_RST_EP, 0); csr &= ~AT91_UDP_FORCESTALL; } __raw_writel(csr, creg); } local_irq_restore(flags); return status;}static const struct usb_ep_ops at91_ep_ops = { .enable = at91_ep_enable, .disable = at91_ep_disable, .alloc_request = at91_ep_alloc_request, .free_request = at91_ep_free_request, .queue = at91_ep_queue, .dequeue = at91_ep_dequeue, .set_halt = at91_ep_set_halt, // there's only imprecise fifo status reporting};/*-------------------------------------------------------------------------*/static int at91_get_frame(struct usb_gadget *gadget){ struct at91_udc *udc = to_udc(gadget); if (!to_udc(gadget)->clocked) return -EINVAL; return at91_udp_read(udc, AT91_UDP_FRM_NUM) & AT91_UDP_NUM;}static int at91_wakeup(struct usb_gadget *gadget){ struct at91_udc *udc = to_udc(gadget); u32 glbstate; int status = -EINVAL; unsigned long flags; DBG("%s\n", __FUNCTION__ ); local_irq_save(flags); if (!udc->clocked || !udc->suspended) goto done; /* NOTE: some "early versions" handle ESR differently ... */ glbstate = at91_udp_read(udc, AT91_UDP_GLB_STAT); if (!(glbstate & AT91_UDP_ESR)) goto done; glbstate |= AT91_UDP_ESR; at91_udp_write(udc, AT91_UDP_GLB_STAT, glbstate);done: local_irq_restore(flags); return status;}/* reinit == restore inital software state */static void udc_reinit(struct at91_udc *udc){ u32 i; INIT_LIST_HEAD(&udc->gadget.ep_list); INIT_LIST_HEAD(&udc->gadget.ep0->ep_list); for (i = 0; i < NUM_ENDPOINTS; i++) { struct at91_ep *ep = &udc->ep[i]; if (i != 0) list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); ep->desc = NULL; ep->stopped = 0; ep->fifo_bank = 0; ep->ep.maxpacket = ep->maxpacket; ep->creg = (void __iomem *) udc->udp_baseaddr + AT91_UDP_CSR(i); // initialiser une queue par endpoint INIT_LIST_HEAD(&ep->queue); }}static void stop_activity(struct at91_udc *udc){ struct usb_gadget_driver *driver = udc->driver; int i; if (udc->gadget.speed == USB_SPEED_UNKNOWN) driver = NULL; udc->gadget.speed = USB_SPEED_UNKNOWN; udc->suspended = 0; for (i = 0; i < NUM_ENDPOINTS; i++) { struct at91_ep *ep = &udc->ep[i]; ep->stopped = 1; nuke(ep, -ESHUTDOWN); } if (driver) driver->disconnect(&udc->gadget); udc_reinit(udc);}static void clk_on(struct at91_udc *udc){ if (udc->clocked) return; udc->clocked = 1; clk_enable(udc->iclk); clk_enable(udc->fclk);}static void clk_off(struct at91_udc *udc){ if (!udc->clocked) return; udc->clocked = 0; udc->gadget.speed = USB_SPEED_UNKNOWN; clk_disable(udc->fclk); clk_disable(udc->iclk);}/* * activate/deactivate link with host; minimize power usage for * inactive links by cutting clocks and transceiver power. */static void pullup(struct at91_udc *udc, int is_on){ if (!udc->enabled || !udc->vbus) is_on = 0; DBG("%sactive\n", is_on ? "" : "in"); if (is_on) { clk_on(udc); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_TXVC, 0); if (cpu_is_at91rm9200()) at91_set_gpio_value(udc->board.pullup_pin, 1); else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); txvc |= AT91_UDP_TXVC_PUON; at91_udp_write(udc, AT91_UDP_TXVC, txvc); } else if (cpu_is_at91sam9261()) { u32 usbpucr; usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); usbpucr |= AT91_MATRIX_USBPUCR_PUON; at91_sys_write(AT91_MATRIX_USBPUCR, usbpucr); } } else { stop_activity(udc); at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); if (cpu_is_at91rm9200()) at91_set_gpio_value(udc->board.pullup_pin, 0); else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); txvc &= ~AT91_UDP_TXVC_PUON; at91_udp_write(udc, AT91_UDP_TXVC, txvc); } else if (cpu_is_at91sam9261()) { u32 usbpucr; usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); usbpucr &= ~AT91_MATRIX_USBPUCR_PUON; at91_sys_write(AT91_MATRIX_USBPUCR, usbpucr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -