[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