xref: /haiku/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp (revision 89755088d790ff4fe36f8aa77dacb2bd15507108)
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 "SerialDevice.h"
9 #include "USB3.h"
10 
11 #include "ACM.h"
12 #include "FTDI.h"
13 #include "KLSI.h"
14 #include "Prolific.h"
15 
16 SerialDevice::SerialDevice(usb_device device, uint16 vendorID,
17 	uint16 productID, const char *description)
18 	:	fDevice(device),
19 		fVendorID(vendorID),
20 		fProductID(productID),
21 		fDescription(description),
22 		fControlPipe(0),
23 		fReadPipe(0),
24 		fWritePipe(0),
25 		fBufferArea(-1),
26 		fReadBuffer(NULL),
27 		fReadBufferSize(0),
28 		fWriteBuffer(NULL),
29 		fWriteBufferSize(0),
30 		fInterruptBuffer(NULL),
31 		fInterruptBufferSize(0),
32 		fDoneRead(-1),
33 		fDoneWrite(-1),
34 		fControlOut(0),
35 		fInputStopped(false),
36 		fDeviceThread(-1),
37 		fStopDeviceThread(false)
38 {
39 	memset(&fTTYFile, 0, sizeof(ttyfile));
40 	memset(&fTTY, 0, sizeof(tty));
41 }
42 
43 
44 SerialDevice::~SerialDevice()
45 {
46 	fStopDeviceThread = true;
47 	gUSBModule->cancel_queued_transfers(fReadPipe);
48 	gUSBModule->cancel_queued_transfers(fWritePipe);
49 	gUSBModule->cancel_queued_transfers(fControlPipe);
50 
51 	if (fDoneRead >= B_OK)
52 		delete_sem(fDoneRead);
53 	if (fDoneWrite >= B_OK)
54 		delete_sem(fDoneWrite);
55 
56 	int32 result = B_OK;
57 	wait_for_thread(fDeviceThread, &result);
58 
59 	if (fBufferArea >= B_OK)
60 		delete_area(fBufferArea);
61 
62 	benaphore_destroy(&fReadLock);
63 	benaphore_destroy(&fWriteLock);
64 }
65 
66 
67 status_t
68 SerialDevice::Init()
69 {
70 	fDoneRead = create_sem(0, "usb_serial:done_read");
71 	fDoneWrite = create_sem(0, "usb_serial:done_write");
72 	benaphore_init(&fReadLock, "usb_serial:read_lock");
73 	benaphore_init(&fWriteLock, "usb_serial:write_lock");
74 
75 	fReadBufferSize = fWriteBufferSize = ROUNDUP(DEF_BUFFER_SIZE, 16);
76 	fInterruptBufferSize = 16;
77 
78 	size_t totalBuffers = fReadBufferSize + fWriteBufferSize + fInterruptBufferSize;
79 	fBufferArea = create_area("usb_serial:buffers_area", (void **)&fReadBuffer,
80 		B_ANY_KERNEL_ADDRESS, ROUNDUP(totalBuffers, B_PAGE_SIZE), B_CONTIGUOUS,
81 		B_READ_AREA | B_WRITE_AREA);
82 
83 	fWriteBuffer = fReadBuffer + fReadBufferSize;
84 	fInterruptBuffer = fWriteBuffer + fWriteBufferSize;
85 	return B_OK;
86 }
87 
88 
89 void
90 SerialDevice::SetControlPipe(usb_pipe handle)
91 {
92 	fControlPipe = handle;
93 }
94 
95 
96 void
97 SerialDevice::SetReadPipe(usb_pipe handle)
98 {
99 	fReadPipe = handle;
100 }
101 
102 
103 void
104 SerialDevice::SetWritePipe(usb_pipe handle)
105 {
106 	fWritePipe = handle;
107 }
108 
109 
110 void
111 SerialDevice::SetModes()
112 {
113 	struct termios tios;
114 	memcpy(&tios, &fTTY.t, sizeof(struct termios));
115 	uint16 newControl = fControlOut;
116 	TRACE_FUNCRES(trace_termios, &tios);
117 
118 	static uint32 baudRates[] = {
119 		0x00000000, //B0
120 		0x00000032, //B50
121 		0x0000004B, //B75
122 		0x0000006E, //B110
123 		0x00000086, //B134
124 		0x00000096, //B150
125 		0x000000C8, //B200
126 		0x0000012C, //B300
127 		0x00000258, //B600
128 		0x000004B0, //B1200
129 		0x00000708, //B1800
130 		0x00000960, //B2400
131 		0x000012C0, //B4800
132 		0x00002580, //B9600
133 		0x00004B00, //B19200
134 		0x00009600, //B38400
135 		0x0000E100, //B57600
136 		0x0001C200, //B115200
137 		0x00038400, //B230400
138 		0x00070800, //460800
139 		0x000E1000, //921600
140 	};
141 
142 	uint32 baudCount = sizeof(baudRates) / sizeof(baudRates[0]);
143 	uint32 baudIndex = tios.c_cflag & CBAUD;
144 	if (baudIndex > baudCount)
145 		baudIndex = baudCount - 1;
146 
147 	usb_serial_line_coding lineCoding;
148 	lineCoding.speed = baudRates[baudIndex];
149 	lineCoding.stopbits = (tios.c_cflag & CSTOPB) ? LC_STOP_BIT_2 : LC_STOP_BIT_1;
150 
151 	if (tios.c_cflag & PARENB) {
152 		lineCoding.parity = LC_PARITY_EVEN;
153 		if (tios.c_cflag & PARODD)
154 			lineCoding.parity = LC_PARITY_ODD;
155 	} else
156 		lineCoding.parity = LC_PARITY_NONE;
157 
158 	lineCoding.databits = (tios.c_cflag & CS8) ? 8 : 7;
159 
160 	if (lineCoding.speed == 0) {
161 		newControl &= 0xfffffffe;
162 		lineCoding.speed = fLineCoding.speed;
163 	} else
164 		newControl = CLS_LINE_DTR;
165 
166 	if (fControlOut != newControl) {
167 		fControlOut = newControl;
168 		TRACE("newctrl send to modem: 0x%08x\n", newControl);
169 		SetControlLineState(newControl);
170 	}
171 
172 	if (memcmp(&lineCoding, &fLineCoding, sizeof(usb_serial_line_coding)) != 0) {
173 		fLineCoding.speed = lineCoding.speed;
174 		fLineCoding.stopbits = lineCoding.stopbits;
175 		fLineCoding.databits = lineCoding.databits;
176 		fLineCoding.parity = lineCoding.parity;
177 		TRACE("send to modem: speed %d sb: 0x%08x db: 0x%08x parity: 0x%08x\n",
178 			fLineCoding.speed, fLineCoding.stopbits, fLineCoding.databits,
179 			fLineCoding.parity);
180 		SetLineCoding(&fLineCoding);
181 	}
182 }
183 
184 
185 bool
186 SerialDevice::Service(struct tty *ptty, struct ddrover *ddr, uint flags)
187 {
188 	if (&fTTY != ptty)
189 		return false;
190 
191 	if (flags <= TTYGETSIGNALS) {
192 		switch (flags) {
193 			case TTYENABLE:
194 				TRACE("TTYENABLE\n");
195 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, false);
196 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true);
197 				fControlOut = CLS_LINE_DTR | CLS_LINE_RTS;
198 				SetControlLineState(fControlOut);
199 				break;
200 
201 			case TTYDISABLE:
202 				TRACE("TTYDISABLE\n");
203 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, false);
204 				fControlOut = 0x0;
205 				SetControlLineState(fControlOut);
206 				break;
207 
208 			case TTYISTOP:
209 				TRACE("TTYISTOP\n");
210 				fInputStopped = true;
211 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, false);
212 				break;
213 
214 			case TTYIRESUME:
215 				TRACE("TTYIRESUME\n");
216 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true);
217 				fInputStopped = false;
218 				break;
219 
220 			case TTYGETSIGNALS:
221 				TRACE("TTYGETSIGNALS\n");
222 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, true);
223 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true);
224 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDSR, false);
225 				gTTYModule->ttyhwsignal(ptty, ddr, TTYHWRI, false);
226 				break;
227 
228 			case TTYSETMODES:
229 				TRACE("TTYSETMODES\n");
230 				SetModes();
231 				break;
232 
233 			case TTYOSTART:
234 			case TTYOSYNC:
235 			case TTYSETBREAK:
236 			case TTYCLRBREAK:
237 			case TTYSETDTR:
238 			case TTYCLRDTR:
239 				TRACE("TTY other\n");
240 				break;
241 		}
242 
243 		return true;
244 	}
245 
246 	return false;
247 }
248 
249 
250 status_t
251 SerialDevice::Open(uint32 flags)
252 {
253 	gTTYModule->ttyinit(&fTTY, true);
254 	fTTYFile.tty = &fTTY;
255 	fTTYFile.flags = flags;
256 	ResetDevice();
257 
258 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
259 	if (!ddr)
260 		return B_NO_MEMORY;
261 
262 	gTTYModule->ddacquire(ddr, &gSerialDomain);
263 	status_t status = gTTYModule->ttyopen(&fTTYFile, ddr, usb_serial_service);
264 	gTTYModule->ddrdone(ddr);
265 
266 	if (status < B_OK) {
267 		TRACE_ALWAYS("open: failed to open tty\n");
268 		return status;
269 	}
270 
271 	fDeviceThread = spawn_kernel_thread(DeviceThread, "usb_serial device thread",
272 		B_NORMAL_PRIORITY, this);
273 
274 	if (fDeviceThread < B_OK) {
275 		TRACE_ALWAYS("open: failed to spawn kernel thread\n");
276 		return fDeviceThread;
277 	}
278 
279 	resume_thread(fDeviceThread);
280 
281 	fControlOut = CLS_LINE_DTR | CLS_LINE_RTS;
282 	SetControlLineState(fControlOut);
283 
284 	status = gUSBModule->queue_interrupt(fControlPipe, fInterruptBuffer,
285 		fInterruptBufferSize, InterruptCallbackFunction, this);
286 	if (status < B_OK)
287 		TRACE_ALWAYS("failed to queue initial interrupt\n");
288 	return status;
289 }
290 
291 
292 status_t
293 SerialDevice::Read(char *buffer, size_t *numBytes)
294 {
295 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
296 	if (!ddr) {
297 		*numBytes = 0;
298 		return B_NO_MEMORY;
299 	}
300 
301 	status_t status = benaphore_lock(&fReadLock);
302 	if (status != B_OK) {
303 		TRACE_ALWAYS("read: failed to get read lock\n");
304 		*numBytes = 0;
305 		return status;
306 	}
307 
308 	status = gTTYModule->ttyread(&fTTYFile, ddr, buffer, numBytes);
309 	gTTYModule->ddrdone(ddr);
310 
311 	benaphore_unlock(&fReadLock);
312 	return status;
313 }
314 
315 
316 status_t
317 SerialDevice::Write(const char *buffer, size_t *numBytes)
318 {
319 	size_t bytesLeft = *numBytes;
320 	*numBytes = 0;
321 
322 	status_t status = benaphore_lock(&fWriteLock);
323 	if (status != B_OK) {
324 		TRACE_ALWAYS("write: failed to get write lock\n");
325 		return status;
326 	}
327 
328 	while (bytesLeft > 0) {
329 		size_t length = MIN(bytesLeft, fWriteBufferSize);
330 		OnWrite(buffer, &length);
331 
332 		status = gUSBModule->queue_bulk(fWritePipe, fWriteBuffer,
333 			length, WriteCallbackFunction, this);
334 		if (status < B_OK) {
335 			TRACE_ALWAYS("write: queueing failed with status 0x%08x\n", status);
336 			break;
337 		}
338 
339 		status = acquire_sem_etc(fDoneWrite, 1, B_CAN_INTERRUPT, 0);
340 		if (status < B_OK) {
341 			TRACE_ALWAYS("write: failed to get write done sem 0x%08x\n", status);
342 			break;
343 		}
344 
345 		if (fStatusWrite != B_OK) {
346 			TRACE("write: device status error 0x%08x\n", fStatusWrite);
347 			status = gUSBModule->clear_feature(fWritePipe,
348 				USB_FEATURE_ENDPOINT_HALT);
349 			if (status < B_OK) {
350 				TRACE_ALWAYS("write: failed to clear device halt\n");
351 				status = B_ERROR;
352 				break;
353 			}
354 			continue;
355 		}
356 
357 		buffer += fActualLengthWrite;
358 		*numBytes += fActualLengthWrite;
359 		bytesLeft -= fActualLengthWrite;
360 	}
361 
362 	benaphore_unlock(&fWriteLock);
363 	return status;
364 }
365 
366 
367 status_t
368 SerialDevice::Control(uint32 op, void *arg, size_t length)
369 {
370 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
371 	if (!ddr)
372 		return B_NO_MEMORY;
373 
374 	status_t status = gTTYModule->ttycontrol(&fTTYFile, ddr, op, arg, length);
375 	gTTYModule->ddrdone(ddr);
376 	return status;
377 }
378 
379 
380 status_t
381 SerialDevice::Select(uint8 event, uint32 ref, selectsync *sync)
382 {
383 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
384 	if (!ddr)
385 		return B_NO_MEMORY;
386 
387 	status_t status = gTTYModule->ttyselect(&fTTYFile, ddr, event, ref, sync);
388 	gTTYModule->ddrdone(ddr);
389 	return status;
390 }
391 
392 
393 status_t
394 SerialDevice::DeSelect(uint8 event, selectsync *sync)
395 {
396 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
397 	if (!ddr)
398 		return B_NO_MEMORY;
399 
400 	status_t status = gTTYModule->ttydeselect(&fTTYFile, ddr, event, sync);
401 	gTTYModule->ddrdone(ddr);
402 	return status;
403 }
404 
405 
406 status_t
407 SerialDevice::Close()
408 {
409 	OnClose();
410 
411 	gUSBModule->cancel_queued_transfers(fReadPipe);
412 	gUSBModule->cancel_queued_transfers(fWritePipe);
413 	gUSBModule->cancel_queued_transfers(fControlPipe);
414 
415 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
416 	if (!ddr)
417 		return B_NO_MEMORY;
418 
419 	status_t status = gTTYModule->ttyclose(&fTTYFile, ddr);
420 	gTTYModule->ddrdone(ddr);
421 	return status;
422 }
423 
424 
425 status_t
426 SerialDevice::Free()
427 {
428 	struct ddrover *ddr = gTTYModule->ddrstart(NULL);
429 	if (!ddr)
430 		return B_NO_MEMORY;
431 
432 	status_t status = gTTYModule->ttyfree(&fTTYFile, ddr);
433 	gTTYModule->ddrdone(ddr);
434 	return status;
435 }
436 
437 
438 status_t
439 SerialDevice::AddDevice(const usb_configuration_info *config)
440 {
441 	// default implementation - does nothing
442 	return B_ERROR;
443 }
444 
445 
446 status_t
447 SerialDevice::ResetDevice()
448 {
449 	// default implementation - does nothing
450 	return B_OK;
451 }
452 
453 
454 status_t
455 SerialDevice::SetLineCoding(usb_serial_line_coding *coding)
456 {
457 	// default implementation - does nothing
458 	return B_OK;
459 }
460 
461 
462 status_t
463 SerialDevice::SetControlLineState(uint16 state)
464 {
465 	// default implementation - does nothing
466 	return B_OK;
467 }
468 
469 
470 void
471 SerialDevice::OnRead(char **buffer, size_t *numBytes)
472 {
473 	// default implementation - does nothing
474 }
475 
476 
477 void
478 SerialDevice::OnWrite(const char *buffer, size_t *numBytes)
479 {
480 	// default implementation - does nothing
481 }
482 
483 
484 void
485 SerialDevice::OnClose()
486 {
487 	// default implementation - does nothing
488 }
489 
490 
491 int32
492 SerialDevice::DeviceThread(void *data)
493 {
494 	SerialDevice *device = (SerialDevice *)data;
495 
496 	while (!device->fStopDeviceThread) {
497 		status_t status = gUSBModule->queue_bulk(device->fReadPipe,
498 			device->fReadBuffer, device->fReadBufferSize,
499 			device->ReadCallbackFunction, data);
500 		if (status < B_OK) {
501 			TRACE_ALWAYS("device thread: queueing failed with error: 0x%08x\n", status);
502 			break;
503 		}
504 
505 		status = acquire_sem_etc(device->fDoneRead, 1, B_CAN_INTERRUPT, 0);
506 		if (status < B_OK) {
507 			TRACE_ALWAYS("device thread: failed to get read done sem 0x%08x\n", status);
508 			break;
509 		}
510 
511 		if (device->fStatusRead != B_OK) {
512 			TRACE("device thread: device status error 0x%08x\n",
513 				device->fStatusRead);
514 			if (gUSBModule->clear_feature(device->fReadPipe,
515 				USB_FEATURE_ENDPOINT_HALT) != B_OK) {
516 				TRACE_ALWAYS("device thread: failed to clear halt feature\n");
517 				break;
518 			}
519 		}
520 
521 		char *buffer = device->fReadBuffer;
522 		size_t readLength = device->fActualLengthRead;
523 		device->OnRead(&buffer, &readLength);
524 		if (readLength == 0)
525 			continue;
526 
527 		ddrover *ddr = gTTYModule->ddrstart(NULL);
528 		if (!ddr) {
529 			TRACE_ALWAYS("device thread: ddrstart problem\n");
530 			return B_NO_MEMORY;
531 		}
532 
533 		while (device->fInputStopped)
534 			snooze(100);
535 
536 		gTTYModule->ttyilock(&device->fTTY, ddr, true);
537 		for (size_t i = 0; i < readLength; i++)
538 			gTTYModule->ttyin(&device->fTTY, ddr, buffer[i]);
539 
540 		gTTYModule->ttyilock(&device->fTTY, ddr, false);
541 		gTTYModule->ddrdone(ddr);
542 	}
543 
544 	return B_OK;
545 }
546 
547 
548 void
549 SerialDevice::ReadCallbackFunction(void *cookie, int32 status, void *data,
550 	uint32 actualLength)
551 {
552 	TRACE_FUNCALLS("read callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
553 		cookie, status, data, actualLength);
554 
555 	SerialDevice *device = (SerialDevice *)cookie;
556 	device->fActualLengthRead = actualLength;
557 	device->fStatusRead = status;
558 	release_sem_etc(device->fDoneRead, 1, B_DO_NOT_RESCHEDULE);
559 }
560 
561 
562 void
563 SerialDevice::WriteCallbackFunction(void *cookie, int32 status, void *data,
564 	uint32 actualLength)
565 {
566 	TRACE_FUNCALLS("write callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
567 		cookie, status, data, actualLength);
568 
569 	SerialDevice *device = (SerialDevice *)cookie;
570 	device->fActualLengthWrite = actualLength;
571 	device->fStatusWrite = status;
572 	release_sem_etc(device->fDoneWrite, 1, B_DO_NOT_RESCHEDULE);
573 }
574 
575 
576 void
577 SerialDevice::InterruptCallbackFunction(void *cookie, int32 status,
578 	void *data, uint32 actualLength)
579 {
580 	TRACE_FUNCALLS("interrupt callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
581 		cookie, status, data, actualLength);
582 
583 	SerialDevice *device = (SerialDevice *)cookie;
584 	device->fActualLengthInterrupt = actualLength;
585 	device->fStatusInterrupt = status;
586 
587 	// ToDo: maybe handle those somehow?
588 
589 	if (status == B_OK) {
590 		status = gUSBModule->queue_interrupt(device->fControlPipe,
591 			device->fInterruptBuffer, device->fInterruptBufferSize,
592 			device->InterruptCallbackFunction, device);
593 	}
594 }
595 
596 
597 
598 SerialDevice *
599 SerialDevice::MakeDevice(usb_device device, uint16 vendorID,
600 	uint16 productID)
601 {
602 	const char *description = NULL;
603 
604 	switch (vendorID) {
605 		case VENDOR_IODATA:
606 		case VENDOR_ATEN:
607 		case VENDOR_TDK:
608 		case VENDOR_RATOC:
609 		case VENDOR_PROLIFIC:
610 		case VENDOR_ELECOM:
611 		case VENDOR_SOURCENEXT:
612 		case VENDOR_HAL:
613 		{
614 			switch (productID) {
615 				case PRODUCT_PROLIFIC_RSAQ2: description = "PL2303 Serial adapter (IODATA USB-RSAQ2)"; break;
616 				case PRODUCT_IODATA_USBRSAQ: description = "I/O Data USB serial adapter USB-RSAQ1"; break;
617 				case PRODUCT_ATEN_UC232A: description = "Aten Serial adapter"; break;
618 				case PRODUCT_TDK_UHA6400: description = "TDK USB-PHS Adapter UHA6400"; break;
619 				case PRODUCT_RATOC_REXUSB60: description = "Ratoc USB serial adapter REX-USB60"; break;
620 				case PRODUCT_PROLIFIC_PL2303: description = "PL2303 Serial adapter (ATEN/IOGEAR UC232A)"; break;
621 				case PRODUCT_ELECOM_UCSGT: description = "Elecom UC-SGT"; break;
622 				case PRODUCT_SOURCENEXT_KEIKAI8: description = "SOURCENEXT KeikaiDenwa 8"; break;
623 				case PRODUCT_SOURCENEXT_KEIKAI8_CHG: description = "SOURCENEXT KeikaiDenwa 8 with charger"; break;
624 				case PRODUCT_HAL_IMR001: description = "HAL Corporation Crossam2+USB"; break;
625 			}
626 
627 			if (!description)
628 				break;
629 
630 			return new ProlificDevice(device, vendorID, productID, description);
631 		}
632 
633 		case VENDOR_FTDI:
634 		{
635 			switch (productID) {
636 				case PRODUCT_FTDI_8U100AX: description = "FTDI 8U100AX serial converter"; break;
637 				case PRODUCT_FTDI_8U232AM: description = "FTDI 8U232AM serial converter"; break;
638 			}
639 
640 			if (!description)
641 				break;
642 
643 			return new FTDIDevice(device, vendorID, productID, description);
644 		}
645 
646 		case VENDOR_PALM:
647 		case VENDOR_KLSI:
648 		{
649 			switch (productID) {
650 				case PRODUCT_PALM_CONNECT: description = "PalmConnect RS232"; break;
651 				case PRODUCT_KLSI_KL5KUSB105D: description = "KLSI KL5KUSB105D"; break;
652 			}
653 
654 			if (!description)
655 				break;
656 
657 			return new KLSIDevice(device, vendorID, productID, description);
658 		}
659 	}
660 
661 	return new ACMDevice(device, vendorID, productID, "CDC ACM compatible device");
662 }
663