1/*
2 * Copyright 2007-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2004-2009, Axel D��rfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8// This file could be moved into a generic tty module.
9// The whole hardware signaling stuff is missing, though - it's currently
10// tailored for pseudo-TTYs. Have a look at Be's TTY includes (drivers/tty/*)
11
12
13#include "tty_private.h"
14
15#include <ctype.h>
16#include <errno.h>
17#include <signal.h>
18#include <stdio.h>
19#include <string.h>
20#include <sys/ioctl.h>
21#include <sys/stat.h>
22#include <unistd.h>
23
24#include <util/AutoLock.h>
25#include <util/kernel_cpp.h>
26
27#include <team.h>
28
29#include <tty.h>
30
31
32//#define TTY_TRACE
33#ifdef TTY_TRACE
34#	define TRACE(x) dprintf x
35#else
36#	define TRACE(x) ;
37#endif
38
39
40/*
41	Locking
42	-------
43
44	There are three locks involved. If more than one needs to be held at a
45	time, they must be acquired in the order they are listed here.
46
47	gTTYCookieLock: Guards the access to the fields
48	tty_cookie::{thread_count,closed}, or more precisely makes access to them
49	atomic. thread_count is the number of threads currently using the cookie
50	(i.e. read(), write(), ioctl() operations in progress). Together with
51	blocking_semaphore this serves the purpose to make sure that all pending
52	operations are done at a certain point when closing a cookie
53	(cf. tty_close_cookie() and TTYReference).
54
55	tty::lock: Guards the access to tty::{input_buffer,settings::{termios,
56	window_size,pgrp_id}}. Moreover when held guarantees that tty::open_count
57	won't drop to zero. A tty and the tty connected to it (master and slave)
58	share the same lock.
59
60	gTTYRequestLock: Guards access to tty::{reader,writer}_queue (most
61	RequestQueue methods do the locking themselves (the lock is a
62	recursive_lock)), queued Requests and associated RequestOwners.
63
64
65	Reading/Writing
66	---------------
67
68	Most of the dirty work when dealing with reading/writing is done by the
69	{Reader,Writer}Locker classes. Upon construction they lock the tty,
70	(tty::lock) create a RequestOwner and queue Requests in the respective
71	reader/writer queues (tty::{reader,writer}_queue). The
72	Acquire{Reader,Writer}() methods need to be called before being allowed to
73	read/write. They ensure that there is actually something to read/space for
74	writing -- in blocking mode they wait, if necessary. When destroyed the
75	{Reader,Writer}Locker() remove the formerly enqueued Requests and notify
76	waiting reader/writer and/or send out select events, whatever is appropiate.
77
78	Acquire{Reader,Writer}() never return without an actual event being
79	occurred. Either an error has occurred (return value) -- in this case the
80	caller should terminate -- or bytes are available for reading/space for
81	writing (cf. AvailableBytes()).
82*/
83
84
85static void tty_notify_select_event(struct tty* tty, uint8 event);
86static void tty_notify_if_available(struct tty* tty, struct tty* otherTTY,
87	bool notifySelect);
88
89
90class AbstractLocker {
91public:
92	AbstractLocker(tty_cookie* cookie)
93		:
94		fCookie(cookie),
95		fBytes(0)
96	{
97	}
98
99	size_t AvailableBytes() const
100		{ return fBytes; }
101
102protected:
103	void Lock()
104		{ mutex_lock(&fCookie->tty->lock); }
105	void Unlock()
106		{ mutex_unlock(&fCookie->tty->lock); }
107
108	tty_cookie*	fCookie;
109	size_t		fBytes;
110};
111
112
113class WriterLocker : public AbstractLocker {
114public:
115								WriterLocker(tty_cookie* sourceCookie);
116								~WriterLocker();
117
118			status_t			AcquireWriter(bool dontBlock,
119									size_t bytesNeeded);
120
121private:
122			size_t				_CheckAvailableBytes() const;
123
124			struct tty*			fSource;
125			struct tty*			fTarget;
126			RequestOwner		fRequestOwner;
127			bool				fEcho;
128};
129
130
131class ReaderLocker : public AbstractLocker {
132public:
133								ReaderLocker(tty_cookie* cookie);
134								~ReaderLocker();
135
136			status_t			AcquireReader(bigtime_t timeout,
137									size_t bytesNeeded);
138
139private:
140			size_t				_CheckAvailableBytes() const;
141
142			struct tty*			fTTY;
143			RequestOwner		fRequestOwner;
144};
145
146
147class TTYReferenceLocking {
148public:
149	inline bool Lock(tty_cookie* cookie)
150	{
151		MutexLocker _(gTTYCookieLock);
152
153		if (cookie->closed)
154			return false;
155
156		cookie->thread_count++;
157
158		return true;
159	}
160
161	inline void Unlock(tty_cookie* cookie)
162	{
163		MutexLocker locker(gTTYCookieLock);
164
165		sem_id semaphore = -1;
166		if (--cookie->thread_count == 0 && cookie->closed)
167			semaphore = cookie->blocking_semaphore;
168
169		locker.Unlock();
170
171		if (semaphore >= 0) {
172			TRACE(("TTYReference: cookie %p closed, last operation done, "
173				"releasing blocking sem %ld\n", cookie, semaphore));
174
175			release_sem(semaphore);
176		}
177	}
178};
179
180typedef AutoLocker<tty_cookie, TTYReferenceLocking> TTYReference;
181
182
183// #pragma mark -
184
185
186Request::Request()
187	:
188	fOwner(NULL),
189	fCookie(NULL),
190	fBytesNeeded(0),
191	fNotified(false),
192	fError(false)
193{
194}
195
196
197void
198Request::Init(RequestOwner* owner, tty_cookie* cookie, size_t bytesNeeded)
199{
200	fOwner = owner;
201	fCookie = cookie;
202	fBytesNeeded = bytesNeeded;
203	fNotified = false;
204	fError = false;
205}
206
207
208void
209Request::Notify(size_t bytesAvailable)
210{
211	if (!fNotified && bytesAvailable >= fBytesNeeded && fOwner) {
212		fOwner->Notify(this);
213		fNotified = true;
214	}
215}
216
217
218void
219Request::NotifyError(status_t error)
220{
221	if (!fError && fOwner) {
222		fOwner->NotifyError(this, error);
223		fError = true;
224		fNotified = true;
225	}
226}
227
228
229void
230Request::Dump(const char* prefix)
231{
232	kprintf("%srequest: %p\n", prefix, this);
233	kprintf("%s  owner:        %p\n", prefix, fOwner);
234	kprintf("%s  cookie:       %p\n", prefix, fCookie);
235	kprintf("%s  bytes needed: %lu\n", prefix, fBytesNeeded);
236	kprintf("%s  notified:     %s\n", prefix, fNotified ? "true" : "false");
237	kprintf("%s  error:        %s\n", prefix, fError ? "true" : "false");
238}
239
240
241// #pragma mark -
242
243
244RequestQueue::RequestQueue()
245	:
246	fRequests()
247{
248}
249
250
251void
252RequestQueue::Add(Request* request)
253{
254	if (request) {
255		RecursiveLocker _(gTTYRequestLock);
256
257		fRequests.Add(request, true);
258	}
259}
260
261
262void
263RequestQueue::Remove(Request* request)
264{
265	if (request) {
266		RecursiveLocker _(gTTYRequestLock);
267
268		fRequests.Remove(request);
269	}
270}
271
272
273void
274RequestQueue::NotifyFirst(size_t bytesAvailable)
275{
276	RecursiveLocker _(gTTYRequestLock);
277
278	if (Request* first = First())
279		first->Notify(bytesAvailable);
280}
281
282
283void
284RequestQueue::NotifyError(status_t error)
285{
286	RecursiveLocker _(gTTYRequestLock);
287
288	for (RequestList::Iterator it = fRequests.GetIterator(); it.HasNext();) {
289		Request* request = it.Next();
290		request->NotifyError(error);
291	}
292}
293
294
295void
296RequestQueue::NotifyError(tty_cookie* cookie, status_t error)
297{
298	RecursiveLocker _(gTTYRequestLock);
299
300	for (RequestList::Iterator it = fRequests.GetIterator(); it.HasNext();) {
301		Request* request = it.Next();
302		if (request->TTYCookie() == cookie)
303			request->NotifyError(error);
304	}
305}
306
307
308void
309RequestQueue::Dump(const char* prefix)
310{
311	RequestList::Iterator it = fRequests.GetIterator();
312	while (Request* request = it.Next())
313		request->Dump(prefix);
314}
315
316
317// #pragma mark -
318
319
320RequestOwner::RequestOwner()
321	:
322	fConditionVariable(NULL),
323	fCookie(NULL),
324	fError(B_OK),
325	fBytesNeeded(1)
326{
327	fRequestQueues[0] = NULL;
328	fRequestQueues[1] = NULL;
329}
330
331
332/*!	The caller must already hold the request lock.
333*/
334void
335RequestOwner::Enqueue(tty_cookie* cookie, RequestQueue* queue1,
336	RequestQueue* queue2)
337{
338	TRACE(("%p->RequestOwner::Enqueue(%p, %p, %p)\n", this, cookie, queue1,
339		queue2));
340
341	fCookie = cookie;
342
343	fRequestQueues[0] = queue1;
344	fRequestQueues[1] = queue2;
345
346	fRequests[0].Init(this, cookie, fBytesNeeded);
347	if (queue1)
348		queue1->Add(&fRequests[0]);
349	else
350		fRequests[0].Notify(fBytesNeeded);
351
352	fRequests[1].Init(this, cookie, fBytesNeeded);
353	if (queue2)
354		queue2->Add(&fRequests[1]);
355	else
356		fRequests[1].Notify(fBytesNeeded);
357}
358
359
360/*!	The caller must already hold the request lock.
361*/
362void
363RequestOwner::Dequeue()
364{
365	TRACE(("%p->RequestOwner::Dequeue()\n", this));
366
367	if (fRequestQueues[0])
368		fRequestQueues[0]->Remove(&fRequests[0]);
369	if (fRequestQueues[1])
370		fRequestQueues[1]->Remove(&fRequests[1]);
371
372	fRequestQueues[0] = NULL;
373	fRequestQueues[1] = NULL;
374}
375
376
377void
378RequestOwner::SetBytesNeeded(size_t bytesNeeded)
379{
380	if (fRequestQueues[0])
381		fRequests[0].Init(this, fCookie, bytesNeeded);
382
383	if (fRequestQueues[1])
384		fRequests[1].Init(this, fCookie, bytesNeeded);
385}
386
387
388/*!	The request lock MUST NOT be held!
389*/
390status_t
391RequestOwner::Wait(bool interruptable, bigtime_t timeout)
392{
393	TRACE(("%p->RequestOwner::Wait(%d)\n", this, interruptable));
394
395	status_t error = B_OK;
396
397	RecursiveLocker locker(gTTYRequestLock);
398
399	// check, if already done
400	if (fError == B_OK
401		&& (!fRequests[0].WasNotified() || !fRequests[1].WasNotified())) {
402		// not yet done
403
404		// publish the condition variable
405		ConditionVariable conditionVariable;
406		conditionVariable.Init(this, "tty request");
407		fConditionVariable = &conditionVariable;
408
409		// add an entry to wait on
410		ConditionVariableEntry entry;
411		conditionVariable.Add(&entry);
412
413		locker.Unlock();
414
415		// wait
416		TRACE(("%p->RequestOwner::Wait(): waiting for condition...\n", this));
417
418		error = entry.Wait(
419			(interruptable ? B_CAN_INTERRUPT : 0) | B_RELATIVE_TIMEOUT,
420			timeout);
421
422		TRACE(("%p->RequestOwner::Wait(): condition occurred: %lx\n", this,
423			error));
424
425		// remove the condition variable
426		locker.Lock();
427		fConditionVariable = NULL;
428	}
429
430	// get the result
431	if (error == B_OK)
432		error = fError;
433
434	return error;
435}
436
437
438bool
439RequestOwner::IsFirstInQueues()
440{
441	RecursiveLocker locker(gTTYRequestLock);
442
443	for (int i = 0; i < 2; i++) {
444		if (fRequestQueues[i] && fRequestQueues[i]->First() != &fRequests[i])
445			return false;
446	}
447
448	return true;
449}
450
451
452void
453RequestOwner::Notify(Request* request)
454{
455	TRACE(("%p->RequestOwner::Notify(%p)\n", this, request));
456
457	if (fError == B_OK && !request->WasNotified()) {
458		bool notify = false;
459
460		if (&fRequests[0] == request) {
461			notify = fRequests[1].WasNotified();
462		} else if (&fRequests[1] == request) {
463			notify = fRequests[0].WasNotified();
464		} else {
465			// spurious call
466		}
467
468		if (notify && fConditionVariable)
469			fConditionVariable->NotifyOne();
470	}
471}
472
473
474void
475RequestOwner::NotifyError(Request* request, status_t error)
476{
477	TRACE(("%p->RequestOwner::NotifyError(%p, %lx)\n", this, request, error));
478
479	if (fError == B_OK) {
480		fError = error;
481
482		if (!fRequests[0].WasNotified() || !fRequests[1].WasNotified()) {
483			if (fConditionVariable)
484				fConditionVariable->NotifyOne();
485		}
486	}
487}
488
489
490// #pragma mark -
491
492
493WriterLocker::WriterLocker(tty_cookie* sourceCookie)
494	:
495	AbstractLocker(sourceCookie),
496	fSource(fCookie->tty),
497	fTarget(fCookie->other_tty),
498	fRequestOwner(),
499	fEcho(false)
500{
501	Lock();
502
503	// Now that the tty pair is locked, we can check, whether the target is
504	// open at all.
505	if (fTarget->open_count > 0) {
506		// The target tty is open. As soon as we have appended a request to
507		// the writer queue of the target, it is guaranteed to remain valid
508		// until we have removed the request (and notified the
509		// tty_close_cookie() pseudo request).
510
511		// get the echo mode
512		fEcho = (fSource->is_master
513			&& fSource->settings.termios.c_lflag & ECHO) != 0;
514
515		// enqueue ourselves in the respective request queues
516		RecursiveLocker locker(gTTYRequestLock);
517		fRequestOwner.Enqueue(fCookie, &fTarget->writer_queue,
518			(fEcho ? &fSource->writer_queue : NULL));
519	} else {
520		// target is not open: we set it to NULL; all further operations on
521		// this locker will fail
522		fTarget = NULL;
523	}
524}
525
526
527WriterLocker::~WriterLocker()
528{
529	// dequeue from request queues
530	RecursiveLocker locker(gTTYRequestLock);
531	fRequestOwner.Dequeue();
532
533	// check the tty queues and notify the next in line, and send out select
534	// events
535	if (fTarget)
536		tty_notify_if_available(fTarget, fSource, true);
537	if (fEcho)
538		tty_notify_if_available(fSource, fTarget, true);
539
540	locker.Unlock();
541
542	Unlock();
543}
544
545
546size_t
547WriterLocker::_CheckAvailableBytes() const
548{
549	size_t writable = line_buffer_writable(fTarget->input_buffer);
550	if (fEcho) {
551		// we can only write as much as is available on both ends
552		size_t locallyWritable = line_buffer_writable(fSource->input_buffer);
553		if (locallyWritable < writable)
554			writable = locallyWritable;
555	}
556	return writable;
557}
558
559
560status_t
561WriterLocker::AcquireWriter(bool dontBlock, size_t bytesNeeded)
562{
563	if (!fTarget)
564		return B_FILE_ERROR;
565	if (fEcho && fCookie->closed)
566		return B_FILE_ERROR;
567
568	RecursiveLocker requestLocker(gTTYRequestLock);
569
570	// check, if we're first in queue, and if there is space to write
571	if (fRequestOwner.IsFirstInQueues()) {
572		fBytes = _CheckAvailableBytes();
573		if (fBytes >= bytesNeeded)
574			return B_OK;
575	}
576
577	// We are not the first in queue or currently there's no space to write:
578	// bail out, if we shall not block.
579	if (dontBlock)
580		return B_WOULD_BLOCK;
581
582	// set the number of bytes we need and notify, just in case we're first in
583	// one of the queues (RequestOwner::SetBytesNeeded() resets the notification
584	// state)
585	fRequestOwner.SetBytesNeeded(bytesNeeded);
586	if (fTarget)
587		tty_notify_if_available(fTarget, fSource, false);
588	if (fEcho)
589		tty_notify_if_available(fSource, fTarget, false);
590
591	requestLocker.Unlock();
592
593	// block until something happens
594	Unlock();
595	status_t status = fRequestOwner.Wait(true);
596	Lock();
597
598	// RequestOwner::Wait() returns the error, but to avoid a race condition
599	// when closing a tty, we re-get the error with the tty lock being held.
600	if (status == B_OK) {
601		RecursiveLocker _(gTTYRequestLock);
602		status = fRequestOwner.Error();
603	}
604
605	if (status == B_OK) {
606		if (fTarget->open_count > 0)
607			fBytes = _CheckAvailableBytes();
608		else
609			status = B_FILE_ERROR;
610	}
611
612	return status;
613}
614
615
616//	#pragma mark -
617
618
619ReaderLocker::ReaderLocker(tty_cookie* cookie)
620	:
621	AbstractLocker(cookie),
622	fTTY(cookie->tty),
623	fRequestOwner()
624{
625	Lock();
626
627	// enqueue ourselves in the reader request queue
628	RecursiveLocker locker(gTTYRequestLock);
629	fRequestOwner.Enqueue(fCookie, &fTTY->reader_queue);
630}
631
632
633ReaderLocker::~ReaderLocker()
634{
635	// dequeue from reader request queue
636	RecursiveLocker locker(gTTYRequestLock);
637	fRequestOwner.Dequeue();
638
639	// check the tty queues and notify the next in line, and send out select
640	// events
641	struct tty* otherTTY = fCookie->other_tty;
642	tty_notify_if_available(fTTY, (otherTTY->open_count > 0 ? otherTTY : NULL),
643		true);
644
645	locker.Unlock();
646
647	Unlock();
648}
649
650
651status_t
652ReaderLocker::AcquireReader(bigtime_t timeout, size_t bytesNeeded)
653{
654	if (fCookie->closed)
655		return B_FILE_ERROR;
656
657	// check, if we're first in queue, and if there is something to read
658	if (fRequestOwner.IsFirstInQueues()) {
659		fBytes = _CheckAvailableBytes();
660		if (fBytes >= bytesNeeded)
661			return B_OK;
662	}
663
664	// We are not the first in queue or currently there's nothing to read:
665	// bail out, if we shall not block.
666	if (timeout <= 0)
667		return B_WOULD_BLOCK;
668
669	// reset the number of bytes we need
670	fRequestOwner.SetBytesNeeded(bytesNeeded);
671
672	// block until something happens
673	Unlock();
674	status_t status = fRequestOwner.Wait(true, timeout);
675	Lock();
676
677	if (status == B_OK)
678		fBytes = _CheckAvailableBytes();
679
680	return status;
681}
682
683
684size_t
685ReaderLocker::_CheckAvailableBytes() const
686{
687	// Reading from the slave with canonical input processing enabled means
688	// that we read at max until hitting a line end or EOF.
689	if (!fTTY->is_master && (fTTY->settings.termios.c_lflag & ICANON) != 0) {
690		return line_buffer_readable_line(fTTY->input_buffer,
691			fTTY->settings.termios.c_cc[VEOL],
692			fTTY->settings.termios.c_cc[VEOF]);
693	}
694
695	return line_buffer_readable(fTTY->input_buffer);
696}
697
698
699// #pragma mark -
700
701
702static void
703reset_termios(struct termios& termios)
704{
705	memset(&termios, 0, sizeof(struct termios));
706
707	termios.c_iflag = ICRNL;
708	termios.c_oflag = OPOST | ONLCR;
709	termios.c_cflag = B19200 | CS8 | CREAD | HUPCL;
710		// enable receiver, hang up on last close
711	termios.c_lflag = ECHO | ISIG | ICANON;
712	termios.c_ispeed = B19200;
713	termios.c_ospeed = B19200;
714
715	// control characters
716	termios.c_cc[VINTR] = CTRL('C');
717	termios.c_cc[VQUIT] = CTRL('\\');
718	termios.c_cc[VERASE] = 0x7f;
719	termios.c_cc[VKILL] = CTRL('U');
720	termios.c_cc[VEOF] = CTRL('D');
721	termios.c_cc[VEOL] = '\0';
722	termios.c_cc[VEOL2] = '\0';
723	termios.c_cc[VSTART] = CTRL('S');
724	termios.c_cc[VSTOP] = CTRL('Q');
725	termios.c_cc[VSUSP] = CTRL('Z');
726}
727
728
729static void
730reset_tty_settings(tty_settings& settings)
731{
732	reset_termios(settings.termios);
733
734	settings.pgrp_id = 0;
735		// this value prevents any signal of being sent
736	settings.session_id = -1;
737
738	// some initial window size - the TTY in question should set these values
739	settings.window_size.ws_col = 80;
740	settings.window_size.ws_row = 25;
741	settings.window_size.ws_xpixel = settings.window_size.ws_col * 8;
742	settings.window_size.ws_ypixel = settings.window_size.ws_row * 8;
743}
744
745
746/*!	Processes the input character and puts it into the TTY's input buffer.
747	Depending on the termios flags set, signals may be sent, the input
748	character changed or removed, etc.
749*/
750static void
751tty_input_putc_locked(struct tty* tty, int c)
752{
753	// process signals if needed
754
755	if ((tty->settings.termios.c_lflag & ISIG) != 0) {
756		// enable signals, process INTR, QUIT, and SUSP
757		int signal = -1;
758
759		if (c == tty->settings.termios.c_cc[VINTR])
760			signal = SIGINT;
761		else if (c == tty->settings.termios.c_cc[VQUIT])
762			signal = SIGQUIT;
763		else if (c == tty->settings.termios.c_cc[VSUSP])
764			signal = SIGTSTP;
765
766		// do we need to deliver a signal?
767		if (signal != -1) {
768			// we may have to flush the input buffer
769			if ((tty->settings.termios.c_lflag & NOFLSH) == 0)
770				clear_line_buffer(tty->input_buffer);
771
772			if (tty->settings.pgrp_id != 0)
773				send_signal(-tty->settings.pgrp_id, signal);
774			return;
775		}
776	}
777
778	// process special canonical input characters
779
780	if ((tty->settings.termios.c_lflag & ICANON) != 0) {
781		// canonical mode, process ERASE and KILL
782		cc_t* controlChars = tty->settings.termios.c_cc;
783
784		if (c == controlChars[VERASE]) {
785			// erase one character
786			char lastChar;
787			if (line_buffer_tail_getc(tty->input_buffer, &lastChar)) {
788				if (lastChar == controlChars[VEOF]
789					|| lastChar == controlChars[VEOL]
790					|| lastChar == '\n' || lastChar == '\r') {
791					// EOF or end of line -- put it back
792					line_buffer_putc(tty->input_buffer, lastChar);
793				}
794			}
795			return;
796		} else if (c == controlChars[VKILL]) {
797			// erase line
798			char lastChar;
799			while (line_buffer_tail_getc(tty->input_buffer, &lastChar)) {
800				if (lastChar == controlChars[VEOF]
801					|| lastChar == controlChars[VEOL]
802					|| lastChar == '\n' || lastChar == '\r') {
803					// EOF or end of line -- put it back
804					line_buffer_putc(tty->input_buffer, lastChar);
805					break;
806				}
807			}
808			return;
809		} else if (c == controlChars[VEOF]) {
810			// we still write the EOF to the stream -- tty_input_read() needs
811			// to recognize it
812			tty->pending_eof++;
813		}
814	}
815
816	// Input character conversions have already been done. What reaches this
817	// point can directly be written to the line buffer.
818
819	line_buffer_putc(tty->input_buffer, c);
820}
821
822
823static int32
824tty_readable(struct tty* tty)
825{
826	if (!tty->is_master && (tty->settings.termios.c_lflag & ICANON) != 0) {
827		return line_buffer_readable_line(tty->input_buffer,
828			tty->settings.termios.c_cc[VEOL],
829			tty->settings.termios.c_cc[VEOF]);
830	}
831
832	return line_buffer_readable(tty->input_buffer);
833}
834
835
836static void
837tty_notify_select_event(struct tty* tty, uint8 event)
838{
839	TRACE(("tty_notify_select_event(%p, %u)\n", tty, event));
840
841	if (tty->select_pool)
842		notify_select_event_pool(tty->select_pool, event);
843}
844
845
846/*!	\brief Checks whether bytes can be read from/written to the line buffer of
847		   the given TTY and notifies the respective queues.
848
849	Also sends out \c B_SELECT_READ and \c B_SELECT_WRITE events as needed.
850
851	The TTY and the request lock must be held.
852
853	\param tty The TTY.
854	\param otherTTY The connected TTY.
855*/
856static void
857tty_notify_if_available(struct tty* tty, struct tty* otherTTY,
858	bool notifySelect)
859{
860	if (!tty)
861		return;
862
863	// Check, if something is readable (depending on whether canonical input
864	// processing is enabled).
865	int32 readable = tty_readable(tty);
866	if (readable > 0) {
867		// if nobody is waiting send select events, otherwise notify the waiter
868		if (!tty->reader_queue.IsEmpty())
869			tty->reader_queue.NotifyFirst(readable);
870		else if (notifySelect)
871			tty_notify_select_event(tty, B_SELECT_READ);
872	}
873
874	int32 writable = line_buffer_writable(tty->input_buffer);
875	if (writable > 0) {
876		// if nobody is waiting send select events, otherwise notify the waiter
877		if (!tty->writer_queue.IsEmpty()) {
878			tty->writer_queue.NotifyFirst(writable);
879		} else if (notifySelect) {
880			if (otherTTY && otherTTY->open_count > 0)
881				tty_notify_select_event(otherTTY, B_SELECT_WRITE);
882		}
883	}
884}
885
886
887/*!	\brief Performs input character conversion and writes the result to
888		\a buffer.
889	\param tty The master tty.
890	\param c The input character.
891	\param buffer The buffer to which to write the converted character.
892	\param _bytesNeeded The number of bytes needed in the target tty's
893		line buffer.
894	\return \c true, if the character shall be processed further, \c false, if
895		it shall be skipped.
896*/
897static bool
898process_input_char(struct tty* tty, char c, char* buffer,
899	size_t* _bytesNeeded)
900{
901	tcflag_t flags = tty->settings.termios.c_iflag;
902
903	// signals
904	if (tty->settings.termios.c_lflag & ISIG) {
905		if (c == tty->settings.termios.c_cc[VINTR]
906			|| c == tty->settings.termios.c_cc[VQUIT]
907			|| c == tty->settings.termios.c_cc[VSUSP]) {
908			*buffer = c;
909			*_bytesNeeded = 0;
910			return true;
911		}
912	}
913
914	// canonical input characters
915	if (tty->settings.termios.c_lflag & ICANON) {
916		if (c == tty->settings.termios.c_cc[VERASE]
917			|| c == tty->settings.termios.c_cc[VKILL]) {
918			*buffer = c;
919			*_bytesNeeded = 0;
920			return true;
921		}
922	}
923
924	// convert chars
925	if (c == '\r') {
926		if (flags & IGNCR)		// ignore CR
927			return false;
928		if (flags & ICRNL)		// CR -> NL
929			c = '\n';
930	} else if (c == '\n') {
931		if (flags & INLCR)		// NL -> CR
932			c = '\r';
933	} else if ((flags & ISTRIP)	!= 0)	// strip off eighth bit
934		c &= 0x7f;
935
936	*buffer = c;
937	*_bytesNeeded = 1;
938	return true;
939}
940
941
942/*!	\brief Performs output character conversion and writes the result to
943		\a buffer.
944	\param tty The master tty.
945	\param c The output character.
946	\param buffer The buffer to which to write the converted character(s).
947	\param _bytesWritten The number of bytes written to the output buffer
948		(max 3).
949	\param echoed \c true if the output char to be processed has been echoed
950		from the input.
951*/
952static void
953process_output_char(struct tty* tty, char c, char* buffer,
954	size_t* _bytesWritten, bool echoed)
955{
956	tcflag_t flags = tty->settings.termios.c_oflag;
957
958	if (flags & OPOST) {
959		if (echoed && c == tty->settings.termios.c_cc[VERASE]) {
960			if (tty->settings.termios.c_lflag & ECHOE) {
961				// ERASE -> BS SPACE BS
962				buffer[0] = CTRL('H');
963				buffer[1] = ' ';
964				buffer[2] = CTRL('H');;
965				*_bytesWritten = 3;
966				return;
967			}
968		} else if (echoed && c == tty->settings.termios.c_cc[VKILL]) {
969			if (!(tty->settings.termios.c_lflag & ECHOK)) {
970				// don't echo KILL
971				*_bytesWritten = 0;
972				return;
973			}
974		} else if (echoed && c == tty->settings.termios.c_cc[VEOF]) {
975			// don't echo EOF
976			*_bytesWritten = 0;
977			return;
978		} else if (c == '\n') {
979			if (echoed && !(tty->settings.termios.c_lflag & ECHONL)) {
980				// don't echo NL
981				*_bytesWritten = 0;
982				return;
983			}
984			if (flags & ONLCR) {			// NL -> CR-NL
985				buffer[0] = '\r';
986				buffer[1] = '\n';
987				*_bytesWritten = 2;
988				return;
989			}
990		} else if (c == '\r') {
991			if (flags & OCRNL) {			// CR -> NL
992				c = '\n';
993			} else if (flags & ONLRET) {	// NL also does RET, ignore CR
994				*_bytesWritten = 0;
995				return;
996			} else if (flags & ONOCR) {		// don't output CR at column 0
997				// TODO: We can't decide that here.
998			}
999		} else {
1000			if (flags & OLCUC)				// lower case -> upper case
1001				c = toupper(c);
1002		}
1003	}
1004
1005	*buffer = c;
1006	*_bytesWritten = 1;
1007}
1008
1009
1010static status_t
1011tty_write_to_tty_master_unsafe(tty_cookie* sourceCookie, const char* data,
1012	size_t* _length)
1013{
1014	struct tty* source = sourceCookie->tty;
1015	struct tty* target = sourceCookie->other_tty;
1016	size_t length = *_length;
1017	size_t bytesWritten = 0;
1018	uint32 mode = sourceCookie->open_mode;
1019	bool dontBlock = (mode & O_NONBLOCK) != 0;
1020
1021	// bail out, if source is already closed
1022	TTYReference sourceTTYReference(sourceCookie);
1023	if (!sourceTTYReference.IsLocked())
1024		return B_FILE_ERROR;
1025
1026	if (length == 0)
1027		return B_OK;
1028
1029	WriterLocker locker(sourceCookie);
1030
1031	// if the target is not open, fail now
1032	if (target->open_count <= 0)
1033		return B_FILE_ERROR;
1034
1035	bool echo = (source->settings.termios.c_lflag & ECHO) != 0;
1036
1037	TRACE(("tty_write_to_tty_master(source = %p, target = %p, "
1038		"length = %lu%s)\n", source, target, length,
1039		(echo ? ", echo mode" : "")));
1040
1041	// Make sure we are first in the writer queue(s) and AvailableBytes() is
1042	// initialized.
1043	status_t status = locker.AcquireWriter(dontBlock, 0);
1044	if (status != B_OK) {
1045		*_length = 0;
1046		return status;
1047	}
1048	size_t writable = locker.AvailableBytes();
1049	size_t writtenSinceLastNotify = 0;
1050
1051	while (bytesWritten < length) {
1052		// fetch next char and do input processing
1053		char c;
1054		size_t bytesNeeded;
1055		if (!process_input_char(source, *data, &c, &bytesNeeded)) {
1056			// input char shall be skipped
1057			data++;
1058			bytesWritten++;
1059			continue;
1060		}
1061
1062		// If in echo mode, we do the output conversion and need to update
1063		// the needed bytes count.
1064		char echoBuffer[3];
1065		size_t echoBytes;
1066		if (echo) {
1067			process_output_char(source, c, echoBuffer, &echoBytes, true);
1068			if (echoBytes > bytesNeeded)
1069				bytesNeeded = echoBytes;
1070		}
1071
1072		// If there's not enough space to write what we have, we need to wait
1073		// until it is available.
1074		if (writable < bytesNeeded) {
1075			if (writtenSinceLastNotify > 0) {
1076				tty_notify_if_available(target, source, true);
1077				if (echo)
1078					tty_notify_if_available(source, target, true);
1079				writtenSinceLastNotify = 0;
1080			}
1081
1082			status = locker.AcquireWriter(dontBlock, bytesNeeded);
1083			if (status != B_OK) {
1084				*_length = bytesWritten;
1085				return status;
1086			}
1087
1088			writable = locker.AvailableBytes();
1089
1090			// XXX: do we need to support VMIN & VTIME for write() ?
1091
1092			// We need to restart the loop, since the termios flags might have
1093			// changed in the meantime (while we've unlocked the tty). Note,
1094			// that we don't re-get "echo" -- maybe we should.
1095			continue;
1096		}
1097
1098		// write the bytes
1099		tty_input_putc_locked(target, c);
1100
1101		if (echo) {
1102			for (size_t i = 0; i < echoBytes; i++)
1103				line_buffer_putc(source->input_buffer, echoBuffer[i]);
1104		}
1105
1106		writable -= bytesNeeded;
1107		data++;
1108		bytesWritten++;
1109		writtenSinceLastNotify++;
1110	}
1111
1112	return B_OK;
1113}
1114
1115
1116static status_t
1117tty_write_to_tty_slave_unsafe(tty_cookie* sourceCookie, const char* data,
1118	size_t* _length)
1119{
1120	struct tty* target = sourceCookie->other_tty;
1121	size_t length = *_length;
1122	size_t bytesWritten = 0;
1123	uint32 mode = sourceCookie->open_mode;
1124	bool dontBlock = (mode & O_NONBLOCK) != 0;
1125
1126	// bail out, if source is already closed
1127	TTYReference sourceTTYReference(sourceCookie);
1128	if (!sourceTTYReference.IsLocked())
1129		return B_FILE_ERROR;
1130
1131	if (length == 0)
1132		return B_OK;
1133
1134	WriterLocker locker(sourceCookie);
1135
1136	// if the target is not open, fail now
1137	if (target->open_count <= 0)
1138		return B_FILE_ERROR;
1139
1140	TRACE(("tty_write_to_tty_slave(source = %p, target = %p, length = %lu)\n",
1141		sourceCookie->tty, target, length));
1142
1143	// Make sure we are first in the writer queue(s) and AvailableBytes() is
1144	// initialized.
1145	status_t status = locker.AcquireWriter(dontBlock, 0);
1146	if (status != B_OK) {
1147		*_length = 0;
1148		return status;
1149	}
1150	size_t writable = locker.AvailableBytes();
1151	size_t writtenSinceLastNotify = 0;
1152
1153	while (bytesWritten < length) {
1154		// fetch next char and do output processing
1155		char buffer[3];
1156		size_t bytesNeeded;
1157		process_output_char(target, *data, buffer, &bytesNeeded, false);
1158
1159		// If there's not enough space to write what we have, we need to wait
1160		// until it is available.
1161		if (writable < bytesNeeded) {
1162			if (writtenSinceLastNotify > 0) {
1163				tty_notify_if_available(target, sourceCookie->tty, true);
1164				writtenSinceLastNotify = 0;
1165			}
1166
1167			status = locker.AcquireWriter(dontBlock, bytesNeeded);
1168			if (status != B_OK) {
1169				*_length = bytesWritten;
1170				return status;
1171			}
1172
1173			writable = locker.AvailableBytes();
1174
1175			// We need to restart the loop, since the termios flags might have
1176			// changed in the meantime (while we've unlocked the tty).
1177			continue;
1178		}
1179
1180		// write the bytes
1181		for (size_t i = 0; i < bytesNeeded; i++)
1182			line_buffer_putc(target->input_buffer, buffer[i]);
1183
1184		writable -= bytesNeeded;
1185		data++;
1186		bytesWritten++;
1187		writtenSinceLastNotify++;
1188	}
1189
1190	return B_OK;
1191}
1192
1193
1194static status_t
1195tty_write_to_tty_master(tty_cookie* sourceCookie, const void* _buffer,
1196	size_t* _length)
1197{
1198	const char* buffer = (const char*)_buffer;
1199	size_t bytesRemaining = *_length;
1200	*_length = 0;
1201
1202	while (bytesRemaining > 0) {
1203		// copy data to stack
1204		char safeBuffer[256];
1205		size_t toWrite = min_c(sizeof(safeBuffer), bytesRemaining);
1206		status_t error = user_memcpy(safeBuffer, buffer, toWrite);
1207		if (error != B_OK)
1208			return error;
1209
1210		// write them
1211		size_t written = toWrite;
1212		error = tty_write_to_tty_master_unsafe(sourceCookie, safeBuffer,
1213			&written);
1214		if (error != B_OK)
1215			return error;
1216
1217		buffer += written;
1218		bytesRemaining -= written;
1219		*_length += written;
1220
1221		if (written < toWrite)
1222			return B_OK;
1223	}
1224
1225	return B_OK;
1226}
1227
1228
1229static status_t
1230tty_write_to_tty_slave(tty_cookie* sourceCookie, const void* _buffer,
1231	size_t* _length)
1232{
1233	const char* buffer = (const char*)_buffer;
1234	size_t bytesRemaining = *_length;
1235	*_length = 0;
1236
1237	while (bytesRemaining > 0) {
1238		// copy data to stack
1239		char safeBuffer[256];
1240		size_t toWrite = min_c(sizeof(safeBuffer), bytesRemaining);
1241		status_t error = user_memcpy(safeBuffer, buffer, toWrite);
1242		if (error != B_OK)
1243			return error;
1244
1245		// write them
1246		size_t written = toWrite;
1247		error = tty_write_to_tty_slave_unsafe(sourceCookie, safeBuffer,
1248			&written);
1249		if (error != B_OK)
1250			return error;
1251
1252		buffer += written;
1253		bytesRemaining -= written;
1254		*_length += written;
1255
1256		if (written < toWrite)
1257			return B_OK;
1258	}
1259
1260	return B_OK;
1261}
1262
1263
1264#if 0
1265static void
1266dump_tty_settings(struct tty_settings& settings)
1267{
1268	kprintf("  pgrp_id:      %ld\n", settings.pgrp_id);
1269	kprintf("  session_id:   %ld\n", settings.session_id);
1270
1271	kprintf("  termios:\n");
1272	kprintf("    c_iflag:    0x%08lx\n", settings.termios.c_iflag);
1273	kprintf("    c_oflag:    0x%08lx\n", settings.termios.c_oflag);
1274	kprintf("    c_cflag:    0x%08lx\n", settings.termios.c_cflag);
1275	kprintf("    c_lflag:    0x%08lx\n", settings.termios.c_lflag);
1276	kprintf("    c_line:     %d\n", settings.termios.c_line);
1277	kprintf("    c_ispeed:   %u\n", settings.termios.c_ispeed);
1278	kprintf("    c_ospeed:   %u\n", settings.termios.c_ospeed);
1279	for (int i = 0; i < NCCS; i++)
1280		kprintf("    c_cc[%02d]:   %d\n", i, settings.termios.c_cc[i]);
1281
1282	kprintf("  wsize:        %u x %u c, %u x %u pxl\n",
1283		settings.window_size.ws_row, settings.window_size.ws_col,
1284		settings.window_size.ws_xpixel, settings.window_size.ws_ypixel);
1285}
1286
1287
1288static void
1289dump_tty_struct(struct tty& tty)
1290{
1291	kprintf("  tty @:        %p\n", &tty);
1292	kprintf("  is_master:    %s\n", tty.is_master ? "true" : "false");
1293	kprintf("  open_count:   %ld\n", tty.open_count);
1294	kprintf("  select_pool:  %p\n", tty.select_pool);
1295	kprintf("  pending_eof:  %lu\n", tty.pending_eof);
1296
1297	kprintf("  input_buffer:\n");
1298	kprintf("    first:      %ld\n", tty.input_buffer.first);
1299	kprintf("    in:         %lu\n", tty.input_buffer.in);
1300	kprintf("    size:       %lu\n", tty.input_buffer.size);
1301	kprintf("    buffer:     %p\n", tty.input_buffer.buffer);
1302
1303	kprintf("  reader queue:\n");
1304	tty.reader_queue.Dump("    ");
1305	kprintf("  writer queue:\n");
1306	tty.writer_queue.Dump("    ");
1307
1308	kprintf("  cookies:     ");
1309	TTYCookieList::Iterator it = tty.cookies.GetIterator();
1310	while (tty_cookie* cookie = it.Next())
1311		kprintf(" %p", cookie);
1312	kprintf("\n");
1313}
1314#endif
1315
1316
1317// #pragma mark - public API
1318
1319
1320struct tty*
1321tty_create(tty_service_func func, bool isMaster)
1322{
1323	struct tty* tty = new(std::nothrow) (struct tty);
1324	if (tty == NULL)
1325		return NULL;
1326
1327	mutex_init(&tty->lock, "tty lock");
1328
1329	tty->ref_count = 0;
1330	tty->open_count = 0;
1331	tty->select_pool = NULL;
1332	tty->is_master = isMaster;
1333	tty->pending_eof = 0;
1334	tty->hardware_bits = 0;
1335
1336	reset_tty_settings(tty->settings);
1337
1338	if (init_line_buffer(tty->input_buffer, TTY_BUFFER_SIZE) < B_OK) {
1339		delete tty;
1340		return NULL;
1341	}
1342
1343	tty->service_func = func;
1344
1345	// construct the queues
1346	new(&tty->reader_queue) RequestQueue;
1347	new(&tty->writer_queue) RequestQueue;
1348	new(&tty->cookies) TTYCookieList;
1349
1350	return tty;
1351}
1352
1353
1354void
1355tty_destroy(struct tty* tty)
1356{
1357	// destroy the queues
1358	tty->reader_queue.~RequestQueue();
1359	tty->writer_queue.~RequestQueue();
1360	tty->cookies.~TTYCookieList();
1361
1362	uninit_line_buffer(tty->input_buffer);
1363
1364	delete tty;
1365}
1366
1367
1368tty_cookie*
1369tty_create_cookie(struct tty* tty, struct tty* otherTTY, uint32 openMode)
1370{
1371	tty_cookie* cookie = new(std::nothrow) tty_cookie;
1372	if (cookie == NULL)
1373		return NULL;
1374
1375	cookie->blocking_semaphore = create_sem(0, "wait for tty close");
1376	if (cookie->blocking_semaphore < 0) {
1377		delete cookie;
1378		return NULL;
1379	}
1380
1381	cookie->tty = tty;
1382	cookie->other_tty = otherTTY;
1383	cookie->open_mode = openMode;
1384	cookie->thread_count = 0;
1385	cookie->closed = false;
1386
1387
1388	MutexLocker locker(cookie->tty->lock);
1389
1390	// add to the TTY's cookie list
1391	tty->cookies.Add(cookie);
1392	tty->open_count++;
1393	tty->ref_count++;
1394
1395	return cookie;
1396}
1397
1398
1399void
1400tty_close_cookie(tty_cookie* cookie)
1401{
1402	MutexLocker locker(gTTYCookieLock);
1403
1404	// Already closed? This can happen for slaves that have been closed when
1405	// the master was closed.
1406	if (cookie->closed)
1407		return;
1408
1409	// set the cookie's `closed' flag
1410	cookie->closed = true;
1411	bool unblock = (cookie->thread_count > 0);
1412
1413	// unblock blocking threads
1414	if (unblock) {
1415		cookie->tty->reader_queue.NotifyError(cookie, B_FILE_ERROR);
1416		cookie->tty->writer_queue.NotifyError(cookie, B_FILE_ERROR);
1417
1418		if (cookie->other_tty->open_count > 0) {
1419			cookie->other_tty->reader_queue.NotifyError(cookie, B_FILE_ERROR);
1420			cookie->other_tty->writer_queue.NotifyError(cookie, B_FILE_ERROR);
1421		}
1422	}
1423
1424	locker.Unlock();
1425
1426	// wait till all blocking (and now unblocked) threads have left the
1427	// critical code
1428	if (unblock) {
1429		TRACE(("tty_close_cookie(): cookie %p, there're still pending "
1430			"operations, acquire blocking sem %ld\n", cookie,
1431			cookie->blocking_semaphore));
1432
1433		acquire_sem(cookie->blocking_semaphore);
1434	}
1435
1436	// For the removal of the cookie acquire the TTY's lock. This ensures, that
1437	// cookies will not be removed from a TTY (or added -- cf. add_tty_cookie())
1438	// as long as the TTY's lock is being held.
1439	MutexLocker ttyLocker(cookie->tty->lock);
1440
1441	// remove the cookie from the TTY's cookie list
1442	cookie->tty->cookies.Remove(cookie);
1443
1444	// close the tty, if no longer used
1445	if (--cookie->tty->open_count == 0) {
1446		// The last cookie of this tty has been closed. We're going to close
1447		// the TTY and need to unblock all write requests before. There should
1448		// be no read requests, since only a cookie of this TTY issues those.
1449		// We do this by first notifying all queued requests of the error
1450		// condition. We then clear the line buffer for the TTY and queue
1451		// an own request.
1452
1453		// Notify the other TTY first; it doesn't accept any read/writes
1454		// while there is only one end.
1455		cookie->other_tty->reader_queue.NotifyError(B_FILE_ERROR);
1456		cookie->other_tty->writer_queue.NotifyError(B_FILE_ERROR);
1457
1458		RecursiveLocker requestLocker(gTTYRequestLock);
1459
1460		// we only need to do all this, if the writer queue is not empty
1461		if (!cookie->tty->writer_queue.IsEmpty()) {
1462	 		// notify the blocking writers
1463			cookie->tty->writer_queue.NotifyError(B_FILE_ERROR);
1464
1465			// enqueue our request
1466			RequestOwner requestOwner;
1467			requestOwner.Enqueue(cookie, &cookie->tty->writer_queue);
1468
1469			requestLocker.Unlock();
1470
1471			// clear the line buffer
1472			clear_line_buffer(cookie->tty->input_buffer);
1473
1474			ttyLocker.Unlock();
1475
1476			// wait for our turn
1477			requestOwner.Wait(false);
1478
1479			// re-lock
1480			ttyLocker.Lock();
1481			requestLocker.Lock();
1482
1483			// dequeue our request
1484			requestOwner.Dequeue();
1485		}
1486
1487		requestLocker.Unlock();
1488	}
1489
1490	// notify pending select()s and cleanup the select sync pool
1491
1492	// notify a select write event on the other tty, if we've closed this tty
1493	if (cookie->tty->open_count == 0 && cookie->other_tty->open_count > 0)
1494		tty_notify_select_event(cookie->other_tty, B_SELECT_WRITE);
1495}
1496
1497
1498void
1499tty_destroy_cookie(tty_cookie* cookie)
1500{
1501	MutexLocker locker(cookie->tty->lock);
1502	cookie->tty->ref_count--;
1503	locker.Unlock();
1504
1505	if (cookie->blocking_semaphore >= 0)
1506		delete_sem(cookie->blocking_semaphore);
1507
1508	delete cookie;
1509}
1510
1511
1512status_t
1513tty_read(tty_cookie* cookie, void* _buffer, size_t* _length)
1514{
1515	char* buffer = (char*)_buffer;
1516	struct tty* tty = cookie->tty;
1517	uint32 mode = cookie->open_mode;
1518	bool dontBlock = (mode & O_NONBLOCK) != 0;
1519	size_t length = *_length;
1520	bool canon = true;
1521	bigtime_t timeout = dontBlock ? 0 : B_INFINITE_TIMEOUT;
1522	bigtime_t interCharTimeout = 0;
1523	size_t bytesNeeded = 1;
1524
1525	TRACE(("tty_input_read(tty = %p, length = %lu, mode = %lu)\n", tty, length, mode));
1526
1527	if (length == 0)
1528		return B_OK;
1529
1530	// bail out, if the TTY is already closed
1531	TTYReference ttyReference(cookie);
1532	if (!ttyReference.IsLocked())
1533		return B_FILE_ERROR;
1534
1535	ReaderLocker locker(cookie);
1536
1537	// handle raw mode
1538	if ((!tty->is_master) && ((tty->settings.termios.c_lflag & ICANON) == 0)) {
1539		canon = false;
1540		if (!dontBlock) {
1541			// Non-blocking mode. Handle VMIN and VTIME.
1542			bytesNeeded = tty->settings.termios.c_cc[VMIN];
1543			bigtime_t vtime = tty->settings.termios.c_cc[VTIME] * 100000;
1544			TRACE(("tty_input_read: icanon vmin %lu, vtime %Ldus\n", bytesNeeded,
1545				vtime));
1546
1547			if (bytesNeeded == 0) {
1548				// In this case VTIME specifies a relative total timeout. We
1549				// have no inter-char timeout, though.
1550				timeout = vtime;
1551			} else {
1552				// VTIME specifies the inter-char timeout. 0 is indefinitely.
1553				if (vtime == 0)
1554					interCharTimeout = B_INFINITE_TIMEOUT;
1555				else
1556					interCharTimeout = vtime;
1557
1558				if (bytesNeeded > length)
1559					bytesNeeded = length;
1560			}
1561		}
1562	}
1563
1564	status_t status;
1565	*_length = 0;
1566
1567	do {
1568		TRACE(("tty_input_read: AcquireReader(%Ldus, %ld)\n", timeout,
1569			bytesNeeded));
1570		status = locker.AcquireReader(timeout, bytesNeeded);
1571		if (status != B_OK)
1572			break;
1573
1574		size_t toRead = locker.AvailableBytes();
1575		if (toRead > length)
1576			toRead = length;
1577
1578		bool _hitEOF = false;
1579		bool* hitEOF = canon && tty->pending_eof > 0 ? &_hitEOF : NULL;
1580
1581		ssize_t bytesRead = line_buffer_user_read(tty->input_buffer, buffer,
1582			toRead, tty->settings.termios.c_cc[VEOF], hitEOF);
1583		if (bytesRead < 0) {
1584			status = bytesRead;
1585			break;
1586		}
1587
1588		buffer += bytesRead;
1589		length -= bytesRead;
1590		*_length += bytesRead;
1591		bytesNeeded = (size_t)bytesRead > bytesNeeded
1592			? 0 : bytesNeeded - bytesRead;
1593
1594		// we hit an EOF char -- bail out, whatever amount of data we have
1595		if (hitEOF && *hitEOF) {
1596			tty->pending_eof--;
1597			break;
1598		}
1599
1600		// Once we have read something reset the timeout to the inter-char
1601		// timeout, if applicable.
1602		if (!dontBlock && !canon && *_length > 0)
1603			timeout = interCharTimeout;
1604	} while (bytesNeeded > 0);
1605
1606	if (status == B_WOULD_BLOCK || status == B_TIMED_OUT) {
1607		// In non-blocking non-canonical-input-processing mode never return
1608		// timeout errors. Just return 0, if nothing has been read.
1609		if (!dontBlock && !canon)
1610			status = B_OK;
1611	}
1612
1613	return *_length == 0 ? status : B_OK;
1614}
1615
1616
1617status_t
1618tty_write(tty_cookie* sourceCookie, const void* _buffer, size_t* _length)
1619{
1620	if (sourceCookie->tty->is_master)
1621		return tty_write_to_tty_master(sourceCookie, _buffer, _length);
1622
1623	return tty_write_to_tty_slave(sourceCookie, _buffer, _length);
1624}
1625
1626
1627status_t
1628tty_control(tty_cookie* cookie, uint32 op, void* buffer, size_t length)
1629{
1630	struct tty* tty = cookie->tty;
1631
1632	// bail out, if already closed
1633	TTYReference ttyReference(cookie);
1634	if (!ttyReference.IsLocked())
1635		return B_FILE_ERROR;
1636
1637	TRACE(("tty_ioctl: tty %p, op %lu, buffer %p, length %lu\n", tty, op, buffer, length));
1638	MutexLocker locker(tty->lock);
1639
1640	switch (op) {
1641		// blocking/non-blocking mode
1642
1643		case B_SET_BLOCKING_IO:
1644			cookie->open_mode &= ~O_NONBLOCK;
1645			return B_OK;
1646		case B_SET_NONBLOCKING_IO:
1647			cookie->open_mode |= O_NONBLOCK;
1648			return B_OK;
1649
1650		// get and set TTY attributes
1651
1652		case TCGETA:
1653			TRACE(("tty: get attributes\n"));
1654			return user_memcpy(buffer, &tty->settings.termios,
1655				sizeof(struct termios));
1656
1657		case TCSETA:
1658		case TCSETAW:
1659		case TCSETAF:
1660		{
1661			TRACE(("tty: set attributes (iflag = %lx, oflag = %lx, "
1662				"cflag = %lx, lflag = %lx)\n", tty->settings.termios.c_iflag,
1663				tty->settings.termios.c_oflag, tty->settings.termios.c_cflag,
1664				tty->settings.termios.c_lflag));
1665
1666			status_t status = user_memcpy(&tty->settings.termios, buffer,
1667				sizeof(struct termios));
1668			if (status != B_OK)
1669				return status;
1670
1671			tty->service_func(tty, TTYSETMODES, &tty->settings.termios,
1672				sizeof(struct termios));
1673			return status;
1674		}
1675
1676		// get and set window size
1677
1678		case TIOCGWINSZ:
1679			TRACE(("tty: set window size\n"));
1680			return user_memcpy(buffer, &tty->settings.window_size,
1681				sizeof(struct winsize));
1682
1683		case TIOCSWINSZ:
1684		{
1685			uint16 oldColumns = tty->settings.window_size.ws_col;
1686			uint16 oldRows = tty->settings.window_size.ws_row;
1687
1688			TRACE(("tty: set window size\n"));
1689			if (user_memcpy(&tty->settings.window_size, buffer,
1690					sizeof(struct winsize)) < B_OK) {
1691				return B_BAD_ADDRESS;
1692			}
1693
1694			// send a signal only if the window size has changed
1695			if ((oldColumns != tty->settings.window_size.ws_col
1696					|| oldRows != tty->settings.window_size.ws_row)
1697				&& tty->settings.pgrp_id != 0) {
1698				send_signal(-tty->settings.pgrp_id, SIGWINCH);
1699			}
1700
1701			return B_OK;
1702		}
1703
1704		case FIONREAD:
1705		{
1706			int toRead = 0;
1707
1708			// release the mutex and grab a read lock
1709			locker.Unlock();
1710			ReaderLocker readLocker(cookie);
1711
1712			status_t status = readLocker.AcquireReader(0, 1);
1713			if (status == B_OK)
1714				toRead = readLocker.AvailableBytes();
1715			else if (status != B_WOULD_BLOCK)
1716				return status;
1717
1718			if (user_memcpy(buffer, &toRead, sizeof(int)) != B_OK)
1719				return B_BAD_ADDRESS;
1720
1721			return B_OK;
1722		}
1723
1724		case TCXONC:			// Unix, but even Linux doesn't handle it
1725			//dprintf("tty: unsupported TCXONC\n");
1726			break;
1727
1728		case TCSETDTR:
1729		case TCSETRTS:
1730		case TIOCMSET:
1731		{
1732			// control line state setting, we only support DTR and RTS
1733			int value;
1734			if (user_memcpy(&value, buffer, sizeof(value)) != B_OK)
1735				return B_BAD_ADDRESS;
1736
1737			bool result = false;
1738			bool dtr = (op == TCSETDTR  && value != 0)
1739				|| (op == TIOCMSET && (value & TIOCM_DTR) != 0);
1740			if (op == TCSETDTR || op == TIOCMSET)
1741				result = tty->service_func(tty, TTYSETDTR, &dtr, sizeof(dtr));
1742
1743			bool rts = (op == TCSETRTS && value != 0)
1744				|| (op == TIOCMSET && (value & TIOCM_RTS) != 0);
1745			if (op == TCSETRTS || op == TIOCMSET)
1746				result = tty->service_func(tty, TTYSETRTS, &rts, sizeof(rts));
1747
1748			return result ? B_OK : B_ERROR;
1749		}
1750
1751		case TCGETBITS:
1752		case TIOCMGET:
1753		{
1754			tty->service_func(tty, TTYGETSIGNALS, NULL, 0);
1755			int bits = tty->hardware_bits;
1756			return user_memcpy(buffer, &bits, sizeof(bits));
1757		}
1758
1759		case TIOCSBRK:
1760		case TIOCCBRK:
1761		case TCSBRK:
1762		{
1763			bool set;
1764			if (op == TIOCSBRK)
1765				set = true;
1766			else if (op == TIOCCBRK)
1767				set = false;
1768			else {
1769				int value;
1770				if (user_memcpy(&value, buffer, sizeof(value)) != B_OK)
1771					return B_BAD_ADDRESS;
1772
1773				set = value != 0;
1774			}
1775
1776			if (tty->service_func(tty, TTYSETBREAK, &set, sizeof(set)))
1777				return B_OK;
1778
1779			return B_ERROR;
1780		}
1781	}
1782
1783	TRACE(("tty: unsupported opcode %lu\n", op));
1784	return B_BAD_VALUE;
1785}
1786
1787
1788status_t
1789tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync)
1790{
1791	struct tty* tty = cookie->tty;
1792
1793	TRACE(("tty_select(cookie = %p, event = %u, ref = %lu, sync = %p)\n",
1794		cookie, event, ref, sync));
1795
1796	// we don't support all kinds of events
1797	if (event < B_SELECT_READ || event > B_SELECT_ERROR)
1798		return B_BAD_VALUE;
1799
1800	// if the TTY is already closed, we notify immediately
1801	TTYReference ttyReference(cookie);
1802	if (!ttyReference.IsLocked()) {
1803		TRACE(("tty_select() done: cookie %p already closed\n", cookie));
1804
1805		notify_select_event(sync, event);
1806		return B_OK;
1807	}
1808
1809	// lock the TTY (allows us to freely access the cookie lists of this and
1810	// the other TTY)
1811	MutexLocker ttyLocker(tty->lock);
1812
1813	// get the other TTY -- needed for `write' events
1814	struct tty* otherTTY = cookie->other_tty;
1815	if (otherTTY->open_count <= 0)
1816		otherTTY = NULL;
1817
1818	// add the event to the TTY's pool
1819	status_t error = add_select_sync_pool_entry(&tty->select_pool, sync, event);
1820	if (error != B_OK) {
1821		TRACE(("tty_select() done: add_select_sync_pool_entry() failed: %lx\n",
1822			error));
1823
1824		return error;
1825	}
1826
1827	// finally also acquire the request mutex, for access to the reader/writer
1828	// queues
1829	RecursiveLocker requestLocker(gTTYRequestLock);
1830
1831	// check, if the event is already present
1832	switch (event) {
1833		case B_SELECT_READ:
1834			if (tty->reader_queue.IsEmpty() && tty_readable(tty) > 0)
1835				notify_select_event(sync, event);
1836			break;
1837
1838		case B_SELECT_WRITE:
1839		{
1840			// writes go to the other TTY
1841			if (!otherTTY) {
1842				notify_select_event(sync, event);
1843				break;
1844			}
1845
1846			// In case input is echoed, we have to check, whether we can
1847			// currently can write to our TTY as well.
1848			bool echo = (tty->is_master
1849				&& tty->settings.termios.c_lflag & ECHO);
1850
1851			if (otherTTY->writer_queue.IsEmpty()
1852				&& line_buffer_writable(otherTTY->input_buffer) > 0) {
1853				if (!echo
1854					|| (tty->writer_queue.IsEmpty()
1855						&& line_buffer_writable(tty->input_buffer) > 0)) {
1856					notify_select_event(sync, event);
1857				}
1858			}
1859			break;
1860		}
1861
1862		case B_SELECT_ERROR:
1863		default:
1864			break;
1865	}
1866
1867	return B_OK;
1868}
1869
1870
1871status_t
1872tty_deselect(tty_cookie* cookie, uint8 event, selectsync* sync)
1873{
1874	struct tty* tty = cookie->tty;
1875
1876	TRACE(("tty_deselect(cookie = %p, event = %u, sync = %p)\n", cookie, event,
1877		sync));
1878
1879	// we don't support all kinds of events
1880	if (event < B_SELECT_READ || event > B_SELECT_ERROR)
1881		return B_BAD_VALUE;
1882
1883	// lock the TTY (guards the select sync pool, among other things)
1884	MutexLocker ttyLocker(tty->lock);
1885
1886	return remove_select_sync_pool_entry(&tty->select_pool, sync, event);
1887}
1888
1889
1890status_t
1891tty_hardware_signal(tty_cookie* cookie, int signal, bool set)
1892{
1893	int bit = 0;
1894
1895	switch (signal) {
1896		case TTYHWDCD:
1897			bit = TCGB_DCD;
1898			break;
1899		case TTYHWCTS:
1900			bit = TCGB_CTS;
1901			break;
1902		case TTYHWDSR:
1903			bit = TCGB_DSR;
1904			break;
1905		case TTYHWRI:
1906			bit = TCGB_RI;
1907			break;
1908
1909		default:
1910			return B_BAD_VALUE;
1911	}
1912
1913	if (set)
1914		cookie->tty->hardware_bits |= bit;
1915	else
1916		cookie->tty->hardware_bits &= ~bit;
1917
1918	return B_ERROR;
1919}
1920