usb_rndis: improve logging and error handling

- On read errors, make sure to reset the read buffer pointer and not
  return any more packets from the incorrectly read buffer
- Fix a case where an error didn't stop the processing, and we would
  return a zero-byte packet to the network stack instead of an error
- Make sure to use different log messages for different error cases
- Use strerror where appropriate
- Add some TRACE calls to be able to fully trace the code.

There seem to be an error at the XHCI level where it has an "USB
Transaction" error (converted to a B_DEVICE_CRC_ERROR Haiku error code).
After that, despite cancelling that transfer and clearing the HALT
feature, it is not possible to receive anything from the receive
endpoint and the connexion is stuck (it appears sending data still
works, at least from the network stack point of view).

Change-Id: I58687a6eb7b19ba7e7ca594c55499b60fb8b5b26
Reviewed-on: https://review.haiku-os.org/c/haiku/+/9658
Reviewed-by: Adrien Destugues <[email protected]>
Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
PulkoMandy
2025-09-28 14:01:31 +00:00
committed by Adrien Destugues
parent b6f74b4905
commit fc3e0e860a
@@ -251,6 +251,7 @@ status_t
RNDISDevice::Read(uint8 *buffer, size_t *numBytes)
{
if (fRemoved) {
TRACE("Reading, but device is removed\n");
*numBytes = 0;
return B_DEVICE_NOT_FOUND;
}
@@ -260,21 +261,28 @@ RNDISDevice::Read(uint8 *buffer, size_t *numBytes)
// buffer for each Read() call before scheduling a new USB transfer. This would be more
// efficient if the network stack had a way to read multiple frames at once.
if (fReadHeader == NULL) {
TRACE("Reading next packet batch\n");
status_t result = gUSBModule->queue_bulk(fReadEndpoint, fReadBuffer, sizeof(fReadBuffer),
_ReadCallback, this);
if (result != B_OK) {
TRACE_ALWAYS("failed to schedule read transfer: %s\n", strerror(result));
fReadHeader = NULL;
*numBytes = 0;
return result;
}
result = acquire_sem_etc(fNotifyReadSem, 1, B_CAN_INTERRUPT, 0);
if (result < B_OK) {
TRACE_ALWAYS("error while waiting for frame: %s\n", strerror(result));
fReadHeader = NULL;
*numBytes = 0;
return result;
}
if (fStatusRead == B_CANCELED) {
TRACE_ALWAYS("request was cancelled: %s\n", strerror(result));
// The transfer was canceled, so no data was actually received.
fReadHeader = NULL;
*numBytes = 0;
return fStatusRead;
}
@@ -282,18 +290,21 @@ RNDISDevice::Read(uint8 *buffer, size_t *numBytes)
if ((fStatusRead != B_OK) && !fRemoved) {
// In other error cases (triggered by the device), we need to clear the "halt" feature
// so that the next transfers will work.
TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", fStatusRead);
TRACE_ALWAYS("device read status error: %s\n", strerror(fStatusRead));
gUSBModule->cancel_queued_transfers(fReadEndpoint);
result = gUSBModule->clear_feature(fReadEndpoint, USB_FEATURE_ENDPOINT_HALT);
if (result != B_OK) {
TRACE_ALWAYS("failed to clear halt state on read\n");
*numBytes = 0;
return result;
}
fReadHeader = NULL;
*numBytes = 0;
return fStatusRead;
}
fReadHeader = (uint32*)fReadBuffer;
} else {
TRACE("Returning buffered packet\n");
}
if (fReadHeader[0] != REMOTE_NDIS_PACKET_MSG) {
@@ -418,7 +429,7 @@ RNDISDevice::Write(const uint8 *buffer, size_t *numBytes)
}
if ((fStatusWrite != B_OK) && !fRemoved) {
TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", fStatusWrite);
TRACE_ALWAYS("device write status error 0x%08" B_PRIx32 "\n", fStatusWrite);
gUSBModule->cancel_queued_transfers(fReadEndpoint);
@@ -467,6 +478,11 @@ RNDISDevice::Control(uint32 op, void *buffer, size_t length)
return B_OK;
}
case ETHER_SEND_NET_BUFFER:
case ETHER_RECEIVE_NET_BUFFER:
// Ignored for now, we use the old read/write interface instead
return B_DEV_INVALID_IOCTL;
default:
TRACE_ALWAYS("unsupported ioctl %" B_PRIu32 "\n", op);
}
@@ -843,7 +859,7 @@ RNDISDevice::_NotifyCallback(void *cookie, int32 status, void *_data,
}
if (status != B_OK) {
TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", status);
TRACE_ALWAYS("device notify status error 0x%08" B_PRIx32 "\n", status);
if (gUSBModule->clear_feature(device->fNotifyEndpoint,
USB_FEATURE_ENDPOINT_HALT) != B_OK)