Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 264930
b: refs/heads/master
c: 2e27980
h: refs/heads/master
v: v3
  • Loading branch information
Sarah Sharp authored and Greg Kroah-Hartman committed Sep 9, 2011
1 parent 830c8a0 commit 2a18c7f
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 4 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 9af5d71d8e1fc404ad2ac1b568dafa1a2f9b3be2
refs/heads/master: 2e27980e6eb78114c4ecbaad1ba71836e3887d18
26 changes: 24 additions & 2 deletions trunk/drivers/usb/host/xhci-mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
{
struct xhci_virt_device *dev;
int i;
int old_active_eps = 0;

/* Slot ID 0 is reserved */
if (slot_id == 0 || !xhci->devs[slot_id])
Expand All @@ -793,15 +794,29 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
if (!dev)
return;

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);
/* Endpoints on the TT/root port lists should have been removed
* when usb_disable_device() was called for the device.
* We can't drop them anyway, because the udev might have gone
* away by this point, and we can't tell what speed it was.
*/
if (!list_empty(&dev->eps[i].bw_endpoint_list))
xhci_warn(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->ring_cache) {
for (i = 0; i < dev->num_rings_cached; i++)
Expand Down Expand Up @@ -855,6 +870,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
for (i = 0; i < 31; i++) {
xhci_init_endpoint_timer(xhci, &dev->eps[i]);
INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list);
INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list);
}

/* Allocate endpoint 0 ring */
Expand Down Expand Up @@ -1994,7 +2010,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
__le32 __iomem *addr;
u32 offset;
unsigned int num_ports;
int i, port_index;
int i, j, port_index;

addr = &xhci->cap_regs->hcc_params;
offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr));
Expand All @@ -2012,8 +2028,14 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
xhci->rh_bw = kzalloc(sizeof(*xhci->rh_bw)*num_ports, flags);
if (!xhci->rh_bw)
return -ENOMEM;
for (i = 0; i < num_ports; i++)
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);
}

/*
* For whatever reason, the first capability offset is from the
Expand Down
255 changes: 254 additions & 1 deletion trunk/drivers/usb/host/xhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1747,6 +1747,241 @@ static void xhci_finish_resource_reservation(struct xhci_hcd *xhci,
xhci->num_active_eps);
}

/* Run the algorithm on the bandwidth table. If this table is part of a
* TT, see if we need to update the number of active TTs.
*/
static int xhci_check_bw_table(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
int old_active_eps)
{
return 0;
}

static bool xhci_is_async_ep(unsigned int ep_type)
{
return (ep_type != ISOC_OUT_EP && ep_type != INT_OUT_EP &&
ep_type != ISOC_IN_EP &&
ep_type != INT_IN_EP);
}

void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci,
struct xhci_bw_info *ep_bw,
struct xhci_interval_bw_table *bw_table,
struct usb_device *udev,
struct xhci_virt_ep *virt_ep,
struct xhci_tt_bw_info *tt_info)
{
struct xhci_interval_bw *interval_bw;
int normalized_interval;

if (xhci_is_async_ep(ep_bw->type) ||
list_empty(&virt_ep->bw_endpoint_list))
return;

/* For LS/FS devices, we need to translate the interval expressed in
* microframes to frames.
*/
if (udev->speed == USB_SPEED_HIGH)
normalized_interval = ep_bw->ep_interval;
else
normalized_interval = ep_bw->ep_interval - 3;

if (normalized_interval == 0)
bw_table->interval0_esit_payload -= ep_bw->max_esit_payload;
interval_bw = &bw_table->interval_bw[normalized_interval];
interval_bw->num_packets -= ep_bw->num_packets;
switch (udev->speed) {
case USB_SPEED_LOW:
interval_bw->overhead[LS_OVERHEAD_TYPE] -= 1;
break;
case USB_SPEED_FULL:
interval_bw->overhead[FS_OVERHEAD_TYPE] -= 1;
break;
case USB_SPEED_HIGH:
interval_bw->overhead[HS_OVERHEAD_TYPE] -= 1;
break;
case USB_SPEED_SUPER:
case USB_SPEED_UNKNOWN:
case USB_SPEED_WIRELESS:
/* Should never happen because only LS/FS/HS endpoints will get
* added to the endpoint list.
*/
return;
}
if (tt_info)
tt_info->active_eps -= 1;
list_del_init(&virt_ep->bw_endpoint_list);
}

