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