Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/char/dtlk.c
15109 views
1
/* -*- linux-c -*-
2
* dtlk.c - DoubleTalk PC driver for Linux
3
*
4
* Original author: Chris Pallotta <[email protected]>
5
* Current maintainer: Jim Van Zandt <[email protected]>
6
*
7
* 2000-03-18 Jim Van Zandt: Fix polling.
8
* Eliminate dtlk_timer_active flag and separate dtlk_stop_timer
9
* function. Don't restart timer in dtlk_timer_tick. Restart timer
10
* in dtlk_poll after every poll. dtlk_poll returns mask (duh).
11
* Eliminate unused function dtlk_write_byte. Misc. code cleanups.
12
*/
13
14
/* This driver is for the DoubleTalk PC, a speech synthesizer
15
manufactured by RC Systems (http://www.rcsys.com/). It was written
16
based on documentation in their User's Manual file and Developer's
17
Tools disk.
18
19
The DoubleTalk PC contains four voice synthesizers: text-to-speech
20
(TTS), linear predictive coding (LPC), PCM/ADPCM, and CVSD. It
21
also has a tone generator. Output data for LPC are written to the
22
LPC port, and output data for the other modes are written to the
23
TTS port.
24
25
Two kinds of data can be read from the DoubleTalk: status
26
information (in response to the "\001?" interrogation command) is
27
read from the TTS port, and index markers (which mark the progress
28
of the speech) are read from the LPC port. Not all models of the
29
DoubleTalk PC implement index markers. Both the TTS and LPC ports
30
can also display status flags.
31
32
The DoubleTalk PC generates no interrupts.
33
34
These characteristics are mapped into the Unix stream I/O model as
35
follows:
36
37
"write" sends bytes to the TTS port. It is the responsibility of
38
the user program to switch modes among TTS, PCM/ADPCM, and CVSD.
39
This driver was written for use with the text-to-speech
40
synthesizer. If LPC output is needed some day, other minor device
41
numbers can be used to select among output modes.
42
43
"read" gets index markers from the LPC port. If the device does
44
not implement index markers, the read will fail with error EINVAL.
45
46
Status information is available using the DTLK_INTERROGATE ioctl.
47
48
*/
49
50
#include <linux/module.h>
51
52
#define KERNEL
53
#include <linux/types.h>
54
#include <linux/fs.h>
55
#include <linux/mm.h>
56
#include <linux/errno.h> /* for -EBUSY */
57
#include <linux/ioport.h> /* for request_region */
58
#include <linux/delay.h> /* for loops_per_jiffy */
59
#include <linux/sched.h>
60
#include <linux/mutex.h>
61
#include <asm/io.h> /* for inb_p, outb_p, inb, outb, etc. */
62
#include <asm/uaccess.h> /* for get_user, etc. */
63
#include <linux/wait.h> /* for wait_queue */
64
#include <linux/init.h> /* for __init, module_{init,exit} */
65
#include <linux/poll.h> /* for POLLIN, etc. */
66
#include <linux/dtlk.h> /* local header file for DoubleTalk values */
67
68
#ifdef TRACING
69
#define TRACE_TEXT(str) printk(str);
70
#define TRACE_RET printk(")")
71
#else /* !TRACING */
72
#define TRACE_TEXT(str) ((void) 0)
73
#define TRACE_RET ((void) 0)
74
#endif /* TRACING */
75
76
static DEFINE_MUTEX(dtlk_mutex);
77
static void dtlk_timer_tick(unsigned long data);
78
79
static int dtlk_major;
80
static int dtlk_port_lpc;
81
static int dtlk_port_tts;
82
static int dtlk_busy;
83
static int dtlk_has_indexing;
84
static unsigned int dtlk_portlist[] =
85
{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
86
static wait_queue_head_t dtlk_process_list;
87
static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick, 0, 0);
88
89
/* prototypes for file_operations struct */
90
static ssize_t dtlk_read(struct file *, char __user *,
91
size_t nbytes, loff_t * ppos);
92
static ssize_t dtlk_write(struct file *, const char __user *,
93
size_t nbytes, loff_t * ppos);
94
static unsigned int dtlk_poll(struct file *, poll_table *);
95
static int dtlk_open(struct inode *, struct file *);
96
static int dtlk_release(struct inode *, struct file *);
97
static long dtlk_ioctl(struct file *file,
98
unsigned int cmd, unsigned long arg);
99
100
static const struct file_operations dtlk_fops =
101
{
102
.owner = THIS_MODULE,
103
.read = dtlk_read,
104
.write = dtlk_write,
105
.poll = dtlk_poll,
106
.unlocked_ioctl = dtlk_ioctl,
107
.open = dtlk_open,
108
.release = dtlk_release,
109
.llseek = no_llseek,
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->f_path.dentry->d_inode);
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->f_path.dentry->d_inode) != 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 unsigned int dtlk_poll(struct file *file, poll_table * wait)
232
{
233
int 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
del_timer(&dtlk_timer);
247
mask = POLLIN | POLLRDNORM;
248
}
249
if (dtlk_writeable()) {
250
del_timer(&dtlk_timer);
251
mask |= POLLOUT | POLLWRNORM;
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(unsigned long data)
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
nonseekable_open(inode, file);
302
switch (iminor(inode)) {
303
case DTLK_MINOR:
304
if (dtlk_busy)
305
return -EBUSY;
306
return nonseekable_open(inode, file);
307
308
default:
309
return -ENXIO;
310
}
311
}
312
313
static int dtlk_release(struct inode *inode, struct file *file)
314
{
315
TRACE_TEXT("(dtlk_release");
316
317
switch (iminor(inode)) {
318
case DTLK_MINOR:
319
break;
320
321
default:
322
break;
323
}
324
TRACE_RET;
325
326
del_timer_sync(&dtlk_timer);
327
328
return 0;
329
}
330
331
static int __init dtlk_init(void)
332
{
333
int err;
334
335
dtlk_port_lpc = 0;
336
dtlk_port_tts = 0;
337
dtlk_busy = 0;
338
dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
339
if (dtlk_major < 0) {
340
printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
341
return dtlk_major;
342
}
343
err = dtlk_dev_probe();
344
if (err) {
345
unregister_chrdev(dtlk_major, "dtlk");
346
return err;
347
}
348
printk(", MAJOR %d\n", dtlk_major);
349
350
init_waitqueue_head(&dtlk_process_list);
351
352
return 0;
353
}
354
355
static void __exit dtlk_cleanup (void)
356
{
357
dtlk_write_bytes("goodbye", 8);
358
msleep_interruptible(500); /* nap 0.50 sec but
359
could be awakened
360
earlier by
361
signals... */
362
363
dtlk_write_tts(DTLK_CLEAR);
364
unregister_chrdev(dtlk_major, "dtlk");
365
release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
366
}
367
368
module_init(dtlk_init);
369
module_exit(dtlk_cleanup);
370
371
/* ------------------------------------------------------------------------ */
372
373
static int dtlk_readable(void)
374
{
375
#ifdef TRACING
376
printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
377
#endif
378
return inb_p(dtlk_port_lpc) != 0x7f;
379
}
380
381
static int dtlk_writeable(void)
382
{
383
/* TRACE_TEXT(" dtlk_writeable"); */
384
#ifdef TRACINGMORE
385
printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
386
#endif
387
return inb_p(dtlk_port_tts) & TTS_WRITABLE;
388
}
389
390
static int __init dtlk_dev_probe(void)
391
{
392
unsigned int testval = 0;
393
int i = 0;
394
struct dtlk_settings *sp;
395
396
if (dtlk_port_lpc | dtlk_port_tts)
397
return -EBUSY;
398
399
for (i = 0; dtlk_portlist[i]; i++) {
400
#if 0
401
printk("DoubleTalk PC - Port %03x = %04x\n",
402
dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
403
#endif
404
405
if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT,
406
"dtlk"))
407
continue;
408
testval = inw_p(dtlk_portlist[i]);
409
if ((testval &= 0xfbff) == 0x107f) {
410
dtlk_port_lpc = dtlk_portlist[i];
411
dtlk_port_tts = dtlk_port_lpc + 1;
412
413
sp = dtlk_interrogate();
414
printk("DoubleTalk PC at %03x-%03x, "
415
"ROM version %s, serial number %u",
416
dtlk_portlist[i], dtlk_portlist[i] +
417
DTLK_IO_EXTENT - 1,
418
sp->rom_version, sp->serial_number);
419
420
/* put LPC port into known state, so
421
dtlk_readable() gives valid result */
422
outb_p(0xff, dtlk_port_lpc);
423
424
/* INIT string and index marker */
425
dtlk_write_bytes("\036\1@\0\0012I\r", 8);
426
/* posting an index takes 18 msec. Here, we
427
wait up to 100 msec to see whether it
428
appears. */
429
msleep_interruptible(100);
430
dtlk_has_indexing = dtlk_readable();
431
#ifdef TRACING
432
printk(", indexing %d\n", dtlk_has_indexing);
433
#endif
434
#ifdef INSCOPE
435
{
436
/* This macro records ten samples read from the LPC port, for later display */
437
#define LOOK \
438
for (i = 0; i < 10; i++) \
439
{ \
440
buffer[b++] = inb_p(dtlk_port_lpc); \
441
__delay(loops_per_jiffy/(1000000/HZ)); \
442
}
443
char buffer[1000];
444
int b = 0, i, j;
445
446
LOOK
447
outb_p(0xff, dtlk_port_lpc);
448
buffer[b++] = 0;
449
LOOK
450
dtlk_write_bytes("\0012I\r", 4);
451
buffer[b++] = 0;
452
__delay(50 * loops_per_jiffy / (1000/HZ));
453
outb_p(0xff, dtlk_port_lpc);
454
buffer[b++] = 0;
455
LOOK
456
457
printk("\n");
458
for (j = 0; j < b; j++)
459
printk(" %02x", buffer[j]);
460
printk("\n");
461
}
462
#endif /* INSCOPE */
463
464
#ifdef OUTSCOPE
465
{
466
/* This macro records ten samples read from the TTS port, for later display */
467
#define LOOK \
468
for (i = 0; i < 10; i++) \
469
{ \
470
buffer[b++] = inb_p(dtlk_port_tts); \
471
__delay(loops_per_jiffy/(1000000/HZ)); /* 1 us */ \
472
}
473
char buffer[1000];
474
int b = 0, i, j;
475
476
mdelay(10); /* 10 ms */
477
LOOK
478
outb_p(0x03, dtlk_port_tts);
479
buffer[b++] = 0;
480
LOOK
481
LOOK
482
483
printk("\n");
484
for (j = 0; j < b; j++)
485
printk(" %02x", buffer[j]);
486
printk("\n");
487
}
488
#endif /* OUTSCOPE */
489
490
dtlk_write_bytes("Double Talk found", 18);
491
492
return 0;
493
}
494
release_region(dtlk_portlist[i], DTLK_IO_EXTENT);
495
}
496
497
printk(KERN_INFO "DoubleTalk PC - not found\n");
498
return -ENODEV;
499
}
500
501
/*
502
static void dtlk_handle_error(char op, char rc, unsigned int minor)
503
{
504
printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n",
505
minor, op, rc);
506
return;
507
}
508
*/
509
510
/* interrogate the DoubleTalk PC and return its settings */
511
static struct dtlk_settings *dtlk_interrogate(void)
512
{
513
unsigned char *t;
514
static char buf[sizeof(struct dtlk_settings) + 1];
515
int total, i;
516
static struct dtlk_settings status;
517
TRACE_TEXT("(dtlk_interrogate");
518
dtlk_write_bytes("\030\001?", 3);
519
for (total = 0, i = 0; i < 50; i++) {
520
buf[total] = dtlk_read_tts();
521
if (total > 2 && buf[total] == 0x7f)
522
break;
523
if (total < sizeof(struct dtlk_settings))
524
total++;
525
}
526
/*
527
if (i==50) printk("interrogate() read overrun\n");
528
for (i=0; i<sizeof(buf); i++)
529
printk(" %02x", buf[i]);
530
printk("\n");
531
*/
532
t = buf;
533
status.serial_number = t[0] + t[1] * 256; /* serial number is
534
little endian */
535
t += 2;
536
537
i = 0;
538
while (*t != '\r') {
539
status.rom_version[i] = *t;
540
if (i < sizeof(status.rom_version) - 1)
541
i++;
542
t++;
543
}
544
status.rom_version[i] = 0;
545
t++;
546
547
status.mode = *t++;
548
status.punc_level = *t++;
549
status.formant_freq = *t++;
550
status.pitch = *t++;
551
status.speed = *t++;
552
status.volume = *t++;
553
status.tone = *t++;
554
status.expression = *t++;
555
status.ext_dict_loaded = *t++;
556
status.ext_dict_status = *t++;
557
status.free_ram = *t++;
558
status.articulation = *t++;
559
status.reverb = *t++;
560
status.eob = *t++;
561
status.has_indexing = dtlk_has_indexing;
562
TRACE_RET;
563
return &status;
564
}
565
566
static char dtlk_read_tts(void)
567
{
568
int portval, retries = 0;
569
char ch;
570
TRACE_TEXT("(dtlk_read_tts");
571
572
/* verify DT is ready, read char, wait for ACK */
573
do {
574
portval = inb_p(dtlk_port_tts);
575
} while ((portval & TTS_READABLE) == 0 &&
576
retries++ < DTLK_MAX_RETRIES);
577
if (retries > DTLK_MAX_RETRIES)
578
printk(KERN_ERR "dtlk_read_tts() timeout\n");
579
580
ch = inb_p(dtlk_port_tts); /* input from TTS port */
581
ch &= 0x7f;
582
outb_p(ch, dtlk_port_tts);
583
584
retries = 0;
585
do {
586
portval = inb_p(dtlk_port_tts);
587
} while ((portval & TTS_READABLE) != 0 &&
588
retries++ < DTLK_MAX_RETRIES);
589
if (retries > DTLK_MAX_RETRIES)
590
printk(KERN_ERR "dtlk_read_tts() timeout\n");
591
592
TRACE_RET;
593
return ch;
594
}
595
596
static char dtlk_read_lpc(void)
597
{
598
int retries = 0;
599
char ch;
600
TRACE_TEXT("(dtlk_read_lpc");
601
602
/* no need to test -- this is only called when the port is readable */
603
604
ch = inb_p(dtlk_port_lpc); /* input from LPC port */
605
606
outb_p(0xff, dtlk_port_lpc);
607
608
/* acknowledging a read takes 3-4
609
usec. Here, we wait up to 20 usec
610
for the acknowledgement */
611
retries = (loops_per_jiffy * 20) / (1000000/HZ);
612
while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
613
if (retries == 0)
614
printk(KERN_ERR "dtlk_read_lpc() timeout\n");
615
616
TRACE_RET;
617
return ch;
618
}
619
620
/* write n bytes to tts port */
621
static char dtlk_write_bytes(const char *buf, int n)
622
{
623
char val = 0;
624
/* printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */
625
TRACE_TEXT("(dtlk_write_bytes");
626
while (n-- > 0)
627
val = dtlk_write_tts(*buf++);
628
TRACE_RET;
629
return val;
630
}
631
632
static char dtlk_write_tts(char ch)
633
{
634
int retries = 0;
635
#ifdef TRACINGMORE
636
printk(" dtlk_write_tts(");
637
if (' ' <= ch && ch <= '~')
638
printk("'%c'", ch);
639
else
640
printk("0x%02x", ch);
641
#endif
642
if (ch != DTLK_CLEAR) /* no flow control for CLEAR command */
643
while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
644
retries++ < DTLK_MAX_RETRIES) /* DT ready? */
645
;
646
if (retries > DTLK_MAX_RETRIES)
647
printk(KERN_ERR "dtlk_write_tts() timeout\n");
648
649
outb_p(ch, dtlk_port_tts); /* output to TTS port */
650
/* the RDY bit goes zero 2-3 usec after writing, and goes
651
1 again 180-190 usec later. Here, we wait up to 10
652
usec for the RDY bit to go zero. */
653
for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
654
if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
655
break;
656
657
#ifdef TRACINGMORE
658
printk(")\n");
659
#endif
660
return 0;
661
}
662
663
MODULE_LICENSE("GPL");
664
665