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