Linux Audio

Check our new training course

Loading...
v3.1
 
  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");
v5.9
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*                                              -*- linux-c -*-
  3 * dtlk.c - DoubleTalk PC driver for Linux
  4 *
  5 * Original author: Chris Pallotta <chris@allmedia.com>
  6 * Current maintainer: Jim Van Zandt <jrv@vanzandt.mv.com>
  7 * 
  8 * 2000-03-18 Jim Van Zandt: Fix polling.
  9 *  Eliminate dtlk_timer_active flag and separate dtlk_stop_timer
 10 *  function.  Don't restart timer in dtlk_timer_tick.  Restart timer
 11 *  in dtlk_poll after every poll.  dtlk_poll returns mask (duh).
 12 *  Eliminate unused function dtlk_write_byte.  Misc. code cleanups.
 13 */
 14
 15/* This driver is for the DoubleTalk PC, a speech synthesizer
 16   manufactured by RC Systems (http://www.rcsys.com/).  It was written
 17   based on documentation in their User's Manual file and Developer's
 18   Tools disk.
 19
 20   The DoubleTalk PC contains four voice synthesizers: text-to-speech
 21   (TTS), linear predictive coding (LPC), PCM/ADPCM, and CVSD.  It
 22   also has a tone generator.  Output data for LPC are written to the
 23   LPC port, and output data for the other modes are written to the
 24   TTS port.
 25
 26   Two kinds of data can be read from the DoubleTalk: status
 27   information (in response to the "\001?" interrogation command) is
 28   read from the TTS port, and index markers (which mark the progress
 29   of the speech) are read from the LPC port.  Not all models of the
 30   DoubleTalk PC implement index markers.  Both the TTS and LPC ports
 31   can also display status flags.
 32
 33   The DoubleTalk PC generates no interrupts.
 34
 35   These characteristics are mapped into the Unix stream I/O model as
 36   follows:
 37
 38   "write" sends bytes to the TTS port.  It is the responsibility of
 39   the user program to switch modes among TTS, PCM/ADPCM, and CVSD.
 40   This driver was written for use with the text-to-speech
 41   synthesizer.  If LPC output is needed some day, other minor device
 42   numbers can be used to select among output modes.
 43
 44   "read" gets index markers from the LPC port.  If the device does
 45   not implement index markers, the read will fail with error EINVAL.
 46
 47   Status information is available using the DTLK_INTERROGATE ioctl.
 48
 49 */
 50
 51#include <linux/module.h>
 52
 53#define KERNEL
 54#include <linux/types.h>
 55#include <linux/fs.h>
 56#include <linux/mm.h>
 57#include <linux/errno.h>	/* for -EBUSY */
 58#include <linux/ioport.h>	/* for request_region */
 59#include <linux/delay.h>	/* for loops_per_jiffy */
 60#include <linux/sched.h>
 61#include <linux/mutex.h>
 62#include <asm/io.h>		/* for inb_p, outb_p, inb, outb, etc. */
 63#include <linux/uaccess.h>	/* for get_user, etc. */
 64#include <linux/wait.h>		/* for wait_queue */
 65#include <linux/init.h>		/* for __init, module_{init,exit} */
 66#include <linux/poll.h>		/* for EPOLLIN, etc. */
 67#include <linux/dtlk.h>		/* local header file for DoubleTalk values */
 68
 69#ifdef TRACING
 70#define TRACE_TEXT(str) printk(str);
 71#define TRACE_RET printk(")")
 72#else				/* !TRACING */
 73#define TRACE_TEXT(str) ((void) 0)
 74#define TRACE_RET ((void) 0)
 75#endif				/* TRACING */
 76
 77static DEFINE_MUTEX(dtlk_mutex);
 78static void dtlk_timer_tick(struct timer_list *unused);
 79
 80static int dtlk_major;
 81static int dtlk_port_lpc;
 82static int dtlk_port_tts;
 83static int dtlk_busy;
 84static int dtlk_has_indexing;
 85static unsigned int dtlk_portlist[] =
 86{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
 87static wait_queue_head_t dtlk_process_list;
 88static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick);
 89
 90/* prototypes for file_operations struct */
 91static ssize_t dtlk_read(struct file *, char __user *,
 92			 size_t nbytes, loff_t * ppos);
 93static ssize_t dtlk_write(struct file *, const char __user *,
 94			  size_t nbytes, loff_t * ppos);
 95static __poll_t dtlk_poll(struct file *, poll_table *);
 96static int dtlk_open(struct inode *, struct file *);
 97static int dtlk_release(struct inode *, struct file *);
 98static long dtlk_ioctl(struct file *file,
 99		       unsigned int cmd, unsigned long arg);
