File: /usr/src/linux/drivers/char/dtlk.c

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