Commit c3485ee0 authored by Rob Herring's avatar Rob Herring Committed by Greg Kroah-Hartman

tty_port: Add port client functions

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: default avatarRob Herring <robh@kernel.org>
Reviewed-By: default avatarSebastian Reichel <sre@kernel.org>
Tested-By: default avatarSebastian Reichel <sre@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a380ed46
...@@ -437,7 +437,7 @@ int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p, ...@@ -437,7 +437,7 @@ int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p,
EXPORT_SYMBOL_GPL(tty_ldisc_receive_buf); EXPORT_SYMBOL_GPL(tty_ldisc_receive_buf);
static int 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); unsigned char *p = char_buf_ptr(head, head->read);
char *f = NULL; char *f = NULL;
...@@ -445,7 +445,7 @@ receive_buf(struct tty_ldisc *ld, struct tty_buffer *head, int count) ...@@ -445,7 +445,7 @@ receive_buf(struct tty_ldisc *ld, struct tty_buffer *head, int count)
if (~head->flags & TTYB_NORMAL) if (~head->flags & TTYB_NORMAL)
f = flag_buf_ptr(head, head->read); 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) ...@@ -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_port *port = container_of(work, struct tty_port, buf.work);
struct tty_bufhead *buf = &port->buf; 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); mutex_lock(&buf->lock);
...@@ -504,7 +494,7 @@ static void flush_to_ldisc(struct work_struct *work) ...@@ -504,7 +494,7 @@ static void flush_to_ldisc(struct work_struct *work)
continue; continue;
} }
count = receive_buf(disc, head, count); count = receive_buf(port, head, count);
if (!count) if (!count)
break; break;
head->read += count; head->read += count;
...@@ -512,7 +502,6 @@ static void flush_to_ldisc(struct work_struct *work) ...@@ -512,7 +502,6 @@ static void flush_to_ldisc(struct work_struct *work)
mutex_unlock(&buf->lock); mutex_unlock(&buf->lock);
tty_ldisc_deref(disc);
} }
/** /**
......
...@@ -17,6 +17,44 @@ ...@@ -17,6 +17,44 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.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) void tty_port_init(struct tty_port *port)
{ {
memset(port, 0, sizeof(*port)); memset(port, 0, sizeof(*port));
...@@ -28,6 +66,7 @@ void tty_port_init(struct tty_port *port) ...@@ -28,6 +66,7 @@ void tty_port_init(struct tty_port *port)
spin_lock_init(&port->lock); spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100; port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100; port->closing_wait = (3000 * HZ) / 100;
port->client_ops = &default_client_ops;
kref_init(&port->kref); kref_init(&port->kref);
} }
EXPORT_SYMBOL(tty_port_init); EXPORT_SYMBOL(tty_port_init);
...@@ -272,12 +311,7 @@ EXPORT_SYMBOL_GPL(tty_port_tty_hangup); ...@@ -272,12 +311,7 @@ EXPORT_SYMBOL_GPL(tty_port_tty_hangup);
*/ */
void tty_port_tty_wakeup(struct tty_port *port) void tty_port_tty_wakeup(struct tty_port *port)
{ {
struct tty_struct *tty = tty_port_tty_get(port); port->client_ops->write_wakeup(port);
if (tty) {
tty_wakeup(tty);
tty_kref_put(tty);
}
} }
EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); EXPORT_SYMBOL_GPL(tty_port_tty_wakeup);
......
...@@ -217,12 +217,18 @@ struct tty_port_operations { ...@@ -217,12 +217,18 @@ struct tty_port_operations {
/* Called on the final put of a port */ /* Called on the final put of a port */
void (*destruct)(struct tty_port *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_port {
struct tty_bufhead buf; /* Locked internally */ struct tty_bufhead buf; /* Locked internally */
struct tty_struct *tty; /* Back pointer */ struct tty_struct *tty; /* Back pointer */
struct tty_struct *itty; /* internal back ptr */ struct tty_struct *itty; /* internal back ptr */
const struct tty_port_operations *ops; /* Port operations */ 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 */ spinlock_t lock; /* Lock protecting tty field */
int blocked_open; /* Waiting to open */ int blocked_open; /* Waiting to open */
int count; /* Usage count */ int count; /* Usage count */
...@@ -241,6 +247,7 @@ struct tty_port { ...@@ -241,6 +247,7 @@ struct tty_port {
based drain is needed else based drain is needed else
set to size of fifo */ set to size of fifo */
struct kref kref; /* Ref counter */ struct kref kref; /* Ref counter */
void *client_data;
}; };
/* tty_port::iflags bits -- use atomic bit ops */ /* tty_port::iflags bits -- use atomic bit ops */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment