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