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 28SerialDevice::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 64SerialDevice::~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 78status_t 79SerialDevice::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 104void 105SerialDevice::SetControlPipe(usb_pipe handle) 106{ 107 fControlPipe = handle; 108} 109 110 111void 112SerialDevice::SetReadPipe(usb_pipe handle) 113{ 114 fReadPipe = handle; 115} 116 117 118void 119SerialDevice::SetWritePipe(usb_pipe handle) 120{ 121 fWritePipe = handle; 122} 123 124 125inline int32 126baud_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 156void 157SerialDevice::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 = B19200; 165 speed = 19200; 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 210bool 211SerialDevice::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 280status_t 281SerialDevice::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 353status_t 354SerialDevice::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 365status_t 366SerialDevice::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 417status_t 418SerialDevice::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 427status_t 428SerialDevice::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 437status_t 438SerialDevice::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 447status_t 448SerialDevice::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 479status_t 480SerialDevice::Free() 481{ 482 return B_OK; 483} 484 485 486void 487SerialDevice::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 504status_t 505SerialDevice::AddDevice(const usb_configuration_info *config) 506{ 507 // default implementation - does nothing 508 return B_ERROR; 509} 510 511 512status_t 513SerialDevice::ResetDevice() 514{ 515 // default implementation - does nothing 516 return B_OK; 517} 518 519 520status_t 521SerialDevice::SetLineCoding(usb_cdc_line_coding *coding) 522{ 523 // default implementation - does nothing 524 return B_OK; 525} 526 527 528status_t 529SerialDevice::SetControlLineState(uint16 state) 530{ 531 // default implementation - does nothing 532 return B_OK; 533} 534 535 536void 537SerialDevice::OnRead(char **buffer, size_t *numBytes) 538{ 539 // default implementation - does nothing 540} 541 542 543void 544SerialDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes) 545{ 546 memcpy(fWriteBuffer, buffer, *numBytes); 547} 548 549 550void 551SerialDevice::OnClose() 552{ 553 // default implementation - does nothing 554} 555 556 557int32 558SerialDevice::_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 613status_t 614SerialDevice::_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 670void 671SerialDevice::_ReadCallbackFunction(void *cookie, status_t status, void *data, 672 uint32 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 684void 685SerialDevice::_WriteCallbackFunction(void *cookie, status_t status, void *data, 686 uint32 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 698void 699SerialDevice::_InterruptCallbackFunction(void *cookie, status_t status, 700 void *data, uint32 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 719SerialDevice * 720SerialDevice::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