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