📄 sl811-hcd.c
字号:
if (retval < 0) goto fail; ep->branch = retval; retval = 0; urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1)) + ep->branch; /* sort each schedule branch by period (slow before fast) * to share the faster parts of the tree without needing * dummy/placeholder nodes */ DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { struct sl811h_ep **prev = &sl811->periodic[i]; struct sl811h_ep *here = *prev; while (here && ep != here) { if (ep->period > here->period) break; prev = &here->next; here = *prev; } if (ep != here) { ep->next = here; *prev = ep; } sl811->load[i] += ep->load; } sl811->periodic_count++; hcd->self.bandwidth_allocated += ep->load / ep->period; sofirq_on(sl811); } /* in case of unlink-during-submit */ spin_lock(&urb->lock); if (urb->status != -EINPROGRESS) { spin_unlock(&urb->lock); finish_request(sl811, ep, urb, NULL, 0); retval = 0; goto fail; } urb->hcpriv = hep; spin_unlock(&urb->lock); start_transfer(sl811); sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);fail: spin_unlock_irqrestore(&sl811->lock, flags); return retval;}static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb){ struct sl811 *sl811 = hcd_to_sl811(hcd); struct usb_host_endpoint *hep; unsigned long flags; struct sl811h_ep *ep; int retval = 0; spin_lock_irqsave(&sl811->lock, flags); hep = urb->hcpriv; if (!hep) goto fail; ep = hep->hcpriv; if (ep) { /* finish right away if this urb can't be active ... * note that some drivers wrongly expect delays */ if (ep->hep->urb_list.next != &urb->urb_list) { /* not front of queue? never active */ /* for active transfers, we expect an IRQ */ } else if (sl811->active_a == ep) { if (time_before_eq(sl811->jiffies_a, jiffies)) { /* happens a lot with lowspeed?? */ DBG("giveup on DONE_A: ctrl %02x sts %02x\n", sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)), sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG))); sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); sl811->active_a = NULL; } else urb = NULL;#ifdef USE_B } else if (sl811->active_b == ep) { if (time_before_eq(sl811->jiffies_a, jiffies)) { /* happens a lot with lowspeed?? */ DBG("giveup on DONE_B: ctrl %02x sts %02x\n", sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)), sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG))); sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); sl811->active_b = NULL; } else urb = NULL;#endif } else { /* front of queue for inactive endpoint */ } if (urb) finish_request(sl811, ep, urb, NULL, 0); else VDBG("dequeue, urb %p active %s; wait4irq\n", urb, (sl811->active_a == ep) ? "A" : "B"); } elsefail: retval = -EINVAL; spin_unlock_irqrestore(&sl811->lock, flags); return retval;}static voidsl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep){ struct sl811h_ep *ep = hep->hcpriv; if (!ep) return; /* assume we'd just wait for the irq */ if (!list_empty(&hep->urb_list)) msleep(3); if (!list_empty(&hep->urb_list)) WARN("ep %p not empty?\n", ep); usb_put_dev(ep->udev); kfree(ep); hep->hcpriv = NULL;}static intsl811h_get_frame(struct usb_hcd *hcd){ struct sl811 *sl811 = hcd_to_sl811(hcd); /* wrong except while periodic transfers are scheduled; * never matches the on-the-wire frame; * subject to overruns. */ return sl811->frame;}/*-------------------------------------------------------------------------*//* the virtual root hub timer IRQ checks for hub status */static intsl811h_hub_status_data(struct usb_hcd *hcd, char *buf){ struct sl811 *sl811 = hcd_to_sl811(hcd);#ifdef QUIRK3 unsigned long flags; /* non-SMP HACK: use root hub timer as i/o watchdog * this seems essential when SOF IRQs aren't in use... */ local_irq_save(flags); if (!timer_pending(&sl811->timer)) { if (sl811h_irq( /* ~0, */ hcd, NULL) != IRQ_NONE) sl811->stat_lost++; } local_irq_restore(flags);#endif if (!(sl811->port1 & (0xffff << 16))) return 0; /* tell khubd port 1 changed */ *buf = (1 << 1); return 1;}static voidsl811h_hub_descriptor ( struct sl811 *sl811, struct usb_hub_descriptor *desc) { u16 temp = 0; desc->bDescriptorType = 0x29; desc->bHubContrCurrent = 0; desc->bNbrPorts = 1; desc->bDescLength = 9; /* per-port power switching (gang of one!), or none */ desc->bPwrOn2PwrGood = 0; if (sl811->board && sl811->board->port_power) { desc->bPwrOn2PwrGood = sl811->board->potpg; if (!desc->bPwrOn2PwrGood) desc->bPwrOn2PwrGood = 10; temp = 0x0001; } else temp = 0x0002; /* no overcurrent errors detection/handling */ temp |= 0x0010; desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp); /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ desc->bitmap[0] = 0 << 1; desc->bitmap[1] = ~0;}static voidsl811h_timer(unsigned long _sl811){ struct sl811 *sl811 = (void *) _sl811; unsigned long flags; u8 irqstat; u8 signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE; const u32 mask = (1 << USB_PORT_FEAT_CONNECTION) | (1 << USB_PORT_FEAT_ENABLE) | (1 << USB_PORT_FEAT_LOWSPEED); spin_lock_irqsave(&sl811->lock, flags); /* stop special signaling */ sl811->ctrl1 &= ~SL11H_CTL1MASK_FORCE; sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); udelay(3); irqstat = sl811_read(sl811, SL11H_IRQ_STATUS); switch (signaling) { case SL11H_CTL1MASK_SE0: DBG("end reset\n"); sl811->port1 = (1 << USB_PORT_FEAT_C_RESET) | (1 << USB_PORT_FEAT_POWER); sl811->ctrl1 = 0; /* don't wrongly ack RD */ if (irqstat & SL11H_INTMASK_INSRMV) irqstat &= ~SL11H_INTMASK_RD; break; case SL11H_CTL1MASK_K: DBG("end resume\n"); sl811->port1 &= ~(1 << USB_PORT_FEAT_SUSPEND); break; default: DBG("odd timer signaling: %02x\n", signaling); break; } sl811_write(sl811, SL11H_IRQ_STATUS, irqstat); if (irqstat & SL11H_INTMASK_RD) { /* usbcore nukes all pending transactions on disconnect */ if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION) | (1 << USB_PORT_FEAT_C_ENABLE); sl811->port1 &= ~mask; sl811->irq_enable = SL11H_INTMASK_INSRMV; } else { sl811->port1 |= mask; if (irqstat & SL11H_INTMASK_DP) sl811->port1 &= ~(1 << USB_PORT_FEAT_LOWSPEED); sl811->irq_enable = SL11H_INTMASK_INSRMV | SL11H_INTMASK_RD; } if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) { u8 ctrl2 = SL811HS_CTL2_INIT; sl811->irq_enable |= SL11H_INTMASK_DONE_A;#ifdef USE_B sl811->irq_enable |= SL11H_INTMASK_DONE_B;#endif if (sl811->port1 & (1 << USB_PORT_FEAT_LOWSPEED)) { sl811->ctrl1 |= SL11H_CTL1MASK_LSPD; ctrl2 |= SL811HS_CTL2MASK_DSWAP; } /* start SOFs flowing, kickstarting with A registers */ sl811->ctrl1 |= SL11H_CTL1MASK_SOF_ENA; sl811_write(sl811, SL11H_SOFLOWREG, 0xe0); sl811_write(sl811, SL811HS_CTLREG2, ctrl2); /* autoincrementing */ sl811_write(sl811, SL811_EP_A(SL11H_BUFLNTHREG), 0); writeb(SL_SOF, sl811->data_reg); writeb(0, sl811->data_reg); sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), SL11H_HCTLMASK_ARM); /* khubd provides debounce delay */ } else { sl811->ctrl1 = 0; } sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); /* reenable irqs */ sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); spin_unlock_irqrestore(&sl811->lock, flags);}static intsl811h_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct sl811 *sl811 = hcd_to_sl811(hcd); int retval = 0; unsigned long flags; spin_lock_irqsave(&sl811->lock, flags); switch (typeReq) { case ClearHubFeature: case SetHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: break; default: goto error; } break; case ClearPortFeature: if (wIndex != 1 || wLength != 0) goto error; switch (wValue) { case USB_PORT_FEAT_ENABLE: sl811->port1 &= (1 << USB_PORT_FEAT_POWER); 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); break; case USB_PORT_FEAT_SUSPEND: if (!(sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))) break; /* 20 msec of resume/K signaling, other irqs blocked */ DBG("start resume...\n"); sl811->irq_enable = 0; sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); sl811->ctrl1 |= SL11H_CTL1MASK_K; sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); mod_timer(&sl811->timer, jiffies + msecs_to_jiffies(20)); break; case USB_PORT_FEAT_POWER: port_power(sl811, 0); break; case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_SUSPEND: case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_RESET: break; default: goto error; } sl811->port1 &= ~(1 << wValue); break; case GetHubDescriptor: sl811h_hub_descriptor(sl811, (struct usb_hub_descriptor *) buf); break; case GetHubStatus: *(__le32 *) buf = cpu_to_le32(0); break; case GetPortStatus: if (wIndex != 1) goto error; *(__le32 *) buf = cpu_to_le32(sl811->port1);#ifndef VERBOSE if (*(u16*)(buf+2)) /* only if wPortChange is interesting */#endif DBG("GetPortStatus %08x\n", sl811->port1); break; case SetPortFeature: if (wIndex != 1 || wLength != 0) goto error; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (sl811->port1 & (1 << USB_PORT_FEAT_RESET)) goto error; if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))) goto error; DBG("suspend...\n"); sl811->ctrl1 &= ~SL11H_CTL1MASK_SOF_ENA; sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); break; case USB_PORT_FEAT_POWER: port_power(sl811, 1); break; case USB_PORT_FEAT_RESET: if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) goto error; if (!(sl811->port1 & (1 << USB_PORT_FEAT_POWER))) break; /* 50 msec of reset/SE0 signaling, irqs blocked */ sl811->irq_enable = 0; sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); sl811->ctrl1 = SL11H_CTL1MASK_SE0; sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); sl811->port1 |= (1 << USB_PORT_FEAT_RESET); mod_timer(&sl811->timer, jiffies + msecs_to_jiffies(50)); break; default: goto error; } sl811->port1 |= 1 << wValue; break; default:error: /* "protocol stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore(&sl811->lock, flags); return retval;}#ifdef CONFIG_PMstatic intsl811h_bus_suspend(struct usb_hcd *hcd){ // SOFs off DBG("%s\n", __FUNCTION__); return 0;}static intsl811h_bus_resume(struct usb_hcd *hcd){ // SOFs on DBG("%s\n", __FUNCTION__); return 0;}#else#define sl811h_bus_suspend NULL#define sl811h_bus_resume NULL#endif/*-------------------------------------------------------------------------*/#ifdef STUB_DEBUG_FILEstatic inline void create_debug_file(struct sl811 *sl811) { }static inline void remove_debug_file(struct sl811 *sl811) { }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -