brintos

brintos / linux-shallow public Read only

0
0
Text · 8.8 KiB · 4c0a6e1 Raw
388 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/types.h>3#include <linux/tty.h>4#include <linux/tty_flip.h>5#include <linux/slab.h>6 7#include "speakup.h"8#include "spk_types.h"9#include "spk_priv.h"10 11struct spk_ldisc_data {12	char buf;13	struct completion completion;14	bool buf_free;15	struct spk_synth *synth;16};17 18/*19 * This allows to catch within spk_ttyio_ldisc_open whether it is getting set20 * on for a speakup-driven device.21 */22static struct tty_struct *speakup_tty;23/* This mutex serializes the use of such global speakup_tty variable */24static DEFINE_MUTEX(speakup_tty_mutex);25 26static int ser_to_dev(int ser, dev_t *dev_no)27{28	if (ser < 0 || ser > (255 - 64)) {29		pr_err("speakup: Invalid ser param. Must be between 0 and 191 inclusive.\n");30		return -EINVAL;31	}32 33	*dev_no = MKDEV(4, (64 + ser));34	return 0;35}36 37static int get_dev_to_use(struct spk_synth *synth, dev_t *dev_no)38{39	/* use ser only when dev is not specified */40	if (strcmp(synth->dev_name, SYNTH_DEFAULT_DEV) ||41	    synth->ser == SYNTH_DEFAULT_SER)42		return tty_dev_name_to_number(synth->dev_name, dev_no);43 44	return ser_to_dev(synth->ser, dev_no);45}46 47static int spk_ttyio_ldisc_open(struct tty_struct *tty)48{49	struct spk_ldisc_data *ldisc_data;50 51	if (tty != speakup_tty)52		/* Somebody tried to use this line discipline outside speakup */53		return -ENODEV;54 55	if (!tty->ops->write)56		return -EOPNOTSUPP;57 58	ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL);59	if (!ldisc_data)60		return -ENOMEM;61 62	init_completion(&ldisc_data->completion);63	ldisc_data->buf_free = true;64	tty->disc_data = ldisc_data;65 66	return 0;67}68 69static void spk_ttyio_ldisc_close(struct tty_struct *tty)70{71	kfree(tty->disc_data);72}73 74static size_t spk_ttyio_receive_buf2(struct tty_struct *tty, const u8 *cp,75				     const u8 *fp, size_t count)76{77	struct spk_ldisc_data *ldisc_data = tty->disc_data;78	struct spk_synth *synth = ldisc_data->synth;79 80	if (synth->read_buff_add) {81		unsigned int i;82 83		for (i = 0; i < count; i++)84			synth->read_buff_add(cp[i]);85 86		return count;87	}88 89	if (!ldisc_data->buf_free)90		/* ttyio_in will tty_flip_buffer_push */91		return 0;92 93	/* Make sure the consumer has read buf before we have seen94	 * buf_free == true and overwrite buf95	 */96	mb();97 98	ldisc_data->buf = cp[0];99	ldisc_data->buf_free = false;100	complete(&ldisc_data->completion);101 102	return 1;103}104 105static struct tty_ldisc_ops spk_ttyio_ldisc_ops = {106	.owner          = THIS_MODULE,107	.num		= N_SPEAKUP,108	.name           = "speakup_ldisc",109	.open           = spk_ttyio_ldisc_open,110	.close          = spk_ttyio_ldisc_close,111	.receive_buf2	= spk_ttyio_receive_buf2,112};113 114static int spk_ttyio_out(struct spk_synth *in_synth, const char ch);115static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch);116static void spk_ttyio_send_xchar(struct spk_synth *in_synth, char ch);117static void spk_ttyio_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear);118static unsigned char spk_ttyio_in(struct spk_synth *in_synth);119static unsigned char spk_ttyio_in_nowait(struct spk_synth *in_synth);120static void spk_ttyio_flush_buffer(struct spk_synth *in_synth);121static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth);122 123struct spk_io_ops spk_ttyio_ops = {124	.synth_out = spk_ttyio_out,125	.synth_out_unicode = spk_ttyio_out_unicode,126	.send_xchar = spk_ttyio_send_xchar,127	.tiocmset = spk_ttyio_tiocmset,128	.synth_in = spk_ttyio_in,129	.synth_in_nowait = spk_ttyio_in_nowait,130	.flush_buffer = spk_ttyio_flush_buffer,131	.wait_for_xmitr = spk_ttyio_wait_for_xmitr,132};133EXPORT_SYMBOL_GPL(spk_ttyio_ops);134 135static inline void get_termios(struct tty_struct *tty,136			       struct ktermios *out_termios)137{138	down_read(&tty->termios_rwsem);139	*out_termios = tty->termios;140	up_read(&tty->termios_rwsem);141}142 143static int spk_ttyio_initialise_ldisc(struct spk_synth *synth)144{145	int ret = 0;146	struct tty_struct *tty;147	struct ktermios tmp_termios;148	dev_t dev;149 150	ret = get_dev_to_use(synth, &dev);151	if (ret)152		return ret;153 154	tty = tty_kopen_exclusive(dev);155	if (IS_ERR(tty))156		return PTR_ERR(tty);157 158	if (tty->ops->open)159		ret = tty->ops->open(tty, NULL);160	else161		ret = -ENODEV;162 163	if (ret) {164		tty_unlock(tty);165		return ret;166	}167 168	clear_bit(TTY_HUPPED, &tty->flags);169	/* ensure hardware flow control is enabled */170	get_termios(tty, &tmp_termios);171	if (!(tmp_termios.c_cflag & CRTSCTS)) {172		tmp_termios.c_cflag |= CRTSCTS;173		tty_set_termios(tty, &tmp_termios);174		/*175		 * check c_cflag to see if it's updated as tty_set_termios176		 * may not return error even when no tty bits are177		 * changed by the request.178		 */179		get_termios(tty, &tmp_termios);180		if (!(tmp_termios.c_cflag & CRTSCTS))181			pr_warn("speakup: Failed to set hardware flow control\n");182	}183 184	tty_unlock(tty);185 186	mutex_lock(&speakup_tty_mutex);187	speakup_tty = tty;188	ret = tty_set_ldisc(tty, N_SPEAKUP);189	speakup_tty = NULL;190	mutex_unlock(&speakup_tty_mutex);191 192	if (!ret) {193		/* Success */194		struct spk_ldisc_data *ldisc_data = tty->disc_data;195 196		ldisc_data->synth = synth;197		synth->dev = tty;198		return 0;199	}200 201	pr_err("speakup: Failed to set N_SPEAKUP on tty\n");202 203	tty_lock(tty);204	if (tty->ops->close)205		tty->ops->close(tty, NULL);206	tty_unlock(tty);207 208	tty_kclose(tty);209 210	return ret;211}212 213void spk_ttyio_register_ldisc(void)214{215	if (tty_register_ldisc(&spk_ttyio_ldisc_ops))216		pr_warn("speakup: Error registering line discipline. Most synths won't work.\n");217}218 219void spk_ttyio_unregister_ldisc(void)220{221	tty_unregister_ldisc(&spk_ttyio_ldisc_ops);222}223 224static int spk_ttyio_out(struct spk_synth *in_synth, const char ch)225{226	struct tty_struct *tty = in_synth->dev;227	int ret;228 229	if (!in_synth->alive || !tty->ops->write)230		return 0;231 232	ret = tty->ops->write(tty, &ch, 1);233 234	if (ret == 0)235		/* No room */236		return 0;237 238	if (ret > 0)239		/* Success */240		return 1;241 242	pr_warn("%s: I/O error, deactivating speakup\n",243		in_synth->long_name);244	/* No synth any more, so nobody will restart TTYs,245	 * and we thus need to do it ourselves.  Now that there246	 * is no synth we can let application flood anyway247	 */248	in_synth->alive = 0;249	speakup_start_ttys();250	return 0;251}252 253static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch)254{255	int ret;256 257	if (ch < 0x80) {258		ret = spk_ttyio_out(in_synth, ch);259	} else if (ch < 0x800) {260		ret  = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6));261		ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));262	} else {263		ret  = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12));264		ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f));265		ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));266	}267	return ret;268}269 270static void spk_ttyio_send_xchar(struct spk_synth *in_synth, char ch)271{272	struct tty_struct *tty = in_synth->dev;273 274	if (tty->ops->send_xchar)275		tty->ops->send_xchar(tty, ch);276}277 278static void spk_ttyio_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear)279{280	struct tty_struct *tty = in_synth->dev;281 282	if (tty->ops->tiocmset)283		tty->ops->tiocmset(tty, set, clear);284}285 286static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth)287{288	return 1;289}290 291static unsigned char ttyio_in(struct spk_synth *in_synth, int timeout)292{293	struct tty_struct *tty = in_synth->dev;294	struct spk_ldisc_data *ldisc_data = tty->disc_data;295	char rv;296 297	if (!timeout) {298		if (!try_wait_for_completion(&ldisc_data->completion))299			return 0xff;300	} else if (wait_for_completion_timeout(&ldisc_data->completion,301					usecs_to_jiffies(timeout)) == 0) {302		pr_warn("spk_ttyio: timeout (%d)  while waiting for input\n",303			timeout);304		return 0xff;305	}306 307	rv = ldisc_data->buf;308	/* Make sure we have read buf before we set buf_free to let309	 * the producer overwrite it310	 */311	mb();312	ldisc_data->buf_free = true;313	/* Let TTY push more characters */314	tty_flip_buffer_push(tty->port);315 316	return rv;317}318 319static unsigned char spk_ttyio_in(struct spk_synth *in_synth)320{321	return ttyio_in(in_synth, SPK_SYNTH_TIMEOUT);322}323 324static unsigned char spk_ttyio_in_nowait(struct spk_synth *in_synth)325{326	u8 rv = ttyio_in(in_synth, 0);327 328	return (rv == 0xff) ? 0 : rv;329}330 331static void spk_ttyio_flush_buffer(struct spk_synth *in_synth)332{333	struct tty_struct *tty = in_synth->dev;334 335	if (tty->ops->flush_buffer)336		tty->ops->flush_buffer(tty);337}338 339int spk_ttyio_synth_probe(struct spk_synth *synth)340{341	int rv = spk_ttyio_initialise_ldisc(synth);342 343	if (rv)344		return rv;345 346	synth->alive = 1;347 348	return 0;349}350EXPORT_SYMBOL_GPL(spk_ttyio_synth_probe);351 352void spk_ttyio_release(struct spk_synth *in_synth)353{354	struct tty_struct *tty = in_synth->dev;355 356	if (tty == NULL)357		return;358 359	tty_lock(tty);360 361	if (tty->ops->close)362		tty->ops->close(tty, NULL);363 364	tty_ldisc_flush(tty);365	tty_unlock(tty);366	tty_kclose(tty);367 368	in_synth->dev = NULL;369}370EXPORT_SYMBOL_GPL(spk_ttyio_release);371 372const char *spk_ttyio_synth_immediate(struct spk_synth *in_synth, const char *buff)373{374	struct tty_struct *tty = in_synth->dev;375	u_char ch;376 377	while ((ch = *buff)) {378		if (ch == '\n')379			ch = in_synth->procspeech;380		if (tty_write_room(tty) < 1 ||381		    !in_synth->io_ops->synth_out(in_synth, ch))382			return buff;383		buff++;384	}385	return NULL;386}387EXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate);388