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