diff options
author | Okash Khawaja <okash.khawaja@gmail.com> | 2017-05-15 18:45:33 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-05-16 14:05:42 +0200 |
commit | 1ab92da32e37758c0e2e2a455f06d5f40609f14e (patch) | |
tree | b18ad58e8088c5b731480c2155034f6edf590281 /drivers/staging/speakup/spk_ttyio.c | |
parent | tty: export tty_open_by_driver (diff) | |
download | linux-dev-1ab92da32e37758c0e2e2a455f06d5f40609f14e.tar.xz linux-dev-1ab92da32e37758c0e2e2a455f06d5f40609f14e.zip |
staging: speakup: add tty-based comms functions
This adds spk_ttyio.c file. It contains a set of functions which implement
those methods in spk_synth struct which relate to sending bytes out using
serial comms. Implementations in this file perform the same function but
using TTY subsystem instead. Currently synths access serial ports, directly
poking standard ISA ports by trying to steal them from serial driver. Some ISA
cards actually need this way of doing it, but most other synthesizers don't,
and can actually work by using the proper TTY subsystem through a new N_SPEAKUP
line discipline. So this adds the methods for drivers to switch to accessing
serial ports through the TTY subsystem, whenever appropriate.
Signed-off-by: Okash Khawaja <okash.khawaja@gmail.com>
Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/speakup/spk_ttyio.c')
-rw-r--r-- | drivers/staging/speakup/spk_ttyio.c | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/drivers/staging/speakup/spk_ttyio.c b/drivers/staging/speakup/spk_ttyio.c new file mode 100644 index 000000000000..ee37550e59b3 --- /dev/null +++ b/drivers/staging/speakup/spk_ttyio.c @@ -0,0 +1,143 @@ +#include <linux/types.h> +#include <linux/tty.h> + +#include "speakup.h" +#include "spk_types.h" + +static struct tty_struct *speakup_tty; + +static int spk_ttyio_ldisc_open(struct tty_struct *tty) +{ + if (tty->ops->write == NULL) + return -EOPNOTSUPP; + speakup_tty = tty; + + return 0; +} + +static void spk_ttyio_ldisc_close(struct tty_struct *tty) +{ + speakup_tty = NULL; +} + +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, +}; + +static int spk_ttyio_out(struct spk_synth *in_synth, const char ch); +struct spk_io_ops spk_ttyio_ops = { + .synth_out = spk_ttyio_out, +}; +EXPORT_SYMBOL_GPL(spk_ttyio_ops); + +static int spk_ttyio_initialise_ldisc(int ser) +{ + int ret = 0; + struct tty_struct *tty; + + ret = tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops); + if (ret) { + pr_err("Error registering line discipline.\n"); + return ret; + } + + if (ser < 0 || ser > (255 - 64)) { + pr_err("speakup: Invalid ser param. Must be between 0 and 191 inclusive.\n"); + return -EINVAL; + } + + /* TODO: support more than ttyS* */ + tty = tty_open_by_driver(MKDEV(4, (ser + 64)), NULL, NULL); + if (IS_ERR(tty)) + return PTR_ERR(tty); + + if (tty->ops->open) + ret = tty->ops->open(tty, NULL); + else + ret = -ENODEV; + + if (ret) { + tty_unlock(tty); + return ret; + } + + clear_bit(TTY_HUPPED, &tty->flags); + tty_unlock(tty); + + ret = tty_set_ldisc(tty, N_SPEAKUP); + + return ret; +} + +static int spk_ttyio_out(struct spk_synth *in_synth, const char ch) +{ + if (in_synth->alive && speakup_tty && speakup_tty->ops->write) { + int ret = speakup_tty->ops->write(speakup_tty, &ch, 1); + if (ret == 0) + /* No room */ + return 0; + if (ret < 0) { + pr_warn("%s: I/O error, deactivating speakup\n", in_synth->long_name); + /* No synth any more, so nobody will restart TTYs, and we thus + * need to do it ourselves. Now that there is no synth we can + * let application flood anyway + */ + in_synth->alive = 0; + speakup_start_ttys(); + return 0; + } + return 1; + } + return 0; +} + +int spk_ttyio_synth_probe(struct spk_synth *synth) +{ + int rv = spk_ttyio_initialise_ldisc(synth->ser); + + if (rv) + return rv; + + synth->alive = 1; + + return 0; +} +EXPORT_SYMBOL_GPL(spk_ttyio_synth_probe); + +void spk_ttyio_release(void) +{ + int idx; + + if (!speakup_tty) + return; + + tty_lock(speakup_tty); + idx = speakup_tty->index; + + if (speakup_tty->ops->close) + speakup_tty->ops->close(speakup_tty, NULL); + + tty_ldisc_flush(speakup_tty); + tty_unlock(speakup_tty); + tty_ldisc_release(speakup_tty); +} +EXPORT_SYMBOL_GPL(spk_ttyio_release); + +const char *spk_ttyio_synth_immediate(struct spk_synth *synth, const char *buff) +{ + u_char ch; + + while ((ch = *buff)) { + if (ch == '\n') + ch = synth->procspeech; + if (tty_write_room(speakup_tty) < 1 || !synth->io_ops->synth_out(synth, ch)) + return buff; + buff++; + } + return NULL; +} +EXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate); |