1 /* 2 * Copyright (c) 2007-2008 by Michael Lotz 3 * Heavily based on the original usb_serial driver which is: 4 * 5 * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li> 6 * Distributed under the terms of the MIT License. 7 * 8 * Authors: 9 * Alexander von Gluck IV, kallisti5@unixzen.com 10 */ 11 12 13 #include <new> 14 15 #include "SerialDevice.h" 16 #include "USB3.h" 17 18 #include "ACM.h" 19 #include "FTDI.h" 20 #include "KLSI.h" 21 #include "Option.h" 22 #include "Prolific.h" 23 #include "Silicon.h" 24 25 #include <sys/ioctl.h> 26 27 28 SerialDevice::SerialDevice(usb_device device, uint16 vendorID, 29 uint16 productID, const char *description) 30 : fDevice(device), 31 fVendorID(vendorID), 32 fProductID(productID), 33 fDescription(description), 34 fDeviceOpen(false), 35 fDeviceRemoved(false), 36 fControlPipe(0), 37 fReadPipe(0), 38 fWritePipe(0), 39 fBufferArea(-1), 40 fReadBuffer(NULL), 41 fReadBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)), 42 fOutputBuffer(NULL), 43 fOutputBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)), 44 fWriteBuffer(NULL), 45 fWriteBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)), 46 fInterruptBuffer(NULL), 47 fInterruptBufferSize(16), 48 fDoneRead(-1), 49 fDoneWrite(-1), 50 fControlOut(0), 51 fInputStopped(false), 52 fMasterTTY(NULL), 53 fSlaveTTY(NULL), 54 fSystemTTYCookie(NULL), 55 fDeviceTTYCookie(NULL), 56 fInputThread(-1), 57 fStopThreads(false) 58 { 59 memset(&fTTYConfig, 0, sizeof(termios)); 60 fTTYConfig.c_cflag = B9600 | CS8 | CREAD; 61 } 62 63 64 SerialDevice::~SerialDevice() 65 { 66 Removed(); 67 68 if (fDoneRead >= 0) 69 delete_sem(fDoneRead); 70 if (fDoneWrite >= 0) 71 delete_sem(fDoneWrite); 72 73 if (fBufferArea >= 0) 74 delete_area(fBufferArea); 75 } 76 77 78 status_t 79 SerialDevice::Init() 80 { 81 fDoneRead = create_sem(0, "usb_serial:done_read"); 82 if (fDoneRead < 0) 83 return fDoneRead; 84 85 fDoneWrite = create_sem(0, "usb_serial:done_write"); 86 if (fDoneWrite < 0) 87 return fDoneWrite; 88 89 size_t totalBuffers = fReadBufferSize + fOutputBufferSize + fWriteBufferSize 90 + fInterruptBufferSize; 91 fBufferArea = create_area("usb_serial:buffers_area", (void **)&fReadBuffer, 92 B_ANY_KERNEL_ADDRESS, ROUNDUP(totalBuffers, B_PAGE_SIZE), B_CONTIGUOUS, 93 B_READ_AREA | B_WRITE_AREA); 94 if (fBufferArea < 0) 95 return fBufferArea; 96 97 fOutputBuffer = fReadBuffer + fReadBufferSize; 98 fWriteBuffer = fOutputBuffer + fOutputBufferSize; 99 fInterruptBuffer = fWriteBuffer + fWriteBufferSize; 100 return B_OK; 101 } 102 103 104 void 105 SerialDevice::SetControlPipe(usb_pipe handle) 106 { 107 fControlPipe = handle; 108 } 109 110 111 void 112 SerialDevice::SetReadPipe(usb_pipe handle) 113 { 114 fReadPipe = handle; 115 } 116 117 118 void 119 SerialDevice::SetWritePipe(usb_pipe handle) 120 { 121 fWritePipe = handle; 122 } 123 124 125 inline int32 126 baud_index_to_speed(int index) 127 { 128 switch (index) { 129 case B0: return 0; 130 case B50: return 50; 131 case B75: return 75; 132 case B110: return 110; 133 case B134: return 134; 134 case B150: return 150; 135 case B200: return 200; 136 case B300: return 300; 137 case B600: return 600; 138 case B1200: return 1200; 139 case B1800: return 1800; 140 case B2400: return 2400; 141 case B4800: return 4800; 142 case B9600: return 9600; 143 case B19200: return 19200; 144 case B31250: return 31250; 145 case B38400: return 38400; 146 case B57600: return 57600; 147 case B115200: return 115200; 148 case B230400: return 230400; 149 } 150 151 TRACE_ALWAYS("invalid baud index %d\n", index); 152 return -1; 153 } 154 155 156 void 157 SerialDevice::SetModes(struct termios *tios) 158 { 159 TRACE_FUNCRES(trace_termios, tios); 160 161 uint8 baud = tios->c_cflag & CBAUD; 162 int32 speed = baud_index_to_speed(baud); 163 if (speed < 0) { 164 baud = CBAUD; 165 speed = tios->c_ospeed; 166 } 167 168 // update our master config in full 169 memcpy(&fTTYConfig, tios, sizeof(termios)); 170 fTTYConfig.c_cflag &= ~CBAUD; 171 fTTYConfig.c_cflag |= baud; 172 173 // only apply the relevant parts to the device side 174 termios config; 175 memset(&config, 0, sizeof(termios)); 176 config.c_cflag = tios->c_cflag; 177 config.c_cflag &= ~CBAUD; 178 config.c_cflag |= baud; 179 180 // update the termios of the device side 181 gTTYModule->tty_control(fDeviceTTYCookie, TCSETA, &config, sizeof(termios)); 182 183 usb_cdc_line_coding lineCoding; 184 lineCoding.speed = speed; 185 lineCoding.stopbits = (tios->c_cflag & CSTOPB) 186 ? USB_CDC_LINE_CODING_2_STOPBITS : USB_CDC_LINE_CODING_1_STOPBIT; 187 188 if (tios->c_cflag & PARENB) { 189 lineCoding.parity = USB_CDC_LINE_CODING_EVEN_PARITY; 190 if (tios->c_cflag & PARODD) 191 lineCoding.parity = USB_CDC_LINE_CODING_ODD_PARITY; 192 } else 193 lineCoding.parity = USB_CDC_LINE_CODING_NO_PARITY; 194 195 lineCoding.databits = (tios->c_cflag & CS8) ? 8 : 7; 196 197 if (memcmp(&lineCoding, &fLineCoding, sizeof(usb_cdc_line_coding)) != 0) { 198 fLineCoding.speed = lineCoding.speed; 199 fLineCoding.stopbits = lineCoding.stopbits; 200 fLineCoding.databits = lineCoding.databits; 201 fLineCoding.parity = lineCoding.parity; 202 TRACE("send to modem: speed %d sb: 0x%08x db: 0x%08x parity: 0x%08x\n", 203 fLineCoding.speed, fLineCoding.stopbits, fLineCoding.databits, 204 fLineCoding.parity); 205 SetLineCoding(&fLineCoding); 206 } 207 } 208 209 210 bool 211 SerialDevice::Service(struct tty *tty, uint32 op, void *buffer, size_t length) 212 { 213 if (tty != fMasterTTY) 214 return false; 215 216 switch (op) { 217 case TTYENABLE: 218 { 219 bool enable = *(bool *)buffer; 220 TRACE("TTYENABLE: %sable\n", enable ? "en" : "dis"); 221 222 gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD, enable); 223 gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS, enable); 224 225 fControlOut = enable ? USB_CDC_CONTROL_SIGNAL_STATE_DTR 226 | USB_CDC_CONTROL_SIGNAL_STATE_RTS : 0; 227 SetControlLineState(fControlOut); 228 return true; 229 } 230 231 case TTYISTOP: 232 fInputStopped = *(bool *)buffer; 233 TRACE("TTYISTOP: %sstopped\n", fInputStopped ? "" : "not "); 234 gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS, 235 !fInputStopped); 236 return true; 237 238 case TTYGETSIGNALS: 239 TRACE("TTYGETSIGNALS\n"); 240 gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD, 241 (fControlOut & (USB_CDC_CONTROL_SIGNAL_STATE_DTR 242 | USB_CDC_CONTROL_SIGNAL_STATE_RTS)) != 0); 243 gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS, 244 !fInputStopped); 245 gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDSR, false); 246 gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWRI, false); 247 return true; 248 249 case TTYSETMODES: 250 TRACE("TTYSETMODES\n"); 251 SetModes((struct termios *)buffer); 252 return true; 253 254 case TTYSETDTR: 255 case TTYSETRTS: 256 { 257 bool set = *(bool *)buffer; 258 uint8 bit = op == TTYSETDTR ? USB_CDC_CONTROL_SIGNAL_STATE_DTR 259 : USB_CDC_CONTROL_SIGNAL_STATE_RTS; 260 if (set) 261 fControlOut |= bit; 262 else 263 fControlOut &= ~bit; 264 265 SetControlLineState(fControlOut); 266 return true; 267 } 268 269 case TTYOSTART: 270 case TTYOSYNC: 271 case TTYSETBREAK: 272 TRACE("TTY other\n"); 273 return true; 274 } 275 276 return false; 277 } 278 279 280 status_t 281 SerialDevice::Open(uint32 flags) 282 { 283 if (fDeviceOpen) 284 return B_BUSY; 285 286 if (fDeviceRemoved) 287 return B_DEV_NOT_READY; 288 289 fMasterTTY = gTTYModule->tty_create(usb_serial_service, true); 290 if (fMasterTTY == NULL) { 291 TRACE_ALWAYS("open: failed to init master tty\n"); 292 return B_NO_MEMORY; 293 } 294 295 fSlaveTTY = gTTYModule->tty_create(usb_serial_service, false); 296 if (fSlaveTTY == NULL) { 297 TRACE_ALWAYS("open: failed to init slave tty\n"); 298 gTTYModule->tty_destroy(fMasterTTY); 299 return B_NO_MEMORY; 300 } 301 302 fSystemTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY, 303 O_RDWR); 304 if (fSystemTTYCookie == NULL) { 305 TRACE_ALWAYS("open: failed to init system tty cookie\n"); 306 gTTYModule->tty_destroy(fMasterTTY); 307 gTTYModule->tty_destroy(fSlaveTTY); 308 return B_NO_MEMORY; 309 } 310 311 fDeviceTTYCookie = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY, 312 O_RDWR); 313 if (fDeviceTTYCookie == NULL) { 314 TRACE_ALWAYS("open: failed to init device tty cookie\n"); 315 gTTYModule->tty_destroy_cookie(fSystemTTYCookie); 316 gTTYModule->tty_destroy(fMasterTTY); 317 gTTYModule->tty_destroy(fSlaveTTY); 318 return B_NO_MEMORY; 319 } 320 321 ResetDevice(); 322 323 fStopThreads = false; 324 325 fInputThread = spawn_kernel_thread(_InputThread, 326 "usb_serial input thread", B_NORMAL_PRIORITY, this); 327 if (fInputThread < 0) { 328 TRACE_ALWAYS("open: failed to spawn input thread\n"); 329 return fInputThread; 330 } 331 332 resume_thread(fInputThread); 333 334 fControlOut = USB_CDC_CONTROL_SIGNAL_STATE_DTR 335 | USB_CDC_CONTROL_SIGNAL_STATE_RTS; 336 SetControlLineState(fControlOut); 337 338 status_t status = gUSBModule->queue_interrupt(fControlPipe, 339 fInterruptBuffer, fInterruptBufferSize, _InterruptCallbackFunction, 340 this); 341 if (status < B_OK) 342 TRACE_ALWAYS("failed to queue initial interrupt\n"); 343 344 // set our config (will propagate to the slave config as well in SetModes() 345 gTTYModule->tty_control(fSystemTTYCookie, TCSETA, &fTTYConfig, 346 sizeof(termios)); 347 348 fDeviceOpen = true; 349 return B_OK; 350 } 351 352 353 status_t 354 SerialDevice::Read(char *buffer, size_t *numBytes) 355 { 356 if (fDeviceRemoved) { 357 *numBytes = 0; 358 return B_DEV_NOT_READY; 359 } 360 361 return gTTYModule->tty_read(fSystemTTYCookie, buffer, numBytes); 362 } 363 364 365 status_t 366 SerialDevice::Write(const char *buffer, size_t *numBytes) 367 { 368 if (fDeviceRemoved) { 369 *numBytes = 0; 370 return B_DEV_NOT_READY; 371 } 372 373 size_t bytesLeft = *numBytes; 374 *numBytes = 0; 375 376 while (bytesLeft > 0) { 377 size_t length = MIN(bytesLeft, 256); 378 // TODO: This is an ugly hack; We use a small buffer size so that 379 // we don't overrun the tty line buffer and cause it to block. While 380 // that isn't a problem, we shouldn't just hardcode the value here. 381 382 status_t result = gTTYModule->tty_write(fSystemTTYCookie, buffer, 383 &length); 384 if (result != B_OK) { 385 TRACE_ALWAYS("failed to write to tty: %s\n", strerror(result)); 386 return result; 387 } 388 389 buffer += length; 390 *numBytes += length; 391 bytesLeft -= length; 392 393 while (true) { 394 // Write to the device as long as there's anything in the tty buffer 395 int readable = 0; 396 gTTYModule->tty_control(fDeviceTTYCookie, FIONREAD, &readable, 397 sizeof(readable)); 398 if (readable == 0) 399 break; 400 401 result = _WriteToDevice(); 402 if (result != B_OK) { 403 TRACE_ALWAYS("failed to write to device: %s\n", 404 strerror(result)); 405 return result; 406 } 407 } 408 } 409 410 if (*numBytes > 0) 411 return B_OK; 412 413 return B_ERROR; 414 } 415 416 417 status_t 418 SerialDevice::Control(uint32 op, void *arg, size_t length) 419 { 420 if (fDeviceRemoved) 421 return B_DEV_NOT_READY; 422 423 return gTTYModule->tty_control(fSystemTTYCookie, op, arg, length); 424 } 425 426 427 status_t 428 SerialDevice::Select(uint8 event, uint32 ref, selectsync *sync) 429 { 430 if (fDeviceRemoved) 431 return B_DEV_NOT_READY; 432 433 return gTTYModule->tty_select(fSystemTTYCookie, event, ref, sync); 434 } 435 436 437 status_t 438 SerialDevice::DeSelect(uint8 event, selectsync *sync) 439 { 440 if (fDeviceRemoved) 441 return B_DEV_NOT_READY; 442 443 return gTTYModule->tty_deselect(fSystemTTYCookie, event, sync); 444 } 445 446 447 status_t 448 SerialDevice::Close() 449 { 450 OnClose(); 451 452 fStopThreads = true; 453 fInputStopped = false; 454 455 if (!fDeviceRemoved) { 456 gUSBModule->cancel_queued_transfers(fReadPipe); 457 gUSBModule->cancel_queued_transfers(fWritePipe); 458 gUSBModule->cancel_queued_transfers(fControlPipe); 459 } 460 461 gTTYModule->tty_close_cookie(fSystemTTYCookie); 462 gTTYModule->tty_close_cookie(fDeviceTTYCookie); 463 464 int32 result = B_OK; 465 wait_for_thread(fInputThread, &result); 466 fInputThread = -1; 467 468 gTTYModule->tty_destroy_cookie(fSystemTTYCookie); 469 gTTYModule->tty_destroy_cookie(fDeviceTTYCookie); 470 471 gTTYModule->tty_destroy(fMasterTTY); 472 gTTYModule->tty_destroy(fSlaveTTY); 473 474 fDeviceOpen = false; 475 return B_OK; 476 } 477 478 479 status_t 480 SerialDevice::Free() 481 { 482 return B_OK; 483 } 484 485 486 void 487 SerialDevice::Removed() 488 { 489 if (fDeviceRemoved) 490 return; 491 492 // notifies us that the device was removed 493 fDeviceRemoved = true; 494 495 // we need to ensure that we do not use the device anymore 496 fStopThreads = true; 497 fInputStopped = false; 498 gUSBModule->cancel_queued_transfers(fReadPipe); 499 gUSBModule->cancel_queued_transfers(fWritePipe); 500 gUSBModule->cancel_queued_transfers(fControlPipe); 501 } 502 503 504 status_t 505 SerialDevice::AddDevice(const usb_configuration_info *config) 506 { 507 // default implementation - does nothing 508 return B_ERROR; 509 } 510 511 512 status_t 513 SerialDevice::ResetDevice() 514 { 515 // default implementation - does nothing 516 return B_OK; 517 } 518 519 520 status_t 521 SerialDevice::SetLineCoding(usb_cdc_line_coding *coding) 522 { 523 // default implementation - does nothing 524 return B_OK; 525 } 526 527 528 status_t 529 SerialDevice::SetControlLineState(uint16 state) 530 { 531 // default implementation - does nothing 532 return B_OK; 533 } 534 535 536 void 537 SerialDevice::OnRead(char **buffer, size_t *numBytes) 538 { 539 // default implementation - does nothing 540 } 541 542 543 void 544 SerialDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes) 545 { 546 memcpy(fWriteBuffer, buffer, *numBytes); 547 } 548 549 550 void 551 SerialDevice::OnClose() 552 { 553 // default implementation - does nothing 554 } 555 556 557 int32 558 SerialDevice::_InputThread(void *data) 559 { 560 SerialDevice *device = (SerialDevice *)data; 561 562 while (!device->fStopThreads) { 563 status_t status = gUSBModule->queue_bulk(device->fReadPipe, 564 device->fReadBuffer, device->fReadBufferSize, 565 device->_ReadCallbackFunction, data); 566 if (status < B_OK) { 567 TRACE_ALWAYS("input thread: queueing failed with error: 0x%08x\n", 568 status); 569 return status; 570 } 571 572 status = acquire_sem_etc(device->fDoneRead, 1, B_CAN_INTERRUPT, 0); 573 if (status < B_OK) { 574 TRACE_ALWAYS("input thread: failed to get read done sem 0x%08x\n", 575 status); 576 return status; 577 } 578 579 if (device->fStatusRead != B_OK) { 580 TRACE("input thread: device status error 0x%08x\n", 581 device->fStatusRead); 582 if (device->fStatusRead == B_DEV_STALLED 583 && gUSBModule->clear_feature(device->fReadPipe, 584 USB_FEATURE_ENDPOINT_HALT) != B_OK) { 585 TRACE_ALWAYS("input thread: failed to clear halt feature\n"); 586 return B_ERROR; 587 } 588 589 continue; 590 } 591 592 char *buffer = device->fReadBuffer; 593 size_t readLength = device->fActualLengthRead; 594 device->OnRead(&buffer, &readLength); 595 if (readLength == 0) 596 continue; 597 598 while (device->fInputStopped) 599 snooze(100); 600 601 status = gTTYModule->tty_write(device->fDeviceTTYCookie, buffer, 602 &readLength); 603 if (status != B_OK) { 604 TRACE_ALWAYS("input thread: failed to write into TTY\n"); 605 return status; 606 } 607 } 608 609 return B_OK; 610 } 611 612 613 status_t 614 SerialDevice::_WriteToDevice() 615 { 616 char *buffer = fOutputBuffer; 617 size_t bytesLeft = fOutputBufferSize; 618 status_t status = gTTYModule->tty_read(fDeviceTTYCookie, buffer, 619 &bytesLeft); 620 if (status != B_OK) { 621 TRACE_ALWAYS("write to device: failed to read from TTY: %s\n", 622 strerror(status)); 623 return status; 624 } 625 626 while (!fDeviceRemoved && bytesLeft > 0) { 627 size_t length = MIN(bytesLeft, fWriteBufferSize); 628 size_t packetLength = length; 629 OnWrite(buffer, &length, &packetLength); 630 631 status = gUSBModule->queue_bulk(fWritePipe, fWriteBuffer, packetLength, 632 _WriteCallbackFunction, this); 633 if (status != B_OK) { 634 TRACE_ALWAYS("write to device: queueing failed with status " 635 "0x%08x\n", status); 636 return status; 637 } 638 639 status = acquire_sem_etc(fDoneWrite, 1, B_CAN_INTERRUPT, 0); 640 if (status != B_OK) { 641 TRACE_ALWAYS("write to device: failed to get write done sem " 642 "0x%08x\n", status); 643 return status; 644 } 645 646 if (fStatusWrite != B_OK) { 647 TRACE("write to device: device status error 0x%08x\n", 648 fStatusWrite); 649 if (fStatusWrite == B_DEV_STALLED) { 650 status = gUSBModule->clear_feature(fWritePipe, 651 USB_FEATURE_ENDPOINT_HALT); 652 if (status != B_OK) { 653 TRACE_ALWAYS("write to device: failed to clear device " 654 "halt\n"); 655 return B_ERROR; 656 } 657 } 658 659 continue; 660 } 661 662 buffer += length; 663 bytesLeft -= length; 664 } 665 666 return B_OK; 667 } 668 669 670 void 671 SerialDevice::_ReadCallbackFunction(void *cookie, status_t status, void *data, 672 size_t actualLength) 673 { 674 TRACE_FUNCALLS("read callback: cookie: 0x%08x status: 0x%08x data: 0x%08x " 675 "length: %lu\n", cookie, status, data, actualLength); 676 677 SerialDevice *device = (SerialDevice *)cookie; 678 device->fActualLengthRead = actualLength; 679 device->fStatusRead = status; 680 release_sem_etc(device->fDoneRead, 1, B_DO_NOT_RESCHEDULE); 681 } 682 683 684 void 685 SerialDevice::_WriteCallbackFunction(void *cookie, status_t status, void *data, 686 size_t actualLength) 687 { 688 TRACE_FUNCALLS("write callback: cookie: 0x%08x status: 0x%08x data: 0x%08x " 689 "length: %lu\n", cookie, status, data, actualLength); 690 691 SerialDevice *device = (SerialDevice *)cookie; 692 device->fActualLengthWrite = actualLength; 693 device->fStatusWrite = status; 694 release_sem_etc(device->fDoneWrite, 1, B_DO_NOT_RESCHEDULE); 695 } 696 697 698 void 699 SerialDevice::_InterruptCallbackFunction(void *cookie, status_t status, 700 void *data, size_t actualLength) 701 { 702 TRACE_FUNCALLS("interrupt callback: cookie: 0x%08x status: 0x%08x data: " 703 "0x%08x len: %lu\n", cookie, status, data, actualLength); 704 705 SerialDevice *device = (SerialDevice *)cookie; 706 device->fActualLengthInterrupt = actualLength; 707 device->fStatusInterrupt = status; 708 709 // ToDo: maybe handle those somehow? 710 711 if (status == B_OK && !device->fDeviceRemoved) { 712 status = gUSBModule->queue_interrupt(device->fControlPipe, 713 device->fInterruptBuffer, device->fInterruptBufferSize, 714 device->_InterruptCallbackFunction, device); 715 } 716 } 717 718 719 SerialDevice * 720 SerialDevice::MakeDevice(usb_device device, uint16 vendorID, 721 uint16 productID) 722 { 723 // FTDI Serial Device 724 for (uint32 i = 0; i < sizeof(kFTDIDevices) 725 / sizeof(kFTDIDevices[0]); i++) { 726 if (vendorID == kFTDIDevices[i].vendorID 727 && productID == kFTDIDevices[i].productID) { 728 return new(std::nothrow) FTDIDevice(device, vendorID, productID, 729 kFTDIDevices[i].deviceName); 730 } 731 } 732 733 // KLSI Serial Device 734 for (uint32 i = 0; i < sizeof(kKLSIDevices) 735 / sizeof(kKLSIDevices[0]); i++) { 736 if (vendorID == kKLSIDevices[i].vendorID 737 && productID == kKLSIDevices[i].productID) { 738 return new(std::nothrow) KLSIDevice(device, vendorID, productID, 739 kKLSIDevices[i].deviceName); 740 } 741 } 742 743 // Prolific Serial Device 744 for (uint32 i = 0; i < sizeof(kProlificDevices) 745 / sizeof(kProlificDevices[0]); i++) { 746 if (vendorID == kProlificDevices[i].vendorID 747 && productID == kProlificDevices[i].productID) { 748 return new(std::nothrow) ProlificDevice(device, vendorID, productID, 749 kProlificDevices[i].deviceName); 750 } 751 } 752 753 // Silicon Serial Device 754 for (uint32 i = 0; i < sizeof(kSiliconDevices) 755 / sizeof(kSiliconDevices[0]); i++) { 756 if (vendorID == kSiliconDevices[i].vendorID 757 && productID == kSiliconDevices[i].productID) { 758 return new(std::nothrow) SiliconDevice(device, vendorID, productID, 759 kSiliconDevices[i].deviceName); 760 } 761 } 762 763 // Option Serial Device 764 for (uint32 i = 0; i < sizeof(kOptionDevices) 765 / sizeof(kOptionDevices[0]); i++) { 766 if (vendorID == kOptionDevices[i].vendorID 767 && productID == kOptionDevices[i].productID) { 768 return new(std::nothrow) OptionDevice(device, vendorID, productID, 769 kOptionDevices[i].deviceName); 770 } 771 } 772 773 // Otherwise, return standard ACM device 774 return new(std::nothrow) ACMDevice(device, vendorID, productID, 775 "CDC ACM compatible device"); 776 } 777