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)
#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.
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
*
* header file.
*/
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
#include "dwc_otg_hcd.h"
#include "dwc_otg_regs.h"
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;
+
+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+ while (!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_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+ DWC_FREE(item);
+
+ usb_hcd_unlink_urb_from_ep(hcd->priv, urb);
+ usb_hcd_giveback_urb(hcd->priv, urb, urb->status);
+
+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+ }
+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+ return;
+}
+
static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
{
dwc_list_link_t *item;
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 &&
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.
/* 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 */
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.
/* 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;
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),
}
}
}
-
+ 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) {
}
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;
}