[patch 6/7] staging: speakup: add TTY-based input functionality
Okash Khawaja
okash.khawaja at gmail.com
Mon Apr 3 16:21:29 EDT 2017
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 at gmail.com>
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);
More information about the Speakup
mailing list