static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci,
struct xhci_bw_info *ep_bw,
struct xhci_interval_bw_table *bw_table,
struct usb_device *udev,
struct xhci_virt_ep *virt_ep,
struct xhci_tt_bw_info *tt_info)
{
struct xhci_interval_bw *interval_bw;
struct xhci_virt_ep *smaller_ep;
int normalized_interval;

if (xhci_is_async_ep(ep_bw->type))
return;

/* For LS/FS devices, we need to translate the interval expressed in
* microframes to frames.
*/
if (udev->speed == USB_SPEED_HIGH)
normalized_interval = ep_bw->ep_interval;
else
normalized_interval = ep_bw->ep_interval - 3;

if (normalized_interval == 0)
bw_table->interval0_esit_payload += ep_bw->max_esit_payload;
interval_bw = &bw_table->interval_bw[normalized_interval];
interval_bw->num_packets += ep_bw->num_packets;
switch (udev->speed) {
case USB_SPEED_LOW:
interval_bw->overhead[LS_OVERHEAD_TYPE] += 1;
break;
case USB_SPEED_FULL:
interval_bw->overhead[FS_OVERHEAD_TYPE] += 1;
break;
case USB_SPEED_HIGH:
interval_bw->overhead[HS_OVERHEAD_TYPE] += 1;
break;
case USB_SPEED_SUPER:
case USB_SPEED_UNKNOWN:
case USB_SPEED_WIRELESS:
/* Should never happen because only LS/FS/HS endpoints will get
* added to the endpoint list.
*/
return;
}

if (tt_info)
tt_info->active_eps += 1;
/* Insert the endpoint into the list, largest max packet size first. */
list_for_each_entry(smaller_ep, &interval_bw->endpoints,
bw_endpoint_list) {
if (ep_bw->max_packet_size >=
smaller_ep->bw_info.max_packet_size) {
/* Add the new ep before the smaller endpoint */
list_add_tail(&virt_ep->bw_endpoint_list,
&smaller_ep->bw_endpoint_list);
return;
}
}
/* Add the new endpoint at the end of the list. */
list_add_tail(&virt_ep->bw_endpoint_list,
&interval_bw->endpoints);
}

void xhci_update_tt_active_eps(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
int old_active_eps)
{
struct xhci_root_port_bw_info *rh_bw_info;
if (!virt_dev->tt_info)
return;

rh_bw_info = &xhci->rh_bw[virt_dev->real_port - 1];
if (old_active_eps == 0 &&
virt_dev->tt_info->active_eps != 0) {
rh_bw_info->num_active_tts += 1;
} else if (old_active_eps != 0 &&
virt_dev->tt_info->active_eps == 0) {
rh_bw_info->num_active_tts -= 1;
}
}

static int xhci_reserve_bandwidth(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
struct xhci_container_ctx *in_ctx)
{
struct xhci_bw_info ep_bw_info[31];
int i;
struct xhci_input_control_ctx *ctrl_ctx;
int old_active_eps = 0;

if (virt_dev->udev->speed == USB_SPEED_SUPER)
return 0;

if (virt_dev->tt_info)
old_active_eps = virt_dev->tt_info->active_eps;

ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);

for (i = 0; i < 31; i++) {
if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i))
continue;

