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