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
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
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
KISS is the protocol used to send IP traffic over AX.25 radio
Ioctl Numbers
-25 Jul 1997
+7 Aug 1997
Michael Chastain
<mec@shout.net>
<mailto:b.kohl@ipn-b.comlink.apc.org>
0xA0 all Small Device Project in development:
<mailto:khollis@northwest.com>
+0xA3 80-8F Port ACL in development:
+ <mailto:tlewis@mindspring.com>
0xA3 90-9F DoubleTalk driver in development:
<mailto:jrv@vanzandt.mv.com>
--- /dev/null
+ LINUX DRIVER FOR BAYCOM MODEMS
+
+ Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+
+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
--- /dev/null
+ LINUX DRIVER FOR SOUNDCARDS AS AX.25 MODEMS
+
+ Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+
+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
VERSION = 2
PATCHLEVEL = 1
-SUBLEVEL = 48
+SUBLEVEL = 49
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
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
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
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
--- /dev/null
+#
+# 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
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * 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 <linux/hfmodem.h>
+#include <math.h>
+#include <stdio.h>
+
+/* --------------------------------------------------------------------- */
+
+#define SINTABBITS 9
+#define SINTABSIZE (1<<SINTABBITS)
+
+/* --------------------------------------------------------------------- */
+
+static void gensintbl(void)
+{
+ int i;
+
+ printf("#define SINTABBITS %d\n#define SINTABSIZE (1<<SINTABBITS)\n"
+ "\nstatic short isintab[SINTABSIZE+SINTABSIZE/4] = {\n\t", SINTABBITS);
+ for (i = 0; i < (SINTABSIZE+SINTABSIZE/4); i++) {
+ printf("%6d", (int)(32767.0 * sin(2.0 * M_PI / SINTABSIZE * i)));
+ if (i < (SINTABSIZE+SINTABSIZE/4)-1) {
+ if ((i & 7) == 7)
+ printf(",\n\t");
+ else
+ printf(",");
+ }
+ }
+ printf("\n};\n\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+int main(int argc, char *argv[])
+{
+ printf("/*\n * This file is automatically generated by %s, DO NOT EDIT!\n*/\n\n",
+ argv[0]);
+ gensintbl();
+ exit(0);
+}
+
+/* --------------------------------------------------------------------- */
+
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * main.c -- Linux soundcard HF FSK driver.
+ *
+ * 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.
+ *
+ * Command line options (insmod command line)
+ *
+ * History:
+ * 0.1 15.04.97 Adapted from baycom.c and made network driver interface
+ * 0.2 05.07.97 All floating point stuff thrown out due to Linus' rantings :)
+ *
+ */
+
+/*****************************************************************************/
+
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/hfmodem.h>
+
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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 <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#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 <linux/init.h>
+#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 */
+
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * 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 <linux/wait.h>
+#include <linux/malloc.h>
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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 <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#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 <linux/init.h>
+#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);
+}
+
+/* --------------------------------------------------------------------- */
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * 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 <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * 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 <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#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);
+}
+
+/* --------------------------------------------------------------------- */
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * 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 <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/hfmodem.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+/* --------------------------------------------------------------------- */
+/*
+ * 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
+};
+
+/* --------------------------------------------------------------------- */
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * 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 <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+#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
+};
+
+/* --------------------------------------------------------------------- */
#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",
* 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;
*/
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;
/* Restore the interrupts */
outb(ier, ser->port + UART_IER);
-
- return c;
}
static int serial_console_device(void)
case KIOCSOUND:
if (!perm)
return -EPERM;
- if (arg)
- arg = 1193180 / arg;
kd_mksound(arg, 0);
return 0;
* 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;
}
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)
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);
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);
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) {
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);
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)
{
return dma;
}
+#if 0
/* Only called if port supports ECP mode.
*
* The only restriction on DMA channels is that it has to be
return retv;
}
+#endif
+
/******************************************************
* MODE detection section:
*/
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;
}
dev->interrupt = 1;
+ sti();
/* Change to page 0 and read the intr status reg. */
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
# 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
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)
+++ /dev/null
- LINUX DRIVER FOR BAYCOM MODEMS
-
- Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
-
-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
+++ /dev/null
- LINUX DRIVER FOR SOUNDCARDS AS AX.25 MODEMS
-
- Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
-
-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
* 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)
*/
/*****************************************************************************/
* 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";
/* --------------------------------------------------------------------- */
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;
* ===================== 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)
{
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)
{
"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
*/
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;
}
* 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)
*/
/*****************************************************************************/
__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
/* 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;
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);
\f
enum plip_connection_state {
PLIP_CN_NONE=0,
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
"#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) ? ',' : ' ');
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
}
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"
#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) ? ',' : ' ');
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
"DO NOT EDIT!\n */\n\n", progname);
}
-static void gentbl_needs_config(FILE *f)
-{
- fprintf(f, "\n#include <linux/config.h>\n\n");
-}
-
/* -------------------------------------------------------------------- */
void main(int argc, char *argv[])
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);
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);
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);
* 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
*/
/*****************************************************************************/
/*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";
/* --------------------------------------------------------------------- */
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);
*/
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)))
#define WSS_EXTENT 8
+#define CS423X_HOTFIX
+
/* --------------------------------------------------------------------- */
static void write_codec(struct device *dev, unsigned char idx,
/* 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
*/
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);
}
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();
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);
} 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
*/
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)))
#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 ===========================
* Returns : NULL on failure, pointer to host structure on success.
*/
+#if 0
static struct Scsi_Host *
find_host (int host) {
struct Scsi_Host *h;
hostdata->options &= ~OPTION_DISCONNECT;
return 0;
}
+#endif
/*
* Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host)
* Returns : char * representation of state, "unknown" on error.
*/
+#if 0
static const char *
ncr_state (int state) {
switch (state) {
default: return "unknown";
}
}
+#endif
/*
* Function : int NCR53c7xx_abort (Scsi_Cmnd *cmd)
+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.
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
#define SCSI_PARITY_ENA 0x0001
#define LOW_BYTE_TERM 0x0010
#define HIGH_BYTE_TERM 0x0020
- #define BUSTYPE_PCI 0x3
+ #define _BUSTYPE_PCI 0x3
#endif
#define SUPPORT_16TAR_32LUN 0x0002
pCardInfo->si_flags |= SUPPORT_16TAR_32LUN;
pCardInfo->si_card_family = HARPOON_FAMILY;
- pCardInfo->si_bustype = BUSTYPE_PCI;
+ pCardInfo->si_bustype = _BUSTYPE_PCI;
if(pCurrNvRam){
pCardInfo->si_card_model[0] = '9';
}
pAdapterInfo->ai_product_family = HARPOON_FAMILY;
- pAdapterInfo->ai_HBAbustype = BUSTYPE_PCI;
+ pAdapterInfo->ai_HBAbustype = _BUSTYPE_PCI;
for (i=0;i<CARD_MODEL_NAMELEN;i++)
{
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
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 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
$(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o
21 Rue Carnot
95170 DEUIL LA BARRE - FRANCE
-9 May 1997
+19 June 1997
===============================================================================
1. Introduction
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
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
===============================================================================
Scatter / gather
Shared interrupt
Boot setup commands
+ Serial NVRAM: Symbios and Tekram formats
4. Memory mapped I/O versus normal I/O
If you select an error type, it will be triggered by the driver every
30 seconds.
-8.9 PCI configuration fix-up
-
- pcifix <option bits>
-
- Available option bits:
- 0x1: Set PCI cache-line size register if not set.
- 0x2: Set write and invalidate bit in PCI command register.
-
- Use 'pcifix:3' in order to allow the driver to fix both PCI features.
-
- These options only apply to new SYMBIOS chips 810A, 825A, 860 and 875
- and are only supported for Pentium and 486 class processors.
- Recent SYMBIOS 53C8XX scsi processors are able to use PCI read multiple
- and PCI write and invalidate commands. These features require the
- cache line size register to be properly set in the PCI configuration
- space of the chips. On the other hand, chips will use PCI write and
- invalidate commands only if the corresponding bit is set to 1 in the
- PCI command register.
-
- Not all PCI bioses set the PCI cache line register and the PCI write and
- invalidate bit in the PCI configuration space of 53C8XX chips.
- Optimized PCI accesses may be broken for some PCI/memory controllers or
- make problems with some PCI boards.
-
- This fix-up works flawlessly on my system.
- (MB Triton HX / 53C875 / 53C810A)
- I use these options at my own risks as you will do if you decide to
- use them too.
-
9. Configuration parameters
If the firmware of all your devices is perfect enough, all the
GPIO wiring. So, this option must not be enabled if your system has
such a board installed.
+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).
10. Boot setup commands
810, 815, 820, 860, 875, 885, 895, 896
revprob:y probe chip ids in the reverse order.
+Fix up PCI configuration space
+ pcifix:<option bits>
+
+ Available option bits:
+ 0x1: Set PCI cache-line size register if not set.
+ 0x2: Set write and invalidate bit in PCI command register.
+ 0x4: Increase if necessary PCI latency timer according to burst max.
+
+ Use 'pcifix:7' in order to allow the driver to fix up all PCI features.
+
+Serial NVRAM
+ nvram:n do not look for serial NVRAM
+ nvram:y test controllers for onboard serial NVRAM
+
Boot fail safe
safe:y load the following assumed fail safe initial setup
ultra scsi disabled ultra:n
force sync negotiation disabled fsn:n
reverse probe disabled revprob:n
+ PCI fix up disabled pcifix:0
+ serial NVRAM enabled nvram:y
verbosity level 2 verb:2
tagged command queuing disabled tags:0
synchronous negotiation disabled sync:255
to your boot setup command in order to check the actual setup the driver is
using.
+10.4 PCI configuration fix-up boot option
+
+pcifix:<option bits>
+
+Available option bits:
+ 0x1: Set PCI cache-line size register if not set.
+ 0x2: Set write and invalidate bit in PCI command register.
+
+Use 'pcifix:3' in order to allow the driver to fix both PCI features.
+
+These options only apply to new SYMBIOS chips 810A, 825A, 860 and 875
+and are only supported for Pentium and 486 class processors.
+Recent SYMBIOS 53C8XX scsi processors are able to use PCI read multiple
+and PCI write and invalidate commands. These features require the
+cache line size register to be properly set in the PCI configuration
+space of the chips. On the other hand, chips will use PCI write and
+invalidate commands only if the corresponding bit is set to 1 in the
+PCI command register.
+
+Not all PCI bioses set the PCI cache line register and the PCI write and
+invalidate bit in the PCI configuration space of 53C8XX chips.
+Optimized PCI accesses may be broken for some PCI/memory controllers or
+make problems with some PCI boards.
+
+This fix-up works flawlessly on my system.
+(MB Triton HX / 53C875 / 53C810A)
+I use these options at my own risks as you will do if you decide to
+use them too.
+
+
+10.5 Serial NVRAM support boot option
+
+nvram:n do not look for serial NVRAM
+nvram:y test controllers for onboard serial NVRAM
+
+This option is described below (see 17. Serial NVRAM support).
+When this option is enabled, the driver tries to detect all boards using
+a Serial NVRAM. This memory is used to hold user set up parameters.
+
+The parameters the driver is able to get from the NVRAM depend on the
+data format used, as follow:
+
+ Tekram format Symbios format
+General and host parameters
+ Boot order N Y
+ Host SCSI ID Y Y
+ SCSI parity checking Y Y
+ Verbose boot messages N Y
+SCSI devices parameters
+ Synchronous transfer speed Y Y
+ Wide 16 / Narrow Y Y
+ Tagged Command Queuing enabled Y Y
+ Disconnections enabled Y Y
+ Scan at boot time N Y
+
+In order to speed up the system boot, for each device configured without
+the "scan at boot time" option, the driver forces an error on the
+first TEST UNIT READY command received for this device.
+
+Some SDMS BIOS revisions seem to be unable to boot cleanly with very fast
+hard disks. In such a situation you cannot configure the NVRAM with
+optimized parameters value.
+
+The 'nvram' boot option can be entered in hexadecimal form in order
+to ignore some options configured in the NVRAM, as follow:
+
+mvram=<bits options>
+ 0x01 look for NVRAM (equivalent to nvram=y)
+ 0x02 ignore NVRAM "Synchronous negotiation" parameters for all devices
+ 0x04 ignore NVRAM "Wide negotiation" parameter for all devices
+ 0x08 ignore NVRAM "Scan at boot time" parameter for all devices
+
+My Atlas Wide only boots cleanly in 8 bits asynchronous data transfer
+mode. However, it works flawlessly at 20 MB/second with the driver.
+Using "nvram=0x7" allows me to boot in 8 bits/async and to let the driver
+use its setup for synchronous and wide negotiations.
+
11. Some constants and flags of the ncr53c8xx.h header file
SCSI_NCR_MAX_LUN (default: 8)
Max number of LUNs per target.
+
12. Installation
12.1 Provided files
You must untar the distribution with the following command:
- tar zxvf ncrBsd2Linux-2.1b-src.tar.gz
+ tar zxvf ncrBsd2Linux-2.2b-src.tar.gz
-The sub-directory ncr53c8xx-2.1b will be created. Change to this directory.
+The sub-directory ncr53c8xx-2.2b will be created. Change to this directory.
12.2 Installation procedure
has one or more controllers of this family, it may happen that the order
the driver registers them to the kernel causes problems due to device
name changes.
-SDMS BIOS version 4 allows you to define the order the BIOS will scan the
-scsi boards and stores this information for next reboots. Unfortunately, the
-driver is not currently able to read this information and so may register
-controllers in a different order.
+When at least one controller uses NvRAM, SDMS BIOS version 4 allows you to
+define the order the BIOS will scan the scsi boards. The driver attaches
+controllers according to BIOS information if NvRAM detect option is set.
-If you have such a problem, you can:
+If your controllers do not have NvRAM, you can:
- Ask the driver to probe chip ids in reverse order from the boot command
line: ncr53c8xx=revprob:y
47 188 5.319 200 5.000
48 192 5.208 200 5.000
49 196 5.102 200 5.000
+
+
+17. Serial NVRAM (added by Richard Waltham: dormouse@farsrobt.demon.co.uk)
+
+17.1 Features
+
+Enabling serial NVRAM support enables detection of the serial NVRAM included
+on Symbios and some Symbios compatible host adaptors, and Tekram boards. The
+serial NVRAM is used by Symbios and Tekram to hold set up parameters for the
+host adaptor and it's attached drives.
+
+The Symbios NVRAM also holds data on the boot order of host adaptors in a
+system with more than one host adaptor. This enables the order of scanning
+the cards for drives to be changed from the default used during host adaptor
+detection.
+
+This can be done to a limited extent at the moment using "reverse probe" but
+this only changes the order of detection of different types of cards. The
+NVRAM boot order settings can do this as well as change the order the same
+types of cards are scanned in, something "reverse probe" cannot do.
+
+Tekram boards using Symbios chips, DC390W/F/U, which have NVRAM are detected
+and this is used to distinguish between Symbios compatible and Tekram host
+adaptors. This is used to disable the Symbios compatible "diff" setting
+incorrectly set on Tekram boards if the CONFIG_SCSI_53C8XX_SYMBIOS_COMPAT
+configuration parameter is set enabling both Symbios and Tekram boards to be
+used together with the Symbios cards using all their features, including
+"diff" support. ("led pin" support for Symbios compatible cards can remain
+enabled when using Tekram cards. It does nothing useful for Tekram host
+adaptors but does not cause problems either.)
+
+
+17.2 Symbios NVRAM layout
+
+typical data at NVRAM address 0x100 (53c810a NVRAM)
+-----------------------------------------------------------
+00 00
+64 01
+8e 0b
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62
+04 00 03 00 00 10 00 58 00 00 01 00 00 63
+04 00 01 00 00 10 00 48 00 00 01 00 00 61
+00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+fe fe
+00 00
+00 00
+-----------------------------------------------------------
+NVRAM layout details
+
+NVRAM Address 0x000-0x0ff not used
+ 0x100-0x26f initialised data
+ 0x270-0x7ff not used
+
+general layout
+
+ header - 6 bytes,
+ data - 356 bytes (checksum is byte sum of this data)
+ trailer - 6 bytes
+ ---
+ total 368 bytes
+
+data area layout
+
+ controller set up - 20 bytes
+ boot configuration - 56 bytes (4x14 bytes)
+ device set up - 128 bytes (16x8 bytes)
+ unused (spare?) - 152 bytes (19x8 bytes)
+ ---
+ total 356 bytes
+
+-----------------------------------------------------------
+header
+
+00 00 - ?? start marker
+64 01 - byte count (lsb/msb excludes header/trailer)
+8e 0b - checksum (lsb/msb excludes header/trailer)
+-----------------------------------------------------------
+controller set up
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
+ | | |
+ | | -- host ID
+ | --flag bits 2
+ | 0x00000001= scan order hi->low
+ | (default 0x00 - scan low->hi)
+ --flag bits 1
+ 0x00000001 scam enable
+ 0x00000010 parity enable
+ 0x00000100 verbose boot msgs
+
+remaining bytes unknown - they do not appear to change in my
+current set up for any of the controllers.
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+boot configuration
+
+boot order set by order of the devices in this table
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62 -- 1st controller
+04 00 03 00 00 10 00 58 00 00 01 00 00 63 2nd controller
+04 00 01 00 00 10 00 48 00 00 01 00 00 61 3rd controller
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 4th controller
+ | | | | | | | |
+ | | | | | | ---- PCI io port adr
+ | | | | | --0x01 init/scan at boot time
+ | | | | --PCI device/function number (0xdddddfff)
+ | | ----- ?? PCI vendor ID (lsb/msb)
+ ----PCI device ID (lsb/msb)
+
+?? use of this data is a guess but seems reasonable
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+device set up (up to 16 devices - includes controller)
+
+0f 00 08 08 64 00 0a 00 - id 0
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00 - id 15
+ | | | | | |
+ | | | | ----timeout (lsb/msb)
+ | | | --synch period (0x?? 40 Mtrans/sec- fast 40) (probably 0x28)
+ | | | (0x30 20 Mtrans/sec- fast 20)
+ | | | (0x64 10 Mtrans/sec- fast )
+ | | | (0xc8 5 Mtrans/sec)
+ | | | (0x00 asynchronous)
+ | | -- ?? max sync offset (0x08 in NVRAM on 53c810a)
+ | | (0x10 in NVRAM on 53c875)
+ | --device bus width (0x08 narrow)
+ | (0x10 16 bit wide)
+ --flag bits
+ 0x00000001 - disconnect enabled
+ 0x00000010 - scan at boot time
+ 0x00000100 - scan luns
+ 0x00001000 - queue tags enabled
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+?? use of this data is a guess but seems reasonable
+(but it could be max bus width)
+
+default set up for 53c810a NVRAM
+default set up for 53c875 NVRAM - bus width - 0x10
+ - sync offset ? - 0x10
+ - sync period - 0x30
+-----------------------------------------------------------
+?? spare device space (32 bit bus ??)
+
+00 00 00 00 00 00 00 00 (19x8bytes)
+.
+.
+00 00 00 00 00 00 00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+trailer
+
+fe fe - ? end marker ?
+00 00
+00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+
+
+
+17.3 Tekram NVRAM layout
+
+nvram 64x16 (1024 bit)
+
+Drive settings
+
+Drive ID 0-15 (addr 0x0yyyy0 = device setup, yyyy = ID)
+ (addr 0x0yyyy1 = 0x0000)
+
+ x x x x x x x x x x x x x x x x
+ | | | | | | | | |
+ | | | | | | | | ----- parity check 0 - off
+ | | | | | | | | 1 - on
+ | | | | | | | |
+ | | | | | | | ------- sync neg 0 - off
+ | | | | | | | 1 - on
+ | | | | | | |
+ | | | | | | --------- disconnect 0 - off
+ | | | | | | 1 - on
+ | | | | | |
+ | | | | | ----------- start cmd 0 - off
+ | | | | | 1 - on
+ | | | | |
+ | | | | -------------- tagged cmds 0 - off
+ | | | | 1 - on
+ | | | |
+ | | | ---------------- wide neg 0 - off
+ | | | 1 - on
+ | | |
+ --------------------------- sync rate 0 - 10.0 Mtrans/sec
+ 1 - 8.0
+ 2 - 6.6
+ 3 - 5.7
+ 4 - 5.0
+ 5 - 4.0
+ 6 - 3.0
+ 7 - 2.0
+ 7 - 2.0
+ 8 - 20.0
+ 9 - 16.7
+ a - 13.9
+ b - 11.9
+
+Global settings
+
+Host flags 0 (addr 0x100000, 32)
+
+ x x x x x x x x x x x x x x x x
+ | | | | | | | | | | | |
+ | | | | | | | | ----------- host ID 0x00 - 0x0f
+ | | | | | | | |
+ | | | | | | | ----------------------- support for 0 - off
+ | | | | | | | > 2 drives 1 - on
+ | | | | | | |
+ | | | | | | ------------------------- support drives 0 - off
+ | | | | | | > 1Gbytes 1 - on
+ | | | | | |
+ | | | | | --------------------------- bus reset on 0 - off
+ | | | | | power on 1 - on
+ | | | | |
+ | | | | ----------------------------- active neg 0 - off
+ | | | | 1 - on
+ | | | |
+ | | | -------------------------------- imm seek 0 - off
+ | | | 1 - on
+ | | |
+ | | ---------------------------------- scan luns 0 - off
+ | | 1 - on
+ | |
+ -------------------------------------- removable 0 - disable
+ as BIOS dev 1 - boot device
+ 2 - all
+
+Host flags 1 (addr 0x100001, 33)
+
+ x x x x x x x x x x x x x x x x
+ | | | | | |
+ | | | --------- boot delay 0 - 3 sec
+ | | | 1 - 5
+ | | | 2 - 10
+ | | | 3 - 20
+ | | | 4 - 30
+ | | | 5 - 60
+ | | | 6 - 120
+ | | |
+ --------------------------- max tag cmds 0 - 2
+ 1 - 4
+ 2 - 8
+ 3 - 16
+ 4 - 32
+
+Host flags 2 (addr 0x100010, 34)
+
+ x x x x x x x x x x x x x x x x
+ |
+ ----- F2/F6 enable 0 - off ???
+ 1 - on ???
+
+checksum (addr 0x111111)
+
+checksum = 0x1234 - (sum addr 0-63)
+
+----------------------------------------------------------------------------
+
+default nvram data:
+
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+
+0x0f07 0x0400 0x0001 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0xfbbc
+
===============================================================================
End of NCR53C8XX driver README file
{
old_verbose = aic7xxx_verbose;
printk(KERN_INFO "aic7xxx: Warning - detected auto-termination. Please "
- "verify driver");
+ "verify driver\n");
printk(KERN_INFO " detected settings and use manual termination "
- "if necessary.");
+ "if necessary.\n");
/* Configure auto termination. */
outb(SEECS | SEEMS, p->base + SEECTL);
{
struct Scsi_Host *HBAptr;
struct aic7xxx_host *p;
- static u8 buff[512];
int i;
int found = FALSE;
int size = 0;
return (aic7xxx_set_info(buffer, length, HBAptr));
}
- if (offset == 0)
- {
- memset(buff, 0, sizeof(buff));
- }
-
p = (struct aic7xxx_host *) HBAptr->hostdata;
size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
#if 0
size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
#endif
+ if (size > 512)
+ printk(KERN_CRIT "aic7xxx: possible overflow at first position\n");
len += size; pos = begin + len; size = 0;
+ if (pos < offset)
+ {
+ begin = pos;
+ len = 0;
+ }
size += sprintf(BLS, "\n");
size += sprintf(BLS, "Compile Options:\n");
#else
size += sprintf(BLS, " AIC7XXX_PROC_STATS : Disabled\n");
#endif
+ if (size > 512)
+ printk(KERN_CRIT "aic7xxx: possible overflow at second position\n");
len += size; pos = begin + len; size = 0;
+ if (pos < offset)
+ {
+ begin = pos;
+ len = 0;
+ }
+ else if (pos >= offset + length)
+ goto stop_output;
size += sprintf(BLS, "\n");
size += sprintf(BLS, "Adapter Configuration:\n");
(p->flags & ULTRA_ENABLED) ? "En" : "Dis");
size += sprintf(BLS, " Target Disconnect: %sabled\n",
p->discenable ? "En" : "Dis");
+ if (size > 512)
+ printk(KERN_CRIT "aic7xxx: possible overflow at third position\n");
len += size; pos = begin + len; size = 0;
+ if (pos < offset)
+ {
+ begin = pos;
+ len = 0;
+ }
+ else if (pos >= offset + length)
+ goto stop_output;
#ifdef AIC7XXX_PROC_STATS
{
/*
* XXX: Need to fix this to avoid overflow...
+ * Fixed - gordo.
*/
size += sprintf(BLS, "\n");
size += sprintf(BLS, "Statistics:\n");
sp->w_bins[9]);
size += sprintf(BLS, "\n");
}
+ if (size > 512)
+ printk(KERN_CRIT "aic7xxx: possible overflow at loop %d:%d\n", target, lun);
+ len += size; pos = begin + len; size = 0;
+ if (pos < offset)
+ {
+ begin = pos;
+ len = 0;
+ }
+ else if (pos >= offset + length)
+ goto stop_output;
}
}
- len += size; pos = begin + len; size = 0;
}
#endif /* AIC7XXX_PROC_STATS */
proc_debug("2pos: %ld offset: %ld len: %d\n", pos, offset, len);
*start = buffer + (offset - begin); /* Start of wanted data */
len -= (offset - begin); /* Start slop */
- if (len > length)
+ if (len < 0)
+ {
+ len = 0; /* off end of file */
+ }
+ else if (len > length)
{
len = length; /* Ending slop */
}
** And has been ported to NetBSD by
** Charles M. Hannum <mycroft@gnu.ai.mit.edu>
**
+**-----------------------------------------------------------------------------
+**
+** Brief history
+**
+** December 10 1995 by Gerard Roudier:
+** Initial port to Linux.
+**
+** June 23 1996 by Gerard Roudier:
+** Support for 64 bits architectures (Alpha).
+**
+** November 30 1996 by Gerard Roudier:
+** Support for Fast-20 scsi.
+** Support for large DMA fifo and 128 dwords bursting.
+**
+** February 27 1997 by Gerard Roudier:
+** Support for Fast-40 scsi.
+** Support for on-Board RAM.
+**
+** May 3 1997 by Gerard Roudier:
+** Full support for scsi scripts instructions pre-fetching.
+**
+** May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>:
+** Support for NvRAM detection and reading.
+**
*******************************************************************************
*/
/*
-** 9 May 1997, version 2.1b
+** 26 July 1997, version 2.4
**
** Supported SCSI-II features:
** Synchronous negotiation
** Etc...
**
** Supported NCR chips:
-** 53C810 (NCR BIOS in flash-bios required)
-** 53C815 (~53C810 with on board rom BIOS)
-** 53C820 (Wide, NCR BIOS in flash bios required)
-** 53C825 (Wide, ~53C820 with on board rom BIOS)
-** 53C860 (Narrow fast 20, BIOS required)
-** 53C875 (Wide fast 20 with on board rom BIOS)
-** 53C895 (Ultra2 80 MB/s with on board rom BIOS)
+** 53C810 (8 bits, Fast SCSI-2, no rom BIOS)
+** 53C815 (8 bits, Fast SCSI-2, on board rom BIOS)
+** 53C820 (Wide, Fast SCSI-2, no rom BIOS)
+** 53C825 (Wide, Fast SCSI-2, on board rom BIOS)
+** 53C860 (8 bits, Fast 20, no rom BIOS)
+** 53C875 (Wide, Fast 20, on board rom BIOS)
+** 53C895 (Wide, Fast 40, on board rom BIOS)
**
** Other features:
** Memory mapped IO (linux-1.3.X and above only)
#define SCSI_NCR_DEBUG_FLAGS (0)
-#define NCR_DATE "pl24 96/12/14"
-
-#define NCR_VERSION (2)
-
#define NCR_GETCC_WITHMSG
/*==========================================================
#else
#define MAX_LUN (1)
#endif
+
+/*
+** Asynchronous pre-scaler (ns). Shall be 40
+*/
+#ifndef SCSI_NCR_MIN_ASYNC
+#define SCSI_NCR_MIN_ASYNC (40)
+#endif
/*
** The maximum number of jobs scheduled for starting.
** architecture.
*/
-static inline vm_offset_t remap_pci_mem(u_long base, u_long size)
+__initfunc(
+static vm_offset_t remap_pci_mem(u_long base, u_long size)
+)
{
u_long page_base = ((u_long) base) & PAGE_MASK;
u_long page_offs = ((u_long) base) - page_base;
return (vm_offset_t) (page_remapped ? (page_remapped + page_offs) : 0UL);
}
-static inline void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+__initfunc(
+static void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+)
{
if (vaddr)
#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
unsigned ultra_scsi : 2;
unsigned force_sync_nego: 1;
unsigned reverse_probe: 1;
- unsigned pci_fix_up: 2;
+ unsigned pci_fix_up: 4;
+ u_char use_nvram;
u_char verbose;
u_char default_tags;
u_short default_sync;
static void ncr53c8xx_timeout(unsigned long np);
-#define bootverbose (driver_setup.verbose)
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+** Symbios NvRAM data format
+*/
+#define SYMBIOS_NVRAM_SIZE 368
+#define SYMBIOS_NVRAM_ADDRESS 0x100
+
+struct Symbios_nvram {
+/* Header 6 bytes */
+ u_short start_marker; /* 0x0000 */
+ u_short byte_count; /* excluding header/trailer */
+ u_short checksum;
+
+/* Controller set up 20 bytes */
+ u_short word0; /* 0x3000 */
+ u_short word2; /* 0x0000 */
+ u_short word4; /* 0x0000 */
+ u_short flags;
+#define SYMBIOS_SCAM_ENABLE (1)
+#define SYMBIOS_PARITY_ENABLE (1<<1)
+#define SYMBIOS_VERBOSE_MSGS (1<<2)
+ u_short flags1;
+#define SYMBIOS_SCAN_HI_LO (1)
+ u_short word10; /* 0x00 */
+ u_short word12; /* 0x00 */
+ u_char host_id;
+ u_char byte15; /* 0x04 */
+ u_short word16; /* 0x0410 */
+ u_short word18; /* 0x0000 */
+
+/* Boot order 14 bytes * 4 */
+ struct Symbios_host{
+ u_char word0; /* 0x0004:ok / 0x0000:nok */
+ u_short device_id; /* PCI device id */
+ u_short vendor_id; /* PCI vendor id */
+ u_char byte6; /* 0x00 */
+ u_char device_fn; /* PCI device/function number << 3*/
+ u_short word8;
+ u_short flags;
+#define SYMBIOS_INIT_SCAN_AT_BOOT (1)
+ u_short io_port; /* PCI io_port address */
+ } host[4];
+
+/* Targets 8 bytes * 16 */
+ struct Symbios_target {
+ u_short flags;
+#define SYMBIOS_DISCONNECT_ENABLE (1)
+#define SYMBIOS_SCAN_AT_BOOT_TIME (1<<1)
+#define SYMBIOS_SCAN_LUNS (1<<2)
+#define SYMBIOS_QUEUE_TAGS_ENABLED (1<<3)
+ u_char bus_width; /* 0x08/0x10 */
+ u_char sync_offset;
+ u_char sync_period; /* 4*period factor */
+ u_char byte6; /* 0x00 */
+ u_short timeout;
+ } target[16];
+ u_char spare_devices[19*8];
+ u_char trailer[6]; /* 0xfe 0xfe 0x00 0x00 0x00 0x00 */
+};
+typedef struct Symbios_nvram Symbios_nvram;
+typedef struct Symbios_host Symbios_host;
+typedef struct Symbios_target Symbios_target;
+
+/*
+** Tekram NvRAM data format.
+*/
+#define TEKRAM_NVRAM_SIZE 64
+#define TEKRAM_NVRAM_ADDRESS 0
+
+struct Tekram_nvram {
+ struct Tekram_target {
+ u_char flags;
+#define TEKRAM_PARITY_CHECK (1)
+#define TEKRAM_SYNC_NEGO (1<<1)
+#define TEKRAM_DISCONNECT_ENABLE (1<<2)
+#define TEKRAM_START_CMD (1<<3)
+#define TEKRAM_TAGGED_COMMANDS (1<<4)
+#define TEKRAM_WIDE_NEGO (1<<5)
+ u_char sync_index;
+ u_short word2;
+ } target[16];
+ u_char host_id;
+ u_char flags;
+#define TEKRAM_MORE_THAN_2_DRIVES (1)
+#define TEKRAM_DRIVES_SUP_1GB (1<<1)
+#define TEKRAM_RESET_ON_POWER_ON (1<<2)
+#define TEKRAM_ACTIVE_NEGATION (1<<3)
+#define TEKRAM_IMMEDIATE_SEEK (1<<4)
+#define TEKRAM_SCAN_LUNS (1<<5)
+#define TEKRAM_REMOVABLE_FLAGS (3<<6) /* 0: disable; 1: boot device; 2:all */
+ u_char boot_delay_index;
+ u_char max_tags_index;
+ u_short flags1;
+#define TEKRAM_F2_F6_ENABLED (1)
+ u_short spare[29];
+};
+typedef struct Tekram_nvram Tekram_nvram;
+typedef struct Tekram_target Tekram_target;
+
+static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21};
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+/*
+** Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to
+** transmit device configuration to the ncr_attach() function.
+*/
+typedef struct {
+ int bus;
+ u_char device_fn;
+ u_int base;
+ u_int io_port;
+ int irq;
+/* port and reg fields to use INB, OUTB macros */
+ u_int port;
+ volatile struct ncr_reg *reg;
+} ncr_slot;
+
+typedef struct {
+ int type;
+#define SCSI_NCR_SYMBIOS_NVRAM (1)
+#define SCSI_NCR_TEKRAM_NVRAM (2)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ union {
+ Symbios_nvram Symbios;
+ Tekram_nvram Tekram;
+ } data;
+#endif
+} ncr_nvram;
+
+/*
+** Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init
+** to save data on each detected board for ncr_attach().
+*/
+typedef struct {
+ ncr_slot slot;
+ ncr_chip chip;
+ ncr_nvram *nvram;
+ int attached;
+} ncr_device;
/*==========================================================
**
#define UF_TRACE (0x01)
#define UF_NODISC (0x02)
+#define UF_NOSCAN (0x04)
/*---------------------------------------
**
ccb_p hold_cp;
+ /*
+ ** pointer to ccb used for negotiating.
+ ** Avoid to start a nego for all queued commands
+ ** when tagged command queuing is enabled.
+ */
+
+ ccb_p nego_cp;
+
/*
** statistical data
*/
/*
** user settable limits for sync transfer
** and tagged commands.
+ ** These limits are read from the NVRAM if present.
*/
u_char usrsync;
- u_char usrtags;
u_char usrwide;
+ u_char usrtags;
u_char usrflag;
+ u_char numtags;
u_char maxtags;
u_short num_good;
int unit; /* Unit number */
char chip_name[8]; /* Chip name */
char inst_name[16]; /* Instance name */
- u_int features; /* Chip features map */
struct timer_list timer; /* Timer link header */
int ncr_cache; /* Cache test variable */
Scsi_Cmnd *waiting_list; /* Waiting list header for commands */
/* that we can't put into the squeue */
u_long settle_time; /* Reset in progess */
u_char release_stage; /* Synchronisation stage on release */
+ u_char verbose; /* Boot verbosity for this controller*/
#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
u_char debug_error_recovery;
u_char stalling;
*/
u_short device_id;
u_char revision_id;
+
+ u_char sv_scntl0;
u_char sv_scntl3;
u_char sv_dmode;
u_char sv_dcntl;
u_char sv_stest2;
u_char sv_stest4;
+ u_char rv_scntl0;
+ u_char rv_scntl3;
u_char rv_dmode;
u_char rv_dcntl;
u_char rv_ctest3;
u_char rv_ctest5;
u_char rv_stest2;
- u_char maxburst;
u_char scsi_mode;
- u_char multiplier;
/*-----------------------------------------------
** Scripts ..
u_char myaddr;
/*
- ** timing parameters
+ ** Max dwords burst supported by the adapter.
*/
- u_char ns_sync;
- u_char rv_scntl3;
+ u_char maxburst; /* log base 2 of dwords burst */
/*
- ** controller chip dependent maximal offset.
+ ** timing parameters
*/
- u_char maxoffs;
+ u_char minsync; /* Minimum sync period factor */
+ u_char maxsync; /* Maximum sync period factor */
+ u_char maxoffs; /* Max scsi offset */
+ u_char multiplier; /* Clock multiplier (1,2,4) */
+ u_char clock_divn; /* Number of clock divisors */
+ u_long clock_khz; /* SCSI clock frequency in KHz */
+ u_int features; /* Chip features map */
- /*
- ** controller scsi clock frequency and available divisors
- */
- u_long clock_khz;
- int clock_divn;
/*-----------------------------------------------
** Link to the generic SCSI driver
(ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
static void ncr_script_fill (struct script * scr, struct scripth * scripth);
static int ncr_scatter (ccb_p cp, Scsi_Cmnd *cmd);
-static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags);
-static int ncr_getsync (ncb_p np, u_char fac, u_char *fakp, u_char *scntl3p);
+static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags);
+static void ncr_getsync (ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
static void ncr_settags (tcb_p tp, lcb_p lp);
-static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide);
+static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack);
static int ncr_show_msg (u_char * msg);
static int ncr_snooptest (ncb_p np);
static void ncr_timeout (ncb_p np);
static void ncr_usercmd (ncb_p np);
#endif
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit,
- ncr_chip *chip, u_int base, u_int io_port,
- int irq, int bus, u_char device_fn);
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device);
static void insert_into_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd);
#define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
#define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram);
+static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram);
+#endif
+
/*==========================================================
**
**
** If PREFETCH feature not enabled, remove
** the NO FLUSH bit if present.
*/
- if ((opcode & SCR_NO_FLUSH) && !(np->features & _F_PFEN)) {
+ if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) {
dst[-1] = (opcode & ~SCR_NO_FLUSH);
++opchanged;
}
*/
#define PRINT_LUN(np, target, lun) \
-printf("%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
+printf(KERN_INFO "%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
static void PRINT_ADDR(Scsi_Cmnd *cmd)
{
if (np) PRINT_LUN(np, cmd->target, cmd->lun);
}
+/*==========================================================
+**
+** NCR chip clock divisor table.
+** Divisors are multiplied by 10,000,000 in order to make
+** calculations more simple.
+**
+**==========================================================
+*/
+
+#define _5M 5000000
+static u_long div_10M[] =
+ {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M};
+
+
/*===============================================================
**
** Prepare io register values used by ncr_init() according
}
}
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/*
+** Get target set-up from Symbios format NVRAM.
+*/
+
+__initfunc(
+static void
+ ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram)
+)
+{
+ tcb_p tp = &np->target[target];
+ Symbios_target *tn = &nvram->target[target];
+
+ tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255;
+ tp->usrwide = tn->bus_width == 0x10 ? 1 : 0;
+ tp->usrtags =
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0;
+
+ if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
+ tp->usrflag |= UF_NODISC;
+ if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME))
+ tp->usrflag |= UF_NOSCAN;
+}
+
+/*
+** Get target set-up from Tekram format NVRAM.
+*/
+
+__initfunc(
+static void
+ ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram)
+)
+{
+ tcb_p tp = &np->target[target];
+ struct Tekram_target *tn = &nvram->target[target];
+ int i;
+
+ if (tn->flags & TEKRAM_SYNC_NEGO) {
+ i = tn->sync_index & 0xf;
+ tp->usrsync = i < 12 ? Tekram_sync[i] : 255;
+ }
+
+ tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0;
+
+ if (tn->flags & TEKRAM_TAGGED_COMMANDS) {
+ tp->usrtags = 2 << nvram->max_tags_index;
+ if (tp->usrtags > SCSI_NCR_MAX_TAGS)
+ tp->usrtags = SCSI_NCR_MAX_TAGS;
+ }
+
+ if (!(tn->flags & TEKRAM_DISCONNECT_ENABLE))
+ tp->usrflag = UF_NODISC;
+
+ /* If any device does not support parity, we will not use this option */
+ if (!(tn->flags & TEKRAM_PARITY_CHECK))
+ np->rv_scntl0 &= ~0x0a; /* SCSI parity checking disabled */
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
__initfunc(
-static int ncr_prepare_setting(ncb_p np)
+static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
)
{
u_char burst_max;
+ u_long period;
+ int i;
/*
** Save assumed BIOS setting
*/
+ np->sv_scntl0 = INB(nc_scntl0) & 0x0a;
np->sv_scntl3 = INB(nc_scntl3) & 0x07;
np->sv_dmode = INB(nc_dmode) & 0xce;
np->sv_dcntl = INB(nc_dcntl) & 0xa8;
np->sv_stest2 = INB(nc_stest2) & 0x20;
np->sv_stest4 = INB(nc_stest4);
+ /*
+ ** Wide ?
+ */
+
+ np->maxwide = (np->features & FE_WIDE)? 1 : 0;
+
/*
** Get the frequency of the chip's clock.
** Find the right value for scntl3.
*/
- if (np->features & _F_QUAD)
+ if (np->features & FE_QUAD)
np->multiplier = 4;
- else if (np->features & _F_DBLR)
+ else if (np->features & FE_DBLR)
np->multiplier = 2;
else
np->multiplier = 1;
- np->clock_khz = (np->features & _F_CLK80)? 80000 : 40000;
+ np->clock_khz = (np->features & FE_CLK80)? 80000 : 40000;
np->clock_khz *= np->multiplier;
if (np->clock_khz != 40000)
ncr_getclock(np, np->multiplier);
- if (np->clock_khz <= 25000) np->rv_scntl3 = 0x01;
- else if (np->clock_khz <= 37500) np->rv_scntl3 = 0x02;
- else if (np->clock_khz <= 50000) np->rv_scntl3 = 0x03;
- else if (np->clock_khz <= 75000) np->rv_scntl3 = 0x04;
- else if (np->clock_khz <= 100000) np->rv_scntl3 = 0x05;
- else if (np->clock_khz <= 150000) np->rv_scntl3 = 0x06;
- else np->rv_scntl3 = 0x07;
+ /*
+ * Divisor to be used for async (timer pre-scaler).
+ */
+ i = np->clock_divn - 1;
+ while (i >= 0) {
+ --i;
+ if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) {
+ ++i;
+ break;
+ }
+ }
+ np->rv_scntl3 = i+1;
+
+ /*
+ * Minimum synchronous period factor supported by the chip.
+ * Btw, 'period' is in tenths of nanoseconds.
+ */
+
+ period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz;
+ if (period <= 250) np->minsync = 10;
+ else if (period <= 303) np->minsync = 11;
+ else if (period <= 500) np->minsync = 12;
+ else np->minsync = (period + 40 - 1) / 40;
+
+ /*
+ * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2).
+ */
+
+ if (np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2)))
+ np->minsync = 25;
+ else if (np->minsync < 12 && !(np->features & FE_ULTRA2))
+ np->minsync = 12;
+
+ /*
+ * Maximum synchronous period factor supported by the chip.
+ */
+
+ period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz);
+ np->maxsync = period > 2540 ? 254 : period / 10;
/*
** Get on-board RAM bus address when supported
*/
- if (np->features & _F_RAM) {
+ if (np->features & FE_RAM) {
OUTONB(nc_ctest2, 0x8);
np->paddr2 = INL(nc_scr0);
OUTOFFB(nc_ctest2, 0x8);
** Prepare initial value of other IO registers
*/
#if defined SCSI_NCR_TRUST_BIOS_SETTING
+ np->rv_scntl0 = np->sv_scntl0;
np->rv_dmode = np->sv_dmode;
np->rv_dcntl = np->sv_dcntl;
np->rv_ctest3 = np->sv_ctest3;
np->rv_ctest5 = np->sv_ctest5;
burst_max = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
#else
- np->rv_dmode = 0;
- np->rv_dcntl = 0;
- np->rv_ctest3 = 0;
- np->rv_ctest4 = 0;
- np->rv_ctest5 = 0;
- np->rv_stest2 = 0;
/*
** Select burst length (dwords)
/*
** Select all supported special features
*/
- if (np->features & _F_ERL)
+ if (np->features & FE_ERL)
np->rv_dmode |= ERL; /* Enable Read Line */
- if (np->features & _F_BOF)
+ if (np->features & FE_BOF)
np->rv_dmode |= BOF; /* Burst Opcode Fetch */
- if (np->features & _F_ERMP)
+ if (np->features & FE_ERMP)
np->rv_dmode |= ERMP; /* Enable Read Multiple */
- if (np->features & _F_PFEN)
+ if (np->features & FE_PFEN)
np->rv_dcntl |= PFEN; /* Prefetch Enable */
- if (np->features & _F_CLSE)
+ if (np->features & FE_CLSE)
np->rv_dcntl |= CLSE; /* Cache Line Size Enable */
- if (np->features & _F_WRIE)
+ if (np->features & FE_WRIE)
np->rv_ctest3 |= WRIE; /* Write and Invalidate */
- if (np->features & _F_DFS)
+ if (np->features & FE_DFS)
np->rv_ctest5 |= DFS; /* Dma Fifo Size */
/*
*/
if (driver_setup.master_parity)
np->rv_ctest4 |= MPEE; /* Master parity checking */
+ if (driver_setup.scsi_parity)
+ np->rv_scntl0 |= 0x0a; /* full arb., ena parity, par->ATN */
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ /*
+ ** Get parity checking, host ID and verbose mode from NVRAM
+ **/
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ np->myaddr = nvram->data.Tekram.host_id & 0x0f;
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE))
+ np->rv_scntl0 &= ~0x0a;
+ np->myaddr = nvram->data.Symbios.host_id & 0x0f;
+ if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS)
+ np->verbose += 1;
+ break;
+ }
+ }
+#endif
+ /*
+ ** Get SCSI addr of host adapter (set by bios?).
+ */
+ if (!np->myaddr) np->myaddr = INB(nc_scid) & 0x07;
+ if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+
#endif /* SCSI_NCR_TRUST_BIOS_SETTING */
ncr_init_burst(np, burst_max);
/*
- ** Set differential mode.
+ ** Set differential mode and LED support.
+ ** Ignore these features for boards known to use a
+ ** specific GPIO wiring (Tekram only for now).
+ ** Probe initial setting of GPREG and GPCNTL for
+ ** other ones.
*/
- switch(driver_setup.diff_support) {
- case 3:
- if (INB(nc_gpreg) & 0x08)
+ if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) {
+ switch(driver_setup.diff_support) {
+ case 3:
+ if (INB(nc_gpreg) & 0x08)
break;
- case 2:
- np->rv_stest2 |= 0x20;
- break;
- case 1:
- np->rv_stest2 |= (np->sv_stest2 & 0x20);
- break;
- default:
- break;
+ case 2:
+ np->rv_stest2 |= 0x20;
+ break;
+ case 1:
+ np->rv_stest2 |= (np->sv_stest2 & 0x20);
+ break;
+ default:
+ break;
+ }
}
+ if ((driver_setup.led_pin ||
+ (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) &&
+ !(np->sv_gpcntl & 0x01))
+ np->features |= FE_LED0;
/*
** Set irq mode.
np->rv_dcntl |= IRQM;
break;
case 1:
- np->rv_stest2 |= (np->sv_dcntl & IRQM);
+ np->rv_dcntl |= (np->sv_dcntl & IRQM);
break;
default:
break;
}
+ /*
+ ** Configure targets according to driver setup.
+ ** If NVRAM present get targets setup from NVRAM.
+ ** Allow to override sync, wide and NOSCAN from
+ ** boot command line.
+ */
+ for (i = 0 ; i < MAX_TARGET ; i++) {
+ tcb_p tp = &np->target[i];
+
+ tp->usrsync = 255;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ ncr_Tekram_setup_target(np, i, &nvram->data.Tekram);
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ ncr_Symbios_setup_target(np, i, &nvram->data.Symbios);
+ break;
+ }
+ if (driver_setup.use_nvram & 0x2)
+ tp->usrsync = driver_setup.default_sync;
+ if (driver_setup.use_nvram & 0x4)
+ tp->usrwide = driver_setup.max_wide;
+ if (driver_setup.use_nvram & 0x8)
+ tp->usrflag &= ~UF_NOSCAN;
+ }
+ else {
+#else
+ if (1) {
+#endif
+ tp->usrsync = driver_setup.default_sync;
+ tp->usrwide = driver_setup.max_wide;
+ tp->usrtags = driver_setup.default_tags;
+ if (!driver_setup.disconnection)
+ np->target[i].usrflag = UF_NODISC;
+ }
+ }
+
/*
** Announce all that stuff to user.
*/
+
+ i = nvram ? nvram->type : 0;
+ printf(KERN_INFO "%s: %sID %d, Fast-%d%s%s\n", ncr_name(np),
+ i == SCSI_NCR_SYMBIOS_NVRAM ? "Symbios format NVRAM, " :
+ (i == SCSI_NCR_TEKRAM_NVRAM ? "Tekram format NVRAM, " : ""),
+ np->myaddr,
+ np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10),
+ (np->rv_scntl0 & 0xa) ? ", Parity Checking" : ", NO Parity",
+ (np->rv_stest2 & 0x20) ? ", Differential" : "");
+
if (bootverbose > 1) {
- printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n",
- ncr_name(np), np->sv_scntl3, np->rv_scntl3);
- printf ("%s: initial value of dmode/dcntl/ctest3/4/5 = (hex) %02x/%02x/%02x/%02x/%02x\n",
- ncr_name(np), np->sv_dmode, np->sv_dcntl, np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
- printf ("%s: final value of dmode/dcntl/ctest3/4/5 = (hex) %02x/%02x/%02x/%02x/%02x\n",
- ncr_name(np), np->rv_dmode, np->rv_dcntl, np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
- if (np->rv_stest2 & 0x20)
- printf ("%s: DIFF mode set\n", ncr_name(np));
+ printf ("%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ ncr_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl,
+ np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
+
+ printf ("%s: final SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ ncr_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl,
+ np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
}
if (bootverbose && np->paddr2)
- printf ("%s: on-board RAM at 0x%lx\n", ncr_name(np), np->paddr2);
-
- if (bootverbose && np->ns_sync < 25)
- printf ("%s: Ultra%s SCSI support enabled\n", ncr_name(np),
- np->ns_sync < 12 ? "-2": "");
+ printf (KERN_INFO "%s: on-board RAM at 0x%lx\n",
+ ncr_name(np), np->paddr2);
return 0;
}
+
+#ifdef SCSI_NCR_DEBUG_NVRAM
+
+__initfunc(
+void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
+)
+{
+ int i;
+
+ /* display Symbios nvram host data */
+ printf("%s: HOST ID=%d%s%s%s%s\n",
+ ncr_name(np), nvram->host_id & 0x0f,
+ (nvram->flags & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & SYMBIOS_PARITY_ENABLE) ? " PARITY" :"",
+ (nvram->flags & SYMBIOS_VERBOSE_MSGS) ? " VERSBOSE" :"",
+ (nvram->flags1 & SYMBIOS_SCAN_HI_LO) ? " HI_LO" :"");
+
+ /* display Symbios nvram drive data */
+ for (i = 0 ; i < 15 ; i++) {
+ struct Symbios_target *tn = &nvram->target[i];
+ printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+ ncr_name(np), i,
+ (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT" : "",
+ (tn->flags & SYMBIOS_SCAN_LUNS) ? " SCAN_LUNS" : "",
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ" : "",
+ tn->bus_width,
+ tn->sync_period / 4,
+ tn->timeout);
+ }
+}
+
+static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
+
+__initfunc(
+void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
+)
+{
+ int i, tags, boot_delay;
+ char *rem;
+
+ /* display Tekram nvram host data */
+ tags = 2 << nvram->max_tags_index;
+ boot_delay = 0;
+ if (nvram->boot_delay_index < 6)
+ boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+ switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+ default:
+ case 0: rem = ""; break;
+ case 1: rem = " REMOVABLE=boot device"; break;
+ case 2: rem = " REMOVABLE=all"; break;
+ }
+
+ printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+ ncr_name(np), nvram->host_id & 0x0f,
+ (nvram->flags1 & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES" :"",
+ (nvram->flags & TEKRAM_DRIVES_SUP_1GB) ? " >1GB" :"",
+ (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET" :"",
+ (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG" :"",
+ (nvram->flags & TEKRAM_IMMEDIATE_SEEK) ? " IMM_SEEK" :"",
+ (nvram->flags & TEKRAM_SCAN_LUNS) ? " SCAN_LUNS" :"",
+ (nvram->flags1 & TEKRAM_F2_F6_ENABLED) ? " F2_F6" :"",
+ rem, boot_delay, tags);
+
+ /* display Tekram nvram drive data */
+ for (i = 0; i <= 15; i++) {
+ int sync, j;
+ struct Tekram_target *tn = &nvram->target[i];
+ j = tn->sync_index & 0xf;
+ sync = j < 12 ? Tekram_sync[j] : 255;
+ printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
+ ncr_name(np), i,
+ (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "",
+ (tn->flags & TEKRAM_SYNC_NEGO) ? " SYNC" : "",
+ (tn->flags & TEKRAM_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & TEKRAM_START_CMD) ? " START" : "",
+ (tn->flags & TEKRAM_TAGGED_COMMANDS) ? " TCQ" : "",
+ (tn->flags & TEKRAM_WIDE_NEGO) ? " WIDE" : "",
+ sync);
+ }
+}
+#endif /* SCSI_NCR_DEBUG_NVRAM */
+
/*
** Host attach and initialisations.
**
*/
__initfunc(
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit,
- ncr_chip *chip, u_int base, u_int io_port,
- int irq, int bus, u_char device_fn)
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
)
{
struct host_data *host_data;
ncb_p np;
struct Scsi_Host *instance = 0;
u_long flags = 0;
+ ncr_nvram *nvram = device->nvram;
-printf("ncr53c8xx: unit=%d chip=%s rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
- unit, chip->name, chip->revision_id, base, io_port, irq);
+printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
+ device->chip.name, unit, device->chip.revision_id, device->slot.base,
+ device->slot.io_port, device->slot.irq);
/*
** Allocate host_data structure
/*
** Initialize structure.
*/
- instance->irq = irq;
host_data = (struct host_data *) instance->hostdata;
/*
/*
** Store input informations in the host data structure.
*/
- strncpy(np->chip_name, chip->name, sizeof(np->chip_name) - 1);
+ strncpy(np->chip_name, device->chip.name, sizeof(np->chip_name) - 1);
np->unit = unit;
+ np->verbose = driver_setup.verbose;
sprintf(np->inst_name, "ncr53c%s-%d", np->chip_name, np->unit);
- np->device_id = chip->device_id;
- np->revision_id = chip->revision_id;
- np->features = chip->features;
- np->maxwide = (np->features & _F_WIDE)? 1 : 0;
- np->ns_sync = 25;
- np->clock_khz = 40000;
- np->clock_divn = chip->nr_divisor;
- np->maxoffs = chip->offset_max;
- np->maxburst = chip->burst_max;
+ np->device_id = device->chip.device_id;
+ np->revision_id = device->chip.revision_id;
+ np->features = device->chip.features;
+ np->clock_divn = device->chip.nr_divisor;
+ np->maxoffs = device->chip.offset_max;
+ np->maxburst = device->chip.burst_max;
np->script0 =
(struct script *) (((u_long) &host_data->script_data) & SCR_ALIGN_MASK);
** virtual and physical memory.
*/
- np->paddr = base;
+ np->paddr = device->slot.base;
#ifndef NCR_IOMAPPED
- np->vaddr = remap_pci_mem((u_long) base, (u_long) 128);
+ np->vaddr = remap_pci_mem((u_long) np->paddr, (u_long) 128);
if (!np->vaddr) {
printf("%s: can't map memory mapped IO region\n", ncr_name(np));
goto attach_error;
** Try to map the controller chip into iospace.
*/
- request_region(io_port, 128, "ncr53c8xx");
- np->port = io_port;
+ request_region(device->slot.io_port, 128, "ncr53c8xx");
+ np->port = device->slot.io_port;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_SYMBIOS_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ ncr_display_Symbios_nvram(np, &nvram->data.Symbios);
+#endif
+ break;
+ case SCSI_NCR_TEKRAM_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ ncr_display_Tekram_nvram(np, &nvram->data.Tekram);
+#endif
+ break;
+ default:
+ nvram = 0;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ printf("%s: NVRAM: None or invalid data.\n", ncr_name(np));
+#endif
+ }
+ }
+#endif
/*
** Do chip dependent initialization.
*/
- (void)ncr_prepare_setting(np);
+ (void)ncr_prepare_setting(np, nvram);
#ifndef NCR_IOMAPPED
if (np->paddr2 && sizeof(struct script) <= 4096) {
#ifndef NCR_IOMAPPED
instance->base = (char *) np->reg;
#endif
- instance->io_port = io_port;
+ instance->irq = device->slot.irq;
+ instance->io_port = device->slot.io_port;
instance->n_io_port = 128;
instance->dma_channel = 0;
#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
** Patch the script for LED support.
*/
- if (driver_setup.led_pin & (~np->sv_gpcntl) & 0x01) {
- np->features |= _F_LED0;
+ if (np->features & FE_LED0) {
np->script0->reselect[0] = SCR_REG_REG(gpreg, SCR_OR, 0x01);
np->script0->reselect1[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
np->script0->reselect2[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
np->jump_tcb.l_cmd = SCR_JUMP;
np->jump_tcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort);
- /*
- ** Get SCSI addr of host adapter (set by bios?).
- */
-
- np->myaddr = INB(nc_scid) & 0x07;
- if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
-
/*
** Reset chip.
*/
- OUTW (nc_sien , 0); /* Disable scsi interrupts */
- OUTB (nc_dien , 0); /* Disable dma interrupts */
-
OUTB (nc_istat, SRST);
DELAY (1000);
OUTB (nc_istat, 0 );
** Install the interrupt handler.
*/
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-# ifdef SCSI_NCR_SHARE_IRQ
+#ifdef SCSI_NCR_SHARE_IRQ
if (bootverbose > 1)
printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
- ncr_name(np), irq, (u_long) np);
- if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT|SA_SHIRQ, "53c8xx", np)) {
-# else
- if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx", NULL)) {
-# endif
+ ncr_name(np), device->slot.irq, (u_long) np);
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) {
#else
- if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx")) {
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT, "ncr53c8xx", NULL)) {
#endif
- printf("%s: request irq %d failure\n", ncr_name(np), irq);
+#else
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT, "ncr53c8xx")) {
+#endif
+ printf("%s: request irq %d failure\n", ncr_name(np), device->slot.irq);
goto attach_error;
}
- np->irq = irq;
-
- /*
- ** Not allow disconnections for all targets if asked by config
- */
-
- if (!driver_setup.disconnection) {
- int i;
- for (i = 0 ; i < MAX_TARGET ; i++)
- np->target[i].usrflag |= UF_NODISC;
- }
+ np->irq = device->slot.irq;
/*
** After SCSI devices have been opened, we cannot
return(DID_BAD_TARGET);
}
+ /*---------------------------------------------
+ **
+ ** Complete the 1st TEST UNIT READY command
+ ** with error condition if the device is
+ ** flagged NOSCAN, in order to speed up
+ ** the boot.
+ **
+ **---------------------------------------------
+ */
+ if (cmd->cmnd[0] == 0 && (tp->usrflag & UF_NOSCAN)) {
+ tp->usrflag &= ~UF_NOSCAN;
+ return DID_BAD_TARGET;
+ }
if (DEBUG_FLAGS & DEBUG_TINY) {
PRINT_ADDR(cmd);
/*---------------------------------------------------
**
- ** Enable tagged queue if asked by user
+ ** Enable tagged queue if asked by scsi ioctl
**
**----------------------------------------------------
*/
- if (driver_setup.default_tags < SCSI_NCR_MAX_TAGS) {
- if (cmd->device && cmd->device->tagged_queue &&
- (lp = tp->lp[cmd->lun]) && (!lp->usetags)) {
- ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
- }
+ if (!tp->usrtags && cmd->device && cmd->device->tagged_queue) {
+ tp->usrtags = SCSI_NCR_MAX_TAGS;
+ ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
}
/*---------------------------------------------------
nego = 0;
- if (cmd->lun == 0 && (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
+ if (cmd->lun == 0 && !tp->nego_cp &&
+ (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
/*
** negotiate wide transfers ?
*/
printf ("asynchronous.\n");
};
};
+
+ /*
+ ** remember nego is pending for the target.
+ ** Avoid to start a nego for all queued commands
+ ** when tagged command queuing is enabled.
+ */
+
+ if (nego)
+ tp->nego_cp = cp;
};
/*---------------------------------------------------
tp = &np->target[cmd->target];
lp = tp->lp[cmd->lun];
+ /*
+ ** We donnot queue more than 1 ccb per target
+ ** with negotiation at any time. If this ccb was
+ ** used for negotiation, clear this info in the tcb.
+ */
+
+ if (cp == tp->nego_cp)
+ tp->nego_cp = 0;
+
/*
** Check for parity errors.
*/
** If tags was reduced due to queue full,
** increase tags if 100 good status received.
*/
- if (tp->usrtags < tp->maxtags) {
+ if (tp->numtags < tp->maxtags) {
++tp->num_good;
if (tp->num_good >= 100) {
tp->num_good = 0;
- ++tp->usrtags;
- if (tp->usrtags == 1) {
+ ++tp->numtags;
+ if (tp->numtags == 1) {
PRINT_ADDR(cmd);
printf("tagged command queueing resumed\n");
}
** Suspend tagged queuing and start good status counter.
** Announce changes to the generic driver.
*/
- if (tp->usrtags) {
+ if (tp->numtags) {
PRINT_ADDR(cmd);
printf("QUEUE FULL! suspending tagged command queueing\n");
- tp->usrtags = 0;
+ tp->numtags = 0;
tp->num_good = 0;
if (lp) {
ncr_settags (tp, lp);
void ncr_init (ncb_p np, char * msg, u_long code)
{
int i;
- u_long usrsync;
- u_char usrwide;
/*
** Reset chip.
** Message.
*/
- if (msg) printf ("%s: restart (%s).\n", ncr_name (np), msg);
+ if (msg) printf (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg);
/*
** Clear Start Queue
*/
OUTB (nc_istat, 0x00 ); /* Remove Reset, abort */
- if (driver_setup.scsi_parity)
- OUTB (nc_scntl0, 0xca); /* full arb., ena parity, par->ATN */
- else
- OUTB (nc_scntl0, 0xc0); /* full arb., (no parity) */
-
+ OUTB (nc_scntl0, np->rv_scntl0 | 0xc0);
+ /* full arb., ena parity, par->ATN */
OUTB (nc_scntl1, 0x00); /* odd parity, and remove CRST!! */
ncr_selectclock(np, np->rv_scntl3); /* Select SCSI clock */
OUTB (nc_stest3, TE); /* TolerANT enable */
OUTB (nc_stime0, 0x0d ); /* HTH disabled STO 0.4 sec. */
- /*
- ** Reinitialize usrsync.
- ** Have to renegotiate synch mode.
- */
-
- usrsync = driver_setup.default_sync;
- if (usrsync != 255) {
- if (4 * usrsync <= 11 * 50) {
- if (usrsync < np->ns_sync) {
- usrsync = np->ns_sync;
- }
- }
- else
- usrsync = 255;
- };
-
- /*
- ** Reinitialize usrwide.
- ** Have to renegotiate wide mode.
- */
-
- usrwide = driver_setup.max_wide;
- if (usrwide > np->maxwide) usrwide=np->maxwide;
-
/*
** Disable disconnects.
*/
** Enable GPIO0 pin for writing if LED support.
*/
- if (np->features & _F_LED0) {
+ if (np->features & FE_LED0) {
OUTOFFB (nc_gpcntl, 0x01);
}
/*
** For 895/6 enable SBMC interrupt and save current SCSI bus mode.
*/
- if (np->features & _F_ULTRA2) {
+ if (np->features & FE_ULTRA2) {
OUTONW (nc_sien, SBMC);
np->scsi_mode = INB (nc_stest4) & SMODE;
}
/*
** Fill in target structure.
+ ** Reinitialize usrsync.
+ ** Reinitialize usrwide.
** Prepare sync negotiation according to actual SCSI bus mode.
*/
tp->sval = 0;
tp->wval = np->rv_scntl3;
- tp->usrsync = usrsync;
- tp->usrwide = usrwide;
+ if (tp->usrsync != 255) {
+ if (tp->usrsync <= np->maxsync) {
+ if (tp->usrsync < np->minsync) {
+ tp->usrsync = np->minsync;
+ }
+ }
+ else
+ tp->usrsync = 255;
+ };
+
+ if (tp->usrwide > np->maxwide)
+ tp->usrwide = np->maxwide;
ncr_negotiate (np, tp);
}
** our limit ..
*/
- if (minsync < np->ns_sync)
- minsync = np->ns_sync;
+ if (minsync < np->minsync)
+ minsync = np->minsync;
/*
** divider limit
*/
- if (minsync > (11*50)/4)
+ if (minsync > np->maxsync)
minsync = 255;
tp->minsync = minsync;
/*==========================================================
**
-** Get clock factor and sync divisor.
+** Get clock factor and sync divisor for a given
+** synchronous factor period.
+** Returns the clock factor (in sxfer) and scntl3
+** synchronous divisor field.
**
**==========================================================
*/
-#define SCSI_NCR_USE_ALL_DIVISORS
-
-/*
-** NCR chip clock divisor table.
-** Multiplied by 2x2000000 in order to avoid useless operations in
-** the code that gets clock factor and sync divisor from sync factor.
-*/
-#define _2M 2000000
-static u_long ncr_div2_2M[] = {2*_2M, 3*_2M, 4*_2M, 6*_2M, 8*_2M, 12*_2M, 16*_2M};
-
-/*
-** Get clock factor and sync divisor for a given sync factor period.
-** Returns the clock factor, scntl3 and resulting period.
-*/
-static int ncr_getsync(ncb_p np, u_char fac, u_char *fakp, u_char *scntl3p)
+static void ncr_getsync(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p)
{
- u_long clk = np->clock_khz; /* Clock in kHz */
- int idiv = np->clock_divn; /* # divisors supported */
- u_long fak, per, per_clk;
+ u_long clk = np->clock_khz; /* SCSI clock frequency in kHz */
+ int div = np->clock_divn; /* Number of divisors supported */
+ u_long fak; /* Sync factor in sxfer */
+ u_long per; /* Period in tenths of ns */
+ u_long kpc; /* (per * clk) */
/*
- ** Compute the synchronous period in nano-seconds
+ ** Compute the synchronous period in tenths of nano-seconds
*/
- if (fac <= 10) per = 25;
- else if (fac == 11) per = 30;
- else if (fac == 12) per = 50;
- else per = 4 * fac;
+ if (sfac <= 10) per = 250;
+ else if (sfac == 11) per = 303;
+ else if (sfac == 12) per = 500;
+ else per = 40 * sfac;
/*
- ** Find the greatest divisor that allows an input speed
- ** faster than the period.
+ ** Look for the greatest clock divisor that allows an
+ ** input speed faster than the period.
*/
- per_clk = per * clk;
- while (--idiv >= 0) {
-#ifndef SCSI_NCR_USE_ALL_DIVISORS
- if (idiv & 1) continue;
-#endif
- if (ncr_div2_2M[idiv] <= per_clk) break;
- }
- if (idiv < 0) idiv = 0; /* Should never happen */
+ kpc = per * clk;
+ while (--div >= 0)
+ if (kpc >= (div_10M[div] << 2)) break;
/*
** Calculate the lowest clock factor that allows an output
** speed not faster than the period.
*/
- fak = (4 * per_clk - 1) / ncr_div2_2M[idiv] + 1;
- per = (fak * ncr_div2_2M[idiv]) / (4 * clk);
+ fak = (kpc - 1) / div_10M[div] + 1;
+
+#if 0 /* This optimization does not seem very usefull */
+
+ per = (fak * div_10M[div]) / clk;
-#ifdef SCSI_NCR_USE_ALL_DIVISORS
/*
- ** Try the next divisor and choose the one that give
- ** the fastest output speed.
+ ** Why not to try the immediate lower divisor and to choose
+ ** the one that allows the fastest output speed ?
+ ** We dont want input speed too much greater than output speed.
*/
- if (idiv >= 1 && fak < 8) {
+ if (div >= 1 && fak < 8) {
u_long fak2, per2;
- fak2 = (4 * per_clk - 1) / ncr_div2_2M[idiv-1] + 1;
- per2 = (fak2 * ncr_div2_2M[idiv-1]) / (4 * clk);
+ fak2 = (kpc - 1) / div_10M[div-1] + 1;
+ per2 = (fak2 * div_10M[div-1]) / clk;
if (per2 < per && fak2 <= 8) {
fak = fak2;
per = per2;
- --idiv;
+ --div;
}
}
#endif
- if (fak < 4) fak = 4; /* Should never happen */
+
+ if (fak < 4) fak = 4; /* Should never happen, too bad ... */
/*
** Compute and return sync parameters for the ncr
*/
*fakp = fak - 4;
- *scntl3p = ((idiv+1) << 4) + (fac < 25 ? ULTRA : 0);
-
-#ifdef DEBUG_NCR53C8XX
-printf("fac=%d idiv=%d per=%d fak=%x ", fac, idiv, per, *fakp);
-#endif
-
- return per;
+ *scntl3p = ((div+1) << 4) + (sfac < 25 ? 0x80 : 0);
}
+
/*==========================================================
**
-** Switch sync mode for current job and it's target
+** Set actual values, sync status and patch all ccbs of
+** a target according to new sync/wide agreement.
**
**==========================================================
*/
-static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
+static void ncr_set_sync_wide_status (ncb_p np, u_char target)
{
- Scsi_Cmnd *cmd;
- tcb_p tp;
- u_char target = INB (nc_ctest0) & 0x0f;
- u_char idiv;
+ ccb_p cp;
+ tcb_p tp = &np->target[target];
+
+ /*
+ ** set actual value and sync_status
+ */
+ OUTB (nc_sxfer, tp->sval);
+ np->sync_st = tp->sval;
+ OUTB (nc_scntl3, tp->wval);
+ np->wide_st = tp->wval;
+
+ /*
+ ** patch ALL ccbs of this target.
+ */
+ for (cp = np->ccb; cp; cp = cp->link_ccb) {
+ if (!cp->cmd) continue;
+ if (cp->cmd->target != target) continue;
+ cp->sync_status = tp->sval;
+ cp->wide_status = tp->wval;
+ };
+}
+
+/*==========================================================
+**
+** Switch sync mode for current job and it's target
+**
+**==========================================================
+*/
+
+static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
+{
+ Scsi_Cmnd *cmd;
+ tcb_p tp;
+ u_char target = INB (nc_ctest0) & 0x0f;
+ u_char idiv;
assert (cp);
if (!cp) return;
/*
** Deduce the value of controller sync period from scntl3.
+ ** period is in tenths of nano-seconds.
*/
+
idiv = ((scntl3 >> 4) & 0x7);
if ((sxfer & 0x1f) && idiv)
- tp->period = (((sxfer>>5)+4)*ncr_div2_2M[idiv-1])/(4*np->clock_khz);
+ tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
else
tp->period = 0xffff;
*/
PRINT_ADDR(cmd);
if (sxfer & 0x01f) {
- unsigned f10 = 10000 << (tp->widedone ? tp->widedone -1 : 0);
+ unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0);
unsigned mb10 = (f10 + tp->period/2) / tp->period;
- char *msg;
+ char *scsi;
/*
** Disable extended Sreq/Sack filtering
*/
- if (tp->period <= 200) OUTOFFB (nc_stest2, EXT);
+ if (tp->period <= 2000) OUTOFFB (nc_stest2, EXT);
/*
** Bells and whistles ;-)
*/
- msg = "";
- if (tp->widedone > 1) {
- if (tp->period < 50) msg = "ULTRA-2 WIDE SCSI ";
- else if (tp->period < 100) msg = "ULTRA WIDE SCSI ";
- else if (tp->period < 200) msg = "FAST WIDE SCSI-2 ";
- }
- else {
- if (tp->period < 50) msg = "ULTRA-2 SCSI ";
- else if (tp->period < 100) msg = "ULTRA SCSI ";
- else if (tp->period < 200) msg = "FAST SCSI-2 ";
- }
+ if (tp->period < 500) scsi = "FAST-40";
+ else if (tp->period < 1000) scsi = "FAST-20";
+ else if (tp->period < 2000) scsi = "FAST-10";
+ else scsi = "SLOW";
- printf ("%s%d.%d MB/s (%d ns, offset %d)\n", msg,
- mb10 / 10, mb10 % 10, tp->period, sxfer & 0x1f);
- } else printf ("asynchronous.\n");
+ printf ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
+ tp->widedone > 1 ? "WIDE " : "",
+ mb10 / 10, mb10 % 10, tp->period / 10, sxfer & 0x1f);
+ } else
+ printf ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : "");
/*
** set actual value and sync_status
- */
- OUTB (nc_sxfer, sxfer);
- np->sync_st = sxfer;
- OUTB (nc_scntl3, scntl3);
- np->wide_st = scntl3;
-
- /*
** patch ALL ccbs of this target.
*/
- for (cp = np->ccb; cp; cp = cp->link_ccb) {
- if (!cp->cmd) continue;
- if (cp->cmd->target != target) continue;
- cp->sync_status = sxfer;
- cp->wide_status = scntl3;
- };
+ ncr_set_sync_wide_status(np, target);
}
/*==========================================================
**
** Switch wide mode for current job and it's target
+** SCSI specs say: a SCSI device that accepts a WDTR
+** message shall reset the synchronous agreement to
+** asynchronous mode.
**
**==========================================================
*/
-static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
+static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack)
{
Scsi_Cmnd *cmd;
u_short target = INB (nc_ctest0) & 0x0f;
tcb_p tp;
u_char scntl3;
+ u_char sxfer;
assert (cp);
if (!cp) return;
tp = &np->target[target];
tp->widedone = wide+1;
scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0);
- if (tp->wval == scntl3) return;
+
+ sxfer = ack ? 0 : tp->sval;
+
+ /*
+ ** Stop there if sync/wide parameters are unchanged
+ */
+ if (tp->sval == sxfer && tp->wval == scntl3) return;
+ tp->sval = sxfer;
tp->wval = scntl3;
/*
/*
** set actual value and sync_status
- */
- OUTB (nc_scntl3, scntl3);
- np->wide_st = scntl3;
-
- /*
** patch ALL ccbs of this target.
*/
- for (cp = np->ccb; cp; cp = cp->link_ccb) {
- if (!cp->cmd) continue;
- if (cp->cmd->target != target) continue;
- cp->wide_status = scntl3;
- };
+ ncr_set_sync_wide_status(np, target);
}
/*==========================================================
**==========================================================
*/
-static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags)
+static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags)
{
int l;
- tp->usrtags = usrtags;
- tp->maxtags = usrtags;
+ if (numtags > tp->usrtags)
+ numtags = tp->usrtags;
+ tp->numtags = numtags;
+ tp->maxtags = numtags;
for (l=0; l<MAX_LUN; l++) {
lcb_p lp;
wastags = lp->usetags;
ncr_settags (tp, lp);
- if (usrtags > 1 && lp->reqccbs > 1) {
+ if (numtags > 1 && lp->reqccbs > 1) {
PRINT_LUN(np, tp - np->target, l);
- printf("using tagged command queueing, up to %ld cmds/lun\n", usrtags);
+ printf("using tagged command queueing, up to %ld cmds/lun\n", numtags);
}
- else if (usrtags <= 1 && wastags) {
+ else if (numtags <= 1 && wastags) {
PRINT_LUN(np, tp - np->target, l);
printf("disabling tagged command queueing\n");
}
*/
if (( tp->inqdata[2] & 0x7) >= 2 &&
( tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00)
- && tp->usrtags > 1) {
- reqtags = tp->usrtags;
+ && tp->numtags > 1) {
+ reqtags = tp->numtags;
if (lp->actlink <= 1)
lp->usetags=reqtags;
} else {
np->user.data = SCSI_NCR_MAX_TAGS;
for (t=0; t<MAX_TARGET; t++) {
if (!((np->user.target>>t)&1)) continue;
+ np->target[t].usrtags = np->user.data;
ncr_setmaxtags (np, &np->target[t], np->user.data);
};
break;
**
**==========================================================
**
-** I'm not quite sure of what is to be done in such a
-** situation.
-** For now,
-** Reset the bus if some devices use too fast sync transfers.
-** Otherwise, just try to renegotiate sync with targets.
+** spi2-r12 11.2.3 says a transceiver mode change must
+** generate a reset event and a device that detects a reset
+** event shall initiate a hard reset. It says also that a
+** device that detects a mode change shall set data transfer
+** mode to eight bit asynchronous, etc...
+** So, just resetting should be enough.
+**
**
**----------------------------------------------------------
*/
static int ncr_int_sbmc (ncb_p np)
{
u_char scsi_mode = INB (nc_stest4) & SMODE;
- int i;
- int oversync;
-
- printf("%s: SCSI bus mode change from %x to %x\n", ncr_name(np),
- np->scsi_mode, scsi_mode);
- if (scsi_mode == np->scsi_mode)
- return 0;
+ printf("%s: SCSI bus mode change from %x to %x, resetting ...\n",
+ ncr_name(np), np->scsi_mode, scsi_mode);
np->scsi_mode = scsi_mode;
- oversync = 0;
- for (i = 0; i < MAX_TARGET; i++) {
- tcb_p tp = &np->target[i];
-
- if (np->ns_sync < 12 && tp->maxoffs && tp->usrsync < 12) {
- if (scsi_mode != SMODE_SE)
- ncr_negotiate(np, tp);
- else
- ++oversync;
- }
- }
-
- if (oversync)
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, 2);
- return oversync;
+ return 1;
}
/*==========================================================
/*
** Take into account dma fifo and various buffers and latches,
- ** only if the interrupted phase was DATA OUT.
+ ** only if the interrupted phase is an OUTPUT phase.
*/
- if ((cmd & 7) == 0) {
+ if ((cmd & 1) == 0) {
u_char ctest5, ss0, ss2;
u_short delta;
break;
case NS_WIDE:
- ncr_setwide (np, cp, 0);
+ ncr_setwide (np, cp, 0, 0);
break;
};
** check values against driver limits.
*/
- if (per < np->ns_sync)
- {chg = 1; per = np->ns_sync;}
+ if (per < np->minsync)
+ {chg = 1; per = np->minsync;}
if (per < tp->minsync)
{chg = 1; per = tp->minsync;}
if (ofs > tp->maxoffs)
fak = 7;
scntl3 = 0;
if (ofs != 0) {
- (void) ncr_getsync(np, per, &fak, &scntl3);
+ ncr_getsync(np, per, &fak, &scntl3);
if (fak > 7) {
chg = 1;
ofs = 0;
return;
case NS_WIDE:
- ncr_setwide (np, cp, 0);
+ ncr_setwide (np, cp, 0, 0);
break;
};
};
/*
** Answer wasn't acceptable.
*/
- ncr_setwide (np, cp, 0);
+ ncr_setwide (np, cp, 0, 1);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
} else {
/*
** Answer is ok.
*/
- ncr_setwide (np, cp, wide);
+ ncr_setwide (np, cp, wide, 1);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
};
return;
** prepare an answer message
*/
- ncr_setwide (np, cp, wide);
+ ncr_setwide (np, cp, wide, 1);
np->msgout[0] = M_EXTENDED;
np->msgout[1] = 2;
tp->jump_tcb.l_cmd = (SCR_JUMP^IFFALSE (DATA (0x80 + target)));
tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
- tp->getscr[0] = (np->features & _F_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
+ tp->getscr[0] =
+ (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
tp->getscr[1] = vtophys (&tp->sval);
tp->getscr[2] = np->paddr + offsetof (struct ncr_reg, nc_sxfer);
- tp->getscr[3] = (np->features & _F_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
+ tp->getscr[3] =
+ (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
tp->getscr[4] = vtophys (&tp->wval);
tp->getscr[5] = np->paddr + offsetof (struct ncr_reg, nc_scntl3);
*/
f1 *= np->multiplier;
np->clock_khz = f1;
- np->ns_sync = 25;
-
- if (f1 >= 160000) {
- if (np->features & _F_ULTRA2) np->ns_sync = 10;
- else if (np->features & _F_ULTRA) np->ns_sync = 12;
- }
- else if (f1 >= 80000) {
- if (np->features & _F_ULTRA) np->ns_sync = 12;
- }
}
/*===================== LINUX ENTRY POINTS SECTION ==========================*/
{
#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
char *cur = str;
- char *pv;
+ char *pc, *pv;
int val;
int base;
int c;
- while (cur != NULL && (pv = strchr(cur, ':')) != NULL) {
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
val = 0;
+ pv = pc;
c = *++pv;
if (c == 'n')
val = 0;
driver_setup.irqm = val;
else if (!strncmp(cur, "pcifix:", 7))
driver_setup.pci_fix_up = val;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ else if (!strncmp(cur, "nvram:", 6))
+ driver_setup.use_nvram = val;
+#endif
else if (!strncmp(cur, "safe:", 5) && val)
memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
+ else
+ printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", pc-cur+1, cur);
if ((cur = strchr(cur, ',')) != NULL)
++cur;
#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
}
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit,
- uchar bus, uchar device_fn);
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+ uchar bus, uchar device_fn, ncr_device *device);
/*
** Linux entry point for NCR53C8XX devices detection routine.
** Read the PCI configuration and try to attach each
** detected NCR board.
**
+** If NVRAM is present, try to attach boards according to
+** the used defined boot order.
+**
** Returns the number of boards successfully attached.
*/
static ncr_chip ncr_chip_table[] __initdata = SCSI_NCR_CHIP_TABLE;
static ushort ncr_chip_ids[] __initdata = SCSI_NCR_CHIP_IDS;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+__initfunc(
+static int
+ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr_device device[])
+)
+{
+ int i, j;
+ int attach_count = 0;
+ ncr_nvram *nvram;
+ ncr_device *devp;
+
+ if (!nvram_index)
+ return 0;
+
+ /* find first Symbios NVRAM if there is one as we need to check it for host boot order */
+ for (i = 0, nvram_index = -1; i < count; i++) {
+ devp = &device[i];
+ nvram = devp->nvram;
+ if (!nvram)
+ continue;
+ if (nvram->type == SCSI_NCR_SYMBIOS_NVRAM) {
+ if (nvram_index == -1)
+ nvram_index = i;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ printf("ncr53c8xx: NVRAM: Symbios format Boot Block, 53c%s, PCI bus %d, device %d, function %d\n",
+ devp->chip.name, devp->slot.bus,
+ (int) (devp->slot.device_fn & 0xf8) >> 3,
+ (int) devp->slot.device_fn & 7);
+ for (j = 0 ; j < 4 ; j++) {
+ Symbios_host *h = &nvram->data.Symbios.host[j];
+ printf("ncr53c8xx: BOOT[%d] device_id=%04x vendor_id=%04x device_fn=%02x io_port=%04x %s\n",
+ j, h->device_id, h->vendor_id,
+ h->device_fn, h->io_port,
+ (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) ? "SCAN AT BOOT" : "");
+ }
+ }
+ else if (nvram->type == SCSI_NCR_TEKRAM_NVRAM) {
+ /* display Tekram nvram data */
+ printf("ncr53c8xx: NVRAM: Tekram format data, 53c%s, PCI bus %d, device %d, function %d\n",
+ devp->chip.name, devp->slot.bus,
+ (int) (devp->slot.device_fn & 0xf8) >> 3,
+ (int) devp->slot.device_fn & 7);
+#endif
+ }
+ }
+
+ if (nvram_index >= 0 && nvram_index < count)
+ nvram = device[nvram_index].nvram;
+ else
+ nvram = 0;
+
+ if (!nvram)
+ goto out;
+
+ /*
+ ** check devices in the boot record against devices detected.
+ ** attach devices if we find a match. boot table records that
+ ** do not match any detected devices will be ignored.
+ ** devices that do not match any boot table will not be attached
+ ** here but will attempt to be attached during the device table
+ ** rescan.
+ */
+ for (i = 0; i < 4; i++) {
+ Symbios_host *h = &nvram->data.Symbios.host[i];
+ for (j = 0 ; j < count ; j++) {
+ devp = &device[j];
+ if (h->device_fn == devp->slot.device_fn &&
+#if 0 /* bus number location in nvram ? */
+ h->bus == devp->slot.bus &&
+#endif
+ h->device_id == devp->chip.device_id)
+ break;
+ }
+ if (j < count && !devp->attached &&
+ !ncr_attach (tpnt, attach_count, devp)) {
+ attach_count++;
+ devp->attached = 1;
+ }
+ }
+
+out:
+ return attach_count;
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
__initfunc(
int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
)
int count = 0;
uchar bus, device_fn;
short index;
+ int attach_count = 0;
+ ncr_device device[8];
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_nvram nvram[4];
+ int k, nvrams;
+#endif
+ int hosts;
- if (bootverbose >= 2)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ int nvram_index = 0;
+#endif
+ if (initverbose >= 2)
ncr_print_driver_setup();
#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
# endif
#endif
+ /*
+ ** Detect all 53c8xx hosts and then attach them.
+ **
+ ** If we are using NVRAM, once all hosts are detected, we need to check
+ ** any NVRAM for boot order in case detect and boot order differ and
+ ** attach them using the order in the NVRAM.
+ **
+ ** If no NVRAM is found or data appears invalid attach boards in the
+ ** the order they are detected.
+ */
+
if (!pcibios_present())
return 0;
- chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[0]);
+ chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[0]);
+ hosts = sizeof(device) / sizeof(device[0]);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ k = 0;
+ if (driver_setup.use_nvram & 0x1)
+ nvrams = sizeof(nvram) / sizeof(nvram[0]);
+ else
+ nvrams = 0;
+#endif
+
for (j = 0; j < chips ; ++j) {
i = driver_setup.reverse_probe ? chips-1 - j : j;
for (index = 0; ; index++) {
- if (pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
- index, &bus, &device_fn))
+ char *msg = "";
+ if ((pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+ index, &bus, &device_fn)) ||
+ (count == hosts))
break;
- if (!ncr53c8xx_pci_init(tpnt, count, bus, device_fn))
- ++count;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ device[count].nvram = k < nvrams ? &nvram[k] : 0;
+#else
+ device[count].nvram = 0;
+#endif
+ if (ncr53c8xx_pci_init(tpnt, bus, device_fn, &device[count])) {
+ device[count].nvram = 0;
+ continue;
+ }
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (device[count].nvram) {
+ ++k;
+ nvram_index |= device[count].nvram->type;
+ switch (device[count].nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ msg = "with Tekram NVRAM";
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ msg = "with Symbios NVRAM";
+ break;
+ default:
+ msg = "";
+ device[count].nvram = 0;
+ --k;
+ }
+ }
+#endif
+ printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
+ device[count].chip.name, msg);
+
+ device[count].attached = 0;
+ ++count;
+ }
+ }
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ attach_count = ncr_attach_using_nvram(tpnt, nvram_index, count, device);
+#endif
+ /*
+ ** rescan device list to make sure all boards attached.
+ ** devices without boot records will not be attached yet
+ ** so try to attach them here.
+ */
+ for (i= 0; i < count; i++) {
+ if ((!device[i].attached) && (!ncr_attach (tpnt, attach_count, &device[i]))) {
+ attach_count++;
+ device[i].attached = 1;
}
}
- return count;
-}
+ return attach_count;
+}
/*
-** Read the PCI configuration of a found NCR board and
-** try yo attach it.
+** Read and check the PCI configuration for any detected NCR
+** boards and save data for attaching after all boards have
+** been detected.
*/
__initfunc(
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit,
- uchar bus, uchar device_fn)
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+ uchar bus, uchar device_fn, ncr_device *device)
)
{
ushort vendor_id, device_id, command;
ulong base, io_port;
#endif
int i, error;
- ncr_chip ncrchip, *chip;
- printk("ncr53c8xx: at PCI bus %d, device %d, function %d\n",
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_nvram *nvram = device->nvram;
+#endif
+ ncr_chip *chip;
+
+ printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
/*
* Read info from the PCI config space
*/
if (
-(error=pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id)) ||
-(error=pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id)) ||
-(error=pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command)) ||
-(error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,&io_port)) ||
-(error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base)) ||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION,&revision)) ||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq)) ||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, &cache_line_size)) ||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, &latency_timer))
+ (error=pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id)) ||
+ (error=pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id)) ||
+ (error=pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command)) ||
+ (error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,&io_port)) ||
+ (error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base)) ||
+ (error=pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION,&revision)) ||
+ (error=pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq)) ||
+ (error=pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, &cache_line_size)) ||
+ (error=pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, &latency_timer))
)
goto err_pcibios;
continue;
if (revision > ncr_chip_table[i].revision_id)
continue;
- chip = &ncrchip;
+ chip = &device->chip;
memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
chip->revision_id = revision;
break;
}
/*
- * Remove not wished features.
+ * Fix some features according to driver setup.
*/
if (!driver_setup.special_features)
- chip->features &= ~_F_SPECIAL_SET;
- if (driver_setup.ultra_scsi < 2 && chip->features & _F_ULTRA2)
- chip->features &= ~(_F_ULTRA2 | _F_ULTRA);
+ chip->features &= ~FE_SPECIAL_SET;
+ if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
+ chip->features |= FE_ULTRA;
+ chip->features &= ~FE_ULTRA2;
+ }
if (driver_setup.ultra_scsi < 1)
- chip->features &= ~_F_ULTRA;
+ chip->features &= ~FE_ULTRA;
if (!driver_setup.max_wide)
- chip->features &= ~_F_WIDE;
+ chip->features &= ~FE_WIDE;
+
+
+#ifdef SCSI_NCR_PCI_FIX_UP_SUPPORT
/*
* Try to fix up PCI config according to wished features.
*/
-#ifdef __i386
+#if defined(__i386) && !defined(MODULE)
if ((driver_setup.pci_fix_up & 1) &&
- (chip->features & _F_CLSE) && cache_line_size == 0) {
+ (chip->features & FE_CLSE) && cache_line_size == 0) {
extern char x86;
switch(x86) {
case 4: cache_line_size = 4; break;
error = pcibios_write_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, cache_line_size);
if (error)
goto err_pcibios;
- if (bootverbose)
+ if (initverbose)
printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
}
if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
- (chip->features & _F_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+ (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
command |= PCI_COMMAND_INVALIDATE;
error=pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
if (error)
goto err_pcibios;
- if (bootverbose)
+ if (initverbose)
printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
}
#endif
/*
- * Remove features that will not work
+ * Fix up for old chips that support READ LINE but not CACHE LINE SIZE.
+ * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords
+ * and donnot enable READ LINE.
+ * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed).
*/
- if ((chip->features & _F_CLSE) && cache_line_size == 0) {
- chip->features &= ~_F_CACHE_SET;
+
+ if (!(chip->features & FE_CLSE)) {
+ int burst_max = chip->burst_max;
+ if (cache_line_size == 0) {
+ chip->features &= ~FE_ERL;
+ if (burst_max > 3)
+ burst_max = 3;
+ }
+ else {
+ while (cache_line_size < (1 << burst_max))
+ --burst_max;
+ }
+ chip->burst_max = burst_max;
+ }
+
+ /*
+ * Tune PCI LATENCY TIMER according to burst max length transfer.
+ * (latency timer >= burst length + 6, we add 10 to be quite sure)
+ * If current value is zero, the device has probably been configured
+ * for no bursting due to some broken hardware.
+ */
+
+ if (latency_timer == 0 && chip->burst_max)
+ printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n");
+
+ if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
+ uchar lt = (1 << chip->burst_max) + 6 + 10;
+ if (latency_timer < lt) {
+ latency_timer = lt;
+ if (initverbose)
+ printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
+ error = pcibios_write_config_byte(bus, device_fn,
+ PCI_LATENCY_TIMER, latency_timer);
+ if (error)
+ goto err_pcibios;
+ }
+ }
+
+ /*
+ * Fix up for recent chips that support CACHE LINE SIZE.
+ * If PCI config space is not OK, remove features that shall not be
+ * used by the chip. No need to trigger possible chip bugs.
+ */
+
+ if ((chip->features & FE_CLSE) && cache_line_size == 0) {
+ chip->features &= ~FE_CACHE_SET;
printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n");
}
- if ((chip->features & _F_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
- chip->features &= ~_F_WRIE;
+ if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+ chip->features &= ~FE_WRIE;
printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n");
}
+#endif /* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+
+ /* initialise ncr_device structure with items required by ncr_attach */
+ device->slot.bus = bus;
+ device->slot.device_fn = device_fn;
+ device->slot.base = base;
+ device->slot.io_port = io_port;
+ device->slot.irq = irq;
+ device->attached = 0;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (!nvram)
+ goto out;
+
/*
- * Try to attach the controller
- */
- return ncr_attach (tpnt, unit, chip, base, io_port, (int) irq,
- bus, (uchar) device_fn);
+ ** Get access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ request_region(io_port, 128, "ncr53c8xx");
+ device->slot.port = ioport;
+#else
+ device->slot.reg = (struct ncr_reg *) remap_pci_mem((ulong) base, 128);
+ if (!device->slot.reg)
+ goto out;
+#endif
+
+ /*
+ ** Try to read SYMBIOS nvram.
+ ** Data can be used to order booting of boards.
+ **
+ ** Data is saved in ncr_device structure if NVRAM found. This
+ ** is then used to find drive boot order for ncr_attach().
+ **
+ ** NVRAM data is passed to Scsi_Host_Template later during ncr_attach()
+ ** for any device set up.
+ **
+ ** Try to read TEKRAM nvram if Symbios nvram not found.
+ */
+
+ if (!ncr_get_Symbios_nvram(&device->slot, &nvram->data.Symbios))
+ nvram->type = SCSI_NCR_SYMBIOS_NVRAM;
+ else if (!ncr_get_Tekram_nvram(&device->slot, &nvram->data.Tekram))
+ nvram->type = SCSI_NCR_TEKRAM_NVRAM;
+ else
+ nvram->type = 0;
+out:
+ /*
+ ** Release access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ release_region(device->slot.port, 128);
+#else
+ unmap_pci_mem((vm_offset_t) device->slot.reg, (u_long) 128);
+#endif
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+ return 0;
+
err_pcibios:
printk("ncr53c8xx: error %s reading configuration space\n",
pcibios_strerror(error));
copy_info(&info, " Using memory mapped IO at virtual address 0x%lx\n",
(u_long) np->reg);
#endif
- copy_info(&info, " Synchronous period factor %d, ", (int) np->ns_sync);
+ copy_info(&info, " Synchronous period factor %d, ", (int) np->minsync);
copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS);
if (driver_setup.debug || driver_setup.verbose > 1) {
*/
#endif
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/* ---------------------------------------------------------------------
+**
+** Try reading Symbios format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in/data out
+** GPIO1 - clock
+**
+** return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
+static void nvram_start(ncr_slot *np, u_char *gpreg);
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
+static void nvram_stop(ncr_slot *np, u_char *gpreg);
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
+
+__initfunc(
+static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+)
+{
+ static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_short csum;
+ u_char ack_data;
+ int retv = 1;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+ gpcntl = old_gpcntl & 0xfc;
+
+ /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+ OUTB (nc_gpreg, old_gpreg);
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* this is to set NVRAM into a known state with GPIO0/1 both low */
+ gpreg = old_gpreg;
+ nvram_setBit(np, 0, &gpreg, CLR_CLK);
+ nvram_setBit(np, 0, &gpreg, CLR_BIT);
+
+ /* now set NVRAM inactive with GPIO0/1 both high */
+ nvram_stop(np, &gpreg);
+
+ /* activate NVRAM */
+ nvram_start(np, &gpreg);
+
+ /* write device code and random address MSB */
+ nvram_write_byte(np, &ack_data,
+ 0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* write random address LSB */
+ nvram_write_byte(np, &ack_data,
+ (SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* regenerate START state to set up for reading */
+ nvram_start(np, &gpreg);
+
+ /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+ nvram_write_byte(np, &ack_data,
+ 0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* now set up GPIO0 for inputting data */
+ gpcntl |= 0x01;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all active data - only part of total NVRAM */
+ csum = nvram_read_data(np,
+ (u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
+
+ /* finally put NVRAM back in inactive mode */
+ gpcntl &= 0xfe;
+ OUTB (nc_gpcntl, gpcntl);
+ nvram_stop(np, &gpreg);
+
+#ifdef SCSI_NCR_DEBUG_NVRAM
+printf("ncr53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
+ nvram->start_marker,
+ nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
+ nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
+ nvram->byte_count, sizeof(*nvram) - 12,
+ nvram->checksum, csum);
+#endif
+
+ /* check valid NVRAM signature, verify byte count and checksum */
+ if (nvram->start_marker == 0 &&
+ !memcmp(nvram->trailer, Symbios_trailer, 6) &&
+ nvram->byte_count == sizeof(*nvram) - 12 &&
+ csum == nvram->checksum)
+ retv = 0;
+out:
+ /* return GPIO0/1 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ return retv;
+}
+
+/*
+ * Read Symbios NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+ u_short csum;
+
+ for (x = 0; x < len; x++)
+ nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
+
+ for (x = 6, csum = 0; x < len - 6; x++)
+ csum += data[x];
+
+ return csum;
+}
+
+/*
+ * Send START condition to NVRAM to wake it up.
+ */
+__initfunc(
+static void nvram_start(ncr_slot *np, u_char *gpreg)
+)
+{
+ nvram_setBit(np, 1, gpreg, SET_BIT);
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ nvram_setBit(np, 0, gpreg, CLR_BIT);
+ nvram_setBit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ * GPIO0 must already be set as an output
+ */
+__initfunc(
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+
+ for (x = 0; x < 8; x++)
+ nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+
+ nvram_readAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * READ a byte from the NVRAM and then send an ACK to say we have got it,
+ * GPIO0 must already be set as an input
+ */
+__initfunc(
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+ u_char read_bit;
+
+ *read_data = 0;
+ for (x = 0; x < 8; x++) {
+ nvram_doBit(np, &read_bit, 1, gpreg);
+ *read_data |= ((read_bit & 0x01) << (7 - x));
+ }
+
+ nvram_writeAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * Output an ACK to the NVRAM after reading,
+ * change GPIO0 to output and when done back to an input
+ */
+__initfunc(
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+ OUTB (nc_gpcntl, *gpcntl & 0xfe);
+ nvram_doBit(np, 0, write_bit, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Input an ACK from NVRAM after writing,
+ * change GPIO0 to input and when done back to an output
+ */
+__initfunc(
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+ OUTB (nc_gpcntl, *gpcntl | 0x01);
+ nvram_doBit(np, read_bit, 1, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Read or write a bit to the NVRAM,
+ * read if GPIO0 input else write if GPIO0 output
+ */
+__initfunc(
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+)
+{
+ nvram_setBit(np, write_bit, gpreg, SET_BIT);
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ if (read_bit)
+ *read_bit = INB (nc_gpreg);
+ nvram_setBit(np, 0, gpreg, CLR_CLK);
+ nvram_setBit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+__initfunc(
+static void nvram_stop(ncr_slot *np, u_char *gpreg)
+)
+{
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ nvram_setBit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ * Set/clear data/clock bit in GPIO0
+ */
+__initfunc(
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+)
+{
+ DELAY(5);
+ switch (bit_mode){
+ case SET_BIT:
+ *gpreg |= write_bit;
+ break;
+ case CLR_BIT:
+ *gpreg &= 0xfe;
+ break;
+ case SET_CLK:
+ *gpreg |= 0x02;
+ break;
+ case CLR_CLK:
+ *gpreg &= 0xfd;
+ break;
+
+ }
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(5);
+}
+
+#undef SET_BIT 0
+#undef CLR_BIT 1
+#undef SET_CLK 2
+#undef CLR_CLK 3
+
+
+/* ---------------------------------------------------------------------
+**
+** Try reading Tekram format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in
+** GPIO1 - data out
+** GPIO2 - clock
+** GPIO4 - chip select
+**
+** return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
+
+__initfunc(
+static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
+)
+{
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_short csum;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+
+ /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+ 1/2/4 out */
+ gpreg = old_gpreg & 0xe9;
+ OUTB (nc_gpreg, gpreg);
+ gpcntl = (old_gpcntl & 0xe9) | 0x09;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all of NVRAM, 64 words */
+ csum = Tnvram_read_data(np, (u_short *) nvram,
+ sizeof(*nvram) / sizeof(short), &gpreg);
+
+ /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ /* check data valid */
+ if (csum != 0x1234)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Read Tekram NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
+)
+{
+ u_char read_bit;
+ u_short csum;
+ int x;
+
+ for (x = 0, csum = 0; x < len; x++) {
+
+ /* output read command and address */
+ Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+ if (read_bit & 0x01)
+ return 0; /* Force bad checksum */
+
+ Tnvram_Read_Word(np, &data[x], gpreg);
+ csum += data[x];
+
+ Tnvram_Stop(np, gpreg);
+ }
+
+ return csum;
+}
+
+/*
+ * Send read command and address to NVRAM
+ */
+__initfunc(
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
+)
+{
+ int x;
+
+ /* send 9 bits, start bit (1), command (2), address (6) */
+ for (x = 0; x < 9; x++)
+ Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * READ a byte from the NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+)
+{
+ int x;
+ u_char read_bit;
+
+ *nvram_data = 0;
+ for (x = 0; x < 16; x++) {
+ Tnvram_Read_Bit(np, &read_bit, gpreg);
+
+ if (read_bit & 0x01)
+ *nvram_data |= (0x01 << (15 - x));
+ else
+ *nvram_data &= ~(0x01 << (15 - x));
+ }
+}
+
+/*
+ * Read bit from NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+)
+{
+ DELAY(2);
+ Tnvram_Clk(np, gpreg);
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * Write bit to GPIO0
+ */
+__initfunc(
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+)
+{
+ if (write_bit & 0x01)
+ *gpreg |= 0x02;
+ else
+ *gpreg &= 0xfd;
+
+ *gpreg |= 0x10;
+
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(2);
+
+ Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+__initfunc(
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg)
+)
+{
+ *gpreg &= 0xef;
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(2);
+
+ Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Pulse clock bit in GPIO0
+ */
+__initfunc(
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg)
+)
+{
+ OUTB (nc_gpreg, *gpreg | 0x04);
+ DELAY(2);
+ OUTB (nc_gpreg, *gpreg);
+}
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
/*
** Module stuff
*/
/*
** Name and revision of the driver
*/
-#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.1b"
+#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.4"
/*
** Check supported Linux versions
#define SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
#define SCSI_NCR_DEBUG_INFO_SUPPORT
+#define SCSI_NCR_PCI_FIX_UP_SUPPORT
#ifdef SCSI_NCR_PROC_INFO_SUPPORT
# define SCSI_NCR_PROFILE_SUPPORT
# define SCSI_NCR_USER_COMMAND_SUPPORT
/* # define SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT */
#endif
+/*==========================================================
+**
+** nvram settings - #define SCSI_NCR_NVRAM_SUPPORT to enable
+**
+**==========================================================
+*/
+
+#ifdef CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+#define SCSI_NCR_NVRAM_SUPPORT
+/* #define SCSI_NCR_DEBUG_NVRAM */
+#endif
+
/* ---------------------------------------------------------------------
** Take into account kernel configured parameters.
** Most of these options can be overridden at startup by a command line.
unsigned char offset_max;
unsigned char nr_divisor;
unsigned int features;
-#define _F_LED0 (1<<0)
-#define _F_WIDE (1<<1)
-#define _F_ULTRA (1<<2)
-#define _F_ULTRA2 (1<<3)
-#define _F_DBLR (1<<4)
-#define _F_QUAD (1<<5)
-#define _F_ERL (1<<6)
-#define _F_CLSE (1<<7)
-#define _F_WRIE (1<<8)
-#define _F_ERMP (1<<9)
-#define _F_BOF (1<<10)
-#define _F_DFS (1<<11)
-#define _F_PFEN (1<<12)
-#define _F_LDSTR (1<<13)
-#define _F_RAM (1<<14)
-#define _F_CLK80 (1<<15)
-#define _F_CACHE_SET (_F_ERL|_F_CLSE|_F_WRIE|_F_ERMP)
-#define _F_SCSI_SET (_F_WIDE|_F_ULTRA|_F_ULTRA2|_F_DBLR|_F_QUAD|F_CLK80)
-#define _F_SPECIAL_SET (_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM)
+#define FE_LED0 (1<<0)
+#define FE_WIDE (1<<1)
+#define FE_ULTRA (1<<2)
+#define FE_ULTRA2 (1<<3)
+#define FE_DBLR (1<<4)
+#define FE_QUAD (1<<5)
+#define FE_ERL (1<<6)
+#define FE_CLSE (1<<7)
+#define FE_WRIE (1<<8)
+#define FE_ERMP (1<<9)
+#define FE_BOF (1<<10)
+#define FE_DFS (1<<11)
+#define FE_PFEN (1<<12)
+#define FE_LDSTR (1<<13)
+#define FE_RAM (1<<14)
+#define FE_CLK80 (1<<15)
+#define FE_CACHE_SET (FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP)
+#define FE_SCSI_SET (FE_WIDE|FE_ULTRA|FE_ULTRA2|FE_DBLR|FE_QUAD|F_CLK80)
+#define FE_SPECIAL_SET (FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM)
} ncr_chip;
#define SCSI_NCR_CHIP_TABLE \
{ \
{PCI_DEVICE_ID_NCR_53C810, 0x0f, "810", 4, 8, 4, \
- _F_ERL} \
+ FE_ERL} \
, \
{PCI_DEVICE_ID_NCR_53C810, 0xff, "810a", 4, 8, 4, \
- _F_CACHE_SET|_F_LDSTR|_F_PFEN|_F_BOF} \
+ FE_CACHE_SET|FE_LDSTR|FE_PFEN|FE_BOF} \
, \
{PCI_DEVICE_ID_NCR_53C815, 0xff, "815", 4, 8, 4, \
- _F_ERL|_F_BOF} \
+ FE_ERL|FE_BOF} \
, \
{PCI_DEVICE_ID_NCR_53C820, 0xff, "820", 4, 8, 4, \
- _F_WIDE|_F_ERL} \
+ FE_WIDE|FE_ERL} \
, \
{PCI_DEVICE_ID_NCR_53C825, 0x0f, "825", 4, 8, 4, \
- _F_WIDE|_F_ERL|_F_BOF} \
+ FE_WIDE|FE_ERL|FE_BOF} \
, \
{PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 7, 8, 4, \
- _F_WIDE|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} \
, \
{PCI_DEVICE_ID_NCR_53C860, 0xff, "860", 4, 8, 5, \
- _F_WIDE|_F_ULTRA|_F_CLK80|_F_CACHE_SET|_F_BOF|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN|FE_RAM} \
, \
{PCI_DEVICE_ID_NCR_53C875, 0x01, "875", 7, 16, 5, \
- _F_WIDE|_F_ULTRA|_F_CLK80|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
{PCI_DEVICE_ID_NCR_53C875, 0xff, "875", 7, 16, 5, \
- _F_WIDE|_F_ULTRA|_F_DBLR|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
{PCI_DEVICE_ID_NCR_53C875J, 0xff, "875J", 7, 16, 5, \
- _F_WIDE|_F_ULTRA|_F_DBLR|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
{PCI_DEVICE_ID_NCR_53C885, 0xff, "885", 7, 16, 5, \
- _F_WIDE|_F_ULTRA|_F_DBLR|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
{PCI_DEVICE_ID_NCR_53C895, 0xff, "895", 7, 31, 7, \
- _F_WIDE|_F_ULTRA|_F_ULTRA2|_F_QUAD|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
{PCI_DEVICE_ID_NCR_53C896, 0xff, "896", 7, 31, 7, \
- _F_WIDE|_F_ULTRA|_F_ULTRA2|_F_QUAD|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM} \
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
}
/*
0, \
0, \
1, \
+ 1, \
SCSI_NCR_SETUP_DEFAULT_TAGS, \
SCSI_NCR_SETUP_DEFAULT_SYNC, \
0x00, \
0, \
0, \
0, \
+ 1, \
2, \
0, \
255, \
int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 100, 100, 1, 1};
int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
-void wakeup_bdflush(int);
+static void wakeup_bdflush(int);
/*
* Rewrote the wait-routines to use the "new" wait-queue functionality,
next = tmp->b_next;
if (tmp->b_blocknr != block || tmp->b_size != size || tmp->b_dev != dev)
continue;
- return tmp;
+ next = tmp;
+ break;
}
- return NULL;
+ return next;
}
/*
bh=find_buffer(dev,block,size);
if (!bh)
- return NULL;
+ return bh;
bh->b_count++;
bh->b_lru_time = jiffies;
wait_on_buffer(bh);
* response to dirty buffers. Once this process is activated, we write back
* a limited number of buffers to the disks and then go back to sleep again.
*/
-struct wait_queue * bdflush_wait = NULL;
-struct wait_queue * bdflush_done = NULL;
+static struct wait_queue * bdflush_wait = NULL;
+static struct wait_queue * bdflush_done = NULL;
struct task_struct *bdflush_tsk = 0;
-void wakeup_bdflush(int wait)
+static void wakeup_bdflush(int wait)
{
if (current == bdflush_tsk)
return;
+
/*
* fs/dcache.c
*
(dentry->d_name.len == len);
}
-static inline void d_insert_to_parent(struct dentry * entry, struct dentry * parent)
-{
- list_add(&entry->d_hash, d_hash(dget(parent), entry->d_name.hash));
-}
-
-static inline void d_remove_from_parent(struct dentry * dentry, struct dentry * parent)
-{
- list_del(&dentry->d_hash);
- dput(parent);
-}
-
-
/*
* When a file is deleted, we have two options:
* - turn this dentry into a negative dentry
void d_add(struct dentry * entry, struct inode * inode)
{
- d_insert_to_parent(entry, entry->d_parent);
+ struct dentry * parent = dget(entry->d_parent);
+
+ list_add(&entry->d_hash, d_hash(parent, entry->d_name.hash));
d_instantiate(entry, inode);
}
-static inline void alloc_new_name(struct dentry * entry, struct qstr *newname)
-{
- int len = newname->len;
- int hash = newname->hash;
- char *name = (char *) entry->d_name.name;
-
- if (NAME_ALLOC_LEN(len) != NAME_ALLOC_LEN(entry->d_name.len)) {
- name = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL);
- if (!name)
- printk("out of memory for dcache\n");
- kfree(entry->d_name.name);
- entry->d_name.name = name;
- }
- memcpy(name, newname->name, len);
- name[len] = 0;
- entry->d_name.len = len;
- entry->d_name.hash = hash;
-}
+#define switch(x,y) do { \
+ __typeof__ (x) __tmp = x; \
+ x = y; y = __tmp; } while (0)
-void d_move(struct dentry * dentry, struct dentry * newdir, struct qstr * newname)
+/*
+ * We cannibalize "newdentry" when moving dentry on top of it,
+ * because it's going to be thrown away anyway. We could be more
+ * polite about it, though.
+ *
+ * This forceful removal will result in ugly /proc output if
+ * somebody holds a file open that got deleted due to a rename.
+ * We could be nicer about the deleted file, and let it show
+ * up under the name it got deleted rather than the name that
+ * deleted it.
+ *
+ * Careful with the hash switch. The hash switch depends on
+ * the fact that any list-entry can be a head of the list.
+ * Think about it.
+ */
+void d_move(struct dentry * dentry, struct dentry * target)
{
- if (!dentry)
- return;
+ struct list_head * oldhead;
if (!dentry->d_inode)
printk("VFS: moving negative dcache entry\n");
- d_remove_from_parent(dentry, dentry->d_parent);
- alloc_new_name(dentry, newname);
- dentry->d_parent = newdir;
- d_insert_to_parent(dentry, newdir);
+ /* Switch the hashes.. */
+ oldhead = dentry->d_hash.prev;
+ list_del(&dentry->d_hash);
+ list_add(&dentry->d_hash, &target->d_hash);
+ list_del(&target->d_hash);
+ list_add(&target->d_hash, oldhead);
+
+ /* Switch the parents and the names.. */
+ switch(dentry->d_parent, target->d_parent);
+ switch(dentry->d_name.name, target->d_name.name);
+ switch(dentry->d_name.len, target->d_name.len);
+ switch(dentry->d_name.hash, target->d_name.hash);
+
+ /* Mark the (now overwritten) target deleted. */
+ d_delete(target);
}
+
/*
* "buflen" should be PAGE_SIZE or more.
*/
*err = -EINVAL;
*res_dir = NULL;
- if (!dir)
+ if (!dir || !dir->i_nlink)
return NULL;
sb = dir->i_sb;
struct inode * inode;
struct buffer_head * bh;
struct ext2_dir_entry * de;
- int err;
+ int err = -EIO;
if (!dir)
return -ENOENT;
+ /*
+ * N.B. Several error exits in ext2_new_inode don't set err.
+ */
inode = ext2_new_inode (dir, mode, &err);
if (!inode)
return err;
struct inode * inode;
struct buffer_head * bh;
struct ext2_dir_entry * de;
- int err;
+ int err = -EIO;
if (!dir)
return -ENOENT;
struct inode * inode;
struct buffer_head * bh, * dir_block;
struct ext2_dir_entry * de;
- int err;
+ int err = -EIO;
if (dentry->d_name.len > EXT2_NAME_LEN)
return -ENAMETOOLONG;
struct inode * inode = NULL;
struct buffer_head * bh = NULL, * name_block = NULL;
char * link;
- int i, err;
- int l;
+ int i, l, err = -EIO;
char c;
if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
}
/* Update the dcache */
- d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
- d_delete(new_dentry);
+ d_move(old_dentry, new_dentry);
retval = 0;
end_rename:
brelse (dir_bh);
* Each inode can be on two separate lists. One is
* the hash list of the inode, used for lookups. The
* other linked list is the "type" list:
- * "in_use" - valid inode, hashed
- * "dirty" - valid inode, hashed, dirty.
+ * "in_use" - valid inode, hashed if i_nlink > 0
+ * "dirty" - valid inode, hashed if i_nlink > 0, dirty.
* "unused" - ready to be re-used. Not hashed.
*
- * The two first versions also have a dirty list, allowing
- * for low-overhead inode sync() operations.
+ * A "dirty" list is maintained for each super block,
+ * allowing for low-overhead inode sync() operations.
*/
static LIST_HEAD(inode_in_use);
-static LIST_HEAD(inode_dirty);
static LIST_HEAD(inode_unused);
static struct list_head inode_hashtable[HASH_SIZE];
int max_inodes = NR_INODE;
+/*
+ * Put the inode on the super block's dirty list
+ */
void __mark_inode_dirty(struct inode *inode)
{
- spin_lock(&inode_lock);
- list_del(&inode->i_list);
- list_add(&inode->i_list, &inode_dirty);
- spin_unlock(&inode_lock);
+ struct super_block * sb = inode->i_sb;
+
+ if (sb) {
+ spin_lock(&inode_lock);
+ list_del(&inode->i_list);
+ list_add(&inode->i_list, &sb->s_dirty);
+ spin_unlock(&inode_lock);
+ }
}
static inline void unlock_inode(struct inode *inode)
}
/*
- * "sync_inodes()" goes through the dirty list
- * and writes them out and puts them back on
- * the normal list.
+ * "sync_inodes()" goes through the super block's dirty list,
+ * writes them out, and puts them back on the normal list.
*/
void sync_inodes(kdev_t dev)
{
+ struct super_block * sb = super_blocks + 0;
+ int i;
+
+ /*
+ * Search the super_blocks array for the device(s) to sync.
+ */
spin_lock(&inode_lock);
- sync_list(&inode_dirty, &inode_in_use);
+ for (i = NR_SUPER ; i-- ; sb++) {
+ if (!sb->s_dev)
+ continue;
+ if (dev && sb->s_dev != dev)
+ continue;
+
+ sync_list(&sb->s_dirty, &inode_in_use);
+ if (dev)
+ break;
+ }
spin_unlock(&inode_lock);
}
*/
void write_inode_now(struct inode *inode)
{
- spin_lock(&inode_lock);
- if (test_bit(I_DIRTY, &inode->i_state))
- sync_one(&inode_dirty, &inode_in_use, &inode->i_list, inode);
- spin_unlock(&inode_lock);
+ struct super_block * sb = inode->i_sb;
+
+ if (sb) {
+ spin_lock(&inode_lock);
+ if (test_bit(I_DIRTY, &inode->i_state))
+ sync_one(&sb->s_dirty, &inode_in_use, &inode->i_list,
+ inode);
+ spin_unlock(&inode_lock);
+ }
+ else
+ printk("write_inode_now: no super block\n");
}
/*
spin_unlock(&inode_lock);
}
-static int invalidate_list(struct list_head *head, kdev_t dev, struct list_head * dispose)
+/*
+ * Invalidate all inodes for a device, except for the root inode.
+ */
+static int invalidate_list(struct list_head *head, kdev_t dev,
+ struct inode * root, struct list_head * dispose)
{
struct list_head *next;
int busy = 0;
inode = list_entry(tmp, struct inode, i_list);
if (inode->i_dev != dev)
continue;
+ if (inode == root)
+ continue;
if (!inode->i_count && !inode->i_state) {
list_del(&inode->i_hash);
INIT_LIST_HEAD(&inode->i_hash);
* is because we don't want to sleep while messing
* with the global lists..
*/
-int invalidate_inodes(kdev_t dev)
+static int invalidate_inodes_except(kdev_t dev, struct inode * root)
{
+ struct super_block * sb = get_super(dev);
int busy;
LIST_HEAD(throw_away);
spin_lock(&inode_lock);
- busy = invalidate_list(&inode_in_use, dev, &throw_away);
- busy |= invalidate_list(&inode_dirty, dev, &throw_away);
+ busy = invalidate_list(&inode_in_use, dev, root, &throw_away);
+ if (sb)
+ busy |= invalidate_list(&sb->s_dirty, dev, root, &throw_away);
spin_unlock(&inode_lock);
dispose_list(&throw_away);
return busy;
}
+int invalidate_inodes(kdev_t dev)
+{
+ return invalidate_inodes_except(dev, NULL);
+}
+
/*
* This is called with the inode lock held. It just looks at the last
* inode on the in-use list, and if the inode is trivially freeable
}
/*
- * FIXME! These need to go through the in-use inodes to
- * check whether we can mount/umount/remount.
+ * Check whether we can mount.
*/
int fs_may_mount(kdev_t dev)
{
- return 1;
+ return !invalidate_inodes(dev);
}
+/*
+ * Check whether we can unmount.
+ */
int fs_may_umount(struct super_block *sb, struct dentry * root)
{
+ int busy;
+
shrink_dcache();
- return root->d_count == 1;
+
+ if (!root->d_inode || root->d_inode->i_dev != sb->s_dev) {
+ printk("fs_may_umount: root inode not on device??\n");
+ return 0;
+ }
+
+ /*
+ * Invalidate the inodes for this device. Device has been synced
+ * prior to call, so there should be no dirty inodes.
+ */
+ busy = invalidate_inodes_except(sb->s_dev, root->d_inode);
+
+ return (root->d_count == 1) && !busy;
}
/* This belongs in file_table.c, not here... */
inode->i_uid = inode->i_sb->u.isofs_sb.s_uid;
inode->i_gid = inode->i_sb->u.isofs_sb.s_gid;
inode->i_size = isonum_733 (raw_inode->size);
+ inode->i_blocks = inode->i_blksize = 0;
/* There are defective discs out there - we do this to protect
ourselves. A cdrom will never contain more than 800Mb */
inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
inode->u.isofs_i.i_first_extent = 0;
inode->i_size = 0;
+ inode->i_blocks = inode->i_blksize = 0;
inode->i_nlink = 1;
inode->i_uid = inode->i_gid = 0;
inode->i_mode = S_IFREG; /*Regular file, no one gets to read*/
}
}
/* Update the dcache */
- d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
- d_delete(new_dentry);
+ d_move(old_dentry, new_dentry);
retval = 0;
end_rename:
brelse(dir_bh);
}
memcpy(old_de->name,new_name,MSDOS_NAME);
/* Update the dcache */
- d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
- d_delete(new_dentry);
+ d_move(old_dentry, new_dentry);
set_hid:
old_de->attr = is_hid
? (old_de->attr | ATTR_HIDDEN)
struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode;
struct dentry *walk;
int new_ino,free_ino,dotdot_ino;
- int error,exists,ino;
+ int error,exists;
if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
if (old_ino == new_dir->i_ino) return -EINVAL;
fat_brelse(sb, dotdot_bh);
}
/* Update the dcache */
- d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
- d_delete(new_dentry);
+ d_move(old_dentry, new_dentry);
error = 0;
rename_done:
fat_brelse(sb, free_bh);
if (!result) {
int error;
result = d_alloc(parent, name);
+ result->d_count++;
error = dir->i_op->lookup(dir, result);
+ result->d_count--;
if (error) {
d_free(result);
result = ERR_PTR(error);
up(s2);
}
-static inline int is_reserved(struct dentry *dentry)
-{
- if (dentry->d_name.name[0] == '.') {
- switch (dentry->d_name.len) {
- case 2:
- if (dentry->d_name.name[1] != '.')
- break;
- /* fallthrough */
- case 1:
- return 1;
- }
- }
- return 0;
-}
-
static inline int do_rename(const char * oldname, const char * newname)
{
int error;
if (error)
goto exit_lock;
- error = -EPERM;
- if (is_reserved(new_dentry) || is_reserved(old_dentry))
- goto exit_lock;
-
- /* Disallow moves of mountpoints. */
+ /*
+ * Disallow moves of mountpoints. There is no technical
+ * reason for this, but user level stuff gets too confused.
+ */
error = -EBUSY;
if (old_dentry->d_covers != old_dentry)
goto exit_lock;
nfs_invalidate_dircache(new_dir);
/* Update the dcache */
- d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
- d_delete(new_dentry);
+ d_move(old_dentry, new_dentry);
return 0;
}
!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
return -EINVAL;
- dprintk("exp_export called for %s:%s (%x/%d fl %x).\n",
+ dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
nxp->ex_client, nxp->ex_path,
nxp->ex_dev, nxp->ex_ino, nxp->ex_flags);
dev = nxp->ex_dev;
struct dentry *dentry;
struct inode *inode;
- dprintk("nfsd: exp_rootfh(%s:%x/%d)\n", clp->cl_ident, dev, ino);
+ dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino);
if (!(exp = exp_get(clp, dev, ino)))
return -EPERM;
}
if(inode->i_dev != dev || inode->i_ino != ino) {
printk("exp_rootfh: Aieee, ino/dev mismatch\n");
- printk("exp_rootfh: arg[dev(%x):ino(%d)] inode[dev(%x):ino(%ld)]\n",
+ printk("exp_rootfh: arg[dev(%x):ino(%ld)] inode[dev(%x):ino(%ld)]\n",
dev, ino, inode->i_dev, inode->i_ino);
}
if(fhp->fh_dverified)
return 0;
- dprintk("nfsd: fh_lookup(exp %x/%d fh %p)\n",
+ dprintk("nfsd: fh_lookup(exp %x/%ld fh %p)\n",
fh->fh_xdev, fh->fh_xino, fh->fh_dentry);
/* Look up the export entry. */
void
fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
{
- dprintk("nfsd: fh_compose(exp %x/%d dentry %p)\n",
+ dprintk("nfsd: fh_compose(exp %x/%ld dentry %p)\n",
exp->ex_dev, exp->ex_ino, dentry);
fh_init(fhp); /* initialize empty fh */
__initfunc(void mount_root(void))
{
+ struct super_block * sb = super_blocks;
+ int i;
+
memset(super_blocks, 0, sizeof(super_blocks));
+ /*
+ * Initialize the dirty inode list headers for the super blocks
+ */
+ for (i = NR_SUPER ; i-- ; sb++)
+ INIT_LIST_HEAD(&sb->s_dirty);
+
do_mount_root();
}
extern void d_add(struct dentry * entry, struct inode * inode);
/* used for rename() and baskets */
-extern void d_move(struct dentry * entry, struct dentry * newparent, struct qstr * newname);
+extern void d_move(struct dentry * entry, struct dentry * newdentry);
/* appendix may either be NULL or be used for transname suffixes */
extern struct dentry * d_lookup(struct dentry * dir, struct qstr * name);
struct inode *s_ibasket;
short int s_ibasket_count;
short int s_ibasket_max;
+ struct list_head s_dirty; /* dirty inodes */
union {
struct minix_sb_info minix_sb;
#define HDLCDRV_MAGIC 0x5ac6e778
#define HDLCDRV_IFNAMELEN 6
-#define HDLCDRV_HDLCBUFFER 16 /* should be a power of 2 for speed reasons */
+#define HDLCDRV_HDLCBUFFER 32 /* should be a power of 2 for speed reasons */
#define HDLCDRV_BITBUFFER 256 /* should be a power of 2 for speed reasons */
#undef HDLCDRV_LOOPBACK /* define for HDLC debugging purposes */
#define HDLCDRV_DEBUG
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * hfmodem.h -- Linux soundcard HF FSK driver.
+ *
+ * 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.
+ *
+ *
+ * This is the Linux realtime sound output driver
+ */
+
+/*****************************************************************************/
+
+#ifndef _HFMODEM_H
+#define _HFMODEM_H
+/* --------------------------------------------------------------------- */
+
+#include <linux/version.h>
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#if LINUX_VERSION_CODE >= 0x20100
+#include <linux/poll.h>
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#define HFMODEM_MINOR 145
+
+#define HFMODEM_SRATE 8000
+#define HFMODEM_MAXBITS 4800 /* required for GTOR 300 baud mode */
+#define HFMODEM_MINBAUD 40
+#define HFMODEM_MAXBAUD 400
+#define HFMODEM_MAXCORRLEN ((HFMODEM_SRATE+HFMODEM_MINBAUD-1)/HFMODEM_MINBAUD)
+
+/* --------------------------------------------------------------------- */
+
+typedef unsigned long hfmodem_time_t;
+typedef int hfmodem_soft_t;
+typedef unsigned long hfmodem_id_t;
+
+/* --------------------------------------------------------------------- */
+
+struct hfmodem_ioctl_fsk_tx_request {
+ hfmodem_time_t tstart;
+ hfmodem_time_t tinc;
+ int inv;
+ hfmodem_id_t id;
+ unsigned int nbits;
+ unsigned char *data;
+ unsigned int freq[2];
+};
+
+struct hfmodem_ioctl_fsk_rx_request {
+ hfmodem_time_t tstart;
+ hfmodem_time_t tinc;
+ unsigned int baud;
+ hfmodem_id_t id;
+ unsigned int nbits;
+ hfmodem_soft_t *data;
+ unsigned int freq[2];
+};
+
+struct hfmodem_ioctl_mixer_params {
+ int src;
+ int igain;
+ int ogain;
+};
+
+struct hfmodem_ioctl_sample_params {
+ __s16 *data;
+ int len;
+};
+
+#define HFMODEM_IOCTL_FSKTXREQUEST _IOW('H', 0, struct hfmodem_ioctl_fsk_tx_request)
+#define HFMODEM_IOCTL_FSKRXREQUEST _IOW('H', 1, struct hfmodem_ioctl_fsk_rx_request)
+#define HFMODEM_IOCTL_CLEARRQ _IO('H', 3)
+#define HFMODEM_IOCTL_GETCURTIME _IOR('H', 4, hfmodem_time_t)
+#define HFMODEM_IOCTL_WAITRQ _IOR('H', 5, hfmodem_id_t)
+#define HFMODEM_IOCTL_MIXERPARAMS _IOW('H', 6, struct hfmodem_ioctl_mixer_params)
+#define HFMODEM_IOCTL_SAMPLESTART _IOW('H', 7, struct hfmodem_ioctl_sample_params)
+#define HFMODEM_IOCTL_SAMPLEFINISHED _IO('H', 8)
+
+/* --------------------------------------------------------------------- */
+#ifdef __KERNEL__
+
+
+#define DMA_MODE_AUTOINIT 0x10
+
+#define NR_DEVICE 1
+
+#define HFMODEM_FRAGSAMPLES (HFMODEM_SRATE/100)
+#define HFMODEM_FRAGSIZE (HFMODEM_FRAGSAMPLES*2)
+#define HFMODEM_NUMFRAGS 8
+#define HFMODEM_EXCESSFRAGS 3
+
+#define HFMODEM_NUMRXSLOTS 20
+#define HFMODEM_NUMTXSLOTS 4
+
+#define HFMODEM_CORRELATOR_CACHE 8
+
+enum slot_st { ss_unused = 0, ss_ready, ss_oper, ss_retired };
+typedef int hfmodem_conv_t;
+
+struct hfmodem_state {
+ const struct hfmodem_scops *scops;
+
+ /* io params */
+ struct {
+ unsigned int base_addr;
+ unsigned int dma;
+ unsigned int irq;
+ } io;
+
+ struct {
+ unsigned int seriobase;
+ unsigned int pariobase;
+ unsigned int midiiobase;
+ unsigned int flags;
+ } ptt_out;
+
+ struct {
+ __s16 *buf;
+ unsigned int lastfrag;
+ unsigned int fragptr;
+ unsigned int last_dmaptr;
+ int ptt_frames;
+ } dma;
+
+ struct {
+ unsigned int last_tvusec;
+ unsigned long long time_cnt;
+ hfmodem_time_t lasttime;
+#ifdef __i386__
+ unsigned int starttime_lo, starttime_hi;
+#endif /* __i386__ */
+ } clk;
+
+ int active;
+ struct wait_queue *wait;
+
+ struct {
+ __s16 *kbuf;
+ __s16 *ubuf;
+ __s16 *kptr;
+ unsigned int size;
+ int rem;
+ } sbuf;
+
+ struct {
+ hfmodem_time_t last_time;
+ unsigned int tx_phase;
+
+ struct hfmodem_l1_rxslot {
+ enum slot_st state;
+ hfmodem_time_t tstart, tinc;
+ hfmodem_soft_t *data;
+ hfmodem_soft_t *userdata;
+ unsigned int nbits;
+ unsigned int cntbits;
+ hfmodem_id_t id;
+ unsigned int corrlen;
+ hfmodem_conv_t scale;
+ unsigned int corr_cache;
+ } rxslots[HFMODEM_NUMRXSLOTS];
+
+ struct hfmodem_l1_txslot {
+ enum slot_st state;
+ hfmodem_time_t tstart, tinc;
+ unsigned char *data;
+ unsigned int nbits;
+ unsigned int cntbits;
+ hfmodem_id_t id;
+ unsigned char inv;
+ unsigned int phinc;
+ unsigned int phase_incs[2];
+ } txslots[HFMODEM_NUMTXSLOTS];
+ } l1;
+};
+
+struct hfmodem_correlator_cache {
+ int refcnt;
+ int lru;
+ unsigned short phase_incs[2];
+ hfmodem_conv_t correlator[2][2][HFMODEM_MAXCORRLEN];
+};
+
+struct hfmodem_scops {
+ unsigned int extent;
+
+ void (*init)(struct hfmodem_state *dev);
+ void (*prepare_input)(struct hfmodem_state *dev);
+ void (*trigger_input)(struct hfmodem_state *dev);
+ void (*prepare_output)(struct hfmodem_state *dev);
+ void (*trigger_output)(struct hfmodem_state *dev);
+ void (*stop)(struct hfmodem_state *dev);
+ unsigned int (*intack)(struct hfmodem_state *dev);
+ void (*mixer)(struct hfmodem_state *dev, int src, int igain, int ogain);
+};
+
+/* --------------------------------------------------------------------- */
+
+extern int hfmodem_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+#if LINUX_VERSION_CODE >= 0x20100
+extern unsigned int hfmodem_poll(struct file *file, poll_table *wait);
+#else
+extern int hfmodem_select(struct inode *inode, struct file *file, int sel_type, select_table *wait);
+#endif
+
+extern void hfmodem_clear_rq(struct hfmodem_state *dev);
+extern void hfmodem_input_samples(struct hfmodem_state *dev, hfmodem_time_t tstart,
+ hfmodem_time_t tinc, __s16 *samples);
+extern int hfmodem_output_samples(struct hfmodem_state *dev, hfmodem_time_t tstart,
+ hfmodem_time_t tinc, __s16 *samples);
+extern long hfmodem_next_tx_event(struct hfmodem_state *dev, hfmodem_time_t curr);
+extern void hfmodem_finish_pending_rx_requests(struct hfmodem_state *dev);
+extern void hfmodem_wakeup(struct hfmodem_state *dev);
+
+
+extern int hfmodem_sbcprobe(struct hfmodem_state *dev);
+extern int hfmodem_wssprobe(struct hfmodem_state *dev);
+
+extern void hfmodem_refclock_probe(void);
+extern void hfmodem_refclock_init(struct hfmodem_state *dev);
+extern hfmodem_time_t hfmodem_refclock_current(struct hfmodem_state *dev, hfmodem_time_t expected, int exp_valid);
+
+/* --------------------------------------------------------------------- */
+
+extern const char hfmodem_drvname[];
+extern const char hfmodem_drvinfo[];
+
+extern struct hfmodem_state hfmodem_state[NR_DEVICE];
+extern struct hfmodem_correlator_cache hfmodem_correlator_cache[HFMODEM_CORRELATOR_CACHE];
+
+/* --------------------------------------------------------------------- */
+#endif /* __KERNEL__ */
+/* --------------------------------------------------------------------- */
+#endif /* _HFMODEM_H */
* Kernel syscall implementation.
*/
#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
-extern asmlinkage int sys_nfsservctl(int, struct nfsctl_arg *,
- union nfsctl_res *);
+extern asmlinkage int sys_nfsservctl(int, void *, void *);
#else
#define sys_nfsservctl sys_ni_syscall
#endif
default:
if (oldstate==TCP_ESTABLISHED)
tcp_statistics.TcpCurrEstab--;
- if (state == TCP_TIME_WAIT)
+ if (state == TCP_TIME_WAIT || state == TCP_CLOSE)
sk->prot->rehash(sk);
}
}