1 /* 2 * ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver. 3 * Copyright (c) 2008, 2011 S.Zharski <imker@gmx.li> 4 * Distributed under the terms of the MIT license. 5 * 6 * Heavily based on code of the 7 * Driver for USB Ethernet Control Model devices 8 * Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch> 9 * Distributed under the terms of the MIT license. 10 * 11 */ 12 13 14 #include "ASIXDevice.h" 15 16 #include <stdio.h> 17 18 #include "ASIXVendorRequests.h" 19 #include "Driver.h" 20 #include "Settings.h" 21 22 23 // frame header used during transfer data 24 struct TRXHeader { 25 uint16 fLength; 26 uint16 fInvertedLength; 27 28 TRXHeader(uint16 length = 0) { SetLength(length); } 29 bool IsValid() { return (fLength ^ fInvertedLength) == 0xffff; } 30 uint16 Length() { return fLength; } 31 // TODO: low-endian convertion? 32 void SetLength(uint16 length) { 33 fLength = length; 34 fInvertedLength = ~fLength; 35 } 36 }; 37 38 39 ASIXDevice::ASIXDevice(usb_device device, DeviceInfo& deviceInfo) 40 : 41 fDevice(device), 42 fStatus(B_ERROR), 43 fOpen(false), 44 fRemoved(false), 45 fHasConnection(false), 46 fNonBlocking(false), 47 fInsideNotify(0), 48 fFrameSize(0), 49 fNotifyEndpoint(0), 50 fReadEndpoint(0), 51 fWriteEndpoint(0), 52 fActualLengthRead(0), 53 fActualLengthWrite(0), 54 fStatusRead(B_OK), 55 fStatusWrite(B_OK), 56 fNotifyReadSem(-1), 57 fNotifyWriteSem(-1), 58 fNotifyBuffer(NULL), 59 fNotifyBufferLength(0), 60 fLinkStateChangeSem(-1), 61 fUseTRXHeader(false), 62 fReadNodeIDRequest(kInvalidRequest) 63 { 64 fDeviceInfo = deviceInfo; 65 66 fIPG[0] = 0x15; 67 fIPG[1] = 0x0c; 68 fIPG[2] = 0x12; 69 70 memset(&fMACAddress, 0, sizeof(fMACAddress)); 71 72 fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read"); 73 if (fNotifyReadSem < B_OK) { 74 TRACE_ALWAYS("Error of creating read notify semaphore:%#010x\n", 75 fNotifyReadSem); 76 return; 77 } 78 79 fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write"); 80 if (fNotifyWriteSem < B_OK) { 81 TRACE_ALWAYS("Error of creating write notify semaphore:%#010x\n", 82 fNotifyWriteSem); 83 return; 84 } 85 86 if (_SetupEndpoints() != B_OK) { 87 return; 88 } 89 90 // must be set in derived class constructor 91 // fStatus = B_OK; 92 } 93 94 95 ASIXDevice::~ASIXDevice() 96 { 97 if (fNotifyReadSem >= B_OK) 98 delete_sem(fNotifyReadSem); 99 if (fNotifyWriteSem >= B_OK) 100 delete_sem(fNotifyWriteSem); 101 102 if (!fRemoved) // ??? 103 gUSBModule->cancel_queued_transfers(fNotifyEndpoint); 104 105 if (fNotifyBuffer) 106 free(fNotifyBuffer); 107 } 108 109 110 status_t 111 ASIXDevice::Open(uint32 flags) 112 { 113 if (fOpen) 114 return B_BUSY; 115 if (fRemoved) 116 return B_ERROR; 117 118 status_t result = StartDevice(); 119 if (result != B_OK) { 120 return result; 121 } 122 123 // setup state notifications 124 result = gUSBModule->queue_interrupt(fNotifyEndpoint, fNotifyBuffer, 125 fNotifyBufferLength, _NotifyCallback, this); 126 if (result != B_OK) { 127 TRACE_ALWAYS("Error of requesting notify interrupt:%#010x\n", result); 128 return result; 129 } 130 131 fNonBlocking = (flags & O_NONBLOCK) == O_NONBLOCK; 132 fOpen = true; 133 return result; 134 } 135 136 137 status_t 138 ASIXDevice::Close() 139 { 140 if (fRemoved) { 141 fOpen = false; 142 return B_OK; 143 } 144 145 // wait until possible notification handling finished... 146 while (atomic_add(&fInsideNotify, 0) != 0) 147 snooze(100); 148 gUSBModule->cancel_queued_transfers(fNotifyEndpoint); 149 gUSBModule->cancel_queued_transfers(fReadEndpoint); 150 gUSBModule->cancel_queued_transfers(fWriteEndpoint); 151 152 fOpen = false; 153 154 return StopDevice(); 155 } 156 157 158 status_t 159 ASIXDevice::Free() 160 { 161 return B_OK; 162 } 163 164 165 status_t 166 ASIXDevice::Read(uint8 *buffer, size_t *numBytes) 167 { 168 size_t numBytesToRead = *numBytes; 169 *numBytes = 0; 170 171 if (fRemoved) { 172 TRACE_ALWAYS("Error of receiving %d bytes from removed device.\n", 173 numBytesToRead); 174 return B_DEVICE_NOT_FOUND; 175 } 176 177 TRACE_FLOW("Request %d bytes.\n", numBytesToRead); 178 179 TRXHeader header; 180 iovec rxData[] = { 181 { &header, sizeof(TRXHeader) }, 182 { buffer, numBytesToRead } 183 }; 184 185 size_t startIndex = fUseTRXHeader ? 0 : 1 ; 186 size_t chunkCount = fUseTRXHeader ? 2 : 1 ; 187 188 status_t result = gUSBModule->queue_bulk_v(fReadEndpoint, 189 &rxData[startIndex], chunkCount, _ReadCallback, this); 190 191 if (result != B_OK) { 192 TRACE_ALWAYS("Error of queue_bulk_v request:%#010x\n", result); 193 return result; 194 } 195 196 uint32 flags = B_CAN_INTERRUPT | (fNonBlocking ? B_TIMEOUT : 0); 197 result = acquire_sem_etc(fNotifyReadSem, 1, flags, 0); 198 if (result < B_OK) { 199 TRACE_ALWAYS("Error of acquiring notify semaphore:%#010x.\n", result); 200 return result; 201 } 202 203 if (fStatusRead != B_OK && fStatusRead != B_CANCELED && !fRemoved) { 204 TRACE_ALWAYS("Device status error:%#010x\n", fStatusRead); 205 result = gUSBModule->clear_feature(fReadEndpoint, 206 USB_FEATURE_ENDPOINT_HALT); 207 if (result != B_OK) { 208 TRACE_ALWAYS("Error during clearing of HALT state:%#010x.\n", 209 result); 210 return result; 211 } 212 } 213 214 if (fUseTRXHeader) { 215 if (fActualLengthRead < sizeof(TRXHeader)) { 216 TRACE_ALWAYS("Error: no place for TRXHeader:only %d of %d bytes.\n", 217 fActualLengthRead, sizeof(TRXHeader)); 218 return B_ERROR; // TODO: ??? 219 } 220 221 if (!header.IsValid()) { 222 TRACE_ALWAYS("Error:TRX Header is invalid: len:%#04x; ilen:%#04x\n", 223 header.fLength, header.fInvertedLength); 224 return B_ERROR; // TODO: ??? 225 } 226 227 *numBytes = header.Length(); 228 229 // the device pushes packets 16bit aligned 230 if (fActualLengthRead - sizeof(TRXHeader) > header.Length() 231 + (header.Length() % 2u)) { 232 TRACE_ALWAYS("MISMATCH of the frame length: hdr %d; received:%d\n", 233 header.Length(), fActualLengthRead - sizeof(TRXHeader)); 234 } else if (fActualLengthRead - sizeof(TRXHeader) < header.Length()) { 235 TRACE_ALWAYS("Error: received too little data: hdr %d; received:%d\n", 236 header.Length(), fActualLengthRead - sizeof(TRXHeader)); 237 } 238 239 } else { 240 241 *numBytes = fActualLengthRead; 242 } 243 244 TRACE_FLOW("Read %d bytes.\n", *numBytes); 245 return B_OK; 246 } 247 248 249 status_t 250 ASIXDevice::Write(const uint8 *buffer, size_t *numBytes) 251 { 252 size_t numBytesToWrite = *numBytes; 253 *numBytes = 0; 254 255 if (fRemoved) { 256 TRACE_ALWAYS("Error of writing %d bytes to removed device.\n", 257 numBytesToWrite); 258 return B_DEVICE_NOT_FOUND; 259 } 260 261 TRACE_FLOW("Write %d bytes.\n", numBytesToWrite); 262 263 TRXHeader header(numBytesToWrite); 264 iovec txData[] = { 265 { &header, sizeof(TRXHeader) }, 266 { (uint8*)buffer, numBytesToWrite } 267 }; 268 269 size_t startIndex = fUseTRXHeader ? 0 : 1 ; 270 size_t chunkCount = fUseTRXHeader ? 2 : 1 ; 271 272 status_t result = gUSBModule->queue_bulk_v(fWriteEndpoint, 273 &txData[startIndex], chunkCount, _WriteCallback, this); 274 275 if (result != B_OK) { 276 TRACE_ALWAYS("Error of queue_bulk_v request:%#010x\n", result); 277 return result; 278 } 279 280 result = acquire_sem_etc(fNotifyWriteSem, 1, B_CAN_INTERRUPT, 0); 281 282 if (result < B_OK) { 283 TRACE_ALWAYS("Error of acquiring notify semaphore:%#010x.\n", result); 284 return result; 285 } 286 287 if (fStatusWrite != B_OK && fStatusWrite != B_CANCELED && !fRemoved) { 288 TRACE_ALWAYS("Device status error:%#010x\n", fStatusWrite); 289 result = gUSBModule->clear_feature(fWriteEndpoint, 290 USB_FEATURE_ENDPOINT_HALT); 291 if (result != B_OK) { 292 TRACE_ALWAYS("Error during clearing of HALT state:%#010x\n", result); 293 return result; 294 } 295 } 296 297 if (fUseTRXHeader) { 298 *numBytes = fActualLengthWrite - sizeof(TRXHeader); 299 } else { 300 *numBytes = fActualLengthWrite; 301 } 302 303 TRACE_FLOW("Written %d bytes.\n", *numBytes); 304 return B_OK; 305 } 306 307 308 status_t 309 ASIXDevice::Control(uint32 op, void *buffer, size_t length) 310 { 311 switch (op) { 312 case ETHER_INIT: 313 return B_OK; 314 315 case ETHER_GETADDR: 316 memcpy(buffer, &fMACAddress, sizeof(fMACAddress)); 317 return B_OK; 318 319 case ETHER_GETFRAMESIZE: 320 *(uint32 *)buffer = fFrameSize; 321 return B_OK; 322 323 case ETHER_NONBLOCK: 324 TRACE("ETHER_NONBLOCK\n"); 325 fNonBlocking = *((uint8*)buffer); 326 return B_OK; 327 328 case ETHER_SETPROMISC: 329 TRACE("ETHER_SETPROMISC\n"); 330 return SetPromiscuousMode(*((uint8*)buffer)); 331 332 case ETHER_ADDMULTI: 333 TRACE("ETHER_ADDMULTI\n"); 334 return ModifyMulticastTable(true, (ether_address_t*)buffer); 335 336 case ETHER_REMMULTI: 337 TRACE("ETHER_REMMULTI\n"); 338 return ModifyMulticastTable(false, (ether_address_t*)buffer); 339 340 case ETHER_SET_LINK_STATE_SEM: 341 fLinkStateChangeSem = *(sem_id *)buffer; 342 return B_OK; 343 344 case ETHER_GET_LINK_STATE: 345 return GetLinkState((ether_link_state *)buffer); 346 347 default: 348 TRACE_ALWAYS("Unhandled IOCTL catched: %#010x\n", op); 349 } 350 351 return B_DEV_INVALID_IOCTL; 352 } 353 354 355 void 356 ASIXDevice::Removed() 357 { 358 fRemoved = true; 359 fHasConnection = false; 360 361 // the notify hook is different from the read and write hooks as it does 362 // itself schedule traffic (while the other hooks only release a semaphore 363 // to notify another thread which in turn safly checks for the removed 364 // case) - so we must ensure that we are not inside the notify hook anymore 365 // before returning, as we would otherwise violate the promise not to use 366 // any of the pipes after returning from the removed hook 367 while (atomic_add(&fInsideNotify, 0) != 0) 368 snooze(100); 369 370 gUSBModule->cancel_queued_transfers(fNotifyEndpoint); 371 gUSBModule->cancel_queued_transfers(fReadEndpoint); 372 gUSBModule->cancel_queued_transfers(fWriteEndpoint); 373 374 if (fLinkStateChangeSem >= B_OK) 375 release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE); 376 } 377 378 379 status_t 380 ASIXDevice::SetupDevice(bool deviceReplugged) 381 { 382 ether_address address; 383 status_t result = ReadMACAddress(&address); 384 if (result != B_OK) { 385 TRACE_ALWAYS("Error of reading MAC address:%#010x\n", result); 386 return result; 387 } 388 389 TRACE("MAC address is:%02x:%02x:%02x:%02x:%02x:%02x\n", 390 address.ebyte[0], address.ebyte[1], address.ebyte[2], 391 address.ebyte[3], address.ebyte[4], address.ebyte[5]); 392 393 if (deviceReplugged) { 394 // this might be the same device that was replugged - read the MAC 395 // address (which should be at the same index) to make sure 396 if (memcmp(&address, &fMACAddress, sizeof(address)) != 0) { 397 TRACE_ALWAYS("Cannot replace device with MAC address:" 398 "%02x:%02x:%02x:%02x:%02x:%02x\n", fMACAddress.ebyte[0], 399 fMACAddress.ebyte[1], fMACAddress.ebyte[2], 400 fMACAddress.ebyte[3], fMACAddress.ebyte[4], 401 fMACAddress.ebyte[5]); 402 return B_BAD_VALUE; // is not the same 403 } 404 } else 405 fMACAddress = address; 406 407 return B_OK; 408 } 409 410 411 status_t 412 ASIXDevice::CompareAndReattach(usb_device device) 413 { 414 const usb_device_descriptor *deviceDescriptor 415 = gUSBModule->get_device_descriptor(device); 416 417 if (deviceDescriptor == NULL) { 418 TRACE_ALWAYS("Error of getting USB device descriptor.\n"); 419 return B_ERROR; 420 } 421 422 if (deviceDescriptor->vendor_id != fDeviceInfo.VendorId() 423 && deviceDescriptor->product_id != fDeviceInfo.ProductId()) { 424 // this certainly isn't the same device 425 return B_BAD_VALUE; 426 } 427 428 // this is the same device that was replugged - clear the removed state, 429 // re-setup the endpoints and transfers and open the device if it was 430 // previously opened 431 fDevice = device; 432 fRemoved = false; 433 status_t result = _SetupEndpoints(); 434 if (result != B_OK) { 435 fRemoved = true; 436 return result; 437 } 438 439 // we need to setup hardware on device replug 440 result = SetupDevice(true); 441 if (result != B_OK) { 442 return result; 443 } 444 445 if (fOpen) { 446 fOpen = false; 447 result = Open(fNonBlocking ? O_NONBLOCK : 0); 448 } 449 450 return result; 451 } 452 453 454 status_t 455 ASIXDevice::_SetupEndpoints() 456 { 457 const usb_configuration_info *config 458 = gUSBModule->get_nth_configuration(fDevice, 0); 459 460 if (config == NULL) { 461 TRACE_ALWAYS("Error of getting USB device configuration.\n"); 462 return B_ERROR; 463 } 464 465 if (config->interface_count <= 0) { 466 TRACE_ALWAYS("Error:no interfaces found in USB device configuration\n"); 467 return B_ERROR; 468 } 469 470 usb_interface_info *interface = config->interface[0].active; 471 if (interface == 0) { 472 TRACE_ALWAYS("Error:invalid active interface in " 473 "USB device configuration\n"); 474 return B_ERROR; 475 } 476 477 int notifyEndpoint = -1; 478 int readEndpoint = -1; 479 int writeEndpoint = -1; 480 481 for (size_t ep = 0; ep < interface->endpoint_count; ep++) { 482 usb_endpoint_descriptor *epd = interface->endpoint[ep].descr; 483 if ((epd->attributes & USB_ENDPOINT_ATTR_MASK) 484 == USB_ENDPOINT_ATTR_INTERRUPT) { 485 notifyEndpoint = ep; 486 continue; 487 } 488 489 if ((epd->attributes & USB_ENDPOINT_ATTR_MASK) 490 != USB_ENDPOINT_ATTR_BULK) { 491 TRACE_ALWAYS("Error: USB endpoint type %#04x is unknown.\n", 492 epd->attributes); 493 continue; 494 } 495 496 if ((epd->endpoint_address & USB_ENDPOINT_ADDR_DIR_MASK) 497 == USB_ENDPOINT_ADDR_DIR_IN) { 498 readEndpoint = ep; 499 continue; 500 } 501 502 if ((epd->endpoint_address & USB_ENDPOINT_ADDR_DIR_MASK) 503 == USB_ENDPOINT_ADDR_DIR_OUT) { 504 writeEndpoint = ep; 505 continue; 506 } 507 } 508 509 if (notifyEndpoint == -1 || readEndpoint == -1 || writeEndpoint == -1) { 510 TRACE_ALWAYS("Error: not all USB endpoints were found: " 511 "notify:%d; read:%d; write:%d\n", notifyEndpoint, readEndpoint, 512 writeEndpoint); 513 return B_ERROR; 514 } 515 516 gUSBModule->set_configuration(fDevice, config); 517 518 fNotifyEndpoint = interface->endpoint[notifyEndpoint].handle; 519 fReadEndpoint = interface->endpoint[readEndpoint].handle; 520 fWriteEndpoint = interface->endpoint[writeEndpoint].handle; 521 522 return B_OK; 523 } 524 525 526 status_t 527 ASIXDevice::ReadMACAddress(ether_address_t *address) 528 { 529 size_t actual_length = 0; 530 status_t result = gUSBModule->send_request(fDevice, 531 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, fReadNodeIDRequest, 532 0, 0, sizeof(ether_address), address, &actual_length); 533 534 if (result != B_OK) { 535 TRACE_ALWAYS("Error of reading MAC address:%#010x\n", result); 536 return result; 537 } 538 539 if (actual_length != sizeof(ether_address)) { 540 TRACE_ALWAYS("Mismatch of NODE ID data size: %d instead of %d bytes\n", 541 actual_length, sizeof(ether_address)); 542 return B_ERROR; 543 } 544 545 return B_OK; 546 } 547 548 549 status_t 550 ASIXDevice::ReadRXControlRegister(uint16 *rxcontrol) 551 { 552 size_t actual_length = 0; 553 *rxcontrol = 0; 554 555 status_t result = gUSBModule->send_request(fDevice, 556 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_RX_CONTROL, 557 0, 0, sizeof(*rxcontrol), rxcontrol, &actual_length); 558 559 if (sizeof(*rxcontrol) != actual_length) { 560 TRACE_ALWAYS("Mismatch during reading RX control register." 561 "Read %d bytes instead of %d.\n", actual_length, 562 sizeof(*rxcontrol)); 563 } 564 565 return result; 566 } 567 568 569 status_t 570 ASIXDevice::WriteRXControlRegister(uint16 rxcontrol) 571 { 572 status_t result = gUSBModule->send_request(fDevice, 573 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_RX_CONTROL, 574 rxcontrol, 0, 0, 0, 0); 575 return result; 576 } 577 578 579 status_t 580 ASIXDevice::StopDevice() 581 { 582 status_t result = WriteRXControlRegister(0); 583 584 if (result != B_OK) 585 TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 0, result); 586 587 TRACE_RET(result); 588 return result; 589 } 590 591 592 status_t 593 ASIXDevice::SetPromiscuousMode(bool on) 594 { 595 uint16 rxcontrol = 0; 596 597 status_t result = ReadRXControlRegister(&rxcontrol); 598 if (result != B_OK) { 599 TRACE_ALWAYS("Error of reading RX Control:%#010x\n", result); 600 return result; 601 } 602 603 if (on) 604 rxcontrol |= RXCTL_PROMISCUOUS; 605 else 606 rxcontrol &= ~RXCTL_PROMISCUOUS; 607 608 result = WriteRXControlRegister(rxcontrol); 609 610 if (result != B_OK ) { 611 TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 612 rxcontrol, result); 613 } 614 615 TRACE_RET(result); 616 return result; 617 } 618 619 620 uint32 621 ASIXDevice::EthernetCRC32(const uint8* buffer, size_t length) 622 { 623 uint32 result = 0xffffffff; 624 for (size_t i = 0; i < length; i++) { 625 uint8 data = *buffer++; 626 for (int bit = 0; bit < 8; bit++, data >>= 1) { 627 uint32 carry = ((result & 0x80000000) ? 1 : 0) ^ (data & 0x01); 628 result <<= 1; 629 if (carry != 0) 630 result = (result ^ 0x04c11db6) | carry; 631 } 632 } 633 return result; 634 } 635 636 637 status_t 638 ASIXDevice::ModifyMulticastTable(bool join, ether_address_t* group) 639 { 640 char groupName[6 * 3 + 1] = { 0 }; 641 sprintf(groupName, "%02x:%02x:%02x:%02x:%02x:%02x", 642 group->ebyte[0], group->ebyte[1], group->ebyte[2], 643 group->ebyte[3], group->ebyte[4], group->ebyte[5]); 644 TRACE("%s multicast group %s\n", join ? "Joining" : "Leaving", groupName); 645 646 uint32 hash = EthernetCRC32(group->ebyte, 6); 647 bool isInTable = fMulticastHashes.Find(hash) != fMulticastHashes.End(); 648 649 if (isInTable && join) 650 return B_OK; // already listed - nothing to do 651 652 if (!isInTable && !join) { 653 TRACE_ALWAYS("Cannot leave unlisted multicast group %s!\n", groupName); 654 return B_ERROR; 655 } 656 657 const size_t hashLength = 8; 658 uint8 hashTable[hashLength] = { 0 }; 659 660 if (join) 661 fMulticastHashes.PushBack(hash); 662 else 663 fMulticastHashes.Remove(hash); 664 665 for (int32 i = 0; i < fMulticastHashes.Count(); i++) { 666 uint32 hash = fMulticastHashes[i] >> 26; 667 hashTable[hash / 8] |= 1 << (hash % 8); 668 } 669 670 uint16 rxcontrol = 0; 671 672 status_t result = ReadRXControlRegister(&rxcontrol); 673 if (result != B_OK) { 674 TRACE_ALWAYS("Error of reading RX Control:%#010x\n", result); 675 return result; 676 } 677 678 if (fMulticastHashes.Count() > 0) 679 rxcontrol |= RXCTL_MULTICAST; 680 else 681 rxcontrol &= ~RXCTL_MULTICAST; 682 683 // write multicast hash table 684 size_t actualLength = 0; 685 result = gUSBModule->send_request(fDevice, 686 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_MF_ARRAY, 687 0, 0, hashLength, hashTable, &actualLength); 688 689 if (result != B_OK) { 690 TRACE_ALWAYS("Error writing hash table in MAR: %#010x.\n", result); 691 return result; 692 } 693 694 if (actualLength != hashLength) 695 TRACE_ALWAYS("Incomplete writing of hash table: %d bytes of %d\n", 696 actualLength, hashLength); 697 698 result = WriteRXControlRegister(rxcontrol); 699 if (result != B_OK) 700 TRACE_ALWAYS("Error writing %#02X to RXC:%#010x.\n", rxcontrol, result); 701 702 return result; 703 } 704 705 706 void 707 ASIXDevice::_ReadCallback(void *cookie, int32 status, void *data, 708 size_t actualLength) 709 { 710 TRACE_FLOW("ReadCB: %d bytes; status:%#010x\n", actualLength, status); 711 ASIXDevice *device = (ASIXDevice *)cookie; 712 device->fActualLengthRead = actualLength; 713 device->fStatusRead = status; 714 release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE); 715 } 716 717 718 void 719 ASIXDevice::_WriteCallback(void *cookie, int32 status, void *data, 720 size_t actualLength) 721 { 722 TRACE_FLOW("WriteCB: %d bytes; status:%#010x\n", actualLength, status); 723 ASIXDevice *device = (ASIXDevice *)cookie; 724 device->fActualLengthWrite = actualLength; 725 device->fStatusWrite = status; 726 release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE); 727 } 728 729 730 void 731 ASIXDevice::_NotifyCallback(void *cookie, int32 status, void *data, 732 size_t actualLength) 733 { 734 ASIXDevice *device = (ASIXDevice *)cookie; 735 atomic_add(&device->fInsideNotify, 1); 736 if (status == B_CANCELED || device->fRemoved) { 737 atomic_add(&device->fInsideNotify, -1); 738 return; 739 } 740 741 if (status != B_OK) { 742 TRACE_ALWAYS("Device status error:%#010x\n", status); 743 status_t result = gUSBModule->clear_feature(device->fNotifyEndpoint, 744 USB_FEATURE_ENDPOINT_HALT); 745 if (result != B_OK) 746 TRACE_ALWAYS("Error during clearing of HALT state:%#010x.\n", 747 result); 748 } 749 750 // parse data in overriden class 751 device->OnNotify(actualLength); 752 753 // schedule next notification buffer 754 gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyBuffer, 755 device->fNotifyBufferLength, _NotifyCallback, device); 756 atomic_add(&device->fInsideNotify, -1); 757 } 758