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
17SerialDevice::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
61SerialDevice::~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
74bool
75SerialDevice::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
89status_t
90SerialDevice::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
107void
108SerialDevice::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
249bool
250SerialDevice::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
383bool
384SerialDevice::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
408int32
409SerialDevice::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
537status_t
538SerialDevice::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	status = gTTYModule->tty_create(pc_serial_service, NULL, &fMasterTTY);
549	if (status != B_OK) {
550		TRACE_ALWAYS("open: failed to init master tty\n");
551		return status;
552	}
553
554	status = gTTYModule->tty_create(pc_serial_service, fMasterTTY, &fSlaveTTY);
555	if (status != B_OK) {
556		TRACE_ALWAYS("open: failed to init slave tty\n");
557		gTTYModule->tty_destroy(fMasterTTY);
558		return status;
559	}
560
561	status = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY, O_RDWR, &fSystemTTYCookie);
562	if (status != B_OK) {
563		TRACE_ALWAYS("open: failed to init system tty cookie\n");
564		gTTYModule->tty_destroy(fMasterTTY);
565		gTTYModule->tty_destroy(fSlaveTTY);
566		return status;
567	}
568
569	status = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY, O_RDWR, &fDeviceTTYCookie);
570	if (status != B_OK) {
571		TRACE_ALWAYS("open: failed to init device tty cookie\n");
572		gTTYModule->tty_destroy_cookie(fSystemTTYCookie);
573		gTTYModule->tty_destroy(fMasterTTY);
574		gTTYModule->tty_destroy(fSlaveTTY);
575		return status;
576	}
577
578	ResetDevice();
579
580	//XXX: we shouldn't have to do this!
581	bool en = true;
582	Service(fMasterTTY, TTYENABLE, &en, sizeof(en));
583
584	if (status < B_OK) {
585		TRACE_ALWAYS("open: failed to open tty\n");
586		return status;
587	}
588
589	// set our config (will propagate to the slave config as well in SetModes()
590	gTTYModule->tty_control(fSystemTTYCookie, TCSETA, &fTTYConfig,
591		sizeof(termios));
592
593#if 0
594	fDeviceThread = spawn_kernel_thread(_DeviceThread, "usb_serial device thread",
595		B_NORMAL_PRIORITY, this);
596
597	if (fDeviceThread < B_OK) {
598		TRACE_ALWAYS("open: failed to spawn kernel thread\n");
599		return fDeviceThread;
600	}
601
602	resume_thread(fDeviceThread);
603
604	fControlOut = CLS_LINE_DTR | CLS_LINE_RTS;
605	SetControlLineState(fControlOut);
606
607	status = gUSBModule->queue_interrupt(fControlPipe, fInterruptBuffer,
608		fInterruptBufferSize, InterruptCallbackFunction, this);
609	if (status < B_OK)
610		TRACE_ALWAYS("failed to queue initial interrupt\n");
611
612#endif
613	fDeviceOpen = true;
614	return B_OK;
615}
616
617
618status_t
619SerialDevice::Read(char *buffer, size_t *numBytes)
620{
621	if (fDeviceRemoved) {
622		*numBytes = 0;
623		return B_DEV_NOT_READY;
624	}
625
626	status_t status;
627
628	status = gTTYModule->tty_read(fSystemTTYCookie, buffer, numBytes);
629
630	return status;
631}
632
633
634status_t
635SerialDevice::Write(const char *buffer, size_t *numBytes)
636{
637	TRACE("%s(,&%d)\n", __FUNCTION__, *numBytes);
638	if (fDeviceRemoved) {
639		*numBytes = 0;
640		return B_DEV_NOT_READY;
641	}
642
643	status_t status;
644	size_t bytesLeft = *numBytes;
645	*numBytes = 0;
646
647	while (bytesLeft > 0) {
648		size_t length = MIN(bytesLeft, 256);
649			// TODO: This is an ugly hack; We use a small buffer size so that
650			// we don't overrun the tty line buffer and cause it to block. While
651			// that isn't a problem, we shouldn't just hardcode the value here.
652
653		TRACE("%s: tty_write(,&%d)\n", __FUNCTION__, length);
654		status = gTTYModule->tty_write(fSystemTTYCookie, buffer,
655			&length);
656		if (status != B_OK) {
657			TRACE_ALWAYS("failed to write to tty: %s\n", strerror(status));
658			return status;
659		}
660
661		buffer += length;
662		*numBytes += length;
663		bytesLeft -= length;
664
665		// XXX: WTF: this ought to be done by the tty module calling service_func!
666		// enable irqs
667		Service(fMasterTTY, TTYOSTART, NULL, 0);
668	}
669
670	status = acquire_sem_etc(fDoneWrite, 1, B_CAN_INTERRUPT, 0);
671	if (status != B_OK) {
672		TRACE_ALWAYS("write: failed to get write done sem "
673				"0x%08x\n", status);
674		return status;
675	}
676
677
678	if (*numBytes > 0)
679		return B_OK;
680
681	return B_ERROR;
682}
683
684
685status_t
686SerialDevice::Control(uint32 op, void *arg, size_t length)
687{
688	status_t status = B_OK;
689
690	if (fDeviceRemoved)
691		return B_DEV_NOT_READY;
692
693	status = gTTYModule->tty_control(fSystemTTYCookie, op, arg, length);
694
695	return status;
696}
697
698
699status_t
700SerialDevice::Select(uint8 event, uint32 ref, selectsync *sync)
701{
702	if (fDeviceRemoved)
703		return B_DEV_NOT_READY;
704
705	return gTTYModule->tty_select(fSystemTTYCookie, event, ref, sync);
706}
707
708
709status_t
710SerialDevice::DeSelect(uint8 event, selectsync *sync)
711{
712	if (fDeviceRemoved)
713		return B_DEV_NOT_READY;
714
715	return gTTYModule->tty_deselect(fSystemTTYCookie, event, sync);
716}
717
718
719status_t
720SerialDevice::Close()
721{
722	status_t status = B_OK;
723
724	OnClose();
725
726	if (!fDeviceRemoved) {
727#if 0
728		gUSBModule->cancel_queued_transfers(fReadPipe);
729		gUSBModule->cancel_queued_transfers(fWritePipe);
730		gUSBModule->cancel_queued_transfers(fControlPipe);
731#endif
732	}
733
734	fDeviceOpen = false;
735
736	gTTYModule->tty_close_cookie(fSystemTTYCookie);
737	gTTYModule->tty_close_cookie(fDeviceTTYCookie);
738
739	//XXX: we shouldn't have to do this!
740	bool en = false;
741	Service(fMasterTTY, TTYENABLE, &en, sizeof(en));
742
743	return status;
744}
745
746
747status_t
748SerialDevice::Free()
749{
750	status_t status = B_OK;
751
752	// wait until currently executing DPC is done. In case another one
753	// is run beyond this point it will just bail out on !IsOpen().
754	//while (atomic_get(&fPendingDPC))
755	//	snooze(1000);
756
757	gTTYModule->tty_destroy_cookie(fSystemTTYCookie);
758	gTTYModule->tty_destroy_cookie(fDeviceTTYCookie);
759	fSystemTTYCookie = fDeviceTTYCookie = NULL;
760
761	gTTYModule->tty_destroy(fMasterTTY);
762	gTTYModule->tty_destroy(fSlaveTTY);
763	fMasterTTY = fSlaveTTY = NULL;
764
765	return status;
766}
767
768
769void
770SerialDevice::Removed()
771{
772	if (fDeviceRemoved)
773		return;
774
775	// notifies us that the device was removed
776	fDeviceRemoved = true;
777
778	// we need to ensure that we do not use the device anymore
779	fStopDeviceThread = true;
780	fInputStopped = false;
781#if 0
782	gUSBModule->cancel_queued_transfers(fReadPipe);
783	gUSBModule->cancel_queued_transfers(fWritePipe);
784	gUSBModule->cancel_queued_transfers(fControlPipe);
785#endif
786
787	int32 result = B_OK;
788	wait_for_thread(fDeviceThread, &result);
789	fDeviceThread = -1;
790}
791
792
793status_t
794SerialDevice::AddDevice(const serial_config_descriptor *config)
795{
796	// default implementation - does nothing
797	return B_ERROR;
798}
799
800
801status_t
802SerialDevice::ResetDevice()
803{
804	// default implementation - does nothing
805	return B_OK;
806}
807
808
809#if 0
810status_t
811SerialDevice::SetLineCoding(usb_serial_line_coding *coding)
812{
813	// default implementation - does nothing
814	return B_OK;
815}
816#endif
817
818status_t
819SerialDevice::SignalControlLineState(int line, bool enable)
820{
821	gTTYModule->tty_hardware_signal(fSystemTTYCookie, line, enable);
822
823	return B_OK;
824}
825
826
827void
828SerialDevice::OnRead(char **buffer, size_t *numBytes)
829{
830	// default implementation - does nothing
831}
832
833
834void
835SerialDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes)
836{
837	// default implementation - does nothing
838}
839
840
841void
842SerialDevice::OnClose()
843{
844	// default implementation - does nothing
845}
846
847
848int32
849SerialDevice::_DeviceThread(void *data)
850{
851#if 0
852	SerialDevice *device = (SerialDevice *)data;
853
854	while (!device->fStopDeviceThread) {
855		status_t status = gUSBModule->queue_bulk(device->fReadPipe,
856			device->fReadBuffer, device->fReadBufferSize,
857			device->ReadCallbackFunction, data);
858		if (status < B_OK) {
859			TRACE_ALWAYS("device thread: queueing failed with error: 0x%08x\n", status);
860			break;
861		}
862
863		status = acquire_sem_etc(device->fDoneRead, 1, B_CAN_INTERRUPT, 0);
864		if (status < B_OK) {
865			TRACE_ALWAYS("device thread: failed to get read done sem 0x%08x\n", status);
866			break;
867		}
868
869		if (device->fStatusRead != B_OK) {
870			TRACE("device thread: device status error 0x%08x\n",
871				device->fStatusRead);
872			if (gUSBModule->clear_feature(device->fReadPipe,
873				USB_FEATURE_ENDPOINT_HALT) != B_OK) {
874				TRACE_ALWAYS("device thread: failed to clear halt feature\n");
875				break;
876			}
877		}
878
879		char *buffer = device->fReadBuffer;
880		size_t readLength = device->fActualLengthRead;
881		device->OnRead(&buffer, &readLength);
882		if (readLength == 0)
883			continue;
884
885		ddrover *ddr = gTTYModule->ddrstart(NULL);
886		if (!ddr) {
887			TRACE_ALWAYS("device thread: ddrstart problem\n");
888			return B_NO_MEMORY;
889		}
890
891		while (device->fInputStopped)
892			snooze(100);
893
894		gTTYModule->ttyilock(&device->fTTY, ddr, true);
895		for (size_t i = 0; i < readLength; i++)
896			gTTYModule->ttyin(&device->fTTY, ddr, buffer[i]);
897
898		gTTYModule->ttyilock(&device->fTTY, ddr, false);
899		gTTYModule->ddrdone(ddr);
900	}
901
902#endif
903	return B_OK;
904}
905
906
907status_t
908SerialDevice::_WriteToDevice()
909{
910	char *buffer = &fWriteBuffer[fWriteBufferIn];
911	size_t bytesLeft = DEF_BUFFER_SIZE - atomic_get(&fWriteBufferAvail);
912	bytesLeft = MIN(bytesLeft, DEF_BUFFER_SIZE - fWriteBufferIn);
913	TRACE("%s: in %d left %d\n", __FUNCTION__, fWriteBufferIn, bytesLeft);
914	status_t status = gTTYModule->tty_read(fDeviceTTYCookie, buffer,
915		&bytesLeft);
916	TRACE("%s: tty_read: %d\n", __FUNCTION__, bytesLeft);
917	if (status != B_OK) {
918		TRACE_ALWAYS("write to device: failed to read from TTY: %s\n",
919			strerror(status));
920		return status;
921	}
922	fWriteBufferIn += bytesLeft;
923	fWriteBufferIn %= DEF_BUFFER_SIZE;
924	atomic_add(&fWriteBufferAvail, bytesLeft);
925
926	// XXX: WTF: this ought to be done by the tty module calling service_func!
927	// enable irqs
928	Service(fMasterTTY, TTYOSTART, NULL, 0);
929
930	status = acquire_sem_etc(fWriteBufferSem, 1, B_CAN_INTERRUPT, 0);
931	if (status != B_OK) {
932		TRACE_ALWAYS("write to device: failed to acquire sem: %s\n",
933			strerror(status));
934		return status;
935	}
936	return B_OK;
937}
938
939
940void
941SerialDevice::ReadCallbackFunction(void *cookie, int32 status, void *data,
942	uint32 actualLength)
943{
944	TRACE_FUNCALLS("read callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
945		cookie, status, data, actualLength);
946
947	SerialDevice *device = (SerialDevice *)cookie;
948	device->fActualLengthRead = actualLength;
949	device->fStatusRead = status;
950	release_sem_etc(device->fDoneRead, 1, B_DO_NOT_RESCHEDULE);
951}
952
953
954void
955SerialDevice::WriteCallbackFunction(void *cookie, int32 status, void *data,
956	uint32 actualLength)
957{
958	TRACE_FUNCALLS("write callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
959		cookie, status, data, actualLength);
960
961	SerialDevice *device = (SerialDevice *)cookie;
962	device->fActualLengthWrite = actualLength;
963	device->fStatusWrite = status;
964	release_sem_etc(device->fDoneWrite, 1, B_DO_NOT_RESCHEDULE);
965}
966
967
968void
969SerialDevice::InterruptCallbackFunction(void *cookie, int32 status,
970	void *data, uint32 actualLength)
971{
972	TRACE_FUNCALLS("interrupt callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
973		cookie, status, data, actualLength);
974
975	SerialDevice *device = (SerialDevice *)cookie;
976	device->fActualLengthInterrupt = actualLength;
977	device->fStatusInterrupt = status;
978
979	// ToDo: maybe handle those somehow?
980
981	if (status == B_OK && !device->fDeviceRemoved) {
982#if 0
983		status = gUSBModule->queue_interrupt(device->fControlPipe,
984			device->fInterruptBuffer, device->fInterruptBufferSize,
985			device->InterruptCallbackFunction, device);
986#endif
987	}
988}
989
990
991
992#if 0
993SerialDevice *
994SerialDevice::MakeDevice(usb_device device, uint16 vendorID,
995	uint16 productID)
996{
997	const char *description = NULL;
998
999	switch (vendorID) {
1000		case VENDOR_IODATA:
1001		case VENDOR_ATEN:
1002		case VENDOR_TDK:
1003		case VENDOR_RATOC:
1004		case VENDOR_PROLIFIC:
1005		case VENDOR_ELECOM:
1006		case VENDOR_SOURCENEXT:
1007		case VENDOR_HAL:
1008		{
1009			switch (productID) {
1010				case PRODUCT_PROLIFIC_RSAQ2: description = "PL2303 Serial adapter (IODATA USB-RSAQ2)"; break;
1011				case PRODUCT_IODATA_USBRSAQ: description = "I/O Data USB serial adapter USB-RSAQ1"; break;
1012				case PRODUCT_ATEN_UC232A: description = "Aten Serial adapter"; break;
1013				case PRODUCT_TDK_UHA6400: description = "TDK USB-PHS Adapter UHA6400"; break;
1014				case PRODUCT_RATOC_REXUSB60: description = "Ratoc USB serial adapter REX-USB60"; break;
1015				case PRODUCT_PROLIFIC_PL2303: description = "PL2303 Serial adapter (ATEN/IOGEAR UC232A)"; break;
1016				case PRODUCT_ELECOM_UCSGT: description = "Elecom UC-SGT"; break;
1017				case PRODUCT_SOURCENEXT_KEIKAI8: description = "SOURCENEXT KeikaiDenwa 8"; break;
1018				case PRODUCT_SOURCENEXT_KEIKAI8_CHG: description = "SOURCENEXT KeikaiDenwa 8 with charger"; break;
1019				case PRODUCT_HAL_IMR001: description = "HAL Corporation Crossam2+USB"; break;
1020			}
1021
1022			if (!description)
1023				break;
1024
1025			return new(std::nothrow) ProlificDevice(device, vendorID, productID, description);
1026		}
1027
1028		case VENDOR_FTDI:
1029		{
1030			switch (productID) {
1031				case PRODUCT_FTDI_8U100AX: description = "FTDI 8U100AX serial converter"; break;
1032				case PRODUCT_FTDI_8U232AM: description = "FTDI 8U232AM serial converter"; break;
1033			}
1034
1035			if (!description)
1036				break;
1037
1038			return new(std::nothrow) FTDIDevice(device, vendorID, productID, description);
1039		}
1040
1041		case VENDOR_PALM:
1042		case VENDOR_KLSI:
1043		{
1044			switch (productID) {
1045				case PRODUCT_PALM_CONNECT: description = "PalmConnect RS232"; break;
1046				case PRODUCT_KLSI_KL5KUSB105D: description = "KLSI KL5KUSB105D"; break;
1047			}
1048
1049			if (!description)
1050				break;
1051
1052			return new(std::nothrow) KLSIDevice(device, vendorID, productID, description);
1053		}
1054	}
1055
1056	return new(std::nothrow) ACMDevice(device, vendorID, productID, "CDC ACM compatible device");
1057}
1058#endif
1059
1060
1061uint8
1062SerialDevice::ReadReg8(int reg)
1063{
1064	uint8 ret;
1065	switch (fBus) {
1066	case B_ISA_BUS:
1067		ret = gISAModule->read_io_8(IOBase() + reg);
1068		break;
1069	case B_PCI_BUS:
1070		ret = gPCIModule->read_io_8(IOBase() + reg);
1071		break;
1072	default:
1073		TRACE_ALWAYS("%s: unknown bus!\n", __FUNCTION__);
1074		ret = 0;
1075	//XXX:pcmcia ?
1076	}
1077	TRACE/*_ALWAYS*/("RR8(%d) = %d [%02x]\n", reg, ret, ret);
1078	//spin(1000);
1079	return ret;
1080}
1081
1082void
1083SerialDevice::WriteReg8(int reg, uint8 value)
1084{
1085//	TRACE_ALWAYS("WR8(0x%04x+%d, %d [0x%x])\n", IOBase(), reg, value, value);
1086	TRACE/*_ALWAYS*/("WR8(%d, %d [0x%x])\n", reg, value, value);
1087	switch (fBus) {
1088	case B_ISA_BUS:
1089		gISAModule->write_io_8(IOBase() + reg, value);
1090		break;
1091	case B_PCI_BUS:
1092		gPCIModule->write_io_8(IOBase() + reg, value);
1093		break;
1094	default:
1095		TRACE_ALWAYS("%s: unknown bus!\n", __FUNCTION__);
1096	//XXX:pcmcia ?
1097	}
1098	//spin(10000);
1099}
1100
1101
1102void
1103SerialDevice::OrReg8(int reg, uint8 value)
1104{
1105	WriteReg8(reg, ReadReg8(reg) | value);
1106}
1107
1108
1109void
1110SerialDevice::AndReg8(int reg, uint8 value)
1111{
1112	WriteReg8(reg, ReadReg8(reg) & value);
1113}
1114
1115
1116void
1117SerialDevice::MaskReg8(int reg, uint8 value)
1118{
1119	WriteReg8(reg, ReadReg8(reg) & ~value);
1120}
1121