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