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