📄 net2280.c
字号:
retval = driver->bind (&dev->gadget); if (retval) { DEBUG (dev, "bind to driver %s --> %d\n", driver->driver.name, retval); dev->driver = 0; return retval; } /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. */ net2280_led_active (dev, 1); ep0_start (dev); DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n", driver->driver.name, readl (&dev->usb->usbctl), readl (&dev->usb->stdrsp)); /* pci writes may still be posted */ return 0;}EXPORT_SYMBOL (usb_gadget_register_driver);static voidstop_activity (struct net2280 *dev, struct usb_gadget_driver *driver){ int i; /* don't disconnect if it's not connected */ if (dev->gadget.speed == USB_SPEED_UNKNOWN) driver = 0; /* stop hardware; prevent new request submissions; * and kill any outstanding requests. */ usb_reset (dev); for (i = 0; i < 7; i++) nuke (&dev->ep [i]); /* report disconnect; the driver is already quiesced */ if (driver) { spin_unlock (&dev->lock); driver->disconnect (&dev->gadget); spin_lock (&dev->lock); } usb_reinit (dev);}int usb_gadget_unregister_driver (struct usb_gadget_driver *driver){ struct net2280 *dev = the_controller; unsigned long flags; if (!dev) return -ENODEV; if (!driver || driver != dev->driver) return -EINVAL; spin_lock_irqsave (&dev->lock, flags); stop_activity (dev, driver); spin_unlock_irqrestore (&dev->lock, flags); driver->unbind (&dev->gadget); dev->driver = 0; net2280_led_active (dev, 0); DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name); return 0;}EXPORT_SYMBOL (usb_gadget_unregister_driver);/*-------------------------------------------------------------------------*//* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq. * also works for dma-capable endpoints, in pio mode or just * to manually advance the queue after short OUT transfers. */static void handle_ep_small (struct net2280_ep *ep){ struct net2280_request *req; u32 t; /* 0 error, 1 mid-data, 2 done */ int mode = 1; if (!list_empty (&ep->queue)) req = list_entry (ep->queue.next, struct net2280_request, queue); else req = 0; /* ack all, and handle what we care about */ t = readl (&ep->regs->ep_stat); ep->irqs++;#if 0 VDEBUG (ep->dev, "%s ack ep_stat %08x, req %p\n", ep->ep.name, t, req ? &req->req : 0);#endif writel (t & ~(1 << NAK_OUT_PACKETS), &ep->regs->ep_stat); /* for ep0, monitor token irqs to catch data stage length errors * and to synchronize on status. * * also, to defer reporting of protocol stalls ... here's where * data or status first appears, handling stalls here should never * cause trouble on the host side.. * * control requests could be slightly faster without token synch for * status, but status can jam up that way. */ if (unlikely (ep->num == 0)) { if (ep->is_in) { /* status; stop NAKing */ if (t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) { if (ep->dev->protocol_stall) { ep->stopped = 1; set_halt (ep); } if (!req) allow_status (ep); mode = 2; /* reply to extra IN data tokens with a zlp */ } else if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) { if (ep->dev->protocol_stall) { ep->stopped = 1; set_halt (ep); mode = 2; } else if (!req && ep->stopped) write_fifo (ep, 0); } } else { /* status; stop NAKing */ if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) { if (ep->dev->protocol_stall) { ep->stopped = 1; set_halt (ep); } mode = 2; /* an extra OUT token is an error */ } else if (((t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) && req && req->req.actual == req->req.length) || !req) { ep->dev->protocol_stall = 1; set_halt (ep); ep->stopped = 1; if (req) done (ep, req, -EOVERFLOW); req = 0; } } } if (unlikely (!req)) return; /* manual DMA queue advance after short OUT */ if (likely (ep->dma != 0)) { if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) { u32 count; int stopped = ep->stopped; /* TRANSFERRED works around OUT_DONE erratum 0112. * we expect (N <= maxpacket) bytes; host wrote M. * iff (M < N) we won't ever see a DMA interrupt. */ ep->stopped = 1; for (count = 0; ; t = readl (&ep->regs->ep_stat)) { /* any preceding dma transfers must finish. * dma handles (M >= N), may empty the queue */ scan_dma_completions (ep); if (unlikely (list_empty (&ep->queue) || ep->out_overflow)) { req = 0; break; } req = list_entry (ep->queue.next, struct net2280_request, queue); /* here either (M < N), a "real" short rx; * or (M == N) and the queue didn't empty */ if (likely (t & (1 << FIFO_EMPTY))) { count = readl (&ep->dma->dmacount); count &= DMA_BYTE_COUNT_MASK; if (readl (&ep->dma->dmadesc) != req->td_dma) req = 0; break; } udelay(1); } /* stop DMA, leave ep NAKing */ writel ((1 << DMA_ABORT), &ep->dma->dmastat); spin_stop_dma (ep->dma); if (likely (req != 0)) { req->td->dmacount = 0; t = readl (&ep->regs->ep_avail); dma_done (ep, req, count, t); } /* also flush to prevent erratum 0106 trouble */ if (unlikely (ep->out_overflow || (ep->dev->chiprev == 0x0100 && ep->dev->gadget.speed == USB_SPEED_FULL))) { out_flush (ep); ep->out_overflow = 0; } /* (re)start dma if needed, stop NAKing */ ep->stopped = stopped; if (!list_empty (&ep->queue)) restart_dma (ep); } else DEBUG (ep->dev, "%s dma ep_stat %08x ??\n", ep->ep.name, t); return; /* data packet(s) received (in the fifo, OUT) */ } else if (t & (1 << DATA_PACKET_RECEIVED_INTERRUPT)) { if (read_fifo (ep, req) && ep->num != 0) mode = 2; /* data packet(s) transmitted (IN) */ } else if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) { unsigned len; len = req->req.length - req->req.actual; if (len > ep->ep.maxpacket) len = ep->ep.maxpacket; req->req.actual += len; /* if we wrote it all, we're usually done */ if (req->req.actual == req->req.length) { if (ep->num == 0) { /* wait for control status */ if (mode != 2) req = 0; } else if (!req->req.zero || len != ep->ep.maxpacket) mode = 2; } /* there was nothing to do ... */ } else if (mode == 1) return; /* done */ if (mode == 2) { /* stream endpoints often resubmit/unlink in completion */ done (ep, req, 0); /* maybe advance queue to next request */ if (ep->num == 0) { /* NOTE: net2280 could let gadget driver start the * status stage later. since not all controllers let * them control that, the api doesn't (yet) allow it. */ if (!ep->stopped) allow_status (ep); req = 0; } else { if (!list_empty (&ep->queue) && !ep->stopped) req = list_entry (ep->queue.next, struct net2280_request, queue); else req = 0; if (req && !ep->is_in) stop_out_naking (ep); } } /* is there a buffer for the next packet? * for best streaming performance, make sure there is one. */ if (req && !ep->stopped) { /* load IN fifo with next packet (may be zlp) */ if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) write_fifo (ep, &req->req); }}static struct net2280_ep *get_ep_by_addr (struct net2280 *dev, u16 wIndex){ struct net2280_ep *ep; if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) return &dev->ep [0]; list_for_each_entry (ep, &dev->gadget.ep_list, ep.ep_list) { u8 bEndpointAddress; if (!ep->desc) continue; bEndpointAddress = ep->desc->bEndpointAddress; if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) continue; if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f)) return ep; } return 0;}static void handle_stat0_irqs (struct net2280 *dev, u32 stat){ struct net2280_ep *ep; u32 num, scratch; /* most of these don't need individual acks */ stat &= ~(1 << INTA_ASSERTED); if (!stat) return; // DEBUG (dev, "irqstat0 %04x\n", stat); /* starting a control request? */ if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT))) { union { u32 raw [2]; struct usb_ctrlrequest r; } u; int tmp = 0; struct net2280_request *req; if (dev->gadget.speed == USB_SPEED_UNKNOWN) { if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED)) dev->gadget.speed = USB_SPEED_HIGH; else dev->gadget.speed = USB_SPEED_FULL; net2280_led_speed (dev, dev->gadget.speed); DEBUG (dev, "%s speed\n", (dev->gadget.speed == USB_SPEED_HIGH) ? "high" : "full"); } ep = &dev->ep [0]; ep->irqs++; /* make sure any leftover request state is cleared */ stat &= ~(1 << ENDPOINT_0_INTERRUPT); while (!list_empty (&ep->queue)) { req = list_entry (ep->queue.next, struct net2280_request, queue); done (ep, req, (req->req.actual == req->req.length) ? 0 : -EPROTO); } ep->stopped = 0; dev->protocol_stall = 0; writel ( (1 << TIMEOUT) | (1 << USB_STALL_SENT) | (1 << USB_IN_NAK_SENT) | (1 << USB_IN_ACK_RCVD) | (1 << USB_OUT_PING_NAK_SENT) | (1 << USB_OUT_ACK_SENT) | (1 << FIFO_OVERFLOW) | (1 << FIFO_UNDERFLOW) | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) | (1 << DATA_PACKET_RECEIVED_INTERRUPT) | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) | (1 << DATA_IN_TOKEN_INTERRUPT) , &ep->regs->ep_stat); u.raw [0] = readl (&dev->usb->setup0123); u.raw [1] = readl (&dev->usb->setup4567); cpu_to_le32s (&u.raw [0]); cpu_to_le32s (&u.raw [1]); le16_to_cpus (&u.r.wValue); le16_to_cpus (&u.r.wIndex); le16_to_cpus (&u.r.wLength); /* ack the irq */ writel (1 << SETUP_PACKET_INTERRUPT, &dev->regs->irqstat0); stat ^= (1 << SETUP_PACKET_INTERRUPT); /* watch control traffic at the token level, and force * synchronization before letting the status stage happen. * FIXME ignore tokens we'll NAK, until driver responds. * that'll mean a lot less irqs for some drivers. */ ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; if (ep->is_in) { scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) | (1 << DATA_IN_TOKEN_INTERRUPT); stop_out_naking (ep); } else scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT) | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) | (1 << DATA_IN_TOKEN_INTERRUPT); writel (scratch, &dev->epregs [0].ep_irqenb); /* we made the hardware handle most lowlevel requests; * everything else goes uplevel to the gadget code. */ switch (u.r.bRequest) { case USB_REQ_GET_STATUS: { struct net2280_ep *e; u16 status; /* hw handles device and interface status */ if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) goto delegate; if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0 || u.r.wLength > 2) goto do_stall; if (readl (&e->regs->ep_rsp) & (1 << SET_ENDPOINT_HALT)) status = __constant_cpu_to_le16 (1); else status = __constant_cpu_to_le16 (0); /* don't bother with a request object! */ writel (0, &dev->epregs [0].ep_irqenb); set_fifo_bytecount (ep, u.r.wLength); writel (status, &dev->epregs [0].ep_data); allow_status (ep); VDEBUG (dev, "%s stat %02x\n", ep->ep.name, status); goto next_endpoints; } break; case USB_REQ_CLEAR_FEATURE: { struct net2280_ep *e; /* hw handles device features */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; if (u.r.wValue != 0 /* HALT feature */ || u.r.wLength != 0) goto do_stall; if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0) goto do_stall; clear_halt (e); allow_status (ep); VDEBUG (dev, "%s clear halt\n", ep->ep.name); goto next_endpoints; } break; case USB_REQ_SET_FEATURE: { struct net2280_ep *e; /* hw handles device features */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; if (u.r.wValue != 0 /* HALT feature */ || u.r.wLength != 0) goto do_stall; if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0) goto do_stall; set_halt (e); allow_status (ep); VDEBUG (dev, "%s set halt\n", ep->ep.name); goto next_endpoints; } break; default:delegate: VDEBUG (dev, "setup %02x.%02x v%04x i%04x " "ep_cfg %08x\n", u.r.bRequestType, u.r.bRequest, u.r.wValue, u.r.wIndex, readl (&ep->regs->ep_cfg)); spin_unlock (&dev->lock); tmp = dev->driver->setup (&dev->gadget, &u.r); spin_lock (&dev->lock); } /* stall ep0 on error */ if (tmp < 0) {do_stall: VDEBUG (dev, "req %02x.%02x protocol STALL; stat %d\n", u.r.bRequestType, u.r.bRequest, tmp); dev->protocol_stall = 1; } /* some in/out token irq should follow; maybe stall then. * driver must queue a request (even zlp) or halt ep0 *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -