xref: /haiku/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp (revision 079eccf655ba39812b421ae1b87a727d41b50354)
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 #include "SerialDevice.h"
9 #include "USB3.h"
10 
11 #include "ACM.h"
12 #include "FTDI.h"
13 #include "KLSI.h"
14 #include "Prolific.h"
15 
16 SerialDevice::SerialDevice(usb_device device, uint16 vendorID,
17 	uint16 productID, const char *description)
18 	:	fDevice(device),
19 		fVendorID(vendorID),
20 		fProductID(productID),
21 		fDescription(description),
22 		fDeviceOpen(false),
23 		fDeviceRemoved(false),
24 		fControlPipe(0),
25 		fReadPipe(0),
26 		fWritePipe(0),
27 		fBufferArea(-1),
28 		fReadBuffer(NULL),
29 		fReadBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
30 		fWriteBuffer(NULL),
31 		fWriteBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
32 		fInterruptBuffer(NULL),
33 		fInterruptBufferSize(16),
34 		fDoneRead(-1),
35 		fDoneWrite(-1),
36 		fControlOut(0),
37 		fInputStopped(false),
38 		fDeviceThread(-1),
39 		fStopDeviceThread(false)
40 {
41 	memset(&fTTYFile, 0, sizeof(ttyfile));
42 	memset(&fTTY, 0, sizeof(tty));
43 }
44 
45 
46 SerialDevice::~SerialDevice()
47 {
48 	Removed();
49 
50 	if (fDoneRead >= B_OK)
51 		delete_sem(fDoneRead);
52 	if (fDoneWrite >= B_OK)
53 		delete_sem(fDoneWrite);
54 
55 	if (fBufferArea >= B_OK)
56 		delete_area(fBufferArea);
57 
58 	mutex_destroy(&fReadLock);
59 	mutex_destroy(&fWriteLock);
60 }
61 
62 
63 status_t
64 SerialDevice::Init()
65 {
66 	fDoneRead = create_sem(0, "usb_serial:done_read");
67 	fDoneWrite = create_sem(0, "usb_serial:done_write");
68 	mutex_init(&fReadLock, "usb_serial:read_lock");
69 	mutex_init(&fWriteLock, "usb_serial:write_lock");
70 
71 	size_t totalBuffers = fReadBufferSize + fWriteBufferSize + fInterruptBufferSize;
72 	fBufferArea = create_area("usb_serial:buffers_area", (void **)&fReadBuffer,
73 		B_ANY_KERNEL_ADDRESS, ROUNDUP(totalBuffers, B_PAGE_SIZE), B_CONTIGUOUS,
74 		B_READ_AREA | B_WRITE_AREA);
75 
76 	fWriteBuffer = fReadBuffer + fReadBufferSize;
77 	fInterruptBuffer = fWriteBuffer + fWriteBufferSize;
78 	return B_OK;
79 }
80 
81 
82 void
83 SerialDevice::SetControlPipe(usb_pipe handle)
84 {
85 	fControlPipe = handle;
86 }
87 
88 
89 void
90 SerialDevice::SetReadPipe(usb_pipe handle)
91 {
92 	fReadPipe = handle;
93 }
94 
95 
96 void
97 SerialDevice::SetWritePipe(usb_pipe handle)
98 {
99 	fWritePipe = handle;
100 }
101 
102 
103 void
104 SerialDevice::SetModes()
105 {
106 	struct termios tios;
107 	memcpy(&tios, &fTTY.t, sizeof(struct termios));
108 	uint16 newControl = fControlOut;
109 	TRACE_FUNCRES(trace_termios, &tios);
110 
111 	static uint32 baudRates[] = {
112 		0x00000000, //B0
113 		0x00000032, //B50
114 		0x0000004B, //B75
115 		0x0000006E, //B110
116 		0x00000086, //B134
117 		0x00000096, //B150
118 		0x000000C8, //B200
119 		0x0000012C, //B300
120 		0x00000258, //B600
121 		0x000004B0, //B1200
122 		0x00000708, //B1800
123 		0x00000960, //B2400
124 		0x000012C0, //B4800
125 		0x00002580, //B9600
126 		0x00004B00, //B19200
127 		0x00009600, //B38400
128 		0x0000E100, //B57600
129 		0x0001C200, //B115200
130 		0x00038400, //B230400
131 		0x00070800, //460800
132 		0x000E1000, //921600
133 	};
134 
135 	uint32 baudCount = sizeof(baudRates) / sizeof(baudRates[0]);
136 	uint32 baudIndex = tios.c_cflag & CBAUD;
137 	if (baudIndex > baudCount)
138 		baudIndex = baudCount - 1;
139 
140 	usb_serial_line_coding lineCoding;
141 	lineCoding.speed = baudRates[baudIndex];
142 	lineCoding.stopbits = (tios.c_cflag & CSTOPB) ? LC_STOP_BIT_2 : LC_STOP_BIT_1;
143 
144 	if (tios.c_cflag & PARENB) {
145 		lineCoding.parity = LC_PARITY_EVEN;
146 		if (tios.c_cflag & PARODD)
147 			lineCoding.parity = LC_PARITY_ODD;
148 	} else
149 		lineCoding.parity = LC_PARITY_NONE;
150 
151 	lineCoding.databits = (tios.c_cflag & CS8) ? 8 : 7;
152 
153 	if (lineCoding.speed == 0) {
154 		newControl &= 0xfffffffe;
155 		lineCoding.speed = fLineCoding.speed;
156 	} else
157 		newControl = CLS_LINE_DTR;
158 
159 	if (fControlOut != newControl) {
160 		fControlOut = newControl;
161 		TRACE("newctrl send to modem: 0x%08x\n", newControl);
162 		SetControlLineState(newControl);
163 	}
164 
165 	if (memcmp(&lineCoding, &fLineCoding, sizeof(usb_serial_line_coding)) != 0) {
166 		fLineCoding.speed = lineCoding.speed;
167 		fLineCoding.stopbits = lineCoding.stopbits;
168 		fLineCoding.databits = lineCoding.databits;
169 		fLineCoding.parity = lineCoding.parity;
170 		TRACE("send to modem: speed %d sb: 0x%08x db: 0x%08x parity: 0x%08x\n",
171 			fLineCoding.speed, fLineCoding.stopbits, fLineCoding.databits,
172 			fLineCoding.parity);
173 		SetLineCoding(&fLineCoding);
174 	}
175 }
176 
177 
178 bool
179 SerialDevice::Service(struct tty *ptty, struct ddrover *ddr, uint flags)
180 {
181 	if (&fTTY != ptty)
182 		return false;
183 
184 	if (flags <= TTYGETSIGNALS) {
185 		switch (flags) {
186 			case TTYENABLE:
187 				TRACE("TTYENABLE\n");
188 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, false);
189 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true);
190 				fControlOut = CLS_LINE_DTR | CLS_LINE_RTS;
191 				SetControlLineState(fControlOut);
192 				break;
193 
194 			case TTYDISABLE:
195 				TRACE("TTYDISABLE\n");
196 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, false);
197 				fControlOut = 0x0;
198 				SetControlLineState(fControlOut);
199 				break;
200 
201 			case TTYISTOP:
202 				TRACE("TTYISTOP\n");
203 				fInputStopped = true;
204 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, false);
205 				break;
206 
207 			case TTYIRESUME:
208 				TRACE("TTYIRESUME\n");
209 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true);
210 				fInputStopped = false;
211 				break;
212 
213 			case TTYGETSIGNALS:
214 				TRACE("TTYGETSIGNALS\n");
215 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, true);
216 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true);
217 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDSR, false);
218 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWRI, false);
219 				break;
220 
221 			case TTYSETMODES:
222 				TRACE("TTYSETMODES\n");
223 				SetModes();
224 				break;
225 
226 			case TTYOSTART:
227 			case TTYOSYNC:
228 			case TTYSETBREAK:
229 			case TTYCLRBREAK:
230 			case TTYSETDTR:
231 			case TTYCLRDTR:
232 				TRACE("TTY other\n");
233 				break;
234 		}
235 
236 		return true;
237 	}
238 
239 	return false;
240 }
241 
242 
243 status_t
244 SerialDevice::Open(uint32 flags)
245 {
246 	if (fDeviceOpen)
247 		return B_BUSY;
248 
249 	if (fDeviceRemoved)
250 		return B_DEV_NOT_READY;
251 
252 	gTTYModule->ttyinit(&fTTY, true);
253 	fTTYFile.tty = &fTTY;
254 	fTTYFile.flags = flags;
255 	ResetDevice();
256 
257 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
258 	if (!ddr)
259 		return B_NO_MEMORY;
260 
261 	gTTYModule->ddacquire(ddr, &gSerialDomain);
262 	status_t status = gTTYModule->ttyopen(&fTTYFile, ddr, usb_serial_service);
263 	gTTYModule->ddrdone(ddr);
264 
265 	if (status < B_OK) {
266 		TRACE_ALWAYS("open: failed to open tty\n");
267 		return status;
268 	}
269 
270 	fDeviceThread = spawn_kernel_thread(DeviceThread, "usb_serial device thread",
271 		B_NORMAL_PRIORITY, this);
272 
273 	if (fDeviceThread < B_OK) {
274 		TRACE_ALWAYS("open: failed to spawn kernel thread\n");
275 		return fDeviceThread;
276 	}
277 
278 	resume_thread(fDeviceThread);
279 
280 	fControlOut = CLS_LINE_DTR | CLS_LINE_RTS;
281 	SetControlLineState(fControlOut);
282 
283 	status = gUSBModule->queue_interrupt(fControlPipe, fInterruptBuffer,
284 		fInterruptBufferSize, InterruptCallbackFunction, this);
285 	if (status < B_OK)
286 		TRACE_ALWAYS("failed to queue initial interrupt\n");
287 
288 	fDeviceOpen = true;
289 	return B_OK;
290 }
291 
292 
293 status_t
294 SerialDevice::Read(char *buffer, size_t *numBytes)
295 {
296 	if (fDeviceRemoved) {
297 		*numBytes = 0;
298 		return B_DEV_NOT_READY;
299 	}
300 
301 	status_t status = mutex_lock(&fReadLock);
302 	if (status != B_OK) {
303 		TRACE_ALWAYS("read: failed to get read lock\n");
304 		*numBytes = 0;
305 		return status;
306 	}
307 
308 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
309 	if (!ddr) {
310 		*numBytes = 0;
311 		mutex_unlock(&fReadLock);
312 		return B_NO_MEMORY;
313 	}
314 
315 	status = gTTYModule->ttyread(&fTTYFile, ddr, buffer, numBytes);
316 	gTTYModule->ddrdone(ddr);
317 
318 	mutex_unlock(&fReadLock);
319 	return status;
320 }
321 
322 
323 status_t
324 SerialDevice::Write(const char *buffer, size_t *numBytes)
325 {
326 	size_t bytesLeft = *numBytes;
327 	*numBytes = 0;
328 
329 	status_t status = mutex_lock(&fWriteLock);
330 	if (status != B_OK) {
331 		TRACE_ALWAYS("write: failed to get write lock\n");
332 		return status;
333 	}
334 
335 	if (fDeviceRemoved) {
336 		mutex_unlock(&fWriteLock);
337 		return B_DEV_NOT_READY;
338 	}
339 
340 	while (bytesLeft > 0) {
341 		size_t length = MIN(bytesLeft, fWriteBufferSize);
342 		size_t packetLength = length;
343 		OnWrite(buffer, &length, &packetLength);
344 
345 		status = gUSBModule->queue_bulk(fWritePipe, fWriteBuffer,
346 			packetLength, WriteCallbackFunction, this);
347 		if (status < B_OK) {
348 			TRACE_ALWAYS("write: queueing failed with status 0x%08x\n", status);
349 			break;
350 		}
351 
352 		status = acquire_sem_etc(fDoneWrite, 1, B_CAN_INTERRUPT, 0);
353 		if (status < B_OK) {
354 			TRACE_ALWAYS("write: failed to get write done sem 0x%08x\n", status);
355 			break;
356 		}
357 
358 		if (fStatusWrite != B_OK) {
359 			TRACE("write: device status error 0x%08x\n", fStatusWrite);
360 			status = gUSBModule->clear_feature(fWritePipe,
361 				USB_FEATURE_ENDPOINT_HALT);
362 			if (status < B_OK) {
363 				TRACE_ALWAYS("write: failed to clear device halt\n");
364 				status = B_ERROR;
365 				break;
366 			}
367 			continue;
368 		}
369 
370 		buffer += length;
371 		*numBytes += length;
372 		bytesLeft -= length;
373 	}
374 
375 	mutex_unlock(&fWriteLock);
376 	return status;
377 }
378 
379 
380 status_t
381 SerialDevice::Control(uint32 op, void *arg, size_t length)
382 {
383 	if (fDeviceRemoved)
384 		return B_DEV_NOT_READY;
385 
386 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
387 	if (!ddr)
388 		return B_NO_MEMORY;
389 
390 	status_t status = gTTYModule->ttycontrol(&fTTYFile, ddr, op, arg, length);
391 	gTTYModule->ddrdone(ddr);
392 	return status;
393 }
394 
395 
396 status_t
397 SerialDevice::Select(uint8 event, uint32 ref, selectsync *sync)
398 {
399 	if (fDeviceRemoved)
400 		return B_DEV_NOT_READY;
401 
402 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
403 	if (!ddr)
404 		return B_NO_MEMORY;
405 
406 	status_t status = gTTYModule->ttyselect(&fTTYFile, ddr, event, ref, sync);
407 	gTTYModule->ddrdone(ddr);
408 	return status;
409 }
410 
411 
412 status_t
413 SerialDevice::DeSelect(uint8 event, selectsync *sync)
414 {
415 	if (fDeviceRemoved)
416 		return B_DEV_NOT_READY;
417 
418 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
419 	if (!ddr)
420 		return B_NO_MEMORY;
421 
422 	status_t status = gTTYModule->ttydeselect(&fTTYFile, ddr, event, sync);
423 	gTTYModule->ddrdone(ddr);
424 	return status;
425 }
426 
427 
428 status_t
429 SerialDevice::Close()
430 {
431 	OnClose();
432 
433 	if (!fDeviceRemoved) {
434 		gUSBModule->cancel_queued_transfers(fReadPipe);
435 		gUSBModule->cancel_queued_transfers(fWritePipe);
436 		gUSBModule->cancel_queued_transfers(fControlPipe);
437 	}
438 
439 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
440 	if (!ddr)
441 		return B_NO_MEMORY;
442 
443 	status_t status = gTTYModule->ttyclose(&fTTYFile, ddr);
444 	gTTYModule->ddrdone(ddr);
445 
446 	fDeviceOpen = false;
447 	return status;
448 }
449 
450 
451 status_t
452 SerialDevice::Free()
453 {
454 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
455 	if (!ddr)
456 		return B_NO_MEMORY;
457 
458 	status_t status = gTTYModule->ttyfree(&fTTYFile, ddr);
459 	gTTYModule->ddrdone(ddr);
460 	return status;
461 }
462 
463 
464 void
465 SerialDevice::Removed()
466 {
467 	if (fDeviceRemoved)
468 		return;
469 
470 	// notifies us that the device was removed
471 	fDeviceRemoved = true;
472 
473 	// we need to ensure that we do not use the device anymore
474 	fStopDeviceThread = true;
475 	fInputStopped = false;
476 	gUSBModule->cancel_queued_transfers(fReadPipe);
477 	gUSBModule->cancel_queued_transfers(fWritePipe);
478 	gUSBModule->cancel_queued_transfers(fControlPipe);
479 
480 	int32 result = B_OK;
481 	wait_for_thread(fDeviceThread, &result);
482 	fDeviceThread = -1;
483 
484 	mutex_lock(&fWriteLock);
485 	mutex_unlock(&fWriteLock);
486 }
487 
488 
489 status_t
490 SerialDevice::AddDevice(const usb_configuration_info *config)
491 {
492 	// default implementation - does nothing
493 	return B_ERROR;
494 }
495 
496 
497 status_t
498 SerialDevice::ResetDevice()
499 {
500 	// default implementation - does nothing
501 	return B_OK;
502 }
503 
504 
505 status_t
506 SerialDevice::SetLineCoding(usb_serial_line_coding *coding)
507 {
508 	// default implementation - does nothing
509 	return B_OK;
510 }
511 
512 
513 status_t
514 SerialDevice::SetControlLineState(uint16 state)
515 {
516 	// default implementation - does nothing
517 	return B_OK;
518 }
519 
520 
521 void
522 SerialDevice::OnRead(char **buffer, size_t *numBytes)
523 {
524 	// default implementation - does nothing
525 }
526 
527 
528 void
529 SerialDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes)
530 {
531 	// default implementation - does nothing
532 }
533 
534 
535 void
536 SerialDevice::OnClose()
537 {
538 	// default implementation - does nothing
539 }
540 
541 
542 int32
543 SerialDevice::DeviceThread(void *data)
544 {
545 	SerialDevice *device = (SerialDevice *)data;
546 
547 	while (!device->fStopDeviceThread) {
548 		status_t status = gUSBModule->queue_bulk(device->fReadPipe,
549 			device->fReadBuffer, device->fReadBufferSize,
550 			device->ReadCallbackFunction, data);
551 		if (status < B_OK) {
552 			TRACE_ALWAYS("device thread: queueing failed with error: 0x%08x\n", status);
553 			break;
554 		}
555 
556 		status = acquire_sem_etc(device->fDoneRead, 1, B_CAN_INTERRUPT, 0);
557 		if (status < B_OK) {
558 			TRACE_ALWAYS("device thread: failed to get read done sem 0x%08x\n", status);
559 			break;
560 		}
561 
562 		if (device->fStatusRead != B_OK) {
563 			TRACE("device thread: device status error 0x%08x\n",
564 				device->fStatusRead);
565 			if (gUSBModule->clear_feature(device->fReadPipe,
566 				USB_FEATURE_ENDPOINT_HALT) != B_OK) {
567 				TRACE_ALWAYS("device thread: failed to clear halt feature\n");
568 				break;
569 			}
570 		}
571 
572 		char *buffer = device->fReadBuffer;
573 		size_t readLength = device->fActualLengthRead;
574 		device->OnRead(&buffer, &readLength);
575 		if (readLength == 0)
576 			continue;
577 
578 		ddrover *ddr = gTTYModule->ddrstart(NULL);
579 		if (!ddr) {
580 			TRACE_ALWAYS("device thread: ddrstart problem\n");
581 			return B_NO_MEMORY;
582 		}
583 
584 		while (device->fInputStopped)
585 			snooze(100);
586 
587 		gTTYModule->ttyilock(&device->fTTY, ddr, true);
588 		for (size_t i = 0; i < readLength; i++)
589 			gTTYModule->ttyin(&device->fTTY, ddr, buffer[i]);
590 
591 		gTTYModule->ttyilock(&device->fTTY, ddr, false);
592 		gTTYModule->ddrdone(ddr);
593 	}
594 
595 	return B_OK;
596 }
597 
598 
599 void
600 SerialDevice::ReadCallbackFunction(void *cookie, int32 status, void *data,
601 	uint32 actualLength)
602 {
603 	TRACE_FUNCALLS("read callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
604 		cookie, status, data, actualLength);
605 
606 	SerialDevice *device = (SerialDevice *)cookie;
607 	device->fActualLengthRead = actualLength;
608 	device->fStatusRead = status;
609 	release_sem_etc(device->fDoneRead, 1, B_DO_NOT_RESCHEDULE);
610 }
611 
612 
613 void
614 SerialDevice::WriteCallbackFunction(void *cookie, int32 status, void *data,
615 	uint32 actualLength)
616 {
617 	TRACE_FUNCALLS("write callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
618 		cookie, status, data, actualLength);
619 
620 	SerialDevice *device = (SerialDevice *)cookie;
621 	device->fActualLengthWrite = actualLength;
622 	device->fStatusWrite = status;
623 	release_sem_etc(device->fDoneWrite, 1, B_DO_NOT_RESCHEDULE);
624 }
625 
626 
627 void
628 SerialDevice::InterruptCallbackFunction(void *cookie, int32 status,
629 	void *data, uint32 actualLength)
630 {
631 	TRACE_FUNCALLS("interrupt callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
632 		cookie, status, data, actualLength);
633 
634 	SerialDevice *device = (SerialDevice *)cookie;
635 	device->fActualLengthInterrupt = actualLength;
636 	device->fStatusInterrupt = status;
637 
638 	// ToDo: maybe handle those somehow?
639 
640 	if (status == B_OK && !device->fDeviceRemoved) {
641 		status = gUSBModule->queue_interrupt(device->fControlPipe,
642 			device->fInterruptBuffer, device->fInterruptBufferSize,
643 			device->InterruptCallbackFunction, device);
644 	}
645 }
646 
647 
648 
649 SerialDevice *
650 SerialDevice::MakeDevice(usb_device device, uint16 vendorID,
651 	uint16 productID)
652 {
653 	const char *description = NULL;
654 
655 	switch (vendorID) {
656 		case VENDOR_IODATA:
657 		case VENDOR_ATEN:
658 		case VENDOR_TDK:
659 		case VENDOR_RATOC:
660 		case VENDOR_PROLIFIC:
661 		case VENDOR_ELECOM:
662 		case VENDOR_SOURCENEXT:
663 		case VENDOR_HAL:
664 		{
665 			switch (productID) {
666 				case PRODUCT_PROLIFIC_RSAQ2: description = "PL2303 Serial adapter (IODATA USB-RSAQ2)"; break;
667 				case PRODUCT_IODATA_USBRSAQ: description = "I/O Data USB serial adapter USB-RSAQ1"; break;
668 				case PRODUCT_ATEN_UC232A: description = "Aten Serial adapter"; break;
669 				case PRODUCT_TDK_UHA6400: description = "TDK USB-PHS Adapter UHA6400"; break;
670 				case PRODUCT_RATOC_REXUSB60: description = "Ratoc USB serial adapter REX-USB60"; break;
671 				case PRODUCT_PROLIFIC_PL2303: description = "PL2303 Serial adapter (ATEN/IOGEAR UC232A)"; break;
672 				case PRODUCT_ELECOM_UCSGT: description = "Elecom UC-SGT"; break;
673 				case PRODUCT_SOURCENEXT_KEIKAI8: description = "SOURCENEXT KeikaiDenwa 8"; break;
674 				case PRODUCT_SOURCENEXT_KEIKAI8_CHG: description = "SOURCENEXT KeikaiDenwa 8 with charger"; break;
675 				case PRODUCT_HAL_IMR001: description = "HAL Corporation Crossam2+USB"; break;
676 			}
677 
678 			if (!description)
679 				break;
680 
681 			return new ProlificDevice(device, vendorID, productID, description);
682 		}
683 
684 		case VENDOR_FTDI:
685 		{
686 			switch (productID) {
687 				case PRODUCT_FTDI_8U100AX: description = "FTDI 8U100AX serial converter"; break;
688 				case PRODUCT_FTDI_8U232AM: description = "FTDI 8U232AM serial converter"; break;
689 			}
690 
691 			if (!description)
692 				break;
693 
694 			return new FTDIDevice(device, vendorID, productID, description);
695 		}
696 
697 		case VENDOR_PALM:
698 		case VENDOR_KLSI:
699 		{
700 			switch (productID) {
701 				case PRODUCT_PALM_CONNECT: description = "PalmConnect RS232"; break;
702 				case PRODUCT_KLSI_KL5KUSB105D: description = "KLSI KL5KUSB105D"; break;
703 			}
704 
705 			if (!description)
706 				break;
707 
708 			return new KLSIDevice(device, vendorID, productID, description);
709 		}
710 	}
711 
712 	return new ACMDevice(device, vendorID, productID, "CDC ACM compatible device");
713 }
714