wimax/i2400m: handle USB stalls
When the device stalls, clear it and retry; if it keeps failing too often, reset the device. This specially happens when running on virtual machines; the real hardware doesn't seem to trip on stalls too much, except for a few reports in the mailing list (still to be confirmed this is the cause, although it seems likely. NOTE: it is not clear if the URB has to be resubmitted fully or start only at the offset of the first transaction sent. Can't find documentation to clarify one end or the other. Tests that just resubmit the whole URB seemed to work in my environment. Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
This commit is contained in:
parent
fae92216da
commit
faf57162e4
|
@ -113,6 +113,28 @@ retry:
|
||||||
}
|
}
|
||||||
result = len;
|
result = len;
|
||||||
break;
|
break;
|
||||||
|
case -EPIPE:
|
||||||
|
/*
|
||||||
|
* Stall -- maybe the device is choking with our
|
||||||
|
* requests. Clear it and give it some time. If they
|
||||||
|
* happen to often, it might be another symptom, so we
|
||||||
|
* reset.
|
||||||
|
*
|
||||||
|
* No error handling for usb_clear_halt(0; if it
|
||||||
|
* works, the retry works; if it fails, this switch
|
||||||
|
* does the error handling for us.
|
||||||
|
*/
|
||||||
|
if (edc_inc(&i2400mu->urb_edc,
|
||||||
|
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
|
||||||
|
dev_err(dev, "BM-CMD: too many stalls in "
|
||||||
|
"URB; resetting device\n");
|
||||||
|
usb_queue_reset_device(i2400mu->usb_iface);
|
||||||
|
/* fallthrough */
|
||||||
|
} else {
|
||||||
|
usb_clear_halt(i2400mu->usb_dev, pipe);
|
||||||
|
msleep(10); /* give the device some time */
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
case -EINVAL: /* while removing driver */
|
case -EINVAL: /* while removing driver */
|
||||||
case -ENODEV: /* dev disconnect ... */
|
case -ENODEV: /* dev disconnect ... */
|
||||||
case -ENOENT: /* just ignore it */
|
case -ENOENT: /* just ignore it */
|
||||||
|
|
|
@ -222,6 +222,26 @@ retry:
|
||||||
goto retry; /* ZLP, just resubmit */
|
goto retry; /* ZLP, just resubmit */
|
||||||
skb_put(rx_skb, read_size);
|
skb_put(rx_skb, read_size);
|
||||||
break;
|
break;
|
||||||
|
case -EPIPE:
|
||||||
|
/*
|
||||||
|
* Stall -- maybe the device is choking with our
|
||||||
|
* requests. Clear it and give it some time. If they
|
||||||
|
* happen to often, it might be another symptom, so we
|
||||||
|
* reset.
|
||||||
|
*
|
||||||
|
* No error handling for usb_clear_halt(0; if it
|
||||||
|
* works, the retry works; if it fails, this switch
|
||||||
|
* does the error handling for us.
|
||||||
|
*/
|
||||||
|
if (edc_inc(&i2400mu->urb_edc,
|
||||||
|
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
|
||||||
|
dev_err(dev, "BM-CMD: too many stalls in "
|
||||||
|
"URB; resetting device\n");
|
||||||
|
goto do_reset;
|
||||||
|
}
|
||||||
|
usb_clear_halt(i2400mu->usb_dev, usb_pipe);
|
||||||
|
msleep(10); /* give the device some time */
|
||||||
|
goto retry;
|
||||||
case -EINVAL: /* while removing driver */
|
case -EINVAL: /* while removing driver */
|
||||||
case -ENODEV: /* dev disconnect ... */
|
case -ENODEV: /* dev disconnect ... */
|
||||||
case -ENOENT: /* just ignore it */
|
case -ENOENT: /* just ignore it */
|
||||||
|
@ -283,6 +303,7 @@ out:
|
||||||
error_reset:
|
error_reset:
|
||||||
dev_err(dev, "RX: maximum errors in URB exceeded; "
|
dev_err(dev, "RX: maximum errors in URB exceeded; "
|
||||||
"resetting device\n");
|
"resetting device\n");
|
||||||
|
do_reset:
|
||||||
usb_queue_reset_device(i2400mu->usb_iface);
|
usb_queue_reset_device(i2400mu->usb_iface);
|
||||||
rx_skb = ERR_PTR(result);
|
rx_skb = ERR_PTR(result);
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -115,6 +115,28 @@ retry:
|
||||||
result = -EIO;
|
result = -EIO;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case -EPIPE:
|
||||||
|
/*
|
||||||
|
* Stall -- maybe the device is choking with our
|
||||||
|
* requests. Clear it and give it some time. If they
|
||||||
|
* happen to often, it might be another symptom, so we
|
||||||
|
* reset.
|
||||||
|
*
|
||||||
|
* No error handling for usb_clear_halt(0; if it
|
||||||
|
* works, the retry works; if it fails, this switch
|
||||||
|
* does the error handling for us.
|
||||||
|
*/
|
||||||
|
if (edc_inc(&i2400mu->urb_edc,
|
||||||
|
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
|
||||||
|
dev_err(dev, "BM-CMD: too many stalls in "
|
||||||
|
"URB; resetting device\n");
|
||||||
|
usb_queue_reset_device(i2400mu->usb_iface);
|
||||||
|
/* fallthrough */
|
||||||
|
} else {
|
||||||
|
usb_clear_halt(i2400mu->usb_dev, usb_pipe);
|
||||||
|
msleep(10); /* give the device some time */
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
case -EINVAL: /* while removing driver */
|
case -EINVAL: /* while removing driver */
|
||||||
case -ENODEV: /* dev disconnect ... */
|
case -ENODEV: /* dev disconnect ... */
|
||||||
case -ENOENT: /* just ignore it */
|
case -ENOENT: /* just ignore it */
|
||||||
|
|
|
@ -172,14 +172,59 @@ int __i2400mu_send_barker(struct i2400mu *i2400mu,
|
||||||
epd = usb_get_epd(i2400mu->usb_iface, endpoint);
|
epd = usb_get_epd(i2400mu->usb_iface, endpoint);
|
||||||
pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
|
pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
|
||||||
memcpy(buffer, barker, barker_size);
|
memcpy(buffer, barker, barker_size);
|
||||||
|
retry:
|
||||||
ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size,
|
ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size,
|
||||||
&actual_len, 200);
|
&actual_len, 200);
|
||||||
if (ret < 0) {
|
switch (ret) {
|
||||||
if (ret != -EINVAL)
|
case 0:
|
||||||
dev_err(dev, "E: barker error: %d\n", ret);
|
if (actual_len != barker_size) { /* Too short? drop it */
|
||||||
} else if (actual_len != barker_size) {
|
dev_err(dev, "E: %s: short write (%d B vs %zu "
|
||||||
dev_err(dev, "E: only %d bytes transmitted\n", actual_len);
|
"expected)\n",
|
||||||
ret = -EIO;
|
__func__, actual_len, barker_size);
|
||||||
|
ret = -EIO;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case -EPIPE:
|
||||||
|
/*
|
||||||
|
* Stall -- maybe the device is choking with our
|
||||||
|
* requests. Clear it and give it some time. If they
|
||||||
|
* happen to often, it might be another symptom, so we
|
||||||
|
* reset.
|
||||||
|
*
|
||||||
|
* No error handling for usb_clear_halt(0; if it
|
||||||
|
* works, the retry works; if it fails, this switch
|
||||||
|
* does the error handling for us.
|
||||||
|
*/
|
||||||
|
if (edc_inc(&i2400mu->urb_edc,
|
||||||
|
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
|
||||||
|
dev_err(dev, "E: %s: too many stalls in "
|
||||||
|
"URB; resetting device\n", __func__);
|
||||||
|
usb_queue_reset_device(i2400mu->usb_iface);
|
||||||
|
/* fallthrough */
|
||||||
|
} else {
|
||||||
|
usb_clear_halt(i2400mu->usb_dev, pipe);
|
||||||
|
msleep(10); /* give the device some time */
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
case -EINVAL: /* while removing driver */
|
||||||
|
case -ENODEV: /* dev disconnect ... */
|
||||||
|
case -ENOENT: /* just ignore it */
|
||||||
|
case -ESHUTDOWN: /* and exit */
|
||||||
|
case -ECONNRESET:
|
||||||
|
ret = -ESHUTDOWN;
|
||||||
|
break;
|
||||||
|
default: /* Some error? */
|
||||||
|
if (edc_inc(&i2400mu->urb_edc,
|
||||||
|
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
|
||||||
|
dev_err(dev, "E: %s: maximum errors in URB "
|
||||||
|
"exceeded; resetting device\n",
|
||||||
|
__func__);
|
||||||
|
usb_queue_reset_device(i2400mu->usb_iface);
|
||||||
|
} else {
|
||||||
|
dev_warn(dev, "W: %s: cannot send URB: %d\n",
|
||||||
|
__func__, ret);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
kfree(buffer);
|
kfree(buffer);
|
||||||
error_kzalloc:
|
error_kzalloc:
|
||||||
|
|
Loading…
Reference in a new issue