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