usb_rndis: accept incoming USB transfers up to 0x4000 size

As required by the spec. Then split them into multiple ethernet frames
as needed.

Fixes #17738.

Change-Id: I71ebff0fe1fc5c8a342d6d06b26eda8e87115e04
Reviewed-on: https://review.haiku-os.org/c/haiku/+/5312
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
PulkoMandy
2022-05-14 15:48:51 +00:00
committed by waddlesplash
parent a806e3678a
commit 3ad6b5ecba
2 changed files with 50 additions and 44 deletions
@@ -74,6 +74,7 @@ RNDISDevice::RNDISDevice(usb_device device)
fNotifyReadSem(-1), fNotifyReadSem(-1),
fNotifyWriteSem(-1), fNotifyWriteSem(-1),
fNotifyControlSem(-1), fNotifyControlSem(-1),
fReadHeader(NULL),
fLinkStateChangeSem(-1), fLinkStateChangeSem(-1),
fMediaConnectState(MEDIA_STATE_UNKNOWN), fMediaConnectState(MEDIA_STATE_UNKNOWN),
fDownstreamSpeed(0) fDownstreamSpeed(0)
@@ -243,72 +244,75 @@ RNDISDevice::Read(uint8 *buffer, size_t *numBytes)
return B_DEVICE_NOT_FOUND; return B_DEVICE_NOT_FOUND;
} }
iovec vec[2]; // The read funcion can return only one packet at a time, but we can receive multiple ones in
uint32 header[11] = { 0 }; // a single USB transfer. So we need to buffer them, and check if we have something in our
// buffer for each Read() call before scheduling a new USB transfer. This would be more
vec[0].iov_base = &header; // efficient if the network stack had a way to read multiple frames at once.
vec[0].iov_len = sizeof(header); if (fReadHeader == NULL) {
status_t result = gUSBModule->queue_bulk(fReadEndpoint, fReadBuffer, sizeof(fReadBuffer),
vec[1].iov_base = buffer; _ReadCallback, this);
vec[1].iov_len = *numBytes;
status_t result = gUSBModule->queue_bulk_v(fReadEndpoint, vec, 2,
_ReadCallback, this);
if (result != B_OK) {
*numBytes = 0;
return result;
}
result = acquire_sem_etc(fNotifyReadSem, 1, B_CAN_INTERRUPT, 0);
if (result < B_OK) {
*numBytes = 0;
return result;
}
if (fStatusRead != B_OK && fStatusRead != B_CANCELED && !fRemoved) {
TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", fStatusRead);
result = gUSBModule->clear_feature(fReadEndpoint,
USB_FEATURE_ENDPOINT_HALT);
if (result != B_OK) { if (result != B_OK) {
TRACE_ALWAYS("failed to clear halt state on read\n");
*numBytes = 0; *numBytes = 0;
return result; return result;
} }
result = acquire_sem_etc(fNotifyReadSem, 1, B_CAN_INTERRUPT, 0);
if (result < B_OK) {
*numBytes = 0;
return result;
}
if (fStatusRead != B_OK && fStatusRead != B_CANCELED && !fRemoved) {
TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", fStatusRead);
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 = (uint32*)fReadBuffer;
} }
// TODO buffering is needed if we receive multiple packets, OOB data, etc since each Read if (fReadHeader[0] != REMOTE_NDIS_PACKET_MSG) {
// call can only return one packet? (which isn't great and could be improved on network stack TRACE_ALWAYS("Received unexpected packet type %08" B_PRIx32 " on data link\n",
// side to reduce syscalls if we had a readmmsg device call or something similar...) fReadHeader[0]);
if (header[0] != REMOTE_NDIS_PACKET_MSG) {
TRACE_ALWAYS("Received unexpected packet type %08" B_PRIx32 " on data link\n", header[0]);
*numBytes = 0; *numBytes = 0;
fReadHeader = NULL;
return B_BAD_VALUE; return B_BAD_VALUE;
} }
if (header[1] != fActualLengthRead) { if (fReadHeader[1] + ((uint8*)fReadHeader - fReadBuffer) > fActualLengthRead) {
TRACE_ALWAYS("Received frame length %08" B_PRIx32 " but USB transfer length is %08" TRACE_ALWAYS("Received frame at %ld length %08" B_PRIx32 " out of bounds of receive buffer"
B_PRIx32 "\n", header[1], fActualLengthRead); "%08" B_PRIx32 "\n", (uint8*) fReadHeader - fReadBuffer, fReadHeader[1],
fActualLengthRead);
} }
if (header[4] != 0 || header[5] != 0 || header[6] != 0) { if (fReadHeader[4] != 0 || fReadHeader[5] != 0 || fReadHeader[6] != 0) {
TRACE_ALWAYS("Received frame has out of bound data: off %08" B_PRIx32 " len %08" B_PRIx32 TRACE_ALWAYS("Received frame has out of bound data: off %08" B_PRIx32 " len %08" B_PRIx32
" count %08" B_PRIx32 "\n", header[4], header[5], header[6]); " count %08" B_PRIx32 "\n", fReadHeader[4], fReadHeader[5], fReadHeader[6]);
} }
if (header[7] != 0 || header[8] != 0) { if (fReadHeader[7] != 0 || fReadHeader[8] != 0) {
TRACE_ALWAYS("Received frame has per-packet info: off %08" B_PRIx32 " len %08" B_PRIx32 TRACE_ALWAYS("Received frame has per-packet info: off %08" B_PRIx32 " len %08" B_PRIx32
"\n", header[7], header[8]); "\n", fReadHeader[7], fReadHeader[8]);
} }
if (header[9] != 0) { if (fReadHeader[9] != 0) {
TRACE_ALWAYS("Received frame has non-0 reserved fied %08x\n", header[9]); TRACE_ALWAYS("Received frame has non-0 reserved fied %08x\n", fReadHeader[9]);
} }
*numBytes = header[3]; *numBytes = fReadHeader[3];
memcpy(buffer, fReadHeader + 11, fReadHeader[3]);
TRACE("Received data packet len %08x data [off %08x len %08x]\n", TRACE("Received data packet len %08x data [off %08x len %08x]\n",
header[1], header[2], header[3]); fReadHeader[1], fReceivHeader[2], fReadHeader[3]);
// Advance to next packet
fReadHeader += fReadHeader[1];
if ((uint8*)fReadHeader - fReadBuffer >= fActualLengthRead)
fReadHeader = NULL;
return B_OK; return B_OK;
} }
@@ -79,6 +79,8 @@ static void _NotifyCallback(void *cookie, int32 status,
sem_id fNotifyControlSem; sem_id fNotifyControlSem;
uint8 fNotifyBuffer[8]; uint8 fNotifyBuffer[8];
uint8 fReadBuffer[0x4000];
uint32* fReadHeader;
// connection data // connection data
sem_id fLinkStateChangeSem; sem_id fLinkStateChangeSem;