Skip to content

Commit 6fece01

Browse files
soda0289Grarak
authored andcommitted
usb: xhci: Add support for URB_ZERO_PACKET to bulk/sg transfers
commit 4758dcd upstream. This commit checks for the URB_ZERO_PACKET flag and creates an extra zero-length td if the urb transfer length is a multiple of the endpoint's max packet length. Signed-off-by: Reyad Attiyat <[email protected]> Signed-off-by: Mathias Nyman <[email protected]> Cc: Oliver Neukum <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 000f194 commit 6fece01

File tree

2 files changed

+51
-10
lines changed

2 files changed

+51
-10
lines changed

drivers/usb/host/xhci-ring.c

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3186,9 +3186,11 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
31863186
struct xhci_td *td;
31873187
struct scatterlist *sg;
31883188
int num_sgs;
3189-
int trb_buff_len, this_sg_len, running_total;
3189+
int trb_buff_len, this_sg_len, running_total, ret;
31903190
unsigned int total_packet_count;
3191+
bool zero_length_needed;
31913192
bool first_trb;
3193+
int last_trb_num;
31923194
u64 addr;
31933195
bool more_trbs_coming;
31943196

@@ -3204,13 +3206,27 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
32043206
total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length,
32053207
usb_endpoint_maxp(&urb->ep->desc));
32063208

3207-
trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
3209+
ret = prepare_transfer(xhci, xhci->devs[slot_id],
32083210
ep_index, urb->stream_id,
32093211
num_trbs, urb, 0, mem_flags);
3210-
if (trb_buff_len < 0)
3211-
return trb_buff_len;
3212+
if (ret < 0)
3213+
return ret;
32123214

32133215
urb_priv = urb->hcpriv;
3216+
3217+
/* Deal with URB_ZERO_PACKET - need one more td/trb */
3218+
zero_length_needed = urb->transfer_flags & URB_ZERO_PACKET &&
3219+
urb_priv->length == 2;
3220+
if (zero_length_needed) {
3221+
num_trbs++;
3222+
xhci_dbg(xhci, "Creating zero length td.\n");
3223+
ret = prepare_transfer(xhci, xhci->devs[slot_id],
3224+
ep_index, urb->stream_id,
3225+
1, urb, 1, mem_flags);
3226+
if (ret < 0)
3227+
return ret;
3228+
}
3229+
32143230
td = urb_priv->td[0];
32153231

32163232
/*
@@ -3240,6 +3256,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
32403256
trb_buff_len = urb->transfer_buffer_length;
32413257

32423258
first_trb = true;
3259+
last_trb_num = zero_length_needed ? 2 : 1;
32433260
/* Queue the first TRB, even if it's zero-length */
32443261
do {
32453262
u32 field = 0;
@@ -3257,12 +3274,15 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
32573274
/* Chain all the TRBs together; clear the chain bit in the last
32583275
* TRB to indicate it's the last TRB in the chain.
32593276
*/
3260-
if (num_trbs > 1) {
3277+
if (num_trbs > last_trb_num) {
32613278
field |= TRB_CHAIN;
3262-
} else {
3263-
/* FIXME - add check for ZERO_PACKET flag before this */
3279+
} else if (num_trbs == last_trb_num) {
32643280
td->last_trb = ep_ring->enqueue;
32653281
field |= TRB_IOC;
3282+
} else if (zero_length_needed && num_trbs == 1) {
3283+
trb_buff_len = 0;
3284+
urb_priv->td[1]->last_trb = ep_ring->enqueue;
3285+
field |= TRB_IOC;
32663286
}
32673287

32683288
/* Only set interrupt on short packet for IN endpoints */
@@ -3324,7 +3344,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
33243344
if (running_total + trb_buff_len > urb->transfer_buffer_length)
33253345
trb_buff_len =
33263346
urb->transfer_buffer_length - running_total;
3327-
} while (running_total < urb->transfer_buffer_length);
3347+
} while (num_trbs > 0);
33283348

33293349
check_trb_math(urb, num_trbs, running_total);
33303350
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
@@ -3342,7 +3362,9 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
33423362
int num_trbs;
33433363
struct xhci_generic_trb *start_trb;
33443364
bool first_trb;
3365+
int last_trb_num;
33453366
bool more_trbs_coming;
3367+
bool zero_length_needed;
33463368
int start_cycle;
33473369
u32 field, length_field;
33483370

@@ -3381,6 +3403,20 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
33813403
return ret;
33823404

33833405
urb_priv = urb->hcpriv;
3406+
3407+
/* Deal with URB_ZERO_PACKET - need one more td/trb */
3408+
zero_length_needed = urb->transfer_flags & URB_ZERO_PACKET &&
3409+
urb_priv->length == 2;
3410+
if (zero_length_needed) {
3411+
num_trbs++;
3412+
xhci_dbg(xhci, "Creating zero length td.\n");
3413+
ret = prepare_transfer(xhci, xhci->devs[slot_id],
3414+
ep_index, urb->stream_id,
3415+
1, urb, 1, mem_flags);
3416+
if (ret < 0)
3417+
return ret;
3418+
}
3419+
33843420
td = urb_priv->td[0];
33853421

33863422
/*
@@ -3402,7 +3438,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
34023438
trb_buff_len = urb->transfer_buffer_length;
34033439

34043440
first_trb = true;
3405-
3441+
last_trb_num = zero_length_needed ? 2 : 1;
34063442
/* Queue the first TRB, even if it's zero-length */
34073443
do {
34083444
u32 remainder = 0;
@@ -3468,7 +3504,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
34683504
trb_buff_len = urb->transfer_buffer_length - running_total;
34693505
if (trb_buff_len > TRB_MAX_BUFF_SIZE)
34703506
trb_buff_len = TRB_MAX_BUFF_SIZE;
3471-
} while (running_total < urb->transfer_buffer_length);
3507+
} while (num_trbs > 0);
34723508

34733509
check_trb_math(urb, num_trbs, running_total);
34743510
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,

drivers/usb/host/xhci.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,11 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
13211321

13221322
if (usb_endpoint_xfer_isoc(&urb->ep->desc))
13231323
size = urb->number_of_packets;
1324+
else if (usb_endpoint_is_bulk_out(&urb->ep->desc) &&
1325+
urb->transfer_buffer_length > 0 &&
1326+
urb->transfer_flags & URB_ZERO_PACKET &&
1327+
!(urb->transfer_buffer_length % usb_endpoint_maxp(&urb->ep->desc)))
1328+
size = 2;
13241329
else
13251330
size = 1;
13261331

0 commit comments

Comments
 (0)