Author: mmlr Date: 2010-12-08 01:24:12 +0100 (Wed, 08 Dec 2010) New Revision: 39760 Changeset: http://dev.haiku-os.org/changeset/39760 Added: haiku/trunk/headers/os/drivers/tty/tty_module.h haiku/trunk/src/add-ons/kernel/generic/tty/ haiku/trunk/src/add-ons/kernel/generic/tty/Jamfile haiku/trunk/src/add-ons/kernel/generic/tty/line_buffer.cpp haiku/trunk/src/add-ons/kernel/generic/tty/line_buffer.h haiku/trunk/src/add-ons/kernel/generic/tty/module.cpp haiku/trunk/src/add-ons/kernel/generic/tty/tty.cpp haiku/trunk/src/add-ons/kernel/generic/tty/tty_private.h Modified: haiku/trunk/src/add-ons/kernel/generic/Jamfile Log: Adding a generic tty module based largely on the tty driver. This has a new API and doesn't come with BeOS backwards compatibility. It also has the BeOS compatibility ioctl ops removed and such. I've actually made this back in april, so I don't really remember any more details. Copied: haiku/trunk/headers/os/drivers/tty/tty_module.h (from rev 36866, haiku/trunk/headers/os/drivers/tty/ttylayer.h) =================================================================== --- haiku/trunk/headers/os/drivers/tty/tty_module.h (rev 0) +++ haiku/trunk/headers/os/drivers/tty/tty_module.h 2010-12-08 00:24:12 UTC (rev 39760) @@ -0,0 +1,73 @@ +/* + * Copyright 2010, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + +#ifndef _TTY_MODULE_H +#define _TTY_MODULE_H + +#include <module.h> +#include <termios.h> +#include <Select.h> + +struct tty; +struct tty_cookie; + +typedef bool (*tty_service_func)(struct tty *tty, uint32 op, void *buffer, + size_t length); + +// flags +#define TTYCARRIER (1 << 0) +#define TTYWRITABLE (1 << 1) +#define TTYWRITING (1 << 2) +#define TTYREADING (1 << 3) +#define TTYOSTOPPED (1 << 4) +#define TTYEXCLUSIVE (1 << 5) +#define TTYHWDCD (1 << 6) +#define TTYHWCTS (1 << 7) +#define TTYHWDSR (1 << 8) +#define TTYHWRI (1 << 9) +#define TTYFLOWFORCED (1 << 10) + +// ops +#define TTYENABLE 0 /* bool enabled */ +#define TTYSETMODES 1 /* struct termios termios */ +#define TTYOSTART 2 +#define TTYOSYNC 3 +#define TTYISTOP 4 /* bool stopInput */ +#define TTYSETBREAK 5 /* bool break */ +#define TTYSETDTR 6 /* bool dataTerminalReady */ +#define TTYSETRTS 7 /* bool requestToSend */ +#define TTYGETSIGNALS 8 /* call tty_hardware_signal for all bits */ + +typedef struct tty_module_info tty_module_info; + +struct tty_module_info { + module_info mi; + + struct tty *(*tty_create)(tty_service_func serviceFunction, bool isMaster); + void (*tty_destroy)(struct tty *tty); + + struct tty_cookie * + (*tty_create_cookie)(struct tty *masterTTY, struct tty *slaveTTY, + uint32 openMode); + void (*tty_destroy_cookie)(struct tty_cookie *cookie); + + status_t (*tty_read)(struct tty_cookie *cookie, void *_buffer, + size_t *_length); + status_t (*tty_write)(struct tty_cookie *cookie, const void *buffer, + size_t *length); + status_t (*tty_control)(struct tty_cookie *cookie, uint32 op, + void *buffer, size_t length); + status_t (*tty_select)(struct tty_cookie *cookie, uint8 event, + uint32 ref, selectsync *sync); + status_t (*tty_deselect)(struct tty_cookie *cookie, uint8 event, + selectsync *sync); + + status_t (*tty_hardware_signal)(struct tty_cookie *cookie, + int signal, bool); +}; + +#define B_TTY_MODULE_NAME "generic/tty/v1" + +#endif /* _TTY_MODULE_H */ Modified: haiku/trunk/src/add-ons/kernel/generic/Jamfile =================================================================== --- haiku/trunk/src/add-ons/kernel/generic/Jamfile 2010-12-08 00:11:01 UTC (rev 39759) +++ haiku/trunk/src/add-ons/kernel/generic/Jamfile 2010-12-08 00:24:12 UTC (rev 39760) @@ -1,4 +1,4 @@ -SubDir HAIKU_TOP src add-ons kernel generic ; +SubDir HAIKU_TOP src add-ons kernel generic ; SubInclude HAIKU_TOP src add-ons kernel generic ata_adapter ; SubInclude HAIKU_TOP src add-ons kernel generic atomizer ; @@ -7,3 +7,4 @@ SubInclude HAIKU_TOP src add-ons kernel generic locked_pool ; SubInclude HAIKU_TOP src add-ons kernel generic mpu401 ; SubInclude HAIKU_TOP src add-ons kernel generic scsi_periph ; +SubInclude HAIKU_TOP src add-ons kernel generic tty ; Added: haiku/trunk/src/add-ons/kernel/generic/tty/Jamfile =================================================================== --- haiku/trunk/src/add-ons/kernel/generic/tty/Jamfile (rev 0) +++ haiku/trunk/src/add-ons/kernel/generic/tty/Jamfile 2010-12-08 00:24:12 UTC (rev 39760) @@ -0,0 +1,10 @@ +SubDir HAIKU_TOP src add-ons kernel generic tty ; + +UsePrivateKernelHeaders ; +UsePrivateHeaders drivers ; + +KernelAddon <module>tty : + line_buffer.cpp + module.cpp + tty.cpp +; Copied: haiku/trunk/src/add-ons/kernel/generic/tty/line_buffer.cpp (from rev 36318, haiku/trunk/src/add-ons/kernel/drivers/tty/line_buffer.cpp) =================================================================== --- haiku/trunk/src/add-ons/kernel/generic/tty/line_buffer.cpp (rev 0) +++ haiku/trunk/src/add-ons/kernel/generic/tty/line_buffer.cpp 2010-12-08 00:24:12 UTC (rev 39760) @@ -0,0 +1,169 @@ +/* + * Copyright 2004, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "line_buffer.h" + +#include <KernelExport.h> +#include <stdlib.h> + + +status_t +clear_line_buffer(struct line_buffer &buffer) +{ + buffer.in = 0; + buffer.first = 0; + return B_OK; +} + + +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_readable_line(struct line_buffer &buffer, char eol, char eof) +{ + size_t size = buffer.in; + if (size == 0) + return 0; + + // find EOL or EOF char + for (size_t i = 0; i < size; i++) { + char c = buffer.buffer[(buffer.first + i) % buffer.size]; + if (c == eol || c == '\n' || c == '\r' || c == eof) + return i + 1; + } + + // If the buffer is full, but doesn't contain a EOL or EOF, we report the + // full size anyway, since otherwise the reader would wait forever. + return buffer.in == buffer.size ? buffer.in : 0; +} + + +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, + char eof, bool* hitEOF) +{ + size_t available = buffer.in; + + if (length > available) + length = available; + + if (length == 0) + return 0; + + // check for EOF, if the caller asked us to + if (hitEOF) { + *hitEOF = false; + for (size_t i = 0; i < available; i++) { + char c = buffer.buffer[(buffer.first + i) % buffer.size]; + if (c == eof) { + *hitEOF = true; + length = i; + break; + } + } + } + + 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; + buffer.in -= bytesRead; + } + + // dispose of EOF char + if (hitEOF && *hitEOF) { + buffer.first = (buffer.first + 1) % buffer.size; + buffer.in--; + } + + 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; + return B_OK; +} + + +#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 + + +bool +line_buffer_tail_getc(struct line_buffer &buffer, char *c) +{ + if (buffer.in == 0) + return false; + + *c = buffer.buffer[(buffer.first + --buffer.in) % buffer.size]; + return true; +} Copied: haiku/trunk/src/add-ons/kernel/generic/tty/line_buffer.h (from rev 36318, haiku/trunk/src/add-ons/kernel/drivers/tty/line_buffer.h) =================================================================== --- haiku/trunk/src/add-ons/kernel/generic/tty/line_buffer.h (rev 0) +++ haiku/trunk/src/add-ons/kernel/generic/tty/line_buffer.h 2010-12-08 00:24:12 UTC (rev 39760) @@ -0,0 +1,32 @@ +/* +** Copyright 2004, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx All rights reserved. +** Distributed under the terms of the Haiku License. +*/ +#ifndef LINE_BUFFER_H +#define LINE_BUFFER_H + + +#include <OS.h> + + +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 clear_line_buffer(struct line_buffer &buffer); +int32 line_buffer_readable(struct line_buffer &buffer); +int32 line_buffer_readable_line(struct line_buffer &buffer, char eol, char eof); +int32 line_buffer_writable(struct line_buffer &buffer); +ssize_t line_buffer_user_read(struct line_buffer &buffer, char *data, + size_t length, char eof = 0, bool* hitEOF = NULL); +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); +bool line_buffer_tail_getc(struct line_buffer &buffer, char *c); + +#endif /* LINE_BUFFER_H */ Added: haiku/trunk/src/add-ons/kernel/generic/tty/module.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/generic/tty/module.cpp (rev 0) +++ haiku/trunk/src/add-ons/kernel/generic/tty/module.cpp 2010-12-08 00:24:12 UTC (rev 39760) @@ -0,0 +1,87 @@ +/* + * Copyright 2010, Michael Lotz, mmlr@xxxxxxxxx + * Copyright 2004, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx + * Distributed under the terms of the Haiku License. + */ + +#include <new> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <lock.h> + +#include <tty/tty_module.h> + +#include "tty_private.h" + +struct mutex gGlobalTTYLock; +struct mutex gTTYCookieLock; +struct recursive_lock gTTYRequestLock; + + +static status_t +init_tty_module() +{ + // create the request mutex + recursive_lock_init(&gTTYRequestLock, "tty requests"); + + // create the global mutex + mutex_init(&gGlobalTTYLock, "tty global"); + + // create the cookie mutex + mutex_init(&gTTYCookieLock, "tty cookies"); + + return B_OK; +} + + +static void +uninit_tty_module() +{ + recursive_lock_destroy(&gTTYRequestLock); + mutex_destroy(&gTTYCookieLock); + mutex_destroy(&gGlobalTTYLock); +} + + +static int32 +tty_module_std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + return init_tty_module(); + + case B_MODULE_UNINIT: + uninit_tty_module(); + return B_OK; + } + + return B_BAD_VALUE; +} + + +static struct tty_module_info sTTYModule = { + { + B_TTY_MODULE_NAME, + 0, //B_KEEP_LOADED, + tty_module_std_ops + }, + &tty_create, + &tty_destroy, + &tty_create_cookie, + &tty_destroy_cookie, + &tty_read, + &tty_write, + &tty_control, + &tty_select, + &tty_deselect, + &tty_hardware_signal +}; + + +module_info *modules[] = { + (module_info *)&sTTYModule, + NULL +}; Copied: haiku/trunk/src/add-ons/kernel/generic/tty/tty.cpp (from rev 36318, haiku/trunk/src/add-ons/kernel/drivers/tty/tty.cpp) =================================================================== --- haiku/trunk/src/add-ons/kernel/generic/tty/tty.cpp (rev 0) +++ haiku/trunk/src/add-ons/kernel/generic/tty/tty.cpp 2010-12-08 00:24:12 UTC (rev 39760) @@ -0,0 +1,1973 @@ +/* + * Copyright 2007-2010, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Copyright 2004-2009, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx + * Distributed under the terms of the MIT 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 <ctype.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <util/AutoLock.h> +#include <util/kernel_cpp.h> + +#include <team.h> + +#include <tty.h> + + +//#define TTY_TRACE +#ifdef TTY_TRACE +# define TRACE(x) dprintf x +#else +# define TRACE(x) ; +#endif + + +/* + Locking + ------- + + There are four locks involved. If more than one needs to be held at a + time, they must be acquired in the order they are listed here. + + gGlobalTTYLock: Guards open/close operations. When held, tty_open(), + tty_close(), tty_close_cookie() etc. won't be invoked by other threads, + cookies won't be added to/removed from TTYs, and tty::ref_count, + tty::open_count, tty_cookie::closed won't change. + + gTTYCookieLock: Guards the access to the fields + tty_cookie::{thread_count,closed}, or more precisely makes access to them + atomic. thread_count is the number of threads currently using the cookie + (i.e. read(), write(), ioctl() operations in progress). Together with + blocking_semaphore this serves the purpose to make sure that all pending + operations are done at a certain point when closing a cookie + (cf. tty_close_cookie() and TTYReference). + + tty::lock: Guards the access to tty::{input_buffer,settings::{termios, + window_size,pgrp_id}}. Moreover when held guarantees that tty::open_count + won't drop to zero (both gGlobalTTYLock and tty::lock must be held to + decrement it). A tty and the tty connected to it (master and slave) share + the same lock. + + gTTYRequestLock: Guards access to tty::{reader,writer}_queue (most + RequestQueue methods do the locking themselves (the lock is a + recursive_lock)), queued Requests and associated RequestOwners. + + + Reading/Writing + --------------- + + Most of the dirty work when dealing with reading/writing is done by the + {Reader,Writer}Locker classes. Upon construction they lock the tty, + (tty::lock) create a RequestOwner and queue Requests in the respective + reader/writer queues (tty::{reader,writer}_queue). The + Acquire{Reader,Writer}() methods need to be called before being allowed to + read/write. They ensure that there is actually something to read/space for + writing -- in blocking mode they wait, if necessary. When destroyed the + {Reader,Writer}Locker() remove the formerly enqueued Requests and notify + waiting reader/writer and/or send out select events, whatever is appropiate. + + Acquire{Reader,Writer}() never return without an actual event being + occurred. Either an error has occurred (return value) -- in this case the + caller should terminate -- or bytes are available for reading/space for + writing (cf. AvailableBytes()). +*/ + + +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); + + +class AbstractLocker { +public: + AbstractLocker(tty_cookie* cookie) + : + fCookie(cookie), + fBytes(0) + { + } + + size_t AvailableBytes() const + { return fBytes; } + +protected: + void Lock() + { mutex_lock(&fCookie->tty->lock); } + void Unlock() + { mutex_unlock(&fCookie->tty->lock); } + + tty_cookie* fCookie; + size_t fBytes; +}; + + +class WriterLocker : public AbstractLocker { +public: + WriterLocker(tty_cookie* sourceCookie); + ~WriterLocker(); + + status_t AcquireWriter(bool dontBlock, + size_t bytesNeeded); + +private: + size_t _CheckAvailableBytes() const; + + struct tty* fSource; + struct tty* fTarget; + RequestOwner fRequestOwner; + bool fEcho; +}; + + +class ReaderLocker : public AbstractLocker { +public: + ReaderLocker(tty_cookie* cookie); + ~ReaderLocker(); + + status_t AcquireReader(bigtime_t timeout, + size_t bytesNeeded); + +private: + size_t _CheckAvailableBytes() const; + + struct tty* fTTY; + RequestOwner fRequestOwner; +}; + + +class TTYReferenceLocking { +public: + inline bool Lock(tty_cookie* cookie) + { + MutexLocker _(gTTYCookieLock); + + if (cookie->closed) + return false; + + cookie->thread_count++; + + return true; + } + + inline void Unlock(tty_cookie* cookie) + { + MutexLocker locker(gTTYCookieLock); + + sem_id semaphore = -1; + if (--cookie->thread_count == 0 && cookie->closed) + semaphore = cookie->blocking_semaphore; + + locker.Unlock(); + + if (semaphore >= 0) { + TRACE(("TTYReference: cookie %p closed, last operation done, " + "releasing blocking sem %ld\n", cookie, semaphore)); + + release_sem(semaphore); + } + } +}; + +typedef AutoLocker<tty_cookie, TTYReferenceLocking> TTYReference; + + +// #pragma mark - + + +Request::Request() + : + fOwner(NULL), + fCookie(NULL), + fBytesNeeded(0), + fNotified(false), + fError(false) +{ +} + + +void +Request::Init(RequestOwner* owner, tty_cookie* cookie, size_t bytesNeeded) +{ + fOwner = owner; + fCookie = cookie; + fBytesNeeded = bytesNeeded; + fNotified = false; + fError = false; +} + + +void +Request::Notify(size_t bytesAvailable) +{ + if (!fNotified && bytesAvailable >= fBytesNeeded && fOwner) { + fOwner->Notify(this); + fNotified = true; + } +} + + +void +Request::NotifyError(status_t error) +{ + if (!fError && fOwner) { + fOwner->NotifyError(this, error); + fError = true; + fNotified = true; + } +} + + +void +Request::Dump(const char* prefix) +{ + kprintf("%srequest: %p\n", prefix, this); + kprintf("%s owner: %p\n", prefix, fOwner); + kprintf("%s cookie: %p\n", prefix, fCookie); + kprintf("%s bytes needed: %lu\n", prefix, fBytesNeeded); + kprintf("%s notified: %s\n", prefix, fNotified ? "true" : "false"); + kprintf("%s error: %s\n", prefix, fError ? "true" : "false"); +} + + +// #pragma mark - + + +RequestQueue::RequestQueue() + : + fRequests() +{ +} + + +void +RequestQueue::Add(Request* request) +{ + if (request) { + RecursiveLocker _(gTTYRequestLock); + + fRequests.Add(request, true); + } +} + + +void +RequestQueue::Remove(Request* request) +{ + if (request) { + RecursiveLocker _(gTTYRequestLock); + + fRequests.Remove(request); + } +} + + +void +RequestQueue::NotifyFirst(size_t bytesAvailable) +{ + RecursiveLocker _(gTTYRequestLock); + + if (Request* first = First()) + first->Notify(bytesAvailable); +} + + +void +RequestQueue::NotifyError(status_t error) +{ + RecursiveLocker _(gTTYRequestLock); + + for (RequestList::Iterator it = fRequests.GetIterator(); it.HasNext();) { + Request* request = it.Next(); + request->NotifyError(error); + } +} + + +void +RequestQueue::NotifyError(tty_cookie* cookie, status_t error) +{ + RecursiveLocker _(gTTYRequestLock); + + for (RequestList::Iterator it = fRequests.GetIterator(); it.HasNext();) { + Request* request = it.Next(); + if (request->TTYCookie() == cookie) + request->NotifyError(error); + } +} + + +void +RequestQueue::Dump(const char* prefix) +{ + RequestList::Iterator it = fRequests.GetIterator(); + while (Request* request = it.Next()) + request->Dump(prefix); +} + + +// #pragma mark - + + +RequestOwner::RequestOwner() + : + fConditionVariable(NULL), + fCookie(NULL), + fError(B_OK), + fBytesNeeded(1) +{ + fRequestQueues[0] = NULL; + fRequestQueues[1] = NULL; +} + + +/*! The caller must already hold the request lock. +*/ +void +RequestOwner::Enqueue(tty_cookie* cookie, RequestQueue* queue1, + RequestQueue* queue2) +{ + TRACE(("%p->RequestOwner::Enqueue(%p, %p, %p)\n", this, cookie, queue1, + queue2)); + + fCookie = cookie; + + fRequestQueues[0] = queue1; + fRequestQueues[1] = queue2; + + fRequests[0].Init(this, cookie, fBytesNeeded); + if (queue1) + queue1->Add(&fRequests[0]); + else + fRequests[0].Notify(fBytesNeeded); + + fRequests[1].Init(this, cookie, fBytesNeeded); + if (queue2) + queue2->Add(&fRequests[1]); + else + fRequests[1].Notify(fBytesNeeded); +} + + +/*! The caller must already hold the request lock. +*/ +void +RequestOwner::Dequeue() +{ + TRACE(("%p->RequestOwner::Dequeue()\n", this)); + + if (fRequestQueues[0]) + fRequestQueues[0]->Remove(&fRequests[0]); + if (fRequestQueues[1]) + fRequestQueues[1]->Remove(&fRequests[1]); + + fRequestQueues[0] = NULL; + fRequestQueues[1] = NULL; +} + + +void +RequestOwner::SetBytesNeeded(size_t bytesNeeded) +{ + if (fRequestQueues[0]) + fRequests[0].Init(this, fCookie, bytesNeeded); + + if (fRequestQueues[1]) + fRequests[1].Init(this, fCookie, bytesNeeded); +} + + +/*! The request lock MUST NOT be held! +*/ +status_t +RequestOwner::Wait(bool interruptable, bigtime_t timeout) +{ + TRACE(("%p->RequestOwner::Wait(%d)\n", this, interruptable)); + + status_t error = B_OK; + + RecursiveLocker locker(gTTYRequestLock); + + // check, if already done + if (fError == B_OK + && (!fRequests[0].WasNotified() || !fRequests[1].WasNotified())) { + // not yet done + + // publish the condition variable + ConditionVariable conditionVariable; + conditionVariable.Init(this, "tty request"); + fConditionVariable = &conditionVariable; + + // add an entry to wait on + ConditionVariableEntry entry; + conditionVariable.Add(&entry); + + locker.Unlock(); + + // wait + TRACE(("%p->RequestOwner::Wait(): waiting for condition...\n", this)); + + error = entry.Wait( + (interruptable ? B_CAN_INTERRUPT : 0) | B_RELATIVE_TIMEOUT, + timeout); + + TRACE(("%p->RequestOwner::Wait(): condition occurred: %lx\n", this, + error)); + + // remove the condition variable + locker.Lock(); + fConditionVariable = NULL; + } + + // get the result + if (error == B_OK) + error = fError; + + return error; +} + + +bool +RequestOwner::IsFirstInQueues() +{ + RecursiveLocker locker(gTTYRequestLock); + + for (int i = 0; i < 2; i++) { + if (fRequestQueues[i] && fRequestQueues[i]->First() != &fRequests[i]) + return false; + } + + return true; +} + + +void +RequestOwner::Notify(Request* request) +{ + TRACE(("%p->RequestOwner::Notify(%p)\n", this, request)); + + if (fError == B_OK && !request->WasNotified()) { + bool notify = false; + + if (&fRequests[0] == request) { + notify = fRequests[1].WasNotified(); + } else if (&fRequests[1] == request) { + notify = fRequests[0].WasNotified(); + } else { + // spurious call + } + + if (notify && fConditionVariable) + fConditionVariable->NotifyOne(); + } +} + + +void +RequestOwner::NotifyError(Request* request, status_t error) +{ + TRACE(("%p->RequestOwner::NotifyError(%p, %lx)\n", this, request, error)); + + if (fError == B_OK) { + fError = error; + + if (!fRequests[0].WasNotified() || !fRequests[1].WasNotified()) { + if (fConditionVariable) + fConditionVariable->NotifyOne(); + } + } +} + + +// #pragma mark - + + +WriterLocker::WriterLocker(tty_cookie* sourceCookie) + : + AbstractLocker(sourceCookie), + fSource(fCookie->tty), + fTarget(fCookie->other_tty), + fRequestOwner(), + fEcho(false) +{ + Lock(); + + // Now that the tty pair is locked, we can check, whether the target is + // open at all. + if (fTarget->open_count > 0) { + // The target tty is open. As soon as we have appended a request to + // the writer queue of the target, it is guaranteed to remain valid + // until we have removed the request (and notified the + // tty_close_cookie() pseudo request). + + // get the echo mode + fEcho = (fSource->is_master + && fSource->settings.termios.c_lflag & ECHO) != 0; + + // enqueue ourselves in the respective request queues + RecursiveLocker locker(gTTYRequestLock); + fRequestOwner.Enqueue(fCookie, &fTarget->writer_queue, + (fEcho ? &fSource->writer_queue : NULL)); + } else { + // target is not open: we set it to NULL; all further operations on + // this locker will fail + fTarget = NULL; + } +} + + +WriterLocker::~WriterLocker() +{ + // dequeue from request queues + RecursiveLocker locker(gTTYRequestLock); + fRequestOwner.Dequeue(); + + // check the tty queues and notify the next in line, and send out select + // events + if (fTarget) + tty_notify_if_available(fTarget, fSource, true); + if (fEcho) + tty_notify_if_available(fSource, fTarget, true); + + locker.Unlock(); + + Unlock(); +} + + +size_t +WriterLocker::_CheckAvailableBytes() const +{ + size_t writable = line_buffer_writable(fTarget->input_buffer); + if (fEcho) { + // we can only write as much as is available on both ends + size_t locallyWritable = line_buffer_writable(fSource->input_buffer); + if (locallyWritable < writable) + writable = locallyWritable; + } + return writable; +} + + +status_t +WriterLocker::AcquireWriter(bool dontBlock, size_t bytesNeeded) +{ + if (!fTarget) + return B_FILE_ERROR; + if (fEcho && fCookie->closed) + return B_FILE_ERROR; + + RecursiveLocker requestLocker(gTTYRequestLock); + + // check, if we're first in queue, and if there is space to write + if (fRequestOwner.IsFirstInQueues()) { + fBytes = _CheckAvailableBytes(); [... truncated: 1571 lines follow ...]