xref: /openbmc/linux/drivers/char/dtlk.c (revision 87c2ce3b)
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 #include <linux/devfs_fs_kernel.h>
66 #include <linux/smp_lock.h>
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 
77 static int dtlk_major;
78 static int dtlk_port_lpc;
79 static int dtlk_port_tts;
80 static int dtlk_busy;
81 static int dtlk_has_indexing;
82 static unsigned int dtlk_portlist[] =
83 {0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
84 static wait_queue_head_t dtlk_process_list;
85 static struct timer_list dtlk_timer;
86 
87 /* prototypes for file_operations struct */
88 static ssize_t dtlk_read(struct file *, char __user *,
89 			 size_t nbytes, loff_t * ppos);
90 static ssize_t dtlk_write(struct file *, const char __user *,
91 			  size_t nbytes, loff_t * ppos);
92 static unsigned int dtlk_poll(struct file *, poll_table *);
93 static int dtlk_open(struct inode *, struct file *);
94 static int dtlk_release(struct inode *, struct file *);
95 static int dtlk_ioctl(struct inode *inode, struct file *file,
96 		      unsigned int cmd, unsigned long arg);
97 
98 static struct file_operations dtlk_fops =
99 {
100 	.owner		= THIS_MODULE,
101 	.read		= dtlk_read,
102 	.write		= dtlk_write,
103 	.poll		= dtlk_poll,
104 	.ioctl		= dtlk_ioctl,
105 	.open		= dtlk_open,
106 	.release	= dtlk_release,
107 };
108 
109 /* local prototypes */
110 static int dtlk_dev_probe(void);
111 static struct dtlk_settings *dtlk_interrogate(void);
112 static int dtlk_readable(void);
113 static char dtlk_read_lpc(void);
114 static char dtlk_read_tts(void);
115 static int dtlk_writeable(void);
116 static char dtlk_write_bytes(const char *buf, int n);
117 static char dtlk_write_tts(char);
118 /*
119    static void dtlk_handle_error(char, char, unsigned int);
120  */
121 static void dtlk_timer_tick(unsigned long data);
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_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_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 int dtlk_ioctl(struct inode *inode,
267 		      struct file *file,
268 		      unsigned int cmd,
269 		      unsigned long arg)
270 {
271 	char __user *argp = (char __user *)arg;
272 	struct dtlk_settings *sp;
273 	char portval;
274 	TRACE_TEXT(" dtlk_ioctl");
275 
276 	switch (cmd) {
277 
278 	case DTLK_INTERROGATE:
279 		sp = dtlk_interrogate();
280 		if (copy_to_user(argp, sp, sizeof(struct dtlk_settings)))
281 			return -EINVAL;
282 		return 0;
283 
284 	case DTLK_STATUS:
285 		portval = inb_p(dtlk_port_tts);
286 		return put_user(portval, argp);
287 
288 	default:
289 		return -EINVAL;
290 	}
291 }
292 
293 static int dtlk_open(struct inode *inode, struct file *file)
294 {
295 	TRACE_TEXT("(dtlk_open");
296 
297 	nonseekable_open(inode, file);
298 	switch (iminor(inode)) {
299 	case DTLK_MINOR:
300 		if (dtlk_busy)
301 			return -EBUSY;
302 		return nonseekable_open(inode, file);
303 
304 	default:
305 		return -ENXIO;
306 	}
307 }
308 
309 static int dtlk_release(struct inode *inode, struct file *file)
310 {
311 	TRACE_TEXT("(dtlk_release");
312 
313 	switch (iminor(inode)) {
314 	case DTLK_MINOR:
315 		break;
316 
317 	default:
318 		break;
319 	}
320 	TRACE_RET;
321 
322 	del_timer(&dtlk_timer);
323 
324 	return 0;
325 }
326 
327 static int __init dtlk_init(void)
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 0;
336 	}
337 	if (dtlk_dev_probe() == 0)
338 		printk(", MAJOR %d\n", dtlk_major);
339 
340 	devfs_mk_cdev(MKDEV(dtlk_major, DTLK_MINOR),
341 		       S_IFCHR | S_IRUSR | S_IWUSR, "dtlk");
342 
343 	init_timer(&dtlk_timer);
344 	dtlk_timer.function = dtlk_timer_tick;
345 	init_waitqueue_head(&dtlk_process_list);
346 
347 	return 0;
348 }
349 
350 static void __exit dtlk_cleanup (void)
351 {
352 	dtlk_write_bytes("goodbye", 8);
353 	msleep_interruptible(500);		/* nap 0.50 sec but
354 						   could be awakened
355 						   earlier by
356 						   signals... */
357 
358 	dtlk_write_tts(DTLK_CLEAR);
359 	unregister_chrdev(dtlk_major, "dtlk");
360 	devfs_remove("dtlk");
361 	release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
362 }
363 
364 module_init(dtlk_init);
365 module_exit(dtlk_cleanup);
366 
367 /* ------------------------------------------------------------------------ */
368 
369 static int dtlk_readable(void)
370 {
371 #ifdef TRACING
372 	printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
373 #endif
374 	return inb_p(dtlk_port_lpc) != 0x7f;
375 }
376 
377 static int dtlk_writeable(void)
378 {
379 	/* TRACE_TEXT(" dtlk_writeable"); */
380 #ifdef TRACINGMORE
381 	printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
382 #endif
383 	return inb_p(dtlk_port_tts) & TTS_WRITABLE;
384 }
385 
386 static int __init dtlk_dev_probe(void)
387 {
388 	unsigned int testval = 0;
389 	int i = 0;
390 	struct dtlk_settings *sp;
391 
392 	if (dtlk_port_lpc | dtlk_port_tts)
393 		return -EBUSY;
394 
395 	for (i = 0; dtlk_portlist[i]; i++) {
396 #if 0
397 		printk("DoubleTalk PC - Port %03x = %04x\n",
398 		       dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
399 #endif
400 
401 		if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT,
402 			       "dtlk"))
403 			continue;
404 		testval = inw_p(dtlk_portlist[i]);
405 		if ((testval &= 0xfbff) == 0x107f) {
406 			dtlk_port_lpc = dtlk_portlist[i];
407 			dtlk_port_tts = dtlk_port_lpc + 1;
408 
409 			sp = dtlk_interrogate();
410 			printk("DoubleTalk PC at %03x-%03x, "
411 			       "ROM version %s, serial number %u",
412 			       dtlk_portlist[i], dtlk_portlist[i] +
413 			       DTLK_IO_EXTENT - 1,
414 			       sp->rom_version, sp->serial_number);
415 
416                         /* put LPC port into known state, so
417 			   dtlk_readable() gives valid result */
418 			outb_p(0xff, dtlk_port_lpc);
419 
420                         /* INIT string and index marker */
421 			dtlk_write_bytes("\036\1@\0\0012I\r", 8);
422 			/* posting an index takes 18 msec.  Here, we
423 			   wait up to 100 msec to see whether it
424 			   appears. */
425 			msleep_interruptible(100);
426 			dtlk_has_indexing = dtlk_readable();
427 #ifdef TRACING
428 			printk(", indexing %d\n", dtlk_has_indexing);
429 #endif
430 #ifdef INSCOPE
431 			{
432 /* This macro records ten samples read from the LPC port, for later display */
433 #define LOOK					\
434 for (i = 0; i < 10; i++)			\
435   {						\
436     buffer[b++] = inb_p(dtlk_port_lpc);		\
437     __delay(loops_per_jiffy/(1000000/HZ));             \
438   }
439 				char buffer[1000];
440 				int b = 0, i, j;
441 
442 				LOOK
443 				outb_p(0xff, dtlk_port_lpc);
444 				buffer[b++] = 0;
445 				LOOK
446 				dtlk_write_bytes("\0012I\r", 4);
447 				buffer[b++] = 0;
448 				__delay(50 * loops_per_jiffy / (1000/HZ));
449 				outb_p(0xff, dtlk_port_lpc);
450 				buffer[b++] = 0;
451 				LOOK
452 
453 				printk("\n");
454 				for (j = 0; j < b; j++)
455 					printk(" %02x", buffer[j]);
456 				printk("\n");
457 			}
458 #endif				/* INSCOPE */
459 
460 #ifdef OUTSCOPE
461 			{
462 /* This macro records ten samples read from the TTS port, for later display */
463 #define LOOK					\
464 for (i = 0; i < 10; i++)			\
465   {						\
466     buffer[b++] = inb_p(dtlk_port_tts);		\
467     __delay(loops_per_jiffy/(1000000/HZ));  /* 1 us */ \
468   }
469 				char buffer[1000];
470 				int b = 0, i, j;
471 
472 				mdelay(10);	/* 10 ms */
473 				LOOK
474 				outb_p(0x03, dtlk_port_tts);
475 				buffer[b++] = 0;
476 				LOOK
477 				LOOK
478 
479 				printk("\n");
480 				for (j = 0; j < b; j++)
481 					printk(" %02x", buffer[j]);
482 				printk("\n");
483 			}
484 #endif				/* OUTSCOPE */
485 
486 			dtlk_write_bytes("Double Talk found", 18);
487 
488 			return 0;
489 		}
490 		release_region(dtlk_portlist[i], DTLK_IO_EXTENT);
491 	}
492 
493 	printk(KERN_INFO "\nDoubleTalk PC - not found\n");
494 	return -ENODEV;
495 }
496 
497 /*
498    static void dtlk_handle_error(char op, char rc, unsigned int minor)
499    {
500    printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n",
501    minor, op, rc);
502    return;
503    }
504  */
505 
506 /* interrogate the DoubleTalk PC and return its settings */
507 static struct dtlk_settings *dtlk_interrogate(void)
508 {
509 	unsigned char *t;
510 	static char buf[sizeof(struct dtlk_settings) + 1];
511 	int total, i;
512 	static struct dtlk_settings status;
513 	TRACE_TEXT("(dtlk_interrogate");
514 	dtlk_write_bytes("\030\001?", 3);
515 	for (total = 0, i = 0; i < 50; i++) {
516 		buf[total] = dtlk_read_tts();
517 		if (total > 2 && buf[total] == 0x7f)
518 			break;
519 		if (total < sizeof(struct dtlk_settings))
520 			total++;
521 	}
522 	/*
523 	   if (i==50) printk("interrogate() read overrun\n");
524 	   for (i=0; i<sizeof(buf); i++)
525 	   printk(" %02x", buf[i]);
526 	   printk("\n");
527 	 */
528 	t = buf;
529 	status.serial_number = t[0] + t[1] * 256; /* serial number is
530 						     little endian */
531 	t += 2;
532 
533 	i = 0;
534 	while (*t != '\r') {
535 		status.rom_version[i] = *t;
536 		if (i < sizeof(status.rom_version) - 1)
537 			i++;
538 		t++;
539 	}
540 	status.rom_version[i] = 0;
541 	t++;
542 
543 	status.mode = *t++;
544 	status.punc_level = *t++;
545 	status.formant_freq = *t++;
546 	status.pitch = *t++;
547 	status.speed = *t++;
548 	status.volume = *t++;
549 	status.tone = *t++;
550 	status.expression = *t++;
551 	status.ext_dict_loaded = *t++;
552 	status.ext_dict_status = *t++;
553 	status.free_ram = *t++;
554 	status.articulation = *t++;
555 	status.reverb = *t++;
556 	status.eob = *t++;
557 	status.has_indexing = dtlk_has_indexing;
558 	TRACE_RET;
559 	return &status;
560 }
561 
562 static char dtlk_read_tts(void)
563 {
564 	int portval, retries = 0;
565 	char ch;
566 	TRACE_TEXT("(dtlk_read_tts");
567 
568 	/* verify DT is ready, read char, wait for ACK */
569 	do {
570 		portval = inb_p(dtlk_port_tts);
571 	} while ((portval & TTS_READABLE) == 0 &&
572 		 retries++ < DTLK_MAX_RETRIES);
573 	if (retries == DTLK_MAX_RETRIES)
574 		printk(KERN_ERR "dtlk_read_tts() timeout\n");
575 
576 	ch = inb_p(dtlk_port_tts);	/* input from TTS port */
577 	ch &= 0x7f;
578 	outb_p(ch, dtlk_port_tts);
579 
580 	retries = 0;
581 	do {
582 		portval = inb_p(dtlk_port_tts);
583 	} while ((portval & TTS_READABLE) != 0 &&
584 		 retries++ < DTLK_MAX_RETRIES);
585 	if (retries == DTLK_MAX_RETRIES)
586 		printk(KERN_ERR "dtlk_read_tts() timeout\n");
587 
588 	TRACE_RET;
589 	return ch;
590 }
591 
592 static char dtlk_read_lpc(void)
593 {
594 	int retries = 0;
595 	char ch;
596 	TRACE_TEXT("(dtlk_read_lpc");
597 
598 	/* no need to test -- this is only called when the port is readable */
599 
600 	ch = inb_p(dtlk_port_lpc);	/* input from LPC port */
601 
602 	outb_p(0xff, dtlk_port_lpc);
603 
604 	/* acknowledging a read takes 3-4
605 	   usec.  Here, we wait up to 20 usec
606 	   for the acknowledgement */
607 	retries = (loops_per_jiffy * 20) / (1000000/HZ);
608 	while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
609 	if (retries == 0)
610 		printk(KERN_ERR "dtlk_read_lpc() timeout\n");
611 
612 	TRACE_RET;
613 	return ch;
614 }
615 
616 /* write n bytes to tts port */
617 static char dtlk_write_bytes(const char *buf, int n)
618 {
619 	char val = 0;
620 	/*  printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */
621 	TRACE_TEXT("(dtlk_write_bytes");
622 	while (n-- > 0)
623 		val = dtlk_write_tts(*buf++);
624 	TRACE_RET;
625 	return val;
626 }
627 
628 static char dtlk_write_tts(char ch)
629 {
630 	int retries = 0;
631 #ifdef TRACINGMORE
632 	printk("  dtlk_write_tts(");
633 	if (' ' <= ch && ch <= '~')
634 		printk("'%c'", ch);
635 	else
636 		printk("0x%02x", ch);
637 #endif
638 	if (ch != DTLK_CLEAR)	/* no flow control for CLEAR command */
639 		while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
640 		       retries++ < DTLK_MAX_RETRIES)	/* DT ready? */
641 			;
642 	if (retries == DTLK_MAX_RETRIES)
643 		printk(KERN_ERR "dtlk_write_tts() timeout\n");
644 
645 	outb_p(ch, dtlk_port_tts);	/* output to TTS port */
646 	/* the RDY bit goes zero 2-3 usec after writing, and goes
647 	   1 again 180-190 usec later.  Here, we wait up to 10
648 	   usec for the RDY bit to go zero. */
649 	for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
650 		if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
651 			break;
652 
653 #ifdef TRACINGMORE
654 	printk(")\n");
655 #endif
656 	return 0;
657 }
658 
659 MODULE_LICENSE("GPL");
660