diff --git a/src/add-ons/kernel/drivers/tty/driver.cpp b/src/add-ons/kernel/drivers/tty/driver.cpp index b18a329f1e..b6be4e049b 100644 --- a/src/add-ons/kernel/drivers/tty/driver.cpp +++ b/src/add-ons/kernel/drivers/tty/driver.cpp @@ -11,6 +11,7 @@ #include +#include "tty_driver.h" #include "tty_private.h" @@ -32,12 +33,105 @@ char *gDeviceNames[kNumTTYs * 2 + 3]; // terminating NULL static mutex sTTYLocks[kNumTTYs]; +static tty_settings sTTYSettings[kNumTTYs]; struct mutex gGlobalTTYLock; struct mutex gTTYCookieLock; struct recursive_lock gTTYRequestLock; +static void +dump_tty_settings(struct tty_settings& settings) +{ + kprintf(" pgrp_id: %" B_PRId32 "\n", settings.pgrp_id); + kprintf(" session_id: %" B_PRId32 "\n", settings.session_id); + + kprintf(" termios:\n"); + kprintf(" c_iflag: 0x%08" B_PRIx32 "\n", settings.termios.c_iflag); + kprintf(" c_oflag: 0x%08" B_PRIx32 "\n", settings.termios.c_oflag); + kprintf(" c_cflag: 0x%08" B_PRIx32 "\n", settings.termios.c_cflag); + kprintf(" c_lflag: 0x%08" B_PRIx32 "\n", settings.termios.c_lflag); + kprintf(" c_line: %d\n", settings.termios.c_line); + kprintf(" c_ispeed: %u\n", settings.termios.c_ispeed); + kprintf(" c_ospeed: %u\n", settings.termios.c_ospeed); + for (int i = 0; i < NCCS; i++) + kprintf(" c_cc[%02d]: %d\n", i, settings.termios.c_cc[i]); + + kprintf(" wsize: %u x %u c, %u x %u pxl\n", + settings.window_size.ws_row, settings.window_size.ws_col, + settings.window_size.ws_xpixel, settings.window_size.ws_ypixel); +} + + +static void +dump_tty_struct(struct tty& tty) +{ + kprintf(" tty @: %p\n", &tty); + kprintf(" index: %" B_PRId32 "\n", tty.index); + kprintf(" is_master: %s\n", tty.is_master ? "true" : "false"); + kprintf(" open_count: %" B_PRId32 "\n", tty.open_count); + kprintf(" select_pool: %p\n", tty.select_pool); + kprintf(" pending_eof: %" B_PRIu32 "\n", tty.pending_eof); + kprintf(" lock: %p\n", tty.lock); + + kprintf(" input_buffer:\n"); + kprintf(" first: %" B_PRId32 "\n", tty.input_buffer.first); + kprintf(" in: %lu\n", tty.input_buffer.in); + kprintf(" size: %lu\n", tty.input_buffer.size); + kprintf(" buffer: %p\n", tty.input_buffer.buffer); + + kprintf(" reader queue:\n"); + tty.reader_queue.Dump(" "); + kprintf(" writer queue:\n"); + tty.writer_queue.Dump(" "); + + kprintf(" cookies: "); + TTYCookieList::Iterator it = tty.cookies.GetIterator(); + while (tty_cookie* cookie = it.Next()) + kprintf(" %p", cookie); + kprintf("\n"); +} + + +static int +dump_tty(int argc, char** argv) +{ + if (argc < 2) { + kprintf("Usage: %s \n", argv[0]); + return 0; + } + + int32 index = atol(argv[1]); + if (index < 0 || index >= (int32)kNumTTYs) { + kprintf("Invalid tty index.\n"); + return 0; + } + + kprintf("master:\n"); + dump_tty_struct(gMasterTTYs[index]); + kprintf("slave:\n"); + dump_tty_struct(gSlaveTTYs[index]); + kprintf("settings:\n"); + dump_tty_settings(sTTYSettings[index]); + + return 0; +} + + +void +tty_add_debugger_commands() +{ + add_debugger_command("tty", &dump_tty, "Dump info on a tty"); +} + + +void +tty_remove_debugger_commands() +{ + remove_debugger_command("tty", &dump_tty); +} + + status_t init_hardware(void) { @@ -89,7 +183,7 @@ init_driver(void) mutex_init(&sTTYLocks[i], "tty lock"); reset_tty(&gMasterTTYs[i], i, &sTTYLocks[i], true); reset_tty(&gSlaveTTYs[i], i, &sTTYLocks[i], false); - reset_tty_settings(&gTTYSettings[i], i); + reset_tty_settings(&sTTYSettings[i]); if (!gDeviceNames[i] || !gDeviceNames[i + kNumTTYs]) { uninit_driver(); @@ -147,3 +241,32 @@ find_device(const char *name) return NULL; } + +int32 +get_tty_index(const char* name) +{ + // device names follow this form: "pt/%c%x" + int8 digit = name[4]; + if (digit >= 'a') { + // hexadecimal digits + digit -= 'a' - 10; + } else + digit -= '0'; + + return (name[3] - 'p') * 16 + digit; +} + + +void +reset_tty(struct tty* tty, int32 index, mutex* lock, bool isMaster) +{ + tty->ref_count = 0; + tty->open_count = 0; + tty->opened_count = 0; + tty->index = index; + tty->lock = lock; + tty->settings = &sTTYSettings[index]; + tty->select_pool = NULL; + tty->is_master = isMaster; + tty->pending_eof = 0; +} diff --git a/src/add-ons/kernel/drivers/tty/master.cpp b/src/add-ons/kernel/drivers/tty/master.cpp index dbd94b0ae6..e4c11b1d35 100644 --- a/src/add-ons/kernel/drivers/tty/master.cpp +++ b/src/add-ons/kernel/drivers/tty/master.cpp @@ -4,6 +4,7 @@ */ +#include "tty_driver.h" #include "tty_private.h" #include @@ -100,8 +101,6 @@ master_open(const char *name, uint32 flags, void **_cookie) return status; } - add_tty_cookie(cookie); - *_cookie = cookie; return B_OK; diff --git a/src/add-ons/kernel/drivers/tty/slave.cpp b/src/add-ons/kernel/drivers/tty/slave.cpp index 506cc211bd..52684df5ba 100644 --- a/src/add-ons/kernel/drivers/tty/slave.cpp +++ b/src/add-ons/kernel/drivers/tty/slave.cpp @@ -11,6 +11,7 @@ #include +#include "tty_driver.h" #include "tty_private.h" @@ -115,8 +116,6 @@ slave_open(const char *name, uint32 flags, void **_cookie) team_set_controlling_tty(gSlaveTTYs[index].index); } - add_tty_cookie(cookie); - *_cookie = cookie; return B_OK; diff --git a/src/add-ons/kernel/drivers/tty/tty.cpp b/src/add-ons/kernel/drivers/tty/tty.cpp index 0117696b44..90be39629f 100644 --- a/src/add-ons/kernel/drivers/tty/tty.cpp +++ b/src/add-ons/kernel/drivers/tty/tty.cpp @@ -5,11 +5,6 @@ */ -// This file could be moved into a generic tty module. -// The whole hardware signaling stuff is missing, though - it's currently -// tailored for pseudo-TTYs. Have a look at Be's TTY includes (drivers/tty/*) - - #include "tty_private.h" #include @@ -88,9 +83,6 @@ */ -tty_settings gTTYSettings[kNumTTYs]; - - static void tty_notify_select_event(struct tty* tty, uint8 event); static void tty_notify_if_available(struct tty* tty, struct tty* otherTTY, bool notifySelect); @@ -764,21 +756,6 @@ ReaderLocker::_CheckBackgroundRead() const // #pragma mark - -int32 -get_tty_index(const char* name) -{ - // device names follow this form: "pt/%c%x" - int8 digit = name[4]; - if (digit >= 'a') { - // hexadecimal digits - digit -= 'a' - 10; - } else - digit -= '0'; - - return (name[3] - 'p') * 16 + digit; -} - - static void reset_termios(struct termios& termios) { @@ -807,7 +784,7 @@ reset_termios(struct termios& termios) void -reset_tty_settings(tty_settings* settings, int32 index) +reset_tty_settings(tty_settings* settings) { reset_termios(settings->termios); @@ -823,28 +800,6 @@ reset_tty_settings(tty_settings* settings, int32 index) } -void -reset_tty(struct tty* tty, int32 index, mutex* lock, bool isMaster) -{ - tty->ref_count = 0; - tty->open_count = 0; - tty->opened_count = 0; - tty->index = index; - tty->lock = lock; - tty->settings = &gTTYSettings[index]; - tty->select_pool = NULL; - tty->is_master = isMaster; - tty->pending_eof = 0; -} - - -status_t -tty_output_getc(struct tty* tty, int* _c) -{ - return B_ERROR; -} - - /*! Processes the input character and puts it into the TTY's input buffer. Depending on the termios flags set, signals may be sent, the input character changed or removed, etc. @@ -922,196 +877,6 @@ tty_input_putc_locked(struct tty* tty, int c) } -#if 0 -status_t -tty_input_putc(struct tty* tty, int c) -{ - status_t status = acquire_sem_etc(tty->write_sem, 1, B_CAN_INTERRUPT, 0); - if (status != B_OK) - return status; - - MutexLocker locker(&tty->lock); - - bool wasEmpty = line_buffer_readable(tty->input_buffer) == 0; - - tty_input_putc_locked(tty, c); - - // If the buffer was empty before, we can now start other readers on it. - // We assume that most of the time more than one character will be written - // using this function, so we don't want to reschedule after every character - if (wasEmpty) - release_sem_etc(tty->read_sem, 1, B_DO_NOT_RESCHEDULE); - - // We only wrote one char - we give others the opportunity - // to write if there is still space left in the buffer - if (line_buffer_writable(tty->input_buffer)) - release_sem_etc(tty->write_sem, 1, B_DO_NOT_RESCHEDULE); - - return B_OK; -} -#endif // 0 - - -/*! The global lock must be held. -*/ -status_t -init_tty_cookie(tty_cookie* cookie, struct tty* tty, struct tty* otherTTY, - uint32 openMode) -{ - cookie->blocking_semaphore = create_sem(0, "wait for tty close"); - if (cookie->blocking_semaphore < 0) - return cookie->blocking_semaphore; - - cookie->tty = tty; - cookie->other_tty = otherTTY; - cookie->open_mode = openMode; - cookie->thread_count = 0; - cookie->closed = false; - - tty->ref_count++; - - return B_OK; -} - - -/*! The global lock must be held. -*/ -void -uninit_tty_cookie(tty_cookie* cookie) -{ - cookie->tty->ref_count--; - - if (cookie->blocking_semaphore >= 0) { - delete_sem(cookie->blocking_semaphore); - cookie->blocking_semaphore = -1; - } - - cookie->tty = NULL; - cookie->thread_count = 0; - cookie->closed = false; -} - - -/*! The global lock must be held. -*/ -void -add_tty_cookie(tty_cookie* cookie) -{ - MutexLocker locker(cookie->tty->lock); - - // add to the TTY's cookie list - cookie->tty->cookies.Add(cookie); - cookie->tty->open_count++; - cookie->tty->opened_count++; -} - - -/*! The global lock must be held. -*/ -void -tty_close_cookie(struct tty_cookie* cookie) -{ - MutexLocker locker(gTTYCookieLock); - - // Already closed? This can happen for slaves that have been closed when - // the master was closed. - if (cookie->closed) - return; - - // set the cookie's `closed' flag - cookie->closed = true; - bool unblock = (cookie->thread_count > 0); - - // unblock blocking threads - if (unblock) { - cookie->tty->reader_queue.NotifyError(cookie, B_FILE_ERROR); - cookie->tty->writer_queue.NotifyError(cookie, B_FILE_ERROR); - - if (cookie->other_tty->open_count > 0) { - cookie->other_tty->reader_queue.NotifyError(cookie, B_FILE_ERROR); - cookie->other_tty->writer_queue.NotifyError(cookie, B_FILE_ERROR); - } - } - - locker.Unlock(); - - // wait till all blocking (and now unblocked) threads have left the - // critical code - if (unblock) { - TRACE(("tty_close_cookie(): cookie %p, there're still pending " - "operations, acquire blocking sem %" B_PRId32 "\n", cookie, - cookie->blocking_semaphore)); - - acquire_sem(cookie->blocking_semaphore); - } - - // For the removal of the cookie acquire the TTY's lock. This ensures, that - // cookies will not be removed from a TTY (or added -- cf. add_tty_cookie()) - // as long as the TTY's lock is being held. This is required for the select - // support, since we need to iterate through the cookies of a TTY without - // having to acquire the global lock. - MutexLocker ttyLocker(cookie->tty->lock); - - // remove the cookie from the TTY's cookie list - cookie->tty->cookies.Remove(cookie); - - // close the tty, if no longer used - if (--cookie->tty->open_count == 0) { - // The last cookie of this tty has been closed. We're going to close - // the TTY and need to unblock all write requests before. There should - // be no read requests, since only a cookie of this TTY issues those. - // We do this by first notifying all queued requests of the error - // condition. We then clear the line buffer for the TTY and queue - // an own request. - - // Notify the other TTY first; it doesn't accept any read/writes - // while there is only one end. - cookie->other_tty->reader_queue.NotifyError(B_FILE_ERROR); - cookie->other_tty->writer_queue.NotifyError(B_FILE_ERROR); - - RecursiveLocker requestLocker(gTTYRequestLock); - - // we only need to do all this, if the writer queue is not empty - if (!cookie->tty->writer_queue.IsEmpty()) { - // notify the blocking writers - cookie->tty->writer_queue.NotifyError(B_FILE_ERROR); - - // enqueue our request - RequestOwner requestOwner; - requestOwner.Enqueue(cookie, &cookie->tty->writer_queue); - - requestLocker.Unlock(); - - // clear the line buffer - clear_line_buffer(cookie->tty->input_buffer); - - ttyLocker.Unlock(); - - // wait for our turn - requestOwner.Wait(false); - - // re-lock - ttyLocker.Lock(); - requestLocker.Lock(); - - // dequeue our request - requestOwner.Dequeue(); - } - - requestLocker.Unlock(); - - // finally close the tty - tty_close(cookie->tty); - } - - // notify pending select()s and cleanup the select sync pool - - // notify a select write event on the other tty, if we've closed this tty - if (cookie->tty->open_count == 0 && cookie->other_tty->open_count > 0) - tty_notify_select_event(cookie->other_tty, B_SELECT_WRITE); -} - - static int32 tty_readable(struct tty* tty) { @@ -1483,101 +1248,79 @@ tty_write_to_tty_slave_unsafe(tty_cookie* sourceCookie, const char* data, } -static void -dump_tty_settings(struct tty_settings& settings) +status_t +tty_write_to_tty_master(tty_cookie* sourceCookie, const void* _buffer, + size_t* _length) { - kprintf(" pgrp_id: %" B_PRId32 "\n", settings.pgrp_id); - kprintf(" session_id: %" B_PRId32 "\n", settings.session_id); + const char* buffer = (const char*)_buffer; + size_t bytesRemaining = *_length; + *_length = 0; - kprintf(" termios:\n"); - kprintf(" c_iflag: 0x%08" B_PRIx32 "\n", settings.termios.c_iflag); - kprintf(" c_oflag: 0x%08" B_PRIx32 "\n", settings.termios.c_oflag); - kprintf(" c_cflag: 0x%08" B_PRIx32 "\n", settings.termios.c_cflag); - kprintf(" c_lflag: 0x%08" B_PRIx32 "\n", settings.termios.c_lflag); - kprintf(" c_line: %d\n", settings.termios.c_line); - kprintf(" c_ispeed: %u\n", settings.termios.c_ispeed); - kprintf(" c_ospeed: %u\n", settings.termios.c_ospeed); - for (int i = 0; i < NCCS; i++) - kprintf(" c_cc[%02d]: %d\n", i, settings.termios.c_cc[i]); + while (bytesRemaining > 0) { + // copy data to stack + char safeBuffer[256]; + size_t toWrite = min_c(sizeof(safeBuffer), bytesRemaining); + status_t error = user_memcpy(safeBuffer, buffer, toWrite); + if (error != B_OK) + return error; - kprintf(" wsize: %u x %u c, %u x %u pxl\n", - settings.window_size.ws_row, settings.window_size.ws_col, - settings.window_size.ws_xpixel, settings.window_size.ws_ypixel); + // write them + size_t written = toWrite; + error = tty_write_to_tty_master_unsafe(sourceCookie, safeBuffer, + &written); + if (error != B_OK) + return error; + + buffer += written; + bytesRemaining -= written; + *_length += written; + + if (written < toWrite) + return B_OK; + } + + return B_OK; } -static void -dump_tty_struct(struct tty& tty) +status_t +tty_write_to_tty_slave(tty_cookie* sourceCookie, const void* _buffer, + size_t* _length) { - kprintf(" tty @: %p\n", &tty); - kprintf(" index: %" B_PRId32 "\n", tty.index); - kprintf(" is_master: %s\n", tty.is_master ? "true" : "false"); - kprintf(" open_count: %" B_PRId32 "\n", tty.open_count); - kprintf(" select_pool: %p\n", tty.select_pool); - kprintf(" pending_eof: %" B_PRIu32 "\n", tty.pending_eof); - kprintf(" lock: %p\n", tty.lock); + const char* buffer = (const char*)_buffer; + size_t bytesRemaining = *_length; + *_length = 0; - kprintf(" input_buffer:\n"); - kprintf(" first: %" B_PRId32 "\n", tty.input_buffer.first); - kprintf(" in: %lu\n", tty.input_buffer.in); - kprintf(" size: %lu\n", tty.input_buffer.size); - kprintf(" buffer: %p\n", tty.input_buffer.buffer); + while (bytesRemaining > 0) { + // copy data to stack + char safeBuffer[256]; + size_t toWrite = min_c(sizeof(safeBuffer), bytesRemaining); + status_t error = user_memcpy(safeBuffer, buffer, toWrite); + if (error != B_OK) + return error; - kprintf(" reader queue:\n"); - tty.reader_queue.Dump(" "); - kprintf(" writer queue:\n"); - tty.writer_queue.Dump(" "); + // write them + size_t written = toWrite; + error = tty_write_to_tty_slave_unsafe(sourceCookie, safeBuffer, + &written); + if (error != B_OK) + return error; - kprintf(" cookies: "); - TTYCookieList::Iterator it = tty.cookies.GetIterator(); - while (tty_cookie* cookie = it.Next()) - kprintf(" %p", cookie); - kprintf("\n"); -} + buffer += written; + bytesRemaining -= written; + *_length += written; - -static int -dump_tty(int argc, char** argv) -{ - if (argc < 2) { - kprintf("Usage: %s \n", argv[0]); - return 0; + if (written < toWrite) + return B_OK; } - int32 index = atol(argv[1]); - if (index < 0 || index >= (int32)kNumTTYs) { - kprintf("Invalid tty index.\n"); - return 0; - } - - kprintf("master:\n"); - dump_tty_struct(gMasterTTYs[index]); - kprintf("slave:\n"); - dump_tty_struct(gSlaveTTYs[index]); - kprintf("settings:\n"); - dump_tty_settings(gTTYSettings[index]); - - return 0; + return B_OK; } // #pragma mark - device functions -status_t -tty_close(struct tty* tty) -{ - // destroy the queues - tty->reader_queue.~RequestQueue(); - tty->writer_queue.~RequestQueue(); - tty->cookies.~TTYCookieList(); - - uninit_line_buffer(tty->input_buffer); - - return B_OK; -} - - status_t tty_open(struct tty* tty, tty_service_func func) { @@ -1595,6 +1338,283 @@ tty_open(struct tty* tty, tty_service_func func) } +status_t +tty_close(struct tty* tty) +{ + // destroy the queues + tty->reader_queue.~RequestQueue(); + tty->writer_queue.~RequestQueue(); + tty->cookies.~TTYCookieList(); + + uninit_line_buffer(tty->input_buffer); + + return B_OK; +} + + +/*! The global lock must be held. +*/ +status_t +init_tty_cookie(tty_cookie* cookie, struct tty* tty, struct tty* otherTTY, + uint32 openMode) +{ + cookie->blocking_semaphore = create_sem(0, "wait for tty close"); + if (cookie->blocking_semaphore < 0) + return cookie->blocking_semaphore; + + cookie->tty = tty; + cookie->other_tty = otherTTY; + cookie->open_mode = openMode; + cookie->thread_count = 0; + cookie->closed = false; + + + MutexLocker locker(cookie->tty->lock); + + // add to the TTY's cookie list + tty->cookies.Add(cookie); + tty->open_count++; + tty->ref_count++; + tty->opened_count++; + + return B_OK; +} + + +/*! The global lock must be held. +*/ +void +tty_close_cookie(struct tty_cookie* cookie) +{ + MutexLocker locker(gTTYCookieLock); + + // Already closed? This can happen for slaves that have been closed when + // the master was closed. + if (cookie->closed) + return; + + // set the cookie's `closed' flag + cookie->closed = true; + bool unblock = (cookie->thread_count > 0); + + // unblock blocking threads + if (unblock) { + cookie->tty->reader_queue.NotifyError(cookie, B_FILE_ERROR); + cookie->tty->writer_queue.NotifyError(cookie, B_FILE_ERROR); + + if (cookie->other_tty->open_count > 0) { + cookie->other_tty->reader_queue.NotifyError(cookie, B_FILE_ERROR); + cookie->other_tty->writer_queue.NotifyError(cookie, B_FILE_ERROR); + } + } + + locker.Unlock(); + + // wait till all blocking (and now unblocked) threads have left the + // critical code + if (unblock) { + TRACE(("tty_close_cookie(): cookie %p, there're still pending " + "operations, acquire blocking sem %" B_PRId32 "\n", cookie, + cookie->blocking_semaphore)); + + acquire_sem(cookie->blocking_semaphore); + } + + // For the removal of the cookie acquire the TTY's lock. This ensures, that + // cookies will not be removed from a TTY (or added -- cf. add_tty_cookie()) + // as long as the TTY's lock is being held. This is required for the select + // support, since we need to iterate through the cookies of a TTY without + // having to acquire the global lock. + MutexLocker ttyLocker(cookie->tty->lock); + + // remove the cookie from the TTY's cookie list + cookie->tty->cookies.Remove(cookie); + + // close the tty, if no longer used + if (--cookie->tty->open_count == 0) { + // The last cookie of this tty has been closed. We're going to close + // the TTY and need to unblock all write requests before. There should + // be no read requests, since only a cookie of this TTY issues those. + // We do this by first notifying all queued requests of the error + // condition. We then clear the line buffer for the TTY and queue + // an own request. + + // Notify the other TTY first; it doesn't accept any read/writes + // while there is only one end. + cookie->other_tty->reader_queue.NotifyError(B_FILE_ERROR); + cookie->other_tty->writer_queue.NotifyError(B_FILE_ERROR); + + RecursiveLocker requestLocker(gTTYRequestLock); + + // we only need to do all this, if the writer queue is not empty + if (!cookie->tty->writer_queue.IsEmpty()) { + // notify the blocking writers + cookie->tty->writer_queue.NotifyError(B_FILE_ERROR); + + // enqueue our request + RequestOwner requestOwner; + requestOwner.Enqueue(cookie, &cookie->tty->writer_queue); + + requestLocker.Unlock(); + + // clear the line buffer + clear_line_buffer(cookie->tty->input_buffer); + + ttyLocker.Unlock(); + + // wait for our turn + requestOwner.Wait(false); + + // re-lock + ttyLocker.Lock(); + requestLocker.Lock(); + + // dequeue our request + requestOwner.Dequeue(); + } + + requestLocker.Unlock(); + + // finally close the tty + tty_close(cookie->tty); + } + + // notify pending select()s and cleanup the select sync pool + + // notify a select write event on the other tty, if we've closed this tty + if (cookie->tty->open_count == 0 && cookie->other_tty->open_count > 0) + tty_notify_select_event(cookie->other_tty, B_SELECT_WRITE); +} + + +/*! The global lock must be held. +*/ +void +uninit_tty_cookie(tty_cookie* cookie) +{ + cookie->tty->ref_count--; + + if (cookie->blocking_semaphore >= 0) { + delete_sem(cookie->blocking_semaphore); + cookie->blocking_semaphore = -1; + } + + cookie->tty = NULL; + cookie->thread_count = 0; + cookie->closed = false; +} + + +status_t +tty_input_read(tty_cookie* cookie, void* _buffer, size_t* _length) +{ + char* buffer = (char*)_buffer; + struct tty* tty = cookie->tty; + uint32 mode = cookie->open_mode; + bool dontBlock = (mode & O_NONBLOCK) != 0; + size_t length = *_length; + bool canon = true; + bigtime_t timeout = dontBlock ? 0 : B_INFINITE_TIMEOUT; + bigtime_t interCharTimeout = 0; + size_t bytesNeeded = 1; + + TRACE(("tty_input_read(tty = %p, length = %lu, mode = %" B_PRIu32 ")\n", + tty, length, mode)); + + if (length == 0) + return B_OK; + + // bail out, if the TTY is already closed + TTYReference ttyReference(cookie); + if (!ttyReference.IsLocked()) + return B_FILE_ERROR; + + ReaderLocker locker(cookie); + + // handle raw mode + if ((!tty->is_master) && ((tty->settings->termios.c_lflag & ICANON) == 0)) { + canon = false; + if (!dontBlock) { + // Non-blocking mode. Handle VMIN and VTIME. + bytesNeeded = tty->settings->termios.c_cc[VMIN]; + bigtime_t vtime = tty->settings->termios.c_cc[VTIME] * 100000; + TRACE(("tty_input_read: icanon vmin %lu, vtime %" B_PRIdBIGTIME + "us\n", bytesNeeded, vtime)); + + if (bytesNeeded == 0) { + // In this case VTIME specifies a relative total timeout. We + // have no inter-char timeout, though. + timeout = vtime; + } else { + // VTIME specifies the inter-char timeout. 0 is indefinitely. + if (vtime == 0) + interCharTimeout = B_INFINITE_TIMEOUT; + else + interCharTimeout = vtime; + + if (bytesNeeded > length) + bytesNeeded = length; + } + } + } + + status_t status; + *_length = 0; + + do { + TRACE(("tty_input_read: AcquireReader(%" B_PRIdBIGTIME "us, %ld)\n", + timeout, bytesNeeded)); + status = locker.AcquireReader(timeout, bytesNeeded); + size_t toRead = locker.AvailableBytes(); + if (status != B_OK && toRead == 0) { + TRACE(("tty_input_read() AcquireReader failed\n")); + break; + } + + if (toRead > length) + toRead = length; + + bool _hitEOF = false; + bool* hitEOF = canon && tty->pending_eof > 0 ? &_hitEOF : NULL; + + ssize_t bytesRead = line_buffer_user_read(tty->input_buffer, buffer, + toRead, tty->settings->termios.c_cc[VEOF], hitEOF); + if (bytesRead < 0) { + status = bytesRead; + break; + } + + buffer += bytesRead; + length -= bytesRead; + *_length += bytesRead; + bytesNeeded = (size_t)bytesRead > bytesNeeded + ? 0 : bytesNeeded - bytesRead; + + // we hit an EOF char -- bail out, whatever amount of data we have + if (hitEOF && *hitEOF) { + tty->pending_eof--; + break; + } + + // Once we have read something reset the timeout to the inter-char + // timeout, if applicable. + if (!dontBlock && !canon && *_length > 0) + timeout = interCharTimeout; + } while (bytesNeeded > 0); + + if (status == B_WOULD_BLOCK || status == B_TIMED_OUT) { + // In non-blocking non-canonical-input-processing mode never return + // timeout errors. Just return 0, if nothing has been read. + if (!dontBlock && !canon) + status = B_OK; + } + + TRACE(("tty_input_read() status 0x%" B_PRIx32 "\n", status)); + + return *_length == 0 ? status : B_OK; +} + + status_t tty_ioctl(tty_cookie* cookie, uint32 op, void* buffer, size_t length) { @@ -1630,6 +1650,7 @@ tty_ioctl(tty_cookie* cookie, uint32 op, void* buffer, size_t length) case TCSETA: case TCSETAW: case TCSETAF: + { TRACE(("tty: set attributes (iflag = %" B_PRIx32 ", oflag = %" B_PRIx32 ", cflag = %" B_PRIx32 ", lflag = %" B_PRIx32 ")\n", tty->settings->termios.c_iflag, tty->settings->termios.c_oflag, @@ -1638,6 +1659,7 @@ tty_ioctl(tty_cookie* cookie, uint32 op, void* buffer, size_t length) return user_memcpy(&tty->settings->termios, buffer, sizeof(struct termios)); + } // get and set process group ID @@ -1826,186 +1848,6 @@ tty_ioctl(tty_cookie* cookie, uint32 op, void* buffer, size_t length) } -status_t -tty_input_read(tty_cookie* cookie, void* _buffer, size_t* _length) -{ - char* buffer = (char*)_buffer; - struct tty* tty = cookie->tty; - uint32 mode = cookie->open_mode; - bool dontBlock = (mode & O_NONBLOCK) != 0; - size_t length = *_length; - bool canon = true; - bigtime_t timeout = dontBlock ? 0 : B_INFINITE_TIMEOUT; - bigtime_t interCharTimeout = 0; - size_t bytesNeeded = 1; - - TRACE(("tty_input_read(tty = %p, length = %lu, mode = %" B_PRIu32 ")\n", - tty, length, mode)); - - if (length == 0) - return B_OK; - - // bail out, if the TTY is already closed - TTYReference ttyReference(cookie); - if (!ttyReference.IsLocked()) - return B_FILE_ERROR; - - ReaderLocker locker(cookie); - - // handle raw mode - if ((!tty->is_master) && ((tty->settings->termios.c_lflag & ICANON) == 0)) { - canon = false; - if (!dontBlock) { - // Non-blocking mode. Handle VMIN and VTIME. - bytesNeeded = tty->settings->termios.c_cc[VMIN]; - bigtime_t vtime = tty->settings->termios.c_cc[VTIME] * 100000; - TRACE(("tty_input_read: icanon vmin %lu, vtime %" B_PRIdBIGTIME - "us\n", bytesNeeded, vtime)); - - if (bytesNeeded == 0) { - // In this case VTIME specifies a relative total timeout. We - // have no inter-char timeout, though. - timeout = vtime; - } else { - // VTIME specifies the inter-char timeout. 0 is indefinitely. - if (vtime == 0) - interCharTimeout = B_INFINITE_TIMEOUT; - else - interCharTimeout = vtime; - - if (bytesNeeded > length) - bytesNeeded = length; - } - } - } - - status_t status; - *_length = 0; - - do { - TRACE(("tty_input_read: AcquireReader(%" B_PRIdBIGTIME "us, %ld)\n", - timeout, bytesNeeded)); - status = locker.AcquireReader(timeout, bytesNeeded); - size_t toRead = locker.AvailableBytes(); - if (status != B_OK && toRead == 0) { - TRACE(("tty_input_read() AcquireReader failed\n")); - break; - } - - if (toRead > length) - toRead = length; - - bool _hitEOF = false; - bool* hitEOF = canon && tty->pending_eof > 0 ? &_hitEOF : NULL; - - ssize_t bytesRead = line_buffer_user_read(tty->input_buffer, buffer, - toRead, tty->settings->termios.c_cc[VEOF], hitEOF); - if (bytesRead < 0) { - status = bytesRead; - break; - } - - buffer += bytesRead; - length -= bytesRead; - *_length += bytesRead; - bytesNeeded = (size_t)bytesRead > bytesNeeded - ? 0 : bytesNeeded - bytesRead; - - // we hit an EOF char -- bail out, whatever amount of data we have - if (hitEOF && *hitEOF) { - tty->pending_eof--; - break; - } - - // Once we have read something reset the timeout to the inter-char - // timeout, if applicable. - if (!dontBlock && !canon && *_length > 0) - timeout = interCharTimeout; - } while (bytesNeeded > 0); - - if (status == B_WOULD_BLOCK || status == B_TIMED_OUT) { - // In non-blocking non-canonical-input-processing mode never return - // timeout errors. Just return 0, if nothing has been read. - if (!dontBlock && !canon) - status = B_OK; - } - - TRACE(("tty_input_read() status 0x%" B_PRIx32 "\n", status)); - - return *_length == 0 ? status : B_OK; -} - - -status_t -tty_write_to_tty_master(tty_cookie* sourceCookie, const void* _buffer, - size_t* _length) -{ - const char* buffer = (const char*)_buffer; - size_t bytesRemaining = *_length; - *_length = 0; - - while (bytesRemaining > 0) { - // copy data to stack - char safeBuffer[256]; - size_t toWrite = min_c(sizeof(safeBuffer), bytesRemaining); - status_t error = user_memcpy(safeBuffer, buffer, toWrite); - if (error != B_OK) - return error; - - // write them - size_t written = toWrite; - error = tty_write_to_tty_master_unsafe(sourceCookie, safeBuffer, - &written); - if (error != B_OK) - return error; - - buffer += written; - bytesRemaining -= written; - *_length += written; - - if (written < toWrite) - return B_OK; - } - - return B_OK; -} - - -status_t -tty_write_to_tty_slave(tty_cookie* sourceCookie, const void* _buffer, - size_t* _length) -{ - const char* buffer = (const char*)_buffer; - size_t bytesRemaining = *_length; - *_length = 0; - - while (bytesRemaining > 0) { - // copy data to stack - char safeBuffer[256]; - size_t toWrite = min_c(sizeof(safeBuffer), bytesRemaining); - status_t error = user_memcpy(safeBuffer, buffer, toWrite); - if (error != B_OK) - return error; - - // write them - size_t written = toWrite; - error = tty_write_to_tty_slave_unsafe(sourceCookie, safeBuffer, - &written); - if (error != B_OK) - return error; - - buffer += written; - bytesRemaining -= written; - *_length += written; - - if (written < toWrite) - return B_OK; - } - - return B_OK; -} - - status_t tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync) { @@ -2106,17 +1948,3 @@ tty_deselect(tty_cookie* cookie, uint8 event, selectsync* sync) return remove_select_sync_pool_entry(&tty->select_pool, sync, event); } - - -void -tty_add_debugger_commands() -{ - add_debugger_command("tty", &dump_tty, "Dump info on a tty"); -} - - -void -tty_remove_debugger_commands() -{ - remove_debugger_command("tty", &dump_tty); -} diff --git a/src/add-ons/kernel/drivers/tty/tty_driver.h b/src/add-ons/kernel/drivers/tty/tty_driver.h new file mode 100644 index 0000000000..5898201d22 --- /dev/null +++ b/src/add-ons/kernel/drivers/tty/tty_driver.h @@ -0,0 +1,25 @@ +/* + * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef TTY_DRIVER_H +#define TTY_DRIVER_H + + +#include "tty_private.h" + + +extern tty gMasterTTYs[kNumTTYs]; +extern tty gSlaveTTYs[kNumTTYs]; + +extern device_hooks gMasterTTYHooks; +extern device_hooks gSlaveTTYHooks; + +extern struct mutex gGlobalTTYLock; + +extern int32 get_tty_index(const char *name); + +extern void reset_tty(struct tty *tty, int32 index, mutex* lock, bool isMaster); + + +#endif /* !TTY_DRIVER_H */ diff --git a/src/add-ons/kernel/drivers/tty/tty_private.h b/src/add-ons/kernel/drivers/tty/tty_private.h index a0c796fea3..c77424150d 100644 --- a/src/add-ons/kernel/drivers/tty/tty_private.h +++ b/src/add-ons/kernel/drivers/tty/tty_private.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ #ifndef TTY_PRIVATE_H @@ -132,9 +132,6 @@ struct tty { int32 ref_count; // referenced by cookies int32 open_count; int32 opened_count; - int32 index; - struct mutex* lock; - tty_settings* settings; select_sync_pool* select_pool; RequestQueue reader_queue; RequestQueue writer_queue; @@ -143,53 +140,39 @@ struct tty { tty_service_func service_func; uint32 pending_eof; bool is_master; + struct mutex* lock; + tty_settings* settings; + int32 index; }; static const uint32 kNumTTYs = 64; extern char *gDeviceNames[kNumTTYs * 2 + 3]; -extern tty gMasterTTYs[kNumTTYs]; -extern tty gSlaveTTYs[kNumTTYs]; -extern tty_settings gTTYSettings[kNumTTYs]; - -extern device_hooks gMasterTTYHooks; -extern device_hooks gSlaveTTYHooks; - -extern struct mutex gGlobalTTYLock; extern struct mutex gTTYCookieLock; extern struct recursive_lock gTTYRequestLock; - -// functions available for master/slave TTYs - -extern int32 get_tty_index(const char *name); -extern void reset_tty(struct tty *tty, int32 index, mutex* lock, bool isMaster); -extern void reset_tty_settings(tty_settings *settings, int32 index); -//extern status_t tty_input_putc(struct tty *tty, int c); -extern status_t tty_input_read(tty_cookie *cookie, void *buffer, - size_t *_length); -extern status_t tty_output_getc(struct tty *tty, int *_c); -extern status_t tty_write_to_tty_master(tty_cookie *sourceCookie, - const void *buffer, size_t *_length); -extern status_t tty_write_to_tty_slave(tty_cookie *sourceCookie, - const void *buffer, size_t *_length); +extern status_t tty_open(struct tty *tty, tty_service_func func); +extern status_t tty_close(struct tty *tty); extern status_t init_tty_cookie(tty_cookie *cookie, struct tty *tty, struct tty *otherTTY, uint32 openMode); extern void uninit_tty_cookie(tty_cookie *cookie); -extern void add_tty_cookie(tty_cookie *cookie); extern void tty_close_cookie(struct tty_cookie *cookie); -extern status_t tty_open(struct tty *tty, tty_service_func func); -extern status_t tty_close(struct tty *tty); +// functions available for master/slave TTYs +extern void reset_tty_settings(tty_settings *settings); + +extern status_t tty_input_read(tty_cookie *cookie, void *buffer, + size_t *_length); +extern status_t tty_write_to_tty_master(tty_cookie *sourceCookie, + const void *buffer, size_t *_length); +extern status_t tty_write_to_tty_slave(tty_cookie *sourceCookie, + const void *buffer, size_t *_length); extern status_t tty_ioctl(tty_cookie *cookie, uint32 op, void *buffer, size_t length); extern status_t tty_select(tty_cookie *cookie, uint8 event, uint32 ref, - selectsync *sync); + selectsync *sync); extern status_t tty_deselect(tty_cookie *cookie, uint8 event, selectsync *sync); -extern void tty_add_debugger_commands(); -extern void tty_remove_debugger_commands(); - #endif /* TTY_PRIVATE_H */ diff --git a/src/add-ons/kernel/generic/tty/tty.cpp b/src/add-ons/kernel/generic/tty/tty.cpp index 3a8053affb..a5594d1b1b 100644 --- a/src/add-ons/kernel/generic/tty/tty.cpp +++ b/src/add-ons/kernel/generic/tty/tty.cpp @@ -5,11 +5,6 @@ */ -// This file could be moved into a generic tty module. -// The whole hardware signaling stuff is missing, though - it's currently -// tailored for pseudo-TTYs. Have a look at Be's TTY includes (drivers/tty/*) - - #include "tty_private.h" #include @@ -1267,59 +1262,6 @@ tty_write_to_tty_slave(tty_cookie* sourceCookie, const void* _buffer, } -#if 0 -static void -dump_tty_settings(struct tty_settings& settings) -{ - kprintf(" pgrp_id: %ld\n", settings.pgrp_id); - kprintf(" session_id: %ld\n", settings.session_id); - - kprintf(" termios:\n"); - kprintf(" c_iflag: 0x%08lx\n", settings.termios.c_iflag); - kprintf(" c_oflag: 0x%08lx\n", settings.termios.c_oflag); - kprintf(" c_cflag: 0x%08lx\n", settings.termios.c_cflag); - kprintf(" c_lflag: 0x%08lx\n", settings.termios.c_lflag); - kprintf(" c_line: %d\n", settings.termios.c_line); - kprintf(" c_ispeed: %u\n", settings.termios.c_ispeed); - kprintf(" c_ospeed: %u\n", settings.termios.c_ospeed); - for (int i = 0; i < NCCS; i++) - kprintf(" c_cc[%02d]: %d\n", i, settings.termios.c_cc[i]); - - kprintf(" wsize: %u x %u c, %u x %u pxl\n", - settings.window_size.ws_row, settings.window_size.ws_col, - settings.window_size.ws_xpixel, settings.window_size.ws_ypixel); -} - - -static void -dump_tty_struct(struct tty& tty) -{ - kprintf(" tty @: %p\n", &tty); - kprintf(" is_master: %s\n", tty.is_master ? "true" : "false"); - kprintf(" open_count: %ld\n", tty.open_count); - kprintf(" select_pool: %p\n", tty.select_pool); - kprintf(" pending_eof: %lu\n", tty.pending_eof); - - kprintf(" input_buffer:\n"); - kprintf(" first: %ld\n", tty.input_buffer.first); - kprintf(" in: %lu\n", tty.input_buffer.in); - kprintf(" size: %lu\n", tty.input_buffer.size); - kprintf(" buffer: %p\n", tty.input_buffer.buffer); - - kprintf(" reader queue:\n"); - tty.reader_queue.Dump(" "); - kprintf(" writer queue:\n"); - tty.writer_queue.Dump(" "); - - kprintf(" cookies: "); - TTYCookieList::Iterator it = tty.cookies.GetIterator(); - while (tty_cookie* cookie = it.Next()) - kprintf(" %p", cookie); - kprintf("\n"); -} -#endif - - // #pragma mark - public API @@ -1435,7 +1377,7 @@ tty_close_cookie(tty_cookie* cookie) // critical code if (unblock) { TRACE(("tty_close_cookie(): cookie %p, there're still pending " - "operations, acquire blocking sem %ld\n", cookie, + "operations, acquire blocking sem %" B_PRId32 "\n", cookie, cookie->blocking_semaphore)); acquire_sem(cookie->blocking_semaphore); @@ -1530,7 +1472,8 @@ tty_read(tty_cookie* cookie, void* _buffer, size_t* _length) bigtime_t interCharTimeout = 0; size_t bytesNeeded = 1; - TRACE(("tty_input_read(tty = %p, length = %lu, mode = %lu)\n", tty, length, mode)); + TRACE(("tty_input_read(tty = %p, length = %lu, mode = %" B_PRIu32 ")\n", + tty, length, mode)); if (length == 0) return B_OK; @@ -1549,8 +1492,8 @@ tty_read(tty_cookie* cookie, void* _buffer, size_t* _length) // Non-blocking mode. Handle VMIN and VTIME. bytesNeeded = tty->settings.termios.c_cc[VMIN]; bigtime_t vtime = tty->settings.termios.c_cc[VTIME] * 100000; - TRACE(("tty_input_read: icanon vmin %lu, vtime %Ldus\n", bytesNeeded, - vtime)); + TRACE(("tty_input_read: icanon vmin %lu, vtime %" B_PRIdBIGTIME + "us\n", bytesNeeded, vtime)); if (bytesNeeded == 0) { // In this case VTIME specifies a relative total timeout. We @@ -1573,8 +1516,8 @@ tty_read(tty_cookie* cookie, void* _buffer, size_t* _length) *_length = 0; do { - TRACE(("tty_input_read: AcquireReader(%Ldus, %ld)\n", timeout, - bytesNeeded)); + TRACE(("tty_input_read: AcquireReader(%" B_PRIdBIGTIME "us, %ld)\n", + timeout, bytesNeeded)); status = locker.AcquireReader(timeout, bytesNeeded); size_t toRead = locker.AvailableBytes(); if (status != B_OK && toRead == 0) { @@ -1646,7 +1589,8 @@ tty_control(tty_cookie* cookie, uint32 op, void* buffer, size_t length) if (!ttyReference.IsLocked()) return B_FILE_ERROR; - TRACE(("tty_ioctl: tty %p, op %lu, buffer %p, length %lu\n", tty, op, buffer, length)); + TRACE(("tty_ioctl: tty %p, op %" B_PRIu32 ", buffer %p, length %" + B_PRIuSIZE "\n", tty, op, buffer, length)); MutexLocker locker(tty->lock); switch (op) { @@ -1670,9 +1614,10 @@ tty_control(tty_cookie* cookie, uint32 op, void* buffer, size_t length) case TCSETAW: case TCSETAF: { - TRACE(("tty: set attributes (iflag = %lx, oflag = %lx, " - "cflag = %lx, lflag = %lx)\n", tty->settings.termios.c_iflag, - tty->settings.termios.c_oflag, tty->settings.termios.c_cflag, + TRACE(("tty: set attributes (iflag = %" B_PRIx32 ", oflag = %" + B_PRIx32 ", cflag = %" B_PRIx32 ", lflag = %" B_PRIx32 ")\n", + tty->settings.termios.c_iflag, tty->settings.termios.c_oflag, + tty->settings.termios.c_cflag, tty->settings.termios.c_lflag)); status_t status = user_memcpy(&tty->settings.termios, buffer, @@ -1688,7 +1633,7 @@ tty_control(tty_cookie* cookie, uint32 op, void* buffer, size_t length) // get and set window size case TIOCGWINSZ: - TRACE(("tty: set window size\n")); + TRACE(("tty: get window size\n")); return user_memcpy(buffer, &tty->settings.window_size, sizeof(struct winsize)); @@ -1834,7 +1779,7 @@ tty_control(tty_cookie* cookie, uint32 op, void* buffer, size_t length) } } - TRACE(("tty: unsupported opcode %lu\n", op)); + TRACE(("tty: unsupported opcode %" B_PRIu32 "\n", op)); return B_BAD_VALUE; } @@ -1844,8 +1789,8 @@ tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync) { struct tty* tty = cookie->tty; - TRACE(("tty_select(cookie = %p, event = %u, ref = %lu, sync = %p)\n", - cookie, event, ref, sync)); + TRACE(("tty_select(cookie = %p, event = %u, ref = %" B_PRIu32 ", sync = " + "%p)\n", cookie, event, ref, sync)); // we don't support all kinds of events if (event < B_SELECT_READ || event > B_SELECT_ERROR) @@ -1872,8 +1817,8 @@ tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync) // add the event to the TTY's pool status_t error = add_select_sync_pool_entry(&tty->select_pool, sync, event); if (error != B_OK) { - TRACE(("tty_select() done: add_select_sync_pool_entry() failed: %lx\n", - error)); + TRACE(("tty_select() done: add_select_sync_pool_entry() failed: %" + B_PRIx32 "\n", error)); return error; } diff --git a/src/add-ons/kernel/generic/tty/tty_private.h b/src/add-ons/kernel/generic/tty/tty_private.h index 0da79e7078..44f8fc84d2 100644 --- a/src/add-ons/kernel/generic/tty/tty_private.h +++ b/src/add-ons/kernel/generic/tty/tty_private.h @@ -129,8 +129,6 @@ struct tty { int32 ref_count; // referenced by cookies int32 open_count; int32 opened_count; - struct mutex lock; - tty_settings settings; select_sync_pool* select_pool; RequestQueue reader_queue; RequestQueue writer_queue; @@ -139,6 +137,8 @@ struct tty { tty_service_func service_func; uint32 pending_eof; bool is_master; + struct mutex lock; + tty_settings settings; uint8 hardware_bits; };