Skip to content

Commit 6f65126

Browse files
AlanSterngregkh
authored andcommitted
USB: OHCI: add SG support
Apparently nobody ever remembered to add Scatter-Gather support to ohci-hcd. This patch adds it. Signed-off-by: Alan Stern <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 1299cff commit 6f65126

File tree

2 files changed

+73
-19
lines changed

2 files changed

+73
-19
lines changed

drivers/usb/host/ohci-hcd.c

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,33 @@ MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");
109109

110110
/*-------------------------------------------------------------------------*/
111111

112+
static int number_of_tds(struct urb *urb)
113+
{
114+
int len, i, num, this_sg_len;
115+
struct scatterlist *sg;
116+
117+
len = urb->transfer_buffer_length;
118+
i = urb->num_mapped_sgs;
119+
120+
if (len > 0 && i > 0) { /* Scatter-gather transfer */
121+
num = 0;
122+
sg = urb->sg;
123+
for (;;) {
124+
this_sg_len = min_t(int, sg_dma_len(sg), len);
125+
num += DIV_ROUND_UP(this_sg_len, 4096);
126+
len -= this_sg_len;
127+
if (--i <= 0 || len <= 0)
128+
break;
129+
sg = sg_next(sg);
130+
}
131+
132+
} else { /* Non-SG transfer */
133+
/* one TD for every 4096 Bytes (could be up to 8K) */
134+
num = DIV_ROUND_UP(len, 4096);
135+
}
136+
return num;
137+
}
138+
112139
/*
113140
* queue up an urb for anything except the root hub
114141
*/
@@ -142,12 +169,8 @@ static int ohci_urb_enqueue (
142169
// case PIPE_INTERRUPT:
143170
// case PIPE_BULK:
144171
default:
145-
/* one TD for every 4096 Bytes (can be up to 8K) */
146-
size += urb->transfer_buffer_length / 4096;
147-
/* ... and for any remaining bytes ... */
148-
if ((urb->transfer_buffer_length % 4096) != 0)
149-
size++;
150-
/* ... and maybe a zero length packet to wrap it up */
172+
size += number_of_tds(urb);
173+
/* maybe a zero-length packet to wrap it up */
151174
if (size == 0)
152175
size++;
153176
else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
@@ -506,6 +529,9 @@ static int ohci_init (struct ohci_hcd *ohci)
506529
int ret;
507530
struct usb_hcd *hcd = ohci_to_hcd(ohci);
508531

532+
/* Accept arbitrarily long scatter-gather lists */
533+
hcd->self.sg_tablesize = ~0;
534+
509535
if (distrust_firmware)
510536
ohci->flags |= OHCI_QUIRK_HUB_POWER;
511537

drivers/usb/host/ohci-q.c

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,8 @@ static void td_submit_urb (
602602
u32 info = 0;
603603
int is_out = usb_pipeout (urb->pipe);
604604
int periodic = 0;
605+
int i, this_sg_len, n;
606+
struct scatterlist *sg;
605607

606608
/* OHCI handles the bulk/interrupt data toggles itself. We just
607609
* use the device toggle bits for resetting, and rely on the fact
@@ -615,10 +617,24 @@ static void td_submit_urb (
615617

616618
list_add (&urb_priv->pending, &ohci->pending);
617619

618-
if (data_len)
619-
data = urb->transfer_dma;
620-
else
621-
data = 0;
620+
i = urb->num_mapped_sgs;
621+
if (data_len > 0 && i > 0) {
622+
sg = urb->sg;
623+
data = sg_dma_address(sg);
624+
625+
/*
626+
* urb->transfer_buffer_length may be smaller than the
627+
* size of the scatterlist (or vice versa)
628+
*/
629+
this_sg_len = min_t(int, sg_dma_len(sg), data_len);
630+
} else {
631+
sg = NULL;
632+
if (data_len)
633+
data = urb->transfer_dma;
634+
else
635+
data = 0;
636+
this_sg_len = data_len;
637+
}
622638

623639
/* NOTE: TD_CC is set so we can tell which TDs the HC processed by
624640
* using TD_CC_GET, as well as by seeing them on the done list.
@@ -639,17 +655,29 @@ static void td_submit_urb (
639655
? TD_T_TOGGLE | TD_CC | TD_DP_OUT
640656
: TD_T_TOGGLE | TD_CC | TD_DP_IN;
641657
/* TDs _could_ transfer up to 8K each */
642-
while (data_len > 4096) {
643-
td_fill (ohci, info, data, 4096, urb, cnt);
644-
data += 4096;
645-
data_len -= 4096;
658+
for (;;) {
659+
n = min(this_sg_len, 4096);
660+
661+
/* maybe avoid ED halt on final TD short read */
662+
if (n >= data_len || (i == 1 && n >= this_sg_len)) {
663+
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
664+
info |= TD_R;
665+
}
666+
td_fill(ohci, info, data, n, urb, cnt);
667+
this_sg_len -= n;
668+
data_len -= n;
669+
data += n;
646670
cnt++;
671+
672+
if (this_sg_len <= 0) {
673+
if (--i <= 0 || data_len <= 0)
674+
break;
675+
sg = sg_next(sg);
676+
data = sg_dma_address(sg);
677+
this_sg_len = min_t(int, sg_dma_len(sg),
678+
data_len);
679+
}
647680
}
648-
/* maybe avoid ED halt on final TD short read */
649-
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
650-
info |= TD_R;
651-
td_fill (ohci, info, data, data_len, urb, cnt);
652-
cnt++;
653681
if ((urb->transfer_flags & URB_ZERO_PACKET)
654682
&& cnt < urb_priv->length) {
655683
td_fill (ohci, info, 0, 0, urb, cnt);

0 commit comments

Comments
 (0)