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