GNU Linux-libre 4.19.286-gnu1
[releases.git] / drivers / staging / speakup / speakup_keypc.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * written by David Borowski
4  *
5  * Copyright (C) 2003 David Borowski.
6  *
7  * specificly written as a driver for the speakup screenreview
8  * package it's not a general device driver.
9  * This driver is for the Keynote Gold internal synthesizer.
10  */
11 #include <linux/jiffies.h>
12 #include <linux/sched.h>
13 #include <linux/timer.h>
14 #include <linux/kthread.h>
15 #include <linux/serial_reg.h>
16
17 #include "spk_priv.h"
18 #include "speakup.h"
19
20 #define DRV_VERSION "2.10"
21 #define SYNTH_IO_EXTENT 0x04
22 #define SWAIT udelay(70)
23 #define PROCSPEECH 0x1f
24 #define SYNTH_CLEAR 0x03
25
26 static int synth_probe(struct spk_synth *synth);
27 static void keynote_release(void);
28 static const char *synth_immediate(struct spk_synth *synth, const char *buf);
29 static void do_catch_up(struct spk_synth *synth);
30 static void synth_flush(struct spk_synth *synth);
31
32 static int synth_port;
33 static int port_forced;
34 static unsigned int synth_portlist[] = { 0x2a8, 0 };
35
36 static struct var_t vars[] = {
37         { CAPS_START, .u.s = {"[f130]" } },
38         { CAPS_STOP, .u.s = {"[f90]" } },
39         { RATE, .u.n = {"\04%c ", 8, 0, 10, 81, -8, NULL } },
40         { PITCH, .u.n = {"[f%d]", 5, 0, 9, 40, 10, NULL } },
41         { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
42         V_LAST_VAR
43 };
44
45 /*
46  * These attributes will appear in /sys/accessibility/speakup/keypc.
47  */
48 static struct kobj_attribute caps_start_attribute =
49         __ATTR(caps_start, 0644, spk_var_show, spk_var_store);
50 static struct kobj_attribute caps_stop_attribute =
51         __ATTR(caps_stop, 0644, spk_var_show, spk_var_store);
52 static struct kobj_attribute pitch_attribute =
53         __ATTR(pitch, 0644, spk_var_show, spk_var_store);
54 static struct kobj_attribute rate_attribute =
55         __ATTR(rate, 0644, spk_var_show, spk_var_store);
56
57 static struct kobj_attribute delay_time_attribute =
58         __ATTR(delay_time, 0644, spk_var_show, spk_var_store);
59 static struct kobj_attribute direct_attribute =
60         __ATTR(direct, 0644, spk_var_show, spk_var_store);
61 static struct kobj_attribute full_time_attribute =
62         __ATTR(full_time, 0644, spk_var_show, spk_var_store);
63 static struct kobj_attribute jiffy_delta_attribute =
64         __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store);
65 static struct kobj_attribute trigger_time_attribute =
66         __ATTR(trigger_time, 0644, spk_var_show, spk_var_store);
67
68 /*
69  * Create a group of attributes so that we can create and destroy them all
70  * at once.
71  */
72 static struct attribute *synth_attrs[] = {
73         &caps_start_attribute.attr,
74         &caps_stop_attribute.attr,
75         &pitch_attribute.attr,
76         &rate_attribute.attr,
77         &delay_time_attribute.attr,
78         &direct_attribute.attr,
79         &full_time_attribute.attr,
80         &jiffy_delta_attribute.attr,
81         &trigger_time_attribute.attr,
82         NULL,   /* need to NULL terminate the list of attributes */
83 };
84
85 static struct spk_synth synth_keypc = {
86         .name = "keypc",
87         .version = DRV_VERSION,
88         .long_name = "Keynote PC",
89         .init = "[t][n7,1][n8,0]",
90         .procspeech = PROCSPEECH,
91         .clear = SYNTH_CLEAR,
92         .delay = 500,
93         .trigger = 50,
94         .jiffies = 50,
95         .full = 1000,
96         .startup = SYNTH_START,
97         .checkval = SYNTH_CHECK,
98         .vars = vars,
99         .io_ops = &spk_serial_io_ops,
100         .probe = synth_probe,
101         .release = keynote_release,
102         .synth_immediate = synth_immediate,
103         .catch_up = do_catch_up,
104         .flush = synth_flush,
105         .is_alive = spk_synth_is_alive_nop,
106         .synth_adjust = NULL,
107         .read_buff_add = NULL,
108         .get_index = NULL,
109         .indexing = {
110                 .command = NULL,
111                 .lowindex = 0,
112                 .highindex = 0,
113                 .currindex = 0,
114         },
115         .attributes = {
116                 .attrs = synth_attrs,
117                 .name = "keypc",
118         },
119 };
120
121 static inline bool synth_writable(void)
122 {
123         return (inb_p(synth_port + UART_RX) & 0x10) != 0;
124 }
125
126 static inline bool synth_full(void)
127 {
128         return (inb_p(synth_port + UART_RX) & 0x80) == 0;
129 }
130
131 static char *oops(void)
132 {
133         int s1, s2, s3, s4;
134
135         s1 = inb_p(synth_port);
136         s2 = inb_p(synth_port + 1);
137         s3 = inb_p(synth_port + 2);
138         s4 = inb_p(synth_port + 3);
139         pr_warn("synth timeout %d %d %d %d\n", s1, s2, s3, s4);
140         return NULL;
141 }
142
143 static const char *synth_immediate(struct spk_synth *synth, const char *buf)
144 {
145         u_char ch;
146         int timeout;
147
148         while ((ch = *buf)) {
149                 if (ch == '\n')
150                         ch = PROCSPEECH;
151                 if (synth_full())
152                         return buf;
153                 timeout = 1000;
154                 while (synth_writable())
155                         if (--timeout <= 0)
156                                 return oops();
157                 outb_p(ch, synth_port);
158                 udelay(70);
159                 buf++;
160         }
161         return NULL;
162 }
163
164 static void do_catch_up(struct spk_synth *synth)
165 {
166         u_char ch;
167         int timeout;
168         unsigned long flags;
169         unsigned long jiff_max;
170         struct var_t *jiffy_delta;
171         struct var_t *delay_time;
172         struct var_t *full_time;
173         int delay_time_val;
174         int full_time_val;
175         int jiffy_delta_val;
176
177         jiffy_delta = spk_get_var(JIFFY);
178         delay_time = spk_get_var(DELAY);
179         full_time = spk_get_var(FULL);
180 spin_lock_irqsave(&speakup_info.spinlock, flags);
181         jiffy_delta_val = jiffy_delta->u.n.value;
182         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
183
184         jiff_max = jiffies + jiffy_delta_val;
185         while (!kthread_should_stop()) {
186                 spin_lock_irqsave(&speakup_info.spinlock, flags);
187                 if (speakup_info.flushing) {
188                         speakup_info.flushing = 0;
189                         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
190                         synth->flush(synth);
191                         continue;
192                 }
193                 synth_buffer_skip_nonlatin1();
194                 if (synth_buffer_empty()) {
195                         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
196                         break;
197                 }
198                 set_current_state(TASK_INTERRUPTIBLE);
199                 full_time_val = full_time->u.n.value;
200                 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
201                 if (synth_full()) {
202                         schedule_timeout(msecs_to_jiffies(full_time_val));
203                         continue;
204                 }
205                 set_current_state(TASK_RUNNING);
206                 timeout = 1000;
207                 while (synth_writable())
208                         if (--timeout <= 0)
209                                 break;
210                 if (timeout <= 0) {
211                         oops();
212                         break;
213                 }
214                 spin_lock_irqsave(&speakup_info.spinlock, flags);
215                 ch = synth_buffer_getc();
216                 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
217                 if (ch == '\n')
218                         ch = PROCSPEECH;
219                 outb_p(ch, synth_port);
220                 SWAIT;
221                 if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) {
222                         timeout = 1000;
223                         while (synth_writable())
224                                 if (--timeout <= 0)
225                                         break;
226                         if (timeout <= 0) {
227                                 oops();
228                                 break;
229                         }
230                         outb_p(PROCSPEECH, synth_port);
231                         spin_lock_irqsave(&speakup_info.spinlock, flags);
232                         jiffy_delta_val = jiffy_delta->u.n.value;
233                         delay_time_val = delay_time->u.n.value;
234                         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
235                         schedule_timeout(msecs_to_jiffies(delay_time_val));
236                         jiff_max = jiffies+jiffy_delta_val;
237                 }
238         }
239         timeout = 1000;
240         while (synth_writable())
241                 if (--timeout <= 0)
242                         break;
243         if (timeout <= 0)
244                 oops();
245         else
246                 outb_p(PROCSPEECH, synth_port);
247 }
248
249 static void synth_flush(struct spk_synth *synth)
250 {
251         outb_p(SYNTH_CLEAR, synth_port);
252 }
253
254 static int synth_probe(struct spk_synth *synth)
255 {
256         unsigned int port_val = 0;
257         int i = 0;
258
259         pr_info("Probing for %s.\n", synth->long_name);
260         if (port_forced) {
261                 synth_port = port_forced;
262                 pr_info("probe forced to %x by kernel command line\n",
263                         synth_port);
264                 if (synth_request_region(synth_port-1, SYNTH_IO_EXTENT)) {
265                         pr_warn("sorry, port already reserved\n");
266                         return -EBUSY;
267                 }
268                 port_val = inb(synth_port);
269         } else {
270                 for (i = 0; synth_portlist[i]; i++) {
271                         if (synth_request_region(synth_portlist[i],
272                                                  SYNTH_IO_EXTENT)) {
273                                 pr_warn
274                                     ("request_region: failed with 0x%x, %d\n",
275                                      synth_portlist[i], SYNTH_IO_EXTENT);
276                                 continue;
277                         }
278                         port_val = inb(synth_portlist[i]);
279                         if (port_val == 0x80) {
280                                 synth_port = synth_portlist[i];
281                                 break;
282                         }
283                 }
284         }
285         if (port_val != 0x80) {
286                 pr_info("%s: not found\n", synth->long_name);
287                 synth_release_region(synth_port, SYNTH_IO_EXTENT);
288                 synth_port = 0;
289                 return -ENODEV;
290         }
291         pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name,
292                 synth_port, synth_port+SYNTH_IO_EXTENT-1,
293                 synth->version);
294         synth->alive = 1;
295         return 0;
296 }
297
298 static void keynote_release(void)
299 {
300         spk_stop_serial_interrupt();
301         if (synth_port)
302                 synth_release_region(synth_port, SYNTH_IO_EXTENT);
303         synth_port = 0;
304 }
305
306 module_param_hw_named(port, port_forced, int, ioport, 0444);
307 module_param_named(start, synth_keypc.startup, short, 0444);
308
309 MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");
310 MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
311
312 module_spk_synth(synth_keypc);
313
314 MODULE_AUTHOR("David Borowski");
315 MODULE_DESCRIPTION("Speakup support for Keynote Gold PC synthesizers");
316 MODULE_LICENSE("GPL");
317 MODULE_VERSION(DRV_VERSION);
318