ia64/linux-2.6.18-xen.hg

diff 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
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/drivers/char/dtlk.c	Wed Apr 11 14:15:44 2007 +0100
     1.3 @@ -0,0 +1,654 @@
     1.4 +/*                                              -*- linux-c -*-
     1.5 + * dtlk.c - DoubleTalk PC driver for Linux
     1.6 + *
     1.7 + * Original author: Chris Pallotta <chris@allmedia.com>
     1.8 + * Current maintainer: Jim Van Zandt <jrv@vanzandt.mv.com>
     1.9 + * 
    1.10 + * 2000-03-18 Jim Van Zandt: Fix polling.
    1.11 + *  Eliminate dtlk_timer_active flag and separate dtlk_stop_timer
    1.12 + *  function.  Don't restart timer in dtlk_timer_tick.  Restart timer
    1.13 + *  in dtlk_poll after every poll.  dtlk_poll returns mask (duh).
    1.14 + *  Eliminate unused function dtlk_write_byte.  Misc. code cleanups.
    1.15 + */
    1.16 +
    1.17 +/* This driver is for the DoubleTalk PC, a speech synthesizer
    1.18 +   manufactured by RC Systems (http://www.rcsys.com/).  It was written
    1.19 +   based on documentation in their User's Manual file and Developer's
    1.20 +   Tools disk.
    1.21 +
    1.22 +   The DoubleTalk PC contains four voice synthesizers: text-to-speech
    1.23 +   (TTS), linear predictive coding (LPC), PCM/ADPCM, and CVSD.  It
    1.24 +   also has a tone generator.  Output data for LPC are written to the
    1.25 +   LPC port, and output data for the other modes are written to the
    1.26 +   TTS port.
    1.27 +
    1.28 +   Two kinds of data can be read from the DoubleTalk: status
    1.29 +   information (in response to the "\001?" interrogation command) is
    1.30 +   read from the TTS port, and index markers (which mark the progress
    1.31 +   of the speech) are read from the LPC port.  Not all models of the
    1.32 +   DoubleTalk PC implement index markers.  Both the TTS and LPC ports
    1.33 +   can also display status flags.
    1.34 +
    1.35 +   The DoubleTalk PC generates no interrupts.
    1.36 +
    1.37 +   These characteristics are mapped into the Unix stream I/O model as
    1.38 +   follows:
    1.39 +
    1.40 +   "write" sends bytes to the TTS port.  It is the responsibility of
    1.41 +   the user program to switch modes among TTS, PCM/ADPCM, and CVSD.
    1.42 +   This driver was written for use with the text-to-speech
    1.43 +   synthesizer.  If LPC output is needed some day, other minor device
    1.44 +   numbers can be used to select among output modes.
    1.45 +
    1.46 +   "read" gets index markers from the LPC port.  If the device does
    1.47 +   not implement index markers, the read will fail with error EINVAL.
    1.48 +
    1.49 +   Status information is available using the DTLK_INTERROGATE ioctl.
    1.50 +
    1.51 + */
    1.52 +
    1.53 +#include <linux/module.h>
    1.54 +
    1.55 +#define KERNEL
    1.56 +#include <linux/types.h>
    1.57 +#include <linux/fs.h>
    1.58 +#include <linux/mm.h>
    1.59 +#include <linux/errno.h>	/* for -EBUSY */
    1.60 +#include <linux/ioport.h>	/* for request_region */
    1.61 +#include <linux/delay.h>	/* for loops_per_jiffy */
    1.62 +#include <asm/io.h>		/* for inb_p, outb_p, inb, outb, etc. */
    1.63 +#include <asm/uaccess.h>	/* for get_user, etc. */
    1.64 +#include <linux/wait.h>		/* for wait_queue */
    1.65 +#include <linux/init.h>		/* for __init, module_{init,exit} */
    1.66 +#include <linux/poll.h>		/* for POLLIN, etc. */
    1.67 +#include <linux/dtlk.h>		/* local header file for DoubleTalk values */
    1.68 +#include <linux/smp_lock.h>
    1.69 +
    1.70 +#ifdef TRACING
    1.71 +#define TRACE_TEXT(str) printk(str);
    1.72 +#define TRACE_RET printk(")")
    1.73 +#else				/* !TRACING */
    1.74 +#define TRACE_TEXT(str) ((void) 0)
    1.75 +#define TRACE_RET ((void) 0)
    1.76 +#endif				/* TRACING */
    1.77 +
    1.78 +
    1.79 +static int dtlk_major;
    1.80 +static int dtlk_port_lpc;
    1.81 +static int dtlk_port_tts;
    1.82 +static int dtlk_busy;
    1.83 +static int dtlk_has_indexing;
    1.84 +static unsigned int dtlk_portlist[] =
    1.85 +{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
    1.86 +static wait_queue_head_t dtlk_process_list;
    1.87 +static struct timer_list dtlk_timer;
    1.88 +
    1.89 +/* prototypes for file_operations struct */
    1.90 +static ssize_t dtlk_read(struct file *, char __user *,
    1.91 +			 size_t nbytes, loff_t * ppos);
    1.92 +static ssize_t dtlk_write(struct file *, const char __user *,
    1.93 +			  size_t nbytes, loff_t * ppos);
    1.94 +static unsigned int dtlk_poll(struct file *, poll_table *);
    1.95 +static int dtlk_open(struct inode *, struct file *);
    1.96 +static int dtlk_release(struct inode *, struct file *);
    1.97 +static int dtlk_ioctl(struct inode *inode, struct file *file,
    1.98 +		      unsigned int cmd, unsigned long arg);
    1.99 +
   1.100 +static const struct file_operations dtlk_fops =
   1.101 +{
   1.102 +	.owner		= THIS_MODULE,
   1.103 +	.read		= dtlk_read,
   1.104 +	.write		= dtlk_write,
   1.105 +	.poll		= dtlk_poll,
   1.106 +	.ioctl		= dtlk_ioctl,
   1.107 +	.open		= dtlk_open,
   1.108 +	.release	= dtlk_release,
   1.109 +};
   1.110 +
   1.111 +/* local prototypes */
   1.112 +static int dtlk_dev_probe(void);
   1.113 +static struct dtlk_settings *dtlk_interrogate(void);
   1.114 +static int dtlk_readable(void);
   1.115 +static char dtlk_read_lpc(void);
   1.116 +static char dtlk_read_tts(void);
   1.117 +static int dtlk_writeable(void);
   1.118 +static char dtlk_write_bytes(const char *buf, int n);
   1.119 +static char dtlk_write_tts(char);
   1.120 +/*
   1.121 +   static void dtlk_handle_error(char, char, unsigned int);
   1.122 + */
   1.123 +static void dtlk_timer_tick(unsigned long data);
   1.124 +
   1.125 +static ssize_t dtlk_read(struct file *file, char __user *buf,
   1.126 +			 size_t count, loff_t * ppos)
   1.127 +{
   1.128 +	unsigned int minor = iminor(file->f_dentry->d_inode);
   1.129 +	char ch;
   1.130 +	int i = 0, retries;
   1.131 +
   1.132 +	TRACE_TEXT("(dtlk_read");
   1.133 +	/*  printk("DoubleTalk PC - dtlk_read()\n"); */
   1.134 +
   1.135 +	if (minor != DTLK_MINOR || !dtlk_has_indexing)
   1.136 +		return -EINVAL;
   1.137 +
   1.138 +	for (retries = 0; retries < loops_per_jiffy; retries++) {
   1.139 +		while (i < count && dtlk_readable()) {
   1.140 +			ch = dtlk_read_lpc();
   1.141 +			/*        printk("dtlk_read() reads 0x%02x\n", ch); */
   1.142 +			if (put_user(ch, buf++))
   1.143 +				return -EFAULT;
   1.144 +			i++;
   1.145 +		}
   1.146 +		if (i)
   1.147 +			return i;
   1.148 +		if (file->f_flags & O_NONBLOCK)
   1.149 +			break;
   1.150 +		msleep_interruptible(100);
   1.151 +	}
   1.152 +	if (retries == loops_per_jiffy)
   1.153 +		printk(KERN_ERR "dtlk_read times out\n");
   1.154 +	TRACE_RET;
   1.155 +	return -EAGAIN;
   1.156 +}
   1.157 +
   1.158 +static ssize_t dtlk_write(struct file *file, const char __user *buf,
   1.159 +			  size_t count, loff_t * ppos)
   1.160 +{
   1.161 +	int i = 0, retries = 0, ch;
   1.162 +
   1.163 +	TRACE_TEXT("(dtlk_write");
   1.164 +#ifdef TRACING
   1.165 +	printk(" \"");
   1.166 +	{
   1.167 +		int i, ch;
   1.168 +		for (i = 0; i < count; i++) {
   1.169 +			if (get_user(ch, buf + i))
   1.170 +				return -EFAULT;
   1.171 +			if (' ' <= ch && ch <= '~')
   1.172 +				printk("%c", ch);
   1.173 +			else
   1.174 +				printk("\\%03o", ch);
   1.175 +		}
   1.176 +		printk("\"");
   1.177 +	}
   1.178 +#endif
   1.179 +
   1.180 +	if (iminor(file->f_dentry->d_inode) != DTLK_MINOR)
   1.181 +		return -EINVAL;
   1.182 +
   1.183 +	while (1) {
   1.184 +		while (i < count && !get_user(ch, buf) &&
   1.185 +		       (ch == DTLK_CLEAR || dtlk_writeable())) {
   1.186 +			dtlk_write_tts(ch);
   1.187 +			buf++;
   1.188 +			i++;
   1.189 +			if (i % 5 == 0)
   1.190 +				/* We yield our time until scheduled
   1.191 +				   again.  This reduces the transfer
   1.192 +				   rate to 500 bytes/sec, but that's
   1.193 +				   still enough to keep up with the
   1.194 +				   speech synthesizer. */
   1.195 +				msleep_interruptible(1);
   1.196 +			else {
   1.197 +				/* the RDY bit goes zero 2-3 usec
   1.198 +				   after writing, and goes 1 again
   1.199 +				   180-190 usec later.  Here, we wait
   1.200 +				   up to 250 usec for the RDY bit to
   1.201 +				   go nonzero. */
   1.202 +				for (retries = 0;
   1.203 +				     retries < loops_per_jiffy / (4000/HZ);
   1.204 +				     retries++)
   1.205 +					if (inb_p(dtlk_port_tts) &
   1.206 +					    TTS_WRITABLE)
   1.207 +						break;
   1.208 +			}
   1.209 +			retries = 0;
   1.210 +		}
   1.211 +		if (i == count)
   1.212 +			return i;
   1.213 +		if (file->f_flags & O_NONBLOCK)
   1.214 +			break;
   1.215 +
   1.216 +		msleep_interruptible(1);
   1.217 +
   1.218 +		if (++retries > 10 * HZ) { /* wait no more than 10 sec
   1.219 +					      from last write */
   1.220 +			printk("dtlk: write timeout.  "
   1.221 +			       "inb_p(dtlk_port_tts) = 0x%02x\n",
   1.222 +			       inb_p(dtlk_port_tts));
   1.223 +			TRACE_RET;
   1.224 +			return -EBUSY;
   1.225 +		}
   1.226 +	}
   1.227 +	TRACE_RET;
   1.228 +	return -EAGAIN;
   1.229 +}
   1.230 +
   1.231 +static unsigned int dtlk_poll(struct file *file, poll_table * wait)
   1.232 +{
   1.233 +	int mask = 0;
   1.234 +	unsigned long expires;
   1.235 +
   1.236 +	TRACE_TEXT(" dtlk_poll");
   1.237 +	/*
   1.238 +	   static long int j;
   1.239 +	   printk(".");
   1.240 +	   printk("<%ld>", jiffies-j);
   1.241 +	   j=jiffies;
   1.242 +	 */
   1.243 +	poll_wait(file, &dtlk_process_list, wait);
   1.244 +
   1.245 +	if (dtlk_has_indexing && dtlk_readable()) {
   1.246 +	        del_timer(&dtlk_timer);
   1.247 +		mask = POLLIN | POLLRDNORM;
   1.248 +	}
   1.249 +	if (dtlk_writeable()) {
   1.250 +	        del_timer(&dtlk_timer);
   1.251 +		mask |= POLLOUT | POLLWRNORM;
   1.252 +	}
   1.253 +	/* there are no exception conditions */
   1.254 +
   1.255 +	/* There won't be any interrupts, so we set a timer instead. */
   1.256 +	expires = jiffies + 3*HZ / 100;
   1.257 +	mod_timer(&dtlk_timer, expires);
   1.258 +
   1.259 +	return mask;
   1.260 +}
   1.261 +
   1.262 +static void dtlk_timer_tick(unsigned long data)
   1.263 +{
   1.264 +	TRACE_TEXT(" dtlk_timer_tick");
   1.265 +	wake_up_interruptible(&dtlk_process_list);
   1.266 +}
   1.267 +
   1.268 +static int dtlk_ioctl(struct inode *inode,
   1.269 +		      struct file *file,
   1.270 +		      unsigned int cmd,
   1.271 +		      unsigned long arg)
   1.272 +{
   1.273 +	char __user *argp = (char __user *)arg;
   1.274 +	struct dtlk_settings *sp;
   1.275 +	char portval;
   1.276 +	TRACE_TEXT(" dtlk_ioctl");
   1.277 +
   1.278 +	switch (cmd) {
   1.279 +
   1.280 +	case DTLK_INTERROGATE:
   1.281 +		sp = dtlk_interrogate();
   1.282 +		if (copy_to_user(argp, sp, sizeof(struct dtlk_settings)))
   1.283 +			return -EINVAL;
   1.284 +		return 0;
   1.285 +
   1.286 +	case DTLK_STATUS:
   1.287 +		portval = inb_p(dtlk_port_tts);
   1.288 +		return put_user(portval, argp);
   1.289 +
   1.290 +	default:
   1.291 +		return -EINVAL;
   1.292 +	}
   1.293 +}
   1.294 +
   1.295 +static int dtlk_open(struct inode *inode, struct file *file)
   1.296 +{
   1.297 +	TRACE_TEXT("(dtlk_open");
   1.298 +
   1.299 +	nonseekable_open(inode, file);
   1.300 +	switch (iminor(inode)) {
   1.301 +	case DTLK_MINOR:
   1.302 +		if (dtlk_busy)
   1.303 +			return -EBUSY;
   1.304 +		return nonseekable_open(inode, file);
   1.305 +
   1.306 +	default:
   1.307 +		return -ENXIO;
   1.308 +	}
   1.309 +}
   1.310 +
   1.311 +static int dtlk_release(struct inode *inode, struct file *file)
   1.312 +{
   1.313 +	TRACE_TEXT("(dtlk_release");
   1.314 +
   1.315 +	switch (iminor(inode)) {
   1.316 +	case DTLK_MINOR:
   1.317 +		break;
   1.318 +
   1.319 +	default:
   1.320 +		break;
   1.321 +	}
   1.322 +	TRACE_RET;
   1.323 +	
   1.324 +	del_timer(&dtlk_timer);
   1.325 +
   1.326 +	return 0;
   1.327 +}
   1.328 +
   1.329 +static int __init dtlk_init(void)
   1.330 +{
   1.331 +	dtlk_port_lpc = 0;
   1.332 +	dtlk_port_tts = 0;
   1.333 +	dtlk_busy = 0;
   1.334 +	dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
   1.335 +	if (dtlk_major == 0) {
   1.336 +		printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
   1.337 +		return 0;
   1.338 +	}
   1.339 +	if (dtlk_dev_probe() == 0)
   1.340 +		printk(", MAJOR %d\n", dtlk_major);
   1.341 +
   1.342 +	init_timer(&dtlk_timer);
   1.343 +	dtlk_timer.function = dtlk_timer_tick;
   1.344 +	init_waitqueue_head(&dtlk_process_list);
   1.345 +
   1.346 +	return 0;
   1.347 +}
   1.348 +
   1.349 +static void __exit dtlk_cleanup (void)
   1.350 +{
   1.351 +	dtlk_write_bytes("goodbye", 8);
   1.352 +	msleep_interruptible(500);		/* nap 0.50 sec but
   1.353 +						   could be awakened
   1.354 +						   earlier by
   1.355 +						   signals... */
   1.356 +
   1.357 +	dtlk_write_tts(DTLK_CLEAR);
   1.358 +	unregister_chrdev(dtlk_major, "dtlk");
   1.359 +	release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
   1.360 +}
   1.361 +
   1.362 +module_init(dtlk_init);
   1.363 +module_exit(dtlk_cleanup);
   1.364 +
   1.365 +/* ------------------------------------------------------------------------ */
   1.366 +
   1.367 +static int dtlk_readable(void)
   1.368 +{
   1.369 +#ifdef TRACING
   1.370 +	printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
   1.371 +#endif
   1.372 +	return inb_p(dtlk_port_lpc) != 0x7f;
   1.373 +}
   1.374 +
   1.375 +static int dtlk_writeable(void)
   1.376 +{
   1.377 +	/* TRACE_TEXT(" dtlk_writeable"); */
   1.378 +#ifdef TRACINGMORE
   1.379 +	printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
   1.380 +#endif
   1.381 +	return inb_p(dtlk_port_tts) & TTS_WRITABLE;
   1.382 +}
   1.383 +
   1.384 +static int __init dtlk_dev_probe(void)
   1.385 +{
   1.386 +	unsigned int testval = 0;
   1.387 +	int i = 0;
   1.388 +	struct dtlk_settings *sp;
   1.389 +
   1.390 +	if (dtlk_port_lpc | dtlk_port_tts)
   1.391 +		return -EBUSY;
   1.392 +
   1.393 +	for (i = 0; dtlk_portlist[i]; i++) {
   1.394 +#if 0
   1.395 +		printk("DoubleTalk PC - Port %03x = %04x\n",
   1.396 +		       dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
   1.397 +#endif
   1.398 +
   1.399 +		if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT, 
   1.400 +			       "dtlk"))
   1.401 +			continue;
   1.402 +		testval = inw_p(dtlk_portlist[i]);
   1.403 +		if ((testval &= 0xfbff) == 0x107f) {
   1.404 +			dtlk_port_lpc = dtlk_portlist[i];
   1.405 +			dtlk_port_tts = dtlk_port_lpc + 1;
   1.406 +
   1.407 +			sp = dtlk_interrogate();
   1.408 +			printk("DoubleTalk PC at %03x-%03x, "
   1.409 +			       "ROM version %s, serial number %u",
   1.410 +			       dtlk_portlist[i], dtlk_portlist[i] +
   1.411 +			       DTLK_IO_EXTENT - 1,
   1.412 +			       sp->rom_version, sp->serial_number);
   1.413 +
   1.414 +                        /* put LPC port into known state, so
   1.415 +			   dtlk_readable() gives valid result */
   1.416 +			outb_p(0xff, dtlk_port_lpc); 
   1.417 +
   1.418 +                        /* INIT string and index marker */
   1.419 +			dtlk_write_bytes("\036\1@\0\0012I\r", 8);
   1.420 +			/* posting an index takes 18 msec.  Here, we
   1.421 +			   wait up to 100 msec to see whether it
   1.422 +			   appears. */
   1.423 +			msleep_interruptible(100);
   1.424 +			dtlk_has_indexing = dtlk_readable();
   1.425 +#ifdef TRACING
   1.426 +			printk(", indexing %d\n", dtlk_has_indexing);
   1.427 +#endif
   1.428 +#ifdef INSCOPE
   1.429 +			{
   1.430 +/* This macro records ten samples read from the LPC port, for later display */
   1.431 +#define LOOK					\
   1.432 +for (i = 0; i < 10; i++)			\
   1.433 +  {						\
   1.434 +    buffer[b++] = inb_p(dtlk_port_lpc);		\
   1.435 +    __delay(loops_per_jiffy/(1000000/HZ));             \
   1.436 +  }
   1.437 +				char buffer[1000];
   1.438 +				int b = 0, i, j;
   1.439 +
   1.440 +				LOOK
   1.441 +				outb_p(0xff, dtlk_port_lpc);
   1.442 +				buffer[b++] = 0;
   1.443 +				LOOK
   1.444 +				dtlk_write_bytes("\0012I\r", 4);
   1.445 +				buffer[b++] = 0;
   1.446 +				__delay(50 * loops_per_jiffy / (1000/HZ));
   1.447 +				outb_p(0xff, dtlk_port_lpc);
   1.448 +				buffer[b++] = 0;
   1.449 +				LOOK
   1.450 +
   1.451 +				printk("\n");
   1.452 +				for (j = 0; j < b; j++)
   1.453 +					printk(" %02x", buffer[j]);
   1.454 +				printk("\n");
   1.455 +			}
   1.456 +#endif				/* INSCOPE */
   1.457 +
   1.458 +#ifdef OUTSCOPE
   1.459 +			{
   1.460 +/* This macro records ten samples read from the TTS port, for later display */
   1.461 +#define LOOK					\
   1.462 +for (i = 0; i < 10; i++)			\
   1.463 +  {						\
   1.464 +    buffer[b++] = inb_p(dtlk_port_tts);		\
   1.465 +    __delay(loops_per_jiffy/(1000000/HZ));  /* 1 us */ \
   1.466 +  }
   1.467 +				char buffer[1000];
   1.468 +				int b = 0, i, j;
   1.469 +
   1.470 +				mdelay(10);	/* 10 ms */
   1.471 +				LOOK
   1.472 +				outb_p(0x03, dtlk_port_tts);
   1.473 +				buffer[b++] = 0;
   1.474 +				LOOK
   1.475 +				LOOK
   1.476 +
   1.477 +				printk("\n");
   1.478 +				for (j = 0; j < b; j++)
   1.479 +					printk(" %02x", buffer[j]);
   1.480 +				printk("\n");
   1.481 +			}
   1.482 +#endif				/* OUTSCOPE */
   1.483 +
   1.484 +			dtlk_write_bytes("Double Talk found", 18);
   1.485 +
   1.486 +			return 0;
   1.487 +		}
   1.488 +		release_region(dtlk_portlist[i], DTLK_IO_EXTENT);
   1.489 +	}
   1.490 +
   1.491 +	printk(KERN_INFO "DoubleTalk PC - not found\n");
   1.492 +	return -ENODEV;
   1.493 +}
   1.494 +
   1.495 +/*
   1.496 +   static void dtlk_handle_error(char op, char rc, unsigned int minor)
   1.497 +   {
   1.498 +   printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n", 
   1.499 +   minor, op, rc);
   1.500 +   return;
   1.501 +   }
   1.502 + */
   1.503 +
   1.504 +/* interrogate the DoubleTalk PC and return its settings */
   1.505 +static struct dtlk_settings *dtlk_interrogate(void)
   1.506 +{
   1.507 +	unsigned char *t;
   1.508 +	static char buf[sizeof(struct dtlk_settings) + 1];
   1.509 +	int total, i;
   1.510 +	static struct dtlk_settings status;
   1.511 +	TRACE_TEXT("(dtlk_interrogate");
   1.512 +	dtlk_write_bytes("\030\001?", 3);
   1.513 +	for (total = 0, i = 0; i < 50; i++) {
   1.514 +		buf[total] = dtlk_read_tts();
   1.515 +		if (total > 2 && buf[total] == 0x7f)
   1.516 +			break;
   1.517 +		if (total < sizeof(struct dtlk_settings))
   1.518 +			total++;
   1.519 +	}
   1.520 +	/*
   1.521 +	   if (i==50) printk("interrogate() read overrun\n");
   1.522 +	   for (i=0; i<sizeof(buf); i++)
   1.523 +	   printk(" %02x", buf[i]);
   1.524 +	   printk("\n");
   1.525 +	 */
   1.526 +	t = buf;
   1.527 +	status.serial_number = t[0] + t[1] * 256; /* serial number is
   1.528 +						     little endian */
   1.529 +	t += 2;
   1.530 +
   1.531 +	i = 0;
   1.532 +	while (*t != '\r') {
   1.533 +		status.rom_version[i] = *t;
   1.534 +		if (i < sizeof(status.rom_version) - 1)
   1.535 +			i++;
   1.536 +		t++;
   1.537 +	}
   1.538 +	status.rom_version[i] = 0;
   1.539 +	t++;
   1.540 +
   1.541 +	status.mode = *t++;
   1.542 +	status.punc_level = *t++;
   1.543 +	status.formant_freq = *t++;
   1.544 +	status.pitch = *t++;
   1.545 +	status.speed = *t++;
   1.546 +	status.volume = *t++;
   1.547 +	status.tone = *t++;
   1.548 +	status.expression = *t++;
   1.549 +	status.ext_dict_loaded = *t++;
   1.550 +	status.ext_dict_status = *t++;
   1.551 +	status.free_ram = *t++;
   1.552 +	status.articulation = *t++;
   1.553 +	status.reverb = *t++;
   1.554 +	status.eob = *t++;
   1.555 +	status.has_indexing = dtlk_has_indexing;
   1.556 +	TRACE_RET;
   1.557 +	return &status;
   1.558 +}
   1.559 +
   1.560 +static char dtlk_read_tts(void)
   1.561 +{
   1.562 +	int portval, retries = 0;
   1.563 +	char ch;
   1.564 +	TRACE_TEXT("(dtlk_read_tts");
   1.565 +
   1.566 +	/* verify DT is ready, read char, wait for ACK */
   1.567 +	do {
   1.568 +		portval = inb_p(dtlk_port_tts);
   1.569 +	} while ((portval & TTS_READABLE) == 0 &&
   1.570 +		 retries++ < DTLK_MAX_RETRIES);
   1.571 +	if (retries == DTLK_MAX_RETRIES)
   1.572 +		printk(KERN_ERR "dtlk_read_tts() timeout\n");
   1.573 +
   1.574 +	ch = inb_p(dtlk_port_tts);	/* input from TTS port */
   1.575 +	ch &= 0x7f;
   1.576 +	outb_p(ch, dtlk_port_tts);
   1.577 +
   1.578 +	retries = 0;
   1.579 +	do {
   1.580 +		portval = inb_p(dtlk_port_tts);
   1.581 +	} while ((portval & TTS_READABLE) != 0 &&
   1.582 +		 retries++ < DTLK_MAX_RETRIES);
   1.583 +	if (retries == DTLK_MAX_RETRIES)
   1.584 +		printk(KERN_ERR "dtlk_read_tts() timeout\n");
   1.585 +
   1.586 +	TRACE_RET;
   1.587 +	return ch;
   1.588 +}
   1.589 +
   1.590 +static char dtlk_read_lpc(void)
   1.591 +{
   1.592 +	int retries = 0;
   1.593 +	char ch;
   1.594 +	TRACE_TEXT("(dtlk_read_lpc");
   1.595 +
   1.596 +	/* no need to test -- this is only called when the port is readable */
   1.597 +
   1.598 +	ch = inb_p(dtlk_port_lpc);	/* input from LPC port */
   1.599 +
   1.600 +	outb_p(0xff, dtlk_port_lpc);
   1.601 +
   1.602 +	/* acknowledging a read takes 3-4
   1.603 +	   usec.  Here, we wait up to 20 usec
   1.604 +	   for the acknowledgement */
   1.605 +	retries = (loops_per_jiffy * 20) / (1000000/HZ);
   1.606 +	while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
   1.607 +	if (retries == 0)
   1.608 +		printk(KERN_ERR "dtlk_read_lpc() timeout\n");
   1.609 +
   1.610 +	TRACE_RET;
   1.611 +	return ch;
   1.612 +}
   1.613 +
   1.614 +/* write n bytes to tts port */
   1.615 +static char dtlk_write_bytes(const char *buf, int n)
   1.616 +{
   1.617 +	char val = 0;
   1.618 +	/*  printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */
   1.619 +	TRACE_TEXT("(dtlk_write_bytes");
   1.620 +	while (n-- > 0)
   1.621 +		val = dtlk_write_tts(*buf++);
   1.622 +	TRACE_RET;
   1.623 +	return val;
   1.624 +}
   1.625 +
   1.626 +static char dtlk_write_tts(char ch)
   1.627 +{
   1.628 +	int retries = 0;
   1.629 +#ifdef TRACINGMORE
   1.630 +	printk("  dtlk_write_tts(");
   1.631 +	if (' ' <= ch && ch <= '~')
   1.632 +		printk("'%c'", ch);
   1.633 +	else
   1.634 +		printk("0x%02x", ch);
   1.635 +#endif
   1.636 +	if (ch != DTLK_CLEAR)	/* no flow control for CLEAR command */
   1.637 +		while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
   1.638 +		       retries++ < DTLK_MAX_RETRIES)	/* DT ready? */
   1.639 +			;
   1.640 +	if (retries == DTLK_MAX_RETRIES)
   1.641 +		printk(KERN_ERR "dtlk_write_tts() timeout\n");
   1.642 +
   1.643 +	outb_p(ch, dtlk_port_tts);	/* output to TTS port */
   1.644 +	/* the RDY bit goes zero 2-3 usec after writing, and goes
   1.645 +	   1 again 180-190 usec later.  Here, we wait up to 10
   1.646 +	   usec for the RDY bit to go zero. */
   1.647 +	for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
   1.648 +		if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
   1.649 +			break;
   1.650 +
   1.651 +#ifdef TRACINGMORE
   1.652 +	printk(")\n");
   1.653 +#endif
   1.654 +	return 0;
   1.655 +}
   1.656 +
   1.657 +MODULE_LICENSE("GPL");