On 23.02.2015 19:02, Aleksander Morgado wrote:
> On Mon, Feb 23, 2015 at 4:23 PM, Mathias Nyman
> <[email protected]> wrote:
>> Hi
>>
>> On 23.02.2015 13:52, Aleksander Morgado wrote:
>>> When a control transfer has a short data stage, the xHCI controller
>>> generates
>>> two transfer events: a COMP_SHORT_TX event that specifies the untransferred
>>> amount, and a COMP_SUCCESS event. But when the data stage is not short,
>>> only the
>>> COMP_SUCCESS event occurs. Therefore, xhci-hcd must set urb->actual_length
>>> to
>>> urb->transfer_buffer_length while processing the COMP_SUCCESS event, unless
>>> urb->actual_length was set already by a previous COMP_SHORT_TX event.
>>>
>>
>> I think that the only case you should see a COMP_SUCCESS after a
>> COMP_SHORT_TX
>> is if xhci hits a link TRB while automatically moving to the next TRB after
>> the
>> short packet.
>>
>> If Event Data TRBs are used, or a later TRB in that TD has the IOC flag set
>> then xhci should just
>> generate a second transfer event with the same COMP_SHORT_TX completion code.
>> (xhci specs section 4.10.1.1)
>>
>
> Well, I can only speak for this usecase I have here with the Option
> HSO modem; in this case the COMP_SHORT_TX+COMP_SUCCESS pair happens
> always, as the hso driver submits a URB with a 1024 byte buffer, and
> the modem usually replies with AT responses character by character;
> for each character I end up getting both events: first with
> COMP_SHORT_TX and the event length set to 1023, second with
> COMP_SUCESS and event length set to 0.
>
Looking at the code it seems that xhci controllers after 0.96 generate a
spurious COMP_SUCCESS after short packet, code says:
/* In xhci controllers which follow xhci 1.0 spec gives a spurious
* success event after a short transfer. This quirk will ignore such
* spurious event.
*/
if (xhci->hci_version > 0x96)
xhci->quirks |= XHCI_SPURIOUS_SUCCESS;
> I read the xhci specs as well and I also got the impression that there
> wasn't anything explicitly stating that a COMP_SUCCESS event always
> follows a COMP_SHORT_TX. But, the code already assumes that if you get
> a COMP_SHORT_TX event for a TRB which isn't the last one, you should
> still get an event for the last TRB (i.e. finish_td() isn't called for
> a COMP_SHORT_TX event if the TRB isn't the first one (event_trb !=
> ep_ring->dequeue) and if it isn't the last one (event_trb !=
> td->last_trb).
Thats right, we always set the IOC (interrupt on completion) in the last TRB of
a control transfer, so it will always interrupt, generating a transfer event.
I read through your previous mails where you were investigating this and also
looked a bit
in more detail at the xhci code. I now understand better the issue. Its clearly
a bug in xhci driver.
Can you still add some debugging and check the if the second event after the
COMP_SHORT_TX really
is a COMP_SUCCESS ? and also what it says about the transfer length.
printing out these values for the second event should do it:
GET_COMP_CODE(le32_to_cpu(event->transfer_len))
EVENT_TRB_LEN(le32_to_cpu(event->transfer_len))
Right now process_ctrl_td() sets the urb->actual_length based on TRB position
in TD (first, last, neither) and
what value urb->actual_length contains. I think that by checking the actual
event completion code, and the event
reported remaining length we could get this correct without adding any
additional fields to struct xhci_td.
If I find the time I'll rewrite this part, if not then I'll add your patch,
(v4)
>
>>
>>> The driver checks this by seeing whether urb->actual_length == 0, but this
>>> alone
>>> is the wrong test, as it is entirely possible for a short transfer to have
>>> an
>>> urb->actual_length = 0.
>>
>> This should be fixed, handling short packets look a bit messy in general
>> right now
>>
>>>
>>> This patch changes the xhci driver to rely not only on the
>>> urb->actual_length,
>>> but also on the ep_ring->last_td_was_short flag, which is set to true when a
>>> COMP_SHORT_TX event is received.
>>>
>>> This fixes a bug which affected the HSO plugin, which relies on URBs with
>>> urb->actual_length == 0 to halt re-submitting the RX URB in the control
>>> endpoint.
>>>
>>> Signed-off-by: Aleksander Morgado <[email protected]>
>>> ---
>>>
>>> Hey,
>>>
>>> This is the third update of the patch:
>>>
>>> * v2 modified the commit message to make it shorter and clearer.
>>>
>>> * v3 updated the format of the commented lines in the patch.
>>>
>>> Cheers!
>>>
>>> ---
>>> drivers/usb/host/xhci-ring.c | 16 +++++++++++-----
>>> 1 file changed, 11 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
>>> index 88da8d6..eda3276 100644
>>> --- a/drivers/usb/host/xhci-ring.c
>>> +++ b/drivers/usb/host/xhci-ring.c
>>> @@ -1955,7 +1955,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci,
>>> struct xhci_td *td,
>>> /* Did we already see a short data
>>> * stage? */
>>> *status = -EREMOTEIO;
>>> - } else {
>>> + } else if (!ep_ring->last_td_was_short) {
>>> td->urb->actual_length =
>>> td->urb->transfer_buffer_length;
>>> }
>>> @@ -2447,10 +2447,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
>>> ret = skip_isoc_td(xhci, td, event, ep, &status);
>>> goto cleanup;
>>> }
>>> - if (trb_comp_code == COMP_SHORT_TX)
>>> - ep_ring->last_td_was_short = true;
>>> - else
>>> - ep_ring->last_td_was_short = false;
>>>
>>> if (ep->skip) {
>>> xhci_dbg(xhci, "Found td. Clear skip flag.\n");
>>> @@ -2484,6 +2480,16 @@ static int handle_tx_event(struct xhci_hcd *xhci,
>>> ret = process_bulk_intr_td(xhci, td, event_trb, event,
>>> ep, &status);
>>>
>>> + /*
>>> + * Flag whether the just processed TRB was short. Do it after
>>> + * processing, so that the processor methods can also use this
>>> + * flag.
>>> + */
>>> + if (trb_comp_code == COMP_SHORT_TX)
>>> + ep_ring->last_td_was_short = true;
>>> + else
>>> + ep_ring->last_td_was_short = false;
>>> +
>>
>> How about the case where we only get one COMP_SHORT_TX event for that
>> control transfer,
>> xhci then advances to the next TD, which completes successfully? That
>> successful TD won't get
>> its td->urb->actual length set because the last_td_was_short flag it still
>> set?
>>
>
> If that is something that can happen, i.e. if COMP_SHORT_TX isn't
> always followed by a COMP_SUCCESS, then we should definitely not use
> the flag in the ep_ring. Maybe some flag in the URB itself?
I think this is possible for xhci host v0.96 and older, we probably get a
second COMP_SHORT_TX for the status stage TRB
>
> The other thing I thought of was to somehow always initialize the URB
> actual length to the transfer buffer length from the very beginning,
> and only update it if a COMP_SHORT_TX event is received. Not sure if
> that would be much more complex to handle, though.
>
This could be an option, need to look into it.
-Mathias
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html