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