Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/accessibility/speakup/speakup_dectlk.c
26282 views
1
// SPDX-License-Identifier: GPL-2.0+
2
/*
3
* originally written by: Kirk Reiser <[email protected]>
4
* this version considerably modified by David Borowski, [email protected]
5
*
6
* Copyright (C) 1998-99 Kirk Reiser.
7
* Copyright (C) 2003 David Borowski.
8
*
9
* specifically written as a driver for the speakup screenreview
10
* s not a general device driver.
11
*/
12
#include <linux/unistd.h>
13
#include <linux/proc_fs.h>
14
#include <linux/jiffies.h>
15
#include <linux/spinlock.h>
16
#include <linux/sched.h>
17
#include <linux/timer.h>
18
#include <linux/kthread.h>
19
#include "speakup.h"
20
#include "spk_priv.h"
21
22
#define DRV_VERSION "2.20"
23
#define SYNTH_CLEAR 0x03
24
#define PROCSPEECH 0x0b
25
static int xoff;
26
27
static inline int synth_full(void)
28
{
29
return xoff;
30
}
31
32
static void do_catch_up(struct spk_synth *synth);
33
static void synth_flush(struct spk_synth *synth);
34
static void read_buff_add(u_char c);
35
static unsigned char get_index(struct spk_synth *synth);
36
37
static int in_escape;
38
static int is_flushing;
39
40
static DEFINE_SPINLOCK(flush_lock);
41
static DECLARE_WAIT_QUEUE_HEAD(flush);
42
43
enum default_vars_id {
44
CAPS_START_ID = 0, CAPS_STOP_ID,
45
RATE_ID, PITCH_ID, INFLECTION_ID,
46
VOL_ID, PUNCT_ID, VOICE_ID,
47
DIRECT_ID, V_LAST_VAR_ID,
48
NB_ID,
49
};
50
51
static struct var_t vars[NB_ID] = {
52
[CAPS_START_ID] = { CAPS_START, .u.s = {"[:dv ap 160] " } },
53
[CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"[:dv ap 100 ] " } },
54
[RATE_ID] = { RATE, .u.n = {"[:ra %d] ", 180, 75, 650, 0, 0, NULL } },
55
[PITCH_ID] = { PITCH, .u.n = {"[:dv ap %d] ", 122, 50, 350, 0, 0, NULL } },
56
[INFLECTION_ID] = { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } },
57
[VOL_ID] = { VOL, .u.n = {"[:dv g5 %d] ", 86, 60, 86, 0, 0, NULL } },
58
[PUNCT_ID] = { PUNCT, .u.n = {"[:pu %c] ", 0, 0, 2, 0, 0, "nsa" } },
59
[VOICE_ID] = { VOICE, .u.n = {"[:n%c] ", 0, 0, 9, 0, 0, "phfdburwkv" } },
60
[DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
61
V_LAST_VAR
62
};
63
64
/*
65
* These attributes will appear in /sys/accessibility/speakup/dectlk.
66
*/
67
static struct kobj_attribute caps_start_attribute =
68
__ATTR(caps_start, 0644, spk_var_show, spk_var_store);
69
static struct kobj_attribute caps_stop_attribute =
70
__ATTR(caps_stop, 0644, spk_var_show, spk_var_store);
71
static struct kobj_attribute pitch_attribute =
72
__ATTR(pitch, 0644, spk_var_show, spk_var_store);
73
static struct kobj_attribute inflection_attribute =
74
__ATTR(inflection, 0644, spk_var_show, spk_var_store);
75
static struct kobj_attribute punct_attribute =
76
__ATTR(punct, 0644, spk_var_show, spk_var_store);
77
static struct kobj_attribute rate_attribute =
78
__ATTR(rate, 0644, spk_var_show, spk_var_store);
79
static struct kobj_attribute voice_attribute =
80
__ATTR(voice, 0644, spk_var_show, spk_var_store);
81
static struct kobj_attribute vol_attribute =
82
__ATTR(vol, 0644, spk_var_show, spk_var_store);
83
84
static struct kobj_attribute delay_time_attribute =
85
__ATTR(delay_time, 0644, spk_var_show, spk_var_store);
86
static struct kobj_attribute direct_attribute =
87
__ATTR(direct, 0644, spk_var_show, spk_var_store);
88
static struct kobj_attribute full_time_attribute =
89
__ATTR(full_time, 0644, spk_var_show, spk_var_store);
90
static struct kobj_attribute flush_time_attribute =
91
__ATTR(flush_time, 0644, spk_var_show, spk_var_store);
92
static struct kobj_attribute jiffy_delta_attribute =
93
__ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store);
94
static struct kobj_attribute trigger_time_attribute =
95
__ATTR(trigger_time, 0644, spk_var_show, spk_var_store);
96
97
/*
98
* Create a group of attributes so that we can create and destroy them all
99
* at once.
100
*/
101
static struct attribute *synth_attrs[] = {
102
&caps_start_attribute.attr,
103
&caps_stop_attribute.attr,
104
&pitch_attribute.attr,
105
&inflection_attribute.attr,
106
&punct_attribute.attr,
107
&rate_attribute.attr,
108
&voice_attribute.attr,
109
&vol_attribute.attr,
110
&delay_time_attribute.attr,
111
&direct_attribute.attr,
112
&full_time_attribute.attr,
113
&flush_time_attribute.attr,
114
&jiffy_delta_attribute.attr,
115
&trigger_time_attribute.attr,
116
NULL, /* need to NULL terminate the list of attributes */
117
};
118
119
static int ap_defaults[] = {122, 89, 155, 110, 208, 240, 200, 106, 306};
120
static int g5_defaults[] = {86, 81, 86, 84, 81, 80, 83, 83, 73};
121
122
static struct spk_synth synth_dectlk = {
123
.name = "dectlk",
124
.version = DRV_VERSION,
125
.long_name = "Dectalk Express",
126
.init = "[:error sp :name paul :rate 180 :tsr off] ",
127
.procspeech = PROCSPEECH,
128
.clear = SYNTH_CLEAR,
129
.delay = 500,
130
.trigger = 50,
131
.jiffies = 50,
132
.full = 40000,
133
.flush_time = 4000,
134
.dev_name = SYNTH_DEFAULT_DEV,
135
.startup = SYNTH_START,
136
.checkval = SYNTH_CHECK,
137
.vars = vars,
138
.default_pitch = ap_defaults,
139
.default_vol = g5_defaults,
140
.io_ops = &spk_ttyio_ops,
141
.probe = spk_ttyio_synth_probe,
142
.release = spk_ttyio_release,
143
.synth_immediate = spk_ttyio_synth_immediate,
144
.catch_up = do_catch_up,
145
.flush = synth_flush,
146
.is_alive = spk_synth_is_alive_restart,
147
.synth_adjust = NULL,
148
.read_buff_add = read_buff_add,
149
.get_index = get_index,
150
.indexing = {
151
.command = "[:in re %d ] ",
152
.lowindex = 1,
153
.highindex = 8,
154
.currindex = 1,
155
},
156
.attributes = {
157
.attrs = synth_attrs,
158
.name = "dectlk",
159
},
160
};
161
162
static int is_indnum(u_char *ch)
163
{
164
if ((*ch >= '0') && (*ch <= '9')) {
165
*ch = *ch - '0';
166
return 1;
167
}
168
return 0;
169
}
170
171
static u_char lastind;
172
173
static unsigned char get_index(struct spk_synth *synth)
174
{
175
u_char rv;
176
177
rv = lastind;
178
lastind = 0;
179
return rv;
180
}
181
182
static void read_buff_add(u_char c)
183
{
184
static int ind = -1;
185
186
if (c == 0x01) {
187
unsigned long flags;
188
189
spin_lock_irqsave(&flush_lock, flags);
190
is_flushing = 0;
191
wake_up_interruptible(&flush);
192
spin_unlock_irqrestore(&flush_lock, flags);
193
} else if (c == 0x13) {
194
xoff = 1;
195
} else if (c == 0x11) {
196
xoff = 0;
197
} else if (is_indnum(&c)) {
198
if (ind == -1)
199
ind = c;
200
else
201
ind = ind * 10 + c;
202
} else if ((c > 31) && (c < 127)) {
203
if (ind != -1)
204
lastind = (u_char)ind;
205
ind = -1;
206
}
207
}
208
209
static void do_catch_up(struct spk_synth *synth)
210
{
211
int synth_full_val = 0;
212
static u_char ch;
213
static u_char last = '\0';
214
unsigned long flags;
215
unsigned long jiff_max;
216
unsigned long timeout;
217
DEFINE_WAIT(wait);
218
struct var_t *jiffy_delta;
219
struct var_t *delay_time;
220
struct var_t *flush_time;
221
int jiffy_delta_val;
222
int delay_time_val;
223
int timeout_val;
224
225
jiffy_delta = spk_get_var(JIFFY);
226
delay_time = spk_get_var(DELAY);
227
flush_time = spk_get_var(FLUSH);
228
spin_lock_irqsave(&speakup_info.spinlock, flags);
229
jiffy_delta_val = jiffy_delta->u.n.value;
230
timeout_val = flush_time->u.n.value;
231
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
232
timeout = msecs_to_jiffies(timeout_val);
233
jiff_max = jiffies + jiffy_delta_val;
234
235
while (!kthread_should_stop()) {
236
/* if no ctl-a in 4, send data anyway */
237
spin_lock_irqsave(&flush_lock, flags);
238
while (is_flushing && timeout) {
239
prepare_to_wait(&flush, &wait, TASK_INTERRUPTIBLE);
240
spin_unlock_irqrestore(&flush_lock, flags);
241
timeout = schedule_timeout(timeout);
242
spin_lock_irqsave(&flush_lock, flags);
243
}
244
finish_wait(&flush, &wait);
245
is_flushing = 0;
246
spin_unlock_irqrestore(&flush_lock, flags);
247
248
spin_lock_irqsave(&speakup_info.spinlock, flags);
249
if (speakup_info.flushing) {
250
speakup_info.flushing = 0;
251
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
252
synth->flush(synth);
253
continue;
254
}
255
synth_buffer_skip_nonlatin1();
256
if (synth_buffer_empty()) {
257
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
258
break;
259
}
260
ch = synth_buffer_peek();
261
set_current_state(TASK_INTERRUPTIBLE);
262
delay_time_val = delay_time->u.n.value;
263
synth_full_val = synth_full();
264
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
265
if (ch == '\n')
266
ch = 0x0D;
267
if (synth_full_val || !synth->io_ops->synth_out(synth, ch)) {
268
schedule_timeout(msecs_to_jiffies(delay_time_val));
269
continue;
270
}
271
set_current_state(TASK_RUNNING);
272
spin_lock_irqsave(&speakup_info.spinlock, flags);
273
synth_buffer_getc();
274
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
275
if (ch == '[') {
276
in_escape = 1;
277
} else if (ch == ']') {
278
in_escape = 0;
279
} else if (ch <= SPACE) {
280
if (!in_escape && strchr(",.!?;:", last))
281
synth->io_ops->synth_out(synth, PROCSPEECH);
282
if (time_after_eq(jiffies, jiff_max)) {
283
if (!in_escape)
284
synth->io_ops->synth_out(synth,
285
PROCSPEECH);
286
spin_lock_irqsave(&speakup_info.spinlock,
287
flags);
288
jiffy_delta_val = jiffy_delta->u.n.value;
289
delay_time_val = delay_time->u.n.value;
290
spin_unlock_irqrestore(&speakup_info.spinlock,
291
flags);
292
schedule_timeout(msecs_to_jiffies
293
(delay_time_val));
294
jiff_max = jiffies + jiffy_delta_val;
295
}
296
}
297
last = ch;
298
}
299
if (!in_escape)
300
synth->io_ops->synth_out(synth, PROCSPEECH);
301
}
302
303
static void synth_flush(struct spk_synth *synth)
304
{
305
if (in_escape)
306
/* if in command output ']' so we don't get an error */
307
synth->io_ops->synth_out(synth, ']');
308
in_escape = 0;
309
is_flushing = 1;
310
synth->io_ops->flush_buffer(synth);
311
synth->io_ops->synth_out(synth, SYNTH_CLEAR);
312
}
313
314
module_param_named(ser, synth_dectlk.ser, int, 0444);
315
module_param_named(dev, synth_dectlk.dev_name, charp, 0444);
316
module_param_named(start, synth_dectlk.startup, short, 0444);
317
module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
318
module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
319
module_param_named(inflection, vars[INFLECTION_ID].u.n.default_val, int, 0444);
320
module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
321
module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444);
322
module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444);
323
module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
324
325
326
327
MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
328
MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
329
MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
330
MODULE_PARM_DESC(rate, "Set the rate variable on load.");
331
MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
332
MODULE_PARM_DESC(inflection, "Set the inflection variable on load.");
333
MODULE_PARM_DESC(vol, "Set the vol variable on load.");
334
MODULE_PARM_DESC(punct, "Set the punct variable on load.");
335
MODULE_PARM_DESC(voice, "Set the voice variable on load.");
336
MODULE_PARM_DESC(direct, "Set the direct variable on load.");
337
338
339
module_spk_synth(synth_dectlk);
340
341
MODULE_AUTHOR("Kirk Reiser <[email protected]>");
342
MODULE_AUTHOR("David Borowski");
343
MODULE_DESCRIPTION("Speakup support for DECtalk Express synthesizers");
344
MODULE_LICENSE("GPL");
345
MODULE_VERSION(DRV_VERSION);
346
347
348