Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/char/dtlk.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/* -*- linux-c -*-
3
* dtlk.c - DoubleTalk PC driver for Linux
4
*
5
* Original author: Chris Pallotta <[email protected]>
6
* Current maintainer: Jim Van Zandt <[email protected]>
7
*
8
* 2000-03-18 Jim Van Zandt: Fix polling.
9
* Eliminate dtlk_timer_active flag and separate dtlk_stop_timer
10
* function. Don't restart timer in dtlk_timer_tick. Restart timer
11
* in dtlk_poll after every poll. dtlk_poll returns mask (duh).
12
* Eliminate unused function dtlk_write_byte. Misc. code cleanups.
13
*/
14
15
/* This driver is for the DoubleTalk PC, a speech synthesizer
16
manufactured by RC Systems (http://www.rcsys.com/). It was written
17
based on documentation in their User's Manual file and Developer's
18
Tools disk.
19
20
The DoubleTalk PC contains four voice synthesizers: text-to-speech
21
(TTS), linear predictive coding (LPC), PCM/ADPCM, and CVSD. It
22
also has a tone generator. Output data for LPC are written to the
23
LPC port, and output data for the other modes are written to the
24
TTS port.
25
26
Two kinds of data can be read from the DoubleTalk: status
27
information (in response to the "\001?" interrogation command) is
28
read from the TTS port, and index markers (which mark the progress
29
of the speech) are read from the LPC port. Not all models of the
30
DoubleTalk PC implement index markers. Both the TTS and LPC ports
31
can also display status flags.
32
33
The DoubleTalk PC generates no interrupts.
34
35
These characteristics are mapped into the Unix stream I/O model as
36
follows:
37
38
"write" sends bytes to the TTS port. It is the responsibility of
39
the user program to switch modes among TTS, PCM/ADPCM, and CVSD.
40
This driver was written for use with the text-to-speech
41
synthesizer. If LPC output is needed some day, other minor device
42
numbers can be used to select among output modes.
43
44
"read" gets index markers from the LPC port. If the device does
45
not implement index markers, the read will fail with error EINVAL.
46
47
Status information is available using the DTLK_INTERROGATE ioctl.
48
49
*/
50
51
#include <linux/module.h>
52
53
#define KERNEL
54
#include <linux/types.h>
55
#include <linux/fs.h>
56
#include <linux/mm.h>
57
#include <linux/errno.h> /* for -EBUSY */
58
#include <linux/ioport.h> /* for request_region */
59
#include <linux/delay.h> /* for loops_per_jiffy */
60
#include <linux/sched.h>
61
#include <linux/mutex.h>
62
#include <asm/io.h> /* for inb_p, outb_p, inb, outb, etc. */
63
#include <linux/uaccess.h> /* for get_user, etc. */
64
#include <linux/wait.h> /* for wait_queue */
65
#include <linux/init.h> /* for __init, module_{init,exit} */
66
#include <linux/poll.h> /* for EPOLLIN, etc. */
67
#include <linux/dtlk.h> /* local header file for DoubleTalk values */
68
69
#ifdef TRACING
70
#define TRACE_TEXT(str) printk(str);
71
#define TRACE_RET printk(")")
72
#else /* !TRACING */
73
#define TRACE_TEXT(str) ((void) 0)
74
#define TRACE_RET ((void) 0)
75
#endif /* TRACING */
76
77
static DEFINE_MUTEX(dtlk_mutex);
78
static void dtlk_timer_tick(struct timer_list *unused);
79
80
static int dtlk_major;
81
static int dtlk_port_lpc;
82
static int dtlk_port_tts;
83
static int dtlk_busy;
84
static int dtlk_has_indexing;
85
static unsigned int dtlk_portlist[] =
86
{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
87
static wait_queue_head_t dtlk_process_list;
88
static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick);
89
90
/* prototypes for file_operations struct */
91
static ssize_t dtlk_read(struct file *, char __user *,
92
size_t nbytes, loff_t * ppos);
93
static ssize_t dtlk_write(struct file *, const char __user *,
94
size_t nbytes, loff_t * ppos);
95
static __poll_t dtlk_poll(struct file *, poll_table *);
96
static int dtlk_open(struct inode *, struct file *);
97
static int dtlk_release(struct inode *, struct file *);
98
static long dtlk_ioctl(struct file *file,
99
unsigned int cmd, unsigned long arg);
100
101
static const struct file_operations dtlk_fops =
102
{
103
.owner = THIS_MODULE,
104
.read = dtlk_read,
105
.write = dtlk_write,
106
.poll = dtlk_poll,
107
.unlocked_ioctl = dtlk_ioctl,
108
.open = dtlk_open,
109
.release = dtlk_release,
110
};
111
112
/* local prototypes */
113
static int dtlk_dev_probe(void);
114
static struct dtlk_settings *dtlk_interrogate(void);
115
static int dtlk_readable(void);
116
static char dtlk_read_lpc(void);
117
static char dtlk_read_tts(void);
118
static int dtlk_writeable(void);
119
static char dtlk_write_bytes(const char *buf, int n);
120
static char dtlk_write_tts(char);
121
/*
122
static void dtlk_handle_error(char, char, unsigned int);
123
*/
124
125
static ssize_t dtlk_read(struct file *file, char __user *buf,
126
size_t count, loff_t * ppos)
127
{
128
unsigned int minor = iminor(file_inode(file));
129
char ch;
130
int i = 0, retries;
131
132
TRACE_TEXT("(dtlk_read");
133
/* printk("DoubleTalk PC - dtlk_read()\n"); */
134
135
if (minor != DTLK_MINOR || !dtlk_has_indexing)
136
return -EINVAL;
137
138
for (retries = 0; retries < loops_per_jiffy; retries++) {
139
while (i < count && dtlk_readable()) {
140
ch = dtlk_read_lpc();
141
/* printk("dtlk_read() reads 0x%02x\n", ch); */
142
if (put_user(ch, buf++))
143
return -EFAULT;
144
i++;
145
}
146
if (i)
147
return i;
148
if (file->f_flags & O_NONBLOCK)
149
break;
150
msleep_interruptible(100);
151
}
152
if (retries == loops_per_jiffy)
153
printk(KERN_ERR "dtlk_read times out\n");
154
TRACE_RET;
155
return -EAGAIN;
156
}
157
158
static ssize_t dtlk_write(struct file *file, const char __user *buf,
159
size_t count, loff_t * ppos)
160
{
161
int i = 0, retries = 0, ch;
162
163
TRACE_TEXT("(dtlk_write");
164
#ifdef TRACING
165
printk(" \"");
166
{
167
int i, ch;
168
for (i = 0; i < count; i++) {
169
if (get_user(ch, buf + i))
170
return -EFAULT;
171
if (' ' <= ch && ch <= '~')
172
printk("%c", ch);
173
else
174
printk("\\%03o", ch);
175
}
176
printk("\"");
177
}
178
#endif
179
180
if (iminor(file_inode(file)) != DTLK_MINOR)
181
return -EINVAL;
182
183
while (1) {
184
while (i < count && !get_user(ch, buf) &&
185
(ch == DTLK_CLEAR || dtlk_writeable())) {
186
dtlk_write_tts(ch);
187
buf++;
188
i++;
189
if (i % 5 == 0)
190
/* We yield our time until scheduled
191
again. This reduces the transfer
192
rate to 500 bytes/sec, but that's
193
still enough to keep up with the
194
speech synthesizer. */
195
msleep_interruptible(1);
196
else {
197
/* the RDY bit goes zero 2-3 usec
198
after writing, and goes 1 again
199
180-190 usec later. Here, we wait
200
up to 250 usec for the RDY bit to
201
go nonzero. */
202
for (retries = 0;
203
retries < loops_per_jiffy / (4000/HZ);
204
retries++)
205
if (inb_p(dtlk_port_tts) &
206
TTS_WRITABLE)
207
break;
208
}
209
retries = 0;
210
}
211
if (i == count)
212
return i;
213
if (file->f_flags & O_NONBLOCK)
214
break;
215
216
msleep_interruptible(1);
217
218
if (++retries > 10 * HZ) { /* wait no more than 10 sec
219
from last write */
220
printk("dtlk: write timeout. "
221
"inb_p(dtlk_port_tts) = 0x%02x\n",
222
inb_p(dtlk_port_tts));
223
TRACE_RET;
224
return -EBUSY;
225
}
226
}
227
TRACE_RET;
228
return -EAGAIN;
229
}
230
231
static __poll_t dtlk_poll(struct file *file, poll_table * wait)
232
{
233
__poll_t mask = 0;
234
unsigned long expires;
235
236
TRACE_TEXT(" dtlk_poll");
237
/*
238
static long int j;
239
printk(".");
240
printk("<%ld>", jiffies-j);
241
j=jiffies;
242
*/
243
poll_wait(file, &dtlk_process_list, wait);
244
245
if (dtlk_has_indexing && dtlk_readable()) {
246
timer_delete(&dtlk_timer);
247
mask = EPOLLIN | EPOLLRDNORM;
248
}
249
if (dtlk_writeable()) {
250
timer_delete(&dtlk_timer);
251
mask |= EPOLLOUT | EPOLLWRNORM;
252
}
253
/* there are no exception conditions */
254
255
/* There won't be any interrupts, so we set a timer instead. */
256
expires = jiffies + 3*HZ / 100;
257
mod_timer(&dtlk_timer, expires);
258
259
return mask;
260
}
261
262
static void dtlk_timer_tick(struct timer_list *unused)
263
{
264
TRACE_TEXT(" dtlk_timer_tick");
265
wake_up_interruptible(&dtlk_process_list);
266
}
267
268
static long dtlk_ioctl(struct file *file,
269
unsigned int cmd,
270
unsigned long arg)
271
{
272
char __user *argp = (char __user *)arg;
273
struct dtlk_settings *sp;
274
char portval;
275
TRACE_TEXT(" dtlk_ioctl");
276
277
switch (cmd) {
278
279
case DTLK_INTERROGATE:
280
mutex_lock(&dtlk_mutex);
281
sp = dtlk_interrogate();
282
mutex_unlock(&dtlk_mutex);
283
if (copy_to_user(argp, sp, sizeof(struct dtlk_settings)))
284
return -EINVAL;
285
return 0;
286
287
case DTLK_STATUS:
288
portval = inb_p(dtlk_port_tts);
289
return put_user(portval, argp);
290
291
default:
292
return -EINVAL;
293
}
294
}
295
296
/* Note that nobody ever sets dtlk_busy... */
297
static int dtlk_open(struct inode *inode, struct file *file)
298
{
299
TRACE_TEXT("(dtlk_open");
300
301
switch (iminor(inode)) {
302
case DTLK_MINOR:
303
if (dtlk_busy)
304
return -EBUSY;
305
return stream_open(inode, file);
306
307
default:
308
return -ENXIO;
309
}
310
}
311
312
static int dtlk_release(struct inode *inode, struct file *file)
313
{
314
TRACE_TEXT("(dtlk_release");
315
316
switch (iminor(inode)) {
317
case DTLK_MINOR:
318
break;
319
320
default:
321
break;
322
}
323
TRACE_RET;
324
325
timer_delete_sync(&dtlk_timer);
326
327
return 0;
328
}
329
330
static int __init dtlk_init(void)
331
{
332
int err;
333
334
dtlk_port_lpc = 0;
335
dtlk_port_tts = 0;
336
dtlk_busy = 0;
337
dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
338
if (dtlk_major < 0) {
339
printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
340
return dtlk_major;
341
}
342
err = dtlk_dev_probe();
343
if (err) {
344
unregister_chrdev(dtlk_major, "dtlk");
345
return err;
346
}
347
printk(", MAJOR %d\n", dtlk_major);
348
349
init_waitqueue_head(&dtlk_process_list);
350
351
return 0;
352
}
353
354
static void __exit dtlk_cleanup (void)
355
{
356
dtlk_write_bytes("goodbye", 8);
357
msleep_interruptible(500); /* nap 0.50 sec but
358
could be awakened
359
earlier by
360
signals... */
361
362
dtlk_write_tts(DTLK_CLEAR);
363
unregister_chrdev(dtlk_major, "dtlk");
364
release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
365
}
366
367
module_init(dtlk_init);
368
module_exit(dtlk_cleanup);
369
370
/* ------------------------------------------------------------------------ */
371
372
static int dtlk_readable(void)
373
{
374
#ifdef TRACING
375
printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
376
#endif
377
return inb_p(dtlk_port_lpc) != 0x7f;
378
}
379
380
static int dtlk_writeable(void)
381
{
382
/* TRACE_TEXT(" dtlk_writeable"); */
383
#ifdef TRACINGMORE
384
printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
385
#endif
386
return inb_p(dtlk_port_tts) & TTS_WRITABLE;
387
}
388
389
static int __init dtlk_dev_probe(void)
390
{
391
unsigned int testval = 0;
392
int i = 0;
393
struct dtlk_settings *sp;
394
395
if (dtlk_port_lpc | dtlk_port_tts)
396
return -EBUSY;
397
398
for (i = 0; dtlk_portlist[i]; i++) {
399
#if 0
400
printk("DoubleTalk PC - Port %03x = %04x\n",
401
dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
402
#endif
403
404
if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT,
405
"dtlk"))
406
continue;
407
testval = inw_p(dtlk_portlist[i]);
408
if ((testval &= 0xfbff) == 0x107f) {
409
dtlk_port_lpc = dtlk_portlist[i];
410
dtlk_port_tts = dtlk_port_lpc + 1;
411
412
sp = dtlk_interrogate();
413
printk("DoubleTalk PC at %03x-%03x, "
414
"ROM version %s, serial number %u",
415
dtlk_portlist[i], dtlk_portlist[i] +
416
DTLK_IO_EXTENT - 1,
417
sp->rom_version, sp->serial_number);
418
419
/* put LPC port into known state, so
420
dtlk_readable() gives valid result */
421
outb_p(0xff, dtlk_port_lpc);
422
423
/* INIT string and index marker */
424
dtlk_write_bytes("\036\1@\0\0012I\r", 8);
425
/* posting an index takes 18 msec. Here, we
426
wait up to 100 msec to see whether it
427
appears. */
428
msleep_interruptible(100);
429
dtlk_has_indexing = dtlk_readable();
430
#ifdef TRACING
431
printk(", indexing %d\n", dtlk_has_indexing);
432
#endif
433
#ifdef INSCOPE
434
{
435
/* This macro records ten samples read from the LPC port, for later display */
436
#define LOOK \
437
for (i = 0; i < 10; i++) \
438
{ \
439
buffer[b++] = inb_p(dtlk_port_lpc); \
440
__delay(loops_per_jiffy/(1000000/HZ)); \
441
}
442
char buffer[1000];
443
int b = 0, i, j;
444
445
LOOK
446
outb_p(0xff, dtlk_port_lpc);
447
buffer[b++] = 0;
448
LOOK
449
dtlk_write_bytes("\0012I\r", 4);
450
buffer[b++] = 0;
451
__delay(50 * loops_per_jiffy / (1000/HZ));
452
outb_p(0xff, dtlk_port_lpc);
453
buffer[b++] = 0;
454
LOOK
455
456
printk("\n");
457
for (j = 0; j < b; j++)
458
printk(" %02x", buffer[j]);
459
printk("\n");
460
}
461
#endif /* INSCOPE */
462
463
#ifdef OUTSCOPE
464
{
465
/* This macro records ten samples read from the TTS port, for later display */
466
#define LOOK \
467
for (i = 0; i < 10; i++) \
468
{ \
469
buffer[b++] = inb_p(dtlk_port_tts); \
470
__delay(loops_per_jiffy/(1000000/HZ)); /* 1 us */ \
471
}
472
char buffer[1000];
473
int b = 0, i, j;
474
475
mdelay(10); /* 10 ms */
476
LOOK
477
outb_p(0x03, dtlk_port_tts);
478
buffer[b++] = 0;
479
LOOK
480
LOOK
481
482
printk("\n");
483
for (j = 0; j < b; j++)
484
printk(" %02x", buffer[j]);
485
printk("\n");
486
}
487
#endif /* OUTSCOPE */
488
489
dtlk_write_bytes("Double Talk found", 18);
490
491
return 0;
492
}
493
release_region(dtlk_portlist[i], DTLK_IO_EXTENT);
494
}
495
496
printk(KERN_INFO "DoubleTalk PC - not found\n");
497
return -ENODEV;
498
}
499
500
/*
501
static void dtlk_handle_error(char op, char rc, unsigned int minor)
502
{
503
printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n",
504
minor, op, rc);
505
return;
506
}
507
*/
508
509
/* interrogate the DoubleTalk PC and return its settings */
510
static struct dtlk_settings *dtlk_interrogate(void)
511
{
512
unsigned char *t;
513
static char buf[sizeof(struct dtlk_settings) + 1];
514
int total, i;
515
static struct dtlk_settings status;
516
TRACE_TEXT("(dtlk_interrogate");
517
dtlk_write_bytes("\030\001?", 3);
518
for (total = 0, i = 0; i < 50; i++) {
519
buf[total] = dtlk_read_tts();
520
if (total > 2 && buf[total] == 0x7f)
521
break;
522
if (total < sizeof(struct dtlk_settings))
523
total++;
524
}
525
/*
526
if (i==50) printk("interrogate() read overrun\n");
527
for (i=0; i<sizeof(buf); i++)
528
printk(" %02x", buf[i]);
529
printk("\n");
530
*/
531
t = buf;
532
status.serial_number = t[0] + t[1] * 256; /* serial number is
533
little endian */
534
t += 2;
535
536
i = 0;
537
while (*t != '\r') {
538
status.rom_version[i] = *t;
539
if (i < sizeof(status.rom_version) - 1)
540
i++;
541
t++;
542
}
543
status.rom_version[i] = 0;
544
t++;
545
546
status.mode = *t++;
547
status.punc_level = *t++;
548
status.formant_freq = *t++;
549
status.pitch = *t++;
550
status.speed = *t++;
551
status.volume = *t++;
552
status.tone = *t++;
553
status.expression = *t++;
554
status.ext_dict_loaded = *t++;
555
status.ext_dict_status = *t++;
556
status.free_ram = *t++;
557
status.articulation = *t++;
558
status.reverb = *t++;
559
status.eob = *t++;
560
status.has_indexing = dtlk_has_indexing;
561
TRACE_RET;
562
return &status;
563
}
564
565
static char dtlk_read_tts(void)
566
{
567
int portval, retries = 0;
568
char ch;
569
TRACE_TEXT("(dtlk_read_tts");
570
571
/* verify DT is ready, read char, wait for ACK */
572
do {
573
portval = inb_p(dtlk_port_tts);
574
} while ((portval & TTS_READABLE) == 0 &&
575
retries++ < DTLK_MAX_RETRIES);
576
if (retries > DTLK_MAX_RETRIES)
577
printk(KERN_ERR "dtlk_read_tts() timeout\n");
578
579
ch = inb_p(dtlk_port_tts); /* input from TTS port */
580
ch &= 0x7f;
581
outb_p(ch, dtlk_port_tts);
582
583
retries = 0;
584
do {
585
portval = inb_p(dtlk_port_tts);
586
} while ((portval & TTS_READABLE) != 0 &&
587
retries++ < DTLK_MAX_RETRIES);
588
if (retries > DTLK_MAX_RETRIES)
589
printk(KERN_ERR "dtlk_read_tts() timeout\n");
590
591
TRACE_RET;
592
return ch;
593
}
594
595
static char dtlk_read_lpc(void)
596
{
597
int retries = 0;
598
char ch;
599
TRACE_TEXT("(dtlk_read_lpc");
600
601
/* no need to test -- this is only called when the port is readable */
602
603
ch = inb_p(dtlk_port_lpc); /* input from LPC port */
604
605
outb_p(0xff, dtlk_port_lpc);
606
607
/* acknowledging a read takes 3-4
608
usec. Here, we wait up to 20 usec
609
for the acknowledgement */
610
retries = (loops_per_jiffy * 20) / (1000000/HZ);
611
while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
612
if (retries == 0)
613
printk(KERN_ERR "dtlk_read_lpc() timeout\n");
614
615
TRACE_RET;
616
return ch;
617
}
618
619
/* write n bytes to tts port */
620
static char dtlk_write_bytes(const char *buf, int n)
621
{
622
char val = 0;
623
/* printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */
624
TRACE_TEXT("(dtlk_write_bytes");
625
while (n-- > 0)
626
val = dtlk_write_tts(*buf++);
627
TRACE_RET;
628
return val;
629
}
630
631
static char dtlk_write_tts(char ch)
632
{
633
int retries = 0;
634
#ifdef TRACINGMORE
635
printk(" dtlk_write_tts(");
636
if (' ' <= ch && ch <= '~')
637
printk("'%c'", ch);
638
else
639
printk("0x%02x", ch);
640
#endif
641
if (ch != DTLK_CLEAR) /* no flow control for CLEAR command */
642
while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
643
retries++ < DTLK_MAX_RETRIES) /* DT ready? */
644
;
645
if (retries > DTLK_MAX_RETRIES)
646
printk(KERN_ERR "dtlk_write_tts() timeout\n");
647
648
outb_p(ch, dtlk_port_tts); /* output to TTS port */
649
/* the RDY bit goes zero 2-3 usec after writing, and goes
650
1 again 180-190 usec later. Here, we wait up to 10
651
usec for the RDY bit to go zero. */
652
for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
653
if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
654
break;
655
656
#ifdef TRACINGMORE
657
printk(")\n");
658
#endif
659
return 0;
660
}
661
662
MODULE_DESCRIPTION("RC Systems DoubleTalk PC speech card driver");
663
MODULE_LICENSE("GPL");
664
665