Skip to content

Instantly share code, notes, and snippets.

@P33M
Created March 21, 2013 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save P33M/5213278 to your computer and use it in GitHub Desktop.
Save P33M/5213278 to your computer and use it in GitHub Desktop.
BH tasklet for dwc_otg
diff --git a/drivers/usb/host/dwc_common_port/dwc_common_linux.c b/drivers/usb/host/dwc_common_port/dwc_common_linux.c
index f00a9ff..1788812 100644
--- a/drivers/usb/host/dwc_common_port/dwc_common_linux.c
+++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c
@@ -991,6 +991,11 @@ void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
tasklet_schedule(&task->t);
}
+void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task)
+{
+ tasklet_hi_schedule(&task->t);
+}
+
/* workqueues
- run in process context (can sleep)
@@ -1296,7 +1301,7 @@ EXPORT_SYMBOL(DWC_EXCEPTION);
EXPORT_SYMBOL(__DWC_DEBUG);
#endif
-EXPORT_SYMBOL(__DWC_DMA_ALLOC);
+EXPORT_SYMBOL(__DWC_DMA_ALLOC);
EXPORT_SYMBOL(__DWC_DMA_ALLOC_ATOMIC);
EXPORT_SYMBOL(__DWC_DMA_FREE);
EXPORT_SYMBOL(__DWC_ALLOC);
diff --git a/drivers/usb/host/dwc_common_port/dwc_list.h b/drivers/usb/host/dwc_common_port/dwc_list.h
index 89cc325..a764df2 100644
--- a/drivers/usb/host/dwc_common_port/dwc_list.h
+++ b/drivers/usb/host/dwc_common_port/dwc_list.h
@@ -384,17 +384,17 @@ struct { \
#define DWC_TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define DWC_TAILQ_EMPTY(head) \
- (TAILQ_FIRST(head) == TAILQ_END(head))
+ (DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head))
#define DWC_TAILQ_FOREACH(var, head, field) \
- for((var) = TAILQ_FIRST(head); \
- (var) != TAILQ_END(head); \
- (var) = TAILQ_NEXT(var, field))
+ for((var) = DWC_TAILQ_FIRST(head); \
+ (var) != DWC_TAILQ_END(head); \
+ (var) = DWC_TAILQ_NEXT(var, field))
#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
- for((var) = TAILQ_LAST(head, headname); \
- (var) != TAILQ_END(head); \
- (var) = TAILQ_PREV(var, headname, field))
+ for((var) = DWC_TAILQ_LAST(head, headname); \
+ (var) != DWC_TAILQ_END(head); \
+ (var) = DWC_TAILQ_PREV(var, headname, field))
/*
* Tail queue functions.
diff --git a/drivers/usb/host/dwc_common_port/dwc_os.h b/drivers/usb/host/dwc_common_port/dwc_os.h
index 308ddd5..8eb24ea 100644
--- a/drivers/usb/host/dwc_common_port/dwc_os.h
+++ b/drivers/usb/host/dwc_common_port/dwc_os.h
@@ -981,6 +981,8 @@ extern void DWC_TASK_FREE(dwc_tasklet_t *task);
extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task);
#define dwc_task_schedule DWC_TASK_SCHEDULE
+extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task);
+#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE
/** @name Timer
*
diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
index fcec97f..d7caafe 100644
--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
@@ -40,6 +40,9 @@
* header file.
*/
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
#include "dwc_otg_hcd.h"
#include "dwc_otg_regs.h"
@@ -694,6 +697,32 @@ static void reset_tasklet_func(void *data)
dwc_otg_hcd->flags.b.port_reset_change = 1;
}
+static void completion_tasklet_func(void *ptr)
+{
+ dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) ptr;
+ struct urb *urb;
+ urb_tq_entry_t *item;
+ dwc_irqflags_t flags;
+ /* grab the first element in the URB tail queue (hold irq lock)
+ * and remove from queue */
+retry:
+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+ if(!DWC_TAILQ_EMPTY(&hcd->completed_urb_list)) {
+ item = DWC_TAILQ_FIRST(&hcd->completed_urb_list);
+ urb = item->urb;
+ DWC_TAILQ_REMOVE(&hcd->completed_urb_list, item, urb_tq_entries);
+ DWC_FREE(item);
+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+ } else {
+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+ return;
+ }
+ usb_hcd_unlink_urb_from_ep(hcd->priv, urb);
+ usb_hcd_giveback_urb(hcd->priv, urb, urb->status);
+ /* We could have had another URB added to the tailq while we were busy */
+ goto retry;
+}
+
static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
{
dwc_list_link_t *item;
@@ -833,6 +862,7 @@ static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd)
DWC_TIMER_FREE(dwc_otg_hcd->conn_timer);
DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet);
+ DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet);
#ifdef DWC_DEV_SRPCAP
if (dwc_otg_hcd->core_if->power_down == 2 &&
@@ -877,7 +907,7 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
DWC_LIST_INIT(&hcd->periodic_sched_ready);
DWC_LIST_INIT(&hcd->periodic_sched_assigned);
DWC_LIST_INIT(&hcd->periodic_sched_queued);
-
+ DWC_TAILQ_INIT(&hcd->completed_urb_list);
/*
* Create a host channel descriptor for each host channel implemented
* in the controller. Initialize the channel descriptor array.
@@ -915,6 +945,8 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
/* Initialize reset tasklet. */
hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd);
+
+ hcd->completion_tasklet = DWC_TASK_ALLOC("completion_tasklet", completion_tasklet_func, hcd);
#ifdef DWC_DEV_SRPCAP
if (hcd->core_if->power_down == 2) {
/* Initialize Power on timer for Host power up in case hibernation */
diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
index 45e44ea..a68d619 100644
--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
@@ -254,6 +254,7 @@ typedef struct dwc_otg_qtd {
DWC_CIRCLEQ_HEAD(dwc_otg_qtd_list, dwc_otg_qtd);
+
/**
* A Queue Head (QH) holds the static characteristics of an endpoint and
* maintains a list of transfers (QTDs) for that endpoint. A QH structure may
@@ -374,6 +375,13 @@ typedef struct dwc_otg_qh {
DWC_CIRCLEQ_HEAD(hc_list, dwc_hc);
+typedef struct urb_tq_entry {
+ struct urb *urb;
+ DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries;
+} urb_tq_entry_t;
+
+DWC_TAILQ_HEAD(urb_list, urb_tq_entry);
+
/**
* This structure holds the state of the HCD, including the non-periodic and
* periodic schedules.
@@ -551,6 +559,9 @@ struct dwc_otg_hcd {
/* Tasket to do a reset */
dwc_tasklet_t *reset_tasklet;
+ dwc_tasklet_t *completion_tasklet;
+ struct urb_list completed_urb_list;
+
/* */
dwc_spinlock_t *lock;
dwc_spinlock_t *channel_lock;
diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
index f91c4b1..4d45e1e 100644
--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
@@ -271,7 +271,7 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status)
{
struct urb *urb = (struct urb *)urb_handle;
-
+ urb_tq_entry_t * new_entry;
if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n",
__func__, urb, usb_pipedevice(urb->pipe),
@@ -285,7 +285,7 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
}
}
}
-
+ new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t));
urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb);
/* Convert status value. */
switch (status) {
@@ -348,18 +348,23 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
}
DWC_FREE(dwc_otg_urb);
-
+ if(!new_entry) {
+ DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n");
+ urb->status = -EPROTO;
+ /* don't schedule the tasklet - directly return the packet here with error. */
#if USB_URB_EP_LINKING
- usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
+ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
#endif
- DWC_SPINUNLOCK(hcd->lock);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
#else
- usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
#endif
- DWC_SPINLOCK(hcd->lock);
-
+ } else {
+ new_entry->urb = urb;
+ DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry, urb_tq_entries);
+ DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet);
+ }
return 0;
}
@@ -400,7 +405,7 @@ int hcd_init(dwc_bus_dev_t *_dev)
dmamask = DMA_BIT_MASK(32);
else
dmamask = 0;
-
+
#if defined(LM_INTERFACE) || defined(PLATFORM_INTERFACE)
dma_set_mask(&_dev->dev, dmamask);
dma_set_coherent_mask(&_dev->dev, dmamask);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment