📄 sl811-hcd.c
字号:
DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { struct sl811h_ep *temp; struct sl811h_ep **prev = &sl811->periodic[i]; while (*prev && ((temp = *prev) != ep)) prev = &temp->next; if (*prev) *prev = ep->next; sl811->load[i] -= ep->load; } ep->branch = PERIODIC_SIZE; sl811->periodic_count--; sl811_to_hcd(sl811)->self.bandwidth_allocated -= ep->load / ep->period; if (ep == sl811->next_periodic) sl811->next_periodic = ep->next; /* we might turn SOFs back on again for the async schedule */ if (sl811->periodic_count == 0) sofirq_off(sl811);}static voiddone(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs){ u8 status; struct urb *urb; int urbstat = -EINPROGRESS; if (unlikely(!ep)) return; status = sl811_read(sl811, bank + SL11H_PKTSTATREG); urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); /* we can safely ignore NAKs */ if (status & SL11H_STATMASK_NAK) { // PACKET("...NAK_%02x qh%p\n", bank, ep); if (!ep->period) ep->nak_count++; ep->error_count = 0; /* ACK advances transfer, toggle, and maybe queue */ } else if (status & SL11H_STATMASK_ACK) { struct usb_device *udev = urb->dev; int len; unsigned char *buf; /* urb->iso_frame_desc is currently ignored here... */ ep->nak_count = ep->error_count = 0; switch (ep->nextpid) { case USB_PID_OUT: // PACKET("...ACK/out_%02x qh%p\n", bank, ep); urb->actual_length += ep->length; usb_dotoggle(udev, ep->epnum, 1); if (urb->actual_length == urb->transfer_buffer_length) { if (usb_pipecontrol(urb->pipe)) ep->nextpid = USB_PID_ACK; /* some bulk protocols terminate OUT transfers * by a short packet, using ZLPs not padding. */ else if (ep->length < ep->maxpacket || !(urb->transfer_flags & URB_ZERO_PACKET)) urbstat = 0; } break; case USB_PID_IN: // PACKET("...ACK/in_%02x qh%p\n", bank, ep); buf = urb->transfer_buffer + urb->actual_length; prefetchw(buf); len = ep->maxpacket - sl811_read(sl811, bank + SL11H_XFERCNTREG); if (len > ep->length) { len = ep->length; urb->status = -EOVERFLOW; } urb->actual_length += len; sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0), buf, len); usb_dotoggle(udev, ep->epnum, 0); if (urb->actual_length == urb->transfer_buffer_length) urbstat = 0; else if (len < ep->maxpacket) { if (urb->transfer_flags & URB_SHORT_NOT_OK) urbstat = -EREMOTEIO; else urbstat = 0; } if (usb_pipecontrol(urb->pipe) && (urbstat == -EREMOTEIO || urbstat == 0)) { /* NOTE if the status stage STALLs (why?), * this reports the wrong urb status. */ spin_lock(&urb->lock); if (urb->status == -EINPROGRESS) urb->status = urbstat; spin_unlock(&urb->lock); urb = NULL; ep->nextpid = USB_PID_ACK; } break; case USB_PID_SETUP: // PACKET("...ACK/setup_%02x qh%p\n", bank, ep); if (urb->transfer_buffer_length == urb->actual_length) ep->nextpid = USB_PID_ACK; else if (usb_pipeout(urb->pipe)) { usb_settoggle(udev, 0, 1, 1); ep->nextpid = USB_PID_OUT; } else { usb_settoggle(udev, 0, 0, 1); ep->nextpid = USB_PID_IN; } break; case USB_PID_ACK: // PACKET("...ACK/status_%02x qh%p\n", bank, ep); urbstat = 0; break; } /* STALL stops all transfers */ } else if (status & SL11H_STATMASK_STALL) { PACKET("...STALL_%02x qh%p\n", bank, ep); ep->nak_count = ep->error_count = 0; urbstat = -EPIPE; /* error? retry, until "3 strikes" */ } else if (++ep->error_count >= 3) { if (status & SL11H_STATMASK_TMOUT) urbstat = -ETIMEDOUT; else if (status & SL11H_STATMASK_OVF) urbstat = -EOVERFLOW; else urbstat = -EPROTO; ep->error_count = 0; PACKET("...3STRIKES_%02x %02x qh%p stat %d\n", bank, status, ep, urbstat); } if (urb && (urbstat != -EINPROGRESS || urb->status != -EINPROGRESS)) finish_request(sl811, ep, urb, regs, urbstat);}static inline u8 checkdone(struct sl811 *sl811){ u8 ctl; u8 irqstat = 0; if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) { ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)); if (ctl & SL11H_HCTLMASK_ARM) sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); DBG("%s DONE_A: ctrl %02x sts %02x\n", (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", ctl, sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG))); irqstat |= SL11H_INTMASK_DONE_A; }#ifdef USE_B if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) { ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)); if (ctl & SL11H_HCTLMASK_ARM) sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); DBG("%s DONE_B: ctrl %02x sts %02x\n", (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", ctl, sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG))); irqstat |= SL11H_INTMASK_DONE_A; }#endif return irqstat;}static irqreturn_t sl811h_irq(struct usb_hcd *hcd, struct pt_regs *regs){ struct sl811 *sl811 = hcd_to_sl811(hcd); u8 irqstat; irqreturn_t ret = IRQ_NONE; unsigned retries = 5; spin_lock(&sl811->lock);retry: irqstat = sl811_read(sl811, SL11H_IRQ_STATUS) & ~SL11H_INTMASK_DP; if (irqstat) { sl811_write(sl811, SL11H_IRQ_STATUS, irqstat); irqstat &= sl811->irq_enable; }#ifdef QUIRK2 /* this may no longer be necessary ... */ if (irqstat == 0) { irqstat = checkdone(sl811); if (irqstat) sl811->stat_lost++; }#endif /* USB packets, not necessarily handled in the order they're * issued ... that's fine if they're different endpoints. */ if (irqstat & SL11H_INTMASK_DONE_A) { done(sl811, sl811->active_a, SL811_EP_A(SL811_HOST_BUF), regs); sl811->active_a = NULL; sl811->stat_a++; }#ifdef USE_B if (irqstat & SL11H_INTMASK_DONE_B) { done(sl811, sl811->active_b, SL811_EP_B(SL811_HOST_BUF), regs); sl811->active_b = NULL; sl811->stat_b++; }#endif if (irqstat & SL11H_INTMASK_SOFINTR) { unsigned index; index = sl811->frame++ % (PERIODIC_SIZE - 1); sl811->stat_sof++; /* be graceful about almost-inevitable periodic schedule * overruns: continue the previous frame's transfers iff * this one has nothing scheduled. */ if (sl811->next_periodic) { // ERR("overrun to slot %d\n", index); sl811->stat_overrun++; } if (sl811->periodic[index]) sl811->next_periodic = sl811->periodic[index]; } /* khubd manages debouncing and wakeup */ if (irqstat & SL11H_INTMASK_INSRMV) { sl811->stat_insrmv++; /* most stats are reset for each VBUS session */ sl811->stat_wake = 0; sl811->stat_sof = 0; sl811->stat_a = 0; sl811->stat_b = 0; sl811->stat_lost = 0; sl811->ctrl1 = 0; sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); sl811->irq_enable = SL11H_INTMASK_INSRMV; sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); /* usbcore nukes other pending transactions on disconnect */ if (sl811->active_a) { sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); finish_request(sl811, sl811->active_a, container_of(sl811->active_a ->hep->urb_list.next, struct urb, urb_list), NULL, -ESHUTDOWN); sl811->active_a = NULL; }#ifdef USE_B if (sl811->active_b) { sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); finish_request(sl811, sl811->active_b, container_of(sl811->active_b ->hep->urb_list.next, struct urb, urb_list), NULL, -ESHUTDOWN); sl811->active_b = NULL; }#endif /* port status seems weird until after reset, so * force the reset and make khubd clean up later. */ sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION) | (1 << USB_PORT_FEAT_CONNECTION); } else if (irqstat & SL11H_INTMASK_RD) { if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) { DBG("wakeup\n"); sl811->port1 |= 1 << USB_PORT_FEAT_C_SUSPEND; sl811->stat_wake++; } else irqstat &= ~SL11H_INTMASK_RD; } if (irqstat) { if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)) start_transfer(sl811); ret = IRQ_HANDLED; if (retries--) goto retry; } if (sl811->periodic_count == 0 && list_empty(&sl811->async)) sofirq_off(sl811); sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); spin_unlock(&sl811->lock); return ret;}/*-------------------------------------------------------------------------*//* usb 1.1 says max 90% of a frame is available for periodic transfers. * this driver doesn't promise that much since it's got to handle an * IRQ per packet; irq handling latencies also use up that time. * * NOTE: the periodic schedule is a sparse tree, with the load for * each branch minimized. see fig 3.5 in the OHCI spec for example. */#define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */static int balance(struct sl811 *sl811, u16 period, u16 load){ int i, branch = -ENOSPC; /* search for the least loaded schedule branch of that period * which has enough bandwidth left unreserved. */ for (i = 0; i < period ; i++) { if (branch < 0 || sl811->load[branch] > sl811->load[i]) { int j; for (j = i; j < PERIODIC_SIZE; j += period) { if ((sl811->load[j] + load) > MAX_PERIODIC_LOAD) break; } if (j < PERIODIC_SIZE) continue; branch = i; } } return branch;}/*-------------------------------------------------------------------------*/static int sl811h_urb_enqueue( struct usb_hcd *hcd, struct usb_host_endpoint *hep, struct urb *urb, gfp_t mem_flags) { struct sl811 *sl811 = hcd_to_sl811(hcd); struct usb_device *udev = urb->dev; unsigned int pipe = urb->pipe; int is_out = !usb_pipein(pipe); int type = usb_pipetype(pipe); int epnum = usb_pipeendpoint(pipe); struct sl811h_ep *ep = NULL; unsigned long flags; int i; int retval = 0;#ifdef DISABLE_ISO if (type == PIPE_ISOCHRONOUS) return -ENOSPC;#endif /* avoid all allocations within spinlocks */ if (!hep->hcpriv) ep = kzalloc(sizeof *ep, mem_flags); spin_lock_irqsave(&sl811->lock, flags); /* don't submit to a dead or disabled port */ if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)) || !HC_IS_RUNNING(hcd->state)) { retval = -ENODEV; kfree(ep); goto fail; } if (hep->hcpriv) { kfree(ep); ep = hep->hcpriv; } else if (!ep) { retval = -ENOMEM; goto fail; } else { INIT_LIST_HEAD(&ep->schedule); ep->udev = udev; ep->epnum = epnum; ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE; usb_settoggle(udev, epnum, is_out, 0); if (type == PIPE_CONTROL) ep->nextpid = USB_PID_SETUP; else if (is_out) ep->nextpid = USB_PID_OUT; else ep->nextpid = USB_PID_IN; if (ep->maxpacket > H_MAXPACKET) { /* iso packets up to 240 bytes could work... */ DBG("dev %d ep%d maxpacket %d\n", udev->devnum, epnum, ep->maxpacket); retval = -EINVAL; goto fail; } if (udev->speed == USB_SPEED_LOW) { /* send preamble for external hub? */ if (!(sl811->ctrl1 & SL11H_CTL1MASK_LSPD)) ep->defctrl |= SL11H_HCTLMASK_PREAMBLE; } switch (type) { case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT: if (urb->interval > PERIODIC_SIZE) urb->interval = PERIODIC_SIZE; ep->period = urb->interval; ep->branch = PERIODIC_SIZE; if (type == PIPE_ISOCHRONOUS) ep->defctrl |= SL11H_HCTLMASK_ISOCH; ep->load = usb_calc_bus_time(udev->speed, !is_out, (type == PIPE_ISOCHRONOUS), usb_maxpacket(udev, pipe, is_out)) / 1000; break; } ep->hep = hep; hep->hcpriv = ep; } /* maybe put endpoint into schedule */ switch (type) { case PIPE_CONTROL: case PIPE_BULK: if (list_empty(&ep->schedule)) list_add_tail(&ep->schedule, &sl811->async); break; case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT: urb->interval = ep->period; if (ep->branch < PERIODIC_SIZE) { /* NOTE: the phase is correct here, but the value * needs offsetting by the transfer queue depth. * All current drivers ignore start_frame, so this * is unlikely to ever matter... */ urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1)) + ep->branch; break; } retval = balance(sl811, ep->period, ep->load); if (retval < 0) goto fail; ep->branch = retval;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -