linux/drivers/char/dtlk.c
<<
>>
Prefs
   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
  74static void dtlk_timer_tick(unsigned long data);
  75
  76static int dtlk_major;
  77static int dtlk_port_lpc;
  78static int dtlk_port_tts;
  79static int dtlk_busy;
  80static int dtlk_has_indexing;
  81static unsigned int dtlk_portlist[] =
  82{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
  83static wait_queue_head_t dtlk_process_list;
  84static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick, 0, 0);
  85
  86/* prototypes for file_operations struct */
  87static ssize_t dtlk_read(struct file *, char __user *,
  88                         size_t nbytes, loff_t * ppos);
  89static ssize_t dtlk_write(struct file *, const char __user *,
  90                          size_t nbytes, loff_t * ppos);
  91static unsigned int dtlk_poll(struct file *, poll_table *);
  92static int dtlk_open(struct inode *, struct file *);
  93static int dtlk_release(struct inode *, struct file *);
  94static int dtlk_ioctl(struct inode *inode, struct file *file,
  95                      unsigned int cmd, unsigned long arg);
  96
  97static 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 */
 109static int dtlk_dev_probe(void);
 110static struct dtlk_settings *dtlk_interrogate(void);
 111static int dtlk_readable(void);
 112static char dtlk_read_lpc(void);
 113static char dtlk_read_tts(void);
 114static int dtlk_writeable(void);
 115static char dtlk_write_bytes(const char *buf, int n);
 116static char dtlk_write_tts(char);
 117/*
 118   static void dtlk_handle_error(char, char, unsigned int);
 119 */
 120
 121static 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
 154static 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
 227static 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
 258static void dtlk_timer_tick(unsigned long data)
 259{
 260        TRACE_TEXT(" dtlk_timer_tick");
 261        wake_up_interruptible(&dtlk_process_list);
 262}
 263
 264static 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
 291static 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
 307static 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
 325static 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
 349static 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
 362module_init(dtlk_init);
 363module_exit(dtlk_cleanup);
 364
 365/* ------------------------------------------------------------------------ */
 366
 367static 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
 375static 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
 384static 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                                    \
 432for (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                                    \
 462for (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 */
 505static 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
 560static 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
 590static 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 */
 615static 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
 626static 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
 657MODULE_LICENSE("GPL");
 658