[haiku-commits] r39760 - in haiku/trunk: headers/os/drivers/tty src/add-ons/kernel/generic src/add-ons/kernel/generic/tty

  • From: mmlr@xxxxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Wed, 8 Dec 2010 01:24:12 +0100 (CET)

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 ...]

Other related posts:

  • » [haiku-commits] r39760 - in haiku/trunk: headers/os/drivers/tty src/add-ons/kernel/generic src/add-ons/kernel/generic/tty - mmlr