* [PATCH 8/8] serial: Add an upper level interface for the polled interface
@ 2007-04-26 18:54 Corey Minyard
0 siblings, 0 replies; only message in thread
From: Corey Minyard @ 2007-04-26 18:54 UTC (permalink / raw)
To: Alan Cox, linux-serial
Subject: serial: Add an upper level interface for the polled interface
Add an upper level interface so that an in-kernel use can direclty
grab a serial port for its own use. This is the interface that the
IPMI serial driver and kgdb can share.
Signed-off-by: Corey Minyard <minyard@acm.org>
drivers/serial/serial_core.c | 234 +++++++++++++++++++++++++++++++++++++++----
include/linux/serial_core.h | 115 ++++++++++++++++++++-
2 files changed, 324 insertions(+), 25 deletions(-)
Index: linux-2.6.21/drivers/serial/serial_core.c
===================================================================
--- linux-2.6.21.orig/drivers/serial/serial_core.c
+++ linux-2.6.21/drivers/serial/serial_core.c
@@ -82,6 +82,13 @@ void uart_write_wakeup(struct uart_port
* closed. No cookie for you.
*/
BUG_ON(!info);
+
+ if (info->direct) {
+ if (info->direct->tx_ready)
+ info->direct->tx_ready(port);
+ return;
+ }
+
tasklet_schedule(&info->tlet);
}
@@ -487,6 +494,27 @@ static void uart_flush_chars(struct tty_
uart_start(tty);
}
+static int uart_circ_write(struct circ_buf *circ, const unsigned char *buf,
+ int count)
+{
+ int c, ret = 0;
+
+ while (1) {
+ c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
+ if (count < c)
+ c = count;
+ if (c <= 0)
+ break;
+ memcpy(circ->buf + circ->head, buf, c);
+ circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+
+ return ret;
+}
+
static int
uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
@@ -494,7 +522,7 @@ uart_write(struct tty_struct *tty, const
struct uart_port *port;
struct circ_buf *circ;
unsigned long flags;
- int c, ret = 0;
+ int ret;
/*
* This means you called this function _after_ the port was
@@ -512,18 +540,7 @@ uart_write(struct tty_struct *tty, const
return 0;
spin_lock_irqsave(&port->lock, flags);
- while (1) {
- c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
- if (count < c)
- c = count;
- if (c <= 0)
- break;
- memcpy(circ->buf + circ->head, buf, c);
- circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
- buf += c;
- count -= c;
- ret += c;
- }
+ ret = uart_circ_write(circ, buf, count);
spin_unlock_irqrestore(&port->lock, flags);
uart_start(tty);
@@ -1300,6 +1317,9 @@ static void uart_close(struct tty_struct
* Wake up anyone trying to open this port.
*/
state->info->flags &= ~UIF_NORMAL_ACTIVE;
+ spin_lock_irq(&state->port->lock);
+ port->flags &= ~UPF_INUSE_NORMAL;
+ spin_unlock_irq(&state->port->lock);
wake_up_interruptible(&state->info->open_wait);
done:
@@ -1436,6 +1456,7 @@ uart_block_til_ready(struct file *filp,
struct uart_info *info = state->info;
struct uart_port *port = state->port;
unsigned int mctrl;
+ int retval = 0;
info->blocked_open++;
state->count--;
@@ -1496,6 +1517,22 @@ uart_block_til_ready(struct file *filp,
if (signal_pending(current))
break;
+
+ /*
+ * The UPF_INUSE_NORMAL flag may have been cleared
+ * while we were waiting on the port. Make sure the
+ * that the port wasn't grabbed by a direct user and
+ * that the UPF_INUSE_NORMAL flags is set if we are
+ * trying to grab the port now.
+ */
+ spin_lock_irq(&state->port->lock);
+ if (port->flags & UPF_INUSE_DIRECT) {
+ spin_unlock_irq(&state->port->lock);
+ retval = -EAGAIN;
+ break;
+ }
+ port->flags |= UPF_INUSE_NORMAL;
+ spin_unlock_irq(&state->port->lock);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
@@ -1504,12 +1541,11 @@ uart_block_til_ready(struct file *filp,
info->blocked_open--;
if (signal_pending(current))
- return -ERESTARTSYS;
-
- if (!info->tty || tty_hung_up_p(filp))
- return -EAGAIN;
+ retval = -ERESTARTSYS;
+ else if (!info->tty || tty_hung_up_p(filp))
+ retval = -EAGAIN;
- return 0;
+ return retval;
}
static struct uart_state *uart_get(struct uart_driver *drv, int line)
@@ -1529,6 +1565,15 @@ static struct uart_state *uart_get(struc
goto err_unlock;
}
+ spin_lock_irq(&state->port->lock);
+ if (state->port->flags & UPF_INUSE_DIRECT) {
+ spin_unlock_irq(&state->port->lock);
+ ret = -EAGAIN;
+ goto err_unlock;
+ }
+ state->port->flags |= UPF_INUSE_NORMAL;
+ spin_unlock_irq(&state->port->lock);
+
if (!state->info) {
/*
* This code is convoluted because the console code
@@ -1543,7 +1588,7 @@ static struct uart_state *uart_get(struc
GFP_KERNEL);
if (!state->info) {
ret = -ENOMEM;
- goto err_unlock;
+ goto err_unlock_release;
}
init_waitqueue_head(&state->info->open_wait);
@@ -1564,6 +1609,10 @@ static struct uart_state *uart_get(struc
}
return state;
+ err_unlock_release:
+ spin_lock_irq(&state->port->lock);
+ state->port->flags &= ~UPF_INUSE_NORMAL;
+ spin_unlock_irq(&state->port->lock);
err_unlock:
state->count--;
mutex_unlock(&state->mutex);
@@ -1648,6 +1697,16 @@ static int uart_open(struct tty_struct *
*/
if (retval == 0)
retval = uart_block_til_ready(filp, state);
+
+ spin_lock_irq(&state->port->lock);
+ if (retval && (state->port->flags & UPF_INUSE_NORMAL))
+ /*
+ * The block failed, make sure that the inuse flag is
+ * cleared.
+ */
+ state->port->flags &= ~UPF_INUSE_NORMAL;
+ spin_unlock_irq(&state->port->lock);
+
mutex_unlock(&state->mutex);
/*
@@ -2337,6 +2396,132 @@ static const struct tty_operations uart_
.tiocmset = uart_tiocmset,
};
+/*
+ * Holds a list of poll-capable uart drivers, so that polled driver
+ * users can look them up.
+ */
+static LIST_HEAD(polled_list);
+static DEFINE_MUTEX(polled_list_lock);
+
+#define PORT_INUSE (UPF_INUSE_NORMAL | UPF_INUSE_DIRECT)
+
+/**
+ * uart_get_direct_port - Directly get a port for use
+ * @name: The name to search for
+ * @line: The line number to reserve
+ * @force: If true, get the port even if it is in use already.
+ *
+ * Find and reserve (if found) a serial port. This is so things
+ * like device drivers and debuggers can directly reserve a
+ * serial port. The force options is the serial port can be
+ * taken over even while in use. If the force option is set, it
+ * is assumed that the system is in a stopped state and nothing
+ * else will be touching the port until the debugger returns it.
+ */
+struct uart_port *uart_get_direct_port(char *name, int line, int force)
+{
+ struct uart_driver *drv;
+ struct uart_port *port = NULL;
+ unsigned long flags = 0;
+
+ if (!force)
+ mutex_lock(&polled_list_lock);
+ list_for_each_entry(drv, &polled_list, polled_link) {
+ if (strcmp(drv->dev_name, name) == 0) {
+ if (line < drv->nr_pollable
+ && drv->pollable_ports[line])
+ port = drv->pollable_ports[line];
+ else
+ break;
+ if (!try_module_get(drv->owner)) {
+ port = NULL;
+ break;
+ }
+ if (!force)
+ spin_lock_irqsave(&port->lock, flags);
+ if (!force && port->flags & PORT_INUSE) {
+ spin_unlock_irqrestore(&port->lock, flags);
+ module_put(drv->owner);
+ port = NULL;
+ break;
+ }
+ port->flags |= UPF_INUSE_DIRECT;
+ if (!force)
+ spin_unlock_irqrestore(&port->lock, flags);
+ break;
+ }
+ }
+ if (!force)
+ mutex_unlock(&polled_list_lock);
+
+ return port;
+}
+
+/**
+ * uart_put_direct_port - Release a port reserved for direct use.
+ * @port: The port to release
+ * @force: If true, free the port even if it is in use already.
+ *
+ * Free a port that was previously returned by uart_get_direct_port.
+ * If you specified force in the get, you should do so in the put.
+ */
+int uart_put_direct_port(struct uart_port *port, int force)
+{
+ struct uart_driver *drv;
+ unsigned long flags = 0;
+ int i;
+ int retval = -EINVAL;
+
+ if (!force)
+ mutex_lock(&polled_list_lock);
+ list_for_each_entry(drv, &polled_list, polled_link) {
+ for (i = 0; i < drv->nr_pollable; i++) {
+ if (drv->pollable_ports[i] == port) {
+ if (!force)
+ spin_lock_irqsave(&port->lock, flags);
+ port->flags &= ~UPF_INUSE_DIRECT;
+ if (!force)
+ spin_unlock_irqrestore(&port->lock,
+ flags);
+ module_put(drv->owner);
+ retval = 0;
+ goto out_unlock;
+ }
+ }
+ }
+ out_unlock:
+ if (!force)
+ mutex_unlock(&polled_list_lock);
+
+ return retval;
+}
+
+/**
+ * uart_direct_write - Write data as a direct serial user
+ * @port: The port to write on
+ * @buf: The data to write
+ * @count: The number of bytes to write.
+ *
+ * Write bytes to a serial port that is reserved with the
+ * uart_get_direct_port() function. This is non-blocking and
+ * will return the number of bytes actually written.
+ */
+int
+uart_direct_write(struct uart_port *port, const unsigned char *buf, int count)
+{
+ struct circ_buf *circ;
+ unsigned long flags;
+ int ret;
+
+ circ = &port->info->xmit;
+ spin_lock_irqsave(&port->lock, flags);
+ ret = uart_circ_write(circ, buf, count);
+ if (ret > 0)
+ port->ops->start_tx(port);
+ spin_unlock_irqrestore(&port->lock, flags);
+ return ret;
+}
+
/**
* uart_register_polled - register a driver to be used as a polled device
* @drv: low level driver structure
@@ -2347,6 +2532,10 @@ static const struct tty_operations uart_
*/
void uart_register_polled(struct uart_driver *drv)
{
+ mutex_lock(&polled_list_lock);
+ list_add_tail(&drv->polled_link, &polled_list);
+ mutex_unlock(&polled_list_lock);
+
#ifdef CONFIG_SERIAL_CORE_CONSOLE
if (drv->nr_pollable && drv->cons) {
/* We manage the consoles for this driver. */
@@ -2373,6 +2562,10 @@ void uart_unregister_polled(struct uart_
unregister_console(drv->cons);
}
#endif
+
+ mutex_lock(&polled_list_lock);
+ list_del(&drv->polled_link);
+ mutex_unlock(&polled_list_lock);
}
/**
@@ -2656,6 +2849,9 @@ EXPORT_SYMBOL(uart_suspend_port);
EXPORT_SYMBOL(uart_resume_port);
EXPORT_SYMBOL(uart_add_one_port);
EXPORT_SYMBOL(uart_remove_one_port);
+EXPORT_SYMBOL(uart_get_direct_port);
+EXPORT_SYMBOL(uart_put_direct_port);
+EXPORT_SYMBOL(uart_direct_write);
MODULE_DESCRIPTION("Serial driver core");
MODULE_LICENSE("GPL");
Index: linux-2.6.21/include/linux/serial_core.h
===================================================================
--- linux-2.6.21.orig/include/linux/serial_core.h
+++ linux-2.6.21/include/linux/serial_core.h
@@ -295,6 +295,11 @@ struct uart_port {
upf_t flags;
+/*
+ * The port lock protects UPF_INUSE_DIRECT and UPF_INUSE_NORMAL. One
+ * of those two bits must be set before any other bits can be changed
+ * in port->flags.
+ */
#define UPF_FOURPORT ((__force upf_t) (1 << 1))
#define UPF_SAK ((__force upf_t) (1 << 2))
#define UPF_SPD_MASK ((__force upf_t) (0x1030))
@@ -311,7 +316,9 @@ struct uart_port {
#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))
#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))
+#define UPF_INUSE_NORMAL ((__force uif_t) (1 << 27))
#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))
+#define UPF_INUSE_DIRECT ((__force uif_t) (1 << 29))
#define UPF_DEAD ((__force upf_t) (1 << 30))
#define UPF_IOREMAP ((__force upf_t) (1 << 31))
@@ -352,6 +359,44 @@ struct uart_state {
#define UART_XMIT_SIZE PAGE_SIZE
+/*
+ * Structure used by the direct uart driver.
+ */
+struct uart_direct {
+ /* Generic data for use by the layered driver. */
+ void *direct_data;
+
+ /*
+ * Port status, called with the port lock held.
+ */
+ int (*handle_break)(struct uart_port *port);
+ void (*handle_dcd_change)(struct uart_port *port, unsigned int status);
+ void (*handle_cts_change)(struct uart_port *port, unsigned int status);
+
+ /*
+ * A receive character from the port. Called with the port
+ * lock held, buffer and use the "push" function to actually
+ * handle the characters.
+ */
+ void (*handle_char)(struct uart_port *port, unsigned int status,
+ unsigned int overrun, unsigned int ch,
+ unsigned int flag);
+
+ /*
+ * Done receiving characters for now, called with the port
+ * lock not held.
+ */
+ void (*push)(struct uart_port *port);
+
+ /*
+ * MSR values changed, called with the port lock not held.
+ */
+ void (*push_msr)(struct uart_port *port);
+
+ /* Might be ready to transmit more characters. */
+ void (*tx_ready)(struct uart_port *port);
+};
+
typedef unsigned int __bitwise__ uif_t;
/*
@@ -383,6 +428,9 @@ struct uart_info {
wait_queue_head_t open_wait;
wait_queue_head_t delta_msr_wait;
+
+ /* For the direct serial interface */
+ struct uart_direct *direct;
};
/* number of characters left in xmit buffer before we ask for more */
@@ -418,6 +466,7 @@ struct uart_driver {
*/
struct uart_state *state;
struct tty_driver *tty_driver;
+ struct list_head polled_link;
};
void uart_write_wakeup(struct uart_port *port);
@@ -458,6 +507,14 @@ int uart_remove_one_port(struct uart_dri
int uart_match_port(struct uart_port *port1, struct uart_port *port2);
/*
+ * Direct serial port access.
+ */
+struct uart_port *uart_get_direct_port(char *name, int line, int force);
+int uart_put_direct_port(struct uart_port *port, int force);
+int uart_direct_write(struct uart_port *port, const unsigned char *buf,
+ int count);
+
+/*
* Power Management
*/
int uart_suspend_port(struct uart_driver *reg, struct uart_port *port);
@@ -509,6 +566,13 @@ uart_handle_sysrq_char(struct uart_port
static inline int uart_handle_break(struct uart_port *port)
{
struct uart_info *info = port->info;
+
+ if (info->direct) {
+ if (info->direct->handle_break)
+ info->direct->handle_break(port);
+ return 0;
+ }
+
#ifdef SUPPORT_SYSRQ
if (port->cons && port->cons->index == port->line) {
if (!port->sysrq) {
@@ -531,7 +595,13 @@ static inline int uart_handle_break(stru
static inline void
uart_handle_dcd_change(struct uart_port *port, unsigned int status)
{
- struct uart_info *info = port->info;
+ struct uart_info *info = port->info;;
+
+ if (info->direct) {
+ if (info->direct->handle_dcd_change)
+ info->direct->handle_dcd_change(port, status);
+ return;
+ }
port->icount.dcd++;
@@ -557,7 +627,15 @@ static inline void
uart_handle_cts_change(struct uart_port *port, unsigned int status)
{
struct uart_info *info = port->info;
- struct tty_struct *tty = info->tty;
+ struct tty_struct *tty;
+
+ if (info->direct) {
+ if (info->direct->handle_cts_change)
+ info->direct->handle_cts_change(port, status);
+ return;
+ }
+
+ tty = info->tty;
port->icount.cts++;
@@ -583,7 +661,17 @@ static inline void
uart_insert_char(struct uart_port *port, unsigned int status,
unsigned int overrun, unsigned int ch, unsigned int flag)
{
- struct tty_struct *tty = port->info->tty;
+ struct uart_info *info = port->info;
+ struct tty_struct *tty;
+
+ if (info->direct) {
+ if (info->direct->handle_char)
+ info->direct->handle_char(port, status, overrun,
+ ch, flag);
+ return;
+ }
+
+ tty = info->tty;
if (!tty)
return;
@@ -602,16 +690,31 @@ uart_insert_char(struct uart_port *port,
static inline void
uart_push(struct uart_port *port)
{
- if (!port->info->tty)
+ struct uart_info *info = port->info;
+
+ if (info->direct) {
+ if (info->direct->push)
+ info->direct->push(port);
+ return;
+ }
+
+ if (!info->tty)
return;
- tty_flip_buffer_push(port->info->tty);
+ tty_flip_buffer_push(info->tty);
}
static inline void
uart_msr_change(struct uart_port *port)
{
- wake_up_interruptible(&port->info->delta_msr_wait);
+ struct uart_info *info = port->info;
+
+ if (info->direct) {
+ if (info->direct->push_msr)
+ info->direct->push_msr(port);
+ return;
+ }
+ wake_up_interruptible(&info->delta_msr_wait);
}
/*
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2007-04-26 19:17 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-04-26 18:54 [PATCH 8/8] serial: Add an upper level interface for the polled interface Corey Minyard
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox