diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/xhci-mem.c | 89 |
1 files changed, 55 insertions, 34 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 212012c97df3..47b762994ae9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -65,6 +65,20 @@ static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) kfree(seg); } +static void xhci_free_segments_for_ring(struct xhci_hcd *xhci, + struct xhci_segment *first) +{ + struct xhci_segment *seg; + + seg = first->next; + while (seg != first) { + struct xhci_segment *next = seg->next; + xhci_segment_free(xhci, seg); + seg = next; + } + xhci_segment_free(xhci, first); +} + /* * Make the prev segment point to the next segment. * @@ -101,22 +115,12 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, /* XXX: Do we need the hcd structure in all these functions? */ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring) { - struct xhci_segment *seg; - struct xhci_segment *first_seg; - if (!ring) return; - if (ring->first_seg) { - first_seg = ring->first_seg; - seg = first_seg->next; - while (seg != first_seg) { - struct xhci_segment *next = seg->next; - xhci_segment_free(xhci, seg); - seg = next; - } - xhci_segment_free(xhci, first_seg); - ring->first_seg = NULL; - } + + if (ring->first_seg) + xhci_free_segments_for_ring(xhci, ring->first_seg); + kfree(ring); } @@ -143,6 +147,38 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring) ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1; } +/* Allocate segments and link them for a ring */ +static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, + struct xhci_segment **first, struct xhci_segment **last, + unsigned int num_segs, enum xhci_ring_type type, gfp_t flags) +{ + struct xhci_segment *prev; + + prev = xhci_segment_alloc(xhci, flags); + if (!prev) + return -ENOMEM; + num_segs--; + + *first = prev; + while (num_segs > 0) { + struct xhci_segment *next; + + next = xhci_segment_alloc(xhci, flags); + if (!next) { + xhci_free_segments_for_ring(xhci, *first); + return -ENOMEM; + } + xhci_link_segments(xhci, prev, next, type); + + prev = next; + num_segs--; + } + xhci_link_segments(xhci, prev, *first, type); + *last = prev; + + return 0; +} + /** * Create a new ring with zero or more segments. * @@ -154,7 +190,7 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, enum xhci_ring_type type, gfp_t flags) { struct xhci_ring *ring; - struct xhci_segment *prev; + int ret; ring = kzalloc(sizeof *(ring), flags); if (!ring) @@ -166,30 +202,15 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, if (num_segs == 0) return ring; - ring->first_seg = xhci_segment_alloc(xhci, flags); - if (!ring->first_seg) + ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg, + &ring->last_seg, num_segs, type, flags); + if (ret) goto fail; - num_segs--; - - prev = ring->first_seg; - while (num_segs > 0) { - struct xhci_segment *next; - - next = xhci_segment_alloc(xhci, flags); - if (!next) - goto fail; - xhci_link_segments(xhci, prev, next, type); - - prev = next; - num_segs--; - } - xhci_link_segments(xhci, prev, ring->first_seg, type); - ring->last_seg = prev; /* Only event ring does not use link TRB */ if (type != TYPE_EVENT) { /* See section 4.9.2.1 and 6.4.4.1 */ - prev->trbs[TRBS_PER_SEGMENT-1].link.control |= + ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= cpu_to_le32(LINK_TOGGLE); } xhci_initialize_ring_info(ring); |