xref: /haiku/src/add-ons/kernel/generic/tty/tty.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 %ld\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: %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 
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, %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 
493 WriterLocker::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 
527 WriterLocker::~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 
546 size_t
547 WriterLocker::_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 
560 status_t
561 WriterLocker::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 
619 ReaderLocker::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 
633 ReaderLocker::~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 
651 status_t
652 ReaderLocker::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 
684 size_t
685 ReaderLocker::_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 
702 static void
703 reset_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 
729 static void
730 reset_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 */
750 static void
751 tty_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 
823 static int32
824 tty_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 
836 static void
837 tty_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 */
856 static void
857 tty_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 */
897 static bool
898 process_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 */
952 static void
953 process_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 
1010 static status_t
1011 tty_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 = 0;
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 
1116 static status_t
1117 tty_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 
1194 static status_t
1195 tty_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 
1229 static status_t
1230 tty_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
1265 static void
1266 dump_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 
1288 static void
1289 dump_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 
1320 struct tty*
1321 tty_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 
1354 void
1355 tty_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 
1368 tty_cookie*
1369 tty_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 
1399 void
1400 tty_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 
1498 void
1499 tty_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 
1512 status_t
1513 tty_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 
1617 status_t
1618 tty_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 
1627 status_t
1628 tty_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 = true;
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 TIOCMBIS:
1760 		case TIOCMBIC:
1761 		{
1762 			// control line state setting, we only support DTR and RTS
1763 			int value;
1764 			if (user_memcpy(&value, buffer, sizeof(value)) != B_OK)
1765 				return B_BAD_ADDRESS;
1766 
1767 			bool result = true;
1768 			bool dtr = (op == TIOCMBIS);
1769 			if (value & TIOCM_DTR)
1770 				result &= tty->service_func(tty, TTYSETDTR, &dtr, sizeof(dtr));
1771 
1772 			bool rts = (op == TIOCMBIS);
1773 			if (value & TIOCM_RTS)
1774 				result &= tty->service_func(tty, TTYSETRTS, &rts, sizeof(rts));
1775 
1776 			return result ? B_OK : B_ERROR;
1777 		}
1778 
1779 		case TIOCSBRK:
1780 		case TIOCCBRK:
1781 		case TCSBRK:
1782 		{
1783 			bool set;
1784 			if (op == TIOCSBRK)
1785 				set = true;
1786 			else if (op == TIOCCBRK)
1787 				set = false;
1788 			else {
1789 				int value;
1790 				if (user_memcpy(&value, buffer, sizeof(value)) != B_OK)
1791 					return B_BAD_ADDRESS;
1792 
1793 				set = value != 0;
1794 			}
1795 
1796 			if (tty->service_func(tty, TTYSETBREAK, &set, sizeof(set)))
1797 				return B_OK;
1798 
1799 			return B_ERROR;
1800 		}
1801 	}
1802 
1803 	TRACE(("tty: unsupported opcode %lu\n", op));
1804 	return B_BAD_VALUE;
1805 }
1806 
1807 
1808 status_t
1809 tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync)
1810 {
1811 	struct tty* tty = cookie->tty;
1812 
1813 	TRACE(("tty_select(cookie = %p, event = %u, ref = %lu, sync = %p)\n",
1814 		cookie, event, ref, sync));
1815 
1816 	// we don't support all kinds of events
1817 	if (event < B_SELECT_READ || event > B_SELECT_ERROR)
1818 		return B_BAD_VALUE;
1819 
1820 	// if the TTY is already closed, we notify immediately
1821 	TTYReference ttyReference(cookie);
1822 	if (!ttyReference.IsLocked()) {
1823 		TRACE(("tty_select() done: cookie %p already closed\n", cookie));
1824 
1825 		notify_select_event(sync, event);
1826 		return B_OK;
1827 	}
1828 
1829 	// lock the TTY (allows us to freely access the cookie lists of this and
1830 	// the other TTY)
1831 	MutexLocker ttyLocker(tty->lock);
1832 
1833 	// get the other TTY -- needed for `write' events
1834 	struct tty* otherTTY = cookie->other_tty;
1835 	if (otherTTY->open_count <= 0)
1836 		otherTTY = NULL;
1837 
1838 	// add the event to the TTY's pool
1839 	status_t error = add_select_sync_pool_entry(&tty->select_pool, sync, event);
1840 	if (error != B_OK) {
1841 		TRACE(("tty_select() done: add_select_sync_pool_entry() failed: %lx\n",
1842 			error));
1843 
1844 		return error;
1845 	}
1846 
1847 	// finally also acquire the request mutex, for access to the reader/writer
1848 	// queues
1849 	RecursiveLocker requestLocker(gTTYRequestLock);
1850 
1851 	// check, if the event is already present
1852 	switch (event) {
1853 		case B_SELECT_READ:
1854 			if (tty->reader_queue.IsEmpty() && tty_readable(tty) > 0)
1855 				notify_select_event(sync, event);
1856 			break;
1857 
1858 		case B_SELECT_WRITE:
1859 		{
1860 			// writes go to the other TTY
1861 			if (!otherTTY) {
1862 				notify_select_event(sync, event);
1863 				break;
1864 			}
1865 
1866 			// In case input is echoed, we have to check, whether we can
1867 			// currently can write to our TTY as well.
1868 			bool echo = (tty->is_master
1869 				&& tty->settings.termios.c_lflag & ECHO);
1870 
1871 			if (otherTTY->writer_queue.IsEmpty()
1872 				&& line_buffer_writable(otherTTY->input_buffer) > 0) {
1873 				if (!echo
1874 					|| (tty->writer_queue.IsEmpty()
1875 						&& line_buffer_writable(tty->input_buffer) > 0)) {
1876 					notify_select_event(sync, event);
1877 				}
1878 			}
1879 			break;
1880 		}
1881 
1882 		case B_SELECT_ERROR:
1883 		default:
1884 			break;
1885 	}
1886 
1887 	return B_OK;
1888 }
1889 
1890 
1891 status_t
1892 tty_deselect(tty_cookie* cookie, uint8 event, selectsync* sync)
1893 {
1894 	struct tty* tty = cookie->tty;
1895 
1896 	TRACE(("tty_deselect(cookie = %p, event = %u, sync = %p)\n", cookie, event,
1897 		sync));
1898 
1899 	// we don't support all kinds of events
1900 	if (event < B_SELECT_READ || event > B_SELECT_ERROR)
1901 		return B_BAD_VALUE;
1902 
1903 	// lock the TTY (guards the select sync pool, among other things)
1904 	MutexLocker ttyLocker(tty->lock);
1905 
1906 	return remove_select_sync_pool_entry(&tty->select_pool, sync, event);
1907 }
1908 
1909 
1910 status_t
1911 tty_hardware_signal(tty_cookie* cookie, int signal, bool set)
1912 {
1913 	int bit = 0;
1914 
1915 	switch (signal) {
1916 		case TTYHWDCD:
1917 			bit = TCGB_DCD;
1918 			break;
1919 		case TTYHWCTS:
1920 			bit = TCGB_CTS;
1921 			break;
1922 		case TTYHWDSR:
1923 			bit = TCGB_DSR;
1924 			break;
1925 		case TTYHWRI:
1926 			bit = TCGB_RI;
1927 			break;
1928 
1929 		default:
1930 			return B_BAD_VALUE;
1931 	}
1932 
1933 	if (set)
1934 		cookie->tty->hardware_bits |= bit;
1935 	else
1936 		cookie->tty->hardware_bits &= ~bit;
1937 
1938 	return B_ERROR;
1939 }
1940