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