|
@@ -602,6 +602,8 @@ static void td_submit_urb (
|
|
u32 info = 0;
|
|
u32 info = 0;
|
|
int is_out = usb_pipeout (urb->pipe);
|
|
int is_out = usb_pipeout (urb->pipe);
|
|
int periodic = 0;
|
|
int periodic = 0;
|
|
|
|
+ int i, this_sg_len, n;
|
|
|
|
+ struct scatterlist *sg;
|
|
|
|
|
|
/* OHCI handles the bulk/interrupt data toggles itself. We just
|
|
/* OHCI handles the bulk/interrupt data toggles itself. We just
|
|
* use the device toggle bits for resetting, and rely on the fact
|
|
* use the device toggle bits for resetting, and rely on the fact
|
|
@@ -615,10 +617,24 @@ static void td_submit_urb (
|
|
|
|
|
|
list_add (&urb_priv->pending, &ohci->pending);
|
|
list_add (&urb_priv->pending, &ohci->pending);
|
|
|
|
|
|
- if (data_len)
|
|
|
|
- data = urb->transfer_dma;
|
|
|
|
- else
|
|
|
|
- data = 0;
|
|
|
|
|
|
+ i = urb->num_mapped_sgs;
|
|
|
|
+ if (data_len > 0 && i > 0) {
|
|
|
|
+ sg = urb->sg;
|
|
|
|
+ data = sg_dma_address(sg);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * urb->transfer_buffer_length may be smaller than the
|
|
|
|
+ * size of the scatterlist (or vice versa)
|
|
|
|
+ */
|
|
|
|
+ this_sg_len = min_t(int, sg_dma_len(sg), data_len);
|
|
|
|
+ } else {
|
|
|
|
+ sg = NULL;
|
|
|
|
+ if (data_len)
|
|
|
|
+ data = urb->transfer_dma;
|
|
|
|
+ else
|
|
|
|
+ data = 0;
|
|
|
|
+ this_sg_len = data_len;
|
|
|
|
+ }
|
|
|
|
|
|
/* NOTE: TD_CC is set so we can tell which TDs the HC processed by
|
|
/* NOTE: TD_CC is set so we can tell which TDs the HC processed by
|
|
* using TD_CC_GET, as well as by seeing them on the done list.
|
|
* using TD_CC_GET, as well as by seeing them on the done list.
|
|
@@ -639,17 +655,29 @@ static void td_submit_urb (
|
|
? TD_T_TOGGLE | TD_CC | TD_DP_OUT
|
|
? TD_T_TOGGLE | TD_CC | TD_DP_OUT
|
|
: TD_T_TOGGLE | TD_CC | TD_DP_IN;
|
|
: TD_T_TOGGLE | TD_CC | TD_DP_IN;
|
|
/* TDs _could_ transfer up to 8K each */
|
|
/* TDs _could_ transfer up to 8K each */
|
|
- while (data_len > 4096) {
|
|
|
|
- td_fill (ohci, info, data, 4096, urb, cnt);
|
|
|
|
- data += 4096;
|
|
|
|
- data_len -= 4096;
|
|
|
|
|
|
+ for (;;) {
|
|
|
|
+ n = min(this_sg_len, 4096);
|
|
|
|
+
|
|
|
|
+ /* maybe avoid ED halt on final TD short read */
|
|
|
|
+ if (n >= data_len || (i == 1 && n >= this_sg_len)) {
|
|
|
|
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
|
|
|
|
+ info |= TD_R;
|
|
|
|
+ }
|
|
|
|
+ td_fill(ohci, info, data, n, urb, cnt);
|
|
|
|
+ this_sg_len -= n;
|
|
|
|
+ data_len -= n;
|
|
|
|
+ data += n;
|
|
cnt++;
|
|
cnt++;
|
|
|
|
+
|
|
|
|
+ if (this_sg_len <= 0) {
|
|
|
|
+ if (--i <= 0 || data_len <= 0)
|
|
|
|
+ break;
|
|
|
|
+ sg = sg_next(sg);
|
|
|
|
+ data = sg_dma_address(sg);
|
|
|
|
+ this_sg_len = min_t(int, sg_dma_len(sg),
|
|
|
|
+ data_len);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- /* maybe avoid ED halt on final TD short read */
|
|
|
|
- if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
|
|
|
|
- info |= TD_R;
|
|
|
|
- td_fill (ohci, info, data, data_len, urb, cnt);
|
|
|
|
- cnt++;
|
|
|
|
if ((urb->transfer_flags & URB_ZERO_PACKET)
|
|
if ((urb->transfer_flags & URB_ZERO_PACKET)
|
|
&& cnt < urb_priv->length) {
|
|
&& cnt < urb_priv->length) {
|
|
td_fill (ohci, info, 0, 0, urb, cnt);
|
|
td_fill (ohci, info, 0, 0, urb, cnt);
|