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