These patches are against kernels 2.6.18 through at least 2.6.18-git7.
patch 13: Adds low-level scheduler mechanisms for linking, unlinking,
manipulating and maintaining FSTNs. Turns on shadow budget logic to
allow/use FSTNs in budgeting.
Signed-off-by: Christopher "Monty" Montgomery <[EMAIL PROTECTED]>
---
diff -X b/Documentation/dontdiff -upr a/drivers/usb/host/ehci.h
b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h 2006-09-26 22:33:14.000000000 -0400
+++ b/drivers/usb/host/ehci.h 2006-09-26 22:33:21.000000000 -0400
@@ -76,11 +76,14 @@ struct ehci_hcd { /* one per controlle
struct ehci_shadow_budget **budget; /* pointer to the
shadow budget
of bandwidth placeholders */
+ struct ehci_fstn *periodic_restore_fstn;
+ struct ehci_fstn **periodic_save_fstns; /*
[PERIODIC_QH_MAX_PERIOD] */
/* per root hub port */
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
/* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */
+ struct dma_pool *fstn_pool; /* a qh has [up to] two fstns */
struct dma_pool *qtd_pool; /* one or more per qh */
struct dma_pool *itd_pool; /* itd per iso urb */
struct dma_pool *sitd_pool; /* sitd per split iso urb */
@@ -358,6 +361,7 @@ struct ehci_qtd {
/* next async queue entry, or pointer to interrupt/periodic QH */
#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
+#define FSTN_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_FSTN)
/* for periodic/async schedules and qtd lists, mark end of list */
#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to
hw */
@@ -658,6 +662,7 @@ struct ehci_fstn {
/* the rest is HCD-private */
dma_addr_t fstn_dma;
union ehci_shadow fstn_next; /* ptr to periodic q entry */
+ struct ehci_qh *fstn_prev; /* ptr to backlinked qh */
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
diff -X b/Documentation/dontdiff -upr a/drivers/usb/host/ehci-hcd.c
b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c 2006-09-26 22:31:58.000000000 -0400
+++ b/drivers/usb/host/ehci-hcd.c 2006-09-26 22:33:21.000000000 -0400
@@ -427,6 +427,11 @@ static int ehci_init(struct usb_hcd *hcd
if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0)
return retval;
+ /* a periodic schedule that supports FSTNs always has a single
+ static restore FSTN that matches all possible save
+ FSTNs. */
+ periodic_init_link_restore_fstn(ehci);
+
/* controllers may cache some of the periodic schedule ... */
hcc_params = readl(&ehci->caps->hcc_params);
if (HCC_ISOC_CACHE(hcc_params)) // full frame cache
diff -X b/Documentation/dontdiff -upr a/drivers/usb/host/ehci-mem.c
b/drivers/usb/host/ehci-mem.c
--- a/drivers/usb/host/ehci-mem.c 2006-09-26 22:31:58.000000000 -0400
+++ b/drivers/usb/host/ehci-mem.c 2006-09-26 22:33:21.000000000 -0400
@@ -62,6 +62,26 @@ static inline void ehci_qtd_free (struct
dma_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma);
}
+static struct ehci_fstn *ehci_fstn_alloc (struct ehci_hcd *ehci, gfp_t flags)
+{
+ struct ehci_fstn *fstn = NULL;
+ dma_addr_t dma;
+
+ if(ehci->fstn_pool){ /* alloced only if HC actually supports fstn */
+ fstn = dma_pool_alloc (ehci->fstn_pool, flags, &dma);
+ if (fstn != NULL) {
+ memset (fstn, 0, sizeof *fstn);
+ fstn->fstn_dma = dma;
+ }
+ }
+ return fstn;
+}
+
+static inline void ehci_fstn_free (struct ehci_hcd *ehci,
+ struct ehci_fstn *fstn)
+{
+ dma_pool_free (ehci->fstn_pool, fstn, fstn->fstn_dma);
+}
static void qh_destroy (struct kref *kref)
{
@@ -144,6 +164,10 @@ static void ehci_mem_cleanup (struct ehc
dma_pool_destroy (ehci->qtd_pool);
ehci->qtd_pool = NULL;
+ if (ehci->fstn_pool)
+ dma_pool_destroy (ehci->fstn_pool);
+ ehci->fstn_pool = NULL;
+
if (ehci->qh_pool) {
dma_pool_destroy (ehci->qh_pool);
ehci->qh_pool = NULL;
@@ -163,6 +187,8 @@ static void ehci_mem_cleanup (struct ehc
ehci->periodic, ehci->periodic_dma);
ehci->periodic = NULL;
+ if(ehci->periodic_save_fstns)
+ kfree(ehci->periodic_save_fstns);
if(ehci->budget)
kfree(ehci->budget);
ehci->budget = NULL;
@@ -171,7 +197,8 @@ static void ehci_mem_cleanup (struct ehc
ehci->budget_pool = NULL;
/* shadow periodic table */
- kfree(ehci->pshadow);
+ if(ehci->pshadow)
+ kfree(ehci->pshadow);
ehci->pshadow = NULL;
}
@@ -181,45 +208,70 @@ static int ehci_mem_init (struct ehci_hc
int i;
/* QTDs for control/bulk/intr transfers */
- ehci->qtd_pool = dma_pool_create ("ehci_qtd",
- ehci_to_hcd(ehci)->self.controller,
- sizeof (struct ehci_qtd),
- 32 /* byte alignment (for hw parts) */,
- 4096 /* can't cross 4K */);
+ ehci->qtd_pool =
+ dma_pool_create ("ehci_qtd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_qtd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
if (!ehci->qtd_pool) {
goto fail;
}
+ /* does this HC support FSTNs? Don't allocate FSTN structs if not. */
+ if( HC_VERSION(readl (&ehci->caps->hc_capbase)) >= 96){
+ /* EHCI 0.96+ */
+ ehci->fstn_pool =
+ dma_pool_create ("ehci_fstn",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_fstn),
+ 32,
+ 4096);
+ /* do not give up on failure;
+ scheduling is merely constrained */
+ if(ehci->fstn_pool)
+ ehci->periodic_save_fstns =
+ kcalloc(PERIODIC_QH_MAX_PERIOD,
+ sizeof(*ehci->periodic_save_fstns),
+ flags);
+ /* lazy-alloc the rest */
+
+ }
+
/* QHs for control/bulk/intr transfers */
- ehci->qh_pool = dma_pool_create ("ehci_qh",
- ehci_to_hcd(ehci)->self.controller,
- sizeof (struct ehci_qh),
- 32 /* byte alignment (for hw parts) */,
- 4096 /* can't cross 4K */);
+ ehci->qh_pool =
+ dma_pool_create ("ehci_qh",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_qh),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
if (!ehci->qh_pool) {
goto fail;
}
+
ehci->async = ehci_qh_alloc (ehci, flags);
if (!ehci->async) {
goto fail;
}
/* ITD for high speed ISO transfers */
- ehci->itd_pool = dma_pool_create ("ehci_itd",
- ehci_to_hcd(ehci)->self.controller,
- sizeof (struct ehci_itd),
- 32 /* byte alignment (for hw parts) */,
- 4096 /* can't cross 4K */);
+ ehci->itd_pool =
+ dma_pool_create ("ehci_itd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_itd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
if (!ehci->itd_pool) {
goto fail;
}
/* SITD for full/low speed split ISO transfers */
- ehci->sitd_pool = dma_pool_create ("ehci_sitd",
- ehci_to_hcd(ehci)->self.controller,
- sizeof (struct ehci_sitd),
- 32 /* byte alignment (for hw parts) */,
- 4096 /* can't cross 4K */);
+ ehci->sitd_pool =
+ dma_pool_create ("ehci_sitd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof (struct ehci_sitd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */);
if (!ehci->sitd_pool) {
goto fail;
}
@@ -227,8 +279,8 @@ static int ehci_mem_init (struct ehci_hc
/* Hardware periodic table */
ehci->periodic = (__le32 *)
dma_alloc_coherent (ehci_to_hcd(ehci)->self.controller,
- ehci->periodic_size * sizeof(__le32),
- &ehci->periodic_dma, 0);
+ ehci->periodic_size * sizeof(__le32),
+ &ehci->periodic_dma, 0);
if (ehci->periodic == NULL) {
goto fail;
}
@@ -241,9 +293,9 @@ static int ehci_mem_init (struct ehci_hc
goto fail;
}
ehci->budget_pool =
- kmem_cache_create ("ehci_budget",
- sizeof(struct ehci_shadow_budget),
- 0,0,NULL,NULL);
+ kmem_cache_create ("ehci_budget",
+ sizeof(struct ehci_shadow_budget),
+ 0,0,NULL,NULL);
if (!ehci->budget_pool) {
goto fail;
}
diff -X b/Documentation/dontdiff -upr a/drivers/usb/host/ehci-sched.c
b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c 2006-09-26 22:33:14.000000000 -0400
+++ b/drivers/usb/host/ehci-sched.c 2006-09-26 22:33:21.000000000 -0400
@@ -61,6 +61,10 @@
* For isochronous, an "iso_stream" head serves the same role as a QH.
* It keeps track of every ITD (or SITD) that's scheduled.
*
+ * This scheduler supports and aggressively uses both FSTNs in order
+ * to allow interrupt QH transfers to span H-frame boundaries. This
+ * support is necessary for efficient FS bus bandwidth usage through a
+ * 2.0 hub's TT mechanism.
*
* The transfer ordering in the shadow schedule looks like:
*
@@ -98,7 +102,10 @@
*
* Transaction ordering is determined by the budgeting code and set in
* the budget. Transactions are linked into the actual hardware
- * schedule in the order specified by the budget.
+ * schedule in the order specified by the budget. FSTN positioning is
+ * static; it follows the recommendations of the EHCI 1.0 spec and
+ * places one save-state FSTN at the lowest level in each branch of
+ * the INTR tree and one restore FSTN at the head of level one.
*
*/
@@ -110,6 +117,13 @@ MODULE_PARM_DESC (sched_verbose,
"debugging information to syslog. Incurs large latencies in"
" near-realtime code; default is 0 (off)");
+/* enable/disable use of FSTNs */
+static unsigned use_fstn = 1;
+module_param (use_fstn, uint, S_IRUGO);
+MODULE_PARM_DESC (use_fstn,
+ "use FSTNs: allow use of FSTN to schedule full speed INTR "
+ "transfers across frame boundaries; default is 1 (on)");
+
/* set limit on periodic load per TT uframe */
static unsigned fs_bytes_per_uframe = 188;
module_param (fs_bytes_per_uframe, uint, S_IRUGO);
@@ -267,8 +281,9 @@ static void print_schedule_frame (char *
if(fstn->hw_prev == EHCI_LIST_END)
printk("[FSTN restore 0x%p]",fstn);
else
- printk("[FSTN save 0x%p]",
- fstn);
+ printk("[FSTN save 0x%p <- 0x%p]",
+ fstn,
+ fstn->fstn_prev);
}
break;
case Q_TYPE_QH:
@@ -930,10 +945,14 @@ static int budget_calc_fs_frame(struct e
/* USB 2.0 11.18.4.3.b */
cmask = (0x1c << start_frame);
- /* don't allow spanning yet */
- if(cmask&(~0xff))
+ /* if we need an FSTN are they even
+ available? */
+ if(cmask&(~0xff)){
+ if(use_fstn==0)
return -1;
-
+ if(ehci->fstn_pool==NULL)
+ return -1;
+ }
}else{
/* sitd */
if(budget->owner.iso->bEndpointAddress & USB_DIR_IN){
@@ -1382,10 +1401,256 @@ static int disable_periodic (struct ehci
}
/*-------------------------------------------------------------------------*/
+/* FSTN machinery
+
+ FSTN decision making and manipulation is self contained and has
+ nothing to do with the budget, only the schedule. The master restore
+ FSTN is set up along with the ehci initialization; save-place FSTNs
+ are manipulated and adjusted after any changes to the QH tree in the
+ hardware schedule. */
+
+/* periodic_init_link_restore_fstn - Place one restore FSTN at the
+ * beginning of node level 1 of the hardware schedule interrupt tree
+ * [EHCI 1.0 4.12.2.2.2]. Called once by the ehci initialization code
+ * and not again.
+ *
+ * @ehci: pointer to ehci host controller device structure.
+ */
+static void periodic_init_link_restore_fstn(struct ehci_hcd *ehci)
+{
+ int i;
+
+ if(use_fstn){
+ ehci->periodic_restore_fstn =
+ ehci_fstn_alloc (ehci, GFP_ATOMIC);
+
+ if(ehci->periodic_restore_fstn){
+ ehci->periodic_restore_fstn->hw_next = EHCI_LIST_END;
+ ehci->periodic_restore_fstn->hw_prev = EHCI_LIST_END;
+
+ for(i=0; i<ehci->periodic_size; i++){
+ union ehci_shadow *here = &ehci->pshadow [i];
+ __le32 *hw_p = &ehci->periodic [i];
+
+ here->fstn = ehci->periodic_restore_fstn;
+ *hw_p = FSTN_NEXT (here->fstn->fstn_dma);
+ wmb ();
+
+ }
+ }
+ }
+}
+
+/* periodic_position_save_fstn - determine the insertion point within
+ * the specified frame for a save-place FSTN. Any given frame
+ * contains only one save-state FSTN, which sits at the beginning of
+ * the lowest level (highest interval) of the hardware schedule
+ * interrupt tree.
+ *
+ * @ehci: pointer to ehci host controller device structure
+ * @frame: frame number in shadow/hardware schedule
+ * @hw_p: returns pointer to insertion point in hardware schedule
+ */
+static union ehci_shadow *
+periodic_position_save_fstn(struct ehci_hcd *ehci,
+ unsigned frame,
+ __le32 **hw_p)
+{
+ union ehci_shadow *here =
+ &ehci->pshadow [frame & (ehci->periodic_size-1)];
+ __le32 type;
+
+ *hw_p = &ehci->periodic [frame & (ehci->periodic_size-1)];
+ type = Q_NEXT_TYPE (**hw_p);
+
+ while (here->ptr){
+
+ if(type == Q_TYPE_FSTN || type == Q_TYPE_QH)
+ break;
+
+ *hw_p = here->hw_next;
+ here = periodic_next_shadow (here, type);
+ type = Q_NEXT_TYPE (**hw_p);
+ }
+
+ return here;
+}
+
+/* periodic_find_save_fstn - search a frame in the shadow/hardware
+ * schedule for a save FSTN, returning the insertion point if found or
+ * NULL if not found. Any given frame will contain at most a single
+ * save-state FSTN.
+ *
+ * @ehci: pointer to ehci host controller device structure
+ * @frame: frame number in shadow/hardware schedule
+ * @hw_p: returns pointer to insertion point in hardware schedule
+ */
+static union ehci_shadow *
+periodic_find_save_fstn(struct ehci_hcd *ehci,
+ unsigned frame,
+ __le32 **hw_p)
+{
+ union ehci_shadow *here;
+ int fframe = frame & (PERIODIC_QH_MAX_PERIOD-1);
+ struct ehci_fstn *fstn;
+
+ if(ehci->periodic_save_fstns == NULL)
+ goto out; /* no FSTN support in HC */
+ fstn = ehci->periodic_save_fstns[fframe];
+ if(fstn == NULL)
+ goto out; /* this FSTN not yet alloced, couldn't be linked */
+
+ here = periodic_position_save_fstn(ehci, frame, hw_p);
+ if(here->ptr == fstn){
+ return here;
+ }
+
+out:
+ return NULL;
+}
+
+/* _periodic_unlink_fstn - unconditionally unlinks the save-state FSTN
+ * from the specified frame. noop if the frame does not have an
+ * active save-state FSTN.
+ *
+ * @ehci: pointer to ehci host controller device structure
+ * @frame: frame number in shadow/hardware schedule
+ */
+static void _periodic_unlink_fstn (struct ehci_hcd *ehci, unsigned frame)
+{
+ __le32 *hw_p;
+ union ehci_shadow *here = periodic_find_save_fstn(ehci, frame, &hw_p);
+
+ if (!here)
+ return;
+
+ /* inactivate */
+ here->fstn->hw_prev = EHCI_LIST_END;
+ here->fstn->fstn_prev = NULL;
+
+ /* unlink */
+ *hw_p = *here->hw_next;
+ *here = here->fstn->fstn_next;
+
+}
+
+/* _periodic_link_fstn - allocs [if needed] and links save-state
+ * fstn into frame in appropraite position, also patching FSTN
+ * backpointer. If this frame already contains an active FSTN, only
+ * patches backpointer.
+ *
+ * @ehci: pointer to ehci host controller device structure
+ * @frame: frame number in shadow/hardware schedule
+ * @back_qh: pointer to qh which the FSTN's backpointer should reference
+ */
+static void _periodic_link_fstn (struct ehci_hcd *ehci,
+ int frame,
+ struct ehci_qh *back_qh)
+{
+
+ if(ehci->periodic_save_fstns){
+ int fframe = frame & (PERIODIC_QH_MAX_PERIOD-1);
+ struct ehci_fstn *fstn = ehci->periodic_save_fstns[fframe];
+ __le32 *hw_p;
+ union ehci_shadow *here =
+ periodic_position_save_fstn(ehci, frame, &hw_p);
+
+ /* is the FSTN already alloced? */
+ if(!fstn){
+ fstn = ehci_fstn_alloc (ehci, GFP_ATOMIC);
+ if(!fstn){
+ ehci_err(ehci,
+ "Unable to allocate FSTN\n");
+ return;
+ }
+
+ ehci->periodic_save_fstns[fframe] = fstn;
+ }
+
+ /* patch the FSTN */
+ fstn->hw_prev = QH_NEXT(back_qh->qh_dma);
+ fstn->fstn_prev = back_qh;
+
+ /* already linked? */
+ if(here->ptr != fstn){
+ /* splice right to save-place fstn */
+ fstn->fstn_next = *here;
+ fstn->hw_next = *hw_p;
+ wmb ();
+
+ /* splice it to left; it's now live */
+ here->fstn = fstn;
+ *hw_p = FSTN_NEXT (fstn->fstn_dma);
+ }
+ wmb ();
+ }
+}
+
+/* _periodic_inspect_fstn_frame - toplevel machinery for managing all
+ * changes to FSTN state in the schedule; called whenever changes are
+ * made to the QH structure of a frame, automagically updates FSTN
+ * state in the hardware/shadow schedule to correctly reflect QH frame
+ * spanning. Links/unlinks/patches FSTNs in the following frame as
+ * needed.
+ *
+ * @ehci: pointer to ehci host controller device structure
+ * @frame: frame number in shadow/hardware schedule
+ * @insert: if changes are to be affected to FSTN preceeding linking
+ * new QH into live schedule, *insert designates new QH
+ * insertion point
+ * @new_qh: points to new qh to be linked into schedule following FSTN updates;
+ * allows valid FSTN to preceed activating the QH; NULL if this is an
+ * unlink update.
+ */
+static void _periodic_inspect_fstn_frame (struct ehci_hcd *ehci,
+ int frame,
+ union ehci_shadow *insert,
+ union ehci_shadow *new_qh)
+{
+ __le32 type =
+ Q_NEXT_TYPE (ehci->periodic [frame & (ehci->periodic_size-1)]);
+ __le32 save_type = type;
+ union ehci_shadow *here =
+ &ehci->pshadow [frame & (ehci->periodic_size-1)];
+
+ if(here == insert){
+ here = new_qh;
+ type = Q_TYPE_QH;
+ }
+
+ /* search frame for first spanning QH */
+ while (here->ptr){
+ if(type == Q_TYPE_QH){
+ if(BUDGET_WRAP_P(here->qh->budget)){
+ _periodic_link_fstn (ehci, frame+1, here->qh);
+ return;
+ }
+ }
+
+ /* advance */
+ if(here == new_qh){
+ here = insert;
+ type = save_type;
+ }else{
+ save_type = Q_NEXT_TYPE (*here->hw_next);
+ here = periodic_next_shadow (here, type);
+ if(here == insert){
+ here = new_qh;
+ type = Q_TYPE_QH;
+ }else
+ type = save_type;
+ }
+ }
+
+ /* no QH found, make sure there's no FSTN in following frame */
+ _periodic_unlink_fstn (ehci, frame+1);
+}
+
+/*-------------------------------------------------------------------------*/
/* Periodic interrupt (QH) endpoint machinery */
/* periodic_qh_unlink_frame - unlink the passed in QH from the
- * specified frame
+ * specified frame and update frame FSTN state
*
* @ehci: pointer to ehci host controller device structure
* @frame: frame number in shadow/hardware schedule
@@ -1406,9 +1671,10 @@ static void periodic_qh_unlink_frame (st
/*... the old "next" pointers from ptr (and if a qh, the
fstns) may still be in use, the caller updates them. */
-
}
+ _periodic_inspect_fstn_frame(ehci,frame,NULL,NULL);
+
}
/* periodic_qh_deschedule - toplevel entry point for removing a given
@@ -1471,7 +1737,7 @@ static void periodic_qh_deschedule(struc
}
/* periodic_qh_link - link the passed in QH into all relevant frames of the
- * hardware schedule; assumes QH is already budgeted.
+ * hardware schedule and update FSTN state; assumes QH is already budgeted.
*
* @ehci: pointer to ehci host controller device structure
* @qh: QH to link
@@ -1505,6 +1771,14 @@ static int periodic_qh_link (struct ehci
continue;
}
+ /* perform FSTN inspection/adjustment before qh is
+ * linked */
+ {
+ union ehci_shadow temp;
+ temp.qh=qh;
+ _periodic_inspect_fstn_frame (ehci, i, here, &temp);
+ }
+
/* already linked in? */
if(here->ptr != qh){
@@ -2323,7 +2597,7 @@ itd_link_urb (
next_uframe += stream->interval;
stream->depth += stream->interval;
- next_uframe %= mod;
+ next_uframe &= (mod-1);
packet++;
/* link completed itds into the schedule */
@@ -2934,9 +3208,17 @@ static int scan_frame(struct ehci_hcd *e
q = *q_p;
- modified = qh_completions (ehci, temp.qh, regs);
- if (unlikely (list_empty (&temp.qh->qtd_list)))
- periodic_qh_deschedule (ehci, temp.qh);
+ /* process completions for this frame only if
+ * we're certain all completions for a
+ * preceeding spanning frame have first been
+ * processed; that is true iff clock was past
+ * uframe 1 when we started */
+ if(uframes>2){
+ modified =
+ qh_completions (ehci, temp.qh, regs);
+ if (unlikely (list_empty (&temp.qh->qtd_list)))
+ periodic_qh_deschedule (ehci, temp.qh);
+ }
qh_put (temp.qh);
break;
@@ -3073,7 +3355,7 @@ scan_periodic (struct ehci_hcd *ehci, st
clock = readl (&ehci->regs->frame_index);
else
clock = now_uframe + mod - 1;
- clock %= mod;
+ clock &= (mod-1);
for (;;) {
unsigned uframes;
@@ -3111,7 +3393,7 @@ scan_periodic (struct ehci_hcd *ehci, st
break;
ehci->next_uframe = now_uframe;
- now = readl (&ehci->regs->frame_index) % mod;
+ now = readl (&ehci->regs->frame_index) & (mod-1);
if (now_uframe == now)
break;
@@ -3119,7 +3401,7 @@ scan_periodic (struct ehci_hcd *ehci, st
clock = now;
} else {
now_uframe++;
- now_uframe %= mod;
+ now_uframe &= (mod-1);
if(now_uframe == 0){
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel