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