1 /* -*- linux-c -*-
2 * dtlk.c - DoubleTalk PC driver for Linux
3 *
4 * Original author: Chris Pallotta <chris@allmedia.com>
5 * Current maintainer: Jim Van Zandt <jrv@vanzandt.mv.com>
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 #ifdef MODVERSIONS
51 #include <linux/modversions.h>
52 #endif
53
54 #include <linux/module.h>
55 #include <linux/version.h>
56
57 #define KERNEL
58 #include <linux/types.h>
59 #include <linux/fs.h>
60 #include <linux/mm.h> /* for verify_area */
61 #include <linux/errno.h> /* for -EBUSY */
62 #include <linux/ioport.h> /* for check_region, request_region */
63 #include <linux/delay.h> /* for loops_per_jiffy */
64 #include <asm/segment.h> /* for put_user_byte */
65 #include <asm/io.h> /* for inb_p, outb_p, inb, outb, etc. */
66 #include <asm/uaccess.h> /* for get_user, etc. */
67 #include <linux/wait.h> /* for wait_queue */
68 #include <linux/init.h> /* for __init, module_{init,exit} */
69 #include <linux/poll.h> /* for POLLIN, etc. */
70 #include <linux/dtlk.h> /* local header file for DoubleTalk values */
71 #include <linux/devfs_fs_kernel.h>
72 #include <linux/smp_lock.h>
73
74 #ifdef TRACING
75 #define TRACE_TEXT(str) printk(str);
76 #define TRACE_RET printk(")")
77 #else /* !TRACING */
78 #define TRACE_TEXT(str) ((void) 0)
79 #define TRACE_RET ((void) 0)
80 #endif /* TRACING */
81
82
83 static int dtlk_major;
84 static int dtlk_port_lpc;
85 static int dtlk_port_tts;
86 static int dtlk_busy;
87 static int dtlk_has_indexing;
88 static unsigned int dtlk_portlist[] =
89 {0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
90 static wait_queue_head_t dtlk_process_list;
91 static struct timer_list dtlk_timer;
92
93 /* prototypes for file_operations struct */
94 static ssize_t dtlk_read(struct file *, char *,
95 size_t nbytes, loff_t * ppos);
96 static ssize_t dtlk_write(struct file *, const char *,
97 size_t nbytes, loff_t * ppos);
98 static unsigned int dtlk_poll(struct file *, poll_table *);
99 static int dtlk_open(struct inode *, struct file *);
100 static int dtlk_release(struct inode *, struct file *);
101 static int dtlk_ioctl(struct inode *inode, struct file *file,
102 unsigned int cmd, unsigned long arg);
103
104 static struct file_operations dtlk_fops =
105 {
106 owner: THIS_MODULE,
107 read: dtlk_read,
108 write: dtlk_write,
109 poll: dtlk_poll,
110 ioctl: dtlk_ioctl,
111 open: dtlk_open,
112 release: dtlk_release,
113 };
114
115 /* local prototypes */
116 static void dtlk_delay(int ms);
117 static int dtlk_dev_probe(void);
118 static struct dtlk_settings *dtlk_interrogate(void);
119 static int dtlk_readable(void);
120 static char dtlk_read_lpc(void);
121 static char dtlk_read_tts(void);
122 static int dtlk_writeable(void);
123 static char dtlk_write_bytes(const char *buf, int n);
124 static char dtlk_write_tts(char);
125 /*
126 static void dtlk_handle_error(char, char, unsigned int);
127 */
128 static void dtlk_timer_tick(unsigned long data);
129
130 static ssize_t dtlk_read(struct file *file, char *buf,
131 size_t count, loff_t * ppos)
132 {
133 unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
134 char ch;
135 int retval, i = 0, retries;
136
137 /* Can't seek (pread) on the DoubleTalk. */
138 if (ppos != &file->f_pos)
139 return -ESPIPE;
140
141 TRACE_TEXT("(dtlk_read");
142 /* printk("DoubleTalk PC - dtlk_read()\n"); */
143
144 if (minor != DTLK_MINOR || !dtlk_has_indexing)
145 return -EINVAL;
146
147 for (retries = 0; retries < loops_per_jiffy; retries++) {
148 while (i < count && dtlk_readable()) {
149 ch = dtlk_read_lpc();
150 /* printk("dtlk_read() reads 0x%02x\n", ch); */
151 if ((retval = put_user(ch, buf++)))
152 return retval;
153 i++;
154 }
155 if (i)
156 return i;
157 if (file->f_flags & O_NONBLOCK)
158 break;
159 dtlk_delay(100);
160 }
161 if (retries == loops_per_jiffy)
162 printk(KERN_ERR "dtlk_read times out\n");
163 TRACE_RET;
164 return -EAGAIN;
165 }
166
167 static ssize_t dtlk_write(struct file *file, const char *buf,
168 size_t count, loff_t * ppos)
169 {
170 int i = 0, retries = 0, err, ch;
171
172 TRACE_TEXT("(dtlk_write");
173 #ifdef TRACING
174 printk(" \"");
175 {
176 int i, ch;
177 for (i = 0; i < count; i++) {
178 err = get_user(ch, buf + i);
179 if (' ' <= ch && ch <= '~')
180 printk("%c", ch);
181 else
182 printk("\\%03o", ch);
183 }
184 printk("\"");
185 }
186 #endif
187
188 /* Can't seek (pwrite) on the DoubleTalk. */
189 if (ppos != &file->f_pos)
190 return -ESPIPE;
191
192 if (MINOR(file->f_dentry->d_inode->i_rdev) != DTLK_MINOR)
193 return -EINVAL;
194
195 while (1) {
196 while (i < count && (err = get_user(ch, buf)) == 0 &&
197 (ch == DTLK_CLEAR || dtlk_writeable())) {
198 dtlk_write_tts(ch);
199 buf++;
200 i++;
201 if (i % 5 == 0)
202 /* We yield our time until scheduled
203 again. This reduces the transfer
204 rate to 500 bytes/sec, but that's
205 still enough to keep up with the
206 speech synthesizer. */
207 dtlk_delay(1);
208 else {
209 /* the RDY bit goes zero 2-3 usec
210 after writing, and goes 1 again
211 180-190 usec later. Here, we wait
212 up to 250 usec for the RDY bit to
213 go nonzero. */
214 for (retries = 0;
215 retries < loops_per_jiffy / (4000/HZ);
216 retries++)
217 if (inb_p(dtlk_port_tts) &
218 TTS_WRITABLE)
219 break;
220 }
221 retries = 0;
222 }
223 if (i == count)
224 return i;
225 if (file->f_flags & O_NONBLOCK)
226 break;
227
228 dtlk_delay(1);
229
230 if (++retries > 10 * HZ) { /* wait no more than 10 sec
231 from last write */
232 printk("dtlk: write timeout. "
233 "inb_p(dtlk_port_tts) = 0x%02x\n",
234 inb_p(dtlk_port_tts));
235 TRACE_RET;
236 return -EBUSY;
237 }
238 }
239 TRACE_RET;
240 return -EAGAIN;
241 }
242
243 static unsigned int dtlk_poll(struct file *file, poll_table * wait)
244 {
245 int mask = 0;
246 unsigned long expires;
247
248 TRACE_TEXT(" dtlk_poll");
249 /*
250 static long int j;
251 printk(".");
252 printk("<%ld>", jiffies-j);
253 j=jiffies;
254 */
255 poll_wait(file, &dtlk_process_list, wait);
256
257 if (dtlk_has_indexing && dtlk_readable()) {
258 del_timer(&dtlk_timer);
259 mask = POLLIN | POLLRDNORM;
260 }
261 if (dtlk_writeable()) {
262 del_timer(&dtlk_timer);
263 mask |= POLLOUT | POLLWRNORM;
264 }
265 /* there are no exception conditions */
266
267 /* There won't be any interrupts, so we set a timer instead. */
268 expires = jiffies + 3*HZ / 100;
269 mod_timer(&dtlk_timer, expires);
270
271 return mask;
272 }
273
274 static void dtlk_timer_tick(unsigned long data)
275 {
276 TRACE_TEXT(" dtlk_timer_tick");
277 wake_up_interruptible(&dtlk_process_list);
278 }
279
280 static int dtlk_ioctl(struct inode *inode,
281 struct file *file,
282 unsigned int cmd,
283 unsigned long arg)
284 {
285 struct dtlk_settings *sp;
286 int err;
287 char portval;
288 TRACE_TEXT(" dtlk_ioctl");
289
290 switch (cmd) {
291
292 case DTLK_INTERROGATE:
293 sp = dtlk_interrogate();
294 err = copy_to_user((char *) arg, (char *) sp,
295 sizeof(struct dtlk_settings));
296 if (err)
297 return -EINVAL;
298 return 0;
299
300 case DTLK_STATUS:
301 portval = inb_p(dtlk_port_tts);
302 return put_user(portval, (char *) arg);
303
304 default:
305 return -EINVAL;
306 }
307 }
308
309 static int dtlk_open(struct inode *inode, struct file *file)
310 {
311 TRACE_TEXT("(dtlk_open");
312
313 switch (MINOR(inode->i_rdev)) {
314 case DTLK_MINOR:
315 if (dtlk_busy)
316 return -EBUSY;
317 return 0;
318
319 default:
320 return -ENXIO;
321 }
322 }
323
324 static int dtlk_release(struct inode *inode, struct file *file)
325 {
326 TRACE_TEXT("(dtlk_release");
327
328 switch (MINOR(inode->i_rdev)) {
329 case DTLK_MINOR:
330 break;
331
332 default:
333 break;
334 }
335 TRACE_RET;
336
337 lock_kernel();
338 del_timer(&dtlk_timer);
339 unlock_kernel();
340
341 return 0;
342 }
343
344 static devfs_handle_t devfs_handle;
345
346 static int __init dtlk_init(void)
347 {
348 dtlk_port_lpc = 0;
349 dtlk_port_tts = 0;
350 dtlk_busy = 0;
351 dtlk_major = devfs_register_chrdev(0, "dtlk", &dtlk_fops);
352 if (dtlk_major == 0) {
353 printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
354 return 0;
355 }
356 if (dtlk_dev_probe() == 0)
357 printk(", MAJOR %d\n", dtlk_major);
358 devfs_handle = devfs_register (NULL, "dtlk", DEVFS_FL_DEFAULT,
359 dtlk_major, DTLK_MINOR,
360 S_IFCHR | S_IRUSR | S_IWUSR,
361 &dtlk_fops, NULL);
362
363 init_timer(&dtlk_timer);
364 dtlk_timer.function = dtlk_timer_tick;
365 init_waitqueue_head(&dtlk_process_list);
366
367 return 0;
368 }
369
370 static void __exit dtlk_cleanup (void)
371 {
372 dtlk_write_bytes("goodbye", 8);
373 current->state = TASK_INTERRUPTIBLE;
374 schedule_timeout(5 * HZ / 10); /* nap 0.50 sec but
375 could be awakened
376 earlier by
377 signals... */
378
379 dtlk_write_tts(DTLK_CLEAR);
380 devfs_unregister_chrdev(dtlk_major, "dtlk");
381 devfs_unregister(devfs_handle);
382 release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
383 }
384
385 module_init(dtlk_init);
386 module_exit(dtlk_cleanup);
387
388 /* ------------------------------------------------------------------------ */
389
390 /* sleep for ms milliseconds */
391 static void dtlk_delay(int ms)
392 {
393 current->state = TASK_INTERRUPTIBLE;
394 schedule_timeout((ms * HZ + 1000 - HZ) / 1000);
395 }
396
397 static int dtlk_readable(void)
398 {
399 #ifdef TRACING
400 printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
401 #endif
402 return inb_p(dtlk_port_lpc) != 0x7f;
403 }
404
405 static int dtlk_writeable(void)
406 {
407 /* TRACE_TEXT(" dtlk_writeable"); */
408 #ifdef TRACINGMORE
409 printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
410 #endif
411 return inb_p(dtlk_port_tts) & TTS_WRITABLE;
412 }
413
414 static int __init dtlk_dev_probe(void)
415 {
416 unsigned int testval = 0;
417 int i = 0;
418 struct dtlk_settings *sp;
419
420 if (dtlk_port_lpc | dtlk_port_tts)
421 return -EBUSY;
422
423 for (i = 0; dtlk_portlist[i]; i++) {
424 #if 0
425 printk("DoubleTalk PC - Port %03x = %04x\n",
426 dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
427 #endif
428
429 if (check_region(dtlk_portlist[i], DTLK_IO_EXTENT))
430 continue;
431 testval = inw_p(dtlk_portlist[i]);
432 if ((testval &= 0xfbff) == 0x107f) {
433 request_region(dtlk_portlist[i], DTLK_IO_EXTENT,
434 "dtlk");
435 dtlk_port_lpc = dtlk_portlist[i];
436 dtlk_port_tts = dtlk_port_lpc + 1;
437
438 sp = dtlk_interrogate();
439 printk("DoubleTalk PC at %03x-%03x, "
440 "ROM version %s, serial number %u",
441 dtlk_portlist[i], dtlk_portlist[i] +
442 DTLK_IO_EXTENT - 1,
443 sp->rom_version, sp->serial_number);
444
445 /* put LPC port into known state, so
446 dtlk_readable() gives valid result */
447 outb_p(0xff, dtlk_port_lpc);
448
449 /* INIT string and index marker */
450 dtlk_write_bytes("\036\1@\0\0012I\r", 8);
451 /* posting an index takes 18 msec. Here, we
452 wait up to 100 msec to see whether it
453 appears. */
454 dtlk_delay(100);
455 dtlk_has_indexing = dtlk_readable();
456 #ifdef TRACING
457 printk(", indexing %d\n", dtlk_has_indexing);
458 #endif
459 #ifdef INSCOPE
460 {
461 /* This macro records ten samples read from the LPC port, for later display */
462 #define LOOK \
463 for (i = 0; i < 10; i++) \
464 { \
465 buffer[b++] = inb_p(dtlk_port_lpc); \
466 __delay(loops_per_jiffy/(1000000/HZ)); \
467 }
468 char buffer[1000];
469 int b = 0, i, j;
470
471 LOOK
472 outb_p(0xff, dtlk_port_lpc);
473 buffer[b++] = 0;
474 LOOK
475 dtlk_write_bytes("\0012I\r", 4);
476 buffer[b++] = 0;
477 __delay(50 * loops_per_jiffy / (1000/HZ));
478 outb_p(0xff, dtlk_port_lpc);
479 buffer[b++] = 0;
480 LOOK
481
482 printk("\n");
483 for (j = 0; j < b; j++)
484 printk(" %02x", buffer[j]);
485 printk("\n");
486 }
487 #endif /* INSCOPE */
488
489 #ifdef OUTSCOPE
490 {
491 /* This macro records ten samples read from the TTS port, for later display */
492 #define LOOK \
493 for (i = 0; i < 10; i++) \
494 { \
495 buffer[b++] = inb_p(dtlk_port_tts); \
496 __delay(loops_per_jiffy/(1000000/HZ)); /* 1 us */ \
497 }
498 char buffer[1000];
499 int b = 0, i, j;
500
501 mdelay(10); /* 10 ms */
502 LOOK
503 outb_p(0x03, dtlk_port_tts);
504 buffer[b++] = 0;
505 LOOK
506 LOOK
507
508 printk("\n");
509 for (j = 0; j < b; j++)
510 printk(" %02x", buffer[j]);
511 printk("\n");
512 }
513 #endif /* OUTSCOPE */
514
515 dtlk_write_bytes("Double Talk found", 18);
516
517 return 0;
518 }
519 }
520
521 printk(KERN_INFO "\nDoubleTalk PC - not found\n");
522 return -ENODEV;
523 }
524
525 /*
526 static void dtlk_handle_error(char op, char rc, unsigned int minor)
527 {
528 printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n",
529 minor, op, rc);
530 return;
531 }
532 */
533
534 /* interrogate the DoubleTalk PC and return its settings */
535 static struct dtlk_settings *dtlk_interrogate(void)
536 {
537 unsigned char *t;
538 static char buf[sizeof(struct dtlk_settings) + 1];
539 int total, i;
540 static struct dtlk_settings status;
541 TRACE_TEXT("(dtlk_interrogate");
542 dtlk_write_bytes("\030\001?", 3);
543 for (total = 0, i = 0; i < 50; i++) {
544 buf[total] = dtlk_read_tts();
545 if (total > 2 && buf[total] == 0x7f)
546 break;
547 if (total < sizeof(struct dtlk_settings))
548 total++;
549 }
550 /*
551 if (i==50) printk("interrogate() read overrun\n");
552 for (i=0; i<sizeof(buf); i++)
553 printk(" %02x", buf[i]);
554 printk("\n");
555 */
556 t = buf;
557 status.serial_number = t[0] + t[1] * 256; /* serial number is
558 little endian */
559 t += 2;
560
561 i = 0;
562 while (*t != '\r') {
563 status.rom_version[i] = *t;
564 if (i < sizeof(status.rom_version) - 1)
565 i++;
566 t++;
567 }
568 status.rom_version[i] = 0;
569 t++;
570
571 status.mode = *t++;
572 status.punc_level = *t++;
573 status.formant_freq = *t++;
574 status.pitch = *t++;
575 status.speed = *t++;
576 status.volume = *t++;
577 status.tone = *t++;
578 status.expression = *t++;
579 status.ext_dict_loaded = *t++;
580 status.ext_dict_status = *t++;
581 status.free_ram = *t++;
582 status.articulation = *t++;
583 status.reverb = *t++;
584 status.eob = *t++;
585 status.has_indexing = dtlk_has_indexing;
586 TRACE_RET;
587 return &status;
588 }
589
590 static char dtlk_read_tts(void)
591 {
592 int portval, retries = 0;
593 char ch;
594 TRACE_TEXT("(dtlk_read_tts");
595
596 /* verify DT is ready, read char, wait for ACK */
597 do {
598 portval = inb_p(dtlk_port_tts);
599 } while ((portval & TTS_READABLE) == 0 &&
600 retries++ < DTLK_MAX_RETRIES);
601 if (retries == DTLK_MAX_RETRIES)
602 printk(KERN_ERR "dtlk_read_tts() timeout\n");
603
604 ch = inb_p(dtlk_port_tts); /* input from TTS port */
605 ch &= 0x7f;
606 outb_p(ch, dtlk_port_tts);
607
608 retries = 0;
609 do {
610 portval = inb_p(dtlk_port_tts);
611 } while ((portval & TTS_READABLE) != 0 &&
612 retries++ < DTLK_MAX_RETRIES);
613 if (retries == DTLK_MAX_RETRIES)
614 printk(KERN_ERR "dtlk_read_tts() timeout\n");
615
616 TRACE_RET;
617 return ch;
618 }
619
620 static char dtlk_read_lpc(void)
621 {
622 int retries = 0;
623 char ch;
624 TRACE_TEXT("(dtlk_read_lpc");
625
626 /* no need to test -- this is only called when the port is readable */
627
628 ch = inb_p(dtlk_port_lpc); /* input from LPC port */
629
630 outb_p(0xff, dtlk_port_lpc);
631
632 /* acknowledging a read takes 3-4
633 usec. Here, we wait up to 20 usec
634 for the acknowledgement */
635 retries = (loops_per_jiffy * 20) / (1000000/HZ);
636 while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
637 if (retries == 0)
638 printk(KERN_ERR "dtlk_read_lpc() timeout\n");
639
640 TRACE_RET;
641 return ch;
642 }
643
644 /* write n bytes to tts port */
645 static char dtlk_write_bytes(const char *buf, int n)
646 {
647 char val = 0;
648 /* printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */
649 TRACE_TEXT("(dtlk_write_bytes");
650 while (n-- > 0)
651 val = dtlk_write_tts(*buf++);
652 TRACE_RET;
653 return val;
654 }
655
656 static char dtlk_write_tts(char ch)
657 {
658 int retries = 0;
659 #ifdef TRACINGMORE
660 printk(" dtlk_write_tts(");
661 if (' ' <= ch && ch <= '~')
662 printk("'%c'", ch);
663 else
664 printk("0x%02x", ch);
665 #endif
666 if (ch != DTLK_CLEAR) /* no flow control for CLEAR command */
667 while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
668 retries++ < DTLK_MAX_RETRIES) /* DT ready? */
669 ;
670 if (retries == DTLK_MAX_RETRIES)
671 printk(KERN_ERR "dtlk_write_tts() timeout\n");
672
673 outb_p(ch, dtlk_port_tts); /* output to TTS port */
674 /* the RDY bit goes zero 2-3 usec after writing, and goes
675 1 again 180-190 usec later. Here, we wait up to 10
676 usec for the RDY bit to go zero. */
677 for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
678 if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
679 break;
680
681 #ifdef TRACINGMORE
682 printk(")\n");
683 #endif
684 return 0;
685 }
686
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.