]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/char/dtlk.c
Merge branch 'linus' into core/iommu
[mv-sheeva.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 <linux/smp_lock.h>     /* cycle_kernel_lock() */
60 #include <asm/io.h>             /* for inb_p, outb_p, inb, outb, etc. */
61 #include <asm/uaccess.h>        /* for get_user, etc. */
62 #include <linux/wait.h>         /* for wait_queue */
63 #include <linux/init.h>         /* for __init, module_{init,exit} */
64 #include <linux/poll.h>         /* for POLLIN, etc. */
65 #include <linux/dtlk.h>         /* local header file for DoubleTalk values */
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 /* Note that nobody ever sets dtlk_busy... */
293 static int dtlk_open(struct inode *inode, struct file *file)
294 {
295         TRACE_TEXT("(dtlk_open");
296
297         cycle_kernel_lock();
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_sync(&dtlk_timer);
324
325         return 0;
326 }
327
328 static int __init dtlk_init(void)
329 {
330         int err;
331
332         dtlk_port_lpc = 0;
333         dtlk_port_tts = 0;
334         dtlk_busy = 0;
335         dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
336         if (dtlk_major < 0) {
337                 printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
338                 return dtlk_major;
339         }
340         err = dtlk_dev_probe();
341         if (err) {
342                 unregister_chrdev(dtlk_major, "dtlk");
343                 return err;
344         }
345         printk(", MAJOR %d\n", dtlk_major);
346
347         init_waitqueue_head(&dtlk_process_list);
348
349         return 0;
350 }
351
352 static void __exit dtlk_cleanup (void)
353 {
354         dtlk_write_bytes("goodbye", 8);
355         msleep_interruptible(500);              /* 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         release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
363 }
364
365 module_init(dtlk_init);
366 module_exit(dtlk_cleanup);
367
368 /* ------------------------------------------------------------------------ */
369
370 static int dtlk_readable(void)
371 {
372 #ifdef TRACING
373         printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
374 #endif
375         return inb_p(dtlk_port_lpc) != 0x7f;
376 }
377
378 static int dtlk_writeable(void)
379 {
380         /* TRACE_TEXT(" dtlk_writeable"); */
381 #ifdef TRACINGMORE
382         printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
383 #endif
384         return inb_p(dtlk_port_tts) & TTS_WRITABLE;
385 }
386
387 static int __init dtlk_dev_probe(void)
388 {
389         unsigned int testval = 0;
390         int i = 0;
391         struct dtlk_settings *sp;
392
393         if (dtlk_port_lpc | dtlk_port_tts)
394                 return -EBUSY;
395
396         for (i = 0; dtlk_portlist[i]; i++) {
397 #if 0
398                 printk("DoubleTalk PC - Port %03x = %04x\n",
399                        dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
400 #endif
401
402                 if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT, 
403                                "dtlk"))
404                         continue;
405                 testval = inw_p(dtlk_portlist[i]);
406                 if ((testval &= 0xfbff) == 0x107f) {
407                         dtlk_port_lpc = dtlk_portlist[i];
408                         dtlk_port_tts = dtlk_port_lpc + 1;
409
410                         sp = dtlk_interrogate();
411                         printk("DoubleTalk PC at %03x-%03x, "
412                                "ROM version %s, serial number %u",
413                                dtlk_portlist[i], dtlk_portlist[i] +
414                                DTLK_IO_EXTENT - 1,
415                                sp->rom_version, sp->serial_number);
416
417                         /* put LPC port into known state, so
418                            dtlk_readable() gives valid result */
419                         outb_p(0xff, dtlk_port_lpc); 
420
421                         /* INIT string and index marker */
422                         dtlk_write_bytes("\036\1@\0\0012I\r", 8);
423                         /* posting an index takes 18 msec.  Here, we
424                            wait up to 100 msec to see whether it
425                            appears. */
426                         msleep_interruptible(100);
427                         dtlk_has_indexing = dtlk_readable();
428 #ifdef TRACING
429                         printk(", indexing %d\n", dtlk_has_indexing);
430 #endif
431 #ifdef INSCOPE
432                         {
433 /* This macro records ten samples read from the LPC port, for later display */
434 #define LOOK                                    \
435 for (i = 0; i < 10; i++)                        \
436   {                                             \
437     buffer[b++] = inb_p(dtlk_port_lpc);         \
438     __delay(loops_per_jiffy/(1000000/HZ));             \
439   }
440                                 char buffer[1000];
441                                 int b = 0, i, j;
442
443                                 LOOK
444                                 outb_p(0xff, dtlk_port_lpc);
445                                 buffer[b++] = 0;
446                                 LOOK
447                                 dtlk_write_bytes("\0012I\r", 4);
448                                 buffer[b++] = 0;
449                                 __delay(50 * loops_per_jiffy / (1000/HZ));
450                                 outb_p(0xff, dtlk_port_lpc);
451                                 buffer[b++] = 0;
452                                 LOOK
453
454                                 printk("\n");
455                                 for (j = 0; j < b; j++)
456                                         printk(" %02x", buffer[j]);
457                                 printk("\n");
458                         }
459 #endif                          /* INSCOPE */
460
461 #ifdef OUTSCOPE
462                         {
463 /* This macro records ten samples read from the TTS port, for later display */
464 #define LOOK                                    \
465 for (i = 0; i < 10; i++)                        \
466   {                                             \
467     buffer[b++] = inb_p(dtlk_port_tts);         \
468     __delay(loops_per_jiffy/(1000000/HZ));  /* 1 us */ \
469   }
470                                 char buffer[1000];
471                                 int b = 0, i, j;
472
473                                 mdelay(10);     /* 10 ms */
474                                 LOOK
475                                 outb_p(0x03, dtlk_port_tts);
476                                 buffer[b++] = 0;
477                                 LOOK
478                                 LOOK
479
480                                 printk("\n");
481                                 for (j = 0; j < b; j++)
482                                         printk(" %02x", buffer[j]);
483                                 printk("\n");
484                         }
485 #endif                          /* OUTSCOPE */
486
487                         dtlk_write_bytes("Double Talk found", 18);
488
489                         return 0;
490                 }
491                 release_region(dtlk_portlist[i], DTLK_IO_EXTENT);
492         }
493
494         printk(KERN_INFO "DoubleTalk PC - not found\n");
495         return -ENODEV;
496 }
497
498 /*
499    static void dtlk_handle_error(char op, char rc, unsigned int minor)
500    {
501    printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n", 
502    minor, op, rc);
503    return;
504    }
505  */
506
507 /* interrogate the DoubleTalk PC and return its settings */
508 static struct dtlk_settings *dtlk_interrogate(void)
509 {
510         unsigned char *t;
511         static char buf[sizeof(struct dtlk_settings) + 1];
512         int total, i;
513         static struct dtlk_settings status;
514         TRACE_TEXT("(dtlk_interrogate");
515         dtlk_write_bytes("\030\001?", 3);
516         for (total = 0, i = 0; i < 50; i++) {
517                 buf[total] = dtlk_read_tts();
518                 if (total > 2 && buf[total] == 0x7f)
519                         break;
520                 if (total < sizeof(struct dtlk_settings))
521                         total++;
522         }
523         /*
524            if (i==50) printk("interrogate() read overrun\n");
525            for (i=0; i<sizeof(buf); i++)
526            printk(" %02x", buf[i]);
527            printk("\n");
528          */
529         t = buf;
530         status.serial_number = t[0] + t[1] * 256; /* serial number is
531                                                      little endian */
532         t += 2;
533
534         i = 0;
535         while (*t != '\r') {
536                 status.rom_version[i] = *t;
537                 if (i < sizeof(status.rom_version) - 1)
538                         i++;
539                 t++;
540         }
541         status.rom_version[i] = 0;
542         t++;
543
544         status.mode = *t++;
545         status.punc_level = *t++;
546         status.formant_freq = *t++;
547         status.pitch = *t++;
548         status.speed = *t++;
549         status.volume = *t++;
550         status.tone = *t++;
551         status.expression = *t++;
552         status.ext_dict_loaded = *t++;
553         status.ext_dict_status = *t++;
554         status.free_ram = *t++;
555         status.articulation = *t++;
556         status.reverb = *t++;
557         status.eob = *t++;
558         status.has_indexing = dtlk_has_indexing;
559         TRACE_RET;
560         return &status;
561 }
562
563 static char dtlk_read_tts(void)
564 {
565         int portval, retries = 0;
566         char ch;
567         TRACE_TEXT("(dtlk_read_tts");
568
569         /* verify DT is ready, read char, wait for ACK */
570         do {
571                 portval = inb_p(dtlk_port_tts);
572         } while ((portval & TTS_READABLE) == 0 &&
573                  retries++ < DTLK_MAX_RETRIES);
574         if (retries == DTLK_MAX_RETRIES)
575                 printk(KERN_ERR "dtlk_read_tts() timeout\n");
576
577         ch = inb_p(dtlk_port_tts);      /* input from TTS port */
578         ch &= 0x7f;
579         outb_p(ch, dtlk_port_tts);
580
581         retries = 0;
582         do {
583                 portval = inb_p(dtlk_port_tts);
584         } while ((portval & TTS_READABLE) != 0 &&
585                  retries++ < DTLK_MAX_RETRIES);
586         if (retries == DTLK_MAX_RETRIES)
587                 printk(KERN_ERR "dtlk_read_tts() timeout\n");
588
589         TRACE_RET;
590         return ch;
591 }
592
593 static char dtlk_read_lpc(void)
594 {
595         int retries = 0;
596         char ch;
597         TRACE_TEXT("(dtlk_read_lpc");
598
599         /* no need to test -- this is only called when the port is readable */
600
601         ch = inb_p(dtlk_port_lpc);      /* input from LPC port */
602
603         outb_p(0xff, dtlk_port_lpc);
604
605         /* acknowledging a read takes 3-4
606            usec.  Here, we wait up to 20 usec
607            for the acknowledgement */
608         retries = (loops_per_jiffy * 20) / (1000000/HZ);
609         while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
610         if (retries == 0)
611                 printk(KERN_ERR "dtlk_read_lpc() timeout\n");
612
613         TRACE_RET;
614         return ch;
615 }
616
617 /* write n bytes to tts port */
618 static char dtlk_write_bytes(const char *buf, int n)
619 {
620         char val = 0;
621         /*  printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */
622         TRACE_TEXT("(dtlk_write_bytes");
623         while (n-- > 0)
624                 val = dtlk_write_tts(*buf++);
625         TRACE_RET;
626         return val;
627 }
628
629 static char dtlk_write_tts(char ch)
630 {
631         int retries = 0;
632 #ifdef TRACINGMORE
633         printk("  dtlk_write_tts(");
634         if (' ' <= ch && ch <= '~')
635                 printk("'%c'", ch);
636         else
637                 printk("0x%02x", ch);
638 #endif
639         if (ch != DTLK_CLEAR)   /* no flow control for CLEAR command */
640                 while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
641                        retries++ < DTLK_MAX_RETRIES)    /* DT ready? */
642                         ;
643         if (retries == DTLK_MAX_RETRIES)
644                 printk(KERN_ERR "dtlk_write_tts() timeout\n");
645
646         outb_p(ch, dtlk_port_tts);      /* output to TTS port */
647         /* the RDY bit goes zero 2-3 usec after writing, and goes
648            1 again 180-190 usec later.  Here, we wait up to 10
649            usec for the RDY bit to go zero. */
650         for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
651                 if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
652                         break;
653
654 #ifdef TRACINGMORE
655         printk(")\n");
656 #endif
657         return 0;
658 }
659
660 MODULE_LICENSE("GPL");