Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dwc_otg: implement tasklet for returning URBs to usbcore hcd layer #255

Merged
merged 1 commit into from
Mar 24, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions drivers/usb/host/dwc_common_port/dwc_common_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 7 additions & 7 deletions drivers/usb/host/dwc_common_port/dwc_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/dwc_common_port/dwc_os.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
34 changes: 33 additions & 1 deletion drivers/usb/host/dwc_otg/dwc_otg_hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -694,6 +697,31 @@ 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;

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;
Expand Down Expand Up @@ -833,6 +861,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 &&
Expand Down Expand Up @@ -877,7 +906,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.
Expand Down Expand Up @@ -915,6 +944,9 @@ 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 */
Expand Down
10 changes: 10 additions & 0 deletions drivers/usb/host/dwc_otg/dwc_otg_hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,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.
Expand Down Expand Up @@ -551,6 +558,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;
Expand Down
25 changes: 16 additions & 9 deletions drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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) {
Expand Down Expand Up @@ -348,18 +348,25 @@ 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;
}

Expand Down