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