Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c
Show First 20 Lines • Show All 257 Lines • ▼ Show 20 Lines | |||||
* used in both polled and interrupt-driven modes, as JTAG UARTs may be hooked | * used in both polled and interrupt-driven modes, as JTAG UARTs may be hooked | ||||
* up with, or without, IRQs allocated. | * up with, or without, IRQs allocated. | ||||
*/ | */ | ||||
static void | static void | ||||
aju_handle_input(struct altera_jtag_uart_softc *sc, struct tty *tp) | aju_handle_input(struct altera_jtag_uart_softc *sc, struct tty *tp) | ||||
{ | { | ||||
int c; | int c; | ||||
tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
markj: This is a cosmetic note, but in other places we've changed the pattern of lock assertions to… | |||||
AJU_LOCK_ASSERT(sc); | AJU_LOCK_ASSERT(sc); | ||||
while (aju_readable(sc)) { | while (aju_readable(sc)) { | ||||
c = aju_read(sc); | c = aju_read(sc); | ||||
AJU_UNLOCK(sc); | AJU_UNLOCK(sc); | ||||
#ifdef KDB | #ifdef KDB | ||||
if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) | if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) | ||||
kdb_alt_break(c, &sc->ajus_alt_break_state); | kdb_alt_break(c, &sc->ajus_alt_break_state); | ||||
Show All 15 Lines | |||||
* FIFO in bugger chunks. | * FIFO in bugger chunks. | ||||
*/ | */ | ||||
static void | static void | ||||
aju_handle_output(struct altera_jtag_uart_softc *sc, struct tty *tp) | aju_handle_output(struct altera_jtag_uart_softc *sc, struct tty *tp) | ||||
{ | { | ||||
uint32_t v; | uint32_t v; | ||||
uint8_t ch; | uint8_t ch; | ||||
tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
AJU_LOCK_ASSERT(sc); | AJU_LOCK_ASSERT(sc); | ||||
AJU_UNLOCK(sc); | AJU_UNLOCK(sc); | ||||
while (ttydisc_getc_poll(tp) != 0) { | while (ttydisc_getc_poll(tp) != 0) { | ||||
AJU_LOCK(sc); | AJU_LOCK(sc); | ||||
if (*sc->ajus_jtag_presentp == 0) { | if (*sc->ajus_jtag_presentp == 0) { | ||||
/* | /* | ||||
* If JTAG is not present, then we will drop this | * If JTAG is not present, then we will drop this | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | if (sc->ajus_irq_res != NULL && (v & ALTERA_JTAG_UART_CONTROL_WE) != 0) | ||||
aju_intr_writable_disable(sc); | aju_intr_writable_disable(sc); | ||||
} | } | ||||
static void | static void | ||||
aju_outwakeup(struct tty *tp) | aju_outwakeup(struct tty *tp) | ||||
{ | { | ||||
struct altera_jtag_uart_softc *sc = tty_softc(tp); | struct altera_jtag_uart_softc *sc = tty_softc(tp); | ||||
tty_assert_locked(tp); | ttydisc_assert_locked(tp); | ||||
AJU_LOCK(sc); | AJU_LOCK(sc); | ||||
aju_handle_output(sc, tp); | aju_handle_output(sc, tp); | ||||
AJU_UNLOCK(sc); | AJU_UNLOCK(sc); | ||||
} | } | ||||
static void | static void | ||||
aju_io_callout(void *arg) | aju_io_callout(void *arg) | ||||
{ | { | ||||
struct altera_jtag_uart_softc *sc = arg; | struct altera_jtag_uart_softc *sc = arg; | ||||
struct tty *tp = sc->ajus_ttyp; | struct tty *tp = sc->ajus_ttyp; | ||||
tty_lock(tp); | |||||
AJU_LOCK(sc); | AJU_LOCK(sc); | ||||
/* | /* | ||||
* It would be convenient if we could share code with aju_intr() here | * It would be convenient if we could share code with aju_intr() here | ||||
* by testing the control register for ALTERA_JTAG_UART_CONTROL_RI and | * by testing the control register for ALTERA_JTAG_UART_CONTROL_RI and | ||||
* ALTERA_JTAG_UART_CONTROL_WI. Unfortunately, it's not clear that | * ALTERA_JTAG_UART_CONTROL_WI. Unfortunately, it's not clear that | ||||
* this is supported, so do all the work to poll for both input and | * this is supported, so do all the work to poll for both input and | ||||
* output. | * output. | ||||
*/ | */ | ||||
aju_handle_input(sc, tp); | aju_handle_input(sc, tp); | ||||
aju_handle_output(sc, tp); | aju_handle_output(sc, tp); | ||||
/* | /* | ||||
* Reschedule next poll attempt. There's some argument that we should | * Reschedule next poll attempt. There's some argument that we should | ||||
* do adaptive polling based on the expectation of I/O: is something | * do adaptive polling based on the expectation of I/O: is something | ||||
* pending in the output buffer, or have we recently had input, but we | * pending in the output buffer, or have we recently had input, but we | ||||
* don't. | * don't. | ||||
*/ | */ | ||||
callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL, | callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL, | ||||
aju_io_callout, sc); | aju_io_callout, sc); | ||||
AJU_UNLOCK(sc); | AJU_UNLOCK(sc); | ||||
tty_unlock(tp); | |||||
} | } | ||||
static void | static void | ||||
aju_ac_callout(void *arg) | aju_ac_callout(void *arg) | ||||
{ | { | ||||
struct altera_jtag_uart_softc *sc = arg; | struct altera_jtag_uart_softc *sc = arg; | ||||
struct tty *tp = sc->ajus_ttyp; | struct tty *tp = sc->ajus_ttyp; | ||||
uint32_t v; | uint32_t v; | ||||
tty_lock(tp); | |||||
AJU_LOCK(sc); | AJU_LOCK(sc); | ||||
v = aju_control_read(sc); | v = aju_control_read(sc); | ||||
if (v & ALTERA_JTAG_UART_CONTROL_AC) { | if (v & ALTERA_JTAG_UART_CONTROL_AC) { | ||||
v &= ~ALTERA_JTAG_UART_CONTROL_AC; | v &= ~ALTERA_JTAG_UART_CONTROL_AC; | ||||
aju_control_write(sc, v); | aju_control_write(sc, v); | ||||
if (*sc->ajus_jtag_presentp == 0) { | if (*sc->ajus_jtag_presentp == 0) { | ||||
*sc->ajus_jtag_presentp = 1; | *sc->ajus_jtag_presentp = 1; | ||||
atomic_add_int(&aju_jtag_appeared, 1); | atomic_add_int(&aju_jtag_appeared, 1); | ||||
Show All 13 Lines | if (*sc->ajus_jtag_missedp > AJU_JTAG_MAXMISS) { | ||||
atomic_add_int(&aju_jtag_vanished, 1); | atomic_add_int(&aju_jtag_vanished, 1); | ||||
aju_handle_output(sc, tp); | aju_handle_output(sc, tp); | ||||
} else | } else | ||||
(*sc->ajus_jtag_missedp)++; | (*sc->ajus_jtag_missedp)++; | ||||
} | } | ||||
callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL, | callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL, | ||||
aju_ac_callout, sc); | aju_ac_callout, sc); | ||||
AJU_UNLOCK(sc); | AJU_UNLOCK(sc); | ||||
tty_unlock(tp); | |||||
} | } | ||||
static void | static void | ||||
aju_intr(void *arg) | aju_intr(void *arg) | ||||
{ | { | ||||
struct altera_jtag_uart_softc *sc = arg; | struct altera_jtag_uart_softc *sc = arg; | ||||
struct tty *tp = sc->ajus_ttyp; | struct tty *tp = sc->ajus_ttyp; | ||||
uint32_t v; | uint32_t v; | ||||
tty_lock(tp); | ttydisc_lock(tp); | ||||
AJU_LOCK(sc); | AJU_LOCK(sc); | ||||
v = aju_control_read(sc); | v = aju_control_read(sc); | ||||
if (v & ALTERA_JTAG_UART_CONTROL_RI) { | if (v & ALTERA_JTAG_UART_CONTROL_RI) { | ||||
atomic_add_int(&aju_intr_read_count, 1); | atomic_add_int(&aju_intr_read_count, 1); | ||||
aju_handle_input(sc, tp); | aju_handle_input(sc, tp); | ||||
} | } | ||||
if (v & ALTERA_JTAG_UART_CONTROL_WI) { | if (v & ALTERA_JTAG_UART_CONTROL_WI) { | ||||
atomic_add_int(&aju_intr_write_count, 1); | atomic_add_int(&aju_intr_write_count, 1); | ||||
aju_handle_output(sc, tp); | aju_handle_output(sc, tp); | ||||
} | } | ||||
AJU_UNLOCK(sc); | AJU_UNLOCK(sc); | ||||
tty_unlock(tp); | ttydisc_unlock(tp); | ||||
} | } | ||||
int | int | ||||
altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc) | altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc) | ||||
{ | { | ||||
struct tty *tp; | struct tty *tp; | ||||
int error; | int error; | ||||
Show All 40 Lines | if (sc->ajus_irq_res != NULL) { | ||||
} | } | ||||
} | } | ||||
tp = sc->ajus_ttyp = tty_alloc(&aju_ttydevsw, sc); | tp = sc->ajus_ttyp = tty_alloc(&aju_ttydevsw, sc); | ||||
if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) { | if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) { | ||||
aju_cons_sc = sc; | aju_cons_sc = sc; | ||||
tty_init_console(tp, 0); | tty_init_console(tp, 0); | ||||
} | } | ||||
tty_makedev(tp, NULL, "%s%d", AJU_TTYNAME, sc->ajus_unit); | tty_makedev(tp, NULL, "%s%d", AJU_TTYNAME, sc->ajus_unit); | ||||
/* Grab the lock now for callout work. */ | |||||
ttydisc_lock(tp); | |||||
/* | /* | ||||
* If we will be using interrupts, enable them now; otherwise, start | * If we will be using interrupts, enable them now; otherwise, start | ||||
* polling. From this point onwards, input can arrive. | * polling. From this point onwards, input can arrive. | ||||
*/ | */ | ||||
if (sc->ajus_irq_res != NULL) { | if (sc->ajus_irq_res != NULL) { | ||||
AJU_LOCK(sc); | AJU_LOCK(sc); | ||||
aju_intr_readable_enable(sc); | aju_intr_readable_enable(sc); | ||||
AJU_UNLOCK(sc); | AJU_UNLOCK(sc); | ||||
} else { | } else { | ||||
callout_init(&sc->ajus_io_callout, 1); | callout_init_mtx(&sc->ajus_io_callout, ttydisc_getlock(tp), 0); | ||||
callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL, | callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL, | ||||
aju_io_callout, sc); | aju_io_callout, sc); | ||||
} | } | ||||
callout_init(&sc->ajus_ac_callout, 1); | callout_init_mtx(&sc->ajus_ac_callout, ttydisc_getlock(tp), 0); | ||||
callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL, | callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL, | ||||
aju_ac_callout, sc); | aju_ac_callout, sc); | ||||
ttydisc_unlock(tp); | |||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
altera_jtag_uart_detach(struct altera_jtag_uart_softc *sc) | altera_jtag_uart_detach(struct altera_jtag_uart_softc *sc) | ||||
{ | { | ||||
struct tty *tp = sc->ajus_ttyp; | struct tty *tp = sc->ajus_ttyp; | ||||
/* | /* | ||||
* If we're using interrupts, disable and release the interrupt | * If we're using interrupts, disable and release the interrupt | ||||
* handler now. Otherwise drain the polling timeout. | * handler now. Otherwise drain the polling timeout. Grab the tty | ||||
* lock early to block any requests from userland until we've finished | |||||
* detaching. | |||||
*/ | */ | ||||
tty_lock(tp); | |||||
if (sc->ajus_irq_res != NULL) { | if (sc->ajus_irq_res != NULL) { | ||||
AJU_LOCK(sc); | AJU_LOCK(sc); | ||||
aju_intr_disable(sc); | aju_intr_disable(sc); | ||||
AJU_UNLOCK(sc); | AJU_UNLOCK(sc); | ||||
bus_teardown_intr(sc->ajus_dev, sc->ajus_irq_res, | bus_teardown_intr(sc->ajus_dev, sc->ajus_irq_res, | ||||
sc->ajus_irq_cookie); | sc->ajus_irq_cookie); | ||||
} else | } else | ||||
callout_drain(&sc->ajus_io_callout); | callout_drain(&sc->ajus_io_callout); | ||||
callout_drain(&sc->ajus_ac_callout); | callout_drain(&sc->ajus_ac_callout); | ||||
Done Inline Actionscallout_drain() is allowed to sleep, so you can't be holding a mtx here. markj: callout_drain() is allowed to sleep, so you can't be holding a mtx here. | |||||
Done Inline ActionsI suspect the nomenclature for the tty lock here might be confusing, as that one's now an sx and the callout got converted to using the ttydisc lock, so that one must be held (IIRC) kevans: I suspect the nomenclature for the tty lock here might be confusing, as that one's now an sx… | |||||
Done Inline Actionscallout_stop() must be called with the associated lock held, but callout_drain() must not. If the callout thread is blocked on the ttydisc lock and you call callout_drain() with that lock held, you'll get a deadlock. I think it is sufficient to just defer acquiring the ttydisc lock until after the callout has been drained. markj: callout_stop() must be called with the associated lock held, but callout_drain() must not. If… | |||||
Done Inline ActionsGot it, thanks! Will fix kevans: Got it, thanks! Will fix | |||||
if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) | if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) | ||||
aju_cons_sc = NULL; | aju_cons_sc = NULL; | ||||
tty_lock(tp); | ttydisc_lock(tp); | ||||
tty_rel_gone(tp); | tty_rel_gone(tp); | ||||
AJU_LOCK_DESTROY(sc); | AJU_LOCK_DESTROY(sc); | ||||
} | } |
This is a cosmetic note, but in other places we've changed the pattern of lock assertions to embed the assertion in the name rather than using a mtx(9)-specific parameter. In this case it'd become ttydisc_assert_locked(tp). That makes it easier to change the underlying implementation, like you had to do with tty_lock_assert().