Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 264928
b: refs/heads/master
c: 839c817
h: refs/heads/master
v: v3
  • Loading branch information
Sarah Sharp authored and Greg Kroah-Hartman committed Sep 9, 2011
1 parent 84b48f0 commit 6412812
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 2 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: 66381755442189bbeb15f1a51b1e0059327d84ed
refs/heads/master: 839c817ce67178ca3c7c7ad534c571bba1e69ebe
132 changes: 131 additions & 1 deletion trunk/drivers/usb/host/xhci-mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,98 @@ static void xhci_init_endpoint_timer(struct xhci_hcd *xhci,
ep->xhci = xhci;
}

/* All the xhci_tds in the ring's TD list should be freed at this point */
static void xhci_free_tt_info(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
int slot_id)
{
struct list_head *tt;
struct list_head *tt_list_head;
struct list_head *tt_next;
struct xhci_tt_bw_info *tt_info;

/* If the device never made it past the Set Address stage,
* it may not have the real_port set correctly.
*/
if (virt_dev->real_port == 0 ||
virt_dev->real_port > HCS_MAX_PORTS(xhci->hcs_params1)) {
xhci_dbg(xhci, "Bad real port.\n");
return;
}

tt_list_head = &(xhci->rh_bw[virt_dev->real_port - 1].tts);
if (list_empty(tt_list_head))
return;

list_for_each(tt, tt_list_head) {
tt_info = list_entry(tt, struct xhci_tt_bw_info, tt_list);
if (tt_info->slot_id == slot_id)
break;
}
/* Cautionary measure in case the hub was disconnected before we
* stored the TT information.
*/
if (tt_info->slot_id != slot_id)
return;

tt_next = tt->next;
tt_info = list_entry(tt, struct xhci_tt_bw_info,
tt_list);
/* Multi-TT hubs will have more than one entry */
do {
list_del(tt);
kfree(tt_info);
tt = tt_next;
if (list_empty(tt_list_head))
break;
tt_next = tt->next;
tt_info = list_entry(tt, struct xhci_tt_bw_info,
tt_list);
} while (tt_info->slot_id == slot_id);
}

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;
unsigned int num_ports;
int i, j;

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;

tt_info = kzalloc(sizeof(*tt_info), mem_flags);
if (!tt_info)
goto free_tts;
INIT_LIST_HEAD(&tt_info->tt_list);
list_add(&tt_info->tt_list,
&xhci->rh_bw[virt_dev->real_port - 1].tts);
tt_info->slot_id = virt_dev->udev->slot_id;
if (tt->multi)
tt_info->ttport = i+1;
bw_table = &tt_info->bw_table;
for (j = 0; j < XHCI_MAX_INTERVAL; j++)
INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints);
}
return 0;

free_tts:
xhci_free_tt_info(xhci, virt_dev, virt_dev->udev->slot_id);
return -ENOMEM;
}


/* All the xhci_tds in the ring's TD list should be freed at this point.
* Should be called with xhci->lock held if there is any chance the TT lists
* will be manipulated by the configure endpoint, allocate device, or update
* hub functions while this function is removing the TT entries from the list.
*/
void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
{
struct xhci_virt_device *dev;
Expand All @@ -709,6 +800,8 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
xhci_free_stream_info(xhci,
dev->eps[i].stream_info);
}
/* If this is a hub, free the TT(s) from the TT list */
xhci_free_tt_info(xhci, dev, slot_id);

if (dev->ring_cache) {
for (i = 0; i < dev->num_rings_cached; i++)
Expand Down Expand Up @@ -926,6 +1019,36 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
xhci_dbg(xhci, "Set root hub portnum to %d\n", port_num);
xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->fake_port);

