diff -u --recursive --new-file v2.1.48/linux/CREDITS linux/CREDITS --- v2.1.48/linux/CREDITS Mon Aug 4 16:25:35 1997 +++ linux/CREDITS Tue Aug 5 09:48:55 1997 @@ -1274,7 +1274,7 @@ N: Thomas Sailer E: sailer@ife.ee.ethz.ch E: HB9JNX@HB9W.CHE.EU (packet radio) -D: Baycom and Soundcard radio modem driver +D: hfmodem, Baycom and Soundcard radio modem driver S: Weinbergstrasse 76 S: 8408 Winterthur S: Switzerland diff -u --recursive --new-file v2.1.48/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.48/linux/Documentation/Configure.help Mon Aug 4 16:25:35 1997 +++ linux/Documentation/Configure.help Wed Aug 6 13:02:58 1997 @@ -1967,20 +1967,44 @@ possible. The default value is 4. Minimum is 2, maximum is 12. The normal answer therefore is the default one. +detect and read serial NVRAM +CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT + Enable support for reading the serial NVRAM data on Symbios and + some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful for + systems with more than one Symbios compatible controller where at least + one has a serial NVRAM, or for a system with a mixture of Symbios and + Tekram cards. Enables setting the boot order of host adaptors + to something other than the default order or "reverse probe" order. + Also enables Symbios and Tekram cards to be distinguished so + CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in a system with a + mixture of Symbios and Tekram cards so the Symbios cards can make use of + the full range of Symbios features, differential, led pin, without + causing problems for the Tekram card(s). + (added by Richard Waltham: dormouse@farsrobt.demon.co.uk) + Also enables setting host and targets SCSI features as defined in the + user setup for each host using a serial NVRAM (added by the maintainer). + The default answer is N, the normal answer should be Y. + Read drivers/scsi/README.ncr53c8xx for more information. + assume boards are SYMBIOS compatible CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT - This option allows you to enable some features depending on GPIO wiring. - These general purpose input/output pins can be used for vendor specific - features or implementation of the standard SYMBIOS features. - Genuine SYMBIOS boards use GPIO0 in output for controller LED and GPIO3 - bit as a flag indicating singled-ended/differential interface. + This option allows you to enable some features depending on GPIO + wiring. These General Purpose Input/Output pins can be used for + vendor specific features or implementation of the standard SYMBIOS + features. Genuine SYMBIOS boards use GPIO0 in output for controller + LED and GPIO3 bit as a flag indicating singled-ended/differential + interface. If all the boards of your system are genuine SYMBIOS boards or use - BIOS and drivers from SYMBIOS, you would want to enable this option, - obviously at your own risks. + BIOS and drivers from SYMBIOS, you would want to enable this option. The driver behaves correctly on my system with this option enabled. - (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev 0x12). - This option must be set to N if your system has at least one 53C8XX based - scsi board with a vendor-specific BIOS (example: Tekram DC-390/U/W/F). + (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev + 0x12). This option must be set to N if your system has at least one + 53C8XX based scsi board with a vendor-specific BIOS (example: Tekram + DC-390/U/W/F). If unsure, say N. + However, if all your non Symbios compatible boards have NvRAM, setting + option CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT allows the driver to + distinguish Symbios compatible boards from other ones. + So, you can answer Y if all non Symbios compatible boards have NVRAM. assume boards are SYMBIOS compatible CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT @@ -2518,6 +2542,26 @@ both 1200 baud AFSK and 9600 baud FSK if you want (but obviously you can only use one protocol at a time, depending on what the other end can understand). + +Shortwave radio modem driver +CONFIG_HFMODEM + This experimental driver is used by a package (to be released) + that implements the shortwave radio protocols RTTY, Sitor (Amtor), + Pactor 1 and GTOR using a standard PC soundcard. If unsure, + say N. + +Shortwave radio modem driver support for SoundBlaster and compatible cards +CONFIG_HFMODEM_SBC + This option enables the hfmodem driver to use SoundBlaster and + compatible cards. It requires a 16bit capable card, i.e. + SB16 or better, or ESS1688 or newer. + +Shortwave radio modem driver support for WSS and Crystal cards +CONFIG_HFMODEM_WSS + This option enables the hfmodem driver to use WindowsSoundSystem + compatible cards. These cards feature a codec chip from either + Analog Devices (such as AD1848, AD1845) or Crystal + Semiconductors (such as CS4248, CS423x). Serial port KISS driver for AX.25 CONFIG_MKISS diff -u --recursive --new-file v2.1.48/linux/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt --- v2.1.48/linux/Documentation/ioctl-number.txt Mon Aug 4 16:25:35 1997 +++ linux/Documentation/ioctl-number.txt Thu Aug 7 09:37:04 1997 @@ -1,5 +1,5 @@ Ioctl Numbers -25 Jul 1997 +7 Aug 1997 Michael Chastain @@ -119,5 +119,7 @@ 0xA0 all Small Device Project in development: +0xA3 80-8F Port ACL in development: + 0xA3 90-9F DoubleTalk driver in development: diff -u --recursive --new-file v2.1.48/linux/Documentation/networking/baycom.txt linux/Documentation/networking/baycom.txt --- v2.1.48/linux/Documentation/networking/baycom.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/baycom.txt Tue Aug 5 09:49:50 1997 @@ -0,0 +1,118 @@ + LINUX DRIVER FOR BAYCOM MODEMS + + Thomas M. Sailer, HB9JNX/AE4WA, + +This document describes the Linux Kernel Driver for simple Baycom style +amateur radio modems. The driver supports the following modems: + +ser12: This is a very simple 1200 baud AFSK modem. The modem consists only + of a modulator/demodulator chip, usually a TI TCM3105. The computer + is responsible for regenerating the receiver bit clock, as well as + for handling the HDLC protocol. The modem connects to a serial port, + hence the name. Since the serial port is not used as an async serial + port, the kernel driver for serial ports cannot be used, and this + driver only supports standard serial hardware (8250, 16450, 16550) + +par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. + The modem does all the filtering and regenerates the receiver clock. + Data is transferred from and to the PC via a shift register. + The shift register is filled with 16 bits and an interrupt is signalled. + The PC then empties the shift register in a burst. This modem connects + to the parallel port, hence the name. The modem leaves the + implementation of the HDLC protocol and the scrambler polynomial to + the PC. + +picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem + is protocol compatible to par96, but uses only three low power ICs + and can therefore be fed from the parallel port and does not require + an additional power supply. Furthermore, it incorporates a carrier + detect circuitry. + +All of the above modems only support half duplex communications. However, +the driver supports the KISS (see below) fullduplex command. It then simply +starts to send as soon as there's a packet to transmit and does not care +about DCD, i.e. it starts to send even if there's someone else on the channel. +This command is required by some implementations of the DAMA channel +access protocol. + + +The Interface of the driver + +Unlike previous drivers, the driver is no longer a character device, +but it is now a true kernel network interface. Installation is therefore +simple. Once installed, four interfaces named bc[0-3] are available. +sethdlc from the ax25 utilities may be used to set driver states etc. +Users of userland AX.25 stacks may use the net2kiss utility (also available +in the ax25 utilities package) to converts packets of a network interface +to a KISS stream on a pseudo tty. There's also a patch available from +me for WAMPES which allows attaching a kernel network interface directly. + + +Configuring the driver + +Every time the driver is inserted into the kernel, it has to know which +modems it should access at which ports. This can be done with the setbaycom +utility. If you are only using one modem, you can also configure the +driver from the insmod command line (or by means of an option line in +/etc/conf.modules). + +Examples: + insmod baycom modem=1 iobase=0x3f8 irq=4 options=1 + sethdlc -i bc0 -p type ser12 io 0x3f8 irq 4 options 1 + +Both lines configure the first port to drive a ser12 modem at the first +serial port (COM1 under DOS). options=1 instructs the driver to use +the software DCD algorithm (see below). + + insmod baycom modem=2 iobase=0x378 irq=7 options=1 + sethdlc -i bc0 -p type par96 io 0x378 irq 7 options 1 + +Both lines configure the first port to drive a par96 or par97 modem at the +first parallel port (LPT1 under DOS). options=1 instructs the driver to use +the software DCD algorithm (see below). + +The channel access parameters can be set with sethdlc -a or kissparms. +Note that both utilities interpret the values slightly different. + + +Hardware DCD versus Software DCD + +To avoid collisions on the air, the driver must know when the channel is +busy. This is the task of the DCD circuitry/software. The driver may either +utilise a software DCD algorithm (options=1) or use a DCD signal from +the hardware (options=0). + +ser12: if software DCD is utilised, the radio's squelch should always be + open. It is highly recommended to use the software DCD algorithm, + as it is much faster than most hardware squelch circuitry. The + disadvantage is a slightly higher load on the system. + +par96: the software DCD algorithm for this type of modem is rather poor. + The modem simply does not provide enough information to implement + a reasonable DCD algorithm in software. Therefore, if your radio + feeds the DCD input of the PAR96 modem, the use of the hardware + DCD circuitry is recommended. + +picpar: the picpar modem features a builtin DCD hardware, which is highly + recommended. + + + +Compatibility with the rest of the Linux kernel + +The serial driver, the line printer (lp) driver and the baycom driver compete +for the same hardware resources. Of course only one driver can access a given +interface at a time. The serial driver grabs all interfaces it can find at +startup time. Therefore the baycom driver subsequently won't be able to +access a serial port. You might therefore find it necessary to release +a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where +# is the number of the interface. The baycom driver does not reserve any +port at startup, unless one is specified on the 'insmod' command line. Another +method to solve the problem is to compile all three drivers as modules and +leave it to kerneld to load the correct driver depending on the application. + + + +vy 73s de +Tom Sailer, sailer@ife.ee.ethz.ch +hb9jnx @ hb9w.ampr.org diff -u --recursive --new-file v2.1.48/linux/Documentation/networking/soundmodem.txt linux/Documentation/networking/soundmodem.txt --- v2.1.48/linux/Documentation/networking/soundmodem.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/soundmodem.txt Tue Aug 5 09:49:50 1997 @@ -0,0 +1,88 @@ + LINUX DRIVER FOR SOUNDCARDS AS AX.25 MODEMS + + Thomas M. Sailer, HB9JNX/AE4WA, + +This driver allows either SoundBlaster (sbc) or WindowsSoundSystem (wss) +compatible soundcards to be used as either 1200 baud AFSK or 9600 baud FSK +AX.25 packet radio modems. Only half duplex operation is supported; an +attempt to include full duplex support failed because the hardware did +not support it (it appeared that the card only provides one DMA channel, +although the Codec chip would support two channels). The driver needs +some processing power! A 486DX/2 66MHz is a minimum requirement, otherwise +interactive performance of the computer may become sluggish. + + +The Interface of the driver + +The driver provides a kernel network drivers named sm[0-3]. sethdlc +from the ax25 utilities may be used to set driver states etc. Users +of userland AX.25 stacks may use the net2kiss utility (also available +in the ax25 utilities package) to converts packets of a network interface +to a KISS stream on a pseudo tty. There's also a patch available from +me for WAMPES which allows attaching a kernel network interface directly. + + +Configuring the driver + +Some sound cards need to be initialized before they operate in either +SoundBlaster or WSS compatibility mode. The driver does _NOT_ do this; +you may use the standard linux sound driver to initialize the soundcard; +compile it as a module, and do + insmod sound + rmmod sound +The soundcard should then be initialized correctly. If this does not help, +you'll have to write your own initialization utility. + +Every time the driver is inserted into the kernel, it has to know which +modems it should access at which ports. This can be done with the setbaycom +utility. If you are only using one modem, you can also configure the +driver from the insmod command line (or by means of an option line in +/etc/conf.modules). + +Examples: + insmod soundmodem hw=0 mode=0 iobase=0x220 irq=5 dma=1 + sethdlc -i sm0 -p hw sbc type afsk1200 io 0x220 irq 5 dma 1 + +Both lines configure the first port to drive a soundblaster card +in 1200 baud AFSK mode. + +The channel access parameters can be set with sethdlc -a or kissparms. +Note that both utilities interpret the values slightly different. + + +Input and output levels + +It is important that the input and output levels are adjusted properly. +There are two utilities, available in the ax25 utilities distribution, +to facilitate this: smmixer and smdiag. smdiag allows you to display +the input signal in an oscilloscope like display or an eye diagram. +smmixer allows you to adjust input/output levels. See the respective +man pages. + + +Transmitter keying + +Since soundcards do not have a DC coupled output; PTT keying options include +the following: +* VOX circuitry +* Serial port pin +* Parallel port pin +* MPU401 MIDI output via a retriggerable monoflop. +Circuit schematics may be found at +http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html. + + +Compatibility with the rest of the Linux kernel + +The sound driver and the soundcard modem driver compete for the same +hardware resources. Of course only one driver can access a given +interface at a time. Worse yet, the sound driver grabs the soundcard +at startup time. Therefore the soundcard modem driver subsequently won't +be able to access the soundcard. You might therefore find it necessary to +unload the sound driver before using the soundcard modem driver. + + + +vy 73s de +Tom Sailer, sailer@ife.ee.ethz.ch +hb9jnx @ hb9w.ampr.org diff -u --recursive --new-file v2.1.48/linux/Makefile linux/Makefile --- v2.1.48/linux/Makefile Mon Aug 4 16:25:35 1997 +++ linux/Makefile Wed Aug 6 15:18:17 1997 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 48 +SUBLEVEL = 49 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) @@ -338,7 +338,9 @@ rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash rm -f drivers/net/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h rm -f drivers/net/soundmodem/sm_tbl_{hapn4800,psk4800}.h + rm -f drivers/net/soundmodem/sm_tbl_{afsk2400_7,afsk2400_8}.h rm -f drivers/net/soundmodem/gentbl + rm -f drivers/char/hfmodem/gentbl drivers/char/hfmodem/tables.h rm -f .version .config* config.in config.old rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog diff -u --recursive --new-file v2.1.48/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S --- v2.1.48/linux/arch/i386/kernel/head.S Mon Jun 16 16:35:53 1997 +++ linux/arch/i386/kernel/head.S Tue Aug 5 09:21:29 1997 @@ -312,15 +312,16 @@ pushl %eax pushl %ecx pushl %edx + push %es push %ds movl $(KERNEL_DS),%eax mov %ax,%ds mov %ax,%es - mov %ax,%fs pushl $int_msg call SYMBOL_NAME(printk) popl %eax pop %ds + pop %es popl %edx popl %ecx popl %eax diff -u --recursive --new-file v2.1.48/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.1.48/linux/drivers/char/Makefile Mon Aug 4 16:25:37 1997 +++ linux/drivers/char/Makefile Tue Aug 5 09:48:55 1997 @@ -311,6 +311,17 @@ endif endif +ifeq ($(CONFIG_HFMODEM),y) +ALL_SUB_DIRS += hfmodem +SUB_DIRS += hfmodem +L_OBJS += hfmodem/hfmodem.o +else + ifeq ($(CONFIG_HFMODEM),m) + ALL_SUB_DIRS += hfmodem + MOD_SUB_DIRS += hfmodem + endif +endif + include $(TOPDIR)/Rules.make fastdep: uni_hash.tbl diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/Makefile linux/drivers/char/hfmodem/Makefile --- v2.1.48/linux/drivers/char/hfmodem/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/hfmodem/Makefile Tue Aug 5 09:48:55 1997 @@ -0,0 +1,37 @@ +# +# Makefile for the hfmodem device driver. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. +# + +O_TARGET := hfmodem.o + +O_OBJS := refclock.o modem.o main.o +ifeq ($(CONFIG_HFMODEM_SBC),y) +O_OBJS += sbc.o +endif +ifeq ($(CONFIG_HFMODEM_WSS),y) +O_OBJS += wss.o +endif + +M_OBJS := $(O_TARGET) + +all: all_targets +.PHONY: all + +gentbl: gentbl.c + $(HOSTCC) -Wall $< -o $@ -lm + +TBLHDR := tables.h + +tables.h: gentbl + ./gentbl > $@ + +fastdep: $(TBLHDR) + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/gentbl.c linux/drivers/char/hfmodem/gentbl.c --- v2.1.48/linux/drivers/char/hfmodem/gentbl.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/hfmodem/gentbl.c Tue Aug 5 09:48:55 1997 @@ -0,0 +1,69 @@ +/*****************************************************************************/ + +/* + * gentbl.c -- Linux soundcard HF FSK driver, + * Table generator. + * + * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Swiss Federal Institute of Technology (ETH), Electronics Lab + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +/*****************************************************************************/ + +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +#define SINTABBITS 9 +#define SINTABSIZE (1< + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +/* + * currently this module is supposed to support both module styles, i.e. + * the old one present up to about 2.1.9, and the new one functioning + * starting with 2.1.21. The reason is I have a kit allowing to compile + * this module also under 2.0.x which was requested by several people. + * This will go in 2.2 + */ +#include + +#if LINUX_VERSION_CODE >= 0x20100 +#include +#else +#include +#include + +#undef put_user +#undef get_user + +#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) +#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) + +extern inline int copy_from_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_READ, from, n); + if (i) + return i; + memcpy_fromfs(to, from, n); + return 0; +} + +extern inline int copy_to_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_WRITE, to, n); + if (i) + return i; + memcpy_tofs(to, from, n); + return 0; +} +#endif + +#if LINUX_VERSION_CODE >= 0x20123 +#include +#else +#define __init +#define __initdata +#define __initfunc(x) x +#endif + +/* --------------------------------------------------------------------- */ + +/*static*/ const char hfmodem_drvname[] = "hfmodem"; +static const char hfmodem_drvinfo[] = KERN_INFO "hfmodem: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "hfmodem: version 0.2 compiled " __TIME__ " " __DATE__ "\n"; + +/* --------------------------------------------------------------------- */ +/* + * currently we support only one device + */ + +struct hfmodem_state hfmodem_state[NR_DEVICE]; + +/* --------------------------------------------------------------------- */ +/* + * ===================== port checking routines ======================== + */ + + +#define UART_RBR(iobase) (iobase+0) +#define UART_THR(iobase) (iobase+0) +#define UART_IER(iobase) (iobase+1) +#define UART_IIR(iobase) (iobase+2) +#define UART_FCR(iobase) (iobase+2) +#define UART_LCR(iobase) (iobase+3) +#define UART_MCR(iobase) (iobase+4) +#define UART_LSR(iobase) (iobase+5) +#define UART_MSR(iobase) (iobase+6) +#define UART_SCR(iobase) (iobase+7) +#define UART_DLL(iobase) (iobase+0) +#define UART_DLM(iobase) (iobase+1) + +#define SER_EXTENT 8 + +#define LPT_DATA(iobase) (iobase+0) +#define LPT_STATUS(iobase) (iobase+1) +#define LPT_CONTROL(iobase) (iobase+2) +#define LPT_IRQ_ENABLE 0x10 + +#define LPT_EXTENT 3 + +#define MIDI_DATA(iobase) (iobase) +#define MIDI_STATUS(iobase) (iobase+1) +#define MIDI_READ_FULL 0x80 /* attention: negative logic!! */ +#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */ + +#define MIDI_EXTENT 2 + +#define SP_SER 1 +#define SP_PAR 2 +#define SP_MIDI 4 + +/* ---------------------------------------------------------------------- */ +/* + * returns 0 if ok and != 0 on error; + * the same behaviour as par96_check_lpt in baycom.c + */ + +__initfunc(static int check_lpt(unsigned int iobase)) +{ + unsigned char b1,b2; + int i; + + if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT) + return 0; + if (check_region(iobase, LPT_EXTENT)) + return 0; + b1 = inb(LPT_DATA(iobase)); + b2 = inb(LPT_CONTROL(iobase)); + outb(0xaa, LPT_DATA(iobase)); + i = inb(LPT_DATA(iobase)) == 0xaa; + outb(0x55, LPT_DATA(iobase)); + i &= inb(LPT_DATA(iobase)) == 0x55; + outb(0x0a, LPT_CONTROL(iobase)); + i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a; + outb(0x05, LPT_CONTROL(iobase)); + i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05; + outb(b1, LPT_DATA(iobase)); + outb(b2, LPT_CONTROL(iobase)); + return !i; +} + +/* --------------------------------------------------------------------- */ + +enum uart { c_uart_unknown, c_uart_8250, c_uart_16450, c_uart_16550, c_uart_16550A }; +static const char *uart_str[] __initdata = { "unknown", "8250", "16450", "16550", "16550A" }; + +__initfunc(static enum uart check_uart(unsigned int iobase)) +{ + unsigned char b1,b2,b3; + enum uart u; + enum uart uart_tab[] = { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; + + if (iobase <= 0 || iobase > 0x1000-SER_EXTENT) + return c_uart_unknown; + if (check_region(iobase, SER_EXTENT)) + return c_uart_unknown; + b1 = inb(UART_MCR(iobase)); + outb(b1 | 0x10, UART_MCR(iobase)); /* loopback mode */ + b2 = inb(UART_MSR(iobase)); + outb(0x1a, UART_MCR(iobase)); + b3 = inb(UART_MSR(iobase)) & 0xf0; + outb(b1, UART_MCR(iobase)); /* restore old values */ + outb(b2, UART_MSR(iobase)); + if (b3 != 0x90) + return c_uart_unknown; + inb(UART_RBR(iobase)); + inb(UART_RBR(iobase)); + outb(0x01, UART_FCR(iobase)); /* enable FIFOs */ + u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3]; + if (u == c_uart_16450) { + outb(0x5a, UART_SCR(iobase)); + b1 = inb(UART_SCR(iobase)); + outb(0xa5, UART_SCR(iobase)); + b2 = inb(UART_SCR(iobase)); + if ((b1 != 0x5a) || (b2 != 0xa5)) + u = c_uart_8250; + } + return u; +} + +/* --------------------------------------------------------------------- */ + +__initfunc(static int check_midi(unsigned int iobase)) +{ + unsigned long timeout; + unsigned long flags; + unsigned char b; + + if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT) + return 0; + if (check_region(iobase, MIDI_EXTENT)) + return 0; + timeout = jiffies + (HZ / 100); + while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY) + if ((signed)(jiffies - timeout) > 0) + return 0; + save_flags(flags); + cli(); + outb(0xff, MIDI_DATA(iobase)); + b = inb(MIDI_STATUS(iobase)); + restore_flags(flags); + if (!(b & MIDI_WRITE_EMPTY)) + return 0; + while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY) + if ((signed)(jiffies - timeout) > 0) + return 0; + return 1; +} + +/* --------------------------------------------------------------------- */ + +static void output_status(struct hfmodem_state *dev, int ptt) +{ + int dcd = 0; + + ptt = !!ptt; + if (dev->ptt_out.flags & SP_SER) { + outb(dcd | (ptt << 1), UART_MCR(dev->ptt_out.seriobase)); + outb(0x40 & (-ptt), UART_LCR(dev->ptt_out.seriobase)); + } + if (dev->ptt_out.flags & SP_PAR) { + outb(ptt | (dcd << 1), LPT_DATA(dev->ptt_out.pariobase)); + } + if (dev->ptt_out.flags & SP_MIDI && ptt) { + outb(0, MIDI_DATA(dev->ptt_out.midiiobase)); + } +} + +/* --------------------------------------------------------------------- */ + +__initfunc(static void output_check(struct hfmodem_state *dev)) +{ + enum uart u = c_uart_unknown; + + if (dev->ptt_out.seriobase > 0 && dev->ptt_out.seriobase <= 0x1000-SER_EXTENT && + ((u = check_uart(dev->ptt_out.seriobase))) != c_uart_unknown) + printk(KERN_INFO "%s: PTT output: uart found at address 0x%x type %s\n", + hfmodem_drvname, dev->ptt_out.seriobase, uart_str[u]); + else { + if (dev->ptt_out.seriobase > 0) + printk(KERN_WARNING "%s: PTT output: no uart found at address 0x%x\n", + hfmodem_drvname, dev->ptt_out.seriobase); + dev->ptt_out.seriobase = 0; + } + if (dev->ptt_out.pariobase > 0 && dev->ptt_out.pariobase <= 0x1000-LPT_EXTENT && + !check_lpt(dev->ptt_out.pariobase)) + printk(KERN_INFO "%s: PTT output: parallel port found at address 0x%x\n", + hfmodem_drvname, dev->ptt_out.pariobase); + else { + if (dev->ptt_out.pariobase > 0) + printk(KERN_WARNING "%s: PTT output: no parallel port found at address 0x%x\n", + hfmodem_drvname, dev->ptt_out.pariobase); + dev->ptt_out.pariobase = 0; + } + if (dev->ptt_out.midiiobase > 0 && dev->ptt_out.midiiobase <= 0x1000-MIDI_EXTENT && + check_midi(dev->ptt_out.midiiobase)) + printk(KERN_INFO "%s: PTT output: midi port found at address 0x%x\n", + hfmodem_drvname, dev->ptt_out.midiiobase); + else { + if (dev->ptt_out.midiiobase > 0) + printk(KERN_WARNING "%s: PTT output: no midi port found at address 0x%x\n", + hfmodem_drvname, dev->ptt_out.midiiobase); + dev->ptt_out.midiiobase = 0; + } +} + +/* --------------------------------------------------------------------- */ + +static void output_open(struct hfmodem_state *dev) +{ + dev->ptt_out.flags = 0; + if (dev->ptt_out.seriobase > 0) { + if (!check_region(dev->ptt_out.seriobase, SER_EXTENT)) { + request_region(dev->ptt_out.seriobase, SER_EXTENT, "hfmodem ser ptt"); + dev->ptt_out.flags |= SP_SER; + outb(0, UART_IER(dev->ptt_out.seriobase)); + /* 5 bits, 1 stop, no parity, no break, Div latch access */ + outb(0x80, UART_LCR(dev->ptt_out.seriobase)); + outb(0, UART_DLM(dev->ptt_out.seriobase)); + outb(1, UART_DLL(dev->ptt_out.seriobase)); /* as fast as possible */ + /* LCR and MCR set by output_status */ + } else + printk(KERN_WARNING "%s: PTT output: serial port at 0x%x busy\n", + hfmodem_drvname, dev->ptt_out.seriobase); + } + if (dev->ptt_out.pariobase > 0) { + if (!check_region(dev->ptt_out.pariobase, LPT_EXTENT)) { + request_region(dev->ptt_out.pariobase, LPT_EXTENT, "hfmodem par ptt"); + dev->ptt_out.flags |= SP_PAR; + } else + printk(KERN_WARNING "%s: PTT output: parallel port at 0x%x busy\n", + hfmodem_drvname, dev->ptt_out.pariobase); + } + if (dev->ptt_out.midiiobase > 0) { + if (!check_region(dev->ptt_out.midiiobase, MIDI_EXTENT)) { + request_region(dev->ptt_out.midiiobase, MIDI_EXTENT, "hfmodem midi ptt"); + dev->ptt_out.flags |= SP_MIDI; + } else + printk(KERN_WARNING "%s: PTT output: midi port at 0x%x busy\n", + hfmodem_drvname, dev->ptt_out.midiiobase); + } + output_status(dev, 0); + printk(KERN_INFO "%s: PTT output:", hfmodem_drvname); + if (dev->ptt_out.flags & SP_SER) + printk(" serial interface at 0x%x", dev->ptt_out.seriobase); + if (dev->ptt_out.flags & SP_PAR) + printk(" parallel interface at 0x%x", dev->ptt_out.pariobase); + if (dev->ptt_out.flags & SP_MIDI) + printk(" mpu401 (midi) interface at 0x%x", dev->ptt_out.midiiobase); + if (!dev->ptt_out.flags) + printk(" none"); + printk("\n"); +} + +/* --------------------------------------------------------------------- */ + +static void output_close(struct hfmodem_state *dev) +{ + /* release regions used for PTT output */ + output_status(dev, 0); + if (dev->ptt_out.flags & SP_SER) + release_region(dev->ptt_out.seriobase, SER_EXTENT); + if (dev->ptt_out.flags & SP_PAR) + release_region(dev->ptt_out.pariobase, LPT_EXTENT); + if (dev->ptt_out.flags & SP_MIDI) + release_region(dev->ptt_out.midiiobase, MIDI_EXTENT); + dev->ptt_out.flags = 0; +} + +/* --------------------------------------------------------------------- */ + +#define INC_SAMPLE (1000000/HFMODEM_SRATE) +#define INC_FRAGMENT (HFMODEM_FRAGSAMPLES*1000000/HFMODEM_SRATE) +#define SIZE (HFMODEM_FRAGSAMPLES*HFMODEM_NUMFRAGS) + +static void hfmodem_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct hfmodem_state *dev = (struct hfmodem_state *)dev_id; + unsigned int dmaptr; + __s16 *s; + unsigned int curfrag, nfrags; + int i; + hfmodem_time_t l1time; + + dmaptr = dev->scops->intack(dev); + l1time = hfmodem_refclock_current(dev, ((SIZE+dmaptr-dev->dma.last_dmaptr) % SIZE) * + INC_SAMPLE, 1); + curfrag = (dev->dma.last_dmaptr = dmaptr) / HFMODEM_FRAGSAMPLES; + l1time -= INC_SAMPLE * (SIZE+dmaptr-dev->dma.fragptr*HFMODEM_FRAGSAMPLES) % SIZE; + sti(); + /* + * handle receiving + */ + if (dev->dma.ptt_frames <= 0) { + while (dev->dma.fragptr != curfrag) { + if (dev->dma.fragptr < HFMODEM_EXCESSFRAGS) { + s = dev->dma.buf + SIZE + HFMODEM_FRAGSAMPLES * dev->dma.fragptr; + memcpy(s, s - SIZE, HFMODEM_FRAGSIZE); + } else + s = dev->dma.buf + HFMODEM_FRAGSAMPLES * dev->dma.fragptr; + if (dev->sbuf.kbuf && dev->sbuf.kptr && dev->sbuf.rem > 0) { + i = HFMODEM_FRAGSAMPLES; + if (i > dev->sbuf.rem) + i = dev->sbuf.rem; + memcpy(dev->sbuf.kptr, s, i * sizeof(s[0])); + dev->sbuf.rem -= i; + dev->sbuf.kptr += i; + } + hfmodem_input_samples(dev, l1time, INC_SAMPLE, s); + l1time += INC_FRAGMENT; + dev->dma.fragptr++; + if (dev->dma.fragptr >= HFMODEM_NUMFRAGS) + dev->dma.fragptr = 0; + } + /* + * check for output + */ + if (hfmodem_next_tx_event(dev, l1time) > (long)INC_FRAGMENT/2) + goto int_return; + /* + * start output + */ + output_status(dev, 1); + dev->scops->prepare_output(dev); + dev->dma.last_dmaptr = 0; + /* + * clock adjust + */ + l1time = hfmodem_refclock_current(dev, 0, 0); + /* + * fill first two fragments + */ + dev->dma.ptt_frames = 1; + for (i = 0; i < 2 && i < HFMODEM_NUMFRAGS; i++) + if (hfmodem_output_samples(dev, l1time+i*INC_FRAGMENT, INC_SAMPLE, + dev->dma.buf+i*HFMODEM_FRAGSAMPLES)) + dev->dma.ptt_frames = i + 1; + dev->dma.lastfrag = 0; + dev->scops->trigger_output(dev); + /* + * finish already pending rx requests + */ + hfmodem_finish_pending_rx_requests(dev); + goto int_return; + } + /* + * handle transmitting + */ + nfrags = HFMODEM_NUMFRAGS + curfrag - dev->dma.lastfrag; + dev->dma.lastfrag = curfrag; + if (nfrags >= HFMODEM_NUMFRAGS) + nfrags -= HFMODEM_NUMFRAGS; + dev->dma.ptt_frames -= nfrags; + if (dev->dma.ptt_frames < 0) + dev->dma.ptt_frames = 0; + while (dev->dma.ptt_frames < HFMODEM_NUMFRAGS && dev->dma.ptt_frames < 4 && + hfmodem_output_samples(dev, l1time+dev->dma.ptt_frames*INC_FRAGMENT, + INC_SAMPLE, dev->dma.buf + HFMODEM_FRAGSAMPLES * + ((curfrag + dev->dma.ptt_frames) % HFMODEM_NUMFRAGS))) + dev->dma.ptt_frames++; + if (dev->dma.ptt_frames > 0) + goto int_return; + /* + * start receiving + */ + output_status(dev, 0); + dev->dma.last_dmaptr = 0; + dev->dma.lastfrag = 0; + dev->dma.fragptr = 0; + dev->dma.ptt_frames = 0; + dev->scops->prepare_input(dev); + dev->scops->trigger_input(dev); + hfmodem_refclock_current(dev, 0, 0); /* needed to reset the time difference */ +int_return: + hfmodem_wakeup(dev); +} + +/* --------------------------------------------------------------------- */ + +static int hfmodem_close(struct inode *inode, struct file *file) +{ + struct hfmodem_state *dev = &hfmodem_state[0]; + + if (!dev->active) + return -EPERM; + dev->active = 0; + dev->scops->stop(dev); + free_irq(dev->io.irq, dev); + disable_dma(dev->io.dma); + free_dma(dev->io.dma); + release_region(dev->io.base_addr, dev->scops->extent); + kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS)); + hfmodem_clear_rq(dev); + if (dev->sbuf.kbuf) { + kfree_s(dev->sbuf.kbuf, dev->sbuf.size); + dev->sbuf.kbuf = dev->sbuf.kptr = NULL; + dev->sbuf.size = dev->sbuf.rem = 0; + } + output_close(dev); + MOD_DEC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int hfmodem_open(struct inode *inode, struct file *file) +{ + struct hfmodem_state *dev = &hfmodem_state[0]; + + if (dev->active) + return -EBUSY; + if (!dev->scops) + return -EPERM; + /* + * clear vars + */ + memset(&dev->l1, 0, sizeof(dev->l1)); + dev->dma.last_dmaptr = 0; + dev->dma.lastfrag = 0; + dev->dma.fragptr = 0; + dev->dma.ptt_frames = 0; + /* + * allocate memory + */ + if (!(dev->dma.buf = kmalloc(HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS), GFP_KERNEL | GFP_DMA))) + return -ENOMEM; + /* + * allocate resources + */ + if (request_dma(dev->io.dma, hfmodem_drvname)) { + kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS)); + return -EBUSY; + } + if (request_irq(dev->io.irq, hfmodem_interrupt, SA_INTERRUPT, hfmodem_drvname, dev)) { + free_dma(dev->io.dma); + kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS)); + return -EBUSY; + } + request_region(dev->io.base_addr, dev->scops->extent, hfmodem_drvname); + + /* clear requests */ + dev->active++; + MOD_INC_USE_COUNT; + hfmodem_refclock_init(dev); + output_open(dev); + dev->scops->init(dev); + dev->scops->prepare_input(dev); + dev->scops->trigger_input(dev); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static struct file_operations hfmodem_fops = { + NULL, /* hfmodem_seek */ + NULL, /* hfmodem_read */ + NULL, /* hfmodem_write */ + NULL, /* hfmodem_readdir */ +#if LINUX_VERSION_CODE >= 0x20100 + hfmodem_poll, /* hfmodem_poll */ +#else + hfmodem_select, /* hfmodem_select */ +#endif + hfmodem_ioctl, /* hfmodem_ioctl */ + NULL, /* hfmodem_mmap */ + hfmodem_open, /* hfmodem_open */ + hfmodem_close, /* hfmodem_close */ + NULL, /* hfmodem_fsync */ + NULL, /* hfmodem_fasync */ + NULL, /* hfmodem_check_media_change */ + NULL /* hfmodem_revalidate */ +}; + +/* --------------------------------------------------------------------- */ + +static struct miscdevice hfmodem_device = { + HFMODEM_MINOR, hfmodem_drvname, &hfmodem_fops +}; + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +/* + * Command line parameters + */ + +static int hw = 0; +static unsigned int iobase = 0x220; +static unsigned int irq = 7; +static unsigned int dma = 1; + +static unsigned int serio = 0; +static unsigned int pario = 0; +static unsigned int midiio = 0; + +#if LINUX_VERSION_CODE >= 0x20115 + +MODULE_PARM(hw, "i"); +MODULE_PARM_DESC(hw, "hardware type: 0=SBC, 1=WSS"); +MODULE_PARM(iobase, "i"); +MODULE_PARM_DESC(iobase, "io base address"); +MODULE_PARM(irq, "i"); +MODULE_PARM_DESC(irq, "interrupt number"); +MODULE_PARM(dma, "i"); +MODULE_PARM_DESC(dma, "dma number (>=4 for SB16/32/64/etc, <=3 for the rest)"); +MODULE_PARM(serio, "i"); +MODULE_PARM_DESC(serio, "address of serial port to output PTT"); +MODULE_PARM(pario, "i"); +MODULE_PARM_DESC(pario, "address of parial port to output PTT"); +MODULE_PARM(midiio, "i"); +MODULE_PARM_DESC(midiio, "address of midi (MPU401) port to output PTT"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("HF FSK modem code"); + +/* these are the module parameters from refclock.c */ + +MODULE_PARM(scale_tvusec, "i"); +MODULE_PARM_DESC(scale_tvusec, "Scaling value for the tv_usec field (can be obta +ined by refclock)"); + +#ifdef __i386__ +MODULE_PARM(scale_rdtsc, "i"); +MODULE_PARM_DESC(scale_rdtsc, "Scaling value for the rdtsc counter (can be obtai +ned by refclock)"); +MODULE_PARM(rdtsc_ok, "i"); +MODULE_PARM_DESC(rdtsc_ok, "Set to 0 to disable the use of the rdtsc instruction +"); +#endif /* __i386__ */ + +#endif + +__initfunc(int init_module(void)) +{ + int i; + + printk(hfmodem_drvinfo); + memset(hfmodem_state, 0, sizeof(hfmodem_state)); + memset(hfmodem_correlator_cache, 0, sizeof(hfmodem_correlator_cache)); + hfmodem_state[0].io.base_addr = iobase; + hfmodem_state[0].io.irq = irq; + hfmodem_state[0].io.dma = dma; + hfmodem_state[0].ptt_out.seriobase = serio; + hfmodem_state[0].ptt_out.pariobase = pario; + hfmodem_state[0].ptt_out.midiiobase = midiio; + hfmodem_refclock_probe(); + output_check(&hfmodem_state[0]); +#if defined(CONFIG_HFMODEM_WSS) && defined(CONFIG_HFMODEM_SBC) + if (hw) + i = hfmodem_wssprobe(&hfmodem_state[0]); + else + i = hfmodem_sbcprobe(&hfmodem_state[0]); +#else + i = -EINVAL; +#ifdef CONFIG_HFMODEM_WSS + i = hfmodem_wssprobe(&hfmodem_state[0]); +#endif +#ifdef CONFIG_HFMODEM_SBC + i = hfmodem_sbcprobe(&hfmodem_state[0]); +#endif +#endif + if (i) + return i; + if ((i = misc_register(&hfmodem_device))) { + printk(KERN_ERR "%s: cannot register misc device\n", hfmodem_drvname); + return i; + } + return 0; +} + +void cleanup_module(void) +{ + misc_deregister(&hfmodem_device); +} + +#else /* MODULE */ +/* --------------------------------------------------------------------- */ + +static int hw = 0; + +__initfunc(void hfmodem_setup(char *str, int *ints)) +{ + if (ints[0] < 7) { + printk(KERN_WARNING "%s: setup: too few parameters\n", hfmodem_drvname); + return; + } + memset(hfmodem_state, 0, sizeof(hfmodem_state)); + memset(hfmodem_correlator_cache, 0, sizeof(hfmodem_correlator_cache)); + hw = ints[1]; + hfmodem_state[0].io.base_addr = ints[2]; + hfmodem_state[0].io.irq = ints[3]; + hfmodem_state[0].io.dma = ints[4]; + if (ints[0] >= 8) + hfmodem_state[0].ptt_out.seriobase = ints[5]; + if (ints[0] >= 9) + hfmodem_state[0].ptt_out.pariobase = ints[6]; + if (ints[0] >= 10) + hfmodem_state[0].ptt_out.midiiobase = ints[7]; + hfmodem_refclock_setscale(ints[ints[0]-2], ints[ints[0]-1], ints[ints[0]]); +} + +__initfunc(void hfmodem_init(void)) +{ + int i; + + printk(hfmodem_drvinfo); + hfmodem_refclock_probe(); + output_check(&hfmodem_state[0]); +#if defined(CONFIG_HFMODEM_WSS) && defined(CONFIG_HFMODEM_SBC) + if (hw) + i = hfmodem_wssprobe(&hfmodem_state[0]); + else + i = hfmodem_sbcprobe(&hfmodem_state[0]); +#else + i = -EINVAL; +#ifdef CONFIG_HFMODEM_WSS + i = hfmodem_wssprobe(&hfmodem_state[0]); +#endif +#ifdef CONFIG_HFMODEM_SBC + i = hfmodem_sbcprobe(&hfmodem_state[0]); +#endif +#endif + if (i) { + printk(KERN_ERR "%s: soundcard probe failed\n", hfmodem_drvname); + return; + } + if ((i = misc_register(&hfmodem_device))) { + printk(KERN_ERR "%s: cannot register misc device\n", hfmodem_drvname); + return; + } +} + +/* --------------------------------------------------------------------- */ +#endif /* MODULE */ + diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/modem.c linux/drivers/char/hfmodem/modem.c --- v2.1.48/linux/drivers/char/hfmodem/modem.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/hfmodem/modem.c Tue Aug 5 09:48:55 1997 @@ -0,0 +1,792 @@ +/*****************************************************************************/ + +/* + * modem.c -- Linux soundcard HF FSK driver, + * Modem code. + * + * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Swiss Federal Institute of Technology (ETH), Electronics Lab + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +/*****************************************************************************/ + + +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +/* + * currently this module is supposed to support both module styles, i.e. + * the old one present up to about 2.1.9, and the new one functioning + * starting with 2.1.21. The reason is I have a kit allowing to compile + * this module also under 2.0.x which was requested by several people. + * This will go in 2.2 + */ +#include + +#if LINUX_VERSION_CODE >= 0x20100 +#include +#else +#include +#include + +#undef put_user +#undef get_user + +#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) +#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) + +extern inline int copy_from_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_READ, from, n); + if (i) + return i; + memcpy_fromfs(to, from, n); + return 0; +} + +extern inline int copy_to_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_WRITE, to, n); + if (i) + return i; + memcpy_tofs(to, from, n); + return 0; +} +#endif + +#if LINUX_VERSION_CODE >= 0x20123 +#include +#else +#define __init +#define __initdata +#define __initfunc(x) x +#endif + +/* --------------------------------------------------------------------- */ + +struct hfmodem_correlator_cache hfmodem_correlator_cache[HFMODEM_CORRELATOR_CACHE]; + +/* --------------------------------------------------------------------- */ + +#include "tables.h" + +#define M_PI 3.14159265358979323846 /* pi */ + +/* --------------------------------------------------------------------- */ + +extern __inline__ int isimplecos(unsigned int arg) +{ + return isintab[((arg+0x4000) >> (16-SINTABBITS)) & (SINTABSIZE-1)]; +} + +extern __inline__ int isimplesin(unsigned int arg) +{ + return isintab[(arg >> (16-SINTABBITS)) & (SINTABSIZE-1)]; +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ int itblcos(unsigned int arg) +{ + unsigned int x; + int dx; + int s, c; + + x = (arg + (0x8000 >> SINTABBITS)) & (0xffffu & (0xffffu << (16-SINTABBITS))); + dx = arg - x; + x >>= (16-SINTABBITS); + c = isintab[x+(0x4000 >> (16-SINTABBITS))]; + s = isintab[x]; + return c - ((s * dx * (int)(M_PI*64.0)) >> 21); +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ void itblcossin(unsigned int arg, int *cos, int *sin) +{ + unsigned int x; + int dx; + int s, c; + + x = (arg + (0x8000 >> SINTABBITS)) & (0xffffu & (0xffffu << (16-SINTABBITS))); + dx = arg - x; + x >>= (16-SINTABBITS); + c = isintab[x+(0x4000 >> (16-SINTABBITS))]; + s = isintab[x]; + *cos = c - ((s * dx * (int)(M_PI*64.0)) >> 21); + *sin = s + ((c * dx * (int)(M_PI*64.0)) >> 21); +} + +/* --------------------------------------------------------------------- */ + +static unsigned short random_seed; + +extern __inline__ unsigned short random_num(void) +{ + random_seed = 28629 * random_seed + 157; + return random_seed; +} + +/* --------------------------------------------------------------------- */ +/* + * correlator cache routines + */ + +extern __inline__ void cc_lock(unsigned int u) +{ + if (u >= HFMODEM_CORRELATOR_CACHE) + return; + hfmodem_correlator_cache[u].refcnt++; +} + +extern __inline__ void cc_unlock(unsigned int u) +{ + if (u >= HFMODEM_CORRELATOR_CACHE) + return; + if ((--hfmodem_correlator_cache[u].refcnt) <= 0) { + unsigned int i; + + for (i = 0; i < HFMODEM_CORRELATOR_CACHE; i++) + if (hfmodem_correlator_cache[i].lru < 32767) + hfmodem_correlator_cache[i].lru++; + hfmodem_correlator_cache[u].lru = 0; + hfmodem_correlator_cache[u].refcnt = 0; + } +} + + +/* --------------------------------------------------------------------- */ + +extern __inline__ unsigned int cc_lookup(unsigned short phinc0, unsigned short phinc1) +{ + unsigned int j; + + /* find correlator cache entry */ + for (j = 0; j < HFMODEM_CORRELATOR_CACHE; j++) + if (hfmodem_correlator_cache[j].phase_incs[0] == phinc0 && + hfmodem_correlator_cache[j].phase_incs[1] == phinc1) + return j; + return ~0; +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ unsigned int cc_replace(void) +{ + unsigned int j, k = HFMODEM_CORRELATOR_CACHE; + int l = -1; + + for (j = 0; j < HFMODEM_CORRELATOR_CACHE; j++) + if (hfmodem_correlator_cache[j].refcnt <= 0 && hfmodem_correlator_cache[j].lru > l) { + k = j; + l = hfmodem_correlator_cache[j].lru; + } + if (k < HFMODEM_CORRELATOR_CACHE) + return k; + printk(KERN_ERR "%s: modem: out of filter coefficient cache entries\n", hfmodem_drvname); + return random_num() % HFMODEM_CORRELATOR_CACHE; +} + +/* --------------------------------------------------------------------- */ + +#define SH1 8 /* min. ceil(log2(L1CORR_LEN)) - (31-(2*15-1)) */ +#define SH2 (3*15-2*SH1) + +#ifdef __i386__ + +extern __inline__ int icorr(int n, const int *coeff, const short *inp) +{ + int ret, rethi, tmp1 = 0, tmp2 = 0; + + __asm__("\n1:\n\t" + "movswl (%0),%%eax\n\t" + "imull (%1)\n\t" + "subl $2,%0\n\t" + "addl $4,%1\n\t" + "addl %%eax,%3\n\t" + "adcl %%edx,%4\n\t" + "decl %2\n\t" + "jne 1b\n\t" + : "=&S" (inp), "=&D" (coeff), "=&c" (n), "=m" (tmp1), "=m" (tmp2) + : "0" (inp), "1" (coeff), "2" (n) + : "ax", "dx"); + __asm__("shrdl %2,%1,%0\n\t" + "# sarl %2,%1\n\t" + : "=&r" (ret), "=&r" (rethi) + : "i" (SH1), "0" (tmp1), "1" (tmp2)); + + + return ret; +} + +#else /* __i386__ */ + +extern __inline__ int icorr(int n, const int *coeff, const short *inp) +{ + long long sum = 0; + int i; + + for (i = n; i > 0; i--, coeff++, inp--) + sum += (*coeff) * (*inp); + sum >>= SH1; + return sum; +} + +#endif /* __i386__ */ + +/* --------------------------------------------------------------------- */ + +extern __inline__ long long isqr(int x) __attribute__ ((const)); + +extern __inline__ long long isqr(int x) +{ + return ((long long)x) * ((long long)x); +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ hfmodem_soft_t do_filter(struct hfmodem_l1_rxslot *slot, short *s) +{ + unsigned int cc = slot->corr_cache; + long long ll; + + if (cc >= HFMODEM_CORRELATOR_CACHE) { + printk(KERN_ERR "do_filter: correlator cache index overrange\n"); + return 0; + } + ll = isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[1][0], s)) + + isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[1][1], s)) - + isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[0][0], s)) - + isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[0][1], s)); + ll >>= SH2; + return (ll * slot->scale) >> 23; +} + +/* --------------------------------------------------------------------- */ + +static void cc_prepare(struct hfmodem_l1_rxslot *slot, unsigned short phinc0, unsigned short phinc1) +{ + unsigned int j, k, l, ph, phinc; + + slot->scale = (1<<23) / (slot->corrlen*slot->corrlen); + + j = cc_lookup(phinc0, phinc1); + if (j >= HFMODEM_CORRELATOR_CACHE) { + j = cc_replace(); + /* calculate the correlator values */ + printk(KERN_DEBUG "%s: corr cache calc: %u phases: 0x%04x 0x%04x\n", + hfmodem_drvname, j, phinc0, phinc1); + hfmodem_correlator_cache[j].phase_incs[0] = phinc0; + hfmodem_correlator_cache[j].phase_incs[1] = phinc1; + for (k = 0; k < 2; k++) { + phinc = hfmodem_correlator_cache[j].phase_incs[k]; + for (ph = l = 0; l < HFMODEM_MAXCORRLEN; l++, ph = (ph + phinc) & 0xffff) + itblcossin(ph, &hfmodem_correlator_cache[j].correlator[k][0][l], + &hfmodem_correlator_cache[j].correlator[k][1][l]); + } + hfmodem_correlator_cache[j].refcnt = 0; + +#if 0 + printk(KERN_DEBUG "%s: corr: %u ph: 0x%04x 0x%04x\n", hfmodem_drvname, j, + hfmodem_correlator_cache[j].phase_incs[0], + hfmodem_correlator_cache[j].phase_incs[1]); + for (l = 0; l < HFMODEM_MAXCORRLEN; l++) + printk(KERN_DEBUG "%s: corr: %6d %6d %6d %6d\n", hfmodem_drvname, + hfmodem_correlator_cache[j].correlator[0][0][l], + hfmodem_correlator_cache[j].correlator[0][1][l], + hfmodem_correlator_cache[j].correlator[1][0][l], + hfmodem_correlator_cache[j].correlator[1][1][l]); +#endif + } + slot->corr_cache = j; + cc_lock(j); +} + +/* --------------------------------------------------------------------- */ + +void hfmodem_clear_rq(struct hfmodem_state *dev) +{ + unsigned long flags; + unsigned int i; + + save_flags(flags); + cli(); + for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { + if (dev->l1.rxslots[i].state == ss_unused) + continue; + dev->l1.rxslots[i].state = ss_unused; + kfree_s(dev->l1.rxslots[i].data, dev->l1.rxslots[i].nbits * sizeof(hfmodem_soft_t)); + } + for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) { + if (dev->l1.txslots[i].state == ss_unused) + continue; + dev->l1.txslots[i].state = ss_unused; + kfree_s(dev->l1.txslots[i].data, (dev->l1.txslots[i].nbits + 7) >> 3); + } + for (i = 0; i < HFMODEM_CORRELATOR_CACHE; i++) + hfmodem_correlator_cache[i].refcnt = 0; + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +int hfmodem_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct hfmodem_state *dev = &hfmodem_state[0]; + struct hfmodem_ioctl_fsk_tx_request txrq; + struct hfmodem_ioctl_fsk_rx_request rxrq; + struct hfmodem_ioctl_mixer_params mix; + struct hfmodem_ioctl_sample_params spar; + unsigned long flags; + unsigned int len; + int ret, i, idx; + void *data, *userdata; + hfmodem_id_t id; + hfmodem_time_t tm = 0; + + if (!dev->active) + return -EBUSY; + switch(cmd) { + default: + return -EINVAL; + + case HFMODEM_IOCTL_FSKTXREQUEST: + if ((ret = copy_from_user(&txrq, (void *)arg, sizeof(txrq)))) + return ret; + if (txrq.nbits > HFMODEM_MAXBITS) + return -EINVAL; + len = (txrq.nbits + 7) >> 3; + if (!(data = kmalloc(len, GFP_KERNEL))) + return -ENOMEM; + if (copy_from_user(data, txrq.data, len)) { + kfree_s(data, len); + return -EFAULT; + } + save_flags(flags); + cli(); + for (i = 0; i < HFMODEM_NUMTXSLOTS && dev->l1.txslots[i].state != ss_unused; i++); + if (i >= HFMODEM_NUMTXSLOTS) { + restore_flags(flags); + kfree_s(data, len); + return -EBUSY; + } + dev->l1.txslots[i].state = ss_ready; + dev->l1.txslots[i].tstart = txrq.tstart; + dev->l1.txslots[i].tinc = txrq.tinc; + dev->l1.txslots[i].data = data; + dev->l1.txslots[i].nbits = txrq.nbits; + dev->l1.txslots[i].cntbits = 0; + dev->l1.txslots[i].inv = txrq.inv ? 0xff : 0; + dev->l1.txslots[i].id = txrq.id; + dev->l1.txslots[i].phase_incs[0] = ((txrq.freq[0]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) + & 0xffff; + dev->l1.txslots[i].phase_incs[1] = ((txrq.freq[1]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) + & 0xffff; + restore_flags(flags); + return 0; + + case HFMODEM_IOCTL_FSKRXREQUEST: + if ((ret = copy_from_user(&rxrq, (void *)arg, sizeof(rxrq)))) + return ret; + if (rxrq.nbits > HFMODEM_MAXBITS) + return -EINVAL; + if (rxrq.baud < HFMODEM_MINBAUD || rxrq.baud > HFMODEM_MAXBAUD) + return -EINVAL; + len = rxrq.nbits * sizeof(hfmodem_soft_t); + if (verify_area(VERIFY_WRITE, rxrq.data, len)) + return -EFAULT; + if (!(data = kmalloc(len, GFP_KERNEL))) + return -ENOMEM; + save_flags(flags); + cli(); + for (i = 0; i < HFMODEM_NUMRXSLOTS && dev->l1.rxslots[i].state != ss_unused; i++); + if (i >= HFMODEM_NUMRXSLOTS) { + restore_flags(flags); + kfree_s(data, len); + return -EBUSY; + } + dev->l1.rxslots[i].state = ss_ready; + dev->l1.rxslots[i].tstart = rxrq.tstart; + dev->l1.rxslots[i].tinc = rxrq.tinc; + dev->l1.rxslots[i].data = data; + dev->l1.rxslots[i].userdata = rxrq.data; + dev->l1.rxslots[i].nbits = rxrq.nbits; + dev->l1.rxslots[i].cntbits = 0; + dev->l1.rxslots[i].id = rxrq.id; + dev->l1.rxslots[i].corrlen = HFMODEM_SRATE/rxrq.baud; + cc_prepare(dev->l1.rxslots+i, + ((rxrq.freq[0]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) & 0xffff, + ((rxrq.freq[1]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) & 0xffff); + restore_flags(flags); + return 0; + + case HFMODEM_IOCTL_CLEARRQ: + hfmodem_clear_rq(dev); + return 0; + + case HFMODEM_IOCTL_GETCURTIME: + return put_user(dev->l1.last_time + 20000L, (hfmodem_time_t *)arg); /* heuristic */ + + case HFMODEM_IOCTL_WAITRQ: + save_flags(flags); + cli(); + ret = 0; + for (idx = -1, i = 0; i < HFMODEM_NUMRXSLOTS; i++) { + if (dev->l1.rxslots[i].state == ss_unused) + continue; + if (dev->l1.rxslots[i].state != ss_retired) { + ret++; + continue; + } + if (idx < 0 || (signed)(tm - dev->l1.rxslots[i].tstart) > 0) { + idx = i; + tm = dev->l1.rxslots[i].tstart; + } + } + if (idx >= 0) { + cc_unlock(dev->l1.rxslots[idx].corr_cache); + id = dev->l1.rxslots[idx].id; + data = dev->l1.rxslots[idx].data; + userdata = dev->l1.rxslots[idx].userdata; + len = dev->l1.rxslots[idx].nbits * sizeof(hfmodem_soft_t); + dev->l1.rxslots[idx].state = ss_unused; + restore_flags(flags); + ret = copy_to_user(userdata, data, len); + kfree_s(data, len); + return (put_user(id, (hfmodem_id_t *)arg)) ? -EFAULT : ret; + } + for (idx = -1, i = 0; i < HFMODEM_NUMTXSLOTS; i++) { + if (dev->l1.txslots[i].state == ss_unused) + continue; + if (dev->l1.txslots[i].state != ss_retired) { + ret++; + continue; + } + if (idx < 0 || (signed)(tm - dev->l1.txslots[i].tstart) > 0) { + idx = i; + tm = dev->l1.txslots[i].tstart; + } + } + if (idx >= 0) { + id = dev->l1.txslots[idx].id; + data = dev->l1.txslots[idx].data; + len = (dev->l1.txslots[idx].nbits + 7) >> 3; + dev->l1.txslots[idx].state = ss_unused; + restore_flags(flags); + kfree_s(data, len); + return put_user(id, (hfmodem_id_t *)arg); + } + restore_flags(flags); + return ret ? -EAGAIN : -EPIPE; + + case HFMODEM_IOCTL_MIXERPARAMS: + if ((ret = copy_from_user(&mix, (void *)arg, sizeof(mix)))) + return ret; + dev->scops->mixer(dev, mix.src, mix.igain, mix.ogain); + return 0; + + case HFMODEM_IOCTL_SAMPLESTART: + save_flags(flags); + cli(); + if (dev->sbuf.kbuf) + kfree_s(dev->sbuf.kbuf, dev->sbuf.size); + dev->sbuf.kbuf = dev->sbuf.kptr = NULL; + dev->sbuf.size = dev->sbuf.rem = 0; + restore_flags(flags); + if ((ret = copy_from_user(&spar, (void *)arg, sizeof(spar)))) + return ret; + if (spar.len == 0) + return 0; + if (spar.len < 2 || spar.len > 8192) + return -EINVAL; + if (verify_area(VERIFY_WRITE, spar.data, spar.len * sizeof(__s16))) + return -EFAULT; + if (!(dev->sbuf.kbuf = kmalloc(spar.len * sizeof(__s16), GFP_KERNEL))) + return -ENOMEM; + save_flags(flags); + cli(); + dev->sbuf.kptr = dev->sbuf.kbuf; + dev->sbuf.size = spar.len * sizeof(__s16); + dev->sbuf.rem = spar.len; + dev->sbuf.ubuf = spar.data; + restore_flags(flags); + return 0; + + case HFMODEM_IOCTL_SAMPLEFINISHED: + save_flags(flags); + cli(); + if (dev->sbuf.rem > 0) { + restore_flags(flags); + return -EAGAIN; + } + if (!dev->sbuf.kbuf || !dev->sbuf.size) { + restore_flags(flags); + return -EPIPE; + } + restore_flags(flags); + ret = copy_to_user(dev->sbuf.ubuf, dev->sbuf.kbuf, dev->sbuf.size); + kfree_s(dev->sbuf.kbuf, dev->sbuf.size); + dev->sbuf.kbuf = dev->sbuf.kptr = NULL; + dev->sbuf.size = dev->sbuf.rem = 0; + return ret; + } +} + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE >= 0x20100 + +unsigned int hfmodem_poll(struct file *file, poll_table *wait) +{ + struct hfmodem_state *dev = &hfmodem_state[0]; + unsigned long flags; + int i, cnt1, cnt2; + + save_flags(flags); + cli(); + for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) { + if (dev->l1.txslots[i].state == ss_retired) + cnt1++; + if (dev->l1.txslots[i].state != ss_unused) + cnt2++; + } + for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { + if (dev->l1.rxslots[i].state == ss_retired) + cnt1++; + if (dev->l1.rxslots[i].state != ss_unused) + cnt2++; + } + restore_flags(flags); + poll_wait(&dev->wait, wait); + if (cnt1 || !cnt2) + return POLLIN | POLLRDNORM; + return 0; +} + +#else + +int hfmodem_select(struct inode *inode, struct file *file, int sel_type, select_table *wait) +{ + struct hfmodem_state *dev = &hfmodem_state[0]; + unsigned long flags; + int i, cnt1, cnt2; + + if (sel_type == SEL_IN) { + save_flags(flags); + cli(); + for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) { + if (dev->l1.txslots[i].state == ss_retired) + cnt1++; + if (dev->l1.txslots[i].state != ss_unused) + cnt2++; + } + for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { + if (dev->l1.rxslots[i].state == ss_retired) + cnt1++; + if (dev->l1.rxslots[i].state != ss_unused) + cnt2++; + } + restore_flags(flags); + if (cnt1 || !cnt2) + return 1; + select_wait(&dev->wait, wait); + } + return 0; +} + +#endif + +/* --------------------------------------------------------------------- */ + +extern __inline__ unsigned int l1fsk_phinc(struct hfmodem_l1_txslot *txs, unsigned int nbit) +{ + return txs->phase_incs[!!((txs->data[nbit >> 3] ^ txs->inv) & (1 << (nbit & 7)))]; +} + +/* --------------------------------------------------------------------- */ + +void hfmodem_input_samples(struct hfmodem_state *dev, hfmodem_time_t tstart, + hfmodem_time_t tinc, __s16 *samples) +{ + hfmodem_time_t tst, tend; + __s16 *s; + int i, j; + hfmodem_soft_t sample; + + dev->l1.last_time = tstart + (HFMODEM_FRAGSAMPLES-1) * tinc; + for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { + struct hfmodem_l1_rxslot *rxs = dev->l1.rxslots + i; + + if (rxs->state == ss_unused || rxs->state == ss_retired) + continue; + tst = tstart - (rxs->corrlen-1) * tinc; + tend = tst + (HFMODEM_FRAGSAMPLES-1) * tinc; + if (rxs->state == ss_ready) { + if ((signed)(rxs->tstart - tend) > 0) + continue; + rxs->state = ss_oper; + } + for (s = samples, j = 0; j < HFMODEM_FRAGSAMPLES; j++, s++, tst += tinc) + if ((signed)(rxs->tstart - tst) <= 0) { + sample = do_filter(rxs, s); + while ((signed)(rxs->tstart - tst) <= 0 && + rxs->cntbits < rxs->nbits) { + rxs->data[rxs->cntbits] = sample; + rxs->cntbits++; + rxs->tstart += rxs->tinc; + } + if (rxs->cntbits >= rxs->nbits) { + rxs->state = ss_retired; + break; + } + } + } +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ unsigned int output_one_sample(struct hfmodem_state *dev, hfmodem_time_t tm) +{ + int i, j, k; + struct hfmodem_l1_txslot *txs; + /* + * first activate new output slots + */ + for (j = -1, i = 0; i < HFMODEM_NUMTXSLOTS; i++) { + txs = dev->l1.txslots + i; + if (txs->state == ss_ready && (signed)(txs->tstart - tm) <= 0) { + for (k = 0; k < HFMODEM_NUMTXSLOTS; k++) { + if (dev->l1.txslots[k].state != ss_oper) + continue; + dev->l1.txslots[k].state = ss_retired; + } + txs->state = ss_oper; + txs->tstart += txs->tinc; + txs->phinc = l1fsk_phinc(txs, 0); + txs->cntbits = 1; + }; + if (txs->state != ss_oper) + continue; + j = i; + } + if (j < 0 || j >= HFMODEM_NUMTXSLOTS) + return 0; + /* + * calculate the current slot + */ + txs = dev->l1.txslots + j; + while ((signed)(txs->tstart - tm) <= 0) { + if (txs->cntbits >= txs->nbits) { + txs->state = ss_retired; + return 0; + } + txs->tstart += txs->tinc; + txs->phinc = l1fsk_phinc(txs, txs->cntbits); + txs->cntbits++; + } + return txs->phinc; +} + +/* --------------------------------------------------------------------- */ + +int hfmodem_output_samples(struct hfmodem_state *dev, hfmodem_time_t tstart, + hfmodem_time_t tinc, __s16 *samples) +{ + int i, j; + hfmodem_time_t tend = tstart + (HFMODEM_FRAGSAMPLES-1) * tinc; + + for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) { + if (dev->l1.txslots[i].state == ss_oper) + break; + if (dev->l1.txslots[i].state == ss_ready && + (signed)(dev->l1.txslots[i].tstart - tend) <= 0) + break; + } + if (i >= HFMODEM_NUMTXSLOTS) + return 0; + for (j = 0; j < HFMODEM_FRAGSAMPLES; j++, tstart += tinc, samples++) { + *samples = isimplecos(dev->l1.tx_phase); + dev->l1.tx_phase += output_one_sample(dev, tstart); + } + return 1; +} + +/* --------------------------------------------------------------------- */ + +long hfmodem_next_tx_event(struct hfmodem_state *dev, hfmodem_time_t curr) +{ + long diff = LONG_MAX, t; + int i; + + for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) { + if (dev->l1.txslots[i].state == ss_oper) + if (diff > 0) + diff = 0; + if (dev->l1.txslots[i].state == ss_ready) { + t = dev->l1.txslots[i].tstart - curr; + if (t < diff) + diff = t; + } + } + return diff; +} + +/* --------------------------------------------------------------------- */ + +void hfmodem_finish_pending_rx_requests(struct hfmodem_state *dev) +{ + int i; + + for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { + if (dev->l1.rxslots[i].state != ss_oper) + continue; + while (dev->l1.rxslots[i].cntbits < dev->l1.rxslots[i].nbits) { + dev->l1.rxslots[i].data[dev->l1.rxslots[i].cntbits] = 0; + dev->l1.rxslots[i].cntbits++; + } + dev->l1.rxslots[i].state = ss_retired; + } +} + +/* --------------------------------------------------------------------- */ + +void hfmodem_wakeup(struct hfmodem_state *dev) +{ + int i, cnt1, cnt2; + + for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) { + if (dev->l1.txslots[i].state == ss_retired) + cnt1++; + if (dev->l1.txslots[i].state != ss_unused) + cnt2++; + } + for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { + if (dev->l1.rxslots[i].state == ss_retired) + cnt1++; + if (dev->l1.rxslots[i].state != ss_unused) + cnt2++; + } + if (cnt1 || !cnt2) + wake_up_interruptible(&dev->wait); +} + +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/refclock.c linux/drivers/char/hfmodem/refclock.c --- v2.1.48/linux/drivers/char/hfmodem/refclock.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/hfmodem/refclock.c Tue Aug 5 09:48:55 1997 @@ -0,0 +1,189 @@ +/*****************************************************************************/ + +/* + * refclock.c -- Linux soundcard HF FSK driver, + * Reference clock routines. + * + * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Swiss Federal Institute of Technology (ETH), Electronics Lab + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +/* + * currently this module is supposed to support both module styles, i.e. + * the old one present up to about 2.1.9, and the new one functioning + * starting with 2.1.21. The reason is I have a kit allowing to compile + * this module also under 2.0.x which was requested by several people. + * This will go in 2.2 + */ +#include + +#if LINUX_VERSION_CODE >= 0x20123 +#include +#else +#define __init +#define __initdata +#define __initfunc(x) x +#endif + +/* --------------------------------------------------------------------- */ +/* + * command line params + */ + +static unsigned int scale_tvusec = 1UL<<24; + +#ifdef __i386__ +static unsigned int scale_rdtsc = 0; +static int rdtsc_ok = 1; +#endif /* __i386__ */ + +/* --------------------------------------------------------------------- */ + +#ifdef __i386__ + +__initfunc(static void i386_capability(void)) +{ + unsigned long flags; + unsigned long fl1; + union { + struct { + unsigned int ebx, edx, ecx; + } r; + unsigned char s[13]; + } id; + unsigned int eax; + unsigned int x86_capability; + + save_flags(flags); + flags |= 0x200000; + restore_flags(flags); + save_flags(flags); + fl1 = flags; + flags &= ~0x200000; + restore_flags(flags); + save_flags(flags); + if (!(fl1 & 0x200000) || (flags & 0x200000)) { + printk(KERN_WARNING "%s: cpu does not support CPUID\n", hfmodem_drvname); + return; + } + __asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) : + "0" (0)); + id.s[12] = 0; + if (eax < 1) { + printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability " + "list\n", hfmodem_drvname, id.s); + return; + } + printk(KERN_INFO "%s: cpu: vendor string %s ", hfmodem_drvname, id.s); + __asm__ ("cpuid" : "=a" (eax), "=d" (x86_capability) : "0" (1) : "ebx", "ecx"); + printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15, eax & 15, + x86_capability); + if (x86_capability & 0x10) + rdtsc_ok = 1; + else + printk(KERN_INFO "%s: cpu does not support the rdtsc instruction\n", hfmodem_drvname); +} +#endif /* __i386__ */ + +/* --------------------------------------------------------------------- */ + +__initfunc(void hfmodem_refclock_probe(void)) +{ +#ifdef __i386__ + if (rdtsc_ok) { + rdtsc_ok = 0; + i386_capability(); + if (rdtsc_ok) { + unsigned int tmp0, tmp1, tmp2, tmp3; + __asm__("rdtsc" : "=a" (tmp0), "=d" (tmp1)); + __asm__("rdtsc" : "=a" (tmp2), "=d" (tmp3)); + if (tmp0 == tmp2 && tmp1 == tmp3) { + rdtsc_ok = 0; + printk(KERN_WARNING "%s: rdtsc unusable, does not change\n", + hfmodem_drvname); + } + } + } + printk(KERN_INFO "%s: using %s as timing source\n", hfmodem_drvname, + rdtsc_ok ? "rdtsc" : "gettimeofday"); +#endif /* __i386__ */ +} + +/* --------------------------------------------------------------------- */ + +void hfmodem_refclock_init(struct hfmodem_state *dev) +{ + struct timeval tv; + + dev->clk.lasttime = 0; +#ifdef __i386__ + if (rdtsc_ok) { + __asm__("rdtsc;" : "=&d" (dev->clk.starttime_hi), "=&a" (dev->clk.starttime_lo)); + return; + } +#endif /* __i386__ */ + do_gettimeofday(&tv); + dev->clk.last_tvusec = tv.tv_usec; + dev->clk.time_cnt = 0; +} + +/* --------------------------------------------------------------------- */ + +hfmodem_time_t hfmodem_refclock_current(struct hfmodem_state *dev, hfmodem_time_t expected, int exp_valid) +{ + struct timeval tv; + hfmodem_time_t curtime; + long diff; + +#ifdef __i386__ + if (rdtsc_ok) { + unsigned int tmp0, tmp1; + unsigned int tmp2, tmp3; + + __asm__("rdtsc;\n\t" + "subl %2,%%eax\n\t" + "sbbl %3,%%edx\n\t" : "=&a" (tmp0), "=&d" (tmp1) + : "m" (dev->clk.starttime_lo), "m" (dev->clk.starttime_hi) : "ax", "dx"); + __asm__("mull %1" : "=d" (tmp2) : "m" (scale_rdtsc), "a" (tmp0) : "ax"); + __asm__("mull %1" : "=a" (tmp3) : "m" (scale_rdtsc), "a" (tmp1) : "dx"); + curtime = tmp2 + tmp3; + goto time_known; + } +#endif /* __i386__ */ + do_gettimeofday(&tv); + dev->clk.time_cnt += (unsigned)(1000000 + tv.tv_usec - dev->clk.last_tvusec) % 1000000; + dev->clk.last_tvusec = tv.tv_usec; + curtime = (dev->clk.time_cnt * scale_tvusec) >> 24; + time_known: + if (exp_valid && abs(diff = (curtime - dev->clk.lasttime - expected)) >= 1000) + printk(KERN_DEBUG "%s: refclock adjustment %ld more than 1ms\n", + hfmodem_drvname, diff); + return (dev->clk.lasttime = curtime); +} + +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/sbc.c linux/drivers/char/hfmodem/sbc.c --- v2.1.48/linux/drivers/char/hfmodem/sbc.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/hfmodem/sbc.c Tue Aug 5 09:48:55 1997 @@ -0,0 +1,741 @@ +/*****************************************************************************/ + +/* + * sbc.c -- Linux soundcard HF FSK driver, + * Soundblaster specific functions. + * + * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Swiss Federal Institute of Technology (ETH), Electronics Lab + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* --------------------------------------------------------------------- */ +/* + * the sbc converter's registers + */ +#define DSP_RESET(iobase) (iobase+0x6) +#define DSP_READ_DATA(iobase) (iobase+0xa) +#define DSP_WRITE_DATA(iobase) (iobase+0xc) +#define DSP_WRITE_STATUS(iobase) (iobase+0xc) +#define DSP_DATA_AVAIL(iobase) (iobase+0xe) +#define DSP_MIXER_ADDR(iobase) (iobase+0x4) +#define DSP_MIXER_DATA(iobase) (iobase+0x5) +#define DSP_INTACK_16BIT(iobase) (iobase+0xf) +#define SBC_EXTENT 16 + +/* --------------------------------------------------------------------- */ +/* + * SBC commands + */ + +#define SBC_OUTPUT 0x14 +#define SBC_INPUT 0x24 +#define SBC_BLOCKSIZE 0x48 +#define SBC_HI_OUTPUT 0x91 +#define SBC_HI_INPUT 0x99 +#define SBC_LO_OUTPUT_AUTOINIT 0x1c +#define SBC_LO_INPUT_AUTOINIT 0x2c +#define SBC_HI_OUTPUT_AUTOINIT 0x90 +#define SBC_HI_INPUT_AUTOINIT 0x98 +#define SBC_IMMED_INT 0xf2 +#define SBC_GET_REVISION 0xe1 +#define ESS_GET_REVISION 0xe7 +#define ESS_EXTENDED_MODE 0xc6 +#define SBC_SPEAKER_ON 0xd1 +#define SBC_SPEAKER_OFF 0xd3 +#define SBC_DMA_ON 0xd0 +#define SBC_DMA_OFF 0xd4 +#define SBC_SAMPLE_RATE 0x40 +#define SBC_SAMPLE_RATE_OUT 0x41 +#define SBC_SAMPLE_RATE_IN 0x42 +#define SBC_MONO_8BIT 0xa0 +#define SBC_MONO_16BIT 0xa4 +#define SBC_STEREO_8BIT 0xa8 +#define SBC_STEREO_16BIT 0xac + +#define SBC4_OUT8_AI 0xc6 +#define SBC4_IN8_AI 0xce +#define SBC4_MODE_UNS_MONO 0x00 +#define SBC4_MODE_SIGN_MONO 0x10 + +#define SBC4_OUT16_AI 0xb6 +#define SBC4_IN16_AI 0xbe +#define SBC4_OUT16_AI_NO_FIFO 0xb4 +#define SBC4_IN16_AI_NO_FIFO 0xbc + +/* --------------------------------------------------------------------- */ + +extern const struct hfmodem_scops sbc4_scops; +extern const struct hfmodem_scops ess_scops; + +/* --------------------------------------------------------------------- */ + +static int reset_dsp(struct hfmodem_state *dev) +{ + int i; + + outb(1, DSP_RESET(dev->io.base_addr)); + udelay(3); + outb(0, DSP_RESET(dev->io.base_addr)); + for (i = 0; i < 0xffff; i++) + if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) + if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa) + return 1; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void write_dsp(struct hfmodem_state *dev, unsigned char data) +{ + int i; + + for (i = 0; i < 0xffff; i++) + if (!(inb(DSP_WRITE_STATUS(dev->io.base_addr)) & 0x80)) { + outb(data, DSP_WRITE_DATA(dev->io.base_addr)); + return; + } +} + +/* --------------------------------------------------------------------- */ + +static int read_dsp(struct hfmodem_state *dev, unsigned char *data) +{ + int i; + + if (!data) + return 0; + for (i = 0; i < 0xffff; i++) + if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) { + *data = inb(DSP_READ_DATA(dev->io.base_addr)); + return 1; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void write_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char data) +{ + write_dsp(dev, reg); + write_dsp(dev, data); +} + +/* --------------------------------------------------------------------- */ + +static int read_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char *data) +{ + write_dsp(dev, 0xc0); + write_dsp(dev, reg); + return read_dsp(dev, data); +} + +/* --------------------------------------------------------------------- */ + +static int reset_ess(struct hfmodem_state *dev) +{ + int i; + + outb(3, DSP_RESET(dev->io.base_addr)); /* reset FIFOs too */ + udelay(3); + outb(0, DSP_RESET(dev->io.base_addr)); + for (i = 0; i < 0xffff; i++) + if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) + if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa) { + write_dsp(dev, ESS_EXTENDED_MODE); + return 1; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int config_resources(struct hfmodem_state *dev) +{ + unsigned char irqreg = 0, dmareg = 0, realirq, realdma; + unsigned long flags; + + switch (dev->io.irq) { + case 2: + case 9: + irqreg |= 0x01; + break; + + case 5: + irqreg |= 0x02; + break; + + case 7: + irqreg |= 0x04; + break; + + case 10: + irqreg |= 0x08; + break; + + default: + return -ENODEV; + } + + switch (dev->io.dma) { + case 0: + dmareg |= 0x01; + break; + + case 1: + dmareg |= 0x02; + break; + + case 3: + dmareg |= 0x08; + break; + + case 5: + dmareg |= 0x20; + break; + + case 6: + dmareg |= 0x40; + break; + + case 7: + dmareg |= 0x80; + break; + + default: + return -ENODEV; + } + save_flags(flags); + cli(); + outb(0x80, DSP_MIXER_ADDR(dev->io.base_addr)); + outb(irqreg, DSP_MIXER_DATA(dev->io.base_addr)); + realirq = inb(DSP_MIXER_DATA(dev->io.base_addr)); + outb(0x81, DSP_MIXER_ADDR(dev->io.base_addr)); + outb(dmareg, DSP_MIXER_DATA(dev->io.base_addr)); + realdma = inb(DSP_MIXER_DATA(dev->io.base_addr)); + restore_flags(flags); + if ((~realirq) & irqreg || (~realdma) & dmareg) { + printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device " + "and IRQ/DMA specified wrongly?\n", hfmodem_drvname); + return -EINVAL; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ void sbc_int_ack_8bit(struct hfmodem_state *dev) +{ + inb(DSP_DATA_AVAIL(dev->io.base_addr)); +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ void sbc_int_ack_16bit(struct hfmodem_state *dev) +{ + inb(DSP_INTACK_16BIT(dev->io.base_addr)); +} + +/* --------------------------------------------------------------------- */ + +static void set_mixer(struct hfmodem_state *dev, unsigned char reg, unsigned char data) +{ + outb(reg, DSP_MIXER_ADDR(dev->io.base_addr)); + outb(data, DSP_MIXER_DATA(dev->io.base_addr)); +} + +/* --------------------------------------------------------------------- */ + +int hfmodem_sbcprobe(struct hfmodem_state *dev) +{ + unsigned char revhi, revlo, essrevhi, essrevlo, tmp; + int ret; + + if (dev->io.base_addr <= 0 || dev->io.base_addr > 0x1000-SBC_EXTENT || + dev->io.irq < 2 || dev->io.irq > 15 || dev->io.dma > 7 || dev->io.dma == 2) + return -ENXIO; + if (check_region(dev->io.base_addr, SBC_EXTENT)) + return -EACCES; + /* + * check if a card is available + */ + if (!reset_dsp(dev)) { + printk(KERN_ERR "%s: sbc: no card at io address 0x%x\n", + hfmodem_drvname, dev->io.base_addr); + return -ENODEV; + } + set_mixer(dev, 0, 0); /* reset mixer */ + write_dsp(dev, SBC_GET_REVISION); + if (!read_dsp(dev, &revhi) || !read_dsp(dev, &revlo)) + return -ENODEV; + printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%02d\n", hfmodem_drvname, revhi, revlo); + if (revhi == 3 && revlo == 1) { + write_dsp(dev, ESS_GET_REVISION); + if (!read_dsp(dev, &essrevhi) || !read_dsp(dev, &essrevlo)) + return -ENODEV; + if (essrevhi == 0x48 && (essrevlo & 0xf0) == 0x80) { + printk(KERN_INFO "%s: ESS ES488 AudioDrive (rev %d): unsupported.\n", + hfmodem_drvname, essrevlo & 0x0f); + return -ENODEV; + } + if (essrevhi == 0x68 && (essrevlo & 0xf0) == 0x80) { + printk(KERN_INFO "%s: ESS ES%s688 AudioDrive (rev %d)\n", + hfmodem_drvname, ((essrevlo & 0x0f) >= 8) ? "1" : "", essrevlo & 0x0f); + if (dev->io.dma > 3) { + printk(KERN_INFO "%s: DMA number out of range\n", hfmodem_drvname); + return -ENXIO; + } + printk(KERN_INFO "%s: ess: irq: ", hfmodem_drvname); + read_ess(dev, 0xb1, &tmp); + switch (tmp & 0xf) { + case 0: + printk("2, 9, \"all others\""); + break; + + case 5: + printk("5"); + break; + + case 10: + printk("7"); + break; + + case 15: + printk("10"); + break; + + default: + printk("unknown (%d)", tmp & 0xf); + break; + } + printk(" dma: "); + read_ess(dev, 0xb2, &tmp); + switch (tmp & 0xf) { + case 0: + printk("\"all others\""); + break; + + case 5: + printk("0"); + break; + + case 10: + printk("1"); + break; + + case 15: + printk("3"); + break; + + default: + printk("unknown (%d)", tmp & 0xf); + break; + } + printk("\n"); + dev->scops = &ess_scops; + return 0; + } + } + if (revhi < 4) { + printk(KERN_INFO "%s: at least SB16 required\n", hfmodem_drvname); + return -ENODEV; + } + if (dev->io.dma < 4) { + printk(KERN_INFO "%s: DMA number out of range\n", hfmodem_drvname); + return -ENXIO; + } + if ((ret = config_resources(dev))) + return ret; + dev->scops = &sbc4_scops; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void sbc4_init(struct hfmodem_state *dev) +{ +} + +/* --------------------------------------------------------------------- */ + +static void sbc4_prepare_input(struct hfmodem_state *dev) +{ + unsigned long flags; + + if (!reset_dsp(dev)) { + printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", hfmodem_drvname); + return; + } + save_flags(flags); + cli(); + disable_dma(dev->io.dma); + clear_dma_ff(dev->io.dma); + set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT); + set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); + set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); + enable_dma(dev->io.dma); + sbc_int_ack_16bit(dev); + write_dsp(dev, SBC_SAMPLE_RATE_IN); /* set sampling rate */ + write_dsp(dev, HFMODEM_SRATE >> 8); + write_dsp(dev, HFMODEM_SRATE & 0xff); + write_dsp(dev, SBC_SPEAKER_OFF); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void sbc4_trigger_input(struct hfmodem_state *dev) +{ + unsigned long flags; + + save_flags(flags); + cli(); + write_dsp(dev, SBC4_IN16_AI_NO_FIFO); + write_dsp(dev, SBC4_MODE_UNS_MONO); + write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) & 0xff); + write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) >> 8); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void sbc4_prepare_output(struct hfmodem_state *dev) +{ + unsigned long flags; + + if (!reset_dsp(dev)) { + printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", hfmodem_drvname); + return; + } + save_flags(flags); + cli(); + disable_dma(dev->io.dma); + clear_dma_ff(dev->io.dma); + set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); + set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); + enable_dma(dev->io.dma); + sbc_int_ack_16bit(dev); + write_dsp(dev, SBC_SAMPLE_RATE_OUT); /* set sampling rate */ + write_dsp(dev, HFMODEM_SRATE >> 8); + write_dsp(dev, HFMODEM_SRATE & 0xff); + write_dsp(dev, SBC_SPEAKER_ON); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void sbc4_trigger_output(struct hfmodem_state *dev) +{ + unsigned long flags; + + save_flags(flags); + cli(); + write_dsp(dev, SBC4_OUT16_AI_NO_FIFO); + write_dsp(dev, SBC4_MODE_UNS_MONO); + write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) & 0xff); + write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) >> 8); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void sbc4_stop(struct hfmodem_state *dev) +{ + reset_dsp(dev); +} + +/* --------------------------------------------------------------------- */ + +static unsigned int sbc4_intack(struct hfmodem_state *dev) +{ + unsigned int dmaptr; + unsigned long flags; + unsigned char intsrc; + + save_flags(flags); + cli(); + outb(0x82, DSP_MIXER_ADDR(dev->io.base_addr)); + intsrc = inb(DSP_MIXER_DATA(dev->io.base_addr)); + if (intsrc & 0x01) + sbc_int_ack_8bit(dev); + if (intsrc & 0x02) + sbc_int_ack_16bit(dev); + disable_dma(dev->io.dma); + clear_dma_ff(dev->io.dma); + dmaptr = get_dma_residue(dev->io.dma); + enable_dma(dev->io.dma); + restore_flags(flags); + if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE) + dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE; + return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2; +} + +/* --------------------------------------------------------------------- */ + +static void sbc4_mixer(struct hfmodem_state *dev, int src, int igain, int ogain) +{ + unsigned long flags; + static const unsigned char srcbits[3] = { 0x18, 0x01, 0x06 }; + + save_flags(flags); + cli(); + if (src >= 0 && src <= 2) { + set_mixer(dev, 0x3d, srcbits[src]); + set_mixer(dev, 0x3e, srcbits[src]); + } + if (ogain >= 0 && ogain <= 255) { + set_mixer(dev, 0x30, ogain); + set_mixer(dev, 0x31, ogain); + } + if (igain >= 0 && igain <= 255) { + set_mixer(dev, 0x36, igain); + set_mixer(dev, 0x37, igain); + set_mixer(dev, 0x38, igain); + set_mixer(dev, 0x39, igain); + set_mixer(dev, 0x3a, igain); + } + set_mixer(dev, 0x32, 0xff); + set_mixer(dev, 0x33, 0xff); + set_mixer(dev, 0x34, 0); + set_mixer(dev, 0x35, 0); + set_mixer(dev, 0x3b, 0); /* pc spkr vol */ + set_mixer(dev, 0x3c, 0); /* output src */ + set_mixer(dev, 0x3f, 0); /* inp gain */ + set_mixer(dev, 0x40, 0); + set_mixer(dev, 0x41, 0); /* outp gain */ + set_mixer(dev, 0x42, 0); + set_mixer(dev, 0x43, 1); /* mic agc off */ + set_mixer(dev, 0x44, 8<<4); /* treble */ + set_mixer(dev, 0x45, 8<<4); + set_mixer(dev, 0x46, 8<<4); /* bass */ + set_mixer(dev, 0x47, 8<<4); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void ess_prepare_input(struct hfmodem_state *dev) +{ + unsigned long flags; + unsigned char tmp; + + if (!reset_ess(dev)) { + printk(KERN_ERR "%s: sbc: cannot reset ess dsp\n", hfmodem_drvname); + return; + } + save_flags(flags); + cli(); + disable_dma(dev->io.dma); + clear_dma_ff(dev->io.dma); + set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT); + set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); + set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); + enable_dma(dev->io.dma); + sbc_int_ack_8bit(dev); + write_ess(dev, 0xa1, 128 - (397700 + HFMODEM_SRATE/2) / HFMODEM_SRATE); + /* + * Set filter divider register + * Rolloff at 90% of the half sampling rate + */ + write_ess(dev, 0xa2, 256-(7160000 / (82 * (HFMODEM_SRATE * 9 / 20)))); + write_dsp(dev, SBC_SPEAKER_OFF); + write_ess(dev, 0xb8, 0x0e); /* Auto init DMA mode */ + read_ess(dev, 0xa8, &tmp); + write_ess(dev, 0xa8, (tmp & ~0x03) | 2); /* Mono */ + write_ess(dev, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */ + /* 16 bit mono */ + write_ess(dev, 0xb7, 0x71); + write_ess(dev, 0xb7, 0xf4); + + read_ess(dev, 0xb1, &tmp); + write_ess(dev, 0xb1, (tmp & 0x0f) | 0x50); + read_ess(dev, 0xb2, &tmp); + write_ess(dev, 0xb2, (tmp & 0x0f) | 0x50); + + write_ess(dev, 0xa4, (unsigned char) ((-HFMODEM_FRAGSIZE) & 0xff)); + write_ess(dev, 0xa5, (unsigned char) (((-HFMODEM_FRAGSIZE) >> 8) & 0xff)); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void ess_trigger_input(struct hfmodem_state *dev) +{ + unsigned long flags; + unsigned char tmp; + + save_flags(flags); + cli(); + read_ess(dev, 0xb8, &tmp); + write_ess(dev, 0xb8, tmp | 0x0f); /* Go */ + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +void ess_prepare_output(struct hfmodem_state *dev) +{ + unsigned long flags; + unsigned char tmp; + + if (!reset_ess(dev)) { + printk(KERN_ERR "%s: sbc: cannot reset ess dsp\n", hfmodem_drvname); + return; + } + save_flags(flags); + cli(); + disable_dma(dev->io.dma); + clear_dma_ff(dev->io.dma); + set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); + set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); + enable_dma(dev->io.dma); + sbc_int_ack_8bit(dev); + write_ess(dev, 0xa1, 128 - (397700 + HFMODEM_SRATE/2) / HFMODEM_SRATE); + /* + * Set filter divider register + * Rolloff at 90% of the half sampling rate + */ + write_ess(dev, 0xa2, 256-(7160000 / (82 * (HFMODEM_SRATE * 9 / 20)))); + write_ess(dev, 0xb8, 0x04); /* Auto init DMA mode */ + read_ess(dev, 0xa8, &tmp); + write_ess(dev, 0xa8, (tmp & ~0x03) | 2); /* Mono */ + write_ess(dev, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */ + /* 16 bit mono */ + write_ess(dev, 0xb6, 0x00); + write_ess(dev, 0xb7, 0x71); + write_ess(dev, 0xb7, 0xf4); + + read_ess(dev, 0xb1, &tmp); + write_ess(dev, 0xb1, (tmp & 0x0f) | 0x50); + read_ess(dev, 0xb2, &tmp); + write_ess(dev, 0xb2, (tmp & 0x0f) | 0x50); + + write_ess(dev, 0xa4, (unsigned char) ((-HFMODEM_FRAGSIZE) & 0xff)); + write_ess(dev, 0xa5, (unsigned char) (((-HFMODEM_FRAGSIZE) >> 8) & 0xff)); + + write_dsp(dev, SBC_SPEAKER_ON); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +void ess_trigger_output(struct hfmodem_state *dev) +{ + unsigned long flags; + unsigned char tmp; + + save_flags(flags); + cli(); + read_ess(dev, 0xb8, &tmp); + write_ess(dev, 0xb8, tmp | 0x05); /* Go */ + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +unsigned int ess_intack(struct hfmodem_state *dev) +{ + unsigned int dmaptr; + unsigned long flags; + unsigned char st; +#if 0 + static unsigned int cnt = 0; +#endif + + save_flags(flags); + cli(); + st = inb(DSP_WRITE_STATUS(dev->io.base_addr)); + sbc_int_ack_8bit(dev); + disable_dma(dev->io.dma); + clear_dma_ff(dev->io.dma); + dmaptr = get_dma_residue(dev->io.dma); + enable_dma(dev->io.dma); + restore_flags(flags); +#if 0 + cnt = (cnt + 1) & 0x3f; + if (!cnt) + printk(KERN_DEBUG "%s: ess: FIFO: full:%c empty:%c half empty:%c IRQ: cpu:%c half empty:%c DMA:%c\n", + hfmodem_drvname, '1'-!(st&0x20), '1'-!(st&0x10), '1'-!(st&0x8), + '1'-!(st&0x4), '1'-!(st&0x2), '1'-!(st&0x1)); +#endif + if (st & 0x20) /* FIFO full, 256 bytes */ + dmaptr += 256; + else if (!(st & 0x10)) /* FIFO not empty, assume half full 128 bytes */ + dmaptr += 128; + if (dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE) + dmaptr -= HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE; + if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE) + dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE; + return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2; +} + +/* --------------------------------------------------------------------- */ + +static void ess_mixer(struct hfmodem_state *dev, int src, int igain, int ogain) +{ + unsigned long flags; + + save_flags(flags); + cli(); + if (src >= 0 && src <= 2) + set_mixer(dev, 0x0c, ((src+3) & 3) << 1); + if (ogain >= 0 && ogain <= 255) + set_mixer(dev, 0x22, (ogain & 0xf0) | ((ogain >> 4) & 0xf)); + if (igain >= 0 && igain <= 255) { + set_mixer(dev, 0x36, igain); + set_mixer(dev, 0x37, igain); + set_mixer(dev, 0x38, igain); + set_mixer(dev, 0x39, igain); + set_mixer(dev, 0x3a, igain); + } + set_mixer(dev, 0x4, 0xff); + set_mixer(dev, 0xe, 0x0); + set_mixer(dev, 0x26, 0); + set_mixer(dev, 0x28, 0); + set_mixer(dev, 0x2e, 0); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static const struct hfmodem_scops sbc4_scops = { + SBC_EXTENT, sbc4_init, sbc4_prepare_input, sbc4_trigger_input, + sbc4_prepare_output, sbc4_trigger_output, sbc4_stop, sbc4_intack, sbc4_mixer +}; + +static const struct hfmodem_scops ess_scops = { + SBC_EXTENT, sbc4_init, ess_prepare_input, ess_trigger_input, + ess_prepare_output, ess_trigger_output, sbc4_stop, ess_intack, ess_mixer +}; + +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/wss.c linux/drivers/char/hfmodem/wss.c --- v2.1.48/linux/drivers/char/hfmodem/wss.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/hfmodem/wss.c Tue Aug 5 09:48:55 1997 @@ -0,0 +1,437 @@ +/*****************************************************************************/ + +/* + * wss.c -- Linux soundcard HF FSK driver, + * WindowsSoundSystem specific functions. + * + * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Swiss Federal Institute of Technology (ETH), Electronics Lab + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* --------------------------------------------------------------------- */ + +#define WSS_CONFIG(iobase) (iobase+0) +#define WSS_STATUS(iobase) (iobase+3) +#define WSS_CODEC_IA(iobase) (iobase+4) +#define WSS_CODEC_ID(iobase) (iobase+5) +#define WSS_CODEC_STATUS(iobase) (iobase+6) +#define WSS_CODEC_DATA(iobase) (iobase+7) + +#define WSS_EXTENT 8 + +/* --------------------------------------------------------------------- */ + +extern const struct hfmodem_scops wss_scops; + +/* --------------------------------------------------------------------- */ + +static void write_codec(struct hfmodem_state *dev, unsigned char idx, + unsigned char data) +{ + int timeout = 900000; + + /* wait until codec ready */ + while (timeout > 0 && inb(WSS_CODEC_IA(dev->io.base_addr)) & 0x80) + timeout--; + outb(idx, WSS_CODEC_IA(dev->io.base_addr)); + outb(data, WSS_CODEC_ID(dev->io.base_addr)); +} + +/* --------------------------------------------------------------------- */ + +static unsigned char read_codec(struct hfmodem_state *dev, unsigned char idx) +{ + int timeout = 900000; + + /* wait until codec ready */ + while (timeout > 0 && inb(WSS_CODEC_IA(dev->io.base_addr)) & 0x80) + timeout--; + outb(idx & 0x1f, WSS_CODEC_IA(dev->io.base_addr)); + return inb(WSS_CODEC_ID(dev->io.base_addr)); +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ void wss_ack_int(struct hfmodem_state *dev) +{ + outb(0, WSS_CODEC_STATUS(dev->io.base_addr)); +} + +/* --------------------------------------------------------------------- */ + +static int wss_srate_tab[16] = { + 8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050, + -1, 37800, -1, 44100, 48000, 33075, 9600, 6620 +}; + +static int wss_srate_index(int srate) +{ + int i; + + for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++) + if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0) + return i; + return -1; +} + +/* --------------------------------------------------------------------- */ + +static int wss_set_codec_fmt(struct hfmodem_state *dev, unsigned char fmt) +{ + unsigned long time; + unsigned long flags; + + save_flags(flags); + cli(); + /* Clock and data format register */ + write_codec(dev, 0x48, fmt); + /* MCE and interface config reg */ + write_codec(dev, 0x49, 0xc); + outb(0xb, WSS_CODEC_IA(dev->io.base_addr)); /* leave MCE */ + /* + * wait for ACI start + */ + time = 1000; + while (!(read_codec(dev, 0x0b) & 0x20)) + if (!(--time)) { + printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n", + hfmodem_drvname); + restore_flags(flags); + return -1; + } + /* + * wait for ACI end + */ + sti(); + time = jiffies + HZ/4; + while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0)); + restore_flags(flags); + if ((signed)(jiffies - time) >= 0) { + printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n", + hfmodem_drvname); + return -1; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int wss_init_codec(struct hfmodem_state *dev) +{ + unsigned char tmp, revwss, revid; + static const signed char irqtab[16] = { + -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1, -1, -1 + }; + static const signed char dmatab[4] = { 1, 2, -1, 3 }; + int fmt; + + if ((fmt = wss_srate_index(HFMODEM_SRATE)) < 0) { + printk(KERN_ERR "%s: WSS: sampling rate not supported\n", hfmodem_drvname); + return -1; + } + fmt &= 0x0f; +#ifdef __BIG_ENDIAN + fmt |= 0xc0; +#else /* __BIG_ENDIAN */ + fmt |= 0x40; +#endif /* __BIG_ENDIAN */ + tmp = inb(WSS_STATUS(dev->io.base_addr)); + if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && + (tmp & 0x3f) != 0x0f) { + printk(KERN_WARNING "%s: WSS card id register not found, " + "address 0x%x, ID register 0x%02x\n", hfmodem_drvname, + dev->io.base_addr, (int)tmp); + /* return -1; */ + revwss = 0; + } else { + if ((tmp & 0x80) && ((dev->io.dma == 0) || ((dev->io.irq >= 8) && (dev->io.irq != 9)))) { + printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 " + "(except IRQ9) cannot be used on an 8bit " + "card\n", hfmodem_drvname); + return -1; + } + if (dev->io.irq > 15 || irqtab[dev->io.irq] == -1) { + printk(KERN_ERR "%s: WSS: invalid interrupt %d\n", + hfmodem_drvname, (int)dev->io.irq); + return -1; + } + if (dev->io.dma > 3 || dmatab[dev->io.dma] == -1) { + printk(KERN_ERR "%s: WSS: invalid dma channel %d\n", + hfmodem_drvname, (int)dev->io.dma); + return -1; + } + tmp = irqtab[dev->io.irq] | dmatab[dev->io.dma]; + /* irq probe */ + outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->io.base_addr)); + if (!(inb(WSS_STATUS(dev->io.base_addr)) & 0x40)) { + outb(0, WSS_CONFIG(dev->io.base_addr)); + printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n", + hfmodem_drvname, dev->io.irq); + } + outb(tmp, WSS_CONFIG(dev->io.base_addr)); + revwss = inb(WSS_STATUS(dev->io.base_addr)) & 0x3f; + } + /* + * initialize the codec + */ + write_codec(dev, 9, 0); + write_codec(dev, 12, 0); + write_codec(dev, 0, 0x45); + if (read_codec(dev, 0) != 0x45) + goto codec_err; + write_codec(dev, 0, 0xaa); + if (read_codec(dev, 0) != 0xaa) + goto codec_err; + if (wss_set_codec_fmt(dev, fmt)) + goto codec_err; + write_codec(dev, 0, 0x40); /* left input control */ + write_codec(dev, 1, 0x40); /* right input control */ + write_codec(dev, 2, 0x80); /* left aux#1 input control */ + write_codec(dev, 3, 0x80); /* right aux#1 input control */ + write_codec(dev, 4, 0x80); /* left aux#2 input control */ + write_codec(dev, 5, 0x80); /* right aux#2 input control */ + write_codec(dev, 6, 0x80); /* left dac control */ + write_codec(dev, 7, 0x80); /* right dac control */ + write_codec(dev, 0xa, 0x2); /* pin control register */ + write_codec(dev, 0xd, 0x0); /* digital mix control */ + revid = read_codec(dev, 0xc) & 0xf; + /* + * print revisions + */ + printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n", + hfmodem_drvname, (int)revwss, (int)revid); + return 0; + codec_err: + outb(0, WSS_CONFIG(dev->io.base_addr)); + printk(KERN_ERR "%s: no WSS soundcard found at address 0x%x\n", + hfmodem_drvname, dev->io.base_addr); + return -1; +} + +/* --------------------------------------------------------------------- */ + +int hfmodem_wssprobe(struct hfmodem_state *dev) +{ + if (dev->io.base_addr <= 0 || dev->io.base_addr > 0x1000-WSS_EXTENT || + dev->io.irq < 2 || dev->io.irq > 15 || dev->io.dma > 3 || dev->io.dma == 2) + return -ENXIO; + if (check_region(dev->io.base_addr, WSS_EXTENT)) + return -EACCES; + /* + * check if a card is available + */ + if (wss_init_codec(dev)) { + printk(KERN_ERR "%s: sbc: no card at io address 0x%x\n", + hfmodem_drvname, dev->io.base_addr); + return -ENODEV; + } + dev->scops = &wss_scops; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void wss_init(struct hfmodem_state *dev) +{ + wss_init_codec(dev); +} + +/* --------------------------------------------------------------------- */ + +static void wss_stop(struct hfmodem_state *dev) +{ + unsigned long flags; + unsigned char oldcodecmode; + long abrt; + + save_flags(flags); + cli(); + /* + * perform the final DMA sequence to disable the codec request + */ + oldcodecmode = read_codec(dev, 9); + write_codec(dev, 9, 0xc); /* disable codec */ + wss_ack_int(dev); + if (read_codec(dev, 11) & 0x10) { + disable_dma(dev->io.dma); + clear_dma_ff(dev->io.dma); + set_dma_mode(dev->io.dma, (oldcodecmode & 1) ? + (DMA_MODE_WRITE | DMA_MODE_AUTOINIT) : (DMA_MODE_READ | DMA_MODE_AUTOINIT)); + set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); + set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); + enable_dma(dev->io.dma); + abrt = 0; + while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000)); + } + disable_dma(dev->io.dma); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void wss_prepare_input(struct hfmodem_state *dev) +{ + unsigned long flags; + + wss_stop(dev); + save_flags(flags); + cli(); + disable_dma(dev->io.dma); + clear_dma_ff(dev->io.dma); + set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT); + set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); + set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); + enable_dma(dev->io.dma); + write_codec(dev, 15, (HFMODEM_FRAGSAMPLES-1) & 0xff); + write_codec(dev, 14, (HFMODEM_FRAGSAMPLES-1) >> 8); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void wss_trigger_input(struct hfmodem_state *dev) +{ + unsigned long flags; + + save_flags(flags); + cli(); + write_codec(dev, 9, 0x0e); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void wss_prepare_output(struct hfmodem_state *dev) +{ + unsigned long flags; + + wss_stop(dev); + save_flags(flags); + cli(); + disable_dma(dev->io.dma); + clear_dma_ff(dev->io.dma); + set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); + set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); + enable_dma(dev->io.dma); + write_codec(dev, 15, (HFMODEM_FRAGSAMPLES-1) & 0xff); + write_codec(dev, 14, (HFMODEM_FRAGSAMPLES-1) >> 8); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void wss_trigger_output(struct hfmodem_state *dev) +{ + unsigned long flags; + + save_flags(flags); + cli(); + write_codec(dev, 9, 0x0d); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static unsigned int wss_intack(struct hfmodem_state *dev) +{ + unsigned int dmaptr, nums; + unsigned long flags; + + save_flags(flags); + cli(); + wss_ack_int(dev); + disable_dma(dev->io.dma); + clear_dma_ff(dev->io.dma); + dmaptr = get_dma_residue(dev->io.dma); + if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE) + dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE; + nums = (((dmaptr - 1) % HFMODEM_FRAGSIZE) - 1) / 2; + write_codec(dev, 15, nums & 0xff); + write_codec(dev, 14, nums >> 8); + enable_dma(dev->io.dma); + restore_flags(flags); + return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2; +} + +/* --------------------------------------------------------------------- */ + +static void wss_mixer(struct hfmodem_state *dev, int src, int igain, int ogain) +{ + unsigned long flags; + static const unsigned char srctoreg[3] = { 1, 2, 0 }; + static const unsigned char regtosrc[4] = { 2, 0, 1, 0 }; + unsigned char tmp; + + save_flags(flags); + cli(); + tmp = read_codec(dev, 0x00); + if (src < 0 || src > 2) + src = regtosrc[(tmp >> 6) & 3]; + if (igain < 0 || igain > 255) { + if (src == 1) + igain = ((tmp & 0xf) + ((tmp & 0x20) ? 13 : 0)) << 3; + else + igain = (tmp & 0xf) << 4; + } + if (src == 1) { + if (igain > (28<<3)) + tmp = 0x2f; + else if (igain >= (13<<3)) + tmp = 0x20 + (((igain >> 3) - 13) & 0xf); + else + tmp = (igain >> 3) & 0xf; + } else + tmp = (igain >> 4) & 0xf; + tmp |= srctoreg[src] << 6; + write_codec(dev, 0, tmp); + write_codec(dev, 1, tmp); + if (ogain > 0 && ogain <= 255) { + tmp = 63 - (ogain >> 2); + write_codec(dev, 6, tmp); + write_codec(dev, 7, tmp); + } else if (ogain == 0) { + write_codec(dev, 6, 0x80); + write_codec(dev, 7, 0x80); + } + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static const struct hfmodem_scops wss_scops = { + WSS_EXTENT, wss_init, wss_prepare_input, wss_trigger_input, + wss_prepare_output, wss_trigger_output, wss_stop, wss_intack, wss_mixer +}; + +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.1.48/linux/drivers/char/misc.c linux/drivers/char/misc.c --- v2.1.48/linux/drivers/char/misc.c Mon Aug 4 16:25:37 1997 +++ linux/drivers/char/misc.c Tue Aug 5 09:48:55 1997 @@ -246,6 +246,9 @@ #ifdef CONFIG_NVRAM nvram_init(); #endif +#ifdef CONFIG_HFMODEM + hfmodem_init(); +#endif #endif /* !MODULE */ if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { printk("unable to get major %d for misc devices\n", diff -u --recursive --new-file v2.1.48/linux/drivers/char/serial.c linux/drivers/char/serial.c --- v2.1.48/linux/drivers/char/serial.c Thu Jun 26 12:33:39 1997 +++ linux/drivers/char/serial.c Tue Aug 5 09:24:32 1997 @@ -3503,7 +3503,7 @@ * Print a string to the serial port trying not to disturb any possible * real use of the port... */ -static int serial_console_write(const char *s, unsigned count) +static void serial_console_write(const char *s, unsigned count) { struct serial_state *ser; int ier; @@ -3538,14 +3538,12 @@ */ wait_for_xmitr(ser); outb(ier, ser->port + UART_IER); - - return (0); } /* * Receive character from the serial port */ -static int serial_console_wait_key(void) +static void serial_console_wait_key(void) { struct serial_state *ser; int ier; @@ -3569,8 +3567,6 @@ /* Restore the interrupts */ outb(ier, ser->port + UART_IER); - - return c; } static int serial_console_device(void) diff -u --recursive --new-file v2.1.48/linux/drivers/char/vt.c linux/drivers/char/vt.c --- v2.1.48/linux/drivers/char/vt.c Mon Jun 16 16:35:55 1997 +++ linux/drivers/char/vt.c Wed Aug 6 13:10:58 1997 @@ -490,8 +490,6 @@ case KIOCSOUND: if (!perm) return -EPERM; - if (arg) - arg = 1193180 / arg; kd_mksound(arg, 0); return 0; @@ -506,8 +504,7 @@ * If the time is zero, turn off sound ourselves. */ ticks = HZ * ((arg >> 16) & 0xffff) / 1000; - if ((arg & 0xffff) == 0 ) arg |= 1; /* jp: huh? */ - count = ticks ? (1193180 / (arg & 0xffff)) : 0; + count = ticks ? arg : 0; kd_mksound(count, ticks); return 0; } diff -u --recursive --new-file v2.1.48/linux/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c --- v2.1.48/linux/drivers/isdn/avmb1/capi.c Thu May 29 21:53:05 1997 +++ linux/drivers/isdn/avmb1/capi.c Wed Aug 6 13:02:58 1997 @@ -253,7 +253,7 @@ capi_poll(struct file *file, poll_table * wait) { unsigned int mask = 0; - unsigned int minor = MINOR(file->f_inode->i_rdev); + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); struct capidev *cdev; if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) diff -u --recursive --new-file v2.1.48/linux/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c --- v2.1.48/linux/drivers/isdn/hisax/l3_1tr6.c Thu May 29 21:53:06 1997 +++ linux/drivers/isdn/hisax/l3_1tr6.c Wed Aug 6 13:02:58 1997 @@ -700,7 +700,7 @@ if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld state %d", + sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d state %d", (pr == DL_DATA) ? " " : "(broadcast) ", skb->data[0], skb->len, st->l3.state); l3_debug(st, tmp); diff -u --recursive --new-file v2.1.48/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c --- v2.1.48/linux/drivers/isdn/hisax/l3dss1.c Thu May 29 21:53:06 1997 +++ linux/drivers/isdn/hisax/l3dss1.c Wed Aug 6 13:02:58 1997 @@ -721,7 +721,7 @@ if (skb->data[0] != PROTO_DIS_EURO) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "dss1up%sunexpected discriminator %x message len %ld state %d", + sprintf(tmp, "dss1up%sunexpected discriminator %x message len %d state %d", (pr == DL_DATA) ? " " : "(broadcast) ", skb->data[0], skb->len, st->l3.state); l3_debug(st, tmp); diff -u --recursive --new-file v2.1.48/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c --- v2.1.48/linux/drivers/isdn/isdn_common.c Thu May 29 21:53:06 1997 +++ linux/drivers/isdn/isdn_common.c Wed Aug 6 13:02:58 1997 @@ -1012,7 +1012,7 @@ isdn_poll(struct file *file, poll_table * wait) { unsigned int mask = 0; - unsigned int minor = MINOR(file->f_inode->i_rdev); + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); if (minor == ISDN_MINOR_STATUS) { diff -u --recursive --new-file v2.1.48/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c --- v2.1.48/linux/drivers/isdn/isdn_ppp.c Thu May 29 21:53:06 1997 +++ linux/drivers/isdn/isdn_ppp.c Wed Aug 6 13:02:58 1997 @@ -700,7 +700,7 @@ is = file->private_data; if (is->debug & 0x2) - printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_inode->i_rdev)); + printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_dentry->d_inode->i_rdev)); poll_wait(&is->wq, wait); diff -u --recursive --new-file v2.1.48/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c --- v2.1.48/linux/drivers/misc/parport_pc.c Mon Aug 4 16:25:37 1997 +++ linux/drivers/misc/parport_pc.c Wed Aug 6 13:02:59 1997 @@ -44,10 +44,12 @@ return; } +#if 0 static void pc_write_epp(struct parport *p, unsigned int d) { outb(d, p->base+EPPREG); } +#endif static unsigned int pc_read_epp(struct parport *p) { @@ -320,6 +322,7 @@ return dma; } +#if 0 /* Only called if port supports ECP mode. * * The only restriction on DMA channels is that it has to be @@ -385,6 +388,8 @@ return retv; } +#endif + /****************************************************** * MODE detection section: */ diff -u --recursive --new-file v2.1.48/linux/drivers/net/8390.c linux/drivers/net/8390.c --- v2.1.48/linux/drivers/net/8390.c Thu Jul 17 10:06:05 1997 +++ linux/drivers/net/8390.c Tue Aug 5 09:24:32 1997 @@ -191,7 +191,8 @@ printk("%s: Tx request while isr active.\n",dev->name); outb_p(ENISR_ALL, e8390_base + EN0_IMR); ei_local->stat.tx_errors++; - return 1; + dev_kfree_skb(skb, FREE_WRITE); + return 0; } ei_local->irqlock = 1; @@ -305,6 +306,7 @@ } dev->interrupt = 1; + sti(); /* Change to page 0 and read the intr status reg. */ outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD); diff -u --recursive --new-file v2.1.48/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.1.48/linux/drivers/net/Config.in Thu Jul 17 10:06:05 1997 +++ linux/drivers/net/Config.in Tue Aug 5 09:48:55 1997 @@ -161,6 +161,11 @@ # bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800 # fi fi + tristate 'Shortwave radio modem driver' CONFIG_HFMODEM + if [ "$CONFIG_HFMODEM" != "n" ]; then + bool 'HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC + bool 'HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS + fi fi tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN diff -u --recursive --new-file v2.1.48/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.1.48/linux/drivers/net/Makefile Thu Jul 17 10:06:05 1997 +++ linux/drivers/net/Makefile Tue Aug 5 09:49:50 1997 @@ -692,6 +692,7 @@ ifeq ($(CONFIG_SOUNDMODEM),y) ALL_SUB_DIRS += soundmodem SUB_DIRS += soundmodem +L_OBJS += soundmodem/soundmodem.o CONFIG_HDLCDRV_BUILTIN = y else ifeq ($(CONFIG_SOUNDMODEM),m) diff -u --recursive --new-file v2.1.48/linux/drivers/net/README.baycom linux/drivers/net/README.baycom --- v2.1.48/linux/drivers/net/README.baycom Wed Oct 30 04:15:12 1996 +++ linux/drivers/net/README.baycom Tue Aug 5 09:49:50 1997 @@ -1,118 +0,0 @@ - LINUX DRIVER FOR BAYCOM MODEMS - - Thomas M. Sailer, HB9JNX/AE4WA, - -This document describes the Linux Kernel Driver for simple Baycom style -amateur radio modems. The driver supports the following modems: - -ser12: This is a very simple 1200 baud AFSK modem. The modem consists only - of a modulator/demodulator chip, usually a TI TCM3105. The computer - is responsible for regenerating the receiver bit clock, as well as - for handling the HDLC protocol. The modem connects to a serial port, - hence the name. Since the serial port is not used as an async serial - port, the kernel driver for serial ports cannot be used, and this - driver only supports standard serial hardware (8250, 16450, 16550) - -par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. - The modem does all the filtering and regenerates the receiver clock. - Data is transferred from and to the PC via a shift register. - The shift register is filled with 16 bits and an interrupt is signalled. - The PC then empties the shift register in a burst. This modem connects - to the parallel port, hence the name. The modem leaves the - implementation of the HDLC protocol and the scrambler polynomial to - the PC. - -picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem - is protocol compatible to par96, but uses only three low power ICs - and can therefore be fed from the parallel port and does not require - an additional power supply. Furthermore, it incorporates a carrier - detect circuitry. - -All of the above modems only support half duplex communications. However, -the driver supports the KISS (see below) fullduplex command. It then simply -starts to send as soon as there's a packet to transmit and does not care -about DCD, i.e. it starts to send even if there's someone else on the channel. -This command is required by some implementations of the DAMA channel -access protocol. - - -The Interface of the driver - -Unlike previous drivers, the driver is no longer a character device, -but it is now a true kernel network interface. Installation is therefore -simple. Once installed, four interfaces named bc[0-3] are available. -sethdlc from the ax25 utilities may be used to set driver states etc. -Users of userland AX.25 stacks may use the net2kiss utility (also available -in the ax25 utilities package) to converts packets of a network interface -to a KISS stream on a pseudo tty. There's also a patch available from -me for WAMPES which allows attaching a kernel network interface directly. - - -Configuring the driver - -Every time the driver is inserted into the kernel, it has to know which -modems it should access at which ports. This can be done with the setbaycom -utility. If you are only using one modem, you can also configure the -driver from the insmod command line (or by means of an option line in -/etc/conf.modules). - -Examples: - insmod baycom modem=1 iobase=0x3f8 irq=4 options=1 - sethdlc -i bc0 -p type ser12 io 0x3f8 irq 4 options 1 - -Both lines configure the first port to drive a ser12 modem at the first -serial port (COM1 under DOS). options=1 instructs the driver to use -the software DCD algorithm (see below). - - insmod baycom modem=2 iobase=0x378 irq=7 options=1 - sethdlc -i bc0 -p type par96 io 0x378 irq 7 options 1 - -Both lines configure the first port to drive a par96 or par97 modem at the -first parallel port (LPT1 under DOS). options=1 instructs the driver to use -the software DCD algorithm (see below). - -The channel access parameters can be set with sethdlc -a or kissparms. -Note that both utilities interpret the values slightly different. - - -Hardware DCD versus Software DCD - -To avoid collisions on the air, the driver must know when the channel is -busy. This is the task of the DCD circuitry/software. The driver may either -utilise a software DCD algorithm (options=1) or use a DCD signal from -the hardware (options=0). - -ser12: if software DCD is utilised, the radio's squelch should always be - open. It is highly recommended to use the software DCD algorithm, - as it is much faster than most hardware squelch circuitry. The - disadvantage is a slightly higher load on the system. - -par96: the software DCD algorithm for this type of modem is rather poor. - The modem simply does not provide enough information to implement - a reasonable DCD algorithm in software. Therefore, if your radio - feeds the DCD input of the PAR96 modem, the use of the hardware - DCD circuitry is recommended. - -picpar: the picpar modem features a builtin DCD hardware, which is highly - recommended. - - - -Compatibility with the rest of the Linux kernel - -The serial driver, the line printer (lp) driver and the baycom driver compete -for the same hardware resources. Of course only one driver can access a given -interface at a time. The serial driver grabs all interfaces it can find at -startup time. Therefore the baycom driver subsequently won't be able to -access a serial port. You might therefore find it necessary to release -a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where -# is the number of the interface. The baycom driver does not reserve any -port at startup, unless one is specified on the 'insmod' command line. Another -method to solve the problem is to compile all three drivers as modules and -leave it to kerneld to load the correct driver depending on the application. - - - -vy 73s de -Tom Sailer, sailer@ife.ee.ethz.ch -hb9jnx @ hb9w.ampr.org diff -u --recursive --new-file v2.1.48/linux/drivers/net/README.soundmodem linux/drivers/net/README.soundmodem --- v2.1.48/linux/drivers/net/README.soundmodem Wed Oct 30 04:15:12 1996 +++ linux/drivers/net/README.soundmodem Tue Aug 5 09:49:50 1997 @@ -1,88 +0,0 @@ - LINUX DRIVER FOR SOUNDCARDS AS AX.25 MODEMS - - Thomas M. Sailer, HB9JNX/AE4WA, - -This driver allows either SoundBlaster (sbc) or WindowsSoundSystem (wss) -compatible soundcards to be used as either 1200 baud AFSK or 9600 baud FSK -AX.25 packet radio modems. Only half duplex operation is supported; an -attempt to include full duplex support failed because the hardware did -not support it (it appeared that the card only provides one DMA channel, -although the Codec chip would support two channels). The driver needs -some processing power! A 486DX/2 66MHz is a minimum requirement, otherwise -interactive performance of the computer may become sluggish. - - -The Interface of the driver - -The driver provides a kernel network drivers named sm[0-3]. sethdlc -from the ax25 utilities may be used to set driver states etc. Users -of userland AX.25 stacks may use the net2kiss utility (also available -in the ax25 utilities package) to converts packets of a network interface -to a KISS stream on a pseudo tty. There's also a patch available from -me for WAMPES which allows attaching a kernel network interface directly. - - -Configuring the driver - -Some sound cards need to be initialized before they operate in either -SoundBlaster or WSS compatibility mode. The driver does _NOT_ do this; -you may use the standard linux sound driver to initialize the soundcard; -compile it as a module, and do - insmod sound - rmmod sound -The soundcard should then be initialized correctly. If this does not help, -you'll have to write your own initialization utility. - -Every time the driver is inserted into the kernel, it has to know which -modems it should access at which ports. This can be done with the setbaycom -utility. If you are only using one modem, you can also configure the -driver from the insmod command line (or by means of an option line in -/etc/conf.modules). - -Examples: - insmod soundmodem hw=0 mode=0 iobase=0x220 irq=5 dma=1 - sethdlc -i sm0 -p hw sbc type afsk1200 io 0x220 irq 5 dma 1 - -Both lines configure the first port to drive a soundblaster card -in 1200 baud AFSK mode. - -The channel access parameters can be set with sethdlc -a or kissparms. -Note that both utilities interpret the values slightly different. - - -Input and output levels - -It is important that the input and output levels are adjusted properly. -There are two utilities, available in the ax25 utilities distribution, -to facilitate this: smmixer and smdiag. smdiag allows you to display -the input signal in an oscilloscope like display or an eye diagram. -smmixer allows you to adjust input/output levels. See the respective -man pages. - - -Transmitter keying - -Since soundcards do not have a DC coupled output; PTT keying options include -the following: -* VOX circuitry -* Serial port pin -* Parallel port pin -* MPU401 MIDI output via a retriggerable monoflop. -Circuit schematics may be found at -http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html. - - -Compatibility with the rest of the Linux kernel - -The sound driver and the soundcard modem driver compete for the same -hardware resources. Of course only one driver can access a given -interface at a time. Worse yet, the sound driver grabs the soundcard -at startup time. Therefore the soundcard modem driver subsequently won't -be able to access the soundcard. You might therefore find it necessary to -unload the sound driver before using the soundcard modem driver. - - - -vy 73s de -Tom Sailer, sailer@ife.ee.ethz.ch -hb9jnx @ hb9w.ampr.org diff -u --recursive --new-file v2.1.48/linux/drivers/net/baycom.c linux/drivers/net/baycom.c --- v2.1.48/linux/drivers/net/baycom.c Mon Jun 16 16:35:55 1997 +++ linux/drivers/net/baycom.c Tue Aug 5 09:49:50 1997 @@ -68,7 +68,8 @@ * History: * 0.1 26.06.96 Adapted from baycom.c and made network driver interface * 18.10.96 Changed to new user space access routines (copy_{to,from}_user) - * 0.3 26.04.96 init code/data tagged + * 0.3 26.04.97 init code/data tagged + * 0.4 08.07.97 alternative ser12 decoding algorithm (uses delta CTS ints) */ /*****************************************************************************/ @@ -149,12 +150,13 @@ * modem options; bit mask */ #define BAYCOM_OPTIONS_SOFTDCD 1 +#define BAYCOM_ALT_SER12 /* --------------------------------------------------------------------- */ static const char bc_drvname[] = "baycom"; static const char bc_drvinfo[] = KERN_INFO "baycom: (C) 1996 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "baycom: version 0.3 compiled " __TIME__ " " __DATE__ "\n"; +KERN_INFO "baycom: version 0.4 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ @@ -214,14 +216,20 @@ unsigned char flags; unsigned int shreg; struct modem_state_ser12 { + unsigned char tx_bit; + int dcd_sum0, dcd_sum1, dcd_sum2; unsigned char last_sample; - unsigned char interm_sample; - unsigned int bit_pll; + unsigned char last_rxbit; unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; unsigned int dcd_time; - unsigned char last_rxbit; - unsigned char tx_bit; + unsigned int bit_pll; +#ifdef BAYCOM_ALT_SER12 + unsigned long last_jiffies; + unsigned int pll_time; + unsigned int txshreg; +#else /* BAYCOM_ALT_SER12 */ + unsigned char interm_sample; +#endif /* BAYCOM_ALT_SER12 */ } ser12; struct modem_state_par96 { int dcd_count; @@ -272,6 +280,183 @@ * ===================== SER12 specific routines ========================= */ +#ifdef BAYCOM_ALT_SER12 + +#define SER12_BAUD 1200 + +/* --------------------------------------------------------------------- */ + +extern inline unsigned int hweight16(unsigned short w) + __attribute__ ((unused)); +extern inline unsigned int hweight8(unsigned char w) + __attribute__ ((unused)); + +extern inline unsigned int hweight16(unsigned short w) +{ + unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555); + res = (res & 0x3333) + ((res >> 2) & 0x3333); + res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F); + return (res & 0x00FF) + ((res >> 8) & 0x00FF); +} + +extern inline unsigned int hweight8(unsigned char w) +{ + unsigned short res = (w & 0x55) + ((w >> 1) & 0x55); + res = (res & 0x33) + ((res >> 2) & 0x33); + return (res & 0x0F) + ((res >> 4) & 0x0F); +} + +/* --------------------------------------------------------------------- */ + +static __inline__ void ser12_rxsample(struct device *dev, struct baycom_state *bc, unsigned char news) +{ + bc->modem.ser12.dcd_shreg <<= 1; + bc->modem.ser12.bit_pll += 0x2000; + if (bc->modem.ser12.last_sample != news) { + bc->modem.ser12.last_sample = news; + bc->modem.ser12.dcd_shreg |= 1; + if (bc->modem.ser12.bit_pll < 0x9000) + bc->modem.ser12.bit_pll += 0x1000; + else + bc->modem.ser12.bit_pll -= 0x1000; + bc->modem.ser12.dcd_sum0 += 4 * hweight8(bc->modem.ser12.dcd_shreg & 0x38) + - hweight16(bc->modem.ser12.dcd_shreg & 0x7c0); + } + hdlcdrv_channelbit(&bc->hdrv, !!bc->modem.ser12.last_sample); + if ((--bc->modem.ser12.dcd_time) <= 0) { + hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + + bc->modem.ser12.dcd_sum1 + + bc->modem.ser12.dcd_sum2) < 0); + bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; + bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; + bc->modem.ser12.dcd_sum0 = 2; /* slight bias */ + bc->modem.ser12.dcd_time = 120; + } + if (bc->modem.ser12.bit_pll >= 0x10000) { + bc->modem.ser12.bit_pll &= 0xffff; + bc->modem.shreg >>= 1; + if (bc->modem.ser12.last_rxbit == bc->modem.ser12.last_sample) + bc->modem.shreg |= 0x10000; + bc->modem.ser12.last_rxbit = bc->modem.ser12.last_sample; + if (bc->modem.shreg & 1) { + hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1); + bc->modem.shreg = 0x10000; + } + } +} + +/* --------------------------------------------------------------------- */ + +static __inline__ void ser12_rx(struct device *dev, struct baycom_state *bc, unsigned char curs) +{ + unsigned long curjiff; + struct timeval tv; + unsigned int timediff; + + /* + * get current time + */ + curjiff = jiffies; + do_gettimeofday(&tv); + if ((signed)(curjiff - bc->modem.ser12.last_jiffies) >= HZ/4) { + /* long inactivity; clear HDLC and DCD */ + bc->modem.ser12.dcd_sum1 = 0; + bc->modem.ser12.dcd_sum2 = 0; + bc->modem.ser12.dcd_sum0 = 2; + bc->modem.ser12.dcd_time = 120; + hdlcdrv_setdcd(&bc->hdrv, 0); + hdlcdrv_putbits(&bc->hdrv, 0xffff); + bc->modem.ser12.last_jiffies = curjiff; + bc->modem.ser12.pll_time = tv.tv_usec; + } + bc->modem.ser12.last_jiffies = curjiff; + timediff = tv.tv_usec + 1000000 - bc->modem.ser12.pll_time; + timediff %= 1000000; + timediff /= 125000/SER12_BAUD; + bc->modem.ser12.pll_time = (bc->modem.ser12.pll_time + timediff * (125000/SER12_BAUD)) % 1000000; + for (; timediff > 1; timediff--) + ser12_rxsample(dev, bc, bc->modem.ser12.last_sample); + if (timediff >= 1) + ser12_rxsample(dev, bc, curs); +} + +/* --------------------------------------------------------------------- */ + +static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct baycom_state *bc = (struct baycom_state *)dev->priv; + unsigned char iir, msr = 0; + unsigned int txcount = 0; + unsigned int rxcount = 0; + + if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) + return; + + for (;;) { + iir = inb(IIR(dev->base_addr)); + if (iir & 1) + break; + switch (iir & 6) { + case 6: + inb(LSR(dev->base_addr)); + continue; + + case 4: + inb(RBR(dev->base_addr)); + continue; + + case 2: + /* + * make sure the next interrupt is generated; + * 0 must be used to power the modem; the modem draws its + * power from the TxD line + */ + outb(0x00, THR(dev->base_addr)); + bc->modem.arb_divider--; + baycom_int_freq(bc); + if (hdlcdrv_ptt(&bc->hdrv)) { + /* + * first output the last bit (!) then call HDLC transmitter, + * since this may take quite long + */ + outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr)); + txcount++; + } else + outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ + continue; + + default: + msr = inb(MSR(dev->base_addr)); + if (msr & 1) /* delta CTS interrupt */ + rxcount++; + continue; + } + } + if (rxcount) + ser12_rx(dev, bc, msr & 0x10); + if (txcount) { +#ifdef BAYCOM_DEBUG + if (bc->debug_vals.cur_pllcorr < txcount) + bc->debug_vals.cur_pllcorr = txcount; +#endif /* BAYCOM_DEBUG */ + if (bc->modem.ser12.txshreg <= 1) + bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); + bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1)); + bc->modem.ser12.txshreg >>= 1; + } + sti(); + if (bc->modem.arb_divider <= 0) { + bc->modem.arb_divider = SER12_BAUD/100; + hdlcdrv_arbitrate(dev, &bc->hdrv); + } + hdlcdrv_transmitter(dev, &bc->hdrv); + hdlcdrv_receiver(dev, &bc->hdrv); +} + +/* --------------------------------------------------------------------- */ +#else /* BAYCOM_ALT_SER12 */ + static void inline ser12_set_divisor(struct device *dev, unsigned char divisor) { @@ -503,13 +688,15 @@ hdlcdrv_transmitter(dev, &bc->hdrv); hdlcdrv_receiver(dev, &bc->hdrv); } +#endif /* BAYCOM_ALT_SER12 */ /* --------------------------------------------------------------------- */ enum uart { c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A}; -static const char *uart_str[] = - { "unknown", "8250", "16450", "16550", "16550A" }; + c_uart_16450, c_uart_16550, c_uart_16550A}; +static const char *uart_str[] = { + "unknown", "8250", "16450", "16550", "16550A" +}; static enum uart ser12_check_uart(unsigned int iobase) { @@ -568,6 +755,30 @@ "baycom_ser12", dev)) return -EBUSY; request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12"); +#ifdef BAYCOM_ALT_SER12 + bc->hdrv.par.bitrate = SER12_BAUD; + /* + * set the SIO to 6 Bits/character and 19600 baud, so that + * we get exactly (hopefully) one interrupt per radio symbol + */ + outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ + outb(115200/8/SER12_BAUD, DLL(dev->base_addr)); + outb(0, DLM(dev->base_addr)); + outb(0x01, LCR(dev->base_addr)); /* word length = 6 */ + /* + * enable transmitter empty interrupt and modem status interrupt + */ + outb(0x0a, IER(dev->base_addr)); + /* + * make sure the next interrupt is generated; + * 0 must be used to power the modem; the modem draws its + * power from the TxD line + */ + outb(0x00, THR(dev->base_addr)); + printk(KERN_INFO "%s: ser12(alt modem) at iobase 0x%lx irq %u options " + "0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq, + bc->options, uart_str[u]); +#else /* BAYCOM_ALT_SER12 */ /* * enable transmitter empty interrupt */ @@ -581,6 +792,7 @@ printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u options " "0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq, bc->options, uart_str[u]); +#endif /* BAYCOM_ALT_SER12 */ MOD_INC_USE_COUNT; return 0; } diff -u --recursive --new-file v2.1.48/linux/drivers/net/hdlcdrv.c linux/drivers/net/hdlcdrv.c --- v2.1.48/linux/drivers/net/hdlcdrv.c Mon Jun 16 16:35:55 1997 +++ linux/drivers/net/hdlcdrv.c Tue Aug 5 09:49:50 1997 @@ -33,6 +33,8 @@ * 0.2 21.11.96 various small changes * 0.3 03.03.97 fixed (hopefully) IP not working with ax.25 as a module * 0.4 16.04.97 init code/data tagged + * 0.5 30.07.97 made HDLC buffers bigger (solves a problem with the + * soundmodem driver) */ /*****************************************************************************/ @@ -1019,7 +1021,7 @@ __initfunc(int init_module(void)) { printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n"); - printk(KERN_INFO "hdlcdrv: version 0.4 compiled " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "hdlcdrv: version 0.5 compiled " __TIME__ " " __DATE__ "\n"); #if LINUX_VERSION_CODE < 0x20115 register_symtab(&hdlcdrv_syms); #endif diff -u --recursive --new-file v2.1.48/linux/drivers/net/ne.c linux/drivers/net/ne.c --- v2.1.48/linux/drivers/net/ne.c Wed Apr 23 19:01:20 1997 +++ linux/drivers/net/ne.c Tue Aug 5 09:24:32 1997 @@ -425,7 +425,7 @@ /* Snarf the interrupt now. There's no point in waiting since we cannot share and the board will usually be enabled. */ { - int irqval = request_irq(dev->irq, ei_interrupt, 0, name, NULL); + int irqval = request_irq(dev->irq, ei_interrupt, SA_INTERRUPT, name, NULL); if (irqval) { printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval); return EAGAIN; diff -u --recursive --new-file v2.1.48/linux/drivers/net/plip.c linux/drivers/net/plip.c --- v2.1.48/linux/drivers/net/plip.c Mon Aug 4 16:25:37 1997 +++ linux/drivers/net/plip.c Tue Aug 5 09:24:32 1997 @@ -150,7 +150,7 @@ static int plip_config(struct device *dev, struct ifmap *map); static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd); static int plip_preempt(void *handle); -static int plip_wakeup(void *handle); +static void plip_wakeup(void *handle); enum plip_connection_state { PLIP_CN_NONE=0, diff -u --recursive --new-file v2.1.48/linux/drivers/net/soundmodem/gentbl.c linux/drivers/net/soundmodem/gentbl.c --- v2.1.48/linux/drivers/net/soundmodem/gentbl.c Mon Jun 16 16:35:56 1997 +++ linux/drivers/net/soundmodem/gentbl.c Tue Aug 5 09:49:51 1997 @@ -81,7 +81,6 @@ static void gentbl_afsk1200(FILE *f) { int i, v, sum; - float fv, fsum; #define ARGLO(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_LO/(double)AFSK12_SAMPLE_RATE #define ARGHI(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_HI/(double)AFSK12_SAMPLE_RATE @@ -93,34 +92,7 @@ "#define AFSK12_CORRLEN %u\n\n", AFSK12_SAMPLE_RATE, AFSK12_TX_FREQ_LO, AFSK12_TX_FREQ_HI, AFSK12_CORRLEN); - fprintf(f, "#if defined(CONFIG_SOUNDMODEM_FLOAT) && " - "(defined(CONFIG_M586) || defined(CONFIG_M686))\n\n" - "static const float afsk12_tx_lo_i_f[] = {\n\t"); - for(fsum = i = 0; i < AFSK12_CORRLEN; i++) { - fsum += (fv = cos(ARGLO(i))); - fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' '); - } - fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %7f\n\n" - "static const float afsk12_tx_lo_q_f[] = {\n\t", fsum); - for(fsum = i = 0; i < AFSK12_CORRLEN; i++) { - fsum += (fv = sin(ARGLO(i))); - fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' '); - } - fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %7f\n\n" - "static const float afsk12_tx_hi_i_f[] = {\n\t", fsum); - for(fsum = i = 0; i < AFSK12_CORRLEN; i++) { - fsum += (fv = cos(ARGHI(i))); - fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' '); - } - fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_I %7f\n\n" - "static const float afsk12_tx_hi_q_f[] = {\n\t", fsum); - for(fsum = i = 0; i < AFSK12_CORRLEN; i++) { - fsum += (fv = sin(ARGHI(i))); - fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' '); - } - fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %7f\n\n" - "#else /* CONFIG_SOUNDMODEM_FLOAT */\n\n" - "static const int afsk12_tx_lo_i[] = {\n\t", fsum); + fprintf(f, "static const int afsk12_tx_lo_i[] = {\n\t"); for(sum = i = 0; i < AFSK12_CORRLEN; i++) { sum += (v = 127.0*cos(ARGLO(i))); fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' '); @@ -143,8 +115,7 @@ sum += (v = 127.0*sin(ARGHI(i))); fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' '); } - fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n" - "#endif /* CONFIG_SOUNDMODEM_FLOAT */\n\n", sum); + fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n", sum); #undef ARGLO #undef ARGHI } @@ -594,7 +565,6 @@ static void gentbl_afsk2400(FILE *f, float tcm3105clk) { int i, sum, v; - float fsum, fv; fprintf(f, "\n/*\n * afsk2400 specific tables (tcm3105 clk %7fHz)\n */\n" "#define AFSK24_TX_FREQ_LO %d\n" @@ -608,34 +578,7 @@ #define ARGHI(x) 2.0*M_PI*(double)x*(tcm3105clk/2015.0)/(double)AFSK24_SAMPLERATE #define WINDOW(x) hamming((float)(x)/(AFSK24_CORRLEN-1.0)) - fprintf(f, "#if defined(CONFIG_SOUNDMODEM_FLOAT) && " - "(defined(CONFIG_M586) || defined(CONFIG_M686))\n\n" - "static const float afsk24_tx_lo_i_f[] = {\n\t"); - for(fsum = i = 0; i < AFSK24_CORRLEN; i++) { - fsum += (fv = cos(ARGLO(i))*WINDOW(i)); - fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' '); - } - fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %7f\n\n" - "static const float afsk24_tx_lo_q_f[] = {\n\t", fsum); - for(fsum = i = 0; i < AFSK24_CORRLEN; i++) { - fsum += (fv = sin(ARGLO(i))*WINDOW(i)); - fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' '); - } - fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %7f\n\n" - "static const float afsk24_tx_hi_i_f[] = {\n\t", fsum); - for(fsum = i = 0; i < AFSK24_CORRLEN; i++) { - fsum += (fv = cos(ARGHI(i))*WINDOW(i)); - fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' '); - } - fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_I %7f\n\n" - "static const float afsk24_tx_hi_q_f[] = {\n\t", fsum); - for(fsum = i = 0; i < AFSK24_CORRLEN; i++) { - fsum += (fv = sin(ARGHI(i))*WINDOW(i)); - fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' '); - } - fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %7f\n\n" - "#else /* CONFIG_SOUNDMODEM_FLOAT */\n\n" - "static const int afsk24_tx_lo_i[] = {\n\t", fsum); + fprintf(f, "static const int afsk24_tx_lo_i[] = {\n\t"); for(sum = i = 0; i < AFSK24_CORRLEN; i++) { sum += (v = 127.0*cos(ARGLO(i))*WINDOW(i)); fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' '); @@ -658,8 +601,7 @@ sum += (v = 127.0*sin(ARGHI(i))*WINDOW(i)); fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' '); } - fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n" - "#endif /* CONFIG_SOUNDMODEM_FLOAT */\n\n", sum); + fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n", sum); #undef ARGLO #undef ARGHI #undef WINDOW @@ -675,11 +617,6 @@ "DO NOT EDIT!\n */\n\n", progname); } -static void gentbl_needs_config(FILE *f) -{ - fprintf(f, "\n#include \n\n"); -} - /* -------------------------------------------------------------------- */ void main(int argc, char *argv[]) @@ -690,7 +627,6 @@ if (!(f = fopen("sm_tbl_afsk1200.h", "w"))) exit(1); gentbl_banner(f); - gentbl_needs_config(f); gentbl_offscostab(f, 6); gentbl_costab(f, 6); gentbl_afsk1200(f); @@ -722,7 +658,6 @@ if (!(f = fopen("sm_tbl_afsk2400_8.h", "w"))) exit(1); gentbl_banner(f); - gentbl_needs_config(f); gentbl_offscostab(f, 6); gentbl_costab(f, 6); gentbl_afsk2400(f, 8000000); @@ -730,7 +665,6 @@ if (!(f = fopen("sm_tbl_afsk2400_7.h", "w"))) exit(1); gentbl_banner(f); - gentbl_needs_config(f); gentbl_offscostab(f, 6); gentbl_costab(f, 6); gentbl_afsk2400(f, 7372800); diff -u --recursive --new-file v2.1.48/linux/drivers/net/soundmodem/sm.c linux/drivers/net/soundmodem/sm.c --- v2.1.48/linux/drivers/net/soundmodem/sm.c Mon Jun 16 16:35:56 1997 +++ linux/drivers/net/soundmodem/sm.c Tue Aug 5 09:49:51 1997 @@ -39,6 +39,7 @@ * 0.4 21.01.97 Separately compileable soundcard/modem modules * 0.5 03.03.97 fixed LPT probing (check_lpt result was interpreted the wrong way round) * 0.6 16.04.97 init code/data tagged + * 0.7 30.07.97 fixed halfduplex interrupt handlers/hotfix for CS423X */ /*****************************************************************************/ @@ -113,7 +114,7 @@ /*static*/ const char sm_drvname[] = "soundmodem"; static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1997 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "soundmodem: version 0.6 compiled " __TIME__ " " __DATE__ "\n"; +KERN_INFO "soundmodem: version 0.7 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.1.48/linux/drivers/net/soundmodem/sm_sbc.c linux/drivers/net/soundmodem/sm_sbc.c --- v2.1.48/linux/drivers/net/soundmodem/sm_sbc.c Mon Aug 4 16:25:37 1997 +++ linux/drivers/net/soundmodem/sm_sbc.c Tue Aug 5 09:49:51 1997 @@ -343,6 +343,7 @@ if (hdlcdrv_ptt(&sm->hdrv)) { /* starting to transmit */ disable_dma(dev->dma); + hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */ dma_start_transmit(sm); setup_dma_dsp(dev, sm, 1); dma_transmit(sm); @@ -416,11 +417,7 @@ */ dma_init_receive(sm); dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz; - if (sm->dma.i16bit) - dmasz <<= 1; u = NUM_FRAGMENTS * sm->dma.ofragsz; - if (sm->dma.o16bit) - u <<= 1; if (u > dmasz) dmasz = u; if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA))) diff -u --recursive --new-file v2.1.48/linux/drivers/net/soundmodem/sm_wss.c linux/drivers/net/soundmodem/sm_wss.c --- v2.1.48/linux/drivers/net/soundmodem/sm_wss.c Mon Aug 4 16:25:37 1997 +++ linux/drivers/net/soundmodem/sm_wss.c Tue Aug 5 09:49:51 1997 @@ -98,6 +98,8 @@ #define WSS_EXTENT 8 +#define CS423X_HOTFIX + /* --------------------------------------------------------------------- */ static void write_codec(struct device *dev, unsigned char idx, @@ -170,6 +172,8 @@ /* MCE and interface config reg */ write_codec(dev, 0x49, fdx ? 0x8 : 0xc); outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */ + if (SCSTATE->crystal && !fullcalib) + return 0; /* * wait for ACI start */ @@ -361,15 +365,16 @@ abrt = 0; while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000)); } +#ifdef CS423X_HOTFIX + if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal) + wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0); +#else /* CS423X_HOTFIX */ if (read_codec(dev, 0x8) != fmt) wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0); +#endif /* CS423X_HOTFIX */ numsamps = dma_setup(sm, send, dev->dma) - 1; write_codec(dev, 15, numsamps & 0xff); write_codec(dev, 14, numsamps >> 8); - if (SCSTATE->crystal) { - write_codec(dev, 31, numsamps & 0xff); - write_codec(dev, 30, numsamps >> 8); - } write_codec(dev, 9, codecmode[send]); restore_flags(flags); } @@ -393,10 +398,6 @@ nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1; write_codec(dev, 15, nums & 0xff); write_codec(dev, 14, nums >> 8); - if (SCSTATE->crystal) { - write_codec(dev, 31, nums & 0xff); - write_codec(dev, 30, nums >> 8); - } enable_dma(dev->dma); sm_int_freq(sm); sti(); @@ -406,6 +407,7 @@ if (hdlcdrv_ptt(&sm->hdrv)) { /* starting to transmit */ disable_dma(dev->dma); + hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */ dma_start_transmit(sm); setup_dma_wss(dev, sm, 1); dma_transmit(sm); @@ -413,7 +415,6 @@ } else if (dma_end_transmit(sm, curfrag)) { /* stopping transmission */ disable_dma(dev->dma); - sti(); dma_init_receive(sm); setup_dma_wss(dev, sm, 0); } else @@ -451,11 +452,7 @@ */ dma_init_receive(sm); dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz; - if (sm->dma.i16bit) - dmasz <<= 1; u = NUM_FRAGMENTS * sm->dma.ofragsz; - if (sm->dma.o16bit) - u <<= 1; if (u > dmasz) dmasz = u; if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA))) diff -u --recursive --new-file v2.1.48/linux/drivers/net/soundmodem/smdma.h linux/drivers/net/soundmodem/smdma.h --- v2.1.48/linux/drivers/net/soundmodem/smdma.h Mon Jun 16 16:35:56 1997 +++ linux/drivers/net/soundmodem/smdma.h Tue Aug 5 09:49:51 1997 @@ -37,6 +37,13 @@ #define DMA_MODE_AUTOINIT 0x10 #define NUM_FRAGMENTS 4 +/* + * NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space + * for the modulator to fill the whole DMA buffer without underrun + * at the highest possible baud rate, otherwise the TX state machine will + * not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS + */ + /* --------------------------------------------------------------------- */ /* * ===================== DMA buffer management =========================== diff -u --recursive --new-file v2.1.48/linux/drivers/net/tlan.h linux/drivers/net/tlan.h --- v2.1.48/linux/drivers/net/tlan.h Thu Jul 17 10:06:05 1997 +++ linux/drivers/net/tlan.h Wed Aug 6 13:02:59 1997 @@ -479,7 +479,3 @@ } - - - -#endif diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c --- v2.1.48/linux/drivers/scsi/53c7,8xx.c Mon Aug 4 16:25:38 1997 +++ linux/drivers/scsi/53c7,8xx.c Wed Aug 6 13:02:59 1997 @@ -678,6 +678,7 @@ * Returns : NULL on failure, pointer to host structure on success. */ +#if 0 static struct Scsi_Host * find_host (int host) { struct Scsi_Host *h; @@ -767,6 +768,7 @@ hostdata->options &= ~OPTION_DISCONNECT; return 0; } +#endif /* * Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host) @@ -5468,6 +5470,7 @@ * Returns : char * representation of state, "unknown" on error. */ +#if 0 static const char * ncr_state (int state) { switch (state) { @@ -5479,6 +5482,7 @@ default: return "unknown"; } } +#endif /* * Function : int NCR53c7xx_abort (Scsi_Cmnd *cmd) diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c --- v2.1.48/linux/drivers/scsi/BusLogic.c Wed Apr 23 19:01:21 1997 +++ linux/drivers/scsi/BusLogic.c Mon Aug 11 00:10:00 1997 @@ -27,8 +27,8 @@ */ -#define BusLogic_DriverVersion "2.0.9" -#define BusLogic_DriverDate "29 March 1997" +#define BusLogic_DriverVersion "2.0.10" +#define BusLogic_DriverDate "11 August 1997" #include @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -223,8 +222,7 @@ Host Adapter. */ -__initfunc(static boolean -BusLogic_CreateMailboxes(BusLogic_HostAdapter_T *HostAdapter)) +static boolean BusLogic_CreateMailboxes(BusLogic_HostAdapter_T *HostAdapter) { /* FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes. @@ -306,8 +304,7 @@ BusLogic_CreateInitialCCBs allocates the initial CCBs for Host Adapter. */ -__initfunc(static boolean -BusLogic_CreateInitialCCBs(BusLogic_HostAdapter_T *HostAdapter)) +static boolean BusLogic_CreateInitialCCBs(BusLogic_HostAdapter_T *HostAdapter) { int Allocated; for (Allocated = 0; Allocated < HostAdapter->InitialCCBs; Allocated++) @@ -420,8 +417,8 @@ structure for Host Adapter. */ -__initfunc(static boolean -BusLogic_CreateTargetDeviceStatistics(BusLogic_HostAdapter_T *HostAdapter)) +static boolean BusLogic_CreateTargetDeviceStatistics(BusLogic_HostAdapter_T + *HostAdapter) { HostAdapter->TargetDeviceStatistics = (BusLogic_TargetDeviceStatistics_T *) @@ -508,13 +505,14 @@ Wait for the Host Adapter Ready bit to be set and the Command/Parameter Register Busy bit to be reset in the Status Register. */ - TimeoutCounter = loops_per_sec >> 3; + TimeoutCounter = 10000; while (--TimeoutCounter >= 0) { StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); if (StatusRegister.Bits.HostAdapterReady && !StatusRegister.Bits.CommandParameterRegisterBusy) break; + udelay(100); } if (TimeoutCounter < 0) { @@ -590,11 +588,11 @@ case BusLogic_InquireInstalledDevicesID8to15: case BusLogic_InquireTargetDevices: /* Approximately 60 seconds. */ - TimeoutCounter = loops_per_sec << 2; + TimeoutCounter = 60*10000; break; default: /* Approximately 1 second. */ - TimeoutCounter = loops_per_sec >> 4; + TimeoutCounter = 10000; break; } /* @@ -614,6 +612,7 @@ else BusLogic_ReadDataInRegister(HostAdapter); if (OperationCode == BusLogic_FetchHostAdapterLocalRAM && StatusRegister.Bits.HostAdapterReady) break; + udelay(100); } if (TimeoutCounter < 0) { @@ -711,7 +710,7 @@ only from the list of standard BusLogic MultiMaster ISA I/O Addresses. */ -static inline void BusLogic_InitializeProbeInfoListISA(void) +static void BusLogic_InitializeProbeInfoListISA(void) { int StandardAddressIndex; /* @@ -749,9 +748,8 @@ of increasing PCI Bus and Device Number. */ -__initfunc(static void -BusLogic_SortProbeInfo(BusLogic_ProbeInfo_T *ProbeInfoList, - int ProbeInfoCount)) +static void BusLogic_SortProbeInfo(BusLogic_ProbeInfo_T *ProbeInfoList, + int ProbeInfoCount) { int LastInterchange = ProbeInfoCount-1, Bound, j; while (LastInterchange > 0) @@ -785,12 +783,12 @@ I/O Addresses. It returns the number of PCI MultiMaster Host Adapters found. */ -__initfunc(static int BusLogic_InitializeMultiMasterProbeInfo(void)) +static int BusLogic_InitializeMultiMasterProbeInfo(void) { boolean StandardAddressSeen[BusLogic_ISA_StandardAddressesCount]; BusLogic_ProbeInfo_T *PrimaryProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount]; - int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount; + int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount + 1; int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0; boolean ForceBusDeviceScanningOrder = false; boolean ForceBusDeviceScanningOrderChecked = false; @@ -1016,7 +1014,7 @@ number of FlashPoint Host Adapters found. */ -__initfunc(static int BusLogic_InitializeFlashPointProbeInfo(void)) +static int BusLogic_InitializeFlashPointProbeInfo(void) { int FlashPointIndex = BusLogic_ProbeInfoCount, FlashPointCount = 0; unsigned char Bus, DeviceFunction, IRQ_Channel; @@ -1123,7 +1121,7 @@ particular probe order. */ -static inline void BusLogic_InitializeProbeInfoList(void) +static void BusLogic_InitializeProbeInfoList(void) { /* If BusLogic_Setup has provided an I/O Address probe list, do not override @@ -1232,8 +1230,7 @@ BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter. */ -__initfunc(static boolean -BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter)) +static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter) { BusLogic_StatusRegister_T StatusRegister; BusLogic_InterruptRegister_T InterruptRegister; @@ -1245,13 +1242,16 @@ { FlashPoint_Info_T *FlashPointInfo = (FlashPoint_Info_T *) scsi_init_malloc(sizeof(FlashPoint_Info_T), GFP_ATOMIC); + int Retries = 10; if (FlashPointInfo == NULL) return BusLogic_Failure(HostAdapter, "ALLOCATING FLASHPOINT INFO"); FlashPointInfo->BaseAddress = HostAdapter->IO_Address; FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel; FlashPointInfo->Present = false; - if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 && - FlashPointInfo->Present)) + while (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 && + FlashPointInfo->Present) && + --Retries >= 0) ; + if (!FlashPointInfo->Present) { scsi_init_free((char *) FlashPointInfo, sizeof(FlashPoint_Info_T)); BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at " @@ -1326,7 +1326,7 @@ *HostAdapter) { BusLogic_StatusRegister_T StatusRegister; - int TimeoutCounter = loops_per_sec; + int TimeoutCounter; /* FlashPoint Host Adapters are Hard Reset by the FlashPoint SCCB Manager. */ @@ -1349,10 +1349,12 @@ /* Wait until Diagnostic Active is set in the Status Register. */ + TimeoutCounter = 5*10000; while (--TimeoutCounter >= 0) { StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); if (StatusRegister.Bits.DiagnosticActive) break; + udelay(100); } if (BusLogic_GlobalOptions.Bits.TraceHardReset) BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Active, " @@ -1368,10 +1370,12 @@ /* Wait until Diagnostic Active is reset in the Status Register. */ + TimeoutCounter = 10*10000; while (--TimeoutCounter >= 0) { StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); if (!StatusRegister.Bits.DiagnosticActive) break; + udelay(100); } if (BusLogic_GlobalOptions.Bits.TraceHardReset) BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Completed, " @@ -1382,6 +1386,7 @@ Wait until at least one of the Diagnostic Failure, Host Adapter Ready, or Data In Register Ready bits is set in the Status Register. */ + TimeoutCounter = 10000; while (--TimeoutCounter >= 0) { StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); @@ -1389,6 +1394,7 @@ StatusRegister.Bits.HostAdapterReady || StatusRegister.Bits.DataInRegisterReady) break; + udelay(100); } if (BusLogic_GlobalOptions.Bits.TraceHardReset) BusLogic_Notice("BusLogic_HardReset(0x%X): Host Adapter Ready, " @@ -1427,8 +1433,7 @@ Host Adapter. It also determines the IRQ Channel for non-PCI Host Adapters. */ -__initfunc(static boolean -BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter)) +static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter) { BusLogic_Configuration_T Configuration; BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation; @@ -1491,8 +1496,8 @@ from Host Adapter and initializes the Host Adapter structure. */ -__initfunc(static boolean -BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T *HostAdapter)) +static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T + *HostAdapter) { BusLogic_BoardID_T BoardID; BusLogic_Configuration_T Configuration; @@ -2005,8 +2010,8 @@ Host Adapter. */ -__initfunc(static boolean -BusLogic_ReportHostAdapterConfiguration(BusLogic_HostAdapter_T *HostAdapter)) +static boolean BusLogic_ReportHostAdapterConfiguration(BusLogic_HostAdapter_T + *HostAdapter) { unsigned short AllTargetsMask = (1 << HostAdapter->MaxTargetDevices) - 1; unsigned short SynchronousPermitted, FastPermitted; @@ -2231,8 +2236,7 @@ Host Adapter. */ -__initfunc(static boolean -BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter)) +static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter) { BusLogic_HostAdapter_T *FirstHostAdapter = BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel]; @@ -2318,8 +2322,7 @@ interrupts do not get through as a result. */ -__initfunc(static boolean -BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter)) +static boolean BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter) { unsigned int InitialInterruptCount, FinalInterruptCount; int TestCount = 5, i; @@ -2608,10 +2611,9 @@ through explicit acquisition and release of the Host Adapter's Lock. */ -__initfunc(static void -BusLogic_InitializeHostStructure(BusLogic_HostAdapter_T - *HostAdapter, - SCSI_Host_T *Host)) +static void BusLogic_InitializeHostStructure(BusLogic_HostAdapter_T + *HostAdapter, + SCSI_Host_T *Host) { Host->max_id = HostAdapter->MaxTargetDevices; Host->max_lun = HostAdapter->MaxLogicalUnits; @@ -2682,7 +2684,7 @@ registered. */ -__initfunc(int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)) +int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate) { int BusLogicHostAdapterCount = 0, CommandLineEntryIndex = 0, ProbeIndex; char *MessageBuffer = NULL; @@ -3007,8 +3009,6 @@ "Incoming Mailbox\n", HostAdapter, CCB->SerialNumber, CCB->Status); } - else BusLogic_Warning("Aborted CCB #%ld to Target %d Not Found\n", - HostAdapter, CCB->SerialNumber, CCB->TargetID); NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree; if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox) NextIncomingMailbox = HostAdapter->FirstIncomingMailbox; diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx --- v2.1.48/linux/drivers/scsi/ChangeLog.ncr53c8xx Tue May 13 22:41:12 1997 +++ linux/drivers/scsi/ChangeLog.ncr53c8xx Wed Aug 6 13:02:59 1997 @@ -1,3 +1,73 @@ +Sat July 26 18:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.4 + Several clean-ups: + - Asynchronous pre-scaler calculation. + Synchronous divisor calculation. + - Use FE_ as feature identifier prefix instead of _F_. + - Change 'ns_sync' identifier to "minsync". + - Some others. + Apply some SPI2-R12 recommendations. + - Use Slow, Fast-10, Fast-20, Fast-40 SCSI instead of SCSI-2, + FAST SCSI-2, ULTRA, ULTRA-2. + - Reset the SCSI on bus mode change. + +Wed July 02 22:58 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3c + - Add define SCSI_NCR_PCI_FIX_UP_SUPPORT for conditionnal compilation + of the corresponding pci fix-up code when a small driver is needed. + - Use "ncr53c8xx" as driver name for both request_irq() and + request_region(). Using different names confused 'lsdev'. + (Suggestion sent by Henrik Storner). + +Wed June 24 22:08 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3b + - Print an error message on unexpected boot command line option. + - Switch to asynchronous data transfer mode after SCSI wide + negotiation. + +Wed June 14 22:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3a + - Add PCI LATENCY TIMER fixup code. + Increase it if necessary according to burst size. + Boot option bit : 'pcifix:4' + - On phase mismatch, calculate residual data size for all OUTPUT + phases. That's only required for interrupted DATA OUT phase, but + this information is usefull for problem solving. + - Add KERN_INFO to some messages printed to the log. + (Patch sent by Wolfram Kleff). + +Tue June 02 22:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3 + - NvRAM support code slightly improved (I think): + Use IO or MMIO according to driver setup for reading the NvRAM. + Use structures for NvRAM data instead of raw data. + - Prevent from queuing more than 1 command to the scsi SCRIPT with + negotiation attached when tagged command queueing is enabled. + - Fix-up for old 53C8XX chips that support PCI READ LINE but not + CACHE LINE SIZE. If the cache line size is unknown, set burst + to 8 dwords and disable READ LINE, otherwise set burst max to + the cache line size value. + +Sat May 24 12:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.2c (for linux-2.1.40) + - Remove reference to 'x86' symbol when MODULE is defined, since this + symbol is not exported for module loading. + The value of 'x86' is used for fixing up the PCI CACHE LINE SIZE + configuration register. + - Bytes/words read one bit at a time from the serial NVRAM were'nt + initialized with zero. + - Some comments added. Minor cosmetic changes. + +Mon May 19 20:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.2b + - Patch for NVRAM support by Richard Waltham applied. + The code detects Symbios NVRAM format and Tekram NVRAM format. + This enhancement allows to get hosts and devices user set up + from the NVRAM. + - Use the NVRAM contents when present to initialize user definable + target parameters. + - Update the README file. + Sun May 11 22:30 1997 Gerard Roudier (groudier@club-internet.fr) * revision 2.1b - Cosmetic changes. diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.1.48/linux/drivers/scsi/Config.in Mon Aug 4 16:25:38 1997 +++ linux/drivers/scsi/Config.in Wed Aug 6 13:02:59 1997 @@ -65,6 +65,7 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI if [ "$CONFIG_SCSI_NCR53C8XX" != "n" ]; then + bool ' detect and read serial NVRAMs' CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT bool ' enable tagged command queueing' CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE bool ' use normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED int ' maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 4 diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/FlashPoint.c linux/drivers/scsi/FlashPoint.c --- v2.1.48/linux/drivers/scsi/FlashPoint.c Tue Mar 4 10:25:24 1997 +++ linux/drivers/scsi/FlashPoint.c Mon Aug 11 00:10:00 1997 @@ -31,6 +31,18 @@ #endif +/* + FlashPoint support is only available for the Intel x86 Architecture. +*/ + +#ifndef __i386__ + +#undef CONFIG_SCSI_OMIT_FLASHPOINT +#define CONFIG_SCSI_OMIT_FLASHPOINT + +#endif + + #ifndef CONFIG_SCSI_OMIT_FLASHPOINT diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v2.1.48/linux/drivers/scsi/Makefile Mon Aug 4 16:25:38 1997 +++ linux/drivers/scsi/Makefile Wed Aug 6 13:02:59 1997 @@ -427,10 +427,6 @@ seagate.o: seagate.c $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -DPARITY -c seagate.c -# For debugging, use the -g flag -53c7,8xx.o : 53c7,8xx.c - $(CC) $(CFLAGS) -g -c 53c7,8xx.c - 53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl ln -sf 53c7,8xx.scr fake.c $(CPP) -traditional -DCHIP=810 fake.c | grep -v '^#' | perl script_asm.pl @@ -438,18 +434,12 @@ mv scriptu.h 53c8xx_u.h rm fake.c -53c7xx.o : 53c7xx_d.h 53c7xx.c - $(CC) $(CFLAGS) -c 53c7xx.c - 53c7xx_d.h 53c7xx_u.h : 53c7xx.scr script_asm.pl ln -sf 53c7xx.scr fake.c $(CPP) -traditional -DCHIP=710 fake.c | grep -v '^#' | perl -s script_asm.pl -ncr7x0_family mv script.h 53c7xx_d.h mv scriptu.h 53c7xx_u.h rm fake.c - -ncr53c8xx.o : ncr53c8xx.c - $(CC) $(CFLAGS) -c ncr53c8xx.c scsi_mod.o: $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \ scsicam.o scsi_proc.o diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic --- v2.1.48/linux/drivers/scsi/README.BusLogic Fri Apr 4 08:52:23 1997 +++ linux/drivers/scsi/README.BusLogic Mon Aug 11 00:10:00 1997 @@ -1,10 +1,10 @@ BusLogic MultiMaster and FlashPoint SCSI Driver for Linux - Version 2.0.9 for Linux 2.0 + Version 2.0.10 for Linux 2.0 PRODUCTION RELEASE - 29 March 1997 + 11 August 1997 Leonard N. Zubkoff Dandelion Digital @@ -284,6 +284,14 @@ so as to recognize the host adapters in the same order as they are enumerated by the host adapter's BIOS. +o Mega-Transfers/Second + + The driver reports on the synchronous transfer parameters negotiated between + the host adapter and target devices in units of "mega-transfers/second". For + wide devices, the unit of transfer is 16 bits if wide negotiation has been + successfully completed. Therefore, the total transfer rate to wide devices + will generally be twice the synchronous tranfer rate reported by the driver. + COMMAND LINE OPTIONS @@ -372,16 +380,17 @@ INSTALLATION -This distribution was prepared for Linux kernel version 2.0.29, but should be -compatible with 2.0.4 or any later 2.0 series kernel. +This distribution was prepared for Linux kernel version 2.0.30, but should be +compatible with 2.0.4 or any later 2.0 series kernel if BusLogic.patch is also +applied. To install the new BusLogic SCSI driver, you may use the following commands, replacing "/usr/src" with wherever you keep your Linux kernel source tree: cd /usr/src - tar -xvzf BusLogic-2.0.9.tar.gz + tar -xvzf BusLogic-2.0.10.tar.gz mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi - patch -p < BusLogic.patch + patch -p < BusLogic.patch # Only for kernels prior to 2.0.30 cd linux make config make depend diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx --- v2.1.48/linux/drivers/scsi/README.ncr53c8xx Tue May 13 22:41:12 1997 +++ linux/drivers/scsi/README.ncr53c8xx Wed Aug 6 13:02:59 1997 @@ -4,7 +4,7 @@ 21 Rue Carnot 95170 DEUIL LA BARRE - FRANCE -9 May 1997 +19 June 1997 =============================================================================== 1. Introduction @@ -28,6 +28,8 @@ 10.1 Syntax 10.2 Available arguments 10.3 Advised boot setup commands + 10.4 PCI configuration fix-up boot option + 10.5 Serial NVRAM support boot option 11. Some constants and flags of the ncr53c8xx.h header file 12. Installation 12.1 Provided files @@ -40,6 +42,10 @@ 16. Synchonous transfer negotiation tables 16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers 16.2 Synchronous timings for fast SCSI-2 53C8XX controllers +17. Serial NVRAM support (by Richard Waltham) + 17.1 Features + 17.2 Symbios NVRAM layout + 17.3 Tekram NVRAM layout =============================================================================== @@ -124,6 +130,7 @@ Scatter / gather Shared interrupt Boot setup commands + Serial NVRAM: Symbios and Tekram formats 4. Memory mapped I/O versus normal I/O @@ -412,35 +419,6 @@ If you select an error type, it will be triggered by the driver every 30 seconds. -8.9 PCI configuration fix-up - - pcifix