if (sl811->board && sl811->board->port_power) { /* switch VBUS, at 500mA unless hub power budget gets set */
dev_dbg(hcd->self.controller, "power %s\n",
str_on_off(is_on));
sl811->board->port_power(hcd->self.controller, is_on);
}
/* reset as thoroughly as we can */ if (sl811->board && sl811->board->reset)
sl811->board->reset(hcd->self.controller); else {
sl811_write(sl811, SL11H_CTLREG1, SL11H_CTL1MASK_SE0);
mdelay(20);
}
/* This is a PIO-only HCD. Queueing appends URBs to the endpoint's queue, *andmaystartI/O.Endpointqueuesarescannedduringcompletionirq *handlers(oneperpacket:ACK,NAK,faults,etc)andurbcancellation. * *UsinganexternalDMAenginetocopyapacketatatimecouldwork, *thoughsetup/teardowncostsmaybetoobigtomakeitworthwhile.
*/
/* SETUP starts a new control request. Devices are not allowed to *STALLorNAKthese;theymustcancelanypendingcontrolrequests.
*/ staticvoid setup_packet( struct sl811 *sl811, struct sl811h_ep *ep, struct urb *urb,
u8 bank,
u8 control
)
{
u8 addr;
u8 len; void __iomem *data_reg;
/* always OUT/data0 */
sl811_write(sl811, bank + SL11H_HOSTCTLREG,
control | SL11H_HCTLMASK_OUT);
ep->length = 0;
PACKET("SETUP qh%p\n", ep);
}
/* STATUS finishes control requests, often after IN or OUT data packets */ staticvoid status_packet( struct sl811 *sl811, struct sl811h_ep *ep, struct urb *urb,
u8 bank,
u8 control
)
{ int do_out; void __iomem *data_reg;
/* always data1; sometimes IN */
control |= SL11H_HCTLMASK_TOGGLE; if (do_out)
control |= SL11H_HCTLMASK_OUT;
sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
ep->length = 0;
PACKET("STATUS%s/%s qh%p\n", ep->nak_count ? "/retry" : "",
do_out ? "out" : "in", ep);
}
/* IN packets can be used with any type of endpoint. here we just *startthetransfer,datafromtheperipheralmayarrivelater. *urb->iso_frame_desciscurrentlyignoredhere...
*/ staticvoid in_packet( struct sl811 *sl811, struct sl811h_ep *ep, struct urb *urb,
u8 bank,
u8 control
)
{
u8 addr;
u8 len; void __iomem *data_reg;
/* avoid losing data on overflow */
len = ep->maxpacket;
addr = SL811HS_PACKET_BUF(bank == 0); if (!(control & SL11H_HCTLMASK_ISOCH)
&& usb_gettoggle(urb->dev, ep->epnum, 0))
control |= SL11H_HCTLMASK_TOGGLE;
data_reg = sl811->data_reg;
/* OUT packets can be used with any type of endpoint. *urb->iso_frame_desciscurrentlyignoredhere...
*/ staticvoid out_packet( struct sl811 *sl811, struct sl811h_ep *ep, struct urb *urb,
u8 bank,
u8 control
)
{ void *buf;
u8 addr;
u8 len; void __iomem *data_reg;
/* pick the next endpoint for a transaction, and issue it. *framesstartwithperiodictransfers(afterwhateverispending *fromthepreviousframe),andtherestofthetimeisasync *transfers,scheduledround-robin.
*/ staticstruct sl811h_ep *start(struct sl811 *sl811, u8 bank)
{ struct sl811h_ep *ep; struct urb *urb; int fclock;
u8 control;
/* use endpoint at schedule head */ if (sl811->next_periodic) {
ep = sl811->next_periodic;
sl811->next_periodic = ep->next;
} else { if (sl811->next_async)
ep = sl811->next_async; elseif (!list_empty(&sl811->async))
ep = container_of(sl811->async.next, struct sl811h_ep, schedule); else { /* could set up the first fullspeed periodic *transferforthenextframe...
*/ return NULL;
}
urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);
control = ep->defctrl;
/* if this frame doesn't have enough time left to transfer this *packet,waittillthenextframe.too-simplealgorithm...
*/
fclock = sl811_read(sl811, SL11H_SOFTMRREG) << 6;
fclock -= 100; /* setup takes not much time */ if (urb->dev->speed == USB_SPEED_LOW) { if (control & SL11H_HCTLMASK_PREAMBLE) { /* also note erratum 1: some hubs won't work */
fclock -= 800;
}
fclock -= ep->maxpacket << 8;
/* erratum 2: AFTERSOF only works for fullspeed */ if (fclock < 0) { if (ep->period)
sl811->stat_overrun++;
sofirq_on(sl811); return NULL;
}
} else {
fclock -= 12000 / 19; /* 19 64byte packets/msec */ if (fclock < 0) { if (ep->period)
sl811->stat_overrun++;
control |= SL11H_HCTLMASK_AFTERSOF;
switch (ep->nextpid) { case USB_PID_IN:
in_packet(sl811, ep, urb, bank, control); break; case USB_PID_OUT:
out_packet(sl811, ep, urb, bank, control); break; case USB_PID_SETUP:
setup_packet(sl811, ep, urb, bank, control); break; case USB_PID_ACK: /* for control status */
status_packet(sl811, ep, urb, bank, control); break; default:
dev_dbg(sl811_to_hcd(sl811)->self.controller, "bad ep%p pid %02x\n", ep, ep->nextpid);
ep = NULL;
} return ep;
}
/* 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;
/* port status seems weird until after reset, so *forcetheresetandmakehub_wqcleanuplater.
*/ if (irqstat & SL11H_INTMASK_RD)
sl811->port1 &= ~USB_PORT_STAT_CONNECTION; else
sl811->port1 |= USB_PORT_STAT_CONNECTION;
/* usb 1.1 says max 90% of a frame is available for periodic transfers. *thisdriverdoesn'tpromisethatmuchsinceit'sgottohandlean *IRQperpacket;irqhandlinglatenciesalsouseupthattime. * *NOTE:theperiodicscheduleisasparsetree,withtheloadfor *eachbranchminimized.seefig3.5intheOHCIspecforexample.
*/ #define MAX_PERIODIC_LOAD 500/* out of 1000 usec */
staticint balance(struct sl811 *sl811, u16 period, u16 load)
{ int i, branch = -ENOSPC;
/* search for the least loaded schedule branch of that period *whichhasenoughbandwidthleftunreserved.
*/ for (i = 0; i < period ; i++) { if (branch < 0 || sl811->load[branch] > sl811->load[i]) { int j;
staticint sl811h_urb_enqueue( struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags
) { struct sl811 *sl811 = hcd_to_sl811(hcd); struct usb_device *udev = urb->dev; unsignedint pipe = urb->pipe; int is_out = !usb_pipein(pipe); int type = usb_pipetype(pipe); int epnum = usb_pipeendpoint(pipe); struct sl811h_ep *ep = NULL; unsignedlong flags; int i; int retval; struct usb_host_endpoint *hep = urb->ep;
#ifndef CONFIG_USB_SL811_HCD_ISO if (type == PIPE_ISOCHRONOUS) return -ENOSPC; #endif
/* avoid all allocations within spinlocks */ if (!hep->hcpriv) {
ep = kzalloc(sizeof *ep, mem_flags); if (ep == NULL) return -ENOMEM;
}
spin_lock_irqsave(&sl811->lock, flags);
/* don't submit to a dead or disabled port */ if (!(sl811->port1 & USB_PORT_STAT_ENABLE)
|| !HC_IS_RUNNING(hcd->state)) {
retval = -ENODEV;
kfree(ep); goto fail_not_linked;
}
retval = usb_hcd_link_urb_to_ep(hcd, urb); if (retval) {
kfree(ep); goto fail_not_linked;
}
if (hep->hcpriv) {
kfree(ep);
ep = hep->hcpriv;
} elseif (!ep) {
retval = -ENOMEM; goto fail;
if (ep->maxpacket > H_MAXPACKET) { /* iso packets up to 240 bytes could work... */
dev_dbg(hcd->self.controller, "dev %d ep%d maxpacket %d\n", udev->devnum,
epnum, ep->maxpacket);
retval = -EINVAL;
kfree(ep); 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))
/ 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 *needsoffsettingbythetransferqueuedepth. *Allcurrentdriversignorestart_frame,sothis *isunlikelytoevermatter...
*/
urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1))
+ ep->branch; break;
}
hep = urb->hcpriv;
ep = hep->hcpriv; if (ep) { /* finish right away if this urb can't be active ... *notethatsomedriverswronglyexpectdelays
*/ if (ep->hep->urb_list.next != &urb->urb_list) { /* not front of queue? never active */
/* for active transfers, we expect an IRQ */
} elseif (sl811->active_a == ep) { if (time_before_eq(sl811->jiffies_a, jiffies)) { /* happens a lot with lowspeed?? */
dev_dbg(hcd->self.controller, "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
} elseif (sl811->active_b == ep) { if (time_before_eq(sl811->jiffies_a, jiffies)) { /* happens a lot with lowspeed?? */
dev_dbg(hcd->self.controller, "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 */
}
/* assume we'd just wait for the irq */ if (!list_empty(&hep->urb_list))
msleep(3); if (!list_empty(&hep->urb_list))
dev_warn(hcd->self.controller, "ep %p not empty?\n", ep);
mod_timer(&sl811->timer, jiffies
+ msecs_to_jiffies(USB_RESUME_TIMEOUT)); 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:
put_unaligned_le32(0, buf); break; case GetPortStatus: if (wIndex != 1) goto error;
put_unaligned_le32(sl811->port1, buf);
if (__is_defined(VERBOSE) ||
*(u16*)(buf+2)) /* only if wPortChange is interesting */
dev_dbg(hcd->self.controller, "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 & USB_PORT_STAT_RESET) goto error; if (!(sl811->port1 & USB_PORT_STAT_ENABLE)) goto error;
dev_dbg(hcd->self.controller,"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 & USB_PORT_STAT_SUSPEND) goto error; if (!(sl811->port1 & USB_PORT_STAT_POWER)) break;
/* expect just one sl811 per system */ staticvoid create_debug_file(struct sl811 *sl811)
{
debugfs_create_file("sl811h", S_IRUGO, usb_debug_root, sl811,
&sl811h_debug_fops);
}
/* the chip may be wired for either kind of addressing */
addr = platform_get_mem_or_io(dev, 0);
data = platform_get_mem_or_io(dev, 1); if (!addr || !data || resource_type(addr) != resource_type(data)) return -ENODEV;
/* with no "check to see if VBUS is still powered" board hook, *let'sassumeit'donlybepoweredtoenableremotewakeup.
*/ if (!sl811->port1 || !device_can_wakeup(&hcd->self.root_hub->dev)) {
sl811->port1 = 0;
port_power(sl811, 1);
usb_root_hub_lost_power(hcd->self.root_hub); return0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.