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