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