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