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_KERNEL_READ_AREA | B_KERNEL_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 = 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	SetHardwareFlowControl((tios->c_cflag & CRTSCTS) != 0);
184
185	usb_cdc_line_coding lineCoding;
186	lineCoding.speed = speed;
187	lineCoding.stopbits = (tios->c_cflag & CSTOPB)
188		? USB_CDC_LINE_CODING_2_STOPBITS : USB_CDC_LINE_CODING_1_STOPBIT;
189
190	if (tios->c_cflag & PARENB) {
191		lineCoding.parity = USB_CDC_LINE_CODING_EVEN_PARITY;
192		if (tios->c_cflag & PARODD)
193			lineCoding.parity = USB_CDC_LINE_CODING_ODD_PARITY;
194	} else
195		lineCoding.parity = USB_CDC_LINE_CODING_NO_PARITY;
196
197	lineCoding.databits = (tios->c_cflag & CS8) ? 8 : 7;
198
199	if (memcmp(&lineCoding, &fLineCoding, sizeof(usb_cdc_line_coding)) != 0) {
200		fLineCoding.speed = lineCoding.speed;
201		fLineCoding.stopbits = lineCoding.stopbits;
202		fLineCoding.databits = lineCoding.databits;
203		fLineCoding.parity = lineCoding.parity;
204		TRACE("send to modem: speed %d sb: 0x%08x db: 0x%08x parity: 0x%08x\n",
205			fLineCoding.speed, fLineCoding.stopbits, fLineCoding.databits,
206			fLineCoding.parity);
207		SetLineCoding(&fLineCoding);
208	}
209}
210
211
212bool
213SerialDevice::Service(struct tty *tty, uint32 op, void *buffer, size_t length)
214{
215	if (!fDeviceOpen)
216		return false;
217
218	if (tty != fMasterTTY)
219		return false;
220
221	switch (op) {
222		case TTYENABLE:
223		{
224			bool enable = *(bool *)buffer;
225			TRACE("TTYENABLE: %sable\n", enable ? "en" : "dis");
226
227			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD, enable);
228			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS, enable);
229
230			fControlOut = enable ? USB_CDC_CONTROL_SIGNAL_STATE_DTR
231				| USB_CDC_CONTROL_SIGNAL_STATE_RTS : 0;
232			SetControlLineState(fControlOut);
233			return true;
234		}
235
236		case TTYISTOP:
237			fInputStopped = *(bool *)buffer;
238			TRACE("TTYISTOP: %sstopped\n", fInputStopped ? "" : "not ");
239			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS,
240				!fInputStopped);
241			return true;
242
243		case TTYGETSIGNALS:
244			TRACE("TTYGETSIGNALS\n");
245			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD,
246				(fControlOut & (USB_CDC_CONTROL_SIGNAL_STATE_DTR
247					| USB_CDC_CONTROL_SIGNAL_STATE_RTS)) != 0);
248			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS,
249				!fInputStopped);
250			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDSR, false);
251			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWRI, false);
252			return true;
253
254		case TTYSETMODES:
255			TRACE("TTYSETMODES\n");
256			SetModes((struct termios *)buffer);
257			return true;
258
259		case TTYSETDTR:
260		case TTYSETRTS:
261		{
262			bool set = *(bool *)buffer;
263			uint8 bit = op == TTYSETDTR ? USB_CDC_CONTROL_SIGNAL_STATE_DTR
264				: USB_CDC_CONTROL_SIGNAL_STATE_RTS;
265			if (set)
266				fControlOut |= bit;
267			else
268				fControlOut &= ~bit;
269
270			SetControlLineState(fControlOut);
271			return true;
272		}
273
274		case TTYOSTART:
275		case TTYOSYNC:
276		case TTYSETBREAK:
277			TRACE("TTY other\n");
278			return true;
279	}
280
281	return false;
282}
283
284
285status_t
286SerialDevice::Open(uint32 flags)
287{
288	if (fDeviceOpen)
289		return B_BUSY;
290
291	if (fDeviceRemoved)
292		return B_DEV_NOT_READY;
293
294	fMasterTTY = gTTYModule->tty_create(usb_serial_service, true);
295	if (fMasterTTY == NULL) {
296		TRACE_ALWAYS("open: failed to init master tty\n");
297		return B_NO_MEMORY;
298	}
299
300	fSlaveTTY = gTTYModule->tty_create(usb_serial_service, false);
301	if (fSlaveTTY == NULL) {
302		TRACE_ALWAYS("open: failed to init slave tty\n");
303		gTTYModule->tty_destroy(fMasterTTY);
304		return B_NO_MEMORY;
305	}
306
307	fSystemTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY,
308		O_RDWR);
309	if (fSystemTTYCookie == NULL) {
310		TRACE_ALWAYS("open: failed to init system tty cookie\n");
311		gTTYModule->tty_destroy(fMasterTTY);
312		gTTYModule->tty_destroy(fSlaveTTY);
313		return B_NO_MEMORY;
314	}
315
316	fDeviceTTYCookie = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY,
317		O_RDWR);
318	if (fDeviceTTYCookie == NULL) {
319		TRACE_ALWAYS("open: failed to init device tty cookie\n");
320		gTTYModule->tty_destroy_cookie(fSystemTTYCookie);
321		gTTYModule->tty_destroy(fMasterTTY);
322		gTTYModule->tty_destroy(fSlaveTTY);
323		return B_NO_MEMORY;
324	}
325
326	ResetDevice();
327
328	fStopThreads = false;
329
330	fInputThread = spawn_kernel_thread(_InputThread,
331		"usb_serial input thread", B_NORMAL_PRIORITY, this);
332	if (fInputThread < 0) {
333		TRACE_ALWAYS("open: failed to spawn input thread\n");
334		return fInputThread;
335	}
336
337	resume_thread(fInputThread);
338
339	fControlOut = USB_CDC_CONTROL_SIGNAL_STATE_DTR
340		| USB_CDC_CONTROL_SIGNAL_STATE_RTS;
341	SetControlLineState(fControlOut);
342
343	status_t status = gUSBModule->queue_interrupt(fControlPipe,
344		fInterruptBuffer, fInterruptBufferSize, _InterruptCallbackFunction,
345		this);
346	if (status < B_OK)
347		TRACE_ALWAYS("failed to queue initial interrupt\n");
348
349	// set our config (will propagate to the slave config as well in SetModes()
350	gTTYModule->tty_control(fSystemTTYCookie, TCSETA, &fTTYConfig,
351		sizeof(termios));
352
353	fDeviceOpen = true;
354	return B_OK;
355}
356
357
358status_t
359SerialDevice::Read(char *buffer, size_t *numBytes)
360{
361	if (fDeviceRemoved) {
362		*numBytes = 0;
363		return B_DEV_NOT_READY;
364	}
365
366	return gTTYModule->tty_read(fSystemTTYCookie, buffer, numBytes);
367}
368
369
370status_t
371SerialDevice::Write(const char *buffer, size_t *numBytes)
372{
373	if (fDeviceRemoved) {
374		*numBytes = 0;
375		return B_DEV_NOT_READY;
376	}
377
378	size_t bytesLeft = *numBytes;
379	*numBytes = 0;
380
381	while (bytesLeft > 0) {
382		size_t length = MIN(bytesLeft, 256);
383			// TODO: This is an ugly hack; We use a small buffer size so that
384			// we don't overrun the tty line buffer and cause it to block. While
385			// that isn't a problem, we shouldn't just hardcode the value here.
386
387		status_t result = gTTYModule->tty_write(fSystemTTYCookie, buffer,
388			&length);
389		if (result != B_OK) {
390			TRACE_ALWAYS("failed to write to tty: %s\n", strerror(result));
391			return result;
392		}
393
394		buffer += length;
395		*numBytes += length;
396		bytesLeft -= length;
397
398		while (true) {
399			// Write to the device as long as there's anything in the tty buffer
400			int readable = 0;
401			gTTYModule->tty_control(fDeviceTTYCookie, FIONREAD, &readable,
402				sizeof(readable));
403			if (readable == 0)
404				break;
405
406			result = _WriteToDevice();
407			if (result != B_OK) {
408				TRACE_ALWAYS("failed to write to device: %s\n",
409					strerror(result));
410				return result;
411			}
412		}
413	}
414
415	if (*numBytes > 0)
416		return B_OK;
417
418	return B_ERROR;
419}
420
421
422status_t
423SerialDevice::Control(uint32 op, void *arg, size_t length)
424{
425	if (fDeviceRemoved)
426		return B_DEV_NOT_READY;
427
428	return gTTYModule->tty_control(fSystemTTYCookie, op, arg, length);
429}
430
431
432status_t
433SerialDevice::Select(uint8 event, uint32 ref, selectsync *sync)
434{
435	if (fDeviceRemoved)
436		return B_DEV_NOT_READY;
437
438	return gTTYModule->tty_select(fSystemTTYCookie, event, ref, sync);
439}
440
441
442status_t
443SerialDevice::DeSelect(uint8 event, selectsync *sync)
444{
445	if (fDeviceRemoved)
446		return B_DEV_NOT_READY;
447
448	return gTTYModule->tty_deselect(fSystemTTYCookie, event, sync);
449}
450
451
452status_t
453SerialDevice::Close()
454{
455	OnClose();
456
457	fStopThreads = true;
458	fInputStopped = false;
459	fDeviceOpen = false;
460
461	if (!fDeviceRemoved) {
462		gUSBModule->cancel_queued_transfers(fReadPipe);
463		gUSBModule->cancel_queued_transfers(fWritePipe);
464		gUSBModule->cancel_queued_transfers(fControlPipe);
465	}
466
467	gTTYModule->tty_close_cookie(fSystemTTYCookie);
468	gTTYModule->tty_close_cookie(fDeviceTTYCookie);
469
470	int32 result = B_OK;
471	wait_for_thread(fInputThread, &result);
472	fInputThread = -1;
473
474	gTTYModule->tty_destroy_cookie(fSystemTTYCookie);
475	gTTYModule->tty_destroy_cookie(fDeviceTTYCookie);
476
477	gTTYModule->tty_destroy(fMasterTTY);
478	gTTYModule->tty_destroy(fSlaveTTY);
479
480	fMasterTTY = NULL;
481	fSlaveTTY = NULL;
482	fSystemTTYCookie = NULL;
483	fDeviceTTYCookie = NULL;
484	return B_OK;
485}
486
487
488status_t
489SerialDevice::Free()
490{
491	return B_OK;
492}
493
494
495void
496SerialDevice::Removed()
497{
498	if (fDeviceRemoved)
499		return;
500
501	// notifies us that the device was removed
502	fDeviceRemoved = true;
503
504	// we need to ensure that we do not use the device anymore
505	fStopThreads = true;
506	fInputStopped = false;
507	gUSBModule->cancel_queued_transfers(fReadPipe);
508	gUSBModule->cancel_queued_transfers(fWritePipe);
509	gUSBModule->cancel_queued_transfers(fControlPipe);
510}
511
512
513status_t
514SerialDevice::AddDevice(const usb_configuration_info *config)
515{
516	// default implementation - does nothing
517	return B_ERROR;
518}
519
520
521status_t
522SerialDevice::ResetDevice()
523{
524	// default implementation - does nothing
525	return B_OK;
526}
527
528
529status_t
530SerialDevice::SetLineCoding(usb_cdc_line_coding *coding)
531{
532	// default implementation - does nothing
533	return B_NOT_SUPPORTED;
534}
535
536
537status_t
538SerialDevice::SetControlLineState(uint16 state)
539{
540	// default implementation - does nothing
541	return B_NOT_SUPPORTED;
542}
543
544
545status_t
546SerialDevice::SetHardwareFlowControl(bool enable)
547{
548	// default implementation - does nothing
549	return B_NOT_SUPPORTED;
550}
551
552
553void
554SerialDevice::OnRead(char **buffer, size_t *numBytes)
555{
556	// default implementation - does nothing
557}
558
559
560void
561SerialDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes)
562{
563	memcpy(fWriteBuffer, buffer, *numBytes);
564}
565
566
567void
568SerialDevice::OnClose()
569{
570	// default implementation - does nothing
571}
572
573
574int32
575SerialDevice::_InputThread(void *data)
576{
577	SerialDevice *device = (SerialDevice *)data;
578
579	while (!device->fStopThreads) {
580		status_t status = gUSBModule->queue_bulk(device->fReadPipe,
581			device->fReadBuffer, device->fReadBufferSize,
582			device->_ReadCallbackFunction, data);
583		if (status < B_OK) {
584			TRACE_ALWAYS("input thread: queueing failed with error: 0x%08x\n",
585				status);
586			return status;
587		}
588
589		status = acquire_sem_etc(device->fDoneRead, 1, B_CAN_INTERRUPT, 0);
590		if (status < B_OK) {
591			TRACE_ALWAYS("input thread: failed to get read done sem 0x%08x\n",
592				status);
593			return status;
594		}
595
596		if (device->fStatusRead != B_OK) {
597			TRACE("input thread: device status error 0x%08x\n",
598				device->fStatusRead);
599			if (device->fStatusRead == B_DEV_STALLED
600				&& gUSBModule->clear_feature(device->fReadPipe,
601					USB_FEATURE_ENDPOINT_HALT) != B_OK) {
602				TRACE_ALWAYS("input thread: failed to clear halt feature\n");
603				return B_ERROR;
604			}
605
606			continue;
607		}
608
609		char *buffer = device->fReadBuffer;
610		size_t readLength = device->fActualLengthRead;
611		device->OnRead(&buffer, &readLength);
612		if (readLength == 0)
613			continue;
614
615		while (device->fInputStopped)
616			snooze(100);
617
618		status = gTTYModule->tty_write(device->fDeviceTTYCookie, buffer,
619			&readLength);
620		if (status != B_OK) {
621			TRACE_ALWAYS("input thread: failed to write into TTY\n");
622			return status;
623		}
624	}
625
626	return B_OK;
627}
628
629
630status_t
631SerialDevice::_WriteToDevice()
632{
633	char *buffer = fOutputBuffer;
634	size_t bytesLeft = fOutputBufferSize;
635	status_t status = gTTYModule->tty_read(fDeviceTTYCookie, buffer,
636		&bytesLeft);
637	if (status != B_OK) {
638		TRACE_ALWAYS("write to device: failed to read from TTY: %s\n",
639			strerror(status));
640		return status;
641	}
642
643	while (!fDeviceRemoved && bytesLeft > 0) {
644		size_t length = MIN(bytesLeft, fWriteBufferSize);
645		size_t packetLength = length;
646		OnWrite(buffer, &length, &packetLength);
647
648		status = gUSBModule->queue_bulk(fWritePipe, fWriteBuffer, packetLength,
649			_WriteCallbackFunction, this);
650		if (status != B_OK) {
651			TRACE_ALWAYS("write to device: queueing failed with status "
652				"0x%08x\n", status);
653			return status;
654		}
655
656		status = acquire_sem_etc(fDoneWrite, 1, B_CAN_INTERRUPT, 0);
657		if (status != B_OK) {
658			TRACE_ALWAYS("write to device: failed to get write done sem "
659				"0x%08x\n", status);
660			return status;
661		}
662
663		if (fStatusWrite != B_OK) {
664			TRACE("write to device: device status error 0x%08x\n",
665				fStatusWrite);
666			if (fStatusWrite == B_DEV_STALLED) {
667				status = gUSBModule->clear_feature(fWritePipe,
668					USB_FEATURE_ENDPOINT_HALT);
669				if (status != B_OK) {
670					TRACE_ALWAYS("write to device: failed to clear device "
671						"halt\n");
672					return B_ERROR;
673				}
674			}
675
676			continue;
677		}
678
679		buffer += length;
680		bytesLeft -= length;
681	}
682
683	return B_OK;
684}
685
686
687void
688SerialDevice::_ReadCallbackFunction(void *cookie, status_t status, void *data,
689	size_t actualLength)
690{
691	TRACE_FUNCALLS("read callback: cookie: 0x%08x status: 0x%08x data: 0x%08x "
692		"length: %lu\n", cookie, status, data, actualLength);
693
694	SerialDevice *device = (SerialDevice *)cookie;
695	device->fActualLengthRead = actualLength;
696	device->fStatusRead = status;
697	release_sem_etc(device->fDoneRead, 1, B_DO_NOT_RESCHEDULE);
698}
699
700
701void
702SerialDevice::_WriteCallbackFunction(void *cookie, status_t status, void *data,
703	size_t actualLength)
704{
705	TRACE_FUNCALLS("write callback: cookie: 0x%08x status: 0x%08x data: 0x%08x "
706		"length: %lu\n", cookie, status, data, actualLength);
707
708	SerialDevice *device = (SerialDevice *)cookie;
709	device->fActualLengthWrite = actualLength;
710	device->fStatusWrite = status;
711	release_sem_etc(device->fDoneWrite, 1, B_DO_NOT_RESCHEDULE);
712}
713
714
715void
716SerialDevice::_InterruptCallbackFunction(void *cookie, status_t status,
717	void *data, size_t actualLength)
718{
719	TRACE_FUNCALLS("interrupt callback: cookie: 0x%08x status: 0x%08x data: "
720		"0x%08x len: %lu\n", cookie, status, data, actualLength);
721
722	SerialDevice *device = (SerialDevice *)cookie;
723	device->fActualLengthInterrupt = actualLength;
724	device->fStatusInterrupt = status;
725
726	// ToDo: maybe handle those somehow?
727
728	if (status == B_OK && !device->fDeviceRemoved) {
729		status = gUSBModule->queue_interrupt(device->fControlPipe,
730			device->fInterruptBuffer, device->fInterruptBufferSize,
731			device->_InterruptCallbackFunction, device);
732	}
733}
734
735
736SerialDevice *
737SerialDevice::MakeDevice(usb_device device, uint16 vendorID,
738	uint16 productID)
739{
740	// FTDI Serial Device
741	for (uint32 i = 0; i < sizeof(kFTDIDevices)
742		/ sizeof(kFTDIDevices[0]); i++) {
743		if (vendorID == kFTDIDevices[i].vendorID
744			&& productID == kFTDIDevices[i].productID) {
745			return new(std::nothrow) FTDIDevice(device, vendorID, productID,
746				kFTDIDevices[i].deviceName);
747		}
748	}
749
750	// KLSI Serial Device
751	for (uint32 i = 0; i < sizeof(kKLSIDevices)
752		/ sizeof(kKLSIDevices[0]); i++) {
753		if (vendorID == kKLSIDevices[i].vendorID
754			&& productID == kKLSIDevices[i].productID) {
755			return new(std::nothrow) KLSIDevice(device, vendorID, productID,
756				kKLSIDevices[i].deviceName);
757		}
758	}
759
760	// Prolific Serial Device
761	for (uint32 i = 0; i < sizeof(kProlificDevices)
762		/ sizeof(kProlificDevices[0]); i++) {
763		if (vendorID == kProlificDevices[i].vendorID
764			&& productID == kProlificDevices[i].productID) {
765			return new(std::nothrow) ProlificDevice(device, vendorID, productID,
766				kProlificDevices[i].deviceName);
767		}
768	}
769
770	// Silicon Serial Device
771	for (uint32 i = 0; i < sizeof(kSiliconDevices)
772		/ sizeof(kSiliconDevices[0]); i++) {
773		if (vendorID == kSiliconDevices[i].vendorID
774			&& productID == kSiliconDevices[i].productID) {
775			return new(std::nothrow) SiliconDevice(device, vendorID, productID,
776				kSiliconDevices[i].deviceName);
777		}
778	}
779
780	// Option Serial Device
781	for (uint32 i = 0; i < sizeof(kOptionDevices)
782		/ sizeof(kOptionDevices[0]); i++) {
783		if (vendorID == kOptionDevices[i].vendorID
784			&& productID == kOptionDevices[i].productID) {
785			return new(std::nothrow) OptionDevice(device, vendorID, productID,
786				kOptionDevices[i].deviceName);
787		}
788	}
789
790	// Otherwise, return standard ACM device
791	return new(std::nothrow) ACMDevice(device, vendorID, productID,
792		"CDC ACM compatible device");
793}
794