1 /* 2 Driver for USB Ethernet Control Model devices 3 Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch> 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 "ECMDevice.h" 12 #include "Driver.h" 13 14 ECMDevice::ECMDevice(usb_device device) 15 : fStatus(B_ERROR), 16 fOpen(false), 17 fRemoved(false), 18 fInsideNotify(0), 19 fDevice(device), 20 fControlInterfaceIndex(0), 21 fDataInterfaceIndex(0), 22 fMACAddressIndex(0), 23 fMaxSegmentSize(0), 24 fNotifyEndpoint(0), 25 fReadEndpoint(0), 26 fWriteEndpoint(0), 27 fNotifyReadSem(-1), 28 fNotifyWriteSem(-1), 29 fNotifyBuffer(NULL), 30 fNotifyBufferLength(0), 31 fLinkStateChangeSem(-1), 32 fHasConnection(false), 33 fDownstreamSpeed(0), 34 fUpstreamSpeed(0) 35 { 36 const usb_device_descriptor *deviceDescriptor 37 = gUSBModule->get_device_descriptor(device); 38 39 if (deviceDescriptor == NULL) { 40 TRACE_ALWAYS("failed to get device descriptor\n"); 41 return; 42 } 43 44 fVendorID = deviceDescriptor->vendor_id; 45 fProductID = deviceDescriptor->product_id; 46 47 fNotifyBufferLength = 64; 48 fNotifyBuffer = (uint8 *)malloc(fNotifyBufferLength); 49 if (fNotifyBuffer == NULL) { 50 TRACE_ALWAYS("out of memory for notify buffer allocation\n"); 51 return; 52 } 53 54 fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read"); 55 if (fNotifyReadSem < B_OK) { 56 TRACE_ALWAYS("failed to create read notify sem\n"); 57 return; 58 } 59 60 fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write"); 61 if (fNotifyWriteSem < B_OK) { 62 TRACE_ALWAYS("failed to create write notify sem\n"); 63 return; 64 } 65 66 if (_SetupDevice() != B_OK) { 67 TRACE_ALWAYS("failed to setup device\n"); 68 return; 69 } 70 71 if (_ReadMACAddress(fDevice, fMACAddress) != B_OK) { 72 TRACE_ALWAYS("failed to read mac address\n"); 73 return; 74 } 75 76 fStatus = B_OK; 77 } 78 79 80 ECMDevice::~ECMDevice() 81 { 82 if (fNotifyReadSem >= B_OK) 83 delete_sem(fNotifyReadSem); 84 if (fNotifyWriteSem >= B_OK) 85 delete_sem(fNotifyWriteSem); 86 87 if (!fRemoved) 88 gUSBModule->cancel_queued_transfers(fNotifyEndpoint); 89 90 free(fNotifyBuffer); 91 } 92 93 94 status_t 95 ECMDevice::Open() 96 { 97 if (fOpen) 98 return B_BUSY; 99 if (fRemoved) 100 return B_ERROR; 101 102 // reset the device by switching the data interface to the disabled first 103 // interface and then enable it by setting the second actual data interface 104 const usb_configuration_info *config 105 = gUSBModule->get_configuration(fDevice); 106 107 gUSBModule->set_alt_interface(fDevice, 108 &config->interface[fDataInterfaceIndex].alt[0]); 109 110 // update to the changed config 111 config = gUSBModule->get_configuration(fDevice); 112 gUSBModule->set_alt_interface(fDevice, 113 &config->interface[fDataInterfaceIndex].alt[1]); 114 gUSBModule->set_alt_interface(fDevice, 115 &config->interface[fControlInterfaceIndex].alt[0]); 116 117 // update again 118 config = gUSBModule->get_configuration(fDevice); 119 usb_interface_info *interface = config->interface[fDataInterfaceIndex].active; 120 if (interface->endpoint_count < 2) { 121 TRACE_ALWAYS("setting the data alternate interface failed\n"); 122 return B_ERROR; 123 } 124 125 if (!(interface->endpoint[0].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)) 126 fWriteEndpoint = interface->endpoint[0].handle; 127 else 128 fReadEndpoint = interface->endpoint[0].handle; 129 130 if (interface->endpoint[1].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) 131 fReadEndpoint = interface->endpoint[1].handle; 132 else 133 fWriteEndpoint = interface->endpoint[1].handle; 134 135 if (fReadEndpoint == 0 || fWriteEndpoint == 0) { 136 TRACE_ALWAYS("no read and write endpoints found\n"); 137 return B_ERROR; 138 } 139 140 if (gUSBModule->queue_interrupt(fNotifyEndpoint, fNotifyBuffer, 141 fNotifyBufferLength, _NotifyCallback, this) != B_OK) { 142 // we cannot use notifications - hardcode to active connection 143 fHasConnection = true; 144 fDownstreamSpeed = 1000 * 1000 * 10; // 10Mbps 145 fUpstreamSpeed = 1000 * 1000 * 10; // 10Mbps 146 } 147 148 // the device should now be ready 149 fOpen = true; 150 return B_OK; 151 } 152 153 154 status_t 155 ECMDevice::Close() 156 { 157 if (fRemoved) { 158 fOpen = false; 159 return B_OK; 160 } 161 162 gUSBModule->cancel_queued_transfers(fNotifyEndpoint); 163 gUSBModule->cancel_queued_transfers(fReadEndpoint); 164 gUSBModule->cancel_queued_transfers(fWriteEndpoint); 165 166 // put the device into non-connected mode again by switching the data 167 // interface to the disabled alternate 168 const usb_configuration_info *config 169 = gUSBModule->get_configuration(fDevice); 170 171 gUSBModule->set_alt_interface(fDevice, 172 &config->interface[fDataInterfaceIndex].alt[0]); 173 174 fOpen = false; 175 return B_OK; 176 } 177 178 179 status_t 180 ECMDevice::Free() 181 { 182 return B_OK; 183 } 184 185 186 status_t 187 ECMDevice::Read(uint8 *buffer, size_t *numBytes) 188 { 189 if (fRemoved) { 190 *numBytes = 0; 191 return B_DEVICE_NOT_FOUND; 192 } 193 194 status_t result = gUSBModule->queue_bulk(fReadEndpoint, buffer, *numBytes, 195 _ReadCallback, this); 196 if (result != B_OK) { 197 *numBytes = 0; 198 return result; 199 } 200 201 result = acquire_sem_etc(fNotifyReadSem, 1, B_CAN_INTERRUPT, 0); 202 if (result < B_OK) { 203 *numBytes = 0; 204 return result; 205 } 206 207 if (fStatusRead != B_OK && fStatusRead != B_CANCELED && !fRemoved) { 208 TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", fStatusRead); 209 result = gUSBModule->clear_feature(fReadEndpoint, 210 USB_FEATURE_ENDPOINT_HALT); 211 if (result != B_OK) { 212 TRACE_ALWAYS("failed to clear halt state on read\n"); 213 *numBytes = 0; 214 return result; 215 } 216 } 217 218 *numBytes = fActualLengthRead; 219 return B_OK; 220 } 221 222 223 status_t 224 ECMDevice::Write(const uint8 *buffer, size_t *numBytes) 225 { 226 if (fRemoved) { 227 *numBytes = 0; 228 return B_DEVICE_NOT_FOUND; 229 } 230 231 status_t result = gUSBModule->queue_bulk(fWriteEndpoint, (uint8 *)buffer, 232 *numBytes, _WriteCallback, this); 233 if (result != B_OK) { 234 *numBytes = 0; 235 return result; 236 } 237 238 result = acquire_sem_etc(fNotifyWriteSem, 1, B_CAN_INTERRUPT, 0); 239 if (result < B_OK) { 240 *numBytes = 0; 241 return result; 242 } 243 244 if (fStatusWrite != B_OK && fStatusWrite != B_CANCELED && !fRemoved) { 245 TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", fStatusWrite); 246 result = gUSBModule->clear_feature(fWriteEndpoint, 247 USB_FEATURE_ENDPOINT_HALT); 248 if (result != B_OK) { 249 TRACE_ALWAYS("failed to clear halt state on write\n"); 250 *numBytes = 0; 251 return result; 252 } 253 } 254 255 *numBytes = fActualLengthWrite; 256 return B_OK; 257 } 258 259 260 status_t 261 ECMDevice::Control(uint32 op, void *buffer, size_t length) 262 { 263 switch (op) { 264 case ETHER_INIT: 265 return B_OK; 266 267 case ETHER_GETADDR: 268 memcpy(buffer, &fMACAddress, sizeof(fMACAddress)); 269 return B_OK; 270 271 case ETHER_GETFRAMESIZE: 272 *(uint32 *)buffer = fMaxSegmentSize; 273 return B_OK; 274 275 case ETHER_SET_LINK_STATE_SEM: 276 fLinkStateChangeSem = *(sem_id *)buffer; 277 return B_OK; 278 279 case ETHER_GET_LINK_STATE: 280 { 281 ether_link_state *state = (ether_link_state *)buffer; 282 state->media = IFM_ETHER | IFM_FULL_DUPLEX 283 | (fHasConnection ? IFM_ACTIVE : 0); 284 state->quality = 1000; 285 state->speed = fDownstreamSpeed; 286 return B_OK; 287 } 288 289 default: 290 TRACE_ALWAYS("unsupported ioctl %" B_PRIu32 "\n", op); 291 } 292 293 return B_DEV_INVALID_IOCTL; 294 } 295 296 297 void 298 ECMDevice::Removed() 299 { 300 fRemoved = true; 301 fHasConnection = false; 302 fDownstreamSpeed = fUpstreamSpeed = 0; 303 304 // the notify hook is different from the read and write hooks as it does 305 // itself schedule traffic (while the other hooks only release a semaphore 306 // to notify another thread which in turn safly checks for the removed 307 // case) - so we must ensure that we are not inside the notify hook anymore 308 // before returning, as we would otherwise violate the promise not to use 309 // any of the pipes after returning from the removed hook 310 while (atomic_add(&fInsideNotify, 0) != 0) 311 snooze(100); 312 313 gUSBModule->cancel_queued_transfers(fNotifyEndpoint); 314 gUSBModule->cancel_queued_transfers(fReadEndpoint); 315 gUSBModule->cancel_queued_transfers(fWriteEndpoint); 316 317 if (fLinkStateChangeSem >= B_OK) 318 release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE); 319 } 320 321 322 status_t 323 ECMDevice::CompareAndReattach(usb_device device) 324 { 325 const usb_device_descriptor *deviceDescriptor 326 = gUSBModule->get_device_descriptor(device); 327 328 if (deviceDescriptor == NULL) { 329 TRACE_ALWAYS("failed to get device descriptor\n"); 330 return B_ERROR; 331 } 332 333 if (deviceDescriptor->vendor_id != fVendorID 334 && deviceDescriptor->product_id != fProductID) { 335 // this certainly isn't the same device 336 return B_BAD_VALUE; 337 } 338 339 // this might be the same device that was replugged - read the MAC address 340 // (which should be at the same index) to make sure 341 uint8 macBuffer[6]; 342 if (_ReadMACAddress(device, macBuffer) != B_OK 343 || memcmp(macBuffer, fMACAddress, sizeof(macBuffer)) != 0) { 344 // reading the MAC address failed or they are not the same 345 return B_BAD_VALUE; 346 } 347 348 // this is the same device that was replugged - clear the removed state, 349 // re-setup the endpoints and transfers and open the device if it was 350 // previously opened 351 fDevice = device; 352 fRemoved = false; 353 status_t result = _SetupDevice(); 354 if (result != B_OK) { 355 fRemoved = true; 356 return result; 357 } 358 359 // in case notifications do not work we will have a hardcoded connection 360 // need to register that and notify the network stack ourselfs if this is 361 // the case as the open will not result in a corresponding notification 362 bool noNotifications = fHasConnection; 363 364 if (fOpen) { 365 fOpen = false; 366 result = Open(); 367 if (result == B_OK && noNotifications && fLinkStateChangeSem >= B_OK) 368 release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE); 369 } 370 371 return B_OK; 372 } 373 374 375 status_t 376 ECMDevice::_SetupDevice() 377 { 378 const usb_device_descriptor *deviceDescriptor 379 = gUSBModule->get_device_descriptor(fDevice); 380 381 if (deviceDescriptor == NULL) { 382 TRACE_ALWAYS("failed to get device descriptor\n"); 383 return B_ERROR; 384 } 385 386 uint8 controlIndex = 0; 387 uint8 dataIndex = 0; 388 bool foundUnionDescriptor = false; 389 bool foundEthernetDescriptor = false; 390 bool found = false; 391 const usb_configuration_info *config = NULL; 392 for (int i = 0; i < deviceDescriptor->num_configurations && !found; i++) { 393 config = gUSBModule->get_nth_configuration(fDevice, i); 394 if (config == NULL) 395 continue; 396 397 for (size_t j = 0; j < config->interface_count && !found; j++) { 398 const usb_interface_info *interface = config->interface[j].active; 399 usb_interface_descriptor *descriptor = interface->descr; 400 if (descriptor->interface_class != USB_INTERFACE_CLASS_CDC 401 || descriptor->interface_subclass != USB_INTERFACE_SUBCLASS_ECM 402 || interface->generic_count == 0) { 403 continue; 404 } 405 406 // try to find and interpret the union and ethernet functional 407 // descriptors 408 foundUnionDescriptor = foundEthernetDescriptor = false; 409 for (size_t k = 0; k < interface->generic_count; k++) { 410 usb_generic_descriptor *generic = &interface->generic[k]->generic; 411 if (generic->length >= 5 412 && generic->data[0] == FUNCTIONAL_SUBTYPE_UNION) { 413 controlIndex = generic->data[1]; 414 dataIndex = generic->data[2]; 415 foundUnionDescriptor = true; 416 } else if (generic->length >= sizeof(ethernet_functional_descriptor) 417 && generic->data[0] == FUNCTIONAL_SUBTYPE_ETHERNET) { 418 ethernet_functional_descriptor *ethernet 419 = (ethernet_functional_descriptor *)generic->data; 420 fMACAddressIndex = ethernet->mac_address_index; 421 fMaxSegmentSize = ethernet->max_segment_size; 422 foundEthernetDescriptor = true; 423 } 424 425 if (foundUnionDescriptor && foundEthernetDescriptor) { 426 found = true; 427 break; 428 } 429 } 430 } 431 } 432 433 if (!foundUnionDescriptor) { 434 TRACE_ALWAYS("did not find a union descriptor\n"); 435 return B_ERROR; 436 } 437 438 if (!foundEthernetDescriptor) { 439 TRACE_ALWAYS("did not find an ethernet descriptor\n"); 440 return B_ERROR; 441 } 442 443 // set the current configuration 444 gUSBModule->set_configuration(fDevice, config); 445 if (controlIndex >= config->interface_count) { 446 TRACE_ALWAYS("control interface index invalid\n"); 447 return B_ERROR; 448 } 449 450 // check that the indicated control interface fits our needs 451 usb_interface_info *interface = config->interface[controlIndex].active; 452 usb_interface_descriptor *descriptor = interface->descr; 453 if ((descriptor->interface_class != USB_INTERFACE_CLASS_CDC 454 || descriptor->interface_subclass != USB_INTERFACE_SUBCLASS_ECM) 455 || interface->endpoint_count == 0) { 456 TRACE_ALWAYS("control interface invalid\n"); 457 return B_ERROR; 458 } 459 460 fControlInterfaceIndex = controlIndex; 461 fNotifyEndpoint = interface->endpoint[0].handle; 462 fNotifyBufferLength = interface->endpoint[0].descr->max_packet_size; 463 464 if (dataIndex >= config->interface_count) { 465 TRACE_ALWAYS("data interface index invalid\n"); 466 return B_ERROR; 467 } 468 469 // check that the indicated data interface fits our needs 470 if (config->interface[dataIndex].alt_count < 2) { 471 TRACE_ALWAYS("data interface does not provide two alternate interfaces\n"); 472 return B_ERROR; 473 } 474 475 // alternate 0 is the disabled, endpoint-less default interface 476 interface = &config->interface[dataIndex].alt[1]; 477 descriptor = interface->descr; 478 if (descriptor->interface_class != USB_INTERFACE_CLASS_CDC_DATA 479 || interface->endpoint_count < 2) { 480 TRACE_ALWAYS("data interface invalid\n"); 481 return B_ERROR; 482 } 483 484 fDataInterfaceIndex = dataIndex; 485 return B_OK; 486 } 487 488 489 status_t 490 ECMDevice::_ReadMACAddress(usb_device device, uint8 *buffer) 491 { 492 if (fMACAddressIndex == 0) 493 return B_BAD_VALUE; 494 495 size_t actualLength = 0; 496 size_t macStringLength = 26; 497 uint8 macString[macStringLength]; 498 status_t result = gUSBModule->get_descriptor(device, USB_DESCRIPTOR_STRING, 499 fMACAddressIndex, 0, macString, macStringLength, &actualLength); 500 if (result != B_OK) 501 return result; 502 503 if (actualLength != macStringLength) { 504 TRACE_ALWAYS("did not retrieve full mac address\n"); 505 return B_ERROR; 506 } 507 508 char macPart[3]; 509 macPart[2] = 0; 510 for (int32 i = 0; i < 6; i++) { 511 macPart[0] = macString[2 + i * 4 + 0]; 512 macPart[1] = macString[2 + i * 4 + 2]; 513 buffer[i] = strtol(macPart, NULL, 16); 514 } 515 516 TRACE_ALWAYS("read mac address: %02x:%02x:%02x:%02x:%02x:%02x\n", 517 buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]); 518 return B_OK; 519 } 520 521 522 void 523 ECMDevice::_ReadCallback(void *cookie, int32 status, void *data, 524 size_t actualLength) 525 { 526 ECMDevice *device = (ECMDevice *)cookie; 527 device->fActualLengthRead = actualLength; 528 device->fStatusRead = status; 529 release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE); 530 } 531 532 533 void 534 ECMDevice::_WriteCallback(void *cookie, int32 status, void *data, 535 size_t actualLength) 536 { 537 ECMDevice *device = (ECMDevice *)cookie; 538 device->fActualLengthWrite = actualLength; 539 device->fStatusWrite = status; 540 release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE); 541 } 542 543 544 void 545 ECMDevice::_NotifyCallback(void *cookie, int32 status, void *data, 546 size_t actualLength) 547 { 548 ECMDevice *device = (ECMDevice *)cookie; 549 atomic_add(&device->fInsideNotify, 1); 550 if (status == B_CANCELED || device->fRemoved) { 551 atomic_add(&device->fInsideNotify, -1); 552 return; 553 } 554 555 if (status == B_OK && actualLength >= sizeof(cdc_notification)) { 556 bool linkStateChange = false; 557 cdc_notification *notification 558 = (cdc_notification *)device->fNotifyBuffer; 559 560 switch (notification->notification_code) { 561 case CDC_NOTIFY_NETWORK_CONNECTION: 562 TRACE("connection state change to %d\n", notification->value); 563 device->fHasConnection = notification->value > 0; 564 linkStateChange = true; 565 break; 566 567 case CDC_NOTIFY_CONNECTION_SPEED_CHANGE: 568 { 569 if (notification->data_length < sizeof(cdc_connection_speed) 570 || actualLength < sizeof(cdc_notification) 571 + sizeof(cdc_connection_speed)) { 572 TRACE_ALWAYS("not enough data in connection speed change\n"); 573 break; 574 } 575 576 cdc_connection_speed *speed; 577 speed = (cdc_connection_speed *)¬ification->data[0]; 578 device->fUpstreamSpeed = speed->upstream_speed; 579 device->fDownstreamSpeed = speed->downstream_speed; 580 device->fHasConnection = true; 581 TRACE("connection speed change to %ld/%ld\n", 582 speed->downstream_speed, speed->upstream_speed); 583 linkStateChange = true; 584 break; 585 } 586 587 default: 588 TRACE_ALWAYS("unsupported notification 0x%02x\n", 589 notification->notification_code); 590 break; 591 } 592 593 if (linkStateChange && device->fLinkStateChangeSem >= B_OK) 594 release_sem_etc(device->fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE); 595 } 596 597 if (status != B_OK) { 598 TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", status); 599 if (gUSBModule->clear_feature(device->fNotifyEndpoint, 600 USB_FEATURE_ENDPOINT_HALT) != B_OK) 601 TRACE_ALWAYS("failed to clear halt state in notify hook\n"); 602 } 603 604 // schedule next notification buffer 605 gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyBuffer, 606 device->fNotifyBufferLength, _NotifyCallback, device); 607 atomic_add(&device->fInsideNotify, -1); 608 } 609