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