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