staging: speakup: add send_xchar, tiocmset and input functionality for tty

Samuel Thibault samuel.thibault at ens-lyon.org
Sat Apr 29 10:09:54 EDT 2017


Okash Khawaja, on sam. 29 avril 2017 14:53:18 +0100, wrote:
> This patch adds further TTY-based functionality, specifically implementation
> of send_xchar and tiocmset methods, and input. send_xchar and tiocmset
> methods simply delegate to corresponding TTY operations.
> 
> For input, it implements the receive_buf2 callback in tty_ldisc_ops of
> speakup's ldisc. If a synth defines read_buff_add method then receive_buf2
> simply delegates to that and returns.
> 
> For spk_ttyio_in, the data is passed from receive_buf2 thread to
> spk_ttyio_in thread through spk_ldisc_data structure. It has following
> members:
> 
> - char buf: represents data received
> - struct semaphore sem: used to signal to spk_ttyio_in thread that data
> 	is available to be read without having to busy wait
> - bool buf_free: this is used in comination with mb() calls to syncronise
> 	the two threads over buf
> 
> receive_buf2 only writes to buf if buf_free is true. The check for buf_free
> and writing to buf are separated by mb() to ensure that spk_ttyio_in has read
> buf before receive_buf2 writes to it. After writing, it ups the semaphore to
> signal to spk_ttyio_in that there is now data to read.
> 
> spk_ttyio_in waits for data to read by downing the semaphore. Thus when
> signalled by receive_buf2 thread above, it reads from buf and sets buf_free
> to true. These two operations are separated by mb() to ensure that
> receive_buf2 thread finds buf_free to be true only after buf has been read.
> After that spk_ttyio_in calls tty_schedule_flip for subsequent data to come
> in through receive_buf2.
> 
> Signed-off-by: Okash Khawaja <okash.khawaja at gmail.com>

Reviewed-by: Samuel Thibault <samuel.thibault at ens-lyon.org>

> 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,36 +1,97 @@
>  #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;
> +	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 @@
>  	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 @@
>  		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 /* in micro-seconds */
>  
>  const struct old_serial_port *spk_serial_init(int index);
>  void spk_stop_serial_interrupt(void);
> 

-- 
Samuel
<y> t1 faich
<y> les programmes ils segfaultent jamais quand on veut
 -+- #ens-mim en plein d├ębogage -+-


More information about the Speakup mailing list