diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/char/dtlk.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to 'drivers/char/dtlk.c')
-rw-r--r-- | drivers/char/dtlk.c | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c new file mode 100644 index 000000000..6946c1cad --- /dev/null +++ b/drivers/char/dtlk.c @@ -0,0 +1,663 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* -*- linux-c -*- + * dtlk.c - DoubleTalk PC driver for Linux + * + * Original author: Chris Pallotta <chris@allmedia.com> + * Current maintainer: Jim Van Zandt <jrv@vanzandt.mv.com> + * + * 2000-03-18 Jim Van Zandt: Fix polling. + * Eliminate dtlk_timer_active flag and separate dtlk_stop_timer + * function. Don't restart timer in dtlk_timer_tick. Restart timer + * in dtlk_poll after every poll. dtlk_poll returns mask (duh). + * Eliminate unused function dtlk_write_byte. Misc. code cleanups. + */ + +/* This driver is for the DoubleTalk PC, a speech synthesizer + manufactured by RC Systems (http://www.rcsys.com/). It was written + based on documentation in their User's Manual file and Developer's + Tools disk. + + The DoubleTalk PC contains four voice synthesizers: text-to-speech + (TTS), linear predictive coding (LPC), PCM/ADPCM, and CVSD. It + also has a tone generator. Output data for LPC are written to the + LPC port, and output data for the other modes are written to the + TTS port. + + Two kinds of data can be read from the DoubleTalk: status + information (in response to the "\001?" interrogation command) is + read from the TTS port, and index markers (which mark the progress + of the speech) are read from the LPC port. Not all models of the + DoubleTalk PC implement index markers. Both the TTS and LPC ports + can also display status flags. + + The DoubleTalk PC generates no interrupts. + + These characteristics are mapped into the Unix stream I/O model as + follows: + + "write" sends bytes to the TTS port. It is the responsibility of + the user program to switch modes among TTS, PCM/ADPCM, and CVSD. + This driver was written for use with the text-to-speech + synthesizer. If LPC output is needed some day, other minor device + numbers can be used to select among output modes. + + "read" gets index markers from the LPC port. If the device does + not implement index markers, the read will fail with error EINVAL. + + Status information is available using the DTLK_INTERROGATE ioctl. + + */ + +#include <linux/module.h> + +#define KERNEL +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/errno.h> /* for -EBUSY */ +#include <linux/ioport.h> /* for request_region */ +#include <linux/delay.h> /* for loops_per_jiffy */ +#include <linux/sched.h> +#include <linux/mutex.h> +#include <asm/io.h> /* for inb_p, outb_p, inb, outb, etc. */ +#include <linux/uaccess.h> /* for get_user, etc. */ +#include <linux/wait.h> /* for wait_queue */ +#include <linux/init.h> /* for __init, module_{init,exit} */ +#include <linux/poll.h> /* for EPOLLIN, etc. */ +#include <linux/dtlk.h> /* local header file for DoubleTalk values */ + +#ifdef TRACING +#define TRACE_TEXT(str) printk(str); +#define TRACE_RET printk(")") +#else /* !TRACING */ +#define TRACE_TEXT(str) ((void) 0) +#define TRACE_RET ((void) 0) +#endif /* TRACING */ + +static DEFINE_MUTEX(dtlk_mutex); +static void dtlk_timer_tick(struct timer_list *unused); + +static int dtlk_major; +static int dtlk_port_lpc; +static int dtlk_port_tts; +static int dtlk_busy; +static int dtlk_has_indexing; +static unsigned int dtlk_portlist[] = +{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0}; +static wait_queue_head_t dtlk_process_list; +static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick); + +/* prototypes for file_operations struct */ +static ssize_t dtlk_read(struct file *, char __user *, + size_t nbytes, loff_t * ppos); +static ssize_t dtlk_write(struct file *, const char __user *, + size_t nbytes, loff_t * ppos); +static __poll_t dtlk_poll(struct file *, poll_table *); +static int dtlk_open(struct inode *, struct file *); +static int dtlk_release(struct inode *, struct file *); +static long dtlk_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); + +static const struct file_operations dtlk_fops = +{ + .owner = THIS_MODULE, + .read = dtlk_read, + .write = dtlk_write, + .poll = dtlk_poll, + .unlocked_ioctl = dtlk_ioctl, + .open = dtlk_open, + .release = dtlk_release, + .llseek = no_llseek, +}; + +/* local prototypes */ +static int dtlk_dev_probe(void); +static struct dtlk_settings *dtlk_interrogate(void); +static int dtlk_readable(void); +static char dtlk_read_lpc(void); +static char dtlk_read_tts(void); +static int dtlk_writeable(void); +static char dtlk_write_bytes(const char *buf, int n); +static char dtlk_write_tts(char); +/* + static void dtlk_handle_error(char, char, unsigned int); + */ + +static ssize_t dtlk_read(struct file *file, char __user *buf, + size_t count, loff_t * ppos) +{ + unsigned int minor = iminor(file_inode(file)); + 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 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_inode(file)) != 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 __poll_t dtlk_poll(struct file *file, poll_table * wait) +{ + __poll_t 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 = EPOLLIN | EPOLLRDNORM; + } + if (dtlk_writeable()) { + del_timer(&dtlk_timer); + mask |= EPOLLOUT | EPOLLWRNORM; + } + /* 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 void dtlk_timer_tick(struct timer_list *unused) +{ + TRACE_TEXT(" dtlk_timer_tick"); + wake_up_interruptible(&dtlk_process_list); +} + +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; + } +} + +/* Note that nobody ever sets dtlk_busy... */ +static int dtlk_open(struct inode *inode, struct file *file) +{ + TRACE_TEXT("(dtlk_open"); + + switch (iminor(inode)) { + case DTLK_MINOR: + if (dtlk_busy) + return -EBUSY; + return stream_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 __init dtlk_init(void) +{ + int err; + + dtlk_port_lpc = 0; + dtlk_port_tts = 0; + dtlk_busy = 0; + dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops); + if (dtlk_major < 0) { + printk(KERN_ERR "DoubleTalk PC - cannot register device\n"); + return dtlk_major; + } + err = dtlk_dev_probe(); + if (err) { + unregister_chrdev(dtlk_major, "dtlk"); + return err; + } + printk(", MAJOR %d\n", dtlk_major); + + init_waitqueue_head(&dtlk_process_list); + + return 0; +} + +static void __exit dtlk_cleanup (void) +{ + dtlk_write_bytes("goodbye", 8); + msleep_interruptible(500); /* nap 0.50 sec but + could be awakened + earlier by + signals... */ + + dtlk_write_tts(DTLK_CLEAR); + unregister_chrdev(dtlk_major, "dtlk"); + release_region(dtlk_port_lpc, DTLK_IO_EXTENT); +} + +module_init(dtlk_init); +module_exit(dtlk_cleanup); + +/* ------------------------------------------------------------------------ */ + +static int dtlk_readable(void) +{ +#ifdef TRACING + printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies); +#endif + return inb_p(dtlk_port_lpc) != 0x7f; +} + +static int dtlk_writeable(void) +{ + /* TRACE_TEXT(" dtlk_writeable"); */ +#ifdef TRACINGMORE + printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0); +#endif + return inb_p(dtlk_port_tts) & TTS_WRITABLE; +} + +static int __init dtlk_dev_probe(void) +{ + unsigned int testval = 0; + int i = 0; + struct dtlk_settings *sp; + + if (dtlk_port_lpc | dtlk_port_tts) + return -EBUSY; + + for (i = 0; dtlk_portlist[i]; i++) { +#if 0 + printk("DoubleTalk PC - Port %03x = %04x\n", + dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i]))); +#endif + + if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT, + "dtlk")) + continue; + testval = inw_p(dtlk_portlist[i]); + if ((testval &= 0xfbff) == 0x107f) { + dtlk_port_lpc = dtlk_portlist[i]; + dtlk_port_tts = dtlk_port_lpc + 1; + + sp = dtlk_interrogate(); + printk("DoubleTalk PC at %03x-%03x, " + "ROM version %s, serial number %u", + dtlk_portlist[i], dtlk_portlist[i] + + DTLK_IO_EXTENT - 1, + sp->rom_version, sp->serial_number); + + /* put LPC port into known state, so + dtlk_readable() gives valid result */ + outb_p(0xff, dtlk_port_lpc); + + /* INIT string and index marker */ + dtlk_write_bytes("\036\1@\0\0012I\r", 8); + /* posting an index takes 18 msec. Here, we + wait up to 100 msec to see whether it + appears. */ + msleep_interruptible(100); + dtlk_has_indexing = dtlk_readable(); +#ifdef TRACING + printk(", indexing %d\n", dtlk_has_indexing); +#endif +#ifdef INSCOPE + { +/* This macro records ten samples read from the LPC port, for later display */ +#define LOOK \ +for (i = 0; i < 10; i++) \ + { \ + buffer[b++] = inb_p(dtlk_port_lpc); \ + __delay(loops_per_jiffy/(1000000/HZ)); \ + } + char buffer[1000]; + int b = 0, i, j; + + LOOK + outb_p(0xff, dtlk_port_lpc); + buffer[b++] = 0; + LOOK + dtlk_write_bytes("\0012I\r", 4); + buffer[b++] = 0; + __delay(50 * loops_per_jiffy / (1000/HZ)); + outb_p(0xff, dtlk_port_lpc); + buffer[b++] = 0; + LOOK + + printk("\n"); + for (j = 0; j < b; j++) + printk(" %02x", buffer[j]); + printk("\n"); + } +#endif /* INSCOPE */ + +#ifdef OUTSCOPE + { +/* This macro records ten samples read from the TTS port, for later display */ +#define LOOK \ +for (i = 0; i < 10; i++) \ + { \ + buffer[b++] = inb_p(dtlk_port_tts); \ + __delay(loops_per_jiffy/(1000000/HZ)); /* 1 us */ \ + } + char buffer[1000]; + int b = 0, i, j; + + mdelay(10); /* 10 ms */ + LOOK + outb_p(0x03, dtlk_port_tts); + buffer[b++] = 0; + LOOK + LOOK + + printk("\n"); + for (j = 0; j < b; j++) + printk(" %02x", buffer[j]); + printk("\n"); + } +#endif /* OUTSCOPE */ + + dtlk_write_bytes("Double Talk found", 18); + + return 0; + } + release_region(dtlk_portlist[i], DTLK_IO_EXTENT); + } + + printk(KERN_INFO "DoubleTalk PC - not found\n"); + return -ENODEV; +} + +/* + static void dtlk_handle_error(char op, char rc, unsigned int minor) + { + printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n", + minor, op, rc); + return; + } + */ + +/* 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 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 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; +} + +/* 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; +} + +static char dtlk_write_tts(char ch) +{ + int retries = 0; +#ifdef TRACINGMORE + printk(" dtlk_write_tts("); + if (' ' <= ch && ch <= '~') + printk("'%c'", ch); + else + printk("0x%02x", ch); +#endif + if (ch != DTLK_CLEAR) /* no flow control for CLEAR command */ + while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 && + retries++ < DTLK_MAX_RETRIES) /* DT ready? */ + ; + if (retries > DTLK_MAX_RETRIES) + printk(KERN_ERR "dtlk_write_tts() timeout\n"); + + outb_p(ch, dtlk_port_tts); /* output to TTS port */ + /* the RDY bit goes zero 2-3 usec after writing, and goes + 1 again 180-190 usec later. Here, we wait up to 10 + usec for the RDY bit to go zero. */ + for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++) + if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0) + break; + +#ifdef TRACINGMORE + printk(")\n"); +#endif + return 0; +} + +MODULE_LICENSE("GPL"); |