xref: /haiku/src/add-ons/kernel/drivers/network/ether/usb_rndis/RNDISDevice.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
1 /*
2 	Driver for USB RNDIS Network devices
3 	Copyright (C) 2022 Adrien Destugues <pulkomandy@pulkomandy.tk>
4 	Distributed under the terms of the MIT license.
5 */
6 #include <ether_driver.h>
7 #include <net/if_media.h>
8 #include <string.h>
9 #include <stdlib.h>
10 
11 #include "RNDISDevice.h"
12 #include "Driver.h"
13 
14 
15 const uint32 OID_GEN_MAXIMUM_FRAME_SIZE = 0x00010106;
16 const uint32 OID_GEN_LINK_SPEED = 0x00010107;
17 const uint32 OID_GEN_CURRENT_PACKET_FILTER = 0x0001010E;
18 const uint32 OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114;
19 const uint32 OID_802_3_PERMANENT_ADDRESS = 0x01010101;
20 
21 
22 enum RndisCommands {
23 	REMOTE_NDIS_PACKET_MSG = 1,
24 	REMOTE_NDIS_INITIALIZE_MSG = 2,
25 	REMOTE_NDIS_INITIALIZE_CMPLT = 0x80000002,
26 	REMOTE_NDIS_HALT_MSG = 3,
27 	REMOTE_NDIS_QUERY_MSG = 4,
28 	REMOTE_NDIS_QUERY_CMPLT = 0x80000004,
29 	REMOTE_NDIS_SET_MSG = 5,
30 	REMOTE_NDIS_SET_CMPLT = 0x80000005,
31 	REMOTE_NDIS_RESET_MSG = 6,
32 	REMOTE_NDIS_RESET_CMPLT = 0x80000006,
33 	REMOTE_NDIS_INDICATE_STATUS_MSG = 7,
34 	REMOTE_NDIS_KEEPALIVE_MSG = 8,
35 	REMOTE_NDIS_KEEPALIVE_CMPLT = 0x80000008
36 };
37 
38 
39 enum Status
40 {
41 	RNDIS_STATUS_SUCCESS = 0,
42 	RNDIS_STATUS_FAILURE = 0xC0000001,
43 	RNDIS_STATUS_INVALID_DATA = 0xC0010015,
44 	RNDIS_STATUS_NOT_SUPPORTED = 0xC00000BB,
45 	RNDIS_STATUS_MEDIA_CONNECT = 0x4001000B,
46 	RNDIS_STATUS_MEDIA_DISCONNECT = 0x4001000C,
47 };
48 
49 
50 enum MediaConnectStatus
51 {
52 	MEDIA_STATE_UNKNOWN,
53 	MEDIA_STATE_CONNECTED,
54 	MEDIA_STATE_DISCONNECTED
55 };
56 
57 
58 const uint32 NDIS_PACKET_TYPE_ALL_MULTICAST = 0x00000004;
59 const uint32 NDIS_PACKET_TYPE_BROADCAST = 0x00000008;
60 
61 
62 
63 RNDISDevice::RNDISDevice(usb_device device)
64 	:	fStatus(B_ERROR),
65 		fOpen(false),
66 		fRemoved(false),
67 		fInsideNotify(0),
68 		fDevice(device),
69 		fDataInterfaceIndex(0),
70 		fMaxSegmentSize(0),
71 		fNotifyEndpoint(0),
72 		fReadEndpoint(0),
73 		fWriteEndpoint(0),
74 		fNotifyReadSem(-1),
75 		fNotifyWriteSem(-1),
76 		fNotifyControlSem(-1),
77 		fReadHeader(NULL),
78 		fLinkStateChangeSem(-1),
79 		fMediaConnectState(MEDIA_STATE_UNKNOWN),
80 		fDownstreamSpeed(0)
81 {
82 	const usb_device_descriptor *deviceDescriptor
83 		= gUSBModule->get_device_descriptor(device);
84 
85 	if (deviceDescriptor == NULL) {
86 		TRACE_ALWAYS("failed to get device descriptor\n");
87 		return;
88 	}
89 
90 	fVendorID = deviceDescriptor->vendor_id;
91 	fProductID = deviceDescriptor->product_id;
92 
93 	fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read");
94 	if (fNotifyReadSem < B_OK) {
95 		TRACE_ALWAYS("failed to create read notify sem\n");
96 		return;
97 	}
98 
99 	fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write");
100 	if (fNotifyWriteSem < B_OK) {
101 		TRACE_ALWAYS("failed to create write notify sem\n");
102 		return;
103 	}
104 
105 	fNotifyControlSem = create_sem(0, DRIVER_NAME"_notify_control");
106 	if (fNotifyControlSem < B_OK) {
107 		TRACE_ALWAYS("failed to create control notify sem\n");
108 		return;
109 	}
110 
111 	if (_SetupDevice() != B_OK) {
112 		TRACE_ALWAYS("failed to setup device\n");
113 		return;
114 	}
115 
116 	fStatus = B_OK;
117 }
118 
119 
120 RNDISDevice::~RNDISDevice()
121 {
122 	if (fNotifyReadSem >= B_OK)
123 		delete_sem(fNotifyReadSem);
124 	if (fNotifyWriteSem >= B_OK)
125 		delete_sem(fNotifyWriteSem);
126 
127 	if (!fRemoved)
128 		gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
129 }
130 
131 
132 status_t
133 RNDISDevice::Open()
134 {
135 	if (fOpen)
136 		return B_BUSY;
137 	if (fRemoved)
138 		return B_ERROR;
139 
140 	// reset the device by switching the data interface to the disabled first
141 	// interface and then enable it by setting the second actual data interface
142 	const usb_configuration_info *config
143 		= gUSBModule->get_configuration(fDevice);
144 
145 	usb_interface_info *interface = config->interface[fDataInterfaceIndex].active;
146 	if (interface->endpoint_count < 2) {
147 		TRACE_ALWAYS("setting the data alternate interface failed\n");
148 		return B_ERROR;
149 	}
150 
151 	if (!(interface->endpoint[0].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN))
152 		fWriteEndpoint = interface->endpoint[0].handle;
153 	else
154 		fReadEndpoint = interface->endpoint[0].handle;
155 
156 	if (interface->endpoint[1].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
157 		fReadEndpoint = interface->endpoint[1].handle;
158 	else
159 		fWriteEndpoint = interface->endpoint[1].handle;
160 
161 	if (fReadEndpoint == 0 || fWriteEndpoint == 0) {
162 		TRACE_ALWAYS("no read and write endpoints found\n");
163 		return B_ERROR;
164 	}
165 
166 	if (gUSBModule->queue_interrupt(fNotifyEndpoint, &fNotifyBuffer,
167 		sizeof(fNotifyBuffer), _NotifyCallback, this) != B_OK) {
168 		TRACE_ALWAYS("failed to setup notification interrupt\n");
169 		return B_ERROR;
170 	}
171 
172 	status_t status = _RNDISInitialize();
173 	if (status != B_OK) {
174 		TRACE_ALWAYS("failed to read mac address\n");
175 		return status;
176 	}
177 
178 	status = _ReadMACAddress(fDevice, fMACAddress);
179 	if (status != B_OK) {
180 		TRACE_ALWAYS("failed to read mac address\n");
181 		return status;
182 	}
183 
184 	// TODO these are non-fatal but make sure we have sane defaults for them
185 	status = _ReadMaxSegmentSize(fDevice);
186 	if (status != B_OK) {
187 		TRACE_ALWAYS("failed to read fragment siez\n");
188 	}
189 
190 	status = _ReadMediaState(fDevice);
191 	if (status != B_OK) {
192 		fMediaConnectState = MEDIA_STATE_CONNECTED;
193 		TRACE_ALWAYS("failed to read media state\n");
194 	}
195 
196 	status = _ReadLinkSpeed(fDevice);
197 	if (status != B_OK) {
198 		fDownstreamSpeed = 1000 * 100; // 10Mbps
199 		TRACE_ALWAYS("failed to read link speed\n");
200 	}
201 
202 	// Tell the device to connect
203 	status = _EnableBroadcast(fDevice);
204 	TRACE("Initialization result: %s\n", strerror(status));
205 
206 	// the device should now be ready
207 	if (status == B_OK)
208 		fOpen = true;
209 	return status;
210 }
211 
212 
213 status_t
214 RNDISDevice::Close()
215 {
216 	if (fRemoved) {
217 		fOpen = false;
218 		return B_OK;
219 	}
220 
221 	// TODO tell the device to disconnect?
222 
223 	gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
224 	gUSBModule->cancel_queued_transfers(fReadEndpoint);
225 	gUSBModule->cancel_queued_transfers(fWriteEndpoint);
226 
227 	fOpen = false;
228 	return B_OK;
229 }
230 
231 
232 status_t
233 RNDISDevice::Free()
234 {
235 	return B_OK;
236 }
237 
238 
239 status_t
240 RNDISDevice::Read(uint8 *buffer, size_t *numBytes)
241 {
242 	if (fRemoved) {
243 		*numBytes = 0;
244 		return B_DEVICE_NOT_FOUND;
245 	}
246 
247 	// The read funcion can return only one packet at a time, but we can receive multiple ones in
248 	// a single USB transfer. So we need to buffer them, and check if we have something in our
249 	// buffer for each Read() call before scheduling a new USB transfer. This would be more
250 	// efficient if the network stack had a way to read multiple frames at once.
251 	if (fReadHeader == NULL) {
252 		status_t result = gUSBModule->queue_bulk(fReadEndpoint, fReadBuffer, sizeof(fReadBuffer),
253 			_ReadCallback, this);
254 		if (result != B_OK) {
255 			*numBytes = 0;
256 			return result;
257 		}
258 
259 		result = acquire_sem_etc(fNotifyReadSem, 1, B_CAN_INTERRUPT, 0);
260 		if (result < B_OK) {
261 			*numBytes = 0;
262 			return result;
263 		}
264 
265 		if (fStatusRead != B_OK && fStatusRead != B_CANCELED && !fRemoved) {
266 			TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", fStatusRead);
267 			result = gUSBModule->clear_feature(fReadEndpoint,
268 				USB_FEATURE_ENDPOINT_HALT);
269 			if (result != B_OK) {
270 				TRACE_ALWAYS("failed to clear halt state on read\n");
271 				*numBytes = 0;
272 				return result;
273 			}
274 		}
275 		fReadHeader = (uint32*)fReadBuffer;
276 	}
277 
278 	if (fReadHeader[0] != REMOTE_NDIS_PACKET_MSG) {
279 		TRACE_ALWAYS("Received unexpected packet type %08" B_PRIx32 " on data link\n",
280 			fReadHeader[0]);
281 		*numBytes = 0;
282 		fReadHeader = NULL;
283 		return B_BAD_VALUE;
284 	}
285 
286 	if (fReadHeader[1] + ((uint8*)fReadHeader - fReadBuffer) > fActualLengthRead) {
287 		TRACE_ALWAYS("Received frame at %ld length %08" B_PRIx32 " out of bounds of receive buffer"
288 			"%08" B_PRIx32 "\n", (uint8*) fReadHeader - fReadBuffer, fReadHeader[1],
289 			fActualLengthRead);
290 	}
291 
292 	if (fReadHeader[4] != 0 || fReadHeader[5] != 0 || fReadHeader[6] != 0) {
293 		TRACE_ALWAYS("Received frame has out of bound data: off %08" B_PRIx32 " len %08" B_PRIx32
294 			" count %08" B_PRIx32 "\n", fReadHeader[4], fReadHeader[5], fReadHeader[6]);
295 	}
296 
297 	if (fReadHeader[7] != 0 || fReadHeader[8] != 0) {
298 		TRACE_ALWAYS("Received frame has per-packet info: off %08" B_PRIx32 " len %08" B_PRIx32
299 			"\n", fReadHeader[7], fReadHeader[8]);
300 	}
301 
302 	if (fReadHeader[9] != 0) {
303 		TRACE_ALWAYS("Received frame has non-0 reserved field %08" B_PRIx32 "\n", fReadHeader[9]);
304 	}
305 
306 	*numBytes = fReadHeader[3];
307 	memcpy(buffer, fReadHeader + 11, fReadHeader[3]);
308 
309 	TRACE("Received data packet len %08" B_PRIx32 " data [off %08" B_PRIx32 " len %08" B_PRIx32 "]\n",
310 		fReadHeader[1], fReadHeader[2], fReadHeader[3]);
311 
312 	// Advance to next packet
313 	fReadHeader += fReadHeader[1];
314 	if ((uint32)((uint8*)fReadHeader - fReadBuffer) >= fActualLengthRead)
315 		fReadHeader = NULL;
316 
317 	return B_OK;
318 }
319 
320 
321 status_t
322 RNDISDevice::Write(const uint8 *buffer, size_t *numBytes)
323 {
324 	if (fRemoved) {
325 		*numBytes = 0;
326 		return B_DEVICE_NOT_FOUND;
327 	}
328 
329 	iovec vec[2];
330 
331 	uint32 header[11] = { 0 };
332 	header[0] = REMOTE_NDIS_PACKET_MSG;
333 	header[1] = *numBytes + sizeof(header);
334 	header[2] = 0x24;
335 	header[3] = *numBytes;
336 
337 	vec[0].iov_base = &header;
338 	vec[0].iov_len = sizeof(header);
339 
340 	vec[1].iov_base = (void*)buffer;
341 	vec[1].iov_len = *numBytes;
342 
343 	status_t result = gUSBModule->queue_bulk_v(fWriteEndpoint, vec, 2, _WriteCallback, this);
344 	if (result != B_OK) {
345 		*numBytes = 0;
346 		return result;
347 	}
348 
349 	result = acquire_sem_etc(fNotifyWriteSem, 1, B_CAN_INTERRUPT, 0);
350 	if (result < B_OK) {
351 		*numBytes = 0;
352 		return result;
353 	}
354 
355 	if (fStatusWrite != B_OK && fStatusWrite != B_CANCELED && !fRemoved) {
356 		TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", fStatusWrite);
357 		result = gUSBModule->clear_feature(fWriteEndpoint,
358 			USB_FEATURE_ENDPOINT_HALT);
359 		if (result != B_OK) {
360 			TRACE_ALWAYS("failed to clear halt state on write\n");
361 			*numBytes = 0;
362 			return result;
363 		}
364 	}
365 
366 	*numBytes = fActualLengthWrite;
367 	return B_OK;
368 }
369 
370 
371 status_t
372 RNDISDevice::Control(uint32 op, void *buffer, size_t length)
373 {
374 	switch (op) {
375 		case ETHER_INIT:
376 			return B_OK;
377 
378 		case ETHER_GETADDR:
379 			memcpy(buffer, &fMACAddress, sizeof(fMACAddress));
380 			return B_OK;
381 
382 		case ETHER_GETFRAMESIZE:
383 			*(uint32 *)buffer = fMaxSegmentSize;
384 			return B_OK;
385 
386 		case ETHER_SET_LINK_STATE_SEM:
387 			fLinkStateChangeSem = *(sem_id *)buffer;
388 			return B_OK;
389 
390 		case ETHER_GET_LINK_STATE:
391 		{
392 			ether_link_state *state = (ether_link_state *)buffer;
393 			// FIXME get media duplex state from OID_GEN_LINK_STATE if supported
394 			state->media = IFM_ETHER | IFM_FULL_DUPLEX;
395 			if (fMediaConnectState != MEDIA_STATE_DISCONNECTED)
396 				state->media |= IFM_ACTIVE;
397 			state->quality = 1000;
398 			state->speed = fDownstreamSpeed * 100;
399 			return B_OK;
400 		}
401 
402 		default:
403 			TRACE_ALWAYS("unsupported ioctl %" B_PRIu32 "\n", op);
404 	}
405 
406 	return B_DEV_INVALID_IOCTL;
407 }
408 
409 
410 void
411 RNDISDevice::Removed()
412 {
413 	fRemoved = true;
414 	fMediaConnectState = MEDIA_STATE_DISCONNECTED;
415 	fDownstreamSpeed = 0;
416 
417 	// the notify hook is different from the read and write hooks as it does
418 	// itself schedule traffic (while the other hooks only release a semaphore
419 	// to notify another thread which in turn safly checks for the removed
420 	// case) - so we must ensure that we are not inside the notify hook anymore
421 	// before returning, as we would otherwise violate the promise not to use
422 	// any of the pipes after returning from the removed hook
423 	while (atomic_add(&fInsideNotify, 0) != 0)
424 		snooze(100);
425 
426 	gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
427 	gUSBModule->cancel_queued_transfers(fReadEndpoint);
428 	gUSBModule->cancel_queued_transfers(fWriteEndpoint);
429 
430 	if (fLinkStateChangeSem >= B_OK)
431 		release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
432 }
433 
434 
435 status_t
436 RNDISDevice::CompareAndReattach(usb_device device)
437 {
438 	const usb_device_descriptor *deviceDescriptor
439 		= gUSBModule->get_device_descriptor(device);
440 
441 	if (deviceDescriptor == NULL) {
442 		TRACE_ALWAYS("failed to get device descriptor\n");
443 		return B_ERROR;
444 	}
445 
446 	if (deviceDescriptor->vendor_id != fVendorID
447 		&& deviceDescriptor->product_id != fProductID) {
448 		// this certainly isn't the same device
449 		return B_BAD_VALUE;
450 	}
451 
452 	// this might be the same device that was replugged - read the MAC address
453 	// (which should be at the same index) to make sure
454 	uint8 macBuffer[6];
455 	if (_ReadMACAddress(device, macBuffer) != B_OK
456 		|| memcmp(macBuffer, fMACAddress, sizeof(macBuffer)) != 0) {
457 		// reading the MAC address failed or they are not the same
458 		return B_BAD_VALUE;
459 	}
460 
461 	// this is the same device that was replugged - clear the removed state,
462 	// re-setup the endpoints and transfers and open the device if it was
463 	// previously opened
464 	fDevice = device;
465 	fRemoved = false;
466 	status_t result = _SetupDevice();
467 	if (result != B_OK) {
468 		fRemoved = true;
469 		return result;
470 	}
471 
472 	// in case notifications do not work we will have a hardcoded connection
473 	// need to register that and notify the network stack ourselfs if this is
474 	// the case as the open will not result in a corresponding notification
475 	bool noNotifications = (fMediaConnectState == MEDIA_STATE_CONNECTED);
476 
477 	if (fOpen) {
478 		fOpen = false;
479 		result = Open();
480 		if (result == B_OK && noNotifications && fLinkStateChangeSem >= B_OK)
481 			release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
482 	}
483 
484 	return B_OK;
485 }
486 
487 
488 status_t
489 RNDISDevice::_SendCommand(const void* data, size_t length)
490 {
491 	size_t actualLength;
492 	return gUSBModule->send_request(fDevice, USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS,
493 		USB_CDC_SEND_ENCAPSULATED_COMMAND, 0, 0, length, (void*)data, &actualLength);
494 }
495 
496 
497 status_t
498 RNDISDevice::_ReadResponse(void* data, size_t length)
499 {
500 	size_t actualLength;
501 	return gUSBModule->send_request(fDevice, USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS,
502 		USB_CDC_GET_ENCAPSULATED_RESPONSE, 0, 0, length, data, &actualLength);
503 }
504 
505 
506 status_t
507 RNDISDevice::_RNDISInitialize()
508 {
509 	uint32 request[] = {
510 		REMOTE_NDIS_INITIALIZE_MSG,
511 		6 * sizeof(uint32), // MessageLength
512 		0x00000000, // RequestID
513 		0x00000001, 0x00000000, // Version (major, minor)
514 		0x00004000 // MaxTransferSize
515 	};
516 
517 	status_t result = _SendCommand(request, sizeof(request));
518 	TRACE("Send init command results in %s\n", strerror(result));
519 
520 	acquire_sem(fNotifyControlSem);
521 
522 	TRACE("Received notification after init command\n");
523 
524 	uint32 response[0x34 / 4];
525 
526 	result = _ReadResponse(response, sizeof(response));
527 	TRACE("Read init command results in %s\n", strerror(result));
528 	if (result != B_OK)
529 		return result;
530 
531 	TRACE("Type      %" B_PRIx32 "\n", response[0]);
532 	TRACE("Length    %" B_PRIx32 "\n", response[1]);
533 	TRACE("Req ID    %" B_PRIx32 "\n", response[2]);
534 	TRACE("Status    %" B_PRIx32 "\n", response[3]);
535 	TRACE("Vers Maj  %" B_PRIx32 "\n", response[4]);
536 	TRACE("Vers Min  %" B_PRIx32 "\n", response[5]);
537 	TRACE("DevFlags  %" B_PRIx32 "\n", response[6]);
538 	TRACE("Medium    %" B_PRIx32 "\n", response[7]);
539 	TRACE("Max Pkts  %" B_PRIx32 "\n", response[8]);
540 	TRACE("Max Bytes %" B_PRIx32 "\n", response[9]);
541 	TRACE("Alignment %" B_PRIx32 "\n", response[10]);
542 	TRACE("Reserved  ");
543 	for (int i = 11; i < 0x34 / 4; i++)
544 		TRACE("%" B_PRIx32 " ", response[i]);
545 	TRACE("\n");
546 
547 	// TODO configure stuff until we get a SET_CPLT message meaning everything is configured
548 	// TODO set up a notification sytem to be notified if these change? Do we have OIDs for them?
549 	// OID_GEN_HARDWARE_STATUS, OID_GEN_MEDIA_IN_USE
550 
551 	return B_OK;
552 }
553 
554 
555 status_t
556 RNDISDevice::_SetupDevice()
557 {
558 	const usb_device_descriptor *deviceDescriptor
559                 = gUSBModule->get_device_descriptor(fDevice);
560 
561 	if (deviceDescriptor == NULL) {
562 		TRACE_ALWAYS("failed to get device descriptor\n");
563 		return B_ERROR;
564 	}
565 
566 	uint8 controlIndex = 0;
567 	uint8 dataIndex = 0;
568 	bool foundUnionDescriptor = false;
569 	bool foundCMDescriptor = false;
570 	bool found = false;
571 	const usb_configuration_info *config = NULL;
572 	for (int i = 0; i < deviceDescriptor->num_configurations && !found; i++) {
573 		config = gUSBModule->get_nth_configuration(fDevice, i);
574 		if (config == NULL)
575 			continue;
576 
577 		for (size_t j = 0; j < config->interface_count && !found; j++) {
578 			const usb_interface_info *interface = config->interface[j].active;
579 			usb_interface_descriptor *descriptor = interface->descr;
580 			if (descriptor->interface_class != USB_COMMUNICATION_WIRELESS_DEVICE_CLASS
581 				|| descriptor->interface_subclass != 0x01
582 				|| descriptor->interface_protocol != 0x03
583 				|| interface->generic_count == 0) {
584 				continue;
585 			}
586 
587 			// try to find and interpret the union and call management functional
588 			// descriptors (they allow us to locate the data interface)
589 			foundUnionDescriptor = foundCMDescriptor = false;
590 			for (size_t k = 0; k < interface->generic_count; k++) {
591 				usb_generic_descriptor *generic = &interface->generic[k]->generic;
592 				if (generic->length >= sizeof(usb_cdc_union_functional_descriptor)
593 					&& generic->data[0] == USB_CDC_UNION_FUNCTIONAL_DESCRIPTOR) {
594 					controlIndex = generic->data[1];
595 					foundUnionDescriptor = true;
596 				} else if (generic->length >= sizeof(usb_cdc_cm_functional_descriptor)
597 					&& generic->data[0] == USB_CDC_CM_FUNCTIONAL_DESCRIPTOR) {
598 					usb_cdc_cm_functional_descriptor *cm
599 						= (usb_cdc_cm_functional_descriptor *)generic;
600 					dataIndex = cm->data_interface;
601 					foundCMDescriptor = true;
602 				}
603 
604 				if (foundUnionDescriptor && foundCMDescriptor) {
605 					found = true;
606 					break;
607 				}
608 			}
609 		}
610 	}
611 
612 	if (!foundUnionDescriptor) {
613 		TRACE_ALWAYS("did not find a union descriptor\n");
614 		return B_ERROR;
615 	}
616 
617 	if (!foundCMDescriptor) {
618 		TRACE_ALWAYS("did not find an ethernet descriptor\n");
619 		return B_ERROR;
620 	}
621 
622 	// set the current configuration
623 	gUSBModule->set_configuration(fDevice, config);
624 	if (controlIndex >= config->interface_count) {
625 		TRACE_ALWAYS("control interface index invalid\n");
626 		return B_ERROR;
627 	}
628 
629 	// check that the indicated control interface fits our needs
630 	usb_interface_info *interface = config->interface[controlIndex].active;
631 	usb_interface_descriptor *descriptor = interface->descr;
632 	if ((descriptor->interface_class != 0xE0
633 		|| descriptor->interface_subclass != 0x01
634 		|| descriptor->interface_protocol != 0x03)
635 		|| interface->endpoint_count == 0) {
636 		TRACE_ALWAYS("control interface invalid\n");
637 		return B_ERROR;
638 	}
639 
640 	fNotifyEndpoint = interface->endpoint[0].handle;
641 	if (interface->endpoint[0].descr->max_packet_size > sizeof(fNotifyBuffer)) {
642 		TRACE_ALWAYS("Notify buffer is too small, need at least %d bytes\n",
643 			interface->endpoint[0].descr->max_packet_size);
644 		return B_ERROR;
645 	}
646 
647 	if (dataIndex >= config->interface_count) {
648 		TRACE_ALWAYS("data interface index %d out of range %" B_PRIuSIZE "\n", dataIndex,
649 			config->interface_count);
650 		return B_ERROR;
651 	}
652 
653 	interface = &config->interface[dataIndex].alt[0];
654 	descriptor = interface->descr;
655 	if (descriptor->interface_class != USB_CDC_DATA_INTERFACE_CLASS
656 		|| interface->endpoint_count < 2) {
657 		TRACE_ALWAYS("data interface %d invalid (class %x, %" B_PRIuSIZE " endpoints)\n", dataIndex,
658 			descriptor->interface_class, interface->endpoint_count);
659 		return B_ERROR;
660 	}
661 
662 	fDataInterfaceIndex = dataIndex;
663 	return B_OK;
664 }
665 
666 
667 status_t
668 RNDISDevice::_GetOID(uint32 oid, void* buffer, size_t length)
669 {
670 	uint32 request[] = {
671 		REMOTE_NDIS_QUERY_MSG,
672 		7 * sizeof(uint32), // Length of the request
673 		0x00000001, // Request ID (FIXME generate this dynamically if we need multiple requests in
674 					// flight, so we can match up the replies with the different requests)
675 		oid,
676 		0, 0, 0
677 	};
678 
679 	status_t result = _SendCommand(request, sizeof(request));
680 	if (result != B_OK)
681 		return result;
682 
683 	acquire_sem(fNotifyControlSem);
684 
685 	uint8 response[length + 24];
686 	result = _ReadResponse(response, length + 24);
687 	memcpy(buffer, &response[24], length);
688 	return result;
689 }
690 
691 
692 status_t
693 RNDISDevice::_ReadMACAddress(usb_device device, uint8 *buffer)
694 {
695 	status_t result = _GetOID(OID_802_3_PERMANENT_ADDRESS, buffer, 6);
696 	if (result != B_OK)
697 		return result;
698 
699 	TRACE_ALWAYS("mac address: %02x:%02x:%02x:%02x:%02x:%02x\n",
700 		buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
701 	return B_OK;
702 }
703 
704 
705 status_t
706 RNDISDevice::_ReadMaxSegmentSize(usb_device device)
707 {
708 	status_t result = _GetOID(OID_GEN_MAXIMUM_FRAME_SIZE, &fMaxSegmentSize,
709 		sizeof(fMaxSegmentSize));
710 	if (result != B_OK)
711 		return result;
712 
713 	TRACE_ALWAYS("max frame size: %" B_PRId32 "\n", fMaxSegmentSize);
714 	return B_OK;
715 }
716 
717 
718 status_t
719 RNDISDevice::_ReadMediaState(usb_device device)
720 {
721 	status_t result = _GetOID(OID_GEN_MEDIA_CONNECT_STATUS, &fMediaConnectState,
722 		sizeof(fMediaConnectState));
723 	if (result != B_OK)
724 		return result;
725 
726 	TRACE_ALWAYS("media connect state: %" B_PRId32 "\n", fMediaConnectState);
727 	return B_OK;
728 }
729 
730 
731 status_t
732 RNDISDevice::_ReadLinkSpeed(usb_device device)
733 {
734 	status_t result = _GetOID(OID_GEN_LINK_SPEED, &fDownstreamSpeed,
735 		sizeof(fDownstreamSpeed));
736 	if (result != B_OK)
737 		return result;
738 
739 	TRACE_ALWAYS("link speed: %" B_PRId32 " * 100bps\n", fDownstreamSpeed);
740 	return B_OK;
741 }
742 
743 
744 status_t
745 RNDISDevice::_EnableBroadcast(usb_device device)
746 {
747 	uint32 request[] = {
748 		REMOTE_NDIS_SET_MSG,
749 		8 * sizeof(uint32), // Length of the request
750 		0x00000001, // Request ID (FIXME generate this dynamically if we need multiple requests in
751 					// flight, so we can match up the replies with the different requests)
752 		OID_GEN_CURRENT_PACKET_FILTER,
753 		0x14, // buffer length
754 		0x14, // buffer offset
755 		0, // reserved
756 		NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_BROADCAST
757 	};
758 
759 	status_t result = _SendCommand(request, sizeof(request));
760 	if (result != B_OK) {
761 		TRACE_ALWAYS("Failed to start traffic (set oid: %s)\n", strerror(result));
762 		return result;
763 	}
764 
765 	acquire_sem(fNotifyControlSem);
766 
767 	uint32 response[4];
768 	result = _ReadResponse(response, 4 * sizeof(uint32));
769 	if (result != B_OK) {
770 		TRACE_ALWAYS("Failed to start traffic (response: %s)\n", strerror(result));
771 		return result;
772 	}
773 
774 	// TODO check other fields in the response (message type, length, request id) match our request
775 
776 	switch (response[3])
777 	{
778 		case RNDIS_STATUS_SUCCESS:
779 			return B_OK;
780 		case RNDIS_STATUS_FAILURE:
781 			return B_ERROR;
782 		case RNDIS_STATUS_INVALID_DATA:
783 			return B_BAD_DATA;
784 		case RNDIS_STATUS_NOT_SUPPORTED:
785 			return B_NOT_SUPPORTED;
786 		case RNDIS_STATUS_MEDIA_CONNECT:
787 			return EISCONN;
788 		case RNDIS_STATUS_MEDIA_DISCONNECT:
789 			return ENOTCONN;
790 		default:
791 			TRACE_ALWAYS("Unexpected error code %" B_PRIx32 "\n", response[3]);
792 			return B_IO_ERROR;
793 	}
794 }
795 
796 
797 void
798 RNDISDevice::_ReadCallback(void *cookie, int32 status, void *data,
799 	size_t actualLength)
800 {
801 	RNDISDevice *device = (RNDISDevice *)cookie;
802 	device->fActualLengthRead = actualLength;
803 	device->fStatusRead = status;
804 	release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE);
805 }
806 
807 
808 void
809 RNDISDevice::_WriteCallback(void *cookie, int32 status, void *data,
810 	size_t actualLength)
811 {
812 	RNDISDevice *device = (RNDISDevice *)cookie;
813 	device->fActualLengthWrite = actualLength;
814 	device->fStatusWrite = status;
815 	release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE);
816 }
817 
818 
819 void
820 RNDISDevice::_NotifyCallback(void *cookie, int32 status, void *_data,
821 	size_t actualLength)
822 {
823 	RNDISDevice *device = (RNDISDevice *)cookie;
824 	atomic_add(&device->fInsideNotify, 1);
825 	if (status == B_CANCELED || device->fRemoved) {
826 		atomic_add(&device->fInsideNotify, -1);
827 		return;
828 	}
829 
830 	if (status != B_OK) {
831 		TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", status);
832 		if (gUSBModule->clear_feature(device->fNotifyEndpoint,
833 			USB_FEATURE_ENDPOINT_HALT) != B_OK)
834 			TRACE_ALWAYS("failed to clear halt state in notify hook\n");
835 	} else if (actualLength != 8) {
836 		TRACE_ALWAYS("Received notification with unexpected number of bytes %" B_PRIuSIZE "\n",
837 			actualLength);
838 	} else {
839 #ifdef TRACE_RNDIS
840 		uint32* data = (uint32*)_data;
841 		uint32 notification = data[0];
842 		uint32 reserved = data[1];
843 		TRACE("Received notification %" B_PRIx32 " %" B_PRIx32 "\n", notification, reserved);
844 #endif
845 		release_sem_etc(device->fNotifyControlSem, 1, B_DO_NOT_RESCHEDULE);
846 	}
847 
848 	// schedule next notification buffer
849 	gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyBuffer,
850 		sizeof(device->fNotifyBuffer), _NotifyCallback, device);
851 	atomic_add(&device->fInsideNotify, -1);
852 }
853