/* Find the right bandwidth table that this device will be a part of.
* If this is a full speed device attached directly to a root port (or a
* decendent of one), it counts as a primary bandwidth domain, not a
* secondary bandwidth domain under a TT. An xhci_tt_info structure
* will never be created for the HS root hub.
*/
if (!udev->tt || !udev->tt->hub->parent) {
dev->bw_table = &xhci->rh_bw[port_num - 1].bw_table;
} else {
struct xhci_root_port_bw_info *rh_bw;
struct xhci_tt_bw_info *tt_bw;

rh_bw = &xhci->rh_bw[port_num - 1];
/* 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 |
Expand Down Expand Up @@ -1552,6 +1675,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
kfree(xhci->usb2_ports);
kfree(xhci->usb3_ports);
kfree(xhci->port_array);
kfree(xhci->rh_bw);

xhci->page_size = 0;
xhci->page_shift = 0;
Expand Down Expand Up @@ -1822,6 +1946,12 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
if (!xhci->port_array)
return -ENOMEM;

xhci->rh_bw = kzalloc(sizeof(*xhci->rh_bw)*num_ports, flags);
if (!xhci->rh_bw)
return -ENOMEM;
for (i = 0; i < num_ports; i++)
INIT_LIST_HEAD(&xhci->rh_bw[i].tts);

/*
* For whatever reason, the first capability offset is from the
* capability register base, not from the HCCPARAMS register.
Expand Down
8 changes: 8 additions & 0 deletions trunk/drivers/usb/host/xhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -2990,6 +2990,14 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
}

spin_lock_irqsave(&xhci->lock, flags);
if (hdev->speed == USB_SPEED_HIGH &&
xhci_alloc_tt_info(xhci, vdev, hdev, tt, GFP_ATOMIC)) {
xhci_dbg(xhci, "Could not allocate xHCI TT structure.\n");
xhci_free_command(xhci, config_cmd);
spin_unlock_irqrestore(&xhci->lock, flags);
return -ENOMEM;
}

xhci_slot_copy(xhci, config_cmd->in_ctx, vdev->out_ctx);
ctrl_ctx = xhci_get_input_control_ctx(xhci, config_cmd->in_ctx);
ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
Expand Down
50 changes: 50 additions & 0 deletions trunk/drivers/usb/host/xhci.h
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,26 @@ struct xhci_virt_ep {
bool skip;
};

enum xhci_overhead_type {
LS_OVERHEAD_TYPE = 0,
FS_OVERHEAD_TYPE,
HS_OVERHEAD_TYPE,
};

struct xhci_interval_bw {
unsigned int num_packets;
/* How many endpoints of each speed are present. */
unsigned int overhead[3];
};

#define XHCI_MAX_INTERVAL 16

struct xhci_interval_bw_table {
unsigned int interval0_esit_payload;
struct xhci_interval_bw interval_bw[XHCI_MAX_INTERVAL];
};


struct xhci_virt_device {
struct usb_device *udev;
/*
Expand All @@ -800,6 +820,30 @@ struct xhci_virt_device {
struct list_head cmd_list;
u8 fake_port;
u8 real_port;
struct xhci_interval_bw_table *bw_table;
struct xhci_tt_bw_info *tt_info;
};

/*
* For each roothub, keep track of the bandwidth information for each periodic
* interval.
*
* If a high speed hub is attached to the roothub, each TT associated with that
* hub is a separate bandwidth domain. The interval information for the
* endpoints on the devices under that TT will appear in the TT structure.
*/
struct xhci_root_port_bw_info {
struct list_head tts;
unsigned int num_active_tts;
struct xhci_interval_bw_table bw_table;
};

struct xhci_tt_bw_info {
struct list_head tt_list;
int slot_id;
int ttport;
struct xhci_interval_bw_table bw_table;
int active_eps;
};


Expand Down Expand Up @@ -1268,6 +1312,8 @@ struct xhci_hcd {
int slot_id;
/* Internal mirror of the HW's dcbaa */
struct xhci_virt_device *devs[MAX_HC_SLOTS];
/* For keeping track of bandwidth domains per roothub. */
struct xhci_root_port_bw_info *rh_bw;

/* DMA pools */
struct dma_pool *device_pool;
Expand Down Expand Up @@ -1508,6 +1554,10 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd);
irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd);
int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev);
void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev);
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);
int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint **eps, unsigned int num_eps,
unsigned int num_streams, gfp_t mem_flags);
Expand Down

0 comments on commit 6412812

Please sign in to comment.