]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/staging/speakup/spk_ttyio.c
Merge tag 'iio-for-4.13b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23...
[mirror_ubuntu-artful-kernel.git] / drivers / staging / speakup / spk_ttyio.c
CommitLineData
1ab92da3
OK
1#include <linux/types.h>
2#include <linux/tty.h>
6b9ad1c7
OK
3#include <linux/tty_flip.h>
4#include <linux/slab.h>
1ab92da3
OK
5
6#include "speakup.h"
7#include "spk_types.h"
6b9ad1c7 8#include "spk_priv.h"
1ab92da3 9
6b9ad1c7
OK
10struct spk_ldisc_data {
11 char buf;
12 struct semaphore sem;
13 bool buf_free;
14};
15
16static struct spk_synth *spk_ttyio_synth;
1ab92da3
OK
17static struct tty_struct *speakup_tty;
18
19static int spk_ttyio_ldisc_open(struct tty_struct *tty)
20{
6b9ad1c7
OK
21 struct spk_ldisc_data *ldisc_data;
22
1ab92da3
OK
23 if (tty->ops->write == NULL)
24 return -EOPNOTSUPP;
25 speakup_tty = tty;
26
6b9ad1c7
OK
27 ldisc_data = kmalloc(sizeof(struct spk_ldisc_data), GFP_KERNEL);
28 if (!ldisc_data) {
29 pr_err("speakup: Failed to allocate ldisc_data.\n");
30 return -ENOMEM;
31 }
32
33 sema_init(&ldisc_data->sem, 0);
34 ldisc_data->buf_free = true;
35 speakup_tty->disc_data = ldisc_data;
36
1ab92da3
OK
37 return 0;
38}
39
40static void spk_ttyio_ldisc_close(struct tty_struct *tty)
41{
6b9ad1c7 42 kfree(speakup_tty->disc_data);
1ab92da3
OK
43 speakup_tty = NULL;
44}
45
6b9ad1c7
OK
46static int spk_ttyio_receive_buf2(struct tty_struct *tty,
47 const unsigned char *cp, char *fp, int count)
48{
49 struct spk_ldisc_data *ldisc_data = tty->disc_data;
50
51 if (spk_ttyio_synth->read_buff_add) {
52 int i;
650b175d 53
6b9ad1c7
OK
54 for (i = 0; i < count; i++)
55 spk_ttyio_synth->read_buff_add(cp[i]);
56
57 return count;
58 }
59
60 if (!ldisc_data->buf_free)
61 /* ttyio_in will tty_schedule_flip */
62 return 0;
63
64 /* Make sure the consumer has read buf before we have seen
fc0f0bd6 65 * buf_free == true and overwrite buf */
6b9ad1c7
OK
66 mb();
67
68 ldisc_data->buf = cp[0];
69 ldisc_data->buf_free = false;
70 up(&ldisc_data->sem);
71
72 return 1;
73}
74
1ab92da3
OK
75static struct tty_ldisc_ops spk_ttyio_ldisc_ops = {
76 .owner = THIS_MODULE,
77 .magic = TTY_LDISC_MAGIC,
78 .name = "speakup_ldisc",
79 .open = spk_ttyio_ldisc_open,
80 .close = spk_ttyio_ldisc_close,
6b9ad1c7 81 .receive_buf2 = spk_ttyio_receive_buf2,
1ab92da3
OK
82};
83
84static int spk_ttyio_out(struct spk_synth *in_synth, const char ch);
6b9ad1c7
OK
85static void spk_ttyio_send_xchar(char ch);
86static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear);
87static unsigned char spk_ttyio_in(void);
88static unsigned char spk_ttyio_in_nowait(void);
1c597367 89static void spk_ttyio_flush_buffer(void);
6b9ad1c7 90
1ab92da3
OK
91struct spk_io_ops spk_ttyio_ops = {
92 .synth_out = spk_ttyio_out,
6b9ad1c7
OK
93 .send_xchar = spk_ttyio_send_xchar,
94 .tiocmset = spk_ttyio_tiocmset,
95 .synth_in = spk_ttyio_in,
96 .synth_in_nowait = spk_ttyio_in_nowait,
1c597367 97 .flush_buffer = spk_ttyio_flush_buffer,
1ab92da3
OK
98};
99EXPORT_SYMBOL_GPL(spk_ttyio_ops);
100
1c597367
OK
101static inline void get_termios(struct tty_struct *tty, struct ktermios *out_termios)
102{
103 down_read(&tty->termios_rwsem);
104 *out_termios = tty->termios;
105 up_read(&tty->termios_rwsem);
106}
107
1ab92da3
OK
108static int spk_ttyio_initialise_ldisc(int ser)
109{
110 int ret = 0;
111 struct tty_struct *tty;
1c597367 112 struct ktermios tmp_termios;
1ab92da3
OK
113
114 ret = tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops);
115 if (ret) {
116 pr_err("Error registering line discipline.\n");
117 return ret;
118 }
119
120 if (ser < 0 || ser > (255 - 64)) {
121 pr_err("speakup: Invalid ser param. Must be between 0 and 191 inclusive.\n");
122 return -EINVAL;
123 }
124
125 /* TODO: support more than ttyS* */
126 tty = tty_open_by_driver(MKDEV(4, (ser + 64)), NULL, NULL);
127 if (IS_ERR(tty))
128 return PTR_ERR(tty);
129
130 if (tty->ops->open)
131 ret = tty->ops->open(tty, NULL);
132 else
133 ret = -ENODEV;
134
135 if (ret) {
136 tty_unlock(tty);
137 return ret;
138 }
139
140 clear_bit(TTY_HUPPED, &tty->flags);
1c597367
OK
141 /* ensure hardware flow control is enabled */
142 get_termios(tty, &tmp_termios);
143 if (!(tmp_termios.c_cflag & CRTSCTS)) {
144 tmp_termios.c_cflag |= CRTSCTS;
145 tty_set_termios(tty, &tmp_termios);
146 /*
147 * check c_cflag to see if it's updated as tty_set_termios may not return
148 * error even when no tty bits are changed by the request.
149 */
150 get_termios(tty, &tmp_termios);
151 if (!(tmp_termios.c_cflag & CRTSCTS))
152 pr_warn("speakup: Failed to set hardware flow control\n");
153 }
154
1ab92da3
OK
155 tty_unlock(tty);
156
157 ret = tty_set_ldisc(tty, N_SPEAKUP);
158
159 return ret;
160}
161
162static int spk_ttyio_out(struct spk_synth *in_synth, const char ch)
163{
164 if (in_synth->alive && speakup_tty && speakup_tty->ops->write) {
165 int ret = speakup_tty->ops->write(speakup_tty, &ch, 1);
650b175d 166
1ab92da3
OK
167 if (ret == 0)
168 /* No room */
169 return 0;
170 if (ret < 0) {
171 pr_warn("%s: I/O error, deactivating speakup\n", in_synth->long_name);
172 /* No synth any more, so nobody will restart TTYs, and we thus
173 * need to do it ourselves. Now that there is no synth we can
174 * let application flood anyway
175 */
176 in_synth->alive = 0;
177 speakup_start_ttys();
178 return 0;
179 }
180 return 1;
181 }
182 return 0;
183}
184
6b9ad1c7
OK
185static void spk_ttyio_send_xchar(char ch)
186{
187 speakup_tty->ops->send_xchar(speakup_tty, ch);
188}
189
190static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear)
191{
192 speakup_tty->ops->tiocmset(speakup_tty, set, clear);
193}
194
195static unsigned char ttyio_in(int timeout)
196{
197 struct spk_ldisc_data *ldisc_data = speakup_tty->disc_data;
198 char rv;
199
200 if (down_timeout(&ldisc_data->sem, usecs_to_jiffies(timeout)) == -ETIME) {
201 if (timeout)
202 pr_warn("spk_ttyio: timeout (%d) while waiting for input\n",
203 timeout);
204 return 0xff;
205 }
206
207 rv = ldisc_data->buf;
208 /* Make sure we have read buf before we set buf_free to let
209 * the producer overwrite it */
210 mb();
211 ldisc_data->buf_free = true;
212 /* Let TTY push more characters */
213 tty_schedule_flip(speakup_tty->port);
214
215 return rv;
216}
217
218static unsigned char spk_ttyio_in(void)
219{
220 return ttyio_in(SPK_SYNTH_TIMEOUT);
221}
222
223static unsigned char spk_ttyio_in_nowait(void)
224{
e45423d7 225 u8 rv = ttyio_in(0);
6b9ad1c7
OK
226
227 return (rv == 0xff) ? 0 : rv;
228}
229
1c597367
OK
230static void spk_ttyio_flush_buffer(void)
231{
011cca55
OK
232 if (speakup_tty->ops->flush_buffer)
233 speakup_tty->ops->flush_buffer(speakup_tty);
1c597367
OK
234}
235
1ab92da3
OK
236int spk_ttyio_synth_probe(struct spk_synth *synth)
237{
238 int rv = spk_ttyio_initialise_ldisc(synth->ser);
239
240 if (rv)
241 return rv;
242
243 synth->alive = 1;
6b9ad1c7 244 spk_ttyio_synth = synth;
1ab92da3
OK
245
246 return 0;
247}
248EXPORT_SYMBOL_GPL(spk_ttyio_synth_probe);
249
250void spk_ttyio_release(void)
251{
1ab92da3
OK
252 if (!speakup_tty)
253 return;
254
255 tty_lock(speakup_tty);
1ab92da3
OK
256
257 if (speakup_tty->ops->close)
258 speakup_tty->ops->close(speakup_tty, NULL);
259
260 tty_ldisc_flush(speakup_tty);
261 tty_unlock(speakup_tty);
262 tty_ldisc_release(speakup_tty);
263}
264EXPORT_SYMBOL_GPL(spk_ttyio_release);
265
266const char *spk_ttyio_synth_immediate(struct spk_synth *synth, const char *buff)
267{
268 u_char ch;
269
270 while ((ch = *buff)) {
271 if (ch == '\n')
272 ch = synth->procspeech;
273 if (tty_write_room(speakup_tty) < 1 || !synth->io_ops->synth_out(synth, ch))
274 return buff;
275 buff++;
276 }
277 return NULL;
278}
279EXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate);