return !!(uport->status & UPSTAT_DCD_ENABLE);
}
+static inline struct uart_port *uart_port_ref(struct uart_state *state)
+{
+ if (atomic_add_unless(&state->refcount, 1, 0))
+ return state->uart_port;
+ return NULL;
+}
+
+static inline void uart_port_deref(struct uart_port *uport)
+{
+ if (uport && atomic_dec_and_test(&uport->state->refcount))
+ wake_up(&uport->state->remove_wait);
+}
+
+#define uart_port_lock(state, flags) \
+ ({ \
+ struct uart_port *__uport = uart_port_ref(state); \
+ if (__uport) \
+ spin_lock_irqsave(&__uport->lock, flags); \
+ __uport; \
+ })
+
+#define uart_port_unlock(uport, flags) \
+ ({ \
+ struct uart_port *__uport = uport; \
+ if (__uport) \
+ spin_unlock_irqrestore(&__uport->lock, flags); \
+ uart_port_deref(__uport); \
+ })
+
static inline struct uart_port *uart_port_check(struct uart_state *state)
{
#ifdef CONFIG_LOCKDEP
static void uart_stop(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
- struct uart_port *port = state->uart_port;
+ struct uart_port *port;
unsigned long flags;
- spin_lock_irqsave(&port->lock, flags);
- port->ops->stop_tx(port);
- spin_unlock_irqrestore(&port->lock, flags);
+ port = uart_port_lock(state, flags);
+ if (port)
+ port->ops->stop_tx(port);
+ uart_port_unlock(port, flags);
}
static void __uart_start(struct tty_struct *tty)
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
- if (!uart_tx_stopped(port))
+ if (port && !uart_tx_stopped(port))
port->ops->start_tx(port);
}
static void uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
- struct uart_port *port = state->uart_port;
+ struct uart_port *port;
unsigned long flags;
- spin_lock_irqsave(&port->lock, flags);
+ port = uart_port_lock(state, flags);
__uart_start(tty);
- spin_unlock_irqrestore(&port->lock, flags);
+ uart_port_unlock(port, flags);
}
static void
static int uart_put_char(struct tty_struct *tty, unsigned char c)
{
struct uart_state *state = tty->driver_data;
- struct uart_port *port = state->uart_port;
+ struct uart_port *port;
struct circ_buf *circ;
unsigned long flags;
int ret = 0;
if (!circ->buf)
return 0;
- spin_lock_irqsave(&port->lock, flags);
- if (uart_circ_chars_free(circ) != 0) {
+ port = uart_port_lock(state, flags);
+ if (port && uart_circ_chars_free(circ) != 0) {
circ->buf[circ->head] = c;
circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
ret = 1;
}
- spin_unlock_irqrestore(&port->lock, flags);
+ uart_port_unlock(port, flags);
return ret;
}
return -EL3HLT;
}
- port = state->uart_port;
circ = &state->xmit;
-
if (!circ->buf)
return 0;
- spin_lock_irqsave(&port->lock, flags);
- while (1) {
+ port = uart_port_lock(state, flags);
+ while (port) {
c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
if (count < c)
c = count;
}
__uart_start(tty);
- spin_unlock_irqrestore(&port->lock, flags);
-
+ uart_port_unlock(port, flags);
return ret;
}
static int uart_write_room(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
+ struct uart_port *port;
unsigned long flags;
int ret;
- spin_lock_irqsave(&state->uart_port->lock, flags);
+ port = uart_port_lock(state, flags);
ret = uart_circ_chars_free(&state->xmit);
- spin_unlock_irqrestore(&state->uart_port->lock, flags);
+ uart_port_unlock(port, flags);
return ret;
}
static int uart_chars_in_buffer(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
+ struct uart_port *port;
unsigned long flags;
int ret;
- spin_lock_irqsave(&state->uart_port->lock, flags);
+ port = uart_port_lock(state, flags);
ret = uart_circ_chars_pending(&state->xmit);
- spin_unlock_irqrestore(&state->uart_port->lock, flags);
+ uart_port_unlock(port, flags);
return ret;
}
return;
}
- port = state->uart_port;
pr_debug("uart_flush_buffer(%d) called\n", tty->index);
- spin_lock_irqsave(&port->lock, flags);
+ port = uart_port_lock(state, flags);
+ if (!port)
+ return;
uart_circ_clear(&state->xmit);
if (port->ops->flush_buffer)
port->ops->flush_buffer(port);
- spin_unlock_irqrestore(&port->lock, flags);
+ uart_port_unlock(port, flags);
tty_wakeup(tty);
}
static void uart_send_xchar(struct tty_struct *tty, char ch)
{
struct uart_state *state = tty->driver_data;
- struct uart_port *port = state->uart_port;
+ struct uart_port *port;
unsigned long flags;
+ port = uart_port_ref(state);
+ if (!port)
+ return;
+
if (port->ops->send_xchar)
port->ops->send_xchar(port, ch);
else {
port->ops->start_tx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
+ uart_port_deref(port);
}
static void uart_throttle(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
- struct uart_port *port = state->uart_port;
+ struct uart_port *port;
upstat_t mask = 0;
+ port = uart_port_ref(state);
+ if (!port)
+ return;
+
if (I_IXOFF(tty))
mask |= UPSTAT_AUTOXOFF;
if (C_CRTSCTS(tty))
if (mask & UPSTAT_AUTOXOFF)
uart_send_xchar(tty, STOP_CHAR(tty));
+
+ uart_port_deref(port);
}
static void uart_unthrottle(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
- struct uart_port *port = state->uart_port;
+ struct uart_port *port;
upstat_t mask = 0;
+ port = uart_port_ref(state);
+ if (!port)
+ return;
+
if (I_IXOFF(tty))
mask |= UPSTAT_AUTOXOFF;
if (C_CRTSCTS(tty))
if (mask & UPSTAT_AUTOXOFF)
uart_send_xchar(tty, START_CHAR(tty));
+
+ uart_port_deref(port);
}
static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo)
* FIXME: This wants extracting into a common all driver implementation
* of TIOCMWAIT using tty_port.
*/
-static int
-uart_wait_modem_status(struct uart_state *state, unsigned long arg)
+static int uart_wait_modem_status(struct uart_state *state, unsigned long arg)
{
- struct uart_port *uport = state->uart_port;
+ struct uart_port *uport;
struct tty_port *port = &state->port;
DECLARE_WAITQUEUE(wait, current);
struct uart_icount cprev, cnow;
/*
* note the counters on entry
*/
+ uport = uart_port_ref(state);
+ if (!uport)
+ return -EIO;
spin_lock_irq(&uport->lock);
memcpy(&cprev, &uport->icount, sizeof(struct uart_icount));
uart_enable_ms(uport);
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(&port->delta_msr_wait, &wait);
+ uart_port_deref(uport);
return ret;
}
{
struct uart_state *state = tty->driver_data;
struct uart_icount cnow;
- struct uart_port *uport = state->uart_port;
+ struct uart_port *uport;
+ uport = uart_port_ref(state);
+ if (!uport)
+ return -EIO;
spin_lock_irq(&uport->lock);
memcpy(&cnow, &uport->icount, sizeof(struct uart_icount));
spin_unlock_irq(&uport->lock);
+ uart_port_deref(uport);
icount->cts = cnow.cts;
icount->dsr = cnow.dsr;
static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct uart_state *state = tty->driver_data;
- struct uart_port *port = state->uart_port;
+ struct uart_port *port;
unsigned long char_time, expire;
- if (port->type == PORT_UNKNOWN || port->fifosize == 0)
+ port = uart_port_ref(state);
+ if (!port || port->type == PORT_UNKNOWN || port->fifosize == 0) {
+ uart_port_deref(port);
return;
+ }
/*
* Set the check interval to be 1/5 of the estimated time to
if (time_after(jiffies, expire))
break;
}
+ uart_port_deref(port);
}
/*
static int uart_carrier_raised(struct tty_port *port)
{
struct uart_state *state = container_of(port, struct uart_state, port);
- struct uart_port *uport = state->uart_port;
+ struct uart_port *uport;
int mctrl;
+
+ uport = uart_port_ref(state);
+ /*
+ * Should never observe uport == NULL since checks for hangup should
+ * abort the tty_port_block_til_ready() loop before checking for carrier
+ * raised -- but report carrier raised if it does anyway so open will
+ * continue and not sleep
+ */
+ if (WARN_ON(!uport))
+ return 1;
spin_lock_irq(&uport->lock);
uart_enable_ms(uport);
mctrl = uport->ops->get_mctrl(uport);
spin_unlock_irq(&uport->lock);
+ uart_port_deref(uport);
if (mctrl & TIOCM_CAR)
return 1;
return 0;
static void uart_dtr_rts(struct tty_port *port, int onoff)
{
struct uart_state *state = container_of(port, struct uart_state, port);
- struct uart_port *uport = state->uart_port;
+ struct uart_port *uport;
+
+ uport = uart_port_ref(state);
+ if (!uport)
+ return;
if (onoff)
uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
else
uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
+
+ uart_port_deref(uport);
}
/*
struct uart_driver *drv = driver->driver_state;
struct uart_state *state = drv->state + line;
struct uart_port *port;
+ int ret = -1;
- if (!state || !state->uart_port)
- return -1;
-
- port = state->uart_port;
- return port->ops->poll_get_char(port);
+ if (state) {
+ port = uart_port_ref(state);
+ if (port)
+ ret = port->ops->poll_get_char(port);
+ uart_port_deref(port);
+ }
+ return ret;
}
static void uart_poll_put_char(struct tty_driver *driver, int line, char ch)
struct uart_state *state = drv->state + line;
struct uart_port *port;
- if (!state || !state->uart_port)
+ if (!state)
return;
- port = state->uart_port;
+ port = uart_port_ref(state);
+ if (!port)
+ return;
if (ch == '\n')
port->ops->poll_put_char(port, '\r');
port->ops->poll_put_char(port, ch);
+ uart_port_deref(port);
}
#endif
}
/* Link the port to the driver state table and vice versa */
+ atomic_set(&state->refcount, 1);
+ init_waitqueue_head(&state->remove_wait);
state->uart_port = uport;
uport->state = state;
uport->type = PORT_UNKNOWN;
mutex_lock(&port->mutex);
+ WARN_ON(atomic_dec_return(&state->refcount) < 0);
+ wait_event(state->remove_wait, !atomic_read(&state->refcount));
state->uart_port = NULL;
mutex_unlock(&port->mutex);
out: