/* Set the last TRB in the segment to have a TRB type ID of Link TRB */
val = le32_to_cpu(trb->link.control);
val &= ~TRB_TYPE_BITMASK;
val |= TRB_TYPE(TRB_LINK); if (chain_links)
val |= TRB_CHAIN;
trb->link.control = cpu_to_le32(val);
trb->link.segment_ptr = cpu_to_le64(seg->next->dma);
}
/* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */ if (dst->cycle_state == 0) {
xhci_for_each_ring_seg(src->first_seg, seg) { for (int i = 0; i < TRBS_PER_SEGMENT; i++)
seg->trbs[i].link.control |= cpu_to_le32(TRB_CYCLE);
}
}
key = (unsignedlong)(seg->dma >> TRB_SEGMENT_SHIFT); /* Skip any segments that were already added. */ if (radix_tree_lookup(trb_address_map, key)) return0;
ret = radix_tree_maybe_preload(mem_flags); if (ret) return ret;
ret = radix_tree_insert(trb_address_map,
key, ring);
radix_tree_preload_end(); return ret;
}
/* XXX: Do we need the hcd structure in all these functions? */ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
{ if (!ring) return;
trace_xhci_ring_free(ring);
if (ring->first_seg) { if (ring->type == TYPE_STREAM)
xhci_remove_stream_mapping(ring);
xhci_ring_segments_free(xhci, ring);
}
kfree(ring);
}
void xhci_initialize_ring_info(struct xhci_ring *ring)
{ /* The ring is empty, so the enqueue pointer == dequeue pointer */
ring->enqueue = ring->first_seg->trbs;
ring->enq_seg = ring->first_seg;
ring->dequeue = ring->enqueue;
ring->deq_seg = ring->first_seg; /* The ring is initialized to 0. The producer must write 1 to the cycle *bittohandoverownershipoftheTRB,soPCS=1.Theconsumermust *compareCCStothecyclebittocheckownership,soCCS=1. * *Newringsareinitializedwithcyclestateequalto1;ifweare *handlingringexpansion,setthecyclestateequaltotheoldring.
*/
ring->cycle_state = 1;
/* Allocate segments and link them for a ring */ staticint xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, struct xhci_ring *ring, gfp_t flags)
{ struct xhci_segment *prev; unsignedint num = 0;
struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsignedint ep_index)
{ /* increment ep index by offset of start of ep ctx array */
ep_index++; if (ctx->type == XHCI_CTX_TYPE_INPUT)
ep_index++;
/* Initialize the array of virtual pointers to stream rings. */
stream_info->stream_rings = kcalloc_node(
num_streams, sizeof(struct xhci_ring *), mem_flags,
dev_to_node(dev)); if (!stream_info->stream_rings) goto cleanup_info;
/* Initialize the array of DMA addresses for stream rings for the HW. */
stream_info->stream_ctx_array = xhci_alloc_stream_ctx(xhci,
num_stream_ctxs, &stream_info->ctx_array_dma,
mem_flags); if (!stream_info->stream_ctx_array) goto cleanup_ring_array;
/* Allocate everything needed to free the stream rings later */
stream_info->free_streams_command =
xhci_alloc_command_with_ctx(xhci, true, mem_flags); if (!stream_info->free_streams_command) goto cleanup_ctx;
/* If the device never made it past the Set Address stage, *itmaynothavetheroothubportpointersetcorrectly.
*/ if (!virt_dev->rhub_port) {
xhci_dbg(xhci, "Bad rhub port.\n"); return;
}
tt_list_head = &(xhci->rh_bw[virt_dev->rhub_port->hw_portnum].tts);
list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) { /* Multi-TT hubs will have more than one entry */ if (tt_info->slot_id == slot_id) {
slot_found = true;
list_del(&tt_info->tt_list);
kfree(tt_info);
} elseif (slot_found) { break;
}
}
}
int xhci_alloc_tt_info(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags)
{ struct xhci_tt_bw_info *tt_info; unsignedint num_ports; int i, j; struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
if (!tt->multi)
num_ports = 1; else
num_ports = hdev->maxchild;
for (i = 0; i < num_ports; i++, tt_info++) { struct xhci_interval_bw_table *bw_table;
/* All the xhci_tds in the ring's TD list should be freed at this point. *Shouldbecalledwithxhci->lockheldifthereisanychancetheTTlists *willbemanipulatedbytheconfigureendpoint,allocatedevice,orupdate *hubfunctionswhilethisfunctionisremovingtheTTentriesfromthelist.
*/ void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, int slot_id)
{ int i; int old_active_eps = 0;
/* Slot ID 0 is reserved */ if (slot_id == 0 || !dev) return;
/* If device ctx array still points to _this_ device, clear it */ if (dev->out_ctx &&
xhci->dcbaa->dev_context_ptrs[slot_id] == cpu_to_le64(dev->out_ctx->dma))
xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
trace_xhci_free_virt_device(dev);
if (dev->tt_info)
old_active_eps = dev->tt_info->active_eps;
for (i = 0; i < 31; i++) { if (dev->eps[i].ring)
xhci_ring_free(xhci, dev->eps[i].ring); if (dev->eps[i].stream_info)
xhci_free_stream_info(xhci,
dev->eps[i].stream_info); /* *Endpointsarenormallydeletedfromthebandwidthlistwhen *endpointsaredropped,beforedeviceisfreed. *Ifhostisdyingorbeingremovedthenendpointsaren't *droppedcleanly,sodeletetheendpointfromlisthere. *Onlyapplicableforhostswithsoftwarebandwidthchecking.
*/
if (!list_empty(&dev->eps[i].bw_endpoint_list)) {
list_del_init(&dev->eps[i].bw_endpoint_list);
xhci_dbg(xhci, "Slot %u endpoint %u not removed from BW list!\n",
slot_id, i);
}
} /* If this is a hub, free the TT(s) from the TT list */
xhci_free_tt_info(xhci, dev, slot_id); /* If necessary, update the number of active TTs on this root port */
xhci_update_tt_active_eps(xhci, dev, old_active_eps);
if (dev->in_ctx)
xhci_free_container_ctx(xhci, dev->in_ctx); if (dev->out_ctx)
xhci_free_container_ctx(xhci, dev->out_ctx);
if (dev->udev && dev->udev->slot_id)
dev->udev->slot_id = 0; if (dev->rhub_port && dev->rhub_port->slot_id == slot_id)
dev->rhub_port->slot_id = 0; if (xhci->devs[slot_id] == dev)
xhci->devs[slot_id] = NULL;
kfree(dev);
}
if (!vdev->rhub_port) {
xhci_dbg(xhci, "Bad rhub port.\n"); goto out;
}
tt_list_head = &(xhci->rh_bw[vdev->rhub_port->hw_portnum].tts);
list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) { /* is this a hub device that added a tt_info to the tts list */ if (tt_info->slot_id == slot_id) { /* are any devices using this tt_info? */ for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
vdev = xhci->devs[i]; if (vdev && (vdev->tt_info == tt_info))
xhci_free_virt_devices_depth_first(
xhci, i);
}
}
}
out: /* we are now at a leaf device */
xhci_debugfs_remove_slot(xhci, slot_id);
xhci_free_virt_device(xhci, xhci->devs[slot_id], slot_id);
}
int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags)
{ struct xhci_virt_device *dev; int i;
/* Slot ID 0 is reserved */ if (slot_id == 0 || xhci->devs[slot_id]) {
xhci_warn(xhci, "Bad Slot ID %d\n", slot_id); return0;
}
dev = kzalloc(sizeof(*dev), flags); if (!dev) return0;
dev->slot_id = slot_id;
/* Allocate the (output) device context that will be used in the HC. */
dev->out_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE, flags); if (!dev->out_ctx) goto fail;
/* Initialize the cancellation and bandwidth list for each ep */ for (i = 0; i < 31; i++) {
dev->eps[i].ep_index = i;
dev->eps[i].vdev = dev;
dev->eps[i].xhci = xhci;
INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list);
INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list);
}
/* Allocate endpoint 0 ring */
dev->eps[0].ring = xhci_ring_alloc(xhci, 2, TYPE_CTRL, 0, flags); if (!dev->eps[0].ring) goto fail;
dev->udev = udev;
/* Point to output device context in dcbaa. */
xhci->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(dev->out_ctx->dma);
xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n",
slot_id,
&xhci->dcbaa->dev_context_ptrs[slot_id],
le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id]));
trace_xhci_alloc_virt_device(dev);
xhci->devs[slot_id] = dev;
return1;
fail:
if (dev->in_ctx)
xhci_free_container_ctx(xhci, dev->in_ctx); if (dev->out_ctx)
xhci_free_container_ctx(xhci, dev->out_ctx);
kfree(dev);
/* Setup an xHCI virtual device for a Set Address command */ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev)
{ struct xhci_virt_device *dev; struct xhci_ep_ctx *ep0_ctx; struct xhci_slot_ctx *slot_ctx;
u32 max_packets;
dev = xhci->devs[udev->slot_id]; /* Slot ID 0 is reserved */ if (udev->slot_id == 0 || !dev) {
xhci_warn(xhci, "Slot ID %d is not assigned to this device\n",
udev->slot_id); return -EINVAL;
}
ep0_ctx = xhci_get_ep_ctx(xhci, dev->in_ctx, 0);
slot_ctx = xhci_get_slot_ctx(xhci, dev->in_ctx);
/* 3) Only the control endpoint is valid - one endpoint context */
slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | udev->route); switch (udev->speed) { case USB_SPEED_SUPER_PLUS:
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SSP);
max_packets = MAX_PACKET(512); break; case USB_SPEED_SUPER:
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS);
max_packets = MAX_PACKET(512); break; case USB_SPEED_HIGH:
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_HS);
max_packets = MAX_PACKET(64); break; /* USB core guesses at a 64-byte max packet first for FS devices */ case USB_SPEED_FULL:
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_FS);
max_packets = MAX_PACKET(64); break; case USB_SPEED_LOW:
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS);
max_packets = MAX_PACKET(8); break; default: /* Speed was set earlier, this shouldn't happen. */ return -EINVAL;
} /* Find the root hub port this device is under */
dev->rhub_port = xhci_find_rhub_port(xhci, udev); if (!dev->rhub_port) return -EINVAL; /* Slot ID is set to the device directly below the root hub */ if (!udev->parent->parent)
dev->rhub_port->slot_id = udev->slot_id;
slot_ctx->dev_info2 |= cpu_to_le32(ROOT_HUB_PORT(dev->rhub_port->hw_portnum + 1));
xhci_dbg(xhci, "Slot ID %d: HW portnum %d, hcd portnum %d\n",
udev->slot_id, dev->rhub_port->hw_portnum, dev->rhub_port->hcd_portnum);
/* Find the right bandwidth table that this device will be a part of. *Ifthisisafullspeeddeviceattacheddirectlytoarootport(ora *decendentofone),itcountsasaprimarybandwidthdomain,nota *secondarybandwidthdomainunderaTT.Anxhci_tt_infostructure *willneverbecreatedfortheHSroothub.
*/ if (!udev->tt || !udev->tt->hub->parent) {
dev->bw_table = &xhci->rh_bw[dev->rhub_port->hw_portnum].bw_table;
} else { struct xhci_root_port_bw_info *rh_bw; struct xhci_tt_bw_info *tt_bw;
rh_bw = &xhci->rh_bw[dev->rhub_port->hw_portnum]; /* Find the right TT. */
list_for_each_entry(tt_bw, &rh_bw->tts, tt_list) { if (tt_bw->slot_id != udev->tt->hub->slot_id) continue;
if (!dev->udev->tt->multi ||
(udev->tt->multi &&
tt_bw->ttport == dev->udev->ttport)) {
dev->bw_table = &tt_bw->bw_table;
dev->tt_info = tt_bw; break;
}
} if (!dev->tt_info)
xhci_warn(xhci, "WARN: Didn't find a matching TT\n");
}
/* Is this a LS/FS device under an external HS hub? */ if (udev->tt && udev->tt->hub->parent) {
slot_ctx->tt_info = cpu_to_le32(udev->tt->hub->slot_id |
(udev->ttport << 8)); if (udev->tt->multi)
slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
}
xhci_dbg(xhci, "udev->tt = %p\n", udev->tt);
xhci_dbg(xhci, "udev->ttport = 0x%x\n", udev->ttport);
switch (udev->speed) { case USB_SPEED_HIGH: /* Max NAK rate */ if (usb_endpoint_xfer_control(&ep->desc) ||
usb_endpoint_xfer_bulk(&ep->desc)) {
interval = xhci_parse_microframe_interval(udev, ep); break;
}
fallthrough; /* SS and HS isoc/int have same decoding */
case USB_SPEED_SUPER_PLUS: case USB_SPEED_SUPER: if (usb_endpoint_xfer_int(&ep->desc) ||
usb_endpoint_xfer_isoc(&ep->desc)) {
interval = xhci_parse_exponent_interval(udev, ep);
} break;
case USB_SPEED_FULL: if (usb_endpoint_xfer_isoc(&ep->desc)) {
interval = xhci_parse_exponent_interval(udev, ep); break;
} /* *Fallthroughforinterruptendpointintervaldecoding *sinceitusesthesamerulesaslowspeedinterrupt *endpoints.
*/
fallthrough;
case USB_SPEED_LOW: if (usb_endpoint_xfer_int(&ep->desc) ||
usb_endpoint_xfer_isoc(&ep->desc)) {
/* The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps. *Highspeedendpointdescriptorscandefine"thenumberofadditional *transactionopportunitiespermicroframe",butthatgoesintheMaxBurst *endpointcontextfield.
*/ static u32 xhci_get_endpoint_mult(struct usb_device *udev, struct usb_host_endpoint *ep)
{ if (udev->speed < USB_SPEED_SUPER ||
!usb_endpoint_xfer_isoc(&ep->desc)) return0; return ep->ss_ep_comp.bmAttributes;
}
static u32 xhci_get_endpoint_max_burst(struct usb_device *udev, struct usb_host_endpoint *ep)
{ /* Super speed and Plus have max burst in ep companion desc */ if (udev->speed >= USB_SPEED_SUPER) return ep->ss_ep_comp.bMaxBurst;
static u32 xhci_get_endpoint_type(struct usb_host_endpoint *ep)
{ int in;
in = usb_endpoint_dir_in(&ep->desc);
switch (usb_endpoint_type(&ep->desc)) { case USB_ENDPOINT_XFER_CONTROL: return CTRL_EP; case USB_ENDPOINT_XFER_BULK: return in ? BULK_IN_EP : BULK_OUT_EP; case USB_ENDPOINT_XFER_ISOC: return in ? ISOC_IN_EP : ISOC_OUT_EP; case USB_ENDPOINT_XFER_INT: return in ? INT_IN_EP : INT_OUT_EP;
} return0;
}
/* Return the maximum endpoint service interval time (ESIT) payload. *Basically,thisisthemaxpacketsize,multipliedbytheburstsize *andmultsize.
*/ static u32 xhci_get_max_esit_payload(struct usb_device *udev, struct usb_host_endpoint *ep)
{ int max_burst; int max_packet;
/* Only applies for interrupt or isochronous endpoints */ if (usb_endpoint_xfer_control(&ep->desc) ||
usb_endpoint_xfer_bulk(&ep->desc)) return0;
/* SuperSpeedPlus Isoc ep sending over 48k per esit */ if ((udev->speed >= USB_SPEED_SUPER_PLUS) &&
USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes)) return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval);
/* SuperSpeed or SuperSpeedPlus Isoc ep with less than 48k per esit */ if (udev->speed >= USB_SPEED_SUPER) return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
max_packet = usb_endpoint_maxp(&ep->desc);
max_burst = usb_endpoint_maxp_mult(&ep->desc); /* A 0 in max burst means 1 transfer per ESIT */ return max_packet * max_burst;
}
/* Set up an endpoint with one ring segment. Do not allocate stream rings. *Driverswillhavetocallusb_alloc_streams()todothat.
*/ int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *udev, struct usb_host_endpoint *ep,
gfp_t mem_flags)
{ unsignedint ep_index; struct xhci_ep_ctx *ep_ctx; struct xhci_ring *ep_ring; unsignedint max_packet; enum xhci_ring_type ring_type;
u32 max_esit_payload;
u32 endpoint_type; unsignedint max_burst; unsignedint interval; unsignedint mult; unsignedint avg_trb_len; unsignedint err_count = 0;
/* FIXME dig Mult and streams info out of ep companion desc */
/* Allow 3 retries for everything but isoc, set CErr = 3 */ if (!usb_endpoint_xfer_isoc(&ep->desc))
err_count = 3; /* HS bulk max packet should be 512, FS bulk supports 8, 16, 32 or 64 */ if (usb_endpoint_xfer_bulk(&ep->desc)) { if (udev->speed == USB_SPEED_HIGH)
max_packet = 512; if (udev->speed == USB_SPEED_FULL) {
max_packet = rounddown_pow_of_two(max_packet);
max_packet = clamp_val(max_packet, 8, 64);
}
} /* xHCI 1.0 and 1.1 indicates that ctrl ep avg TRB Length should be 8 */ if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version >= 0x100)
avg_trb_len = 8; /* xhci 1.1 with LEC support doesn't use mult field, use RsvdZ */ if ((xhci->hci_version > 0x100) && HCC2_LEC(xhci->hcc_params2))
mult = 0;
/* Set up the endpoint ring */
virt_dev->eps[ep_index].new_ring =
xhci_ring_alloc(xhci, 2, ring_type, max_packet, mem_flags); if (!virt_dev->eps[ep_index].new_ring) return -ENOMEM;
ep_ctx->ep_info = 0;
ep_ctx->ep_info2 = 0;
ep_ctx->deq = 0;
ep_ctx->tx_info = 0; /* Don't free the endpoint ring until the set interface or configuration *requestsucceeds.
*/
}
for (i = 1; i < 31; i++) {
bw_info = &virt_dev->eps[i].bw_info;
/* We can't tell what endpoint type is being dropped, but *unconditionallyclearingthebandwidthinfofornon-periodic *endpointsshouldbeharmlessbecausetheinfowillneverbe *setinthefirstplace.
*/ if (!EP_IS_ADDED(ctrl_ctx, i) && EP_IS_DROPPED(ctrl_ctx, i)) { /* Dropped endpoint */
xhci_clear_endpoint_bw_info(bw_info); continue;
}
/* Set up the scratchpad buffer array and scratchpad buffers, if needed. */ staticint scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
{ int i; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; int num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2);
} elseif (major_revision <= 0x02) {
rhub = &xhci->usb2_rhub;
} else {
xhci_warn(xhci, "Ignoring unknown port speed, Ext Cap %p, revision = 0x%x\n",
addr, major_revision); /* Ignoring port protocol we can't understand. FIXME */ return;
}
/* Port offset and count in the third dword, see section 7.2 */
temp = readl(addr + 2);
port_offset = XHCI_EXT_PORT_OFF(temp);
port_count = XHCI_EXT_PORT_COUNT(temp);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Ext Cap %p, port offset = %u, count = %u, revision = 0x%x",
addr, port_offset, port_count, major_revision); /* Port count includes the current port offset */ if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) /* WTF? "Valid values are ‘1’ to MaxPorts" */ return;
port_cap = &xhci->port_caps[xhci->num_port_caps++]; if (xhci->num_port_caps > max_caps) return;
port_cap->psi_count = XHCI_EXT_PORT_PSIC(temp);
if (port_cap->psi_count) {
port_cap->psi = kcalloc_node(port_cap->psi_count, sizeof(*port_cap->psi),
GFP_KERNEL, dev_to_node(dev)); if (!port_cap->psi)
port_cap->psi_count = 0;
port_cap->psi_uid_count++; for (i = 0; i < port_cap->psi_count; i++) {
port_cap->psi[i] = readl(addr + 4 + i);
/* count unique ID values, two consecutive entries can *havethesameIDiflinkisassymetric
*/ if (i && (XHCI_EXT_PORT_PSIV(port_cap->psi[i]) !=
XHCI_EXT_PORT_PSIV(port_cap->psi[i - 1])))
port_cap->psi_uid_count++;
port_offset--; for (i = port_offset; i < (port_offset + port_count); i++) { struct xhci_port *hw_port = &xhci->hw_ports[i]; /* Duplicate entry. Ignore the port if the revisions differ. */ if (hw_port->rhub) {
xhci_warn(xhci, "Duplicate port entry, Ext Cap %p, port %u\n", addr, i);
xhci_warn(xhci, "Port was marked as USB %u, duplicated as USB %u\n",
hw_port->rhub->maj_rev, major_revision); /* Only adjust the roothub port counts if we haven't *foundasimilarduplicate.
*/ if (hw_port->rhub != rhub &&
hw_port->hcd_portnum != DUPLICATE_ENTRY) {
hw_port->rhub->num_ports--;
hw_port->hcd_portnum = DUPLICATE_ENTRY;
} continue;
}
hw_port->rhub = rhub;
hw_port->port_cap = port_cap;
rhub->num_ports++;
} /* FIXME: Should we disable ports not in the Extended Capabilities? */
}
xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags,
dev_to_node(dev)); if (!xhci->rh_bw) return -ENOMEM; for (i = 0; i < num_ports; i++) { struct xhci_interval_bw_table *bw_table;
INIT_LIST_HEAD(&xhci->rh_bw[i].tts);
bw_table = &xhci->rh_bw[i].bw_table; for (j = 0; j < XHCI_MAX_INTERVAL; j++)
INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints);
}
base = &xhci->cap_regs->hc_capbase;
cap_start = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_PROTOCOL); if (!cap_start) {
xhci_err(xhci, "No Extended Capability registers, unable to set up roothub\n"); return -ENODEV;
}
offset = cap_start; /* count extended protocol capability entries for later caching */ while (offset) {
cap_count++;
offset = xhci_find_next_ext_cap(base, offset,
XHCI_EXT_CAPS_PROTOCOL);
}
xhci->port_caps = kcalloc_node(cap_count, sizeof(*xhci->port_caps),
flags, dev_to_node(dev)); if (!xhci->port_caps) return -ENOMEM;
offset = cap_start;
while (offset) {
xhci_add_in_port(xhci, num_ports, base + offset, cap_count); if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports ==
num_ports) break;
offset = xhci_find_next_ext_cap(base, offset,
XHCI_EXT_CAPS_PROTOCOL);
} if (xhci->usb2_rhub.num_ports == 0 && xhci->usb3_rhub.num_ports == 0) {
xhci_warn(xhci, "No ports on the roothubs?\n"); return -ENODEV;
}
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Found %u USB 2.0 ports and %u USB 3.0 ports.",
xhci->usb2_rhub.num_ports, xhci->usb3_rhub.num_ports);
/* Place limits on the number of roothub ports so that the hub *descriptorsaren'tlongerthantheUSBcorewillallocate.
*/ if (xhci->usb3_rhub.num_ports > USB_SS_MAXPORTS) {
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Limiting USB 3.0 roothub ports to %u.",
USB_SS_MAXPORTS);
xhci->usb3_rhub.num_ports = USB_SS_MAXPORTS;
} if (xhci->usb2_rhub.num_ports > USB_MAXCHILDREN) {
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Limiting USB 2.0 roothub ports to %u.",
USB_MAXCHILDREN);
xhci->usb2_rhub.num_ports = USB_MAXCHILDREN;
}
if (!xhci->usb2_rhub.num_ports)
xhci_info(xhci, "USB2 root hub has no ports\n");
if (!xhci->usb3_rhub.num_ports)
xhci_info(xhci, "USB3 root hub has no ports\n");
ir = xhci->interrupters[intr_num];
ir->intr_num = intr_num;
ir->ir_set = &xhci->run_regs->ir_set[intr_num];
/* set ERST count with the number of entries in the segment table */
erst_size = readl(&ir->ir_set->erst_size);
erst_size &= ~ERST_SIZE_MASK;
erst_size |= ir->event_ring->num_segs;
writel(erst_size, &ir->ir_set->erst_size);
/* Set up the command ring to have one segments for now. */
xhci->cmd_ring = xhci_ring_alloc(xhci, 1, TYPE_COMMAND, 0, flags); if (!xhci->cmd_ring) goto fail;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocated command ring at %p", xhci->cmd_ring);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "First segment DMA is 0x%pad",
&xhci->cmd_ring->first_seg->dma);
/* Allocate and set up primary interrupter 0 with an event ring. */
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring");
xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters),
flags, dev_to_node(dev)); if (!xhci->interrupters) goto fail;
xhci->interrupters[0] = xhci_alloc_interrupter(xhci, 0, flags); if (!xhci->interrupters[0]) goto fail;
if (scratchpad_alloc(xhci, flags)) goto fail;
if (xhci_setup_port_arrays(xhci, flags)) goto fail;
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.