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