tty: synchronize the driver and module sourcecode a bit

The tty module started its life as a copy of the driver, and they
diverged over time as fixes and refactorings were not always brought to
both sides.

This commit does not improve the situation, but it tries to make the
two versions of the tty_private.h and tty.cpp files as similar as
possible to ease comparison.

Change-Id: I62255262b167ab81c9a0619c8f19b36b1a165fad
Reviewed-on: https://review.haiku-os.org/c/haiku/+/4601
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
Adrien Destugues
2021-10-19 08:19:26 +00:00
committed by Adrien Destugues
parent 77c1611f1d
commit e7a62c420f
8 changed files with 522 additions and 620 deletions
+124 -1
View File
@@ -11,6 +11,7 @@
#include <lock.h>
#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 <tty index>\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;
}
+1 -2
View File
@@ -4,6 +4,7 @@
*/
#include "tty_driver.h"
#include "tty_private.h"
#include <stdlib.h>
@@ -100,8 +101,6 @@ master_open(const char *name, uint32 flags, void **_cookie)
return status;
}
add_tty_cookie(cookie);
*_cookie = cookie;
return B_OK;
+1 -2
View File
@@ -11,6 +11,7 @@
#include <team.h>
#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;
+334 -506
View File
@@ -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 <ctype.h>
@@ -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 <tty index>\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);
}
@@ -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 */
+16 -33
View File
@@ -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 */
+19 -74
View File
@@ -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 <ctype.h>
@@ -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;
}
+2 -2
View File
@@ -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;
};