^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /* -*- linux-c -*-
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * dtlk.c - DoubleTalk PC driver for Linux
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Original author: Chris Pallotta <chris@allmedia.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Current maintainer: Jim Van Zandt <jrv@vanzandt.mv.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * 2000-03-18 Jim Van Zandt: Fix polling.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Eliminate dtlk_timer_active flag and separate dtlk_stop_timer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * function. Don't restart timer in dtlk_timer_tick. Restart timer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * in dtlk_poll after every poll. dtlk_poll returns mask (duh).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * Eliminate unused function dtlk_write_byte. Misc. code cleanups.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) /* This driver is for the DoubleTalk PC, a speech synthesizer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) manufactured by RC Systems (http://www.rcsys.com/). It was written
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) based on documentation in their User's Manual file and Developer's
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) Tools disk.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) The DoubleTalk PC contains four voice synthesizers: text-to-speech
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) (TTS), linear predictive coding (LPC), PCM/ADPCM, and CVSD. It
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) also has a tone generator. Output data for LPC are written to the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) LPC port, and output data for the other modes are written to the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) TTS port.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) Two kinds of data can be read from the DoubleTalk: status
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) information (in response to the "\001?" interrogation command) is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) read from the TTS port, and index markers (which mark the progress
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) of the speech) are read from the LPC port. Not all models of the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) DoubleTalk PC implement index markers. Both the TTS and LPC ports
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) can also display status flags.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) The DoubleTalk PC generates no interrupts.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) These characteristics are mapped into the Unix stream I/O model as
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) follows:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) "write" sends bytes to the TTS port. It is the responsibility of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) the user program to switch modes among TTS, PCM/ADPCM, and CVSD.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) This driver was written for use with the text-to-speech
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) synthesizer. If LPC output is needed some day, other minor device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) numbers can be used to select among output modes.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) "read" gets index markers from the LPC port. If the device does
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) not implement index markers, the read will fail with error EINVAL.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) Status information is available using the DTLK_INTERROGATE ioctl.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) #define KERNEL
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #include <linux/fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) #include <linux/mm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) #include <linux/errno.h> /* for -EBUSY */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) #include <linux/ioport.h> /* for request_region */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) #include <linux/delay.h> /* for loops_per_jiffy */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) #include <linux/sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) #include <linux/mutex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) #include <asm/io.h> /* for inb_p, outb_p, inb, outb, etc. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) #include <linux/uaccess.h> /* for get_user, etc. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) #include <linux/wait.h> /* for wait_queue */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) #include <linux/init.h> /* for __init, module_{init,exit} */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) #include <linux/poll.h> /* for EPOLLIN, etc. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) #include <linux/dtlk.h> /* local header file for DoubleTalk values */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) #ifdef TRACING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) #define TRACE_TEXT(str) printk(str);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) #define TRACE_RET printk(")")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) #else /* !TRACING */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) #define TRACE_TEXT(str) ((void) 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) #define TRACE_RET ((void) 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) #endif /* TRACING */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) static DEFINE_MUTEX(dtlk_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static void dtlk_timer_tick(struct timer_list *unused);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) static int dtlk_major;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) static int dtlk_port_lpc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) static int dtlk_port_tts;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) static int dtlk_busy;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) static int dtlk_has_indexing;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) static unsigned int dtlk_portlist[] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) {0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) static wait_queue_head_t dtlk_process_list;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) /* prototypes for file_operations struct */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) static ssize_t dtlk_read(struct file *, char __user *,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) size_t nbytes, loff_t * ppos);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) static ssize_t dtlk_write(struct file *, const char __user *,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) size_t nbytes, loff_t * ppos);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) static __poll_t dtlk_poll(struct file *, poll_table *);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) static int dtlk_open(struct inode *, struct file *);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) static int dtlk_release(struct inode *, struct file *);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) static long dtlk_ioctl(struct file *file,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) unsigned int cmd, unsigned long arg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) static const struct file_operations dtlk_fops =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) .read = dtlk_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) .write = dtlk_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) .poll = dtlk_poll,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) .unlocked_ioctl = dtlk_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) .open = dtlk_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) .release = dtlk_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) .llseek = no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) /* local prototypes */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) static int dtlk_dev_probe(void);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) static struct dtlk_settings *dtlk_interrogate(void);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) static int dtlk_readable(void);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) static char dtlk_read_lpc(void);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) static char dtlk_read_tts(void);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) static int dtlk_writeable(void);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) static char dtlk_write_bytes(const char *buf, int n);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) static char dtlk_write_tts(char);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) static void dtlk_handle_error(char, char, unsigned int);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) static ssize_t dtlk_read(struct file *file, char __user *buf,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) size_t count, loff_t * ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) unsigned int minor = iminor(file_inode(file));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) char ch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) int i = 0, retries;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) TRACE_TEXT("(dtlk_read");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) /* printk("DoubleTalk PC - dtlk_read()\n"); */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) if (minor != DTLK_MINOR || !dtlk_has_indexing)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) for (retries = 0; retries < loops_per_jiffy; retries++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) while (i < count && dtlk_readable()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) ch = dtlk_read_lpc();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) /* printk("dtlk_read() reads 0x%02x\n", ch); */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) if (put_user(ch, buf++))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) i++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) if (i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) return i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) if (file->f_flags & O_NONBLOCK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) msleep_interruptible(100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) if (retries == loops_per_jiffy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) printk(KERN_ERR "dtlk_read times out\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) TRACE_RET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) return -EAGAIN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) static ssize_t dtlk_write(struct file *file, const char __user *buf,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) size_t count, loff_t * ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) int i = 0, retries = 0, ch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) TRACE_TEXT("(dtlk_write");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) #ifdef TRACING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) printk(" \"");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) int i, ch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) for (i = 0; i < count; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) if (get_user(ch, buf + i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) if (' ' <= ch && ch <= '~')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) printk("%c", ch);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) printk("\\%03o", ch);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) printk("\"");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) if (iminor(file_inode(file)) != DTLK_MINOR)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) while (1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) while (i < count && !get_user(ch, buf) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) (ch == DTLK_CLEAR || dtlk_writeable())) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) dtlk_write_tts(ch);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) buf++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) i++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) if (i % 5 == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) /* We yield our time until scheduled
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) again. This reduces the transfer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) rate to 500 bytes/sec, but that's
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) still enough to keep up with the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) speech synthesizer. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) msleep_interruptible(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) /* the RDY bit goes zero 2-3 usec
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) after writing, and goes 1 again
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) 180-190 usec later. Here, we wait
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) up to 250 usec for the RDY bit to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) go nonzero. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) for (retries = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) retries < loops_per_jiffy / (4000/HZ);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) retries++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) if (inb_p(dtlk_port_tts) &
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) TTS_WRITABLE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) retries = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) if (i == count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) return i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) if (file->f_flags & O_NONBLOCK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) msleep_interruptible(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) if (++retries > 10 * HZ) { /* wait no more than 10 sec
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) from last write */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) printk("dtlk: write timeout. "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) "inb_p(dtlk_port_tts) = 0x%02x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) inb_p(dtlk_port_tts));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) TRACE_RET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) TRACE_RET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) return -EAGAIN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) static __poll_t dtlk_poll(struct file *file, poll_table * wait)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) __poll_t mask = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) unsigned long expires;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) TRACE_TEXT(" dtlk_poll");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) static long int j;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) printk(".");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) printk("<%ld>", jiffies-j);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) j=jiffies;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) poll_wait(file, &dtlk_process_list, wait);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) if (dtlk_has_indexing && dtlk_readable()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) del_timer(&dtlk_timer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) mask = EPOLLIN | EPOLLRDNORM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) if (dtlk_writeable()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) del_timer(&dtlk_timer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) mask |= EPOLLOUT | EPOLLWRNORM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) /* there are no exception conditions */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) /* There won't be any interrupts, so we set a timer instead. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) expires = jiffies + 3*HZ / 100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) mod_timer(&dtlk_timer, expires);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) return mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) static void dtlk_timer_tick(struct timer_list *unused)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) TRACE_TEXT(" dtlk_timer_tick");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) wake_up_interruptible(&dtlk_process_list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) static long dtlk_ioctl(struct file *file,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) unsigned int cmd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) char __user *argp = (char __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) struct dtlk_settings *sp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) char portval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) TRACE_TEXT(" dtlk_ioctl");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) case DTLK_INTERROGATE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) mutex_lock(&dtlk_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) sp = dtlk_interrogate();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) mutex_unlock(&dtlk_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) if (copy_to_user(argp, sp, sizeof(struct dtlk_settings)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) case DTLK_STATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) portval = inb_p(dtlk_port_tts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) return put_user(portval, argp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) /* Note that nobody ever sets dtlk_busy... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) static int dtlk_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) TRACE_TEXT("(dtlk_open");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) switch (iminor(inode)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) case DTLK_MINOR:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) if (dtlk_busy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) return stream_open(inode, file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) return -ENXIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) static int dtlk_release(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) TRACE_TEXT("(dtlk_release");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) switch (iminor(inode)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) case DTLK_MINOR:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) TRACE_RET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) del_timer_sync(&dtlk_timer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) static int __init dtlk_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) dtlk_port_lpc = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) dtlk_port_tts = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) dtlk_busy = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) if (dtlk_major < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) return dtlk_major;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) err = dtlk_dev_probe();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) unregister_chrdev(dtlk_major, "dtlk");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) printk(", MAJOR %d\n", dtlk_major);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) init_waitqueue_head(&dtlk_process_list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) static void __exit dtlk_cleanup (void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) dtlk_write_bytes("goodbye", 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) msleep_interruptible(500); /* nap 0.50 sec but
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) could be awakened
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) earlier by
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) signals... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) dtlk_write_tts(DTLK_CLEAR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) unregister_chrdev(dtlk_major, "dtlk");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) module_init(dtlk_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) module_exit(dtlk_cleanup);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) /* ------------------------------------------------------------------------ */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) static int dtlk_readable(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) #ifdef TRACING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) return inb_p(dtlk_port_lpc) != 0x7f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) static int dtlk_writeable(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) /* TRACE_TEXT(" dtlk_writeable"); */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) #ifdef TRACINGMORE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) return inb_p(dtlk_port_tts) & TTS_WRITABLE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) static int __init dtlk_dev_probe(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) unsigned int testval = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) int i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) struct dtlk_settings *sp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) if (dtlk_port_lpc | dtlk_port_tts)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) for (i = 0; dtlk_portlist[i]; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) #if 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) printk("DoubleTalk PC - Port %03x = %04x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) "dtlk"))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) testval = inw_p(dtlk_portlist[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) if ((testval &= 0xfbff) == 0x107f) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) dtlk_port_lpc = dtlk_portlist[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) dtlk_port_tts = dtlk_port_lpc + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) sp = dtlk_interrogate();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) printk("DoubleTalk PC at %03x-%03x, "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) "ROM version %s, serial number %u",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) dtlk_portlist[i], dtlk_portlist[i] +
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) DTLK_IO_EXTENT - 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) sp->rom_version, sp->serial_number);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) /* put LPC port into known state, so
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) dtlk_readable() gives valid result */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) outb_p(0xff, dtlk_port_lpc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) /* INIT string and index marker */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) dtlk_write_bytes("\036\1@\0\0012I\r", 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) /* posting an index takes 18 msec. Here, we
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) wait up to 100 msec to see whether it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) appears. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) msleep_interruptible(100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) dtlk_has_indexing = dtlk_readable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) #ifdef TRACING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) printk(", indexing %d\n", dtlk_has_indexing);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) #ifdef INSCOPE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) /* This macro records ten samples read from the LPC port, for later display */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) #define LOOK \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) for (i = 0; i < 10; i++) \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) { \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) buffer[b++] = inb_p(dtlk_port_lpc); \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) __delay(loops_per_jiffy/(1000000/HZ)); \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) char buffer[1000];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) int b = 0, i, j;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) LOOK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) outb_p(0xff, dtlk_port_lpc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) buffer[b++] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) LOOK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) dtlk_write_bytes("\0012I\r", 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) buffer[b++] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) __delay(50 * loops_per_jiffy / (1000/HZ));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) outb_p(0xff, dtlk_port_lpc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) buffer[b++] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) LOOK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) printk("\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) for (j = 0; j < b; j++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) printk(" %02x", buffer[j]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) printk("\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) #endif /* INSCOPE */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) #ifdef OUTSCOPE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) /* This macro records ten samples read from the TTS port, for later display */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) #define LOOK \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) for (i = 0; i < 10; i++) \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) { \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) buffer[b++] = inb_p(dtlk_port_tts); \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) __delay(loops_per_jiffy/(1000000/HZ)); /* 1 us */ \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) char buffer[1000];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) int b = 0, i, j;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) mdelay(10); /* 10 ms */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) LOOK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) outb_p(0x03, dtlk_port_tts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) buffer[b++] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) LOOK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) LOOK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) printk("\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) for (j = 0; j < b; j++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) printk(" %02x", buffer[j]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) printk("\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) #endif /* OUTSCOPE */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) dtlk_write_bytes("Double Talk found", 18);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) release_region(dtlk_portlist[i], DTLK_IO_EXTENT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) printk(KERN_INFO "DoubleTalk PC - not found\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) static void dtlk_handle_error(char op, char rc, unsigned int minor)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) minor, op, rc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) /* interrogate the DoubleTalk PC and return its settings */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) static struct dtlk_settings *dtlk_interrogate(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) unsigned char *t;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) static char buf[sizeof(struct dtlk_settings) + 1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) int total, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) static struct dtlk_settings status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) TRACE_TEXT("(dtlk_interrogate");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) dtlk_write_bytes("\030\001?", 3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) for (total = 0, i = 0; i < 50; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) buf[total] = dtlk_read_tts();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) if (total > 2 && buf[total] == 0x7f)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) if (total < sizeof(struct dtlk_settings))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) total++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) if (i==50) printk("interrogate() read overrun\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) for (i=0; i<sizeof(buf); i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) printk(" %02x", buf[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) printk("\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) t = buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) status.serial_number = t[0] + t[1] * 256; /* serial number is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) little endian */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) t += 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) while (*t != '\r') {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) status.rom_version[i] = *t;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) if (i < sizeof(status.rom_version) - 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) i++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) status.rom_version[i] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) status.mode = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) status.punc_level = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) status.formant_freq = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) status.pitch = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) status.speed = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) status.volume = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) status.tone = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) status.expression = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) status.ext_dict_loaded = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) status.ext_dict_status = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) status.free_ram = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) status.articulation = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) status.reverb = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) status.eob = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) status.has_indexing = dtlk_has_indexing;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) TRACE_RET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) return &status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) static char dtlk_read_tts(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) int portval, retries = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) char ch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) TRACE_TEXT("(dtlk_read_tts");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) /* verify DT is ready, read char, wait for ACK */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) do {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) portval = inb_p(dtlk_port_tts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) } while ((portval & TTS_READABLE) == 0 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) retries++ < DTLK_MAX_RETRIES);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) if (retries > DTLK_MAX_RETRIES)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) printk(KERN_ERR "dtlk_read_tts() timeout\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) ch = inb_p(dtlk_port_tts); /* input from TTS port */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) ch &= 0x7f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) outb_p(ch, dtlk_port_tts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) retries = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) do {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) portval = inb_p(dtlk_port_tts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) } while ((portval & TTS_READABLE) != 0 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) retries++ < DTLK_MAX_RETRIES);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) if (retries > DTLK_MAX_RETRIES)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) printk(KERN_ERR "dtlk_read_tts() timeout\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) TRACE_RET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) return ch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) static char dtlk_read_lpc(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) int retries = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) char ch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) TRACE_TEXT("(dtlk_read_lpc");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) /* no need to test -- this is only called when the port is readable */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) ch = inb_p(dtlk_port_lpc); /* input from LPC port */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) outb_p(0xff, dtlk_port_lpc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) /* acknowledging a read takes 3-4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) usec. Here, we wait up to 20 usec
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) for the acknowledgement */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) retries = (loops_per_jiffy * 20) / (1000000/HZ);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) if (retries == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) printk(KERN_ERR "dtlk_read_lpc() timeout\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) TRACE_RET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) return ch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) /* write n bytes to tts port */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) static char dtlk_write_bytes(const char *buf, int n)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) char val = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) /* printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) TRACE_TEXT("(dtlk_write_bytes");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) while (n-- > 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) val = dtlk_write_tts(*buf++);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) TRACE_RET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) return val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) static char dtlk_write_tts(char ch)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) int retries = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) #ifdef TRACINGMORE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) printk(" dtlk_write_tts(");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) if (' ' <= ch && ch <= '~')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) printk("'%c'", ch);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) printk("0x%02x", ch);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) if (ch != DTLK_CLEAR) /* no flow control for CLEAR command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) retries++ < DTLK_MAX_RETRIES) /* DT ready? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) ;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) if (retries > DTLK_MAX_RETRIES)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) printk(KERN_ERR "dtlk_write_tts() timeout\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) outb_p(ch, dtlk_port_tts); /* output to TTS port */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) /* the RDY bit goes zero 2-3 usec after writing, and goes
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) 1 again 180-190 usec later. Here, we wait up to 10
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) usec for the RDY bit to go zero. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) #ifdef TRACINGMORE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) printk(")\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) MODULE_LICENSE("GPL");