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