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