static ssize_t dtlk_read(struct file *file, char __user *buf, size_t count, loff_t * ppos) { unsigned int minor = iminor(file->f_path.dentry->d_inode); char ch; int i = 0, retries; TRACE_TEXT("(dtlk_read"); /* printk("DoubleTalk PC - dtlk_read()\n"); */ if (minor != DTLK_MINOR || !dtlk_has_indexing) return -EINVAL; for (retries = 0; retries < loops_per_jiffy; retries++) { while (i < count && dtlk_readable()) { ch = dtlk_read_lpc(); /* printk("dtlk_read() reads 0x%02x\n", ch); */ if (put_user(ch, buf++)) return -EFAULT; i++; } if (i) return i; if (file->f_flags & O_NONBLOCK) break; msleep_interruptible(100); } if (retries == loops_per_jiffy) printk(KERN_ERR "dtlk_read times out\n"); TRACE_RET; return -EAGAIN; }
static char dtlk_read_tts(void) { int portval, retries = 0; char ch; TRACE_TEXT("(dtlk_read_tts"); /* verify DT is ready, read char, wait for ACK */ do { portval = inb_p(dtlk_port_tts); } while ((portval & TTS_READABLE) == 0 && retries++ < DTLK_MAX_RETRIES); if (retries > DTLK_MAX_RETRIES) printk(KERN_ERR "dtlk_read_tts() timeout\n"); ch = inb_p(dtlk_port_tts); /* input from TTS port */ ch &= 0x7f; outb_p(ch, dtlk_port_tts); retries = 0; do { portval = inb_p(dtlk_port_tts); } while ((portval & TTS_READABLE) != 0 && retries++ < DTLK_MAX_RETRIES); if (retries > DTLK_MAX_RETRIES) printk(KERN_ERR "dtlk_read_tts() timeout\n"); TRACE_RET; return ch; }
static long dtlk_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { char __user *argp = (char __user *)arg; struct dtlk_settings *sp; char portval; TRACE_TEXT(" dtlk_ioctl"); switch (cmd) { case DTLK_INTERROGATE: mutex_lock(&dtlk_mutex); sp = dtlk_interrogate(); mutex_unlock(&dtlk_mutex); if (copy_to_user(argp, sp, sizeof(struct dtlk_settings))) return -EINVAL; return 0; case DTLK_STATUS: portval = inb_p(dtlk_port_tts); return put_user(portval, argp); default: return -EINVAL; } }
static unsigned int dtlk_poll(struct file *file, poll_table * wait) { int mask = 0; unsigned long expires; TRACE_TEXT(" dtlk_poll"); /* static long int j; printk("."); printk("<%ld>", jiffies-j); j=jiffies; */ poll_wait(file, &dtlk_process_list, wait); if (dtlk_has_indexing && dtlk_readable()) { del_timer(&dtlk_timer); mask = POLLIN | POLLRDNORM; } if (dtlk_writeable()) { del_timer(&dtlk_timer); mask |= POLLOUT | POLLWRNORM; } /* there are no exception conditions */ /* There won't be any interrupts, so we set a timer instead. */ expires = jiffies + 3*HZ / 100; mod_timer(&dtlk_timer, expires); return mask; }
static int dtlk_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct dtlk_settings *sp; char portval; TRACE_TEXT(" dtlk_ioctl"); switch (cmd) { case DTLK_INTERROGATE: sp = dtlk_interrogate(); if (copy_to_user((char *) arg, (char *) sp, sizeof(struct dtlk_settings))) return -EINVAL; return 0; case DTLK_STATUS: portval = inb_p(dtlk_port_tts); return put_user(portval, (char *) arg); default: return -EINVAL; } }
/* write n bytes to tts port */ static char dtlk_write_bytes(const char *buf, int n) { char val = 0; /* printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */ TRACE_TEXT("(dtlk_write_bytes"); while (n-- > 0) val = dtlk_write_tts(*buf++); TRACE_RET; return val; }
/* interrogate the DoubleTalk PC and return its settings */ static struct dtlk_settings *dtlk_interrogate(void) { unsigned char *t; static char buf[sizeof(struct dtlk_settings) + 1]; int total, i; static struct dtlk_settings status; TRACE_TEXT("(dtlk_interrogate"); dtlk_write_bytes("\030\001?", 3); for (total = 0, i = 0; i < 50; i++) { buf[total] = dtlk_read_tts(); if (total > 2 && buf[total] == 0x7f) break; if (total < sizeof(struct dtlk_settings)) total++; } /* if (i==50) printk("interrogate() read overrun\n"); for (i=0; i<sizeof(buf); i++) printk(" %02x", buf[i]); printk("\n"); */ t = buf; status.serial_number = t[0] + t[1] * 256; /* serial number is little endian */ t += 2; i = 0; while (*t != '\r') { status.rom_version[i] = *t; if (i < sizeof(status.rom_version) - 1) i++; t++; } status.rom_version[i] = 0; t++; status.mode = *t++; status.punc_level = *t++; status.formant_freq = *t++; status.pitch = *t++; status.speed = *t++; status.volume = *t++; status.tone = *t++; status.expression = *t++; status.ext_dict_loaded = *t++; status.ext_dict_status = *t++; status.free_ram = *t++; status.articulation = *t++; status.reverb = *t++; status.eob = *t++; status.has_indexing = dtlk_has_indexing; TRACE_RET; return &status; }
static int dtlk_open(struct inode *inode, struct file *file) { TRACE_TEXT("(dtlk_open"); switch (MINOR(inode->i_rdev)) { case DTLK_MINOR: if (dtlk_busy) return -EBUSY; return 0; default: return -ENXIO; } }
/* Note that nobody ever sets dtlk_busy... */ static int dtlk_open(struct inode *inode, struct file *file) { TRACE_TEXT("(dtlk_open"); nonseekable_open(inode, file); switch (iminor(inode)) { case DTLK_MINOR: if (dtlk_busy) return -EBUSY; return nonseekable_open(inode, file); default: return -ENXIO; } }
static int dtlk_release(struct inode *inode, struct file *file) { TRACE_TEXT("(dtlk_release"); switch (iminor(inode)) { case DTLK_MINOR: break; default: break; } TRACE_RET; del_timer_sync(&dtlk_timer); return 0; }
static int dtlk_release(struct inode *inode, struct file *file) { TRACE_TEXT("(dtlk_release"); switch (MINOR(inode->i_rdev)) { case DTLK_MINOR: break; default: break; } TRACE_RET; lock_kernel(); del_timer(&dtlk_timer); unlock_kernel(); return 0; }
static char dtlk_read_lpc(void) { int retries = 0; char ch; TRACE_TEXT("(dtlk_read_lpc"); /* no need to test -- this is only called when the port is readable */ ch = inb_p(dtlk_port_lpc); /* input from LPC port */ outb_p(0xff, dtlk_port_lpc); /* acknowledging a read takes 3-4 usec. Here, we wait up to 20 usec for the acknowledgement */ retries = (loops_per_jiffy * 20) / (1000000/HZ); while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0); if (retries == 0) printk(KERN_ERR "dtlk_read_lpc() timeout\n"); TRACE_RET; return ch; }
static ssize_t dtlk_read(struct file *file, char __user *buf, size_t count, loff_t * ppos) { unsigned int minor = iminor(file->f_path.dentry->d_inode); char ch; int i = 0, retries; TRACE_TEXT("(dtlk_read"); #ifdef CONFIG_DEBUG_PRINTK /* printk("DoubleTalk PC - dtlk_read()\n"); */ #else /* ; #endif if (minor != DTLK_MINOR || !dtlk_has_indexing) return -EINVAL; for (retries = 0; retries < loops_per_jiffy; retries++) { while (i < count && dtlk_readable()) { ch = dtlk_read_lpc(); #ifdef CONFIG_DEBUG_PRINTK /* printk("dtlk_read() reads 0x%02x\n", ch); */ #else /* ; #endif if (put_user(ch, buf++)) return -EFAULT; i++; } if (i) return i; if (file->f_flags & O_NONBLOCK) break; msleep_interruptible(100); } if (retries == loops_per_jiffy) printk(KERN_ERR "dtlk_read times out\n"); TRACE_RET; return -EAGAIN; } static ssize_t dtlk_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { int i = 0, retries = 0, ch; TRACE_TEXT("(dtlk_write"); #ifdef TRACING #ifdef CONFIG_DEBUG_PRINTK printk(" \""); #else ; #endif { int i, ch; for (i = 0; i < count; i++) { if (get_user(ch, buf + i)) return -EFAULT; if (' ' <= ch && ch <= '~') #ifdef CONFIG_DEBUG_PRINTK printk("%c", ch); #else ; #endif else #ifdef CONFIG_DEBUG_PRINTK printk("\\%03o", ch); #else ; #endif } #ifdef CONFIG_DEBUG_PRINTK printk("\""); #else ; #endif } #endif if (iminor(file->f_path.dentry->d_inode) != DTLK_MINOR) return -EINVAL; while (1) { while (i < count && !get_user(ch, buf) && (ch == DTLK_CLEAR || dtlk_writeable())) { dtlk_write_tts(ch); buf++; i++; if (i % 5 == 0) /* We yield our time until scheduled again. This reduces the transfer rate to 500 bytes/sec, but that's still enough to keep up with the speech synthesizer. */ msleep_interruptible(1); else { /* the RDY bit goes zero 2-3 usec after writing, and goes 1 again 180-190 usec later. Here, we wait up to 250 usec for the RDY bit to go nonzero. */ for (retries = 0;
static void dtlk_timer_tick(unsigned long data) { TRACE_TEXT(" dtlk_timer_tick"); wake_up_interruptible(&dtlk_process_list); }
static ssize_t dtlk_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { int i = 0, retries = 0, ch; TRACE_TEXT("(dtlk_write"); #ifdef TRACING printk(" \""); { int i, ch; for (i = 0; i < count; i++) { if (get_user(ch, buf + i)) return -EFAULT; if (' ' <= ch && ch <= '~') printk("%c", ch); else printk("\\%03o", ch); } printk("\""); } #endif if (iminor(file->f_path.dentry->d_inode) != DTLK_MINOR) return -EINVAL; while (1) { while (i < count && !get_user(ch, buf) && (ch == DTLK_CLEAR || dtlk_writeable())) { dtlk_write_tts(ch); buf++; i++; if (i % 5 == 0) /* We yield our time until scheduled again. This reduces the transfer rate to 500 bytes/sec, but that's still enough to keep up with the speech synthesizer. */ msleep_interruptible(1); else { /* the RDY bit goes zero 2-3 usec after writing, and goes 1 again 180-190 usec later. Here, we wait up to 250 usec for the RDY bit to go nonzero. */ for (retries = 0; retries < loops_per_jiffy / (4000/HZ); retries++) if (inb_p(dtlk_port_tts) & TTS_WRITABLE) break; } retries = 0; } if (i == count) return i; if (file->f_flags & O_NONBLOCK) break; msleep_interruptible(1); if (++retries > 10 * HZ) { /* wait no more than 10 sec from last write */ printk("dtlk: write timeout. " "inb_p(dtlk_port_tts) = 0x%02x\n", inb_p(dtlk_port_tts)); TRACE_RET; return -EBUSY; } } TRACE_RET; return -EAGAIN; }
static void dtlk_timer_tick(struct timer_list *unused) { TRACE_TEXT(" dtlk_timer_tick"); wake_up_interruptible(&dtlk_process_list); }