/* Make a copy of the BW info in case we need to revert this */
memcpy(&ep_bw_info[i], &virt_dev->eps[i].bw_info,
sizeof(ep_bw_info[i]));
/* Drop the endpoint from the interval table if the endpoint is
* being dropped or changed.
*/
if (EP_IS_DROPPED(ctrl_ctx, i))
xhci_drop_ep_from_interval_table(xhci,
&virt_dev->eps[i].bw_info,
virt_dev->bw_table,
virt_dev->udev,
&virt_dev->eps[i],
virt_dev->tt_info);
}
/* Overwrite the information stored in the endpoints' bw_info */
xhci_update_bw_info(xhci, virt_dev->in_ctx, ctrl_ctx, virt_dev);
for (i = 0; i < 31; i++) {
/* Add any changed or added endpoints to the interval table */
if (EP_IS_ADDED(ctrl_ctx, i))
xhci_add_ep_to_interval_table(xhci,
&virt_dev->eps[i].bw_info,
virt_dev->bw_table,
virt_dev->udev,
&virt_dev->eps[i],
virt_dev->tt_info);
}

if (!xhci_check_bw_table(xhci, virt_dev, old_active_eps)) {
/* Ok, this fits in the bandwidth we have.
* Update the number of active TTs.
*/
xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps);
return 0;
}

/* We don't have enough bandwidth for this, revert the stored info. */
for (i = 0; i < 31; i++) {
if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i))
continue;

/* Drop the new copies of any added or changed endpoints from
* the interval table.
*/
if (EP_IS_ADDED(ctrl_ctx, i)) {
xhci_drop_ep_from_interval_table(xhci,
&virt_dev->eps[i].bw_info,
virt_dev->bw_table,
virt_dev->udev,
&virt_dev->eps[i],
virt_dev->tt_info);
}
/* Revert the endpoint back to its old information */
memcpy(&virt_dev->eps[i].bw_info, &ep_bw_info[i],
sizeof(ep_bw_info[i]));
/* Add any changed or dropped endpoints back into the table */
if (EP_IS_DROPPED(ctrl_ctx, i))
xhci_add_ep_to_interval_table(xhci,
&virt_dev->eps[i].bw_info,
virt_dev->bw_table,
virt_dev->udev,
&virt_dev->eps[i],
virt_dev->tt_info);
}
return -ENOMEM;
}


/* Issue a configure endpoint command or evaluate context command
* and wait for it to finish.
*/
Expand Down Expand Up @@ -1779,6 +2014,14 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
xhci->num_active_eps);
return -ENOMEM;
}
if ((xhci->quirks & XHCI_SW_BW_CHECKING) &&
xhci_reserve_bandwidth(xhci, virt_dev, in_ctx)) {
if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK))
xhci_free_host_resources(xhci, in_ctx);
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_warn(xhci, "Not enough bandwidth\n");
return -ENOMEM;
}

if (command) {
cmd_completion = command->completion;
Expand Down Expand Up @@ -1912,7 +2155,6 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
!(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1))))
xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
}
xhci_update_bw_info(xhci, virt_dev->in_ctx, ctrl_ctx, virt_dev);
xhci_zero_in_ctx(xhci, virt_dev);
/*
* Install any rings for completely new endpoints or changed endpoints,
Expand Down Expand Up @@ -2528,6 +2770,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
int timeleft;
int last_freed_endpoint;
struct xhci_slot_ctx *slot_ctx;
int old_active_eps = 0;

ret = xhci_check_args(hcd, udev, NULL, 0, false, __func__);
if (ret <= 0)
Expand Down Expand Up @@ -2669,8 +2912,18 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
last_freed_endpoint = i;
}
if (!list_empty(&virt_dev->eps[i].bw_endpoint_list))
xhci_drop_ep_from_interval_table(xhci,
&virt_dev->eps[i].bw_info,
virt_dev->bw_table,
udev,
&virt_dev->eps[i],
virt_dev->tt_info);
xhci_clear_endpoint_bw_info(&virt_dev->eps[i].bw_info);
}
/* If necessary, update the number of active TTs on this root port */
xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps);

xhci_dbg(xhci, "Output context after successful reset device cmd:\n");
xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint);
ret = 0;
Expand Down
Loading

0 comments on commit 2a18c7f

Please sign in to comment.