This patch introduces TTY-based input functionality. It does this by introducing struct spk_ldisc_data. buf_free is used to prevent the last read character from being overwritten before it is read. The semaphore is used to signal to calling to that there is a new byte to be read, so the calling code doesn't have to busy-wait for new bytes. All input comes in through ldisc's receive_buf2 callback. This method calls read_buff_add method of struct spk_synth if it is defined. Otherwise it stores one byte in a buffer and signals the receivers. Memory barrier calls prevent any race conditions. Signed-off by: Okash Khawaja <okash.khawaja@xxxxxxxxx> Index: linux-staging/drivers/staging/speakup/spk_ttyio.c =================================================================== --- linux-staging.orig/drivers/staging/speakup/spk_ttyio.c +++ linux-staging/drivers/staging/speakup/spk_ttyio.c @@ -1,41 +1,95 @@ #include <linux/types.h> #include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/slab.h> #include "speakup.h" #include "spk_types.h" +#include "spk_priv.h" +struct spk_ldisc_data { + char buf; + struct semaphore sem; + int buf_free; +}; + +static struct spk_synth *spk_ttyio_synth; +/* TODO: make this part of synth and remove the global */ 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 = 1; + 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 = (struct spk_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 == 0) + return 0; + + mb(); + + ldisc_data->buf = cp[0]; + ldisc_data->buf_free--; + 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); @@ -46,7 +100,7 @@ ret = tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops); if (ret) { - pr_err("Error registering line discipline.\n"); + pr_err("speakup: Error registering line discipline.\n"); return ret; } @@ -110,6 +164,37 @@ speakup_tty->ops->tiocmset(speakup_tty, set, clear); } +static unsigned char ttyio_in(int timeout) +{ + struct spk_ldisc_data *ldisc_data = + (struct spk_ldisc_data *)speakup_tty->disc_data; + char rv; + + if (!down_timeout(&ldisc_data->sem, usecs_to_jiffies(timeout))) { + pr_warn("spk_ttyio: timeout (%d) while waiting for input\n", + timeout); + return 0xff; + } + + rv = ldisc_data->buf; + mb(); + ldisc_data->buf_free = 1; + tty_schedule_flip(speakup_tty->port); + up(&ldisc_data->sem); + + return rv; +} + +static unsigned char spk_ttyio_in(void) +{ + return ttyio_in(SPK_SYNTH_TIMEOUT); +} + +static unsigned char spk_ttyio_in_nowait(void) +{ + return ttyio_in(0); +} + int spk_ttyio_synth_probe(struct spk_synth *synth) { int rv = spk_ttyio_initialise_ldisc(synth->ser); @@ -118,6 +203,7 @@ return rv; synth->alive = 1; + spk_ttyio_synth = synth; return 0; } Index: linux-staging/drivers/staging/speakup/serialio.h =================================================================== --- linux-staging.orig/drivers/staging/speakup/serialio.h +++ linux-staging/drivers/staging/speakup/serialio.h @@ -8,6 +8,8 @@ #endif #include <linux/serial_core.h> +#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 @@ }; /* 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 */ Index: linux-staging/drivers/staging/speakup/spk_priv.h =================================================================== --- linux-staging.orig/drivers/staging/speakup/spk_priv.h +++ linux-staging/drivers/staging/speakup/spk_priv.h @@ -39,6 +39,7 @@ #endif #define KT_SPKUP 15 +#define SPK_SYNTH_TIMEOUT 100000 const struct old_serial_port *spk_serial_init(int index); void spk_stop_serial_interrupt(void); _______________________________________________ Speakup mailing list Speakup@xxxxxxxxxxxxxxxxx http://linux-speakup.org/cgi-bin/mailman/listinfo/speakup