xref: /haiku/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2  * Copyright 2009-2010, François Revol, <revol@free.fr>.
3  * Sponsored by TuneTracker Systems.
4  * Based on the Haiku usb_serial driver which is:
5  *
6  * Copyright (c) 2007-2008 by Michael Lotz
7  * Heavily based on the original usb_serial driver which is:
8  *
9  * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li>
10  * Distributed under the terms of the MIT License.
11  */
12 #include "SerialDevice.h"
13 #include "UART.h"
14 
15 #include <sys/ioctl.h>
16 
17 SerialDevice::SerialDevice(const struct serial_support_descriptor *device,
18 	uint32 ioBase, uint32 irq, const SerialDevice *master)
19 	:	/*fSupportDescriptor(device->descriptor),
20 		fDevice(device),
21 		fDescription(device->descriptor->name),*/
22 		fSupportDescriptor(device),
23 		fDevice(NULL),
24 		fDescription(device->name),
25 		//
26 		fDeviceOpen(false),
27 		fDeviceRemoved(false),
28 		fBus(device->bus),
29 		fIOBase(ioBase),
30 		fIRQ(irq),
31 		fMaster(master),
32 		fCachedIER(0x0),
33 		fCachedIIR(0x1),
34 		fPendingDPC(0),
35 		fReadBufferAvail(0),
36 		fReadBufferIn(0),
37 		fReadBufferOut(0),
38 		fReadBufferSem(-1),
39 		fWriteBufferAvail(0),
40 		fWriteBufferIn(0),
41 		fWriteBufferOut(0),
42 		fWriteBufferSem(-1),
43 		fDoneRead(-1),
44 		fDoneWrite(-1),
45 		fControlOut(0),
46 		fInputStopped(false),
47 		fMasterTTY(NULL),
48 		fSlaveTTY(NULL),
49 		fSystemTTYCookie(NULL),
50 		fDeviceTTYCookie(NULL),
51 		fDeviceThread(-1),
52 		fStopDeviceThread(false)
53 {
54 	memset(&fTTYConfig, 0, sizeof(termios));
55 	fTTYConfig.c_cflag = B9600 | CS8 | CREAD;
56 	memset(fReadBuffer, 'z', DEF_BUFFER_SIZE);
57 	memset(fWriteBuffer, 'z', DEF_BUFFER_SIZE);
58 }
59 
60 
61 SerialDevice::~SerialDevice()
62 {
63 	Removed();
64 
65 	if (fDoneWrite >= B_OK)
66 		delete_sem(fDoneWrite);
67 	if (fReadBufferSem >= B_OK)
68 		delete_sem(fReadBufferSem);
69 	if (fWriteBufferSem >= B_OK)
70 		delete_sem(fWriteBufferSem);
71 }
72 
73 
74 bool
75 SerialDevice::Probe()
76 {
77 	uint8 msr;
78 	msr = ReadReg8(MSR);
79 	// just in case read twice to make sure the "delta" bits are 0
80 	msr = ReadReg8(MSR);
81 	// this should be enough to probe for the device for now
82 	// we might want to check the scratch reg, and try identifying
83 	// the model as in:
84 	// http://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming#Software_Identification_of_the_UART
85 	return (msr != 0xff);
86 }
87 
88 
89 status_t
90 SerialDevice::Init()
91 {
92 	fDoneWrite = create_sem(0, "pc_serial:done_write");
93 	fReadBufferSem = create_sem(0, "pc_serial:done_read");
94 	fWriteBufferSem = create_sem(0, "pc_serial:done_write");
95 
96 	// disable IRQ
97 	fCachedIER = 0;
98 	WriteReg8(IER, fCachedIER);
99 
100 	// disable DLAB
101 	WriteReg8(LCR, 0);
102 
103 	return B_OK;
104 }
105 
106 
107 void
108 SerialDevice::SetModes(struct termios *tios)
109 {
110 	//TRACE_FUNCRES(trace_termios, tios);
111 	spin(10000);
112 	uint32 baudIndex = tios->c_cflag & CBAUD;
113 	if (baudIndex > BLAST)
114 		baudIndex = BLAST;
115 
116 	// update our master config in full
117 	memcpy(&fTTYConfig, tios, sizeof(termios));
118 	fTTYConfig.c_cflag &= ~CBAUD;
119 	fTTYConfig.c_cflag |= baudIndex;
120 
121 	// only apply the relevant parts to the device side
122 	termios config;
123 	memset(&config, 0, sizeof(termios));
124 	config.c_cflag = tios->c_cflag;
125 	config.c_cflag &= ~CBAUD;
126 	config.c_cflag |= baudIndex;
127 
128 	// update the termios of the device side
129 	gTTYModule->tty_control(fDeviceTTYCookie, TCSETA, &config, sizeof(termios));
130 
131 	uint8 lcr = 0;
132 	uint16 divisor = SupportDescriptor()->bauds[baudIndex];
133 
134 	switch (tios->c_cflag & CSIZE) {
135 #if	CS5 != CS7
136 	// in case someday...
137 	case CS5:
138 		lcr |= LCR_5BIT;
139 		break;
140 	case CS6:
141 		lcr |= LCR_6BIT;
142 		break;
143 #endif
144 	case CS7:
145 		lcr |= LCR_7BIT;
146 		break;
147 	case CS8:
148 	default:
149 		lcr |= LCR_8BIT;
150 		break;
151 	}
152 
153 	if (tios->c_cflag & CSTOPB)
154 		lcr |= LCR_2STOP;
155 	if (tios->c_cflag & PARENB)
156 		lcr |= LCR_P_EN;
157 	if ((tios->c_cflag & PARODD) == 0)
158 		lcr |= LCR_P_EVEN;
159 
160 	if (baudIndex == B0) {
161 		// disable
162 		MaskReg8(MCR, MCR_DTR);
163 	} else {
164 		// set FCR now,
165 		// 16650 and later chips have another reg at 2 when DLAB=1
166 		uint8 fcr = FCR_ENABLE | FCR_RX_RST | FCR_TX_RST | FCR_F_8 | FCR_F64EN;
167 		// enable fifo
168 		//fcr = 0;
169 		WriteReg8(FCR, fcr);
170 
171 		// unmask the divisor latch regs
172 		WriteReg8(LCR, lcr | LCR_DLAB);
173 		// set divisor
174 		WriteReg8(DLLB, divisor & 0x00ff);
175 		WriteReg8(DLHB, divisor >> 8);
176 		//WriteReg8(2,0);
177 
178 	}
179 	// set control lines, and disable divisor latch reg
180 	WriteReg8(LCR, lcr);
181 
182 
183 #if 0
184 	uint16 newControl = fControlOut;
185 
186 	static uint32 baudRates[] = {
187 		0x00000000, //B0
188 		0x00000032, //B50
189 		0x0000004B, //B75
190 		0x0000006E, //B110
191 		0x00000086, //B134
192 		0x00000096, //B150
193 		0x000000C8, //B200
194 		0x0000012C, //B300
195 		0x00000258, //B600
196 		0x000004B0, //B1200
197 		0x00000708, //B1800
198 		0x00000960, //B2400
199 		0x000012C0, //B4800
200 		0x00002580, //B9600
201 		0x00004B00, //B19200
202 		0x00009600, //B38400
203 		0x0000E100, //B57600
204 		0x0001C200, //B115200
205 		0x00038400, //B230400
206 		0x00070800, //460800
207 		0x000E1000, //921600
208 	};
209 
210 	usb_serial_line_coding lineCoding;
211 	lineCoding.speed = baudRates[baudIndex];
212 	lineCoding.stopbits = (tios->c_cflag & CSTOPB) ? LC_STOP_BIT_2 : LC_STOP_BIT_1;
213 
214 	if (tios->c_cflag & PARENB) {
215 		lineCoding.parity = LC_PARITY_EVEN;
216 		if (tios->c_cflag & PARODD)
217 			lineCoding.parity = LC_PARITY_ODD;
218 	} else
219 		lineCoding.parity = LC_PARITY_NONE;
220 
221 	lineCoding.databits = (tios->c_cflag & CS8) ? 8 : 7;
222 
223 	if (lineCoding.speed == 0) {
224 		newControl &= 0xfffffffe;
225 		lineCoding.speed = fLineCoding.speed;
226 	} else
227 		newControl = CLS_LINE_DTR;
228 
229 	if (fControlOut != newControl) {
230 		fControlOut = newControl;
231 		TRACE("newctrl send to modem: 0x%08x\n", newControl);
232 		SetControlLineState(newControl);
233 	}
234 
235 	if (memcmp(&lineCoding, &fLineCoding, sizeof(usb_serial_line_coding)) != 0) {
236 		fLineCoding.speed = lineCoding.speed;
237 		fLineCoding.stopbits = lineCoding.stopbits;
238 		fLineCoding.databits = lineCoding.databits;
239 		fLineCoding.parity = lineCoding.parity;
240 		TRACE("send to modem: speed %d sb: 0x%08x db: 0x%08x parity: 0x%08x\n",
241 			fLineCoding.speed, fLineCoding.stopbits, fLineCoding.databits,
242 			fLineCoding.parity);
243 		SetLineCoding(&fLineCoding);
244 	}
245 #endif
246 }
247 
248 
249 bool
250 SerialDevice::Service(struct tty *tty, uint32 op, void *buffer, size_t length)
251 {
252 	uint8 msr;
253 	status_t err;
254 
255 	TRACE("%s(,0x%08lx,,%d)\n", __FUNCTION__, op, length);
256 	if (tty != fMasterTTY)
257 		return false;
258 
259 	TRACE("%s(,0x%08lx,,%d)\n", __FUNCTION__, op, length);
260 
261 	switch (op) {
262 		case TTYENABLE:
263 		{
264 			bool enable = *(bool *)buffer;
265 			TRACE("TTYENABLE: %sable\n", enable ? "en" : "dis");
266 
267 			if (enable) {
268 				//XXX:must call SetModes();
269 				err = install_io_interrupt_handler(IRQ(), pc_serial_interrupt, this, 0);
270 				TRACE("installing irq handler for %d: %s\n", IRQ(), strerror(err));
271 			} else {
272 				// remove the handler
273 				remove_io_interrupt_handler(IRQ(), pc_serial_interrupt, this);
274 				// disable IRQ
275 				fCachedIER = 0;
276 				WriteReg8(IER, fCachedIER);
277 				WriteReg8(MCR, 0);
278 			}
279 
280 			msr = ReadReg8(MSR);
281 
282 			SignalControlLineState(TTYHWDCD, msr & MSR_DCD);
283 			SignalControlLineState(TTYHWCTS, msr & MSR_CTS);
284 
285 			if (enable) {
286 				//
287 				WriteReg8(MCR, MCR_DTR | MCR_RTS | MCR_IRQ_EN /*| MCR_LOOP*//*XXXXXXX*/);
288 				// enable irqs
289 				fCachedIER = IER_RLS | IER_MS | IER_RDA;
290 				WriteReg8(IER, fCachedIER);
291 				//WriteReg8(IER, IER_RDA);
292 			}
293 
294 			return true;
295 		}
296 
297 		case TTYISTOP:
298 			fInputStopped = *(bool *)buffer;
299 			TRACE("TTYISTOP: %sstopped\n", fInputStopped ? "" : "not ");
300 
301 			if (fInputStopped)
302 				MaskReg8(MCR, MCR_RTS);
303 			else
304 				OrReg8(MCR, MCR_RTS);
305 
306 			//gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, false);
307 			//SignalControlLineState(TTYHWCTS, !fInputStopped);
308 			//msr = ReadReg8(MSR);
309 			//SignalControlLineState(TTYHWCTS, msr & MSR_CTS);
310 			return true;
311 
312 		case TTYGETSIGNALS:
313 			TRACE("TTYGETSIGNALS\n");
314 			msr = ReadReg8(MSR);
315 			SignalControlLineState(TTYHWDCD, msr & MSR_DCD);
316 			SignalControlLineState(TTYHWCTS, msr & MSR_CTS);
317 			SignalControlLineState(TTYHWDSR, msr & MSR_DSR);
318 			SignalControlLineState(TTYHWRI, msr & MSR_RI);
319 			return true;
320 
321 		case TTYSETMODES:
322 			TRACE("TTYSETMODES\n");
323 			SetModes((struct termios *)buffer);
324 //WriteReg8(IER, IER_RLS | IER_MS | IER_RDA);
325 			return true;
326 
327 		case TTYSETDTR:
328 		case TTYSETRTS:
329 		{
330 			bool set = *(bool *)buffer;
331 			uint8 bit = op == TTYSETDTR ? MCR_DTR : MCR_RTS;
332 			if (set)
333 				OrReg8(MCR, bit);
334 			else
335 				MaskReg8(MCR, bit);
336 
337 			return true;
338 		}
339 
340 		case TTYOSTART:
341 			TRACE("TTYOSTART\n");
342 			// enable irqs
343 			fCachedIER |= IER_THRE;
344 			// XXX: toggle the bit to make VirtualBox happy !?
345 			WriteReg8(IER, fCachedIER & ~IER_THRE);
346 			WriteReg8(IER, fCachedIER);
347 			return true;
348 		case TTYOSYNC:
349 			TRACE("TTYOSYNC\n");
350 			return (ReadReg8(LSR) & (LSR_THRE | LSR_TSRE)) == (LSR_THRE | LSR_TSRE);
351 			return true;
352 		case TTYSETBREAK:
353 		{
354 			bool set = *(bool *)buffer;
355 			if (set)
356 				OrReg8(MCR, LCR_BREAK);
357 			else
358 				MaskReg8(MCR, LCR_BREAK);
359 
360 			return true;
361 		}
362 		case TTYFLUSH:
363 		{
364 			int directions = *(int*)buffer;
365 			uint8 mask = 0;
366 			if (directions & TCIFLUSH)
367 				mask |= FCR_RX_RST;
368 			if (directions & TCOFLUSH)
369 				mask |= FCR_TX_RST;
370 			// These bits clear themselves when flushing is complete
371 			OrReg8(FCR, mask);
372 
373 			return true;
374 		}
375 		default:
376 			return false;
377 	}
378 
379 	return false;
380 }
381 
382 
383 bool
384 SerialDevice::IsInterruptPending()
385 {
386 	TRACE(("IsInterruptPending()\n"));
387 
388 	// because reading the IIR acknowledges some IRQ conditions,
389 	// the next time we'll read we'll miss the IRQ condition
390 	// so we just cache the value for the real handler
391 	fCachedIIR = ReadReg8(IIR);
392 
393 	bool pending = (fCachedIIR & IIR_PENDING) == 0;
394 
395 	if (pending) {
396 		// temporarily mask the IRQ
397 		// else VirtualBox triggers one per every written byte it seems
398 		// not sure it's required on real hardware
399 		WriteReg8(IER, fCachedIER & ~(IER_RLS | IER_MS | IER_RDA | IER_THRE));
400 
401 		atomic_add(&fPendingDPC, 1);
402 	}
403 
404 	return pending; // 0 means yes
405 }
406 
407 
408 int32
409 SerialDevice::InterruptHandler()
410 {
411 	int32 ret = B_UNHANDLED_INTERRUPT;
412 	//XXX: what should we do here ? (certainly not use a mutex !)
413 
414 	uint8 iir, lsr, msr;
415 	uint8 buffer[64];
416 	int tries = 8; // avoid busy looping
417 	TRACE(("InterruptHandler()\n"));
418 
419 	// start with the first (cached) irq condition
420 	iir = fCachedIIR;
421 	while ((iir & IIR_PENDING) == 0) { // 0 means yes
422 		status_t status;
423 		size_t bytesLeft;
424 		size_t readable = 0;
425 		size_t fifoavail = 1;
426 		size_t i;
427 
428 		//DEBUG
429 //		for (int count = 0; ReadReg8(LSR) & LSR_DR; count++)
430 //			gTTYModule->ttyin(&fTTY, &fRover, ReadReg8(RBR));
431 
432 		switch (iir & (IIR_IMASK | IIR_TO)) {
433 		case IIR_THRE:
434 			TRACE(("IIR_THRE\n"));
435 			// check how much fifo we can use
436 			//XXX: move to Init() ?
437 			if ((iir & IIR_FMASK) == IIR_FMASK)
438 				fifoavail = 16;
439 			if (iir & IIR_F64EN)
440 				fifoavail = 64;
441 			// we're not open... just discard the data
442 			if (!IsOpen())
443 				break;
444 			gTTYModule->tty_control(fDeviceTTYCookie, FIONREAD, &readable,
445 				sizeof(readable));
446 			TRACE("%s: FIONREAD: %d\n", __FUNCTION__, readable);
447 
448 			if (readable == 0) {
449 				release_sem_etc(fDoneWrite, 1, B_DO_NOT_RESCHEDULE);
450 				// mask it until there's data again
451 				fCachedIER &= ~IER_THRE;
452 				break;
453 			}
454 
455 			bytesLeft = MIN(fifoavail, sizeof(buffer));
456 			bytesLeft = MIN(bytesLeft, readable);
457 			TRACE("%s: left %d\n", __FUNCTION__, bytesLeft);
458 			status = gTTYModule->tty_read(fDeviceTTYCookie, buffer, &bytesLeft);
459 			TRACE("%s: tty_read: %d\n", __FUNCTION__, bytesLeft);
460 			if (status != B_OK) {
461 				dprintf(DRIVER_NAME ": irq: tty_read: %s\n", strerror(status));
462 				break;
463 			}
464 
465 			for (i = 0; i < bytesLeft; i++) {
466 				WriteReg8(THB, buffer[i]);
467 			}
468 
469 			break;
470 		case IIR_TO:
471 		case IIR_TO | IIR_RDA:
472 			// timeout: FALLTHROUGH
473 		case IIR_RDA:
474 			TRACE(("IIR_TO/RDA\n"));
475 			// while data is ready... and we have room for it, get it
476 			bytesLeft = sizeof(buffer);
477 			for (i = 0; i < bytesLeft && (ReadReg8(LSR) & LSR_DR); i++) {
478 				buffer[i] = ReadReg8(RBR);
479 			}
480 			// we're not open... just discard the data
481 			if (!IsOpen())
482 				break;
483 			// we shouldn't block here but it's < 256 bytes anyway
484 			status = gTTYModule->tty_write(fDeviceTTYCookie, buffer, &i);
485 			if (status != B_OK) {
486 				dprintf(DRIVER_NAME ": irq: tty_write: %s\n", strerror(status));
487 				break;
488 			}
489 			break;
490 		case IIR_RLS:
491 			TRACE(("IIR_RLS\n"));
492 			// ack
493 			lsr = ReadReg8(LSR);
494 			//XXX: handle this somehow
495 			break;
496 		case IIR_MS:
497 			TRACE(("IIR_MS\n"));
498 			// modem signals changed
499 			msr = ReadReg8(MSR);
500 			if (!IsOpen())
501 				break;
502 			if (msr & MSR_DDCD)
503 				SignalControlLineState(TTYHWDCD, msr & MSR_DCD);
504 			if (msr & MSR_DCTS)
505 				SignalControlLineState(TTYHWCTS, msr & MSR_CTS);
506 			if (msr & MSR_DDSR)
507 				SignalControlLineState(TTYHWDSR, msr & MSR_DSR);
508 			if (msr & MSR_TERI)
509 				SignalControlLineState(TTYHWRI, msr & MSR_RI);
510 			break;
511 		default:
512 			TRACE(("IIR_?\n"));
513 			// something happened
514 			break;
515 		}
516 		ret = B_HANDLED_INTERRUPT;
517 		TRACE(("IRQ:h\n"));
518 
519 		// enough for now
520 		if (tries-- == 0)
521 			break;
522 
523 		// check the next IRQ condition
524 		iir = ReadReg8(IIR);
525 	}
526 
527 	atomic_add(&fPendingDPC, -1);
528 
529 	// unmask IRQ
530 	WriteReg8(IER, fCachedIER);
531 
532 	TRACE_FUNCRET("< IRQ:%d\n", ret);
533 	return ret;
534 }
535 
536 
537 status_t
538 SerialDevice::Open(uint32 flags)
539 {
540 	status_t status = B_OK;
541 
542 	if (fDeviceOpen)
543 		return B_BUSY;
544 
545 	if (fDeviceRemoved)
546 		return B_DEV_NOT_READY;
547 
548 	fMasterTTY = gTTYModule->tty_create(pc_serial_service, NULL);
549 	if (fMasterTTY == NULL) {
550 		TRACE_ALWAYS("open: failed to init master tty\n");
551 		return B_NO_MEMORY;
552 	}
553 
554 	fSlaveTTY = gTTYModule->tty_create(pc_serial_service, fMasterTTY);
555 	if (fSlaveTTY == NULL) {
556 		TRACE_ALWAYS("open: failed to init slave tty\n");
557 		gTTYModule->tty_destroy(fMasterTTY);
558 		return B_NO_MEMORY;
559 	}
560 
561 	fSystemTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY,
562 		O_RDWR);
563 	if (fSystemTTYCookie == NULL) {
564 		TRACE_ALWAYS("open: failed to init system tty cookie\n");
565 		gTTYModule->tty_destroy(fMasterTTY);
566 		gTTYModule->tty_destroy(fSlaveTTY);
567 		return B_NO_MEMORY;
568 	}
569 
570 	fDeviceTTYCookie = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY,
571 		O_RDWR);
572 	if (fDeviceTTYCookie == NULL) {
573 		TRACE_ALWAYS("open: failed to init device tty cookie\n");
574 		gTTYModule->tty_destroy_cookie(fSystemTTYCookie);
575 		gTTYModule->tty_destroy(fMasterTTY);
576 		gTTYModule->tty_destroy(fSlaveTTY);
577 		return B_NO_MEMORY;
578 	}
579 
580 	ResetDevice();
581 
582 	//XXX: we shouldn't have to do this!
583 	bool en = true;
584 	Service(fMasterTTY, TTYENABLE, &en, sizeof(en));
585 
586 	if (status < B_OK) {
587 		TRACE_ALWAYS("open: failed to open tty\n");
588 		return status;
589 	}
590 
591 	// set our config (will propagate to the slave config as well in SetModes()
592 	gTTYModule->tty_control(fSystemTTYCookie, TCSETA, &fTTYConfig,
593 		sizeof(termios));
594 
595 #if 0
596 	fDeviceThread = spawn_kernel_thread(_DeviceThread, "usb_serial device thread",
597 		B_NORMAL_PRIORITY, this);
598 
599 	if (fDeviceThread < B_OK) {
600 		TRACE_ALWAYS("open: failed to spawn kernel thread\n");
601 		return fDeviceThread;
602 	}
603 
604 	resume_thread(fDeviceThread);
605 
606 	fControlOut = CLS_LINE_DTR | CLS_LINE_RTS;
607 	SetControlLineState(fControlOut);
608 
609 	status = gUSBModule->queue_interrupt(fControlPipe, fInterruptBuffer,
610 		fInterruptBufferSize, InterruptCallbackFunction, this);
611 	if (status < B_OK)
612 		TRACE_ALWAYS("failed to queue initial interrupt\n");
613 
614 #endif
615 	fDeviceOpen = true;
616 	return B_OK;
617 }
618 
619 
620 status_t
621 SerialDevice::Read(char *buffer, size_t *numBytes)
622 {
623 	if (fDeviceRemoved) {
624 		*numBytes = 0;
625 		return B_DEV_NOT_READY;
626 	}
627 
628 	status_t status;
629 
630 	status = gTTYModule->tty_read(fSystemTTYCookie, buffer, numBytes);
631 
632 	return status;
633 }
634 
635 
636 status_t
637 SerialDevice::Write(const char *buffer, size_t *numBytes)
638 {
639 	TRACE("%s(,&%d)\n", __FUNCTION__, *numBytes);
640 	if (fDeviceRemoved) {
641 		*numBytes = 0;
642 		return B_DEV_NOT_READY;
643 	}
644 
645 	status_t status;
646 	size_t bytesLeft = *numBytes;
647 	*numBytes = 0;
648 
649 	while (bytesLeft > 0) {
650 		size_t length = MIN(bytesLeft, 256);
651 			// TODO: This is an ugly hack; We use a small buffer size so that
652 			// we don't overrun the tty line buffer and cause it to block. While
653 			// that isn't a problem, we shouldn't just hardcode the value here.
654 
655 		TRACE("%s: tty_write(,&%d)\n", __FUNCTION__, length);
656 		status = gTTYModule->tty_write(fSystemTTYCookie, buffer,
657 			&length);
658 		if (status != B_OK) {
659 			TRACE_ALWAYS("failed to write to tty: %s\n", strerror(status));
660 			return status;
661 		}
662 
663 		buffer += length;
664 		*numBytes += length;
665 		bytesLeft -= length;
666 
667 		// XXX: WTF: this ought to be done by the tty module calling service_func!
668 		// enable irqs
669 		Service(fMasterTTY, TTYOSTART, NULL, 0);
670 	}
671 
672 	status = acquire_sem_etc(fDoneWrite, 1, B_CAN_INTERRUPT, 0);
673 	if (status != B_OK) {
674 		TRACE_ALWAYS("write: failed to get write done sem "
675 				"0x%08x\n", status);
676 		return status;
677 	}
678 
679 
680 	if (*numBytes > 0)
681 		return B_OK;
682 
683 	return B_ERROR;
684 }
685 
686 
687 status_t
688 SerialDevice::Control(uint32 op, void *arg, size_t length)
689 {
690 	status_t status = B_OK;
691 
692 	if (fDeviceRemoved)
693 		return B_DEV_NOT_READY;
694 
695 	status = gTTYModule->tty_control(fSystemTTYCookie, op, arg, length);
696 
697 	return status;
698 }
699 
700 
701 status_t
702 SerialDevice::Select(uint8 event, uint32 ref, selectsync *sync)
703 {
704 	if (fDeviceRemoved)
705 		return B_DEV_NOT_READY;
706 
707 	return gTTYModule->tty_select(fSystemTTYCookie, event, ref, sync);
708 }
709 
710 
711 status_t
712 SerialDevice::DeSelect(uint8 event, selectsync *sync)
713 {
714 	if (fDeviceRemoved)
715 		return B_DEV_NOT_READY;
716 
717 	return gTTYModule->tty_deselect(fSystemTTYCookie, event, sync);
718 }
719 
720 
721 status_t
722 SerialDevice::Close()
723 {
724 	status_t status = B_OK;
725 
726 	OnClose();
727 
728 	if (!fDeviceRemoved) {
729 #if 0
730 		gUSBModule->cancel_queued_transfers(fReadPipe);
731 		gUSBModule->cancel_queued_transfers(fWritePipe);
732 		gUSBModule->cancel_queued_transfers(fControlPipe);
733 #endif
734 	}
735 
736 	fDeviceOpen = false;
737 
738 	gTTYModule->tty_close_cookie(fSystemTTYCookie);
739 	gTTYModule->tty_close_cookie(fDeviceTTYCookie);
740 
741 	//XXX: we shouldn't have to do this!
742 	bool en = false;
743 	Service(fMasterTTY, TTYENABLE, &en, sizeof(en));
744 
745 	return status;
746 }
747 
748 
749 status_t
750 SerialDevice::Free()
751 {
752 	status_t status = B_OK;
753 
754 	// wait until currently executing DPC is done. In case another one
755 	// is run beyond this point it will just bail out on !IsOpen().
756 	//while (atomic_get(&fPendingDPC))
757 	//	snooze(1000);
758 
759 	gTTYModule->tty_destroy_cookie(fSystemTTYCookie);
760 	gTTYModule->tty_destroy_cookie(fDeviceTTYCookie);
761 	fSystemTTYCookie = fDeviceTTYCookie = NULL;
762 
763 	gTTYModule->tty_destroy(fMasterTTY);
764 	gTTYModule->tty_destroy(fSlaveTTY);
765 	fMasterTTY = fSlaveTTY = NULL;
766 
767 	return status;
768 }
769 
770 
771 void
772 SerialDevice::Removed()
773 {
774 	if (fDeviceRemoved)
775 		return;
776 
777 	// notifies us that the device was removed
778 	fDeviceRemoved = true;
779 
780 	// we need to ensure that we do not use the device anymore
781 	fStopDeviceThread = true;
782 	fInputStopped = false;
783 #if 0
784 	gUSBModule->cancel_queued_transfers(fReadPipe);
785 	gUSBModule->cancel_queued_transfers(fWritePipe);
786 	gUSBModule->cancel_queued_transfers(fControlPipe);
787 #endif
788 
789 	int32 result = B_OK;
790 	wait_for_thread(fDeviceThread, &result);
791 	fDeviceThread = -1;
792 }
793 
794 
795 status_t
796 SerialDevice::AddDevice(const serial_config_descriptor *config)
797 {
798 	// default implementation - does nothing
799 	return B_ERROR;
800 }
801 
802 
803 status_t
804 SerialDevice::ResetDevice()
805 {
806 	// default implementation - does nothing
807 	return B_OK;
808 }
809 
810 
811 #if 0
812 status_t
813 SerialDevice::SetLineCoding(usb_serial_line_coding *coding)
814 {
815 	// default implementation - does nothing
816 	return B_OK;
817 }
818 #endif
819 
820 status_t
821 SerialDevice::SignalControlLineState(int line, bool enable)
822 {
823 	gTTYModule->tty_hardware_signal(fSystemTTYCookie, line, enable);
824 
825 	return B_OK;
826 }
827 
828 
829 void
830 SerialDevice::OnRead(char **buffer, size_t *numBytes)
831 {
832 	// default implementation - does nothing
833 }
834 
835 
836 void
837 SerialDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes)
838 {
839 	// default implementation - does nothing
840 }
841 
842 
843 void
844 SerialDevice::OnClose()
845 {
846 	// default implementation - does nothing
847 }
848 
849 
850 int32
851 SerialDevice::_DeviceThread(void *data)
852 {
853 #if 0
854 	SerialDevice *device = (SerialDevice *)data;
855 
856 	while (!device->fStopDeviceThread) {
857 		status_t status = gUSBModule->queue_bulk(device->fReadPipe,
858 			device->fReadBuffer, device->fReadBufferSize,
859 			device->ReadCallbackFunction, data);
860 		if (status < B_OK) {
861 			TRACE_ALWAYS("device thread: queueing failed with error: 0x%08x\n", status);
862 			break;
863 		}
864 
865 		status = acquire_sem_etc(device->fDoneRead, 1, B_CAN_INTERRUPT, 0);
866 		if (status < B_OK) {
867 			TRACE_ALWAYS("device thread: failed to get read done sem 0x%08x\n", status);
868 			break;
869 		}
870 
871 		if (device->fStatusRead != B_OK) {
872 			TRACE("device thread: device status error 0x%08x\n",
873 				device->fStatusRead);
874 			if (gUSBModule->clear_feature(device->fReadPipe,
875 				USB_FEATURE_ENDPOINT_HALT) != B_OK) {
876 				TRACE_ALWAYS("device thread: failed to clear halt feature\n");
877 				break;
878 			}
879 		}
880 
881 		char *buffer = device->fReadBuffer;
882 		size_t readLength = device->fActualLengthRead;
883 		device->OnRead(&buffer, &readLength);
884 		if (readLength == 0)
885 			continue;
886 
887 		ddrover *ddr = gTTYModule->ddrstart(NULL);
888 		if (!ddr) {
889 			TRACE_ALWAYS("device thread: ddrstart problem\n");
890 			return B_NO_MEMORY;
891 		}
892 
893 		while (device->fInputStopped)
894 			snooze(100);
895 
896 		gTTYModule->ttyilock(&device->fTTY, ddr, true);
897 		for (size_t i = 0; i < readLength; i++)
898 			gTTYModule->ttyin(&device->fTTY, ddr, buffer[i]);
899 
900 		gTTYModule->ttyilock(&device->fTTY, ddr, false);
901 		gTTYModule->ddrdone(ddr);
902 	}
903 
904 #endif
905 	return B_OK;
906 }
907 
908 
909 status_t
910 SerialDevice::_WriteToDevice()
911 {
912 	char *buffer = &fWriteBuffer[fWriteBufferIn];
913 	size_t bytesLeft = DEF_BUFFER_SIZE - atomic_get(&fWriteBufferAvail);
914 	bytesLeft = MIN(bytesLeft, DEF_BUFFER_SIZE - fWriteBufferIn);
915 	TRACE("%s: in %d left %d\n", __FUNCTION__, fWriteBufferIn, bytesLeft);
916 	status_t status = gTTYModule->tty_read(fDeviceTTYCookie, buffer,
917 		&bytesLeft);
918 	TRACE("%s: tty_read: %d\n", __FUNCTION__, bytesLeft);
919 	if (status != B_OK) {
920 		TRACE_ALWAYS("write to device: failed to read from TTY: %s\n",
921 			strerror(status));
922 		return status;
923 	}
924 	fWriteBufferIn += bytesLeft;
925 	fWriteBufferIn %= DEF_BUFFER_SIZE;
926 	atomic_add(&fWriteBufferAvail, bytesLeft);
927 
928 	// XXX: WTF: this ought to be done by the tty module calling service_func!
929 	// enable irqs
930 	Service(fMasterTTY, TTYOSTART, NULL, 0);
931 
932 	status = acquire_sem_etc(fWriteBufferSem, 1, B_CAN_INTERRUPT, 0);
933 	if (status != B_OK) {
934 		TRACE_ALWAYS("write to device: failed to acquire sem: %s\n",
935 			strerror(status));
936 		return status;
937 	}
938 	return B_OK;
939 }
940 
941 
942 void
943 SerialDevice::ReadCallbackFunction(void *cookie, int32 status, void *data,
944 	uint32 actualLength)
945 {
946 	TRACE_FUNCALLS("read callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
947 		cookie, status, data, actualLength);
948 
949 	SerialDevice *device = (SerialDevice *)cookie;
950 	device->fActualLengthRead = actualLength;
951 	device->fStatusRead = status;
952 	release_sem_etc(device->fDoneRead, 1, B_DO_NOT_RESCHEDULE);
953 }
954 
955 
956 void
957 SerialDevice::WriteCallbackFunction(void *cookie, int32 status, void *data,
958 	uint32 actualLength)
959 {
960 	TRACE_FUNCALLS("write callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
961 		cookie, status, data, actualLength);
962 
963 	SerialDevice *device = (SerialDevice *)cookie;
964 	device->fActualLengthWrite = actualLength;
965 	device->fStatusWrite = status;
966 	release_sem_etc(device->fDoneWrite, 1, B_DO_NOT_RESCHEDULE);
967 }
968 
969 
970 void
971 SerialDevice::InterruptCallbackFunction(void *cookie, int32 status,
972 	void *data, uint32 actualLength)
973 {
974 	TRACE_FUNCALLS("interrupt callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
975 		cookie, status, data, actualLength);
976 
977 	SerialDevice *device = (SerialDevice *)cookie;
978 	device->fActualLengthInterrupt = actualLength;
979 	device->fStatusInterrupt = status;
980 
981 	// ToDo: maybe handle those somehow?
982 
983 	if (status == B_OK && !device->fDeviceRemoved) {
984 #if 0
985 		status = gUSBModule->queue_interrupt(device->fControlPipe,
986 			device->fInterruptBuffer, device->fInterruptBufferSize,
987 			device->InterruptCallbackFunction, device);
988 #endif
989 	}
990 }
991 
992 
993 
994 #if 0
995 SerialDevice *
996 SerialDevice::MakeDevice(usb_device device, uint16 vendorID,
997 	uint16 productID)
998 {
999 	const char *description = NULL;
1000 
1001 	switch (vendorID) {
1002 		case VENDOR_IODATA:
1003 		case VENDOR_ATEN:
1004 		case VENDOR_TDK:
1005 		case VENDOR_RATOC:
1006 		case VENDOR_PROLIFIC:
1007 		case VENDOR_ELECOM:
1008 		case VENDOR_SOURCENEXT:
1009 		case VENDOR_HAL:
1010 		{
1011 			switch (productID) {
1012 				case PRODUCT_PROLIFIC_RSAQ2: description = "PL2303 Serial adapter (IODATA USB-RSAQ2)"; break;
1013 				case PRODUCT_IODATA_USBRSAQ: description = "I/O Data USB serial adapter USB-RSAQ1"; break;
1014 				case PRODUCT_ATEN_UC232A: description = "Aten Serial adapter"; break;
1015 				case PRODUCT_TDK_UHA6400: description = "TDK USB-PHS Adapter UHA6400"; break;
1016 				case PRODUCT_RATOC_REXUSB60: description = "Ratoc USB serial adapter REX-USB60"; break;
1017 				case PRODUCT_PROLIFIC_PL2303: description = "PL2303 Serial adapter (ATEN/IOGEAR UC232A)"; break;
1018 				case PRODUCT_ELECOM_UCSGT: description = "Elecom UC-SGT"; break;
1019 				case PRODUCT_SOURCENEXT_KEIKAI8: description = "SOURCENEXT KeikaiDenwa 8"; break;
1020 				case PRODUCT_SOURCENEXT_KEIKAI8_CHG: description = "SOURCENEXT KeikaiDenwa 8 with charger"; break;
1021 				case PRODUCT_HAL_IMR001: description = "HAL Corporation Crossam2+USB"; break;
1022 			}
1023 
1024 			if (!description)
1025 				break;
1026 
1027 			return new(std::nothrow) ProlificDevice(device, vendorID, productID, description);
1028 		}
1029 
1030 		case VENDOR_FTDI:
1031 		{
1032 			switch (productID) {
1033 				case PRODUCT_FTDI_8U100AX: description = "FTDI 8U100AX serial converter"; break;
1034 				case PRODUCT_FTDI_8U232AM: description = "FTDI 8U232AM serial converter"; break;
1035 			}
1036 
1037 			if (!description)
1038 				break;
1039 
1040 			return new(std::nothrow) FTDIDevice(device, vendorID, productID, description);
1041 		}
1042 
1043 		case VENDOR_PALM:
1044 		case VENDOR_KLSI:
1045 		{
1046 			switch (productID) {
1047 				case PRODUCT_PALM_CONNECT: description = "PalmConnect RS232"; break;
1048 				case PRODUCT_KLSI_KL5KUSB105D: description = "KLSI KL5KUSB105D"; break;
1049 			}
1050 
1051 			if (!description)
1052 				break;
1053 
1054 			return new(std::nothrow) KLSIDevice(device, vendorID, productID, description);
1055 		}
1056 	}
1057 
1058 	return new(std::nothrow) ACMDevice(device, vendorID, productID, "CDC ACM compatible device");
1059 }
1060 #endif
1061 
1062 
1063 uint8
1064 SerialDevice::ReadReg8(int reg)
1065 {
1066 	uint8 ret;
1067 	switch (fBus) {
1068 	case B_ISA_BUS:
1069 		ret = gISAModule->read_io_8(IOBase() + reg);
1070 		break;
1071 	case B_PCI_BUS:
1072 		ret = gPCIModule->read_io_8(IOBase() + reg);
1073 		break;
1074 	default:
1075 		TRACE_ALWAYS("%s: unknown bus!\n", __FUNCTION__);
1076 		ret = 0;
1077 	//XXX:pcmcia ?
1078 	}
1079 	TRACE/*_ALWAYS*/("RR8(%d) = %d [%02x]\n", reg, ret, ret);
1080 	//spin(1000);
1081 	return ret;
1082 }
1083 
1084 void
1085 SerialDevice::WriteReg8(int reg, uint8 value)
1086 {
1087 //	TRACE_ALWAYS("WR8(0x%04x+%d, %d [0x%x])\n", IOBase(), reg, value, value);
1088 	TRACE/*_ALWAYS*/("WR8(%d, %d [0x%x])\n", reg, value, value);
1089 	switch (fBus) {
1090 	case B_ISA_BUS:
1091 		gISAModule->write_io_8(IOBase() + reg, value);
1092 		break;
1093 	case B_PCI_BUS:
1094 		gPCIModule->write_io_8(IOBase() + reg, value);
1095 		break;
1096 	default:
1097 		TRACE_ALWAYS("%s: unknown bus!\n", __FUNCTION__);
1098 	//XXX:pcmcia ?
1099 	}
1100 	//spin(10000);
1101 }
1102 
1103 
1104 void
1105 SerialDevice::OrReg8(int reg, uint8 value)
1106 {
1107 	WriteReg8(reg, ReadReg8(reg) | value);
1108 }
1109 
1110 
1111 void
1112 SerialDevice::AndReg8(int reg, uint8 value)
1113 {
1114 	WriteReg8(reg, ReadReg8(reg) & value);
1115 }
1116 
1117 
1118 void
1119 SerialDevice::MaskReg8(int reg, uint8 value)
1120 {
1121 	WriteReg8(reg, ReadReg8(reg) & ~value);
1122 }
1123