100
101static const struct file_operations dtlk_fops =
102{
103	.owner		= THIS_MODULE,
104	.read		= dtlk_read,
105	.write		= dtlk_write,
106	.poll		= dtlk_poll,
107	.unlocked_ioctl	= dtlk_ioctl,
108	.open		= dtlk_open,
109	.release	= dtlk_release,
110	.llseek		= no_llseek,
111};
112
113/* local prototypes */
114static int dtlk_dev_probe(void);
115static struct dtlk_settings *dtlk_interrogate(void);
116static int dtlk_readable(void);
117static char dtlk_read_lpc(void);
118static char dtlk_read_tts(void);
119static int dtlk_writeable(void);
120static char dtlk_write_bytes(const char *buf, int n);
121static char dtlk_write_tts(char);
122/*
123   static void dtlk_handle_error(char, char, unsigned int);
124 */
125
126static ssize_t dtlk_read(struct file *file, char __user *buf,
127			 size_t count, loff_t * ppos)
128{
129	unsigned int minor = iminor(file_inode(file));
130	char ch;
131	int i = 0, retries;
132
133	TRACE_TEXT("(dtlk_read");
134	/*  printk("DoubleTalk PC - dtlk_read()\n"); */
135
136	if (minor != DTLK_MINOR || !dtlk_has_indexing)
137		return -EINVAL;
138
139	for (retries = 0; retries < loops_per_jiffy; retries++) {
140		while (i < count && dtlk_readable()) {
141			ch = dtlk_read_lpc();
142			/*        printk("dtlk_read() reads 0x%02x\n", ch); */
143			if (put_user(ch, buf++))
144				return -EFAULT;
145			i++;
146		}
147		if (i)
148			return i;
149		if (file->f_flags & O_NONBLOCK)
150			break;
151		msleep_interruptible(100);
152	}
153	if (retries == loops_per_jiffy)
154		printk(KERN_ERR "dtlk_read times out\n");
155	TRACE_RET;
156	return -EAGAIN;
157}
158
159static ssize_t dtlk_write(struct file *file, const char __user *buf,
160			  size_t count, loff_t * ppos)
161{
162	int i = 0, retries = 0, ch;
163
164	TRACE_TEXT("(dtlk_write");
165#ifdef TRACING
166	printk(" \"");
167	{
168		int i, ch;
169		for (i = 0; i < count; i++) {
170			if (get_user(ch, buf + i))
171				return -EFAULT;
172			if (' ' <= ch && ch <= '~')
173				printk("%c", ch);
174			else
175				printk("\\%03o", ch);
176		}
177		printk("\"");
178	}
179#endif
180
181	if (iminor(file_inode(file)) != DTLK_MINOR)
182		return -EINVAL;
183
184	while (1) {
185		while (i < count && !get_user(ch, buf) &&
186		       (ch == DTLK_CLEAR || dtlk_writeable())) {
187			dtlk_write_tts(ch);
188			buf++;
189			i++;
190			if (i % 5 == 0)
191				/* We yield our time until scheduled
192				   again.  This reduces the transfer
193				   rate to 500 bytes/sec, but that's
194				   still enough to keep up with the
195				   speech synthesizer. */
196				msleep_interruptible(1);
197			else {
198				/* the RDY bit goes zero 2-3 usec
199				   after writing, and goes 1 again
200				   180-190 usec later.  Here, we wait
201				   up to 250 usec for the RDY bit to
202				   go nonzero. */
203				for (retries = 0;
204				     retries < loops_per_jiffy / (4000/HZ);
205				     retries++)
206					if (inb_p(dtlk_port_tts) &
207					    TTS_WRITABLE)
208						break;
209			}
210			retries = 0;
211		}
212		if (i == count)
213			return i;
214		if (file->f_flags & O_NONBLOCK)
215			break;
216
217		msleep_interruptible(1);
218
219		if (++retries > 10 * HZ) { /* wait no more than 10 sec
220					      from last write */
221			printk("dtlk: write timeout.  "
222			       "inb_p(dtlk_port_tts) = 0x%02x\n",
223			       inb_p(dtlk_port_tts));
224			TRACE_RET;
225			return -EBUSY;
226		}
227	}
228	TRACE_RET;
229	return -EAGAIN;
230}
231
232static __poll_t dtlk_poll(struct file *file, poll_table * wait)
233{
234	__poll_t mask = 0;
235	unsigned long expires;
236
237	TRACE_TEXT(" dtlk_poll");
238	/*
239	   static long int j;
240	   printk(".");
241	   printk("<%ld>", jiffies-j);
242	   j=jiffies;
243	 */
244	poll_wait(file, &dtlk_process_list, wait);
245
246	if (dtlk_has_indexing && dtlk_readable()) {
247	        del_timer(&dtlk_timer);
248		mask = EPOLLIN | EPOLLRDNORM;
249	}
250	if (dtlk_writeable()) {
251	        del_timer(&dtlk_timer);
252		mask |= EPOLLOUT | EPOLLWRNORM;
253	}
254	/* there are no exception conditions */
255
256	/* There won't be any interrupts, so we set a timer instead. */
257	expires = jiffies + 3*HZ / 100;
258	mod_timer(&dtlk_timer, expires);
259
260	return mask;
261}
262
263static void dtlk_timer_tick(struct timer_list *unused)
264{
265	TRACE_TEXT(" dtlk_timer_tick");
266	wake_up_interruptible(&dtlk_process_list);
267}
268
269static long dtlk_ioctl(struct file *file,
270		       unsigned int cmd,
271		       unsigned long arg)
272{
273	char __user *argp = (char __user *)arg;
274	struct dtlk_settings *sp;
275	char portval;
276	TRACE_TEXT(" dtlk_ioctl");
277
278	switch (cmd) {
279
280	case DTLK_INTERROGATE:
281		mutex_lock(&dtlk_mutex);
282		sp = dtlk_interrogate();
283		mutex_unlock(&dtlk_mutex);
284		if (copy_to_user(argp, sp, sizeof(struct dtlk_settings)))
285			return -EINVAL;
286		return 0;
287
288	case DTLK_STATUS:
289		portval = inb_p(dtlk_port_tts);
290		return put_user(portval, argp);
291
292	default:
293		return -EINVAL;
294	}
295}
296
297/* Note that nobody ever sets dtlk_busy... */
298static int dtlk_open(struct inode *inode, struct file *file)
299{
300	TRACE_TEXT("(dtlk_open");
301
 
302	switch (iminor(inode)) {
303	case DTLK_MINOR:
304		if (dtlk_busy)
305			return -EBUSY;
306		return stream_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");