xref: /haiku/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp (revision e2932f63b00ba76ab3769a9c217754a4d03868ca)
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 <new>
9 
10 #include "SerialDevice.h"
11 #include "USB3.h"
12 
13 #include "ACM.h"
14 #include "FTDI.h"
15 #include "KLSI.h"
16 #include "Prolific.h"
17 #include "Silicon.h"
18 
19 #include <sys/ioctl.h>
20 
21 
22 SerialDevice::SerialDevice(usb_device device, uint16 vendorID,
23 	uint16 productID, const char *description)
24 	:	fDevice(device),
25 		fVendorID(vendorID),
26 		fProductID(productID),
27 		fDescription(description),
28 		fDeviceOpen(false),
29 		fDeviceRemoved(false),
30 		fControlPipe(0),
31 		fReadPipe(0),
32 		fWritePipe(0),
33 		fBufferArea(-1),
34 		fReadBuffer(NULL),
35 		fReadBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
36 		fOutputBuffer(NULL),
37 		fOutputBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
38 		fWriteBuffer(NULL),
39 		fWriteBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
40 		fInterruptBuffer(NULL),
41 		fInterruptBufferSize(16),
42 		fDoneRead(-1),
43 		fDoneWrite(-1),
44 		fControlOut(0),
45 		fInputStopped(false),
46 		fMasterTTY(NULL),
47 		fSlaveTTY(NULL),
48 		fSystemTTYCookie(NULL),
49 		fDeviceTTYCookie(NULL),
50 		fInputThread(-1),
51 		fStopThreads(false)
52 {
53 	memset(&fTTYConfig, 0, sizeof(termios));
54 	fTTYConfig.c_cflag = B9600 | CS8 | CREAD;
55 }
56 
57 
58 SerialDevice::~SerialDevice()
59 {
60 	Removed();
61 
62 	if (fDoneRead >= 0)
63 		delete_sem(fDoneRead);
64 	if (fDoneWrite >= 0)
65 		delete_sem(fDoneWrite);
66 
67 	if (fBufferArea >= 0)
68 		delete_area(fBufferArea);
69 }
70 
71 
72 status_t
73 SerialDevice::Init()
74 {
75 	fDoneRead = create_sem(0, "usb_serial:done_read");
76 	if (fDoneRead < 0)
77 		return fDoneRead;
78 
79 	fDoneWrite = create_sem(0, "usb_serial:done_write");
80 	if (fDoneWrite < 0)
81 		return fDoneWrite;
82 
83 	size_t totalBuffers = fReadBufferSize + fOutputBufferSize + fWriteBufferSize
84 		+ fInterruptBufferSize;
85 	fBufferArea = create_area("usb_serial:buffers_area", (void **)&fReadBuffer,
86 		B_ANY_KERNEL_ADDRESS, ROUNDUP(totalBuffers, B_PAGE_SIZE), B_CONTIGUOUS,
87 		B_READ_AREA | B_WRITE_AREA);
88 	if (fBufferArea < 0)
89 		return fBufferArea;
90 
91 	fOutputBuffer = fReadBuffer + fReadBufferSize;
92 	fWriteBuffer = fOutputBuffer + fOutputBufferSize;
93 	fInterruptBuffer = fWriteBuffer + fWriteBufferSize;
94 	return B_OK;
95 }
96 
97 
98 void
99 SerialDevice::SetControlPipe(usb_pipe handle)
100 {
101 	fControlPipe = handle;
102 }
103 
104 
105 void
106 SerialDevice::SetReadPipe(usb_pipe handle)
107 {
108 	fReadPipe = handle;
109 }
110 
111 
112 void
113 SerialDevice::SetWritePipe(usb_pipe handle)
114 {
115 	fWritePipe = handle;
116 }
117 
118 
119 inline int32
120 baud_index_to_speed(int index)
121 {
122 	switch (index) {
123 		case B0: return 0;
124 		case B50: return 50;
125 		case B75: return 75;
126 		case B110: return 110;
127 		case B134: return 134;
128 		case B150: return 150;
129 		case B200: return 200;
130 		case B300: return 300;
131 		case B600: return 600;
132 		case B1200: return 1200;
133 		case B1800: return 1800;
134 		case B2400: return 2400;
135 		case B4800: return 4800;
136 		case B9600: return 9600;
137 		case B19200: return 19200;
138 		case B31250: return 31250;
139 		case B38400: return 38400;
140 		case B57600: return 57600;
141 		case B115200: return 115200;
142 		case B230400: return 230400;
143 	}
144 
145 	TRACE_ALWAYS("invalid baud index %d\n", index);
146 	return -1;
147 }
148 
149 
150 void
151 SerialDevice::SetModes(struct termios *tios)
152 {
153 	TRACE_FUNCRES(trace_termios, tios);
154 
155 	uint8 baud = tios->c_cflag & CBAUD;
156 	int32 speed = baud_index_to_speed(baud);
157 	if (speed < 0) {
158 		baud = B19200;
159 		speed = 19200;
160 	}
161 
162 	// update our master config in full
163 	memcpy(&fTTYConfig, tios, sizeof(termios));
164 	fTTYConfig.c_cflag &= ~CBAUD;
165 	fTTYConfig.c_cflag |= baud;
166 
167 	// only apply the relevant parts to the device side
168 	termios config;
169 	memset(&config, 0, sizeof(termios));
170 	config.c_cflag = tios->c_cflag;
171 	config.c_cflag &= ~CBAUD;
172 	config.c_cflag |= baud;
173 
174 	// update the termios of the device side
175 	gTTYModule->tty_control(fDeviceTTYCookie, TCSETA, &config, sizeof(termios));
176 
177 	usb_cdc_line_coding lineCoding;
178 	lineCoding.speed = speed;
179 	lineCoding.stopbits = (tios->c_cflag & CSTOPB)
180 		? USB_CDC_LINE_CODING_2_STOPBITS : USB_CDC_LINE_CODING_1_STOPBIT;
181 
182 	if (tios->c_cflag & PARENB) {
183 		lineCoding.parity = USB_CDC_LINE_CODING_EVEN_PARITY;
184 		if (tios->c_cflag & PARODD)
185 			lineCoding.parity = USB_CDC_LINE_CODING_ODD_PARITY;
186 	} else
187 		lineCoding.parity = USB_CDC_LINE_CODING_NO_PARITY;
188 
189 	lineCoding.databits = (tios->c_cflag & CS8) ? 8 : 7;
190 
191 	if (memcmp(&lineCoding, &fLineCoding, sizeof(usb_cdc_line_coding)) != 0) {
192 		fLineCoding.speed = lineCoding.speed;
193 		fLineCoding.stopbits = lineCoding.stopbits;
194 		fLineCoding.databits = lineCoding.databits;
195 		fLineCoding.parity = lineCoding.parity;
196 		TRACE("send to modem: speed %d sb: 0x%08x db: 0x%08x parity: 0x%08x\n",
197 			fLineCoding.speed, fLineCoding.stopbits, fLineCoding.databits,
198 			fLineCoding.parity);
199 		SetLineCoding(&fLineCoding);
200 	}
201 }
202 
203 
204 bool
205 SerialDevice::Service(struct tty *tty, uint32 op, void *buffer, size_t length)
206 {
207 	if (tty != fMasterTTY)
208 		return false;
209 
210 	switch (op) {
211 		case TTYENABLE:
212 		{
213 			bool enable = *(bool *)buffer;
214 			TRACE("TTYENABLE: %sable\n", enable ? "en" : "dis");
215 
216 			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD, enable);
217 			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS, enable);
218 
219 			fControlOut = enable ? USB_CDC_CONTROL_SIGNAL_STATE_DTR
220 				| USB_CDC_CONTROL_SIGNAL_STATE_RTS : 0;
221 			SetControlLineState(fControlOut);
222 			return true;
223 		}
224 
225 		case TTYISTOP:
226 			fInputStopped = *(bool *)buffer;
227 			TRACE("TTYISTOP: %sstopped\n", fInputStopped ? "" : "not ");
228 			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS,
229 				!fInputStopped);
230 			return true;
231 
232 		case TTYGETSIGNALS:
233 			TRACE("TTYGETSIGNALS\n");
234 			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD,
235 				(fControlOut & (USB_CDC_CONTROL_SIGNAL_STATE_DTR
236 					| USB_CDC_CONTROL_SIGNAL_STATE_RTS)) != 0);
237 			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS,
238 				!fInputStopped);
239 			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDSR, false);
240 			gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWRI, false);
241 			return true;
242 
243 		case TTYSETMODES:
244 			TRACE("TTYSETMODES\n");
245 			SetModes((struct termios *)buffer);
246 			return true;
247 
248 		case TTYSETDTR:
249 		case TTYSETRTS:
250 		{
251 			bool set = *(bool *)buffer;
252 			uint8 bit = op == TTYSETDTR ? USB_CDC_CONTROL_SIGNAL_STATE_DTR
253 				: USB_CDC_CONTROL_SIGNAL_STATE_RTS;
254 			if (set)
255 				fControlOut |= bit;
256 			else
257 				fControlOut &= ~bit;
258 
259 			SetControlLineState(fControlOut);
260 			return true;
261 		}
262 
263 		case TTYOSTART:
264 		case TTYOSYNC:
265 		case TTYSETBREAK:
266 			TRACE("TTY other\n");
267 			return true;
268 	}
269 
270 	return false;
271 }
272 
273 
274 status_t
275 SerialDevice::Open(uint32 flags)
276 {
277 	if (fDeviceOpen)
278 		return B_BUSY;
279 
280 	if (fDeviceRemoved)
281 		return B_DEV_NOT_READY;
282 
283 	fMasterTTY = gTTYModule->tty_create(usb_serial_service, true);
284 	if (fMasterTTY == NULL) {
285 		TRACE_ALWAYS("open: failed to init master tty\n");
286 		return B_NO_MEMORY;
287 	}
288 
289 	fSlaveTTY = gTTYModule->tty_create(usb_serial_service, false);
290 	if (fSlaveTTY == NULL) {
291 		TRACE_ALWAYS("open: failed to init slave tty\n");
292 		gTTYModule->tty_destroy(fMasterTTY);
293 		return B_NO_MEMORY;
294 	}
295 
296 	fSystemTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY,
297 		O_RDWR);
298 	if (fSystemTTYCookie == NULL) {
299 		TRACE_ALWAYS("open: failed to init system tty cookie\n");
300 		gTTYModule->tty_destroy(fMasterTTY);
301 		gTTYModule->tty_destroy(fSlaveTTY);
302 		return B_NO_MEMORY;
303 	}
304 
305 	fDeviceTTYCookie = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY,
306 		O_RDWR);
307 	if (fDeviceTTYCookie == NULL) {
308 		TRACE_ALWAYS("open: failed to init device tty cookie\n");
309 		gTTYModule->tty_destroy_cookie(fSystemTTYCookie);
310 		gTTYModule->tty_destroy(fMasterTTY);
311 		gTTYModule->tty_destroy(fSlaveTTY);
312 		return B_NO_MEMORY;
313 	}
314 
315 	ResetDevice();
316 
317 	fStopThreads = false;
318 
319 	fInputThread = spawn_kernel_thread(_InputThread,
320 		"usb_serial input thread", B_NORMAL_PRIORITY, this);
321 	if (fInputThread < 0) {
322 		TRACE_ALWAYS("open: failed to spawn input thread\n");
323 		return fInputThread;
324 	}
325 
326 	resume_thread(fInputThread);
327 
328 	fControlOut = USB_CDC_CONTROL_SIGNAL_STATE_DTR
329 		| USB_CDC_CONTROL_SIGNAL_STATE_RTS;
330 	SetControlLineState(fControlOut);
331 
332 	status_t status = gUSBModule->queue_interrupt(fControlPipe,
333 		fInterruptBuffer, fInterruptBufferSize, _InterruptCallbackFunction,
334 		this);
335 	if (status < B_OK)
336 		TRACE_ALWAYS("failed to queue initial interrupt\n");
337 
338 	// set our config (will propagate to the slave config as well in SetModes()
339 	gTTYModule->tty_control(fSystemTTYCookie, TCSETA, &fTTYConfig,
340 		sizeof(termios));
341 
342 	fDeviceOpen = true;
343 	return B_OK;
344 }
345 
346 
347 status_t
348 SerialDevice::Read(char *buffer, size_t *numBytes)
349 {
350 	if (fDeviceRemoved) {
351 		*numBytes = 0;
352 		return B_DEV_NOT_READY;
353 	}
354 
355 	return gTTYModule->tty_read(fSystemTTYCookie, buffer, numBytes);
356 }
357 
358 
359 status_t
360 SerialDevice::Write(const char *buffer, size_t *numBytes)
361 {
362 	if (fDeviceRemoved) {
363 		*numBytes = 0;
364 		return B_DEV_NOT_READY;
365 	}
366 
367 	size_t bytesLeft = *numBytes;
368 	*numBytes = 0;
369 
370 	while (bytesLeft > 0) {
371 		size_t length = MIN(bytesLeft, 256);
372 			// TODO: This is an ugly hack; We use a small buffer size so that
373 			// we don't overrun the tty line buffer and cause it to block. While
374 			// that isn't a problem, we shouldn't just hardcode the value here.
375 
376 		status_t result = gTTYModule->tty_write(fSystemTTYCookie, buffer,
377 			&length);
378 		if (result != B_OK) {
379 			TRACE_ALWAYS("failed to write to tty: %s\n", strerror(result));
380 			return result;
381 		}
382 
383 		buffer += length;
384 		*numBytes += length;
385 		bytesLeft -= length;
386 
387 		while (true) {
388 			// Write to the device as long as there's anything in the tty buffer
389 			int readable = 0;
390 			gTTYModule->tty_control(fDeviceTTYCookie, FIONREAD, &readable,
391 				sizeof(readable));
392 			if (readable == 0)
393 				break;
394 
395 			result = _WriteToDevice();
396 			if (result != B_OK) {
397 				TRACE_ALWAYS("failed to write to device: %s\n",
398 					strerror(result));
399 				return result;
400 			}
401 		}
402 	}
403 
404 	if (*numBytes > 0)
405 		return B_OK;
406 
407 	return B_ERROR;
408 }
409 
410 
411 status_t
412 SerialDevice::Control(uint32 op, void *arg, size_t length)
413 {
414 	if (fDeviceRemoved)
415 		return B_DEV_NOT_READY;
416 
417 	return gTTYModule->tty_control(fSystemTTYCookie, op, arg, length);
418 }
419 
420 
421 status_t
422 SerialDevice::Select(uint8 event, uint32 ref, selectsync *sync)
423 {
424 	if (fDeviceRemoved)
425 		return B_DEV_NOT_READY;
426 
427 	return gTTYModule->tty_select(fSystemTTYCookie, event, ref, sync);
428 }
429 
430 
431 status_t
432 SerialDevice::DeSelect(uint8 event, selectsync *sync)
433 {
434 	if (fDeviceRemoved)
435 		return B_DEV_NOT_READY;
436 
437 	return gTTYModule->tty_deselect(fSystemTTYCookie, event, sync);
438 }
439 
440 
441 status_t
442 SerialDevice::Close()
443 {
444 	OnClose();
445 
446 	fStopThreads = true;
447 	fInputStopped = false;
448 
449 	if (!fDeviceRemoved) {
450 		gUSBModule->cancel_queued_transfers(fReadPipe);
451 		gUSBModule->cancel_queued_transfers(fWritePipe);
452 		gUSBModule->cancel_queued_transfers(fControlPipe);
453 	}
454 
455 	gTTYModule->tty_close_cookie(fSystemTTYCookie);
456 	gTTYModule->tty_close_cookie(fDeviceTTYCookie);
457 
458 	int32 result = B_OK;
459 	wait_for_thread(fInputThread, &result);
460 	fInputThread = -1;
461 
462 	gTTYModule->tty_destroy_cookie(fSystemTTYCookie);
463 	gTTYModule->tty_destroy_cookie(fDeviceTTYCookie);
464 
465 	gTTYModule->tty_destroy(fMasterTTY);
466 	gTTYModule->tty_destroy(fSlaveTTY);
467 
468 	fDeviceOpen = false;
469 	return B_OK;
470 }
471 
472 
473 status_t
474 SerialDevice::Free()
475 {
476 	return B_OK;
477 }
478 
479 
480 void
481 SerialDevice::Removed()
482 {
483 	if (fDeviceRemoved)
484 		return;
485 
486 	// notifies us that the device was removed
487 	fDeviceRemoved = true;
488 
489 	// we need to ensure that we do not use the device anymore
490 	fStopThreads = true;
491 	fInputStopped = false;
492 	gUSBModule->cancel_queued_transfers(fReadPipe);
493 	gUSBModule->cancel_queued_transfers(fWritePipe);
494 	gUSBModule->cancel_queued_transfers(fControlPipe);
495 }
496 
497 
498 status_t
499 SerialDevice::AddDevice(const usb_configuration_info *config)
500 {
501 	// default implementation - does nothing
502 	return B_ERROR;
503 }
504 
505 
506 status_t
507 SerialDevice::ResetDevice()
508 {
509 	// default implementation - does nothing
510 	return B_OK;
511 }
512 
513 
514 status_t
515 SerialDevice::SetLineCoding(usb_cdc_line_coding *coding)
516 {
517 	// default implementation - does nothing
518 	return B_OK;
519 }
520 
521 
522 status_t
523 SerialDevice::SetControlLineState(uint16 state)
524 {
525 	// default implementation - does nothing
526 	return B_OK;
527 }
528 
529 
530 void
531 SerialDevice::OnRead(char **buffer, size_t *numBytes)
532 {
533 	// default implementation - does nothing
534 }
535 
536 
537 void
538 SerialDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes)
539 {
540 	memcpy(fWriteBuffer, buffer, *numBytes);
541 }
542 
543 
544 void
545 SerialDevice::OnClose()
546 {
547 	// default implementation - does nothing
548 }
549 
550 
551 int32
552 SerialDevice::_InputThread(void *data)
553 {
554 	SerialDevice *device = (SerialDevice *)data;
555 
556 	while (!device->fStopThreads) {
557 		status_t status = gUSBModule->queue_bulk(device->fReadPipe,
558 			device->fReadBuffer, device->fReadBufferSize,
559 			device->_ReadCallbackFunction, data);
560 		if (status < B_OK) {
561 			TRACE_ALWAYS("input thread: queueing failed with error: 0x%08x\n",
562 				status);
563 			return status;
564 		}
565 
566 		status = acquire_sem_etc(device->fDoneRead, 1, B_CAN_INTERRUPT, 0);
567 		if (status < B_OK) {
568 			TRACE_ALWAYS("input thread: failed to get read done sem 0x%08x\n",
569 				status);
570 			return status;
571 		}
572 
573 		if (device->fStatusRead != B_OK) {
574 			TRACE("input thread: device status error 0x%08x\n",
575 				device->fStatusRead);
576 			if (device->fStatusRead == B_DEV_STALLED
577 				&& gUSBModule->clear_feature(device->fReadPipe,
578 					USB_FEATURE_ENDPOINT_HALT) != B_OK) {
579 				TRACE_ALWAYS("input thread: failed to clear halt feature\n");
580 				return B_ERROR;
581 			}
582 
583 			continue;
584 		}
585 
586 		char *buffer = device->fReadBuffer;
587 		size_t readLength = device->fActualLengthRead;
588 		device->OnRead(&buffer, &readLength);
589 		if (readLength == 0)
590 			continue;
591 
592 		while (device->fInputStopped)
593 			snooze(100);
594 
595 		status = gTTYModule->tty_write(device->fDeviceTTYCookie, buffer,
596 			&readLength);
597 		if (status != B_OK) {
598 			TRACE_ALWAYS("input thread: failed to write into TTY\n");
599 			return status;
600 		}
601 	}
602 
603 	return B_OK;
604 }
605 
606 
607 status_t
608 SerialDevice::_WriteToDevice()
609 {
610 	char *buffer = fOutputBuffer;
611 	size_t bytesLeft = fOutputBufferSize;
612 	status_t status = gTTYModule->tty_read(fDeviceTTYCookie, buffer,
613 		&bytesLeft);
614 	if (status != B_OK) {
615 		TRACE_ALWAYS("write to device: failed to read from TTY: %s\n",
616 			strerror(status));
617 		return status;
618 	}
619 
620 	while (!fDeviceRemoved && bytesLeft > 0) {
621 		size_t length = MIN(bytesLeft, fWriteBufferSize);
622 		size_t packetLength = length;
623 		OnWrite(buffer, &length, &packetLength);
624 
625 		status = gUSBModule->queue_bulk(fWritePipe, fWriteBuffer, packetLength,
626 			_WriteCallbackFunction, this);
627 		if (status != B_OK) {
628 			TRACE_ALWAYS("write to device: queueing failed with status "
629 				"0x%08x\n", status);
630 			return status;
631 		}
632 
633 		status = acquire_sem_etc(fDoneWrite, 1, B_CAN_INTERRUPT, 0);
634 		if (status != B_OK) {
635 			TRACE_ALWAYS("write to device: failed to get write done sem "
636 				"0x%08x\n", status);
637 			return status;
638 		}
639 
640 		if (fStatusWrite != B_OK) {
641 			TRACE("write to device: device status error 0x%08x\n",
642 				fStatusWrite);
643 			if (fStatusWrite == B_DEV_STALLED) {
644 				status = gUSBModule->clear_feature(fWritePipe,
645 					USB_FEATURE_ENDPOINT_HALT);
646 				if (status != B_OK) {
647 					TRACE_ALWAYS("write to device: failed to clear device "
648 						"halt\n");
649 					return B_ERROR;
650 				}
651 			}
652 
653 			continue;
654 		}
655 
656 		buffer += length;
657 		bytesLeft -= length;
658 	}
659 
660 	return B_OK;
661 }
662 
663 
664 void
665 SerialDevice::_ReadCallbackFunction(void *cookie, status_t status, void *data,
666 	uint32 actualLength)
667 {
668 	TRACE_FUNCALLS("read callback: cookie: 0x%08x status: 0x%08x data: 0x%08x "
669 		"length: %lu\n", cookie, status, data, actualLength);
670 
671 	SerialDevice *device = (SerialDevice *)cookie;
672 	device->fActualLengthRead = actualLength;
673 	device->fStatusRead = status;
674 	release_sem_etc(device->fDoneRead, 1, B_DO_NOT_RESCHEDULE);
675 }
676 
677 
678 void
679 SerialDevice::_WriteCallbackFunction(void *cookie, status_t status, void *data,
680 	uint32 actualLength)
681 {
682 	TRACE_FUNCALLS("write callback: cookie: 0x%08x status: 0x%08x data: 0x%08x "
683 		"length: %lu\n", cookie, status, data, actualLength);
684 
685 	SerialDevice *device = (SerialDevice *)cookie;
686 	device->fActualLengthWrite = actualLength;
687 	device->fStatusWrite = status;
688 	release_sem_etc(device->fDoneWrite, 1, B_DO_NOT_RESCHEDULE);
689 }
690 
691 
692 void
693 SerialDevice::_InterruptCallbackFunction(void *cookie, status_t status,
694 	void *data, uint32 actualLength)
695 {
696 	TRACE_FUNCALLS("interrupt callback: cookie: 0x%08x status: 0x%08x data: "
697 		"0x%08x len: %lu\n", cookie, status, data, actualLength);
698 
699 	SerialDevice *device = (SerialDevice *)cookie;
700 	device->fActualLengthInterrupt = actualLength;
701 	device->fStatusInterrupt = status;
702 
703 	// ToDo: maybe handle those somehow?
704 
705 	if (status == B_OK && !device->fDeviceRemoved) {
706 		status = gUSBModule->queue_interrupt(device->fControlPipe,
707 			device->fInterruptBuffer, device->fInterruptBufferSize,
708 			device->_InterruptCallbackFunction, device);
709 	}
710 }
711 
712 
713 SerialDevice *
714 SerialDevice::MakeDevice(usb_device device, uint16 vendorID,
715 	uint16 productID)
716 {
717 	const char *description = NULL;
718 
719 	switch (vendorID) {
720 		case VENDOR_IODATA:
721 		case VENDOR_ATEN:
722 		case VENDOR_TDK:
723 		case VENDOR_RATOC:
724 		case VENDOR_PROLIFIC:
725 		case VENDOR_ELECOM:
726 		case VENDOR_SOURCENEXT:
727 		case VENDOR_HAL:
728 		{
729 			switch (productID) {
730 				case PRODUCT_PROLIFIC_RSAQ2:
731 					description = "PL2303 Serial adapter (IODATA USB-RSAQ2)";
732 					break;
733 				case PRODUCT_IODATA_USBRSAQ:
734 					description = "I/O Data USB serial adapter USB-RSAQ1";
735 					break;
736 				case PRODUCT_ATEN_UC232A:
737 					description = "Aten Serial adapter";
738 					break;
739 				case PRODUCT_TDK_UHA6400:
740 					description = "TDK USB-PHS Adapter UHA6400";
741 					break;
742 				case PRODUCT_RATOC_REXUSB60:
743 					description = "Ratoc USB serial adapter REX-USB60";
744 					break;
745 				case PRODUCT_PROLIFIC_PL2303:
746 					description = "PL2303 Serial adapter (ATEN/IOGEAR UC232A)";
747 					break;
748 				case PRODUCT_ELECOM_UCSGT:
749 					description = "Elecom UC-SGT";
750 					break;
751 				case PRODUCT_SOURCENEXT_KEIKAI8:
752 					description = "SOURCENEXT KeikaiDenwa 8";
753 					break;
754 				case PRODUCT_SOURCENEXT_KEIKAI8_CHG:
755 					description = "SOURCENEXT KeikaiDenwa 8 with charger";
756 					break;
757 				case PRODUCT_HAL_IMR001:
758 					description = "HAL Corporation Crossam2+USB";
759 					break;
760 			}
761 
762 			if (description == NULL)
763 				break;
764 
765 			return new(std::nothrow) ProlificDevice(device, vendorID, productID,
766 				description);
767 		}
768 
769 		case VENDOR_FTDI:
770 		{
771 			switch (productID) {
772 				case PRODUCT_FTDI_8U100AX:
773 					description = "FTDI 8U100AX serial converter";
774 					break;
775 				case PRODUCT_FTDI_8U232AM:
776 					description = "FTDI 8U232AM serial converter";
777 					break;
778 			}
779 
780 			if (description == NULL)
781 				break;
782 
783 			return new(std::nothrow) FTDIDevice(device, vendorID, productID,
784 				description);
785 		}
786 
787 		case VENDOR_PALM:
788 		case VENDOR_KLSI:
789 		{
790 			switch (productID) {
791 				case PRODUCT_PALM_CONNECT:
792 					description = "PalmConnect RS232";
793 					break;
794 				case PRODUCT_KLSI_KL5KUSB105D:
795 					description = "KLSI KL5KUSB105D";
796 					break;
797 			}
798 
799 			if (description == NULL)
800 				break;
801 
802 			return new(std::nothrow) KLSIDevice(device, vendorID, productID,
803 				description);
804 		}
805 
806 		case VENDOR_RENESAS:
807 		{
808 			switch (productID) {
809 				case 0x0053:
810 					description = "Renesas RX610 RX-Stick";
811 					break;
812 			}
813 
814 			if (description != NULL)
815 				goto SILICON;
816 			break;
817 		}
818 		case VENDOR_AKATOM:
819 		{
820 			switch (productID) {
821 				case 0x066A:
822 					description = "AKTAKOM ACE-1001";
823 					break;
824 			}
825 
826 			if (description != NULL)
827 				goto SILICON;
828 			break;
829 		}
830 		case VENDOR_PIRELLI:
831 		{
832 			switch (productID) {
833 				case 0xE000:
834 				case 0xE003:
835 					description = "Pirelli DP-L10 GSM Mobile";
836 					break;
837 			}
838 
839 			if (description != NULL)
840 				goto SILICON;
841 			break;
842 		}
843 		case VENDOR_CYPHERLAB:
844 		{
845 			switch (productID) {
846 				case 0x1000:
847 					description = "Cipherlab CCD Barcode Scanner";
848 					break;
849 			}
850 
851 			if (description != NULL)
852 				goto SILICON;
853 			break;
854 		}
855 		case VENDOR_GEMALTO:
856 		{
857 			switch (productID) {
858 				case 0x5501:
859 					description = "Gemalto contactless smartcard reader";
860 					break;
861 			}
862 
863 			if (description != NULL)
864 				goto SILICON;
865 			break;
866 		}
867 		case VENDOR_DIGIANSWER:
868 		{
869 			switch (productID) {
870 				case 0x000A:
871 					description = "Digianswer ZigBee MAC device";
872 					break;
873 			}
874 
875 			if (description != NULL)
876 				goto SILICON;
877 			break;
878 		}
879 		case VENDOR_MEI:
880 		{
881 			switch (productID) {
882 				case 0x1100:
883 				case 0x1101:
884 					description = "MEI Acceptor";
885 					break;
886 			}
887 
888 			if (description != NULL)
889 				goto SILICON;
890 			break;
891 		}
892 		case VENDOR_DYNASTREAM:
893 		{
894 			switch (productID) {
895 				case 0x1003:
896 				case 0x1004:
897 				case 0x1006:
898 					description = "Dynastream ANT development board";
899 					break;
900 			}
901 
902 			if (description != NULL)
903 				goto SILICON;
904 			break;
905 		}
906 		case VENDOR_KNOCKOFF:
907 		{
908 			switch (productID) {
909 				case 0xAA26:
910 					description = "Knock-off DCU-11";
911 					break;
912 			}
913 
914 			if (description != NULL)
915 				goto SILICON;
916 			break;
917 		}
918 		case VENDOR_SIEMENS:
919 		{
920 			switch (productID) {
921 				case 0x10C5:
922 					description = "Siemens MC60";
923 					break;
924 			}
925 
926 			if (description != NULL)
927 				goto SILICON;
928 			break;
929 		}
930 		case VENDOR_NOKIA:
931 		{
932 			switch (productID) {
933 				case 0xAC70:
934 					description = "Nokia CA-42";
935 					break;
936 			}
937 
938 			if (description != NULL)
939 				goto SILICON;
940 			break;
941 		}
942 		case VENDOR_SILICON:
943 		{
944 			switch (productID) {
945 				case 0x0F91:
946 				case 0x1101:
947 				case 0x1601:
948 				case 0x800A:
949 				case 0x803B:
950 				case 0x8044:
951 				case 0x804E:
952 				case 0x8053:
953 				case 0x8054:
954 				case 0x8066:
955 				case 0x806F:
956 				case 0x807A:
957 				case 0x80CA:
958 				case 0x80DD:
959 				case 0x80F6:
960 				case 0x8115:
961 				case 0x813D:
962 				case 0x813F:
963 				case 0x814A:
964 				case 0x814B:
965 				case 0x8156:
966 				case 0x815E:
967 				case 0x818B:
968 				case 0x819F:
969 				case 0x81A6:
970 				case 0x81AC:
971 				case 0x81AD:
972 				case 0x81C8:
973 				case 0x81E2:
974 				case 0x81E7:
975 				case 0x81E8:
976 				case 0x81F2:
977 				case 0x8218:
978 				case 0x822B:
979 				case 0x826B:
980 				case 0x8293:
981 				case 0x82F9:
982 				case 0x8341:
983 				case 0x8382:
984 				case 0x83A8:
985 				case 0x83D8:
986 				case 0x8411:
987 				case 0x8418:
988 				case 0x846E:
989 				case 0x8477:
990 				case 0x85EA:
991 				case 0x85EB:
992 				case 0x8664:
993 				case 0x8665:
994 				case 0xEA60:
995 				case 0xEA61:
996 				case 0xEA71:
997 				case 0xF001:
998 				case 0xF002:
999 				case 0xF003:
1000 				case 0xF004:
1001 					description = "Silicon Labs CP210x USB UART converter";
1002 					break;
1003 			}
1004 
1005 			if (description != NULL)
1006 				goto SILICON;
1007 			break;
1008 		}
1009 		case VENDOR_SILICON2:
1010 		{
1011 			switch (productID) {
1012 				case 0xEA61:
1013 					description = "Silicon Labs GPRS USB Modem";
1014 					break;
1015 			}
1016 
1017 			if (description != NULL)
1018 				goto SILICON;
1019 			break;
1020 		}
1021 		case VENDOR_SILICON3:
1022 		{
1023 			switch (productID) {
1024 				case 0xEA6A:
1025 					description = "Silicon Labs GPRS USB Modem 100EU";
1026 					break;
1027 			}
1028 
1029 			if (description != NULL)
1030 				goto SILICON;
1031 			break;
1032 		}
1033 		case VENDOR_BALTECH:
1034 		{
1035 			switch (productID) {
1036 				case 0x9999:
1037 					description = "Balteck card reader";
1038 					break;
1039 			}
1040 
1041 			if (description != NULL)
1042 				goto SILICON;
1043 			break;
1044 		}
1045 		case VENDOR_OWEN:
1046 		{
1047 			switch (productID) {
1048 				case 0x0004:
1049 					description = "Owen AC4 USB-RS485 Converter";
1050 					break;
1051 			}
1052 
1053 			if (description != NULL)
1054 				goto SILICON;
1055 			break;
1056 		}
1057 		case VENDOR_CLIPSAL:
1058 		{
1059 			switch (productID) {
1060 				case 0x0303:
1061 					description = "Clipsal 5500PCU C-Bus USB interface";
1062 					break;
1063 			}
1064 
1065 			if (description != NULL)
1066 				goto SILICON;
1067 			break;
1068 		}
1069 		case VENDOR_JABLOTRON:
1070 		{
1071 			switch (productID) {
1072 				case 0x0001:
1073 					description = "Jablotron serial interface";
1074 					break;
1075 			}
1076 
1077 			if (description != NULL)
1078 				goto SILICON;
1079 			break;
1080 		}
1081 		case VENDOR_WIENER:
1082 		{
1083 			switch (productID) {
1084 				case 0x0010:
1085 				case 0x0011:
1086 				case 0x0012:
1087 				case 0x0015:
1088 					description = "W-IE-NE-R Plein & Baus GmbH device";
1089 					break;
1090 			}
1091 
1092 			if (description != NULL)
1093 				goto SILICON;
1094 			break;
1095 		}
1096 		case VENDOR_WAVESENSE:
1097 		{
1098 			switch (productID) {
1099 				case 0xAAAA:
1100 					description = "Wavesense Jazz blood glucose meter";
1101 					break;
1102 			}
1103 
1104 			if (description != NULL)
1105 				goto SILICON;
1106 			break;
1107 		}
1108 		case VENDOR_VAISALA:
1109 		{
1110 			switch (productID) {
1111 				case 0x0200:
1112 					description = "Vaisala USB instrument";
1113 					break;
1114 			}
1115 
1116 			if (description != NULL)
1117 				goto SILICON;
1118 			break;
1119 		}
1120 		case VENDOR_ELV:
1121 		{
1122 			switch (productID) {
1123 				case 0xE00F:
1124 					description = "ELV USB I²C interface";
1125 					break;
1126 			}
1127 
1128 			if (description != NULL)
1129 				goto SILICON;
1130 			break;
1131 		}
1132 		case VENDOR_WAGO:
1133 		{
1134 			switch (productID) {
1135 				case 0x07A6:
1136 					description = "WAGO 750-923 USB Service";
1137 					break;
1138 			}
1139 
1140 			if (description != NULL)
1141 				goto SILICON;
1142 			break;
1143 		}
1144 		case VENDOR_DW700:
1145 		{
1146 			switch (productID) {
1147 				case 0x9500:
1148 					description = "DW700 GPS USB interface";
1149 					break;
1150 			}
1151 
1152 			if (description != NULL)
1153 				goto SILICON;
1154 			break;
1155 		}
1156 
1157 SILICON:
1158 		return new(std::nothrow) SiliconDevice(device, vendorID, productID,
1159 			description);
1160 	}
1161 
1162 	return new(std::nothrow) ACMDevice(device, vendorID, productID,
1163 		"CDC ACM compatible device");
1164 }
1165