diff --git a/drivers/staging/speakup/serialio.h b/drivers/staging/speakup/serialio.h index 3ad7ff0bc3c3..89de6fff9cb2 100644 --- a/drivers/staging/speakup/serialio.h +++ b/drivers/staging/speakup/serialio.h @@ -8,6 +8,8 @@ #endif #include +#include "spk_priv.h" + /* * this is cut&paste from 8250.h. Get rid of the structure, the definitions * and this whole broken driver. @@ -21,7 +23,7 @@ struct old_serial_port { }; /* countdown values for serial timeouts in us */ -#define SPK_SERIAL_TIMEOUT 100000 +#define SPK_SERIAL_TIMEOUT SPK_SYNTH_TIMEOUT /* countdown values transmitter/dsr timeouts in us */ #define SPK_XMITR_TIMEOUT 100000 /* countdown values cts timeouts in us */ diff --git a/drivers/staging/speakup/spk_priv.h b/drivers/staging/speakup/spk_priv.h index 53142fee0df6..4f533667d312 100644 --- a/drivers/staging/speakup/spk_priv.h +++ b/drivers/staging/speakup/spk_priv.h @@ -39,6 +39,7 @@ #endif #define KT_SPKUP 15 +#define SPK_SYNTH_TIMEOUT 100000 /* in micro-seconds */ const struct old_serial_port *spk_serial_init(int index); void spk_stop_serial_interrupt(void); diff --git a/drivers/staging/speakup/spk_ttyio.c b/drivers/staging/speakup/spk_ttyio.c index ee37550e59b3..61c01bfe17a8 100644 --- a/drivers/staging/speakup/spk_ttyio.c +++ b/drivers/staging/speakup/spk_ttyio.c @@ -1,36 +1,97 @@ #include #include +#include +#include #include "speakup.h" #include "spk_types.h" +#include "spk_priv.h" +struct spk_ldisc_data { + char buf; + struct semaphore sem; + bool buf_free; +}; + +static struct spk_synth *spk_ttyio_synth; static struct tty_struct *speakup_tty; static int spk_ttyio_ldisc_open(struct tty_struct *tty) { + struct spk_ldisc_data *ldisc_data; + if (tty->ops->write == NULL) return -EOPNOTSUPP; speakup_tty = tty; + ldisc_data = kmalloc(sizeof(struct spk_ldisc_data), GFP_KERNEL); + if (!ldisc_data) { + pr_err("speakup: Failed to allocate ldisc_data.\n"); + return -ENOMEM; + } + + sema_init(&ldisc_data->sem, 0); + ldisc_data->buf_free = true; + speakup_tty->disc_data = ldisc_data; + return 0; } static void spk_ttyio_ldisc_close(struct tty_struct *tty) { + kfree(speakup_tty->disc_data); speakup_tty = NULL; } +static int spk_ttyio_receive_buf2(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) +{ + struct spk_ldisc_data *ldisc_data = tty->disc_data; + + if (spk_ttyio_synth->read_buff_add) { + int i; + for (i = 0; i < count; i++) + spk_ttyio_synth->read_buff_add(cp[i]); + + return count; + } + + if (!ldisc_data->buf_free) + /* ttyio_in will tty_schedule_flip */ + return 0; + + /* Make sure the consumer has read buf before we have seen + * buf_free == true and overwrite buf */ + mb(); + + ldisc_data->buf = cp[0]; + ldisc_data->buf_free = false; + up(&ldisc_data->sem); + + return 1; +} + static struct tty_ldisc_ops spk_ttyio_ldisc_ops = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "speakup_ldisc", .open = spk_ttyio_ldisc_open, .close = spk_ttyio_ldisc_close, + .receive_buf2 = spk_ttyio_receive_buf2, }; static int spk_ttyio_out(struct spk_synth *in_synth, const char ch); +static void spk_ttyio_send_xchar(char ch); +static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear); +static unsigned char spk_ttyio_in(void); +static unsigned char spk_ttyio_in_nowait(void); + struct spk_io_ops spk_ttyio_ops = { .synth_out = spk_ttyio_out, + .send_xchar = spk_ttyio_send_xchar, + .tiocmset = spk_ttyio_tiocmset, + .synth_in = spk_ttyio_in, + .synth_in_nowait = spk_ttyio_in_nowait, }; EXPORT_SYMBOL_GPL(spk_ttyio_ops); @@ -95,6 +156,51 @@ static int spk_ttyio_out(struct spk_synth *in_synth, const char ch) return 0; } +static void spk_ttyio_send_xchar(char ch) +{ + speakup_tty->ops->send_xchar(speakup_tty, ch); +} + +static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear) +{ + speakup_tty->ops->tiocmset(speakup_tty, set, clear); +} + +static unsigned char ttyio_in(int timeout) +{ + struct spk_ldisc_data *ldisc_data = speakup_tty->disc_data; + char rv; + + if (down_timeout(&ldisc_data->sem, usecs_to_jiffies(timeout)) == -ETIME) { + if (timeout) + pr_warn("spk_ttyio: timeout (%d) while waiting for input\n", + timeout); + return 0xff; + } + + rv = ldisc_data->buf; + /* Make sure we have read buf before we set buf_free to let + * the producer overwrite it */ + mb(); + ldisc_data->buf_free = true; + /* Let TTY push more characters */ + tty_schedule_flip(speakup_tty->port); + + return rv; +} + +static unsigned char spk_ttyio_in(void) +{ + return ttyio_in(SPK_SYNTH_TIMEOUT); +} + +static unsigned char spk_ttyio_in_nowait(void) +{ + char rv = ttyio_in(0); + + return (rv == 0xff) ? 0 : rv; +} + int spk_ttyio_synth_probe(struct spk_synth *synth) { int rv = spk_ttyio_initialise_ldisc(synth->ser); @@ -103,6 +209,7 @@ int spk_ttyio_synth_probe(struct spk_synth *synth) return rv; synth->alive = 1; + spk_ttyio_synth = synth; return 0; }