Skip to content

Commit 79ec5aa

Browse files
committed
Merge pull request #255 from P33M/rpi-3.6.y
dwc_otg: implement tasklet for returning URBs to usbcore hcd layer
2 parents 7f26025 + c4564d4 commit 79ec5aa

File tree

6 files changed

+73
-17
lines changed

6 files changed

+73
-17
lines changed

drivers/usb/host/dwc_common_port/dwc_common_linux.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,11 @@ void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
991991
tasklet_schedule(&task->t);
992992
}
993993

994+
void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task)
995+
{
996+
tasklet_hi_schedule(&task->t);
997+
}
998+
994999

9951000
/* workqueues
9961001
- run in process context (can sleep)

drivers/usb/host/dwc_common_port/dwc_list.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -384,17 +384,17 @@ struct { \
384384
#define DWC_TAILQ_PREV(elm, headname, field) \
385385
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
386386
#define DWC_TAILQ_EMPTY(head) \
387-
(TAILQ_FIRST(head) == TAILQ_END(head))
387+
(DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head))
388388

389389
#define DWC_TAILQ_FOREACH(var, head, field) \
390-
for((var) = TAILQ_FIRST(head); \
391-
(var) != TAILQ_END(head); \
392-
(var) = TAILQ_NEXT(var, field))
390+
for ((var) = DWC_TAILQ_FIRST(head); \
391+
(var) != DWC_TAILQ_END(head); \
392+
(var) = DWC_TAILQ_NEXT(var, field))
393393

394394
#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
395-
for((var) = TAILQ_LAST(head, headname); \
396-
(var) != TAILQ_END(head); \
397-
(var) = TAILQ_PREV(var, headname, field))
395+
for ((var) = DWC_TAILQ_LAST(head, headname); \
396+
(var) != DWC_TAILQ_END(head); \
397+
(var) = DWC_TAILQ_PREV(var, headname, field))
398398

399399
/*
400400
* Tail queue functions.

drivers/usb/host/dwc_common_port/dwc_os.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,8 @@ extern void DWC_TASK_FREE(dwc_tasklet_t *task);
981981
extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task);
982982
#define dwc_task_schedule DWC_TASK_SCHEDULE
983983

984+
extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task);
985+
#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE
984986

985987
/** @name Timer
986988
*

drivers/usb/host/dwc_otg/dwc_otg_hcd.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
* header file.
4141
*/
4242

43+
#include <linux/usb.h>
44+
#include <linux/usb/hcd.h>
45+
4346
#include "dwc_otg_hcd.h"
4447
#include "dwc_otg_regs.h"
4548

@@ -694,6 +697,31 @@ static void reset_tasklet_func(void *data)
694697
dwc_otg_hcd->flags.b.port_reset_change = 1;
695698
}
696699

700+
static void completion_tasklet_func(void *ptr)
701+
{
702+
dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) ptr;
703+
struct urb *urb;
704+
urb_tq_entry_t *item;
705+
dwc_irqflags_t flags;
706+
707+
DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
708+
while (!DWC_TAILQ_EMPTY(&hcd->completed_urb_list)) {
709+
item = DWC_TAILQ_FIRST(&hcd->completed_urb_list);
710+
urb = item->urb;
711+
DWC_TAILQ_REMOVE(&hcd->completed_urb_list, item,
712+
urb_tq_entries);
713+
DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
714+
DWC_FREE(item);
715+
716+
usb_hcd_unlink_urb_from_ep(hcd->priv, urb);
717+
usb_hcd_giveback_urb(hcd->priv, urb, urb->status);
718+
719+
DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
720+
}
721+
DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
722+
return;
723+
}
724+
697725
static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
698726
{
699727
dwc_list_link_t *item;
@@ -833,6 +861,7 @@ static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd)
833861

834862
DWC_TIMER_FREE(dwc_otg_hcd->conn_timer);
835863
DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet);
864+
DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet);
836865

837866
#ifdef DWC_DEV_SRPCAP
838867
if (dwc_otg_hcd->core_if->power_down == 2 &&
@@ -877,7 +906,7 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
877906
DWC_LIST_INIT(&hcd->periodic_sched_ready);
878907
DWC_LIST_INIT(&hcd->periodic_sched_assigned);
879908
DWC_LIST_INIT(&hcd->periodic_sched_queued);
880-
909+
DWC_TAILQ_INIT(&hcd->completed_urb_list);
881910
/*
882911
* Create a host channel descriptor for each host channel implemented
883912
* in the controller. Initialize the channel descriptor array.
@@ -915,6 +944,9 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
915944

916945
/* Initialize reset tasklet. */
917946
hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd);
947+
948+
hcd->completion_tasklet = DWC_TASK_ALLOC("completion_tasklet",
949+
completion_tasklet_func, hcd);
918950
#ifdef DWC_DEV_SRPCAP
919951
if (hcd->core_if->power_down == 2) {
920952
/* Initialize Power on timer for Host power up in case hibernation */

drivers/usb/host/dwc_otg/dwc_otg_hcd.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,13 @@ typedef struct dwc_otg_qh {
374374

375375
DWC_CIRCLEQ_HEAD(hc_list, dwc_hc);
376376

377+
typedef struct urb_tq_entry {
378+
struct urb *urb;
379+
DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries;
380+
} urb_tq_entry_t;
381+
382+
DWC_TAILQ_HEAD(urb_list, urb_tq_entry);
383+
377384
/**
378385
* This structure holds the state of the HCD, including the non-periodic and
379386
* periodic schedules.
@@ -551,6 +558,9 @@ struct dwc_otg_hcd {
551558
/* Tasket to do a reset */
552559
dwc_tasklet_t *reset_tasklet;
553560

561+
dwc_tasklet_t *completion_tasklet;
562+
struct urb_list completed_urb_list;
563+
554564
/* */
555565
dwc_spinlock_t *lock;
556566
dwc_spinlock_t *channel_lock;

drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
271271
dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status)
272272
{
273273
struct urb *urb = (struct urb *)urb_handle;
274-
274+
urb_tq_entry_t *new_entry;
275275
if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
276276
DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n",
277277
__func__, urb, usb_pipedevice(urb->pipe),
@@ -285,7 +285,7 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
285285
}
286286
}
287287
}
288-
288+
new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t));
289289
urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb);
290290
/* Convert status value. */
291291
switch (status) {
@@ -348,18 +348,25 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
348348
}
349349

350350
DWC_FREE(dwc_otg_urb);
351-
351+
if (!new_entry) {
352+
DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n");
353+
urb->status = -EPROTO;
354+
/* don't schedule the tasklet -
355+
* directly return the packet here with error. */
352356
#if USB_URB_EP_LINKING
353-
usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
357+
usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
354358
#endif
355-
DWC_SPINUNLOCK(hcd->lock);
356359
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
357-
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
360+
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
358361
#else
359-
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
362+
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
360363
#endif
361-
DWC_SPINLOCK(hcd->lock);
362-
364+
} else {
365+
new_entry->urb = urb;
366+
DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry,
367+
urb_tq_entries);
368+
DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet);
369+
}
363370
return 0;
364371
}
365372

0 commit comments

Comments
 (0)