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.
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/*)
13 #include "tty_private.h"
20 #include <sys/ioctl.h>
24 #include <util/AutoLock.h>
25 #include <util/kernel_cpp.h>
34 # define TRACE(x) dprintf x
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.
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.
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).
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
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.
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.
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()).
91 tty_settings gTTYSettings
[kNumTTYs
];
94 static void tty_notify_select_event(struct tty
* tty
, uint8 event
);
95 static void tty_notify_if_available(struct tty
* tty
, struct tty
* otherTTY
,
99 class AbstractLocker
{
101 AbstractLocker(tty_cookie
* cookie
)
108 size_t AvailableBytes() const
113 { mutex_lock(fCookie
->tty
->lock
); }
115 { mutex_unlock(fCookie
->tty
->lock
); }
122 class WriterLocker
: public AbstractLocker
{
124 WriterLocker(tty_cookie
* sourceCookie
);
127 status_t
AcquireWriter(bool dontBlock
,
131 size_t _CheckAvailableBytes() const;
132 status_t
_CheckBackgroundWrite() const;
136 RequestOwner fRequestOwner
;
141 class ReaderLocker
: public AbstractLocker
{
143 ReaderLocker(tty_cookie
* cookie
);
146 status_t
AcquireReader(bigtime_t timeout
,
150 size_t _CheckAvailableBytes() const;
151 status_t
_CheckBackgroundRead() const;
154 RequestOwner fRequestOwner
;
158 class TTYReferenceLocking
{
160 inline bool Lock(tty_cookie
* cookie
)
162 MutexLocker
_(gTTYCookieLock
);
167 cookie
->thread_count
++;
172 inline void Unlock(tty_cookie
* cookie
)
174 MutexLocker
locker(gTTYCookieLock
);
176 sem_id semaphore
= -1;
177 if (--cookie
->thread_count
== 0 && cookie
->closed
)
178 semaphore
= cookie
->blocking_semaphore
;
182 if (semaphore
>= 0) {
183 TRACE(("TTYReference: cookie %p closed, last operation done, "
184 "releasing blocking sem %ld\n", cookie
, semaphore
));
186 release_sem(semaphore
);
191 typedef AutoLocker
<tty_cookie
, TTYReferenceLocking
> TTYReference
;
209 Request::Init(RequestOwner
* owner
, tty_cookie
* cookie
, size_t bytesNeeded
)
213 fBytesNeeded
= bytesNeeded
;
220 Request::Notify(size_t bytesAvailable
)
222 if (!fNotified
&& bytesAvailable
>= fBytesNeeded
&& fOwner
) {
223 fOwner
->Notify(this);
230 Request::NotifyError(status_t error
)
232 if (!fError
&& fOwner
) {
233 fOwner
->NotifyError(this, error
);
241 Request::Dump(const char* prefix
)
243 kprintf("%srequest: %p\n", prefix
, this);
244 kprintf("%s owner: %p\n", prefix
, fOwner
);
245 kprintf("%s cookie: %p\n", prefix
, fCookie
);
246 kprintf("%s bytes needed: %lu\n", prefix
, fBytesNeeded
);
247 kprintf("%s notified: %s\n", prefix
, fNotified
? "true" : "false");
248 kprintf("%s error: %s\n", prefix
, fError
? "true" : "false");
255 RequestQueue::RequestQueue()
263 RequestQueue::Add(Request
* request
)
266 RecursiveLocker
_(gTTYRequestLock
);
268 fRequests
.Add(request
, true);
274 RequestQueue::Remove(Request
* request
)
277 RecursiveLocker
_(gTTYRequestLock
);
279 fRequests
.Remove(request
);
285 RequestQueue::NotifyFirst(size_t bytesAvailable
)
287 RecursiveLocker
_(gTTYRequestLock
);
289 if (Request
* first
= First())
290 first
->Notify(bytesAvailable
);
295 RequestQueue::NotifyError(status_t error
)
297 RecursiveLocker
_(gTTYRequestLock
);
299 for (RequestList::Iterator it
= fRequests
.GetIterator(); it
.HasNext();) {
300 Request
* request
= it
.Next();
301 request
->NotifyError(error
);
307 RequestQueue::NotifyError(tty_cookie
* cookie
, status_t error
)
309 RecursiveLocker
_(gTTYRequestLock
);
311 for (RequestList::Iterator it
= fRequests
.GetIterator(); it
.HasNext();) {
312 Request
* request
= it
.Next();
313 if (request
->TTYCookie() == cookie
)
314 request
->NotifyError(error
);
320 RequestQueue::Dump(const char* prefix
)
322 RequestList::Iterator it
= fRequests
.GetIterator();
323 while (Request
* request
= it
.Next())
324 request
->Dump(prefix
);
331 RequestOwner::RequestOwner()
333 fConditionVariable(NULL
),
338 fRequestQueues
[0] = NULL
;
339 fRequestQueues
[1] = NULL
;
343 /*! The caller must already hold the request lock.
346 RequestOwner::Enqueue(tty_cookie
* cookie
, RequestQueue
* queue1
,
347 RequestQueue
* queue2
)
349 TRACE(("%p->RequestOwner::Enqueue(%p, %p, %p)\n", this, cookie
, queue1
,
354 fRequestQueues
[0] = queue1
;
355 fRequestQueues
[1] = queue2
;
357 fRequests
[0].Init(this, cookie
, fBytesNeeded
);
359 queue1
->Add(&fRequests
[0]);
361 fRequests
[0].Notify(fBytesNeeded
);
363 fRequests
[1].Init(this, cookie
, fBytesNeeded
);
365 queue2
->Add(&fRequests
[1]);
367 fRequests
[1].Notify(fBytesNeeded
);
371 /*! The caller must already hold the request lock.
374 RequestOwner::Dequeue()
376 TRACE(("%p->RequestOwner::Dequeue()\n", this));
378 if (fRequestQueues
[0])
379 fRequestQueues
[0]->Remove(&fRequests
[0]);
380 if (fRequestQueues
[1])
381 fRequestQueues
[1]->Remove(&fRequests
[1]);
383 fRequestQueues
[0] = NULL
;
384 fRequestQueues
[1] = NULL
;
389 RequestOwner::SetBytesNeeded(size_t bytesNeeded
)
391 if (fRequestQueues
[0])
392 fRequests
[0].Init(this, fCookie
, bytesNeeded
);
394 if (fRequestQueues
[1])
395 fRequests
[1].Init(this, fCookie
, bytesNeeded
);
399 /*! The request lock MUST NOT be held!
402 RequestOwner::Wait(bool interruptable
, bigtime_t timeout
)
404 TRACE(("%p->RequestOwner::Wait(%d)\n", this, interruptable
));
406 status_t error
= B_OK
;
408 RecursiveLocker
locker(gTTYRequestLock
);
410 // check, if already done
412 && (!fRequests
[0].WasNotified() || !fRequests
[1].WasNotified())) {
415 // publish the condition variable
416 ConditionVariable conditionVariable
;
417 conditionVariable
.Init(this, "tty request");
418 fConditionVariable
= &conditionVariable
;
420 // add an entry to wait on
421 ConditionVariableEntry entry
;
422 conditionVariable
.Add(&entry
);
427 TRACE(("%p->RequestOwner::Wait(): waiting for condition...\n", this));
430 (interruptable
? B_CAN_INTERRUPT
: 0) | B_RELATIVE_TIMEOUT
,
433 TRACE(("%p->RequestOwner::Wait(): condition occurred: %lx\n", this,
436 // remove the condition variable
438 fConditionVariable
= NULL
;
450 RequestOwner::IsFirstInQueues()
452 RecursiveLocker
locker(gTTYRequestLock
);
454 for (int i
= 0; i
< 2; i
++) {
455 if (fRequestQueues
[i
] && fRequestQueues
[i
]->First() != &fRequests
[i
])
464 RequestOwner::Notify(Request
* request
)
466 TRACE(("%p->RequestOwner::Notify(%p)\n", this, request
));
468 if (fError
== B_OK
&& !request
->WasNotified()) {
471 if (&fRequests
[0] == request
) {
472 notify
= fRequests
[1].WasNotified();
473 } else if (&fRequests
[1] == request
) {
474 notify
= fRequests
[0].WasNotified();
479 if (notify
&& fConditionVariable
)
480 fConditionVariable
->NotifyOne();
486 RequestOwner::NotifyError(Request
* request
, status_t error
)
488 TRACE(("%p->RequestOwner::NotifyError(%p, %lx)\n", this, request
, error
));
490 if (fError
== B_OK
) {
493 if (!fRequests
[0].WasNotified() || !fRequests
[1].WasNotified()) {
494 if (fConditionVariable
)
495 fConditionVariable
->NotifyOne();
504 WriterLocker::WriterLocker(tty_cookie
* sourceCookie
)
506 AbstractLocker(sourceCookie
),
507 fSource(fCookie
->tty
),
508 fTarget(fCookie
->other_tty
),
514 // Now that the tty pair is locked, we can check, whether the target is
516 if (fTarget
->open_count
> 0) {
517 // The target tty is open. As soon as we have appended a request to
518 // the writer queue of the target, it is guaranteed to remain valid
519 // until we have removed the request (and notified the
520 // tty_close_cookie() pseudo request).
523 fEcho
= (fSource
->is_master
524 && fSource
->settings
->termios
.c_lflag
& ECHO
) != 0;
526 // enqueue ourselves in the respective request queues
527 RecursiveLocker
locker(gTTYRequestLock
);
528 fRequestOwner
.Enqueue(fCookie
, &fTarget
->writer_queue
,
529 (fEcho
? &fSource
->writer_queue
: NULL
));
531 // target is not open: we set it to NULL; all further operations on
532 // this locker will fail
538 WriterLocker::~WriterLocker()
540 // dequeue from request queues
541 RecursiveLocker
locker(gTTYRequestLock
);
542 fRequestOwner
.Dequeue();
544 // check the tty queues and notify the next in line, and send out select
547 tty_notify_if_available(fTarget
, fSource
, true);
549 tty_notify_if_available(fSource
, fTarget
, true);
558 WriterLocker::_CheckAvailableBytes() const
560 size_t writable
= line_buffer_writable(fTarget
->input_buffer
);
562 // we can only write as much as is available on both ends
563 size_t locallyWritable
= line_buffer_writable(fSource
->input_buffer
);
564 if (locallyWritable
< writable
)
565 writable
= locallyWritable
;
572 WriterLocker::AcquireWriter(bool dontBlock
, size_t bytesNeeded
)
576 if (fEcho
&& fCookie
->closed
)
579 RecursiveLocker
requestLocker(gTTYRequestLock
);
581 // check, if we're first in queue, and if there is space to write
582 if (fRequestOwner
.IsFirstInQueues()) {
583 fBytes
= _CheckAvailableBytes();
584 if (fBytes
>= bytesNeeded
)
588 // We are not the first in queue or currently there's no space to write:
589 // bail out, if we shall not block.
591 return B_WOULD_BLOCK
;
593 // set the number of bytes we need and notify, just in case we're first in
594 // one of the queues (RequestOwner::SetBytesNeeded() resets the notification
596 fRequestOwner
.SetBytesNeeded(bytesNeeded
);
598 tty_notify_if_available(fTarget
, fSource
, false);
600 tty_notify_if_available(fSource
, fTarget
, false);
602 requestLocker
.Unlock();
604 // block until something happens
606 status_t status
= fRequestOwner
.Wait(true);
609 // RequestOwner::Wait() returns the error, but to avoid a race condition
610 // when closing a tty, we re-get the error with the tty lock being held.
611 if (status
== B_OK
) {
612 RecursiveLocker
_(gTTYRequestLock
);
613 status
= fRequestOwner
.Error();
617 status
= _CheckBackgroundWrite();
619 if (status
== B_OK
) {
620 if (fTarget
->open_count
> 0)
621 fBytes
= _CheckAvailableBytes();
623 status
= B_FILE_ERROR
;
631 WriterLocker::_CheckBackgroundWrite() const
633 // only relevant for the slave end and only when TOSTOP is set
634 if (fSource
->is_master
635 || (fSource
->settings
->termios
.c_lflag
& TOSTOP
) == 0) {
639 pid_t processGroup
= getpgid(0);
640 if (fSource
->settings
->pgrp_id
!= 0
641 && processGroup
!= fSource
->settings
->pgrp_id
) {
642 if (team_get_controlling_tty() == fSource
->index
)
643 send_signal(-processGroup
, SIGTTOU
);
654 ReaderLocker::ReaderLocker(tty_cookie
* cookie
)
656 AbstractLocker(cookie
),
662 // enqueue ourselves in the reader request queue
663 RecursiveLocker
locker(gTTYRequestLock
);
664 fRequestOwner
.Enqueue(fCookie
, &fTTY
->reader_queue
);
668 ReaderLocker::~ReaderLocker()
670 // dequeue from reader request queue
671 RecursiveLocker
locker(gTTYRequestLock
);
672 fRequestOwner
.Dequeue();
674 // check the tty queues and notify the next in line, and send out select
676 struct tty
* otherTTY
= fCookie
->other_tty
;
677 tty_notify_if_available(fTTY
, (otherTTY
->open_count
> 0 ? otherTTY
: NULL
),
687 ReaderLocker::AcquireReader(bigtime_t timeout
, size_t bytesNeeded
)
692 status_t status
= _CheckBackgroundRead();
696 // check, if we're first in queue, and if there is something to read
697 if (fRequestOwner
.IsFirstInQueues()) {
698 fBytes
= _CheckAvailableBytes();
699 if (fBytes
>= bytesNeeded
)
703 // We are not the first in queue or currently there's nothing to read:
704 // bail out, if we shall not block.
706 return B_WOULD_BLOCK
;
708 // reset the number of bytes we need
709 fRequestOwner
.SetBytesNeeded(bytesNeeded
);
711 // block until something happens
713 status
= fRequestOwner
.Wait(true, timeout
);
717 status
= _CheckBackgroundRead();
720 fBytes
= _CheckAvailableBytes();
727 ReaderLocker::_CheckAvailableBytes() const
729 // Reading from the slave with canonical input processing enabled means
730 // that we read at max until hitting a line end or EOF.
731 if (!fTTY
->is_master
&& (fTTY
->settings
->termios
.c_lflag
& ICANON
) != 0) {
732 return line_buffer_readable_line(fTTY
->input_buffer
,
733 fTTY
->settings
->termios
.c_cc
[VEOL
],
734 fTTY
->settings
->termios
.c_cc
[VEOF
]);
737 return line_buffer_readable(fTTY
->input_buffer
);
742 ReaderLocker::_CheckBackgroundRead() const
744 // only relevant for the slave end
748 pid_t processGroup
= getpgid(0);
749 if (fTTY
->settings
->pgrp_id
!= 0
750 && processGroup
!= fTTY
->settings
->pgrp_id
) {
751 if (team_get_controlling_tty() == fTTY
->index
)
752 send_signal(-processGroup
, SIGTTIN
);
764 get_tty_index(const char* name
)
766 // device names follow this form: "pt/%c%x"
767 int8 digit
= name
[4];
769 // hexadecimal digits
774 return (name
[3] - 'p') * 16 + digit
;
779 reset_termios(struct termios
& termios
)
781 memset(&termios
, 0, sizeof(struct termios
));
783 termios
.c_iflag
= ICRNL
;
784 termios
.c_oflag
= OPOST
| ONLCR
;
785 termios
.c_cflag
= B19200
| CS8
| CREAD
| HUPCL
;
786 // enable receiver, hang up on last close
787 termios
.c_lflag
= ECHO
| ISIG
| ICANON
;
788 termios
.c_ispeed
= B19200
;
789 termios
.c_ospeed
= B19200
;
791 // control characters
792 termios
.c_cc
[VINTR
] = CTRL('C');
793 termios
.c_cc
[VQUIT
] = CTRL('\\');
794 termios
.c_cc
[VERASE
] = 0x7f;
795 termios
.c_cc
[VKILL
] = CTRL('U');
796 termios
.c_cc
[VEOF
] = CTRL('D');
797 termios
.c_cc
[VEOL
] = '\0';
798 termios
.c_cc
[VEOL2
] = '\0';
799 termios
.c_cc
[VSTART
] = CTRL('S');
800 termios
.c_cc
[VSTOP
] = CTRL('Q');
801 termios
.c_cc
[VSUSP
] = CTRL('Z');
806 reset_tty_settings(tty_settings
* settings
, int32 index
)
808 reset_termios(settings
->termios
);
810 settings
->pgrp_id
= 0;
811 // this value prevents any signal of being sent
812 settings
->session_id
= -1;
814 // some initial window size - the TTY in question should set these values
815 settings
->window_size
.ws_col
= 80;
816 settings
->window_size
.ws_row
= 25;
817 settings
->window_size
.ws_xpixel
= settings
->window_size
.ws_col
* 8;
818 settings
->window_size
.ws_ypixel
= settings
->window_size
.ws_row
* 8;
823 reset_tty(struct tty
* tty
, int32 index
, mutex
* lock
, bool isMaster
)
829 tty
->settings
= &gTTYSettings
[index
];
830 tty
->select_pool
= NULL
;
831 tty
->is_master
= isMaster
;
832 tty
->pending_eof
= 0;
837 tty_output_getc(struct tty
* tty
, int* _c
)
843 /*! Processes the input character and puts it into the TTY's input buffer.
844 Depending on the termios flags set, signals may be sent, the input
845 character changed or removed, etc.
848 tty_input_putc_locked(struct tty
* tty
, int c
)
850 // process signals if needed
852 if ((tty
->settings
->termios
.c_lflag
& ISIG
) != 0) {
853 // enable signals, process INTR, QUIT, and SUSP
856 if (c
== tty
->settings
->termios
.c_cc
[VINTR
])
858 else if (c
== tty
->settings
->termios
.c_cc
[VQUIT
])
860 else if (c
== tty
->settings
->termios
.c_cc
[VSUSP
])
863 // do we need to deliver a signal?
865 // we may have to flush the input buffer
866 if ((tty
->settings
->termios
.c_lflag
& NOFLSH
) == 0)
867 clear_line_buffer(tty
->input_buffer
);
869 if (tty
->settings
->pgrp_id
!= 0)
870 send_signal(-tty
->settings
->pgrp_id
, signal
);
875 // process special canonical input characters
877 if ((tty
->settings
->termios
.c_lflag
& ICANON
) != 0) {
878 // canonical mode, process ERASE and KILL
879 cc_t
* controlChars
= tty
->settings
->termios
.c_cc
;
881 if (c
== controlChars
[VERASE
]) {
882 // erase one character
884 if (line_buffer_tail_getc(tty
->input_buffer
, &lastChar
)) {
885 if (lastChar
== controlChars
[VEOF
]
886 || lastChar
== controlChars
[VEOL
]
887 || lastChar
== '\n' || lastChar
== '\r') {
888 // EOF or end of line -- put it back
889 line_buffer_putc(tty
->input_buffer
, lastChar
);
893 } else if (c
== controlChars
[VKILL
]) {
896 while (line_buffer_tail_getc(tty
->input_buffer
, &lastChar
)) {
897 if (lastChar
== controlChars
[VEOF
]
898 || lastChar
== controlChars
[VEOL
]
899 || lastChar
== '\n' || lastChar
== '\r') {
900 // EOF or end of line -- put it back
901 line_buffer_putc(tty
->input_buffer
, lastChar
);
906 } else if (c
== controlChars
[VEOF
]) {
907 // we still write the EOF to the stream -- tty_input_read() needs
913 // Input character conversions have already been done. What reaches this
914 // point can directly be written to the line buffer.
916 line_buffer_putc(tty
->input_buffer
, c
);
922 tty_input_putc(struct tty
* tty
, int c
)
924 status_t status
= acquire_sem_etc(tty
->write_sem
, 1, B_CAN_INTERRUPT
, 0);
928 MutexLocker
locker(&tty
->lock
);
930 bool wasEmpty
= line_buffer_readable(tty
->input_buffer
) == 0;
932 tty_input_putc_locked(tty
, c
);
934 // If the buffer was empty before, we can now start other readers on it.
935 // We assume that most of the time more than one character will be written
936 // using this function, so we don't want to reschedule after every character
938 release_sem_etc(tty
->read_sem
, 1, B_DO_NOT_RESCHEDULE
);
940 // We only wrote one char - we give others the opportunity
941 // to write if there is still space left in the buffer
942 if (line_buffer_writable(tty
->input_buffer
))
943 release_sem_etc(tty
->write_sem
, 1, B_DO_NOT_RESCHEDULE
);
950 /*! The global lock must be held.
953 init_tty_cookie(tty_cookie
* cookie
, struct tty
* tty
, struct tty
* otherTTY
,
956 cookie
->blocking_semaphore
= create_sem(0, "wait for tty close");
957 if (cookie
->blocking_semaphore
< 0)
958 return cookie
->blocking_semaphore
;
961 cookie
->other_tty
= otherTTY
;
962 cookie
->open_mode
= openMode
;
963 cookie
->thread_count
= 0;
964 cookie
->closed
= false;
972 /*! The global lock must be held.
975 uninit_tty_cookie(tty_cookie
* cookie
)
977 cookie
->tty
->ref_count
--;
979 if (cookie
->blocking_semaphore
>= 0) {
980 delete_sem(cookie
->blocking_semaphore
);
981 cookie
->blocking_semaphore
= -1;
985 cookie
->thread_count
= 0;
986 cookie
->closed
= false;
990 /*! The global lock must be held.
993 add_tty_cookie(tty_cookie
* cookie
)
995 MutexLocker
locker(cookie
->tty
->lock
);
997 // add to the TTY's cookie list
998 cookie
->tty
->cookies
.Add(cookie
);
999 cookie
->tty
->open_count
++;
1003 /*! The global lock must be held.
1006 tty_close_cookie(struct tty_cookie
* cookie
)
1008 MutexLocker
locker(gTTYCookieLock
);
1010 // Already closed? This can happen for slaves that have been closed when
1011 // the master was closed.
1015 // set the cookie's `closed' flag
1016 cookie
->closed
= true;
1017 bool unblock
= (cookie
->thread_count
> 0);
1019 // unblock blocking threads
1021 cookie
->tty
->reader_queue
.NotifyError(cookie
, B_FILE_ERROR
);
1022 cookie
->tty
->writer_queue
.NotifyError(cookie
, B_FILE_ERROR
);
1024 if (cookie
->other_tty
->open_count
> 0) {
1025 cookie
->other_tty
->reader_queue
.NotifyError(cookie
, B_FILE_ERROR
);
1026 cookie
->other_tty
->writer_queue
.NotifyError(cookie
, B_FILE_ERROR
);
1032 // wait till all blocking (and now unblocked) threads have left the
1035 TRACE(("tty_close_cookie(): cookie %p, there're still pending "
1036 "operations, acquire blocking sem %ld\n", cookie
,
1037 cookie
->blocking_semaphore
));
1039 acquire_sem(cookie
->blocking_semaphore
);
1042 // For the removal of the cookie acquire the TTY's lock. This ensures, that
1043 // cookies will not be removed from a TTY (or added -- cf. add_tty_cookie())
1044 // as long as the TTY's lock is being held. This is required for the select
1045 // support, since we need to iterate through the cookies of a TTY without
1046 // having to acquire the global lock.
1047 MutexLocker
ttyLocker(cookie
->tty
->lock
);
1049 // remove the cookie from the TTY's cookie list
1050 cookie
->tty
->cookies
.Remove(cookie
);
1052 // close the tty, if no longer used
1053 if (--cookie
->tty
->open_count
== 0) {
1054 // The last cookie of this tty has been closed. We're going to close
1055 // the TTY and need to unblock all write requests before. There should
1056 // be no read requests, since only a cookie of this TTY issues those.
1057 // We do this by first notifying all queued requests of the error
1058 // condition. We then clear the line buffer for the TTY and queue
1061 // Notify the other TTY first; it doesn't accept any read/writes
1062 // while there is only one end.
1063 cookie
->other_tty
->reader_queue
.NotifyError(B_FILE_ERROR
);
1064 cookie
->other_tty
->writer_queue
.NotifyError(B_FILE_ERROR
);
1066 RecursiveLocker
requestLocker(gTTYRequestLock
);
1068 // we only need to do all this, if the writer queue is not empty
1069 if (!cookie
->tty
->writer_queue
.IsEmpty()) {
1070 // notify the blocking writers
1071 cookie
->tty
->writer_queue
.NotifyError(B_FILE_ERROR
);
1073 // enqueue our request
1074 RequestOwner requestOwner
;
1075 requestOwner
.Enqueue(cookie
, &cookie
->tty
->writer_queue
);
1077 requestLocker
.Unlock();
1079 // clear the line buffer
1080 clear_line_buffer(cookie
->tty
->input_buffer
);
1084 // wait for our turn
1085 requestOwner
.Wait(false);
1089 requestLocker
.Lock();
1091 // dequeue our request
1092 requestOwner
.Dequeue();
1095 requestLocker
.Unlock();
1097 // finally close the tty
1098 tty_close(cookie
->tty
);
1101 // notify pending select()s and cleanup the select sync pool
1103 // notify a select write event on the other tty, if we've closed this tty
1104 if (cookie
->tty
->open_count
== 0 && cookie
->other_tty
->open_count
> 0)
1105 tty_notify_select_event(cookie
->other_tty
, B_SELECT_WRITE
);
1110 tty_readable(struct tty
* tty
)
1112 if (!tty
->is_master
&& (tty
->settings
->termios
.c_lflag
& ICANON
) != 0) {
1113 return line_buffer_readable_line(tty
->input_buffer
,
1114 tty
->settings
->termios
.c_cc
[VEOL
],
1115 tty
->settings
->termios
.c_cc
[VEOF
]);
1118 return line_buffer_readable(tty
->input_buffer
);
1123 tty_notify_select_event(struct tty
* tty
, uint8 event
)
1125 TRACE(("tty_notify_select_event(%p, %u)\n", tty
, event
));
1127 if (tty
->select_pool
)
1128 notify_select_event_pool(tty
->select_pool
, event
);
1132 /*! \brief Checks whether bytes can be read from/written to the line buffer of
1133 the given TTY and notifies the respective queues.
1135 Also sends out \c B_SELECT_READ and \c B_SELECT_WRITE events as needed.
1137 The TTY and the request lock must be held.
1140 \param otherTTY The connected TTY.
1143 tty_notify_if_available(struct tty
* tty
, struct tty
* otherTTY
,
1149 // Check, if something is readable (depending on whether canonical input
1150 // processing is enabled).
1151 int32 readable
= tty_readable(tty
);
1153 // if nobody is waiting send select events, otherwise notify the waiter
1154 if (!tty
->reader_queue
.IsEmpty())
1155 tty
->reader_queue
.NotifyFirst(readable
);
1156 else if (notifySelect
)
1157 tty_notify_select_event(tty
, B_SELECT_READ
);
1160 int32 writable
= line_buffer_writable(tty
->input_buffer
);
1162 // if nobody is waiting send select events, otherwise notify the waiter
1163 if (!tty
->writer_queue
.IsEmpty()) {
1164 tty
->writer_queue
.NotifyFirst(writable
);
1165 } else if (notifySelect
) {
1166 if (otherTTY
&& otherTTY
->open_count
> 0)
1167 tty_notify_select_event(otherTTY
, B_SELECT_WRITE
);
1173 /*! \brief Performs input character conversion and writes the result to
1175 \param tty The master tty.
1176 \param c The input character.
1177 \param buffer The buffer to which to write the converted character.
1178 \param _bytesNeeded The number of bytes needed in the target tty's
1180 \return \c true, if the character shall be processed further, \c false, if
1181 it shall be skipped.
1184 process_input_char(struct tty
* tty
, char c
, char* buffer
,
1185 size_t* _bytesNeeded
)
1187 tcflag_t flags
= tty
->settings
->termios
.c_iflag
;
1190 if (tty
->settings
->termios
.c_lflag
& ISIG
) {
1191 if (c
== tty
->settings
->termios
.c_cc
[VINTR
]
1192 || c
== tty
->settings
->termios
.c_cc
[VQUIT
]
1193 || c
== tty
->settings
->termios
.c_cc
[VSUSP
]) {
1200 // canonical input characters
1201 if (tty
->settings
->termios
.c_lflag
& ICANON
) {
1202 if (c
== tty
->settings
->termios
.c_cc
[VERASE
]
1203 || c
== tty
->settings
->termios
.c_cc
[VKILL
]) {
1212 if (flags
& IGNCR
) // ignore CR
1214 if (flags
& ICRNL
) // CR -> NL
1216 } else if (c
== '\n') {
1217 if (flags
& INLCR
) // NL -> CR
1219 } else if ((flags
& ISTRIP
) != 0) // strip off eighth bit
1228 /*! \brief Performs output character conversion and writes the result to
1230 \param tty The master tty.
1231 \param c The output character.
1232 \param buffer The buffer to which to write the converted character(s).
1233 \param _bytesWritten The number of bytes written to the output buffer
1235 \param echoed \c true if the output char to be processed has been echoed
1239 process_output_char(struct tty
* tty
, char c
, char* buffer
,
1240 size_t* _bytesWritten
, bool echoed
)
1242 tcflag_t flags
= tty
->settings
->termios
.c_oflag
;
1244 if (flags
& OPOST
) {
1245 if (echoed
&& c
== tty
->settings
->termios
.c_cc
[VERASE
]) {
1246 if (tty
->settings
->termios
.c_lflag
& ECHOE
) {
1247 // ERASE -> BS SPACE BS
1248 buffer
[0] = CTRL('H');
1250 buffer
[2] = CTRL('H');;
1254 } else if (echoed
&& c
== tty
->settings
->termios
.c_cc
[VKILL
]) {
1255 if (!(tty
->settings
->termios
.c_lflag
& ECHOK
)) {
1260 } else if (echoed
&& c
== tty
->settings
->termios
.c_cc
[VEOF
]) {
1264 } else if (c
== '\n') {
1265 if (echoed
&& !(tty
->settings
->termios
.c_lflag
& ECHONL
)) {
1270 if (flags
& ONLCR
) { // NL -> CR-NL
1276 } else if (c
== '\r') {
1277 if (flags
& OCRNL
) { // CR -> NL
1279 } else if (flags
& ONLRET
) { // NL also does RET, ignore CR
1282 } else if (flags
& ONOCR
) { // don't output CR at column 0
1283 // TODO: We can't decide that here.
1286 if (flags
& OLCUC
) // lower case -> upper case
1297 tty_write_to_tty_master_unsafe(tty_cookie
* sourceCookie
, const char* data
,
1300 struct tty
* source
= sourceCookie
->tty
;
1301 struct tty
* target
= sourceCookie
->other_tty
;
1302 size_t length
= *_length
;
1303 size_t bytesWritten
= 0;
1304 uint32 mode
= sourceCookie
->open_mode
;
1305 bool dontBlock
= (mode
& O_NONBLOCK
) != 0;
1307 // bail out, if source is already closed
1308 TTYReference
sourceTTYReference(sourceCookie
);
1309 if (!sourceTTYReference
.IsLocked())
1310 return B_FILE_ERROR
;
1315 WriterLocker
locker(sourceCookie
);
1317 // if the target is not open, fail now
1318 if (target
->open_count
<= 0)
1319 return B_FILE_ERROR
;
1321 bool echo
= (source
->settings
->termios
.c_lflag
& ECHO
) != 0;
1323 TRACE(("tty_write_to_tty_master(source = %p, target = %p, "
1324 "length = %lu%s)\n", source
, target
, length
,
1325 (echo
? ", echo mode" : "")));
1327 // Make sure we are first in the writer queue(s) and AvailableBytes() is
1329 status_t status
= locker
.AcquireWriter(dontBlock
, 0);
1330 if (status
!= B_OK
) {
1334 size_t writable
= locker
.AvailableBytes();
1335 size_t writtenSinceLastNotify
= 0;
1337 while (bytesWritten
< length
) {
1338 // fetch next char and do input processing
1341 if (!process_input_char(source
, *data
, &c
, &bytesNeeded
)) {
1342 // input char shall be skipped
1348 // If in echo mode, we do the output conversion and need to update
1349 // the needed bytes count.
1351 size_t echoBytes
= 0;
1353 process_output_char(source
, c
, echoBuffer
, &echoBytes
, true);
1354 if (echoBytes
> bytesNeeded
)
1355 bytesNeeded
= echoBytes
;
1358 // If there's not enough space to write what we have, we need to wait
1359 // until it is available.
1360 if (writable
< bytesNeeded
) {
1361 if (writtenSinceLastNotify
> 0) {
1362 tty_notify_if_available(target
, source
, true);
1364 tty_notify_if_available(source
, target
, true);
1365 writtenSinceLastNotify
= 0;
1368 status
= locker
.AcquireWriter(dontBlock
, bytesNeeded
);
1369 if (status
!= B_OK
) {
1370 *_length
= bytesWritten
;
1374 writable
= locker
.AvailableBytes();
1376 // XXX: do we need to support VMIN & VTIME for write() ?
1378 // We need to restart the loop, since the termios flags might have
1379 // changed in the meantime (while we've unlocked the tty). Note,
1380 // that we don't re-get "echo" -- maybe we should.
1385 tty_input_putc_locked(target
, c
);
1388 for (size_t i
= 0; i
< echoBytes
; i
++)
1389 line_buffer_putc(source
->input_buffer
, echoBuffer
[i
]);
1392 writable
-= bytesNeeded
;
1395 writtenSinceLastNotify
++;
1403 tty_write_to_tty_slave_unsafe(tty_cookie
* sourceCookie
, const char* data
,
1406 struct tty
* target
= sourceCookie
->other_tty
;
1407 size_t length
= *_length
;
1408 size_t bytesWritten
= 0;
1409 uint32 mode
= sourceCookie
->open_mode
;
1410 bool dontBlock
= (mode
& O_NONBLOCK
) != 0;
1412 // bail out, if source is already closed
1413 TTYReference
sourceTTYReference(sourceCookie
);
1414 if (!sourceTTYReference
.IsLocked())
1415 return B_FILE_ERROR
;
1420 WriterLocker
locker(sourceCookie
);
1422 // if the target is not open, fail now
1423 if (target
->open_count
<= 0)
1424 return B_FILE_ERROR
;
1426 TRACE(("tty_write_to_tty_slave(source = %p, target = %p, length = %lu)\n",
1427 sourceCookie
->tty
, target
, length
));
1429 // Make sure we are first in the writer queue(s) and AvailableBytes() is
1431 status_t status
= locker
.AcquireWriter(dontBlock
, 0);
1432 if (status
!= B_OK
) {
1436 size_t writable
= locker
.AvailableBytes();
1437 size_t writtenSinceLastNotify
= 0;
1439 while (bytesWritten
< length
) {
1440 // fetch next char and do output processing
1443 process_output_char(target
, *data
, buffer
, &bytesNeeded
, false);
1445 // If there's not enough space to write what we have, we need to wait
1446 // until it is available.
1447 if (writable
< bytesNeeded
) {
1448 if (writtenSinceLastNotify
> 0) {
1449 tty_notify_if_available(target
, sourceCookie
->tty
, true);
1450 writtenSinceLastNotify
= 0;
1453 status
= locker
.AcquireWriter(dontBlock
, bytesNeeded
);
1454 if (status
!= B_OK
) {
1455 *_length
= bytesWritten
;
1459 writable
= locker
.AvailableBytes();
1461 // We need to restart the loop, since the termios flags might have
1462 // changed in the meantime (while we've unlocked the tty).
1467 for (size_t i
= 0; i
< bytesNeeded
; i
++)
1468 line_buffer_putc(target
->input_buffer
, buffer
[i
]);
1470 writable
-= bytesNeeded
;
1473 writtenSinceLastNotify
++;
1481 dump_tty_settings(struct tty_settings
& settings
)
1483 kprintf(" pgrp_id: %" B_PRId32
"\n", settings
.pgrp_id
);
1484 kprintf(" session_id: %" B_PRId32
"\n", settings
.session_id
);
1486 kprintf(" termios:\n");
1487 kprintf(" c_iflag: 0x%08" B_PRIx32
"\n", settings
.termios
.c_iflag
);
1488 kprintf(" c_oflag: 0x%08" B_PRIx32
"\n", settings
.termios
.c_oflag
);
1489 kprintf(" c_cflag: 0x%08" B_PRIx32
"\n", settings
.termios
.c_cflag
);
1490 kprintf(" c_lflag: 0x%08" B_PRIx32
"\n", settings
.termios
.c_lflag
);
1491 kprintf(" c_line: %d\n", settings
.termios
.c_line
);
1492 kprintf(" c_ispeed: %u\n", settings
.termios
.c_ispeed
);
1493 kprintf(" c_ospeed: %u\n", settings
.termios
.c_ospeed
);
1494 for (int i
= 0; i
< NCCS
; i
++)
1495 kprintf(" c_cc[%02d]: %d\n", i
, settings
.termios
.c_cc
[i
]);
1497 kprintf(" wsize: %u x %u c, %u x %u pxl\n",
1498 settings
.window_size
.ws_row
, settings
.window_size
.ws_col
,
1499 settings
.window_size
.ws_xpixel
, settings
.window_size
.ws_ypixel
);
1504 dump_tty_struct(struct tty
& tty
)
1506 kprintf(" tty @: %p\n", &tty
);
1507 kprintf(" index: %" B_PRId32
"\n", tty
.index
);
1508 kprintf(" is_master: %s\n", tty
.is_master
? "true" : "false");
1509 kprintf(" open_count: %" B_PRId32
"\n", tty
.open_count
);
1510 kprintf(" select_pool: %p\n", tty
.select_pool
);
1511 kprintf(" pending_eof: %" B_PRIu32
"\n", tty
.pending_eof
);
1512 kprintf(" lock: %p\n", tty
.lock
);
1514 kprintf(" input_buffer:\n");
1515 kprintf(" first: %" B_PRId32
"\n", tty
.input_buffer
.first
);
1516 kprintf(" in: %lu\n", tty
.input_buffer
.in
);
1517 kprintf(" size: %lu\n", tty
.input_buffer
.size
);
1518 kprintf(" buffer: %p\n", tty
.input_buffer
.buffer
);
1520 kprintf(" reader queue:\n");
1521 tty
.reader_queue
.Dump(" ");
1522 kprintf(" writer queue:\n");
1523 tty
.writer_queue
.Dump(" ");
1525 kprintf(" cookies: ");
1526 TTYCookieList::Iterator it
= tty
.cookies
.GetIterator();
1527 while (tty_cookie
* cookie
= it
.Next())
1528 kprintf(" %p", cookie
);
1534 dump_tty(int argc
, char** argv
)
1537 kprintf("Usage: %s <tty index>\n", argv
[0]);
1541 int32 index
= atol(argv
[1]);
1542 if (index
< 0 || index
>= (int32
)kNumTTYs
) {
1543 kprintf("Invalid tty index.\n");
1547 kprintf("master:\n");
1548 dump_tty_struct(gMasterTTYs
[index
]);
1549 kprintf("slave:\n");
1550 dump_tty_struct(gSlaveTTYs
[index
]);
1551 kprintf("settings:\n");
1552 dump_tty_settings(gTTYSettings
[index
]);
1558 // #pragma mark - device functions
1562 tty_close(struct tty
* tty
)
1564 // destroy the queues
1565 tty
->reader_queue
.~RequestQueue();
1566 tty
->writer_queue
.~RequestQueue();
1567 tty
->cookies
.~TTYCookieList();
1569 uninit_line_buffer(tty
->input_buffer
);
1576 tty_open(struct tty
* tty
, tty_service_func func
)
1578 if (init_line_buffer(tty
->input_buffer
, TTY_BUFFER_SIZE
) < B_OK
)
1581 tty
->service_func
= func
;
1583 // construct the queues
1584 new(&tty
->reader_queue
) RequestQueue
;
1585 new(&tty
->writer_queue
) RequestQueue
;
1586 new(&tty
->cookies
) TTYCookieList
;
1593 tty_ioctl(tty_cookie
* cookie
, uint32 op
, void* buffer
, size_t length
)
1595 struct tty
* tty
= cookie
->tty
;
1597 // bail out, if already closed
1598 TTYReference
ttyReference(cookie
);
1599 if (!ttyReference
.IsLocked())
1600 return B_FILE_ERROR
;
1602 TRACE(("tty_ioctl: tty %p, op %lu, buffer %p, length %lu\n", tty
, op
, buffer
, length
));
1603 MutexLocker
locker(tty
->lock
);
1605 // values marked BeOS are non-standard codes we support for legacy apps
1607 // blocking/non-blocking mode
1609 case B_SET_BLOCKING_IO
:
1610 cookie
->open_mode
&= ~O_NONBLOCK
;
1612 case B_SET_NONBLOCKING_IO
:
1613 cookie
->open_mode
|= O_NONBLOCK
;
1616 // get and set TTY attributes
1619 TRACE(("tty: get attributes\n"));
1620 return user_memcpy(buffer
, &tty
->settings
->termios
,
1621 sizeof(struct termios
));
1626 TRACE(("tty: set attributes (iflag = %lx, oflag = %lx, "
1627 "cflag = %lx, lflag = %lx)\n", tty
->settings
->termios
.c_iflag
,
1628 tty
->settings
->termios
.c_oflag
, tty
->settings
->termios
.c_cflag
,
1629 tty
->settings
->termios
.c_lflag
));
1631 return user_memcpy(&tty
->settings
->termios
, buffer
,
1632 sizeof(struct termios
));
1634 // get and set process group ID
1637 TRACE(("tty: get pgrp_id\n"));
1638 return user_memcpy(buffer
, &tty
->settings
->pgrp_id
, sizeof(pid_t
));
1641 case 'pgid': // BeOS
1643 TRACE(("tty: set pgrp_id\n"));
1646 if (user_memcpy(&groupID
, buffer
, sizeof(pid_t
)) != B_OK
)
1647 return B_BAD_ADDRESS
;
1649 status_t error
= team_set_foreground_process_group(tty
->index
,
1652 tty
->settings
->pgrp_id
= groupID
;
1656 // become controlling TTY
1659 TRACE(("tty: become controlling tty\n"));
1660 pid_t processID
= getpid();
1661 pid_t sessionID
= getsid(processID
);
1662 // Only session leaders can become controlling tty
1663 if (processID
!= sessionID
)
1664 return B_NOT_ALLOWED
;
1665 // Check if already controlling tty
1666 if (team_get_controlling_tty() == tty
->index
)
1668 tty
->settings
->session_id
= sessionID
;
1669 tty
->settings
->pgrp_id
= sessionID
;
1670 team_set_controlling_tty(tty
->index
);
1674 // get and set window size
1677 TRACE(("tty: get window size\n"));
1678 return user_memcpy(buffer
, &tty
->settings
->window_size
,
1679 sizeof(struct winsize
));
1682 case 'wsiz': // BeOS
1684 uint16 oldColumns
= tty
->settings
->window_size
.ws_col
;
1685 uint16 oldRows
= tty
->settings
->window_size
.ws_row
;
1687 TRACE(("tty: set window size\n"));
1688 if (user_memcpy(&tty
->settings
->window_size
, buffer
,
1689 sizeof(struct winsize
)) < B_OK
) {
1690 return B_BAD_ADDRESS
;
1693 // send a signal only if the window size has changed
1694 if ((oldColumns
!= tty
->settings
->window_size
.ws_col
1695 || oldRows
!= tty
->settings
->window_size
.ws_row
)
1696 && tty
->settings
->pgrp_id
!= 0) {
1697 send_signal(-tty
->settings
->pgrp_id
, SIGWINCH
);
1703 case B_IOCTL_GET_TTY_INDEX
:
1704 if (user_memcpy(buffer
, &tty
->index
, sizeof(int32
)) < B_OK
)
1705 return B_BAD_ADDRESS
;
1709 case B_IOCTL_GRANT_TTY
:
1711 if (!tty
->is_master
)
1716 snprintf(path
, sizeof(path
), "/dev/%s",
1717 gDeviceNames
[kNumTTYs
+ tty
->index
]);
1719 // set owner and permissions respectively
1720 if (chown(path
, getuid(), getgid()) != 0
1721 || chmod(path
, S_IRUSR
| S_IWUSR
| S_IWGRP
) != 0) {
1728 case 'ichr': // BeOS (int*) (pre- select() support)
1733 // help identify apps using it
1734 //dprintf("tty: warning: legacy BeOS opcode 'ichr'\n");
1736 if (user_memcpy(&wanted
, buffer
, sizeof(int)) != B_OK
)
1737 return B_BAD_ADDRESS
;
1739 // release the mutex and grab a read lock
1741 ReaderLocker
readLocker(cookie
);
1743 bigtime_t timeout
= wanted
== 0 ? 0 : B_INFINITE_TIMEOUT
;
1745 // TODO: If wanted is > the TTY buffer size, this loop cannot work
1746 // correctly. Refactor the read code!
1748 status_t status
= readLocker
.AcquireReader(timeout
, wanted
);
1752 toRead
= readLocker
.AvailableBytes();
1753 } while (toRead
< wanted
);
1755 if (user_memcpy(buffer
, &toRead
, sizeof(int)) != B_OK
)
1756 return B_BAD_ADDRESS
;
1765 // release the mutex and grab a read lock
1767 ReaderLocker
readLocker(cookie
);
1769 status_t status
= readLocker
.AcquireReader(0, 1);
1771 toRead
= readLocker
.AvailableBytes();
1772 else if (status
!= B_WOULD_BLOCK
)
1775 if (user_memcpy(buffer
, &toRead
, sizeof(int)) != B_OK
)
1776 return B_BAD_ADDRESS
;
1781 case TCWAITEVENT
: // BeOS (uint*)
1782 // wait for event (timeout if !NULL)
1783 case TCVTIME
: // BeOS (bigtime_t*) set highrez VTIME
1784 case 'ochr': // BeOS (int*) same as ichr for write
1785 dprintf("tty: unsupported legacy opcode %" B_PRIu32
"\n", op
);
1789 case TCXONC
: // Unix, but even Linux doesn't handle it
1790 //dprintf("tty: unsupported TCXONC\n");
1793 case TCQUERYCONNECTED
: // BeOS
1794 dprintf("tty: unsupported legacy opcode %" B_PRIu32
"\n", op
);
1795 // BeOS didn't implement them anyway
1801 // TODO: should call the driver service func here,
1802 // for non-virtual tty.
1803 // return tty->driver->service();
1807 TRACE(("tty: unsupported opcode %lu\n", op
));
1813 tty_input_read(tty_cookie
* cookie
, void* _buffer
, size_t* _length
)
1815 char* buffer
= (char*)_buffer
;
1816 struct tty
* tty
= cookie
->tty
;
1817 uint32 mode
= cookie
->open_mode
;
1818 bool dontBlock
= (mode
& O_NONBLOCK
) != 0;
1819 size_t length
= *_length
;
1821 bigtime_t timeout
= dontBlock
? 0 : B_INFINITE_TIMEOUT
;
1822 bigtime_t interCharTimeout
= 0;
1823 size_t bytesNeeded
= 1;
1825 TRACE(("tty_input_read(tty = %p, length = %lu, mode = %lu)\n", tty
, length
, mode
));
1830 // bail out, if the TTY is already closed
1831 TTYReference
ttyReference(cookie
);
1832 if (!ttyReference
.IsLocked())
1833 return B_FILE_ERROR
;
1835 ReaderLocker
locker(cookie
);
1838 if ((!tty
->is_master
) && ((tty
->settings
->termios
.c_lflag
& ICANON
) == 0)) {
1841 // Non-blocking mode. Handle VMIN and VTIME.
1842 bytesNeeded
= tty
->settings
->termios
.c_cc
[VMIN
];
1843 bigtime_t vtime
= tty
->settings
->termios
.c_cc
[VTIME
] * 100000;
1844 TRACE(("tty_input_read: icanon vmin %lu, vtime %Ldus\n", bytesNeeded
,
1847 if (bytesNeeded
== 0) {
1848 // In this case VTIME specifies a relative total timeout. We
1849 // have no inter-char timeout, though.
1852 // VTIME specifies the inter-char timeout. 0 is indefinitely.
1854 interCharTimeout
= B_INFINITE_TIMEOUT
;
1856 interCharTimeout
= vtime
;
1858 if (bytesNeeded
> length
)
1859 bytesNeeded
= length
;
1868 TRACE(("tty_input_read: AcquireReader(%Ldus, %ld)\n", timeout
,
1870 status
= locker
.AcquireReader(timeout
, bytesNeeded
);
1874 size_t toRead
= locker
.AvailableBytes();
1875 if (toRead
> length
)
1878 bool _hitEOF
= false;
1879 bool* hitEOF
= canon
&& tty
->pending_eof
> 0 ? &_hitEOF
: NULL
;
1881 ssize_t bytesRead
= line_buffer_user_read(tty
->input_buffer
, buffer
,
1882 toRead
, tty
->settings
->termios
.c_cc
[VEOF
], hitEOF
);
1883 if (bytesRead
< 0) {
1888 buffer
+= bytesRead
;
1889 length
-= bytesRead
;
1890 *_length
+= bytesRead
;
1891 bytesNeeded
= (size_t)bytesRead
> bytesNeeded
1892 ? 0 : bytesNeeded
- bytesRead
;
1894 // we hit an EOF char -- bail out, whatever amount of data we have
1895 if (hitEOF
&& *hitEOF
) {
1900 // Once we have read something reset the timeout to the inter-char
1901 // timeout, if applicable.
1902 if (!dontBlock
&& !canon
&& *_length
> 0)
1903 timeout
= interCharTimeout
;
1904 } while (bytesNeeded
> 0);
1906 if (status
== B_WOULD_BLOCK
|| status
== B_TIMED_OUT
) {
1907 // In non-blocking non-canonical-input-processing mode never return
1908 // timeout errors. Just return 0, if nothing has been read.
1909 if (!dontBlock
&& !canon
)
1913 return *_length
== 0 ? status
: B_OK
;
1918 tty_write_to_tty_master(tty_cookie
* sourceCookie
, const void* _buffer
,
1921 const char* buffer
= (const char*)_buffer
;
1922 size_t bytesRemaining
= *_length
;
1925 while (bytesRemaining
> 0) {
1926 // copy data to stack
1927 char safeBuffer
[256];
1928 size_t toWrite
= min_c(sizeof(safeBuffer
), bytesRemaining
);
1929 status_t error
= user_memcpy(safeBuffer
, buffer
, toWrite
);
1934 size_t written
= toWrite
;
1935 error
= tty_write_to_tty_master_unsafe(sourceCookie
, safeBuffer
,
1941 bytesRemaining
-= written
;
1942 *_length
+= written
;
1944 if (written
< toWrite
)
1953 tty_write_to_tty_slave(tty_cookie
* sourceCookie
, const void* _buffer
,
1956 const char* buffer
= (const char*)_buffer
;
1957 size_t bytesRemaining
= *_length
;
1960 while (bytesRemaining
> 0) {
1961 // copy data to stack
1962 char safeBuffer
[256];
1963 size_t toWrite
= min_c(sizeof(safeBuffer
), bytesRemaining
);
1964 status_t error
= user_memcpy(safeBuffer
, buffer
, toWrite
);
1969 size_t written
= toWrite
;
1970 error
= tty_write_to_tty_slave_unsafe(sourceCookie
, safeBuffer
,
1976 bytesRemaining
-= written
;
1977 *_length
+= written
;
1979 if (written
< toWrite
)
1988 tty_select(tty_cookie
* cookie
, uint8 event
, uint32 ref
, selectsync
* sync
)
1990 struct tty
* tty
= cookie
->tty
;
1992 TRACE(("tty_select(cookie = %p, event = %u, ref = %lu, sync = %p)\n",
1993 cookie
, event
, ref
, sync
));
1995 // we don't support all kinds of events
1996 if (event
< B_SELECT_READ
|| event
> B_SELECT_ERROR
)
1999 // if the TTY is already closed, we notify immediately
2000 TTYReference
ttyReference(cookie
);
2001 if (!ttyReference
.IsLocked()) {
2002 TRACE(("tty_select() done: cookie %p already closed\n", cookie
));
2004 notify_select_event(sync
, event
);
2008 // lock the TTY (allows us to freely access the cookie lists of this and
2010 MutexLocker
ttyLocker(tty
->lock
);
2012 // get the other TTY -- needed for `write' events
2013 struct tty
* otherTTY
= cookie
->other_tty
;
2014 if (otherTTY
->open_count
<= 0)
2017 // add the event to the TTY's pool
2018 status_t error
= add_select_sync_pool_entry(&tty
->select_pool
, sync
, event
);
2019 if (error
!= B_OK
) {
2020 TRACE(("tty_select() done: add_select_sync_pool_entry() failed: %lx\n",
2026 // finally also acquire the request mutex, for access to the reader/writer
2028 RecursiveLocker
requestLocker(gTTYRequestLock
);
2030 // check, if the event is already present
2033 if (tty
->reader_queue
.IsEmpty() && tty_readable(tty
) > 0)
2034 notify_select_event(sync
, event
);
2037 case B_SELECT_WRITE
:
2039 // writes go to the other TTY
2041 notify_select_event(sync
, event
);
2045 // In case input is echoed, we have to check, whether we can
2046 // currently can write to our TTY as well.
2047 bool echo
= (tty
->is_master
2048 && tty
->settings
->termios
.c_lflag
& ECHO
);
2050 if (otherTTY
->writer_queue
.IsEmpty()
2051 && line_buffer_writable(otherTTY
->input_buffer
) > 0) {
2053 || (tty
->writer_queue
.IsEmpty()
2054 && line_buffer_writable(tty
->input_buffer
) > 0)) {
2055 notify_select_event(sync
, event
);
2061 case B_SELECT_ERROR
:
2071 tty_deselect(tty_cookie
* cookie
, uint8 event
, selectsync
* sync
)
2073 struct tty
* tty
= cookie
->tty
;
2075 TRACE(("tty_deselect(cookie = %p, event = %u, sync = %p)\n", cookie
, event
,
2078 // we don't support all kinds of events
2079 if (event
< B_SELECT_READ
|| event
> B_SELECT_ERROR
)
2082 // lock the TTY (guards the select sync pool, among other things)
2083 MutexLocker
ttyLocker(tty
->lock
);
2085 return remove_select_sync_pool_entry(&tty
->select_pool
, sync
, event
);
2090 tty_add_debugger_commands()
2092 add_debugger_command("tty", &dump_tty
, "Dump info on a tty");
2097 tty_remove_debugger_commands()
2099 remove_debugger_command("tty", &dump_tty
);