/* the rest is HCD-private */ struct list_head qtd_list; struct urb *urb;
size_t length;
size_t actual_length;
/* QTD_ENQUEUED: waiting for transfer (inactive) */ /* QTD_PAYLOAD_ALLOC: chip mem has been allocated for payload */ /* QTD_XFER_STARTED: valid ptd has been written to isp176x - only
interrupt handler may touch this qtd! */ /* QTD_XFER_COMPLETE: payload has been transferred successfully */ /* QTD_RETIRE: transfer error/abort qtd */ #define QTD_ENQUEUED 0 #define QTD_PAYLOAD_ALLOC 1 #define QTD_XFER_STARTED 2 #define QTD_XFER_COMPLETE 3 #define QTD_RETIRE 4
u32 status;
};
/* Queue head, one for each active endpoint */ struct isp1760_qh { struct list_head qh_list; struct list_head qtd_list;
u32 toggle;
u32 ping; int slot; int tt_buffer_dirty; /* See USB2.0 spec section 11.17.5 */
};
/* * We need, in isp176x, to write directly the values to the portsc1 * register so it will make the other values to trigger.
*/ staticvoid isp1760_hcd_portsc1_set_clear(struct isp1760_hcd *priv, u32 field,
u32 val)
{
u32 bit = isp176x_hc_portsc1_fields[field];
u16 portsc1_reg = priv->is_isp1763 ? ISP1763_HC_PORTSC1 :
ISP176x_HC_PORTSC1;
u32 port_status = readl(priv->base + portsc1_reg);
/* * Access functions for isp176x memory (offset >= 0x0400). * * bank_reads8() reads memory locations prefetched by an earlier write to * HC_MEMORY_REG (see isp176x datasheet). Unless you want to do fancy multi- * bank optimizations, you should use the more generic mem_read() below. * * For access to ptd memory, use the specialized ptd_read() and ptd_write() * below. * * These functions copy via MMIO data to/from the device. memcpy_{to|from}io() * doesn't quite work because some people have to enforce 32-bit access
*/ staticvoid bank_reads8(void __iomem *src_base, u32 src_offset, u32 bank_addr,
__u32 *dst, u32 bytes)
{
__u32 __iomem *src;
u32 val;
__u8 *src_byteptr;
__u8 *dst_byteptr;
/* in case we have 3, 2 or 1 by left. The dst buffer may not be fully * allocated.
*/ if (src_offset < PAYLOAD_OFFSET)
val = readl_relaxed(src); else
val = __raw_readl(src);
/* * ISP1763 does not have the banks direct host controller memory access, * needs to use the HC_DATA register. Add data read/write according to this, * and also adjust 16bit access.
*/ staticvoid isp1763_mem_read(struct usb_hcd *hcd, u16 srcaddr,
u16 *dstptr, u32 bytes)
{ struct isp1760_hcd *priv = hcd_to_priv(hcd);
/* Write the starting device address to the hcd memory register */
isp1760_reg_write(priv->regs, ISP1763_HC_MEMORY, srcaddr);
ndelay(100); /* Delay between consecutive access */
/* As long there are at least 16-bit to read ... */ while (bytes >= 2) {
*dstptr = __raw_readw(priv->base + ISP1763_HC_DATA);
bytes -= 2;
dstptr++;
}
/* If there are no more bytes to read, return */ if (bytes <= 0) return;
/* Write the starting device address to the hcd memory register */
isp1760_reg_write(priv->regs, ISP1763_HC_MEMORY, dstaddr);
ndelay(100); /* Delay between consecutive access */
while (bytes >= 2) { /* Get and write the data; then adjust the data ptr and len */
__raw_writew(*src, priv->base + ISP1763_HC_DATA);
bytes -= 2;
src++;
}
/* If there are no more bytes to process, return */ if (bytes <= 0) return;
/* * The only way to get here is if there is a single byte left, * get it and write it to the data reg;
*/
writew(*((u8 *)src), priv->base + ISP1763_HC_DATA);
}
/* * Read and write ptds. 'ptd_offset' should be one of ISO_PTD_OFFSET, * INT_PTD_OFFSET, and ATL_PTD_OFFSET. 'slot' should be less than 32.
*/ staticvoid isp1760_ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, struct ptd *ptd)
{
u16 src_offset = ptd_offset + slot * sizeof(*ptd); struct isp1760_hcd *priv = hcd_to_priv(hcd);
/* * Make sure dw0 gets written last (after other dw's and after payload) * since it contains the enable bit
*/
isp1760_mem_write(base, dst_offset + sizeof(ptd->dw0),
(__force u32 *)&ptd->dw1, 7 * sizeof(ptd->dw1));
wmb();
isp1760_mem_write(base, dst_offset, (__force u32 *)&ptd->dw0, sizeof(ptd->dw0));
}
if (!priv->is_isp1763) return isp1760_ptd_write(priv->base, ptd_offset, slot, ptd);
isp1763_ptd_write(hcd, ptd_offset, slot, ptd);
}
/* memory management of the 60kb on the chip from 0x1000 to 0xffff */ staticvoid init_memory(struct isp1760_hcd *priv)
{ conststruct isp1760_memory_layout *mem = priv->memory_layout; int i, j, curr;
u32 payload_addr;
/* one-time init, only for memory state */ staticint priv_init(struct usb_hcd *hcd)
{ struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 isoc_cache;
u32 isoc_thres; int i;
spin_lock_init(&priv->lock);
for (i = 0; i < QH_END; i++)
INIT_LIST_HEAD(&priv->qh_list[i]);
/* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows.
*/
priv->periodic_size = DEFAULT_I_TDPS;
if (priv->is_isp1763) {
priv->i_thresh = 2; return 0;
}
/* controllers may cache some of the periodic schedule ... */
isoc_cache = isp1760_hcd_read(hcd, HCC_ISOC_CACHE);
isoc_thres = isp1760_hcd_read(hcd, HCC_ISOC_THRES);
/* full frame cache */ if (isoc_cache)
priv->i_thresh = 8; else/* N microframes cached */
priv->i_thresh = 2 + isoc_thres;
if (priv->is_isp1763)
pattern = 0xcafe; else
pattern = 0xdeadcafe;
isp1760_hcd_write(hcd, HC_SCRATCH, pattern);
/* * we do not care about the read value here we just want to * change bus pattern.
*/
isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH);
scratch = isp1760_hcd_read(hcd, HC_SCRATCH); if (scratch != pattern) {
dev_err(hcd->self.controller, "Scratch test failed. 0x%08x\n",
scratch); return -ENODEV;
}
/* * The RESET_HC bit in the SW_RESET register is supposed to reset the * host controller without touching the CPU interface registers, but at * least on the ISP1761 it seems to behave as the RESET_ALL bit and * reset the whole device. We thus can't use it here, so let's reset * the host controller through the EHCI USB Command register. The device * has been reset in core code anyway, so this shouldn't matter.
*/
isp1760_hcd_clear(hcd, ISO_BUF_FILL);
isp1760_hcd_clear(hcd, INT_BUF_FILL);
isp1760_hcd_clear(hcd, ATL_BUF_FILL);
/* according to 3.6.2, max packet len can not be > 0x400 */
maxpacket = usb_maxpacket(qtd->urb->dev, qtd->urb->pipe);
multi = 1 + ((maxpacket >> 11) & 0x3);
maxpacket &= 0x7ff;
/* * Most of this is guessing. ISP1761 datasheet is quite unclear, and * the algorithm from the original Philips driver code, which was * pretty much used in this driver before as well, is quite horrendous * and, i believe, incorrect. The code below follows the datasheet and * USB2.0 spec as far as I can tell, and plug/unplug seems to be much * more reliable this way (fingers crossed...).
*/
if (qtd->urb->dev->speed == USB_SPEED_HIGH) { /* urb->interval is in units of microframes (1/8 ms) */
period = qtd->urb->interval >> 3;
if (qtd->urb->interval > 4)
usof = 0x01; /* One bit set =>
interval 1 ms * uFrame-match */ elseif (qtd->urb->interval > 2)
usof = 0x22; /* Two bits set => interval 1/2 ms */ elseif (qtd->urb->interval > 1)
usof = 0x55; /* Four bits set => interval 1/4 ms */ else
usof = 0xff; /* All bits set => interval 1/8 ms */
} else { /* urb->interval is in units of frames (1 ms) */
period = qtd->urb->interval;
usof = 0x0f; /* Execute Start Split on any of the
four first uFrames */
/* * First 8 bits in dw5 is uSCS and "specifies which uSOF the * complete split needs to be sent. Valid only for IN." Also, * "All bits can be set to one for every transfer." (p 82, * ISP1761 data sheet.) 0x1c is from Philips driver. Where did * that number come from? 0xff seems to work fine...
*/ /* ptd->dw5 = 0x1c; */
ptd->dw5 = TO_DW(0xff); /* Execute Complete Split on any uFrame */
}
period = period >> 1;/* Ensure equal or shorter period than requested */
period &= 0xf8; /* Mask off too large values and lowest unused 3 bits */
list_for_each_entry_safe(qtd, qtd_next, &qh->qtd_list, qtd_list) { if (qtd->status < QTD_XFER_COMPLETE) break;
last_qtd = last_qtd_of_urb(qtd, qh);
if ((!last_qtd) && (qtd->status == QTD_RETIRE))
qtd_next->status = QTD_RETIRE;
if (qtd->status == QTD_XFER_COMPLETE) { if (qtd->actual_length) { switch (qtd->packet_type) { case IN_PID:
mem_read(hcd, qtd->payload_addr,
qtd->data_buffer,
qtd->actual_length);
fallthrough; case OUT_PID:
qtd->urb->actual_length +=
qtd->actual_length;
fallthrough; case SETUP_PID: break;
}
}
if (is_short_bulk(qtd)) { if (qtd->urb->transfer_flags & URB_SHORT_NOT_OK)
qtd->urb->status = -EREMOTEIO; if (!last_qtd)
qtd_next->status = QTD_RETIRE;
}
}
if (qtd->payload_addr)
free_mem(hcd, qtd);
if (last_qtd) { if ((qtd->status == QTD_RETIRE) &&
(qtd->urb->status == -EINPROGRESS))
qtd->urb->status = -EPIPE; /* Defer calling of urb_done() since it releases lock */
urb_listitem = kmem_cache_zalloc(urb_listitem_cachep,
GFP_ATOMIC); if (unlikely(!urb_listitem)) break; /* Try again on next call */
urb_listitem->urb = qtd->urb;
list_add_tail(&urb_listitem->urb_list, urb_list);
}
/* * Schedule packets for transfer. * * According to USB2.0 specification: * * 1st prio: interrupt xfers, up to 80 % of bandwidth * 2nd prio: control xfers * 3rd prio: bulk xfers * * ... but let's use a simpler scheme here (mostly because ISP1761 doc * is very unclear on how to prioritize traffic): * * 1) Enqueue any queued control transfers, as long as payload chip mem * and PTD ATL slots are available. * 2) Enqueue any queued INT transfers, as long as payload chip mem * and PTD INT slots are available. * 3) Enqueue any queued bulk transfers, as long as payload chip mem * and PTD ATL slots are available. * * Use double buffering (ENQUEUE_DEPTH==2) as a compromise between * conservation of chip mem and performance. * * I'm sure this scheme could be improved upon!
*/ for (i = 0; i < QH_END; i++) {
ep_queue = &priv->qh_list[i];
list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list)
enqueue_qtds(hcd, qh);
}
}
if ((ptd->dw3 & DW3_ERROR_BIT) && (ptd->dw3 & DW3_ACTIVE_BIT)) { /* Transfer Error, *but* active and no HALT -> reload */
dev_dbg(hcd->self.controller, "PID error; reloading ptd\n"); return PTD_STATE_QTD_RELOAD;
}
if (!FROM_DW3_NAKCOUNT(ptd->dw3) && (ptd->dw3 & DW3_ACTIVE_BIT)) { /* * NAKs are handled in HW by the chip. Usually if the * device is not able to send data fast enough. * This happens mostly on slower hardware.
*/ return PTD_STATE_QTD_RELOAD;
}
return PTD_STATE_QTD_DONE;
}
staticvoid handle_done_ptds(struct usb_hcd *hcd)
{ struct isp1760_hcd *priv = hcd_to_priv(hcd); struct ptd ptd; struct isp1760_qh *qh; int slot; int state; struct isp1760_slotinfo *slots;
u32 ptd_offset; struct isp1760_qtd *qtd; int modified; int skip_map;
while (priv->int_done_map || priv->atl_done_map) { if (priv->int_done_map) { /* INT ptd */
slot = __ffs(priv->int_done_map);
priv->int_done_map &= ~(1 << slot);
slots = priv->int_slots; /* This should not trigger, and could be removed if
noone have any problems with it triggering: */ if (!slots[slot].qh) {
WARN_ON(1); continue;
}
ptd_offset = INT_PTD_OFFSET;
ptd_read(hcd, INT_PTD_OFFSET, slot, &ptd);
state = check_int_transfer(hcd, &ptd,
slots[slot].qtd->urb);
} else { /* ATL ptd */
slot = __ffs(priv->atl_done_map);
priv->atl_done_map &= ~(1 << slot);
slots = priv->atl_slots; /* This should not trigger, and could be removed if
noone have any problems with it triggering: */ if (!slots[slot].qh) {
WARN_ON(1); continue;
}
ptd_offset = ATL_PTD_OFFSET;
ptd_read(hcd, ATL_PTD_OFFSET, slot, &ptd);
state = check_atl_transfer(hcd, &ptd,
slots[slot].qtd->urb);
}
/* * Workaround for problem described in chip errata 2: * * Sometimes interrupts are not generated when ATL (not INT?) completion occurs. * One solution suggested in the errata is to use SOF interrupts _instead_of_ * ATL done interrupts (the "instead of" might be important since it seems * enabling ATL interrupts also causes the chip to sometimes - rarely - "forget" * to set the PTD's done bit in addition to not generating an interrupt!). * * So if we use SOF + ATL interrupts, we sometimes get stale PTDs since their * done bit is not being set. This is bad - it blocks the endpoint until reboot. * * If we use SOF interrupts only, we get latency between ptd completion and the * actual handling. This is very noticeable in testusb runs which takes several * minutes longer without ATL interrupts. * * A better solution is to run the code below every SLOT_CHECK_PERIOD ms. If it * finds active ATL slots which are older than SLOT_TIMEOUT ms, it checks the * slot's ACTIVE and VALID bits. If these are not set, the ptd is considered * completed and its done map bit is set. * * The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen * not to cause too much lag when this HW bug occurs, while still hopefully * ensuring that the check does not falsely trigger.
*/ #define SLOT_TIMEOUT 300 #define SLOT_CHECK_PERIOD 200 staticstruct timer_list errata2_timer; staticstruct usb_hcd *errata2_timer_hcd;
/* * ISP1763 have some differences in the setup and order to enable * the ports, disable otg, setup buffers, and ATL, INT, ISO status. * So, just handle it a separate sequence.
*/ if (priv->is_isp1763) return isp1763_run(hcd);
hcd->uses_new_polling = 1;
hcd->state = HC_STATE_RUNNING;
/* Set PTD interrupt AND & OR maps */
isp1760_hcd_clear(hcd, HC_ATL_IRQ_MASK_AND);
isp1760_hcd_clear(hcd, HC_INT_IRQ_MASK_AND);
isp1760_hcd_clear(hcd, HC_ISO_IRQ_MASK_AND);
/* GRR this is run-once init(), being done every time the HC starts. * So long as they're part of class devices, we can't do it init() * since the class device isn't created that early.
*/ return 0;
}
/* * Packetize urb->transfer_buffer into list of packets of size wMaxPacketSize. * Also calculate the PID type (SETUP/IN/OUT) for each packet.
*/ staticvoid packetize_urb(struct usb_hcd *hcd, struct urb *urb, struct list_head *head, gfp_t flags)
{ struct isp1760_hcd *priv = hcd_to_priv(hcd); conststruct isp1760_memory_layout *mem = priv->memory_layout; struct isp1760_qtd *qtd; void *buf; int len, maxpacketsize;
u8 packet_type;
/* * URBs map to sequences of QTDs: one logical transaction
*/
if (!urb->transfer_buffer && urb->transfer_buffer_length) { /* XXX This looks like usb storage / SCSI bug */
dev_err(hcd->self.controller, "buf is null, dma is %08lx len is %d\n",
(longunsigned)urb->transfer_dma,
urb->transfer_buffer_length);
WARN_ON(1);
}
if (usb_pipein(urb->pipe))
packet_type = IN_PID; else
packet_type = OUT_PID;
if (usb_pipecontrol(urb->pipe)) {
qtd = qtd_alloc(flags, urb, SETUP_PID); if (!qtd) goto cleanup;
qtd_fill(qtd, urb->setup_packet, sizeof(struct usb_ctrlrequest));
list_add_tail(&qtd->qtd_list, head);
/* for zero length DATA stages, STATUS is always IN */ if (urb->transfer_buffer_length == 0)
packet_type = IN_PID;
}
/* * buffer gets wrapped in one or more qtds; * last one may be "short" (including zero len) * and may serve as a control status ack
*/
buf = urb->transfer_buffer;
len = urb->transfer_buffer_length;
for (;;) { int this_qtd_len;
qtd = qtd_alloc(flags, urb, packet_type); if (!qtd) goto cleanup;
/* * control requests may need a terminating data "status" ack; * bulk ones may need a terminating short packet (zero length).
*/ if (urb->transfer_buffer_length != 0) { int one_more = 0;
/* We need to forcefully reclaim the slot since some transfers never
return, e.g. interrupt transfers and NAKed bulk transfers. */ if (usb_pipecontrol(urb->pipe) || usb_pipebulk(urb->pipe)) { if (qh->slot != -1) {
skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP);
skip_map |= (1 << qh->slot);
isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map);
ndelay(100);
}
priv->atl_slots[qh->slot].qh = NULL;
priv->atl_slots[qh->slot].qtd = NULL;
} else { if (qh->slot != -1) {
skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP);
skip_map |= (1 << qh->slot);
isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map);
}
priv->int_slots[qh->slot].qh = NULL;
priv->int_slots[qh->slot].qtd = NULL;
}
qh->slot = -1;
}
/* * Retire the qtds beginning at 'qtd' and belonging all to the same urb, killing * any active transfer belonging to the urb in the process.
*/ staticvoid dequeue_urb_from_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh, struct isp1760_qtd *qtd)
{ struct urb *urb; int urb_was_running;
/* * Return status information even for ports with OWNER set. * Otherwise hub_wq wouldn't see the disconnect event when a * high-speed device is switched over to the companion * controller by the user.
*/ if (isp1760_hcd_is_set(hcd, PORT_CSC) ||
(isp1760_hcd_is_set(hcd, PORT_RESUME) &&
time_after_eq(jiffies, priv->reset_done))) {
buf [0] |= 1 << (0 + 1);
status = STS_PCD;
} /* FIXME autosuspend idle root hubs */
done:
spin_unlock_irqrestore(&priv->lock, flags); return status ? retval : 0;
}
staticvoid check_reset_complete(struct usb_hcd *hcd, int index)
{ if (!(isp1760_hcd_is_set(hcd, PORT_CONNECT))) return;
/* if reset finished and it's still not enabled -- handoff */ if (!isp1760_hcd_is_set(hcd, PORT_PE)) {
dev_info(hcd->self.controller, "port %d full speed --> companion\n", index + 1);
isp1760_hcd_set(hcd, PORT_OWNER);
isp1760_hcd_clear(hcd, PORT_CSC);
} else {
dev_info(hcd->self.controller, "port %d high speed\n",
index + 1);
}
/* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. * HCS_INDICATOR may say we can change LEDs to off/amber/green. * (track current state ourselves) ... blink for diagnostics, * power, "this is the one", etc. EHCI spec supports this.
*/
spin_lock_irqsave(&priv->lock, flags); switch (typeReq) { case ClearHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error;
} break; case ClearPortFeature: if (!wIndex || wIndex > ports) goto error;
wIndex--;
/* * Even if OWNER is set, so the port is owned by the * companion controller, hub_wq needs to be able to clear * the port-change status bits (especially * USB_PORT_STAT_C_CONNECTION).
*/
switch (wValue) { case USB_PORT_FEAT_ENABLE:
isp1760_hcd_clear(hcd, PORT_PE); break; case USB_PORT_FEAT_C_ENABLE: /* XXX error? */ break; case USB_PORT_FEAT_SUSPEND: if (isp1760_hcd_is_set(hcd, PORT_RESET)) goto error;
if (isp1760_hcd_is_set(hcd, PORT_SUSPEND)) { if (!isp1760_hcd_is_set(hcd, PORT_PE)) goto error; /* resume signaling for 20 msec */
isp1760_hcd_clear(hcd, PORT_CSC);
isp1760_hcd_set(hcd, PORT_RESUME);
priv->reset_done = jiffies +
msecs_to_jiffies(USB_RESUME_TIMEOUT);
} break; case USB_PORT_FEAT_C_SUSPEND: /* we auto-clear this feature */ break; case USB_PORT_FEAT_POWER: if (isp1760_hcd_ppc_is_set(hcd))
isp1760_hcd_clear(hcd, PORT_POWER); break; case USB_PORT_FEAT_C_CONNECTION:
isp1760_hcd_set(hcd, PORT_CSC); break; case USB_PORT_FEAT_C_OVER_CURRENT: /* XXX error ?*/ break; case USB_PORT_FEAT_C_RESET: /* GetPortStatus clears reset */ break; default: goto error;
}
isp1760_hcd_read(hcd, CMD_RUN); break; case GetHubDescriptor:
isp1760_hub_descriptor(priv, (struct usb_hub_descriptor *)
buf); break; case GetHubStatus: /* no hub-wide feature/status flags */
memset(buf, 0, 4); break; case GetPortStatus: if (!wIndex || wIndex > ports) goto error;
wIndex--;
status = 0;
/* wPortChange bits */ if (isp1760_hcd_is_set(hcd, PORT_CSC))
status |= USB_PORT_STAT_C_CONNECTION << 16;
/* whoever resumes must GetPortStatus to complete it!! */ if (isp1760_hcd_is_set(hcd, PORT_RESUME)) {
dev_err(hcd->self.controller, "Port resume should be skipped.\n");
/* Remote Wakeup received? */ if (!priv->reset_done) { /* resume signaling for 20 msec */
priv->reset_done = jiffies
+ msecs_to_jiffies(20); /* check the port again */
mod_timer(&hcd->rh_timer, priv->reset_done);
}
/* whoever resets must GetPortStatus to complete it!! */ if (isp1760_hcd_is_set(hcd, PORT_RESET) &&
time_after_eq(jiffies, priv->reset_done)) {
status |= USB_PORT_STAT_C_RESET << 16;
priv->reset_done = 0;
/* force reset to complete */ /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely...
*/
retval = isp1760_hcd_clear_and_wait(hcd, PORT_RESET,
750); if (retval != 0) {
dev_err(hcd->self.controller, "port %d reset error %d\n",
wIndex + 1, retval); goto error;
}
/* see what we found out */
check_reset_complete(hcd, wIndex);
} /* * Even if OWNER is set, there's no harm letting hub_wq * see the wPortStatus values (they should all be 0 except * for PORT_POWER anyway).
*/
if (isp1760_hcd_is_set(hcd, PORT_OWNER))
dev_err(hcd->self.controller, "PORT_OWNER is set\n");
if (isp1760_hcd_is_set(hcd, PORT_CONNECT)) {
status |= USB_PORT_STAT_CONNECTION; /* status may be from integrated TT */
status |= USB_PORT_STAT_HIGH_SPEED;
} if (isp1760_hcd_is_set(hcd, PORT_PE))
status |= USB_PORT_STAT_ENABLE; if (isp1760_hcd_is_set(hcd, PORT_SUSPEND) &&
isp1760_hcd_is_set(hcd, PORT_RESUME))
status |= USB_PORT_STAT_SUSPEND; if (isp1760_hcd_is_set(hcd, PORT_RESET))
status |= USB_PORT_STAT_RESET; if (isp1760_hcd_is_set(hcd, PORT_POWER))
status |= USB_PORT_STAT_POWER;
put_unaligned(cpu_to_le32(status), (__le32 *) buf); break; case SetHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error;
} break; case SetPortFeature:
wIndex &= 0xff; if (!wIndex || wIndex > ports) goto error;
wIndex--;
if (isp1760_hcd_is_set(hcd, PORT_OWNER)) break;
switch (wValue) { case USB_PORT_FEAT_ENABLE:
isp1760_hcd_set(hcd, PORT_PE); break;
case USB_PORT_FEAT_SUSPEND: if (!isp1760_hcd_is_set(hcd, PORT_PE) ||
isp1760_hcd_is_set(hcd, PORT_RESET)) goto error;
isp1760_hcd_set(hcd, PORT_SUSPEND); break; case USB_PORT_FEAT_POWER: if (isp1760_hcd_ppc_is_set(hcd))
isp1760_hcd_set(hcd, PORT_POWER); break; case USB_PORT_FEAT_RESET: if (isp1760_hcd_is_set(hcd, PORT_RESUME)) goto error; /* line status bits may report this as low speed, * which can be fine if this root hub has a * transaction translator built in.
*/ if ((isp1760_hcd_is_set(hcd, PORT_CONNECT) &&
!isp1760_hcd_is_set(hcd, PORT_PE)) &&
(isp1760_hcd_read(hcd, PORT_LSTATUS) == 1)) {
isp1760_hcd_set(hcd, PORT_OWNER);
} else {
isp1760_hcd_set(hcd, PORT_RESET);
isp1760_hcd_clear(hcd, PORT_PE);
/* * caller must wait, then call GetPortStatus * usb 2.0 spec says 50 ms resets on root
*/
priv->reset_done = jiffies +
msecs_to_jiffies(50);
} break; default: goto error;
} break;
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.