diff options
Diffstat (limited to 'drivers/staging/ozwpan/ozhcd.c')
-rw-r--r-- | drivers/staging/ozwpan/ozhcd.c | 178 |
1 files changed, 48 insertions, 130 deletions
diff --git a/drivers/staging/ozwpan/ozhcd.c b/drivers/staging/ozwpan/ozhcd.c index 30bd9286d47d..e88045228607 100644 --- a/drivers/staging/ozwpan/ozhcd.c +++ b/drivers/staging/ozwpan/ozhcd.c @@ -45,14 +45,6 @@ */ #define OZ_PLAT_DEV_NAME "ozwpan" -/* Maximum number of free urb links that can be kept in the pool. - */ -#define OZ_MAX_LINK_POOL_SIZE 16 - -/* Get endpoint object from the containing link. - */ -#define ep_from_link(__e) container_of((__e), struct oz_endpoint, link) - /*EP0 timeout before ep0 request is again added to TX queue. (13*8 = 98mSec) */ #define EP0_TIMEOUT_COUNTER 13 @@ -75,6 +67,8 @@ struct oz_urb_link { unsigned submit_counter; }; +static struct kmem_cache *oz_urb_link_cache; + /* Holds state information about a USB endpoint. */ #define OZ_EP_BUFFER_SIZE_ISOC (1024 * 24) @@ -198,9 +192,6 @@ static struct platform_device *g_plat_dev; static struct oz_hcd *g_ozhcd; static DEFINE_SPINLOCK(g_hcdlock); /* Guards g_ozhcd. */ static const char g_hcd_name[] = "Ozmo WPAN"; -static struct list_head *g_link_pool; -static int g_link_pool_size; -static DEFINE_SPINLOCK(g_link_lock); static DEFINE_SPINLOCK(g_tasklet_lock); static struct tasklet_struct g_urb_process_tasklet; static struct tasklet_struct g_urb_cancel_tasklet; @@ -265,68 +256,22 @@ static int oz_get_port_from_addr(struct oz_hcd *ozhcd, u8 bus_addr) } /* - * Allocates an urb link, first trying the pool but going to heap if empty. * Context: any */ static struct oz_urb_link *oz_alloc_urb_link(void) { - struct oz_urb_link *urbl = NULL; - unsigned long irq_state; - - spin_lock_irqsave(&g_link_lock, irq_state); - if (g_link_pool) { - urbl = container_of(g_link_pool, struct oz_urb_link, link); - g_link_pool = urbl->link.next; - --g_link_pool_size; - } - spin_unlock_irqrestore(&g_link_lock, irq_state); - if (urbl == NULL) - urbl = kmalloc(sizeof(struct oz_urb_link), GFP_ATOMIC); - return urbl; + return kmem_cache_alloc(oz_urb_link_cache, GFP_ATOMIC); } /* - * Frees an urb link by putting it in the pool if there is enough space or - * deallocating it to heap otherwise. * Context: any */ static void oz_free_urb_link(struct oz_urb_link *urbl) { - if (urbl) { - unsigned long irq_state; - - spin_lock_irqsave(&g_link_lock, irq_state); - if (g_link_pool_size < OZ_MAX_LINK_POOL_SIZE) { - urbl->link.next = g_link_pool; - g_link_pool = &urbl->link; - urbl = NULL; - g_link_pool_size++; - } - spin_unlock_irqrestore(&g_link_lock, irq_state); - kfree(urbl); - } -} - -/* - * Deallocates all the urb links in the pool. - * Context: unknown - */ -static void oz_empty_link_pool(void) -{ - struct list_head *e; - unsigned long irq_state; + if (!urbl) + return; - spin_lock_irqsave(&g_link_lock, irq_state); - e = g_link_pool; - g_link_pool = NULL; - g_link_pool_size = 0; - spin_unlock_irqrestore(&g_link_lock, irq_state); - while (e) { - struct oz_urb_link *urbl = - container_of(e, struct oz_urb_link, link); - e = e->next; - kfree(urbl); - } + kmem_cache_free(oz_urb_link_cache, urbl); } /* @@ -359,12 +304,10 @@ static struct oz_urb_link *oz_uncancel_urb(struct oz_hcd *ozhcd, struct urb *urb) { struct oz_urb_link *urbl; - struct list_head *e; - list_for_each(e, &ozhcd->urb_cancel_list) { - urbl = container_of(e, struct oz_urb_link, link); + list_for_each_entry(urbl, &ozhcd->urb_cancel_list, link) { if (urb == urbl->urb) { - list_del_init(e); + list_del_init(&urbl->link); return urbl; } } @@ -423,10 +366,9 @@ static void oz_complete_urb(struct usb_hcd *hcd, struct urb *urb, static void oz_ep_free(struct oz_port *port, struct oz_endpoint *ep) { if (port) { - struct list_head list; + LIST_HEAD(list); struct oz_hcd *ozhcd = port->ozhcd; - INIT_LIST_HEAD(&list); if (ep->flags & OZ_F_EP_HAVE_STREAM) oz_usb_stream_delete(port->hpd, ep->ep_num); /* Transfer URBs to the orphanage while we hold the lock. */ @@ -572,7 +514,7 @@ static int oz_dequeue_ep_urb(struct oz_port *port, u8 ep_addr, int in_dir, struct list_head *e; list_for_each(e, &ep->urb_list) { - urbl = container_of(e, struct oz_urb_link, link); + urbl = list_entry(e, struct oz_urb_link, link); if (urbl->urb == urb) { list_del_init(e); break; @@ -604,7 +546,7 @@ static struct urb *oz_find_urb_by_id(struct oz_port *port, int ep_ix, struct list_head *e; list_for_each(e, &ep->urb_list) { - urbl = container_of(e, struct oz_urb_link, link); + urbl = list_entry(e, struct oz_urb_link, link); if (urbl->req_id == req_id) { urb = urbl->urb; list_del_init(e); @@ -1068,10 +1010,9 @@ void oz_hcd_data_ind(void *hport, u8 endpoint, const u8 *data, int data_len) urb->actual_length = copy_len; oz_complete_urb(port->ozhcd->hcd, urb, 0); return; - } else { - oz_dbg(ON, "buffering frame as URB is not available\n"); - oz_hcd_buffer_data(ep, data, data_len); } + oz_dbg(ON, "buffering frame as URB is not available\n"); + oz_hcd_buffer_data(ep, data, data_len); break; case USB_ENDPOINT_XFER_ISOC: oz_hcd_buffer_data(ep, data, data_len); @@ -1097,21 +1038,17 @@ int oz_hcd_heartbeat(void *hport) int rc = 0; struct oz_port *port = (struct oz_port *)hport; struct oz_hcd *ozhcd = port->ozhcd; - struct oz_urb_link *urbl; - struct list_head xfr_list; - struct list_head *e; - struct list_head *n; + struct oz_urb_link *urbl, *n; + LIST_HEAD(xfr_list); struct urb *urb; struct oz_endpoint *ep; struct timespec ts, delta; getrawmonotonic(&ts); - INIT_LIST_HEAD(&xfr_list); /* Check the OUT isoc endpoints to see if any URB data can be sent. */ spin_lock_bh(&ozhcd->hcd_lock); - list_for_each(e, &port->isoc_out_ep) { - ep = ep_from_link(e); + list_for_each_entry(ep, &port->isoc_out_ep, link) { if (ep->credit < 0) continue; delta = timespec_sub(ts, ep->timestamp); @@ -1134,10 +1071,9 @@ int oz_hcd_heartbeat(void *hport) spin_unlock_bh(&ozhcd->hcd_lock); /* Send to PD and complete URBs. */ - list_for_each_safe(e, n, &xfr_list) { - urbl = container_of(e, struct oz_urb_link, link); + list_for_each_entry_safe(urbl, n, &xfr_list, link) { urb = urbl->urb; - list_del_init(e); + list_del_init(&urbl->link); urb->error_count = 0; urb->start_frame = oz_usb_get_frame_number(); oz_usb_send_isoc(port->hpd, urbl->ep_num, urb); @@ -1147,9 +1083,7 @@ int oz_hcd_heartbeat(void *hport) /* Check the IN isoc endpoints to see if any URBs can be completed. */ spin_lock_bh(&ozhcd->hcd_lock); - list_for_each(e, &port->isoc_in_ep) { - struct oz_endpoint *ep = ep_from_link(e); - + list_for_each_entry(ep, &port->isoc_in_ep, link) { if (ep->flags & OZ_F_EP_BUFFERING) { if (ep->buffered_units >= OZ_IN_BUFFERING_UNITS) { ep->flags &= ~OZ_F_EP_BUFFERING; @@ -1162,10 +1096,7 @@ int oz_hcd_heartbeat(void *hport) delta = timespec_sub(ts, ep->timestamp); ep->credit += div_u64(timespec_to_ns(&delta), NSEC_PER_MSEC); ep->timestamp = ts; - while (!list_empty(&ep->urb_list)) { - struct oz_urb_link *urbl = - list_first_entry(&ep->urb_list, - struct oz_urb_link, link); + list_for_each_entry_safe(urbl, n, &ep->urb_list, link) { struct urb *urb = urbl->urb; int len = 0; int copy_len; @@ -1212,10 +1143,9 @@ int oz_hcd_heartbeat(void *hport) spin_unlock_bh(&ozhcd->hcd_lock); /* Complete the filled URBs. */ - list_for_each_safe(e, n, &xfr_list) { - urbl = container_of(e, struct oz_urb_link, link); + list_for_each_entry_safe(urbl, n, &xfr_list, link) { urb = urbl->urb; - list_del_init(e); + list_del_init(&urbl->link); oz_free_urb_link(urbl); oz_complete_urb(port->ozhcd->hcd, urb, 0); } @@ -1224,15 +1154,11 @@ int oz_hcd_heartbeat(void *hport) */ ep = port->out_ep[0]; if (ep) { - struct list_head *e; - struct list_head *n; - spin_lock_bh(&ozhcd->hcd_lock); - list_for_each_safe(e, n, &ep->urb_list) { - urbl = container_of(e, struct oz_urb_link, link); + list_for_each_entry_safe(urbl, n, &ep->urb_list, link) { if (urbl->submit_counter > EP0_TIMEOUT_COUNTER) { oz_dbg(ON, "Request 0x%p timeout\n", urbl->urb); - list_move_tail(e, &xfr_list); + list_move_tail(&urbl->link, &xfr_list); urbl->submit_counter = 0; } else { urbl->submit_counter++; @@ -1241,10 +1167,7 @@ int oz_hcd_heartbeat(void *hport) if (!list_empty(&ep->urb_list)) rc = 1; spin_unlock_bh(&ozhcd->hcd_lock); - e = xfr_list.next; - while (e != &xfr_list) { - urbl = container_of(e, struct oz_urb_link, link); - e = e->next; + list_for_each_entry_safe(urbl, n, &xfr_list, link) { oz_dbg(ON, "Resending request to PD\n"); oz_process_ep0_urb(ozhcd, urbl->urb, GFP_ATOMIC); oz_free_urb_link(urbl); @@ -1343,12 +1266,12 @@ static void oz_clean_endpoints_for_interface(struct usb_hcd *hcd, struct oz_hcd *ozhcd = port->ozhcd; unsigned mask; int i; - struct list_head ep_list; + LIST_HEAD(ep_list); + struct oz_endpoint *ep, *n; oz_dbg(ON, "Deleting endpoints for interface %d\n", if_ix); if (if_ix >= port->num_iface) return; - INIT_LIST_HEAD(&ep_list); spin_lock_bh(&ozhcd->hcd_lock); mask = port->iface[if_ix].ep_mask; port->iface[if_ix].ep_mask = 0; @@ -1372,9 +1295,7 @@ static void oz_clean_endpoints_for_interface(struct usb_hcd *hcd, } } spin_unlock_bh(&ozhcd->hcd_lock); - while (!list_empty(&ep_list)) { - struct oz_endpoint *ep = - list_first_entry(&ep_list, struct oz_endpoint, link); + list_for_each_entry_safe(ep, n, &ep_list, link) { list_del_init(&ep->link); oz_ep_free(port, ep); } @@ -1394,8 +1315,8 @@ static int oz_build_endpoints_for_config(struct usb_hcd *hcd, if (num_iface) { struct oz_interface *iface; - iface = kmalloc(num_iface*sizeof(struct oz_interface), - mem_flags | __GFP_ZERO); + iface = kmalloc_array(num_iface, sizeof(struct oz_interface), + mem_flags | __GFP_ZERO); if (!iface) return -ENOMEM; spin_lock_bh(&ozhcd->hcd_lock); @@ -1645,6 +1566,7 @@ static void oz_urb_process_tasklet(unsigned long unused) unsigned long irq_state; struct urb *urb; struct oz_hcd *ozhcd = oz_hcd_claim(); + struct oz_urb_link *urbl, *n; int rc = 0; if (ozhcd == NULL) @@ -1654,10 +1576,7 @@ static void oz_urb_process_tasklet(unsigned long unused) * appropriately while removing urbs. */ spin_lock_irqsave(&g_tasklet_lock, irq_state); - while (!list_empty(&ozhcd->urb_pending_list)) { - struct oz_urb_link *urbl = - list_first_entry(&ozhcd->urb_pending_list, - struct oz_urb_link, link); + list_for_each_entry_safe(urbl, n, &ozhcd->urb_pending_list, link) { list_del_init(&urbl->link); spin_unlock_irqrestore(&g_tasklet_lock, irq_state); urb = urbl->urb; @@ -1702,7 +1621,7 @@ static void oz_urb_cancel(struct oz_port *port, u8 ep_num, struct urb *urb) */ spin_lock_irqsave(&g_tasklet_lock, irq_state); list_for_each(e, &ozhcd->urb_cancel_list) { - urbl = container_of(e, struct oz_urb_link, link); + urbl = list_entry(e, struct oz_urb_link, link); if (urb == urbl->urb) { list_del_init(e); spin_unlock_irqrestore(&g_tasklet_lock, irq_state); @@ -1716,7 +1635,7 @@ static void oz_urb_cancel(struct oz_port *port, u8 ep_num, struct urb *urb) */ spin_lock_irqsave(&ozhcd->hcd_lock, irq_state); list_for_each(e, &ozhcd->orphanage) { - urbl = container_of(e, struct oz_urb_link, link); + urbl = list_entry(e, struct oz_urb_link, link); if (urbl->urb == urb) { list_del(e); oz_dbg(ON, "Found urb in orphanage\n"); @@ -1746,15 +1665,13 @@ static void oz_urb_cancel_tasklet(unsigned long unused) { unsigned long irq_state; struct urb *urb; + struct oz_urb_link *urbl, *n; struct oz_hcd *ozhcd = oz_hcd_claim(); if (ozhcd == NULL) return; spin_lock_irqsave(&g_tasklet_lock, irq_state); - while (!list_empty(&ozhcd->urb_cancel_list)) { - struct oz_urb_link *urbl = - list_first_entry(&ozhcd->urb_cancel_list, - struct oz_urb_link, link); + list_for_each_entry_safe(urbl, n, &ozhcd->urb_cancel_list, link) { list_del_init(&urbl->link); spin_unlock_irqrestore(&g_tasklet_lock, irq_state); urb = urbl->urb; @@ -1773,11 +1690,9 @@ static void oz_urb_cancel_tasklet(unsigned long unused) static void oz_hcd_clear_orphanage(struct oz_hcd *ozhcd, int status) { if (ozhcd) { - struct oz_urb_link *urbl; + struct oz_urb_link *urbl, *n; - while (!list_empty(&ozhcd->orphanage)) { - urbl = list_first_entry(&ozhcd->orphanage, - struct oz_urb_link, link); + list_for_each_entry_safe(urbl, n, &ozhcd->orphanage, link) { list_del(&urbl->link); oz_complete_urb(ozhcd->hcd, urbl->urb, status); oz_free_urb_link(urbl); @@ -1875,14 +1790,13 @@ static struct oz_urb_link *oz_remove_urb(struct oz_endpoint *ep, struct urb *urb) { struct oz_urb_link *urbl; - struct list_head *e; if (unlikely(ep == NULL)) return NULL; - list_for_each(e, &ep->urb_list) { - urbl = container_of(e, struct oz_urb_link, link); + + list_for_each_entry(urbl, &ep->urb_list, link) { if (urbl->urb == urb) { - list_del_init(e); + list_del_init(&urbl->link); if (usb_pipeisoc(urb->pipe)) { ep->credit -= urb->number_of_packets; if (ep->credit < 0) @@ -1988,8 +1902,7 @@ static int oz_hcd_hub_status_data(struct usb_hcd *hcd, char *buf) spin_unlock_bh(&ozhcd->hcd_lock); if (buf[0] != 0 || buf[1] != 0) return 2; - else - return 0; + return 0; } /* @@ -2311,7 +2224,6 @@ static int oz_plat_remove(struct platform_device *dev) oz_dbg(ON, "Removing hcd\n"); usb_remove_hcd(hcd); usb_put_hcd(hcd); - oz_empty_link_pool(); return 0; } @@ -2341,6 +2253,11 @@ int oz_hcd_init(void) if (usb_disabled()) return -ENODEV; + + oz_urb_link_cache = KMEM_CACHE(oz_urb_link, 0); + if (!oz_urb_link_cache) + return -ENOMEM; + tasklet_init(&g_urb_process_tasklet, oz_urb_process_tasklet, 0); tasklet_init(&g_urb_cancel_tasklet, oz_urb_cancel_tasklet, 0); err = platform_driver_register(&g_oz_plat_drv); @@ -2380,4 +2297,5 @@ void oz_hcd_term(void) platform_device_unregister(g_plat_dev); platform_driver_unregister(&g_oz_plat_drv); oz_dbg(ON, "Pending urbs:%d\n", atomic_read(&g_pending_urbs)); + kmem_cache_destroy(oz_urb_link_cache); } |