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