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