Introduce a client (upward direction) operations struct for tty_port
clients. Initially supported operations are for receiving data and write
wake-up. This will allow for having clients other than an ldisc.

Convert the calls to the ldisc to use the client ops as the default
operations.

Signed-off-by: Rob Herring <r...@kernel.org>
Reviewed-By: Sebastian Reichel <s...@kernel.org>
Tested-By: Sebastian Reichel <s...@kernel.org>
---
v4:
- no change
v3:
- Restructured wakeup handling. We need to maintain the behavior of getting
  the tty ptr only once, otherwise the serial port can get in a bad state
  and stops responding.

v2:
- no change

 drivers/tty/tty_buffer.c | 17 +++--------------
 drivers/tty/tty_port.c   | 46 ++++++++++++++++++++++++++++++++++++++++------
 include/linux/tty.h      |  9 ++++++++-
 3 files changed, 51 insertions(+), 21 deletions(-)

diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index f4dc3e296dd5..4e7a4e9dcf4d 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -437,7 +437,7 @@ int tty_ldisc_receive_buf(struct tty_ldisc *ld, const 
unsigned char *p,
 EXPORT_SYMBOL_GPL(tty_ldisc_receive_buf);
 
 static int
-receive_buf(struct tty_ldisc *ld, struct tty_buffer *head, int count)
+receive_buf(struct tty_port *port, struct tty_buffer *head, int count)
 {
        unsigned char *p = char_buf_ptr(head, head->read);
        char          *f = NULL;
@@ -445,7 +445,7 @@ receive_buf(struct tty_ldisc *ld, struct tty_buffer *head, 
int count)
        if (~head->flags & TTYB_NORMAL)
                f = flag_buf_ptr(head, head->read);
 
-       return tty_ldisc_receive_buf(ld, p, f, count);
+       return port->client_ops->receive_buf(port, p, f, count);
 }
 
 /**
@@ -465,16 +465,6 @@ static void flush_to_ldisc(struct work_struct *work)
 {
        struct tty_port *port = container_of(work, struct tty_port, buf.work);
        struct tty_bufhead *buf = &port->buf;
-       struct tty_struct *tty;
-       struct tty_ldisc *disc;
-
-       tty = READ_ONCE(port->itty);
-       if (tty == NULL)
-               return;
-
-       disc = tty_ldisc_ref(tty);
-       if (disc == NULL)
-               return;
 
        mutex_lock(&buf->lock);
 
@@ -504,7 +494,7 @@ static void flush_to_ldisc(struct work_struct *work)
                        continue;
                }
 
-               count = receive_buf(disc, head, count);
+               count = receive_buf(port, head, count);
                if (!count)
                        break;
                head->read += count;
@@ -512,7 +502,6 @@ static void flush_to_ldisc(struct work_struct *work)
 
        mutex_unlock(&buf->lock);
 
-       tty_ldisc_deref(disc);
 }
 
 /**
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index 1d8804843103..8d9886b06037 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -17,6 +17,44 @@
 #include <linux/delay.h>
 #include <linux/module.h>
 
+static int tty_port_default_receive_buf(struct tty_port *port,
+                                       const unsigned char *p,
+                                       const unsigned char *f, size_t count)
+{
+       int ret;
+       struct tty_struct *tty;
+       struct tty_ldisc *disc;
+
+       tty = READ_ONCE(port->itty);
+       if (!tty)
+               return 0;
+
+       disc = tty_ldisc_ref(tty);
+       if (!disc)
+               return 0;
+
+       ret = tty_ldisc_receive_buf(disc, p, (char *)f, count);
+
+       tty_ldisc_deref(disc);
+
+       return ret;
+}
+
+static void tty_port_default_wakeup(struct tty_port *port)
+{
+       struct tty_struct *tty = tty_port_tty_get(port);
+
+       if (tty) {
+               tty_wakeup(tty);
+               tty_kref_put(tty);
+       }
+}
+
+static const struct tty_port_client_operations default_client_ops = {
+       .receive_buf = tty_port_default_receive_buf,
+       .write_wakeup = tty_port_default_wakeup,
+};
+
 void tty_port_init(struct tty_port *port)
 {
        memset(port, 0, sizeof(*port));
@@ -28,6 +66,7 @@ void tty_port_init(struct tty_port *port)
        spin_lock_init(&port->lock);
        port->close_delay = (50 * HZ) / 100;
        port->closing_wait = (3000 * HZ) / 100;
+       port->client_ops = &default_client_ops;
        kref_init(&port->kref);
 }
 EXPORT_SYMBOL(tty_port_init);
@@ -272,12 +311,7 @@ EXPORT_SYMBOL_GPL(tty_port_tty_hangup);
  */
 void tty_port_tty_wakeup(struct tty_port *port)
 {
-       struct tty_struct *tty = tty_port_tty_get(port);
-
-       if (tty) {
-               tty_wakeup(tty);
-               tty_kref_put(tty);
-       }
+       port->client_ops->write_wakeup(port);
 }
 EXPORT_SYMBOL_GPL(tty_port_tty_wakeup);
 
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 21c0fabfed60..1017e904c0a3 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -217,12 +217,18 @@ struct tty_port_operations {
        /* Called on the final put of a port */
        void (*destruct)(struct tty_port *port);
 };
-       
+
+struct tty_port_client_operations {
+       int (*receive_buf)(struct tty_port *port, const unsigned char *, const 
unsigned char *, size_t);
+       void (*write_wakeup)(struct tty_port *port);
+};
+
 struct tty_port {
        struct tty_bufhead      buf;            /* Locked internally */
        struct tty_struct       *tty;           /* Back pointer */
        struct tty_struct       *itty;          /* internal back ptr */
        const struct tty_port_operations *ops;  /* Port operations */
+       const struct tty_port_client_operations *client_ops; /* Port client 
operations */
        spinlock_t              lock;           /* Lock protecting tty field */
        int                     blocked_open;   /* Waiting to open */
        int                     count;          /* Usage count */
@@ -241,6 +247,7 @@ struct tty_port {
                                                   based drain is needed else
                                                   set to size of fifo */
        struct kref             kref;           /* Ref counter */
+       void                    *client_data;
 };
 
 /* tty_port::iflags bits -- use atomic bit ops */
-- 
2.10.1

Reply via email to