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