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