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