diff --git a/src/add-ons/kernel/drivers/tty/Jamfile b/src/add-ons/kernel/drivers/tty/Jamfile index 8219227094..4316dd36a7 100644 --- a/src/add-ons/kernel/drivers/tty/Jamfile +++ b/src/add-ons/kernel/drivers/tty/Jamfile @@ -5,6 +5,7 @@ UsePrivateHeaders kernel ; KernelAddon tty : kernel drivers dev : driver.cpp tty.cpp + line_buffer.cpp master.cpp slave.cpp ; diff --git a/src/add-ons/kernel/drivers/tty/driver.cpp b/src/add-ons/kernel/drivers/tty/driver.cpp index 640c5dae3d..e47fac2962 100644 --- a/src/add-ons/kernel/drivers/tty/driver.cpp +++ b/src/add-ons/kernel/drivers/tty/driver.cpp @@ -40,8 +40,8 @@ init_driver(void) { TRACE((DRIVER_NAME ": init_driver()\n")); - // create driver name array - + // create driver name array and initialize basic TTY structures + char letter = 'p'; int8 digit = 0; @@ -59,15 +59,13 @@ init_driver(void) if (++digit > 15) digit = 0, letter++; + + reset_tty(&gMasterTTYs[i], i); + reset_tty(&gSlaveTTYs[i], i); } sDeviceNames[kNumTTYs * 2] = NULL; - // initialize TTY structures - - memset(gMasterTTYs, 0, kNumTTYs * sizeof(struct tty)); - memset(gSlaveTTYs, 0, kNumTTYs * sizeof(struct tty)); - return B_OK; } diff --git a/src/add-ons/kernel/drivers/tty/line_buffer.cpp b/src/add-ons/kernel/drivers/tty/line_buffer.cpp new file mode 100644 index 0000000000..cd8776dbce --- /dev/null +++ b/src/add-ons/kernel/drivers/tty/line_buffer.cpp @@ -0,0 +1,114 @@ +/* +** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the Haiku License. +*/ + + +#include "line_buffer.h" + +#include +#include + + +status_t +clear_line_buffer(struct line_buffer &buffer) +{ + buffer.in = 0; + buffer.first = 0; +} + + +status_t +init_line_buffer(struct line_buffer &buffer, size_t size) +{ + clear_line_buffer(buffer); + + buffer.buffer = (char *)malloc(size); + if (buffer.buffer == NULL) + return B_NO_MEMORY; + + buffer.size = size; + + return B_OK; +} + + +status_t +uninit_line_buffer(struct line_buffer &buffer) +{ + free(buffer.buffer); + return B_OK; +} + + +int32 +line_buffer_readable(struct line_buffer &buffer) +{ + return buffer.in; +} + + +int32 +line_buffer_writable(struct line_buffer &buffer) +{ + return buffer.size - buffer.in; +} + + +ssize_t +line_buffer_user_read(struct line_buffer &buffer, char *data, size_t length) +{ + size_t available = buffer.in; + + if (length > available) + length = available; + + if (length == 0) + return 0; + + ssize_t bytesRead = length; + + if (buffer.first + length < buffer.size) { + // simple copy + if (user_memcpy(data, buffer.buffer + buffer.first, length) != B_OK) + bytesRead = B_BAD_ADDRESS; + } else { + // need to copy both ends + size_t upper = buffer.size - buffer.first; + size_t lower = length - upper; + + if (user_memcpy(data, buffer.buffer + buffer.first, upper) != B_OK + || user_memcpy(data + upper, buffer.buffer, lower) != B_OK) + bytesRead = B_BAD_ADDRESS; + } + + if (bytesRead > 0) + buffer.first = (buffer.first + bytesRead) % buffer.size; + + return bytesRead; +} + + +status_t +line_buffer_putc(struct line_buffer &buffer, char c) +{ + if (buffer.in == buffer.size) + return B_NO_MEMORY; + + buffer.buffer[(buffer.first + buffer.in++) % buffer.size] = c; +} + + +#if 0 +status_t +line_buffer_getc(struct line_buffer &buffer, char *_c) +{ +} + + +status_t +line_buffer_ungetc(struct line_buffer &buffer, char *c) +{ +} + +#endif diff --git a/src/add-ons/kernel/drivers/tty/line_buffer.h b/src/add-ons/kernel/drivers/tty/line_buffer.h new file mode 100644 index 0000000000..24312ac83b --- /dev/null +++ b/src/add-ons/kernel/drivers/tty/line_buffer.h @@ -0,0 +1,29 @@ +/* +** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the Haiku License. +*/ +#ifndef LINE_BUFFER_H +#define LINE_BUFFER_H + + +#include + + +struct line_buffer { + int32 first; + size_t in; + size_t size; + char *buffer; +}; + +status_t init_line_buffer(struct line_buffer &buffer, size_t size); +status_t uninit_line_buffer(struct line_buffer &buffer); +status_t reset_line_buffer(struct line_buffer &buffer); +int32 line_buffer_readable(struct line_buffer &buffer); +int32 line_buffer_writable(struct line_buffer &buffer); +ssize_t line_buffer_user_read(struct line_buffer &buffer, char *data, size_t length); +status_t line_buffer_getc(struct line_buffer &buffer, char *_c); +status_t line_buffer_putc(struct line_buffer &buffer, char c); +status_t line_buffer_ungetc(struct line_buffer &buffer, char *c); + +#endif /* LINE_BUFFER_H */ diff --git a/src/add-ons/kernel/drivers/tty/master.cpp b/src/add-ons/kernel/drivers/tty/master.cpp index 6c3924c8f8..beb23493e2 100644 --- a/src/add-ons/kernel/drivers/tty/master.cpp +++ b/src/add-ons/kernel/drivers/tty/master.cpp @@ -19,6 +19,7 @@ struct master_cookie { struct tty *tty; + struct tty *slave; uint32 open_mode; }; @@ -26,21 +27,45 @@ struct master_cookie { struct tty gMasterTTYs[kNumTTYs]; +static status_t +master_service(struct tty *tty, uint32 op) +{ + // nothing here yet + return B_OK; +} + + +// #pragma mark - + + static status_t master_open(const char *name, uint32 flags, void **_cookie) { int32 index = get_tty_index(name); + dprintf("TTY index = %ld (name = %s)\n", index, name); if (atomic_or(&gMasterTTYs[index].open_count, 1) != 0) { // we're already open! return B_BUSY; } + status_t status = tty_open(&gMasterTTYs[index], &master_service); + if (status < B_OK) { + // initializing TTY failed, reset open counter + atomic_and(&gMasterTTYs[index].open_count, 0); + return status; + } + master_cookie *cookie = (master_cookie *)malloc(sizeof(struct master_cookie)); - if (cookie == NULL) + if (cookie == NULL) { + atomic_and(&gMasterTTYs[index].open_count, 0); + tty_close(&gMasterTTYs[index]); return B_NO_MEMORY; + } cookie->tty = &gMasterTTYs[index]; + cookie->slave = &gSlaveTTYs[index]; + cookie->open_mode = flags; *_cookie = cookie; return B_OK; @@ -62,7 +87,10 @@ master_close(void *_cookie) static status_t master_free_cookie(void *_cookie) { - free(_cookie); + master_cookie *cookie = (master_cookie *)_cookie; + + tty_close(cookie->tty); + free(cookie); return B_OK; } @@ -70,38 +98,48 @@ master_free_cookie(void *_cookie) static status_t master_ioctl(void *_cookie, uint32 op, void *buffer, size_t length) { + master_cookie *cookie = (master_cookie *)_cookie; + TRACE(("master_ioctl: cookie %p, op %lu, buffer %p, length %lu\n", _cookie, op, buffer, length)); - return B_BAD_VALUE; + return tty_ioctl(cookie->tty, op, buffer, length); } static status_t master_read(void *_cookie, off_t offset, void *buffer, size_t *_length) { + master_cookie *cookie = (master_cookie *)_cookie; + TRACE(("master_read: cookie %p, offset %Ld, buffer %p, length %lu\n", _cookie, offset, buffer, *_length)); - return B_BAD_VALUE; + return tty_input_read(cookie->tty, buffer, _length, cookie->open_mode); } static status_t master_write(void *_cookie, off_t offset, const void *buffer, size_t *_length) { + master_cookie *cookie = (master_cookie *)_cookie; + TRACE(("master_write: cookie %p, offset %Ld, buffer %p, length %lu\n", _cookie, offset, buffer, *_length)); - return B_BAD_VALUE; + return tty_write_to_tty(cookie->tty, cookie->slave, buffer, _length, cookie->open_mode, true); } static status_t master_select(void *_cookie, uint8 event, uint32 ref, selectsync *sync) { - return B_OK; + master_cookie *cookie = (master_cookie *)_cookie; + + return tty_select(cookie->tty, event, ref, sync); } static status_t master_deselect(void *_cookie, uint8 event, selectsync *sync) { - return B_OK; + master_cookie *cookie = (master_cookie *)_cookie; + + return tty_deselect(cookie->tty, event, sync); } diff --git a/src/add-ons/kernel/drivers/tty/slave.cpp b/src/add-ons/kernel/drivers/tty/slave.cpp index bd4e2be113..1c5c2b8be3 100644 --- a/src/add-ons/kernel/drivers/tty/slave.cpp +++ b/src/add-ons/kernel/drivers/tty/slave.cpp @@ -19,6 +19,7 @@ struct slave_cookie { struct tty *tty; + struct tty *master; uint32 open_mode; }; @@ -31,16 +32,32 @@ slave_open(const char *name, uint32 flags, void **_cookie) { int32 index = get_tty_index(name); - if (gSlaveTTYs[index].open_count == 0) + // we may only be used if our master has already been opened + if (gMasterTTYs[index].open_count == 0) return B_IO_ERROR; - atomic_add(&gSlaveTTYs[index].open_count, 1); + if (atomic_add(&gSlaveTTYs[index].open_count, 1) == 0) { + // ToDo: this is broken and must be synchronized with all + // other open calls to that slave + // Also, tty_open() should probably be changed and always called in open(). + status_t status = tty_open(&gSlaveTTYs[index], NULL); + if (status < B_OK) { + // initializing TTY failed, reset open counter + atomic_add(&gSlaveTTYs[index].open_count, -1); + return status; + } + } slave_cookie *cookie = (slave_cookie *)malloc(sizeof(struct slave_cookie)); - if (cookie == NULL) + if (cookie == NULL) { + atomic_add(&gSlaveTTYs[index].open_count, -1); + tty_close(&gSlaveTTYs[index]); return B_NO_MEMORY; + } cookie->tty = &gSlaveTTYs[index]; + cookie->master = &gMasterTTYs[index]; + cookie->open_mode = flags; *_cookie = cookie; return B_OK; @@ -50,11 +67,6 @@ slave_open(const char *name, uint32 flags, void **_cookie) static status_t slave_close(void *_cookie) { - slave_cookie *cookie = (slave_cookie *)_cookie; - - TRACE(("slave_close: cookie %p\n", _cookie)); - - atomic_add(&cookie->tty->open_count, -1); return B_OK; } @@ -62,7 +74,14 @@ slave_close(void *_cookie) static status_t slave_free_cookie(void *_cookie) { - free(_cookie); + slave_cookie *cookie = (slave_cookie *)_cookie; + + TRACE(("slave_close: cookie %p\n", _cookie)); + + if (atomic_add(&cookie->tty->open_count, -1) == 1) + tty_close(cookie->tty); + + free(cookie); return B_OK; } @@ -70,24 +89,30 @@ slave_free_cookie(void *_cookie) static status_t slave_ioctl(void *_cookie, uint32 op, void *buffer, size_t length) { + slave_cookie *cookie = (slave_cookie *)_cookie; + TRACE(("slave_ioctl: cookie %p, op %lu, buffer %p, length %lu\n", _cookie, op, buffer, length)); - return B_BAD_VALUE; + return tty_ioctl(cookie->tty, op, buffer, length); } static status_t slave_read(void *_cookie, off_t offset, void *buffer, size_t *_length) { + slave_cookie *cookie = (slave_cookie *)_cookie; + TRACE(("slave_read: cookie %p, offset %Ld, buffer %p, length %lu\n", _cookie, offset, buffer, *_length)); - return B_BAD_VALUE; + return tty_input_read(cookie->tty, buffer, _length, cookie->open_mode); } static status_t slave_write(void *_cookie, off_t offset, const void *buffer, size_t *_length) { + slave_cookie *cookie = (slave_cookie *)_cookie; + TRACE(("slave_write: cookie %p, offset %Ld, buffer %p, length %lu\n", _cookie, offset, buffer, *_length)); - return B_BAD_VALUE; + return tty_write_to_tty(cookie->tty, cookie->master, buffer, _length, cookie->open_mode, false); } diff --git a/src/add-ons/kernel/drivers/tty/tty.cpp b/src/add-ons/kernel/drivers/tty/tty.cpp index 6ada190c33..4e639a21f5 100644 --- a/src/add-ons/kernel/drivers/tty/tty.cpp +++ b/src/add-ons/kernel/drivers/tty/tty.cpp @@ -3,9 +3,17 @@ ** Distributed under the terms of the Haiku License. */ +// 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 +#include +#include + #define TTY_TRACE #ifdef TTY_TRACE @@ -15,6 +23,217 @@ #endif +class WriterLocker { + public: + WriterLocker(struct tty *source, struct tty *target, bool echo, bool sourceIsMaster); + ~WriterLocker(); + + status_t AcquireWriter(bool dontBlock); + void ReportWritten(size_t written); + + private: + void Lock(); + void Unlock(); + + struct tty *fSource, *fTarget; + size_t fSourceBytes, fTargetBytes; + bool fEcho; +}; + +class ReaderLocker { + public: + ReaderLocker(struct tty *tty); + ~ReaderLocker(); + + status_t AcquireReader(bool dontBlock); + void ReportRead(size_t bytesRead); + + private: + void Lock(); + void Unlock(); + + struct tty *fTTY; + size_t fBytes; +}; + +class MutexLocker { + public: + MutexLocker(struct mutex *mutex) + : fMutex(mutex) + { + mutex_lock(mutex); + } + + ~MutexLocker() + { + mutex_unlock(fMutex); + } + + private: + struct mutex *fMutex; +}; + + +WriterLocker::WriterLocker(struct tty *source, struct tty *target, bool echo, bool sourceIsMaster) + : + fSource(source), + fTarget(target), + fEcho(echo) +{ + if (echo) { + if (!sourceIsMaster) { + // just switch the two, we have to lock both of them anyway + fSource = target; + fTarget = source; + } + } + + Lock(); +} + + +WriterLocker::~WriterLocker() +{ + Unlock(); +} + + +void +WriterLocker::Lock() +{ + mutex_lock(&fSource->lock); + if (fEcho) + mutex_lock(&fTarget->lock); +} + + +void +WriterLocker::Unlock() +{ + if (fEcho) + mutex_unlock(&fTarget->lock); + + mutex_unlock(&fSource->lock); +} + + +status_t +WriterLocker::AcquireWriter(bool dontBlock) +{ + // Release TTY lock in order not to block. We only need to do this + // if we could have to wait for the buffer to become writable. + if (!dontBlock) + Unlock(); + + status_t status = acquire_sem_etc(fSource->write_sem, 1, (dontBlock ? B_TIMEOUT : 0) | B_CAN_INTERRUPT, 0); + if (status == B_OK && fEcho) { + // We need to hold two write semaphores in order to echo the output to + // the local TTY as well. We need to make sure that these semaphores + // are always acquired in the same order to prevent deadlocks + status = acquire_sem_etc(fTarget->write_sem, 1, (dontBlock ? B_TIMEOUT : 0) | B_CAN_INTERRUPT, 0); + if (status != B_OK) + release_sem(fSource->write_sem); + } + + // reacquire TTY lock + if (!dontBlock) + Lock(); + + if (status == B_OK) { + fSourceBytes = line_buffer_writable(fSource->input_buffer); + if (fEcho) + fTargetBytes = line_buffer_writable(fTarget->input_buffer); + } + return status; +} + + +void +WriterLocker::ReportWritten(size_t written) +{ + if (written < fSourceBytes) + release_sem_etc(fSource->write_sem, 1, fTarget ? B_DO_NOT_RESCHEDULE : 0); + + if (fEcho && written < fTargetBytes) + release_sem(fTarget->write_sem); + + // there is now probably something to read, too + + if (written > 0) { + release_sem(fTarget->read_sem); + if (fEcho) + release_sem(fSource->read_sem); + } +} + + +// #pragma mark - + + +ReaderLocker::ReaderLocker(struct tty *tty) + : + fTTY(tty) +{ + Lock(); +} + + +ReaderLocker::~ReaderLocker() +{ + Unlock(); +} + + +status_t +ReaderLocker::AcquireReader(bool dontBlock) +{ + // Release TTY lock in order not to block. We only need to do this + // if we could have to wait for the buffer to become readable. + if (!dontBlock) + Unlock(); + + status_t status = acquire_sem_etc(fTTY->read_sem, 1, (dontBlock ? B_TIMEOUT : 0) | B_CAN_INTERRUPT, 0); + + // reacquire TTY lock + if (!dontBlock) + Lock(); + + if (status == B_OK) + fBytes = line_buffer_readable(fTTY->input_buffer); + + return status; +} + + +void +ReaderLocker::ReportRead(size_t bytesRead) +{ + if (bytesRead < fBytes && bytesRead != 0) + release_sem(fTTY->read_sem); + + // there may be space to write too again + if (bytesRead > 0) + release_sem(fTTY->write_sem); +} + + +void +ReaderLocker::Lock() +{ + mutex_lock(&fTTY->lock); +} + + +void +ReaderLocker::Unlock() +{ + mutex_unlock(&fTTY->lock); +} + + +// #pragma mark - + + int32 get_tty_index(const char *name) { @@ -28,3 +247,298 @@ get_tty_index(const char *name) return (name[3] - 'p') * digit; } + +void +reset_termios(struct termios &termios) +{ + memset(&termios, 0, sizeof(struct termios)); + + termios.c_iflag = 0; + termios.c_oflag = OPOST; + termios.c_cflag = B19200 | CS8 | CREAD | HUPCL; + // enable receiver, hang up on last close + termios.c_lflag = 0; + + // control characters + termios.c_cc[VINTR] = CTRL('C'); + termios.c_cc[VQUIT] = CTRL('\\'); + termios.c_cc[VERASE] = CTRL('H'); + termios.c_cc[VKILL] = CTRL('U'); + termios.c_cc[VEOF] = CTRL('D'); + termios.c_cc[VSTART] = CTRL('S'); + termios.c_cc[VSTOP] = CTRL('Q'); +} + + +void +reset_tty(struct tty *tty, int32 index) +{ + reset_termios(tty->termios); + + tty->open_count = 0; + tty->index = index; + tty->pgrp_id = 0; + + tty->lock.sem = tty->read_sem = tty->write_sem = -1; + // the semaphores are only created when the TTY is actually in use + + // some initial window size - the TTY in question should set these values + tty->window_size.ws_col = 80; + tty->window_size.ws_row = 25; + tty->window_size.ws_xpixel = tty->window_size.ws_col * 8; + tty->window_size.ws_ypixel = tty->window_size.ws_row * 8; +} + + +status_t +tty_output_getc(struct tty *tty, int *_c) +{ +} + + +static void +tty_input_putc_locked(struct tty *tty, int c) +{ + line_buffer_putc(tty->input_buffer, c); +} + + +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); + + tty_input_putc_locked(tty, c); + + if (line_buffer_writable(tty->input_buffer)) + release_sem(tty->write_sem); +} + + +// #pragma mark - +// device functions + + +status_t +tty_close(struct tty *tty) +{ + mutex_destroy(&tty->lock); + delete_sem(tty->write_sem); + delete_sem(tty->read_sem); + + uninit_line_buffer(tty->input_buffer); + + return B_OK; +} + + +status_t +tty_open(struct tty *tty, tty_service_func func) +{ + status_t status; + + if (init_line_buffer(tty->input_buffer, TTY_BUFFER_SIZE) < B_OK) + return B_NO_MEMORY; + + if ((status = mutex_init(&tty->lock, "tty lock")) < B_OK) + goto err1; + + tty->write_sem = create_sem(1, "tty write"); + if (tty->write_sem < B_OK) { + status = tty->write_sem; + goto err2; + } + + tty->read_sem = create_sem(0, "tty read"); + if (tty->read_sem < B_OK) { + status = tty->read_sem; + goto err3; + } + + tty->service_func = func; + + return B_OK; + +err3: + delete_sem(tty->write_sem); +err2: + mutex_destroy(&tty->lock); +err1: + uninit_line_buffer(tty->input_buffer); +} + + +status_t +tty_ioctl(struct tty *tty, uint32 op, void *buffer, size_t length) +{ + TRACE(("tty_ioctl: tty %p, op %lu, buffer %p, length %lu\n", &tty, op, buffer, length)); + MutexLocker locker(&tty->lock); + + switch (op) { + /* get and set TTY attributes */ + + case TCGETA: + TRACE(("tty: get attributes\n")); + return user_memcpy(buffer, &tty->termios, sizeof(struct termios)); + + case TCSETA: + case TCSETAW: + case TCSETAF: + TRACE(("tty: set attributes\n")); + return user_memcpy(&tty->termios, buffer, sizeof(struct termios)); + + /* get and set process group ID */ + + case TIOCGPGRP: + TRACE(("tty: get pgrp_id\n")); + return user_memcpy(buffer, &tty->pgrp_id, sizeof(pid_t)); + case TIOCSPGRP: + case 'pgid': + TRACE(("tty: set pgrp_id\n")); + return user_memcpy(&tty->pgrp_id, buffer, sizeof(pid_t)); + + /* get and set window size */ + + case TIOCGWINSZ: + TRACE(("tty: set window size\n")); + return user_memcpy(buffer, &tty->window_size, sizeof(struct winsize)); + + case TIOCSWINSZ: + { + uint16 oldColumns = tty->window_size.ws_col, oldRows = tty->window_size.ws_row; + + TRACE(("tty: set window size\n")); + if (user_memcpy(&tty->window_size, buffer, sizeof(struct winsize)) < B_OK) + return B_BAD_ADDRESS; + + // send a signal only if the window size has changed + if ((oldColumns != tty->window_size.ws_col || oldRows != tty->window_size.ws_row) + && tty->pgrp_id != 0) + send_signal(-tty->pgrp_id, SIGWINCH); + + return B_OK; + } + } + + TRACE(("tty: unsupported opcode %lu\n", op)); + return B_BAD_VALUE; +} + + +status_t +tty_input_read(struct tty *tty, void *buffer, size_t *_length, uint32 mode) +{ + bool dontBlock = (mode & O_NONBLOCK) != 0; + size_t length = *_length; + size_t bytesLeft = length; + + if (length == 0) + return B_OK; + + ReaderLocker locker(tty); + + while (bytesLeft > 0) { + status_t status = locker.AcquireReader(dontBlock); + if (status != B_OK) { + *_length -= bytesLeft; + return status; + } + + ssize_t bytesRead = line_buffer_user_read(tty->input_buffer, (char *)buffer, bytesLeft); + if (bytesRead < B_OK) { + *_length = 0; + locker.ReportRead(0); + + return bytesRead; + } + + bytesLeft -= bytesRead; + + locker.ReportRead(bytesRead); + } + + return B_OK; +} + + +status_t +tty_write_to_tty(struct tty *source, struct tty *target, const void *buffer, size_t *_length, + uint32 mode, bool sourceIsMaster) +{ + const char *data = (const char *)buffer; + size_t length = *_length; + size_t bytesWritten = 0; + bool dontBlock = (mode & O_NONBLOCK) != 0; + bool echo = (source->termios.c_lflag & ECHO) != 0; + + // ToDo: "buffer" is not yet copied or accessed in a safe way! + + if (length == 0) + return B_OK; + + WriterLocker locker(source, target, echo, sourceIsMaster); + + while (bytesWritten < length) { + status_t status = locker.AcquireWriter(dontBlock); + if (status != B_OK) { + *_length = bytesWritten; + return status; + } + + size_t writable = line_buffer_writable(target->input_buffer); + if (writable > length) + writable = length; + + if (echo) { + size_t locallyWritable = line_buffer_writable(source->input_buffer); + if (locallyWritable < writable) + writable = locallyWritable; + } + + if (writable == 0) { + locker.ReportWritten(0); + continue; + } + + while (writable > bytesWritten) { + char c = data[0]; + + if (c == '\n' && (source->termios.c_oflag & (OPOST | ONLCR)) == OPOST | ONLCR) { + // post-process output and transfrom '\n' to '\r\n' + tty_input_putc_locked(target, '\r'); + if (echo) + tty_input_putc_locked(source, '\r'); + } + + tty_input_putc_locked(target, c); + if (echo) + tty_input_putc_locked(source, c); + + data++; + bytesWritten++; + } + + locker.ReportWritten(bytesWritten); + } + + return B_OK; +} + + +status_t +tty_select(struct tty *tty, uint8 event, uint32 ref, selectsync *sync) +{ + return B_OK; +} + + +status_t +tty_deselect(struct tty *tty, uint8 event, selectsync *sync) +{ + return B_OK; +} + diff --git a/src/add-ons/kernel/drivers/tty/tty_private.h b/src/add-ons/kernel/drivers/tty/tty_private.h index d95b78be96..86097198e6 100644 --- a/src/add-ons/kernel/drivers/tty/tty_private.h +++ b/src/add-ons/kernel/drivers/tty/tty_private.h @@ -8,12 +8,33 @@ #include #include +#include #include +#include "line_buffer.h" + + +#define TTY_BUFFER_SIZE 4096 + +// The only nice Rico'ism :) +#define CTRL(c) ((c) - 0100) + + +typedef status_t (*tty_service_func)(struct tty *tty, uint32 op); + // not yet used... struct tty { - int32 open_count; + int32 open_count; + int32 index; + struct mutex lock; + sem_id read_sem; + sem_id write_sem; + pid_t pgrp_id; + line_buffer input_buffer; + tty_service_func service_func; + struct termios termios; + struct winsize window_size; }; static const uint32 kNumTTYs = 64; @@ -25,6 +46,20 @@ extern device_hooks gMasterTTYHooks; extern device_hooks gSlaveTTYHooks; +// functions available for master/slave TTYs + extern int32 get_tty_index(const char *name); +extern void reset_tty(struct tty *tty, int32 index); +extern status_t tty_input_putc(struct tty *tty, int c); +extern status_t tty_input_read(struct tty *tty, void *buffer, size_t *_length, uint32 mode); +extern status_t tty_output_getc(struct tty *tty, int *_c); +extern status_t tty_write_to_tty(struct tty *tty, struct tty *dest, const void *buffer, + size_t *_length, uint32 mode, bool sourceIsMaster); + +extern status_t tty_open(struct tty *tty, tty_service_func func); +extern status_t tty_close(struct tty *tty); +extern status_t tty_ioctl(struct tty *tty, uint32 op, void *buffer, size_t length); +extern status_t tty_select(struct tty *tty, uint8 event, uint32 ref, selectsync *sync); +extern status_t tty_deselect(struct tty *tty, uint8 event, selectsync *sync); #endif /* TTY_PRIVATE_H */