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