ia64/linux-2.6.18-xen.hg

annotate drivers/char/dtlk.c @ 0:831230e53067

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