📄 superh_udc.c
字号:
unsigned count; int is_last, is_short; count = write_packet(ep->fifo_reg, req, max); /* last packet is usually short (or a zlp) */ if (unlikely (count != max)) is_last = is_short = 1; else { if (likely(req->req.length != req->req.actual) || req->req.zero) is_last = 0; else is_last = 1; /* interrupt/iso maxpacket may not fill the fifo */ is_short = unlikely (max < ep->ep.maxpacket); /* FIXME ep.maxpacket should be the current size, * modified (for periodic endpoints) when the * ep is enabled. do that, re-init as needed, * and change maxpacket refs accordingly. */ } DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n", ep->ep.name, count, is_last ? "/L" : "", is_short ? "/S" : "", req->req.length - req->req.actual, req); /* let loose that packet. maybe try writing another one, * double buffering might work. */ or_b(ep->packet_enable_mask, USBTRG); /* requests complete when all IN data is in the FIFO */ if (is_last) { done (ep, req, 0); if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) { pio_irq_disable (ep); }#ifdef USE_DMA /* TODO */ if (unlikely(ep->dma >= 0) && !list_empty(&ep->queue)) { DMSG("%s pio2dma\n", ep->ep.name); req = list_entry(ep->queue.next, struct superh_request, queue); kick_dma(ep,req); return 0; }#endif return 1; } /* Only loop if on EP2 as it is double buffered */ } while (ep->bEndpointAddress == (2|USB_DIR_IN) && ctrl_inb(USBIFR0) & EP2_EMPTY); return 0;}/* * read_ep0_fifo - unload packets from ep0 control-out fifo. caller * should have made sure there's at least one packet ready. * * returns true if the request completed because of short packet or the * request buffer having filled (and maybe overran till end-of-packet). */static intread_ep0_fifo(struct superh_ep *ep, struct superh_request *req){ u8 *buf; unsigned bufferspace, count; DBG(DBG_VERY_NOISY, "read_ep0_fifo\n"); if (!ep) { DMSG("read_ep0_fifo invalid ep\n"); return -EINVAL; } if (!req) { DMSG("read_ep0_fifo invalid req\n"); return -EINVAL; } if (ep->desc != 0) { DMSG("read_ep0_fifo from invalid EP (%s)\n", ep->ep.name); return -EINVAL; } /* make sure there's a packet in the FIFO. */ if (likely ((ctrl_inb(USBIFR0) & EP0o_TS) == 0)) { buf = req->req.buf + req->req.actual; bufferspace = req->req.length - req->req.actual; /* read all bytes from this packet */ count = ctrl_inb(USBEPSZ0O); req->req.actual += min (count, bufferspace); DBG(DBG_VERY_NOISY, "read %s %d bytes req %p %d/%d\n", ep->ep.name, count, req, req->req.actual, req->req.length); while (likely (count-- != 0)) { u8 byte = ctrl_inb(USBEPDR0O); if (unlikely (bufferspace == 0)) { /* this happens when the driver's buffer * is smaller than what the host sent. * discard the extra data. */ if (req->req.status != -EOVERFLOW) DMSG("%s overflow %d\n", ep->ep.name, count); req->req.status = -EOVERFLOW; } else { *buf++ = byte; bufferspace--; } } /* Send ACK */ or_b(EP0o_RDFN, USBTRG); /* completion */ if (req->req.actual >= req->req.length) { done (ep, req, 0); ep0_idle(ep->dev); return 1; } } return 0;}/* * read_fifo - unload packet(s) from the fifo we use for usb OUT * transfers and put them into the request. caller should have made * sure there's at least one packet ready. * * returns true if the request completed because of short packet or the * request buffer having filled (and maybe overran till end-of-packet). */static intread_fifo (struct superh_ep *ep, struct superh_request *req){ DBG(DBG_VERY_NOISY, "read_fifo\n"); if ((ep->bEndpointAddress & 0x0f) != 1) { DMSG("read_fifo from invalid EP (%s)\n", ep->ep.name); return -EINVAL; } for (;;) { u8 *buf; unsigned bufferspace, count, is_short; /* make sure there's a packet in the FIFO. */ if (unlikely ((ctrl_inb(USBIFR0) & EP1_FULL) == 0)) break; buf = req->req.buf + req->req.actual; bufferspace = req->req.length - req->req.actual; /* read all bytes from this packet */ count = ctrl_inb(USBEPSZ1); req->req.actual += min (count, bufferspace); is_short = (count < ep->desc->wMaxPacketSize); DBG(DBG_VERY_NOISY, "read %s %d bytes%s req %p %d/%d\n", ep->ep.name, count, is_short ? "/S" : "", req, req->req.actual, req->req.length); while (likely (count-- != 0)) { u8 byte = ctrl_inb(USBEPDR1); if (unlikely (bufferspace == 0)) { /* this happens when the driver's buffer * is smaller than what the host sent. * discard the extra data. */ if (req->req.status != -EOVERFLOW) DMSG("%s overflow %d\n", ep->ep.name, count); req->req.status = -EOVERFLOW; } else { *buf++ = byte; bufferspace--; } } or_b(EP1_RDFN, USBTRG); /* There could now be another packet because of dual buffer */ /* completion */ if (is_short || req->req.actual == req->req.length) { done (ep, req, 0); if (list_empty(&ep->queue)) pio_irq_disable (ep); return 1; } /* finished that packet. the next one may be waiting... */ } return 0;}/*--------------------------------------------------------------------------*/ /* Interrupt Handler(s) *//* * superh_udc_irq_f0 - high priority interrupt handler * this deals with data to & from the bulk pipes */static void superh_udc_irq_f0(int irq, void *_dev, struct pt_regs *regs){ unsigned char f0_status; struct superh_udc *dev = (struct superh_udc*) _dev; struct superh_request *req; struct superh_ep *ep; DBG(DBG_NOISY, "superh_udc_irq_f0 %p\n", dev); atomic_inc(&dev->in_interrupt); dev->stats.irqs++; dev->stats.irq0s++; f0_status = ctrl_inb(USBIFR0); /* Acknowledge interrupts */ ctrl_outb(~(f0_status & F0_HIGH), USBIFR0); if (f0_status & EP1_FULL) { DBG(DBG_NOISY, "superh_udc_irq_f0 %p: EP1 FULL\n", dev); ep = &dev->ep[1]; if (likely (!list_empty(&ep->queue))) req = list_entry(ep->queue.next, struct superh_request, queue); else req = 0; if (req) read_fifo(ep, req); else pio_irq_disable(ep); } if ( f0_status & (EP2_TR | EP2_EMPTY) ) { DBG(DBG_NOISY, "superh_udc_irq_f0 %p: EP2 TR | EP2_EMPTY\n", dev); ep = &dev->ep[2]; if (likely (!list_empty(&ep->queue))) req = list_entry(ep->queue.next, struct superh_request, queue); else req = 0; if (req) { if ((f0_status & EP2_TR) && (f0_status & EP2_EMPTY)) write_fifo(ep, req); else and_b(~EP2_EMPTY, USBIER0); } else { pio_irq_disable(ep); } } atomic_dec(&dev->in_interrupt);}/** * superh_udc_irq_f1 - low priority interrupt handler * */static void superh_udc_irq_f1(int irq, void *_dev, struct pt_regs *regs){ unsigned char f0_status; unsigned char f1_status; struct superh_udc *dev = (struct superh_udc*) _dev; atomic_inc(&dev->in_interrupt);; dev->stats.irqs++; dev->stats.irq1s++; f0_status = ctrl_inb(USBIFR0); f1_status = ctrl_inb(USBIFR1); /* Acknowledge interrupts */ ctrl_outb(~(f0_status & F0_LOW), USBIFR0); ctrl_outb(~(f1_status & F1_LOW), USBIFR1); /* VBUSF indicates the USB being connected/disconnected */ if (f1_status & VBUSF) { DBG(DBG_VERY_NOISY, "superh_udc_irq_f1[%lx] VBUSF\n", dev->stats.irqs); if (!is_usb_connected) { /* report disconnect just once */ if (dev->gadget.speed != USB_SPEED_UNKNOWN) { DMSG("disconnect %s\n", dev->driver ? dev->driver->driver.name : 0); stop_activity(dev, dev->driver); } } else if (dev->gadget.speed == USB_SPEED_UNKNOWN) { DMSG("connect\n"); dev->setup_countdown = DEFAULT_SETUP_COUNT; } } /* Bus Reset */ if (f0_status & BRST) { int i; DBG(DBG_VERBOSE, "superh_udc_irq_f1[%lx]: BRST bus reset\n", dev->stats.irqs); /* kill any outstanding requests */ for (i = 0; i < 4; i++) { struct superh_ep *ep = &dev->ep[i]; nuke(ep, -ESHUTDOWN); ep->halted = 0; ep->stopped = 0; } /* reset fifo's and stall's */ ctrl_outb( EP3_CLEAR | EP1_CLEAR | EP2_CLEAR | EP0o_CLEAR | EP0i_CLEAR, USBFCLR ); ctrl_outb( 0, USBEPSTL ); DMSG("gadget driver '%s', address zero\n", dev->driver->driver.name); if (dev->gadget.speed == USB_SPEED_UNKNOWN) init_udc_timer(dev); dev->gadget.speed = USB_SPEED_FULL; memset(&dev->stats, 0, sizeof dev->stats); if (dev->setup_countdown < 0) dev->setup_countdown = RESET_SETUP_COUNT; dev->reset_time = jiffies; dev->fake_config = 0; ep0_idle(dev); } /* EPOi Transmit Complete - data to host on EP0 ACKed * EP0i Transfer Request - no data in FIFO to send on EP0 * either way we send next data if there is any and the FIFO is not busy * it will interrupt again if we later if we don't send anything. */ if ((f0_status & EP0i_TR || f0_status & EP0i_TS) && (ctrl_inb(USBDASTS) & EP0i_DE) == 0) { struct superh_ep *ep = &dev->ep[0]; struct superh_request *req; DBG(DBG_VERY_NOISY, "superh_udc_irq_f1[%lx]: ep0i TR\n", dev->stats.irqs); if (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct superh_request, queue); write_ep0_fifo(ep, req); } or_b(EP0i_PKTE, USBTRG); } /* Setup Command Receive Complete */ if (f0_status & SETUP_TS) { DBG(DBG_NOISY, "superh_udc_irq_f1[%lx]: SETUP TS\n", dev->stats.irqs); or_b( EP0o_CLEAR | EP0i_CLEAR, USBFCLR); handle_ep0_setup(dev); } /* EPOo Receive Complete - EP0 has received data from host */ if (f0_status & EP0o_TS) { struct superh_request *req; struct superh_ep *ep; DBG(DBG_VERY_NOISY, "superh_int_hndlr_f1[%lx]: ep0o TS\n", dev->stats.irqs); ep = &dev->ep[0]; if (likely (!list_empty(&ep->queue))) req = list_entry(ep->queue.next, struct superh_request, queue); else req = 0; if (req) read_ep0_fifo(ep, req); } /* EP3 Transmit Request & Transmit Complete */ if ( f1_status & (EP3_TR | EP3_TS) ) { struct superh_request *req; struct superh_ep *ep; DBG(DBG_VERY_NOISY, "superh_udc_irq_f1[%lx]: EP3 TR | EP3_TS (%x)\n", dev->stats.irqs, f1_status); ep = &dev->ep[3]; if (likely (!list_empty(&ep->queue))) req = list_entry(ep->queue.next, struct superh_request, queue); else req = 0; if (req) { if ((f1_status & EP3_TR) && (ctrl_inb(USBDASTS) & EP3_DE) == 0) write_fifo(ep, req); } else { pio_irq_disable(ep); } } atomic_dec(&dev->in_interrupt);;}/*--------------------------------------------------------------------------*/ /* * endpoint enable/disable * * we need to verify the descriptors used to enable endpoints. since superh * endpoint configurations are fixed, and are pretty much always enabled, * there's not a lot to manage here. * */static int superh_ep_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc){ struct superh_ep *ep; struct superh_udc *dev; DBG(DBG_NOISY, "superh_ep_enable\n"); ep = container_of (_ep, struct superh_ep, ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress || ep->ep.maxpacket < desc->wMaxPacketSize) { DMSG("%s, bad ep or descriptor\n", __FUNCTION__); return -EINVAL; } /* xfer types must match, except that interrupt ~= bulk */ if (ep->bmAttributes != desc->bmAttributes && ep->bmAttributes != USB_ENDPOINT_XFER_BULK && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { DMSG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); return -EINVAL; }#if 0 /* hardware _could_ do smaller, but driver doesn't */ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK && desc->wMaxPacketSize != BULK_FIFO_SIZE) || !desc->wMaxPacketSize) { DMSG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); return -ERANGE; }#endif dev = ep->dev; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { DMSG("%s, bogus device state\n", __FUNCTION__); return -ESHUTDOWN;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -