xref: /haiku/src/add-ons/kernel/drivers/network/ether/usb_rndis/RNDISDevice.cpp (revision 1a76488fc88584bf66b9751d7fb9b6527ac20d87)
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[2] + fReadHeader[3] > fReadHeader[1]) {
293 		TRACE_ALWAYS("Received frame data goes past end of frame: %" B_PRIu32 " + %" B_PRIu32
294 			" > %" B_PRIu32, fReadHeader[2], fReadHeader[3], fReadHeader[1]);
295 	}
296 
297 	if (fReadHeader[4] != 0 || fReadHeader[5] != 0 || fReadHeader[6] != 0) {
298 		TRACE_ALWAYS("Received frame has out of band data: off %08" B_PRIx32 " len %08" B_PRIx32
299 			" count %08" B_PRIx32 "\n", fReadHeader[4], fReadHeader[5], fReadHeader[6]);
300 	}
301 
302 	if (fReadHeader[7] != 0 || fReadHeader[8] != 0) {
303 		TRACE_ALWAYS("Received frame has per-packet info: off %08" B_PRIx32 " len %08" B_PRIx32
304 			"\n", fReadHeader[7], fReadHeader[8]);
305 	}
306 
307 	if (fReadHeader[9] != 0) {
308 		TRACE_ALWAYS("Received frame has non-0 reserved field %08" B_PRIx32 "\n", fReadHeader[9]);
309 	}
310 
311 	*numBytes = fReadHeader[3];
312 	int offset = fReadHeader[2] + 2 * sizeof(uint32);
313 	memcpy(buffer, (uint8*)fReadHeader + offset, fReadHeader[3]);
314 
315 	TRACE("Received data packet len %08" B_PRIx32 " data [off %08" B_PRIx32 " len %08" B_PRIx32 "]\n",
316 		fReadHeader[1], fReadHeader[2], fReadHeader[3]);
317 
318 	// Advance to next packet
319 	fReadHeader = (uint32*)((uint8*)fReadHeader + fReadHeader[1]);
320 
321 	// Are we past the end of the buffer? If so, prepare to receive another one on the next read
322 	if ((uint32)((uint8*)fReadHeader - fReadBuffer) >= fActualLengthRead)
323 		fReadHeader = NULL;
324 
325 	return B_OK;
326 }
327 
328 
329 status_t
330 RNDISDevice::Write(const uint8 *buffer, size_t *numBytes)
331 {
332 	if (fRemoved) {
333 		*numBytes = 0;
334 		return B_DEVICE_NOT_FOUND;
335 	}
336 
337 	iovec vec[2];
338 
339 	uint32 header[11] = { 0 };
340 	header[0] = REMOTE_NDIS_PACKET_MSG;
341 	header[1] = *numBytes + sizeof(header);
342 	header[2] = 0x24;
343 	header[3] = *numBytes;
344 
345 	vec[0].iov_base = &header;
346 	vec[0].iov_len = sizeof(header);
347 
348 	vec[1].iov_base = (void*)buffer;
349 	vec[1].iov_len = *numBytes;
350 
351 	status_t result = gUSBModule->queue_bulk_v(fWriteEndpoint, vec, 2, _WriteCallback, this);
352 	if (result != B_OK) {
353 		*numBytes = 0;
354 		return result;
355 	}
356 
357 	result = acquire_sem_etc(fNotifyWriteSem, 1, B_CAN_INTERRUPT, 0);
358 	if (result < B_OK) {
359 		*numBytes = 0;
360 		return result;
361 	}
362 
363 	if (fStatusWrite != B_OK && fStatusWrite != B_CANCELED && !fRemoved) {
364 		TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", fStatusWrite);
365 		result = gUSBModule->clear_feature(fWriteEndpoint,
366 			USB_FEATURE_ENDPOINT_HALT);
367 		if (result != B_OK) {
368 			TRACE_ALWAYS("failed to clear halt state on write\n");
369 			*numBytes = 0;
370 			return result;
371 		}
372 	}
373 
374 	*numBytes = fActualLengthWrite;
375 	return B_OK;
376 }
377 
378 
379 status_t
380 RNDISDevice::Control(uint32 op, void *buffer, size_t length)
381 {
382 	switch (op) {
383 		case ETHER_INIT:
384 			return B_OK;
385 
386 		case ETHER_GETADDR:
387 			memcpy(buffer, &fMACAddress, sizeof(fMACAddress));
388 			return B_OK;
389 
390 		case ETHER_GETFRAMESIZE:
391 			*(uint32 *)buffer = fMaxSegmentSize;
392 			return B_OK;
393 
394 		case ETHER_SET_LINK_STATE_SEM:
395 			fLinkStateChangeSem = *(sem_id *)buffer;
396 			return B_OK;
397 
398 		case ETHER_GET_LINK_STATE:
399 		{
400 			ether_link_state *state = (ether_link_state *)buffer;
401 			// FIXME get media duplex state from OID_GEN_LINK_STATE if supported
402 			state->media = IFM_ETHER | IFM_FULL_DUPLEX;
403 			if (fMediaConnectState != MEDIA_STATE_DISCONNECTED)
404 				state->media |= IFM_ACTIVE;
405 			state->quality = 1000;
406 			state->speed = fDownstreamSpeed * 100;
407 			return B_OK;
408 		}
409 
410 		default:
411 			TRACE_ALWAYS("unsupported ioctl %" B_PRIu32 "\n", op);
412 	}
413 
414 	return B_DEV_INVALID_IOCTL;
415 }
416 
417 
418 void
419 RNDISDevice::Removed()
420 {
421 	fRemoved = true;
422 	fMediaConnectState = MEDIA_STATE_DISCONNECTED;
423 	fDownstreamSpeed = 0;
424 
425 	// the notify hook is different from the read and write hooks as it does
426 	// itself schedule traffic (while the other hooks only release a semaphore
427 	// to notify another thread which in turn safly checks for the removed
428 	// case) - so we must ensure that we are not inside the notify hook anymore
429 	// before returning, as we would otherwise violate the promise not to use
430 	// any of the pipes after returning from the removed hook
431 	while (atomic_add(&fInsideNotify, 0) != 0)
432 		snooze(100);
433 
434 	gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
435 	gUSBModule->cancel_queued_transfers(fReadEndpoint);
436 	gUSBModule->cancel_queued_transfers(fWriteEndpoint);
437 
438 	if (fLinkStateChangeSem >= B_OK)
439 		release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
440 }
441 
442 
443 status_t
444 RNDISDevice::CompareAndReattach(usb_device device)
445 {
446 	const usb_device_descriptor *deviceDescriptor
447 		= gUSBModule->get_device_descriptor(device);
448 
449 	if (deviceDescriptor == NULL) {
450 		TRACE_ALWAYS("failed to get device descriptor\n");
451 		return B_ERROR;
452 	}
453 
454 	if (deviceDescriptor->vendor_id != fVendorID
455 		&& deviceDescriptor->product_id != fProductID) {
456 		// this certainly isn't the same device
457 		return B_BAD_VALUE;
458 	}
459 
460 	// this might be the same device that was replugged - read the MAC address
461 	// (which should be at the same index) to make sure
462 	uint8 macBuffer[6];
463 	if (_ReadMACAddress(device, macBuffer) != B_OK
464 		|| memcmp(macBuffer, fMACAddress, sizeof(macBuffer)) != 0) {
465 		// reading the MAC address failed or they are not the same
466 		return B_BAD_VALUE;
467 	}
468 
469 	// this is the same device that was replugged - clear the removed state,
470 	// re-setup the endpoints and transfers and open the device if it was
471 	// previously opened
472 	fDevice = device;
473 	fRemoved = false;
474 	status_t result = _SetupDevice();
475 	if (result != B_OK) {
476 		fRemoved = true;
477 		return result;
478 	}
479 
480 	// in case notifications do not work we will have a hardcoded connection
481 	// need to register that and notify the network stack ourselfs if this is
482 	// the case as the open will not result in a corresponding notification
483 	bool noNotifications = (fMediaConnectState == MEDIA_STATE_CONNECTED);
484 
485 	if (fOpen) {
486 		fOpen = false;
487 		result = Open();
488 		if (result == B_OK && noNotifications && fLinkStateChangeSem >= B_OK)
489 			release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
490 	}
491 
492 	return B_OK;
493 }
494 
495 
496 status_t
497 RNDISDevice::_SendCommand(const void* data, size_t length)
498 {
499 	size_t actualLength;
500 	return gUSBModule->send_request(fDevice, USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS,
501 		USB_CDC_SEND_ENCAPSULATED_COMMAND, 0, 0, length, (void*)data, &actualLength);
502 }
503 
504 
505 status_t
506 RNDISDevice::_ReadResponse(void* data, size_t length)
507 {
508 	size_t actualLength;
509 	return gUSBModule->send_request(fDevice, USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS,
510 		USB_CDC_GET_ENCAPSULATED_RESPONSE, 0, 0, length, data, &actualLength);
511 }
512 
513 
514 status_t
515 RNDISDevice::_RNDISInitialize()
516 {
517 	uint32 request[] = {
518 		REMOTE_NDIS_INITIALIZE_MSG,
519 		6 * sizeof(uint32), // MessageLength
520 		0x00000000, // RequestID
521 		0x00000001, 0x00000000, // Version (major, minor)
522 		0x00004000 // MaxTransferSize
523 	};
524 
525 	status_t result = _SendCommand(request, sizeof(request));
526 	TRACE("Send init command results in %s\n", strerror(result));
527 
528 	acquire_sem(fNotifyControlSem);
529 
530 	TRACE("Received notification after init command\n");
531 
532 	uint32 response[0x34 / 4];
533 
534 	result = _ReadResponse(response, sizeof(response));
535 	TRACE("Read init command results in %s\n", strerror(result));
536 	if (result != B_OK)
537 		return result;
538 
539 	TRACE("Type      %" B_PRIx32 "\n", response[0]);
540 	TRACE("Length    %" B_PRIx32 "\n", response[1]);
541 	TRACE("Req ID    %" B_PRIx32 "\n", response[2]);
542 	TRACE("Status    %" B_PRIx32 "\n", response[3]);
543 	TRACE("Vers Maj  %" B_PRIx32 "\n", response[4]);
544 	TRACE("Vers Min  %" B_PRIx32 "\n", response[5]);
545 	TRACE("DevFlags  %" B_PRIx32 "\n", response[6]);
546 	TRACE("Medium    %" B_PRIx32 "\n", response[7]);
547 	TRACE("Max Pkts  %" B_PRIx32 "\n", response[8]);
548 	TRACE("Max Bytes %" B_PRIx32 "\n", response[9]);
549 	TRACE("Alignment %" B_PRIx32 "\n", response[10]);
550 	TRACE("Reserved  ");
551 	for (int i = 11; i < 0x34 / 4; i++)
552 		TRACE("%" B_PRIx32 " ", response[i]);
553 	TRACE("\n");
554 
555 	// TODO configure stuff until we get a SET_CPLT message meaning everything is configured
556 	// TODO set up a notification sytem to be notified if these change? Do we have OIDs for them?
557 	// OID_GEN_HARDWARE_STATUS, OID_GEN_MEDIA_IN_USE
558 
559 	return B_OK;
560 }
561 
562 
563 status_t
564 RNDISDevice::_SetupDevice()
565 {
566 	const usb_device_descriptor *deviceDescriptor
567                 = gUSBModule->get_device_descriptor(fDevice);
568 
569 	if (deviceDescriptor == NULL) {
570 		TRACE_ALWAYS("failed to get device descriptor\n");
571 		return B_ERROR;
572 	}
573 
574 	uint8 controlIndex = 0;
575 	uint8 dataIndex = 0;
576 	bool foundUnionDescriptor = false;
577 	bool foundCMDescriptor = false;
578 	bool found = false;
579 	const usb_configuration_info *config = NULL;
580 	for (int i = 0; i < deviceDescriptor->num_configurations && !found; i++) {
581 		config = gUSBModule->get_nth_configuration(fDevice, i);
582 		if (config == NULL)
583 			continue;
584 
585 		for (size_t j = 0; j < config->interface_count && !found; j++) {
586 			const usb_interface_info *interface = config->interface[j].active;
587 			usb_interface_descriptor *descriptor = interface->descr;
588 			if (descriptor->interface_class != USB_COMMUNICATION_WIRELESS_DEVICE_CLASS
589 				|| descriptor->interface_subclass != 0x01
590 				|| descriptor->interface_protocol != 0x03
591 				|| interface->generic_count == 0) {
592 				continue;
593 			}
594 
595 			// try to find and interpret the union and call management functional
596 			// descriptors (they allow us to locate the data interface)
597 			foundUnionDescriptor = foundCMDescriptor = false;
598 			for (size_t k = 0; k < interface->generic_count; k++) {
599 				usb_generic_descriptor *generic = &interface->generic[k]->generic;
600 				if (generic->length >= sizeof(usb_cdc_union_functional_descriptor)
601 					&& generic->data[0] == USB_CDC_UNION_FUNCTIONAL_DESCRIPTOR) {
602 					controlIndex = generic->data[1];
603 					foundUnionDescriptor = true;
604 				} else if (generic->length >= sizeof(usb_cdc_cm_functional_descriptor)
605 					&& generic->data[0] == USB_CDC_CM_FUNCTIONAL_DESCRIPTOR) {
606 					usb_cdc_cm_functional_descriptor *cm
607 						= (usb_cdc_cm_functional_descriptor *)generic;
608 					dataIndex = cm->data_interface;
609 					foundCMDescriptor = true;
610 				}
611 
612 				if (foundUnionDescriptor && foundCMDescriptor) {
613 					found = true;
614 					break;
615 				}
616 			}
617 		}
618 	}
619 
620 	if (!foundUnionDescriptor) {
621 		TRACE_ALWAYS("did not find a union descriptor\n");
622 		return B_ERROR;
623 	}
624 
625 	if (!foundCMDescriptor) {
626 		TRACE_ALWAYS("did not find an ethernet descriptor\n");
627 		return B_ERROR;
628 	}
629 
630 	// set the current configuration
631 	gUSBModule->set_configuration(fDevice, config);
632 	if (controlIndex >= config->interface_count) {
633 		TRACE_ALWAYS("control interface index invalid\n");
634 		return B_ERROR;
635 	}
636 
637 	// check that the indicated control interface fits our needs
638 	usb_interface_info *interface = config->interface[controlIndex].active;
639 	usb_interface_descriptor *descriptor = interface->descr;
640 	if ((descriptor->interface_class != 0xE0
641 		|| descriptor->interface_subclass != 0x01
642 		|| descriptor->interface_protocol != 0x03)
643 		|| interface->endpoint_count == 0) {
644 		TRACE_ALWAYS("control interface invalid\n");
645 		return B_ERROR;
646 	}
647 
648 	fNotifyEndpoint = interface->endpoint[0].handle;
649 	if (interface->endpoint[0].descr->max_packet_size > sizeof(fNotifyBuffer)) {
650 		TRACE_ALWAYS("Notify buffer is too small, need at least %d bytes\n",
651 			interface->endpoint[0].descr->max_packet_size);
652 		return B_ERROR;
653 	}
654 
655 	if (dataIndex >= config->interface_count) {
656 		TRACE_ALWAYS("data interface index %d out of range %" B_PRIuSIZE "\n", dataIndex,
657 			config->interface_count);
658 		return B_ERROR;
659 	}
660 
661 	interface = &config->interface[dataIndex].alt[0];
662 	descriptor = interface->descr;
663 	if (descriptor->interface_class != USB_CDC_DATA_INTERFACE_CLASS
664 		|| interface->endpoint_count < 2) {
665 		TRACE_ALWAYS("data interface %d invalid (class %x, %" B_PRIuSIZE " endpoints)\n", dataIndex,
666 			descriptor->interface_class, interface->endpoint_count);
667 		return B_ERROR;
668 	}
669 
670 	fDataInterfaceIndex = dataIndex;
671 	return B_OK;
672 }
673 
674 
675 status_t
676 RNDISDevice::_GetOID(uint32 oid, void* buffer, size_t length)
677 {
678 	uint32 request[] = {
679 		REMOTE_NDIS_QUERY_MSG,
680 		7 * sizeof(uint32), // Length of the request
681 		0x00000001, // Request ID (FIXME generate this dynamically if we need multiple requests in
682 					// flight, so we can match up the replies with the different requests)
683 		oid,
684 		0, 0, 0
685 	};
686 
687 	status_t result = _SendCommand(request, sizeof(request));
688 	if (result != B_OK)
689 		return result;
690 
691 	acquire_sem(fNotifyControlSem);
692 
693 	uint8 response[length + 24];
694 	result = _ReadResponse(response, length + 24);
695 	memcpy(buffer, &response[24], length);
696 	return result;
697 }
698 
699 
700 status_t
701 RNDISDevice::_ReadMACAddress(usb_device device, uint8 *buffer)
702 {
703 	status_t result = _GetOID(OID_802_3_PERMANENT_ADDRESS, buffer, 6);
704 	if (result != B_OK)
705 		return result;
706 
707 	TRACE_ALWAYS("mac address: %02x:%02x:%02x:%02x:%02x:%02x\n",
708 		buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
709 	return B_OK;
710 }
711 
712 
713 status_t
714 RNDISDevice::_ReadMaxSegmentSize(usb_device device)
715 {
716 	status_t result = _GetOID(OID_GEN_MAXIMUM_FRAME_SIZE, &fMaxSegmentSize,
717 		sizeof(fMaxSegmentSize));
718 	if (result != B_OK)
719 		return result;
720 
721 	TRACE_ALWAYS("max frame size: %" B_PRId32 "\n", fMaxSegmentSize);
722 	return B_OK;
723 }
724 
725 
726 status_t
727 RNDISDevice::_ReadMediaState(usb_device device)
728 {
729 	status_t result = _GetOID(OID_GEN_MEDIA_CONNECT_STATUS, &fMediaConnectState,
730 		sizeof(fMediaConnectState));
731 	if (result != B_OK)
732 		return result;
733 
734 	TRACE_ALWAYS("media connect state: %" B_PRId32 "\n", fMediaConnectState);
735 	return B_OK;
736 }
737 
738 
739 status_t
740 RNDISDevice::_ReadLinkSpeed(usb_device device)
741 {
742 	status_t result = _GetOID(OID_GEN_LINK_SPEED, &fDownstreamSpeed,
743 		sizeof(fDownstreamSpeed));
744 	if (result != B_OK)
745 		return result;
746 
747 	TRACE_ALWAYS("link speed: %" B_PRId32 " * 100bps\n", fDownstreamSpeed);
748 	return B_OK;
749 }
750 
751 
752 status_t
753 RNDISDevice::_EnableBroadcast(usb_device device)
754 {
755 	uint32 request[] = {
756 		REMOTE_NDIS_SET_MSG,
757 		8 * sizeof(uint32), // Length of the request
758 		0x00000001, // Request ID (FIXME generate this dynamically if we need multiple requests in
759 					// flight, so we can match up the replies with the different requests)
760 		OID_GEN_CURRENT_PACKET_FILTER,
761 		0x14, // buffer length
762 		0x14, // buffer offset
763 		0, // reserved
764 		NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_BROADCAST
765 	};
766 
767 	status_t result = _SendCommand(request, sizeof(request));
768 	if (result != B_OK) {
769 		TRACE_ALWAYS("Failed to start traffic (set oid: %s)\n", strerror(result));
770 		return result;
771 	}
772 
773 	acquire_sem(fNotifyControlSem);
774 
775 	uint32 response[4];
776 	result = _ReadResponse(response, 4 * sizeof(uint32));
777 	if (result != B_OK) {
778 		TRACE_ALWAYS("Failed to start traffic (response: %s)\n", strerror(result));
779 		return result;
780 	}
781 
782 	// TODO check other fields in the response (message type, length, request id) match our request
783 
784 	switch (response[3])
785 	{
786 		case RNDIS_STATUS_SUCCESS:
787 			return B_OK;
788 		case RNDIS_STATUS_FAILURE:
789 			return B_ERROR;
790 		case RNDIS_STATUS_INVALID_DATA:
791 			return B_BAD_DATA;
792 		case RNDIS_STATUS_NOT_SUPPORTED:
793 			return B_NOT_SUPPORTED;
794 		case RNDIS_STATUS_MEDIA_CONNECT:
795 			return EISCONN;
796 		case RNDIS_STATUS_MEDIA_DISCONNECT:
797 			return ENOTCONN;
798 		default:
799 			TRACE_ALWAYS("Unexpected error code %" B_PRIx32 "\n", response[3]);
800 			return B_IO_ERROR;
801 	}
802 }
803 
804 
805 void
806 RNDISDevice::_ReadCallback(void *cookie, int32 status, void *data,
807 	size_t actualLength)
808 {
809 	RNDISDevice *device = (RNDISDevice *)cookie;
810 	device->fActualLengthRead = actualLength;
811 	device->fStatusRead = status;
812 	release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE);
813 }
814 
815 
816 void
817 RNDISDevice::_WriteCallback(void *cookie, int32 status, void *data,
818 	size_t actualLength)
819 {
820 	RNDISDevice *device = (RNDISDevice *)cookie;
821 	device->fActualLengthWrite = actualLength;
822 	device->fStatusWrite = status;
823 	release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE);
824 }
825 
826 
827 void
828 RNDISDevice::_NotifyCallback(void *cookie, int32 status, void *_data,
829 	size_t actualLength)
830 {
831 	RNDISDevice *device = (RNDISDevice *)cookie;
832 	atomic_add(&device->fInsideNotify, 1);
833 	if (status == B_CANCELED || device->fRemoved) {
834 		atomic_add(&device->fInsideNotify, -1);
835 		return;
836 	}
837 
838 	if (status != B_OK) {
839 		TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", status);
840 		if (gUSBModule->clear_feature(device->fNotifyEndpoint,
841 			USB_FEATURE_ENDPOINT_HALT) != B_OK)
842 			TRACE_ALWAYS("failed to clear halt state in notify hook\n");
843 	} else if (actualLength != 8) {
844 		TRACE_ALWAYS("Received notification with unexpected number of bytes %" B_PRIuSIZE "\n",
845 			actualLength);
846 	} else {
847 #ifdef TRACE_RNDIS
848 		uint32* data = (uint32*)_data;
849 		uint32 notification = data[0];
850 		uint32 reserved = data[1];
851 		TRACE("Received notification %" B_PRIx32 " %" B_PRIx32 "\n", notification, reserved);
852 #endif
853 		release_sem_etc(device->fNotifyControlSem, 1, B_DO_NOT_RESCHEDULE);
854 	}
855 
856 	// schedule next notification buffer
857 	gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyBuffer,
858 		sizeof(device->fNotifyBuffer), _NotifyCallback, device);
859 	atomic_add(&device->fInsideNotify, -1);
860 }
861