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
15const uint32 OID_GEN_MAXIMUM_FRAME_SIZE = 0x00010106;
16const uint32 OID_GEN_LINK_SPEED = 0x00010107;
17const uint32 OID_GEN_CURRENT_PACKET_FILTER = 0x0001010E;
18const uint32 OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114;
19const uint32 OID_802_3_PERMANENT_ADDRESS = 0x01010101;
20
21
22enum 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
39enum 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
50enum MediaConnectStatus
51{
52	MEDIA_STATE_UNKNOWN,
53	MEDIA_STATE_CONNECTED,
54	MEDIA_STATE_DISCONNECTED
55};
56
57
58const uint32 NDIS_PACKET_TYPE_ALL_MULTICAST = 0x00000004;
59const uint32 NDIS_PACKET_TYPE_BROADCAST = 0x00000008;
60
61
62
63RNDISDevice::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
127RNDISDevice::~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
143status_t
144RNDISDevice::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
224status_t
225RNDISDevice::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
243status_t
244RNDISDevice::Free()
245{
246	return B_OK;
247}
248
249
250status_t
251RNDISDevice::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
340class SemLocker {
341public:
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;
355private:
356	sem_id fSem;
357};
358
359
360status_t
361RNDISDevice::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
421status_t
422RNDISDevice::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
460void
461RNDISDevice::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
485status_t
486RNDISDevice::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
538status_t
539RNDISDevice::_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
547status_t
548RNDISDevice::_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
556status_t
557RNDISDevice::_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
605status_t
606RNDISDevice::_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
717status_t
718RNDISDevice::_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
742status_t
743RNDISDevice::_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
755status_t
756RNDISDevice::_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
768status_t
769RNDISDevice::_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
781status_t
782RNDISDevice::_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
794status_t
795RNDISDevice::_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
847void
848RNDISDevice::_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
858void
859RNDISDevice::_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
869void
870RNDISDevice::_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