D: bug toaster (A1 sauce makes all the difference)
D: Random linux hacker
+N: Tim Hockin
+E: thockin@hockin.org
+W: http://www.hockin.org/~thockin
+D: Natsemi ethernet
+D: Cobalt Networks (x86) support
+D: This-and-That
+
N: Dirk Hohndel
E: hohndel@suse.de
D: The XFree86[tm] Project
N: Dave Jones
E: davej@suse.de
-W: http://www.suse.de/~davej
-D: Moved PCI bridge tuning to userspace (Powertweak).
-D: Various x86 (& clones) setup code hacking.
-D: AFFS fixes for 2.3.x
-D: Various Janitorial hacks. (kernel-janitor.sourceforge.net)
+W: http://www.codemonkey.org.uk
+D: x86 errata/setup maintenance.
+D: Backport/Forwardport merge monkey.
+D: Various Janitor work.
S: c/o SuSE Linux UK Ltd
S: The Kinetic Centre
S: Theobald Street
S: 80-283 Gdansk
S: Poland
+N: Jakob Kemi
+E: jakob.kemi@telia.com
+D: V4L W9966 Webcam driver
+S: Forsbyvägen 33
+S: 74143 Knivsta
+S: Sweden
+
N: Gero Kuhlmann
E: gero@gkminix.han.de
D: mounting root via NFS
S: 00200 Helsinki
S: Finland
+N: Daniel J. Maas
+E: dmaas@dcine.com
+W: http://www.maasdigital.com
+D: dv1394
+
N: Hamish Macdonald
E: hamishm@lucent.com
D: Linux/68k port
S: Australia
N: Gerard Roudier
-E: groudier@iplus.fr
+E: groudier@free.fr
D: Contributed to asynchronous read-ahead improvement
S: 21 Rue Carnot
S: 95170 Deuil La Barre
o Gnu C 2.95.3 # gcc --version
o Gnu make 3.77 # make --version
-o binutils 2.9.5.0.24 # ld -v
+o binutils 2.9.5.0.25 # ld -v
o util-linux 2.10o # fdformat --version
o modutils 2.4.2 # insmod -V
-o e2fsprogs 1.19 # tune2fs
+o e2fsprogs 1.25 # tune2fs
o reiserfsprogs 3.x.0j # reiserfsck 2>&1|grep reiserfsprogs
o pcmcia-cs 3.1.21 # cardmgr -V
o PPP 2.4.0 # pppd --version
E2fsprogs
---------
-o <http://prdownloads.sourceforge.net/e2fsprogs/e2fsprogs-1.19.tar.gz>
-o <http://prdownloads.sourceforge.net/e2fsprogs/e2fsprogs-1.19-0.src.rpm>
+o <http://prdownloads.sourceforge.net/e2fsprogs/e2fsprogs-1.25.tar.gz>
Reiserfsprogs
-------------
this option only if you have an IEEE 1394 video device connected to
an OHCI-1394 card.
+OHCI-DV I/O support
+CONFIG_IEEE1394_DV1394
+ This driver allows you to transmit and receive DV (digital video)
+ streams on an OHCI-1394 card using a simple frame-oriented
+ interface.
+
+ The user-space API for dv1394 is documented in dv1394.h.
+
+ If you want to compile this as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want),
+ say M here and read <file:Documentation/modules.txt>. The module
+ will be called dv1394.o.
+
SBP-2 support (Harddisks etc.)
CONFIG_IEEE1394_SBP2
This option enables you to use SBP-2 devices connected to your IEEE
$(TOPDIR)/drivers/net/8390.c \
$(TOPDIR)/drivers/char/serial.c \
$(TOPDIR)/drivers/pci/pci.c \
+ $(TOPDIR)/drivers/hotplug/pci_hotplug_core.c \
+ $(TOPDIR)/drivers/hotplug/pci_hotplug_util.c \
$(TOPDIR)/drivers/block/ll_rw_blk.c \
$(TOPDIR)/drivers/sound/sound_core.c \
$(TOPDIR)/drivers/sound/sound_firmware.c \
</sect1>
<sect1><title>PCI Support Library</title>
!Edrivers/pci/pci.c
+ </sect1>
+ <sect1><title>PCI Hotplug Support Library</title>
+!Edrivers/hotplug/pci_hotplug_core.c
+!Edrivers/hotplug/pci_hotplug_util.c
</sect1>
<sect1><title>MCA Architecture</title>
<sect2><title>MCA Device Functions</title>
affinity then the value will not change from the default 0xffffffff.
Here is an example of restricting IRQ44 (eth1) to CPU0-3 then restricting
-the IRQ to CPU4-8 (this is an 8-CPU SMP box):
+the IRQ to CPU4-7 (this is an 8-CPU SMP box):
[root@moon 44]# cat smp_affinity
ffffffff
- Added support for multiple Compaq cpqarray controllers
- Fixed (rare, old) race in <devfs_lookup>
+===============================================================================
+Changes for patch v207
+
+- Fixed deadlock bug in <devfs_d_revalidate_wait>
+
+- Tag VFS deletable in <devfs_mk_symlink> if handle ignored
+
+- Updated README from master HTML file
Linux Devfs (Device File System) FAQ
Richard Gooch
-21-DEC-2001
+20-JAN-2002
+
+
+Document languages:
+
+
+
+
+
+
-----------------------------------------------------------------------------
What I don't like about devfs
How to report bugs
Strange kernel messages
+Compilation problems with devfsd
Other resources
will now work as before.
Hopefully for most people devfs will have enough support so that they
-can mount devfs directly over /dev without loosing most functionality
-(i.e. loosing access to various devices). As of 22-JAN-1998 (devfs
+can mount devfs directly over /dev without losing most functionality
+(i.e. losing access to various devices). As of 22-JAN-1998 (devfs
patch version 10) I am now running this way. All the devices I have
are available in devfs, so I don't lose anything.
What I don't like about devfs
How to report bugs
Strange kernel messages
+Compilation problems with devfsd
minor limitation
-simplying increasing the device number size is insufficient. Apart
+simply increasing the device number size is insufficient. Apart
from causing a lot of pain, it doesn't solve the management issues
of a /dev with thousands or more device nodes
+
+Compilation problems with devfsd
+
+Usually, you can compile devfsd just by typing in
+make in the source directory, followed by a make
+install (as root). Sometimes, you may have problems, particularly
+on broken configurations.
+
+
+
+error messages relating to DEVFSD_NOTIFY_DELETE
+
+This happened because you have an ancient set of kernel headers
+installed in /usr/include/linux or /usr/src/linux.
+Install kernel 2.4.10 or later. You may need to pass the
+KERNEL_DIR variable to make (if you did not install
+the new kernel sources as /usr/src/linux), or you may copy
+the devfs_fs.h file in the kernel source tree into
+/usr/include/linux.
+
+
+
+
-----------------------------------------------------------------------------
+The document master (in English) by rgooch@atnf.csiro.au is
+available at
+
+http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.html
+
+
+
A Korean translation by viatoris@nownuri.net is available at
-http://home.nownuri.net/~viatoris/devfs/devfs.html
+http://home.nownuri.net/~viatoris/devfs/devfs.html
+
+A newer version is under construcation at
+http://viatoris.new21.org/devfs/devfs.html
+
+
+-----------------------------------------------------------------------------
+Most flags courtesy of ITA's
+Flags of All Countries
+used with permission.
BusLogic= [HW,SCSI]
+ cachesize= [BUGS=ix86] Override level 2 CPU cache size detection.
+ Sometimes CPU hardware bugs make them report the cache
+ size incorrectly. The kernel will attempt work arounds
+ to fix known problems, but for some CPUs it is not
+ possible to determine what the correct size should be.
+ This option provides an override for these situations.
+
cdu31a= [HW,CD]
chandev= [HW,NET]
If you are sure the driver is not a hotplug driver then use only
__init/exit __initdata/exitdata.
+ Pointers to functions marked as __devexit must be created using
+ __devexit_p(function_name). That will generate the function
+ name or NULL if the __devexit function will be discarded.
+
2. How to find PCI devices manually (the old style)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
L: linux-decnet-user@lists.sourceforge.net
S: Maintained
+DELL LAPTOP SMM DRIVER
+P: Massimo Dal Zotto
+M: dz@debian.org
+W: http://www.debian.org/~dz/i8k/
+S: Maintained
+
DEVICE NUMBER REGISTRY
P: H. Peter Anvin
M: hpa@zytor.com
W: http://www.sistina.com/lvm
S: Maintained
+LSILOGIC/SYMBIOS/NCR 53C8XX and 53C1010 PCI-SCSI drivers
+P: Gerard Roudier
+M: groudier@free.fr
+L: linux-scsi@vger.kernel.org
+S: Maintained
+
M68K
P: Jes Sorensen
M: jes@trained-monkey.org
M: andrewtv@usa.net
S: Maintained
+NATSEMI ETHERNET DRIVER (DP8381x)
+P: Tim Hockin
+M: thockin@hockin.org
+S: Maintained
+
NCP FILESYSTEM
P: Petr Vandrovec
M: vandrove@vc.cvut.cz
S: Maintained
OPL3-SA2, SA3, and SAx DRIVER
-P: Scott Murray
-M: scott@spiteful.org
+P: Zwane Mwaikambo
+M: zwane@commfireservices.com
L: linux-sound@vger.kernel.org
S: Maintained
VERSION = 2
PATCHLEVEL = 5
SUBLEVEL = 3
-EXTRAVERSION =-pre2
+EXTRAVERSION =-pre3
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
-# $Id: Makefile,v 1.22 2001/10/01 14:42:38 bjornw Exp $
+# $Id: Makefile,v 1.3 2002/01/21 15:21:23 bjornw Exp $
# cris/Makefile
#
# This file is included by the global makefile so that you can add your own
-e s/@CONFIG_ETRAX_DRAM_SIZE_M@/$(CONFIG_ETRAX_DRAM_SIZE)/ \
< $(LD_SCRIPT) > $(LD_SCRIPT).tmp; \
else true; \
- fi && $(CROSS_COMPILE)gcc -mlinux -nostdlib
+ fi && $(CROSS_COMPILE)ld -mcrislinux
-LINKFLAGS = -mlinux -T $(LD_SCRIPT).tmp
+LINKFLAGS = -T $(LD_SCRIPT).tmp
# objcopy is used to make binary images from the resulting linked file
OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
-# normally, gcc on a linux box adds __linux__ but we do it "manually"
-# -mlinux enables -march=v10, -fno-underscores among others
+# -mlinux enables -march=v10, -fno-underscores, -D__linux__ among others
-CFLAGS := $(CFLAGS) -mlinux -fno-strict-aliasing -pipe -D__linux__
+CFLAGS := $(CFLAGS) -mlinux -pipe
ifdef CONFIG_ETRAX_KGDB
CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) -g
# each others config options
SUBDIRS += arch/cris/boot/rescue
endif
-CORE_FILES += arch/cris/kernel/kernel.o arch/cris/mm/mm.o
+CORE_FILES := arch/cris/kernel/kernel.o arch/cris/mm/mm.o $(CORE_FILES)
DRIVERS += arch/cris/drivers/drivers.o
LIBGCC = $(shell $(CC) $(CFLAGS) -print-file-name=libgcc.a)
LIBS := $(TOPDIR)/arch/cris/lib/lib.a $(LIBS) $(TOPDIR)/arch/cris/lib/lib.a $(LIBGCC)
HISTORY:
$Log: README.mm,v $
+Revision 1.1.1.1 2001/12/17 13:59:27 bjornw
+Import of Linux 2.5.1
+
Revision 1.1 2000/07/10 16:25:21 bjornw
Initial revision
Creation of the self-extracting compressed kernel image (vmlinuz)
-----------------------------------------------------------------
-$Id: README,v 1.1 2000/11/22 17:20:46 bjornw Exp $
+$Id: README,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
This can be slightly confusing because it's a process with many steps.
/*
* misc.c
*
- * $Id: misc.c,v 1.6 2001/04/09 10:00:21 starvik Exp $
+ * $Id: misc.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* This is a collection of several routines from gzip-1.0.3
* adapted for Linux.
-/* $Id: head.S,v 1.8 2001/10/03 17:15:15 bjornw Exp $
+/* $Id: head.S,v 1.2 2001/12/18 13:35:12 bjornw Exp $
*
* Rescue code, made to reside at the beginning of the
* flash-memory. when it starts, it checks a partition
#define NOP_DI 0xf025050f
#define RAM_INIT_MAGIC 0x56902387
-
+
.text
;; This is the entry point of the rescue code
jumptarget:
.dword 0xffffffff ; can be overwritten later to insert new code
-no_newjump:
+no_newjump:
+#ifdef CONFIG_ETRAX_ETHERNET
+ ;; Start MII clock to make sure it is running when tranceiver is reset
+ move.d 0x3, $r0 ; enable = on, phy = mii_clk
+ move.d $r0, [R_NETWORK_GEN_CONFIG]
+#endif
+
;; We need to setup the bus registers before we start using the DRAM
#include "../../lib/dram_init.S"
-/* $Id: kimagerescue.S,v 1.5 2001/10/03 17:15:15 bjornw Exp $
+/* $Id: kimagerescue.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* Rescue code to be prepended on a kimage and copied to the
* rescue serial port.
-/* $Id: testrescue.S,v 1.3 2001/10/03 17:15:15 bjornw Exp $
+/* $Id: testrescue.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* Simple testcode to download by the rescue block.
* Just lits some LEDs to show it was downloaded correctly.
bool 'Use kernel gdb debugger' CONFIG_ETRAX_KGDB
bool 'Enable Etrax100 watchdog' CONFIG_ETRAX_WATCHDOG
+if [ "$CONFIG_ETRAX_WATCHDOG" = "y" ]; then
+ bool 'Disable watchdog during Oops printouts' CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+fi
endmenu
if [ "$CONFIG_PROFILE" = "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
fi
-endmenu
source lib/Config.in
+endmenu
hex ' PB user changeable bits mask' CONFIG_ETRAX_PB_CHANGEABLE_BITS FF
fi
-bool 'ARTPEC-1 support' CONFIG_JULIETTE
-
-if [ "$CONFIG_JULIETTE" = "y" ]; then
- source arch/cris/drivers/juliette/Config.in
-fi
+#bool 'ARTPEC-1 support' CONFIG_JULIETTE
+#
+#if [ "$CONFIG_JULIETTE" = "y" ]; then
+# source arch/cris/drivers/juliette/Config.in
+#fi
bool 'USB host' CONFIG_ETRAX_USB_HOST
if [ "$CONFIG_ETRAX_USB_HOST" = "y" ]; then
* partition split defined below.
*
* $Log: axisflashmap.c,v $
+ * Revision 1.2 2001/12/18 13:35:15 bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.17 2001/11/12 19:42:38 pkj
+ * Fixed compiler warnings.
+ *
+ * Revision 1.16 2001/11/08 11:18:58 jonashg
+ * Always read from uncached address to avoid problems with flushing
+ * cachelines after write and MTD-erase. No performance loss have been
+ * seen yet.
+ *
* Revision 1.15 2001/10/19 12:41:04 jonashg
* Name of probe has changed in MTD.
*
static void flash_copy_from(struct map_info *map, void *to,
unsigned long from, ssize_t len)
{
- memcpy(to, (void *)(FLASH_CACHED_ADDR + from), len);
+ memcpy(to, (void *)(FLASH_UNCACHED_ADDR + from), len);
}
static void flash_write8(struct map_info *map, __u8 d, unsigned long adr)
int use_default_ptable = 1; /* Until proven otherwise */
const char *pmsg = " /dev/flash%d at 0x%x, size 0x%x\n";
- printk(KERN_NOTICE "Axis flash mapping: %x at %x\n",
+ printk(KERN_NOTICE "Axis flash mapping: %x at %lx\n",
WINDOW_SIZE, FLASH_CACHED_ADDR);
#ifdef CONFIG_MTD_CFI
--- /dev/null
+include $(APPS)/Rules.elinux
+
+all:
+
+install: src/bluetooth.c include/btcommon.h
+ ln -sfn ../../arch/cris/drivers/bluetooth/include ../../../../include/linux/bluetooth
+ if ! grep arch/cris/drivers/bluetooth/src/Config.in ../Config.in; then \
+ echo '' >> ../Config.in; \
+ echo 'if [ "$$CONFIG_ETRAX_SERIAL" = "y" ]; then' >> ../Config.in; \
+ echo ' source arch/cris/drivers/bluetooth/src/Config.in' >> ../Config.in; \
+ echo 'fi' >> ../Config.in; \
+ fi
+ if ! grep bluetooth/src/bt.o ../Makefile; then \
+ perl -pi -e "s:include:obj-\\\$$(CONFIG_BLUETOOTH) += bluetooth/src/bt.o\nsubdir-\\\$$(CONFIG_BLUETOOTH) += bluetooth/src\n\ninclude:" ../Makefile; \
+ fi
+
+clean:
+
+src/bluetooth.c:
+ @echo "You must install the OpenBT src directory before install can be done here!"
+ @exit 1
+
+include/btcommon.h:
+ @echo "You must install the OpenBT include directory before install can be done here!"
+ @exit 1
*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status
*!
*! $Log: ds1302.c,v $
+*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw
+*! Import of Linux 2.5.1
+*!
*! Revision 1.11 2001/06/14 12:35:52 jonashg
*! The ATA hack is back. It is unfortunately the only way to set g27 to output.
*!
*!
*! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN
*!
-*! $Id: ds1302.c,v 1.11 2001/06/14 12:35:52 jonashg Exp $
+*! $Id: ds1302.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*!
*!***************************************************************************/
*! in the spin-lock.
*!
*! $Log: eeprom.c,v $
+*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw
+*! Import of Linux 2.5.1
+*!
*! Revision 1.8 2001/06/15 13:24:29 jonashg
*! * Added verification of pointers from userspace in read and write.
*! * Made busy counter volatile.
-/* $Id: ethernet.c,v 1.18 2001/10/03 14:40:43 jonashg Exp $
+/* $Id: ethernet.c,v 1.2 2001/12/18 13:35:15 bjornw Exp $
*
* e100net.c: A network driver for the ETRAX 100LX network controller.
*
* The outline of this driver comes from skeleton.c.
*
* $Log: ethernet.c,v $
+ * Revision 1.2 2001/12/18 13:35:15 bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.21 2001/11/23 11:54:49 starvik
+ * Added IFF_PROMISC and IFF_ALLMULTI handling in set_multicast_list
+ * Removed compiler warnings
+ *
+ * Revision 1.20 2001/11/12 19:26:00 pkj
+ * * Corrected e100_negotiate() to not assign half to current_duplex when
+ * it was supposed to compare them...
+ * * Cleaned up failure handling in e100_open().
+ * * Fixed compiler warnings.
+ *
+ * Revision 1.19 2001/11/09 07:43:09 starvik
+ * Added full duplex support
+ * Added ioctl to set speed and duplex
+ * Clear LED timer only runs when LED is lit
+ *
* Revision 1.18 2001/10/03 14:40:43 jonashg
* Update rx_bytes counter.
*
#include <asm/dma.h>
#include <asm/system.h>
#include <asm/bitops.h>
+#include <asm/ethernet.h>
//#define ETHDEBUG
#define D(x)
static struct sockaddr default_mac = {
0,
- { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
+ { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
};
/* Information that need to be kept for each board. */
};
+/* Duplex settings */
+enum duplex
+{
+ half,
+ full,
+ autoneg
+};
+
/* Dma descriptors etc. */
#define RX_BUF_SIZE 32768
/*
** MDIO constants.
*/
-#define MDIO_BASE_STATUS_REG 0x1
-#define MDIO_BASE_CONTROL_REG 0x0
-#define MDIO_LINK_UP_MASK 0x4
+#define MDIO_BASE_STATUS_REG 0x1
+#define MDIO_BASE_CONTROL_REG 0x0
+#define MDIO_BC_NEGOTIATE 0x0200
+#define MDIO_BC_FULL_DUPLEX_MASK 0x0100
+#define MDIO_BC_AUTO_NEG_MASK 0x1000
+#define MDIO_BC_SPEED_SELECT_MASK 0x2000
+#define MDIO_ADVERTISMENT_REG 0x4
+#define MDIO_ADVERT_100_FD 0x100
+#define MDIO_ADVERT_100_HD 0x080
+#define MDIO_ADVERT_10_FD 0x040
+#define MDIO_ADVERT_10_HD 0x020
+#define MDIO_LINK_UP_MASK 0x4
#define MDIO_START 0x1
#define MDIO_READ 0x2
#define MDIO_WRITE 0x1
/* Broadcom specific */
#define MDIO_AUX_CTRL_STATUS_REG 0x18
+#define MDIO_FULL_DUPLEX_IND 0x1
#define MDIO_SPEED 0x2
#define MDIO_PHYS_ADDR 0x0
#define NET_FLASH_TIME (HZ/50) /* 20 ms */
#define NET_FLASH_PAUSE (HZ/100) /* 10 ms */
#define NET_LINK_UP_CHECK_INTERVAL (2*HZ) /* 2 s */
+#define NET_DUPLEX_CHECK_INTERVAL (2*HZ) /* 2 s */
#define NO_NETWORK_ACTIVITY 0
#define NETWORK_ACTIVITY 1
#define RX_DESC_BUF_SIZE 256
#define NBR_OF_RX_DESC (RX_BUF_SIZE / \
- RX_DESC_BUF_SIZE)
+ RX_DESC_BUF_SIZE)
#define GET_BIT(bit,val) (((val) >> (bit)) & 0x01)
+/* Define some macros to access ETRAX 100 registers */
+#define SETF(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \
+ IO_FIELD(##reg##, field, val)
+#define SETS(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \
+ IO_STATE(##reg##, field, val)
+
static etrax_dma_descr *myNextRxDesc; /* Points to the next descriptor to
- to be processed */
+ to be processed */
static etrax_dma_descr *myLastRxDesc; /* The last processed descriptor */
static etrax_dma_descr *myPrevRxDesc; /* The descriptor right before myNextRxDesc */
static struct sk_buff *tx_skb;
+static unsigned int network_rec_config_shadow = 0;
+
/* Network speed indication. */
static struct timer_list speed_timer;
static struct timer_list clear_led_timer;
-static int current_speed;
+static int current_speed; /* Speed read from tranceiver */
+static int current_speed_selection; /* Speed selected by user */
static int led_next_time;
static int led_active;
+/* Duplex */
+static struct timer_list duplex_timer;
+static int full_duplex;
+static enum duplex current_duplex;
+
/* Index to functions, as function prototypes. */
static int etrax_ethernet_init(struct net_device *dev);
static void e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void e100_rx(struct net_device *dev);
static int e100_close(struct net_device *dev);
+static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static void e100_tx_timeout(struct net_device *dev);
static struct net_device_stats *e100_get_stats(struct net_device *dev);
static void set_multicast_list(struct net_device *dev);
static void e100_hardware_send_packet(char *buf, int length);
static void update_tx_stats(struct net_device_stats *);
static void e100_check_speed(unsigned long dummy);
+static void e100_set_speed(unsigned long speed);
+static void e100_check_duplex(unsigned long dummy);
+static void e100_set_duplex(enum duplex);
+static void e100_negotiate(void);
+
static unsigned short e100_get_mdio_reg(unsigned char reg_num);
static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd);
static void e100_send_mdio_bit(unsigned char bit);
dev->get_stats = e100_get_stats;
dev->set_multicast_list = set_multicast_list;
dev->set_mac_address = e100_set_mac_address;
+ dev->do_ioctl = e100_ioctl;
+ dev->tx_timeout = e100_tx_timeout;
/* set the default MAC address */
/* Initialise receive descriptors */
- for(i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
+ for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
RxDescList[i].ctrl = 0;
RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]);
/* Initialize speed indicator stuff. */
current_speed = 10;
+ current_speed_selection = 0; /* Auto */
speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
speed_timer.function = e100_check_speed;
add_timer(&speed_timer);
+
clear_led_timer.function = e100_clear_network_leds;
- clear_led_timer.expires = jiffies + HZ/10;
- add_timer(&clear_led_timer);
+
+ full_duplex = 0;
+ current_duplex = autoneg;
+ duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
+ duplex_timer.function = e100_check_duplex;
+ add_timer(&duplex_timer);
return 0;
}
/* remember it */
- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
/* Write it to the hardware.
* Note the way the address is wrapped:
if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rx_interrupt, 0,
cardname, (void *)dev)) {
- goto grace_exit;
+ goto grace_exit0;
}
/* allocate the irq corresponding to the transmitting DMA */
if (request_irq(NETWORK_DMA_TX_IRQ_NBR, e100tx_interrupt, 0,
cardname, (void *)dev)) {
- goto grace_exit;
+ goto grace_exit1;
}
/* allocate the irq corresponding to the network errors etc */
if (request_irq(NETWORK_STATUS_IRQ_NBR, e100nw_interrupt, 0,
cardname, (void *)dev)) {
- goto grace_exit;
+ goto grace_exit2;
}
/*
* and clean up on failure.
*/
- if(request_dma(NETWORK_TX_DMA_NBR, cardname)) {
- goto grace_exit;
+ if (request_dma(NETWORK_TX_DMA_NBR, cardname)) {
+ goto grace_exit3;
}
- if(request_dma(NETWORK_RX_DMA_NBR, cardname)) {
- grace_exit:
- /* this will cause some 'trying to free free irq' but what the heck... */
- free_dma(NETWORK_TX_DMA_NBR);
- free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
- free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
- free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
- return -EAGAIN;
+ if (request_dma(NETWORK_RX_DMA_NBR, cardname)) {
+ goto grace_exit4;
}
/* give the HW an idea of what MAC address we want */
*R_NETWORK_REC_CONFIG = 0xd; /* broadcast rec, individ. rec, ma0 enabled */
#else
- *R_NETWORK_REC_CONFIG =
- IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) |
- IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable);
+ SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, broadcast, receive);
+ SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, ma0, enable);
+ SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+ *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
#endif
*R_NETWORK_GEN_CONFIG =
netif_start_queue(dev);
return 0;
+
+grace_exit4:
+ free_dma(NETWORK_TX_DMA_NBR);
+grace_exit3:
+ free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
+grace_exit2:
+ free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
+grace_exit1:
+ free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
+grace_exit0:
+ return -EAGAIN;
}
add_timer(&speed_timer);
}
+static void
+e100_negotiate(void)
+{
+ unsigned short cmd;
+ unsigned short data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
+ int bitCounter;
+
+ /* Discard old speed and duplex settings */
+ data &= ~(MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD |
+ MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD);
+
+ switch (current_speed_selection) {
+ case 10 :
+ if (current_duplex == full)
+ data |= MDIO_ADVERT_10_FD;
+ else if (current_duplex == half)
+ data |= MDIO_ADVERT_10_HD;
+ else
+ data |= MDIO_ADVERT_10_HD | MDIO_ADVERT_10_FD;
+ break;
+
+ case 100 :
+ if (current_duplex == full)
+ data |= MDIO_ADVERT_100_FD;
+ else if (current_duplex == half)
+ data |= MDIO_ADVERT_100_HD;
+ else
+ data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD;
+ break;
+
+ case 0 : /* Auto */
+ if (current_duplex == full)
+ data |= MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD;
+ else if (current_duplex == half)
+ data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_10_HD;
+ else
+ data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD;
+ break;
+
+ default : /* assume autoneg speed and duplex */
+ data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD |
+ MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD;
+ }
+
+ cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) |
+ (MDIO_ADVERTISMENT_REG<< 2);
+
+ e100_send_mdio_cmd(cmd, 1);
+
+ /* Data... */
+ for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
+ e100_send_mdio_bit(GET_BIT(bitCounter, data));
+ }
+
+ /* Renegotiate with link partner */
+ data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG);
+ data |= MDIO_BC_NEGOTIATE;
+
+ cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) |
+ (MDIO_BASE_CONTROL_REG<< 2);
+
+ e100_send_mdio_cmd(cmd, 1);
+
+ /* Data... */
+ for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
+ e100_send_mdio_bit(GET_BIT(bitCounter, data));
+ }
+}
+
+static void
+e100_set_speed(unsigned long speed)
+{
+ current_speed_selection = speed;
+ e100_negotiate();
+}
+
+static void
+e100_check_duplex(unsigned long dummy)
+{
+ unsigned long data;
+
+ data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
+
+ if (data & MDIO_FULL_DUPLEX_IND) {
+ if (!full_duplex) { /* Duplex changed to full? */
+ full_duplex = 1;
+ SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+ *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+ }
+ } else { /* half */
+ if (full_duplex) { /* Duplex changed to half? */
+ full_duplex = 0;
+ SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+ *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+ }
+ }
+
+ /* Reinitialize the timer. */
+ duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
+ add_timer(&duplex_timer);
+}
+
+static void
+e100_set_duplex(enum duplex new_duplex)
+{
+ current_duplex = new_duplex;
+ e100_negotiate();
+}
+
+
static unsigned short
e100_get_mdio_reg(unsigned char reg_num)
{
- unsigned long flags;
unsigned short cmd; /* Data to be sent on MDIO port */
unsigned short data; /* Data read from MDIO */
int bitCounter;
data = 0;
/* Data... */
- for(bitCounter=15; bitCounter>=0 ; bitCounter--) {
+ for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
data |= (e100_receive_mdio_bit() << bitCounter);
}
unsigned char data = 0x2;
/* Preamble */
- for(bitCounter = 31; bitCounter>= 0; bitCounter--)
+ for (bitCounter = 31; bitCounter>= 0; bitCounter--)
e100_send_mdio_bit(GET_BIT(bitCounter, MDIO_PREAMBLE));
- for(bitCounter = 15; bitCounter >= 2; bitCounter--)
+ for (bitCounter = 15; bitCounter >= 2; bitCounter--)
e100_send_mdio_bit(GET_BIT(bitCounter, cmd));
/* Turnaround */
- for(bitCounter = 1; bitCounter >= 0 ; bitCounter--)
+ for (bitCounter = 1; bitCounter >= 0 ; bitCounter--)
if (write_cmd)
e100_send_mdio_bit(GET_BIT(bitCounter, data));
else
static void
e100_reset_tranceiver(void)
{
- unsigned long flags;
unsigned short cmd;
unsigned short data;
int bitCounter;
data |= 0x8000;
- for(bitCounter = 15; bitCounter >= 0 ; bitCounter--) {
+ for (bitCounter = 15; bitCounter >= 0 ; bitCounter--) {
e100_send_mdio_bit(GET_BIT(bitCounter, data));
}
}
struct net_device *dev = (struct net_device *)dev_id;
unsigned long irqbits = *R_IRQ_MASK2_RD;
- if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
-
+ if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
/* acknowledge the eop interrupt */
*R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do);
/* check if one or more complete packets were indeed received */
- while(*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) {
+ while (*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) {
/* Take out the buffer and give it to the OS, then
* allocate a new buffer to put a packet in.
*/
struct net_local *np = (struct net_local *)dev->priv;
/* check for a dma0_eop interrupt */
- if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) {
-
+ if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) {
/* This protects us from concurrent execution of
* our dev->hard_start_xmit function above.
*/
*R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
- if(*R_DMA_CH0_FIRST == 0 && tx_skb) {
+ if (*R_DMA_CH0_FIRST == 0 && tx_skb) {
np->stats.tx_bytes += tx_skb->len;
np->stats.tx_packets++;
/* dma is ready with the transmission of the data in tx_skb, so now
unsigned long irqbits = *R_IRQ_MASK0_RD;
/* check for underrun irq */
- if(irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) {
+ if (irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) {
*R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
np->stats.tx_errors++;
D(printk("ethernet receiver underrun!\n"));
}
/* check for overrun irq */
- if(irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) {
+ if (irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) {
update_rx_stats(&np->stats); /* this will ack the irq */
D(printk("ethernet receiver overrun!\n"));
}
/* check for excessive collision irq */
- if(irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) {
+ if (irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) {
*R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
np->stats.tx_errors++;
D(printk("ethernet excessive collisions!\n"));
e100_rx(struct net_device *dev)
{
struct sk_buff *skb;
- int length=0;
- int i;
+ int length = 0;
struct net_local *np = (struct net_local *)dev->priv;
struct etrax_dma_descr *mySaveRxDesc = myNextRxDesc;
unsigned char *skb_data_ptr;
+#ifdef ETHDEBUG
+ int i;
+#endif
if (!led_active && jiffies > led_next_time) {
/* light the network leds depending on the current speed. */
/* Set the earliest time we may clear the LED */
led_next_time = jiffies + NET_FLASH_TIME;
led_active = 1;
+ mod_timer(&clear_led_timer, jiffies + HZ/10);
}
/* If the packet is broken down in many small packages then merge
printk("Got a packet of length %d:\n", length);
/* dump the first bytes in the packet */
skb_data_ptr = (unsigned char *)phys_to_virt(mySaveRxDesc->buf);
- for(i = 0; i < 8; i++) {
+ for (i = 0; i < 8; i++) {
printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8,
skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3],
skb_data_ptr[4],skb_data_ptr[5],skb_data_ptr[6],skb_data_ptr[7]);
/* this loop can be made using max two memcpy's if optimized */
- while(mySaveRxDesc != myNextRxDesc) {
+ while (mySaveRxDesc != myNextRxDesc) {
memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf),
mySaveRxDesc->sw_len);
skb_data_ptr += mySaveRxDesc->sw_len;
return 0;
}
+static int
+e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ /* Maybe default should return -EINVAL instead? */
+ switch (cmd) {
+ case SET_ETH_SPEED_10: /* 10 Mbps */
+ e100_set_speed(10);
+ break;
+ case SET_ETH_SPEED_100: /* 100 Mbps */
+ e100_set_speed(100);
+ break;
+ case SET_ETH_SPEED_AUTO: /* Auto negotiate speed */
+ e100_set_speed(0);
+ break;
+ case SET_ETH_DUPLEX_HALF: /* Hhalf duplex. */
+ e100_set_duplex(half);
+ break;
+ case SET_ETH_DUPLEX_FULL: /* Full duplex. */
+ e100_set_duplex(full);
+ break;
+ case SET_ETH_DUPLEX_AUTO: /* Autonegotiate duplex*/
+ e100_set_duplex(autoneg);
+ break;
+ default: /* Auto neg */
+ e100_set_speed(0);
+ e100_set_duplex(autoneg);
+ break;
+ }
+ return 0;
+}
+
static void
update_rx_stats(struct net_device_stats *es)
{
int num_addr = dev->mc_count;
unsigned long int lo_bits;
unsigned long int hi_bits;
- if (num_addr == -1)
+ if (dev->flags & IFF_PROMISC)
{
/* promiscuous mode */
lo_bits = 0xfffffffful;
hi_bits = 0xfffffffful;
- /* Enable individual receive */
- *R_NETWORK_REC_CONFIG =
- IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) |
- IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable) |
- IO_STATE(R_NETWORK_REC_CONFIG, individual, receive);
+ /* Enable individual receive */
+ SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, receive);
+ *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+ } else if (dev->flags & IFF_ALLMULTI) {
+ /* enable all multicasts */
+ lo_bits = 0xfffffffful;
+ hi_bits = 0xfffffffful;
+
+ /* Disable individual receive */
+ SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+ *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
} else if (num_addr == 0) {
/* Normal, clear the mc list */
lo_bits = 0x00000000ul;
hi_bits = 0x00000000ul;
- /* Disable individual receive */
- *R_NETWORK_REC_CONFIG =
- IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) |
- IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable);
+ /* Disable individual receive */
+ SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+ *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
} else {
/* MC mode, receive normal and MC packets */
char hash_ix;
}
dmi = dmi->next;
}
- /* Disable individual receive */
- *R_NETWORK_REC_CONFIG =
- IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) |
- IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable);
+ /* Disable individual receive */
+ SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+ *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
}
*R_NETWORK_GA_0 = lo_bits;
*R_NETWORK_GA_1 = hi_bits;
/* Set the earliest time we may clear the LED */
led_next_time = jiffies + NET_FLASH_TIME;
led_active = 1;
+ mod_timer(&clear_led_timer, jiffies + HZ/10);
}
/* configure the tx dma descriptor */
static void
e100_clear_network_leds(unsigned long dummy)
{
- if (led_active && jiffies > led_next_time) {
+ if (led_active && jiffies > led_next_time) {
e100_set_network_leds(NO_NETWORK_ACTIVITY);
/* Set the earliest time we may set the LED */
led_next_time = jiffies + NET_FLASH_PAUSE;
led_active = 0;
}
-
- clear_led_timer.expires = jiffies + HZ/10;
- add_timer(&clear_led_timer);
}
static void
d->init = etrax_ethernet_init;
- if(register_netdev(d) == 0)
+ if (register_netdev(d) == 0)
return 0;
else
return -ENODEV;
* Author: Bjorn Wesen
*
* $Log: kiobuftest.c,v $
+ * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw
+ * Import of Linux 2.5.1
+ *
* Revision 1.2 2001/02/27 13:52:50 bjornw
* malloc.h -> slab.h
*
-/* $Id: gpio.c,v 1.11 2001/10/30 14:39:12 johana Exp $
+/* $Id: gpio.c,v 1.2 2001/12/18 13:35:15 bjornw Exp $
*
* Etrax general port I/O device
*
* Johan Adolfsson (read/set directions, write)
*
* $Log: gpio.c,v $
+ * Revision 1.2 2001/12/18 13:35:15 bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.12 2001/11/12 19:42:15 pkj
+ * * Corrected return values from gpio_leds_ioctl().
+ * * Fixed compiler warnings.
+ *
* Revision 1.11 2001/10/30 14:39:12 johana
* Added D() around gpio_write printk.
*
static char gpio_name[] = "etrax gpio";
+#if 0
static wait_queue_head_t *gpio_wq;
+#endif
static int gpio_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
{
/* TODO poll on alarms! */
#if 0
- if(!ANYTHING_WANTED) {
+ if (!ANYTHING_WANTED) {
D(printk("gpio_select sleeping task\n"));
select_wait(&gpio_wq, table);
return 0;
unsigned char data, clk_mask, data_mask, write_msb;
unsigned long flags;
ssize_t retval = count;
- if (verify_area(VERIFY_READ, buf, count))
- {
+ if (verify_area(VERIFY_READ, buf, count)) {
return -EFAULT;
}
clk_mask = priv->clk_mask;
data_mask = priv->data_mask;
/* It must have been configured using the IO_CFG_WRITE_MODE */
/* Perhaps a better error code? */
- if (clk_mask == 0 || data_mask == 0)
- {
+ if (clk_mask == 0 || data_mask == 0) {
return -EPERM;
}
write_msb = priv->write_msb;
int i;
data = *buf++;
if (priv->write_msb) {
- for (i = 7; i>=0;i--) {
+ for (i = 7; i >= 0;i--) {
save_flags(flags); cli();
*priv->port = *priv->shadow &= ~clk_mask;
if (data & 1<<i)
restore_flags(flags);
}
} else {
- for (i = 0; i<=7;i++) {
+ for (i = 0; i <= 7;i++) {
save_flags(flags); cli();
*priv->port = *priv->shadow &= ~clk_mask;
if (data & 1<<i)
struct gpio_private *priv;
int p = MINOR(inode->i_rdev);
- if(p >= NUM_PORTS && p != LEDS)
+ if (p >= NUM_PORTS && p != LEDS)
return -EINVAL;
priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private),
GFP_KERNEL);
- if(!priv)
+ if (!priv)
return -ENOMEM;
priv->minor = p;
/* unlink from alarmlist and free the private structure */
- if(p == todel) {
+ if (p == todel) {
alarmlist = todel->next;
} else {
- while(p->next != todel)
+ while (p->next != todel)
p = p->next;
p->next = todel->next;
}
{
unsigned long flags;
struct gpio_private *priv = (struct gpio_private *)file->private_data;
- if(_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
+ if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
return -EINVAL;
}
if (!((priv->clk_mask & priv->changeable_bits) &&
(priv->data_mask & priv->changeable_bits) &&
(priv->clk_mask & *priv->dir_shadow) &&
- (priv->data_mask & *priv->dir_shadow)) )
+ (priv->data_mask & *priv->dir_shadow)))
{
priv->clk_mask = 0;
priv->data_mask = 0;
}
break;
default:
- if(priv->minor == LEDS)
+ if (priv->minor == LEDS)
return gpio_leds_ioctl(cmd, arg);
else
return -EINVAL;
{
unsigned char green;
unsigned char red;
+
switch (_IOC_NR(cmd)) {
case IO_LEDACTIVE_SET:
green = ((unsigned char) arg) & 1;
LED_ACTIVE_SET_G(green);
LED_ACTIVE_SET_R(red);
break;
- case IO_LED_SETBIT:
- LED_BIT_SET(arg);
- break;
- case IO_LED_CLRBIT:
- LED_BIT_CLR(arg);
+
+ case IO_LED_SETBIT:
+ LED_BIT_SET(arg);
+ break;
+
+ case IO_LED_CLRBIT:
+ LED_BIT_CLR(arg);
+ break;
+
default:
return -EINVAL;
}
+
+ return 0;
}
struct file_operations gpio_fops = {
static __init int
gpio_init(void)
{
- int res,i;
+ extern void init_ioremap(void);
+ int res;
+#if defined (CONFIG_ETRAX_CSP0_LEDS)
+ int i;
+#endif
/* do the formalities */
res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
- if(res < 0) {
+ if (res < 0) {
printk(KERN_ERR "gpio: couldn't get a major number.\n");
return res;
}
/* Clear all leds */
-#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS)
-
- init_ioremap();
- LED_NETWORK_SET(0);
- LED_ACTIVE_SET(0);
- LED_DISK_READ(0);
- LED_DISK_WRITE(0);
+#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS)
+ init_ioremap();
+ LED_NETWORK_SET(0);
+ LED_ACTIVE_SET(0);
+ LED_DISK_READ(0);
+ LED_DISK_WRITE(0);
#if defined (CONFIG_ETRAX_CSP0_LEDS)
- for( i = 0; i < 32; i ++)
- {
- LED_BIT_SET(i);
- }
+ for (i = 0; i < 32; i++) {
+ LED_BIT_SET(i);
+ }
#endif
#endif
*! don't use PB_I2C if DS1302 uses same bits,
*! use PB.
*! $Log: i2c.c,v $
+*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw
+*! Import of Linux 2.5.1
+*!
*! Revision 1.7 2001/04/04 13:11:36 markusl
*! Updated according to review remarks
*!
*! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN
*!
*!***************************************************************************/
-/* $Id: i2c.c,v 1.7 2001/04/04 13:11:36 markusl Exp $ */
+/* $Id: i2c.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ */
/****************** INCLUDE FILES SECTION ***********************************/
#include <linux/module.h>
-/* $Id: i2c.h,v 1.3 2001/03/19 12:43:01 markusl Exp $ */
+/* $Id: i2c.h,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ */
/* High level I2C actions */
int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
-/* $Id: ide.c,v 1.19 2001/05/09 12:53:16 johana Exp $
+/* $Id: ide.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* Etrax specific IDE functions, like init and PIO-mode setting etc.
* Almost the entire ide.c is used for the rest of the Etrax ATA driver.
* Mikael Starvik (pio setup stuff)
*
* $Log: ide.c,v $
+ * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw
+ * Import of Linux 2.5.1
+ *
* Revision 1.19 2001/05/09 12:53:16 johana
* Added #include <asm/dma.h>
*
#!/usr/bin/perl -w
-# $Id: bintocarr.pl,v 1.4 2001/08/08 08:18:13 bjarne Exp $
+# $Id: bintocarr.pl,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
# Copy of mkjulbin.pl made by Olof
# convert a binary stdin to a C-file containing a char array of the input
# first argument is the symbol name
- ;; $Id: e100lpslave.S,v 1.3 2001/06/21 16:55:26 olof Exp $
+ ;; $Id: e100lpslave.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
;;
;; Etrax100 slave network<->parport forwarder
;;
-/* $Id: e100lpslavenet.c,v 1.4 2001/06/21 16:55:26 olof Exp $
+/* $Id: e100lpslavenet.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* e100lpslavenet.c: A network driver for the ETRAX 100LX slave controller.
*
* The outline of this driver comes from skeleton.c.
*
* $Log: e100lpslavenet.c,v $
+ * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw
+ * Import of Linux 2.5.1
+ *
* Revision 1.4 2001/06/21 16:55:26 olof
* Minimized par port setup time to gain bandwidth
*
-/* $Id: parport.c,v 1.8 2001/09/26 11:51:52 bjornw Exp $
+/* $Id: parport.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* Elinux parallel port driver
* NOTE!
-/* $Id: serial.c,v 1.23 2001/10/30 17:53:26 pkj Exp $
+/* $Id: serial.c,v 1.3 2001/12/19 10:32:35 johana Exp $
*
* Serial port driver for the ETRAX 100LX chip
*
* Many, many authors. Based once upon a time on serial.c for 16x50.
*
* $Log: serial.c,v $
+ * Revision 1.3 2001/12/19 10:32:35 johana
+ * Cleaned up write_rs485() - now it works correctly without padding extra
+ * char.
+ * Added sane default initialisation of rs485.
+ * Added #ifdef around dummy variables.
+ *
+ * Revision 1.27 2001/11/29 17:00:41 pkj
+ * 2kB seems to be too small a buffer when using 921600 bps,
+ * so increase it to 4kB (this was already done for the elinux
+ * version of the serial driver).
+ *
+ * Revision 1.26 2001/11/19 14:20:41 pkj
+ * Minor changes to comments and unused code.
+ *
+ * Revision 1.25 2001/11/12 20:03:43 pkj
+ * Fixed compiler warnings.
+ *
+ * Revision 1.24 2001/11/12 15:10:05 pkj
+ * Total redesign of the receiving part of the serial driver.
+ * Uses eight chained descriptors to write to a 4kB buffer.
+ * This data is then serialised into a 2kB buffer. From there it
+ * is copied into the TTY's flip buffers when they become available.
+ * A lot of copying, and the sizes of the buffers might need to be
+ * tweaked, but all in all it should work better than the previous
+ * version, without the need to modify the TTY code in any way.
+ * Also note that erroneous bytes are now correctly marked in the
+ * flag buffers (instead of always marking the first byte).
+ *
* Revision 1.23 2001/10/30 17:53:26 pkj
* * Set info->uses_dma to 0 when a port is closed.
* * Mark the timer1 interrupt as a fast one (SA_INTERRUPT).
* Changed %ul to %lu in printf's
*
* Revision 1.47 2000/10/18 15:06:53 pkj
- * Compile correctly with CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST and
- * CONFIG_SERIAL_PROC_ENTRY together.
+ * Compile correctly with CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST and
+ * CONFIG_ETRAX_SERIAL_PROC_ENTRY together.
* Some clean-up of the /proc/serial file.
*
* Revision 1.46 2000/10/16 12:59:40 johana
- * Added CONFIG_SERIAL_PROC_ENTRY for statistics and debug info.
+ * Added CONFIG_ETRAX_SERIAL_PROC_ENTRY for statistics and debug info.
*
* Revision 1.45 2000/10/13 17:10:59 pkj
* Do not flush DMAs while flipping TTY buffers.
* Uncomment definition of SERIAL_HANDLE_EARLY_ERRORS.
*
* Revision 1.36 2000/09/20 13:12:52 johana
- * Support for CONFIG_ETRAX100_SERIAL_RX_TIMEOUT_TICKS:
+ * Support for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS:
* Number of timer ticks between flush of receive fifo (1 tick = 10ms).
* Try 0-3 for low latency applications. Approx 5 for high load
* applications (e.g. PPP). Maybe this should be more adaptive some day...
*
*/
-static char *serial_version = "$Revision: 1.23 $";
+static char *serial_version = "$Revision: 1.3 $";
#include <linux/config.h>
#include <linux/version.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
+#include <linux/slab.h>
#if (LINUX_VERSION_CODE >= 131343)
#include <linux/init.h>
#endif
#include "serial_compat.h"
#endif
+#define _INLINE_ inline
+
static DECLARE_TASK_QUEUE(tq_serial);
struct tty_driver serial_driver, callout_driver;
#define SERIAL_TYPE_CALLOUT 2
#endif
-#define DEBUG_LOG(line, string, value)
-
-/* Add an x here to log a lot of timer stuff */
-#define TIMERD(x)
-
/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS 256
#define TTY_THROTTLE_LIMIT (TTY_FLIPBUF_SIZE/10)
+#define SERIAL_RECV_SIZE 4096
+#define SERIAL_DESCR_BUF_SIZE 512
+
+/* Add an x here to log a lot of timer stuff */
+#define TIMERD(x)
+
+#define DEBUG_LOG(line, string, value)
+
#ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS
/* Default number of timer ticks before flushing rx fifo
* When using "little data, low latency applications: use 0
#define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5
#endif
-#define _INLINE_ inline
-
static void change_speed(struct e100_serial *info);
static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
static int rs_write(struct tty_struct * tty, int from_user,
static struct e100_serial rs_table[] = {
{ DEF_BAUD, (unsigned char *)R_SERIAL0_CTRL, 1U << 12, /* uses DMA 6 and 7 */
R_DMA_CH6_CLR_INTR, R_DMA_CH6_FIRST, R_DMA_CH6_CMD,
- R_DMA_CH6_STATUS, R_DMA_CH6_HWSW,
+ R_DMA_CH6_STATUS, R_DMA_CH6_HWSW, R_DMA_CH6_DESCR,
R_DMA_CH7_CLR_INTR, R_DMA_CH7_FIRST, R_DMA_CH7_CMD,
- R_DMA_CH7_STATUS, R_DMA_CH7_HWSW,
+ R_DMA_CH7_STATUS, R_DMA_CH7_HWSW, R_DMA_CH7_DESCR,
STD_FLAGS, DEF_RX, DEF_TX, 2,
#ifdef CONFIG_ETRAX_SERIAL_PORT0
1
#ifndef CONFIG_SVINTO_SIM
{ DEF_BAUD, (unsigned char *)R_SERIAL1_CTRL, 1U << 16, /* uses DMA 8 and 9 */
R_DMA_CH8_CLR_INTR, R_DMA_CH8_FIRST, R_DMA_CH8_CMD,
- R_DMA_CH8_STATUS, R_DMA_CH8_HWSW,
+ R_DMA_CH8_STATUS, R_DMA_CH8_HWSW, R_DMA_CH8_DESCR,
R_DMA_CH9_CLR_INTR, R_DMA_CH9_FIRST, R_DMA_CH9_CMD,
- R_DMA_CH9_STATUS, R_DMA_CH9_HWSW,
+ R_DMA_CH9_STATUS, R_DMA_CH9_HWSW, R_DMA_CH9_DESCR,
STD_FLAGS, DEF_RX, DEF_TX, 3 ,
#ifdef CONFIG_ETRAX_SERIAL_PORT1
1
{ DEF_BAUD, (unsigned char *)R_SERIAL2_CTRL, 1U << 4, /* uses DMA 2 and 3 */
R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD,
- R_DMA_CH2_STATUS, R_DMA_CH2_HWSW,
+ R_DMA_CH2_STATUS, R_DMA_CH2_HWSW, R_DMA_CH2_DESCR,
R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD,
- R_DMA_CH3_STATUS, R_DMA_CH3_HWSW,
+ R_DMA_CH3_STATUS, R_DMA_CH3_HWSW, R_DMA_CH3_DESCR,
STD_FLAGS, DEF_RX, DEF_TX, 0,
#ifdef CONFIG_ETRAX_SERIAL_PORT2
1
{ DEF_BAUD, (unsigned char *)R_SERIAL3_CTRL, 1U << 8, /* uses DMA 4 and 5 */
R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD,
- R_DMA_CH4_STATUS, R_DMA_CH4_HWSW,
+ R_DMA_CH4_STATUS, R_DMA_CH4_HWSW, R_DMA_CH4_DESCR,
R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD,
- R_DMA_CH5_STATUS, R_DMA_CH5_HWSW,
+ R_DMA_CH5_STATUS, R_DMA_CH5_HWSW, R_DMA_CH5_DESCR,
STD_FLAGS, DEF_RX, DEF_TX, 1,
#ifdef CONFIG_ETRAX_SERIAL_PORT3
1
static struct termios *serial_termios[NR_PORTS];
static struct termios *serial_termios_locked[NR_PORTS];
-#ifdef CONFIG_SERIAL_PROC_ENTRY
+#ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY
#define PROCSTAT(x) x
-struct ser_statistics_type{
+struct ser_statistics_type {
int overrun_cnt;
int early_errors_cnt;
int ser_ints_ok_cnt;
#define PROCSTAT(x)
-#endif /* CONFIG_SERIAL_PROC_ENTRY */
+#endif /* CONFIG_ETRAX_SERIAL_PROC_ENTRY */
/* RS-485 */
#if defined(CONFIG_ETRAX_RS485)
/* For now we assume that all bits are on the same port for each serial port */
/* Dummy shadow variables */
+#if !defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB)
static unsigned char dummy_ser0 = 0x00;
-static unsigned char dummy_ser1 = 0x00;
-static unsigned char dummy_ser2 = 0x00;
-static unsigned char dummy_ser3 = 0x00;
-
static unsigned char dummy_dir_ser0 = 0x00;
+#endif
+#if !defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB)
+static unsigned char dummy_ser1 = 0x00;
static unsigned char dummy_dir_ser1 = 0x00;
+#endif
+#if !defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA)
+static unsigned char dummy_ser2 = 0x00;
static unsigned char dummy_dir_ser2 = 0x00;
+#endif
+
+static unsigned char dummy_ser3 = 0x00;
static unsigned char dummy_dir_ser3 = 0x00;
/* Info needed for each ports extra control/status signals.
static const struct control_pins e100_modem_pins[NR_PORTS] =
{
-/* Ser 0 */
- {
+ /* Ser 0 */
+ {
#if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB)
- R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
- CONFIG_ETRAX_SER0_DTR_ON_PB_BIT,
- CONFIG_ETRAX_SER0_RI_ON_PB_BIT,
- CONFIG_ETRAX_SER0_DSR_ON_PB_BIT,
- CONFIG_ETRAX_SER0_CD_ON_PB_BIT
+ R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
+ CONFIG_ETRAX_SER0_DTR_ON_PB_BIT,
+ CONFIG_ETRAX_SER0_RI_ON_PB_BIT,
+ CONFIG_ETRAX_SER0_DSR_ON_PB_BIT,
+ CONFIG_ETRAX_SER0_CD_ON_PB_BIT
#else
- &dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3
-#endif
- },
-/* Ser 1 */
- {
+ &dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3
+#endif
+ },
+
+ /* Ser 1 */
+ {
#if defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB)
- R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
- CONFIG_ETRAX_SER1_DTR_ON_PB_BIT,
- CONFIG_ETRAX_SER1_RI_ON_PB_BIT,
- CONFIG_ETRAX_SER1_DSR_ON_PB_BIT,
- CONFIG_ETRAX_SER1_CD_ON_PB_BIT
+ R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
+ CONFIG_ETRAX_SER1_DTR_ON_PB_BIT,
+ CONFIG_ETRAX_SER1_RI_ON_PB_BIT,
+ CONFIG_ETRAX_SER1_DSR_ON_PB_BIT,
+ CONFIG_ETRAX_SER1_CD_ON_PB_BIT
#else
- &dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3
-#endif
- },
-/* Ser 2 */
- {
+ &dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3
+#endif
+ },
+
+ /* Ser 2 */
+ {
#if defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA)
- R_PORT_PA_DATA, &port_pa_data_shadow, &port_pa_dir_shadow,
- CONFIG_ETRAX_SER2_DTR_ON_PA_BIT,
- CONFIG_ETRAX_SER2_RI_ON_PA_BIT,
- CONFIG_ETRAX_SER2_DSR_ON_PA_BIT,
- CONFIG_ETRAX_SER2_CD_ON_PA_BIT
+ R_PORT_PA_DATA, &port_pa_data_shadow, &port_pa_dir_shadow,
+ CONFIG_ETRAX_SER2_DTR_ON_PA_BIT,
+ CONFIG_ETRAX_SER2_RI_ON_PA_BIT,
+ CONFIG_ETRAX_SER2_DSR_ON_PA_BIT,
+ CONFIG_ETRAX_SER2_CD_ON_PA_BIT
#else
- &dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3
-#endif
- },
-/* Ser 3 */
- {
- &dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3
- }
+ &dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3
+#endif
+ },
+
+ /* Ser 3 */
+ {
+ &dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3
+ }
};
#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_RS485_ON_PA)
unsigned char rs485_pa_port = CONFIG_ETRAX_RS485_ON_PA_BIT;
#endif
+
#define E100_RTS_MASK 0x20
#define E100_CTS_MASK 0x40
{
#ifndef CONFIG_SVINTO_SIM
/* disable the receiver */
- info->port[REG_REC_CTRL] = info->rx_ctrl &=
- ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable);
+ info->port[REG_REC_CTRL] =
+ (info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
#endif
}
{
#ifndef CONFIG_SVINTO_SIM
/* enable the receiver */
- info->port[REG_REC_CTRL] = info->rx_ctrl |=
- IO_MASK(R_SERIAL0_REC_CTRL, rec_enable);
+ info->port[REG_REC_CTRL] =
+ (info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
#endif
}
static int
e100_write_rs485(struct tty_struct *tty,struct rs485_write *r)
{
- int stop_delay;
- int total, i;
- int max_j, delay_ms, bits;
- tcflag_t cflags;
- int size = (*r).outc_size;
+ int total;
struct e100_serial * info = (struct e100_serial *)tty->driver_data;
- struct wait_queue wait = { current, NULL };
/* If we are in RS-485 mode, we need to toggle RTS and disable
* the receiver before initiating a DMA transfer
* enable the receiver
*/
- /* wait on transmit shift register */
- /* All is sent, check if we should wait more before toggling rts */
-
- /* calc. number of bits / data byte */
- cflags = info->tty->termios->c_cflag;
-
- /* databits + startbit and 1 stopbit */
- if ((cflags & CSIZE) == CS7)
- bits = 9;
- else
- bits = 10;
-
- if (cflags & CSTOPB) /* 2 stopbits ? */
- bits++;
-
- if (cflags & PARENB) /* parity bit ? */
- bits++;
-
- /* calc timeout */
- delay_ms = ((bits * size * 1000) / info->baud) + 1;
- max_j = jiffies + (delay_ms * HZ)/1000 + 10;
-
- while (jiffies < max_j) {
- if (info->port[REG_STATUS] &
- IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) {
- for (i = 0; i < 100; i++)
- ;
- if (info->port[REG_STATUS] &
- IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) {
- /* ~25 for loops per usec */
- stop_delay = 1000000 / info->baud;
- if (cflags & CSTOPB)
- stop_delay *= 2;
- udelay(stop_delay);
- break;
- }
- }
+ /* Sleep until all sent */
+ tty_wait_until_sent(tty, 0);
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+ /* Now sleep a little more so that shift register is empty */
+ schedule_usleep(info->char_time_usec * 2);
+#else
+ {
+ unsigned int val;
+ /* wait on transmit shift register */
+ do{
+ get_lsr_info(info, &val);
+ }while (!(val & TIOCSER_TEMT));
}
+#endif
e100_rts(info, info->rs485.rts_after_sent);
}
return;
#endif
- /* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */
+ /* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
*info->oclrintradr =
IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
#if defined(CONFIG_ETRAX_RS485)
/* Check if we should toggle RTS now */
- if (info->rs485.enabled)
- {
+ if (info->rs485.enabled) {
/* Make sure fifo is empty */
- int in_fifo = 0 ;
- do{
+ int in_fifo = 0;
+
+ do {
in_fifo = IO_EXTRACT(R_DMA_CH6_STATUS, avail,
- *info->ostatusadr);
- } while (in_fifo > 0) ;
+ *info->ostatusadr);
+ } while (in_fifo > 0);
/* Any way to really check transmitter empty? (TEMT) */
/* Control RTS to set to RX mode */
e100_rts(info, info->rs485.rts_after_sent);
*info->ocmdadr = 1; /* dma command start -> R_DMAx_CMD */
/* DMA is now running (hopefully) */
-
}
static void
transmit_chars(info);
}
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+static int serial_fast_timer_started = 0;
+static int serial_fast_timer_expired = 0;
+static void flush_timeout_function(unsigned long data);
+#define START_FLUSH_FAST_TIMER(info, string) {\
+ unsigned long timer_flags; \
+ save_flags(timer_flags); \
+ cli(); \
+ if (fast_timers[info->line].function == NULL) { \
+ serial_fast_timer_started++; \
+ TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
+ TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \
+ start_one_shot_timer(&fast_timers[info->line], \
+ flush_timeout_function, \
+ (unsigned long)info, \
+ info->char_time_usec*4, \
+ string); \
+ } \
+ else { \
+ TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \
+ } \
+ restore_flags(timer_flags); \
+}
+
+#else
+#define START_FLUSH_FAST_TIMER(info, string)
+#endif
+
+static int
+add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag)
+{
+ if (!CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE))
+ return 0;
+
+ info->recv.buf[info->recv.head] = data;
+ info->flag_buf[info->recv.head] = flag;
+ info->recv.head = (info->recv.head + 1) & (SERIAL_RECV_SIZE - 1);
+
+ info->icount.rx++;
+
+ return 1;
+}
+
+static _INLINE_ unsigned int
+copy_descr_data(struct e100_serial *info, unsigned int recvl, unsigned char *buf)
+{
+ unsigned int count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE);
+ unsigned int length = 0;
+
+ while (length < recvl && count) {
+ if (length + count > recvl)
+ count = recvl - length;
+
+ memcpy(info->recv.buf + info->recv.head, buf + length, count);
+ memset(info->flag_buf + info->recv.head, '\0', count);
+ info->recv.head = (info->recv.head + count) & (SERIAL_RECV_SIZE - 1);
+ length += count;
+
+ count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE);
+ }
+
+ if (length != recvl) {
+ printk(__FUNCTION__ ": Buffer overflow! %d byte(s) did not fit.\n", recvl - length);
+ PROCSTAT(ser_stat[info->line].overrun_cnt += recvl - length);
+ }
+
+ return length;
+}
+
+static _INLINE_ unsigned int
+copy_all_descr_data(struct e100_serial *info)
+{
+ struct etrax_dma_descr *descr;
+ unsigned int recvl;
+ unsigned int ret = 0;
+
+ while (1)
+ {
+ descr = &info->rec_descr[info->cur_rec_descr];
+
+ if (descr == phys_to_virt(*info->idescradr))
+ break;
+
+ if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS)
+ info->cur_rec_descr = 0;
+
+ /* find out how many bytes were read */
+
+ /* if the eop bit was not set, all data has been received */
+ if (!(descr->status & d_eop)) {
+ recvl = descr->sw_len;
+ } else {
+ /* otherwise we find the amount of data received here */
+ recvl = descr->hw_len;
+ }
+
+ /* Reset the status information */
+ descr->status = 0;
+
+ DEBUG_LOG(info->line, "recvl %lu\n", recvl);
+
+ /* update stats */
+ info->icount.rx += recvl;
+
+ ret += copy_descr_data(info, recvl, phys_to_virt(descr->buf));
+ }
+
+ return ret;
+}
+
static _INLINE_ void
receive_chars(struct e100_serial *info)
{
struct tty_struct *tty;
unsigned char rstat;
- unsigned int recvl;
- struct etrax_dma_descr *descr;
+ unsigned int old_head;
#ifdef CONFIG_SVINTO_SIM
/* No receive in the simulator. Will probably be when the rest of
return;
#endif
- tty = info->tty;
-
- /* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */
-
- // ?
+ /* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
*info->iclrintradr =
IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
- if (!tty) /* something wrong... */
+ tty = info->tty;
+ if (!tty) /* Something wrong... */
return;
- descr = &info->rec_descr;
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+ e100_enable_serial_data_irq(info);
+#endif
- /* find out how many bytes were read */
+ if (info->errorcode == ERRCODE_INSERT_BREAK)
+ add_char_and_flag(info, '\0', TTY_BREAK);
- /* if the eop bit was not set, all data has been received */
- if (!(descr->status & d_eop)) {
- recvl = descr->sw_len;
- } else {
- /* otherwise we find the amount of data received here */
- recvl = descr->hw_len;
- }
+ old_head = info->recv.head;
+
+ if (copy_all_descr_data(info) && info->errorcode == ERRCODE_SET_BREAK)
+ info->flag_buf[old_head] = TTY_BREAK;
- /* read the status register so we can detect errors,
- * but we can't really do anything about those errors
- * anyway, since we have the DMA in "force eop at error" mode
- * the fault characters are not in the buffer anyway.
- */
+ info->errorcode = 0;
+ /* Read the status register to detect errors */
rstat = info->port[REG_STATUS];
- if ((rstat & SER_ERROR_MASK) != 0) {
- unsigned char data;
- /* if we got an error, we must reset it by reading the
+ if (rstat & SER_ERROR_MASK) {
+ /* If we got an error, we must reset it by reading the
* data_in field
*/
- data = info->port[REG_DATA];
+ unsigned char data = info->port[REG_DATA];
+
PROCSTAT(ser_stat[info->line].errors_cnt++);
- DEBUG_LOG(info->line, " #dERR: s d 0x%04X\n",
+ DEBUG_LOG(info->line, "#dERR: s d 0x%04X\n",
((rstat & SER_ERROR_MASK) << 8) | data);
- /* Only handle the saved error code, that indicates that we got
- * the last character of a break that looks like it's ok, but
- * is not
- */
- if (info->errorcode == 0) {
- *tty->flip.flag_buf_ptr = TTY_NORMAL;
- } else {
- unsigned char data;
- data = info->port[REG_DATA];
- if (info->errorcode & ERRCODE_INSERT) {
- unsigned char *currbuf;
- /* Get the current buffer */
- if (tty->flip.buf_num) {
- currbuf = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
- } else {
- currbuf = tty->flip.char_buf;
- }
- /* We should insert a character in the buffer! */
- if (recvl == 0) {
- recvl = 1;
- DEBUG_LOG(info->line, "insert to %lu\n", recvl);
- } else {
- /* Move stuff around.. */
- DEBUG_LOG(info->line, "#insert to %lu!\n", recvl);
- if (recvl < TTY_FLIPBUF_SIZE) {
- int i;
- /* Move the data 1 step right */
- i = recvl;
- while (i) {
- currbuf[i] = currbuf[i-1];
- i--;
- }
- recvl++;
- } else {
- /* We can't move it all! Skip break! */
- /* TODO: Handle full buffer? */
- DEBUG_LOG(info->line, "#BRK skipped! %lu!\n", recvl);
- info->errorcode = 0;
- }
- }
- }
-
- PROCSTAT(ser_stat[info->line].errors_cnt++);
- DEBUG_LOG(info->line, " #bERR: s d 0x%04X\n",
- ((rstat & SER_ERROR_MASK) << 8) | data);
- *tty->flip.flag_buf_ptr = (info->errorcode & 0xFF);
- info->errorcode = 0;
-#if 0
- printk("SERERR: 0x%02X data: 0x%02X\n", rstat & SER_ERROR_MASK, data);
-#endif
- /* we only ever write errors into the first byte in
- * the flip flag buffer, so we dont have to clear it
- * all every time
- */
- }
+ if (rstat & SER_PAR_ERR_MASK)
+ add_char_and_flag(info, data, TTY_PARITY);
+ else if (rstat & SER_OVERRUN_MASK)
+ add_char_and_flag(info, data, TTY_OVERRUN);
+ else if (rstat & SER_FRAMING_ERR_MASK)
+ add_char_and_flag(info, data, TTY_FRAME);
}
- DEBUG_LOG(info->line, "recvl %lu\n", recvl);
-
- if (recvl) {
- unsigned char *buf;
- struct async_icount *icount = &info->icount;
-
- /* update stats */
- icount->rx += recvl;
-
- /* use the flip buffer next in turn to restart DMA into */
-
- if (tty->flip.buf_num) {
- buf = tty->flip.char_buf;
- } else {
- buf = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
- }
+ if (!E100_RTS_GET(info) &&
+ CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE) < TTY_THROTTLE_LIMIT)
+ info->tty->driver.throttle(info->tty);
+
+ START_FLUSH_FAST_TIMER(info, "receive_chars");
- if (buf == phys_to_virt(descr->buf)) {
- printk("ttyS%d flip-buffer overrun!\n", info->line);
- icount->overrun++;
- *tty->flip.flag_buf_ptr = TTY_OVERRUN;
- /* restart old buffer */
- } else {
- descr->buf = virt_to_phys(buf);
+ /* Restart the receiving DMA */
+ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
+}
- /* schedule or push a flip of the buffer */
+static _INLINE_ int
+start_recv_dma(struct e100_serial *info)
+{
+ struct etrax_dma_descr *descr = info->rec_descr;
+ unsigned char *buf = info->recv.buf + 2*SERIAL_RECV_SIZE;
+ int i;
- info->tty->flip.count = recvl;
+ /* Set up the receiving descriptors */
+ for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
+ descr[i].ctrl = d_int;
+ descr[i].buf = virt_to_phys(buf);
+ descr[i].sw_len = SERIAL_DESCR_BUF_SIZE;
+ descr[i].hw_len = 0;
+ descr[i].status = 0;
+ descr[i].next = virt_to_phys(&descr[i+1]);
-#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */
- /* this includes a check for low-latency */
- tty_flip_buffer_push(tty);
-#else
- queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-#endif
- }
+ buf += SERIAL_DESCR_BUF_SIZE;
}
- /* restart the receiving dma */
+ /* Link the last descriptor to the first */
+ descr[i-1].next = virt_to_phys(&descr[0]);
- descr->sw_len = TTY_FLIPBUF_SIZE;
- descr->ctrl = d_int | d_eol | d_eop;
- descr->hw_len = 0;
- descr->status = 0;
+ /* Start with the first descriptor in the list */
+ info->cur_rec_descr = 0;
- *info->ifirstadr = virt_to_phys(descr);
+ /* Start the DMA */
+ *info->ifirstadr = virt_to_phys(&descr[info->cur_rec_descr]);
*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
- e100_enable_serial_data_irq(info);
-#endif
- /* input dma should be running now */
-
- /* unthrottle if we have throttled */
- if (E100_RTS_GET(info))
- tty->driver.unthrottle(info->tty);
+ /* Input DMA should be running now */
+ return 1;
}
static void
start_receive(struct e100_serial *info)
{
- struct etrax_dma_descr *descr;
-
#ifdef CONFIG_SVINTO_SIM
/* No receive in the simulator. Will probably be when the rest of
* the serial interface works, and this piece will just be removed.
while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
- descr = &info->rec_descr;
-
- /* start the receiving dma into the flip buffer */
-
- descr->ctrl = d_int | d_eol | d_eop;
- descr->sw_len = TTY_FLIPBUF_SIZE;
- descr->buf = virt_to_phys(info->tty->flip.char_buf_ptr);
- descr->hw_len = 0;
- descr->status = 0;
-
info->tty->flip.count = 0;
- *info->ifirstadr = virt_to_phys(descr);
- *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
-
+ start_recv_dma(info);
+
#ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
start_flush_timer();
#endif
info = rs_table + i;
if (!info->uses_dma)
continue;
- /* check for dma_descr (dont need to check for dma_eop in output dma for serial */
+ /* check for dma_descr (don't need to check for dma_eop in output dma for serial */
if (ireg & info->irq) {
/* we can send a new dma bunch. make it so. */
+ DEBUG_LOG(info->line, "tr_interrupt %i\n", i);
+ /* Read jiffies_usec first,
+ * we want this time to be as late as possible
+ */
+ PROCSTAT(ser_stat[info->line].tx_dma_ints++);
+ info->last_tx_active_usec = GET_JIFFIES_USEC();
+ info->last_tx_active = jiffies;
transmit_chars(info);
}
}
}
-#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
-static int serial_fast_timer_started = 0;
-static int serial_fast_timer_expired = 0;
-static void flush_timeout_function(unsigned long data);
-#define START_FLUSH_FAST_TIMER(info, string) {\
- unsigned long timer_flags; \
- save_flags(timer_flags); \
- cli(); \
- TIMERD(DEBUG_LOG(info->line, "start_timer? %i ", info->line)); \
- if (fast_timers[info->line].function == NULL) { \
- serial_fast_timer_started++; \
- TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
- TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \
- start_one_shot_timer(&fast_timers[info->line], \
- flush_timeout_function, \
- (unsigned long)info, \
- info->char_time_usec*4, \
- string); \
- } \
- else { \
- /* DEBUG_LOG(info->line, " ## timer %i running ##\n", info->line); */ \
- } \
- restore_flags(timer_flags); \
-}
-
-#else
-#define START_FLUSH_FAST_TIMER(info, string)
-#endif
-
-void _INLINE_ check_flush_timeout(struct e100_serial *info)
+static _INLINE_ int
+force_eop_if_needed(struct e100_serial *info)
{
- unsigned char rstat;
- unsigned int magic;
-
- if (0 /*info->tty->processing_flip*/) {
- if (!E100_RTS_GET(info)) {
- int left = (*info->ihwswadr >> 16) - (*info->istatusadr & 0x3F);
-
- if (left < TTY_THROTTLE_LIMIT)
- info->tty->driver.throttle(info->tty);
- }
-
- PROCSTAT(ser_stat[info->line].processing_flip++);
- START_FLUSH_FAST_TIMER(info, "flip");
- return;
- }
-
/* We check data_avail bit to determine if data has
* arrived since last time
*/
- magic = info->fifo_magic;
-#ifdef SERIAL_DEBUG_DATA
- if (info->fifo_magic || info->fifo_didmagic) {
- DEBUG_LOG(info->line, "timeout_int: did fifo_magic %03X\n",
- (info->fifo_didmagic << 8) | info->fifo_magic);
- }
-#endif
- rstat = info->port[REG_STATUS];
+ unsigned char rstat = info->port[REG_STATUS];
+
/* error or datavail? */
if (rstat & SER_ERROR_MASK) {
- /* Some error has occured */
- /* If there has been valid data,
- * an EOP interrupt will be made automatically.
- * If no data, the normal ser_interrupt should be enabled
- * and handle it.
+ /* Some error has occurred. If there has been valid data, an
+ * EOP interrupt will be made automatically. If no data, the
+ * normal ser_interrupt should be enabled and handle it.
* So do nothing!
*/
DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n",
rstat | (info->line << 8));
- return;
+ return 0;
}
+
if (rstat & SER_DATA_AVAIL_MASK) {
/* Ok data, no error, count it */
TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n",
/* Read data to clear status flags */
(void)info->port[REG_DATA];
- magic++;
+ info->forced_eop = 0;
+ START_FLUSH_FAST_TIMER(info, "magic");
+ return 0;
}
- if (magic != info->fifo_magic) {
- info->fifo_magic = magic;
- info->fifo_didmagic = 0;
- START_FLUSH_FAST_TIMER(info, "magic");
- } else {
- /* hit the timeout, force an EOP for the input
- * dma channel if we haven't already
- */
- if (!info->fifo_didmagic && magic) {
- info->fifo_didmagic = 1;
- info->fifo_magic = 0;
- PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
- DEBUG_LOG(info->line, "timeout EOP %i\n", info->line);
- TIMERD(DEBUG_LOG(info->line, "timeout magic %i\n", magic));
- FORCE_EOP(info);
- }
+ /* hit the timeout, force an EOP for the input
+ * dma channel if we haven't already
+ */
+ if (!info->forced_eop) {
+ info->forced_eop = 1;
+ PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
+ DEBUG_LOG(info->line, "timeout EOP %i\n", info->line);
+ FORCE_EOP(info);
}
-} /* check_flush_timeout */
+
+ return 1;
+}
+
+static _INLINE_ void
+flush_to_flip_buffer(struct e100_serial *info)
+{
+ struct tty_struct *tty = info->tty;
+ unsigned int count = CIRC_CNT_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE);
+ unsigned int length;
+ unsigned long flags;
+
+ if (!count)
+ return;
+
+ save_flags(flags);
+ cli();
+
+ length = tty->flip.count;
+
+ do {
+ if (length + count > TTY_FLIPBUF_SIZE)
+ count = TTY_FLIPBUF_SIZE - length;
+
+ memcpy(tty->flip.char_buf_ptr + length, info->recv.buf + info->recv.tail, count);
+ memcpy(tty->flip.flag_buf_ptr + length, info->flag_buf + info->recv.tail, count);
+ info->recv.tail = ((info->recv.tail + count) & (SERIAL_RECV_SIZE-1));
+ length += count;
+
+ count = CIRC_CNT_TO_END(info->recv.head,
+ info->recv.tail,
+ SERIAL_RECV_SIZE);
+ } while (length < TTY_FLIPBUF_SIZE && count);
+
+ tty->flip.count = length;
+
+ restore_flags(flags);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,66)
+ /* this includes a check for low-latency */
+ tty_flip_buffer_push(tty);
+#else
+ queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+#endif
+
+ /* unthrottle if we have throttled */
+ if (E100_RTS_GET(info) &&
+ CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE) > TTY_THROTTLE_LIMIT)
+ tty->driver.unthrottle(info->tty);
+}
+
+static _INLINE_ void
+check_flush_timeout(struct e100_serial *info)
+{
+ force_eop_if_needed(info);
+
+ flush_to_flip_buffer(info);
+
+ if (CIRC_CNT(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE))
+ START_FLUSH_FAST_TIMER(info, "flip");
+}
#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
static void flush_timeout_function(unsigned long data)
{
struct e100_serial *info = (struct e100_serial *)data;
+
fast_timers[info->line].function = NULL;
serial_fast_timer_expired++;
TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line));
{
struct e100_serial *info;
int i;
- unsigned int magic;
#ifdef CONFIG_SVINTO_SIM
return;
for (i = 0; i < NR_PORTS; i++) {
info = rs_table + i;
- if (!info->enabled || !(info->flags & ASYNC_INITIALIZED))
- continue;
-
- /* istatusadr (bit 6-0) hold number of bytes in fifo
- * ihwswadr (bit 31-16) holds number of bytes in dma buffer
- * ihwswadr (bit 15-0) specifies size of dma buffer
- */
-
- magic = (*info->istatusadr & 0x3f);
- magic += ((*info->ihwswadr & 0xffff) - (*info->ihwswadr >> 16));
-
- /* if magic is equal to fifo_magic (magic in previous
- * timeout_interrupt) then no new data has arrived since last
- * interrupt and we'll force eop to flush fifo+dma buffers
- */
-
- if (magic != info->fifo_magic) {
- info->fifo_magic = magic;
- info->fifo_didmagic = 0;
- } else {
- /* hit the timeout, force an EOP for the input
- * dma channel if we haven't already
- */
- if (!info->fifo_didmagic && magic) {
- info->fifo_didmagic = 1;
- info->fifo_magic = 0;
- FORCE_EOP(info);
- }
- }
+ if (info->uses_dma)
+ check_flush_timeout(info);
}
/* restart flush timer */
Multiple frame errors with data == 0x00 (B),
but the part of the break trigs is interpreted as a start bit (and possibly
-som 0 bits followed by a number of 1 bits and a stop bit).
+some 0 bits followed by a number of 1 bits and a stop bit).
Depending on parity settings etc. this last character can be either
a fake "valid" char (F) or have a parity error (E).
/* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
if (rstat & SER_ERROR_MASK) {
unsigned char data;
+
info->last_rx_active_usec = GET_JIFFIES_USEC();
info->last_rx_active = jiffies;
- /* if we got an error, we must reset it by
- * reading the data_in field
+ /* If we got an error, we must reset it by reading the
+ * data_in field
*/
data = info->port[REG_DATA];
- if ((data == 0x00) && (rstat & SER_FRAMING_ERR_MASK)) {
- /* Most likely a break, but we get
- * interrupts over and over again.
+ if (!data && (rstat & SER_FRAMING_ERR_MASK)) {
+ /* Most likely a break, but we get interrupts over and
+ * over again.
*/
- if (info->break_detected_cnt == 0) {
+ if (!info->break_detected_cnt) {
DEBUG_LOG(info->line, "#BRK start\n", 0);
}
if (rstat & SER_RXD_MASK) {
}
info->break_detected_cnt++;
} else {
- /* Error doesn't look like a break,
- * but could be end of a break
+ /* The error does not look like a break, but could be
+ * the end of one
*/
if (info->break_detected_cnt) {
DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
info->errorcode = ERRCODE_INSERT_BREAK;
+ } else {
+ if (info->errorcode == ERRCODE_INSERT_BREAK)
+ add_char_and_flag(info, '\0', TTY_BREAK);
+
+ if (rstat & SER_PAR_ERR_MASK)
+ add_char_and_flag(info, data, TTY_PARITY);
+ else if (rstat & SER_OVERRUN_MASK)
+ add_char_and_flag(info, data, TTY_OVERRUN);
+ else if (rstat & SER_FRAMING_ERR_MASK)
+ add_char_and_flag(info, data, TTY_FRAME);
+ info->errorcode = 0;
}
info->break_detected_cnt = 0;
DEBUG_LOG(info->line, "#iERR s d %04X\n",
((rstat & SER_ERROR_MASK) << 8) | data);
}
PROCSTAT(ser_stat[info->line].early_errors_cnt++);
-
-#if 0
- /* Reset DMA before starting */
- *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
- while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
- IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-#endif
- } else { /* it was a valid byte, now let the dma do the rest */
- unsigned char data;
+ } else { /* It was a valid byte, now let the DMA do the rest */
unsigned long curr_time_u = GET_JIFFIES_USEC();
unsigned long curr_time = jiffies;
if (info->break_detected_cnt) {
- /* Detect if this character is a new
- * valid char or the last char in a
- * break sequence:
- * If LSBits are 0 and MSBits are high
- * AND the time is close to the
- * previous interrupt we should discard
- * it.
+ /* Detect if this character is a new valid char or the
+ * last char in a break sequence: If LSBits are 0 and
+ * MSBits are high AND the time is close to the
+ * previous interrupt we should discard it.
*/
long elapsed_usec =
- (curr_time - info->last_rx_active) * (1000000/HZ) +
- curr_time_u - info->last_rx_active_usec;
- if (elapsed_usec<2*info->char_time_usec) {
+ (curr_time - info->last_rx_active) * (1000000/HZ) +
+ curr_time_u - info->last_rx_active_usec;
+ if (elapsed_usec < 2*info->char_time_usec) {
DEBUG_LOG(info->line, "FBRK %i\n", info->line);
- /* Report as BREAK (error) and
- * let receive_chars handle it
+ /* Report as BREAK (error) and let
+ * receive_chars() handle it
*/
info->errorcode = ERRCODE_SET_BREAK;
} else {
}
DEBUG_LOG(info->line, "num brk %i\n", info->break_detected_cnt);
}
- /* Reset data_avail by
- * reading the data_in field
- */
- data = info->port[REG_DATA];
- info->break_detected_cnt = 0;
- info->fifo_magic++; /* Count received chars */
+
#ifdef SERIAL_DEBUG_INTR
printk("** OK, disabling ser_interupts\n");
#endif
- PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
- DEBUG_LOG(info->line, " ser_int OK %03X\n",
- (info->line << 8) | data);
e100_disable_serial_data_irq(info);
+
+ info->break_detected_cnt = 0;
+
+ PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
+ DEBUG_LOG(info->line, "ser_int OK %d\n", info->line);
}
- /* restart the DMA never hurts */
+ /* Restarting the DMA never hurts */
*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
START_FLUSH_FAST_TIMER(info, "ser_int");
} /* handle_ser_interrupt */
{
struct e100_serial *info = (struct e100_serial *) private_;
struct tty_struct *tty;
-
+
tty = info->tty;
if (!tty)
return;
startup(struct e100_serial * info)
{
unsigned long flags;
- unsigned long page;
+ unsigned long xmit_page;
+ unsigned char *recv_page;
+
+ xmit_page = get_zeroed_page(GFP_KERNEL);
+ if (!xmit_page)
+ return -ENOMEM;
- page = get_zeroed_page(GFP_KERNEL);
- if (!page)
+ recv_page = kmalloc(2 * SERIAL_RECV_SIZE + SERIAL_RECV_DESCRIPTORS * SERIAL_DESCR_BUF_SIZE, GFP_KERNEL);
+ if (!recv_page) {
+ free_page(xmit_page);
return -ENOMEM;
+ }
save_flags(flags); cli();
/* if it was already initialized, skip this */
-
+
if (info->flags & ASYNC_INITIALIZED) {
- free_page(page);
restore_flags(flags);
+ free_page(xmit_page);
+ kfree(recv_page);
return 0;
}
-
+
if (info->xmit.buf)
- free_page(page);
+ free_page(xmit_page);
else
- info->xmit.buf = (unsigned char *) page;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("starting up ttyS%d (xmit_buf 0x%x)...\n", info->line, info->xmit.buf);
-#endif
-
- if (info->tty) {
-
- /* clear the tty flip flag buffer since we will not
- * be using it (we only use the first byte..)
- */
+ info->xmit.buf = (unsigned char *) xmit_page;
- memset(info->tty->flip.flag_buf, 0, TTY_FLIPBUF_SIZE * 2);
+ if (info->recv.buf)
+ kfree(recv_page);
+ else {
+ info->recv.buf = (unsigned char *) recv_page;
+ info->flag_buf = info->recv.buf + SERIAL_RECV_SIZE;
}
- save_flags(flags);
- cli();
-
+#ifdef SERIAL_DEBUG_OPEN
+ printk("starting up ttyS%d (xmit_buf 0x%p, recv_buf 0x%p)...\n", info->line, info->xmit.buf, info->recv.buf);
+#endif
+
#ifdef CONFIG_SVINTO_SIM
/* Bits and pieces collected from below. Better to have them
in one ifdef:ed clause than to mix in a lot of ifdefs,
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
info->xmit.head = info->xmit.tail = 0;
-
+ info->recv.head = info->recv.tail = 0;
+
/* No real action in the simulator, but may set info important
to ioctl. */
change_speed(info);
* Clear the FIFO buffers and disable them
* (they will be reenabled in change_speed())
*/
-
+
/*
* Reset the DMA channels and make sure their interrupts are cleared
*/
-
+
info->uses_dma = 1;
*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
- /* wait until reset cycle is complete */
+ /* Wait until reset cycle is complete */
while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-
+
+ /* Make sure the irqs are cleared */
*info->iclrintradr =
IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
*info->oclrintradr =
IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
-
+
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
- info->xmit.head = info->xmit.tail = 0;
+ info->xmit.head = info->xmit.tail = 0;
+ info->recv.head = info->recv.tail = 0;
/*
* and set the speed and other flags of the serial port
e100_enable_txdma_irq(info);
e100_enable_rxdma_irq(info);
- info->tr_running = 0; /* to be sure we dont lock up the transmitter */
+ info->tr_running = 0; /* to be sure we don't lock up the transmitter */
/* setup the dma input descriptor and start dma */
unsigned long flags;
#ifndef CONFIG_SVINTO_SIM
- /* shut down the transmitter and receiver */
+ /* shut down the transmitter and receiver */
e100_disable_rx(info);
info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40);
cli(); /* Disable interrupts */
if (info->xmit.buf) {
- unsigned long pg = (unsigned long) info->xmit.buf;
- info->xmit.buf = 0;
- free_page(pg);
+ free_page((unsigned long)info->xmit.buf);
+ info->xmit.buf = NULL;
+ }
+
+ if (info->recv.buf) {
+ kfree(info->recv.buf);
+ info->recv.buf = NULL;
+ info->flag_buf = NULL;
}
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
struct e100_serial *info = (struct e100_serial *)tty->driver_data;
unsigned long flags;
- if (info->tr_running
- || info->xmit.head == info->xmit.tail
- || tty->stopped
- || tty->hw_stopped
- || !info->xmit.buf)
+ if (info->tr_running ||
+ info->xmit.head == info->xmit.tail ||
+ tty->stopped ||
+ tty->hw_stopped ||
+ !info->xmit.buf)
return;
#ifdef SERIAL_DEBUG_FLOW
#ifdef CONFIG_SVINTO_SIM
/* Really simple. The output is here and now. */
SIMCOUT(buf, count);
- return;
+ return count;
#endif
save_flags(flags);
* the IRQ's are not running anyway for this port.
*/
- if (info->xmit.head != info->xmit.tail
- && !tty->stopped &&
+ if (info->xmit.head != info->xmit.tail &&
+ !tty->stopped &&
!tty->hw_stopped &&
!info->tr_running) {
start_transmit(info);
tmp.flags = info->flags;
tmp.close_delay = info->close_delay;
tmp.closing_wait = info->closing_wait;
- if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
}
static int
-set_serial_info(struct e100_serial * info,
- struct serial_struct * new_info)
+set_serial_info(struct e100_serial *info,
+ struct serial_struct *new_info)
{
struct serial_struct new_serial;
struct e100_serial old_info;
- int retval = 0;
+ int retval = 0;
- if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+ if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
return -EFAULT;
old_info = *info;
char *get_control_state_str(int MLines, char *s)
{
int i = 0;
+
s[0]='\0';
while (control_state_str[i].str != NULL) {
if (MLines & control_state_str[i].state) {
rs_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
- int error;
struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+#if defined(CONFIG_ETRAX_RS485) || (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
+ int error;
+#endif
+#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
int retval;
+#endif
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
struct e100_serial *info)
{
DECLARE_WAITQUEUE(wait, current);
- unsigned long flags;
+ unsigned long flags;
int retval;
int do_clocal = 0, extra_count = 0;
}
/*
- * If the port is the middle of closing, bail out now
+ * If the port is in the middle of closing, bail out now
*/
if (tty_hung_up_p(filp) ||
(info->flags & ASYNC_CLOSING)) {
static inline int line_info(char *buf, struct e100_serial *info)
{
- char stat_buf[30], control, status;
+ char stat_buf[30];
int ret;
- unsigned long flags;
ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d",
- info->line, info->port, info->irq);
+ info->line, (unsigned long)info->port, info->irq);
if (!info->port || (info->type == PORT_UNKNOWN)) {
ret += sprintf(buf+ret, "\n");
ret += sprintf(buf+ret, " baud:%d", info->baud);
ret += sprintf(buf+ret, " tx:%lu rx:%lu",
- info->icount.tx, info->icount.rx);
+ (unsigned long)info->icount.tx,
+ (unsigned long)info->icount.rx);
if (info->icount.frame)
- ret += sprintf(buf+ret, " fe:%lu", info->icount.frame);
+ ret += sprintf(buf+ret, " fe:%lu",
+ (unsigned long)info->icount.frame);
if (info->icount.parity)
- ret += sprintf(buf+ret, " pe:%lu", info->icount.parity);
+ ret += sprintf(buf+ret, " pe:%lu",
+ (unsigned long)info->icount.parity);
if (info->icount.brk)
- ret += sprintf(buf+ret, " brk:%lu", info->icount.brk);
+ ret += sprintf(buf+ret, " brk:%lu",
+ (unsigned long)info->icount.brk);
if (info->icount.overrun)
- ret += sprintf(buf+ret, " oe:%lu", info->icount.overrun);
+ ret += sprintf(buf+ret, " oe:%lu",
+ (unsigned long)info->icount.overrun);
/*
* Last thing is the RS-232 status lines
info->tty = 0;
info->type = PORT_ETRAX;
info->tr_running = 0;
- info->fifo_magic = 0;
- info->fifo_didmagic = 0;
+ info->forced_eop = 0;
info->flags = 0;
info->close_delay = 5*HZ/10;
info->closing_wait = 30*HZ;
info->normal_termios = serial_driver.init_termios;
init_waitqueue_head(&info->open_wait);
init_waitqueue_head(&info->close_wait);
- info->xmit.buf = 0;
+ info->xmit.buf = NULL;
info->xmit.tail = info->xmit.head = 0;
+ info->recv.buf = NULL;
+ info->recv.tail = info->recv.head = 0;
+ info->flag_buf = NULL;
+ info->last_tx_active_usec = 0;
+ info->last_tx_active = 0;
+
+#if defined(CONFIG_ETRAX_RS485)
+ /* Set sane defaults */
+ info->rs485.rts_on_send = 0;
+ info->rs485.rts_after_sent = 1;
+ info->rs485.delay_rts_before_send = 0;
+ info->rs485.enabled = 0;
+#endif
if (info->enabled) {
printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n",
* For definitions of the flags field, see tty.h
*/
+#define SERIAL_RECV_DESCRIPTORS 8
+
struct e100_serial {
- int baud;
- volatile u8 *port; /* R_SERIALx_CTRL */
- u32 irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */
-
- volatile u8 *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR, output */
- volatile u32 *ofirstadr; /* adr to R_DMA_CHx_FIRST, output */
- volatile u8 *ocmdadr; /* adr to R_DMA_CHx_CMD, output */
- const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS, output */
- volatile u32 *ohwswadr; /* adr to R_DMA_CHx_HWSW, output */
-
- volatile u8 *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR, input */
- volatile u32 *ifirstadr; /* adr to R_DMA_CHx_FIRST, input */
- volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD, input */
- const volatile u8 *istatusadr; /* adr to R_DMA_CHx_STATUS, input */
- volatile u32 *ihwswadr; /* adr to R_DMA_CHx_HWSW, input */
-
- int flags; /* defined in tty.h */
-
- u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
- u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
- u8 iseteop; /* bit number for R_SET_EOP for the input dma */
- int enabled; /* Set to 1 if the port is enabled in HW config */
-
+ int baud;
+ volatile u8 *port; /* R_SERIALx_CTRL */
+ u32 irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */
+
+ /* Output registers */
+ volatile u8 *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
+ volatile u32 *ofirstadr; /* adr to R_DMA_CHx_FIRST */
+ volatile u8 *ocmdadr; /* adr to R_DMA_CHx_CMD */
+ const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS */
+ volatile u32 *ohwswadr; /* adr to R_DMA_CHx_HWSW */
+ volatile u32 *odescradr; /* adr to R_DMA_CHx_DESCR */
+
+ /* Input registers */
+ volatile u8 *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
+ volatile u32 *ifirstadr; /* adr to R_DMA_CHx_FIRST */
+ volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD */
+ const volatile u8 *istatusadr; /* adr to R_DMA_CHx_STATUS */
+ volatile u32 *ihwswadr; /* adr to R_DMA_CHx_HWSW */
+ volatile u32 *idescradr; /* adr to R_DMA_CHx_DESCR */
+
+ int flags; /* defined in tty.h */
+
+ u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
+ u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
+ u8 iseteop; /* bit number for R_SET_EOP for the input dma */
+
+ int enabled; /* Set to 1 if the port is enabled in HW config */
-/* end of fields defined in rs_table[] in .c-file */
- int uses_dma; /* Set to 1 if DMA should be used */
- unsigned char fifo_didmagic; /* a fifo eop has been forced */
+ /* end of fields defined in rs_table[] in .c-file */
- struct etrax_dma_descr tr_descr, rec_descr;
+ int uses_dma; /* Set to 1 if DMA should be used */
+ unsigned char forced_eop; /* a fifo eop has been forced */
- int fifo_magic; /* fifo amount - bytes left in dma buffer */
+ struct etrax_dma_descr tr_descr;
+ struct etrax_dma_descr rec_descr[SERIAL_RECV_DESCRIPTORS];
+ int cur_rec_descr;
- volatile int tr_running; /* 1 if output is running */
+ volatile int tr_running; /* 1 if output is running */
- struct tty_struct *tty;
+ struct tty_struct *tty;
int read_status_mask;
int ignore_status_mask;
int x_char; /* xon/xoff character */
int close_delay;
- unsigned short closing_wait;
- unsigned short closing_wait2;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
unsigned long event;
unsigned long last_active;
int line;
- int type; /* PORT_ETRAX */
+ int type; /* PORT_ETRAX */
int count; /* # of fd on device */
int blocked_open; /* # of blocked opens */
long session; /* Session of opening process */
long pgrp; /* pgrp of opening process */
- struct circ_buf xmit;
+ struct circ_buf xmit;
+ struct circ_buf recv;
+ unsigned char *flag_buf;
struct tq_struct tqueue;
- struct async_icount icount; /* error-statistics etc.*/
- struct termios normal_termios;
- struct termios callout_termios;
+ struct async_icount icount; /* error-statistics etc.*/
+ struct termios normal_termios;
+ struct termios callout_termios;
#ifdef DECLARE_WAITQUEUE
- wait_queue_head_t open_wait;
- wait_queue_head_t close_wait;
-#else
- struct wait_queue *open_wait;
- struct wait_queue *close_wait;
+ wait_queue_head_t open_wait;
+ wait_queue_head_t close_wait;
+#else
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
#endif
- unsigned long char_time_usec; /* The time for 1 char, in usecs */
- unsigned long last_tx_active_usec; /* Last tx usec in the jiffies */
- unsigned long last_tx_active; /* Last tx time in jiffies */
- unsigned long last_rx_active_usec; /* Last rx usec in the jiffies */
- unsigned long last_rx_active; /* Last rx time in jiffies */
+ unsigned long char_time_usec; /* The time for 1 char, in usecs */
+ unsigned long last_tx_active_usec; /* Last tx usec in the jiffies */
+ unsigned long last_tx_active; /* Last tx time in jiffies */
+ unsigned long last_rx_active_usec; /* Last rx usec in the jiffies */
+ unsigned long last_rx_active; /* Last rx time in jiffies */
- int break_detected_cnt;
- int errorcode;
+ int break_detected_cnt;
+ int errorcode;
#ifdef CONFIG_RS485
- struct rs485_control rs485; /* RS-485 support */
+ struct rs485_control rs485; /* RS-485 support */
#endif
};
#endif /* __KERNEL__ */
-#endif /* !(_ETRAX_SERIAL_H) */
+#endif /* !_ETRAX_SERIAL_H */
{
port->out_descr.hw_len = 0;
port->out_descr.next = 0;
- port->out_descr.ctrl = d_int | d_eol | d_eop;
+ port->out_descr.ctrl = d_int | d_eol | d_eop | d_wait;
port->out_descr.sw_len = count;
port->out_descr.buf = virt_to_phys(port->out_buffer);
port->out_descr.status = 0;
static USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
static USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
-static urb_t *URB_List[NBR_OF_EP_DESC];
+static struct urb *URB_List[NBR_OF_EP_DESC];
static kmem_cache_t *usb_desc_cache;
static struct usb_bus *etrax_usb_bus;
static void dump_urb (struct urb *urb);
static void init_rx_buffers(void);
-static int etrax_rh_unlink_urb (urb_t *urb);
-static void etrax_rh_send_irq(urb_t *urb);
-static void etrax_rh_init_int_timer(urb_t *urb);
+static int etrax_rh_unlink_urb (struct urb *urb);
+static void etrax_rh_send_irq(struct urb *urb);
+static void etrax_rh_init_int_timer(struct urb *urb);
static void etrax_rh_int_timer_do(unsigned long ptr);
static void etrax_usb_setup_epid(char epid, char devnum, char endpoint,
static void etrax_usb_free_epid(char epid);
static void cleanup_sb(USB_SB_Desc_t *sb);
-static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen);
-static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen);
+static int etrax_usb_do_ctrl_hw_add(struct urb *urb, char epid, char maxlen);
+static int etrax_usb_do_bulk_hw_add(struct urb *urb, char epid, char maxlen);
-static int etrax_usb_submit_ctrl_urb(urb_t *urb);
+static int etrax_usb_submit_ctrl_urb(struct urb *urb);
-static int etrax_usb_submit_urb(urb_t *urb);
-static int etrax_usb_unlink_urb(urb_t *urb);
+static int etrax_usb_submit_urb(struct urb *urb);
+static int etrax_usb_unlink_urb(struct urb *urb);
static int etrax_usb_get_frame_number(struct usb_device *usb_dev);
static int etrax_usb_allocate_dev(struct usb_device *usb_dev);
static int etrax_usb_deallocate_dev(struct usb_device *usb_dev);
static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs);
static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs);
-static int etrax_rh_submit_urb (urb_t *urb);
+static int etrax_rh_submit_urb (struct urb *urb);
static int etrax_usb_hc_init(void);
static void etrax_usb_hc_cleanup(void);
}
-static int etrax_usb_unlink_intr_urb(urb_t *urb)
+static int etrax_usb_unlink_intr_urb(struct urb *urb)
{
struct usb_device *usb_dev = urb->dev;
etrax_hc_t *hc = usb_dev->bus->hcpriv;
} while (tmp_ep != first_ep);
}
-static int etrax_usb_submit_intr_urb(urb_t *urb)
+static int etrax_usb_submit_intr_urb(struct urb *urb)
{
USB_EP_Desc_t *tmp_ep;
USB_EP_Desc_t *first_ep;
static int handle_intr_transfer_attn(char epid, int status)
{
- urb_t *old_urb;
+ struct urb *old_urb;
DBFENTER;
DBFEXIT;
}
-static int etrax_rh_unlink_urb (urb_t *urb)
+static int etrax_rh_unlink_urb (struct urb *urb)
{
etrax_hc_t *hc;
return 0;
}
-static void etrax_rh_send_irq(urb_t *urb)
+static void etrax_rh_send_irq(struct urb *urb)
{
__u16 data = 0;
etrax_hc_t *hc = urb->dev->bus->hcpriv;
/* DBFEXIT; */
}
-static void etrax_rh_init_int_timer(urb_t *urb)
+static void etrax_rh_init_int_timer(struct urb *urb)
{
etrax_hc_t *hc;
static void etrax_rh_int_timer_do(unsigned long ptr)
{
- urb_t *urb;
+ struct urb *urb;
etrax_hc_t *hc;
/* DBFENTER; */
- urb = (urb_t*)ptr;
+ urb = (struct urb *)ptr;
hc = urb->dev->bus->hcpriv;
if (hc->rh.send) {
return -1;
}
-static int etrax_usb_submit_bulk_urb(urb_t *urb)
+static int etrax_usb_submit_bulk_urb(struct urb *urb)
{
char epid;
char devnum;
char maxlen;
char slow;
- urb_t *tmp_urb;
+ struct urb *tmp_urb;
etrax_urb_priv_t *urb_priv;
unsigned long flags;
return 0;
}
-static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen)
+static int etrax_usb_do_bulk_hw_add(struct urb *urb, char epid, char maxlen)
{
USB_SB_Desc_t *sb_desc_1;
static int handle_bulk_transfer_attn(char epid, int status)
{
- urb_t *old_urb;
+ struct urb *old_urb;
etrax_urb_priv_t *hc_priv;
unsigned long flags;
/* ---------------------------------------------------------------------------- */
-static int etrax_usb_submit_ctrl_urb(urb_t *urb)
+static int etrax_usb_submit_ctrl_urb(struct urb *urb)
{
char epid;
char devnum;
char maxlen;
char slow;
- urb_t *tmp_urb;
+ struct urb *tmp_urb;
etrax_urb_priv_t *urb_priv;
unsigned long flags;
return 0;
}
-static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen)
+static int etrax_usb_do_ctrl_hw_add(struct urb *urb, char epid, char maxlen)
{
USB_SB_Desc_t *sb_desc_1;
USB_SB_Desc_t *sb_desc_2;
DBFEXIT;
}
-static int etrax_usb_submit_urb(urb_t *urb)
+static int etrax_usb_submit_urb(struct urb *urb)
{
etrax_hc_t *hc;
int rval = -EINVAL;
return rval;
}
-static int etrax_usb_unlink_urb(urb_t *urb)
+static int etrax_usb_unlink_urb(struct urb *urb)
{
etrax_hc_t *hc = urb->dev->bus->hcpriv;
int epid;
cli();
for (epid = 0; epid < 32; epid++) {
- urb_t *u = URB_List[epid];
+ struct urb *u = URB_List[epid];
pos = 0;
for (; u; u = u->next) {
URB_List[epid] = u->next;
} else {
- urb_t *up;
+ struct urb *up;
for (up = URB_List[epid]; up->next != u; up = up->next);
up->next = u->next;
}
etrax_hc_t *hc = (etrax_hc_t *)vhc;
int epid;
char eol;
- urb_t *urb;
+ struct urb *urb;
USB_EP_Desc_t *tmp_ep;
USB_SB_Desc_t *tmp_sb;
static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs)
{
int epid = 0;
- urb_t *urb;
+ struct urb *urb;
etrax_urb_priv_t *urb_priv;
*R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
static int handle_control_transfer_attn(char epid, int status)
{
- urb_t *old_urb;
+ struct urb *old_urb;
etrax_urb_priv_t *hc_priv;
DBFENTER;
static void etrax_usb_hc_intr_bottom_half(void *data)
{
struct usb_reg_context *reg = (struct usb_reg_context *)data;
- urb_t *old_urb;
+ struct urb *old_urb;
int error_code;
int epid;
DBFEXIT;
}
-static int etrax_rh_submit_urb(urb_t *urb)
+static int etrax_rh_submit_urb(struct urb *urb)
{
struct usb_device *usb_dev = urb->dev;
etrax_hc_t *hc = usb_dev->bus->hcpriv;
-# $Id: Makefile,v 1.9 2001/10/22 13:10:21 pkj Exp $
+# $Id: Makefile,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
#
# Makefile for the linux kernel.
#
* init_etrax_debug()
*
* $Log: debugport.c,v $
+ * Revision 1.2 2002/01/21 15:21:50 bjornw
+ * Update for kdev_t changes
+ *
* Revision 1.6 2001/04/17 13:58:39 orjanf
* * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB.
*
static kdev_t
console_device(struct console *c)
{
- return MKDEV(TTY_MAJOR, 64 + c->index);
+ return mk_kdev(TTY_MAJOR, 64 + c->index);
}
static int __init
}
static struct console sercons = {
- "ttyS",
- console_write,
- NULL,
- console_device,
- NULL,
- NULL,
- console_setup,
- CON_PRINTBUFFER,
- DEBUG_PORT_IDX,
- 0,
- NULL
+ name: "ttyS",
+ write: console_write,
+ read: NULL,
+ device: console_device,
+ unblank: NULL,
+ setup: console_setup,
+ flags: CON_PRINTBUFFER,
+ index: DEBUG_PORT_IDX,
+ cflag: 0,
+ next: NULL
};
/*
-/* $Id: entry.S,v 1.35 2001/10/30 17:10:15 bjornw Exp $
+/* $Id: entry.S,v 1.3 2002/01/21 15:22:20 bjornw Exp $
*
* linux/arch/cris/entry.S
*
* Authors: Bjorn Wesen (bjornw@axis.com)
*
* $Log: entry.S,v $
+ * Revision 1.3 2002/01/21 15:22:20 bjornw
+ * NICE_DOGGY fix from 2.4 arch/cris
+ *
+ * Revision 1.37 2001/12/07 17:03:55 bjornw
+ * Call a c-hook called watchdog_bite_hook instead of show_registers directly
+ *
+ * Revision 1.36 2001/11/22 13:36:36 bjornw
+ * * In ret_from_intr, check regs->dccr for usermode reentrance instead of
+ * DCCR explicitely (because the latter might not reflect current reality)
+ * * In mmu_bus_fault, set $r9 _after_ calling the C-code instead of before
+ * since $r9 is call-clobbered and is potentially needed afterwards
+ *
* Revision 1.35 2001/10/30 17:10:15 bjornw
* Add some syscalls
*
ret_from_intr:
;; check for resched only if we're going back to user-mode
-
- move $ccr, $r0
+ ;; this test matches the user_regs(regs) macro
+ ;; we cannot simply test $dccr, because that does not necessarily
+ ;; reflect what mode we'll return into.
+
+ move.d [$sp + LDCCR], $r0; regs->dccr
btstq 8, $r0 ; U-flag
bpl _Rexit ; go back directly
nop
moveq 1, $r10
push $r10 ; frametype == 1, BUSFAULT frame type
- moveq 0, $r9 ; busfault is equivalent to an irq
-
move.d $sp, $r10 ; pt_regs argument to handle_mmu_bus_fault
jsr handle_mmu_bus_fault ; in arch/cris/mm/fault.c
;; process due to a SEGV, scheduled due to a page blocking or
;; whatever.
+ moveq 0, $r9 ; busfault is equivalent to an irq
+
ba ret_from_intr
nop
;; We'll see this in ksymoops dumps.
Watchdog_bite:
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+ ;; We just restart the watchdog here to be sure we dont get
+ ;; hit while printing the watchdogmsg below
+ ;; This restart is compatible with the rest of the C-code, so
+ ;; the C-code can keep restarting the watchdog after this point.
+ ;; The non-NICE_DOGGY code below though, disables the possibility
+ ;; to restart since it changes the watchdog key, to avoid any
+ ;; buggy loops etc. keeping the watchdog alive after this.
+ jsr reset_watchdog
+#else
+
;; We need to extend the 3.3ms after the NMI at watchdog bite, so we have
;; time for an oops-dump over a 115k2 serial wire. Another 100ms should do.
| IO_STATE (R_WATCHDOG, enable, start), $r10
move.d $r10, [$r11]
+#endif
+
;; Note that we don't do "setf m" here (or after two necessary NOPs),
;; since *not* doing that saves us from re-entrancy checks. We don't want
;; to get here again due to possible subsequent NMIs; we want the watchdog
jsr printk
move.d $sp, $r10
- jsr show_registers
+ jsr watchdog_bite_hook
;; This nop is here so we see the "Watchdog_bite" label in ksymoops dumps
;; rather than "spurious_interrupt".
-/* $Id: head.S,v 1.41 2001/10/29 14:55:58 pkj Exp $
+/* $Id: head.S,v 1.2 2001/12/18 13:35:19 bjornw Exp $
*
* Head of the kernel - alter with care
*
* Authors: Bjorn Wesen (bjornw@axis.com)
*
* $Log: head.S,v $
+ * Revision 1.2 2001/12/18 13:35:19 bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.43 2001/11/08 15:09:43 starvik
+ * Only start MII clock if Ethernet is configured
+ *
+ * Revision 1.42 2001/11/08 14:37:34 starvik
+ * Start MII clock early to make sure that it is running at tranceiver reset
+ *
* Revision 1.41 2001/10/29 14:55:58 pkj
* Corrected pa$r0 to par0.
*
#define CRAMFS_MAGIC 0x28cd3d45
#define RAM_INIT_MAGIC 0x56902387
-
+
+#define START_ETHERNET_CLOCK IO_STATE(R_NETWORK_GEN_CONFIG, enable, on) |\
+ IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk)
+
;; exported symbols
.globl etrax_irv
;; after init.
.section ".text.init"
_inflash:
+#ifdef CONFIG_ETRAX_ETHERNET
+ ;; Start MII clock to make sure it is running when tranceiver is reset
+ move.d START_ETHERNET_CLOCK, $r0
+ move.d $r0, [R_NETWORK_GEN_CONFIG]
+#endif
+
;; We need to initialze DRAM registers before we start using the DRAM
cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized?
-/* $Id: irq.c,v 1.17 2001/07/25 16:08:01 bjornw Exp $
+/* $Id: irq.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $
*
* linux/arch/cris/kernel/irq.c
*
/* IRQ0 and 1 are special traps */
void hwbreakpoint(void);
void IRQ1_interrupt(void);
-BUILD_IRQ(2, 0x04) /* the timer interrupt */
+BUILD_TIMER_IRQ(2, 0x04) /* the timer interrupt is somewhat special */
BUILD_IRQ(3, 0x08)
BUILD_IRQ(4, 0x10)
BUILD_IRQ(5, 0x20)
*! Jul 21 1999 Bjorn Wesen eLinux port
*!
*! $Log: kgdb.c,v $
+*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw
+*! Import of Linux 2.5.1
+*!
*! Revision 1.6 2001/10/09 13:10:03 matsfg
*! Added $ on registers and removed some underscores
*!
*!
*!---------------------------------------------------------------------------
*!
-*! $Id: kgdb.c,v 1.6 2001/10/09 13:10:03 matsfg Exp $
+*! $Id: kgdb.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*!
*! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN
*!
extern void dump_thread(struct pt_regs *, struct user *);
extern unsigned long get_cmos_time(void);
+extern void __Udiv(void);
extern void __ashrdi3(void);
extern void iounmap(void *addr);
-/* platform dependent support */
-
+/* Platform dependent support */
EXPORT_SYMBOL(dump_thread);
EXPORT_SYMBOL(enable_irq);
EXPORT_SYMBOL(disable_irq);
EXPORT_SYMBOL(kernel_thread);
EXPORT_SYMBOL(get_cmos_time);
+EXPORT_SYMBOL(loops_per_usec);
+/* String functions */
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memmove);
EXPORT_SYMBOL(strtok);
EXPORT_SYMBOL(strpbrk);
EXPORT_SYMBOL(simple_strtol);
EXPORT_SYMBOL(strstr);
-
+EXPORT_SYMBOL(strcpy);
EXPORT_SYMBOL(strchr);
EXPORT_SYMBOL(strcmp);
EXPORT_SYMBOL(strlen);
EXPORT_SYMBOL(strncat);
EXPORT_SYMBOL(strncmp);
+
+/* Math functions */
+EXPORT_SYMBOL(__Udiv);
EXPORT_SYMBOL(__ashrdi3);
+/* Memory functions */
EXPORT_SYMBOL(__ioremap);
EXPORT_SYMBOL(iounmap);
-/* export shadow registers for the CPU I/O pins */
+/* Semaphore functions */
+EXPORT_SYMBOL(__up);
+EXPORT_SYMBOL(__down);
+/* Export shadow registers for the CPU I/O pins */
EXPORT_SYMBOL(genconfig_shadow);
EXPORT_SYMBOL(port_pa_data_shadow);
EXPORT_SYMBOL(port_pa_dir_shadow);
EXPORT_SYMBOL(port_pb_config_shadow);
EXPORT_SYMBOL(port_g_data_shadow);
-/* other stuff */
-
+/* Userspace access functions */
EXPORT_SYMBOL(strncpy_from_user);
EXPORT_SYMBOL(__strncpy_from_user);
EXPORT_SYMBOL(__generic_copy_from_user);
#undef memcpy
#undef memset
-extern void * memset(void *,int,__kernel_size_t);
-extern void * memcpy(void *,const void *,__kernel_size_t);
+extern void * memset(void *, int, __kernel_size_t);
+extern void * memcpy(void *, const void *, __kernel_size_t);
EXPORT_SYMBOL_NOVERS(memcpy);
EXPORT_SYMBOL_NOVERS(memset);
-/* $Id: process.c,v 1.20 2001/10/03 08:21:39 jonashg Exp $
+/* $Id: process.c,v 1.3 2002/01/21 15:22:49 bjornw Exp $
*
* linux/arch/cris/kernel/process.c
*
* Authors: Bjorn Wesen (bjornw@axis.com)
*
* $Log: process.c,v $
+ * Revision 1.3 2002/01/21 15:22:49 bjornw
+ * current->counter is gone
+ *
+ * Revision 1.22 2001/11/13 09:40:43 orjanf
+ * Added dump_fpu (needed for core dumps).
+ *
+ * Revision 1.21 2001/11/12 18:26:21 pkj
+ * Fixed compiler warnings.
+ *
* Revision 1.20 2001/10/03 08:21:39 jonashg
* cause_of_death does not exist if CONFIG_SVINTO_SIM is defined.
*
#include <linux/slab.h>
#include <linux/user.h>
#include <linux/a.out.h>
+#include <linux/elfcore.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
* setup.
*/
-static struct vm_area_struct init_mmap = INIT_MMAP;
static struct fs_struct init_fs = INIT_FS;
static struct files_struct init_files = INIT_FILES;
static struct signal_struct init_signals = INIT_SIGNALS;
* code to know about it than the watchdog handler in entry.S and
* this code, implementing hard reset through the watchdog.
*/
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
extern int cause_of_death;
+#endif
printk("*** HARD RESET ***\n");
cli();
#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
cause_of_death = 0xbedead;
-
#else
/* Since we dont plan to keep on reseting the watchdog,
the key can be arbitrary hence three */
*/
void dump_thread(struct pt_regs * regs, struct user * dump)
{
- int i;
#if 0
-/* changed the size calculations - should hopefully work better. lbt */
+ int i;
+
+ /* changed the size calculations - should hopefully work better. lbt */
dump->magic = CMAGIC;
dump->start_code = 0;
dump->start_stack = regs->esp & ~(PAGE_SIZE - 1);
#endif
}
+/* Fill in the fpu structure for a core dump. */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
+{
+ return 0;
+}
+
/*
* Be aware of the "magic" 7th argument in the four system-calls below.
* They need the latest stackframe, which is put as the 7th argument by
* Authors: Bjorn Wesen
*
* $Log: ptrace.c,v $
+ * Revision 1.2 2001/12/18 13:35:20 bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.8 2001/11/12 18:26:21 pkj
+ * Fixed compiler warnings.
+ *
* Revision 1.7 2001/09/26 11:53:49 bjornw
* PTRACE_DETACH works more simple in 2.4.10
*
static inline int put_reg(struct task_struct *task, unsigned int regno,
unsigned long data)
{
- unsigned long *addr;
-
if (regno == PT_USP)
task->thread.usp = data;
else if (regno < PT_MAX)
break;
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT: { /* restart after signal. */
- long tmp;
-
+ case PTRACE_CONT: /* restart after signal. */
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
wake_up_process(child);
ret = 0;
break;
- }
/*
* make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to
* exit.
*/
- case PTRACE_KILL: {
- long tmp;
-
+ case PTRACE_KILL:
ret = 0;
if (child->state == TASK_ZOMBIE) /* already dead */
break;
/* TODO: make sure any pending breakpoint is killed */
wake_up_process(child);
break;
- }
-
- case PTRACE_SINGLESTEP: { /* set the trap flag. */
- long tmp;
+ case PTRACE_SINGLESTEP: /* set the trap flag. */
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
wake_up_process(child);
ret = 0;
break;
- }
case PTRACE_DETACH:
ret = ptrace_detach(child, data);
-/* $Id: setup.c,v 1.22 2001/10/23 17:42:58 pkj Exp $
+/* $Id: setup.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $
*
* linux/arch/cris/kernel/setup.c
*
#include <linux/config.h>
#include <linux/init.h>
#include <linux/bootmem.h>
+#include <linux/seq_file.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/smp.h>
+#include <asm/pgtable.h>
#include <asm/types.h>
#include <asm/svinto.h>
void __init
setup_arch(char **cmdline_p)
{
- unsigned long bootmap_size;
+ extern void init_etrax_debug(void);
+ unsigned long bootmap_size;
unsigned long start_pfn, max_pfn;
unsigned long memory_start;
- extern void console_print_etrax(const char *b);
/* register an initial console printing routine for printk's */
if(romfs_in_flash || !romfs_length) {
/* if we have the romfs in flash, or if there is no rom filesystem,
- * our free area starts directly after the BSS
+ * our free area starts directly after the BSS
*/
memory_start = (unsigned long) &_end;
} else {
/* otherwise the free area starts after the ROM filesystem */
- printk("ROM fs in RAM, size %d bytes\n", romfs_length);
+ printk("ROM fs in RAM, size %lu bytes\n", romfs_length);
memory_start = romfs_start + romfs_length;
}
#define HAS_ATA 0x0020
#define HAS_USB 0x0040
#define HAS_IRQ_BUG 0x0080
-#define HAS_MMU_BUG 0x0100
+#define HAS_MMU_BUG 0x0100
static struct cpu_info {
char *model;
{ "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG },
{ "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA },
{ "ETRAX 100LX", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU | HAS_MMU_BUG },
- { "ETRAX 100LX v2", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU },
+ { "ETRAX 100LX v2", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU },
{ "Unknown", 0, 0 } /* This entry MUST be the last */
};
-/*
- * get_cpuinfo - Get information on one CPU for use by the procfs.
- *
- * Prints info on the next CPU into buffer. Beware, doesn't check for
- * buffer overflow. Current implementation of procfs assumes that the
- * resulting data is <= 1K.
- *
- * BUFFER is PAGE_SIZE - 1K bytes long.
- *
- * Args:
- * buffer -- you guessed it, the data buffer
- * cpu_np -- Input: next cpu to get (start at 0). Output: Updated.
- *
- * Returns number of bytes written to buffer.
- */
-int get_cpuinfo(char *buffer, unsigned *cpu_np)
+static int show_cpuinfo(struct seq_file *m, void *v)
{
- int revision;
- struct cpu_info *info;
- unsigned n;
+ unsigned long revision;
+ struct cpu_info *info;
/* read the version register in the CPU and print some stuff */
revision = rdvr();
- if (revision < 0 || revision >= sizeof cpu_info/sizeof *cpu_info) {
+ if (revision >= sizeof cpu_info/sizeof *cpu_info)
info = &cpu_info[sizeof cpu_info/sizeof *cpu_info - 1];
- } else
+ else
info = &cpu_info[revision];
- /* No SMP at the moment, so just toggle 0/1 */
- n = *cpu_np;
- *cpu_np = 1;
- if (n != 0) {
- return (0);
- }
-
- return sprintf(buffer,
+ return seq_printf(m,
"cpu\t\t: CRIS\n"
- "cpu revision\t: %d\n"
+ "cpu revision\t: %lu\n"
"cpu model\t: %s\n"
"cache size\t: %d kB\n"
"fpu\t\t: %s\n"
(loops_per_jiffy * HZ + 500) / 500000,
((loops_per_jiffy * HZ + 500) / 5000) % 100);
}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ /* We only got one CPU... */
+ return *pos < 1 ? (void *)1 : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return NULL;
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+struct seq_operations cpuinfo_op = {
+ start: c_start,
+ next: c_next,
+ stop: c_stop,
+ show: show_cpuinfo,
+};
+
#endif /* CONFIG_PROC_FS */
-/* $Id: shadows.c,v 1.2 2001/03/15 14:25:16 bjornw Exp $
+/* $Id: shadows.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* Various shadow registers. Defines for these are in include/asm-etrax100/io.h
*/
-/* $Id: sys_cris.c,v 1.10 2001/06/27 21:16:15 hp Exp $
+/* $Id: sys_cris.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* linux/arch/cris/kernel/sys_cris.c
*
-/* $Id: time.c,v 1.9 2001/10/25 10:26:37 johana Exp $
+/* $Id: time.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $
*
* linux/arch/cris/kernel/time.c
*
* Linux/CRIS specific code:
*
* Authors: Bjorn Wesen
+ * Johan Adolfsson
*
*/
static unsigned long do_slow_gettimeoffset(void)
{
unsigned long count;
+ unsigned long usec_count = 0;
static unsigned long count_p = LATCH; /* for the first call after boot */
static unsigned long jiffies_p = 0;
*/
if( jiffies_t == jiffies_p ) {
if( count > count_p ) {
+ /* Timer wrapped */
+ count = count_p;
+ usec_count = 1000000/CLOCK_TICK_RATE/2;
}
} else
jiffies_p = jiffies_t;
-
count_p = count;
-
+ /* Convert timer value to usec using table lookup */
+ usec_count += cris_timer0_value_us[count];
+#if 0
count = ((LATCH-1) - count) * TICK_SIZE;
count = (count + LATCH/2) / LATCH;
-
- return count;
+#endif
+ return usec_count;
}
static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
{
int retval = 0;
int real_seconds, real_minutes, cmos_minutes;
- unsigned char save_control, save_freq_select;
- printk("set_rtc_mmss(%d)\n", nowtime);
+ printk("set_rtc_mmss(%lu)\n", nowtime);
if(!have_rtc)
return 0;
/* right now, starting the watchdog is the same as resetting it */
#define start_watchdog reset_watchdog
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
static int watchdog_key = 0; /* arbitrary number */
+#endif
/* number of pages to consider "out of memory". it is normal that the memory
* is used though, so put this really low.
if ((time_status & STA_UNSYNC) == 0 &&
xtime.tv_sec > last_rtc_update + 660 &&
xtime.tv_usec > 500000 - (tick >> 1) &&
- xtime.tv_usec < 500000 + (tick >> 1))
+ xtime.tv_usec < 500000 + (tick >> 1)) {
if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec;
else
last_rtc_update = xtime.tv_sec - 600;
-
+ }
}
#if 0
{
unsigned long flags;
unsigned int newjiff;
+
save_flags(flags);
cli();
newjiff = (myjiff << 16) | (unsigned short)(-*R_TIMER01_DATA);
get_cmos_time(void)
{
unsigned int year, mon, day, hour, min, sec;
- int i;
sec = CMOS_READ(RTC_SECONDS);
min = CMOS_READ(RTC_MINUTES);
-/* $Id: traps.c,v 1.15 2001/07/18 14:02:37 bjornw Exp $
+/* $Id: traps.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $
*
* linux/arch/cris/traps.c
*
int kstack_depth_to_print = 24;
+void show_trace(unsigned long * stack)
+{
+ unsigned long addr, module_start, module_end;
+ extern char _stext, _etext;
+ int i;
+
+ printk("\nCall Trace: ");
+
+ i = 1;
+ module_start = VMALLOC_START;
+ module_end = VMALLOC_END;
+
+ while (((long) stack & (THREAD_SIZE-1)) != 0) {
+ if (__get_user (addr, stack)) {
+ /* This message matches "failing address" marked
+ s390 in ksymoops, so lines containing it will
+ not be filtered out by ksymoops. */
+ printk ("Failing address 0x%lx\n", (unsigned long)stack);
+ break;
+ }
+ stack++;
+
+ /*
+ * If the address is either in the text segment of the
+ * kernel, or in the region which contains vmalloc'ed
+ * memory, it *may* be the address of a calling
+ * routine; if so, print it so that someone tracing
+ * down the cause of the crash will be able to figure
+ * out the call path that was taken.
+ */
+ if (((addr >= (unsigned long) &_stext) &&
+ (addr <= (unsigned long) &_etext)) ||
+ ((addr >= module_start) && (addr <= module_end))) {
+ if (i && ((i % 8) == 0))
+ printk("\n ");
+ printk("[<%08lx>] ", addr);
+ i++;
+ }
+ }
+}
+
+void show_trace_task(struct task_struct *tsk)
+{
+ /* TODO, this is not really useful since its called from
+ * SysRq-T and we don't have a keyboard.. :)
+ */
+}
+
+
/*
* These constants are for searching for possible module text
* segments. MODULE_RANGE is a guess of how much space is likely
void
show_stack(unsigned long *sp)
{
- unsigned long *stack, addr, module_start, module_end;
+ unsigned long *stack, addr;
int i;
- extern char _stext, _etext;
/*
* debugging aid: "show_stack(NULL);" prints a
stack = sp;
- printk("\nStack from %08lx:\n ", stack);
+ printk("\nStack from %08lx:\n ", (unsigned long)stack);
for(i = 0; i < kstack_depth_to_print; i++) {
if (((long) stack & (THREAD_SIZE-1)) == 0)
break;
/* This message matches "failing address" marked
s390 in ksymoops, so lines containing it will
not be filtered out by ksymoops. */
- printk ("Failing address 0x%lx\n", stack);
+ printk ("Failing address 0x%lx\n", (unsigned long)stack);
break;
}
stack++;
printk("%08lx ", addr);
}
-
- printk("\nCall Trace: ");
- stack = sp;
- i = 1;
- module_start = VMALLOC_START;
- module_end = VMALLOC_END;
- while (((long) stack & (THREAD_SIZE-1)) != 0) {
- if (__get_user (addr, stack)) {
- /* This message matches "failing address" marked
- s390 in ksymoops, so lines containing it will
- not be filtered out by ksymoops. */
- printk ("Failing address 0x%lx\n", stack);
- break;
- }
- stack++;
-
- /*
- * If the address is either in the text segment of the
- * kernel, or in the region which contains vmalloc'ed
- * memory, it *may* be the address of a calling
- * routine; if so, print it so that someone tracing
- * down the cause of the crash will be able to figure
- * out the call path that was taken.
- */
- if (((addr >= (unsigned long) &_stext) &&
- (addr <= (unsigned long) &_etext)) ||
- ((addr >= module_start) && (addr <= module_end))) {
- if (i && ((i % 8) == 0))
- printk("\n ");
- printk("[<%08lx>] ", addr);
- i++;
- }
- }
+ show_trace(sp);
}
#if 0
regs->r8, regs->r9, regs->r10, regs->r11);
printk("r12: %08lx r13: %08lx oR10: %08lx\n",
regs->r12, regs->r13, regs->orig_r10);
- printk("R_MMU_CAUSE: %08lx\n", *R_MMU_CAUSE);
+ printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
printk("Process %s (pid: %d, stackpage=%08lx)\n",
current->comm, current->pid, (unsigned long)current);
}
}
+/* Called from entry.S when the watchdog has bitten
+ * We print out something resembling an oops dump, and if
+ * we have the nice doggy development flag set, we halt here
+ * instead of rebooting.
+ */
+
+void
+watchdog_bite_hook(struct pt_regs *regs)
+{
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+ cli();
+ stop_watchdog();
+ show_registers(regs);
+ while(1) /* nothing */;
+#else
+ show_registers(regs);
+#endif
+}
+
+/* This is normally the 'Oops' routine */
+
void
die_if_kernel(const char * str, struct pt_regs * regs, long err)
{
+ extern void reset_watchdog(void);
+ extern void stop_watchdog(void);
+
if(user_mode(regs))
return;
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+ /* This printout might take too long and trigger the
+ * watchdog normally. If we're in the nice doggy
+ * development mode, stop the watchdog during printout.
+ */
stop_watchdog();
+#endif
printk("%s: %04lx\n", str, err & 0xffff);
show_registers(regs);
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
reset_watchdog();
-
+#endif
do_exit(SIGSEGV);
}
void __init
trap_init(void)
{
- /* Nothing needs to be done */
+ /* Nothing needs to be done */
}
-/* $Id: checksum.S,v 1.6 2001/10/01 14:47:35 bjornw Exp $
+/* $Id: checksum.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
* A fast checksum routine using movem
* Copyright (c) 1998-2001 Axis Communications AB
*
-/* $Id: checksumcopy.S,v 1.7 2001/10/01 14:47:35 bjornw Exp $
+/* $Id: checksumcopy.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
* A fast checksum+copy routine using movem
* Copyright (c) 1998, 2001 Axis Communications AB
*
-/* $Id: dmacopy.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $
+/* $Id: dmacopy.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* memcpy for large blocks, using memory-memory DMA channels 6 and 7 in Etrax
*/
-/* $Id: dram_init.S,v 1.10 2001/10/04 12:00:21 martinnn Exp $
+/* $Id: dram_init.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* DRAM/SDRAM initialization - alter with care
* This file is intended to be included from other assembler files
* Authors: Mikael Starvik (starvik@axis.com)
*
* $Log: dram_init.S,v $
+ * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw
+ * Import of Linux 2.5.1
+ *
* Revision 1.10 2001/10/04 12:00:21 martinnn
* Added missing underscores.
*
/*
- * $Id: hw_settings.S,v 1.3 2001/04/21 17:02:46 bjornw Exp $
+ * $Id: hw_settings.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* This table is used by some tools to extract hardware parameters.
* The table should be included in the kernel and the decompressor.
-/* $Id: old_checksum.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $
+/* $Id: old_checksum.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $
*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* linux/arch/cris/mm/extable.c
*
* $Log: extable.c,v $
+ * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw
+ * Import of Linux 2.5.1
+ *
* Revision 1.3 2001/09/27 13:52:40 bjornw
* Harmonize underscore-ness with other parts
*
* Authors: Bjorn Wesen
*
* $Log: fault.c,v $
+ * Revision 1.2 2001/12/18 13:35:22 bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.20 2001/11/22 13:34:06 bjornw
+ * * Bug workaround (LX TR89): force a rerun of the whole of an interrupted
+ * unaligned write, because the second half of the write will be corrupted
+ * otherwise. Affected unaligned writes spanning not-yet mapped pages.
+ * * Optimization: use the wr_rd bit in R_MMU_CAUSE to know whether a miss
+ * was due to a read or a write (before we didn't know this until the next
+ * restart of the interrupted instruction, thus wasting one fault-irq)
+ *
+ * Revision 1.19 2001/11/12 19:02:10 pkj
+ * Fixed compiler warnings.
+ *
* Revision 1.18 2001/07/18 22:14:32 bjornw
* Enable interrupts in the bulk of do_page_fault
*
int error_code);
/* debug of low-level TLB reload */
+#undef DEBUG
+
+#ifdef DEBUG
+#define D(x) x
+#else
#define D(x)
+#endif
+
/* debug of higher-level faults */
#define DPG(x)
handle_mmu_bus_fault(struct pt_regs *regs)
{
int cause, select;
+#ifdef DEBUG
int index;
int page_id;
- int miss, we, acc, inv;
+ int acc, inv;
+#endif
+ int miss, we, writeac;
pmd_t *pmd;
pte_t pte;
int errcode;
select = *R_TLB_SELECT;
address = cause & PAGE_MASK; /* get faulting address */
-
- D(page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause));
- D(acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause));
- D(inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause));
- D(index = IO_EXTRACT(R_TLB_SELECT, index, select));
+
+#ifdef DEBUG
+ page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause);
+ acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause);
+ inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause);
+ index = IO_EXTRACT(R_TLB_SELECT, index, select);
+#endif
miss = IO_EXTRACT(R_MMU_CAUSE, miss_excp, cause);
we = IO_EXTRACT(R_MMU_CAUSE, we_excp, cause);
+ writeac = IO_EXTRACT(R_MMU_CAUSE, wr_rd, cause);
+
+ /* ETRAX 100LX TR89 bugfix: if the second half of an unaligned
+ * write causes a MMU-fault, it will not be restarted correctly.
+ * This could happen if a write crosses a page-boundary and the
+ * second page is not yet COW'ed or even loaded. The workaround
+ * is to clear the unaligned bit in the CPU status record, so
+ * that the CPU will rerun both the first and second halves of
+ * the instruction. This will not have any sideeffects unless
+ * the first half goes to any device or memory that can't be
+ * written twice, and which is mapped through the MMU.
+ *
+ * We only need to do this for writes.
+ */
+
+ if(writeac)
+ regs->csrinstr &= ~(1 << 5);
- /* Note: the reason we don't set errcode's r/w flag here
- * using the 'we' flag, is because the latter is only given
- * if there is a write-protection exception, not given as a
- * general r/w access mode flag. It is currently not possible
- * to get this from the MMU (TODO: check if this is the case
- * for LXv2).
- *
- * The page-fault code won't care, but there will be two page-
- * faults instead of one for the case of a write to a non-tabled
- * page (miss, then write-protection).
+ /* Set errcode's R/W flag according to the mode which caused the
+ * fault
*/
- errcode = 0;
+ errcode = writeac << 1;
- D(printk("bus_fault from IRP 0x%x: addr 0x%x, miss %d, inv %d, we %d, acc %d, "
- "idx %d pid %d\n",
+ D(printk("bus_fault from IRP 0x%lx: addr 0x%lx, miss %d, inv %d, we %d, acc %d, dx %d pid %d\n",
regs->irp, address, miss, inv, we, acc, index, page_id));
/* for a miss, we need to reload the TLB entry */
- if(miss) {
-
+ if (miss) {
/* see if the pte exists at all
* refer through current_pgd, dont use mm->pgd
*/
-
+
pmd = (pmd_t *)(current_pgd + pgd_index(address));
- if(pmd_none(*pmd))
+ if (pmd_none(*pmd))
goto dofault;
- if(pmd_bad(*pmd)) {
- printk("bad pgdir entry 0x%x at 0x%x\n", *pmd, pmd);
+ if (pmd_bad(*pmd)) {
+ printk("bad pgdir entry 0x%lx at 0x%p\n", *(unsigned long*)pmd, pmd);
pmd_clear(pmd);
return;
}
pte = *pte_offset(pmd, address);
- if(!pte_present(pte))
+ if (!pte_present(pte))
goto dofault;
-
- D(printk(" found pte %x pg %x ", pte_val(pte), pte_page(pte)));
- D(
- {
- if(pte_val(pte) & _PAGE_SILENT_WRITE)
- printk("Silent-W ");
- if(pte_val(pte) & _PAGE_KERNEL)
- printk("Kernel ");
- if(pte_val(pte) & _PAGE_SILENT_READ)
- printk("Silent-R ");
- if(pte_val(pte) & _PAGE_GLOBAL)
- printk("Global ");
- if(pte_val(pte) & _PAGE_PRESENT)
- printk("Present ");
- if(pte_val(pte) & _PAGE_ACCESSED)
- printk("Accessed ");
- if(pte_val(pte) & _PAGE_MODIFIED)
- printk("Modified ");
- if(pte_val(pte) & _PAGE_READ)
- printk("Readable ");
- if(pte_val(pte) & _PAGE_WRITE)
- printk("Writeable ");
- printk("\n");
- });
+
+#ifdef DEBUG
+ printk(" found pte %lx pg %p ", pte_val(pte), pte_page(pte));
+ if (pte_val(pte) & _PAGE_SILENT_WRITE)
+ printk("Silent-W ");
+ if (pte_val(pte) & _PAGE_KERNEL)
+ printk("Kernel ");
+ if (pte_val(pte) & _PAGE_SILENT_READ)
+ printk("Silent-R ");
+ if (pte_val(pte) & _PAGE_GLOBAL)
+ printk("Global ");
+ if (pte_val(pte) & _PAGE_PRESENT)
+ printk("Present ");
+ if (pte_val(pte) & _PAGE_ACCESSED)
+ printk("Accessed ");
+ if (pte_val(pte) & _PAGE_MODIFIED)
+ printk("Modified ");
+ if (pte_val(pte) & _PAGE_READ)
+ printk("Readable ");
+ if (pte_val(pte) & _PAGE_WRITE)
+ printk("Writeable ");
+ printk("\n");
+#endif
/* load up the chosen TLB entry
* this assumes the pte format is the same as the TLB_LO layout.
}
errcode = 1 | (we << 1);
-
+
dofault:
/* leave it to the MM system fault handler below */
- D(printk("do_page_fault %p errcode %d\n", address, errcode));
+ D(printk("do_page_fault %lx errcode %d\n", address, errcode));
do_page_fault(address, regs, errcode);
}
struct mm_struct *mm;
struct vm_area_struct * vma;
int writeaccess;
- int fault;
unsigned long fixup;
siginfo_t info;
tsk = current;
- /*
- * We fault-in kernel-space virtual memory on-demand. The
- * 'reference' page table is init_mm.pgd.
- *
- * NOTE! We MUST NOT take any locks for this case. We may
- * be in an interrupt or a critical region, and should
- * only copy the information from the master page table,
- * nothing more.
+ /*
+ * We fault-in kernel-space virtual memory on-demand. The
+ * 'reference' page table is init_mm.pgd.
+ *
+ * NOTE! We MUST NOT take any locks for this case. We may
+ * be in an interrupt or a critical region, and should
+ * only copy the information from the master page table,
+ * nothing more.
*
* NOTE2: This is done so that, when updating the vmalloc
* mappings we don't have to walk all processes pgdirs and
* bit set so sometimes the TLB can use a lingering entry.
*
* This verifies that the fault happens in kernel space
- * and that the fault was not a protection error (error_code & 1).
- */
+ * and that the fault was not a protection error (error_code & 1).
+ */
- if (address >= VMALLOC_START &&
+ if (address >= VMALLOC_START &&
!(error_code & 1) &&
!user_mode(regs))
- goto vmalloc_fault;
+ goto vmalloc_fault;
/* we can and should enable interrupts at this point */
sti();
*/
switch (handle_mm_fault(mm, vma, address, writeaccess)) {
- case 1:
- tsk->min_flt++;
- break;
- case 2:
- tsk->maj_flt++;
- break;
- case 0:
- goto do_sigbus;
- default:
- goto out_of_memory;
+ case 1:
+ tsk->min_flt++;
+ break;
+ case 2:
+ tsk->maj_flt++;
+ break;
+ case 0:
+ goto do_sigbus;
+ default:
+ goto out_of_memory;
}
up_read(&mm->mmap_sem);
return;
-
+
/*
* Something tried to access memory that isn't in our memory map..
* Fix it, but check if it's kernel or user first..
*/
bad_area:
-
up_read(&mm->mmap_sem);
bad_area_nosemaphore:
* code)
*/
- if ((fixup = search_exception_table(regs->irp)) != 0) {
+ if ((fixup = search_exception_table(regs->irp)) != 0) {
/* Adjust the instruction pointer in the stackframe */
- regs->irp = fixup;
+ regs->irp = fixup;
/* We do not want to return by restoring the CPU-state
* anymore, so switch frame-types (see ptrace.h)
regs->frametype = CRIS_FRAME_NORMAL;
- D(printk("doing fixup to 0x%x\n", fixup));
- return;
- }
+ D(printk("doing fixup to 0x%lx\n", fixup));
+ return;
+ }
/*
* Oops. The kernel tried to access some bad page. We'll have to
*/
out_of_memory:
- up_read(&mm->mmap_sem);
+ up_read(&mm->mmap_sem);
printk("VM: killing process %s\n", tsk->comm);
- if(user_mode(regs))
+ if (user_mode(regs))
do_exit(SIGKILL);
goto no_context;
up_read(&mm->mmap_sem);
/*
- * Send a sigbus, regardless of whether we were in kernel
- * or user mode.
- */
+ * Send a sigbus, regardless of whether we were in kernel
+ * or user mode.
+ */
info.si_code = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRERR;
info.si_addr = (void *)address;
force_sig_info(SIGBUS, &info, tsk);
-
- /* Kernel mode? Handle exceptions or die */
- if (!user_mode(regs))
- goto no_context;
- return;
+
+ /* Kernel mode? Handle exceptions or die */
+ if (!user_mode(regs))
+ goto no_context;
+ return;
vmalloc_fault:
- {
- /*
- * Synchronize this task's top level page-table
- * with the 'reference' page table.
+ {
+ /*
+ * Synchronize this task's top level page-table
+ * with the 'reference' page table.
*
* Use current_pgd instead of tsk->active_mm->pgd
* since the latter might be unavailable if this
* code is executed in a misfortunately run irq
* (like inside schedule() between switch_mm and
* switch_to...).
- */
+ */
- int offset = pgd_index(address);
- pgd_t *pgd, *pgd_k;
- pmd_t *pmd, *pmd_k;
+ int offset = pgd_index(address);
+ pgd_t *pgd, *pgd_k;
+ pmd_t *pmd, *pmd_k;
pte_t *pte_k;
- pgd = current_pgd + offset;
- pgd_k = init_mm.pgd + offset;
+ pgd = (pgd_t *)current_pgd + offset;
+ pgd_k = init_mm.pgd + offset;
/* Since we're two-level, we don't need to do both
* set_pgd and set_pmd (they do the same thing). If
* it exists.
*/
- pmd = pmd_offset(pgd, address);
- pmd_k = pmd_offset(pgd_k, address);
+ pmd = pmd_offset(pgd, address);
+ pmd_k = pmd_offset(pgd_k, address);
- if (!pmd_present(*pmd_k))
- goto bad_area_nosemaphore;
+ if (!pmd_present(*pmd_k))
+ goto bad_area_nosemaphore;
- set_pmd(pmd, *pmd_k);
+ set_pmd(pmd, *pmd_k);
/* Make sure the actual PTE exists as well to
* catch kernel vmalloc-area accesses to non-mapped
* silently loop forever.
*/
- pte_k = pte_offset(pmd_k, address);
- if (!pte_present(*pte_k))
- goto no_context;
+ pte_k = pte_offset(pmd_k, address);
+ if (!pte_present(*pte_k))
+ goto no_context;
- return;
- }
+ return;
+ }
}
* Authors: Bjorn Wesen (bjornw@axis.com)
*
* $Log: init.c,v $
+ * Revision 1.2 2001/12/18 13:35:22 bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.31 2001/11/13 16:22:00 bjornw
+ * Skip calculating totalram and sharedram in si_meminfo
+ *
+ * Revision 1.30 2001/11/12 19:02:10 pkj
+ * Fixed compiler warnings.
+ *
* Revision 1.29 2001/07/25 16:09:50 bjornw
* val->sharedram will stay 0
*
free_page(addr);
totalram_pages++;
}
- printk ("Freeing unused kernel memory: %dk freed\n",
+ printk ("Freeing unused kernel memory: %luk freed\n",
(&__init_end - &__init_begin) >> 10);
}
void
si_meminfo(struct sysinfo *val)
{
- int i;
-
- i = max_mapnr;
- val->totalram = 0;
- val->sharedram = 0;
- val->freeram = nr_free_pages();
- val->bufferram = atomic_read(&buffermem_pages);
- while (i-- > 0) {
- if (PageReserved(mem_map+i))
- continue;
- val->totalram++;
- if (!atomic_read(&mem_map[i].count))
- continue;
- val->sharedram += atomic_read(&mem_map[i].count) - 1;
- }
- val->mem_unit = PAGE_SIZE;
- val->totalhigh = 0;
- val->freehigh = 0;
+ val->totalram = totalram_pages;
+ val->sharedram = 0;
+ val->freeram = nr_free_pages();
+ val->bufferram = atomic_read(&buffermem_pages);
+ val->totalhigh = 0;
+ val->freehigh = 0;
+ val->mem_unit = PAGE_SIZE;
}
# CONFIG_PCMCIA_XIRTULIP is not set
CONFIG_NET_PCMCIA_RADIO=y
CONFIG_PCMCIA_RAYCS=y
-# CONFIG_PCMCIA_NETWAVE is not set
-# CONFIG_PCMCIA_WAVELAN is not set
# CONFIG_AIRONET4500_CS is not set
#
# USB Host Controller Drivers
#
# CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_OHCI_HCD is not set
CONFIG_USB_UHCI_ALT=y
# CONFIG_USB_OHCI is not set
* through the ICC by us (IPIs)
*/
#ifdef CONFIG_SMP
+BUILD_SMP_INTERRUPT(task_migration_interrupt,TASK_MIGRATION_VECTOR)
BUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR)
BUILD_SMP_INTERRUPT(invalidate_interrupt,INVALIDATE_TLB_VECTOR)
BUILD_SMP_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR)
*/
set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
+ /* IPI for task migration */
+ set_intr_gate(TASK_MIGRATION_VECTOR, task_migration_interrupt);
+
/* IPI for invalidation */
set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt);
do_flush_tlb_all_local();
}
+static spinlock_t migration_lock = SPIN_LOCK_UNLOCKED;
+static task_t *new_task;
+
+/*
+ * This function sends a 'task migration' IPI to another CPU.
+ * Must be called from syscall contexts, with interrupts *enabled*.
+ */
+void smp_migrate_task(int cpu, task_t *p)
+{
+ /*
+ * The target CPU will unlock the migration spinlock:
+ */
+ spin_lock(&migration_lock);
+ new_task = p;
+ send_IPI_mask(1 << cpu, TASK_MIGRATION_VECTOR);
+}
+
+/*
+ * Task migration callback.
+ */
+asmlinkage void smp_task_migration_interrupt(void)
+{
+ task_t *p;
+
+ ack_APIC_irq();
+ p = new_task;
+ spin_unlock(&migration_lock);
+ sched_task_migrated(p);
+}
/*
* this function sends a 'reschedule' IPI to another CPU.
* it goes straight through and wastes no time serializing
* things done here to the most necessary things.
*/
cpu_init();
+ init_idle();
smp_callin();
while (!atomic_read(&smp_commenced))
rep_nop();
* the local TLBs too.
*/
local_flush_tlb();
+ idle_startup_done();
- init_idle();
return cpu_idle();
}
blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), 128);
for (i = 0; i < ps2esdi_drives; i++) {
- register_disk(&ps2esdi_gendisk,MKDEV(MAJOR_NR,i<<6),1<<6,
+ register_disk(&ps2esdi_gendisk,mk_kdev(MAJOR_NR,i<<6),1<<6,
&ps2esdi_fops,
ps2esdi_info[i].head * ps2esdi_info[i].sect *
ps2esdi_info[i].cyl);
#if 0
printk("%s:got request. device : %d minor : %d command : %d sector : %ld count : %ld, buffer: %p\n",
DEVICE_NAME,
- CURRENT_DEV, MINOR(CURRENT->rq_dev),
+ CURRENT_DEV, minor(CURRENT->rq_dev),
CURRENT->cmd, CURRENT->sector,
CURRENT->current_nr_sectors, CURRENT->buffer);
#endif
} /* check for above 16Mb dmas */
else if ((CURRENT_DEV < ps2esdi_drives) &&
(CURRENT->sector + CURRENT->current_nr_sectors <=
- ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects) &&
+ ps2esdi[minor(CURRENT->rq_dev)].nr_sects) &&
CURRENT->flags & REQ_CMD) {
#if 0
printk("%s:got request. device : %d minor : %d command : %d sector : %ld count : %ld\n",
DEVICE_NAME,
- CURRENT_DEV, MINOR(CURRENT->rq_dev),
+ CURRENT_DEV, minor(CURRENT->rq_dev),
CURRENT->cmd, CURRENT->sector,
CURRENT->current_nr_sectors);
#endif
/* is request is valid */
else {
printk("Grrr. error. ps2esdi_drives: %d, %lu %lu\n", ps2esdi_drives,
- CURRENT->sector, ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects);
+ CURRENT->sector, ps2esdi[minor(CURRENT->rq_dev)].nr_sects);
end_request(FAIL);
}
switch (int_ret_code & 0x0f) {
case INT_TRANSFER_REQ:
ps2esdi_prep_dma(CURRENT->buffer, CURRENT->current_nr_sectors,
- (CURRENT->cmd == READ)
+ (rq_data_dir(CURRENT) == READ)
? MCA_DMA_MODE_16 | MCA_DMA_MODE_WRITE | MCA_DMA_MODE_XFER
: MCA_DMA_MODE_16 | MCA_DMA_MODE_READ);
outb(CTRL_ENABLE_DMA | CTRL_ENABLE_INTR, ESDI_CONTROL);
comment "Protocol Drivers"
dep_tristate ' OHCI-1394 Video support' CONFIG_IEEE1394_VIDEO1394 $CONFIG_IEEE1394_OHCI1394
dep_tristate ' SBP-2 support (Harddisks etc.)' CONFIG_IEEE1394_SBP2 $CONFIG_SCSI $CONFIG_IEEE1394
+ dep_tristate ' OHCI-DV I/O support' CONFIG_IEEE1394_DV1394 $CONFIG_IEEE1394_OHCI1394
dep_tristate ' Raw IEEE1394 I/O support' CONFIG_IEEE1394_RAWIO $CONFIG_IEEE1394
bool 'Excessive debugging output' CONFIG_IEEE1394_VERBOSEDEBUG
obj-$(CONFIG_IEEE1394_VIDEO1394) += video1394.o
obj-$(CONFIG_IEEE1394_RAWIO) += raw1394.o
obj-$(CONFIG_IEEE1394_SBP2) += sbp2.o
+obj-$(CONFIG_IEEE1394_DV1394) += dv1394.o
include $(TOPDIR)/Rules.make
{
host->csr.lock = SPIN_LOCK_UNLOCKED;
- host->csr.rom_size = host->template->get_rom(host, &host->csr.rom);
+ host->csr.rom_size = host->ops->get_rom(host, &host->csr.rom);
host->csr.state = 0;
host->csr.node_ids = 0;
case CSR_CYCLE_TIME:
oldcycle = host->csr.cycle_time;
host->csr.cycle_time =
- host->template->devctl(host, GET_CYCLE_COUNTER, 0);
+ host->ops->devctl(host, GET_CYCLE_COUNTER, 0);
if (oldcycle > host->csr.cycle_time) {
/* cycle time wrapped around */
case CSR_BUS_TIME:
oldcycle = host->csr.cycle_time;
host->csr.cycle_time =
- host->template->devctl(host, GET_CYCLE_COUNTER, 0);
+ host->ops->devctl(host, GET_CYCLE_COUNTER, 0);
if (oldcycle > host->csr.cycle_time) {
/* cycle time wrapped around */
return RCODE_ADDRESS_ERROR;
case CSR_BUS_MANAGER_ID:
- if (host->template->hw_csr_reg)
- ret = host->template->hw_csr_reg(host, 0, 0, 0);
+ if (host->ops->hw_csr_reg)
+ ret = host->ops->hw_csr_reg(host, 0, 0, 0);
else
ret = host->csr.bus_manager_id;
*(buf++) = cpu_to_be32(ret);
out;
case CSR_BANDWIDTH_AVAILABLE:
- if (host->template->hw_csr_reg)
- ret = host->template->hw_csr_reg(host, 1, 0, 0);
+ if (host->ops->hw_csr_reg)
+ ret = host->ops->hw_csr_reg(host, 1, 0, 0);
else
ret = host->csr.bandwidth_available;
*(buf++) = cpu_to_be32(ret);
out;
case CSR_CHANNELS_AVAILABLE_HI:
- if (host->template->hw_csr_reg)
- ret = host->template->hw_csr_reg(host, 2, 0, 0);
+ if (host->ops->hw_csr_reg)
+ ret = host->ops->hw_csr_reg(host, 2, 0, 0);
else
ret = host->csr.channels_available_hi;
*(buf++) = cpu_to_be32(ret);
out;
case CSR_CHANNELS_AVAILABLE_LO:
- if (host->template->hw_csr_reg)
- ret = host->template->hw_csr_reg(host, 3, 0, 0);
+ if (host->ops->hw_csr_reg)
+ ret = host->ops->hw_csr_reg(host, 3, 0, 0);
else
ret = host->csr.channels_available_lo;
host->csr.node_ids &= NODE_MASK << 16;
host->csr.node_ids |= be32_to_cpu(*(data++)) & (BUS_MASK << 16);
host->node_id = host->csr.node_ids >> 16;
- host->template->devctl(host, SET_BUS_ID, host->node_id >> 6);
+ host->ops->devctl(host, SET_BUS_ID, host->node_id >> 6);
out;
case CSR_RESET_START:
case CSR_CYCLE_TIME:
/* should only be set by cycle start packet, automatically */
host->csr.cycle_time = be32_to_cpu(*data);
- host->template->devctl(host, SET_CYCLE_COUNTER,
+ host->ops->devctl(host, SET_CYCLE_COUNTER,
be32_to_cpu(*(data++)));
out;
case CSR_BUS_TIME:
data = be32_to_cpu(data);
arg = be32_to_cpu(arg);
- if (host->template->hw_csr_reg) {
+ if (host->ops->hw_csr_reg) {
quadlet_t old;
- old = host->template->
+ old = host->ops->
hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2,
data, arg);
--- /dev/null
+/*
+ * dv1394-private.h - DV input/output over IEEE 1394 on OHCI chips
+ * Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ * receive, proc_fs by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ * video1394.h - driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ * Peter Schlaile <udbz@rz.uni-karlsruhe.de>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_1394_PRIVATE_H
+#define _DV_1394_PRIVATE_H
+
+#include "ieee1394.h"
+#include <linux/pci.h>
+#include <asm/scatterlist.h>
+
+/* data structures private to the dv1394 driver */
+/* none of this is exposed to user-space */
+
+
+/*
+ the 8-byte CIP (Common Isochronous Packet) header that precedes
+ each packet of DV data.
+
+ See the IEC 61883 standard.
+*/
+
+struct CIP_header { unsigned char b[8]; };
+
+static inline void fill_cip_header(struct CIP_header *cip,
+ unsigned char source_node_id,
+ unsigned long counter,
+ enum pal_or_ntsc format,
+ unsigned long timestamp)
+{
+ cip->b[0] = source_node_id;
+ cip->b[1] = 0x78; /* packet size in quadlets (480/4) - even for empty packets! */
+ cip->b[2] = 0x00;
+ cip->b[3] = counter;
+
+ cip->b[4] = 0x80; /* const */
+
+ switch(format) {
+ case DV1394_PAL:
+ cip->b[5] = 0x80;
+ break;
+ case DV1394_NTSC:
+ cip->b[5] = 0x00;
+ break;
+ }
+
+ cip->b[6] = timestamp >> 8;
+ cip->b[7] = timestamp & 0xFF;
+}
+
+
+
+/*
+ DMA commands used to program the OHCI's DMA engine
+
+ See the Texas Instruments OHCI 1394 chipset documentation.
+*/
+
+struct output_more_immediate { u32 q[8]; };
+struct output_more { u32 q[4]; };
+struct output_last { u32 q[4]; };
+struct input_more { u32 q[4]; };
+struct input_last { u32 q[4]; };
+
+/* outputs */
+
+static inline void fill_output_more_immediate(struct output_more_immediate *omi,
+ unsigned char tag,
+ unsigned char channel,
+ unsigned char sync_tag,
+ unsigned int payload_size)
+{
+ omi->q[0] = 0x02000000 | 8 ; /* OUTPUT_MORE_IMMEDIATE; 8 is the size of the IT header */
+ omi->q[1] = 0;
+ omi->q[2] = 0;
+ omi->q[3] = 0;
+
+ /* IT packet header */
+ omi->q[4] = (0x0 << 16) /* DMA_SPEED_100 */
+ | (tag << 14)
+ | (channel << 8)
+ | (TCODE_ISO_DATA << 4)
+ | (sync_tag);
+
+ omi->q[5] = payload_size << 16;
+ omi->q[5] |= (0x7F << 8) | 0xA0; /* reserved field; mimic behavior of my Sony DSR-40 */
+
+ omi->q[6] = 0;
+ omi->q[7] = 0;
+}
+
+static inline void fill_output_more(struct output_more *om,
+ unsigned int data_size,
+ unsigned long data_phys_addr)
+{
+ om->q[0] = 0; /* OUTPUT_MORE */
+ om->q[0] |= data_size;
+
+ om->q[1] = data_phys_addr;
+ om->q[2] = 0;
+ om->q[3] = 0;
+}
+
+static inline void fill_output_last(struct output_last *ol,
+ int want_timestamp,
+ int want_interrupt,
+ unsigned int data_size,
+ unsigned long data_phys_addr)
+{
+ ol->q[0] = 0;
+ ol->q[0] |= 1 << 28; /* OUTPUT_LAST */
+
+ if(want_timestamp) /* controller will update timestamp at DMA time */
+ ol->q[0] |= 1 << 27;
+
+ if(want_interrupt)
+ ol->q[0] |= 3 << 20;
+
+ ol->q[0] |= 3 << 18; /* must take branch */
+ ol->q[0] |= data_size;
+
+ ol->q[1] = data_phys_addr;
+ ol->q[2] = 0;
+ ol->q[3] = 0;
+}
+
+/* inputs */
+
+static inline void fill_input_more(struct input_more *im,
+ int want_interrupt,
+ unsigned int data_size,
+ unsigned long data_phys_addr)
+{
+ im->q[0] = 2 << 28; /* INPUT_MORE */
+ im->q[0] |= 8 << 24; /* s = 1, update xferStatus and resCount */
+ if (want_interrupt)
+ im->q[0] |= 0 << 20; /* interrupts, i=0 in packet-per-buffer mode */
+ im->q[0] |= 0x0 << 16; /* disable branch to address for packet-per-buffer mode */
+ /* disable wait on sync field, not used in DV :-( */
+ im->q[0] |= data_size;
+
+ im->q[1] = data_phys_addr;
+ im->q[2] = 0; /* branchAddress and Z not use in packet-per-buffer mode */
+ im->q[3] = 0; /* xferStatus & resCount, resCount must be initialize to data_size */
+}
+
+static inline void fill_input_last(struct input_last *il,
+ unsigned int data_size,
+ unsigned long data_phys_addr)
+{
+ il->q[0] = 3 << 28; /* INPUT_LAST */
+ il->q[0] |= 8 << 24; /* s = 1, update xferStatus and resCount */
+ il->q[0] |= 3 << 20; /* enable interrupts */
+ il->q[0] |= 0xC << 16; /* enable branch to address */
+ /* disable wait on sync field, not used in DV :-( */
+ il->q[0] |= data_size;
+
+ il->q[1] = data_phys_addr;
+ il->q[2] = 1; /* branchAddress (filled in later) and Z = 1 descriptor in next block */
+ il->q[3] = data_size; /* xferStatus & resCount, resCount must be initialize to data_size */
+}
+
+
+
+/*
+ A "DMA descriptor block" consists of several contiguous DMA commands.
+ struct DMA_descriptor_block encapsulates all of the commands necessary
+ to send one packet of DV data.
+
+ There are three different types of these blocks:
+
+ 1) command to send an empty packet (CIP header only, no DV data):
+
+ OUTPUT_MORE-Immediate <-- contains the iso header in-line
+ OUTPUT_LAST <-- points to the CIP header
+
+ 2) command to send a full packet when the DV data payload does NOT
+ cross a page boundary:
+
+ OUTPUT_MORE-Immediate <-- contains the iso header in-line
+ OUTPUT_MORE <-- points to the CIP header
+ OUTPUT_LAST <-- points to entire DV data payload
+
+ 3) command to send a full packet when the DV payload DOES cross
+ a page boundary:
+
+ OUTPUT_MORE-Immediate <-- contains the iso header in-line
+ OUTPUT_MORE <-- points to the CIP header
+ OUTPUT_MORE <-- points to first part of DV data payload
+ OUTPUT_LAST <-- points to second part of DV data payload
+
+ This struct describes all three block types using unions.
+
+ !!! It is vital that an even number of these descriptor blocks fit on one
+ page of memory, since a block cannot cross a page boundary !!!
+
+ */
+
+struct DMA_descriptor_block {
+
+ union {
+ struct {
+ /* iso header, common to all output block types */
+ struct output_more_immediate omi;
+
+ union {
+ /* empty packet */
+ struct {
+ struct output_last ol; /* CIP header */
+ } empty;
+
+ /* full packet */
+ struct {
+ struct output_more om; /* CIP header */
+
+ union {
+ /* payload does not cross page boundary */
+ struct {
+ struct output_last ol; /* data payload */
+ } nocross;
+
+ /* payload crosses page boundary */
+ struct {
+ struct output_more om; /* data payload */
+ struct output_last ol; /* data payload */
+ } cross;
+ } u;
+
+ } full;
+ } u;
+ } out;
+
+ struct {
+ struct input_last il;
+ } in;
+
+ } u;
+
+ /* ensure that PAGE_SIZE % sizeof(struct DMA_descriptor_block) == 0
+ by padding out to 128 bytes */
+ u32 __pad__[12];
+};
+
+
+/* struct frame contains all data associated with one frame in the
+ ringbuffer these are allocated when the DMA context is initialized
+ do_dv1394_init(). They are re-used after the card finishes
+ transmitting the frame. */
+
+struct video_card; /* forward declaration */
+
+struct frame {
+
+ /* points to the struct video_card that owns this frame */
+ struct video_card *video;
+
+ /* index of this frame in video_card->frames[] */
+ unsigned int frame_num;
+
+ /* FRAME_CLEAR - DMA program not set up, waiting for data
+ FRAME_READY - DMA program written, ready to transmit
+
+ Changes to these should be locked against the interrupt
+ */
+ enum {
+ FRAME_CLEAR = 0,
+ FRAME_READY
+ } state;
+
+ /* whether this frame has been DMA'ed already; used only from
+ the IRQ handler to determine whether the frame can be reset */
+ int done;
+
+
+ /* kernel virtual pointer to the start of this frame's data in
+ the user ringbuffer. Use only for CPU access; to get the DMA
+ bus address you must go through the video->user_dma mapping */
+ unsigned long data;
+
+ /* Max # of packets per frame */
+ #define MAX_PACKETS 320
+
+
+ /* a PAGE_SIZE memory pool for allocating CIP headers
+ !header_pool must be aligned to PAGE_SIZE! */
+ struct CIP_header *header_pool;
+ dma_addr_t header_pool_dma;
+
+
+ /* a physically contiguous memory pool for allocating DMA
+ descriptor blocks; usually around 64KB in size
+ !descriptor_pool must be aligned to PAGE_SIZE! */
+ struct DMA_descriptor_block *descriptor_pool;
+ dma_addr_t descriptor_pool_dma;
+ unsigned long descriptor_pool_size;
+
+
+ /* # of packets allocated for this frame */
+ unsigned int n_packets;
+
+
+ /* below are several pointers (kernel virtual addresses, not
+ DMA bus addresses) to parts of the DMA program. These are
+ set each time the DMA program is written in
+ frame_prepare(). They are used later on, e.g. from the
+ interrupt handler, to check the status of the frame */
+
+ /* points to status/timestamp field of first DMA packet */
+ /* (we'll check it later to monitor timestamp accuracy) */
+ u32 *frame_begin_timestamp;
+
+ /* the timestamp we assigned to the first packet in the frame */
+ u32 assigned_timestamp;
+
+ /* pointer to the first packet's CIP header (where the timestamp goes) */
+ struct CIP_header *cip_syt1;
+
+ /* pointer to the second packet's CIP header
+ (only set if the first packet was empty) */
+ struct CIP_header *cip_syt2;
+
+ /* in order to figure out what caused an interrupt,
+ store pointers to the status fields of the two packets
+ that can cause interrupts. We'll check these from the
+ interrupt handler.
+ */
+ u32 *mid_frame_timestamp;
+ u32 *frame_end_timestamp;
+
+ /* branch address field of final packet. This is effectively
+ the "tail" in the chain of DMA descriptor blocks.
+ We will fill it with the address of the first DMA descriptor
+ block in the subsequent frame, once it is ready.
+ */
+ u32 *frame_end_branch;
+
+ /* the number of descriptors in the first descriptor block
+ of the frame. Needed to start DMA */
+ int first_n_descriptors;
+};
+
+
+struct packet {
+ u16 timestamp;
+ u16 invalid;
+ u16 iso_header;
+ u16 data_length;
+ u32 cip_h1;
+ u32 cip_h2;
+ unsigned char data[480];
+ unsigned char padding[16]; /* force struct size =512 for page alignment */
+};
+
+
+/* allocate/free a frame */
+static struct frame* frame_new(unsigned int frame_num, struct video_card *video);
+static void frame_delete(struct frame *f);
+
+/* reset f so that it can be used again */
+static void frame_reset(struct frame *f);
+
+
+/* structure for bookkeeping of a large non-physically-contiguous DMA buffer */
+
+struct dma_region {
+ unsigned int n_pages;
+ unsigned int n_dma_pages;
+ struct scatterlist *sglist;
+};
+
+/* return the DMA bus address of the byte with the given offset
+ relative to the beginning of the dma_region */
+
+static inline dma_addr_t dma_offset_to_bus(struct dma_region *dma, unsigned long offset)
+{
+ int i;
+ struct scatterlist *sg;
+
+ for(i = 0, sg = &dma->sglist[0]; i < dma->n_dma_pages; i++, sg++) {
+ if(offset < sg_dma_len(sg)) {
+ return sg_dma_address(sg) + offset;
+ }
+ offset -= sg_dma_len(sg);
+ }
+
+ printk(KERN_ERR "dv1394: dma_offset_to_bus failed for offset %lu!\n", offset);
+ return 0;
+}
+
+
+/* struct video_card contains all data associated with one instance
+ of the dv1394 driver
+*/
+
+struct video_card {
+
+ /* ohci card to which this instance corresponds */
+ struct ti_ohci *ohci;
+
+ /* OHCI card id; the link between the VFS inode and a specific video_card
+ (essentially the device minor number) */
+ int id;
+
+ /* entry in dv1394_cards */
+ struct list_head list;
+
+ /* handle to /dev/ieee1394/dv/N, NULL if devfs not in use */
+ devfs_handle_t devfs_handle;
+
+ /* OHCI card IT DMA context number, -1 if not in use */
+ int ohci_it_ctx;
+
+ /* register offsets for current IT DMA context, 0 if not in use */
+ u32 ohci_IsoXmitContextControlSet;
+ u32 ohci_IsoXmitContextControlClear;
+ u32 ohci_IsoXmitCommandPtr;
+
+ /* OHCI card IR DMA context number, -1 if not in use */
+ int ohci_ir_ctx;
+
+ /* register offsets for current IR DMA context, 0 if not in use */
+ u32 ohci_IsoRcvContextControlSet;
+ u32 ohci_IsoRcvContextControlClear;
+ u32 ohci_IsoRcvCommandPtr;
+ u32 ohci_IsoRcvContextMatch;
+
+
+ /* CONCURRENCY CONTROL */
+
+ /* there are THREE levels of locking associated with video_card. */
+
+ /*
+ 1) the 'open' flag - this prevents more than one process from
+ opening the device. (the driver currently assumes only one opener).
+ This is a regular int, but use test_and_set_bit() (on bit zero)
+ for atomicity.
+ */
+ int open;
+
+ /*
+ 2) the spinlock - this provides mutual exclusion between the interrupt
+ handler and process-context operations. Generally you must take the
+ spinlock under the following conditions:
+ 1) DMA (and hence the interrupt handler) may be running
+ AND
+ 2) you need to operate on the video_card, especially active_frame
+
+ It is OK to play with video_card without taking the spinlock if
+ you are certain that DMA is not running. Even if DMA is running,
+ it is OK to *read* active_frame with the lock, then drop it
+ immediately. This is safe because the interrupt handler will never
+ advance active_frame onto a frame that is not READY (and the spinlock
+ must be held while marking a frame READY).
+ */
+ spinlock_t spinlock;
+
+ /*
+ 3) the sleeping semaphore 'sem' - this is used from process context only,
+ to serialize various operations on the video_card. Even though only one
+ open() is allowed, we still need to prevent multiple threads of execution
+ from entering calls like read, write, ioctl, etc.
+
+ I honestly can't think of a good reason to use dv1394 from several threads
+ at once, but we need to serialize anyway to prevent oopses =).
+
+ NOTE: if you need both spinlock and sem, take sem first to avoid deadlock!
+ */
+ struct semaphore sem;
+
+ /* people waiting for buffer space, please form a line here... */
+ wait_queue_head_t waitq;
+
+ /* support asynchronous I/O signals (SIGIO) */
+ struct fasync_struct *fasync;
+
+ /* the large, non-contiguous (rvmalloc()) ringbuffer for DV
+ data, exposed to user-space via mmap() */
+ unsigned char *user_buf;
+ unsigned long user_buf_size;
+ struct dma_region user_dma;
+
+ /* next byte in the ringbuffer that a write() call will fill */
+ size_t write_off;
+
+ struct frame *frames[DV1394_MAX_FRAMES];
+
+ /* n_frames also serves as an indicator that this struct video_card is
+ intialized and ready to run DMA buffers */
+
+ int n_frames;
+
+ /* this is the frame that is currently "owned" by the OHCI DMA controller
+ (set to -1 iff DMA is not running)
+
+ ! must lock against the interrupt handler when accessing it !
+
+ RULES:
+
+ Only the interrupt handler may change active_frame if DMA
+ is running; if not, process may change it
+
+ If the next frame is READY, the interrupt handler will advance
+ active_frame when the current frame is finished.
+
+ If the next frame is CLEAR, the interrupt handler will re-transmit
+ the current frame, and the dropped_frames counter will be incremented.
+
+ The interrupt handler will NEVER advance active_frame to a
+ frame that is not READY.
+
+ */
+ int active_frame;
+ int first_run;
+
+ /* the same locking rules apply to these three fields also: */
+
+ /* altered ONLY from process context. Must check first_clear_frame->state;
+ if it's READY, that means the ringbuffer is full with READY frames;
+ if it's CLEAR, that means one or more ringbuffer frames are CLEAR */
+ unsigned int first_clear_frame;
+
+ /* altered both by process and interrupt */
+ unsigned int n_clear_frames;
+
+ /* only altered by the interrupt */
+ unsigned int dropped_frames;
+
+
+
+ /* the CIP accumulator and continuity counter are properties
+ of the DMA stream as a whole (not a single frame), so they
+ are stored here in the video_card */
+
+ unsigned long cip_accum;
+ unsigned long cip_n, cip_d;
+ unsigned int syt_offset;
+ unsigned int continuity_counter;
+
+ enum pal_or_ntsc pal_or_ntsc;
+
+ /* redundant, but simplifies the code somewhat */
+ unsigned int frame_size; /* in bytes */
+
+ /* the isochronous channel to use, -1 if video card is inactive */
+ int channel;
+
+
+ /* physically contiguous packet ringbuffer for receive */
+#define MAX_PACKET_BUFFER 30
+ struct packet *packet_buffer;
+ dma_addr_t packet_buffer_dma;
+ unsigned long packet_buffer_size;
+
+ unsigned int current_packet;
+ int first_frame; /* received first start frame marker? */
+};
+
+/*
+ if the video_card is not initialized, then the ONLY fields that are valid are:
+ ohci
+ open
+ n_frames
+*/
+
+static inline int video_card_initialized(struct video_card *v)
+{
+ return v->n_frames > 0;
+}
+
+static int do_dv1394_init(struct video_card *video, struct dv1394_init *init);
+static int do_dv1394_init_default(struct video_card *video);
+static int do_dv1394_shutdown(struct video_card *video, int free_user_buf);
+
+
+/* NTSC empty packet rate accurate to within 0.01%,
+ calibrated against a Sony DSR-40 DVCAM deck */
+
+#define CIP_N_NTSC 68000000
+#define CIP_D_NTSC 1000000000
+
+#define CIP_N_PAL 1
+#define CIP_D_PAL 16
+
+#endif /* _DV_1394_PRIVATE_H */
+
--- /dev/null
+/*
+ * dv1394.c - DV input/output over IEEE 1394 on OHCI chips
+ * Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ * receive, proc_fs by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ * video1394.c - video driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ OVERVIEW
+
+ I designed dv1394 as a "pipe" that you can use to shoot DV onto a
+ FireWire bus. In transmission mode, dv1394 does the following:
+
+ 1. accepts contiguous frames of DV data from user-space, via write()
+ or mmap() (see dv1394.h for the complete API)
+ 2. wraps IEC 61883 packets around the DV data, inserting
+ empty synchronization packets as necessary
+ 3. assigns accurate SYT timestamps to the outgoing packets
+ 4. shoots them out using the OHCI card's IT DMA engine
+
+ Thanks to Dan Dennedy, we now have a receive mode that does the following:
+
+ 1. accepts raw IEC 61883 packets from the OHCI card
+ 2. re-assembles the DV data payloads into contiguous frames,
+ discarding empty packets
+ 3. sends the DV data to user-space via read() or mmap()
+*/
+
+/*
+ TODO:
+
+ - expose xmit and recv as separate devices
+
+ - tunable frame-drop behavior: either loop last frame, or halt transmission
+
+ - use a scatter/gather buffer for DMA programs (f->descriptor_pool)
+ so that we don't rely on allocating 64KB of contiguous kernel memory
+ via pci_alloc_consistent()
+
+ DONE:
+ - safely obtain and release ISO Tx channels in cooperation with OHCI driver
+ - map received DIF blocks to their proper location in DV frame (ensure
+ recovery if dropped packet)
+ - handle bus resets gracefully (OHCI card seems to take care of this itself(!))
+ - do not allow resizing the user_buf once allocated; eliminate nuke_buffer_mappings
+ - eliminated #ifdef DV1394_DEBUG_LEVEL by inventing macros debug_printk and irq_printk
+ - added wmb() and mb() to places where PCI read/write ordering needs to be enforced
+ - set video->id correctly
+ - store video_cards in an array indexed by OHCI card ID, rather than a list
+ - implement DMA context allocation to cooperate with other users of the OHCI
+ - fix all XXX showstoppers
+ - disable IR/IT DMA interrupts on shutdown
+ - flush pci writes to the card by issuing a read
+ - devfs and character device dispatching (* needs testing with Linux 2.2.x)
+ - switch over to the new kernel DMA API (pci_map_*()) (* needs testing on platforms with IOMMU!)
+ - keep all video_cards in a list (for open() via chardev), set file->private_data = video
+ - dv1394_poll should indicate POLLIN when receiving buffers are available
+ - add proc fs interface to set cip_n, cip_d, syt_offset, and video signal
+
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <asm/byteorder.h>
+#include <asm/atomic.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+#include <linux/tqueue.h>
+#include <linux/delay.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+#include <linux/wrapper.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"
+#include "dv1394.h"
+#include "dv1394-private.h"
+
+#include "ohci1394.h"
+
+#ifndef virt_to_page
+#define virt_to_page(x) MAP_NR(x)
+#endif
+
+#ifndef vmalloc_32
+#define vmalloc_32(x) vmalloc(x)
+#endif
+
+
+/* DEBUG LEVELS:
+ 0 - no debugging messages
+ 1 - some debugging messages, but none during DMA frame transmission
+ 2 - lots of messages, including during DMA frame transmission
+ (will cause undeflows if your machine is too slow!)
+*/
+
+#define DV1394_DEBUG_LEVEL 0
+
+/* for debugging use ONLY: allow more than one open() of the device */
+/* #define DV1394_ALLOW_MORE_THAN_ONE_OPEN 1 */
+
+#if DV1394_DEBUG_LEVEL >= 2
+#define irq_printk( args... ) printk( args )
+#else
+#define irq_printk( args... )
+#endif
+
+#if DV1394_DEBUG_LEVEL >= 1
+#define debug_printk( args... ) printk( args)
+#else
+#define debug_printk( args... )
+#endif
+
+/* issue a dummy PCI read to force the preceding write
+ to be posted to the PCI bus immediately */
+
+static inline void flush_pci_write(struct ti_ohci *ohci)
+{
+ mb();
+ reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+}
+
+static void irq_handler(int card, quadlet_t isoRecvIntEvent,
+ quadlet_t isoXmitIntEvent, void *data);
+
+
+/* GLOBAL DATA */
+
+/* list of all video_cards */
+static LIST_HEAD(dv1394_cards);
+static spinlock_t dv1394_cards_lock = SPIN_LOCK_UNLOCKED;
+
+static struct hpsb_highlevel *hl_handle; /* = NULL; */
+
+static devfs_handle_t dv1394_devfs_handle;
+
+/* translate from a struct file* to the corresponding struct video_card* */
+
+static inline struct video_card* file_to_video_card(struct file *file)
+{
+ return (struct video_card*) file->private_data;
+}
+
+
+/* Taken from bttv.c */
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+#define MDEBUG(x) do { } while(0) /* Debug memory management */
+
+/* [DaveM] I've recoded most of this so that:
+ * 1) It's easier to tell what is happening
+ * 2) It's more portable, especially for translating things
+ * out of vmalloc mapped areas in the kernel.
+ * 3) Less unnecessary translations happen.
+ *
+ * The code used to assume that the kernel vmalloc mappings
+ * existed in the page tables of every process, this is simply
+ * not guarenteed. We now use pgd_offset_k which is the
+ * defined way to get at the kernel page tables.
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#define page_address(x) (x)
+#endif
+
+/* Given PGD from the address space's page table, return the kernel
+ * virtual mapping of the physical memory mapped at ADR.
+ */
+static inline struct page *uvirt_to_page(pgd_t *pgd, unsigned long adr)
+{
+ pmd_t *pmd;
+ pte_t *ptep, pte;
+ struct page *ret = NULL;
+
+ if (!pgd_none(*pgd)) {
+ pmd = pmd_offset(pgd, adr);
+ if (!pmd_none(*pmd)) {
+ ptep = pte_offset(pmd, adr);
+ pte = *ptep;
+ if(pte_present(pte))
+ ret = pte_page(pte);
+ }
+ }
+ return ret;
+}
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved, and for
+ * handling page faults on the rvmalloc()ed buffer
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+ unsigned long va, kva, ret;
+
+ va = VMALLOC_VMADDR(adr);
+ kva = (unsigned long) page_address(uvirt_to_page(pgd_offset_k(va), va));
+ kva |= adr & (PAGE_SIZE-1); /* restore the offset */
+ ret = __pa(kva);
+ MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret));
+ return ret;
+}
+
+static void * rvmalloc(unsigned long size)
+{
+ void * mem;
+ unsigned long adr, page;
+
+ mem=vmalloc_32(size);
+ if (mem) {
+ memset(mem, 0, size); /* Clear the ram out,
+ no junk to the user */
+ adr=(unsigned long) mem;
+ while (size > 0) {
+ page = kvirt_to_pa(adr);
+ mem_map_reserve(virt_to_page(__va(page)));
+ adr+=PAGE_SIZE;
+ size-=PAGE_SIZE;
+ }
+ }
+ return mem;
+}
+
+static void rvfree(void * mem, unsigned long size)
+{
+ unsigned long adr, page;
+
+ if (mem) {
+ adr=(unsigned long) mem;
+ while (size > 0) {
+ page = kvirt_to_pa(adr);
+ mem_map_unreserve(virt_to_page(__va(page)));
+ adr+=PAGE_SIZE;
+ size-=PAGE_SIZE;
+ }
+ vfree(mem);
+ }
+}
+
+/***********************************/
+/* END Memory management functions */
+/***********************************/
+
+
+/*** FRAME METHODS *********************************************************/
+
+static void frame_reset(struct frame *f)
+{
+ f->state = FRAME_CLEAR;
+ f->done = 0;
+ f->n_packets = 0;
+ f->frame_begin_timestamp = NULL;
+ f->assigned_timestamp = 0;
+ f->cip_syt1 = NULL;
+ f->cip_syt2 = NULL;
+ f->mid_frame_timestamp = NULL;
+ f->frame_end_timestamp = NULL;
+ f->frame_end_branch = NULL;
+}
+
+static struct frame* frame_new(unsigned int frame_num, struct video_card *video)
+{
+ struct frame *f = kmalloc(sizeof(*f), GFP_KERNEL);
+ if(!f)
+ return NULL;
+
+ f->video = video;
+ f->frame_num = frame_num;
+
+ f->header_pool = pci_alloc_consistent(f->video->ohci->dev, PAGE_SIZE, &f->header_pool_dma);
+ if(!f->header_pool) {
+ printk(KERN_ERR "dv1394: failed to allocate CIP header pool\n");
+ kfree(f);
+ return NULL;
+ }
+
+ debug_printk("dv1394: frame_new: allocated CIP header pool at virt 0x%08lx (contig) dma 0x%08lx size %ld\n",
+ (unsigned long) f->header_pool, (unsigned long) f->header_pool_dma, PAGE_SIZE);
+
+ f->descriptor_pool_size = MAX_PACKETS * sizeof(struct DMA_descriptor_block);
+ /* make it an even # of pages */
+ f->descriptor_pool_size += PAGE_SIZE - (f->descriptor_pool_size%PAGE_SIZE);
+
+ f->descriptor_pool = pci_alloc_consistent(f->video->ohci->dev,
+ f->descriptor_pool_size,
+ &f->descriptor_pool_dma);
+ if(!f->descriptor_pool) {
+ pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma);
+ kfree(f);
+ return NULL;
+ }
+
+ debug_printk("dv1394: frame_new: allocated DMA program memory at virt 0x%08lx (contig) dma 0x%08lx size %ld\n",
+ (unsigned long) f->descriptor_pool, (unsigned long) f->descriptor_pool_dma, f->descriptor_pool_size);
+
+ f->data = 0;
+ frame_reset(f);
+
+ return f;
+}
+
+static void frame_delete(struct frame *f)
+{
+ pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma);
+ pci_free_consistent(f->video->ohci->dev, f->descriptor_pool_size, f->descriptor_pool, f->descriptor_pool_dma);
+ kfree(f);
+}
+
+
+
+
+/*
+ frame_prepare() - build the DMA program for transmitting
+
+ Frame_prepare() must be called OUTSIDE the video->spinlock.
+ However, frame_prepare() must still be serialized, so
+ it should be called WITH the video->sem taken.
+ */
+
+static void frame_prepare(struct video_card *video, unsigned int this_frame)
+{
+ struct frame *f = video->frames[this_frame];
+ int last_frame;
+
+ struct DMA_descriptor_block *block;
+ dma_addr_t block_dma;
+ struct CIP_header *cip;
+ dma_addr_t cip_dma;
+
+ unsigned int n_descriptors, full_packets, packets_per_frame, payload_size;
+
+ /* these flags denote packets that need special attention */
+ int empty_packet, first_packet, last_packet, mid_packet;
+
+ u32 *branch_address, *last_branch_address = NULL;
+ unsigned long data_p;
+ int first_packet_empty = 0;
+ u32 cycleTimer, ct_sec, ct_cyc, ct_off;
+ unsigned long irq_flags;
+
+ irq_printk("frame_prepare( %d ) ---------------------\n", this_frame);
+
+ full_packets = 0;
+
+
+
+ if(video->pal_or_ntsc == DV1394_PAL)
+ packets_per_frame = DV1394_PAL_PACKETS_PER_FRAME;
+ else
+ packets_per_frame = DV1394_NTSC_PACKETS_PER_FRAME;
+
+ while( full_packets < packets_per_frame ) {
+ empty_packet = first_packet = last_packet = mid_packet = 0;
+
+ data_p = f->data + full_packets * 480;
+
+ /************************************************/
+ /* allocate a descriptor block and a CIP header */
+ /************************************************/
+
+ /* note: these should NOT cross a page boundary (DMA restriction) */
+
+ if(f->n_packets >= MAX_PACKETS) {
+ printk(KERN_ERR "dv1394: FATAL ERROR: max packet count exceeded\n");
+ return;
+ }
+
+ /* the block surely won't cross a page boundary,
+ since an even number of descriptor_blocks fit on a page */
+ block = &(f->descriptor_pool[f->n_packets]);
+
+ /* DMA address of the block = offset of block relative
+ to the kernel base address of the descriptor pool
+ + DMA base address of the descriptor pool */
+ block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
+
+
+ /* the whole CIP pool fits on one page, so no worries about boundaries */
+ if( ((unsigned long) &(f->header_pool[f->n_packets]) - (unsigned long) f->header_pool)
+ > PAGE_SIZE) {
+ printk(KERN_ERR "dv1394: FATAL ERROR: no room to allocate CIP header\n");
+ return;
+ }
+
+ cip = &(f->header_pool[f->n_packets]);
+
+ /* DMA address of the CIP header = offset of cip
+ relative to kernel base address of the header pool
+ + DMA base address of the header pool */
+ cip_dma = (unsigned long) cip % PAGE_SIZE + f->header_pool_dma;
+
+ /* is this an empty packet? */
+
+ if(video->cip_accum > video->cip_d) {
+ empty_packet = 1;
+ payload_size = 8;
+ video->cip_accum -= video->cip_d;
+ } else {
+ payload_size = 488;
+ video->cip_accum += video->cip_n;
+ }
+
+ /* there are three important packets each frame:
+
+ the first packet in the frame - we ask the card to record the timestamp when
+ this packet is actually sent, so we can monitor
+ how accurate our timestamps are. Also, the first
+ packet serves as a semaphore to let us know that
+ it's OK to free the *previous* frame's DMA buffer
+
+ the last packet in the frame - this packet is used to detect buffer underflows.
+ if this is the last ready frame, the last DMA block
+ will have a branch back to the beginning of the frame
+ (so that the card will re-send the frame on underflow).
+ if this branch gets taken, we know that at least one
+ frame has been dropped. When the next frame is ready,
+ the branch is pointed to its first packet, and the
+ semaphore is disabled.
+
+ a "mid" packet slightly before the end of the frame - this packet should trigger
+ an interrupt so we can go and assign a timestamp to the first packet
+ in the next frame. We don't use the very last packet in the frame
+ for this purpose, because that would leave very little time to set
+ the timestamp before DMA starts on the next frame.
+ */
+
+ if(f->n_packets == 0) {
+ first_packet = 1;
+ } else if ( full_packets == (packets_per_frame-1) ) {
+ last_packet = 1;
+ } else if (f->n_packets == packets_per_frame) {
+ mid_packet = 1;
+ }
+
+
+ /********************/
+ /* setup CIP header */
+ /********************/
+
+ /* the timestamp will be written later from the
+ mid-frame interrupt handler. For now we just
+ store the address of the CIP header(s) that
+ need a timestamp. */
+
+ /* first packet in the frame needs a timestamp */
+ if(first_packet) {
+ f->cip_syt1 = cip;
+ if(empty_packet)
+ first_packet_empty = 1;
+
+ } else if(first_packet_empty && (f->n_packets == 1) ) {
+ /* if the first packet was empty, the second
+ packet's CIP header also needs a timestamp */
+ f->cip_syt2 = cip;
+ }
+
+ fill_cip_header(cip,
+ /* the node ID number of the OHCI card */
+ reg_read(video->ohci, OHCI1394_NodeID) & 0x3F,
+ video->continuity_counter,
+ video->pal_or_ntsc,
+ 0xFFFF /* the timestamp is filled in later */);
+
+ /* advance counter, only for full packets */
+ if( ! empty_packet )
+ video->continuity_counter++;
+
+ /******************************/
+ /* setup DMA descriptor block */
+ /******************************/
+
+ /* first descriptor - OUTPUT_MORE_IMMEDIATE, for the controller's IT header */
+ fill_output_more_immediate( &(block->u.out.omi),
+ /* tag - what is this??? */ 1,
+ video->channel,
+ /* sync tag - what is this??? */ 0,
+ payload_size);
+
+ if(empty_packet) {
+ /* second descriptor - OUTPUT_LAST for CIP header */
+ fill_output_last( &(block->u.out.u.empty.ol),
+
+ /* want completion status on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ /* want interrupts on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ sizeof(struct CIP_header), /* data size */
+ cip_dma);
+
+ if(first_packet)
+ f->frame_begin_timestamp = &(block->u.out.u.empty.ol.q[3]);
+ else if(mid_packet)
+ f->mid_frame_timestamp = &(block->u.out.u.empty.ol.q[3]);
+ else if(last_packet) {
+ f->frame_end_timestamp = &(block->u.out.u.empty.ol.q[3]);
+ f->frame_end_branch = &(block->u.out.u.empty.ol.q[2]);
+ }
+
+ branch_address = &(block->u.out.u.empty.ol.q[2]);
+ n_descriptors = 3;
+ if(first_packet)
+ f->first_n_descriptors = n_descriptors;
+
+ } else { /* full packet */
+
+ /* second descriptor - OUTPUT_MORE for CIP header */
+ fill_output_more( &(block->u.out.u.full.om),
+ sizeof(struct CIP_header), /* data size */
+ cip_dma);
+
+
+ /* third (and possibly fourth) descriptor - for DV data */
+ /* the 480-byte payload can cross a page boundary; if so,
+ we need to split it into two DMA descriptors */
+
+ /* does the 480-byte data payload cross a page boundary? */
+ if( (PAGE_SIZE- ((unsigned long)data_p % PAGE_SIZE) ) < 480 ) {
+
+ /* page boundary crossed */
+
+ fill_output_more( &(block->u.out.u.full.u.cross.om),
+ /* data size - how much of data_p fits on the first page */
+ PAGE_SIZE - (data_p % PAGE_SIZE),
+
+ /* DMA address of data_p */
+ dma_offset_to_bus(&f->video->user_dma,
+ data_p - (unsigned long) f->video->user_buf));
+
+ fill_output_last( &(block->u.out.u.full.u.cross.ol),
+
+ /* want completion status on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ /* want interrupt on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ /* data size - remaining portion of data_p */
+ 480 - (PAGE_SIZE - (data_p % PAGE_SIZE)),
+
+ /* DMA address of data_p + PAGE_SIZE - (data_p % PAGE_SIZE) */
+ dma_offset_to_bus(&f->video->user_dma,
+ data_p + PAGE_SIZE - (data_p % PAGE_SIZE) - (unsigned long) f->video->user_buf));
+
+ if(first_packet)
+ f->frame_begin_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
+ else if(mid_packet)
+ f->mid_frame_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
+ else if(last_packet) {
+ f->frame_end_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
+ f->frame_end_branch = &(block->u.out.u.full.u.cross.ol.q[2]);
+ }
+
+ branch_address = &(block->u.out.u.full.u.cross.ol.q[2]);
+
+ n_descriptors = 5;
+ if(first_packet)
+ f->first_n_descriptors = n_descriptors;
+
+ full_packets++;
+
+ } else {
+ /* fits on one page */
+
+ fill_output_last( &(block->u.out.u.full.u.nocross.ol),
+
+ /* want completion status on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ /* want interrupt on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ 480, /* data size (480 bytes of DV data) */
+
+
+ /* DMA address of data_p */
+ dma_offset_to_bus(&f->video->user_dma,
+ data_p - (unsigned long) f->video->user_buf));
+
+ if(first_packet)
+ f->frame_begin_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
+ else if(mid_packet)
+ f->mid_frame_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
+ else if(last_packet) {
+ f->frame_end_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
+ f->frame_end_branch = &(block->u.out.u.full.u.nocross.ol.q[2]);
+ }
+
+ branch_address = &(block->u.out.u.full.u.nocross.ol.q[2]);
+
+ n_descriptors = 4;
+ if(first_packet)
+ f->first_n_descriptors = n_descriptors;
+
+ full_packets++;
+ }
+ }
+
+ /* link this descriptor block into the DMA program by filling in
+ the branch address of the previous block */
+
+ /* note: we are not linked into the active DMA chain yet */
+
+ if(last_branch_address) {
+ *(last_branch_address) = block_dma | n_descriptors;
+ }
+
+ last_branch_address = branch_address;
+
+
+ f->n_packets++;
+
+ }
+
+ /* when we first assemble a new frame, set the final branch
+ to loop back up to the top */
+ *(f->frame_end_branch) = f->descriptor_pool_dma | f->first_n_descriptors;
+
+
+ /* make the latest version of the frame buffer visible to the PCI card */
+ /* could optimize this by only syncing the pages associated with this frame */
+ pci_dma_sync_sg(video->ohci->dev,
+ &video->user_dma.sglist[0],
+ video->user_dma.n_dma_pages,
+ PCI_DMA_TODEVICE);
+
+ /* lock against DMA interrupt */
+ spin_lock_irqsave(&video->spinlock, irq_flags);
+
+ f->state = FRAME_READY;
+
+ video->n_clear_frames--;
+
+ last_frame = video->first_clear_frame - 1;
+ if(last_frame == -1)
+ last_frame = video->n_frames-1;
+
+ video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
+
+ irq_printk(" frame %d prepared, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n last=%d\n",
+ this_frame, video->active_frame, video->n_clear_frames, video->first_clear_frame, last_frame);
+
+ irq_printk(" begin_ts %08lx mid_ts %08lx end_ts %08lx end_br %08lx\n",
+ (unsigned long) f->frame_begin_timestamp,
+ (unsigned long) f->mid_frame_timestamp,
+ (unsigned long) f->frame_end_timestamp,
+ (unsigned long) f->frame_end_branch);
+
+ if(video->active_frame != -1) {
+
+ /* if DMA is already active, we are almost done */
+ /* just link us onto the active DMA chain */
+ if(video->frames[last_frame]->frame_end_branch) {
+
+ /* point the previous frame's tail to this frame's head */
+ *(video->frames[last_frame]->frame_end_branch) = f->descriptor_pool_dma | f->first_n_descriptors;
+
+ /* this write MUST precede the next one, or we could silently drop frames */
+ wmb();
+
+ /* disable the want_status semaphore on the last packet */
+ *(video->frames[last_frame]->frame_end_branch - 2) &= 0xF7CFFFFF;
+
+ /* flush these writes to memory ASAP */
+ flush_pci_write(video->ohci);
+
+ /* NOTE:
+ ideally the writes should be "atomic": if
+ the OHCI card reads the want_status flag in
+ between them, we'll falsely report a
+ dropped frame. Hopefully this window is too
+ small to really matter, and the consequence
+ is rather harmless. */
+
+
+ irq_printk(" new frame %d linked onto DMA chain\n", this_frame);
+
+ } else {
+ printk(KERN_ERR "dv1394: last frame not ready???\n");
+ }
+
+ } else {
+
+ u32 transmit_sec, transmit_cyc;
+ u32 ts_cyc, ts_off;
+
+ /* DMA is stopped, so this is the very first frame */
+ video->active_frame = this_frame;
+
+ /* set CommandPtr to address and size of first descriptor block */
+ reg_write(video->ohci, video->ohci_IsoXmitCommandPtr,
+ video->frames[video->active_frame]->descriptor_pool_dma |
+ f->first_n_descriptors);
+
+ /* assign a timestamp based on the current cycle time...
+ We'll tell the card to begin DMA 100 cycles from now,
+ and assign a timestamp 103 cycles from now */
+
+ cycleTimer = reg_read(video->ohci, OHCI1394_IsochronousCycleTimer);
+
+ ct_sec = cycleTimer >> 25;
+ ct_cyc = (cycleTimer >> 12) & 0x1FFF;
+ ct_off = cycleTimer & 0xFFF;
+
+ transmit_sec = ct_sec;
+ transmit_cyc = ct_cyc + 100;
+
+ transmit_sec += transmit_cyc/8000;
+ transmit_cyc %= 8000;
+
+ ts_off = ct_off;
+ ts_cyc = transmit_cyc + 3;
+ ts_cyc %= 8000;
+
+ f->assigned_timestamp = (ts_cyc&0xF) << 12;
+
+ /* now actually write the timestamp into the appropriate CIP headers */
+ if(f->cip_syt1) {
+ f->cip_syt1->b[6] = f->assigned_timestamp >> 8;
+ f->cip_syt1->b[7] = f->assigned_timestamp & 0xFF;
+ }
+ if(f->cip_syt2) {
+ f->cip_syt2->b[6] = f->assigned_timestamp >> 8;
+ f->cip_syt2->b[7] = f->assigned_timestamp & 0xFF;
+ }
+
+ /* --- start DMA --- */
+
+ /* clear all bits in ContextControl register */
+
+ reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, 0xFFFFFFFF);
+ wmb();
+
+ /* the OHCI card has the ability to start ISO transmission on a
+ particular cycle (start-on-cycle). This way we can ensure that
+ the first DV frame will have an accurate timestamp.
+
+ However, start-on-cycle only appears to work if the OHCI card
+ is cycle master! Since the consequences of messing up the first
+ timestamp are minimal*, just disable start-on-cycle for now.
+
+ * my DV deck drops the first few frames before it "locks in;"
+ so the first frame having an incorrect timestamp is inconsequential.
+ */
+
+#if 0
+ reg_write(video->ohci, video->ohci_IsoXmitContextControlSet,
+ (1 << 31) /* enable start-on-cycle */
+ | ( (transmit_sec & 0x3) << 29)
+ | (transmit_cyc << 16));
+ wmb();
+#endif
+
+
+
+ /* set the 'run' bit */
+ reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, 0x8000);
+ flush_pci_write(video->ohci);
+
+ /* --- DMA should be running now --- */
+
+ debug_printk(" Cycle = %4u ContextControl = %08x CmdPtr = %08x\n",
+ (reg_read(video->ohci, OHCI1394_IsochronousCycleTimer) >> 12) & 0x1FFF,
+ reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
+ reg_read(video->ohci, video->ohci_IsoXmitCommandPtr));
+
+ debug_printk(" DMA start - current cycle %4u, transmit cycle %4u (%2u), assigning ts cycle %2u\n",
+ ct_cyc, transmit_cyc, transmit_cyc & 0xF, ts_cyc & 0xF);
+
+#if DV1394_DEBUG_LEVEL >= 2
+ {
+ /* check if DMA is really running */
+ int i = 0;
+ while(i < 20) {
+ mb();
+ mdelay(1);
+ if(reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) {
+ printk("DMA ACTIVE after %d msec\n", i);
+ break;
+ }
+ i++;
+ }
+
+ printk("set = %08x, cmdPtr = %08x\n",
+ reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
+ reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)
+ );
+
+ if( ! (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) {
+ printk("DMA did NOT go active after 20ms, event = %x\n",
+ reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & 0x1F);
+ } else
+ printk("DMA is RUNNING!\n");
+ }
+#endif
+
+ }
+
+
+ spin_unlock_irqrestore(&video->spinlock, irq_flags);
+}
+
+
+
+/*** RECEIVE FUNCTIONS *****************************************************/
+
+/*
+ frame method put_packet
+
+ map and copy the packet data to its location in the frame
+ based upon DIF section and sequence
+*/
+
+static void inline
+frame_put_packet (struct frame *f, struct packet *p)
+{
+ int section_type = p->data[0] >> 5; /* section type is in bits 5 - 7 */
+ int dif_sequence = p->data[1] >> 4; /* dif sequence number is in bits 4 - 7 */
+ int dif_block = p->data[2];
+
+ switch (section_type) {
+ case 0: /* 1 Header block */
+ memcpy( (void *) f->data + dif_sequence * 150 * 80, p->data, 480);
+ break;
+
+ case 1: /* 2 Subcode blocks */
+ memcpy( (void *) f->data + dif_sequence * 150 * 80 + (1 + dif_block) * 80, p->data, 480);
+ break;
+
+ case 2: /* 3 VAUX blocks */
+ memcpy( (void *) f->data + dif_sequence * 150 * 80 + (3 + dif_block) * 80, p->data, 480);
+ break;
+
+ case 3: /* 9 Audio blocks interleaved with video */
+ memcpy( (void *) f->data + dif_sequence * 150 * 80 + (6 + dif_block * 16) * 80, p->data, 480);
+ break;
+
+ case 4: /* 135 Video blocks interleaved with audio */
+ memcpy( (void *) f->data + dif_sequence * 150 * 80 + (7 + (dif_block / 15) + dif_block) * 80, p->data, 480);
+ break;
+
+ default: /* we can not handle any other data */
+ break;
+ }
+}
+
+
+static void start_dma_receive(struct video_card *video, struct frame *frame)
+{
+ /* reset iso recv control register */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, 0xFFFFFFFF);
+ wmb();
+
+ /* clear bufferFill, set isochHeader and speed (0=100) */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x40000000);
+
+ /* match on all tags, listen on channel */
+ reg_write(video->ohci, video->ohci_IsoRcvContextMatch, 0xf0000000 | video->channel);
+
+ /* address and first descriptor block + Z=1 */
+ reg_write(video->ohci, video->ohci_IsoRcvCommandPtr,
+ frame->descriptor_pool_dma | 1); /* Z=1 */
+ wmb();
+
+ /* run */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x8000);
+ flush_pci_write(video->ohci);
+
+ debug_printk("dv1394: DMA started\n");
+
+#if DV1394_DEBUG_LEVEL >= 2
+ {
+ int i;
+
+ for(i = 0; i < 1000; ++i) {
+ mdelay(1);
+ if(reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) {
+ printk("DMA ACTIVE after %d msec\n", i);
+ break;
+ }
+ }
+ if( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) {
+ printk("DEAD, event = %x\n",
+ reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F);
+ } else
+ printk("RUNNING!\n");
+ }
+#endif
+}
+
+
+/*
+ receive_packets() - build the DMA program for receiving
+*/
+
+static void receive_packets(struct video_card *video, struct frame *f)
+{
+ struct DMA_descriptor_block *block = NULL;
+ dma_addr_t block_dma = 0;
+ struct packet *data = NULL;
+ dma_addr_t data_dma = 0;
+ u32 *last_branch_address = NULL;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&video->spinlock, irq_flags);
+
+ video->n_clear_frames = 0;
+ video->first_clear_frame = -1;
+
+ for (video->current_packet = 0; video->current_packet < MAX_PACKET_BUFFER; ++video->current_packet) {
+ /* locate a descriptor block and packet from the buffer */
+ block = &(f->descriptor_pool[video->current_packet]);
+ block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
+
+ data = &(video->packet_buffer[video->current_packet]);
+ data_dma = ((unsigned long) data - (unsigned long) video->packet_buffer) + video->packet_buffer_dma;
+
+ /* setup DMA descriptor block */
+ fill_input_last( &(block->u.in.il), 512, data_dma);
+
+ /* link descriptors */
+ last_branch_address = f->frame_end_branch;
+
+ if (last_branch_address)
+ *(last_branch_address) = block_dma | 1; /* set Z=1 */
+
+ f->frame_end_branch = &(block->u.in.il.q[2]);
+ }
+
+ /* loop tail to head */
+ if (f->frame_end_branch)
+ *(f->frame_end_branch) = f->descriptor_pool_dma | 1; /* set Z=1 */
+
+ spin_unlock_irqrestore(&video->spinlock, irq_flags);
+
+ if (video->first_run) {
+ /* start DMA once all of the frames are READY */
+ video->first_run = 0;
+ video->current_packet = 0;
+ video->active_frame = f->frame_num;
+ start_dma_receive(video, f);
+ }
+ else if( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) {
+ debug_printk("DEAD, event = %x\n",
+ reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F);
+
+ /* wake */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12));
+ }
+}
+
+
+
+/*** MANAGEMENT FUNCTIONS **************************************************/
+
+static int do_dv1394_init(struct video_card *video, struct dv1394_init *init)
+{
+ unsigned long flags, new_buf_size;
+ int i;
+ u64 chan_mask;
+ int retval = -EINVAL;
+
+ if(init->api_version != DV1394_API_VERSION)
+ goto err;
+
+ /* first sanitize all the parameters */
+ if( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) )
+ goto err;
+
+ if( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) )
+ goto err;
+
+ if( (init->syt_offset == 0) || (init->syt_offset > 50) )
+ /* default SYT offset is 3 cycles */
+ init->syt_offset = 3;
+
+ if( (init->channel > 63) || (init->channel < 0) )
+ init->channel = 63;
+
+ chan_mask = (u64)1 << init->channel;
+
+ /* calculate what size DMA buffer is needed */
+ if(init->format == DV1394_NTSC)
+ new_buf_size = DV1394_NTSC_FRAME_SIZE * init->n_frames;
+ else
+ new_buf_size = DV1394_PAL_FRAME_SIZE * init->n_frames;
+
+ /* round up to PAGE_SIZE */
+ if(new_buf_size % PAGE_SIZE) new_buf_size += PAGE_SIZE - (new_buf_size % PAGE_SIZE);
+
+ /* don't allow the user to allocate the DMA buffer more than once */
+ if( (video->user_buf) &&
+ (video->user_buf_size != new_buf_size) ) {
+ goto err;
+ }
+
+ /* shutdown the card if it's currently active */
+ /* (the card should not be reset if the parameters are screwy) */
+ if( video_card_initialized(video) )
+ do_dv1394_shutdown(video, 0);
+
+
+ /* try to claim the ISO channel */
+ spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
+ if(video->ohci->ISO_channel_usage & chan_mask) {
+ spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+ retval = -EBUSY;
+ goto err;
+ }
+ video->ohci->ISO_channel_usage |= chan_mask;
+ spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+
+ video->channel = init->channel;
+
+
+ /* find and claim DMA contexts on the OHCI card */
+
+ if(video->ohci_it_ctx == -1) {
+
+ for(i = 0; i < video->ohci->nb_iso_xmit_ctx; i++) {
+
+ if(! test_and_set_bit(i, &video->ohci->it_ctx_usage)) {
+ video->ohci_it_ctx = i;
+ debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx);
+ break;
+ }
+ }
+
+ if(i == video->ohci->nb_iso_xmit_ctx) {
+ printk(KERN_ERR "dv1394: could not find an available IT DMA context\n");
+ retval = -EBUSY;
+ goto err_ctx;
+ }
+ }
+
+
+ if(video->ohci_ir_ctx == -1) {
+ for(i = 0; i < video->ohci->nb_iso_rcv_ctx; i++) {
+
+ if(! test_and_set_bit(i, &video->ohci->ir_ctx_usage)) {
+ video->ohci_ir_ctx = i;
+ debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx);
+ break;
+ }
+ }
+
+ if(i == video->ohci->nb_iso_rcv_ctx) {
+ printk(KERN_ERR "dv1394: could not find an available IR DMA context\n");
+ retval = -EBUSY;
+ goto err_ctx;
+ }
+ }
+
+
+ /* allocate struct frames */
+ for(i = 0; i < init->n_frames; i++) {
+ video->frames[i] = frame_new(i, video);
+
+ if(!video->frames[i]) {
+ printk(KERN_ERR "dv1394: Cannot allocate frame structs\n");
+ retval = -ENOMEM;
+ goto err_frames;
+ }
+ }
+
+ /* initialize misc. fields of video */
+ video->n_frames = init->n_frames;
+ video->pal_or_ntsc = init->format;
+
+
+ video->cip_accum = 0;
+ video->continuity_counter = 0;
+
+ video->active_frame = -1;
+ video->first_clear_frame = 0;
+ video->n_clear_frames = video->n_frames;
+ video->dropped_frames = 0;
+
+ video->write_off = 0;
+
+ video->first_run = 1;
+ video->current_packet = -1;
+ video->first_frame = 0;
+
+
+ if(video->pal_or_ntsc == DV1394_NTSC) {
+ video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_NTSC;
+ video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_NTSC;
+ video->frame_size = DV1394_NTSC_FRAME_SIZE;
+ } else {
+ video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_PAL;
+ video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_PAL;
+ video->frame_size = DV1394_PAL_FRAME_SIZE;
+ }
+
+ video->syt_offset = init->syt_offset;
+
+ if(video->user_buf == NULL) {
+ unsigned int i;
+
+ /* allocate the ringbuffer */
+ video->user_buf = rvmalloc(new_buf_size);
+ if(!video->user_buf) {
+ printk(KERN_ERR "dv1394: Cannot allocate frame buffers\n");
+ goto err_frames;
+ }
+ video->user_buf_size = new_buf_size;
+
+ /* allocate the sglist to hold the DMA addresses */
+ video->user_dma.n_pages = video->user_buf_size / PAGE_SIZE;
+ video->user_dma.sglist = kmalloc(video->user_dma.n_pages * sizeof(struct scatterlist), GFP_KERNEL);
+ if(!video->user_dma.sglist) {
+ printk(KERN_ERR "dv1394: Cannot allocate sglist for user buffer\n");
+ goto err_user_buf;
+ }
+
+ /* initialize all fields of all sglist entries to zero
+ (new requirement due to PCI changes in 2.4.13) */
+
+ memset(video->user_dma.sglist, 0, video->user_dma.n_pages * sizeof(struct scatterlist));
+
+
+ /* fill the sglist with the kernel addresses of pages in the non-contiguous buffer */
+ for(i = 0; i < video->user_dma.n_pages; i++) {
+ unsigned long va = VMALLOC_VMADDR( (unsigned long) video->user_buf + i * PAGE_SIZE );
+
+ video->user_dma.sglist[i].page = uvirt_to_page(pgd_offset_k(va), va);
+ video->user_dma.sglist[i].length = PAGE_SIZE;
+ }
+
+ /* map the buffer in the IOMMU */
+ /* the user_data buffer only allows DMA *to* the card for transmission;
+ incoming DV data comes through the packet_buffer first, and then is copied to user_data */
+ video->user_dma.n_dma_pages = pci_map_sg(video->ohci->dev,
+ &video->user_dma.sglist[0],
+ video->user_dma.n_pages,
+ PCI_DMA_TODEVICE);
+ if(video->user_dma.n_dma_pages == 0) {
+ printk(KERN_ERR "dv1394: Error mapping user buffer to the IOMMU\n");
+ goto err_user_buf;
+ }
+
+ debug_printk("dv1394: Allocated %d frame buffers, total %u pages (%u DMA pages), %lu bytes\n",
+ video->n_frames, video->user_dma.n_pages,
+ video->user_dma.n_dma_pages, video->user_buf_size);
+ }
+
+ /* set up the frame->data pointers */
+ for(i = 0; i < video->n_frames; i++)
+ video->frames[i]->data = (unsigned long) video->user_buf + i * video->frame_size;
+
+ /* allocate packet buffers */
+ video->packet_buffer_size = sizeof(struct packet) * MAX_PACKET_BUFFER;
+ if (video->packet_buffer_size % PAGE_SIZE)
+ video->packet_buffer_size += PAGE_SIZE - (video->packet_buffer_size % PAGE_SIZE);
+
+
+ video->packet_buffer = kmalloc(video->packet_buffer_size, GFP_KERNEL);
+
+ if(!video->packet_buffer) {
+ printk(KERN_ERR "dv1394: Cannot allocate packet buffers");
+ retval = -ENOMEM;
+ goto err_user_buf;
+ }
+
+ /* map the packet buffer into the IOMMU */
+ video->packet_buffer_dma = pci_map_single(video->ohci->dev,
+ video->packet_buffer,
+ video->packet_buffer_size,
+ PCI_DMA_FROMDEVICE);
+ if(!video->packet_buffer_dma) {
+ printk(KERN_ERR "dv1394: Cannot map packet buffer to IOMMU");
+ kfree(video->packet_buffer);
+ video->packet_buffer = NULL;
+ retval = -ENOMEM;
+ goto err_user_buf;
+ }
+
+ debug_printk("dv1394: Allocated %d packet buffers for receive, total %lu bytes\n",
+ MAX_PACKET_BUFFER, video->packet_buffer_size);
+
+
+ /* set up register offsets for IT context */
+ /* IT DMA context registers are spaced 16 bytes apart */
+ video->ohci_IsoXmitContextControlSet = OHCI1394_IsoXmitContextControlSet+16*video->ohci_it_ctx;
+ video->ohci_IsoXmitContextControlClear = OHCI1394_IsoXmitContextControlClear+16*video->ohci_it_ctx;
+ video->ohci_IsoXmitCommandPtr = OHCI1394_IsoXmitCommandPtr+16*video->ohci_it_ctx;
+
+ /* enable interrupts for IT context */
+ reg_write(video->ohci, OHCI1394_IsoXmitIntMaskSet, (1 << video->ohci_it_ctx));
+ debug_printk("dv1394: interrupts enabled for IT context %d\n", video->ohci_it_ctx);
+
+ /* set up register offsets for IR context */
+ /* IR DMA context registers are spaced 32 bytes apart */
+ video->ohci_IsoRcvContextControlSet = OHCI1394_IsoRcvContextControlSet+32*video->ohci_ir_ctx;
+ video->ohci_IsoRcvContextControlClear = OHCI1394_IsoRcvContextControlClear+32*video->ohci_ir_ctx;
+ video->ohci_IsoRcvCommandPtr = OHCI1394_IsoRcvCommandPtr+32*video->ohci_ir_ctx;
+ video->ohci_IsoRcvContextMatch = OHCI1394_IsoRcvContextMatch+32*video->ohci_ir_ctx;
+
+ /* enable interrupts for IR context */
+ reg_write(video->ohci, OHCI1394_IsoRecvIntMaskSet, (1 << video->ohci_ir_ctx) );
+ debug_printk("dv1394: interrupts enabled for IR context %d\n", video->ohci_ir_ctx);
+
+ return 0;
+
+ err_user_buf:
+ if(video->user_buf) {
+ if(video->user_dma.sglist) {
+ if(video->user_dma.n_dma_pages > 0) {
+ /* unmap it from the IOMMU */
+ pci_unmap_sg(video->ohci->dev,
+ video->user_dma.sglist,
+ video->user_dma.n_pages,
+ PCI_DMA_TODEVICE);
+ video->user_dma.n_dma_pages = 0;
+ }
+ kfree(video->user_dma.sglist);
+ video->user_dma.sglist = NULL;
+ video->user_dma.n_pages = 0;
+ }
+ rvfree(video->user_buf, video->user_buf_size);
+ video->user_buf = NULL;
+ video->user_buf_size = 0;
+ }
+
+ err_frames:
+ for(i = 0; i < DV1394_MAX_FRAMES; i++) {
+ if(video->frames[i])
+ frame_delete(video->frames[i]);
+ }
+ video->n_frames = 0;
+
+ err_ctx:
+ if(video->ohci_it_ctx != -1) {
+ clear_bit(video->ohci_it_ctx, &video->ohci->it_ctx_usage);
+ video->ohci_it_ctx = -1;
+ }
+ if(video->ohci_ir_ctx != -1) {
+ clear_bit(video->ohci_ir_ctx, &video->ohci->ir_ctx_usage);
+ video->ohci_ir_ctx = -1;
+ }
+
+ spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
+ video->ohci->ISO_channel_usage &= ~chan_mask;
+ spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+ err:
+ return retval;
+}
+
+/* if the user doesn't bother to call ioctl(INIT) before starting
+ mmap() or read()/write(), just give him some default values */
+
+static int do_dv1394_init_default(struct video_card *video)
+{
+ struct dv1394_init init;
+
+ init.api_version = DV1394_API_VERSION;
+ init.channel = 63;
+ init.n_frames = 2;
+ /* the following are now set via proc_fs */
+ init.format = video->pal_or_ntsc;
+ init.cip_n = video->cip_n;
+ init.cip_d = video->cip_d;
+ init.syt_offset = video->syt_offset;
+
+ return do_dv1394_init(video, &init);
+}
+
+/* do NOT call from interrupt context */
+static void stop_dma(struct video_card *video)
+{
+ unsigned long flags;
+ int i;
+
+ /* no interrupts */
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ /* stop DMA if in progress */
+ if( (video->active_frame != -1) ||
+ (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
+ (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) {
+
+ /* clear the .run bits */
+ reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15));
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15));
+ flush_pci_write(video->ohci);
+
+ video->active_frame = -1;
+ video->first_run = 1;
+
+
+ /* wait until DMA really stops */
+ i = 0;
+ while(i < 1000) {
+
+ /* wait 0.1 millisecond */
+ udelay(100);
+
+ if( (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
+ (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) {
+ /* still active */
+ mb();
+ } else {
+ debug_printk("dv1394: stop_dma: DMA stopped safely after %d ms\n", i/10);
+ break;
+ }
+
+ i++;
+ }
+
+ if(i == 1000) {
+ printk(KERN_ERR "dv1394: stop_dma: DMA still going after %d ms!\n", i/10);
+ }
+ }
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+}
+
+
+
+static int do_dv1394_shutdown(struct video_card *video, int free_user_buf)
+{
+ int i;
+
+ debug_printk("dv1394: shutdown...\n");
+
+ /* stop DMA if in progress */
+ stop_dma(video);
+
+ /* release the ISO channel */
+ if(video->channel != -1) {
+ u64 chan_mask;
+ unsigned long flags;
+
+ chan_mask = (u64)1 << video->channel;
+
+ spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
+ video->ohci->ISO_channel_usage &= ~(chan_mask);
+ spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+
+ video->channel = -1;
+ }
+
+ /* release the DMA contexts */
+ if(video->ohci_it_ctx != -1) {
+ video->ohci_IsoXmitContextControlSet = 0;
+ video->ohci_IsoXmitContextControlClear = 0;
+ video->ohci_IsoXmitCommandPtr = 0;
+
+ /* disable interrupts for IT context */
+ reg_write(video->ohci, OHCI1394_IsoXmitIntMaskClear, (1 << video->ohci_it_ctx));
+
+ clear_bit(video->ohci_it_ctx, &video->ohci->it_ctx_usage);
+ debug_printk("dv1394: IT context %d released\n", video->ohci_it_ctx);
+ video->ohci_it_ctx = -1;
+ }
+
+ if(video->ohci_ir_ctx != -1) {
+ video->ohci_IsoRcvContextControlSet = 0;
+ video->ohci_IsoRcvContextControlClear = 0;
+ video->ohci_IsoRcvCommandPtr = 0;
+ video->ohci_IsoRcvContextMatch = 0;
+
+ /* disable interrupts for IR context */
+ reg_write(video->ohci, OHCI1394_IsoRecvIntMaskClear, (1 << video->ohci_ir_ctx));
+
+ clear_bit(video->ohci_ir_ctx, &video->ohci->ir_ctx_usage);
+ debug_printk("dv1394: IR context %d released\n", video->ohci_ir_ctx);
+ video->ohci_ir_ctx = -1;
+ }
+
+ /* free the frame structs */
+ for(i = 0; i < DV1394_MAX_FRAMES; i++) {
+ if(video->frames[i])
+ frame_delete(video->frames[i]);
+ video->frames[i] = NULL;
+ }
+
+ video->n_frames = 0;
+
+ /* we can't free the DMA buffer unless it is guaranteed that
+ no more user-space mappings exist */
+
+ if(free_user_buf && video->user_buf) {
+ if(video->user_dma.sglist) {
+ if(video->user_dma.n_dma_pages > 0) {
+ /* unmap it from the IOMMU */
+ pci_unmap_sg(video->ohci->dev,
+ video->user_dma.sglist,
+ video->user_dma.n_pages,
+ PCI_DMA_TODEVICE);
+ video->user_dma.n_dma_pages = 0;
+ }
+ kfree(video->user_dma.sglist);
+ video->user_dma.sglist = NULL;
+ video->user_dma.n_pages = 0;
+ }
+ rvfree(video->user_buf, video->user_buf_size);
+ video->user_buf = NULL;
+ video->user_buf_size = 0;
+ }
+
+ if (video->packet_buffer) {
+ pci_unmap_single(video->ohci->dev,
+ video->packet_buffer_dma,
+ video->packet_buffer_size,
+ PCI_DMA_FROMDEVICE);
+ kfree(video->packet_buffer);
+ video->packet_buffer = NULL;
+ video->packet_buffer_size = 0;
+ }
+
+ debug_printk("dv1394: shutdown complete\n");
+
+ return 0;
+}
+
+
+
+/*
+ **********************************
+ *** MMAP() THEORY OF OPERATION ***
+ **********************************
+
+ The ringbuffer cannot be re-allocated or freed while
+ a user program maintains a mapping of it. (note that a mapping
+ can persist even after the device fd is closed!)
+
+ So, only let the user process allocate the DMA buffer once.
+ To resize or deallocate it, you must close the device file
+ and open it again.
+
+ Previously Dan M. hacked out a scheme that allowed the DMA
+ buffer to change by forcefully unmapping it from the user's
+ address space. It was prone to error because it's very hard to
+ track all the places the buffer could have been mapped (we
+ would have had to walk the vma list of every process in the
+ system to be sure we found all the mappings!). Instead, we
+ force the user to choose one buffer size and stick with
+ it. This small sacrifice is worth the huge reduction in
+ error-prone code in dv1394.
+
+ Note: dv1394_mmap does no page table manipulation. The page
+ table entries are created by the dv1394_nopage() handler as
+ page faults are taken by the user.
+*/
+
+static struct page * dv1394_nopage(struct vm_area_struct * area, unsigned long address, int write_access)
+{
+ unsigned long offset;
+ unsigned long page, kernel_virt_addr;
+ struct page *ret = NOPAGE_SIGBUS;
+
+ struct video_card *video = (struct video_card*) area->vm_private_data;
+
+ /* guard against process-context operations and the interrupt */
+ /* (by definition page faults are taken in interrupt context) */
+ spin_lock(&video->spinlock);
+
+ if(!video->user_buf)
+ goto out;
+
+ if( (address < (unsigned long) area->vm_start) ||
+ (address > (unsigned long) area->vm_start + video->user_buf_size) )
+ goto out;
+
+ offset = address - area->vm_start;
+ kernel_virt_addr = (unsigned long) video->user_buf + offset;
+
+ page = kvirt_to_pa(kernel_virt_addr);
+
+ ret = virt_to_page(__va(page));
+ get_page(ret);
+
+ out:
+ spin_unlock(&video->spinlock);
+ return ret;
+}
+
+static struct vm_operations_struct dv1394_vm_ops = {
+ nopage: dv1394_nopage
+};
+
+/*
+ dv1394_mmap does no page table manipulation. The page table entries
+ are created by the dv1394_nopage() handler as page faults are taken
+ by the user.
+*/
+
+int dv1394_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_card *video = file_to_video_card(file);
+ unsigned long size;
+ int res = -EINVAL;
+
+ /* serialize mmap */
+ down(&video->sem);
+
+ if( ! video_card_initialized(video) ) {
+ res = do_dv1394_init_default(video);
+ if(res)
+ goto err;
+ }
+
+ /* region must be page-aligned */
+ if(vma->vm_pgoff != 0)
+ goto err;
+
+ /* check the size the user is trying to map */
+ size = vma->vm_end - vma->vm_start;
+ if(size > video->user_buf_size)
+ goto err;
+
+ /*
+ we don't actually mess with the page tables here.
+ (nopage() takes care of that from the page fault handler)
+ Just set up the vma->vm_ops.
+ */
+
+ vma->vm_ops = &dv1394_vm_ops;
+ vma->vm_private_data = video;
+ vma->vm_file = file;
+
+ /* don't try to swap this out =) */
+ vma->vm_flags |= VM_RESERVED;
+
+ up(&video->sem);
+ return 0;
+ err:
+ up(&video->sem);
+ return res;
+}
+
+
+/*** DEVICE FILE INTERFACE *************************************************/
+
+/* no need to serialize, multiple threads OK */
+static unsigned int dv1394_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_card *video = file_to_video_card(file);
+ unsigned int mask = 0;
+ unsigned long flags;
+
+ poll_wait(file, &video->waitq, wait);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+ if( video->n_frames == 0 ) {
+
+ } else if( video->active_frame == -1 ) {
+ /* nothing going on */
+ mask |= POLLOUT;
+ } else {
+ /* any clear/ready buffers? */
+ if(video->n_clear_frames >0)
+ mask |= POLLOUT | POLLIN;
+ }
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ return mask;
+}
+
+static int dv1394_fasync(int fd, struct file *file, int on)
+{
+ /* I just copied this code verbatim from Alan Cox's mouse driver example
+ (linux/Documentation/DocBook/) */
+
+ struct video_card *video = file_to_video_card(file);
+
+ int retval = fasync_helper(fd, file, on, &video->fasync);
+
+ if (retval < 0)
+ return retval;
+ return 0;
+}
+
+static ssize_t dv1394_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+ struct video_card *video = file_to_video_card(file);
+ DECLARE_WAITQUEUE(wait, current);
+ ssize_t ret;
+ size_t cnt;
+ unsigned long flags;
+ int target_frame;
+
+ /* serialize this to prevent multi-threaded mayhem */
+ if(file->f_flags & O_NONBLOCK) {
+ if(down_trylock(&video->sem))
+ return -EAGAIN;
+ } else {
+ if(down_interruptible(&video->sem))
+ return -ERESTARTSYS;
+ }
+
+ if( !video_card_initialized(video) ) {
+ ret = do_dv1394_init_default(video);
+ if(ret) {
+ up(&video->sem);
+ return ret;
+ }
+ }
+
+ ret = 0;
+ add_wait_queue(&video->waitq, &wait);
+
+ while(count > 0) {
+
+ /* must set TASK_INTERRUPTIBLE *before* checking for free
+ buffers; otherwise we could miss a wakeup if the interrupt
+ fires between the check and the schedule() */
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ target_frame = video->first_clear_frame;
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ if(video->frames[target_frame]->state == FRAME_CLEAR) {
+
+ /* how much room is left in the target frame buffer */
+ cnt = video->frame_size - (video->write_off - target_frame * video->frame_size);
+
+ } else {
+ /* buffer is already used */
+ cnt = 0;
+ }
+
+ if(cnt > count)
+ cnt = count;
+
+ if (cnt <= 0) {
+ /* no room left, gotta wait */
+ if(file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ schedule();
+
+ continue; /* start over from 'while(count > 0)...' */
+ }
+
+ if(copy_from_user(video->user_buf + video->write_off, buffer, cnt)) {
+ if(!ret)
+ ret = -EFAULT;
+ break;
+ }
+
+ video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size);
+
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+
+ if(video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames))
+ frame_prepare(video, target_frame);
+ }
+
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+ up(&video->sem);
+ return ret;
+}
+
+
+static ssize_t dv1394_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+ struct video_card *video = file_to_video_card(file);
+ DECLARE_WAITQUEUE(wait, current);
+ ssize_t ret;
+ size_t cnt;
+ unsigned long flags;
+ int target_frame;
+
+ /* serialize this to prevent multi-threaded mayhem */
+ if(file->f_flags & O_NONBLOCK) {
+ if(down_trylock(&video->sem))
+ return -EAGAIN;
+ } else {
+ if(down_interruptible(&video->sem))
+ return -ERESTARTSYS;
+ }
+
+ if( !video_card_initialized(video) ) {
+ ret = do_dv1394_init_default(video);
+ if(ret) {
+ up(&video->sem);
+ return ret;
+ }
+ receive_packets(video, video->frames[video->first_clear_frame]);
+ }
+
+ ret = 0;
+ add_wait_queue(&video->waitq, &wait);
+
+ while(count > 0) {
+
+ /* must set TASK_INTERRUPTIBLE *before* checking for free
+ buffers; otherwise we could miss a wakeup if the interrupt
+ fires between the check and the schedule() */
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ target_frame = video->first_clear_frame;
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ if(target_frame >= 0 &&
+ video->n_clear_frames > 0 &&
+ video->frames[target_frame]->state == FRAME_CLEAR) {
+
+ /* how much room is left in the target frame buffer */
+ cnt = video->frame_size - (video->write_off - target_frame * video->frame_size);
+
+ } else {
+ /* buffer is already used */
+ cnt = 0;
+ }
+
+ if(cnt > count)
+ cnt = count;
+
+ if (cnt <= 0) {
+ /* no room left, gotta wait */
+ if(file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ schedule();
+
+ continue; /* start over from 'while(count > 0)...' */
+ }
+
+ if(copy_to_user(buffer, video->user_buf + video->write_off, cnt)) {
+ if(!ret)
+ ret = -EFAULT;
+ break;
+ }
+
+ video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size);
+
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+
+ if(video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) {
+ spin_lock_irqsave(&video->spinlock, flags);
+ video->n_clear_frames--;
+ video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
+ spin_unlock_irqrestore(&video->spinlock, flags);
+ }
+ }
+
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+ up(&video->sem);
+ return ret;
+}
+
+
+/*** DEVICE IOCTL INTERFACE ************************************************/
+
+/* I *think* the VFS serializes ioctl() for us, so we don't have to worry
+ about situations like having two threads in here at once... */
+
+static int dv1394_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_card *video = file_to_video_card(file);
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ /* serialize this to prevent multi-threaded mayhem */
+ if(file->f_flags & O_NONBLOCK) {
+ if(down_trylock(&video->sem))
+ return -EAGAIN;
+ } else {
+ if(down_interruptible(&video->sem))
+ return -ERESTARTSYS;
+ }
+
+ switch(cmd)
+ {
+ case DV1394_SUBMIT_FRAMES: {
+ unsigned int n_submit;
+
+ if( !video_card_initialized(video) ) {
+ ret = do_dv1394_init_default(video);
+ if(ret)
+ goto out;
+ }
+
+ n_submit = (unsigned int) arg;
+
+ if(n_submit > video->n_frames) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ while(n_submit > 0) {
+
+ add_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ /* wait until video->first_clear_frame is really CLEAR */
+ while(video->frames[video->first_clear_frame]->state != FRAME_CLEAR) {
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ if(signal_pending(current)) {
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+ ret = -EINTR;
+ goto out;
+ }
+
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+ }
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+
+ frame_prepare(video, video->first_clear_frame);
+
+ n_submit--;
+ }
+
+ ret = 0;
+ break;
+ }
+
+ case DV1394_WAIT_FRAMES: {
+ unsigned int n_wait;
+
+ if( !video_card_initialized(video) ) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ n_wait = (unsigned int) arg;
+
+ /* since we re-run the last frame on underflow, we will
+ never actually have n_frames clear frames; at most only
+ n_frames - 1 */
+
+ if(n_wait > (video->n_frames-1) ) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ add_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ while(video->n_clear_frames < n_wait) {
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ if(signal_pending(current)) {
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+ ret = -EINTR;
+ goto out;
+ }
+
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+ }
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+ ret = 0;
+ break;
+ }
+
+ case DV1394_RECEIVE_FRAMES: {
+ unsigned int n_recv;
+
+ if( !video_card_initialized(video) ) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ n_recv = (unsigned int) arg;
+
+ /* at least one frame must be active */
+ if(n_recv > (video->n_frames-1) ) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ /* release the clear frames */
+ video->n_clear_frames -= n_recv;
+
+ /* advance the clear frame cursor */
+ video->first_clear_frame = (video->first_clear_frame + n_recv) % video->n_frames;
+
+ /* reset dropped_frames */
+ video->dropped_frames = 0;
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ ret = 0;
+ break;
+ }
+
+ case DV1394_START_RECEIVE: {
+
+ if( !video_card_initialized(video) ) {
+ ret = do_dv1394_init_default(video);
+ if(ret)
+ goto out;
+ }
+
+ receive_packets(video, video->frames[video->first_clear_frame]);
+
+ ret = 0;
+ break;
+ }
+
+ case DV1394_INIT: {
+ struct dv1394_init init;
+ if(arg == (unsigned long) NULL) {
+ ret = do_dv1394_init_default(video);
+ } else {
+ if(copy_from_user(&init, (void*)arg, sizeof(init))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ ret = do_dv1394_init(video, &init);
+ }
+ break;
+ }
+
+ case DV1394_SHUTDOWN:
+ ret = do_dv1394_shutdown(video, 0);
+ break;
+
+
+ case DV1394_GET_STATUS: {
+ struct dv1394_status status;
+
+ if( !video_card_initialized(video) ) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ status.init.api_version = DV1394_API_VERSION;
+ status.init.channel = video->channel;
+ status.init.n_frames = video->n_frames;
+ status.init.format = video->pal_or_ntsc;
+ status.init.cip_n = video->cip_n;
+ status.init.cip_d = video->cip_d;
+ status.init.syt_offset = video->syt_offset;
+
+ status.first_clear_frame = video->first_clear_frame;
+
+ /* the rest of the fields need to be locked against the interrupt */
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ status.active_frame = video->active_frame;
+ status.n_clear_frames = video->n_clear_frames;
+
+ status.dropped_frames = video->dropped_frames;
+
+ /* reset dropped_frames */
+ video->dropped_frames = 0;
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ if(copy_to_user((void*)arg, &status, sizeof(status))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = 0;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ out:
+ up(&video->sem);
+ return ret;
+}
+
+
+
+/*** DEVICE FILE INTERFACE CONTINUED ***************************************/
+
+static int dv1394_open(struct inode *inode, struct file *file)
+{
+ struct video_card *video = NULL;
+
+ /* if the device was opened through devfs, then file->private_data
+ has already been set to video by devfs */
+ if(file->private_data) {
+ video = (struct video_card*) file->private_data;
+
+ } else {
+ /* look up the card by ID */
+
+ struct list_head *lh;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dv1394_cards_lock, flags);
+ if(!list_empty(&dv1394_cards)) {
+ struct video_card *p;
+ list_for_each(lh, &dv1394_cards) {
+ p = list_entry(lh, struct video_card, list);
+ if(p->id == ieee1394_file_to_instance(file)) {
+ video = p;
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+
+ if(!video) {
+ debug_printk("dv1394: OHCI card %d not found", ieee1394_file_to_instance(file));
+ return -ENODEV;
+ }
+
+ file->private_data = (void*) video;
+ }
+
+#ifndef DV1394_ALLOW_MORE_THAN_ONE_OPEN
+
+ if( test_and_set_bit(0, &video->open) ) {
+ /* video is already open by someone else */
+ return -EBUSY;
+ }
+
+#endif
+
+ V22_COMPAT_MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+static int dv1394_release(struct inode *inode, struct file *file)
+{
+ struct video_card *video = file_to_video_card(file);
+
+ /* OK to free the DMA buffer, no more mappings can exist */
+ do_dv1394_shutdown(video, 1);
+
+ /* clean up async I/O users */
+ dv1394_fasync(-1, file, 0);
+
+ /* give someone else a turn */
+ clear_bit(0, &video->open);
+
+ V22_COMPAT_MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+/*** PROC_FS INTERFACE ******************************************************/
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *dv1394_procfs_entry;
+
+static int dv1394_procfs_read( char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct video_card *video = (struct video_card*) data;
+
+ V22_COMPAT_MOD_INC_USE_COUNT;
+ snprintf( page, count,
+ "\
+dv1394 settings for host %d:\n\
+----------------------------\n\
+format=%s\n\
+cip_n=%lu\n\
+cip_d=%lu\n\
+syt_offset=%u\n",
+ video->id,
+ (video->pal_or_ntsc == DV1394_NTSC ? "NTSC" : "PAL"),
+ video->cip_n, video->cip_d, video->syt_offset );
+ V22_COMPAT_MOD_DEC_USE_COUNT;
+ return strlen(page);
+}
+#endif /* CONFIG_PROC_FS */
+
+/* lifted from the stallion.c driver */
+#undef TOLOWER
+#define TOLOWER(x) ((((x) >= 'A') && ((x) <= 'Z')) ? ((x) + 0x20) : (x))
+static unsigned long atol(char *str)
+{
+ unsigned long val;
+ int base, c;
+ char *sp;
+
+ val = 0;
+ sp = str;
+ if ((*sp == '0') && (*(sp+1) == 'x')) {
+ base = 16;
+ sp += 2;
+ } else if (*sp == '0') {
+ base = 8;
+ sp++;
+ } else {
+ base = 10;
+ }
+
+ for (; (*sp != 0); sp++) {
+ c = (*sp > '9') ? (TOLOWER(*sp) - 'a' + 10) : (*sp - '0');
+ if ((c < 0) || (c >= base)) {
+ printk(KERN_ERR "dv1394: atol() invalid argument %s\n", str);
+ val = 0;
+ break;
+ }
+ val = (val * base) + c;
+ }
+ return(val);
+}
+
+#ifdef CONFIG_PROC_FS
+static int dv1394_procfs_write( struct file *file,
+ const char *buffer, unsigned long count, void *data)
+{
+ int len = 0;
+ char new_value[64];
+ char *pos;
+ struct video_card *video = (struct video_card*) data;
+
+ V22_COMPAT_MOD_INC_USE_COUNT;
+
+ if (count > 64)
+ len = 64;
+ else
+ len = count;
+
+ if (copy_from_user( new_value, buffer, len)) {
+ V22_COMPAT_MOD_DEC_USE_COUNT;
+ return -EFAULT;
+ }
+
+ pos = strchr(new_value, '=');
+ if (pos != NULL) {
+ int val_len = len - (pos-new_value) - 1;
+ char buf[64];
+ memset(buf, 0, 64);
+ strncpy(buf, pos+1, val_len);
+ if (buf[val_len-1] == '\n') buf[val_len-1] = 0;
+
+ if (strnicmp( new_value, "format", (pos-new_value)) == 0) {
+ if (strnicmp( buf, "NTSC", val_len) == 0)
+ video->pal_or_ntsc = DV1394_NTSC;
+ else if (strnicmp( buf, "PAL", val_len) == 0)
+ video->pal_or_ntsc = DV1394_PAL;
+
+ } else if (strnicmp( new_value, "cip_n", (pos-new_value)) == 0) {
+ video->cip_n = atol(buf);
+ } else if (strnicmp( new_value, "cip_d", (pos-new_value)) == 0) {
+ video->cip_d = atol(buf);
+ } else if (strnicmp( new_value, "syt_offset", (pos-new_value)) == 0) {
+ video->syt_offset = atol(buf);
+ }
+ }
+
+ V22_COMPAT_MOD_DEC_USE_COUNT;
+ return len;
+}
+
+static int dv1394_procfs_add_entry(struct video_card *video)
+{
+ struct proc_dir_entry *procfs_entry = NULL;
+ char buf[16];
+
+ snprintf(buf, sizeof(buf), "%d", video->id);
+
+ procfs_entry = create_proc_entry( buf, 0666, dv1394_procfs_entry);
+ if (procfs_entry == NULL) {
+ printk(KERN_ERR "dv1394: unable to create /proc/bus/ieee1394/dv/X\n");
+ return -ENOMEM;
+ }
+ procfs_entry->owner = THIS_MODULE;
+ procfs_entry->data = video;
+ procfs_entry->read_proc = dv1394_procfs_read;
+ procfs_entry->write_proc = dv1394_procfs_write;
+
+ return 0;
+}
+#endif /* CONFIG_PROC_FS */
+
+/*** DEVICE DRIVER HANDLERS ************************************************/
+
+static void irq_handler(int card, quadlet_t isoRecvIntEvent,
+ quadlet_t isoXmitIntEvent, void *data)
+{
+ int wake = 0;
+ struct video_card *video = (struct video_card*) data;
+
+ irq_printk("INTERRUPT! Video = %08lx Iso event Recv: %08x Xmit: %08x\n",
+ (unsigned long) video, isoRecvIntEvent, isoXmitIntEvent);
+ irq_printk("ContextControl = %08x, CommandPtr = %08x\n",
+ reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
+ reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)
+ );
+
+
+ if( (video->ohci_it_ctx != -1) &&
+ (isoXmitIntEvent & (1 << video->ohci_it_ctx)) &&
+ (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) {
+
+ struct frame *f;
+ unsigned int frame, i;
+
+ spin_lock(&video->spinlock);
+ if(video->active_frame == -1)
+ frame = 0;
+ else
+ frame = video->active_frame;
+
+ /* check all the DMA-able frames */
+ for(i = 0; i < video->n_frames; i++, frame = (frame+1) % video->n_frames) {
+
+ irq_printk("IRQ checking frame %d...", frame);
+ f = video->frames[frame];
+ if(f->state != FRAME_READY) {
+ irq_printk("clear, skipping\n");
+ /* we don't own this frame */
+ continue;
+ }
+
+ irq_printk("DMA\n");
+
+ /* check the frame begin semaphore to see if we can free the previous frame */
+ if( *(f->frame_begin_timestamp) ) {
+ int prev_frame;
+ struct frame *prev_f;
+
+
+
+ /* don't reset, need this later *(f->frame_begin_timestamp) = 0; */
+ irq_printk(" BEGIN\n");
+
+ prev_frame = frame - 1;
+ if(prev_frame == -1)
+ prev_frame += video->n_frames;
+ prev_f = video->frames[prev_frame];
+
+ /* make sure we can actually garbage collect
+ this frame */
+ if( (prev_f->state == FRAME_READY) &&
+ prev_f->done && (!f->done) )
+ {
+ frame_reset(prev_f);
+ video->n_clear_frames++;
+ wake = 1;
+ video->active_frame = frame;
+
+ irq_printk(" BEGIN - freeing previous frame %d, new active frame is %d\n", prev_frame, frame);
+ } else {
+ irq_printk(" BEGIN - can't free yet\n");
+ }
+
+ f->done = 1;
+ }
+
+
+ /* see if we need to set the timestamp for the next frame */
+ if( *(f->mid_frame_timestamp) ) {
+ struct frame *next_frame;
+ u32 ts_cyc, ts_off;
+
+ *(f->mid_frame_timestamp) = 0;
+
+ irq_printk(" MIDDLE - first packet was sent at cycle %4u (%2u), assigned timestamp was (%2u) %4u\n",
+ *(f->frame_begin_timestamp) & 0x1FFF, *(f->frame_begin_timestamp) & 0xF,
+ f->assigned_timestamp >> 12, f->assigned_timestamp & 0xFFF);
+
+ /* prepare next frame and assign timestamp */
+ next_frame = video->frames[ (frame+1) % video->n_frames ];
+
+ if(next_frame->state == FRAME_READY) {
+ irq_printk(" MIDDLE - next frame is ready, good\n");
+ } else {
+ debug_printk("dv1394: Underflow! At least one frame has been dropped.\n");
+ next_frame = f;
+ }
+
+ /* set the timestamp to the timestamp of the last frame sent,
+ plus the length of the last frame sent, plus the syt latency */
+ ts_cyc = *(f->frame_begin_timestamp) & 0xF;
+ /* advance one frame, plus syt latency (typically 2-3) */
+ ts_cyc += f->n_packets + video->syt_offset ;
+
+ ts_off = 0;
+
+ ts_cyc += ts_off/3072;
+ ts_off %= 3072;
+
+ next_frame->assigned_timestamp = ((ts_cyc&0xF) << 12) + ts_off;
+ if(next_frame->cip_syt1) {
+ next_frame->cip_syt1->b[6] = next_frame->assigned_timestamp >> 8;
+ next_frame->cip_syt1->b[7] = next_frame->assigned_timestamp & 0xFF;
+ }
+ if(next_frame->cip_syt2) {
+ next_frame->cip_syt2->b[6] = next_frame->assigned_timestamp >> 8;
+ next_frame->cip_syt2->b[7] = next_frame->assigned_timestamp & 0xFF;
+ }
+
+ }
+
+ /* see if the frame looped */
+ if( *(f->frame_end_timestamp) ) {
+
+ *(f->frame_end_timestamp) = 0;
+
+ debug_printk(" END - the frame looped at least once\n");
+
+ video->dropped_frames++;
+ }
+
+
+
+ } /* for(each frame) */
+
+ spin_unlock(&video->spinlock);
+
+ } /* end XMIT portion */
+
+ /***** RECEIVE INTERRUPT and DMA ACTIVE *****/
+
+ else if( (video->ohci_ir_ctx != -1) &&
+ (isoRecvIntEvent & (1 << video->ohci_ir_ctx)) &&
+ (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) ) {
+
+ int sof=0; /* start-of-frame flag */
+ struct frame *f;
+
+ spin_lock(&video->spinlock);
+
+ irq_printk("received packet %02d, timestamp=%04x, length=%04x, sof=%02x%02x\n", video->current_packet,
+ video->packet_buffer[video->current_packet].timestamp, video->packet_buffer[video->current_packet].data_length,
+ video->packet_buffer[video->current_packet].data[0], video->packet_buffer[video->current_packet].data[1]);
+
+ f = video->frames[video->active_frame];
+
+ /* exclude empty packet */
+ if (video->packet_buffer[video->current_packet].data_length > 8) {
+
+ /* check for start of frame */
+ sof = (video->packet_buffer[video->current_packet].data[0] == 0x1f &&
+ video->packet_buffer[video->current_packet].data[1] == 0x07);
+
+ if (!video->first_frame) {
+ if (sof) {
+ video->first_frame = 1;
+ }
+
+ } else if (sof) {
+ /* close current frame */
+ frame_reset(f); /* f->state = STATE_CLEAR */
+ video->n_clear_frames++;
+ if (video->n_clear_frames > video->n_frames) {
+ video->n_clear_frames = video->n_frames;
+ video->dropped_frames++;
+ }
+ if (video->first_clear_frame == -1)
+ video->first_clear_frame = video->active_frame;
+
+ /* get the next frame */
+ video->active_frame = (video->active_frame + 1) % video->n_frames;
+ f = video->frames[video->active_frame];
+
+ irq_printk(" frame received, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n",
+ video->active_frame, video->n_clear_frames, video->first_clear_frame);
+ }
+ if (video->first_frame) {
+ if (sof) {
+ /* open next frame */
+ f->state = FRAME_READY;
+ }
+
+ /* copy to buffer */
+ if (f->n_packets > (video->frame_size / 480)) {
+ printk(KERN_ERR "frame buffer overflow during receive\n");
+ }
+
+ /* make sure we are seeing the latest changes to packet_buffer */
+ pci_dma_sync_single(video->ohci->dev,
+ video->packet_buffer_dma,
+ video->packet_buffer_size,
+ PCI_DMA_FROMDEVICE);
+
+ frame_put_packet( f, &video->packet_buffer[video->current_packet]);
+
+ } /* first_frame */
+
+ } /* not empty packet */
+
+ /* advance packet_buffer cursor */
+ video->current_packet = (video->current_packet + 1) % MAX_PACKET_BUFFER;
+
+ spin_unlock(&video->spinlock);
+
+ wake = 1; /* why the hell not? */
+
+ } /* receive interrupt */
+
+ if(wake) {
+
+ /* send SIGIO */
+
+ if(isoRecvIntEvent & (1))
+ kill_fasync(&video->fasync, SIGIO, POLL_IN);
+
+ if(isoXmitIntEvent & (1))
+ kill_fasync(&video->fasync, SIGIO, POLL_OUT);
+
+ /* wake readers/writers/ioctl'ers */
+ wake_up_interruptible(&video->waitq);
+ }
+}
+
+static struct file_operations dv1394_fops=
+{
+ OWNER_THIS_MODULE
+ poll: dv1394_poll,
+ ioctl: dv1394_ioctl,
+ mmap: dv1394_mmap,
+ open: dv1394_open,
+ write: dv1394_write,
+ read: dv1394_read,
+ release: dv1394_release,
+ fasync: dv1394_fasync,
+};
+
+
+static int dv1394_init(struct ti_ohci *ohci)
+{
+ struct video_card *video;
+ unsigned long flags;
+ char buf[16];
+ int i;
+
+ video = kmalloc(sizeof(struct video_card), GFP_KERNEL);
+ if(!video) {
+ printk(KERN_ERR "dv1394: cannot allocate video_card\n");
+ goto err;
+ }
+
+ memset(video, 0, sizeof(struct video_card));
+
+ if (ohci1394_hook_irq(ohci, irq_handler, (void*) video) != 0) {
+ printk(KERN_ERR "dv1394: ohci1394_hook_irq() failed\n");
+ goto err_free;
+ }
+
+
+ video->ohci = ohci;
+ video->id = ohci->id;
+
+ if ( dv1394_procfs_add_entry(video) < 0 )
+ goto err_free;
+
+ video->ohci_it_ctx = -1;
+ video->ohci_ir_ctx = -1;
+
+ video->ohci_IsoXmitContextControlSet = 0;
+ video->ohci_IsoXmitContextControlClear = 0;
+ video->ohci_IsoXmitCommandPtr = 0;
+
+ video->ohci_IsoRcvContextControlSet = 0;
+ video->ohci_IsoRcvContextControlClear = 0;
+ video->ohci_IsoRcvCommandPtr = 0;
+ video->ohci_IsoRcvContextMatch = 0;
+
+ video->n_frames = 0; /* flag that video is not initialized */
+ video->channel = -1;
+ video->active_frame = -1;
+
+ /* initialize the following for proc_fs */
+ video->pal_or_ntsc = DV1394_NTSC;
+ video->cip_n = 0; /* 0 = use builtin default */
+ video->cip_d = 0;
+ video->syt_offset = 0;
+
+ for(i = 0; i < DV1394_MAX_FRAMES; i++)
+ video->frames[i] = NULL;
+
+ video->user_buf = NULL;
+ video->user_buf_size = 0;
+
+ clear_bit(0, &video->open);
+ spin_lock_init(&video->spinlock);
+ init_MUTEX(&video->sem);
+ init_waitqueue_head(&video->waitq);
+ video->fasync = NULL;
+
+ spin_lock_irqsave(&dv1394_cards_lock, flags);
+ INIT_LIST_HEAD(&video->list);
+ list_add_tail(&video->list, &dv1394_cards);
+ spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+
+ snprintf(buf, sizeof(buf), "%d", video->id);
+
+ video->devfs_handle = devfs_register(dv1394_devfs_handle,
+ buf, DEVFS_FL_NONE,
+ IEEE1394_MAJOR,
+ IEEE1394_MINOR_BLOCK_DV1394*16 + video->id,
+ S_IFCHR | S_IRUGO | S_IWUGO,
+ &dv1394_fops,
+ (void*) video);
+
+ debug_printk("dv1394: dv1394_init() OK on ID %d\n", ohci->id);
+
+ return 0;
+
+ err_free:
+ kfree(video);
+ err:
+ return -1;
+}
+
+static void dv1394_un_init(struct video_card *video)
+{
+ unsigned long flags;
+
+ /* obviously nobody has the driver open at this point */
+ do_dv1394_shutdown(video, 1);
+ ohci1394_unhook_irq(video->ohci, irq_handler, (void*) video);
+ if(video->devfs_handle)
+ devfs_unregister(video->devfs_handle);
+
+ spin_lock_irqsave(&dv1394_cards_lock, flags);
+ list_del(&video->list);
+ spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+
+ kfree(video);
+}
+
+
+static void dv1394_remove_host (struct hpsb_host *host)
+{
+ struct ti_ohci *ohci;
+ struct video_card *video = NULL;
+ unsigned long flags;
+ struct list_head *lh;
+
+ /* We only work with the OHCI-1394 driver */
+ if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
+ return;
+
+ ohci = (struct ti_ohci *)host->hostdata;
+
+
+ /* find the corresponding video_card */
+ spin_lock_irqsave(&dv1394_cards_lock, flags);
+ if(!list_empty(&dv1394_cards)) {
+ struct video_card *p;
+ list_for_each(lh, &dv1394_cards) {
+ p = list_entry(lh, struct video_card, list);
+ if(p->id == ohci->id) {
+ video = p;
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+
+ if(video) {
+ char buf[16];
+ dv1394_un_init(video);
+ snprintf( buf, sizeof(buf), "%i", video->id);
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry( buf, dv1394_procfs_entry);
+#endif
+ }
+}
+
+static void dv1394_add_host (struct hpsb_host *host)
+{
+ struct ti_ohci *ohci;
+
+ /* We only work with the OHCI-1394 driver */
+ if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
+ return;
+
+ ohci = (struct ti_ohci *)host->hostdata;
+
+ dv1394_init(ohci);
+}
+
+static struct hpsb_highlevel_ops hl_ops = {
+ add_host: dv1394_add_host,
+ remove_host: dv1394_remove_host,
+};
+
+
+/*** KERNEL MODULE HANDLERS ************************************************/
+
+MODULE_AUTHOR("Dan Maas <dmaas@dcine.com>, Dan Dennedy <dan@dennedy.org>");
+MODULE_DESCRIPTION("driver for DV input/output on OHCI board");
+MODULE_SUPPORTED_DEVICE("dv1394");
+MODULE_LICENSE("GPL");
+
+static void __exit dv1394_exit_module(void)
+{
+ hpsb_unregister_highlevel (hl_handle);
+ ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394);
+ devfs_unregister(dv1394_devfs_handle);
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry( "dv", ieee1394_procfs_entry);
+#endif
+}
+
+static int __init dv1394_init_module(void)
+{
+ if (ieee1394_register_chardev(IEEE1394_MINOR_BLOCK_DV1394,
+ THIS_MODULE, &dv1394_fops)) {
+ printk(KERN_ERR "dv1394: unable to register character device\n");
+ return -EIO;
+ }
+
+ dv1394_devfs_handle = devfs_mk_dir(ieee1394_devfs_handle, "dv", NULL);
+
+#ifdef CONFIG_PROC_FS
+ dv1394_procfs_entry = proc_mkdir( "dv", ieee1394_procfs_entry);
+ if (dv1394_procfs_entry == NULL) {
+ printk(KERN_ERR "dv1394: unable to create /proc/ieee1394/dv\n");
+ ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394);
+ devfs_unregister(dv1394_devfs_handle);
+ return -ENOMEM;
+ }
+ dv1394_procfs_entry->owner = THIS_MODULE;
+#endif
+
+ hl_handle = hpsb_register_highlevel ("dv1394", &hl_ops);
+ if (hl_handle == NULL) {
+ printk(KERN_ERR "dv1394: hpsb_register_highlevel failed\n");
+ ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394);
+ devfs_unregister(dv1394_devfs_handle);
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry( "dv", ieee1394_procfs_entry);
+#endif
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+module_init(dv1394_init_module);
+module_exit(dv1394_exit_module);
+
--- /dev/null
+/*
+ * dv1394.h - DV input/output over IEEE 1394 on OHCI chips
+ * Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ * receive, proc_fs by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ * video1394.h - driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ * Peter Schlaile <udbz@rz.uni-karlsruhe.de>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_1394_H
+#define _DV_1394_H
+
+/* This is the public user-space interface. Try not to break it. */
+
+#define DV1394_API_VERSION 0x20011127
+
+/* ********************
+ ** **
+ ** DV1394 API **
+ ** **
+ ********************
+
+ There are two methods of operating the DV1394 DV output device.
+
+ 1)
+
+ The simplest is an interface based on write(): simply write
+ full DV frames of data to the device, and they will be transmitted
+ as quickly as possible. The FD may be set for non-blocking I/O,
+ in which case you can use select() or poll() to wait for output
+ buffer space.
+
+ To set the DV output parameters (e.g. whether you want NTSC or PAL
+ video), use the DV1394_INIT ioctl, passing in the parameters you
+ want in a struct dv1394_init.
+
+ Example 1:
+ To play a raw .DV file: cat foo.DV > /dev/dv1394
+ (cat will use write() internally)
+
+ Example 2:
+ static struct dv1394_init init = {
+ 0x63, (broadcast channel)
+ 4, (four-frame ringbuffer)
+ DV1394_NTSC, (send NTSC video)
+ 0, 0 (default empty packet rate)
+ }
+
+ ioctl(fd, DV1394_INIT, &init);
+
+ while(1) {
+ read( <a raw DV file>, buf, DV1394_NTSC_FRAME_SIZE );
+ write( <the dv1394 FD>, buf, DV1394_NTSC_FRAME_SIZE );
+ }
+
+ 2)
+
+ For more control over buffering, and to avoid unnecessary copies
+ of the DV data, you can use the more sophisticated the mmap() interface.
+ First, call the DV1394_INIT ioctl to specify your parameters,
+ including the number of frames in the ringbuffer. Then, calling mmap()
+ on the dv1394 device will give you direct access to the ringbuffer
+ from which the DV card reads your frame data.
+
+ The ringbuffer is simply one large, contiguous region of memory
+ containing two or more frames of packed DV data. Each frame of DV data
+ is 120000 bytes (NTSC) or 144000 bytes (PAL).
+
+ Fill one or more frames in the ringbuffer, then use the DV1394_SUBMIT_FRAMES
+ ioctl to begin I/O. You can use either the DV1394_WAIT_FRAMES ioctl
+ or select()/poll() to wait until the frames are transmitted. Next, you'll
+ need to call the DV1394_GET_STATUS ioctl to determine which ringbuffer
+ frames are clear (ready to be filled with new DV data). Finally, use
+ DV1394_SUBMIT_FRAMES again to send the new data to the DV output.
+
+
+ Example: here is what a four-frame ringbuffer might look like
+ during DV transmission:
+
+
+ frame 0 frame 1 frame 2 frame 3
+
+ *--------------------------------------*
+ | CLEAR | DV data | DV data | CLEAR |
+ *--------------------------------------*
+ <ACTIVE>
+
+ transmission goes in this direction --->>>
+
+
+ The DV hardware is currently transmitting the data in frame 1.
+ Once frame 1 is finished, it will automatically transmit frame 2.
+ (if frame 2 finishes before frame 3 is submitted, the device
+ will continue to transmit frame 2, and will increase the dropped_frames
+ counter each time it repeats the transmission).
+
+
+ If you called DV1394_GET_STATUS at this instant, you would
+ receive the following values:
+
+ n_frames = 4
+ active_frame = 1
+ first_clear_frame = 3
+ n_clear_frames = 2
+
+ At this point, you should write new DV data into frame 3 and optionally
+ frame 0. Then call DV1394_SUBMIT_FRAMES to inform the device that
+ it may transmit the new frames.
+
+*/
+
+
+/* maximum number of frames in the ringbuffer */
+#define DV1394_MAX_FRAMES 32
+
+/* number of *full* isochronous packets per DV frame */
+#define DV1394_NTSC_PACKETS_PER_FRAME 250
+#define DV1394_PAL_PACKETS_PER_FRAME 300
+
+/* size of one frame's worth of DV data, in bytes */
+#define DV1394_NTSC_FRAME_SIZE (480 * DV1394_NTSC_PACKETS_PER_FRAME)
+#define DV1394_PAL_FRAME_SIZE (480 * DV1394_PAL_PACKETS_PER_FRAME)
+
+
+/* ioctl() commands */
+
+enum {
+ /* I don't like using 0 as a valid ioctl() */
+ DV1394_INVALID = 0,
+
+
+ /* get the driver ready to transmit video.
+ pass a struct dv1394_init* as the parameter (see below),
+ or NULL to get default parameters */
+ DV1394_INIT,
+
+
+ /* stop transmitting video and free the ringbuffer */
+ DV1394_SHUTDOWN,
+
+
+ /* submit N new frames to be transmitted, where
+ the index of the first new frame is first_clear_buffer,
+ and the index of the last new frame is
+ (first_clear_buffer + N) % n_frames */
+ DV1394_SUBMIT_FRAMES,
+
+
+ /* block until N buffers are clear (pass N as the parameter)
+ Because we re-transmit the last frame on underrun, there
+ will at most be n_frames - 1 clear frames at any time */
+ DV1394_WAIT_FRAMES,
+
+ /* capture new frames that have been received, where
+ the index of the first new frame is first_clear_buffer,
+ and the index of the last new frame is
+ (first_clear_buffer + N) % n_frames */
+ DV1394_RECEIVE_FRAMES,
+
+
+ DV1394_START_RECEIVE,
+
+
+ /* pass a struct dv1394_status* as the parameter (see below) */
+ DV1394_GET_STATUS,
+};
+
+
+
+enum pal_or_ntsc {
+ DV1394_NTSC = 0,
+ DV1394_PAL
+};
+
+
+
+
+/* this is the argument to DV1394_INIT */
+struct dv1394_init {
+ /* DV1394_API_VERSION */
+ unsigned int api_version;
+
+ /* isochronous transmission channel to use */
+ unsigned int channel;
+
+ /* number of frames in the ringbuffer. Must be at least 2
+ and at most DV1394_MAX_FRAMES. */
+ unsigned int n_frames;
+
+ /* send/receive PAL or NTSC video format */
+ enum pal_or_ntsc format;
+
+ /* the following are used only for transmission */
+
+ /* set these to zero unless you want a
+ non-default empty packet rate (see below) */
+ unsigned long cip_n;
+ unsigned long cip_d;
+
+ /* set this to zero unless you want a
+ non-default SYT cycle offset (default = 3 cycles) */
+ unsigned int syt_offset;
+};
+
+/* Q: What are cip_n and cip_d? */
+
+/*
+ A: DV video streams do not utilize 100% of the potential bandwidth offered
+ by IEEE 1394 (FireWire). To achieve the correct rate of data transmission,
+ DV devices must periodically insert empty packets into the 1394 data stream.
+ Typically there is one empty packet per 14-16 data-carrying packets.
+
+ Some DV devices will accept a wide range of empty packet rates, while others
+ require a precise rate. If the dv1394 driver produces empty packets at
+ a rate that your device does not accept, you may see ugly patterns on the
+ DV output, or even no output at all.
+
+ The default empty packet insertion rate seems to work for many people; if
+ your DV output is stable, you can simply ignore this discussion. However,
+ we have exposed the empty packet rate as a parameter to support devices that
+ do not work with the default rate.
+
+ The decision to insert an empty packet is made with a numerator/denominator
+ algorithm. Empty packets are produced at an average rate of CIP_N / CIP_D.
+ You can alter the empty packet rate by passing non-zero values for cip_n
+ and cip_d to the INIT ioctl.
+
+ */
+
+
+
+struct dv1394_status {
+ /* this embedded init struct returns the current dv1394
+ parameters in use */
+ struct dv1394_init init;
+
+ /* the ringbuffer frame that is currently being
+ displayed. (-1 if the device is not transmitting anything) */
+ int active_frame;
+
+ /* index of the first buffer (ahead of active_frame) that
+ is ready to be filled with data */
+ unsigned int first_clear_frame;
+
+ /* how many buffers, including first_clear_buffer, are
+ ready to be filled with data */
+ unsigned int n_clear_frames;
+
+ /* how many times the DV output has underflowed
+ since the last call to DV1394_GET_STATUS */
+ unsigned int dropped_frames;
+
+ /* N.B. The dropped_frames counter is only a lower bound on the actual
+ number of dropped frames, with the special case that if dropped_frames
+ is zero, then it is guaranteed that NO frames have been dropped
+ since the last call to DV1394_GET_STATUS.
+ */
+};
+
+
+#endif /* _DV_1394_H */
hl->op = ops;
write_lock_irq(&hl_drivers_lock);
- hl_all_hosts(hl, 1);
list_add_tail(&hl->hl_list, &hl_drivers);
write_unlock_irq(&hl_drivers_lock);
+ hl_all_hosts(hl->op->add_host);
+
return hl;
}
write_lock_irq(&hl_drivers_lock);
list_del(&hl->hl_list);
- hl_all_hosts(hl, 0);
write_unlock_irq(&hl_drivers_lock);
+ if (hl->op->remove_host)
+ hl_all_hosts(hl->op->remove_host);
+
kfree(hl);
}
int retval = 0;
if (((start|end) & 3) || (start >= end) || (end > 0x1000000000000ULL)) {
- HPSB_ERR(__FUNCTION__ " called with invalid addresses");
+ HPSB_ERR("%s called with invalid addresses", __FUNCTION__);
return 0;
}
unsigned int channel)
{
if (channel > 63) {
- HPSB_ERR(__FUNCTION__ " called with invalid channel");
+ HPSB_ERR("%s called with invalid channel", __FUNCTION__);
return;
}
if (host->iso_listen_count[channel]++ == 0) {
- host->template->devctl(host, ISO_LISTEN_CHANNEL, channel);
+ host->ops->devctl(host, ISO_LISTEN_CHANNEL, channel);
}
}
unsigned int channel)
{
if (channel > 63) {
- HPSB_ERR(__FUNCTION__ " called with invalid channel");
+ HPSB_ERR("%s called with invalid channel", __FUNCTION__);
return;
}
if (--host->iso_listen_count[channel] == 0) {
- host->template->devctl(host, ISO_UNLISTEN_CHANNEL, channel);
+ host->ops->devctl(host, ISO_UNLISTEN_CHANNEL, channel);
}
}
-#define DEFINE_MULTIPLEXER(Function) \
-void highlevel_##Function(struct hpsb_host *host) \
-{ \
- struct list_head *lh; \
- void (*funcptr)(struct hpsb_host*); \
- read_lock(&hl_drivers_lock); \
- list_for_each(lh, &hl_drivers) { \
- funcptr = list_entry(lh, struct hpsb_highlevel, hl_list) \
- ->op->Function; \
- if (funcptr) funcptr(host); \
- } \
- read_unlock(&hl_drivers_lock); \
+void highlevel_add_host(struct hpsb_host *host)
+{
+ struct list_head *entry;
+ struct hpsb_highlevel *hl;
+
+ read_lock(&hl_drivers_lock);
+ list_for_each(entry, &hl_drivers) {
+ hl = list_entry(entry, struct hpsb_highlevel, hl_list);
+
+ hl->op->add_host(host);
+ }
+ read_unlock(&hl_drivers_lock);
}
-DEFINE_MULTIPLEXER(add_host)
-DEFINE_MULTIPLEXER(remove_host)
-DEFINE_MULTIPLEXER(host_reset)
-#undef DEFINE_MULTIPLEXER
+void highlevel_remove_host(struct hpsb_host *host)
+{
+ struct list_head *entry;
+ struct hpsb_highlevel *hl;
-/* Add one host to our list */
-void highlevel_add_one_host (struct hpsb_host *host)
+ write_lock_irq(&hl_drivers_lock);
+ list_for_each(entry, &hl_drivers) {
+ hl = list_entry(entry, struct hpsb_highlevel, hl_list);
+
+ if (hl->op->remove_host)
+ hl->op->remove_host(host);
+ }
+ write_unlock_irq(&hl_drivers_lock);
+}
+
+void highlevel_host_reset(struct hpsb_host *host)
{
- if (host->template->initialize_host)
- if (!host->template->initialize_host(host))
- goto fail;
- host->initialized = 1;
- highlevel_add_host (host);
- hpsb_reset_bus (host, LONG_RESET);
-fail:
- host->template->number_of_hosts++;
+ struct list_head *entry;
+ struct hpsb_highlevel *hl;
+
+ read_lock(&hl_drivers_lock);
+ list_for_each(entry, &hl_drivers) {
+ hl = list_entry(entry, struct hpsb_highlevel, hl_list);
+
+ if (hl->op->host_reset)
+ hl->op->host_reset(host);
+ }
+ read_unlock(&hl_drivers_lock);
}
void highlevel_iso_receive(struct hpsb_host *host, quadlet_t *data,
while (as->start <= addr) {
if (as->end > addr) {
- partlength = MIN((unsigned int)(as->end - addr),
- length);
+ partlength = min(as->end - addr, (u64) length);
if (as->op->read != NULL) {
- rcode = as->op->read(host, nodeid, buffer, addr,
- partlength);
+ rcode = as->op->read(host, nodeid, buffer,
+ addr, partlength);
} else {
rcode = RCODE_TYPE_ERROR;
}
while (as->start <= addr) {
if (as->end > addr) {
- partlength = MIN((unsigned int)(as->end - addr),
- length);
+ partlength = min(as->end - addr, (u64) length);
if (as->op->write != NULL) {
- rcode = as->op->write(host, nodeid, destid, data,
- addr, partlength);
+ rcode = as->op->write(host, nodeid, destid,
+ data, addr, partlength);
} else {
rcode = RCODE_TYPE_ERROR;
}
void init_hpsb_highlevel(void);
void highlevel_add_host(struct hpsb_host *host);
-void highlevel_add_one_host(struct hpsb_host *host);
void highlevel_remove_host(struct hpsb_host *host);
void highlevel_host_reset(struct hpsb_host *host);
*/
#include <linux/config.h>
-
#include <linux/types.h>
+#include <linux/list.h>
#include <linux/init.h>
-#include <linux/vmalloc.h>
-#include <linux/wait.h>
+#include <linux/slab.h>
#include "ieee1394_types.h"
#include "hosts.h"
#include "ieee1394_core.h"
#include "highlevel.h"
+static struct list_head hosts = LIST_HEAD_INIT(hosts);
+static struct list_head host_drivers = LIST_HEAD_INIT(host_drivers);
-static LIST_HEAD(templates);
-static spinlock_t templates_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t hosts_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t host_drivers_lock = SPIN_LOCK_UNLOCKED;
-/*
- * This function calls the add_host/remove_host hooks for every host currently
- * registered. Init == TRUE means add_host.
- */
-void hl_all_hosts(struct hpsb_highlevel *hl, int init)
-{
- struct list_head *tlh, *hlh;
- struct hpsb_host_template *tmpl;
- struct hpsb_host *host;
- spin_lock(&templates_lock);
-
- list_for_each(tlh, &templates) {
- tmpl = list_entry(tlh, struct hpsb_host_template, list);
- list_for_each(hlh, &tmpl->hosts) {
- host = list_entry(hlh, struct hpsb_host, list);
- if (host->initialized) {
- if (init) {
- if (hl->op->add_host) {
- hl->op->add_host(host);
- }
- } else {
- if (hl->op->remove_host) {
- hl->op->remove_host(host);
- }
- }
- }
- }
- }
+static int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p)
+{
+ return 0;
+}
- spin_unlock(&templates_lock);
+static int dummy_devctl(struct hpsb_host *h, enum devctl_cmd c, int arg)
+{
+ return -1;
}
-int hpsb_inc_host_usage(struct hpsb_host *host)
+static struct hpsb_host_operations dummy_ops = {
+ transmit_packet: dummy_transmit_packet,
+ devctl: dummy_devctl
+};
+
+
+int hpsb_ref_host(struct hpsb_host *host)
{
- struct list_head *tlh, *hlh;
- struct hpsb_host_template *tmpl;
- int retval = 0;
+ struct list_head *lh;
unsigned long flags;
+ int retval = 0;
- spin_lock_irqsave(&templates_lock, flags);
-
- list_for_each(tlh, &templates) {
- tmpl = list_entry(tlh, struct hpsb_host_template, list);
- list_for_each(hlh, &tmpl->hosts) {
- if (host == list_entry(hlh, struct hpsb_host, list)) {
- tmpl->devctl(host, MODIFY_USAGE, 1);
+ spin_lock_irqsave(&hosts_lock, flags);
+ list_for_each(lh, &hosts) {
+ if (host == list_entry(lh, struct hpsb_host, host_list)) {
+ if (host->ops->devctl(host, MODIFY_USAGE, 1)) {
+ host->refcount++;
retval = 1;
- break;
- }
}
- if (retval)
break;
}
-
- spin_unlock_irqrestore(&templates_lock, flags);
+ }
+ spin_unlock_irqrestore(&hosts_lock, flags);
return retval;
}
-void hpsb_dec_host_usage(struct hpsb_host *host)
+void hpsb_unref_host(struct hpsb_host *host)
{
- host->template->devctl(host, MODIFY_USAGE, 0);
+ unsigned long flags;
+
+ host->ops->devctl(host, MODIFY_USAGE, 0);
+
+ spin_lock_irqsave(&hosts_lock, flags);
+ host->refcount--;
+
+ if (!host->refcount && !host->is_shutdown)
+ kfree(host);
+ spin_unlock_irqrestore(&hosts_lock, flags);
}
-/*
- * The following function is exported for module usage. It will be called from
- * the detect function of a adapter driver.
- */
-struct hpsb_host *hpsb_get_host(struct hpsb_host_template *tmpl,
- size_t hd_size)
+struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra)
{
struct hpsb_host *h;
- h = vmalloc(sizeof(struct hpsb_host) + hd_size);
+ h = kmalloc(sizeof(struct hpsb_host) + extra, SLAB_KERNEL);
if (!h) return NULL;
- memset(h, 0, sizeof(struct hpsb_host) + hd_size);
+ memset(h, 0, sizeof(struct hpsb_host) + extra);
- atomic_set(&h->generation, 0);
+ h->driver = drv;
+ h->ops = drv->ops;
+ h->hostdata = h + 1;
INIT_LIST_HEAD(&h->pending_packets);
spin_lock_init(&h->pending_pkt_lock);
sema_init(&h->tlabel_count, 64);
spin_lock_init(&h->tlabel_lock);
+ atomic_set(&h->generation, 0);
+
INIT_TQUEUE(&h->timeout_tq, (void (*)(void*))abort_timedouts, h);
h->topology_map = h->csr.topology_map + 3;
h->speed_map = (u8 *)(h->csr.speed_map + 2);
- h->template = tmpl;
- if (hd_size)
- h->hostdata = &h->embedded_hostdata[0];
+ return h;
+}
+
+void hpsb_add_host(struct hpsb_host *host)
+{
+ unsigned long flags;
- list_add_tail(&h->list, &tmpl->hosts);
+ spin_lock_irqsave(&hosts_lock, flags);
+ host->driver->number_of_hosts++;
+ list_add_tail(&host->driver_list, &host->driver->hosts);
+ list_add_tail(&host->host_list, &hosts);
+ spin_unlock_irqrestore(&hosts_lock, flags);
- return h;
+ highlevel_add_host(host);
+ host->ops->devctl(host, RESET_BUS, 0);
}
-static void free_all_hosts(struct hpsb_host_template *tmpl)
+void hpsb_remove_host(struct hpsb_host *host)
{
- struct list_head *hlh, *next;
- struct hpsb_host *host;
+ struct hpsb_host_driver *drv = host->driver;
+ unsigned long flags;
- list_for_each_safe(hlh, next, &tmpl->hosts) {
- host = list_entry(hlh, struct hpsb_host, list);
- vfree(host);
- }
+ host->is_shutdown = 1;
+ host->ops = &dummy_ops;
+ highlevel_remove_host(host);
+
+ spin_lock_irqsave(&hosts_lock, flags);
+ list_del(&host->driver_list);
+ list_del(&host->host_list);
+
+ drv->number_of_hosts--;
+ if (!host->refcount) kfree(host);
+ spin_unlock_irqrestore(&hosts_lock, flags);
}
-static void init_hosts(struct hpsb_host_template *tmpl)
+struct hpsb_host_driver *hpsb_register_lowlevel(struct hpsb_host_operations *op,
+ const char *name)
{
- int count;
- struct list_head *hlh;
- struct hpsb_host *host;
+ struct hpsb_host_driver *drv;
- count = tmpl->detect_hosts(tmpl);
+ drv = kmalloc(sizeof(struct hpsb_host_driver), SLAB_KERNEL);
+ if (!drv) return NULL;
- list_for_each(hlh, &tmpl->hosts) {
- host = list_entry(hlh, struct hpsb_host, list);
- if (tmpl->initialize_host(host)) {
- host->initialized = 1;
+ INIT_LIST_HEAD(&drv->list);
+ INIT_LIST_HEAD(&drv->hosts);
+ drv->number_of_hosts = 0;
+ drv->name = name;
+ drv->ops = op;
- highlevel_add_host(host);
- hpsb_reset_bus(host, LONG_RESET);
- }
- }
+ spin_lock(&host_drivers_lock);
+ list_add_tail(&drv->list, &host_drivers);
+ spin_unlock(&host_drivers_lock);
- tmpl->number_of_hosts = count;
- HPSB_INFO("detected %d %s adapter%s", count, tmpl->name,
- (count != 1 ? "s" : ""));
+ return drv;
}
-static void shutdown_hosts(struct hpsb_host_template *tmpl)
+void hpsb_unregister_lowlevel(struct hpsb_host_driver *drv)
{
- struct list_head *hlh;
- struct hpsb_host *host;
+ spin_lock(&host_drivers_lock);
+ list_del(&drv->list);
+ spin_unlock(&host_drivers_lock);
- list_for_each(hlh, &tmpl->hosts) {
- host = list_entry(hlh, struct hpsb_host, list);
- if (host->initialized) {
- host->initialized = 0;
- abort_requests(host);
-
- highlevel_remove_host(host);
-
- tmpl->release_host(host);
- while (test_bit(0, &host->timeout_tq.sync)) {
- schedule();
- }
- }
- }
- free_all_hosts(tmpl);
- tmpl->release_host(NULL);
-
- tmpl->number_of_hosts = 0;
+ kfree(drv);
}
/*
- * The following two functions are exported symbols for module usage.
+ * This function calls the given function for every host currently registered.
*/
-int hpsb_register_lowlevel(struct hpsb_host_template *tmpl)
+void hl_all_hosts(void (*function)(struct hpsb_host*))
{
- INIT_LIST_HEAD(&tmpl->hosts);
- tmpl->number_of_hosts = 0;
-
- spin_lock(&templates_lock);
- list_add_tail(&tmpl->list, &templates);
- spin_unlock(&templates_lock);
-
- /* PCI cards should be smart and use the PCI detection layer, and
- * not this one shot deal. detect_hosts() will be obsoleted soon. */
- if (tmpl->detect_hosts != NULL) {
- HPSB_DEBUG("Registered %s driver, initializing now", tmpl->name);
- init_hosts(tmpl);
- }
-
- return 0;
-}
+ struct list_head *lh;
+ struct hpsb_host *host;
-void hpsb_unregister_lowlevel(struct hpsb_host_template *tmpl)
-{
- shutdown_hosts(tmpl);
-
- if (tmpl->number_of_hosts)
- HPSB_PANIC("attempted to remove busy host template "
- "of %s at address 0x%p", tmpl->name, tmpl);
- else {
- spin_lock(&templates_lock);
- list_del(&tmpl->list);
- spin_unlock(&templates_lock);
+ spin_lock_irq(&hosts_lock);
+ list_for_each (lh, &hosts) {
+ host = list_entry(lh, struct hpsb_host, host_list);
+ function(host);
}
+ spin_unlock_irq(&hosts_lock);
}
struct hpsb_packet;
struct hpsb_host {
-/* private fields (hosts, do not use them) */
- struct list_head list;
+ struct list_head host_list;
+
+ struct hpsb_host_operations *ops;
+ void *hostdata;
atomic_t generation;
+ int refcount;
+
struct list_head pending_packets;
spinlock_t pending_pkt_lock;
struct tq_struct timeout_tq;
struct semaphore tlabel_count;
spinlock_t tlabel_lock;
- int reset_retries;
- quadlet_t *topology_map;
- u8 *speed_map;
- struct csr_control csr;
-
unsigned char iso_listen_count[64];
-/* readonly fields for hosts */
- struct hpsb_host_template *template;
-
int node_count; /* number of identified nodes on this bus */
int selfid_count; /* total number of SelfIDs received */
nodeid_t irm_id; /* ID of this bus' isochronous resource manager */
nodeid_t busmgr_id; /* ID of this bus' bus manager */
- unsigned initialized:1; /* initialized and usable */
- unsigned in_bus_reset:1; /* in bus reset / SelfID stage */
- unsigned attempt_root:1; /* attempt to become root during next reset */
+ /* this nodes state */
+ unsigned in_bus_reset:1;
+ unsigned is_shutdown:1;
/* this nodes' duties on the bus */
unsigned is_root:1;
unsigned is_irm:1;
unsigned is_busmgr:1;
-/* fields readable and writeable by the hosts */
+ int reset_retries;
+ quadlet_t *topology_map;
+ u8 *speed_map;
+ struct csr_control csr;
+
+ struct hpsb_host_driver *driver;
+ struct list_head driver_list;
- void *hostdata;
struct pci_dev *pdev;
- int embedded_hostdata[0];
};
* Return void. */
CANCEL_REQUESTS,
- /* Decrease module usage count if arg == 0, increase otherwise. Return
- * void. */
+ /* Decrease host usage count if arg == 0, increase otherwise. Return
+ * 1 for success, 0 for failure. Increase usage may fail if the driver
+ * is in the process of shutting itself down. Decrease usage can not
+ * fail. */
MODIFY_USAGE,
/* Start or stop receiving isochronous channel in arg. Return void.
SHORT_RESET
};
-struct hpsb_host_template {
- struct list_head list;
-
- struct list_head hosts;
- int number_of_hosts;
-
- /* fields above will be ignored and overwritten after registering */
-
- /* This should be the name of the driver (single word) and must not be
- * NULL. */
- const char *name;
-
- /* This function shall detect all available adapters of this type and
- * call hpsb_get_host for each one. The initialize_host function will
- * be called to actually set up these adapters. The number of detected
- * adapters or zero if there are none must be returned.
- */
- int (*detect_hosts) (struct hpsb_host_template *template);
-
- /* After detecting and registering hosts, this function will be called
- * for every registered host. It shall set up the host to be fully
- * functional for bus operations and return 0 for failure.
- */
- int (*initialize_host) (struct hpsb_host *host);
-
- /* To unload modules, this function is provided. It shall free all
- * resources this host is using (if host is not NULL) or free all
- * resources globally allocated by the driver (if host is NULL).
- */
- void (*release_host) (struct hpsb_host *host);
-
+struct hpsb_host_operations {
/* This function must store a pointer to the configuration ROM into the
* location referenced to by pointer and return the size of the ROM. It
* may not fail. If any allocation is required, it must be done
quadlet_t data, quadlet_t compare);
};
+struct hpsb_host_driver {
+ struct list_head list;
+ struct list_head hosts;
-/* mid level internal use */
+ int number_of_hosts;
+ const char *name;
+
+ struct hpsb_host_operations *ops;
+};
+
+
+/* core internal use */
void register_builtin_lowlevels(void);
/* high level internal use */
struct hpsb_highlevel;
-void hl_all_hosts(struct hpsb_highlevel *hl, int init);
+void hl_all_hosts(void (*function)(struct hpsb_host*));
-/*
- * These functions are for lowlevel (host) driver use.
- */
-int hpsb_register_lowlevel(struct hpsb_host_template *tmpl);
-void hpsb_unregister_lowlevel(struct hpsb_host_template *tmpl);
/*
- * Get a initialized host structure with hostdata_size bytes allocated in
- * embedded_hostdata for free usage. Returns NULL for failure.
+ * In order to prevent hosts from unloading, use hpsb_ref_host(). This prevents
+ * the host from going away (e.g. makes module unloading of the driver
+ * impossible), but still can not guarantee it (e.g. PC-Card being pulled by the
+ * user). hpsb_ref_host() returns false if host could not be locked. If it is
+ * successful, host is valid as a pointer until hpsb_unref_host() (not just
+ * until after remove_host).
*/
-struct hpsb_host *hpsb_get_host(struct hpsb_host_template *tmpl,
- size_t hostdata_size);
+int hpsb_ref_host(struct hpsb_host *host);
+void hpsb_unref_host(struct hpsb_host *host);
-/*
- * Increase / decrease host usage counter. Increase function will return true
- * only if successful (host still existed). Decrease function expects host to
- * exist.
- */
-int hpsb_inc_host_usage(struct hpsb_host *host);
-void hpsb_dec_host_usage(struct hpsb_host *host);
+struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra);
+void hpsb_add_host(struct hpsb_host *host);
+void hpsb_remove_host(struct hpsb_host *h);
+
+struct hpsb_host_driver *hpsb_register_lowlevel(struct hpsb_host_operations *op,
+ const char *name);
+void hpsb_unregister_lowlevel(struct hpsb_host_driver *drv);
#endif /* _IEEE1394_HOSTS_H */
#define ACK_TYPE_ERROR 0xe
/* Non-standard "ACK codes" for internal use */
-#define ACKX_NONE -1
-#define ACKX_SEND_ERROR -2
-#define ACKX_ABORTED -3
-#define ACKX_TIMEOUT -4
+#define ACKX_NONE (-1)
+#define ACKX_SEND_ERROR (-2)
+#define ACKX_ABORTED (-3)
+#define ACKX_TIMEOUT (-4)
#define SPEED_100 0x0
/*
* Note: these mean to be bit fields of a big endian SelfID as seen on a little
- * endian machine.
+ * endian machine. Without swapping.
*/
struct selfid {
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/proc_fs.h>
#include <asm/bitops.h>
#include <asm/byteorder.h>
#include <asm/semaphore.h>
+#include <asm/smplock.h>
#include "ieee1394_types.h"
#include "ieee1394.h"
int hpsb_reset_bus(struct hpsb_host *host, int type)
{
- if (!host->initialized) {
- return 1;
- }
-
if (!host->in_bus_reset) {
- host->template->devctl(host, RESET_BUS, type);
+ host->ops->devctl(host, RESET_BUS, type);
return 0;
} else {
return 1;
int hpsb_bus_reset(struct hpsb_host *host)
{
if (host->in_bus_reset) {
- HPSB_NOTICE(__FUNCTION__
- " called while bus reset already in progress");
+ HPSB_NOTICE("%s called while bus reset already in progress",
+ __FUNCTION__);
return 1;
}
* Verify num_of_selfids SelfIDs and return number of nodes. Return zero in
* case verification failed.
*/
-static int check_selfids(struct hpsb_host *host, unsigned int num_of_selfids)
+static int check_selfids(struct hpsb_host *host)
{
int nodeid = -1;
- int rest_of_selfids = num_of_selfids;
+ int rest_of_selfids = host->selfid_count;
struct selfid *sid = (struct selfid *)host->topology_map;
struct ext_selfid *esid;
int esid_seq = 23;
void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot)
{
- host->node_id = 0xffc0 | phyid;
- host->in_bus_reset = 0;
+ if (!host->in_bus_reset)
+ HPSB_NOTICE("SelfID completion called outside of bus reset!");
+
+ host->node_id = LOCAL_BUS | phyid;
host->is_root = isroot;
- host->node_count = check_selfids(host, host->selfid_count);
+ host->node_count = check_selfids(host);
if (!host->node_count) {
if (host->reset_retries++ < 20) {
/* selfid stage did not complete without error */
HPSB_NOTICE("Error in SelfID stage, resetting");
+ host->in_bus_reset = 0;
hpsb_reset_bus(host, LONG_RESET);
return;
} else {
}
host->reset_retries = 0;
- atomic_inc(&host->generation);
- if (isroot) host->template->devctl(host, ACT_CYCLE_MASTER, 1);
+ if (isroot) host->ops->devctl(host, ACT_CYCLE_MASTER, 1);
+ atomic_inc(&host->generation);
+ host->in_bus_reset = 0;
highlevel_host_reset(host);
}
{
struct hpsb_host *host = packet->host;
- if (!host->initialized || host->in_bus_reset
+ if (host->is_shutdown || host->in_bus_reset
|| (packet->generation != get_hpsb_generation(host))) {
return 0;
}
}
#endif
- return host->template->transmit_packet(host, packet);
+ return host->ops->transmit_packet(host, packet);
}
static void send_packet_nocare(struct hpsb_packet *packet)
struct list_head *lh;
LIST_HEAD(llist);
- host->template->devctl(host, CANCEL_REQUESTS, 0);
+ host->ops->devctl(host, CANCEL_REQUESTS, 0);
spin_lock_irqsave(&host->pending_pkt_lock, flags);
list_splice(&host->pending_packets, &llist);
}
+/*
+ * character device dispatching (see ieee1394_core.h)
+ * Dan Maas <dmaas@dcine.com>
+ */
+
+static struct {
+ struct file_operations *file_ops;
+ struct module *module;
+} ieee1394_chardevs[16];
+
+static rwlock_t ieee1394_chardevs_lock = RW_LOCK_UNLOCKED;
+
+static int ieee1394_dispatch_open(struct inode *inode, struct file *file);
+
+static struct file_operations ieee1394_chardev_ops = {
+ OWNER_THIS_MODULE
+ open: ieee1394_dispatch_open,
+};
+
+devfs_handle_t ieee1394_devfs_handle;
+
+
+/* claim a block of minor numbers */
+int ieee1394_register_chardev(int blocknum,
+ struct module *module,
+ struct file_operations *file_ops)
+{
+ int retval;
+
+ if( (blocknum < 0) || (blocknum > 15) )
+ return -EINVAL;
+
+ write_lock(&ieee1394_chardevs_lock);
+
+ if(ieee1394_chardevs[blocknum].file_ops == NULL) {
+ /* grab the minor block */
+ ieee1394_chardevs[blocknum].file_ops = file_ops;
+ ieee1394_chardevs[blocknum].module = module;
+
+ retval = 0;
+
+ V22_COMPAT_MOD_INC_USE_COUNT;
+ } else {
+ /* block already taken */
+ retval = -EBUSY;
+ }
+
+ write_unlock(&ieee1394_chardevs_lock);
+
+ return retval;
+}
+
+/* release a block of minor numbers */
+void ieee1394_unregister_chardev(int blocknum)
+{
+ if( (blocknum < 0) || (blocknum > 15) )
+ return;
+
+ write_lock(&ieee1394_chardevs_lock);
+
+ if(ieee1394_chardevs[blocknum].file_ops) {
+ ieee1394_chardevs[blocknum].file_ops = NULL;
+ ieee1394_chardevs[blocknum].module = NULL;
+ V22_COMPAT_MOD_DEC_USE_COUNT;
+ }
+
+ write_unlock(&ieee1394_chardevs_lock);
+}
+
+/* the point of entry for open() on any ieee1394 character device */
+static int ieee1394_dispatch_open(struct inode *inode, struct file *file)
+{
+ struct file_operations *file_ops;
+ struct module *module;
+ int blocknum;
+ int retval = -ENODEV;
+
+ /*
+ Maintaining correct module reference counts is tricky here!
+
+ For Linux v2.2:
+
+ The task-specific driver is expected to maintain its own
+ reference count via V22_COMPAT_MOD_[INC,DEC]_USE_COUNT.
+ We don't need to do anything special.
+
+ For Linux v2.4 and later:
+
+ The key thing to remember is that the VFS increments the
+ reference count of ieee1394 before it calls
+ ieee1394_dispatch_open().
+
+ If the open() succeeds, then we need to transfer this extra
+ reference to the task-specific driver module (e.g. raw1394).
+ The VFS will deref the driver module automatically when the
+ file is later released.
+
+ If the open() fails, then the VFS will drop the
+ reference count of whatever module file->f_op->owner points
+ to, immediately after this function returns.
+
+ The comments below refer to the 2.4 case, since the 2.2
+ case is trivial.
+
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+ /* 2.2 */
+#define INCREF(mod) do {} while (0)
+#define DECREF(mod) do {} while (0)
+#else
+ /* 2.4 */
+#define INCREF(mod_) do { struct module *mod = (struct module*) mod_; \
+ if(mod != NULL) __MOD_INC_USE_COUNT(mod); } while(0)
+#define DECREF(mod_) do { struct module *mod = (struct module*) mod_; \
+ if(mod != NULL) __MOD_DEC_USE_COUNT(mod); } while(0)
+#endif
+
+ /* shift away lower four bits of the minor
+ to get the index of the ieee1394_driver
+ we want */
+
+ blocknum = (minor(inode->i_rdev) >> 4) & 0xF;
+
+ /* printk("ieee1394_dispatch_open(%d)", blocknum); */
+
+ /* lock the whole kernel here, to prevent a driver from
+ being unloaded between the file_ops lookup and the open */
+
+ lock_kernel();
+
+ read_lock(&ieee1394_chardevs_lock);
+ file_ops = ieee1394_chardevs[blocknum].file_ops;
+ module = ieee1394_chardevs[blocknum].module;
+ read_unlock(&ieee1394_chardevs_lock);
+
+ if(file_ops == NULL) {
+ goto out_fail;
+ }
+
+ /* redirect all subsequent requests to the driver's
+ own file_operations */
+ file->f_op = file_ops;
+
+ /* bump the reference count of the driver that
+ will receive the open() */
+ INCREF(module);
+
+ /* at this point BOTH ieee1394 and the task-specific driver have
+ an extra reference */
+
+ /* follow through with the open() */
+ retval = file_ops->open(inode, file);
+
+ if(retval) {
+
+ /* if the open() failed, then we need to drop the
+ extra reference we gave to the task-specific
+ driver */
+
+ DECREF(module);
+ goto out_fail;
+
+ } else {
+
+ /* if the open() succeeded, then ieee1394 will be left
+ with an extra module reference, so we discard it here.*/
+
+ DECREF(THIS_MODULE);
+
+ /* the task-specific driver still has the extra
+ reference we gave it. This extra reference prevents
+ the module from unloading while the file is open,
+ and will be dropped by the VFS when the file is
+ released. */
+
+ unlock_kernel();
+ return 0;
+ }
+
+out_fail:
+ /* point the file's f_ops back to ieee1394. The VFS will then
+ decrement ieee1394's reference count immediately after this
+ function returns. */
+
+ file->f_op = &ieee1394_chardev_ops;
+ unlock_kernel();
+ return retval;
+
+#undef INCREF
+#undef DECREF
+
+}
+
+struct proc_dir_entry *ieee1394_procfs_entry;
+
static int __init ieee1394_init(void)
{
hpsb_packet_cache = kmem_cache_create("hpsb_packet", sizeof(struct hpsb_packet),
0, 0, NULL, NULL);
+
+ ieee1394_devfs_handle = devfs_mk_dir(NULL, "ieee1394", NULL);
+
+ if (register_chrdev(IEEE1394_MAJOR, "ieee1394", &ieee1394_chardev_ops)) {
+ HPSB_ERR("unable to register character device major %d!\n", IEEE1394_MAJOR);
+ devfs_unregister(ieee1394_devfs_handle);
+ return -ENODEV;
+ }
+
+#ifdef CONFIG_PROC_FS
+ /* Must be done before we start everything else, since the drivers
+ * may use it. */
+ ieee1394_procfs_entry = proc_mkdir( "ieee1394", proc_bus);
+ if (ieee1394_procfs_entry == NULL) {
+ HPSB_ERR("unable to create /proc/bus/ieee1394\n");
+ unregister_chrdev(IEEE1394_MAJOR, "ieee1394");
+ devfs_unregister(ieee1394_devfs_handle);
+ return -ENOMEM;
+ }
+ ieee1394_procfs_entry->owner = THIS_MODULE;
+#endif
+
init_hpsb_highlevel();
init_csr();
if (!disable_nodemgr)
cleanup_csr();
kmem_cache_destroy(hpsb_packet_cache);
+
+ unregister_chrdev(IEEE1394_MAJOR, "ieee1394");
+
+ /* it's ok to pass a NULL devfs_handle to devfs_unregister */
+ devfs_unregister(ieee1394_devfs_handle);
+
+ remove_proc_entry("ieee1394", proc_bus);
}
module_init(ieee1394_init);
/* Exported symbols */
EXPORT_SYMBOL(hpsb_register_lowlevel);
EXPORT_SYMBOL(hpsb_unregister_lowlevel);
-EXPORT_SYMBOL(hpsb_get_host);
-EXPORT_SYMBOL(hpsb_inc_host_usage);
-EXPORT_SYMBOL(hpsb_dec_host_usage);
+EXPORT_SYMBOL(hpsb_alloc_host);
+EXPORT_SYMBOL(hpsb_add_host);
+EXPORT_SYMBOL(hpsb_remove_host);
+EXPORT_SYMBOL(hpsb_ref_host);
+EXPORT_SYMBOL(hpsb_unref_host);
EXPORT_SYMBOL(hpsb_speedto_str);
EXPORT_SYMBOL(alloc_hpsb_packet);
EXPORT_SYMBOL(highlevel_add_host);
EXPORT_SYMBOL(highlevel_remove_host);
EXPORT_SYMBOL(highlevel_host_reset);
-EXPORT_SYMBOL(highlevel_add_one_host);
EXPORT_SYMBOL(hpsb_guid_get_entry);
EXPORT_SYMBOL(hpsb_nodeid_get_entry);
EXPORT_SYMBOL(hpsb_register_protocol);
EXPORT_SYMBOL(hpsb_unregister_protocol);
EXPORT_SYMBOL(hpsb_release_unit_directory);
+
+EXPORT_SYMBOL(ieee1394_register_chardev);
+EXPORT_SYMBOL(ieee1394_unregister_chardev);
+EXPORT_SYMBOL(ieee1394_devfs_handle);
+
+EXPORT_SYMBOL(ieee1394_procfs_entry);
#include <linux/tqueue.h>
#include <linux/slab.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/proc_fs.h>
#include <asm/semaphore.h>
#include "hosts.h"
return atomic_read(&host->generation);
}
-
/*
* Queue packet for transmitting, return 0 for failure.
*/
void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
int write_acked);
+
+/*
+ * CHARACTER DEVICE DISPATCHING
+ *
+ * All ieee1394 character device drivers share the same major number
+ * (major 171). The 256 minor numbers are allocated to the various
+ * task-specific interfaces (raw1394, video1394, dv1394, etc) in
+ * blocks of 16.
+ *
+ * The core ieee1394.o modules handles the initial open() for all
+ * character devices on major 171; it then dispatches to the
+ * appropriate task-specific driver.
+ *
+ * Minor device number block allocations:
+ *
+ * Block 0 ( 0- 15) raw1394
+ * Block 1 ( 16- 31) video1394
+ * Block 2 ( 32- 47) dv1394
+ *
+ * Blocks 3-14 free for future allocation
+ *
+ * Block 15 (240-255) reserved for drivers under development, etc.
+ */
+
+#define IEEE1394_MAJOR 171
+
+#define IEEE1394_MINOR_BLOCK_RAW1394 0
+#define IEEE1394_MINOR_BLOCK_VIDEO1394 1
+#define IEEE1394_MINOR_BLOCK_DV1394 2
+#define IEEE1394_MINOR_BLOCK_EXPERIMENTAL 15
+
+/* return the index (within a minor number block) of a file */
+static inline unsigned char ieee1394_file_to_instance(struct file *file)
+{
+ unsigned char minor = minor(file->f_dentry->d_inode->i_rdev);
+
+ /* return lower 4 bits */
+ return minor & 0xF;
+}
+
+/*
+ * Task-specific drivers should call ieee1394_register_chardev() to
+ * request a block of 16 minor numbers.
+ *
+ * Returns 0 if the request was successful, -EBUSY if the block was
+ * already taken.
+ */
+
+int ieee1394_register_chardev(int blocknum, /* 0-15 */
+ struct module *module, /* THIS_MODULE */
+ struct file_operations *file_ops);
+
+/* release a block of minor numbers */
+void ieee1394_unregister_chardev(int blocknum);
+
+/* the devfs handle for /dev/ieee1394; NULL if devfs is not in use */
+extern devfs_handle_t ieee1394_devfs_handle;
+
+/* the proc_fs entry for /proc/ieee1394 */
+extern struct proc_dir_entry *ieee1394_procfs_entry;
+
#endif /* _IEEE1394_CORE_H */
+++ /dev/null
-/*
- * IEEE 1394 for Linux
- *
- * Exported symbols for module usage.
- *
- * Copyright (C) 1999 Andreas E. Bombe
- *
- * This code is licensed under the GPL. See the file COPYING in the root
- * directory of the kernel sources for details.
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/string.h>
-
-#include "ieee1394_types.h"
-#include "hosts.h"
-#include "ieee1394_core.h"
-#include "ieee1394_transactions.h"
-#include "ieee1394_hotplug.h"
-#include "highlevel.h"
-#include "nodemgr.h"
-
-EXPORT_SYMBOL(hpsb_register_lowlevel);
-EXPORT_SYMBOL(hpsb_unregister_lowlevel);
-EXPORT_SYMBOL(hpsb_get_host);
-EXPORT_SYMBOL(hpsb_inc_host_usage);
-EXPORT_SYMBOL(hpsb_dec_host_usage);
-
-EXPORT_SYMBOL(alloc_hpsb_packet);
-EXPORT_SYMBOL(free_hpsb_packet);
-EXPORT_SYMBOL(hpsb_send_packet);
-EXPORT_SYMBOL(hpsb_reset_bus);
-EXPORT_SYMBOL(hpsb_bus_reset);
-EXPORT_SYMBOL(hpsb_selfid_received);
-EXPORT_SYMBOL(hpsb_selfid_complete);
-EXPORT_SYMBOL(hpsb_packet_sent);
-EXPORT_SYMBOL(hpsb_packet_received);
-
-EXPORT_SYMBOL(get_tlabel);
-EXPORT_SYMBOL(free_tlabel);
-EXPORT_SYMBOL(fill_async_readquad);
-EXPORT_SYMBOL(fill_async_readquad_resp);
-EXPORT_SYMBOL(fill_async_readblock);
-EXPORT_SYMBOL(fill_async_readblock_resp);
-EXPORT_SYMBOL(fill_async_writequad);
-EXPORT_SYMBOL(fill_async_writeblock);
-EXPORT_SYMBOL(fill_async_write_resp);
-EXPORT_SYMBOL(fill_async_lock);
-EXPORT_SYMBOL(fill_async_lock_resp);
-EXPORT_SYMBOL(fill_iso_packet);
-EXPORT_SYMBOL(fill_phy_packet);
-EXPORT_SYMBOL(hpsb_make_readqpacket);
-EXPORT_SYMBOL(hpsb_make_readbpacket);
-EXPORT_SYMBOL(hpsb_make_writeqpacket);
-EXPORT_SYMBOL(hpsb_make_writebpacket);
-EXPORT_SYMBOL(hpsb_make_lockpacket);
-EXPORT_SYMBOL(hpsb_make_phypacket);
-EXPORT_SYMBOL(hpsb_packet_success);
-EXPORT_SYMBOL(hpsb_make_packet);
-EXPORT_SYMBOL(hpsb_read);
-EXPORT_SYMBOL(hpsb_write);
-EXPORT_SYMBOL(hpsb_lock);
-
-EXPORT_SYMBOL(hpsb_register_highlevel);
-EXPORT_SYMBOL(hpsb_unregister_highlevel);
-EXPORT_SYMBOL(hpsb_register_addrspace);
-EXPORT_SYMBOL(hpsb_listen_channel);
-EXPORT_SYMBOL(hpsb_unlisten_channel);
-EXPORT_SYMBOL(highlevel_read);
-EXPORT_SYMBOL(highlevel_write);
-EXPORT_SYMBOL(highlevel_lock);
-EXPORT_SYMBOL(highlevel_lock64);
-EXPORT_SYMBOL(highlevel_add_host);
-EXPORT_SYMBOL(highlevel_remove_host);
-EXPORT_SYMBOL(highlevel_host_reset);
-EXPORT_SYMBOL(highlevel_add_one_host);
-
-EXPORT_SYMBOL(hpsb_guid_get_entry);
-EXPORT_SYMBOL(hpsb_nodeid_get_entry);
-EXPORT_SYMBOL(hpsb_get_host_by_ne);
-EXPORT_SYMBOL(hpsb_guid_fill_packet);
-EXPORT_SYMBOL(hpsb_register_protocol);
-EXPORT_SYMBOL(hpsb_unregister_protocol);
-EXPORT_SYMBOL(hpsb_release_unit_directory);
-
-MODULE_LICENSE("GPL");
packet->node_id);
return -EAGAIN;
}
- HPSB_PANIC("reached unreachable code 1 in " __FUNCTION__);
+ HPSB_PANIC("reached unreachable code 1 in %s", __FUNCTION__);
case ACK_BUSY_X:
case ACK_BUSY_A:
return -EAGAIN;
}
- HPSB_PANIC("reached unreachable code 2 in " __FUNCTION__);
+ HPSB_PANIC("reached unreachable code 2 in %s", __FUNCTION__);
}
#include <linux/types.h>
#include <linux/version.h>
#include <linux/list.h>
+#include <linux/init.h>
#include <asm/byteorder.h>
#define INIT_TQ_HEAD(tq) INIT_LIST_HEAD(&(tq))
#endif
+/* The great kdev_t changeover in 2.5.x */
+#include <linux/kdev_t.h>
+#ifndef minor
+#define minor(dev) MINOR(dev)
+#endif
+
+#ifndef __devexit_p
+#define __devexit_p(x) x
+#endif
+
/* This showed up around this time */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,12)
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/kmod.h>
+#include <linux/completion.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
#include "ieee1394_types.h"
#include "ieee1394.h"
struct host_info {
struct hpsb_host *host;
- struct tq_struct task;
struct list_head list;
+ struct completion started;
+ struct completion exited;
+ wait_queue_head_t wait;
+ int pid;
};
+#ifdef CONFIG_PROC_FS
+
+static int raw1394_read_proc(char *buffer, char **start, off_t offset,
+ int size, int *eof, void *data )
+{
+ struct list_head *lh;
+ struct node_entry *ne;
+ int disp_size = 0;
+ char display_str[1024];
+
+#define PUTF(fmt, args...) disp_size += sprintf(display_str, fmt, ## args); strcat(buffer,display_str)
+ buffer[0] = '\0';
+ list_for_each(lh, &node_list) {
+ ne = list_entry(lh, struct node_entry, list);
+ if (!ne)
+ continue;
+ PUTF("Node[" NODE_BUS_FMT "] GUID[%016Lx]:\n",
+ NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid);
+ if (ne->host != NULL && ne->host->node_id == ne->nodeid) {
+ PUTF("\tNodes connected : %d\n", ne->host->node_count);
+ PUTF("\tSelfIDs received: %d\n", ne->host->selfid_count);
+ PUTF("\tOwn ID : 0x%08x\n",ne->host->node_id);
+ PUTF("\tIrm ID : 0x%08x\n",ne->host->irm_id);
+ PUTF("\tBusMgr ID : 0x%08x\n",ne->host->busmgr_id);
+ PUTF("\tBR IR IC II IB\n");
+ PUTF("\t%02d %02d %02d %02d %02d\n",
+ ne->host->in_bus_reset, ne->host->is_root,
+ ne->host->is_cycmst, ne->host->is_irm,
+ ne->host->is_busmgr);
+ }
+ PUTF("\tVendor ID: %s [0x%06x]\n",
+ ne->vendor_name ?: "Unknown", ne->vendor_id);
+ PUTF("\tCapabilities: 0x%06x\n", ne->capabilities);
+ PUTF("\tirmc=%d cmc=%d isc=%d bmc=%d pmc=%d cyc_clk_acc=%d max_rec=%d gen=%d lspd=%d\n",
+ ne->busopt.irmc, ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc,
+ ne->busopt.pmc, ne->busopt.cyc_clk_acc, ne->busopt.max_rec,
+ ne->busopt.generation, ne->busopt.lnkspd);
+ }
+#undef PUTF
+ return disp_size;
+}
+#endif /* CONFIG_PROC_FS */
+
static void nodemgr_process_config_rom(struct node_entry *ne,
quadlet_t busoptions);
+static int nodemgr_read_quadlet(struct hpsb_host *host,
+ nodeid_t nodeid, octlet_t address,
+ quadlet_t *quad)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < 3; i++) {
+ ret = hpsb_read(host, nodeid, address, quad, 4);
+ if (ret != -EAGAIN)
+ break;
+ }
+ *quad = be32_to_cpu(*quad);
+
+ return ret;
+}
+
+static int nodemgr_size_text_leaf(struct hpsb_host *host,
+ nodeid_t nodeid,
+ octlet_t address)
+{
+ quadlet_t quad;
+ int size = 0;
+ if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+ return -1;
+ if (CONFIG_ROM_KEY(quad) == CONFIG_ROM_DESCRIPTOR_LEAF) {
+ /* This is the offset. */
+ address += 4 * CONFIG_ROM_VALUE(quad);
+ if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+ return -1;
+ /* Now we got the size of the text descriptor leaf. */
+ size = CONFIG_ROM_LEAF_LENGTH(quad);
+ }
+ return size;
+}
+
+static int nodemgr_read_text_leaf(struct hpsb_host *host,
+ nodeid_t nodeid,
+ octlet_t address,
+ quadlet_t *quadp)
+{
+ quadlet_t quad;
+ int i, size, ret;
+
+ if (nodemgr_read_quadlet(host, nodeid, address, &quad)
+ && CONFIG_ROM_KEY(quad) != CONFIG_ROM_DESCRIPTOR_LEAF)
+ return -1;
+
+ /* This is the offset. */
+ address += 4 * CONFIG_ROM_VALUE(quad);
+ if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+ return -1;
+
+ /* Now we got the size of the text descriptor leaf. */
+ size = CONFIG_ROM_LEAF_LENGTH(quad) - 2;
+ if (size <= 0)
+ return -1;
+
+ address += 4;
+ for (i = 0; i < 2; i++, address += 4, quadp++) {
+ if (nodemgr_read_quadlet(host, nodeid, address, quadp))
+ return -1;
+ }
+
+ /* Now read the text string. */
+ ret = -ENXIO;
+ for (; size > 0; size--, address += 4, quadp++) {
+ for (i = 0; i < 3; i++) {
+ ret = hpsb_read(host, nodeid, address, quadp, 4);
+ if (ret != -EAGAIN)
+ break;
+ }
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static struct node_entry *nodemgr_scan_root_directory
+ (struct hpsb_host *host, nodeid_t nodeid)
+{
+ octlet_t address;
+ quadlet_t quad;
+ int length;
+ int code, size, total_size;
+ struct node_entry *ne;
+
+ address = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
+
+ if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+ return NULL;
+ address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4;
+
+ if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+ return NULL;
+ length = CONFIG_ROM_ROOT_LENGTH(quad);
+ address += 4;
+
+ size = 0;
+ total_size = sizeof(struct node_entry);
+ for (; length > 0; length--, address += 4) {
+ if (nodemgr_read_quadlet(host, nodeid, address, &quad))
+ return NULL;
+ code = CONFIG_ROM_KEY(quad);
+
+ if (code == CONFIG_ROM_VENDOR_ID) {
+ /* Check if there is a text descriptor leaf
+ immediately after this. */
+ length--;
+ if (length <= 0)
+ break;
+ address += 4;
+ size = nodemgr_size_text_leaf(host, nodeid,
+ address);
+ switch (size) {
+ case -1:
+ return NULL;
+ break;
+ case 0:
+ break;
+ default:
+ total_size += (size + 1) * sizeof (quadlet_t);
+ break;
+ }
+ break;
+ }
+ }
+ ne = kmalloc(total_size, SLAB_ATOMIC);
+ if (ne != NULL) {
+ if (size != 0) {
+ ne->vendor_name
+ = (const char *) &(ne->quadlets[2]);
+ ne->quadlets[size] = 0;
+ }
+ else {
+ ne->vendor_name = NULL;
+ }
+ }
+ return ne;
+}
static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoptions,
struct hpsb_host *host, nodeid_t nodeid)
struct node_entry *ne;
unsigned long flags;
- ne = kmalloc(sizeof(struct node_entry), SLAB_ATOMIC);
+ ne = nodemgr_scan_root_directory (host, nodeid);
if (!ne) return NULL;
INIT_LIST_HEAD(&ne->list);
nodemgr_process_config_rom (ne, busoptions);
- HPSB_DEBUG("%s added: node " NODE_BUS_FMT ", GUID %016Lx",
+ HPSB_DEBUG("%s added: Node[" NODE_BUS_FMT "] GUID[%016Lx] [%s]",
(host->node_id == nodeid) ? "Local host" : "Device",
- NODE_BUS_ARGS(nodeid), (unsigned long long)guid);
+ NODE_BUS_ARGS(nodeid), (unsigned long long)guid,
+ ne->vendor_name ?: "Unknown");
return ne;
}
return NULL;
}
-int nodemgr_read_quadlet(struct node_entry *ne,
- octlet_t address, quadlet_t *quad)
+static struct unit_directory *nodemgr_scan_unit_directory
+ (struct node_entry *ne, octlet_t address)
{
- int i;
- int ret = 0;
+ struct unit_directory *ud;
+ quadlet_t quad;
+ u8 flags, todo;
+ int length, size, total_size, count;
+ int vendor_name_size, model_name_size;
- for (i = 0; i < 3; i++) {
- ret = hpsb_read(ne->host, ne->nodeid, address, quad, 4);
- if (ret != -EAGAIN)
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
+ return NULL;
+ length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ;
+ address += 4;
+
+ size = 0;
+ total_size = sizeof (struct unit_directory);
+ flags = 0;
+ count = 0;
+ vendor_name_size = 0;
+ model_name_size = 0;
+ for (; length > 0; length--, address += 4) {
+ int code;
+ quadlet_t value;
+
+retry:
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
+ return NULL;
+ code = CONFIG_ROM_KEY(quad);
+ value = CONFIG_ROM_VALUE(quad);
+
+ todo = 0;
+ switch (code) {
+ case CONFIG_ROM_VENDOR_ID:
+ todo = UNIT_DIRECTORY_VENDOR_TEXT;
break;
- }
- *quad = be32_to_cpu(*quad);
- return ret;
-}
+ case CONFIG_ROM_MODEL_ID:
+ todo = UNIT_DIRECTORY_MODEL_TEXT;
+ break;
+
+ case CONFIG_ROM_SPECIFIER_ID:
+ case CONFIG_ROM_UNIT_SW_VERSION:
+ break;
+
+ case CONFIG_ROM_DESCRIPTOR_LEAF:
+ case CONFIG_ROM_DESCRIPTOR_DIRECTORY:
+ /* TODO: read strings... icons? */
+ break;
+
+ default:
+ /* Which types of quadlets do we want to
+ store? Only count immediate values and
+ CSR offsets for now. */
+ code &= CONFIG_ROM_KEY_TYPE_MASK;
+ if ((code & 0x80) == 0)
+ count++;
+ break;
+ }
-#define CONFIG_ROM_VENDOR_ID 0x03
-#define CONFIG_ROM_MODEL_ID 0x17
-#define CONFIG_ROM_NODE_CAPABILITES 0x0C
-#define CONFIG_ROM_UNIT_DIRECTORY 0xd1
-#define CONFIG_ROM_SPECIFIER_ID 0x12
-#define CONFIG_ROM_VERSION 0x13
-#define CONFIG_ROM_DESCRIPTOR_LEAF 0x81
-#define CONFIG_ROM_DESCRIPTOR_DIRECTORY 0xc1
+ if (todo) {
+ /* Check if there is a text descriptor leaf
+ immediately after this. */
+ length--;
+ if (length <= 0)
+ break;
+ address += 4;
+ size = nodemgr_size_text_leaf(ne->host,
+ ne->nodeid,
+ address);
+ if (todo | UNIT_DIRECTORY_VENDOR_TEXT)
+ vendor_name_size = size;
+ else
+ model_name_size = size;
+ switch (size) {
+ case -1:
+ return NULL;
+ break;
+ case 0:
+ goto retry;
+ break;
+ default:
+ flags |= todo;
+ total_size += (size + 1) * sizeof (quadlet_t);
+ break;
+ }
+ }
+ }
+ total_size += count * sizeof (quadlet_t);
+ ud = kmalloc (total_size, GFP_KERNEL);
+ if (ud != NULL) {
+ memset (ud, 0, sizeof *ud);
+ ud->flags = flags;
+ ud->count = count;
+ ud->vendor_name_size = vendor_name_size;
+ ud->model_name_size = model_name_size;
+ /* If there is no vendor name in the unit directory,
+ use the one in the root directory. */
+ ud->vendor_name = ne->vendor_name;
+ }
+ return ud;
+}
/* This implementation currently only scans the config rom and its
* immediate unit directories looking for software_id and
octlet_t address)
{
struct unit_directory *ud;
- octlet_t a;
quadlet_t quad;
- int length, i;
+ quadlet_t *infop;
+ int length;
- if (!(ud = kmalloc (sizeof *ud, GFP_KERNEL)))
+ if (!(ud = nodemgr_scan_unit_directory(ne, address)))
goto unit_directory_error;
- memset (ud, 0, sizeof *ud);
ud->ne = ne;
ud->address = address;
- ud->arb_count = 0;
- if (nodemgr_read_quadlet(ne, address, &quad))
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
goto unit_directory_error;
- length = quad >> 16;
- a = address + 4;
+ length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ;
+ address += 4;
- for (i = 0; i < length; i++, a += 4) {
+ infop = (quadlet_t *) ud->quadlets;
+ for (; length > 0; length--, address += 4, infop++) {
int code;
quadlet_t value;
+ quadlet_t *quadp;
- if (nodemgr_read_quadlet(ne, a, &quad))
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
goto unit_directory_error;
- code = quad >> 24;
- value = quad & 0xffffff;
+ code = CONFIG_ROM_KEY(quad) ;
+ value = CONFIG_ROM_VALUE(quad);
switch (code) {
case CONFIG_ROM_VENDOR_ID:
ud->vendor_id = value;
ud->flags |= UNIT_DIRECTORY_VENDOR_ID;
+ if ((ud->flags & UNIT_DIRECTORY_VENDOR_TEXT) != 0) {
+ length--;
+ address += 4;
+ quadp = &(ud->quadlets[ud->count]);
+ if (nodemgr_read_text_leaf(ne->host,
+ ne->nodeid,
+ address,
+ quadp) == 0
+ && quadp[0] == 0
+ && quadp[1] == 0) {
+ /* We only support minimal
+ ASCII and English. */
+ quadp[ud->vendor_name_size] = 0;
+ ud->vendor_name
+ = (const char *) &(quadp[2]);
+ }
+ }
break;
case CONFIG_ROM_MODEL_ID:
ud->model_id = value;
ud->flags |= UNIT_DIRECTORY_MODEL_ID;
+ if ((ud->flags & UNIT_DIRECTORY_MODEL_TEXT) != 0) {
+ length--;
+ address += 4;
+ quadp = &(ud->quadlets[ud->count + ud->vendor_name_size + 1]);
+ if (nodemgr_read_text_leaf(ne->host,
+ ne->nodeid,
+ address,
+ quadp) == 0
+ && quadp[0] == 0
+ && quadp[1] == 0) {
+ /* We only support minimal
+ ASCII and English. */
+ quadp[ud->model_name_size] = 0;
+ ud->model_name
+ = (const char *) &(quadp[2]);
+ }
+ }
break;
case CONFIG_ROM_SPECIFIER_ID:
ud->flags |= UNIT_DIRECTORY_SPECIFIER_ID;
break;
- case CONFIG_ROM_VERSION:
+ case CONFIG_ROM_UNIT_SW_VERSION:
ud->version = value;
ud->flags |= UNIT_DIRECTORY_VERSION;
break;
break;
default:
- if (ud->arb_count < 16) {
- /* Place them in the arbitrary pairs */
- ud->arb_keys[ud->arb_count] = code;
- ud->arb_values[ud->arb_count] = value;
- ud->arb_count++;
- }
+ /* Which types of quadlets do we want to
+ store? Only count immediate values and
+ CSR offsets for now. */
+ code &= CONFIG_ROM_KEY_TYPE_MASK;
+ if ((code & 0x80) == 0)
+ *infop = quad;
+ break;
}
}
#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
struct list_head *l;
- HPSB_DEBUG("vendor_id=0x%06x, capabilities=0x%06x",
- ne->vendor_id, ne->capabilities);
+ HPSB_DEBUG("vendor_id=0x%06x [%s], capabilities=0x%06x",
+ ne->vendor_id, ne->vendor_name ?: "Unknown",
+ ne->capabilities);
list_for_each (l, &ne->unit_directories) {
struct unit_directory *ud = list_entry (l, struct unit_directory, node_list);
HPSB_DEBUG("unit directory:");
if (ud->flags & UNIT_DIRECTORY_VENDOR_ID)
- HPSB_DEBUG(" vendor_id=0x%06x ", ud->vendor_id);
+ HPSB_DEBUG(" vendor_id=0x%06x [%s]",
+ ud->vendor_id,
+ ud->vendor_name ?: "Unknown");
if (ud->flags & UNIT_DIRECTORY_MODEL_ID)
- HPSB_DEBUG(" model_id=0x%06x ", ud->model_id);
+ HPSB_DEBUG(" model_id=0x%06x [%s]",
+ ud->model_id,
+ ud->model_name ?: "Unknown");
if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID)
HPSB_DEBUG(" sw_specifier_id=0x%06x ", ud->specifier_id);
if (ud->flags & UNIT_DIRECTORY_VERSION)
HPSB_DEBUG(" sw_version=0x%06x ", ud->version);
}
-#else
- return;
#endif
+ return;
}
static void nodemgr_process_root_directory(struct node_entry *ne)
{
octlet_t address;
quadlet_t quad;
- int length, i;
+ int length;
address = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
- if (nodemgr_read_quadlet(ne, address, &quad))
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
return;
- address += 4 + (quad >> 24) * 4;
+ address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4;
- if (nodemgr_read_quadlet(ne, address, &quad))
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
return;
- length = quad >> 16;
+ length = CONFIG_ROM_ROOT_LENGTH(quad);
address += 4;
- for (i = 0; i < length; i++, address += 4) {
+ for (; length > 0; length--, address += 4) {
int code, value;
- if (nodemgr_read_quadlet(ne, address, &quad))
+ if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad))
return;
- code = quad >> 24;
- value = quad & 0xffffff;
+ code = CONFIG_ROM_KEY(quad);
+ value = CONFIG_ROM_VALUE(quad);
switch (code) {
case CONFIG_ROM_VENDOR_ID:
ne->vendor_id = value;
+ /* Now check if there is a vendor name text
+ string. */
+ if (ne->vendor_name != NULL) {
+ length--;
+ address += 4;
+ if (nodemgr_read_text_leaf(ne->host,
+ ne->nodeid,
+ address,
+ ne->quadlets)
+ != 0
+ || ne->quadlets [0] != 0
+ || ne->quadlets [1] != 0)
+ /* We only support minimal
+ ASCII and English. */
+ ne->vendor_name = NULL;
+ }
break;
case CONFIG_ROM_NODE_CAPABILITES:
kfree(buf);
kfree(envp);
if (value != 0)
- HPSB_DEBUG("NodeMgr: hotplug policy returned 0x%x", value);
+ HPSB_DEBUG("NodeMgr: hotplug policy returned %d", value);
}
#else
{
#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
HPSB_DEBUG("NodeMgr: nodemgr_call_policy(): hotplug not enabled");
-#else
- return;
#endif
+ return;
}
#endif /* CONFIG_HOTPLUG */
struct hpsb_host *host, nodeid_t nodeid)
{
struct list_head *lh;
+ struct unit_directory *ud;
- if (ne->nodeid != nodeid)
+ if (ne->nodeid != nodeid) {
HPSB_DEBUG("Node " NODE_BUS_FMT " changed to " NODE_BUS_FMT,
NODE_BUS_ARGS(ne->nodeid), NODE_BUS_ARGS(nodeid));
-
- ne->host = host;
- ne->nodeid = nodeid;
+ ne->nodeid = nodeid;
+ }
if (ne->busopt.generation != ((busoptions >> 4) & 0xf))
nodemgr_process_config_rom (ne, busoptions);
atomic_set(&ne->generation, get_hpsb_generation(ne->host));
list_for_each (lh, &ne->unit_directories) {
- struct unit_directory *ud;
-
ud = list_entry (lh, struct unit_directory, node_list);
if (ud->driver != NULL && ud->driver->update != NULL)
ud->driver->update(ud);
int header_count;
unsigned header_size;
quadlet_t quad;
+ int ret;
retry_configrom:
* to work though, and we are forced to doing quadlet
* sized reads. */
- if (hpsb_read(host, nodeid, base, &quad, 4)) {
- HPSB_ERR("ConfigROM quadlet transaction error for node " NODE_BUS_FMT,
- NODE_BUS_ARGS(nodeid));
+ ret = hpsb_read(host, nodeid, base, &quad, 4);
+ if (ret) {
+ HPSB_ERR("ConfigROM quadlet transaction error (%d) for node " NODE_BUS_FMT,
+ ret, NODE_BUS_ARGS(nodeid));
goto retry_configrom;
}
buffer[header_count++] = be32_to_cpu(quad);
}
while (header_count <= header_size && header_count < buffer_length) {
- if (hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4)) {
- HPSB_ERR("ConfigROM quadlet transaction error for " NODE_BUS_FMT,
- NODE_BUS_ARGS(nodeid));
+ ret = hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4);
+ if (ret) {
+ HPSB_ERR("ConfigROM quadlet transaction error (%d) for " NODE_BUS_FMT,
+ ret, NODE_BUS_ARGS(nodeid));
goto retry_configrom;
}
buffer[header_count++] = be32_to_cpu(quad);
{
unsigned long flags;
- HPSB_DEBUG("Device removed: node " NODE_BUS_FMT ", GUID %016Lx",
- NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid);
+ HPSB_DEBUG("%s removed: Node[" NODE_BUS_FMT "] GUID[%016Lx] [%s]",
+ (ne->host->node_id == ne->nodeid) ? "Local host" : "Device",
+ NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid,
+ ne->vendor_name ?: "Unknown");
write_lock_irqsave(&unit_directory_lock, flags);
nodemgr_free_unit_directories(ne);
/* This is where we probe the nodes for their information and provided
* features. */
-static void nodemgr_node_probe(void *data)
+static void nodemgr_node_probe_one(struct hpsb_host *host, nodeid_t nodeid)
{
- struct hpsb_host *host = (struct hpsb_host *)data;
- struct selfid *sid = (struct selfid *)host->topology_map;
- struct list_head *lh, *next;
struct node_entry *ne;
- int nodecount = host->node_count;
- nodeid_t nodeid = LOCAL_BUS;
quadlet_t buffer[5];
octlet_t guid;
- unsigned long flags;
/* We need to detect when the ConfigROM's generation has changed,
* so we only update the node's info when it needs to be. */
- for (; nodecount; nodecount--, nodeid++, sid++) {
- /* Skip extended, and non-active node's */
- while (sid->extended)
- sid++;
- if (!sid->link_active)
- continue;
- if (read_businfo_block (host, nodeid, buffer, sizeof(buffer) >> 2))
- continue;
+ if (read_businfo_block (host, nodeid, buffer, sizeof(buffer) >> 2))
+ return;
- if (buffer[1] != IEEE1394_BUSID_MAGIC) {
- /* This isn't a 1394 device */
- HPSB_ERR("Node " NODE_BUS_FMT " isn't an IEEE 1394 device",
- NODE_BUS_ARGS(nodeid));
- continue;
- }
+ if (buffer[1] != IEEE1394_BUSID_MAGIC) {
+ /* This isn't a 1394 device */
+ HPSB_ERR("Node " NODE_BUS_FMT " isn't an IEEE 1394 device",
+ NODE_BUS_ARGS(nodeid));
+ return;
+ }
- guid = ((u64)buffer[3] << 32) | buffer[4];
- ne = hpsb_guid_get_entry(guid);
+ guid = ((u64)buffer[3] << 32) | buffer[4];
+ ne = hpsb_guid_get_entry(guid);
- if (!ne)
- nodemgr_create_node(guid, buffer[2], host, nodeid);
- else
- nodemgr_update_node(ne, buffer[2], host, nodeid);
- }
+ if (!ne)
+ nodemgr_create_node(guid, buffer[2], host, nodeid);
+ else
+ nodemgr_update_node(ne, buffer[2], host, nodeid);
+
+ return;
+}
+
+static void nodemgr_node_probe_cleanup(struct hpsb_host *host)
+{
+ unsigned long flags;
+ struct list_head *lh, *next;
+ struct node_entry *ne;
/* Now check to see if we have any nodes that aren't referenced
* any longer. */
- write_lock_irqsave(&node_lock, flags);
- for (lh = node_list.next; lh != &node_list; lh = next) {
+ write_lock_irqsave(&node_lock, flags);
+ list_for_each_safe(lh, next, &node_list) {
ne = list_entry(lh, struct node_entry, list);
- next = lh->next;
/* Only checking this host */
if (ne->host != host)
return;
}
+static void nodemgr_node_probe(struct hpsb_host *host)
+{
+ int nodecount = host->node_count;
+ struct selfid *sid = (struct selfid *)host->topology_map;
+ nodeid_t nodeid = LOCAL_BUS;
+
+ /* Scan each node on the bus */
+ for (; nodecount; nodecount--, nodeid++, sid++) {
+ while (sid->extended)
+ sid++;
+ if (!sid->link_active)
+ continue;
+
+ nodemgr_node_probe_one(host, nodeid);
+ }
+
+ /* Cleanup if needed */
+ nodemgr_node_probe_cleanup(host);
+
+ return;
+}
+
+static int nodemgr_host_thread(void *__hi)
+{
+ struct host_info *hi = (struct host_info *)__hi;
+ lock_kernel();
+
+ /* No userlevel access needed */
+ daemonize();
+
+ strcpy(current->comm, "NodeMgr");
+
+ complete(&hi->started);
+
+ /* Sit and wait for a signal to probe the nodes on the bus. This
+ * happens when we get a bus reset. */
+ do {
+ interruptible_sleep_on(&hi->wait);
+ if (!signal_pending(current))
+ nodemgr_node_probe(hi->host);
+ } while (!signal_pending(current));
+
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+ HPSB_DEBUG ("NodeMgr: Exiting thread for %s", hi->host->driver->name);
+#endif
+
+ unlock_kernel();
+
+ complete_and_exit(&hi->exited, 0);
+}
struct node_entry *hpsb_guid_get_entry(u64 guid)
{
* until the first bus reset. */
hi->host = host;
INIT_LIST_HEAD(&hi->list);
- INIT_TQUEUE(&hi->task, nodemgr_node_probe, host);
+ init_completion(&hi->started);
+ init_completion(&hi->exited);
+ init_waitqueue_head(&hi->wait);
+
+ hi->pid = kernel_thread(nodemgr_host_thread, hi, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+
+ if (hi->pid < 0) {
+ HPSB_ERR ("NodeMgr: failed to start NodeMgr thread for %s", host->driver->name);
+ return;
+ }
+
+ wait_for_completion(&hi->started);
spin_lock_irqsave (&host_info_lock, flags);
list_add_tail (&hi->list, &host_info_list);
goto done_reset_host;
}
- schedule_task(&hi->task);
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+ HPSB_DEBUG ("NodeMgr: Processing host reset for %s", host->driver->name);
+#endif
+
+ wake_up(&hi->wait);
done_reset_host:
spin_unlock_irqrestore (&host_info_lock, flags);
struct list_head *lh, *next;
struct node_entry *ne;
unsigned long flags;
+ struct host_info *hi = NULL;
- /* Make sure we have no active scans */
- flush_scheduled_tasks();
+ spin_lock_irqsave (&host_info_lock, flags);
+ list_for_each_safe(lh, next, &host_info_list) {
+ struct host_info *myhi = list_entry(lh, struct host_info, list);
+ if (myhi->host == host) {
+ list_del(&myhi->list);
+ hi = myhi;
+ break;
+ }
+ }
- /* First remove all node entries for this host */
- write_lock_irqsave(&node_lock, flags);
+ if (!hi)
+ HPSB_ERR ("NodeMgr: host %s does not exist, cannot remove",
+ host->driver->name);
+ spin_unlock_irqrestore (&host_info_lock, flags);
- for (lh = node_list.next; lh != &node_list; lh = next) {
+ /* Even if we fail the host_info part, remove all the node
+ * entries. */
+ write_lock_irqsave(&node_lock, flags);
+ list_for_each_safe(lh, next, &node_list) {
ne = list_entry(lh, struct node_entry, list);
- next = lh->next;
-
- /* Only checking this host */
- if (ne->host != host)
- continue;
+ if (ne->host == host)
nodemgr_remove_node(ne);
}
write_unlock_irqrestore(&node_lock, flags);
- spin_lock_irqsave (&host_info_lock, flags);
- list_for_each(lh, &host_info_list) {
- struct host_info *hi = list_entry(lh, struct host_info, list);
- if (hi->host == host) {
- list_del(&hi->list);
- kfree (hi);
- break;
+ if (hi) {
+ if (hi->pid >= 0) {
+ kill_proc(hi->pid, SIGTERM, 1);
+ wait_for_completion(&hi->exited);
}
+ kfree(hi);
}
- if (lh == host_info_list.next)
- HPSB_ERR ("NodeMgr: could not remove non-existent host");
-
- spin_unlock_irqrestore (&host_info_lock, flags);
-
return;
}
static struct hpsb_highlevel *hl;
+#define PROC_ENTRY "devices"
+
void init_ieee1394_nodemgr(void)
{
+#ifdef CONFIG_PROC_FS
+ if (!create_proc_read_entry(PROC_ENTRY, 0444, ieee1394_procfs_entry, raw1394_read_proc, NULL))
+ HPSB_ERR("Can't create devices procfs entry");
+#endif
hl = hpsb_register_highlevel("Node manager", &nodemgr_ops);
if (!hl) {
HPSB_ERR("NodeMgr: out of memory during ieee1394 initialization");
void cleanup_ieee1394_nodemgr(void)
{
hpsb_unregister_highlevel(hl);
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry(PROC_ENTRY, ieee1394_procfs_entry);
+#endif
}
#ifndef _IEEE1394_NODEMGR_H
#define _IEEE1394_NODEMGR_H
+#define CONFIG_ROM_BUS_INFO_LENGTH(q) ((q) >> 24)
+#define CONFIG_ROM_BUS_CRC_LENGTH(q) (((q) >> 16) & 0xff)
+#define CONFIG_ROM_BUS_CRC(q) ((q) & 0xffff)
+
+#define CONFIG_ROM_ROOT_LENGTH(q) ((q) >> 16)
+#define CONFIG_ROM_ROOT_CRC(q) ((q) & 0xffff)
+
+#define CONFIG_ROM_DIRECTORY_LENGTH(q) ((q) >> 16)
+#define CONFIG_ROM_DIRECTORY_CRC(q) ((q) & 0xffff)
+
+#define CONFIG_ROM_LEAF_LENGTH(q) ((q) >> 16)
+#define CONFIG_ROM_LEAF_CRC(q) ((q) & 0xffff)
+
+#define CONFIG_ROM_DESCRIPTOR_TYPE(q) ((q) >> 24)
+#define CONFIG_ROM_DESCRIPTOR_SPECIFIER_ID(q) ((q) & 0xffffff)
+#define CONFIG_ROM_DESCRIPTOR_WIDTH(q) ((q) >> 28)
+#define CONFIG_ROM_DESCRIPTOR_CHAR_SET(q) (((q) >> 16) & 0xfff)
+#define CONFIG_ROM_DESCRIPTOR_LANG(q) ((q) & 0xffff)
+
+#define CONFIG_ROM_KEY_ID_MASK 0x3f
+#define CONFIG_ROM_KEY_TYPE_MASK 0xc0
+#define CONFIG_ROM_KEY_TYPE_IMMEDIATE 0x00
+#define CONFIG_ROM_KEY_TYPE_OFFSET 0x40
+#define CONFIG_ROM_KEY_TYPE_LEAF 0x80
+#define CONFIG_ROM_KEY_TYPE_DIRECTORY 0xc0
+
+#define CONFIG_ROM_KEY(q) ((q) >> 24)
+#define CONFIG_ROM_VALUE(q) ((q) & 0xffffff)
+
+#define CONFIG_ROM_VENDOR_ID 0x03
+#define CONFIG_ROM_MODEL_ID 0x17
+#define CONFIG_ROM_NODE_CAPABILITES 0x0C
+#define CONFIG_ROM_UNIT_DIRECTORY 0xd1
+#define CONFIG_ROM_SPECIFIER_ID 0x12
+#define CONFIG_ROM_UNIT_SW_VERSION 0x13
+#define CONFIG_ROM_DESCRIPTOR_LEAF 0x81
+#define CONFIG_ROM_DESCRIPTOR_DIRECTORY 0xc1
+
/* '1' '3' '9' '4' in ASCII */
#define IEEE1394_BUSID_MAGIC 0x31333934
#define UNIT_DIRECTORY_MODEL_ID 0x02
#define UNIT_DIRECTORY_SPECIFIER_ID 0x04
#define UNIT_DIRECTORY_VERSION 0x08
+#define UNIT_DIRECTORY_VENDOR_TEXT 0x10
+#define UNIT_DIRECTORY_MODEL_TEXT 0x20
/*
* A unit directory corresponds to a protocol supported by the
octlet_t address; /* Address of the unit directory on the node */
u8 flags; /* Indicates which entries were read */
quadlet_t vendor_id;
- char *vendor_name;
+ const char *vendor_name;
+ int vendor_name_size;
quadlet_t model_id;
- char *model_name;
+ const char *model_name;
+ int model_name_size;
quadlet_t specifier_id;
quadlet_t version;
- /* Groupings for arbitrary key/value pairs */
- int arb_count; /* Number of arbitrary key/values */
- char arb_keys[16]; /* Up to 16 keys */
- quadlet_t arb_values[16]; /* Same for values */
-
struct hpsb_protocol_driver *driver;
void *driver_data;
/* For linking directories belonging to a node */
struct list_head node_list;
+
+ int count; /* Number of quadlets */
+ quadlet_t quadlets[0];
};
struct node_entry {
u32 vendor_id;
u32 capabilities;
struct list_head unit_directories;
+
+ const char *vendor_name;
+ quadlet_t quadlets[0];
};
static inline int hpsb_node_entry_valid(struct node_entry *ne)
* . Config ROM generation
*/
+/* Issues:
+ *
+ * - devctl BUS_RESET should treat arg as reset type
+ *
+ */
+
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/init.h>
#ifdef CONFIG_ALL_PPC
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#endif
-/* Revert to old bus reset algorithm that works on my Pismo until
- * the new one is fixed
- */
-#undef BUSRESET_WORKAROUND
-
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "hosts.h"
#ifdef CONFIG_IEEE1394_OHCI_DMA_DEBUG
#define OHCI_DMA_ALLOC(fmt, args...) \
- HPSB_ERR("%s("__FUNCTION__")alloc(%d): "fmt, OHCI1394_DRIVER_NAME, \
+ HPSB_ERR("%s(%s)alloc(%d): "fmt, OHCI1394_DRIVER_NAME, __FUNCTION__, \
++global_outstanding_dmas, ## args)
#define OHCI_DMA_FREE(fmt, args...) \
- HPSB_ERR("%s("__FUNCTION__")free(%d): "fmt, OHCI1394_DRIVER_NAME, \
+ HPSB_ERR("%s(%s)free(%d): "fmt, OHCI1394_DRIVER_NAME, __FUNCTION__, \
--global_outstanding_dmas, ## args)
u32 global_outstanding_dmas = 0;
#else
#define PRINT(level, card, fmt, args...) \
printk(level "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args)
-#define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10)
-
-static struct pci_device_id ohci1394_pci_tbl[] __devinitdata = {
- {
- class: PCI_CLASS_FIREWIRE_OHCI,
- class_mask: 0x00ffffff,
- vendor: PCI_ANY_ID,
- device: PCI_ANY_ID,
- subvendor: PCI_ANY_ID,
- subdevice: PCI_ANY_ID,
- },
- { 0, },
-};
-MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl);
-
static char version[] __devinitdata =
- "v0.51 08/08/01 Ben Collins <bcollins@debian.org>";
+ "$Revision: 1.91 $ Ben Collins <bcollins@debian.org>";
/* Module Parameters */
MODULE_PARM(attempt_root,"i");
MODULE_PARM_DESC(attempt_root, "Attempt to make the host root.");
static int attempt_root = 0;
-static unsigned int card_id_counter = 0;
-
static void dma_trm_tasklet(unsigned long data);
-static void remove_card(struct ti_ohci *ohci);
static void dma_trm_reset(struct dma_trm_ctx *d);
+static void __devexit ohci1394_pci_remove(struct pci_dev *pdev);
+
+static inline void ohci1394_run_irq_hooks(struct ti_ohci *ohci,
+ quadlet_t isoRecvEvent,
+ quadlet_t isoXmitEvent);
+
#ifndef __LITTLE_ENDIAN
/* Swap a series of quads inplace. */
static __inline__ void block_swab32(quadlet_t *data, size_t size) {
if ((self_id_count & 0x80000000) ||
((self_id_count & 0x00FF0000) != (q0 & 0x00FF0000))) {
PRINT(KERN_ERR, ohci->id,
- "Error in reception of SelfID packets [0x%08x/0x%08x]",
- self_id_count, q0);
+ "Error in reception of SelfID packets [0x%08x/0x%08x] (count: %d)",
+ self_id_count, q0, ohci->self_id_errors);
/* Tip by James Goodwin <jamesg@Filanet.com>:
* We had an error, generate another bus reset in response. */
}
return;
}
+
+ /* SelfID Ok, reset error counter. */
+ ohci->self_id_errors = 0;
size = ((self_id_count & 0x00001FFC) >> 2) - 1;
q++;
return;
}
-static int ohci_soft_reset(struct ti_ohci *ohci) {
+static void ohci_soft_reset(struct ti_ohci *ohci) {
int i;
reg_write(ohci, OHCI1394_HCControlSet, 0x00010000);
mdelay(1);
}
- DBGMSG (ohci->id, "Soft reset finished");
+ /* Now reenable LPS, since that's usually what we want after a
+ * softreset anyway. Wait 50msec to make sure we have full link
+ * enabled. */
+ reg_write(ohci, OHCI1394_HCControlSet, 0x00080000);
+ mdelay(50);
- return 0;
+ DBGMSG (ohci->id, "Soft reset finished");
}
static int run_context(struct ti_ohci *ohci, int reg, char *msg)
for (i=0; i<d->num_desc; i++) {
u32 c;
- c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
- DMA_CTL_BRANCH;
+ c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH;
if (generate_irq)
c |= DMA_CTL_IRQ;
static void ohci_init_config_rom(struct ti_ohci *ohci);
/* Global initialization */
-static int ohci_initialize(struct hpsb_host *host)
+static void ohci_initialize(struct ti_ohci *ohci)
{
- struct ti_ohci *ohci=host->hostdata;
- int retval, i;
+ int i;
quadlet_t buf;
spin_lock_init(&ohci->phy_reg_lock);
spin_lock_init(&ohci->event_lock);
- /* Soft reset */
- if ((retval = ohci_soft_reset(ohci)) < 0)
- return retval;
-
/* Put some defaults to these undefined bus options */
buf = reg_read(ohci, OHCI1394_BusOptions);
buf |= 0x60000000; /* Enable CMC and ISC */
buf &= ~0x98000000; /* Disable PMC, IRMC and BMC */
reg_write(ohci, OHCI1394_BusOptions, buf);
- /* Set Link Power Status (LPS) */
- reg_write(ohci, OHCI1394_HCControlSet, 0x00080000);
-
- /* After enabling LPS, we need to wait for the connection
- * between phy and link to be established. This should be
- * signaled by the LPS bit becoming 1, but this happens
- * immediately. Instead we wait for reads from LinkControl to
- * give a valid result, i.e. not 0xffffffff. */
- while (reg_read(ohci, OHCI1394_LinkControlSet) == 0xffffffff) {
- DBGMSG(ohci->id, "waiting for phy-link connection");
- mdelay(2);
- }
-
/* Set the bus number */
reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0);
reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
/* Initialize IR dma */
- ohci->nb_iso_rcv_ctx =
- get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet);
- DBGMSG(ohci->id, "%d iso receive contexts available",
- ohci->nb_iso_rcv_ctx);
for (i=0;i<ohci->nb_iso_rcv_ctx;i++) {
reg_write(ohci, OHCI1394_IsoRcvContextControlClear+32*i,
0xffffffff);
reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
/* Initialize IT dma */
- ohci->nb_iso_xmit_ctx =
- get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet);
- DBGMSG(ohci->id, "%d iso transmit contexts available",
- ohci->nb_iso_xmit_ctx);
for (i=0;i<ohci->nb_iso_xmit_ctx;i++) {
reg_write(ohci, OHCI1394_IsoXmitContextControlClear+32*i,
0xffffffff);
OHCI1394_respTxComplete |
OHCI1394_reqTxComplete |
OHCI1394_isochRx |
- OHCI1394_isochTx |
- OHCI1394_unrecoverableError
- );
+ OHCI1394_isochTx);
/* Enable link */
reg_write(ohci, OHCI1394_HCControlSet, 0x00020000);
buf = reg_read(ohci, OHCI1394_Version);
- PRINT(KERN_INFO, ohci->id, "OHCI-1394 %d.%d (PCI): IRQ=[%d] MMIO=[%lx-%lx]"
- " Max Packet=[%d]", ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
+ PRINT(KERN_INFO, ohci->id, "OHCI-1394 %d.%d (PCI): IRQ=[%d] "
+ "MMIO=[%lx-%lx] Max Packet=[%d]",
+ ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq,
pci_resource_start(ohci->dev, 0),
pci_resource_start(ohci->dev, 0) + pci_resource_len(ohci->dev, 0),
ohci->max_packet_size);
-
- return 1;
-}
-
-static void ohci_remove(struct hpsb_host *host)
-{
- struct ti_ohci *ohci;
-
- if (host != NULL) {
- ohci = host->hostdata;
- remove_card(ohci);
- }
}
/*
d->prg_cpu[idx]->begin.address = 0;
d->prg_cpu[idx]->begin.branchAddress = 0;
- if (d->ctx==1) {
+ if (d->type == DMA_CTX_ASYNC_RESP) {
/*
* For response packets, we need to put a timeout value in
* the 16 lower bits of the status... let's try 1 sec timeout
if (cross_bound((unsigned long)packet->data,
packet->data_size)>0) {
/* FIXME: do something about it */
- PRINT(KERN_ERR, ohci->id, __FUNCTION__
- ": packet data addr: %p size %Zd bytes "
- "cross page boundary",
+ PRINT(KERN_ERR, ohci->id,
+ "%s: packet data addr: %p size %Zd bytes "
+ "cross page boundary", __FUNCTION__,
packet->data, packet->data_size);
}
switch (cmd) {
case RESET_BUS:
DBGMSG(ohci->id, "devctl: Bus reset requested%s",
- ((host->attempt_root || attempt_root) ?
- " and attempting to become root" : ""));
- set_phy_reg_mask (ohci, 1, 0x40 | ((host->attempt_root || attempt_root) ?
- 0x80 : 0));
+ attempt_root ? " and attempting to become root" : "");
+ set_phy_reg_mask (ohci, 1, 0x40 | (attempt_root ? 0x80 : 0));
break;
case GET_CYCLE_COUNTER:
u64 mask;
if (arg<0 || arg>63) {
- PRINT(KERN_ERR, ohci->id, __FUNCTION__
- "IS0 listen channel %d is out of range",
- arg);
+ PRINT(KERN_ERR, ohci->id,
+ "%s: IS0 listen channel %d is out of range",
+ __FUNCTION__, arg);
return -EFAULT;
}
spin_lock_irqsave(&ohci->IR_channel_lock, flags);
if (ohci->ISO_channel_usage & mask) {
- PRINT(KERN_ERR, ohci->id, __FUNCTION__
- "IS0 listen channel %d is already used",
- arg);
+ PRINT(KERN_ERR, ohci->id,
+ "%s: IS0 listen channel %d is already used",
+ __FUNCTION__, arg);
spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
return -EFAULT;
}
u64 mask;
if (arg<0 || arg>63) {
- PRINT(KERN_ERR, ohci->id, __FUNCTION__
- "IS0 unlisten channel %d is out of range",
- arg);
+ PRINT(KERN_ERR, ohci->id,
+ "%s: IS0 unlisten channel %d is out of range",
+ __FUNCTION__, arg);
return -EFAULT;
}
spin_lock_irqsave(&ohci->IR_channel_lock, flags);
if (!(ohci->ISO_channel_usage & mask)) {
- PRINT(KERN_ERR, ohci->id, __FUNCTION__
- "IS0 unlisten channel %d is not used",
- arg);
+ PRINT(KERN_ERR, ohci->id,
+ "%s: IS0 unlisten channel %d is not used",
+ __FUNCTION__, arg);
spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
return -EFAULT;
}
* selfIDComplete interrupt. */
spin_lock_irqsave(&ohci->event_lock, flags);
event = reg_read(ohci, OHCI1394_IntEventClear);
-#ifdef BUSRESET_WORKAROUND
- reg_write(ohci, OHCI1394_IntEventClear, event);
-#else
reg_write(ohci, OHCI1394_IntEventClear, event & ~OHCI1394_busReset);
-#endif
spin_unlock_irqrestore(&ohci->event_lock, flags);
if (!event) return;
/* Die right here an now */
if (event & OHCI1394_unrecoverableError) {
PRINT(KERN_ERR, ohci->id, "Unrecoverable error, shutting down card!");
- remove_card(ohci);
return;
}
* selfID phase, so we disable busReset interrupts, to
* avoid burying the cpu in interrupt requests. */
spin_lock_irqsave(&ohci->event_lock, flags);
-#ifdef BUSRESET_WORKAROUND
- reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
-#else
reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset);
-#endif
+ if (ohci->dev->vendor == PCI_VENDOR_ID_APPLE &&
+ ohci->dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) {
+ udelay(10);
+ while(reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
+ reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
+ spin_unlock_irqrestore(&ohci->event_lock, flags);
+ udelay(10);
+ spin_lock_irqsave(&ohci->event_lock, flags);
+ }
+ }
spin_unlock_irqrestore(&ohci->event_lock, flags);
if (!host->in_bus_reset) {
DBGMSG(ohci->id, "irq_handler: Bus reset requested%s",
- ((host->attempt_root || attempt_root) ?
- " and attempting to become root" : ""));
+ (attempt_root) ? " and attempting to become root"
+ : "");
/* Subsystem call */
hpsb_bus_reset(ohci->host);
else
tasklet_schedule(&d->task);
}
- if (ohci->video_tmpl)
- ohci->video_tmpl->irq_handler(ohci->id, isoRecvIntEvent,
- 0);
+
+ ohci1394_run_irq_hooks(ohci, isoRecvIntEvent, 0);
+
event &= ~OHCI1394_isochRx;
}
if (event & OHCI1394_isochTx) {
DBGMSG(ohci->id, "Got isochTx interrupt "
"status=0x%08x isoXmitIntEvent=%08x",
reg_read(ohci, d->ctrlSet), isoXmitIntEvent);
- if (ohci->video_tmpl)
- ohci->video_tmpl->irq_handler(ohci->id, 0,
- isoXmitIntEvent);
+
+ ohci1394_run_irq_hooks(ohci, 0, isoXmitIntEvent);
+
if (isoXmitIntEvent & 0x1) {
if (reg_read(ohci, d->ctrlSet) & 0x800)
ohci1394_stop_context(ohci, d->ctrlClear, "isochTx");
/* Finally, we clear the busReset event and reenable
* the busReset interrupt. */
-#ifndef BUSRESET_WORKAROUND
spin_lock_irqsave(&ohci->event_lock, flags);
reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset);
reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
spin_unlock_irqrestore(&ohci->event_lock, flags);
-#endif
event &= ~OHCI1394_selfIDComplete;
}
{
int length = -1;
- if (d->ctx < 2) { /* Async Receive Response/Request */
+ if (d->type == DMA_CTX_ASYNC_REQ || d->type == DMA_CTX_ASYNC_RESP) {
length = TCODE_SIZE[tcode];
if (length == 0) {
if (offset + 12 >= d->buf_size) {
}
length += 20;
}
- } else if (d->ctx == 2) { /* Iso receive */
+ } else if (d->type == DMA_CTX_ISO) {
/* Assumption: buffer fill mode with header/trailer */
length = (cond_le32_to_cpu(buf_ptr[0], noswap) >> 16) + 8;
}
offset = d->buf_offset;
buf_ptr = d->buf_cpu[idx] + offset/4;
- dma_cache_wback_inv(&(d->prg_cpu[idx]->status), sizeof(d->prg_cpu[idx]->status));
rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff;
-
bytes_left = d->buf_size - rescount - offset;
- dma_cache_wback_inv(buf_ptr, bytes_left);
while (bytes_left > 0) {
tcode = (cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming) >> 4) & 0xf;
insert_dma_buffer(d, idx);
idx = (idx+1) % d->num_desc;
buf_ptr = d->buf_cpu[idx];
- dma_cache_wback_inv(buf_ptr, d->buf_size);
offset=0;
while (split_left >= d->buf_size) {
insert_dma_buffer(d, idx);
idx = (idx+1) % d->num_desc;
buf_ptr = d->buf_cpu[idx];
- dma_cache_wback_inv(buf_ptr, d->buf_size);
}
if (split_left > 0) {
d->ctx);
#endif
- dma_cache_wback_inv(&(d->prg_cpu[idx]->status),
- sizeof(d->prg_cpu[idx]->status));
rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff;
bytes_left = d->buf_size - rescount - offset;
}
if ((*d)->spb) kfree((*d)->spb);
+ /* clear ISO context usage bit */
+ if ((*d)->type == DMA_CTX_ISO) {
+ clear_bit((*d)->ctx, &ohci->ir_ctx_usage);
+ }
+
kfree(*d);
*d = NULL;
}
static struct dma_rcv_ctx *
-alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc,
- int buf_size, int split_buf_size,
- int ctrlSet, int ctrlClear, int cmdPtr)
+alloc_dma_rcv_ctx(struct ti_ohci *ohci, enum context_type type, int ctx, int num_desc,
+ int buf_size, int split_buf_size, int context_base)
{
- struct dma_rcv_ctx *d=NULL;
+ struct dma_rcv_ctx *d;
int i;
- d = (struct dma_rcv_ctx *)kmalloc(sizeof(struct dma_rcv_ctx),
- GFP_KERNEL);
+ if (type == DMA_CTX_ISO) {
+ /* try to claim the ISO context usage bit */
+ if (test_and_set_bit(ctx, &ohci->ir_ctx_usage)) {
+ PRINT(KERN_ERR, ohci->id, "IR DMA context %d is not available", ctx);
+ return NULL;
+ }
+ }
+
+ d = kmalloc(sizeof(struct dma_rcv_ctx), GFP_KERNEL);
if (d == NULL) {
PRINT(KERN_ERR, ohci->id, "Failed to allocate dma_rcv_ctx");
memset (d, 0, sizeof (struct dma_rcv_ctx));
- d->ohci = (void *)ohci;
+ d->ohci = ohci;
+ d->type = type;
d->ctx = ctx;
d->num_desc = num_desc;
d->buf_size = buf_size;
d->split_buf_size = split_buf_size;
- d->ctrlSet = ctrlSet;
- d->ctrlClear = ctrlClear;
- d->cmdPtr = cmdPtr;
- d->buf_cpu = NULL;
- d->buf_bus = NULL;
- d->prg_cpu = NULL;
- d->prg_bus = NULL;
- d->spb = NULL;
+ d->ctrlSet = context_base + OHCI1394_ContextControlSet;
+ d->ctrlClear = context_base + OHCI1394_ContextControlClear;
+ d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
d->buf_cpu = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL);
d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL);
kfree((*d)->prg_bus);
}
+ /* clear the ISO context usage bit */
+ if ((*d)->type == DMA_CTX_ISO) {
+ clear_bit((*d)->ctx, &ohci->it_ctx_usage);
+ }
+
kfree(*d);
*d = NULL;
return 0;
}
static struct dma_trm_ctx *
-alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc,
- int ctrlSet, int ctrlClear, int cmdPtr)
+alloc_dma_trm_ctx(struct ti_ohci *ohci, enum context_type type, int ctx, int num_desc,
+ int context_base)
{
- struct dma_trm_ctx *d=NULL;
+ struct dma_trm_ctx *d;
int i;
- d = (struct dma_trm_ctx *)kmalloc(sizeof(struct dma_trm_ctx),
- GFP_KERNEL);
+ if (type == DMA_CTX_ISO) {
+ /* try to claim the ISO context usage bit */
+ if (test_and_set_bit(ctx, &ohci->it_ctx_usage)) {
+ PRINT(KERN_ERR, ohci->id, "IT DMA context %d is not available", ctx);
+ return NULL;
+ }
+ }
+
+ d = kmalloc(sizeof(struct dma_trm_ctx), GFP_KERNEL);
- if (d==NULL) {
+ if (d == NULL) {
PRINT(KERN_ERR, ohci->id, "Failed to allocate dma_trm_ctx");
return NULL;
}
memset (d, 0, sizeof (struct dma_trm_ctx));
- d->ohci = (void *)ohci;
+ d->ohci = ohci;
+ d->type = type;
d->ctx = ctx;
d->num_desc = num_desc;
- d->ctrlSet = ctrlSet;
- d->ctrlClear = ctrlClear;
- d->cmdPtr = cmdPtr;
- d->prg_cpu = NULL;
- d->prg_bus = NULL;
+ d->ctrlSet = context_base + OHCI1394_ContextControlSet;
+ d->ctrlClear = context_base + OHCI1394_ContextControlClear;
+ d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
d->prg_cpu = kmalloc(d->num_desc * sizeof(struct at_dma_prg*),
GFP_KERNEL);
memset(d->prg_cpu, 0, d->num_desc * sizeof(struct at_dma_prg*));
memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t));
- for (i=0; i<d->num_desc; i++) {
+ for (i = 0; i < d->num_desc; i++) {
d->prg_cpu[i] = pci_alloc_consistent(ohci->dev,
sizeof(struct at_dma_prg),
d->prg_bus+i);
#define cf_put_keyval(cr, key, val) (((cr)->data++)[0] = cpu_to_be32(((key) << 24) | (val)))
+static inline void cf_put_str(struct config_rom_ptr *cr, const char *str)
+{
+ int t;
+ char fourb[4];
+
+ while (str[0]) {
+ memset(fourb, 0, 4);
+ for (t = 0; t < 4 && str[t]; t++)
+ fourb[t] = str[t];
+ cf_put_4bytes(cr, fourb[0], fourb[1], fourb[2], fourb[3]);
+ str += strlen(str) < 4 ? strlen(str) : 4;
+ }
+ return;
+}
+
static inline void cf_put_crc16(struct config_rom_ptr *cr, int unit)
{
*cr->unitdir[unit].start =
cf_put_keyval(&cr, 0x03, 0x00005e); /* Vendor ID */
cf_put_refer(&cr, 0x81, 2); /* Textual description unit */
cf_put_keyval(&cr, 0x0c, 0x0083c0); /* Node capabilities */
- cf_put_refer(&cr, 0xd1, 3); /* IPv4 unit directory */
- cf_put_refer(&cr, 0xd1, 4); /* IPv6 unit directory */
/* NOTE: Add other unit referers here, and append at bottom */
cf_unit_end(&cr);
cf_unit_begin(&cr, 2);
cf_put_keyval(&cr, 0, 0);
cf_put_1quad(&cr, 0);
- cf_put_4bytes(&cr, 'L', 'i', 'n', 'u');
- cf_put_4bytes(&cr, 'x', ' ', '1', '3');
- cf_put_4bytes(&cr, '9', '4', 0x0, 0x0);
- cf_unit_end(&cr);
-
- /* IPv4 unit directory, RFC 2734 */
- cf_unit_begin(&cr, 3);
- cf_put_keyval(&cr, 0x12, 0x00005e); /* Unit spec ID */
- cf_put_refer(&cr, 0x81, 6); /* Textual description unit */
- cf_put_keyval(&cr, 0x13, 0x000001); /* Unit software version */
- cf_put_refer(&cr, 0x81, 7); /* Textual description unit */
- cf_unit_end(&cr);
-
- cf_unit_begin(&cr, 6);
- cf_put_keyval(&cr, 0, 0);
- cf_put_1quad(&cr, 0);
- cf_put_4bytes(&cr, 'I', 'A', 'N', 'A');
- cf_unit_end(&cr);
-
- cf_unit_begin(&cr, 7);
- cf_put_keyval(&cr, 0, 0);
- cf_put_1quad(&cr, 0);
- cf_put_4bytes(&cr, 'I', 'P', 'v', '4');
- cf_unit_end(&cr);
-
- /* IPv6 unit directory, draft-ietf-ipngwg-1394-01.txt */
- cf_unit_begin(&cr, 4);
- cf_put_keyval(&cr, 0x12, 0x00005e); /* Unit spec ID */
- cf_put_refer(&cr, 0x81, 8); /* Textual description unit */
- cf_put_keyval(&cr, 0x13, 0x000002); /* (Proposed) Unit software version */
- cf_put_refer(&cr, 0x81, 9); /* Textual description unit */
- cf_unit_end(&cr);
-
- cf_unit_begin(&cr, 8);
- cf_put_keyval(&cr, 0, 0);
- cf_put_1quad(&cr, 0);
- cf_put_4bytes(&cr, 'I', 'A', 'N', 'A');
- cf_unit_end(&cr);
-
- cf_unit_begin(&cr, 9);
- cf_put_keyval(&cr, 0, 0);
- cf_put_1quad(&cr, 0);
- cf_put_4bytes(&cr, 'I', 'P', 'v', '6');
+ cf_put_str(&cr, "Linux OHCI-1394");
cf_unit_end(&cr);
ohci->csr_config_rom_length = cr.data - ohci->csr_config_rom_cpu;
return ohci->csr_config_rom_length * 4;
}
-int ohci_compare_swap(struct ti_ohci *ohci, quadlet_t *data,
- quadlet_t compare, int sel)
+static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg,
+ quadlet_t data, quadlet_t compare)
{
+ struct ti_ohci *ohci = host->hostdata;
int i;
- reg_write(ohci, OHCI1394_CSRData, *data);
+
+ reg_write(ohci, OHCI1394_CSRData, data);
reg_write(ohci, OHCI1394_CSRCompareData, compare);
- reg_write(ohci, OHCI1394_CSRControl, sel & 0x3);
+ reg_write(ohci, OHCI1394_CSRControl, reg & 0x3);
for (i = 0; i < OHCI_LOOP_COUNT; i++) {
if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000)
mdelay(1);
}
- *data = reg_read(ohci, OHCI1394_CSRData);
- return 0;
+ return reg_read(ohci, OHCI1394_CSRData);
}
-static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg,
- quadlet_t data, quadlet_t compare)
-{
- struct ti_ohci *ohci=host->hostdata;
-
- ohci_compare_swap (ohci, &data, compare, reg);
-
- return data;
-}
-
-static struct hpsb_host_template ohci_template = {
- name: OHCI1394_DRIVER_NAME,
- initialize_host: ohci_initialize,
- release_host: ohci_remove,
+static struct hpsb_host_operations ohci1394_ops = {
get_rom: ohci_get_rom,
transmit_packet: ohci_transmit,
devctl: ohci_devctl,
hw_csr_reg: ohci_hw_csr_reg,
};
+static struct hpsb_host_driver *ohci1394_driver;
+
+\f
+
+/***********************************
+ * PCI Driver Interface functions *
+ ***********************************/
-#define FAIL(fmt, args...) \
+#define FAIL(err, fmt, args...) \
do { \
PRINT_G(KERN_ERR, fmt , ## args); \
- remove_card(ohci); \
- return 1; \
+ ohci1394_pci_remove(dev); \
+ return err; \
} while(0)
-static int __devinit ohci1394_add_one(struct pci_dev *dev, const struct pci_device_id *ent)
+static int __devinit ohci1394_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
{
- struct ti_ohci *ohci; /* shortcut to currently handled device */
- struct hpsb_host *host;
- unsigned long ohci_base, ohci_len;
+ static unsigned int card_id_counter = 0;
static int version_printed = 0;
+ struct hpsb_host *host;
+ struct ti_ohci *ohci; /* shortcut to currently handled device */
+ unsigned long ohci_base, ohci_len;
+ int i;
+
if (version_printed++ == 0)
PRINT_G(KERN_INFO, "%s", version);
- if (pci_enable_device(dev)) {
- /* Skip ID's that fail */
- PRINT_G(KERN_NOTICE, "Failed to enable OHCI hardware %d",
- card_id_counter++);
- return -ENXIO;
- }
+ if (pci_enable_device(dev))
+ FAIL(-ENXIO, "Failed to enable OHCI hardware %d",
+ card_id_counter++);
pci_set_master(dev);
- host = hpsb_get_host(&ohci_template, sizeof (struct ti_ohci));
- if (!host) {
- PRINT_G(KERN_ERR, "Out of memory trying to allocate host structure");
- return -ENOMEM;
- }
+ host = hpsb_alloc_host(ohci1394_driver, sizeof(struct ti_ohci));
+ if (!host) FAIL(-ENOMEM, "Failed to allocate host structure");
+
ohci = host->hostdata;
- ohci->host = host;
- INIT_LIST_HEAD(&ohci->list);
ohci->id = card_id_counter++;
ohci->dev = dev;
- host->pdev = dev;
ohci->host = host;
+ host->pdev = dev;
pci_set_drvdata(dev, ohci);
/* We don't want hardware swapping */
&ohci->csr_config_rom_bus);
OHCI_DMA_ALLOC("consistent csr_config_rom");
if (ohci->csr_config_rom_cpu == NULL)
- FAIL("Failed to allocate buffer config rom");
+ FAIL(-ENOMEM, "Failed to allocate buffer config rom");
+
+
+ ohci_base = pci_resource_start(dev, 0);
+ ohci_len = pci_resource_len(dev, 0);
+
+ if (!request_mem_region (ohci_base, ohci_len, OHCI1394_DRIVER_NAME))
+ FAIL(-ENOMEM, "MMIO resource (0x%lx@0x%lx) unavailable, aborting.",
+ ohci_base, ohci_len);
+
+ ohci->registers = ioremap(ohci_base, ohci_len);
+
+ if (ohci->registers == NULL)
+ FAIL(-ENXIO, "Failed to remap registers - card not accessible");
+ DBGMSG(ohci->id, "Remapped memory spaces reg 0x%p", ohci->registers);
+
+
+ /* Start off with a softreset, to clear everything to a sane
+ * state. This will also set Link Power State (LPS), which we
+ * need in order to start accessing most of the registers. */
+ ohci_soft_reset(ohci);
+
+ /* determinte the number of available IR and IT contexts right away,
+ because they need to be known for alloc_dma_*_ctx() */
+ ohci->nb_iso_rcv_ctx =
+ get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet);
+ DBGMSG(ohci->id, "%d iso receive contexts available",
+ ohci->nb_iso_rcv_ctx);
+
+ ohci->ir_ctx_usage = 0;
+
+ /* set the usage bits for non-existent contexts so they can't be allocated */
+ for(i = ohci->nb_iso_rcv_ctx; i < sizeof(ohci->ir_ctx_usage)*8; i++)
+ __set_bit(i, &ohci->ir_ctx_usage);
+
+ ohci->nb_iso_xmit_ctx =
+ get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet);
+ DBGMSG(ohci->id, "%d iso transmit contexts available",
+ ohci->nb_iso_xmit_ctx);
+
+ ohci->it_ctx_usage = 0;
+
+ /* set the usage bits for non-existent contexts so they can't be allocated */
+ for(i = ohci->nb_iso_xmit_ctx; i < sizeof(ohci->it_ctx_usage)*8; i++)
+ __set_bit(i, &ohci->it_ctx_usage);
+
+
/*
* self-id dma buffer allocation
*/
&ohci->selfid_buf_bus);
OHCI_DMA_ALLOC("consistent selfid_buf");
if (ohci->selfid_buf_cpu == NULL)
- FAIL("Failed to allocate DMA buffer for self-id packets");
+ FAIL(-ENOMEM, "Failed to allocate DMA buffer for self-id packets");
if ((unsigned long)ohci->selfid_buf_cpu & 0x1fff)
PRINT(KERN_INFO, ohci->id, "SelfID buffer %p is not aligned on "
"8Kb boundary... may cause problems on some CXD3222 chip",
ohci->selfid_buf_cpu);
- ohci->it_context =
- alloc_dma_trm_ctx(ohci, 2, IT_NUM_DESC,
- OHCI1394_IsoXmitContextControlSet,
- OHCI1394_IsoXmitContextControlClear,
- OHCI1394_IsoXmitCommandPtr);
-
- if (ohci->it_context == NULL)
- FAIL("Failed to allocate IT context");
-
- ohci_base = pci_resource_start(dev, 0);
- ohci_len = pci_resource_len(dev, 0);
-
- if (!request_mem_region (ohci_base, ohci_len, host->template->name))
- FAIL("MMIO resource (0x%lx@0x%lx) unavailable, aborting.",
- ohci_base, ohci_len);
-
- ohci->registers = ioremap(ohci_base, ohci_len);
-
- if (ohci->registers == NULL)
- FAIL("Failed to remap registers - card not accessible");
-
- DBGMSG(ohci->id, "Remapped memory spaces reg 0x%p",
- ohci->registers);
+ /* No self-id errors at startup */
+ ohci->self_id_errors = 0;
+ /* AR DMA request context allocation */
ohci->ar_req_context =
- alloc_dma_rcv_ctx(ohci, 0, AR_REQ_NUM_DESC,
+ alloc_dma_rcv_ctx(ohci, DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC,
AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE,
- OHCI1394_AsReqRcvContextControlSet,
- OHCI1394_AsReqRcvContextControlClear,
- OHCI1394_AsReqRcvCommandPtr);
+ OHCI1394_AsReqRcvContextBase);
if (ohci->ar_req_context == NULL)
- FAIL("Failed to allocate AR Req context");
+ FAIL(-ENOMEM, "Failed to allocate AR Req context");
+ /* AR DMA response context allocation */
ohci->ar_resp_context =
- alloc_dma_rcv_ctx(ohci, 1, AR_RESP_NUM_DESC,
+ alloc_dma_rcv_ctx(ohci, DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC,
AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE,
- OHCI1394_AsRspRcvContextControlSet,
- OHCI1394_AsRspRcvContextControlClear,
- OHCI1394_AsRspRcvCommandPtr);
+ OHCI1394_AsRspRcvContextBase);
if (ohci->ar_resp_context == NULL)
- FAIL("Failed to allocate AR Resp context");
+ FAIL(-ENOMEM, "Failed to allocate AR Resp context");
+ /* AT DMA request context */
ohci->at_req_context =
- alloc_dma_trm_ctx(ohci, 0, AT_REQ_NUM_DESC,
- OHCI1394_AsReqTrContextControlSet,
- OHCI1394_AsReqTrContextControlClear,
- OHCI1394_AsReqTrCommandPtr);
+ alloc_dma_trm_ctx(ohci, DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC,
+ OHCI1394_AsReqTrContextBase);
if (ohci->at_req_context == NULL)
- FAIL("Failed to allocate AT Req context");
+ FAIL(-ENOMEM, "Failed to allocate AT Req context");
+ /* AT DMA response context */
ohci->at_resp_context =
- alloc_dma_trm_ctx(ohci, 1, AT_RESP_NUM_DESC,
- OHCI1394_AsRspTrContextControlSet,
- OHCI1394_AsRspTrContextControlClear,
- OHCI1394_AsRspTrCommandPtr);
+ alloc_dma_trm_ctx(ohci, DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC,
+ OHCI1394_AsRspTrContextBase);
if (ohci->at_resp_context == NULL)
- FAIL("Failed to allocate AT Resp context");
+ FAIL(-ENOMEM, "Failed to allocate AT Resp context");
+ /* IR DMA context */
ohci->ir_context =
- alloc_dma_rcv_ctx(ohci, 2, IR_NUM_DESC,
+ alloc_dma_rcv_ctx(ohci, DMA_CTX_ISO, 0, IR_NUM_DESC,
IR_BUF_SIZE, IR_SPLIT_BUF_SIZE,
- OHCI1394_IsoRcvContextControlSet,
- OHCI1394_IsoRcvContextControlClear,
- OHCI1394_IsoRcvCommandPtr);
+ OHCI1394_IsoRcvContextBase);
if (ohci->ir_context == NULL)
- FAIL("Failed to allocate IR context");
+ FAIL(-ENOMEM, "Failed to allocate IR context");
+
+
+ /* IT DMA context allocation */
+ ohci->it_context =
+ alloc_dma_trm_ctx(ohci, DMA_CTX_ISO, 0, IT_NUM_DESC,
+ OHCI1394_IsoXmitContextBase);
+
+ if (ohci->it_context == NULL)
+ FAIL(-ENOMEM, "Failed to allocate IT context");
ohci->ISO_channel_usage = 0;
spin_lock_init(&ohci->IR_channel_lock);
+ for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
+ ohci->irq_hooks[i].irq_handler = NULL;
+ ohci->irq_hooks[i].data = NULL;
+ }
+
if (request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ,
OHCI1394_DRIVER_NAME, ohci))
- FAIL("Failed to allocate shared interrupt %d", dev->irq);
+ FAIL(-ENOMEM, "Failed to allocate shared interrupt %d", dev->irq);
+
+ ohci_initialize(ohci);
/* Tell the highlevel this host is ready */
- highlevel_add_one_host (host);
+ hpsb_add_host(host);
return 0;
#undef FAIL
}
-static void remove_card(struct ti_ohci *ohci)
+static void __devexit ohci1394_pci_remove(struct pci_dev *pdev)
{
+ struct ti_ohci *ohci;
quadlet_t buf;
+ ohci = pci_get_drvdata(pdev);
+ if (!ohci)
+ return;
+
+ if (ohci->host)
+ hpsb_remove_host(ohci->host);
+
/* Soft reset before we start */
ohci_soft_reset(ohci);
of_node = pci_device_to_OF_node(ohci->dev);
if (of_node) {
- feature_set_firewire_power(of_node, 0);
- feature_set_firewire_cable_power(of_node, 0);
+ pmac_call_feature(PMAC_FTR_1394_ENABLE, of_node, 0, 0);
+ pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, of_node, 0, 0);
}
}
#endif /* CONFIG_ALL_PPC */
pci_set_drvdata(ohci->dev, NULL);
+ kfree(ohci);
}
+#define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10)
+
+static struct pci_device_id ohci1394_pci_tbl[] __devinitdata = {
+ {
+ class: PCI_CLASS_FIREWIRE_OHCI,
+ class_mask: 0x00ffffff,
+ vendor: PCI_ANY_ID,
+ device: PCI_ANY_ID,
+ subvendor: PCI_ANY_ID,
+ subdevice: PCI_ANY_ID,
+ },
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl);
+
+static struct pci_driver ohci1394_pci_driver = {
+ name: OHCI1394_DRIVER_NAME,
+ id_table: ohci1394_pci_tbl,
+ probe: ohci1394_pci_probe,
+ remove: __devexit_p(ohci1394_pci_remove),
+};
+
+\f
+
+/***********************************
+ * OHCI1394 Video Interface *
+ ***********************************/
+
+/* essentially the only purpose of this code is to allow another
+ module to hook into ohci's interrupt handler */
+
void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg)
{
int i=0;
if (msg) PRINT(KERN_ERR, ohci->id, "%s: dma prg stopped", msg);
}
-int ohci1394_register_video(struct ti_ohci *ohci,
- struct video_template *tmpl)
+static inline void ohci1394_run_irq_hooks(struct ti_ohci *ohci,
+ quadlet_t isoRecvEvent,
+ quadlet_t isoXmitEvent)
{
- if (ohci->video_tmpl)
- return -ENFILE;
- ohci->video_tmpl = tmpl;
- MOD_INC_USE_COUNT;
- return 0;
+ int i;
+ for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
+ if(ohci->irq_hooks[i].irq_handler != NULL) {
+ ohci->irq_hooks[i].irq_handler(ohci->id, isoRecvEvent, isoXmitEvent,
+ ohci->irq_hooks[i].data);
+ }
+ }
}
-
-void ohci1394_unregister_video(struct ti_ohci *ohci,
- struct video_template *tmpl)
+
+int ohci1394_hook_irq(struct ti_ohci *ohci,
+ void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
+ void *data)
{
- if (ohci->video_tmpl != tmpl) {
- PRINT(KERN_ERR, ohci->id,
- "Trying to unregister wrong video device");
- } else {
- ohci->video_tmpl = NULL;
- MOD_DEC_USE_COUNT;
+ int i;
+
+ /* find a free slot */
+ for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
+ if(ohci->irq_hooks[i].irq_handler == NULL)
+ break;
}
+
+ if(i >= OHCI1394_MAX_IRQ_HOOKS)
+ return -EBUSY;
+
+ ohci->irq_hooks[i].irq_handler = irq_handler;
+ ohci->irq_hooks[i].data = data;
+
+ /* ohci1394 will never be unloaded while an IRQ hook is
+ in use, because the user must reference this symbol */
+
+ return 0;
}
-#if 0
-int ohci1394_request_channel(struct ti_ohci *ohci, int channel)
+void ohci1394_unhook_irq(struct ti_ohci *ohci,
+ void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
+ void *data)
{
- int csrSel;
- quadlet_t chan, data1=0, data2=0;
- int timeout = 32;
-
- if (channel<32) {
- chan = 1<<channel;
- csrSel = 2;
- }
- else {
- chan = 1<<(channel-32);
- csrSel = 3;
- }
- if (ohci_compare_swap(ohci, &data1, 0, csrSel)<0) {
- PRINT(KERN_INFO, ohci->id, "request_channel timeout");
- return -1;
+ int i;
+
+ for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
+ if( (ohci->irq_hooks[i].irq_handler == irq_handler) &&
+ (ohci->irq_hooks[i].data == data) )
+ break;
}
- while (timeout--) {
- if (data1 & chan) {
- PRINT(KERN_INFO, ohci->id,
- "request channel %d failed", channel);
- return -1;
- }
- data2 = data1;
- data1 |= chan;
- if (ohci_compare_swap(ohci, &data1, data2, csrSel)<0) {
- PRINT(KERN_INFO, ohci->id, "request_channel timeout");
- return -1;
- }
- if (data1==data2) {
- PRINT(KERN_INFO, ohci->id,
- "request channel %d succeded", channel);
- return 0;
- }
+
+ if(i < OHCI1394_MAX_IRQ_HOOKS) {
+ ohci->irq_hooks[i].irq_handler = NULL;
+ ohci->irq_hooks[i].data = NULL;
}
- PRINT(KERN_INFO, ohci->id, "request channel %d failed", channel);
- return -1;
}
-#endif
EXPORT_SYMBOL(ohci1394_stop_context);
-EXPORT_SYMBOL(ohci1394_register_video);
-EXPORT_SYMBOL(ohci1394_unregister_video);
+EXPORT_SYMBOL(ohci1394_hook_irq);
+EXPORT_SYMBOL(ohci1394_unhook_irq);
-MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>");
-MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers");
-MODULE_LICENSE("GPL");
-static void __devexit ohci1394_remove_one(struct pci_dev *pdev)
-{
- struct ti_ohci *ohci = pci_get_drvdata(pdev);
- if (ohci) {
- remove_card (ohci);
- pci_set_drvdata(pdev, NULL);
- }
-}
+/***********************************
+ * General module initialization *
+ ***********************************/
-static struct pci_driver ohci1394_driver = {
- name: OHCI1394_DRIVER_NAME,
- id_table: ohci1394_pci_tbl,
- probe: ohci1394_add_one,
- remove: ohci1394_remove_one,
-};
+MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>");
+MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers");
+MODULE_LICENSE("GPL");
static void __exit ohci1394_cleanup (void)
{
- hpsb_unregister_lowlevel(&ohci_template);
- pci_unregister_driver(&ohci1394_driver);
+ pci_unregister_driver(&ohci1394_pci_driver);
+ hpsb_unregister_lowlevel(ohci1394_driver);
}
static int __init ohci1394_init(void)
{
int ret;
- if (hpsb_register_lowlevel(&ohci_template)) {
- PRINT_G(KERN_ERR, "Registering failed");
- return -ENXIO;
+
+ ohci1394_driver = hpsb_register_lowlevel(&ohci1394_ops,
+ OHCI1394_DRIVER_NAME);
+ if (!ohci1394_driver) {
+ PRINT_G(KERN_ERR, "hpsb_register_lowlevel failed");
+ return -ENOMEM;
}
- if ((ret = pci_module_init(&ohci1394_driver))) {
- PRINT_G(KERN_ERR, "PCI module init failed");
- hpsb_unregister_lowlevel(&ohci_template);
+
+ ret = pci_module_init(&ohci1394_pci_driver);
+ if (ret < 0) {
+ PRINT_G(KERN_ERR, "pci_module_init failed");
+ hpsb_unregister_lowlevel(ohci1394_driver);
return ret;
}
return ret;
quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */
};
+/* identify whether a DMA context is asynchronous or isochronous */
+enum context_type { DMA_CTX_ASYNC_REQ, DMA_CTX_ASYNC_RESP, DMA_CTX_ISO };
+
/* DMA receive context */
struct dma_rcv_ctx {
- void *ohci;
+ struct ti_ohci *ohci;
+ enum context_type type;
int ctx;
unsigned int num_desc;
/* DMA transmit context */
struct dma_trm_ctx {
- void *ohci;
+ struct ti_ohci *ohci;
+ enum context_type type;
int ctx;
unsigned int num_desc;
int cmdPtr;
};
-/* video device template */
-struct video_template {
- void (*irq_handler) (int card, quadlet_t isoRecvEvent,
- quadlet_t isoXmitEvent);
-};
-
-
struct ti_ohci {
int id; /* sequential card number */
- struct list_head list;
-
struct pci_dev *dev;
u32 state;
struct dma_rcv_ctx *ir_context;
spinlock_t IR_channel_lock;
int nb_iso_rcv_ctx;
-
+ unsigned long ir_ctx_usage; /* use test_and_set_bit() for atomicity */
+
/* iso transmit */
struct dma_trm_ctx *it_context;
int nb_iso_xmit_ctx;
-
+ unsigned long it_ctx_usage; /* use test_and_set_bit() for atomicity */
+
u64 ISO_channel_usage;
/* IEEE-1394 part follows */
int self_id_errors;
- /* video device */
- struct video_template *video_tmpl;
+ /* IRQ hooks, for video1394 and dv1394 */
+
+#define OHCI1394_MAX_IRQ_HOOKS 4
+
+ struct ohci1394_irq_hook {
+ void (*irq_handler) (int card, quadlet_t isoRecvEvent,
+ quadlet_t isoXmitEvent, void *data);
+ void *data;
+ } irq_hooks[OHCI1394_MAX_IRQ_HOOKS];
/* Swap the selfid buffer? */
unsigned int selfid_swap:1;
/* 2 KiloBytes of register space */
#define OHCI1394_REGISTER_SIZE 0x800
+/* Offsets relative to context bases defined below */
+
+#define OHCI1394_ContextControlSet 0x000
+#define OHCI1394_ContextControlClear 0x004
+#define OHCI1394_ContextCommandPtr 0x00C
+
/* register map */
#define OHCI1394_Version 0x000
#define OHCI1394_GUID_ROM 0x004
#define OHCI1394_PhyReqFilterLoSet 0x118
#define OHCI1394_PhyReqFilterLoClear 0x11C
#define OHCI1394_PhyUpperBound 0x120
+
+#define OHCI1394_AsReqTrContextBase 0x180
#define OHCI1394_AsReqTrContextControlSet 0x180
#define OHCI1394_AsReqTrContextControlClear 0x184
#define OHCI1394_AsReqTrCommandPtr 0x18C
+
+#define OHCI1394_AsRspTrContextBase 0x1A0
#define OHCI1394_AsRspTrContextControlSet 0x1A0
#define OHCI1394_AsRspTrContextControlClear 0x1A4
#define OHCI1394_AsRspTrCommandPtr 0x1AC
+
+#define OHCI1394_AsReqRcvContextBase 0x1C0
#define OHCI1394_AsReqRcvContextControlSet 0x1C0
#define OHCI1394_AsReqRcvContextControlClear 0x1C4
#define OHCI1394_AsReqRcvCommandPtr 0x1CC
+
+#define OHCI1394_AsRspRcvContextBase 0x1E0
#define OHCI1394_AsRspRcvContextControlSet 0x1E0
#define OHCI1394_AsRspRcvContextControlClear 0x1E4
#define OHCI1394_AsRspRcvCommandPtr 0x1EC
/* Isochronous transmit registers */
/* Add (32 * n) for context n */
+#define OHCI1394_IsoXmitContextBase 0x200
#define OHCI1394_IsoXmitContextControlSet 0x200
#define OHCI1394_IsoXmitContextControlClear 0x204
#define OHCI1394_IsoXmitCommandPtr 0x20C
/* Isochronous receive registers */
/* Add (32 * n) for context n */
+#define OHCI1394_IsoRcvContextBase 0x400
#define OHCI1394_IsoRcvContextControlSet 0x400
#define OHCI1394_IsoRcvContextControlClear 0x404
#define OHCI1394_IsoRcvCommandPtr 0x40C
void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg);
struct ti_ohci *ohci1394_get_struct(int card_num);
-int ohci1394_register_video(struct ti_ohci *ohci,
- struct video_template *tmpl);
-void ohci1394_unregister_video(struct ti_ohci *ohci,
- struct video_template *tmpl);
+int ohci1394_hook_irq(struct ti_ohci *ohci,
+ void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
+ void *data);
+
+void ohci1394_unhook_irq(struct ti_ohci *ohci,
+ void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
+ void *data);
#endif
#include "pcilynx.h"
-#if MAX_PCILYNX_CARDS > PCILYNX_MINOR_ROM_START
-#error Max number of cards is bigger than PCILYNX_MINOR_ROM_START - this does not work.
-#endif
-
/* print general (card independent) information */
#define PRINT_G(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args)
/* print card specific information */
#endif
-static struct ti_lynx cards[MAX_PCILYNX_CARDS];
-static int num_of_cards = 0;
-static struct hpsb_host_template lynx_template;
+static struct hpsb_host_driver *lynx_driver;
+static unsigned int card_id;
/*
* PCL handling functions.
#endif
-static int add_card(struct pci_dev *dev, const struct pci_device_id *devid);
-static void remove_card(struct pci_dev *dev);
-
-
/***********************************
* IEEE-1394 functionality section *
unsigned long flags;
if (addr > 15) {
- PRINT(KERN_ERR, lynx->id, __FUNCTION__
- ": PHY register address %d out of range", addr);
+ PRINT(KERN_ERR, lynx->id,
+ "%s: PHY register address %d out of range",
+ __FUNCTION__, addr);
return -1;
}
retval = reg_read(lynx, LINK_PHY);
if (i > 10000) {
- PRINT(KERN_ERR, lynx->id, __FUNCTION__
- ": runaway loop, aborting");
+ PRINT(KERN_ERR, lynx->id, "%s: runaway loop, aborting",
+ __FUNCTION__);
retval = -1;
break;
}
unsigned long flags;
if (addr > 15) {
- PRINT(KERN_ERR, lynx->id, __FUNCTION__
- ": PHY register address %d out of range", addr);
+ PRINT(KERN_ERR, lynx->id,
+ "%s: PHY register address %d out of range", __FUNCTION__, addr);
return -1;
}
if (val > 0xff) {
- PRINT(KERN_ERR, lynx->id, __FUNCTION__
- ": PHY register value %d out of range", val);
+ PRINT(KERN_ERR, lynx->id,
+ "%s: PHY register value %d out of range", __FUNCTION__, val);
return -1;
}
int reg;
if (page > 7) {
- PRINT(KERN_ERR, lynx->id, __FUNCTION__
- ": PHY page %d out of range", page);
+ PRINT(KERN_ERR, lynx->id,
+ "%s: PHY page %d out of range", __FUNCTION__, page);
return -1;
}
int reg;
if (port > 15) {
- PRINT(KERN_ERR, lynx->id, __FUNCTION__
- ": PHY port %d out of range", port);
+ PRINT(KERN_ERR, lynx->id,
+ "%s: PHY port %d out of range", __FUNCTION__, port);
return -1;
}
hpsb_selfid_complete(host, phyid, isroot);
- if (host->in_bus_reset) return;
+ if (host->in_bus_reset) return; /* in bus reset again */
if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER);
reg_set_bits(lynx, LINK_CONTROL,
}
-#if 0
-static int lynx_detect(struct hpsb_host_template *tmpl)
-{
- struct hpsb_host *host;
- int i;
-
- init_driver();
-
- for (i = 0; i < num_of_cards; i++) {
- host = hpsb_get_host(tmpl, 0);
- if (host == NULL) {
- /* simply don't init more after out of mem */
- return i;
- }
- host->hostdata = &cards[i];
- cards[i].host = host;
- }
-
- return num_of_cards;
-}
-#endif
-
-static int lynx_initialize(struct hpsb_host *host)
-{
- struct ti_lynx *lynx = host->hostdata;
- struct ti_pcl pcl;
- int i;
- u32 *pcli;
-
- lynx->selfid_size = -1;
- lynx->phy_reg0 = -1;
-
- lynx->async.queue = NULL;
-
- pcl.next = pcl_bus(lynx, lynx->rcv_pcl);
- put_pcl(lynx, lynx->rcv_pcl_start, &pcl);
-
- pcl.next = PCL_NEXT_INVALID;
- pcl.async_error_next = PCL_NEXT_INVALID;
-#ifdef __BIG_ENDIAN
- pcl.buffer[0].control = PCL_CMD_RCV | 16;
- pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
-#else
- pcl.buffer[0].control = PCL_CMD_RCV | PCL_BIGENDIAN | 16;
- pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
-#endif
- pcl.buffer[0].pointer = lynx->rcv_page_dma;
- pcl.buffer[1].pointer = lynx->rcv_page_dma + 16;
- put_pcl(lynx, lynx->rcv_pcl, &pcl);
-
- pcl.next = pcl_bus(lynx, lynx->async.pcl);
- pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl);
- put_pcl(lynx, lynx->async.pcl_start, &pcl);
-
- pcl.next = pcl_bus(lynx, lynx->iso_send.pcl);
- pcl.async_error_next = PCL_NEXT_INVALID;
- put_pcl(lynx, lynx->iso_send.pcl_start, &pcl);
-
- pcl.next = PCL_NEXT_INVALID;
- pcl.async_error_next = PCL_NEXT_INVALID;
- pcl.buffer[0].control = PCL_CMD_RCV | 4;
-#ifndef __BIG_ENDIAN
- pcl.buffer[0].control |= PCL_BIGENDIAN;
-#endif
- pcl.buffer[1].control = PCL_LAST_BUFF | 2044;
-
- for (i = 0; i < NUM_ISORCV_PCL; i++) {
- int page = i / ISORCV_PER_PAGE;
- int sec = i % ISORCV_PER_PAGE;
-
- pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page]
- + sec * MAX_ISORCV_SIZE;
- pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4;
- put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl);
- }
-
- pcli = (u32 *)&pcl;
- for (i = 0; i < NUM_ISORCV_PCL; i++) {
- pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]);
- }
- put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl);
-
- /* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */
- reg_write(lynx, FIFO_SIZES, 0x003030a0);
- /* 20 byte threshold before triggering PCI transfer */
- reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24);
- /* threshold on both send FIFOs before transmitting:
- FIFO size - cache line size - 1 */
- i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff;
- i = 0x30 - i - 1;
- reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i);
-
- reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394);
-
- reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT
- | LINK_INT_PHY_REG_RCVD | LINK_INT_PHY_BUSRESET
- | LINK_INT_ISO_STUCK | LINK_INT_ASYNC_STUCK
- | LINK_INT_SENT_REJECT | LINK_INT_TX_INVALID_TC
- | LINK_INT_GRF_OVERFLOW | LINK_INT_ITF_UNDERFLOW
- | LINK_INT_ATF_UNDERFLOW);
-
- reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
- reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4);
- reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
- reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV),
- DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST
- | DMA_WORD1_CMP_MATCH_EXACT | DMA_WORD1_CMP_MATCH_BUS_BCAST
- | DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER);
-
- run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV);
-
- reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0);
- reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4);
- reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0);
- reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0);
-
- run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV);
-
- reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID
- | LINK_CONTROL_TX_ISO_EN | LINK_CONTROL_RX_ISO_EN
- | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN
- | LINK_CONTROL_RESET_TX | LINK_CONTROL_RESET_RX);
-
- if (!lynx->phyic.reg_1394a) {
- /* attempt to enable contender bit -FIXME- would this work
- * elsewhere? */
- reg_set_bits(lynx, GPIO_CTRL_A, 0x1);
- reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1);
- } else {
- /* set the contender bit in the extended PHY register
- * set. (Should check that bis 0,1,2 (=0xE0) is set
- * in register 2?)
- */
- i = get_phy_reg(lynx, 4);
- if (i != -1) set_phy_reg(lynx, 4, i | 0x40);
- }
-
- return 1;
-}
-
-static void lynx_release(struct hpsb_host *host)
-{
- struct ti_lynx *lynx;
-
- if (host != NULL) {
- lynx = host->hostdata;
- remove_card(lynx->dev);
- } else {
-#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
- unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME);
-#endif
- }
-}
-
+/* called from subsystem core */
static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet)
{
struct ti_lynx *lynx = host->hostdata;
return 1;
}
+
+/* called from subsystem core */
static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
{
struct ti_lynx *lynx = host->hostdata;
arg |= (retval == -1 ? 63 : retval);
retval = 0;
- PRINT(KERN_INFO, lynx->id, "resetting bus on request%s",
- (host->attempt_root ? " and attempting to become root"
- : ""));
+ PRINT(KERN_INFO, lynx->id, "resetting bus on request");
lynx->selfid_size = -1;
lynx->phy_reg0 = -1;
} else {
MOD_DEC_USE_COUNT;
}
+
+ retval = 1;
break;
case ISO_LISTEN_CHANNEL:
membase = md->lynx->aux_port;
break;
default:
- panic("pcilynx%d: unsupported md->type %d in " __FUNCTION__,
- md->lynx->id, md->type);
+ panic("pcilynx%d: unsupported md->type %d in %s",
+ md->lynx->id, md->type, __FUNCTION__);
}
down(&md->lynx->mem_dma_mutex);
}
}
+
static void iso_rcv_bh(struct ti_lynx *lynx)
{
unsigned int idx;
}
+static void remove_card(struct pci_dev *dev)
+{
+ struct ti_lynx *lynx;
+ int i;
+
+ lynx = pci_get_drvdata(dev);
+ if (!lynx) return;
+ pci_set_drvdata(dev, NULL);
+
+ switch (lynx->state) {
+ case is_host:
+ reg_write(lynx, PCI_INT_ENABLE, 0);
+ hpsb_remove_host(lynx->host);
+ case have_intr:
+ reg_write(lynx, PCI_INT_ENABLE, 0);
+ free_irq(lynx->dev->irq, lynx);
+ case have_iomappings:
+ reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
+ /* Fix buggy cards with autoboot pin not tied low: */
+ reg_write(lynx, DMA0_CHAN_CTRL, 0);
+ iounmap(lynx->registers);
+ iounmap(lynx->local_rom);
+ iounmap(lynx->local_ram);
+ iounmap(lynx->aux_port);
+ case have_1394_buffers:
+ for (i = 0; i < ISORCV_PAGES; i++) {
+ if (lynx->iso_rcv.page[i]) {
+ pci_free_consistent(lynx->dev, PAGE_SIZE,
+ lynx->iso_rcv.page[i],
+ lynx->iso_rcv.page_dma[i]);
+ }
+ }
+ pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page,
+ lynx->rcv_page_dma);
+ case have_aux_buf:
+#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
+ pci_free_consistent(lynx->dev, 65536, lynx->mem_dma_buffer,
+ lynx->mem_dma_buffer_dma);
+#endif
+ case have_pcl_mem:
+#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM
+ pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem,
+ lynx->pcl_mem_dma);
+#endif
+ case clear:
+ /* do nothing - already freed */
+ ;
+ }
+
+ tasklet_kill(&lynx->iso_rcv.tq);
+ kfree(lynx);
+}
+
+
static int __devinit add_card(struct pci_dev *dev,
- const struct pci_device_id *devid)
+ const struct pci_device_id *devid_is_unused)
{
#define FAIL(fmt, args...) do { \
PRINT_G(KERN_ERR, fmt , ## args); \
- num_of_cards--; \
remove_card(dev); \
- return -1; \
+ return error; \
} while (0)
+ struct hpsb_host *host;
struct ti_lynx *lynx; /* shortcut to currently handled device */
- unsigned int i;
+ struct ti_pcl pcl;
+ u32 *pcli;
+ int i;
+ int error;
- if (num_of_cards == MAX_PCILYNX_CARDS) {
- PRINT_G(KERN_WARNING, "cannot handle more than %d cards. "
- "Adjust MAX_PCILYNX_CARDS in pcilynx.h.",
- MAX_PCILYNX_CARDS);
- return -1;
- }
- lynx = &cards[num_of_cards++];
+ error = -ENXIO;
if (pci_set_dma_mask(dev, 0xffffffff))
- FAIL("DMA address limits not supported for PCILynx hardware %d",
- lynx->id);
+ FAIL("DMA address limits not supported for PCILynx hardware");
if (pci_enable_device(dev))
- FAIL("failed to enable PCILynx hardware %d", lynx->id);
+ FAIL("failed to enable PCILynx hardware");
pci_set_master(dev);
- lynx->host = hpsb_get_host(&lynx_template, 0);
- if (!lynx->host)
- FAIL("failed to allocate host structure");
+ error = -ENOMEM;
+
+ host = hpsb_alloc_host(lynx_driver, sizeof(struct ti_lynx));
+ if (!host) FAIL("failed to allocate control structure memory");
- lynx->state = have_host_struct;
- lynx->host->hostdata = lynx;
- lynx->id = num_of_cards-1;
+ lynx = host->hostdata;
+ lynx->id = card_id++;
lynx->dev = dev;
- lynx->host->pdev = dev;
+ lynx->state = clear;
+ lynx->host = host;
+ host->pdev = dev;
+ pci_set_drvdata(dev, lynx);
lynx->lock = SPIN_LOCK_UNLOCKED;
lynx->phy_reg_lock = SPIN_LOCK_UNLOCKED;
}
#endif
- reg_write(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
+ reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
+ /* Fix buggy cards with autoboot pin not tied low: */
+ reg_write(lynx, DMA0_CHAN_CTRL, 0);
if (!request_irq(dev->irq, lynx_irq_handler, SA_SHIRQ,
PCILYNX_DRIVER_NAME, lynx)) {
PRINT(KERN_INFO, lynx->id, "found old 1394 PHY");
}
- /* Tell the highlevel this host is ready */
- highlevel_add_one_host (lynx->host);
-
- return 0;
-#undef FAIL
-}
+ lynx->selfid_size = -1;
+ lynx->phy_reg0 = -1;
-static void remove_card(struct pci_dev *dev)
-{
- struct ti_lynx *lynx;
- int i;
+ lynx->async.queue = NULL;
- lynx = cards;
- while (lynx->dev != dev) lynx++;
+ pcl.next = pcl_bus(lynx, lynx->rcv_pcl);
+ put_pcl(lynx, lynx->rcv_pcl_start, &pcl);
- switch (lynx->state) {
- case have_intr:
- reg_write(lynx, PCI_INT_ENABLE, 0);
- free_irq(lynx->dev->irq, lynx);
- case have_iomappings:
- reg_write(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
- iounmap(lynx->registers);
- iounmap(lynx->local_rom);
- iounmap(lynx->local_ram);
- iounmap(lynx->aux_port);
- case have_1394_buffers:
- for (i = 0; i < ISORCV_PAGES; i++) {
- if (lynx->iso_rcv.page[i]) {
- pci_free_consistent(lynx->dev, PAGE_SIZE,
- lynx->iso_rcv.page[i],
- lynx->iso_rcv.page_dma[i]);
- }
- }
- pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page,
- lynx->rcv_page_dma);
- case have_aux_buf:
-#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
- pci_free_consistent(lynx->dev, 65536, lynx->mem_dma_buffer,
- lynx->mem_dma_buffer_dma);
+ pcl.next = PCL_NEXT_INVALID;
+ pcl.async_error_next = PCL_NEXT_INVALID;
+#ifdef __BIG_ENDIAN
+ pcl.buffer[0].control = PCL_CMD_RCV | 16;
+ pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
+#else
+ pcl.buffer[0].control = PCL_CMD_RCV | PCL_BIGENDIAN | 16;
+ pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
#endif
- case have_pcl_mem:
-#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM
- pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem,
- lynx->pcl_mem_dma);
+ pcl.buffer[0].pointer = lynx->rcv_page_dma;
+ pcl.buffer[1].pointer = lynx->rcv_page_dma + 16;
+ put_pcl(lynx, lynx->rcv_pcl, &pcl);
+
+ pcl.next = pcl_bus(lynx, lynx->async.pcl);
+ pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl);
+ put_pcl(lynx, lynx->async.pcl_start, &pcl);
+
+ pcl.next = pcl_bus(lynx, lynx->iso_send.pcl);
+ pcl.async_error_next = PCL_NEXT_INVALID;
+ put_pcl(lynx, lynx->iso_send.pcl_start, &pcl);
+
+ pcl.next = PCL_NEXT_INVALID;
+ pcl.async_error_next = PCL_NEXT_INVALID;
+ pcl.buffer[0].control = PCL_CMD_RCV | 4;
+#ifndef __BIG_ENDIAN
+ pcl.buffer[0].control |= PCL_BIGENDIAN;
#endif
- case have_host_struct:
- /* FIXME - verify host freeing */
- case clear:;
- /* do nothing - already freed */
+ pcl.buffer[1].control = PCL_LAST_BUFF | 2044;
+
+ for (i = 0; i < NUM_ISORCV_PCL; i++) {
+ int page = i / ISORCV_PER_PAGE;
+ int sec = i % ISORCV_PER_PAGE;
+
+ pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page]
+ + sec * MAX_ISORCV_SIZE;
+ pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4;
+ put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl);
}
- tasklet_kill(&lynx->iso_rcv.tq);
+ pcli = (u32 *)&pcl;
+ for (i = 0; i < NUM_ISORCV_PCL; i++) {
+ pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]);
+ }
+ put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl);
- lynx->state = clear;
-}
+ /* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */
+ reg_write(lynx, FIFO_SIZES, 0x003030a0);
+ /* 20 byte threshold before triggering PCI transfer */
+ reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24);
+ /* threshold on both send FIFOs before transmitting:
+ FIFO size - cache line size - 1 */
+ i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff;
+ i = 0x30 - i - 1;
+ reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i);
-#if 0
-static int init_driver()
-{
- struct pci_dev *dev = NULL;
- int success = 0;
+ reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394);
- if (num_of_cards) {
- PRINT_G(KERN_DEBUG, __PRETTY_FUNCTION__ " called again");
- return 0;
- }
+ reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT
+ | LINK_INT_PHY_REG_RCVD | LINK_INT_PHY_BUSRESET
+ | LINK_INT_ISO_STUCK | LINK_INT_ASYNC_STUCK
+ | LINK_INT_SENT_REJECT | LINK_INT_TX_INVALID_TC
+ | LINK_INT_GRF_OVERFLOW | LINK_INT_ITF_UNDERFLOW
+ | LINK_INT_ATF_UNDERFLOW);
+
+ reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
+ reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4);
+ reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
+ reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV),
+ DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST
+ | DMA_WORD1_CMP_MATCH_EXACT | DMA_WORD1_CMP_MATCH_BUS_BCAST
+ | DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER);
- PRINT_G(KERN_INFO, "looking for PCILynx cards");
+ run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV);
- while ((dev = pci_find_device(PCI_VENDOR_ID_TI,
- PCI_DEVICE_ID_TI_PCILYNX, dev))
- != NULL) {
- if (add_card(dev) == 0) {
- success = 1;
- }
- }
+ reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0);
+ reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4);
+ reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0);
+ reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0);
- if (success == 0) {
- PRINT_G(KERN_WARNING, "no operable PCILynx cards found");
- return -ENXIO;
- }
+ run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV);
-#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
- if (register_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME, &aux_ops)) {
- PRINT_G(KERN_ERR, "allocation of char major number %d failed",
- PCILYNX_MAJOR);
- return -EBUSY;
+ reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID
+ | LINK_CONTROL_TX_ISO_EN | LINK_CONTROL_RX_ISO_EN
+ | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN
+ | LINK_CONTROL_RESET_TX | LINK_CONTROL_RESET_RX);
+
+ if (!lynx->phyic.reg_1394a) {
+ /* attempt to enable contender bit -FIXME- would this work
+ * elsewhere? */
+ reg_set_bits(lynx, GPIO_CTRL_A, 0x1);
+ reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1);
+ } else {
+ /* set the contender bit in the extended PHY register
+ * set. (Should check that bis 0,1,2 (=0xE0) is set
+ * in register 2?)
+ */
+ i = get_phy_reg(lynx, 4);
+ if (i != -1) set_phy_reg(lynx, 4, i | 0x40);
}
-#endif
+
+ hpsb_add_host(host);
+ lynx->state = is_host;
return 0;
+#undef FAIL
}
-#endif
+
static size_t get_lynx_rom(struct hpsb_host *host, const quadlet_t **ptr)
return sizeof(lynx_csr_rom);
}
-static struct hpsb_host_template lynx_template = {
- name: PCILYNX_DRIVER_NAME,
- initialize_host: lynx_initialize,
- release_host: lynx_release,
- get_rom: get_lynx_rom,
- transmit_packet: lynx_transmit,
- devctl: lynx_devctl
-};
-
static struct pci_device_id pci_table[] __devinitdata = {
{
vendor: PCI_VENDOR_ID_TI,
{ } /* Terminating entry */
};
-static struct pci_driver lynx_pcidriver = {
+static struct pci_driver lynx_pci_driver = {
name: PCILYNX_DRIVER_NAME,
id_table: pci_table,
probe: add_card,
- remove: remove_card,
+ remove: __devexit_p(remove_card),
+};
+
+static struct hpsb_host_operations lynx_ops = {
+ get_rom: get_lynx_rom,
+ transmit_packet: lynx_transmit,
+ devctl: lynx_devctl,
};
MODULE_AUTHOR("Andreas E. Bombe <andreas.bombe@munich.netsurf.de>");
MODULE_SUPPORTED_DEVICE("pcilynx");
MODULE_DEVICE_TABLE(pci, pci_table);
-static void __exit pcilynx_cleanup(void)
-{
- pci_unregister_driver(&lynx_pcidriver);
- hpsb_unregister_lowlevel(&lynx_template);
- PRINT_G(KERN_INFO, "removed " PCILYNX_DRIVER_NAME " module");
-}
-
static int __init pcilynx_init(void)
{
int ret;
- if (hpsb_register_lowlevel(&lynx_template)) {
- PRINT_G(KERN_ERR, "registering failed");
- return -ENXIO;
+#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
+ if (register_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME, &aux_ops)) {
+ PRINT_G(KERN_ERR, "allocation of char major number %d failed",
+ PCILYNX_MAJOR);
+ return -EBUSY;
+ }
+#endif
+
+ lynx_driver = hpsb_register_lowlevel(&lynx_ops, PCILYNX_DRIVER_NAME);
+ if (!lynx_driver) {
+ ret = -ENOMEM;
+ goto free_char_dev;
}
- ret = pci_module_init(&lynx_pcidriver);
+ ret = pci_module_init(&lynx_pci_driver);
if (ret < 0) {
PRINT_G(KERN_ERR, "PCI module init failed");
- hpsb_unregister_lowlevel(&lynx_template);
+ goto unregister_lowlevel;
}
+ return 0;
+
+ unregister_lowlevel:
+ hpsb_unregister_lowlevel(lynx_driver);
+ free_char_dev:
+#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
+ unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME);
+#endif
+
return ret;
}
+static void __exit pcilynx_cleanup(void)
+{
+ pci_unregister_driver(&lynx_pci_driver);
+ hpsb_unregister_lowlevel(lynx_driver);
+
+#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
+ unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME);
+#endif
+}
+
+
module_init(pcilynx_init);
module_exit(pcilynx_cleanup);
u32 product;
} phyic;
- enum { clear, have_host_struct, have_intr, have_aux_buf, have_pcl_mem,
- have_1394_buffers, have_iomappings } state;
+ enum { clear, have_intr, have_aux_buf, have_pcl_mem,
+ have_1394_buffers, have_iomappings, is_host } state;
/* remapped memory spaces */
void *registers;
hi = list_entry(lh, struct host_info, list);
khl->nodes = hi->host->node_count;
- strcpy(khl->name, hi->host->template->name);
+ strcpy(khl->name, hi->host->driver->name);
khl++;
}
lh = lh->next;
}
hi = list_entry(lh, struct host_info, list);
- hpsb_inc_host_usage(hi->host);
+ hpsb_ref_host(hi->host);
list_add_tail(&fi->list, &hi->file_info_list);
fi->host = hi->host;
fi->state = connected;
{
struct file_info *fi;
- if (minor(inode->i_rdev)) {
+ if (ieee1394_file_to_instance(file) > 0) {
return -ENXIO;
}
list_del(&fi->list);
spin_unlock_irq(&host_info_lock);
- hpsb_dec_host_usage(fi->host);
+ hpsb_unref_host(fi->host);
}
kfree(fi);
return -ENOMEM;
}
- devfs_handle = devfs_register(NULL, RAW1394_DEVICE_NAME, DEVFS_FL_NONE,
- RAW1394_DEVICE_MAJOR, 0,
+ devfs_handle = devfs_register(NULL,
+ RAW1394_DEVICE_NAME, DEVFS_FL_NONE,
+ IEEE1394_MAJOR,
+ IEEE1394_MINOR_BLOCK_RAW1394 * 16,
S_IFCHR | S_IRUSR | S_IWUSR, &file_ops,
NULL);
- if (devfs_register_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME,
- &file_ops)) {
- HPSB_ERR("raw1394 failed to register /dev/raw1394 device");
+ if (ieee1394_register_chardev(IEEE1394_MINOR_BLOCK_RAW1394,
+ THIS_MODULE, &file_ops)) {
+ HPSB_ERR("raw1394 failed to register minor device block");
+ devfs_unregister(devfs_handle);
+ hpsb_unregister_highlevel(hl_handle);
return -EBUSY;
}
printk(KERN_INFO "raw1394: /dev/%s device initialized\n", RAW1394_DEVICE_NAME);
static void __exit cleanup_raw1394(void)
{
- devfs_unregister_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME);
+ ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_RAW1394);
devfs_unregister(devfs_handle);
hpsb_unregister_highlevel(hl_handle);
}
* sbp2.c - SBP-2 protocol driver for IEEE-1394
*
* Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com)
- * jamesg@filanet.com
+ * jamesg@filanet.com (JSG)
*
* 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 SBP-2 driver is still in an early state, but supports a variety of devices.
* I have read/written many gigabytes of data from/to SBP-2 drives, and have seen
- * performance of more than 16 MBytes/s on individual drives (limit of the media
+ * performance of more than 25 MBytes/s on individual drives (limit of the media
* transfer rate).
*
* Following are the devices that have been tested successfully:
* - LaCie IEEE-1394 hard drives (several flavors)
* - QPS IEEE-1394 CD-RW/DVD drives and hard drives
* - BusLink IEEE-1394 hard drives
- * - Iomega IEEE-1394 Zip/Jazz drives
+ * - Iomega IEEE-1394 Zip/Jazz/Peerless drives
* - ClubMac IEEE-1394 hard drives
* - FirePower IEEE-1394 hard drives
* - EzQuest IEEE-1394 hard drives and CD-RW drives
* - Sony IEEE-1394 CD-RW drives
* - Epson IEEE-1394 scanner
* - ADS IEEE-1394 memory stick and compact flash readers
- * (e.g. "insmod sbp2 mode_sense_hack=1" for mem stick and flash readers))
* - SBP-2 bridge-based devices (LSI, Oxford Semiconductor, Indigita bridges)
* - Various other standard IEEE-1394 hard drives and enclosures
*
* add some init code to the kernel to support this... and modules are much
* more flexible anyway. ;-)
*
- * - The scsi stack in recent kernels pass down the data transfer
- * direction as scsicmd->sc_data_direction, which we should use
- * instead of the sbp2scsi_direction_table.
- *
*
* History:
*
* when we register our driver. This change
* automtically adds hotplug support to the driver.
* Kristian Hogsberg <hogsberg@users.sf.net>
+ *
+ * 11/17/01 - Various bugfixes/cleanups:
+ * * Remember to logout of device in sbp2_disconnect.
+ * * If we fail to reconnect to a device after bus reset
+ * remember to release unit directory, so the ieee1394
+ * knows we no longer manage it.
+ * * Unregister scsi hosts in sbp2_remove_host when a
+ * hpsb_host goes away.
+ * * Remove stupid hack in sbp2_remove_host.
+ * * Switched to "manual" module initialization
+ * (i.e. not scsi_module.c) and moved sbp2_cleanup
+ * moved sbp2scsi_release to sbp2_module_ext. The
+ * release function is called once pr. registered
+ * scsi host, but sbp2_cleanup should only be called
+ * upon module unload. Moved much initialization
+ * from sbp2scsi_detect to sbp2_module_init.
+ * Kristian Hogsberg <hogsberg@users.sf.net>
+ * 01/06/02 - Misc bug fixes/enhancements: (JSG)
+ * * Enable use_new_eh_code for scsi stuff.
+ * * Do not write all ones for NULL ORB high/low fields, but
+ * rather leave reserved areas zeroed (per SBP2 spec).
+ * * Use newer scsi transfer direction passed down instead of our
+ * direction table.
+ * * Bumped login time-out to 20 seconds, as some devices are slow.
+ * * Fixed a couple scsi unregister bugs on module unload
+ * 01/13/02 - Fixed compatibility with certain SBP2 devices, such as Iomega
+ * 1394 devices (Peerless, Jazz). Also a bit of clean-up of the
+ * driver, thanks to H.J.Lu (hjl@lucon.org). Removed mode_sense_hack
+ * module load option, as it's been fixed in the 2.4 scsi stack.
*/
-
-\f
+
/*
* Includes
#include <linux/proc_fs.h>
#include <linux/blk.h>
#include <linux/smp_lock.h>
+#include <linux/init.h>
#include <asm/current.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
+#ifdef CONFIG_KBUILD_2_5
+#include <scsi.h>
+#include <hosts.h>
+#include <sd.h>
+#else
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+#endif
+
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "ieee1394_core.h"
#include "highlevel.h"
#include "ieee1394_transactions.h"
#include "ieee1394_hotplug.h"
-#include "../scsi/scsi.h"
-#include "../scsi/hosts.h"
-#include "../scsi/sd.h"
#include "sbp2.h"
/*
* Module load parameter definitions
*/
-/*
- * Set mode_sense_hack to 1 if you have some sort of unusual sbp2 device,
- * like a 1394 memory stick reader, compact flash reader, or MO drive that
- * does not support mode sense. Allows you to mount the media rw instead
- * of ro.
- */
-MODULE_PARM(mode_sense_hack,"i");
-MODULE_PARM_DESC(mode_sense_hack, "Emulate mode sense for devices like 1394 memory stick readers");
-static int mode_sense_hack = 0;
-
/*
* Change max_speed on module load if you have a bad IEEE-1394 controller
* that has trouble running 2KB packets at 400mb.
*/
#ifdef CONFIG_IEEE1394_SBP2_DEBUG_ORBS
-#define SBP2_ORB_DEBUG(fmt, args...) HPSB_ERR("sbp2("__FUNCTION__"): "fmt, ## args)
+#define SBP2_ORB_DEBUG(fmt, args...) HPSB_ERR("sbp2(%s): "fmt, __FUNCTION__, ## args)
static u32 global_outstanding_command_orbs = 0;
#define outstanding_orb_incr global_outstanding_command_orbs++
#define outstanding_orb_decr global_outstanding_command_orbs--
#ifdef CONFIG_IEEE1394_SBP2_DEBUG_DMA
#define SBP2_DMA_ALLOC(fmt, args...) \
- HPSB_ERR("sbp2("__FUNCTION__")alloc(%d): "fmt, \
+ HPSB_ERR("sbp2(%s)alloc(%d): "fmt, __FUNCTION__, \
++global_outstanding_dmas, ## args)
#define SBP2_DMA_FREE(fmt, args...) \
- HPSB_ERR("sbp2("__FUNCTION__")free(%d): "fmt, \
+ HPSB_ERR("sbp2(%s)free(%d): "fmt, __FUNCTION__, \
--global_outstanding_dmas, ## args)
static u32 global_outstanding_dmas = 0;
#else
#define SBP2_DMA_FREE(fmt, args...)
#endif
-
#if CONFIG_IEEE1394_SBP2_DEBUG >= 2
#define SBP2_DEBUG(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args)
#define SBP2_INFO(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args)
* Globals
*/
-Scsi_Host_Template *global_scsi_tpnt = NULL;
+static Scsi_Host_Template scsi_driver_template;
static u8 sbp2_speedto_maxrec[] = { 0x7, 0x8, 0x9 };
static LIST_HEAD(sbp2_host_info_list);
-static int sbp2_host_count = 0;
static struct hpsb_highlevel *sbp2_hl_handle = NULL;
static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id,
struct sbp2scsi_host_info *hi)
{
- struct list_head *lh;
+ struct list_head *lh, *next;
struct sbp2_command_info *command;
unsigned long flags;
sbp2_spin_lock(&scsi_id->sbp2_command_orb_lock, flags);
if (!list_empty(&scsi_id->sbp2_command_orb_completed)) {
- list_for_each(lh, &scsi_id->sbp2_command_orb_completed) {
+ list_for_each_safe(lh, next, &scsi_id->sbp2_command_orb_completed) {
command = list_entry(lh, struct sbp2_command_info, list);
/* Release our generic DMA's */
hi = (struct sbp2scsi_host_info *) command->Current_SCpnt->host->hostdata[0];
if (hi == NULL) {
- printk(KERN_ERR __FUNCTION__": hi == NULL\n");
+ printk(KERN_ERR "%s: hi == NULL\n", __FUNCTION__);
return;
}
if (command->cmd_dma) {
- pci_unmap_single(hi->host->pdev, command->cmd_dma,
- command->dma_size, command->dma_dir);
- SBP2_DMA_FREE("single bulk");
+ if (command->dma_type == CMD_DMA_SINGLE) {
+ pci_unmap_single(hi->host->pdev, command->cmd_dma,
+ command->dma_size, command->dma_dir);
+ SBP2_DMA_FREE("single bulk");
+ } else if (command->dma_type == CMD_DMA_PAGE) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,13)
+ pci_unmap_single(hi->host->pdev, command->cmd_dma,
+ command->dma_size, command->dma_dir);
+#else
+ pci_unmap_page(hi->host->pdev, command->cmd_dma,
+ command->dma_size, command->dma_dir);
+#endif /* Linux version < 2.4.13 */
+ SBP2_DMA_FREE("single page");
+ } /* XXX: Check for CMD_DMA_NONE bug */
+ command->dma_type = CMD_DMA_NONE;
command->cmd_dma = 0;
}
hpsb_unregister_highlevel(sbp2_hl_handle);
sbp2_hl_handle = NULL;
}
- return;
}
static int sbp2_probe(struct unit_directory *ud)
SBP2_DEBUG("sbp2_disconnect");
hi = sbp2_find_host_info(ud->ne->host);
- if (hi != NULL)
- sbp2_remove_device(hi, scsi_id);
+ if (hi != NULL) {
+ sbp2_logout_device(hi, scsi_id);
+ sbp2_remove_device(hi, scsi_id);
+ }
}
static void sbp2_update(struct unit_directory *ud)
*/
if (sbp2_login_device(hi, scsi_id)) {
- /* Login failed too... so, just mark him as
- * unvalidated, so that he gets cleaned up
- * later.
- */
+ /* Login failed too, just remove the device. */
SBP2_ERR("sbp2_reconnect_device failed!");
sbp2_remove_device(hi, scsi_id);
+ hpsb_release_unit_directory(ud);
return;
}
}
sbp2_spin_lock(&sbp2_host_info_lock, flags);
list_add_tail(&hi->list, &sbp2_host_info_list);
- sbp2_host_count++;
sbp2_spin_unlock(&sbp2_host_info_lock, flags);
/* Register our host with the SCSI stack. */
- sbp2scsi_register_scsi_host(hi);
+ hi->scsi_host = scsi_register (&scsi_driver_template, sizeof(void *));
+ if (hi->scsi_host)
+ hi->scsi_host->hostdata[0] = (unsigned long)hi;
+ scsi_driver_template.present++;
return;
}
list_for_each (lh, &sbp2_host_info_list) {
hi = list_entry(lh, struct sbp2scsi_host_info, list);
- if (hi->host == host) {
+ if (hi->host == host)
return hi;
- }
}
- return(NULL);
+ return NULL;
+}
+
+/*
+ * This function returns a host info structure for a given Scsi_Host
+ * struct.
+ */
+static struct sbp2scsi_host_info *sbp2_find_host_info_scsi(struct Scsi_Host *host)
+{
+ struct list_head *lh;
+ struct sbp2scsi_host_info *hi;
+
+ list_for_each (lh, &sbp2_host_info_list) {
+ hi = list_entry(lh, struct sbp2scsi_host_info, list);
+ if (hi->scsi_host == host)
+ return hi;
+ }
+
+ return NULL;
}
/*
- * This function is called when the host is removed
+ * This function is called when a host is removed.
*/
static void sbp2_remove_host(struct hpsb_host *host)
{
struct sbp2scsi_host_info *hi;
unsigned long flags;
- int i;
SBP2_DEBUG("sbp2_remove_host");
hi = sbp2_find_host_info(host);
if (hi != NULL) {
- /* Here's an annoying hack: we get a disconnect
- * callback for each device, so this loop shouldn't be
- * necessary. However, the sbp2 driver receives the
- * remove_host callback before the nodemgr, so when we
- * get the disconnect callback, we've already freed
- * the host. Thus, we free the devices here...
- */
- for (i = 0; i < SBP2SCSI_MAX_SCSI_IDS; i++) {
- if (hi->scsi_id[i] != NULL) {
- sbp2_logout_device(hi, hi->scsi_id[i]);
- sbp2_remove_device(hi, hi->scsi_id[i]);
- }
- }
sbp2util_remove_request_packet_pool(hi);
- sbp2_host_count--;
list_del(&hi->list);
kfree(hi);
}
*/
if (sbp2_login_device(hi, scsi_id)) {
- /*
- * Login failed... so, just mark him as unvalidated, so
- * that he gets cleaned up later.
- */
+ /* Login failed, just remove the device. */
SBP2_ERR("sbp2_login_device failed");
sbp2_remove_device(hi, scsi_id);
return -EBUSY;
}
/*
- * This function removes (cleans-up after) any unvalidated sbp2 devices
+ * This function removes an sbp2 device from the sbp2scsi_host_info struct.
*/
static void sbp2_remove_device(struct sbp2scsi_host_info *hi,
struct scsi_id_instance_data *scsi_id)
{
+ SBP2_DEBUG("sbp2_remove_device");
+
/* Complete any pending commands with selection timeout */
sbp2scsi_complete_all_commands(hi, scsi_id, DID_NO_CONNECT);
SBP2_DMA_FREE("single logout orb");
}
- SBP2_DEBUG("Unvalidated SBP-2 device removed, SCSI ID = %d",
- scsi_id->id);
+ SBP2_DEBUG("SBP-2 device removed, SCSI ID = %d", scsi_id->id);
hi->scsi_id[scsi_id->id] = NULL;
kfree(scsi_id);
}
save_flags(flags);
cli();
- /* 10 second timeout */
+ /* 20 second timeout */
if (scsi_id->status_block.ORB_offset_lo != scsi_id->login_orb_dma)
- sleep_on_timeout(&scsi_id->sbp2_login_wait, 10*HZ);
+ sleep_on_timeout(&scsi_id->sbp2_login_wait, 20*HZ);
restore_flags(flags);
SBP2_DEBUG("sbp2_login_device: initial check");
/*
* Check status
- */
+ */
if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) ||
STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) ||
STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) {
ud = scsi_id->ud;
/* Handle different fields in the unit directory, based on keys */
- for (i = 0; i < ud->arb_count; i++) {
- switch (ud->arb_keys[i]) {
+ for (i = 0; i < ud->count; i++) {
+ switch (CONFIG_ROM_KEY(ud->quadlets[i])) {
case SBP2_CSR_OFFSET_KEY:
/* Save off the management agent address */
scsi_id->sbp2_management_agent_addr =
- CONFIG_ROM_INITIAL_MEMORY_SPACE +
- (ud->arb_values[i] << 2);
+ CSR_REGISTER_BASE +
+ (CONFIG_ROM_VALUE(ud->quadlets[i]) << 2);
SBP2_DEBUG("sbp2_management_agent_addr = %x",
(unsigned int) scsi_id->sbp2_management_agent_addr);
case SBP2_COMMAND_SET_SPEC_ID_KEY:
/* Command spec organization */
- scsi_id->sbp2_command_set_spec_id = ud->arb_values[i];
+ scsi_id->sbp2_command_set_spec_id
+ = CONFIG_ROM_VALUE(ud->quadlets[i]);
SBP2_DEBUG("sbp2_command_set_spec_id = %x",
(unsigned int) scsi_id->sbp2_command_set_spec_id);
break;
case SBP2_COMMAND_SET_KEY:
/* Command set used by sbp2 device */
- scsi_id->sbp2_command_set = ud->arb_values[i];
+ scsi_id->sbp2_command_set
+ = CONFIG_ROM_VALUE(ud->quadlets[i]);
SBP2_DEBUG("sbp2_command_set = %x",
(unsigned int) scsi_id->sbp2_command_set);
break;
* Unit characterisitcs (orb related stuff
* that I'm not yet paying attention to)
*/
- scsi_id->sbp2_unit_characteristics = ud->arb_values[i];
+ scsi_id->sbp2_unit_characteristics
+ = CONFIG_ROM_VALUE(ud->quadlets[i]);
SBP2_DEBUG("sbp2_unit_characteristics = %x",
(unsigned int) scsi_id->sbp2_unit_characteristics);
break;
* Device type and lun (used for
* detemining type of sbp2 device)
*/
- scsi_id->sbp2_device_type_and_lun = ud->arb_values[i];
+ scsi_id->sbp2_device_type_and_lun
+ = CONFIG_ROM_VALUE(ud->quadlets[i]);
SBP2_DEBUG("sbp2_device_type_and_lun = %x",
(unsigned int) scsi_id->sbp2_device_type_and_lun);
break;
* bridge with 128KB max transfer size
* limitation.
*/
- scsi_id->sbp2_firmware_revision = ud->arb_values[i];
+ scsi_id->sbp2_firmware_revision
+ = CONFIG_ROM_VALUE(ud->quadlets[i]);
if (scsi_id->sbp2_firmware_revision ==
SBP2_128KB_BROKEN_FIRMWARE) {
SBP2_WARN("warning: Bridge chipset supports 128KB max transfer size");
scsi_id->max_payload_size = min(sbp2_speedto_maxrec[scsi_id->speed_code],
(u8)(((be32_to_cpu(hi->host->csr.rom[2]) >> 12) & 0xf) - 1));
- SBP2_ERR("Node " NODE_BUS_FMT ": Max speed [%s] - Max payload [0x%02x/%u]",
+ SBP2_ERR("Node[" NODE_BUS_FMT "]: Max speed [%s] - Max payload [%u]",
NODE_BUS_ARGS(scsi_id->ne->nodeid), hpsb_speedto_str[scsi_id->speed_code],
- scsi_id->max_payload_size, 1 << ((u32)scsi_id->max_payload_size + 2));
+ 1 << ((u32)scsi_id->max_payload_size + 2));
return(0);
}
unchar *scsi_cmd,
unsigned int scsi_use_sg,
unsigned int scsi_request_bufflen,
- void *scsi_request_buffer, int dma_dir)
+ void *scsi_request_buffer,
+ unsigned char scsi_dir)
{
struct scatterlist *sgpnt = (struct scatterlist *) scsi_request_buffer;
struct sbp2_command_orb *command_orb = &command->command_orb;
struct sbp2_unrestricted_page_table *scatter_gather_element =
&command->scatter_gather_element[0];
- u32 sg_count, sg_len;
+ int dma_dir = scsi_to_pci_dma_dir (scsi_dir);
+ u32 sg_count, sg_len, orb_direction;
dma_addr_t sg_addr;
int i;
* that data_size becomes the number of s/g elements, and
* page_size should be zero (for unrestricted).
*/
- command_orb->next_ORB_hi = 0xffffffff;
- command_orb->next_ORB_lo = 0xffffffff;
+ command_orb->next_ORB_hi = ORB_SET_NULL_PTR(1);
+ command_orb->next_ORB_lo = 0x0;
command_orb->misc = ORB_SET_MAX_PAYLOAD(scsi_id->max_payload_size);
command_orb->misc |= ORB_SET_SPEED(scsi_id->speed_code);
command_orb->misc |= ORB_SET_NOTIFY(1); /* Notify us when complete */
+ /*
+ * Get the direction of the transfer. If the direction is unknown, then use our
+ * goofy table as a back-up.
+ */
+ switch (scsi_dir) {
+ case SCSI_DATA_NONE:
+ orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER;
+ break;
+ case SCSI_DATA_WRITE:
+ orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA;
+ break;
+ case SCSI_DATA_READ:
+ orb_direction = ORB_DIRECTION_READ_FROM_MEDIA;
+ break;
+ case SCSI_DATA_UNKNOWN:
+ default:
+ SBP2_ERR("SCSI data transfer direction not specified. "
+ "Update the SBP2 direction table in sbp2.h if "
+ "necessary for your application");
+ print_command (scsi_cmd);
+ orb_direction = sbp2scsi_direction_table[*scsi_cmd];
+ break;
+ }
+
/*
* Set-up our pagetable stuff... unfortunately, this has become
* messier than I'd like. Need to clean this up a bit. ;-)
*/
- if (sbp2scsi_direction_table[*scsi_cmd] == ORB_DIRECTION_NO_DATA_TRANSFER) {
+ if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) {
SBP2_DEBUG("No data transfer");
/*
* Handle no data transfer
*/
- command_orb->data_descriptor_hi = 0xffffffff;
- command_orb->data_descriptor_lo = 0xffffffff;
+ command_orb->data_descriptor_hi = 0x0;
+ command_orb->data_descriptor_lo = 0x0;
command_orb->misc |= ORB_SET_DIRECTION(1);
} else if (scsi_use_sg) {
SBP2_DEBUG("Only one s/g element");
command->dma_dir = dma_dir;
command->dma_size = sgpnt[0].length;
+ command->dma_type = CMD_DMA_PAGE;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,13)
command->cmd_dma = pci_map_single (hi->host->pdev, sgpnt[0].address,
command->dma_size,
command->dma_dir);
- SBP2_DMA_ALLOC("single scatter element");
+#else
+ command->cmd_dma = pci_map_page(hi->host->pdev,
+ sgpnt[0].page,
+ sgpnt[0].offset,
+ command->dma_size,
+ command->dma_dir);
+#endif /* Linux version < 2.4.13 */
+ SBP2_DMA_ALLOC("single page scatter element");
command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id);
command_orb->data_descriptor_lo = command->cmd_dma;
command_orb->misc |= ORB_SET_DATA_SIZE(command->dma_size);
- command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]);
+ command_orb->misc |= ORB_SET_DIRECTION(orb_direction);
} else {
int count = pci_map_sg(hi->host->pdev, sgpnt, scsi_use_sg, dma_dir);
/* use page tables (s/g) */
command_orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1);
- command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]);
+ command_orb->misc |= ORB_SET_DIRECTION(orb_direction);
command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id);
command_orb->data_descriptor_lo = command->sge_dma;
command->dma_dir = dma_dir;
command->dma_size = scsi_request_bufflen;
+ command->dma_type = CMD_DMA_SINGLE;
command->cmd_dma = pci_map_single (hi->host->pdev, scsi_request_buffer,
command->dma_size,
command->dma_dir);
command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id);
command_orb->data_descriptor_lo = command->cmd_dma;
command_orb->misc |= ORB_SET_DATA_SIZE(scsi_request_bufflen);
- command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]);
+ command_orb->misc |= ORB_SET_DIRECTION(orb_direction);
/*
* Sanity, in case our direction table is not
* up-to-date
*/
if (!scsi_request_bufflen) {
- command_orb->data_descriptor_hi = 0xffffffff;
- command_orb->data_descriptor_lo = 0xffffffff;
+ command_orb->data_descriptor_hi = 0x0;
+ command_orb->data_descriptor_lo = 0x0;
command_orb->misc |= ORB_SET_DIRECTION(1);
}
/* Use page tables (s/g) */
command_orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1);
- command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]);
+ command_orb->misc |= ORB_SET_DIRECTION(orb_direction);
/*
* fill out our sbp-2 page tables (and split up
SBP2_ORB_DEBUG("sending command orb %p, linked = %x, total orbs = %x",
command_orb, command->linked, global_outstanding_command_orbs);
+ pci_dma_sync_single(hi->host->pdev, command->command_orb_dma,
+ sizeof(struct sbp2_command_orb),
+ PCI_DMA_BIDIRECTIONAL);
+ pci_dma_sync_single(hi->host->pdev, command->sge_dma,
+ sizeof(command->scatter_gather_element),
+ PCI_DMA_BIDIRECTIONAL);
/*
* Check to see if there are any previous orbs to use
*/
}
scsi_id->last_orb = command_orb;
+ scsi_id->last_orb_dma = command->command_orb_dma;
} else {
cpu_to_be32(command->command_orb_dma);
/* Tells hardware that this pointer is valid */
scsi_id->last_orb->next_ORB_hi = 0x0;
+ pci_dma_sync_single(hi->host->pdev, scsi_id->last_orb_dma,
+ sizeof(struct sbp2_command_orb),
+ PCI_DMA_BIDIRECTIONAL);
/*
* Only ring the doorbell if we need to (first parts of
}
scsi_id->last_orb = command_orb;
+ scsi_id->last_orb_dma = command->command_orb_dma;
}
return(0);
Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
unchar *cmd = (unchar *) SCpnt->cmnd;
- u32 device_type = (scsi_id->sbp2_device_type_and_lun & 0x00ff0000) >> 16;
+ unsigned int request_bufflen = SCpnt->request_bufflen;
+ u8 device_type
+ = SBP2_DEVICE_TYPE (scsi_id->sbp2_device_type_and_lun);
struct sbp2_command_info *command;
SBP2_DEBUG("sbp2_send_command");
- SBP2_DEBUG("SCSI command = %02x", *cmd);
- SBP2_DEBUG("SCSI transfer size = %x", SCpnt->request_bufflen);
+ SBP2_DEBUG("SCSI command:");
+#if CONFIG_IEEE1394_SBP2_DEBUG >= 2
+ print_command (cmd);
+#endif
+ SBP2_DEBUG("SCSI transfer size = %x", request_bufflen);
SBP2_DEBUG("SCSI s/g elements = %x", (unsigned int)SCpnt->use_sg);
/*
(SCpnt->request_bufflen > SBP2_BROKEN_FIRMWARE_MAX_TRANSFER) &&
(device_type == TYPE_DISK) &&
(SCpnt->use_sg) &&
- (*cmd == 0x28 || *cmd == 0x2a || *cmd == 0x0a || *cmd == 0x08)) {
+ (*cmd == READ_6 || *cmd == READ_10 || *cmd == WRITE_6 || *cmd == WRITE_10)) {
/*
* Darn, a broken device. We'll need to split up the
return(-EIO);
}
+ /*
+ * The scsi stack sends down a request_bufflen which does not match the
+ * length field in the scsi cdb. This causes some sbp2 devices to
+ * reject this inquiry command. Fix is to fix request_bufflen to match
+ * the value in the cdb.
+ */
+ if (*cmd == INQUIRY) {
+ request_bufflen = cmd[4];
+ }
+
/*
* Now actually fill in the comamnd orb and sbp2 s/g list
*/
sbp2_create_command_orb(hi, scsi_id, command, cmd, SCpnt->use_sg,
- SCpnt->request_bufflen, SCpnt->request_buffer,
- scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+ request_bufflen, SCpnt->request_buffer,
+ SCpnt->sc_data_direction);
/*
* Update our cdb if necessary (to handle sbp2 RBC command set
- * differences). This is where the command set hacks go! =)
+ * differences). This is where the command set hacks go! =)
*/
if ((device_type == TYPE_DISK) ||
(device_type == TYPE_SDAD) ||
sbp2_create_command_orb(hi, scsi_id, command, new_cmd, total_sg,
total_transfer, &sgpnt[current_sg],
- scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+ SCpnt->sc_data_direction);
/*
* Link up the orb, and ring the doorbell if needed
sbp2_create_command_orb(hi, scsi_id, command, new_cmd, total_sg,
total_transfer, &sgpnt[current_sg],
- scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+ SCpnt->sc_data_direction);
/*
* Link up the orb, and ring the doorbell if needed
Scsi_Cmnd *SCpnt)
{
u8 *scsi_buf = SCpnt->request_buffer;
- u32 device_type = (scsi_id->sbp2_device_type_and_lun & 0x00ff0000) >> 16;
-
+ u8 device_type = SBP2_DEVICE_TYPE (scsi_id->sbp2_device_type_and_lun);
+
SBP2_DEBUG("sbp2_check_sbp2_response");
switch (SCpnt->cmnd[0]) {
case INQUIRY:
- SBP2_DEBUG("Check Inquiry data");
+ /*
+ * Make sure data length is ok. Minimum length is 36 bytes
+ */
+ if (scsi_buf[4] == 0) {
+ scsi_buf[4] = 36 - 5;
+ }
/*
* Check for Simple Direct Access Device and change it to TYPE_DISK
if (command) {
SBP2_DEBUG("Found status for command ORB");
+ pci_dma_sync_single(hi->host->pdev, command->command_orb_dma,
+ sizeof(struct sbp2_command_orb),
+ PCI_DMA_BIDIRECTIONAL);
+ pci_dma_sync_single(hi->host->pdev, command->sge_dma,
+ sizeof(command->scatter_gather_element),
+ PCI_DMA_BIDIRECTIONAL);
SBP2_ORB_DEBUG("matched command orb %p", &command->command_orb);
outstanding_orb_decr;
if (SCpnt && !command->linked) {
/*
- * Handle check conditions
+ * See if the target stored any scsi status information
*/
- if (STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) {
-
- SBP2_DEBUG("CHECK CONDITION");
-
+ if (length > 8) {
/*
* Translate SBP-2 status to SCSI sense data
*/
scsi_status = sbp2_status_to_sense_data((unchar *)&scsi_id->status_block, SCpnt->sense_buffer);
+ }
+ /*
+ * Handle check conditions. If there is either SBP status or SCSI status
+ * then we'll do a fetch agent reset and note that a check condition
+ * occured.
+ */
+ if (STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc) ||
+ scsi_status) {
/*
* Initiate a fetch agent reset.
*/
+ SBP2_DEBUG("CHECK CONDITION");
sbp2_agent_reset(hi, scsi_id, SBP2_SEND_NO_WAIT);
-
}
SBP2_ORB_DEBUG("completing command orb %p", &command->command_orb);
SBP2_DEBUG("Found pending command to complete");
lh = scsi_id->sbp2_command_orb_inuse.next;
command = list_entry(lh, struct sbp2_command_info, list);
+ pci_dma_sync_single(hi->host->pdev, command->command_orb_dma,
+ sizeof(struct sbp2_command_orb),
+ PCI_DMA_BIDIRECTIONAL);
+ pci_dma_sync_single(hi->host->pdev, command->sge_dma,
+ sizeof(command->scatter_gather_element),
+ PCI_DMA_BIDIRECTIONAL);
sbp2util_mark_command_completed(scsi_id, command);
if (command->Current_SCpnt && !command->linked) {
void (*done)(Scsi_Cmnd *) = command->Current_done;
/*
* Debug stuff
*/
+#if CONFIG_IEEE1394_SBP2_DEBUG >= 1
+ print_command (SCpnt->cmnd);
print_sense("bh", SCpnt);
+#endif
break;
case SBP2_SCSI_STATUS_SELECTION_TIMEOUT:
SBP2_ERR("SBP2_SCSI_STATUS_SELECTION_TIMEOUT");
SCpnt->result = DID_NO_CONNECT << 16;
+ print_command (SCpnt->cmnd);
break;
case SBP2_SCSI_STATUS_CONDITION_MET:
case SBP2_SCSI_STATUS_COMMAND_TERMINATED:
SBP2_ERR("Bad SCSI status = %x", scsi_status);
SCpnt->result = DID_ERROR << 16;
+ print_command (SCpnt->cmnd);
break;
default:
sbp2_check_sbp2_response(hi, scsi_id, SCpnt);
}
- /*
- * One more quick hack (not enabled by default). Some sbp2 devices
- * do not support mode sense. Turn-on this hack to allow the
- * device to pass the sd driver's write-protect test (so that you
- * can mount the device rw).
- */
- if (mode_sense_hack && SCpnt->result != DID_OK && SCpnt->cmnd[0] == MODE_SENSE) {
- SBP2_INFO("Returning success to mode sense command");
- SCpnt->result = DID_OK;
- SCpnt->sense_buffer[0] = 0;
- memset (SCpnt->request_buffer, 0, 8);
- }
-
/*
* If a bus reset is in progress and there was an error, complete
* the command as busy so that it will get retried.
unsigned long flags;
SBP2_ERR("aborting sbp2 command");
-
+ print_command (SCpnt->cmnd);
+
if (scsi_id) {
/*
command = sbp2util_find_command_for_SCpnt(scsi_id, SCpnt);
if (command) {
SBP2_DEBUG("Found command to abort");
+ pci_dma_sync_single(hi->host->pdev,
+ command->command_orb_dma,
+ sizeof(struct sbp2_command_orb),
+ PCI_DMA_BIDIRECTIONAL);
+ pci_dma_sync_single(hi->host->pdev,
+ command->sge_dma,
+ sizeof(command->scatter_gather_element),
+ PCI_DMA_BIDIRECTIONAL);
sbp2util_mark_command_completed(scsi_id, command);
if (command->Current_SCpnt && !command->linked) {
void (*done)(Scsi_Cmnd *) = command->Current_done;
sbp2_spin_unlock(&hi->sbp2_command_lock, flags);
}
- return(SCSI_ABORT_SUCCESS);
+ return(SUCCESS);
}
/*
* Called by scsi stack when something has really gone wrong.
*/
-static int sbp2scsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+static int sbp2scsi_reset (Scsi_Cmnd *SCpnt)
{
struct sbp2scsi_host_info *hi = (struct sbp2scsi_host_info *) SCpnt->host->hostdata[0];
hpsb_reset_bus(hi->host, LONG_RESET);
}
- return(SCSI_RESET_SUCCESS);
+ return(SUCCESS);
}
/*
}
/*
- * This routine is called at setup (init) and does nothing. Not used here. =)
+ * Called by scsi stack after scsi driver is registered
*/
-void sbp2scsi_setup( char *str, int *ints)
+static int sbp2scsi_detect (Scsi_Host_Template *tpnt)
{
- SBP2_DEBUG("sbp2scsi_setup");
- return;
+ SBP2_DEBUG("sbp2scsi_detect");
+
+ /*
+ * Call sbp2_init to register with the ieee1394 stack. This
+ * results in a callback to sbp2_add_host for each ieee1394
+ * host controller currently registered, and for each of those
+ * we register a scsi host with the scsi stack.
+ */
+ sbp2_init();
+
+ /* We return the number of hosts registered. */
+ return scsi_driver_template.present;
}
+
/*
- * This is our detection routine, and is where we init everything.
+ * Called for contents of procfs
*/
-static int sbp2scsi_detect (Scsi_Host_Template *tpnt)
+static const char *sbp2scsi_info (struct Scsi_Host *host)
{
- SBP2_DEBUG("sbp2scsi_detect");
+ struct sbp2scsi_host_info *hi = sbp2_find_host_info_scsi(host);
+ static char info[1024];
+
+ if (!hi) /* shouldn't happen, but... */
+ return "IEEE-1394 SBP-2 protocol driver";
+
+ sprintf(info, "IEEE-1394 SBP-2 protocol driver\nHost Driver: %s\nSerial I/O: %s",
+ hi->host->driver->name, serialize_io ? "yes" : "no");
+
+ return info;
+}
- global_scsi_tpnt = tpnt;
+
+MODULE_AUTHOR("James Goodwin <jamesg@filanet.com>");
+MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver");
+MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME);
+MODULE_LICENSE("GPL");
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,26)
+#define PROC_SCSI_SBP2 PROC_SCSI_NOT_PRESENT /* What should I use? */
+static struct proc_dir_entry proc_scsi_sbp2scsi = {
+ low_ino: PROC_SCSI_SBP2,
+ namelen: SBP2_DEVICE_NAME_SIZE,
+ name: SBP2_DEVICE_NAME,
+ mode: S_IFDIR | S_IRUGO | S_IXUGO,
+ nlink: 2
+};
+#endif
+
+/* SCSI host template */
+static Scsi_Host_Template scsi_driver_template = {
+ name: "IEEE-1394 SBP-2 protocol driver",
+ info: sbp2scsi_info,
+ detect: sbp2scsi_detect,
+ queuecommand: sbp2scsi_queuecommand,
+ eh_abort_handler: sbp2scsi_abort,
+ eh_device_reset_handler:sbp2scsi_reset,
+ eh_bus_reset_handler: sbp2scsi_reset,
+ eh_host_reset_handler: sbp2scsi_reset,
+ bios_param: sbp2scsi_biosparam,
+ can_queue: SBP2SCSI_MAX_OUTSTANDING_CMDS,
+ this_id: -1,
+ sg_tablesize: SBP2_MAX_SG_ELEMENTS,
+ cmd_per_lun: SBP2SCSI_MAX_CMDS_PER_LUN,
+ use_clustering: SBP2_CLUSTERING,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ use_new_eh_code: TRUE,
+#endif
+ emulated: 1,
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,26)
- global_scsi_tpnt->proc_name = SBP2_DEVICE_NAME;
+ proc_name: SBP2_DEVICE_NAME,
+#else
+ proc_dir: &proc_scsi_sbp2scsi,
#endif
+};
+
+static int sbp2_module_init(void)
+{
+ SBP2_DEBUG("sbp2_module_init");
+
/*
* Module load option for force one command at a time
*/
if (serialize_io) {
SBP2_ERR("Driver forced to serialize I/O (serialize_io = 1)");
- global_scsi_tpnt->can_queue = 1;
- global_scsi_tpnt->cmd_per_lun = 1;
+ scsi_driver_template.can_queue = 1;
+ scsi_driver_template.cmd_per_lun = 1;
}
/*
- * Module load option to limit max size of requests from the scsi drivers
+ * Module load option to limit max size of requests from the
+ * scsi drivers
*/
if (no_large_packets) {
- SBP2_ERR("Driver forced to limit max transfer size (no_large_packets = 1)");
- global_scsi_tpnt->sg_tablesize = 0x1f;
- global_scsi_tpnt->use_clustering = DISABLE_CLUSTERING;
+ SBP2_ERR("Driver forced to limit max transfer size "
+ "(no_large_packets = 1)");
+ scsi_driver_template.sg_tablesize = 0x1f;
+ scsi_driver_template.use_clustering = DISABLE_CLUSTERING;
}
- if (mode_sense_hack) {
- SBP2_ERR("Mode sense emulation enabled (mode_sense_hack = 1)");
- }
-
- sbp2_init();
-
- if (!sbp2_host_count) {
- SBP2_ERR("Please load the lower level IEEE-1394 driver (e.g. ohci1394) before sbp2...");
+ /*
+ * Ideally we would register our scsi_driver_template with the
+ * scsi stack and after that register with the ieee1394 stack
+ * and process the add_host callbacks. However, the detect
+ * function in the scsi host template requires that we find at
+ * least one host, so we "nest" the registrations by calling
+ * sbp2_init from the detect function.
+ */
+ scsi_driver_template.module = THIS_MODULE;
+ if (SCSI_REGISTER_HOST(&scsi_driver_template) ||
+ !scsi_driver_template.present) {
+ SBP2_ERR("Please load the lower level IEEE-1394 driver "
+ "(e.g. ohci1394) before sbp2...");
sbp2_cleanup();
+ return -ENODEV;
}
- /*
- * Since we are returning this count, it means that sbp2 must be
- * loaded "after" the host adapter module...
- */
- return(sbp2_host_count);
+ return 0;
}
-/*
- * This function is called from sbp2_add_host, and is where we register
- * our scsi host
- */
-static void sbp2scsi_register_scsi_host(struct sbp2scsi_host_info *hi)
+static void __exit sbp2_module_exit(void)
{
- struct Scsi_Host *shpnt = NULL;
-
- SBP2_DEBUG("sbp2scsi_register_scsi_host");
- SBP2_DEBUG("sbp2scsi_host_info = %p", hi);
+ SBP2_DEBUG("sbp2_module_exit");
/*
- * Let's register with the scsi stack
+ * On module unload we unregister with the ieee1394 stack
+ * which results in remove_host callbacks for all ieee1394
+ * host controllers. In the callbacks we unregister the
+ * corresponding scsi hosts.
*/
- if (global_scsi_tpnt) {
-
- shpnt = scsi_register (global_scsi_tpnt, sizeof(void *));
-
- /*
- * If successful, save off a context (to be used when SCSI
- * commands are received)
- */
- if (shpnt) {
- shpnt->hostdata[0] = (unsigned long)hi;
- }
- }
-
- return;
-}
-
-/* Called when our module is released */
-static int sbp2scsi_release(struct Scsi_Host *host)
-{
- SBP2_DEBUG("sbp2scsi_release");
sbp2_cleanup();
- return(0);
-}
-/* Called for contents of procfs */
-static const char *sbp2scsi_info (struct Scsi_Host *host)
-{
- return "IEEE-1394 SBP-2 protocol driver";
-}
+ if (SCSI_UNREGISTER_HOST(&scsi_driver_template))
+ SBP2_ERR("sbp2_module_exit: couldn't unregister scsi driver");
-MODULE_AUTHOR("James Goodwin <jamesg@filanet.com>");
-MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver");
-MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME);
-MODULE_LICENSE("GPL");
-
-/* SCSI host template */
-static Scsi_Host_Template driver_template = {
- name: "IEEE1394 SBP-2",
- detect: sbp2scsi_detect,
- release: sbp2scsi_release,
- info: sbp2scsi_info,
- queuecommand: sbp2scsi_queuecommand,
- abort: sbp2scsi_abort,
- reset: sbp2scsi_reset,
- bios_param: sbp2scsi_biosparam,
- can_queue: SBP2SCSI_MAX_OUTSTANDING_CMDS,
- this_id: -1,
- sg_tablesize: SBP2_MAX_SG_ELEMENTS,
- cmd_per_lun: SBP2SCSI_MAX_CMDS_PER_LUN,
- use_clustering: SBP2_CLUSTERING,
- emulated: 1
-};
+}
-#include "../scsi/scsi_module.c"
+module_init(sbp2_module_init);
+module_exit(sbp2_module_exit);
#ifndef SBP2_H
#define SBP2_H
+/* Some compatibility code */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+#define SCSI_REGISTER_HOST(tmpl) scsi_register_module(MODULE_SCSI_HA, tmpl)
+#define SCSI_UNREGISTER_HOST(tmpl) scsi_unregister_module(MODULE_SCSI_HA, tmpl)
+#else
+#define SCSI_REGISTER_HOST(tmpl) scsi_register_host(tmpl)
+#define SCSI_UNREGISTER_HOST(tmpl) scsi_unregister_host(tmpl)
+#endif
+
#define SBP2_DEVICE_NAME "sbp2"
#define SBP2_DEVICE_NAME_SIZE 4
#define ORB_DIRECTION_READ_FROM_MEDIA 0x1
#define ORB_DIRECTION_NO_DATA_TRANSFER 0x2
+#define ORB_SET_NULL_PTR(value) ((value & 0x1) << 31)
#define ORB_SET_NOTIFY(value) ((value & 0x1) << 31)
#define ORB_SET_RQ_FMT(value) ((value & 0x3) << 29)
#define ORB_SET_NODE_ID(value) ((value & 0xffff) << 16)
#define SBP2_DEVICE_TYPE_AND_LUN_KEY 0x14
#define SBP2_FIRMWARE_REVISION_KEY 0x3c
+#define SBP2_DEVICE_TYPE(q) (((q) >> 16) & 0x1f)
+#define SBP2_DEVICE_LUN(q) ((q) & 0xffff)
+
#define SBP2_AGENT_STATE_OFFSET 0x00ULL
#define SBP2_AGENT_RESET_OFFSET 0x04ULL
#define SBP2_ORB_POINTER_OFFSET 0x08ULL
#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e
#define SBP2_SW_VERSION_ENTRY 0x00010483
-/*
- * Miscellaneous general config rom related defines
- */
-
-#define CONFIG_ROM_INITIAL_MEMORY_SPACE 0xfffff0000000ULL
-
-#define CONFIG_ROM_BASE_ADDRESS 0xfffff0000400ULL
-#define CONFIG_ROM_ROOT_DIR_BASE 0xfffff0000414ULL
-#define CONFIG_ROM_UNIT_DIRECTORY_OFFSET 0xfffff0000424ULL
#define SBP2_128KB_BROKEN_FIRMWARE 0xa0b800
#define SBP2_BROKEN_FIRMWARE_MAX_TRANSFER 0x20000
#endif
/*
- * SCSI direction table... since the scsi stack doesn't specify direction... =(
+ * SCSI direction table...
+ * (now used as a back-up in case the direction passed down from above is "unknown")
*
* DIN = IN data direction
* DOU = OUT data direction
};
+
+/* This is the two dma types we use for cmd_dma below */
+#define CMD_DMA_NONE 0x0
+#define CMD_DMA_PAGE 0x1
+#define CMD_DMA_SINGLE 0x2
+
/*
* Encapsulates all the info necessary for an outstanding command.
*/
struct sbp2_command_info {
struct list_head list;
- struct sbp2_command_orb command_orb;
- dma_addr_t command_orb_dma;
+ struct sbp2_command_orb command_orb ____cacheline_aligned;
+ dma_addr_t command_orb_dma ____cacheline_aligned;
Scsi_Cmnd *Current_SCpnt;
void (*Current_done)(Scsi_Cmnd *);
unsigned int linked;
/* Also need s/g structure for each sbp2 command */
- struct sbp2_unrestricted_page_table scatter_gather_element[SBP2_MAX_SG_ELEMENTS];
- dma_addr_t sge_dma;
+ struct sbp2_unrestricted_page_table scatter_gather_element[SBP2_MAX_SG_ELEMENTS] ____cacheline_aligned;
+ dma_addr_t sge_dma ____cacheline_aligned;
void *sge_buffer;
dma_addr_t cmd_dma;
int dma_type;
* Various sbp2 specific structures
*/
struct sbp2_command_orb *last_orb;
+ dma_addr_t last_orb_dma;
struct sbp2_login_orb *login_orb;
dma_addr_t login_orb_dma;
struct sbp2_login_response *login_response;
spinlock_t sbp2_command_lock;
spinlock_t sbp2_request_packet_lock;
+ /*
+ * This is the scsi host we register with the scsi mid level.
+ * We keep a reference to it here, so we can unregister it
+ * when the hpsb_host is removed.
+ */
+ struct Scsi_Host *scsi_host;
+
/*
* Lists keeping track of inuse/free sbp2_request_packets. These structures are
* used for sending out sbp2 command and agent reset packets. We initially create
unchar *scsi_cmd,
unsigned int scsi_use_sg,
unsigned int scsi_request_bufflen,
- void *scsi_request_buffer, int dma_dir);
+ void *scsi_request_buffer,
+ unsigned char scsi_dir);
static int sbp2_link_orb_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id,
struct sbp2_command_info *command);
static int sbp2_send_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id,
/*
* Scsi interface related prototypes
*/
-static const char *sbp2scsi_info (struct Scsi_Host *host);
static int sbp2scsi_detect (Scsi_Host_Template *tpnt);
+static const char *sbp2scsi_info (struct Scsi_Host *host);
void sbp2scsi_setup(char *str, int *ints);
static int sbp2scsi_biosparam (Scsi_Disk *disk, kdev_t dev, int geom[]);
static int sbp2scsi_abort (Scsi_Cmnd *SCpnt);
-static int sbp2scsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags);
+static int sbp2scsi_reset (Scsi_Cmnd *SCpnt);
static int sbp2scsi_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
static void sbp2scsi_complete_all_commands(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id,
u32 status);
static void sbp2scsi_complete_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id,
u32 scsi_status, Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
-static void sbp2scsi_register_scsi_host(struct sbp2scsi_host_info *hi);
#endif /* SBP2_H */
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/sched.h>
+#include <asm/segment.h>
#include <linux/types.h>
#include <linux/wrapper.h>
#include <linux/vmalloc.h>
+#include <linux/timex.h>
+#include <linux/mm.h>
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "ohci1394.h"
-#define VIDEO1394_MAJOR 172
#define ISO_CHANNELS 64
#define ISO_RECEIVE 0
#define ISO_TRANSMIT 1
struct dma_iso_ctx {
struct ti_ohci *ohci;
+ int type; /* ISO_TRANSMIT or ISO_RECEIVE */
int ctx;
int channel;
int last_buffer;
struct dma_cmd **ir_prg;
struct it_dma_prg **it_prg;
unsigned int *buffer_status;
+ struct timeval *buffer_time; /* time when the buffer was received */
unsigned int *last_used_cmd; /* For ISO Transmit with
variable sized packets only ! */
int ctrlClear;
int cmdPtr;
int ctxMatch;
wait_queue_head_t waitq;
- spinlock_t lock;
- unsigned int syt_offset;
+ spinlock_t lock;
+ unsigned int syt_offset;
int flags;
};
printk(level "video1394_%d: " fmt "\n" , card , ## args)
static void irq_handler(int card, quadlet_t isoRecvIntEvent,
- quadlet_t isoXmitIntEvent);
+ quadlet_t isoXmitIntEvent, void *data);
static LIST_HEAD(video1394_cards);
static spinlock_t video1394_cards_lock = SPIN_LOCK_UNLOCKED;
static devfs_handle_t devfs_handle;
static struct hpsb_highlevel *hl_handle = NULL;
-static struct video_template video_tmpl = { irq_handler };
-
/* Code taken from bttv.c */
/*******************************/
{
int i;
struct ti_ohci *ohci;
+ unsigned long *usage;
if ((*d)==NULL) return -1;
if ((*d)->buffer_status)
kfree((*d)->buffer_status);
+ if ((*d)->buffer_time)
+ kfree((*d)->buffer_time);
if ((*d)->last_used_cmd)
kfree((*d)->last_used_cmd);
if ((*d)->next_buffer)
kfree((*d)->next_buffer);
+ usage = ((*d)->type == ISO_RECEIVE) ? &ohci->ir_ctx_usage :
+ &ohci->it_ctx_usage;
+
+ /* clear the ISO context usage bit */
+ clear_bit((*d)->ctx, usage);
+
kfree(*d);
*d = NULL;
}
static struct dma_iso_ctx *
-alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int ctx, int num_desc,
+alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
int buf_size, int channel, unsigned int packet_size)
{
struct dma_iso_ctx *d=NULL;
int i;
+ unsigned long *usage = (type == ISO_RECEIVE) ? &ohci->ir_ctx_usage :
+ &ohci->it_ctx_usage;
+
+ /* try to claim the ISO context usage bit */
+ for (i = 0; i < ohci->nb_iso_rcv_ctx; i++) {
+ if (!test_and_set_bit(i, usage)) {
+ PRINT(KERN_ERR, ohci->id, "Free iso ctx %d found", i);
+ break;
+ }
+ }
+
+ if (i == ohci->nb_iso_rcv_ctx) {
+ PRINT(KERN_ERR, ohci->id, "No DMA contexts available");
+ return NULL;
+ }
+
d = (struct dma_iso_ctx *)kmalloc(sizeof(struct dma_iso_ctx),
GFP_KERNEL);
if (d==NULL) {
memset(d, 0, sizeof(struct dma_iso_ctx));
d->ohci = (void *)ohci;
- d->ctx = ctx;
+ d->type = type;
+ d->ctx = i;
d->channel = channel;
d->num_desc = num_desc;
d->frame_size = buf_size;
d->buffer_status = kmalloc(d->num_desc * sizeof(unsigned int),
GFP_KERNEL);
+ d->buffer_time = kmalloc(d->num_desc * sizeof(struct timeval),
+ GFP_KERNEL);
d->last_used_cmd = kmalloc(d->num_desc * sizeof(unsigned int),
GFP_KERNEL);
d->next_buffer = kmalloc(d->num_desc * sizeof(int),
free_dma_iso_ctx(&d);
return NULL;
}
+ if (d->buffer_time == NULL) {
+ PRINT(KERN_ERR, ohci->id, "Failed to allocate buffer_time");
+ free_dma_iso_ctx(&d);
+ return NULL;
+ }
if (d->last_used_cmd == NULL) {
PRINT(KERN_ERR, ohci->id, "Failed to allocate last_used_cmd");
free_dma_iso_ctx(&d);
return NULL;
}
memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int));
+ memset(d->buffer_time, 0, d->num_desc * sizeof(struct timeval));
memset(d->last_used_cmd, 0, d->num_desc * sizeof(unsigned int));
memset(d->next_buffer, -1, d->num_desc * sizeof(int));
}
/* find which context is listening to this channel */
-int ir_ctx_listening(struct video_card *video, int channel)
+static struct dma_iso_ctx **
+ir_ctx_listening(struct video_card *video, int channel)
{
int i;
struct ti_ohci *ohci = video->ohci;
- for (i=0;i<ohci->nb_iso_rcv_ctx-1;i++)
- if (video->ir_context[i]) {
- if (video->ir_context[i]->channel==channel)
- return i;
- }
+ for (i = 0; i < ohci->nb_iso_rcv_ctx-1; i++)
+ if (video->ir_context[i] &&
+ video->ir_context[i]->channel == channel)
+ return &video->ir_context[i];
PRINT(KERN_ERR, ohci->id, "No iso context is listening to channel %d",
channel);
- return -1;
+ return NULL;
}
-int it_ctx_talking(struct video_card *video, int channel)
+static struct dma_iso_ctx **
+it_ctx_talking(struct video_card *video, int channel)
{
int i;
struct ti_ohci *ohci = video->ohci;
- for (i=0;i<ohci->nb_iso_xmit_ctx;i++)
- if (video->it_context[i]) {
- if (video->it_context[i]->channel==channel)
- return i;
- }
+ for (i = 0; i < ohci->nb_iso_xmit_ctx; i++)
+ if (video->it_context[i] &&
+ video->it_context[i]->channel==channel)
+ return &video->ir_context[i];
PRINT(KERN_ERR, ohci->id, "No iso context is talking to channel %d",
channel);
- return -1;
+ return NULL;
}
int wakeup_dma_ir_ctx(struct ti_ohci *ohci, struct dma_iso_ctx *d)
if (d->ir_prg[i][d->nb_cmd-1].status & 0xFFFF0000) {
reset_ir_status(d, i);
d->buffer_status[i] = VIDEO1394_BUFFER_READY;
+ get_fast_time(&d->buffer_time[i]);
}
}
spin_unlock(&d->lock);
struct video_card *p;
list_for_each(lh, &video1394_cards) {
p = list_entry(lh, struct video_card, list);
- if (p->id == MINOR(inode->i_rdev)) {
+ if (p->id == ieee1394_file_to_instance(file)) {
video = p;
ohci = video->ohci;
break;
spin_unlock_irqrestore(&video1394_cards_lock, flags);
if (video == NULL) {
- PRINT_G(KERN_ERR, __FUNCTION__": Unknown video card for minor %d", MINOR(inode->i_rdev));
+ PRINT_G(KERN_ERR, "%s: Unknown video card for minor %d",
+ __FUNCTION__, ieee1394_file_to_instance(file));
return -EFAULT;
}
if(copy_from_user(&v, (void *)arg, sizeof(v)))
return -EFAULT;
+
+ /* if channel < 0, find lowest available one */
+ if (v.channel < 0) {
+ mask = (u64)0x1;
+ for (i=0; i<ISO_CHANNELS; i++) {
+ if (!(ohci->ISO_channel_usage & mask)) {
+ v.channel = i;
+ PRINT(KERN_INFO, ohci->id, "Found free channel %d", i);
+ break;
+ }
+ mask = mask << 1;
+ }
+ }
+
if (v.channel<0 || v.channel>(ISO_CHANNELS-1)) {
PRINT(KERN_ERR, ohci->id,
- "Iso channel %d out of bound", v.channel);
+ "Iso channel %d out of bounds", v.channel);
return -EFAULT;
}
mask = (u64)0x1<<v.channel;
}
video->ir_context[i] =
- alloc_dma_iso_ctx(ohci, ISO_RECEIVE, i+1,
+ alloc_dma_iso_ctx(ohci, ISO_RECEIVE,
v.nb_buffers, v.buf_size,
v.channel, 0);
v.buf_size = video->ir_context[i]->buf_size;
PRINT(KERN_INFO, ohci->id,
- "iso context %d listen on channel %d", i+1,
- v.channel);
+ "iso context %d listen on channel %d",
+ video->current_ctx->ctx, v.channel);
}
else {
/* find a free iso transmit context */
}
video->it_context[i] =
- alloc_dma_iso_ctx(ohci, ISO_TRANSMIT, i,
+ alloc_dma_iso_ctx(ohci, ISO_TRANSMIT,
v.nb_buffers, v.buf_size,
v.channel, v.packet_size);
{
int channel;
u64 mask;
- int i;
if(copy_from_user(&channel, (void *)arg, sizeof(int)))
return -EFAULT;
ohci->ISO_channel_usage &= ~mask;
if (cmd == VIDEO1394_UNLISTEN_CHANNEL) {
- i = ir_ctx_listening(video, channel);
- if (i<0) return -EFAULT;
-
- free_dma_iso_ctx(&video->ir_context[i]);
-
+ struct dma_iso_ctx **d;
+ d = ir_ctx_listening(video, channel);
+ if (d == NULL) return -EFAULT;
PRINT(KERN_INFO, ohci->id,
"Iso context %d stop listening on channel %d",
- i+1, channel);
+ (*d)->ctx, channel);
+ free_dma_iso_ctx(d);
}
else {
- i = it_ctx_talking(video, channel);
- if (i<0) return -EFAULT;
-
- free_dma_iso_ctx(&video->it_context[i]);
-
+ struct dma_iso_ctx **d;
+ d = it_ctx_talking(video, channel);
+ if (d == NULL) return -EFAULT;
PRINT(KERN_INFO, ohci->id,
"Iso context %d stop talking on channel %d",
- i, channel);
+ (*d)->ctx, channel);
+ free_dma_iso_ctx(d);
+
}
return 0;
case VIDEO1394_LISTEN_QUEUE_BUFFER:
{
struct video1394_wait v;
- struct dma_iso_ctx *d;
- int i;
+ struct dma_iso_ctx *d, **dd;
if(copy_from_user(&v, (void *)arg, sizeof(v)))
return -EFAULT;
- i = ir_ctx_listening(video, v.channel);
- if (i<0) return -EFAULT;
- d = video->ir_context[i];
+ dd = ir_ctx_listening(video, v.channel);
+ if (dd == NULL) return -EFAULT;
+ d = *dd;
if ((v.buffer<0) || (v.buffer>d->num_desc)) {
PRINT(KERN_ERR, ohci->id,
}
case VIDEO1394_LISTEN_WAIT_BUFFER:
+ case VIDEO1394_LISTEN_POLL_BUFFER:
{
struct video1394_wait v;
- struct dma_iso_ctx *d;
+ struct dma_iso_ctx *d, **dd;
int i;
if(copy_from_user(&v, (void *)arg, sizeof(v)))
return -EFAULT;
- i = ir_ctx_listening(video, v.channel);
- if (i<0) return -EFAULT;
- d = video->ir_context[i];
+ dd = ir_ctx_listening(video, v.channel);
+ if (dd==NULL) return -EFAULT;
+ d = *dd;
if ((v.buffer<0) || (v.buffer>d->num_desc)) {
PRINT(KERN_ERR, ohci->id,
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
break;
case VIDEO1394_BUFFER_QUEUED:
+ if (cmd == VIDEO1394_LISTEN_POLL_BUFFER) {
+ /* for polling, return error code EINTR */
+ spin_unlock_irqrestore(&d->lock, flags);
+ return -EINTR;
+ }
+
#if 1
while(d->buffer_status[v.buffer]!=
VIDEO1394_BUFFER_READY) {
return -EFAULT;
}
+ /* set time of buffer */
+ v.filltime = d->buffer_time[v.buffer];
+// printk("Buffer %d time %d\n", v.buffer, (d->buffer_time[v.buffer]).tv_usec);
+
/*
* Look ahead to see how many more buffers have been received
*/
{
struct video1394_wait v;
struct video1394_queue_variable qv;
- struct dma_iso_ctx *d;
- int i;
+ struct dma_iso_ctx *d, **dd;
if(copy_from_user(&v, (void *)arg, sizeof(v)))
return -EFAULT;
- i = it_ctx_talking(video, v.channel);
- if (i<0) return -EFAULT;
- d = video->it_context[i];
+ dd = it_ctx_talking(video, v.channel);
+ if (dd == NULL) return -EFAULT;
+ d = *dd;
if ((v.buffer<0) || (v.buffer>d->num_desc)) {
PRINT(KERN_ERR, ohci->id,
case VIDEO1394_TALK_WAIT_BUFFER:
{
struct video1394_wait v;
- struct dma_iso_ctx *d;
- int i;
+ struct dma_iso_ctx *d, **dd;
if(copy_from_user(&v, (void *)arg, sizeof(v)))
return -EFAULT;
- i = it_ctx_talking(video, v.channel);
- if (i<0) return -EFAULT;
- d = video->it_context[i];
+ dd = it_ctx_talking(video, v.channel);
+ if (dd == NULL) return -EFAULT;
+ d = *dd;
if ((v.buffer<0) || (v.buffer>d->num_desc)) {
PRINT(KERN_ERR, ohci->id,
struct video_card *p;
list_for_each(lh, &video1394_cards) {
p = list_entry(lh, struct video_card, list);
- if (p->id == MINOR(file->f_dentry->d_inode->i_rdev)) {
+ if (p->id == ieee1394_file_to_instance(file)) {
video = p;
break;
}
spin_unlock_irqrestore(&video1394_cards_lock, flags);
if (video == NULL) {
- PRINT_G(KERN_ERR, __FUNCTION__": Unknown video card for minor %d",
- MINOR(file->f_dentry->d_inode->i_rdev));
+ PRINT_G(KERN_ERR, "%s: Unknown video card for minor %d",
+ __FUNCTION__, ieee1394_file_to_instance(file));
return -EFAULT;
}
static int video1394_open(struct inode *inode, struct file *file)
{
- int i = MINOR(inode->i_rdev);
+ int i = ieee1394_file_to_instance(file);
unsigned long flags;
struct video_card *video = NULL;
struct list_head *lh;
struct video_card *p;
list_for_each(lh, &video1394_cards) {
p = list_entry(lh, struct video_card, list);
- if (p->id == MINOR(inode->i_rdev)) {
+ if (p->id == ieee1394_file_to_instance(file)) {
video = p;
break;
}
spin_unlock_irqrestore(&video1394_cards_lock, flags);
if (video == NULL) {
- PRINT_G(KERN_ERR, __FUNCTION__": Unknown device for minor %d",
- MINOR(inode->i_rdev));
+ PRINT_G(KERN_ERR, "%s: Unknown device for minor %d",
+ __FUNCTION__, ieee1394_file_to_instance(file));
return 1;
}
mask = (u64)0x1<<video->ir_context[i]->channel;
if (!(ohci->ISO_channel_usage & mask))
PRINT(KERN_ERR, ohci->id,
- "Channel %d is not being used",
+ "On release: Channel %d is not being used",
video->ir_context[i]->channel);
else
ohci->ISO_channel_usage &= ~mask;
PRINT(KERN_INFO, ohci->id,
"Iso receive context %d stop listening "
- "on channel %d", i+1,
+ "on channel %d", video->ir_context[i]->ctx,
video->ir_context[i]->channel);
free_dma_iso_ctx(&video->ir_context[i]);
}
ohci->ISO_channel_usage &= ~mask;
PRINT(KERN_INFO, ohci->id,
"Iso transmit context %d stop talking "
- "on channel %d", i+1,
+ "on channel %d", video->it_context[i]->ctx,
video->it_context[i]->channel);
free_dma_iso_ctx(&video->it_context[i]);
}
}
static void irq_handler(int card, quadlet_t isoRecvIntEvent,
- quadlet_t isoXmitIntEvent)
+ quadlet_t isoXmitIntEvent, void *data)
{
int i;
- unsigned long flags;
- struct video_card *video = NULL;
- struct list_head *lh;
-
- spin_lock_irqsave(&video1394_cards_lock, flags);
- if (!list_empty(&video1394_cards)) {
- struct video_card *p;
- list_for_each(lh, &video1394_cards) {
- p = list_entry(lh, struct video_card, list);
- if (p->id == card) {
- video = p;
- break;
- }
- }
- }
- spin_unlock_irqrestore(&video1394_cards_lock, flags);
+ struct video_card *video = (struct video_card*) data;
if (video == NULL) {
- PRINT_G(KERN_ERR, __FUNCTION__": Unknown card number %d!!",
- card);
+ PRINT_G(KERN_ERR, "%s: Unknown card number %d",
+ __FUNCTION__, card);
return;
}
DBGMSG(card, "Iso event Recv: %08x Xmit: %08x",
isoRecvIntEvent, isoXmitIntEvent);
- for (i=0;i<video->ohci->nb_iso_rcv_ctx-1;i++)
- if (isoRecvIntEvent & (1<<(i+1)))
- wakeup_dma_ir_ctx(video->ohci,
- video->ir_context[i]);
+ for (i = 0; i < video->ohci->nb_iso_rcv_ctx-1; i++)
+ if (video->ir_context[i] != NULL &&
+ isoRecvIntEvent & (1<<(video->ir_context[i]->ctx)))
+ wakeup_dma_ir_ctx(video->ohci, video->ir_context[i]);
- for (i=0;i<video->ohci->nb_iso_xmit_ctx;i++)
- if (isoXmitIntEvent & (1<<i))
- wakeup_dma_it_ctx(video->ohci,
- video->it_context[i]);
+ for (i = 0; i < video->ohci->nb_iso_xmit_ctx; i++)
+ if (video->it_context[i] != NULL &&
+ isoXmitIntEvent & (1<<(video->it_context[i]->ctx)))
+ wakeup_dma_it_ctx(video->ohci, video->it_context[i]);
}
static struct file_operations video1394_fops=
list_add_tail(&video->list, &video1394_cards);
spin_unlock_irqrestore(&video1394_cards_lock, flags);
- if (ohci1394_register_video(ohci, &video_tmpl)<0) {
- PRINT(KERN_ERR, ohci->id, "Register_video failed");
+ if (ohci1394_hook_irq(ohci, irq_handler, (void*) video) != 0) {
+ PRINT(KERN_ERR, ohci->id, "ohci1394_hook_irq() failed");
return -1;
}
sprintf(name, "%d", video->id);
video->devfs = devfs_register(devfs_handle, name,
DEVFS_FL_AUTO_OWNER,
- VIDEO1394_MAJOR, 0,
+ IEEE1394_MAJOR,
+ IEEE1394_MINOR_BLOCK_VIDEO1394*16+video->id,
S_IFCHR | S_IRUSR | S_IWUSR,
&video1394_fops, NULL);
{
int i;
- ohci1394_unregister_video(video->ohci, &video_tmpl);
+ ohci1394_unhook_irq(video->ohci, irq_handler, (void*) video);
devfs_unregister(video->devfs);
{
struct ti_ohci *ohci;
unsigned long flags;
- struct list_head *lh;
+ struct list_head *lh, *next;
+ struct video_card *p;
/* We only work with the OHCI-1394 driver */
- if (strcmp(host->template->name, OHCI1394_DRIVER_NAME))
+ if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
return;
ohci = (struct ti_ohci *)host->hostdata;
spin_lock_irqsave(&video1394_cards_lock, flags);
- if (!list_empty(&video1394_cards)) {
- struct video_card *p;
- list_for_each(lh, &video1394_cards) {
- p = list_entry(lh, struct video_card, list);
- if (p ->ohci == ohci) {
- remove_card(p);
- break;
- }
+ list_for_each_safe(lh, next, &video1394_cards) {
+ p = list_entry(lh, struct video_card, list);
+ if (p->ohci == ohci) {
+ remove_card(p);
+ break;
}
}
spin_unlock_irqrestore(&video1394_cards_lock, flags);
struct ti_ohci *ohci;
/* We only work with the OHCI-1394 driver */
- if (strcmp(host->template->name, OHCI1394_DRIVER_NAME))
+ if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
return;
ohci = (struct ti_ohci *)host->hostdata;
hpsb_unregister_highlevel (hl_handle);
devfs_unregister(devfs_handle);
- devfs_unregister_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME);
-
- PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module\n");
+ ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394);
+
+ PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module");
}
static int __init video1394_init_module (void)
{
- if (devfs_register_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME,
- &video1394_fops)) {
- PRINT_G(KERN_ERR, "video1394: unable to get major %d\n",
- VIDEO1394_MAJOR);
- return -EIO;
- }
+ if (ieee1394_register_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394,
+ THIS_MODULE, &video1394_fops)) {
+ PRINT_G(KERN_ERR, "video1394: unable to get minor device block");
+ return -EIO;
+ }
+
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
devfs_handle = devfs_mk_dir(NULL, VIDEO1394_DRIVER_NAME,
strlen(VIDEO1394_DRIVER_NAME), NULL);
if (hl_handle == NULL) {
PRINT_G(KERN_ERR, "No more memory for driver\n");
devfs_unregister(devfs_handle);
- devfs_unregister_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME);
+ ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394);
return -ENOMEM;
}
+ PRINT_G(KERN_INFO, "Installed " VIDEO1394_DRIVER_NAME " module");
return 0;
}
VIDEO1394_LISTEN_CHANNEL = 0,
VIDEO1394_UNLISTEN_CHANNEL,
VIDEO1394_LISTEN_QUEUE_BUFFER,
- VIDEO1394_LISTEN_WAIT_BUFFER,
+ VIDEO1394_LISTEN_WAIT_BUFFER, // wait until buffer is ready
VIDEO1394_TALK_CHANNEL,
VIDEO1394_UNTALK_CHANNEL,
VIDEO1394_TALK_QUEUE_BUFFER,
- VIDEO1394_TALK_WAIT_BUFFER
+ VIDEO1394_TALK_WAIT_BUFFER,
+ VIDEO1394_LISTEN_POLL_BUFFER // return immediately with -EINTR if not ready
};
#define VIDEO1394_SYNC_FRAMES 0x00000001
#define VIDEO1394_VARIABLE_PACKET_SIZE 0x00000004
struct video1394_mmap {
- unsigned int channel;
+ int channel; /* -1 to find an open channel in LISTEN/TALK */
unsigned int sync_tag;
unsigned int nb_buffers;
unsigned int buf_size;
struct video1394_wait {
unsigned int channel;
unsigned int buffer;
+ struct timeval filltime; /* time of buffer full */
};
dep_tristate 'ELSA PCMCIA MicroLink cards' CONFIG_HISAX_ELSA_CS $CONFIG_PCMCIA
dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)' CONFIG_HISAX_ST5481 $CONFIG_HISAX $CONFIG_USB $CONFIG_EXPERIMENTAL
dep_tristate 'AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP $CONFIG_HISAX $CONFIG_EXPERIMENTAL
+ dep_tristate 'AVM Fritz!Card classic support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_CLASSIC $CONFIG_HISAX $CONFIG_EXPERIMENTAL
fi
endmenu
# Objects that export symbols.
-export-objs := config.o fsm.o hisax_isac.o
+export-objs := config.o fsm.o hisax_isac.o hisax_hscx.o
# Multipart objects.
obj-$(CONFIG_HISAX_ELSA_CS) += elsa_cs.o
obj-$(CONFIG_HISAX_ST5481) += hisax_st5481.o
obj-$(CONFIG_HISAX_FRITZ_PCIPNP) += hisax_isac.o hisax_fcpcipnp.o
+obj-$(CONFIG_HISAX_FRITZ_CLASSIC) += hisax_isac.o hisax_hscx.o hisax_fcclassic.o
CERT := $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
CFLAGS_cert.o := -DCERTIFICATION=$(CERT)
case (PH_PULL | REQUEST):
if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
modehdlc(st->l1.bcs, 0, st->l1.bc);
- st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
break;
}
}
if (open_hdlcstate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
- st->l2.l2l1 = hdlc_l2l1;
+ st->l1.l2l1 = hdlc_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
}
}
-
/*
* Dial out
*/
link_debug(chanp, 0, "STAT_DCONN");
HL_LL(chanp, ISDN_STAT_DCONN);
init_b_st(chanp, 0);
- chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+ L4L3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
}
static void
lli_init_bchan_out(fi, event, arg);
} else {
FsmChangeState(fi, ST_OUT_DIAL);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | REQUEST, chanp);
+ L4L3(chanp->d_st, CC_SETUP | REQUEST, chanp);
}
}
lli_init_bchan_out(fi, event, arg);
} else {
FsmChangeState(fi, ST_OUT_DIAL);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_RESUME | REQUEST, chanp);
+ L4L3(chanp->d_st, CC_RESUME | REQUEST, chanp);
}
}
case 1: /* OK, someone likes this call */
FsmDelTimer(&chanp->drel_timer, 61);
FsmChangeState(fi, ST_IN_ALERT_SENT);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
break;
case 5: /* direct redirect */
case 4: /* Proceeding desired */
FsmDelTimer(&chanp->drel_timer, 61);
FsmChangeState(fi, ST_IN_PROCEED_SEND);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc);
if (ret == 5) {
memcpy(&chanp->setup, &ic.parm.setup, sizeof(setup_parm));
- chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
}
break;
case 2: /* Rejecting Call */
break;
case 3: /* incomplete number */
FsmDelTimer(&chanp->drel_timer, 61);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_MORE_INFO | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_MORE_INFO | REQUEST, chanp->proc);
break;
case 0: /* OK, nobody likes this call */
default: /* statcallb problems */
- chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
FsmChangeState(fi, ST_NULL);
break;
}
} else {
- chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
}
}
struct Channel *chanp = fi->userdata;
FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+ L4L3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
}
static void
struct Channel *chanp = fi->userdata;
FsmChangeState(fi, ST_IN_ALERT_SENT);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
}
static void
{
struct Channel *chanp = fi->userdata;
- chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
}
static void
chanp->l2_active_protocol = chanp->l2_protocol;
chanp->incoming = !0;
init_b_st(chanp, !0);
- chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+ L4L3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
}
static void
} else {
FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
#ifdef WANT_ALERT
- chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
#endif
- chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+ L4L3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
}
}
{
struct Channel *chanp = fi->userdata;
- chanp->d_st->lli.l4l3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc);
}
/* Call clearing */
FsmChangeState(fi, ST_WAIT_DRELEASE);
if (chanp->proc)
chanp->proc->para.cause = 0x10; /* Normal Call Clearing */
- chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
- chanp->proc);
+ L4L3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
}
}
FsmChangeState(fi, ST_WAIT_DRELEASE);
if (chanp->proc)
chanp->proc->para.cause = 0x15; /* Call Rejected */
- chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
- chanp->proc);
+ L4L3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
}
}
#ifndef ALERT_REJECT
if (chanp->proc)
chanp->proc->para.cause = 0x15; /* Call Rejected */
- chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc);
lli_dhup_close(fi, event, arg);
#else
FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63);
FsmChangeState(fi, ST_IN_ALERT_SENT);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+ L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
#endif
}
chanp->data_open = 0;
FsmChangeState(fi, ST_WAIT_BRELEASE);
- chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+ L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
}
static void
chanp->data_open = 0;
FsmChangeState(fi, ST_WAIT_BREL_DISC);
- chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+ L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
}
struct Channel *chanp = fi->userdata;
chanp->data_open = 0;
- chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+ L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
lli_bhup_dhup(fi, event, arg);
}
lli_leased_hup(fi, chanp);
} else {
FsmChangeState(fi, ST_WAIT_D_REL_CNF);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST,
- chanp->proc);
+ L4L3(chanp->d_st, CC_RELEASE | REQUEST, chanp->proc);
}
}
struct Channel *chanp = fi->userdata;
chanp->data_open = 0;
- chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+ L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
lli_bhup_fail(fi, event, arg);
}
if (pr == (CC_SETUP | INDICATION)) {
if (!(chanp = selectfreechannel(pc->st, pc->para.bchannel))) {
pc->para.cause = 0x11; /* User busy */
- pc->st->lli.l4l3(pc->st, CC_REJECT | REQUEST, pc);
+ L4L3(pc->st, CC_REJECT | REQUEST, pc);
} else {
chanp->proc = pc;
pc->chan = chanp;
if (!*stp)
return -ENOMEM;
(*stp)->next = NULL;
- (*stp)->l1.l1l2 = dummy_pstack;
(*stp)->l1.l1hw = dummy_pstack;
(*stp)->l1.l1tei = dummy_pstack;
+ (*stp)->l1.l2l1 = dummy_pstack;
+ (*stp)->l2.l1l2 = dummy_pstack;
(*stp)->l2.l2tei = dummy_pstack;
- (*stp)->l2.l2l1 = dummy_pstack;
- (*stp)->l2.l2l3 = dummy_pstack;
- (*stp)->l3.l3l2 = dummy_pstack;
+ (*stp)->l2.l3l2 = dummy_pstack;
+ (*stp)->l3.l2l3 = dummy_pstack;
(*stp)->l3.l3ml3 = dummy_pstack;
- (*stp)->l3.l3l4 = dummy_pstack;
- (*stp)->lli.l4l3 = dummy_pstack;
+ (*stp)->l3.l4l3 = dummy_pstack;
+ (*stp)->lli.l3l4 = dummy_pstack;
(*stp)->ma.layer = dummy_pstack;
return 0;
}
setstack_l3dc(st, chanp);
st->lli.userdata = chanp;
st->lli.l2writewakeup = NULL;
- st->l3.l3l4 = dchan_l3l4;
+ st->lli.l3l4 = dchan_l3l4;
return 0;
}
printk(KERN_INFO "HiSax: MAX_WAITING_CALLS added\n");
if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) {
printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
- csta->channel->d_st->lli.l4l3(csta->channel->d_st,
- DL_ESTABLISH | REQUEST, NULL);
+ L4L3(csta->channel->d_st, DL_ESTABLISH | REQUEST, NULL);
}
return (0);
}
sprintf(tmp, "Ch%d X.75", chanp->chan);
setstack_isdnl2(st, tmp);
setstack_l3bc(st, chanp);
- st->l2.l2l3 = lldata_handler;
+ st->l3.l2l3 = lldata_handler;
st->lli.userdata = chanp;
st->lli.l1writewakeup = NULL;
st->lli.l2writewakeup = ll_writewakeup;
case (ISDN_PROTO_L2_TRANS):
case (ISDN_PROTO_L2_MODEM):
case (ISDN_PROTO_L2_FAX):
- st->l1.l1l2 = lltrans_handler;
+ st->l2.l1l2 = lltrans_handler;
st->lli.userdata = chanp;
st->lli.l1writewakeup = ll_writewakeup;
setstack_transl2(st);
dev_kfree_skb(skb);
break;
case (DL_ESTABLISH | REQUEST):
- st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+ L2L1(st, PH_ACTIVATE | REQUEST, NULL);
break;
case (DL_RELEASE | REQUEST):
break;
HiSax_putstatus(csta, "Card",
"%d channel %d set leased mode\n",
csta->cardnr + 1, num + 1);
- chanp->d_st->l1.l1l2 = leased_l1l2;
- chanp->d_st->lli.l4l3 = leased_l4l3;
- chanp->d_st->lli.l4l3(chanp->d_st,
- DL_ESTABLISH | REQUEST, NULL);
+ chanp->d_st->l2.l1l2 = leased_l1l2;
+ chanp->d_st->l3.l4l3 = leased_l4l3;
+ L4L3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
}
break;
case (6): /* set B-channel test loop */
num = *(unsigned int *) ic->parm.num;
if (csta->stlist)
- csta->stlist->l2.l2l1(csta->stlist,
+ csta->stlist->l1.l2l1(csta->stlist,
PH_TESTLOOP | REQUEST, (void *) (long)num);
break;
case (7): /* set card in PTP mode */
HiSax_putstatus(csta, "set card ", "in PTP mode");
printk(KERN_DEBUG "HiSax: set card in PTP mode\n");
printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
- csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st,
- DL_ESTABLISH | REQUEST, NULL);
+ L4L3(csta->channel[0].d_st, DL_ESTABLISH | REQUEST, NULL);
} else {
test_and_clear_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
test_and_clear_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n",
num);
}
- chanp->d_st->lli.l4l3(chanp->d_st,
- DL_ESTABLISH | REQUEST, NULL);
+ L4L3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
break;
#ifdef MODULE
case (55):
case (ISDN_CMD_PROT_IO):
for (st = csta->stlist; st; st = st->next)
if (st->protocol == (ic->arg & 0xFF))
- return(st->lli.l4l3_proto(st, ic));
+ return(st->l3.l4l3_proto(st, ic));
return(-EINVAL);
break;
default:
if (!ack)
nskb->pkt_type = PACKET_NOACK;
if (chanp->l2_active_protocol == ISDN_PROTO_L2_X75I)
- st->l3.l3l2(st, DL_DATA | REQUEST, nskb);
+ L3L2(st, DL_DATA | REQUEST, nskb);
else {
chanp->bcs->tx_cnt += len;
- st->l2.l2l1(st, PH_DATA | REQUEST, nskb);
+ st->l1.l2l1(st, PH_DATA | REQUEST, nskb);
}
dev_kfree_skb(skb);
} else
cs->hw.hisax_d_if = hisax_d_if;
cs->cardmsg = hisax_cardmsg;
cs->tqueue.routine = (void *) (void *) hisax_bh;
- cs->channel[0].d_st->l2.l2l1 = hisax_d_l2l1;
+ cs->channel[0].d_st->l1.l2l1 = hisax_d_l2l1;
for (i = 0; i < 2; i++) {
cs->bcs[i].BC_SetStack = hisax_bc_setstack;
cs->bcs[i].BC_Close = hisax_bc_close;
else
pr = PH_DEACTIVATE | INDICATION;
for (st = cs->stlist; st; st = st->next)
- st->l1.l1l2(st, pr, NULL);
+ L1L2(st, pr, NULL);
}
}
clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
for (st = cs->stlist; st; st = st->next) {
if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) {
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
break;
}
}
// FIXME use isdnl1?
switch (pr) {
case PH_ACTIVATE | INDICATION:
- st->l1.l1l2(st, pr, NULL);
+ L1L2(st, pr, NULL);
break;
case PH_DEACTIVATE | INDICATION:
- st->l1.l1l2(st, pr, NULL);
+ L1L2(st, pr, NULL);
bcs->hw.b_if = NULL;
break;
case PH_DATA | INDICATION:
}
clear_bit(BC_FLG_BUSY, &bcs->Flag);
if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) {
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
}
break;
default:
break;
case PH_PULL | REQUEST:
if (!test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
else
set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
break;
case PH_PULL | REQUEST:
if (!test_bit(BC_FLG_BUSY, &bcs->Flag))
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
else
set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
hisax_d_if->b_if[st->l1.bc]->bcs = bcs;
st->l1.bcs = bcs;
- st->l2.l2l1 = hisax_b_l2l1;
+ st->l1.l2l1 = hisax_b_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
}
} else if (pr == (PH_ACTIVATE | REQUEST)) {
test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
- st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
set_arcofi(st->l1.bcs->cs, st->l1.bc);
mstartup(st->l1.bcs->cs);
modem_set_dial(st->l1.bcs->cs, test_bit(FLG_ORIG, &st->l2.flag));
case L1_MODE_TRANS:
if (open_hscxstate(st->l1.hardware, bcs))
return (-1);
- st->l2.l2l1 = hscx_l2l1;
+ st->l1.l2l1 = hscx_l2l1;
break;
case L1_MODE_MODEM:
bcs->mode = L1_MODE_MODEM;
bcs->hw.hscx.rcvidx = 0;
bcs->tx_cnt = 0;
bcs->cs->hw.elsa.bcs = bcs;
- st->l2.l2l1 = modem_l2l1;
+ st->l1.l2l1 = modem_l2l1;
break;
}
st->l1.bcs = bcs;
case (PH_PULL | REQUEST):
if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
mode_2bs0(st->l1.bcs, 0, st->l1.bc);
- st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
break;
}
}
if (open_hfcstate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
- st->l2.l2l1 = hfc_l2l1;
+ st->l1.l2l1 = hfc_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
#endif
if (!cs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
case (PH_PULL | REQUEST):
if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
mode_hfc(st->l1.bcs, 0, st->l1.bc);
- st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
break;
}
}
if (open_hfcstate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
- st->l2.l2l1 = hfc_l2l1;
+ st->l1.l2l1 = hfc_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
st->l1.l1hw(st, pr, arg);
break;
case (PH_ACTIVATE | REQUEST):
- st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
break;
case (PH_TESTLOOP | REQUEST):
if (1 & (long) arg)
cs->dc.hfcpci.ph_state = 1;
cs->hw.hfcpci.nt_mode = 1;
cs->hw.hfcpci.nt_timer = 0;
- cs->stlist->l2.l2l1 = dch_nt_l2l1;
+ cs->stlist->l1.l2l1 = dch_nt_l2l1;
restore_flags(flags);
debugl1(cs, "NT mode activated");
return (0);
#endif
if (!cs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
case (PH_PULL | REQUEST):
if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
mode_hfcpci(st->l1.bcs, 0, st->l1.bc);
- st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
break;
}
}
if (open_hfcpcistate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
- st->l2.l2l1 = hfcpci_l2l1;
+ st->l1.l2l1 = hfcpci_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
st->l1.l1hw(st, pr, arg);
break;
case (PH_ACTIVATE | REQUEST):
- st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
break;
case (PH_TESTLOOP | REQUEST):
if (1 & (long) arg)
cs->dc.hfcsx.ph_state = 1;
cs->hw.hfcsx.nt_mode = 1;
cs->hw.hfcsx.nt_timer = 0;
- cs->stlist->l2.l2l1 = dch_nt_l2l1;
+ cs->stlist->l1.l2l1 = dch_nt_l2l1;
restore_flags(flags);
debugl1(cs, "NT mode activated");
return (0);
#endif
if (!cs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
case (PH_PULL | REQUEST):
if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
mode_hfcsx(st->l1.bcs, 0, st->l1.bc);
- st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
break;
}
}
if (open_hfcsxstate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
- st->l2.l2l1 = hfcsx_l2l1;
+ st->l1.l2l1 = hfcsx_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
long Flags;
struct FsmInst l1m;
struct FsmTimer timer;
- void (*l1l2) (struct PStack *, int, void *);
void (*l1hw) (struct PStack *, int, void *);
void (*l1tei) (struct PStack *, int, void *);
+ void (*l2l1) (struct PStack *, int, void *);
int mode, bc;
int delay;
};
struct sk_buff *windowar[MAX_WINDOW];
struct sk_buff_head i_queue;
struct sk_buff_head ui_queue;
- void (*l2l1) (struct PStack *, int, void *);
- void (*l2l3) (struct PStack *, int, void *);
+ void (*l3l2) (struct PStack *, int, void *);
+ void (*l1l2) (struct PStack *, int, void *);
void (*l2tei) (struct PStack *, int, void *);
struct FsmInst l2m;
struct FsmTimer t200, t203;
};
struct Layer3 {
- void (*l3l4) (struct PStack *, int, void *);
+ void (*l4l3) (struct PStack *, int, void *);
+ int (*l4l3_proto) (struct PStack *, isdn_ctrl *);
void (*l3ml3) (struct PStack *, int, void *);
- void (*l3l2) (struct PStack *, int, void *);
+ void (*l2l3) (struct PStack *, int, void *);
struct FsmInst l3m;
struct FsmTimer l3m_timer;
struct sk_buff_head squeue;
};
struct LLInterface {
- void (*l4l3) (struct PStack *, int, void *);
- int (*l4l3_proto) (struct PStack *, isdn_ctrl *);
+ void (*l3l4) (struct PStack *, int, void *);
void *userdata;
void (*l1writewakeup) (struct PStack *, int);
void (*l2writewakeup) (struct PStack *, int);
int TeiNew(void);
void TeiFree(void);
int certification_check(int output);
+
+static inline void
+L2L1(struct PStack *st, int pr, void *arg)
+{
+ st->l1.l2l1(st, pr, arg);
+}
+
+static inline void
+L1L2(struct PStack *st, int pr, void *arg)
+{
+ st->l2.l1l2(st, pr, arg);
+}
+
+static inline void
+L3L2(struct PStack *st, int pr, void *arg)
+{
+ st->l2.l3l2(st, pr, arg);
+}
+
+static inline void
+L2L3(struct PStack *st, int pr, void *arg)
+{
+ st->l3.l2l3(st, pr, arg);
+}
+
+static inline void
+L3L4(struct PStack *st, int pr, void *arg)
+{
+ st->lli.l3l4(st, pr, arg);
+}
+
+static inline void
+L4L3(struct PStack *st, int pr, void *arg)
+{
+ st->l3.l4l3(st, pr, arg);
+}
+
--- /dev/null
+/*
+ * Driver for AVM Fritz!classic (ISA) ISDN card
+ *
+ * Author Kai Germaschewski
+ * Copyright 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 2001 by Karsten Keil <keil@isdn4linux.de>
+ *
+ * based upon Karsten Keil's original avm_a1.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include "hisax_fcclassic.h"
+
+// debugging cruft
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 0;
+MODULE_PARM(debug, "i");
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("AVM Fritz!Card classic ISDN driver");
+
+static int protocol = 2; /* EURO-ISDN Default */
+MODULE_PARM(protocol, "i");
+
+// ----------------------------------------------------------------------
+
+#define AVM_A1_STAT_ISAC 0x01
+#define AVM_A1_STAT_HSCX 0x02
+#define AVM_A1_STAT_TIMER 0x04
+
+// ----------------------------------------------------------------------
+
+static unsigned char
+fcclassic_read_isac(struct isac *isac, unsigned char offset)
+{
+ struct fritz_adapter *adapter = isac->priv;
+ unsigned char val;
+
+ val = inb(adapter->isac_base + offset);
+ DBG(0x1000, " port %#x, value %#x",
+ offset, val);
+ return val;
+}
+
+static void
+fcclassic_write_isac(struct isac *isac, unsigned char offset,
+ unsigned char value)
+{
+ struct fritz_adapter *adapter = isac->priv;
+
+ DBG(0x1000, " port %#x, value %#x",
+ offset, value);
+ outb(value, adapter->isac_base + offset);
+}
+
+static void
+fcclassic_read_isac_fifo(struct isac *isac, unsigned char * data, int size)
+{
+ struct fritz_adapter *adapter = isac->priv;
+
+ insb(adapter->isac_fifo, data, size);
+}
+
+static void
+fcclassic_write_isac_fifo(struct isac *isac, unsigned char * data, int size)
+{
+ struct fritz_adapter *adapter = isac->priv;
+
+ outsb(adapter->isac_fifo, data, size);
+}
+
+static u_char
+fcclassic_read_hscx(struct hscx *hscx, u_char offset)
+{
+ struct fritz_adapter *adapter = hscx->priv;
+
+ return inb(adapter->hscx_base[hscx->channel] + offset);
+}
+
+static void
+fcclassic_write_hscx(struct hscx *hscx, u_char offset, u_char value)
+{
+ struct fritz_adapter *adapter = hscx->priv;
+
+ outb(value, adapter->hscx_base[hscx->channel] + offset);
+}
+
+static void
+fcclassic_read_hscx_fifo(struct hscx *hscx, unsigned char * data, int size)
+{
+ struct fritz_adapter *adapter = hscx->priv;
+
+ insb(adapter->hscx_fifo[hscx->channel], data, size);
+}
+
+static void
+fcclassic_write_hscx_fifo(struct hscx *hscx, unsigned char * data, int size)
+{
+ struct fritz_adapter *adapter = hscx->priv;
+
+ outsb(adapter->hscx_fifo[hscx->channel], data, size);
+}
+
+// ----------------------------------------------------------------------
+
+static void
+fcclassic_irq(int intno, void *dev, struct pt_regs *regs)
+{
+ struct fritz_adapter *adapter = dev;
+ unsigned char sval;
+
+ DBG(2, "");
+ while ((sval = inb(adapter->cfg_reg) & 0xf) != 0x7) {
+ DBG(2, "sval %#x", sval);
+ if (!(sval & AVM_A1_STAT_TIMER)) {
+ outb(0x1e, adapter->cfg_reg);
+ }
+ if (!(sval & AVM_A1_STAT_HSCX)) {
+ hscx_irq(adapter->hscx);
+ }
+ if (!(sval & AVM_A1_STAT_ISAC)) {
+ isac_irq(&adapter->isac);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+
+static int __init
+fcclassic_setup(struct fritz_adapter *adapter)
+{
+ u32 val = 0;
+ int i;
+ int retval;
+
+ DBG(1,"");
+
+ isac_init(&adapter->isac); // FIXME is this okay now
+
+ adapter->cfg_reg = adapter->io + 0x1800;
+ adapter->isac_base = adapter->io + 0x1400 - 0x20;
+ adapter->isac_fifo = adapter->io + 0x1000;
+ adapter->hscx_base[0] = adapter->io + 0x0400 - 0x20;
+ adapter->hscx_fifo[0] = adapter->io;
+ adapter->hscx_base[1] = adapter->io + 0x0c00 - 0x20;
+ adapter->hscx_fifo[1] = adapter->io + 0x0800;
+
+ retval = -EBUSY;
+ if (!request_region(adapter->cfg_reg , 8,
+ "fcclassic cfg"))
+ goto err;
+ if (!request_region(adapter->isac_base + 0x20 , 32,
+ "fcclassic isac"))
+ goto err_cfg_reg;
+ if (!request_region(adapter->isac_fifo , 1,
+ "fcclassic isac fifo"))
+ goto err_isac_base;
+ if (!request_region(adapter->hscx_base[0] + 0x20, 32,
+ "fcclassic hscx"))
+ goto err_isac_fifo;
+ if (!request_region(adapter->hscx_fifo[0] , 1,
+ "fcclassic hscx fifo"))
+ goto err_hscx_base_0;
+ if (!request_region(adapter->hscx_base[1] + 0x20, 32,
+ "fcclassic hscx"))
+ goto err_hscx_fifo_0;
+ if (!request_region(adapter->hscx_fifo[1] , 1,
+ "fcclassic hscx fifo"))
+ goto err_hscx_base_1;
+ retval = request_irq(adapter->irq, fcclassic_irq, 0,
+ "fcclassic", adapter);
+ if (retval)
+ goto err_hscx_fifo_1;
+
+ // Reset
+ outb(0x00, adapter->cfg_reg);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(200 * HZ / 1000); // 200 msec
+ outb(0x01, adapter->cfg_reg);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(200 * HZ / 1000); // 200 msec
+ outb(0x00, adapter->cfg_reg);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(200 * HZ / 1000); // 200 msec
+
+ val = adapter->irq;
+ if (val == 9)
+ val = 2;
+ outb(val, adapter->cfg_reg + 1);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(200 * HZ / 1000); // 200 msec
+ outb(0x00, adapter->cfg_reg);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(200 * HZ / 1000); // 200 msec
+
+ val = inb(adapter->cfg_reg);
+ printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+ adapter->cfg_reg, val);
+ val = inb(adapter->cfg_reg + 3);
+ printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+ adapter->cfg_reg + 3, val);
+ val = inb(adapter->cfg_reg + 2);
+ printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+ adapter->cfg_reg + 2, val);
+ val = inb(adapter->cfg_reg);
+ printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+ adapter->cfg_reg, val);
+
+ outb(0x16, adapter->cfg_reg);
+ outb(0x1e, adapter->cfg_reg);
+
+ adapter->isac.priv = adapter;
+ adapter->isac.read_isac = &fcclassic_read_isac;
+ adapter->isac.write_isac = &fcclassic_write_isac;
+ adapter->isac.read_isac_fifo = &fcclassic_read_isac_fifo;
+ adapter->isac.write_isac_fifo = &fcclassic_write_isac_fifo;
+ isac_setup(&adapter->isac);
+ for (i = 0; i < 2; i++) {
+ hscx_init(&adapter->hscx[i]);
+ adapter->hscx[i].priv = adapter;
+ adapter->hscx[i].read_hscx = &fcclassic_read_hscx;
+ adapter->hscx[i].write_hscx = &fcclassic_write_hscx;
+ adapter->hscx[i].read_hscx_fifo = &fcclassic_read_hscx_fifo;
+ adapter->hscx[i].write_hscx_fifo = &fcclassic_write_hscx_fifo;
+ hscx_setup(&adapter->hscx[i]);
+ }
+
+ return 0;
+
+ err_hscx_fifo_1:
+ release_region(adapter->hscx_fifo[1] , 1);
+ err_hscx_base_1:
+ release_region(adapter->hscx_base[1] + 0x20, 32);
+ err_hscx_fifo_0:
+ release_region(adapter->hscx_fifo[0] , 1);
+ err_hscx_base_0:
+ release_region(adapter->hscx_base[0] + 0x20, 32);
+ err_isac_fifo:
+ release_region(adapter->isac_fifo , 1);
+ err_isac_base:
+ release_region(adapter->isac_base + 0x20, 32);
+ err_cfg_reg:
+ release_region(adapter->cfg_reg , 8);
+ err:
+ return retval;
+}
+
+static void __exit fcclassic_release(struct fritz_adapter *adapter)
+{
+ DBG(1,"");
+
+// outb(0, adapter->io + AVM_STATUS0);
+ free_irq(adapter->irq, adapter);
+ release_region(adapter->hscx_fifo[1] , 1);
+ release_region(adapter->hscx_base[1] + 0x20, 32);
+ release_region(adapter->hscx_fifo[0] , 1);
+ release_region(adapter->hscx_base[0] + 0x20, 32);
+ release_region(adapter->isac_fifo , 1);
+ release_region(adapter->isac_base + 0x20, 32);
+ release_region(adapter->cfg_reg , 8);
+}
+
+// ----------------------------------------------------------------------
+
+static struct fritz_adapter * __init
+new_adapter(struct pci_dev *pdev)
+{
+ struct fritz_adapter *adapter;
+ struct hisax_b_if *b_if[2];
+ int i;
+
+ adapter = kmalloc(sizeof(struct fritz_adapter), GFP_KERNEL);
+ if (!adapter)
+ return NULL;
+
+ memset(adapter, 0, sizeof(struct fritz_adapter));
+
+ SET_MODULE_OWNER(&adapter->isac.hisax_d_if);
+ adapter->isac.hisax_d_if.ifc.priv = &adapter->isac;
+ adapter->isac.hisax_d_if.ifc.l2l1 = isac_d_l2l1;
+
+ for (i = 0; i < 2; i++) {
+ // adapter->hscx[i].adapter = adapter;
+ adapter->hscx[i].channel = i;
+ adapter->hscx[i].b_if.ifc.priv = &adapter->hscx[i];
+ adapter->hscx[i].b_if.ifc.l2l1 = hscx_b_l2l1;
+ }
+ pci_set_drvdata(pdev, adapter);
+
+ for (i = 0; i < 2; i++)
+ b_if[i] = &adapter->hscx[i].b_if;
+
+ hisax_register(&adapter->isac.hisax_d_if, b_if, "fcclassic", protocol);
+
+ return adapter;
+}
+
+static void
+delete_adapter(struct fritz_adapter *adapter)
+{
+ hisax_unregister(&adapter->isac.hisax_d_if);
+ kfree(adapter);
+}
+
+static int __init
+fcclassic_probe(struct pci_dev *pdev, const struct isapnp_device_id *ent)
+{
+ struct fritz_adapter *adapter;
+ int retval;
+
+ retval = -ENOMEM;
+ adapter = new_adapter(pdev);
+ if (!adapter)
+ goto err;
+
+ adapter->io = pdev->resource[0].start;
+ adapter->irq = pdev->irq_resource[0].start;
+
+ printk(KERN_INFO "hisax_fcclassic: found Fritz!Card classic at IO %#x irq %d\n",
+ adapter->io, adapter->irq);
+
+ retval = fcclassic_setup(adapter);
+ if (retval)
+ goto err_free;
+
+ return 0;
+
+ err_free:
+ delete_adapter(adapter);
+ err:
+ return retval;
+}
+
+static int __exit
+fcclassic_remove(struct pci_dev *pdev)
+{
+ struct fritz_adapter *adapter = pci_get_drvdata(pdev);
+
+ fcclassic_release(adapter);
+ delete_adapter(adapter);
+
+ return 0;
+}
+
+static struct pci_dev isa_dev[4];
+
+static int __init
+hisax_fcclassic_init(void)
+{
+ printk(KERN_INFO "hisax_fcclassic: Fritz!Card classic ISDN driver v0.0.1\n");
+
+ isa_dev[0].resource[0].start = 0x300;
+ isa_dev[0].irq_resource[0].start = 7;
+
+ fcclassic_probe(isa_dev, NULL);
+
+ return 0;
+}
+
+static void __exit
+hisax_fcclassic_exit(void)
+{
+ fcclassic_remove(isa_dev);
+}
+
+module_init(hisax_fcclassic_init);
+module_exit(hisax_fcclassic_exit);
--- /dev/null
+#include "hisax_if.h"
+#include "hisax_isac.h"
+#include "hisax_hscx.h"
+
+#include <linux/pci.h>
+
+struct fritz_adapter {
+ unsigned int io;
+ unsigned int irq;
+ unsigned int cfg_reg;
+ unsigned int isac_base;
+ unsigned int isac_fifo;
+ unsigned int hscx_base[2];
+ unsigned int hscx_fifo[2];
+ struct isac isac;
+
+ struct hscx hscx[2];
+};
--- /dev/null
+/*
+ * Driver for HSCX
+ * High-Level Serial Communcation Controller Extended
+ *
+ * Author Kai Germaschewski
+ * Copyright 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 2001 by Karsten Keil <keil@isdn4linux.de>
+ *
+ * based upon Karsten Keil's original isac.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+/* TODO:
+ * comments in .h
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include "hisax_hscx.h"
+
+// debugging cruft
+
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 1;
+MODULE_PARM(debug, "i");
+
+static char *HSCXVer[] =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("HSCX driver");
+
+#define DBG_WARN 0x0001
+#define DBG_IRQ 0x0002
+#define DBG_L1M 0x0004
+#define DBG_PR 0x0008
+#define DBG_RFIFO 0x0100
+#define DBG_RPACKET 0x0200
+#define DBG_XFIFO 0x1000
+#define DBG_XPACKET 0x2000
+
+#define HSCX_ISTA 0x20
+#define HSCX_ISTA_EXB 0x01
+#define HSCX_ISTA_EXA 0x02
+#define HSCX_ISTA_ICA 0x04
+#define HSCX_ISTA_TIN 0x08
+#define HSCX_ISTA_XPR 0x10
+#define HSCX_ISTA_RSC 0x20
+#define HSCX_ISTA_RPF 0x40
+#define HSCX_ISTA_RME 0x80
+
+#define HSCX_CMDR 0x21
+#define HSCX_CMDR_RMC 0x80
+#define HSCX_CMDR_RHR 0x40
+#define HSCX_CMDR_RNR 0x20
+#define HSCX_CMDR_STI 0x10
+#define HSCX_CMDR_XTF 0x08
+#define HSCX_CMDR_XIF 0x04
+#define HSCX_CMDR_XME 0x02
+#define HSCX_CMDR_XRES 0x01
+
+#define HSCX_EXIR 0x24
+#define HSCX_EXIR_XDU 0x40
+
+#define HSCX_RSTA 0x27
+#define HSCX_RSTA_VFR 0x80
+#define HSCX_RSTA_RDO 0x40
+#define HSCX_RSTA_CRC 0x20
+#define HSCX_RSTA_RAB 0x10
+
+#define HSCX_CCR1 0x2f
+#define HSCX_CCR2 0x2c
+#define HSCX_TSAR 0x31
+#define HSCX_TSAX 0x30
+#define HSCX_XCCR 0x32
+#define HSCX_RCCR 0x33
+#define HSCX_MODE 0x22
+
+#define HSCX_XAD1 0x24
+#define HSCX_XAD2 0x25
+#define HSCX_RAH2 0x27
+#define HSCX_TIMR 0x23
+#define HSCX_STAR 0x21
+#define HSCX_RBCL 0x25
+#define HSCX_XBCH 0x2d
+#define HSCX_VSTR 0x2e
+#define HSCX_RLCR 0x2e
+#define HSCX_MASK 0x20
+
+static inline void B_L1L2(struct hscx *hscx, int pr, void *arg)
+{
+ struct hisax_if *ifc = (struct hisax_if *) &hscx->b_if;
+
+ DBG(0x10, "pr %#x", pr);
+ ifc->l1l2(ifc, pr, arg);
+}
+
+static void hscx_version(struct hscx *hscx)
+{
+ int val;
+
+ val = hscx->read_hscx(hscx, HSCX_VSTR) & 0xf;
+ DBG(1, "HSCX version (%x): %s", val, HSCXVer[val]);
+}
+
+static void hscx_empty_fifo(struct hscx *hscx, int count)
+{
+ u_char *ptr;
+
+ DBG(DBG_IRQ, "count %d", count);
+
+ if ((hscx->rcvidx + count) >= HSCX_BUFMAX) {
+ DBG(DBG_WARN, "overrun %d", hscx->rcvidx + count);
+ hscx->write_hscx(hscx, HSCX_CMDR, HSCX_CMDR_RMC);
+ hscx->rcvidx = 0;
+ return;
+ }
+ ptr = hscx->rcvbuf + hscx->rcvidx;
+ hscx->rcvidx += count;
+ hscx->read_hscx_fifo(hscx, ptr, count);
+ hscx->write_hscx(hscx, HSCX_CMDR, HSCX_CMDR_RMC);
+ DBG_PACKET(DBG_RFIFO, ptr, count);
+}
+
+static void hscx_fill_fifo(struct hscx *hscx)
+{
+ int count;
+ unsigned char cmd;
+ int fifo_size = test_bit(HSCX_IPAC, &hscx->flags)? 64: 32;
+ unsigned char *ptr;
+
+ if (!hscx->tx_skb)
+ BUG();
+
+ count = hscx->tx_skb->len;
+ if (count <= 0)
+ BUG();
+
+ DBG(DBG_IRQ, "count %d", count);
+
+ if (count > fifo_size || hscx->mode == L1_MODE_TRANS) {
+ count = fifo_size;
+ cmd = 0x8;
+ } else {
+ cmd = 0xa;
+ }
+
+ ptr = hscx->tx_skb->data;
+ skb_pull(hscx->tx_skb, count);
+ hscx->tx_cnt += count;
+ DBG_PACKET(DBG_XFIFO, ptr, count);
+ hscx->write_hscx_fifo(hscx, ptr, count);
+ hscx->write_hscx(hscx, HSCX_CMDR, cmd);
+}
+
+static void hscx_retransmit(struct hscx *hscx)
+{
+ if (!hscx->tx_skb) {
+ DBG(DBG_WARN, "no skb");
+ return;
+ }
+ skb_push(hscx->tx_skb, hscx->tx_cnt);
+ hscx->tx_cnt = 0;
+ hscx->write_hscx(hscx, HSCX_CMDR, 0x01);
+}
+
+static inline void hscx_rme_interrupt(struct hscx *hscx)
+{
+ unsigned char val;
+ int count;
+ struct sk_buff *skb;
+ int fifo_size = test_bit(HSCX_IPAC, &hscx->flags)? 64: 32;
+
+ val = hscx->read_hscx(hscx, HSCX_RSTA);
+ if ((val & (HSCX_RSTA_VFR | HSCX_RSTA_RDO | HSCX_RSTA_CRC | HSCX_RSTA_RAB) )
+ != (HSCX_RSTA_VFR | HSCX_RSTA_CRC)) {
+ DBG(DBG_WARN, "RSTA %#x, dropped", val);
+ hscx->write_hscx(hscx, HSCX_CMDR, HSCX_CMDR_RMC);
+ goto out;
+ }
+
+ count = hscx->read_hscx(hscx, HSCX_RBCL) & (fifo_size-1);
+ DBG(DBG_IRQ, "RBCL %#x", count);
+ if (count == 0)
+ count = fifo_size;
+
+ hscx_empty_fifo(hscx, count);
+
+ count = hscx->rcvidx;
+ if (count < 1) {
+ DBG(DBG_WARN, "count %d < 1", count);
+ goto out;
+ }
+
+ skb = alloc_skb(count, GFP_ATOMIC);
+ if (!skb) {
+ DBG(DBG_WARN, "no memory, dropping\n");
+ goto out;
+ }
+ memcpy(skb_put(skb, count), hscx->rcvbuf, count);
+ DBG_SKB(DBG_RPACKET, skb);
+ B_L1L2(hscx, PH_DATA | INDICATION, skb);
+ out:
+ hscx->rcvidx = 0;
+}
+
+static inline void hscx_xpr_interrupt(struct hscx *hscx)
+{
+ struct sk_buff *skb;
+
+ skb = hscx->tx_skb;
+ if (!skb)
+ return;
+
+ if (skb->len > 0) {
+ hscx_fill_fifo(hscx);
+ return;
+ }
+ hscx->tx_cnt = 0;
+ hscx->tx_skb = NULL;
+ B_L1L2(hscx, PH_DATA | CONFIRM, (void *) skb->truesize);
+ dev_kfree_skb_irq(skb);
+}
+
+static inline void hscx_exi_interrupt(struct hscx *hscx)
+{
+ unsigned char val;
+
+ val = hscx->read_hscx(hscx, HSCX_EXIR);
+ DBG(2, "EXIR %#x", val);
+
+ if (val & HSCX_EXIR_XDU) {
+ DBG(DBG_WARN, "HSCX XDU");
+ if (hscx->mode == L1_MODE_TRANS) {
+ hscx_fill_fifo(hscx);
+ } else {
+ hscx_retransmit(hscx);
+ }
+ }
+}
+
+static void hscx_reg_interrupt(struct hscx *hscx, unsigned char val)
+{
+ struct sk_buff *skb;
+
+ if (val & HSCX_ISTA_XPR) {
+ DBG(DBG_IRQ, "XPR");
+ hscx_xpr_interrupt(hscx);
+ }
+ if (val & HSCX_ISTA_RME) {
+ DBG(DBG_IRQ, "RME");
+ hscx_rme_interrupt(hscx);
+ }
+ if (val & HSCX_ISTA_RPF) {
+ int fifo_size = test_bit(HSCX_IPAC, &hscx->flags)? 64: 32;
+
+ DBG(DBG_IRQ, "RPF");
+ hscx_empty_fifo(hscx, fifo_size);
+ if (hscx->mode == L1_MODE_TRANS) {
+ skb = dev_alloc_skb(fifo_size);
+ if (!skb) {
+ DBG(DBG_WARN, "no memory, dropping\n");
+ goto out;
+ }
+ memcpy(skb_put(skb, fifo_size), hscx->rcvbuf, fifo_size);
+ DBG_SKB(DBG_RPACKET, skb);
+ B_L1L2(hscx, PH_DATA | INDICATION, skb);
+ out:
+ hscx->rcvidx = 0;
+ }
+ }
+}
+
+void hscx_irq(struct hscx *hscx_a)
+{
+ struct hscx *hscx_b = hscx_a + 1;
+ unsigned char val;
+
+ val = hscx_b->read_hscx(hscx_b, HSCX_ISTA);
+ DBG(DBG_IRQ, "ISTA B %#x", val);
+
+ if (val & HSCX_ISTA_EXB) {
+ DBG(DBG_IRQ, "EXI B");
+ hscx_exi_interrupt(hscx_b);
+ }
+ if (val & HSCX_ISTA_EXA) {
+ DBG(DBG_IRQ, "EXI A");
+ hscx_exi_interrupt(hscx_a);
+ }
+ if (val & 0xf8) {
+ hscx_reg_interrupt(hscx_b, val);
+ }
+ if (val & HSCX_ISTA_ICA) {
+ val = hscx_a->read_hscx(hscx_a, HSCX_ISTA);
+ DBG(DBG_IRQ, "ISTA A %#x", val);
+ hscx_reg_interrupt(hscx_a, val);
+ hscx_a->write_hscx(hscx_a, HSCX_MASK, 0xff);
+ hscx_a->write_hscx(hscx_a, HSCX_MASK, 0x00);
+ }
+ hscx_b->write_hscx(hscx_b, HSCX_MASK, 0xff);
+ hscx_b->write_hscx(hscx_b, HSCX_MASK, 0x00);
+}
+
+static void modehscx(struct hscx *hscx, int mode)
+{
+ int bc = hscx->channel;
+
+ DBG(0x40, "hscx %c mode %d --> %d",
+ 'A' + hscx->channel, hscx->mode, mode);
+
+ hscx->mode = mode;
+ hscx->write_hscx(hscx, HSCX_XAD1, 0xFF);
+ hscx->write_hscx(hscx, HSCX_XAD2, 0xFF);
+ hscx->write_hscx(hscx, HSCX_RAH2, 0xFF);
+ hscx->write_hscx(hscx, HSCX_XBCH, 0x0);
+ hscx->write_hscx(hscx, HSCX_RLCR, 0x0);
+ hscx->write_hscx(hscx, HSCX_CCR1,
+ test_bit(HSCX_IPAC, &hscx->flags) ? 0x82 : 0x85);
+ hscx->write_hscx(hscx, HSCX_CCR2, 0x30);
+ hscx->write_hscx(hscx, HSCX_XCCR, 7);
+ hscx->write_hscx(hscx, HSCX_RCCR, 7);
+
+ /* Switch IOM 1 SSI */
+ if (test_bit(HSCX_IOM1, &hscx->flags))
+ bc = 1;
+
+ hscx->write_hscx(hscx, HSCX_TSAX, hscx->tsaxr);
+ hscx->write_hscx(hscx, HSCX_TSAR, hscx->tsaxr);
+
+ switch (mode) {
+ case (L1_MODE_NULL):
+ hscx->write_hscx(hscx, HSCX_TSAX, 0x1f);
+ hscx->write_hscx(hscx, HSCX_TSAR, 0x1f);
+ hscx->write_hscx(hscx, HSCX_MODE, 0x84);
+ break;
+ case (L1_MODE_TRANS):
+ hscx->write_hscx(hscx, HSCX_MODE, 0xe4);
+ break;
+ case (L1_MODE_HDLC):
+ hscx->write_hscx(hscx, HSCX_CCR1, test_bit(HSCX_IPAC, &hscx->flags) ? 0x8a : 0x8d);
+ hscx->write_hscx(hscx, HSCX_MODE, 0x8c);
+ break;
+ }
+ if (mode)
+ hscx->write_hscx(hscx, HSCX_CMDR, 0x41);
+
+ hscx->write_hscx(hscx, HSCX_ISTA, 0x00);
+}
+
+void hscx_init(struct hscx *hscx)
+{
+ if (hscx->channel)
+ hscx->tsaxr = 0x03;
+ else
+ hscx->tsaxr = 0x2f;
+}
+
+void hscx_setup(struct hscx *hscx)
+{
+ hscx_version(hscx);
+ hscx->mode = -1;
+ modehscx(hscx, L1_MODE_NULL);
+}
+
+void hscx_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+ struct hscx *hscx = ifc->priv;
+ struct sk_buff *skb = arg;
+ int mode;
+
+ DBG(0x10, "pr %#x", pr);
+
+ switch (pr) {
+ case PH_DATA | REQUEST:
+ if (hscx->tx_skb)
+ BUG();
+
+ hscx->tx_skb = skb;
+ DBG_SKB(1, skb);
+ hscx_fill_fifo(hscx);
+ break;
+ case PH_ACTIVATE | REQUEST:
+ mode = (int) arg;
+ DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", hscx->channel + 1, mode);
+ modehscx(hscx, mode);
+ B_L1L2(hscx, PH_ACTIVATE | INDICATION, NULL);
+ break;
+ case PH_DEACTIVATE | REQUEST:
+ DBG(4,"B%d,PH_DEACTIVATE_REQUEST", hscx->channel + 1);
+ modehscx(hscx, L1_MODE_NULL);
+ B_L1L2(hscx, PH_DEACTIVATE | INDICATION, NULL);
+ break;
+ }
+}
+
+static int __init hisax_hscx_init(void)
+{
+ printk(KERN_INFO "hisax_hscx: HSCX ISDN driver v0.1.0\n");
+ return 0;
+}
+
+static void __exit hisax_hscx_exit(void)
+{
+}
+
+EXPORT_SYMBOL(hscx_init);
+EXPORT_SYMBOL(hscx_b_l2l1);
+
+EXPORT_SYMBOL(hscx_setup);
+EXPORT_SYMBOL(hscx_irq);
+
+module_init(hisax_hscx_init);
+module_exit(hisax_hscx_exit);
--- /dev/null
+#ifndef __HISAX_HSCX_H__
+#define __HISAX_HSCX_H__
+
+#include <linux/kernel.h>
+#include "fsm.h"
+#include "hisax_if.h"
+
+#define HSCX_BUFMAX 4096
+
+#define HSCX_IOM1 0
+#define HSCX_IPAC 1
+
+struct hscx {
+ void *priv;
+ u_long flags;
+ struct hisax_b_if b_if;
+ int mode;
+ int channel;
+ u_char tsaxr;
+ struct sk_buff *tx_skb;
+ int tx_cnt;
+ u_char rcvbuf[HSCX_BUFMAX];
+ int rcvidx;
+
+ u_char (*read_hscx) (struct hscx *, u_char);
+ void (*write_hscx) (struct hscx *, u_char, u_char);
+ void (*read_hscx_fifo) (struct hscx *, u_char *, int);
+ void (*write_hscx_fifo)(struct hscx *, u_char *, int);
+};
+
+void hscx_init(struct hscx *hscx);
+void hscx_b_l2l1(struct hisax_if *hisax_b_if, int pr, void *arg);
+
+void hscx_setup(struct hscx *hscx);
+void hscx_irq(struct hscx *hscx);
+
+#endif
case (PH_PULL | REQUEST):
if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
modehscx(st->l1.bcs, 0, st->l1.bc);
- st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
break;
}
}
if (open_hscxstate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
- st->l2.l2l1 = hscx_l2l1;
+ st->l1.l2l1 = hscx_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
debugl1(cs, "D-Channel Busy cleared");
stptr = cs->stlist;
while (stptr != NULL) {
- stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+ L1L2(stptr, PH_PAUSE | CONFIRM, NULL);
stptr = stptr->next;
}
}
#endif
if (!cs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
stptr = cs->stlist;
while (stptr != NULL) {
- stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+ L1L2(stptr, PH_PAUSE | INDICATION, NULL);
stptr = stptr->next;
}
} else {
debugl1(cs, "D-Channel Busy cleared");
stptr = cs->stlist;
while (stptr != NULL) {
- stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+ L1L2(stptr, PH_PAUSE | CONFIRM, NULL);
stptr = stptr->next;
}
}
#endif
if (!cs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
stptr = cs->stlist;
while (stptr != NULL) {
- stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+ L1L2(stptr, PH_PAUSE | INDICATION, NULL);
stptr = stptr->next;
}
} else {
case (PH_PULL | REQUEST):
if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
debugl1(st->l1.bcs->cs, "PDAC clear BC_FLG_BUSY");
modeisar(st->l1.bcs, 0, st->l1.bc);
- st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
break;
}
}
if (open_isarstate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
- st->l2.l2l1 = isar_l2l1;
+ st->l1.l2l1 = isar_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
st = cs->stlist;
while (st) {
if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
- st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
else
- st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL);
+ L1L2(st, PH_ACTIVATE | INDICATION, NULL);
st = st->next;
}
}
st = cs->stlist;
while (st) {
if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
- st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL);
- st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL);
+ L1L2(st, PH_PAUSE | CONFIRM, NULL);
+ L1L2(st, PH_DEACTIVATE | INDICATION, NULL);
st = st->next;
}
test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
stptr = cs->stlist;
while (stptr != NULL)
if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) {
- stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL);
+ L1L2(stptr, PH_PULL | CONFIRM, NULL);
break;
} else
stptr = stptr->next;
if (sapi == CTRL_SAPI) { /* sapi 0 */
while (stptr != NULL) {
if ((nskb = skb_clone(skb, GFP_ATOMIC)))
- stptr->l1.l1l2(stptr, PH_DATA | INDICATION, nskb);
+ L1L2(stptr, PH_DATA | INDICATION, nskb);
else
printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
stptr = stptr->next;
found = 0;
while (stptr != NULL)
if (tei == stptr->l2.tei) {
- stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb);
+ L1L2(stptr, PH_DATA | INDICATION, skb);
found = !0;
break;
} else
}
if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags))
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) {
if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) {
- st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L2L1(st, PH_DEACTIVATE | CONFIRM, NULL);
}
}
}
FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL);
}
while ((skb = skb_dequeue(&bcs->rqueue))) {
- bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb);
+ L1L2(bcs->st, PH_DATA | INDICATION, skb);
}
}
struct PStack *st = fi->userdata;
FsmChangeState(fi, ST_L1_ACTIV);
- st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
}
static void
struct PStack *st = fi->userdata;
FsmChangeState(fi, ST_L1_NULL);
- st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L2L1(st, PH_DEACTIVATE | CONFIRM, NULL);
}
static struct FsmNode L1BFnList[] __initdata =
debugl1(cs, "PH_ACTIVATE_REQ %s",
st->l1.l1m.fsm->strState[st->l1.l1m.state]);
if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags))
- st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_ACTIVATE | CONFIRM, NULL);
else {
test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg);
setstack_tei(st);
setstack_manager(st);
st->l1.stlistp = &(cs->stlist);
- st->l2.l2l1 = dch_l2l1;
+ st->l1.l2l1 = dch_l2l1;
if (cs->setstack_d)
cs->setstack_d(st, cs);
}
static struct Fsm l2fsm;
enum {
- ST_L2_1,
- ST_L2_2,
- ST_L2_3,
- ST_L2_4,
- ST_L2_5,
- ST_L2_6,
- ST_L2_7,
- ST_L2_8,
+ ST_L2_1, /* TEI unassigned */
+ ST_L2_2, /* Assign awaiting TEI */
+ ST_L2_3, /* Establish awaiting TEI */
+ ST_L2_4, /* TEI assigned */
+ ST_L2_5, /* Awaiting establishment */
+ ST_L2_6, /* Awaiting release */
+ ST_L2_7, /* Multiple frame established */
+ ST_L2_8, /* Timer recovery */
};
#define L2_STATE_COUNT (ST_L2_8+1)
{
if (test_bit(FLG_LAPB, &st->l2.flag))
st->l1.bcs->tx_cnt += skb->len;
- st->l2.l2l1(st, PH_DATA | REQUEST, skb);
+ L2L1(st, PH_DATA | REQUEST, skb);
}
#define enqueue_ui(a, b) enqueue_super(a, b)
else
pr = DL_RELEASE | INDICATION;
- st->l2.l2l3(st, pr, NULL);
+ L2L3(st, pr, NULL);
}
inline void
lapb_dl_release_l2l3(struct PStack *st, int f)
{
if (test_bit(FLG_LAPB, &st->l2.flag))
- st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
- st->l2.l2l3(st, DL_RELEASE | f, NULL);
+ L2L1(st, PH_DEACTIVATE | REQUEST, NULL);
+ L2L3(st, DL_RELEASE | f, NULL);
}
static void
struct sk_buff *skb = arg;
skb_pull(skb, l2headersize(&st->l2, 1));
- st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb);
+ L2L3(st, DL_UNIT_DATA | INDICATION, skb);
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* in states 1-3 for broadcast
*/
{
struct PStack *st = fi->userdata;
- st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+ L2L3(st, DL_RELEASE | CONFIRM, NULL);
}
static void
FsmChangeState(fi, ST_L2_7);
FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
- st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
+ L2L3(st, DL_ESTABLISH | INDICATION, NULL);
}
static void
FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
if (est)
- st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
+ L2L3(st, DL_ESTABLISH | INDICATION, NULL);
if ((ST_L2_7==state) || (ST_L2_8 == state))
if (skb_queue_len(&st->l2.i_queue) && cansend(st))
- st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ L2L1(st, PH_PULL | REQUEST, NULL);
}
static void
FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4);
if (pr != -1)
- st->l2.l2l3(st, pr, NULL);
+ L2L3(st, pr, NULL);
if (skb_queue_len(&st->l2.i_queue) && cansend(st))
- st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ L2L1(st, PH_PULL | REQUEST, NULL);
}
static void
if (!test_bit(FLG_L3_INIT, &st->l2.flag))
skb_queue_purge(&st->l2.i_queue);
if (test_bit(FLG_LAPB, &st->l2.flag))
- st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ L2L1(st, PH_DEACTIVATE | REQUEST, NULL);
st5_dl_release_l2l3(st);
FsmChangeState(fi, ST_L2_4);
}
skb_queue_head(&l2->i_queue, l2->windowar[p1]);
l2->windowar[p1] = NULL;
}
- st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ L2L1(st, PH_PULL | REQUEST, NULL);
}
}
restart_t200(st, 12);
}
if (skb_queue_len(&st->l2.i_queue) && (typ == RR))
- st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ L2L1(st, PH_PULL | REQUEST, NULL);
} else
nrerrorrecovery(fi);
}
if (test_bit(FLG_LAPB, &st->l2.flag))
st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
skb_queue_tail(&st->l2.i_queue, skb);
- st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ L2L1(st, PH_PULL | REQUEST, NULL);
}
static void
else
test_and_set_bit(FLG_ACK_PEND, &l2->flag);
skb_pull(skb, l2headersize(l2, 0));
- st->l2.l2l3(st, DL_DATA | INDICATION, skb);
+ L2L3(st, DL_DATA | INDICATION, skb);
} else {
/* n(s)!=v(r) */
FreeSkb(skb);
}
if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7))
- st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ L2L1(st, PH_PULL | REQUEST, NULL);
if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag))
enquiry_cr(st, RR, RSP, 0);
}
skb_queue_purge(&st->l2.i_queue);
st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G');
if (test_bit(FLG_LAPB, &st->l2.flag))
- st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ L2L1(st, PH_DEACTIVATE | REQUEST, NULL);
st5_dl_release_l2l3(st);
} else {
st->l2.rc++;
memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len);
FreeSkb(oskb);
}
- st->l2.l2l1(st, PH_PULL | INDICATION, skb);
+ L2L1(st, PH_PULL | INDICATION, skb);
test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) {
FsmDelTimer(&st->l2.t203, 13);
FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11);
}
if (skb_queue_len(&l2->i_queue) && cansend(st))
- st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ L2L1(st, PH_PULL | REQUEST, NULL);
}
static void
invoke_retransmission(st, nr);
FsmChangeState(fi, ST_L2_7);
if (skb_queue_len(&l2->i_queue) && cansend(st))
- st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ L2L1(st, PH_PULL | REQUEST, NULL);
} else
nrerrorrecovery(fi);
} else {
skb_queue_purge(&st->l2.ui_queue);
st->l2.tei = -1;
- st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+ L2L3(st, DL_RELEASE | INDICATION, NULL);
FsmChangeState(fi, ST_L2_1);
}
skb_queue_purge(&st->l2.ui_queue);
st->l2.tei = -1;
stop_t200(st, 18);
- st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+ L2L3(st, DL_RELEASE | CONFIRM, NULL);
FsmChangeState(fi, ST_L2_1);
}
st->l2.tei = -1;
stop_t200(st, 17);
FsmDelTimer(&st->l2.t203, 19);
- st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+ L2L3(st, DL_RELEASE | INDICATION, NULL);
FsmChangeState(fi, ST_L2_1);
}
skb_queue_purge(&st->l2.i_queue);
skb_queue_purge(&st->l2.ui_queue);
if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
- st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+ L2L3(st, DL_RELEASE | INDICATION, NULL);
}
static void
skb_queue_purge(&st->l2.ui_queue);
stop_t200(st, 20);
- st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+ L2L3(st, DL_RELEASE | CONFIRM, NULL);
FsmChangeState(fi, ST_L2_4);
}
freewin(st);
stop_t200(st, 19);
FsmDelTimer(&st->l2.t203, 19);
- st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+ L2L3(st, DL_RELEASE | INDICATION, NULL);
FsmChangeState(fi, ST_L2_4);
}
test_bit(FLG_ORIG, &st->l2.flag)) {
test_and_set_bit(FLG_ESTAB_PEND, &st->l2.flag);
}
- st->l2.l2l1(st, PH_ACTIVATE, NULL);
+ L2L1(st, PH_ACTIVATE, NULL);
}
break;
case (DL_RELEASE | REQUEST):
if (test_bit(FLG_LAPB, &st->l2.flag)) {
- st->l2.l2l1(st, PH_DEACTIVATE, NULL);
+ L2L1(st, PH_DEACTIVATE, NULL);
}
FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE_REQ, arg);
break;
void
setstack_isdnl2(struct PStack *st, char *debug_id)
{
- st->l1.l1l2 = isdnl2_l1l2;
- st->l3.l3l2 = isdnl2_l3l2;
+ st->l2.l1l2 = isdnl2_l1l2;
+ st->l2.l3l2 = isdnl2_l3l2;
skb_queue_head_init(&st->l2.i_queue);
skb_queue_head_init(&st->l2.ui_queue);
switch (pr) {
case (DL_DATA | REQUEST):
case (DL_UNIT_DATA | REQUEST):
- st->l2.l2l1(st, PH_DATA | REQUEST, arg);
+ L2L1(st, PH_DATA | REQUEST, arg);
break;
case (DL_ESTABLISH | REQUEST):
- st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+ L2L1(st, PH_ACTIVATE | REQUEST, NULL);
break;
case (DL_RELEASE | REQUEST):
- st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ L2L1(st, PH_DEACTIVATE | REQUEST, NULL);
break;
}
}
void
setstack_transl2(struct PStack *st)
{
- st->l3.l3l2 = transl2_l3l2;
+ st->l2.l3l2 = transl2_l3l2;
}
void
#define RSP 1
#define LC_FLUSH_WAIT 1
+
static void
L3ExpireTimer(struct L3Timer *t)
{
- t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc);
+ t->pc->st->l3.l4l3(t->pc->st, t->event, t->pc);
}
void
st->l3.l3m.printdebug = l3m_debug;
FsmInitTimer(&st->l3.l3m, &st->l3.l3m_timer);
strcpy(st->l3.debug_id, "L3DC ");
- st->lli.l4l3_proto = no_l3_proto_spec;
+ st->l3.l4l3_proto = no_l3_proto_spec;
#ifdef CONFIG_HISAX_EURO
if (st->protocol == ISDN_PTYPE_EURO) {
} else
#endif
if (st->protocol == ISDN_PTYPE_LEASED) {
- st->lli.l4l3 = no_l3_proto;
- st->l2.l2l3 = no_l3_proto;
+ st->l3.l4l3 = no_l3_proto;
+ st->l3.l2l3 = no_l3_proto;
st->l3.l3ml3 = no_l3_proto;
printk(KERN_INFO "HiSax: Leased line mode\n");
} else {
- st->lli.l4l3 = no_l3_proto;
- st->l2.l2l3 = no_l3_proto;
+ st->l3.l4l3 = no_l3_proto;
+ st->l3.l2l3 = no_l3_proto;
st->l3.l3ml3 = no_l3_proto;
sprintf(tmp, "protocol %s not supported",
(st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" :
void
isdnl3_trans(struct PStack *st, int pr, void *arg) {
- st->l3.l3l2(st, pr, arg);
+ L3L2(st, pr, arg);
}
void
st->l3.l3m.userint = 0;
st->l3.l3m.printdebug = l3m_debug;
strcpy(st->l3.debug_id, "L3BC ");
- st->lli.l4l3 = isdnl3_trans;
+ st->l3.l4l3 = isdnl3_trans;
}
#define DREL_TIMER_VALUE 40000
struct PStack *st = fi->userdata;
FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT);
- st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL);
+ L3L2(st, DL_ESTABLISH | REQUEST, NULL);
}
static void
FsmChangeState(fi, ST_L3_LC_ESTAB);
while ((skb = skb_dequeue(&st->l3.squeue))) {
- st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+ L3L2(st, DL_DATA | REQUEST, skb);
dequeued++;
}
if ((!st->l3.proc) && dequeued) {
FsmDelTimer(&st->l3.l3m_timer, 51);
FsmChangeState(fi, ST_L3_LC_ESTAB);
while ((skb = skb_dequeue(&st->l3.squeue))) {
- st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+ L3L2(st, DL_DATA | REQUEST, skb);
dequeued++;
}
if ((!st->l3.proc) && dequeued) {
FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51);
} else {
FsmChangeState(fi, ST_L3_LC_REL_WAIT);
- st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL);
+ L3L2(st, DL_RELEASE | REQUEST, NULL);
}
}
switch (pr) {
case (DL_DATA | REQUEST):
if (st->l3.l3m.state == ST_L3_LC_ESTAB) {
- st->l3.l3l2(st, pr, arg);
+ L3L2(st, pr, arg);
} else {
struct sk_buff *skb = arg;
case (PH_PULL | REQUEST):
if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
modejade(st->l1.bcs, 0, st->l1.bc);
- st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
break;
}
}
if (open_jadestate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
- st->l2.l2l1 = jade_l2l1;
+ st->l1.l2l1 = jade_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
l3_debug(pc->st, tmp);
}
newl3state(pc, 6);
- pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+ L3L4(pc->st, CC_SETUP | INDICATION, pc);
} else
release_l3_process(pc);
}
}
dev_kfree_skb(skb);
L3AddTimer(&pc->timer, T304, CC_T304);
- pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+ L3L4(pc->st, CC_MORE_INFO | INDICATION, pc);
}
static void
dev_kfree_skb(skb);
L3AddTimer(&pc->timer, T310, CC_T310);
newl3state(pc, 3);
- pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+ L3L4(pc->st, CC_PROCEEDING | INDICATION, pc);
}
static void
dev_kfree_skb(skb);
L3DelTimer(&pc->timer); /* T304 */
newl3state(pc, 4);
- pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+ L3L4(pc->st, CC_ALERTING | INDICATION, pc);
}
static void
}
if (tmpcharge > pc->para.chargeinfo) {
pc->para.chargeinfo = tmpcharge;
- pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+ L3L4(pc->st, CC_CHARGE | INDICATION, pc);
}
if (pc->st->l3.debug & L3_DEB_CHARGE) {
sprintf(tmp, "charging info %d", pc->para.chargeinfo);
newl3state(pc, 10);
dev_kfree_skb(skb);
pc->para.chargeinfo = 0;
- pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+ L3L4(pc->st, CC_SETUP | CONFIRM, pc);
}
static void
StopAllL3Timer(pc);
newl3state(pc, 0);
l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1);
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
release_l3_process(pc);
}
StopAllL3Timer(pc);
newl3state(pc, 0);
pc->para.cause = NO_CAUSE;
- pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+ L3L4(pc->st, CC_RELEASE | CONFIRM, pc);
release_l3_process(pc);
}
}
if (tmpcharge > pc->para.chargeinfo) {
pc->para.chargeinfo = tmpcharge;
- pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+ L3L4(pc->st, CC_CHARGE | INDICATION, pc);
}
if (pc->st->l3.debug & L3_DEB_CHARGE) {
sprintf(tmp, "charging info %d", pc->para.chargeinfo);
}
dev_kfree_skb(skb);
newl3state(pc, 12);
- pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+ L3L4(pc->st, CC_DISCONNECT | INDICATION, pc);
}
newl3state(pc, 10);
pc->para.chargeinfo = 0;
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+ L3L4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
}
static void
L3DelTimer(&pc->timer);
pc->para.cause = 0xE6;
l3_1tr6_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+ L3L4(pc->st, CC_SETUP_ERR, pc);
}
static void
L3DelTimer(&pc->timer);
pc->para.cause = 0xE6;
l3_1tr6_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+ L3L4(pc->st, CC_SETUP_ERR, pc);
}
static void
L3DelTimer(&pc->timer);
pc->para.cause = 0xE6;
l3_1tr6_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+ L3L4(pc->st, CC_CONNECT_ERR, pc);
}
static void
l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg)
{
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+ L3L4(pc->st, CC_RELEASE_ERR, pc);
release_l3_process(pc);
}
{
pc->para.cause = CAUSE_LocalProcErr;
l3_1tr6_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+ L3L4(pc->st, CC_SETUP_ERR, pc);
}
static void
newl3state(pc, 0);
pc->para.cause = 0x1b; /* Destination out of order */
pc->para.loc = 0;
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
release_l3_process(pc);
}
{
char tmp[64];
- st->lli.l4l3 = down1tr6;
- st->l2.l2l3 = up1tr6;
+ st->l3.l4l3 = down1tr6;
+ st->l3.l2l3 = up1tr6;
st->l3.l3ml3 = man1tr6;
st->l3.N303 = 0;
pc->prot.dss1.remote_result = 0; /* success */
pc->prot.dss1.invoke_id = 0;
pc->redir_result = pc->prot.dss1.remote_result;
- st->l3.l3l4(st, CC_REDIR | INDICATION, pc); } /* Diversion successful */
+ L3L4(st, CC_REDIR | INDICATION, pc); } /* Diversion successful */
else
l3_debug(st,"return error unknown identifier");
break;
pc->prot.dss1.remote_result = err_ret; /* result */
pc->prot.dss1.invoke_id = 0;
pc->redir_result = pc->prot.dss1.remote_result;
- st->l3.l3l4(st, CC_REDIR | INDICATION, pc);
+ L3L4(st, CC_REDIR | INDICATION, pc);
} /* Deflection error */
else
l3_debug(st,"return result unknown identifier");
pc->para.cause = NO_CAUSE;
StopAllL3Timer(pc);
newl3state(pc, 0);
- pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+ L3L4(pc->st, CC_RELEASE | CONFIRM, pc);
dss1_release_l3_process(pc);
}
L3AddTimer(&pc->timer, T310, CC_T310);
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err(pc, ret);
- pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+ L3L4(pc->st, CC_PROCEEDING | INDICATION, pc);
}
static void
L3AddTimer(&pc->timer, T304, CC_T304);
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err(pc, ret);
- pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+ L3L4(pc->st, CC_MORE_INFO | INDICATION, pc);
}
static void
if (cause)
newl3state(pc, 19);
if (11 != ret)
- pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+ L3L4(pc->st, CC_DISCONNECT | INDICATION, pc);
else if (!cause)
l3dss1_release_req(pc, pr, NULL);
if (cause) {
/* here should inserted COLP handling KKe */
if (ret)
l3dss1_std_ie_err(pc, ret);
- pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+ L3L4(pc->st, CC_SETUP | CONFIRM, pc);
}
static void
newl3state(pc, 4);
if (ret)
l3dss1_std_ie_err(pc, ret);
- pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+ L3L4(pc->st, CC_ALERTING | INDICATION, pc);
}
static void
newl3state(pc, 6);
if (err) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err(pc, err);
- pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+ L3L4(pc->st, CC_SETUP | INDICATION, pc);
}
static void
L3DelTimer(&pc->timer);
if (ret)
l3dss1_std_ie_err(pc, ret);
- pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+ L3L4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
}
static void
return;
memcpy(skb_put(skb, l), tmp, l);
l3_msg(pc->st, DL_DATA | REQUEST, skb);
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
newl3state(pc, 0);
dss1_release_l3_process(pc);
}
l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
else
l3dss1_message(pc, MT_RELEASE_COMPLETE);
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
newl3state(pc, 0);
dss1_release_l3_process(pc);
}
{
newl3state(pc, 9);
l3dss1_message(pc, MT_CALL_PROCEEDING);
- pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc);
+ L3L4(pc->st, CC_PROCEED_SEND | INDICATION, pc);
}
static void
if (err)
l3dss1_std_ie_err(pc, err);
if (ERR_IE_COMPREHENSION != err)
- pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
+ L3L4(pc->st, CC_PROGRESS | INDICATION, pc);
}
static void
if (err)
l3dss1_std_ie_err(pc, err);
if (ERR_IE_COMPREHENSION != err)
- pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
+ L3L4(pc->st, CC_NOTIFY | INDICATION, pc);
}
static void
if ((p = findie(p, skb->len, 0x70, 0))) {
iecpy(tmp, p, 1);
strcat(pc->para.setup.eazmsn, tmp);
- pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+ L3L4(pc->st, CC_MORE_INFO | INDICATION, pc);
}
L3AddTimer(&pc->timer, T302, CC_T302);
}
/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
* set down layer 3 without sending any message
*/
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
newl3state(pc, 0);
dss1_release_l3_process(pc);
} else {
- pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
+ L3L4(pc->st, CC_IGNORE | INDICATION, pc);
}
}
pc->para.loc = 0;
pc->para.cause = 28; /* invalid number */
l3dss1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+ L3L4(pc->st, CC_SETUP_ERR, pc);
}
static void
} else {
L3DelTimer(&pc->timer);
l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
- pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+ L3L4(pc->st, CC_NOSETUP_RSP, pc);
dss1_release_l3_process(pc);
}
}
pc->para.loc = 0;
pc->para.cause = 102;
l3dss1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+ L3L4(pc->st, CC_SETUP_ERR, pc);
}
pc->para.loc = 0;
pc->para.cause = 102;
l3dss1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+ L3L4(pc->st, CC_SETUP_ERR, pc);
}
static void
pc->para.loc = 0;
pc->para.cause = 102;
l3dss1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+ L3L4(pc->st, CC_CONNECT_ERR, pc);
}
static void
l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg)
{
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+ L3L4(pc->st, CC_RELEASE_ERR, pc);
dss1_release_l3_process(pc);
}
L3DelTimer(&pc->timer);
pc->para.cause = 102; /* Timer expiry */
pc->para.loc = 0; /* local */
- pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+ L3L4(pc->st, CC_RESUME_ERR, pc);
newl3state(pc, 19);
l3dss1_message(pc, MT_RELEASE);
L3AddTimer(&pc->timer, T308, CC_T308_1);
L3DelTimer(&pc->timer);
pc->para.cause = 102; /* Timer expiry */
pc->para.loc = 0; /* local */
- pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+ L3L4(pc->st, CC_SUSPEND_ERR, pc);
newl3state(pc, 10);
}
l3dss1_restart(struct l3_process *pc, u_char pr, void *arg)
{
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
dss1_release_l3_process(pc);
}
* if received MT_STATUS with cause == 111 and call
* state == 0, then we must set down layer 3
*/
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
newl3state(pc, 0);
dss1_release_l3_process(pc);
}
L3DelTimer(&pc->timer);
newl3state(pc, 0);
pc->para.cause = NO_CAUSE;
- pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+ L3L4(pc->st, CC_SUSPEND | CONFIRM, pc);
/* We don't handle suspend_ack for IE errors now */
if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
if (pc->debug & L3_DEB_WARN)
return;
}
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+ L3L4(pc->st, CC_SUSPEND_ERR, pc);
newl3state(pc, 10);
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err(pc, ret);
return;
}
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+ L3L4(pc->st, CC_RESUME | CONFIRM, pc);
newl3state(pc, 10);
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err(pc, ret);
return;
}
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+ L3L4(pc->st, CC_RESUME_ERR, pc);
newl3state(pc, 0);
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err(pc, ret);
up = pc->st->l3.proc;
while (up) {
if ((ri & 7) == 7)
- up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+ L4L3(up->st, CC_RESTART | REQUEST, up);
else if (up->para.bchannel == chan)
- up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+ L4L3(up->st, CC_RESTART | REQUEST, up);
up = up->next;
}
p = tmp;
pc->para.cause = 0x29; /* Temporary failure */
pc->para.loc = 0;
l3dss1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+ L3L4(pc->st, CC_SETUP_ERR, pc);
}
static void
newl3state(pc, 0);
pc->para.cause = 0x1b; /* Destination out of order */
pc->para.loc = 0;
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
release_l3_process(pc);
}
char tmp[64];
int i;
- st->lli.l4l3 = dss1down;
- st->lli.l4l3_proto = l3dss1_cmd_global;
- st->l2.l2l3 = dss1up;
+ st->l3.l4l3 = dss1down;
+ st->l3.l4l3_proto = l3dss1_cmd_global;
+ st->l3.l2l3 = dss1up;
st->l3.l3ml3 = dss1man;
st->l3.N303 = 1;
st->prot.dss1.last_invoke_id = 0;
pc->prot.ni1.remote_result = 0; /* success */
pc->prot.ni1.invoke_id = 0;
pc->redir_result = pc->prot.ni1.remote_result;
- st->l3.l3l4(st, CC_REDIR | INDICATION, pc); } /* Diversion successful */
+ L3L4(st, CC_REDIR | INDICATION, pc); } /* Diversion successful */
else
l3_debug(st,"return error unknown identifier");
break;
pc->prot.ni1.remote_result = err_ret; /* result */
pc->prot.ni1.invoke_id = 0;
pc->redir_result = pc->prot.ni1.remote_result;
- st->l3.l3l4(st, CC_REDIR | INDICATION, pc);
+ L3L4(st, CC_REDIR | INDICATION, pc);
} /* Deflection error */
else
l3_debug(st,"return result unknown identifier");
pc->para.cause = NO_CAUSE;
StopAllL3Timer(pc);
newl3state(pc, 0);
- pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+ L3L4(pc->st, CC_RELEASE | CONFIRM, pc);
ni1_release_l3_process(pc);
}
L3AddTimer(&pc->timer, T310, CC_T310);
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
l3ni1_std_ie_err(pc, ret);
- pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+ L3L4(pc->st, CC_PROCEEDING | INDICATION, pc);
}
static void
L3AddTimer(&pc->timer, T304, CC_T304);
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
l3ni1_std_ie_err(pc, ret);
- pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+ L3L4(pc->st, CC_MORE_INFO | INDICATION, pc);
}
static void
if (cause)
newl3state(pc, 19);
if (11 != ret)
- pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+ L3L4(pc->st, CC_DISCONNECT | INDICATION, pc);
else if (!cause)
l3ni1_release_req(pc, pr, NULL);
if (cause) {
/* here should inserted COLP handling KKe */
if (ret)
l3ni1_std_ie_err(pc, ret);
- pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+ L3L4(pc->st, CC_SETUP | CONFIRM, pc);
}
static void
newl3state(pc, 4);
if (ret)
l3ni1_std_ie_err(pc, ret);
- pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+ L3L4(pc->st, CC_ALERTING | INDICATION, pc);
}
static void
newl3state(pc, 6);
if (err) /* STATUS for none mandatory IE errors after actions are taken */
l3ni1_std_ie_err(pc, err);
- pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+ L3L4(pc->st, CC_SETUP | INDICATION, pc);
}
static void
L3DelTimer(&pc->timer);
if (ret)
l3ni1_std_ie_err(pc, ret);
- pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+ L3L4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
}
static void
return;
memcpy(skb_put(skb, l), tmp, l);
l3_msg(pc->st, DL_DATA | REQUEST, skb);
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
newl3state(pc, 0);
ni1_release_l3_process(pc);
}
l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
else
l3ni1_message(pc, MT_RELEASE_COMPLETE);
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
newl3state(pc, 0);
ni1_release_l3_process(pc);
}
{
newl3state(pc, 9);
l3ni1_message(pc, MT_CALL_PROCEEDING);
- pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc);
+ L3L4(pc->st, CC_PROCEED_SEND | INDICATION, pc);
}
static void
if (err)
l3ni1_std_ie_err(pc, err);
if (ERR_IE_COMPREHENSION != err)
- pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
+ L3L4(pc->st, CC_PROGRESS | INDICATION, pc);
}
static void
if (err)
l3ni1_std_ie_err(pc, err);
if (ERR_IE_COMPREHENSION != err)
- pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
+ L3L4(pc->st, CC_NOTIFY | INDICATION, pc);
}
static void
if ((p = findie(p, skb->len, 0x70, 0))) {
iecpy(tmp, p, 1);
strcat(pc->para.setup.eazmsn, tmp);
- pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+ L3L4(pc->st, CC_MORE_INFO | INDICATION, pc);
}
L3AddTimer(&pc->timer, T302, CC_T302);
}
/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
* set down layer 3 without sending any message
*/
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
newl3state(pc, 0);
ni1_release_l3_process(pc);
} else {
- pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
+ L3L4(pc->st, CC_IGNORE | INDICATION, pc);
}
}
pc->para.loc = 0;
pc->para.cause = 28; /* invalid number */
l3ni1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+ L3L4(pc->st, CC_SETUP_ERR, pc);
}
static void
} else {
L3DelTimer(&pc->timer);
l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
- pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+ L3L4(pc->st, CC_NOSETUP_RSP, pc);
ni1_release_l3_process(pc);
}
}
pc->para.loc = 0;
pc->para.cause = 102;
l3ni1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+ L3L4(pc->st, CC_SETUP_ERR, pc);
}
pc->para.loc = 0;
pc->para.cause = 102;
l3ni1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+ L3L4(pc->st, CC_SETUP_ERR, pc);
}
static void
pc->para.loc = 0;
pc->para.cause = 102;
l3ni1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+ L3L4(pc->st, CC_CONNECT_ERR, pc);
}
static void
l3ni1_t308_2(struct l3_process *pc, u_char pr, void *arg)
{
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+ L3L4(pc->st, CC_RELEASE_ERR, pc);
ni1_release_l3_process(pc);
}
L3DelTimer(&pc->timer);
pc->para.cause = 102; /* Timer expiry */
pc->para.loc = 0; /* local */
- pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+ L3L4(pc->st, CC_RESUME_ERR, pc);
newl3state(pc, 19);
l3ni1_message(pc, MT_RELEASE);
L3AddTimer(&pc->timer, T308, CC_T308_1);
L3DelTimer(&pc->timer);
pc->para.cause = 102; /* Timer expiry */
pc->para.loc = 0; /* local */
- pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+ L3L4(pc->st, CC_SUSPEND_ERR, pc);
newl3state(pc, 10);
}
l3ni1_restart(struct l3_process *pc, u_char pr, void *arg)
{
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
ni1_release_l3_process(pc);
}
* if received MT_STATUS with cause == 111 and call
* state == 0, then we must set down layer 3
*/
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
newl3state(pc, 0);
ni1_release_l3_process(pc);
}
L3DelTimer(&pc->timer);
newl3state(pc, 0);
pc->para.cause = NO_CAUSE;
- pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+ L3L4(pc->st, CC_SUSPEND | CONFIRM, pc);
/* We don't handle suspend_ack for IE errors now */
if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
if (pc->debug & L3_DEB_WARN)
return;
}
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+ L3L4(pc->st, CC_SUSPEND_ERR, pc);
newl3state(pc, 10);
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
l3ni1_std_ie_err(pc, ret);
return;
}
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+ L3L4(pc->st, CC_RESUME | CONFIRM, pc);
newl3state(pc, 10);
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
l3ni1_std_ie_err(pc, ret);
return;
}
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+ L3L4(pc->st, CC_RESUME_ERR, pc);
newl3state(pc, 0);
if (ret) /* STATUS for none mandatory IE errors after actions are taken */
l3ni1_std_ie_err(pc, ret);
up = pc->st->l3.proc;
while (up) {
if ((ri & 7) == 7)
- up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+ L4L3(up->st, CC_RESTART | REQUEST, up);
else if (up->para.bchannel == chan)
- up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+ L4L3(up->st, CC_RESTART | REQUEST, up);
up = up->next;
}
pc->para.cause = 0x29; /* Temporary failure */
pc->para.loc = 0;
l3ni1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+ L3L4(pc->st, CC_SETUP_ERR, pc);
}
static void
newl3state(pc, 0);
pc->para.cause = 0x1b; /* Destination out of order */
pc->para.loc = 0;
- pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ L3L4(pc->st, CC_RELEASE | INDICATION, pc);
release_l3_process(pc);
}
{
printk( KERN_ERR "SPID not supplied in EAZMSN %s\n", pChan->setup.eazmsn );
newl3state( pc, 0 );
- pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+ L3L2( pc->st, DL_RELEASE | REQUEST, NULL );
return;
}
L3DelTimer( &pc->timer );
L3AddTimer( &pc->timer, TSPID, CC_TSPID );
- pc->st->l3.l3l2( pc->st, DL_DATA | REQUEST, skb );
+ L3L2( pc->st, DL_DATA | REQUEST, skb );
}
static void l3ni1_spid_send( struct l3_process *pc, u_char pr, void *arg )
printk( KERN_ERR "SPID not accepted\n" );
newl3state( pc, 0 );
- pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+ L3L2( pc->st, DL_RELEASE | REQUEST, NULL );
}
}
char tmp[64];
int i;
- st->lli.l4l3 = ni1down;
- st->lli.l4l3_proto = l3ni1_cmd_global;
- st->l2.l2l3 = ni1up;
+ st->l3.l4l3 = ni1down;
+ st->l3.l4l3_proto = l3ni1_cmd_global;
+ st->l3.l2l3 = ni1up;
st->l3.l3ml3 = ni1man;
st->l3.N303 = 1;
st->prot.ni1.last_invoke_id = 0;
case (PH_PULL | REQUEST):
if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
mode_tiger(st->l1.bcs, 0, st->l1.bc);
- st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
break;
}
}
if (open_tigerstate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
- st->l2.l2l1 = tiger_l2l1;
+ st->l1.l2l1 = tiger_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
if (level & __debug_variable) dump_iso_packet(__FUNCTION__,urb)
static void __attribute__((unused))
-dump_iso_packet(const char *name,urb_t *urb)
+dump_iso_packet(const char *name, struct urb *urb)
{
int i,j;
int len,ofs;
unsigned int num_packets, packet_offset;
int len, buf_size, bytes_sent;
struct sk_buff *skb;
- iso_packet_descriptor_t *desc;
+ struct usb_iso_packet_descriptor *desc;
if (d_out->fsm.state != ST_DOUT_NORMAL)
return;
struct usb_interface_descriptor *altsetting;
struct usb_endpoint_descriptor *endpoint;
int status;
- urb_t *urb;
+ struct urb *urb;
u_char *buf;
DBG(1,"");
*/
int st5481_isoc_flatten(struct urb *urb)
{
- iso_packet_descriptor_t *pipd,*pend;
+ struct usb_iso_packet_descriptor *pipd,*pend;
unsigned char *src,*dst;
unsigned int len;
bp[2] = ri & 0xff;
bp[3] = m_id;
bp[4] = (tei << 1) | 1;
- st->l2.l2l1(st, PH_DATA | REQUEST, skb);
+ L2L1(st, PH_DATA | REQUEST, skb);
}
static void
} else if (ri == st->ma.ri) {
FsmDelTimer(&st->ma.t202, 1);
FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
- st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei);
+ L3L2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei);
cs = (struct IsdnCardState *) st->l1.hardware;
cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
}
if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
FsmDelTimer(&st->ma.t202, 5);
FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
- st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0);
+ L3L2(st, MDL_REMOVE | REQUEST, 0);
cs = (struct IsdnCardState *) st->l1.hardware;
cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
}
FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3);
} else {
st->ma.tei_m.printdebug(&st->ma.tei_m, "assign req failed");
- st->l3.l3l2(st, MDL_ERROR | RESPONSE, 0);
+ L3L2(st, MDL_ERROR | RESPONSE, 0);
cs = (struct IsdnCardState *) st->l1.hardware;
cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
FsmChangeState(fi, ST_TEI_NOP);
} else {
st->ma.tei_m.printdebug(&st->ma.tei_m,
"verify req for tei %d failed", st->l2.tei);
- st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0);
+ L3L2(st, MDL_REMOVE | REQUEST, 0);
cs = (struct IsdnCardState *) st->l1.hardware;
cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
FsmChangeState(fi, ST_TEI_NOP);
if (st->ma.debug)
st->ma.tei_m.printdebug(&st->ma.tei_m,
"fixed assign tei %d", st->l2.tei);
- st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei);
+ L3L2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei);
cs = (struct IsdnCardState *) st->l1.hardware;
cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
}
debugl1(cs, "D-Channel Busy cleared");
stptr = cs->stlist;
while (stptr != NULL) {
- stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+ L1L2(stptr, PH_PAUSE | CONFIRM, NULL);
stptr = stptr->next;
}
}
#endif
if (!cs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
stptr = cs->stlist;
while (stptr != NULL) {
- stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+ L1L2(stptr, PH_PAUSE | INDICATION, NULL);
stptr = stptr->next;
}
} else {
case (PH_PULL | REQUEST):
if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ L1L2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
W6692Bmode(st->l1.bcs, 0, st->l1.bc);
- st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ L1L2(st, PH_DEACTIVATE | CONFIRM, NULL);
break;
}
}
if (open_w6692state(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
- st->l2.l2l1 = W6692_l2l1;
+ st->l1.l2l1 = W6692_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
#undef cfg
}
+static struct file_operations isdn_status_fops =
+{
+ owner: THIS_MODULE,
+ llseek: no_llseek,
+ read: isdn_status_read,
+ write: isdn_status_write,
+ poll: isdn_status_poll,
+ ioctl: isdn_status_ioctl,
+ open: isdn_status_open,
+ release: isdn_status_release,
+};
+
/*
* /dev/isdnctrlX
*/
#undef cfg
}
-/*
- *
- */
-
-static ssize_t
-isdn_read(struct file *file, char *buf, size_t count, loff_t * off)
-{
- uint minor = minor(file->f_dentry->d_inode->i_rdev);
-
- if (minor < ISDN_MINOR_CTRL)
- return -ENODEV;
-
- if (minor <= ISDN_MINOR_CTRLMAX)
- return isdn_ctrl_read(file, buf, count, off);
-
-#ifdef CONFIG_ISDN_PPP
- if (minor <= ISDN_MINOR_PPPMAX)
- return isdn_ppp_read(file, buf, count, off);
-#endif
-
- if (minor == ISDN_MINOR_STATUS)
- return isdn_status_read(file, buf, count, off);
-
- return -ENODEV;
-}
-
-static ssize_t
-isdn_write(struct file *file, const char *buf, size_t count, loff_t * off)
-{
- uint minor = minor(file->f_dentry->d_inode->i_rdev);
-
- if (minor < ISDN_MINOR_CTRL)
- return -ENODEV;
-
- if (minor <= ISDN_MINOR_CTRLMAX)
- return isdn_ctrl_write(file, buf, count, off);
-
-#ifdef CONFIG_ISDN_PPP
- if (minor <= ISDN_MINOR_PPPMAX)
- return isdn_ppp_write(file, buf, count, off);
-#endif
-
- if (minor == ISDN_MINOR_STATUS)
- return isdn_status_write(file, buf, count, off);
-
- return -ENODEV;
-}
-
-static unsigned int
-isdn_poll(struct file *file, poll_table * wait)
-{
- unsigned int minor = minor(file->f_dentry->d_inode->i_rdev);
-
- if (minor < ISDN_MINOR_CTRL)
- return POLLERR;
-
- if (minor <= ISDN_MINOR_CTRLMAX)
- return isdn_ctrl_poll(file, wait);
-
-#ifdef CONFIG_ISDN_PPP
- if (minor <= ISDN_MINOR_PPPMAX)
- return isdn_ppp_poll(file, wait);
-#endif
-
- if (minor == ISDN_MINOR_STATUS)
- return isdn_status_poll(file, wait);
-
- return POLLERR;
-}
-
-static int
-isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+static struct file_operations isdn_ctrl_fops =
{
- uint minor = minor(inode->i_rdev);
-
- if (minor == ISDN_MINOR_STATUS)
- return isdn_status_ioctl(inode, file, cmd, arg);
-
- if (minor < ISDN_MINOR_CTRL)
- return -ENODEV;
-
- if (minor <= ISDN_MINOR_CTRLMAX)
- return isdn_ctrl_ioctl(inode, file, cmd, arg);
-
-#ifdef CONFIG_ISDN_PPP
- if (minor <= ISDN_MINOR_PPPMAX)
- return isdn_ppp_ioctl(inode, file, cmd, arg);
-#endif
-
- return -ENODEV;
-}
+ owner: THIS_MODULE,
+ llseek: no_llseek,
+ read: isdn_ctrl_read,
+ write: isdn_ctrl_write,
+ poll: isdn_ctrl_poll,
+ ioctl: isdn_ctrl_ioctl,
+ open: isdn_ctrl_open,
+ release: isdn_ctrl_release,
+};
/*
- * Open the device code.
+ * file_operations for major 43, /dev/isdn*
+ * stolen from drivers/char/misc.c
*/
-static int
-isdn_open(struct inode *ino, struct file *filep)
-{
- uint minor = minor(ino->i_rdev);
-
- if (minor < ISDN_MINOR_CTRL)
- return -ENODEV;
-
- if (minor <= ISDN_MINOR_CTRLMAX)
- return isdn_ctrl_open(ino, filep);
-
- if (minor == ISDN_MINOR_STATUS)
- return isdn_status_open(ino, filep);
-
-#ifdef CONFIG_ISDN_PPP
- if (minor <= ISDN_MINOR_PPPMAX)
- return isdn_ppp_open(ino, filep);
-#endif
-
- return -ENODEV;
-}
static int
-isdn_close(struct inode *ino, struct file *filep)
+isdn_open(struct inode * inode, struct file * file)
{
- uint minor = minor(ino->i_rdev);
-
- if (minor == ISDN_MINOR_STATUS)
- return isdn_status_release(ino, filep);
-
- if (minor < ISDN_MINOR_CTRL)
- return -ENODEV;
-
- if (minor <= ISDN_MINOR_CTRLMAX)
- return isdn_ctrl_release(ino, filep);
-
+ int minor = minor(inode->i_rdev);
+ int err = -ENODEV;
+ struct file_operations *old_fops, *new_fops = NULL;
+
+ if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX)
+ new_fops = fops_get(&isdn_ctrl_fops);
#ifdef CONFIG_ISDN_PPP
- if (minor <= ISDN_MINOR_PPPMAX)
- return isdn_ppp_release(ino, filep);
+ else if (minor >= ISDN_MINOR_PPP && minor <= ISDN_MINOR_PPPMAX)
+ new_fops = fops_get(&isdn_ppp_fops);
#endif
+ else if (minor == ISDN_MINOR_STATUS)
+ new_fops = fops_get(&isdn_status_fops);
+
+ if (!new_fops)
+ goto out;
- return -ENODEV;
+ err = 0;
+ old_fops = file->f_op;
+ file->f_op = new_fops;
+ if (file->f_op->open) {
+ err = file->f_op->open(inode,file);
+ if (err) {
+ fops_put(file->f_op);
+ file->f_op = fops_get(old_fops);
+ }
+ }
+ fops_put(old_fops);
+
+ out:
+ return err;
}
static struct file_operations isdn_fops =
{
owner: THIS_MODULE,
- llseek: no_llseek,
- read: isdn_read,
- write: isdn_write,
- poll: isdn_poll,
- ioctl: isdn_ioctl,
open: isdn_open,
- release: isdn_close,
};
char *
sprintf (buf, "isdn%d", k);
dev->devfs_handle_isdnX[k] =
devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT,
- ISDN_MAJOR, ISDN_MINOR_B + k,0600 | S_IFCHR,
+ ISDN_MAJOR, k,0600 | S_IFCHR,
&isdn_fops, NULL);
sprintf (buf, "isdnctrl%d", k);
dev->devfs_handle_isdnctrlX[k] =
*
*/
-#include <linux/config.h>
+#include <linux/module.h>
#include <linux/isdn.h>
#include <linux/smp_lock.h>
#include <linux/poll.h>
* isdn_ppp_open
*/
-int
+static int
isdn_ppp_open(struct inode *ino, struct file *file)
{
uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
/*
* release ippp device
*/
-int
+static int
isdn_ppp_release(struct inode *ino, struct file *file)
{
uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
/*
* ippp device ioctl
*/
-int
+static int
isdn_ppp_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned long val;
return 0;
}
-unsigned int
+static unsigned int
isdn_ppp_poll(struct file *file, poll_table * wait)
{
unsigned int mask;
* reports, that there is data
*/
-int
-isdn_ppp_read(struct file *file, char *buf, int count, loff_t *off)
+static int
+isdn_ppp_read(struct file *file, char *buf, size_t count, loff_t *off)
{
struct ippp_struct *is;
struct ippp_buf_queue *b;
* ipppd wanna write a packet to the card .. non-blocking
*/
-int
-isdn_ppp_write(struct file *file, const char *buf, int count, loff_t *off)
+static int
+isdn_ppp_write(struct file *file, const char *buf, size_t count, loff_t *off)
{
isdn_net_local *lp;
struct ippp_struct *is;
return retval;
}
+struct file_operations isdn_ppp_fops =
+{
+ owner: THIS_MODULE,
+ llseek: no_llseek,
+ read: isdn_ppp_read,
+ write: isdn_ppp_write,
+ poll: isdn_ppp_poll,
+ ioctl: isdn_ppp_ioctl,
+ open: isdn_ppp_open,
+ release: isdn_ppp_release,
+};
+
/*
* init memory, structures etc.
*/
#include <linux/ppp_defs.h> /* for PPP_PROTOCOL */
#include <linux/isdn_ppp.h> /* for isdn_ppp info */
-extern int isdn_ppp_open(struct inode *, struct file *);
-extern int isdn_ppp_release(struct inode *, struct file *);
-extern int isdn_ppp_read(struct file *, char *, int, loff_t *off);
-extern int isdn_ppp_write(struct file *, const char *, int, loff_t *off);
-extern int isdn_ppp_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
-extern unsigned int isdn_ppp_poll(struct file *, struct poll_table_struct *);
+extern struct file_operations isdn_ppp_fops;
extern int isdn_ppp_init(void);
extern void isdn_ppp_cleanup(void);
struct cpia_sbuf {
char *data;
- urb_t *urb;
+ struct urb *urb;
};
#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100)
static int cpia_usb_open(void *privdata)
{
struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
- urb_t *urb;
+ struct urb *urb;
int ret, retval = 0, fx, err;
if (!ucpia)
bool 'Wireless LAN (non-hamradio)' CONFIG_NET_RADIO
if [ "$CONFIG_NET_RADIO" = "y" ]; then
dep_tristate ' STRIP (Metricom starmode radio IP)' CONFIG_STRIP $CONFIG_INET
- tristate ' AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN
tristate ' Aironet Arlan 655 & IC2200 DS support' CONFIG_ARLAN
tristate ' Aironet 4500/4800 series adapters' CONFIG_AIRONET4500
dep_tristate ' Aironet 4500/4800 ISA/PCI/PNP/365 support ' CONFIG_AIRONET4500_NONCS $CONFIG_AIRONET4500
obj-$(CONFIG_EEXPRESS_PRO) += eepro.o
obj-$(CONFIG_8139CP) += 8139cp.o
obj-$(CONFIG_8139TOO) += 8139too.o
-obj-$(CONFIG_WAVELAN) += wavelan.o
obj-$(CONFIG_ARLAN) += arlan.o arlan-proc.o
obj-$(CONFIG_ZNET) += znet.o
obj-$(CONFIG_LAN_SAA9730) += saa9730.o
//data[3] = mdio_read(ioaddr, data[0], data[1]);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
//mdio_write(ioaddr, data[0], data[1], data[2]);
return 0;
default:
miidata->out_value = mii_read (dev, phy_addr, miidata->reg_num);
break;
case SIOCDEVPRIVATE + 2:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
mii_write (dev, phy_addr, miidata->reg_num, miidata->in_value);
break;
case SIOCDEVPRIVATE + 3:
+++ /dev/null
-/*
- * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor.
- *
- * See:
- * Intel Microcommunications 1991
- * p1-1 to p1-37
- * Intel order No. 231658
- * ISBN 1-55512-119-5
- *
- * Unfortunately, the above chapter mentions neither
- * the System Configuration Pointer (SCP) nor the
- * Intermediate System Configuration Pointer (ISCP),
- * so we probably need to look elsewhere for the
- * whole story -- some recommend the "Intel LAN
- * Components manual" but I have neither a copy
- * nor a full reference. But "elsewhere" may be
- * in the same publication...
- * The description of a later device, the
- * "82596CA High-Performance 32-Bit Local Area Network
- * Coprocessor", (ibid. p1-38 to p1-109) does mention
- * the SCP and ISCP and also has an i82586 compatibility
- * mode. Even more useful is "AP-235 An 82586 Data Link
- * Driver" (ibid. p1-337 to p1-417).
- */
-
-#define I82586_MEMZ (64 * 1024)
-
-#define I82586_SCP_ADDR (I82586_MEMZ - sizeof(scp_t))
-
-#define ADDR_LEN 6
-#define I82586NULL 0xFFFF
-
-#define toff(t,p,f) (unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0)
-
-/*
- * System Configuration Pointer (SCP).
- */
-typedef struct scp_t scp_t;
-struct scp_t
-{
- unsigned short scp_sysbus; /* 82586 bus width: */
-#define SCP_SY_16BBUS (0x0 << 0) /* 16 bits */
-#define SCP_SY_8BBUS (0x1 << 0) /* 8 bits. */
- unsigned short scp_junk[2]; /* Unused */
- unsigned short scp_iscpl; /* lower 16 bits of ISCP_ADDR */
- unsigned short scp_iscph; /* upper 16 bits of ISCP_ADDR */
-};
-
-/*
- * Intermediate System Configuration Pointer (ISCP).
- */
-typedef struct iscp_t iscp_t;
-struct iscp_t
-{
- unsigned short iscp_busy; /* set by CPU before first CA, */
- /* cleared by 82586 after read. */
- unsigned short iscp_offset; /* offset of SCB */
- unsigned short iscp_basel; /* base of SCB */
- unsigned short iscp_baseh; /* " */
-};
-
-/*
- * System Control Block (SCB).
- * The 82586 writes its status to scb_status and then
- * raises an interrupt to alert the CPU.
- * The CPU writes a command to scb_command and
- * then issues a Channel Attention (CA) to alert the 82586.
- */
-typedef struct scb_t scb_t;
-struct scb_t
-{
- unsigned short scb_status; /* Status of 82586 */
-#define SCB_ST_INT (0xF << 12) /* Some of: */
-#define SCB_ST_CX (0x1 << 15) /* Cmd completed */
-#define SCB_ST_FR (0x1 << 14) /* Frame received */
-#define SCB_ST_CNA (0x1 << 13) /* Cmd unit not active */
-#define SCB_ST_RNR (0x1 << 12) /* Rcv unit not ready */
-#define SCB_ST_JUNK0 (0x1 << 11) /* 0 */
-#define SCB_ST_CUS (0x7 << 8) /* Cmd unit status */
-#define SCB_ST_CUS_IDLE (0 << 8) /* Idle */
-#define SCB_ST_CUS_SUSP (1 << 8) /* Suspended */
-#define SCB_ST_CUS_ACTV (2 << 8) /* Active */
-#define SCB_ST_JUNK1 (0x1 << 7) /* 0 */
-#define SCB_ST_RUS (0x7 << 4) /* Rcv unit status */
-#define SCB_ST_RUS_IDLE (0 << 4) /* Idle */
-#define SCB_ST_RUS_SUSP (1 << 4) /* Suspended */
-#define SCB_ST_RUS_NRES (2 << 4) /* No resources */
-#define SCB_ST_RUS_RDY (4 << 4) /* Ready */
- unsigned short scb_command; /* Next command */
-#define SCB_CMD_ACK_CX (0x1 << 15) /* Ack cmd completion */
-#define SCB_CMD_ACK_FR (0x1 << 14) /* Ack frame received */
-#define SCB_CMD_ACK_CNA (0x1 << 13) /* Ack CU not active */
-#define SCB_CMD_ACK_RNR (0x1 << 12) /* Ack RU not ready */
-#define SCB_CMD_JUNKX (0x1 << 11) /* Unused */
-#define SCB_CMD_CUC (0x7 << 8) /* Command Unit command */
-#define SCB_CMD_CUC_NOP (0 << 8) /* Nop */
-#define SCB_CMD_CUC_GO (1 << 8) /* Start cbl_offset */
-#define SCB_CMD_CUC_RES (2 << 8) /* Resume execution */
-#define SCB_CMD_CUC_SUS (3 << 8) /* Suspend " */
-#define SCB_CMD_CUC_ABT (4 << 8) /* Abort " */
-#define SCB_CMD_RESET (0x1 << 7) /* Reset chip (hardware) */
-#define SCB_CMD_RUC (0x7 << 4) /* Receive Unit command */
-#define SCB_CMD_RUC_NOP (0 << 4) /* Nop */
-#define SCB_CMD_RUC_GO (1 << 4) /* Start rfa_offset */
-#define SCB_CMD_RUC_RES (2 << 4) /* Resume reception */
-#define SCB_CMD_RUC_SUS (3 << 4) /* Suspend " */
-#define SCB_CMD_RUC_ABT (4 << 4) /* Abort " */
- unsigned short scb_cbl_offset; /* Offset of first command unit */
- /* Action Command */
- unsigned short scb_rfa_offset; /* Offset of first Receive */
- /* Frame Descriptor in the */
- /* Receive Frame Area */
- unsigned short scb_crcerrs; /* Properly aligned frames */
- /* received with a CRC error */
- unsigned short scb_alnerrs; /* Misaligned frames received */
- /* with a CRC error */
- unsigned short scb_rscerrs; /* Frames lost due to no space */
- unsigned short scb_ovrnerrs; /* Frames lost due to slow bus */
-};
-
-#define scboff(p,f) toff(scb_t, p, f)
-
-/*
- * The eight Action Commands.
- */
-typedef enum acmd_e acmd_e;
-enum acmd_e
-{
- acmd_nop = 0, /* Do nothing */
- acmd_ia_setup = 1, /* Load an (ethernet) address into the */
- /* 82586 */
- acmd_configure = 2, /* Update the 82586 operating parameters */
- acmd_mc_setup = 3, /* Load a list of (ethernet) multicast */
- /* addresses into the 82586 */
- acmd_transmit = 4, /* Transmit a frame */
- acmd_tdr = 5, /* Perform a Time Domain Reflectometer */
- /* test on the serial link */
- acmd_dump = 6, /* Copy 82586 registers to memory */
- acmd_diagnose = 7, /* Run an internal self test */
-};
-
-/*
- * Generic Action Command header.
- */
-typedef struct ach_t ach_t;
-struct ach_t
-{
- unsigned short ac_status; /* Command status: */
-#define AC_SFLD_C (0x1 << 15) /* Command completed */
-#define AC_SFLD_B (0x1 << 14) /* Busy executing */
-#define AC_SFLD_OK (0x1 << 13) /* Completed error free */
-#define AC_SFLD_A (0x1 << 12) /* Command aborted */
-#define AC_SFLD_FAIL (0x1 << 11) /* Selftest failed */
-#define AC_SFLD_S10 (0x1 << 10) /* No carrier sense */
- /* during transmission */
-#define AC_SFLD_S9 (0x1 << 9) /* Tx unsuccessful: */
- /* (stopped) lost CTS */
-#define AC_SFLD_S8 (0x1 << 8) /* Tx unsuccessful: */
- /* (stopped) slow DMA */
-#define AC_SFLD_S7 (0x1 << 7) /* Tx deferred: */
- /* other link traffic */
-#define AC_SFLD_S6 (0x1 << 6) /* Heart Beat: collision */
- /* detect after last tx */
-#define AC_SFLD_S5 (0x1 << 5) /* Tx stopped: */
- /* excessive collisions */
-#define AC_SFLD_MAXCOL (0xF << 0) /* Collision count */
- unsigned short ac_command; /* Command specifier: */
-#define AC_CFLD_EL (0x1 << 15) /* End of command list */
-#define AC_CFLD_S (0x1 << 14) /* Suspend on completion */
-#define AC_CFLD_I (0x1 << 13) /* Interrupt on completion */
-#define AC_CFLD_CMD (0x7 << 0) /* acmd_e */
- unsigned short ac_link; /* Next Action Command */
-};
-
-#define acoff(p,f) toff(ach_t, p, f)
-
-/*
- * The Nop Action Command.
- */
-typedef struct ac_nop_t ac_nop_t;
-struct ac_nop_t
-{
- ach_t nop_h;
-};
-
-/*
- * The IA-Setup Action Command.
- */
-typedef struct ac_ias_t ac_ias_t;
-struct ac_ias_t
-{
- ach_t ias_h;
- unsigned char ias_addr[ADDR_LEN]; /* The (ethernet) address */
-};
-
-/*
- * The Configure Action Command.
- */
-typedef struct ac_cfg_t ac_cfg_t;
-struct ac_cfg_t
-{
- ach_t cfg_h;
- unsigned char cfg_byte_cnt; /* Size foll data: 4-12 */
-#define AC_CFG_BYTE_CNT(v) (((v) & 0xF) << 0)
- unsigned char cfg_fifolim; /* FIFO threshold */
-#define AC_CFG_FIFOLIM(v) (((v) & 0xF) << 0)
- unsigned char cfg_byte8;
-#define AC_CFG_SAV_BF(v) (((v) & 0x1) << 7) /* Save rxd bad frames */
-#define AC_CFG_SRDY(v) (((v) & 0x1) << 6) /* SRDY/ARDY pin means */
- /* external sync. */
- unsigned char cfg_byte9;
-#define AC_CFG_ELPBCK(v) (((v) & 0x1) << 7) /* External loopback */
-#define AC_CFG_ILPBCK(v) (((v) & 0x1) << 6) /* Internal loopback */
-#define AC_CFG_PRELEN(v) (((v) & 0x3) << 4) /* Preamble length */
-#define AC_CFG_PLEN_2 0 /* 2 bytes */
-#define AC_CFG_PLEN_4 1 /* 4 bytes */
-#define AC_CFG_PLEN_8 2 /* 8 bytes */
-#define AC_CFG_PLEN_16 3 /* 16 bytes */
-#define AC_CFG_ALOC(v) (((v) & 0x1) << 3) /* Addr/len data is */
- /* explicit in buffers */
-#define AC_CFG_ADDRLEN(v) (((v) & 0x7) << 0) /* Bytes per address */
- unsigned char cfg_byte10;
-#define AC_CFG_BOFMET(v) (((v) & 0x1) << 7) /* Use alternate expo. */
- /* backoff method */
-#define AC_CFG_ACR(v) (((v) & 0x7) << 4) /* Accelerated cont. res. */
-#define AC_CFG_LINPRIO(v) (((v) & 0x7) << 0) /* Linear priority */
- unsigned char cfg_ifs; /* Interframe spacing */
- unsigned char cfg_slotl; /* Slot time (low byte) */
- unsigned char cfg_byte13;
-#define AC_CFG_RETRYNUM(v) (((v) & 0xF) << 4) /* Max. collision retry */
-#define AC_CFG_SLTTMHI(v) (((v) & 0x7) << 0) /* Slot time (high bits) */
- unsigned char cfg_byte14;
-#define AC_CFG_FLGPAD(v) (((v) & 0x1) << 7) /* Pad with HDLC flags */
-#define AC_CFG_BTSTF(v) (((v) & 0x1) << 6) /* Do HDLC bitstuffing */
-#define AC_CFG_CRC16(v) (((v) & 0x1) << 5) /* 16 bit CCITT CRC */
-#define AC_CFG_NCRC(v) (((v) & 0x1) << 4) /* Insert no CRC */
-#define AC_CFG_TNCRS(v) (((v) & 0x1) << 3) /* Tx even if no carrier */
-#define AC_CFG_MANCH(v) (((v) & 0x1) << 2) /* Manchester coding */
-#define AC_CFG_BCDIS(v) (((v) & 0x1) << 1) /* Disable broadcast */
-#define AC_CFG_PRM(v) (((v) & 0x1) << 0) /* Promiscuous mode */
- unsigned char cfg_byte15;
-#define AC_CFG_ICDS(v) (((v) & 0x1) << 7) /* Internal collision */
- /* detect source */
-#define AC_CFG_CDTF(v) (((v) & 0x7) << 4) /* Collision detect */
- /* filter in bit times */
-#define AC_CFG_ICSS(v) (((v) & 0x1) << 3) /* Internal carrier */
- /* sense source */
-#define AC_CFG_CSTF(v) (((v) & 0x7) << 0) /* Carrier sense */
- /* filter in bit times */
- unsigned short cfg_min_frm_len;
-#define AC_CFG_MNFRM(v) (((v) & 0xFF) << 0) /* Min. bytes/frame (<= 255) */
-};
-
-/*
- * The MC-Setup Action Command.
- */
-typedef struct ac_mcs_t ac_mcs_t;
-struct ac_mcs_t
-{
- ach_t mcs_h;
- unsigned short mcs_cnt; /* No. of bytes of MC addresses */
-#if 0
- unsigned char mcs_data[ADDR_LEN]; /* The first MC address .. */
- ...
-#endif
-};
-
-#define I82586_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */
-
-/*
- * The Transmit Action Command.
- */
-typedef struct ac_tx_t ac_tx_t;
-struct ac_tx_t
-{
- ach_t tx_h;
- unsigned short tx_tbd_offset; /* Address of list of buffers. */
-#if 0
-Linux packets are passed down with the destination MAC address
-and length/type field already prepended to the data,
-so we do not need to insert it. Consistent with this
-we must also set the AC_CFG_ALOC(..) flag during the
-ac_cfg_t action command.
- unsigned char tx_addr[ADDR_LEN]; /* The frame dest. address */
- unsigned short tx_length; /* The frame length */
-#endif /* 0 */
-};
-
-/*
- * The Time Domain Reflectometer Action Command.
- */
-typedef struct ac_tdr_t ac_tdr_t;
-struct ac_tdr_t
-{
- ach_t tdr_h;
- unsigned short tdr_result; /* Result. */
-#define AC_TDR_LNK_OK (0x1 << 15) /* No link problem */
-#define AC_TDR_XCVR_PRB (0x1 << 14) /* Txcvr cable problem */
-#define AC_TDR_ET_OPN (0x1 << 13) /* Open on the link */
-#define AC_TDR_ET_SRT (0x1 << 12) /* Short on the link */
-#define AC_TDR_TIME (0x7FF << 0) /* Distance to problem */
- /* site in transmit */
- /* clock cycles */
-};
-
-/*
- * The Dump Action Command.
- */
-typedef struct ac_dmp_t ac_dmp_t;
-struct ac_dmp_t
-{
- ach_t dmp_h;
- unsigned short dmp_offset; /* Result. */
-};
-
-/*
- * Size of the result of the dump command.
- */
-#define DUMPBYTES 170
-
-/*
- * The Diagnose Action Command.
- */
-typedef struct ac_dgn_t ac_dgn_t;
-struct ac_dgn_t
-{
- ach_t dgn_h;
-};
-
-/*
- * Transmit Buffer Descriptor (TBD).
- */
-typedef struct tbd_t tbd_t;
-struct tbd_t
-{
- unsigned short tbd_status; /* Written by the CPU */
-#define TBD_STATUS_EOF (0x1 << 15) /* This TBD is the */
- /* last for this frame */
-#define TBD_STATUS_ACNT (0x3FFF << 0) /* Actual count of data */
- /* bytes in this buffer */
- unsigned short tbd_next_bd_offset; /* Next in list */
- unsigned short tbd_bufl; /* Buffer address (low) */
- unsigned short tbd_bufh; /* " " (high) */
-};
-
-/*
- * Receive Buffer Descriptor (RBD).
- */
-typedef struct rbd_t rbd_t;
-struct rbd_t
-{
- unsigned short rbd_status; /* Written by the 82586 */
-#define RBD_STATUS_EOF (0x1 << 15) /* This RBD is the */
- /* last for this frame */
-#define RBD_STATUS_F (0x1 << 14) /* ACNT field is valid */
-#define RBD_STATUS_ACNT (0x3FFF << 0) /* Actual no. of data */
- /* bytes in this buffer */
- unsigned short rbd_next_rbd_offset; /* Next rbd in list */
- unsigned short rbd_bufl; /* Data pointer (low) */
- unsigned short rbd_bufh; /* " " (high) */
- unsigned short rbd_el_size; /* EL+Data buf. size */
-#define RBD_EL (0x1 << 15) /* This BD is the */
- /* last in the list */
-#define RBD_SIZE (0x3FFF << 0) /* No. of bytes the */
- /* buffer can hold */
-};
-
-#define rbdoff(p,f) toff(rbd_t, p, f)
-
-/*
- * Frame Descriptor (FD).
- */
-typedef struct fd_t fd_t;
-struct fd_t
-{
- unsigned short fd_status; /* Written by the 82586 */
-#define FD_STATUS_C (0x1 << 15) /* Completed storing frame */
-#define FD_STATUS_B (0x1 << 14) /* FD was consumed by RU */
-#define FD_STATUS_OK (0x1 << 13) /* Frame rxd successfully */
-#define FD_STATUS_S11 (0x1 << 11) /* CRC error */
-#define FD_STATUS_S10 (0x1 << 10) /* Alignment error */
-#define FD_STATUS_S9 (0x1 << 9) /* Ran out of resources */
-#define FD_STATUS_S8 (0x1 << 8) /* Rx DMA overrun */
-#define FD_STATUS_S7 (0x1 << 7) /* Frame too short */
-#define FD_STATUS_S6 (0x1 << 6) /* No EOF flag */
- unsigned short fd_command; /* Command */
-#define FD_COMMAND_EL (0x1 << 15) /* Last FD in list */
-#define FD_COMMAND_S (0x1 << 14) /* Suspend RU after rx */
- unsigned short fd_link_offset; /* Next FD */
- unsigned short fd_rbd_offset; /* First RBD (data) */
- /* Prepared by CPU, */
- /* updated by 82586 */
-#if 0
-I think the rest is unused since we
-have set AC_CFG_ALOC(..). However, just
-in case, we leave the space.
-#endif /* 0 */
- unsigned char fd_dest[ADDR_LEN]; /* Destination address */
- /* Written by 82586 */
- unsigned char fd_src[ADDR_LEN]; /* Source address */
- /* Written by 82586 */
- unsigned short fd_length; /* Frame length or type */
- /* Written by 82586 */
-};
-
-#define fdoff(p,f) toff(fd_t, p, f)
-
-/*
- * This software may only be used and distributed
- * according to the terms of the GNU General Public License.
- *
- * For more details, see wavelan.c.
- */
bool ' Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO
if [ "$CONFIG_NET_PCMCIA_RADIO" = "y" ]; then
dep_tristate ' Aviator/Raytheon 2.4MHz wireless support' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA
- dep_tristate ' Xircom Netwave AirSurfer wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA
- dep_tristate ' AT&T/Lucent Wavelan wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA
dep_tristate ' Aironet 4500/4800 PCMCIA support' CONFIG_AIRONET4500_CS $CONFIG_AIRONET4500 $CONFIG_PCMCIA
fi
fi
# 16-bit wireless client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
-obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o
-obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o
obj-$(CONFIG_AIRONET4500_CS) += aironet4500_cs.o
# Cardbus client drivers
+++ /dev/null
-/*
- * Definitions for Intel 82593 CSMA/CD Core LAN Controller
- * The definitions are taken from the 1992 users manual with Intel
- * order number 297125-001.
- *
- * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp
- *
- * Copyright 1994, Anders Klemets <klemets@it.kth.se>
- *
- * This software may be freely distributed for noncommercial purposes
- * as long as this notice is retained.
- *
- * HISTORY
- * i82593.h,v
- * Revision 1.1 1996/07/17 15:23:12 root
- * Initial revision
- *
- * Revision 1.3 1995/04/05 15:13:58 adj
- * Initial alpha release
- *
- * Revision 1.2 1994/06/16 23:57:31 klemets
- * Mirrored all the fields in the configuration block.
- *
- * Revision 1.1 1994/06/02 20:25:34 klemets
- * Initial revision
- *
- *
- */
-#ifndef _I82593_H
-#define _I82593_H
-
-/* Intel 82593 CSMA/CD Core LAN Controller */
-
-/* Port 0 Command Register definitions */
-
-/* Execution operations */
-#define OP0_NOP 0 /* CHNL = 0 */
-#define OP0_SWIT_TO_PORT_1 0 /* CHNL = 1 */
-#define OP0_IA_SETUP 1
-#define OP0_CONFIGURE 2
-#define OP0_MC_SETUP 3
-#define OP0_TRANSMIT 4
-#define OP0_TDR 5
-#define OP0_DUMP 6
-#define OP0_DIAGNOSE 7
-#define OP0_TRANSMIT_NO_CRC 9
-#define OP0_RETRANSMIT 12
-#define OP0_ABORT 13
-/* Reception operations */
-#define OP0_RCV_ENABLE 8
-#define OP0_RCV_DISABLE 10
-#define OP0_STOP_RCV 11
-/* Status pointer control operations */
-#define OP0_FIX_PTR 15 /* CHNL = 1 */
-#define OP0_RLS_PTR 15 /* CHNL = 0 */
-#define OP0_RESET 14
-
-#define CR0_CHNL (1 << 4) /* 0=Channel 0, 1=Channel 1 */
-#define CR0_STATUS_0 0x00
-#define CR0_STATUS_1 0x20
-#define CR0_STATUS_2 0x40
-#define CR0_STATUS_3 0x60
-#define CR0_INT_ACK (1 << 7) /* 0=No ack, 1=acknowledge */
-
-/* Port 0 Status Register definitions */
-
-#define SR0_NO_RESULT 0 /* dummy */
-#define SR0_EVENT_MASK 0x0f
-#define SR0_IA_SETUP_DONE 1
-#define SR0_CONFIGURE_DONE 2
-#define SR0_MC_SETUP_DONE 3
-#define SR0_TRANSMIT_DONE 4
-#define SR0_TDR_DONE 5
-#define SR0_DUMP_DONE 6
-#define SR0_DIAGNOSE_PASSED 7
-#define SR0_TRANSMIT_NO_CRC_DONE 9
-#define SR0_RETRANSMIT_DONE 12
-#define SR0_EXECUTION_ABORTED 13
-#define SR0_END_OF_FRAME 8
-#define SR0_RECEPTION_ABORTED 10
-#define SR0_DIAGNOSE_FAILED 15
-#define SR0_STOP_REG_HIT 11
-
-#define SR0_CHNL (1 << 4)
-#define SR0_EXECUTION (1 << 5)
-#define SR0_RECEPTION (1 << 6)
-#define SR0_INTERRUPT (1 << 7)
-#define SR0_BOTH_RX_TX (SR0_EXECUTION | SR0_RECEPTION)
-
-#define SR3_EXEC_STATE_MASK 0x03
-#define SR3_EXEC_IDLE 0
-#define SR3_TX_ABORT_IN_PROGRESS 1
-#define SR3_EXEC_ACTIVE 2
-#define SR3_ABORT_IN_PROGRESS 3
-#define SR3_EXEC_CHNL (1 << 2)
-#define SR3_STP_ON_NO_RSRC (1 << 3)
-#define SR3_RCVING_NO_RSRC (1 << 4)
-#define SR3_RCV_STATE_MASK 0x60
-#define SR3_RCV_IDLE 0x00
-#define SR3_RCV_READY 0x20
-#define SR3_RCV_ACTIVE 0x40
-#define SR3_RCV_STOP_IN_PROG 0x60
-#define SR3_RCV_CHNL (1 << 7)
-
-/* Port 1 Command Register definitions */
-
-#define OP1_NOP 0
-#define OP1_SWIT_TO_PORT_0 1
-#define OP1_INT_DISABLE 2
-#define OP1_INT_ENABLE 3
-#define OP1_SET_TS 5
-#define OP1_RST_TS 7
-#define OP1_POWER_DOWN 8
-#define OP1_RESET_RING_MNGMT 11
-#define OP1_RESET 14
-#define OP1_SEL_RST 15
-
-#define CR1_STATUS_4 0x00
-#define CR1_STATUS_5 0x20
-#define CR1_STATUS_6 0x40
-#define CR1_STOP_REG_UPDATE (1 << 7)
-
-/* Receive frame status bits */
-
-#define RX_RCLD (1 << 0)
-#define RX_IA_MATCH (1 << 1)
-#define RX_NO_AD_MATCH (1 << 2)
-#define RX_NO_SFD (1 << 3)
-#define RX_SRT_FRM (1 << 7)
-#define RX_OVRRUN (1 << 8)
-#define RX_ALG_ERR (1 << 10)
-#define RX_CRC_ERR (1 << 11)
-#define RX_LEN_ERR (1 << 12)
-#define RX_RCV_OK (1 << 13)
-#define RX_TYP_LEN (1 << 15)
-
-/* Transmit status bits */
-
-#define TX_NCOL_MASK 0x0f
-#define TX_FRTL (1 << 4)
-#define TX_MAX_COL (1 << 5)
-#define TX_HRT_BEAT (1 << 6)
-#define TX_DEFER (1 << 7)
-#define TX_UND_RUN (1 << 8)
-#define TX_LOST_CTS (1 << 9)
-#define TX_LOST_CRS (1 << 10)
-#define TX_LTCOL (1 << 11)
-#define TX_OK (1 << 13)
-#define TX_COLL (1 << 15)
-
-struct i82593_conf_block {
- u_char fifo_limit : 4,
- forgnesi : 1,
- fifo_32 : 1,
- d6mod : 1,
- throttle_enb : 1;
- u_char throttle : 6,
- cntrxint : 1,
- contin : 1;
- u_char addr_len : 3,
- acloc : 1,
- preamb_len : 2,
- loopback : 2;
- u_char lin_prio : 3,
- tbofstop : 1,
- exp_prio : 3,
- bof_met : 1;
- u_char : 4,
- ifrm_spc : 4;
- u_char : 5,
- slottim_low : 3;
- u_char slottim_hi : 3,
- : 1,
- max_retr : 4;
- u_char prmisc : 1,
- bc_dis : 1,
- : 1,
- crs_1 : 1,
- nocrc_ins : 1,
- crc_1632 : 1,
- : 1,
- crs_cdt : 1;
- u_char cs_filter : 3,
- crs_src : 1,
- cd_filter : 3,
- : 1;
- u_char : 2,
- min_fr_len : 6;
- u_char lng_typ : 1,
- lng_fld : 1,
- rxcrc_xf : 1,
- artx : 1,
- sarec : 1,
- tx_jabber : 1, /* why is this called max_len in the manual? */
- hash_1 : 1,
- lbpkpol : 1;
- u_char : 6,
- fdx : 1,
- : 1;
- u_char dummy_6 : 6, /* supposed to be ones */
- mult_ia : 1,
- dis_bof : 1;
- u_char dummy_1 : 1, /* supposed to be one */
- tx_ifs_retrig : 2,
- mc_all : 1,
- rcv_mon : 2,
- frag_acpt : 1,
- tstrttrs : 1;
- u_char fretx : 1,
- runt_eop : 1,
- hw_sw_pin : 1,
- big_endn : 1,
- syncrqs : 1,
- sttlen : 1,
- tx_eop : 1,
- rx_eop : 1;
- u_char rbuf_size : 5,
- rcvstop : 1,
- : 2;
-};
-
-#define I82593_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */
-
-#endif /* _I82593_H */
+++ /dev/null
-/*********************************************************************
- *
- * Filename: netwave_cs.c
- * Version: 0.4.1
- * Description: Netwave AirSurfer Wireless LAN PC Card driver
- * Status: Experimental.
- * Authors: John Markus Bjørndalen <johnm@cs.uit.no>
- * Dag Brattli <dagb@cs.uit.no>
- * David Hinds <dahinds@users.sourceforge.net>
- * Created at: A long time ago!
- * Modified at: Mon Nov 10 11:54:37 1997
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997 University of Tromsø, Norway
- *
- * Revision History:
- *
- * 08-Nov-97 15:14:47 John Markus Bjørndalen <johnm@cs.uit.no>
- * - Fixed some bugs in netwave_rx and cleaned it up a bit.
- * (One of the bugs would have destroyed packets when receiving
- * multiple packets per interrupt).
- * - Cleaned up parts of newave_hw_xmit.
- * - A few general cleanups.
- * 24-Oct-97 13:17:36 Dag Brattli <dagb@cs.uit.no>
- * - Fixed netwave_rx receive function (got updated docs)
- * Others:
- * - Changed name from xircnw to netwave, take a look at
- * http://www.netwave-wireless.com
- * - Some reorganizing of the code
- * - Removed possible race condition between interrupt handler and transmit
- * function
- * - Started to add wireless extensions, but still needs some coding
- * - Added watchdog for better handling of transmission timeouts
- * (hopefully this works better)
- ********************************************************************/
-
-/* To have statistics (just packets sent) define this */
-#undef NETWAVE_STATS
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/errno.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
-#ifdef CONFIG_NET_PCMCIA_RADIO
-#include <linux/wireless.h>
-#endif
-
-#include <pcmcia/version.h>
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/mem_op.h>
-
-#define NETWAVE_REGOFF 0x8000
-/* The Netwave IO registers, offsets to iobase */
-#define NETWAVE_REG_COR 0x0
-#define NETWAVE_REG_CCSR 0x2
-#define NETWAVE_REG_ASR 0x4
-#define NETWAVE_REG_IMR 0xa
-#define NETWAVE_REG_PMR 0xc
-#define NETWAVE_REG_IOLOW 0x6
-#define NETWAVE_REG_IOHI 0x7
-#define NETWAVE_REG_IOCONTROL 0x8
-#define NETWAVE_REG_DATA 0xf
-/* The Netwave Extended IO registers, offsets to RamBase */
-#define NETWAVE_EREG_ASCC 0x114
-#define NETWAVE_EREG_RSER 0x120
-#define NETWAVE_EREG_RSERW 0x124
-#define NETWAVE_EREG_TSER 0x130
-#define NETWAVE_EREG_TSERW 0x134
-#define NETWAVE_EREG_CB 0x100
-#define NETWAVE_EREG_SPCQ 0x154
-#define NETWAVE_EREG_SPU 0x155
-#define NETWAVE_EREG_LIF 0x14e
-#define NETWAVE_EREG_ISPLQ 0x156
-#define NETWAVE_EREG_HHC 0x158
-#define NETWAVE_EREG_NI 0x16e
-#define NETWAVE_EREG_MHS 0x16b
-#define NETWAVE_EREG_TDP 0x140
-#define NETWAVE_EREG_RDP 0x150
-#define NETWAVE_EREG_PA 0x160
-#define NETWAVE_EREG_EC 0x180
-#define NETWAVE_EREG_CRBP 0x17a
-#define NETWAVE_EREG_ARW 0x166
-
-/*
- * Commands used in the extended command buffer
- * NETWAVE_EREG_CB (0x100-0x10F)
- */
-#define NETWAVE_CMD_NOP 0x00
-#define NETWAVE_CMD_SRC 0x01
-#define NETWAVE_CMD_STC 0x02
-#define NETWAVE_CMD_AMA 0x03
-#define NETWAVE_CMD_DMA 0x04
-#define NETWAVE_CMD_SAMA 0x05
-#define NETWAVE_CMD_ER 0x06
-#define NETWAVE_CMD_DR 0x07
-#define NETWAVE_CMD_TL 0x08
-#define NETWAVE_CMD_SRP 0x09
-#define NETWAVE_CMD_SSK 0x0a
-#define NETWAVE_CMD_SMD 0x0b
-#define NETWAVE_CMD_SAPD 0x0c
-#define NETWAVE_CMD_SSS 0x11
-/* End of Command marker */
-#define NETWAVE_CMD_EOC 0x00
-
-/* ASR register bits */
-#define NETWAVE_ASR_RXRDY 0x80
-#define NETWAVE_ASR_TXBA 0x01
-
-#define TX_TIMEOUT ((32*HZ)/100)
-
-static const unsigned int imrConfRFU1 = 0x10; /* RFU interrupt mask, keep high */
-static const unsigned int imrConfIENA = 0x02; /* Interrupt enable */
-
-static const unsigned int corConfIENA = 0x01; /* Interrupt enable */
-static const unsigned int corConfLVLREQ = 0x40; /* Keep high */
-
-static const unsigned int rxConfRxEna = 0x80; /* Receive Enable */
-static const unsigned int rxConfMAC = 0x20; /* MAC host receive mode*/
-static const unsigned int rxConfPro = 0x10; /* Promiscuous */
-static const unsigned int rxConfAMP = 0x08; /* Accept Multicast Packets */
-static const unsigned int rxConfBcast = 0x04; /* Accept Broadcast Packets */
-
-static const unsigned int txConfTxEna = 0x80; /* Transmit Enable */
-static const unsigned int txConfMAC = 0x20; /* Host sends MAC mode */
-static const unsigned int txConfEUD = 0x10; /* Enable Uni-Data packets */
-static const unsigned int txConfKey = 0x02; /* Scramble data packets */
-static const unsigned int txConfLoop = 0x01; /* Loopback mode */
-
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-MODULE_PARM(pc_debug, "i");
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"netwave_cs.c 0.3.0 Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n";
-#else
-#define DEBUG(n, args...)
-#endif
-
-static dev_info_t dev_info = "netwave_cs";
-
-/*====================================================================*/
-
-/* Parameters that can be set with 'insmod' */
-
-/* Choose the domain, default is 0x100 */
-static u_int domain = 0x100;
-
-/* Scramble key, range from 0x0 to 0xffff.
- * 0x0 is no scrambling.
- */
-static u_int scramble_key = 0x0;
-
-/* Shared memory speed, in ns. The documentation states that
- * the card should not be read faster than every 400ns.
- * This timing should be provided by the HBA. If it becomes a
- * problem, try setting mem_speed to 400.
- */
-static int mem_speed;
-
-/* Bit map of interrupts to choose from */
-/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
-static u_int irq_mask = 0xdeb8;
-static int irq_list[4] = { -1 };
-
-MODULE_PARM(domain, "i");
-MODULE_PARM(scramble_key, "i");
-MODULE_PARM(mem_speed, "i");
-MODULE_PARM(irq_mask, "i");
-MODULE_PARM(irq_list, "1-4i");
-
-/*====================================================================*/
-
-/* PCMCIA (Card Services) related functions */
-static void netwave_release(u_long arg); /* Card removal */
-static int netwave_event(event_t event, int priority,
- event_callback_args_t *args);
-static void netwave_pcmcia_config(dev_link_t *arg); /* Runs after card
- insertion */
-static dev_link_t *netwave_attach(void); /* Create instance */
-static void netwave_detach(dev_link_t *); /* Destroy instance */
-static void netwave_flush_stale_links(void); /* Destroy all staled instances */
-
-/* Hardware configuration */
-static void netwave_doreset(ioaddr_t iobase, u_char* ramBase);
-static void netwave_reset(struct net_device *dev);
-
-/* Misc device stuff */
-static int netwave_open(struct net_device *dev); /* Open the device */
-static int netwave_close(struct net_device *dev); /* Close the device */
-static int netwave_config(struct net_device *dev, struct ifmap *map);
-
-/* Packet transmission and Packet reception */
-static int netwave_start_xmit( struct sk_buff *skb, struct net_device *dev);
-static int netwave_rx( struct net_device *dev);
-
-/* Interrupt routines */
-static void netwave_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static void netwave_watchdog(struct net_device *);
-
-/* Statistics */
-static void update_stats(struct net_device *dev);
-static struct net_device_stats *netwave_get_stats(struct net_device *dev);
-
-/* Wireless extensions */
-#ifdef WIRELESS_EXT
-static struct iw_statistics* netwave_get_wireless_stats(struct net_device *dev);
-#endif
-static int netwave_ioctl(struct net_device *, struct ifreq *, int);
-
-static void set_multicast_list(struct net_device *dev);
-
-/*
- A linked list of "instances" of the skeleton device. Each actual
- PCMCIA card corresponds to one device instance, and is described
- by one dev_link_t structure (defined in ds.h).
-
- You may not want to use a linked list for this -- for example, the
- memory card driver uses an array of dev_link_t pointers, where minor
- device numbers are used to derive the corresponding array index.
-*/
-static dev_link_t *dev_list;
-
-/*
- A dev_link_t structure has fields for most things that are needed
- to keep track of a socket, but there will usually be some device
- specific information that also needs to be kept track of. The
- 'priv' pointer in a dev_link_t structure can be used to point to
- a device-specific private data structure, like this.
-
- A driver needs to provide a dev_node_t structure for each device
- on a card. In some cases, there is only one device per card (for
- example, ethernet cards, modems). In other cases, there may be
- many actual or logical devices (SCSI adapters, memory cards with
- multiple partitions). The dev_node_t structures need to be kept
- in a linked list starting at the 'dev' field of a dev_link_t
- structure. We allocate them in the card's private data structure,
- because they generally can't be allocated dynamically.
-*/
-
-/* Wireless Extension Backward compatibility - Jean II
- * If the new wireless device private ioctl range is not defined,
- * default to standard device private ioctl range */
-#ifndef SIOCIWFIRSTPRIV
-#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
-#endif /* SIOCIWFIRSTPRIV */
-
-#define SIOCGIPSNAP SIOCIWFIRSTPRIV /* Site Survey Snapshot */
-/*#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1*/
-
-#define MAX_ESA 10
-
-typedef struct net_addr {
- u_char addr48[6];
-} net_addr;
-
-struct site_survey {
- u_short length;
- u_char struct_revision;
- u_char roaming_state;
-
- u_char sp_existsFlag;
- u_char sp_link_quality;
- u_char sp_max_link_quality;
- u_char linkQualityGoodFairBoundary;
- u_char linkQualityFairPoorBoundary;
- u_char sp_utilization;
- u_char sp_goodness;
- u_char sp_hotheadcount;
- u_char roaming_condition;
-
- net_addr sp;
- u_char numAPs;
- net_addr nearByAccessPoints[MAX_ESA];
-};
-
-typedef struct netwave_private {
- dev_link_t link;
- struct net_device dev;
- dev_node_t node;
- u_char *ramBase;
- int timeoutCounter;
- int lastExec;
- struct timer_list watchdog; /* To avoid blocking state */
- struct site_survey nss;
- struct net_device_stats stats;
-#ifdef WIRELESS_EXT
- struct iw_statistics iw_stats; /* Wireless stats */
-#endif
-} netwave_private;
-
-#ifdef NETWAVE_STATS
-static struct net_device_stats *netwave_get_stats(struct net_device *dev);
-#endif
-
-/*
- * The Netwave card is little-endian, so won't work for big endian
- * systems.
- */
-static inline unsigned short get_uint16(u_char* staddr)
-{
- return readw(staddr); /* Return only 16 bits */
-}
-
-static inline short get_int16(u_char* staddr)
-{
- return readw(staddr);
-}
-
-/**************************************************************************/
-
-static void cs_error(client_handle_t handle, int func, int ret)
-{
- error_info_t err = { func, ret };
- CardServices(ReportError, handle, &err);
-}
-
-/*
- * Wait until the WOC (Write Operation Complete) bit in the
- * ASR (Adapter Status Register) is asserted.
- * This should have aborted if it takes too long time.
- */
-static inline void wait_WOC(unsigned int iobase)
-{
- /* Spin lock */
- while ((inb(iobase + NETWAVE_REG_ASR) & 0x8) != 0x8) ;
-}
-
-#ifdef WIRELESS_EXT
-static void netwave_snapshot(netwave_private *priv, u_char *ramBase,
- ioaddr_t iobase) {
- u_short resultBuffer;
-
- /* if time since last snapshot is > 1 sec. (100 jiffies?) then take
- * new snapshot, else return cached data. This is the recommended rate.
- */
- if ( jiffies - priv->lastExec > 100) {
- /* Take site survey snapshot */
- /*printk( KERN_DEBUG "Taking new snapshot. %ld\n", jiffies -
- priv->lastExec); */
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_SSS, ramBase + NETWAVE_EREG_CB + 0);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
- wait_WOC(iobase);
-
- /* Get result and copy to cach */
- resultBuffer = readw(ramBase + NETWAVE_EREG_CRBP);
- copy_from_pc( &priv->nss, ramBase+resultBuffer,
- sizeof(struct site_survey));
- }
-}
-#endif
-
-#ifdef WIRELESS_EXT
-/*
- * Function netwave_get_wireless_stats (dev)
- *
- * Wireless extensions statistics
- *
- */
-static struct iw_statistics *netwave_get_wireless_stats(struct net_device *dev)
-{
- unsigned long flags;
- ioaddr_t iobase = dev->base_addr;
- netwave_private *priv = (netwave_private *) dev->priv;
- u_char *ramBase = priv->ramBase;
- struct iw_statistics* wstats;
-
- wstats = &priv->iw_stats;
-
- save_flags(flags);
- cli();
-
- netwave_snapshot( priv, ramBase, iobase);
-
- wstats->status = priv->nss.roaming_state;
- wstats->qual.qual = readb( ramBase + NETWAVE_EREG_SPCQ);
- wstats->qual.level = readb( ramBase + NETWAVE_EREG_ISPLQ);
- wstats->qual.noise = readb( ramBase + NETWAVE_EREG_SPU) & 0x3f;
- wstats->discard.nwid = 0L;
- wstats->discard.code = 0L;
- wstats->discard.misc = 0L;
-
- restore_flags(flags);
-
- return &priv->iw_stats;
-}
-#endif
-
-/*
- * Function netwave_attach (void)
- *
- * Creates an "instance" of the driver, allocating local data
- * structures for one device. The device is registered with Card
- * Services.
- *
- * The dev_link structure is initialized, but we don't actually
- * configure the card at this point -- we wait until we receive a
- * card insertion event.
- */
-static dev_link_t *netwave_attach(void)
-{
- client_reg_t client_reg;
- dev_link_t *link;
- struct net_device *dev;
- netwave_private *priv;
- int i, ret;
-
- DEBUG(0, "netwave_attach()\n");
-
- /* Perform some cleanup */
- netwave_flush_stale_links();
-
- /* Initialize the dev_link_t structure */
- priv = kmalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv) return NULL;
- memset(priv, 0, sizeof(*priv));
- link = &priv->link; dev = &priv->dev;
- link->priv = dev->priv = priv;
- link->release.function = &netwave_release;
- link->release.data = (u_long)link;
-
- /* The io structure describes IO port mapping */
- link->io.NumPorts1 = 16;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- /* link->io.NumPorts2 = 16;
- link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; */
- link->io.IOAddrLines = 5;
-
- /* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
- if (irq_list[0] == -1)
- link->irq.IRQInfo2 = irq_mask;
- else
- for (i = 0; i < 4; i++)
- link->irq.IRQInfo2 |= 1 << irq_list[i];
- link->irq.Handler = &netwave_interrupt;
-
- /* General socket configuration */
- link->conf.Attributes = CONF_ENABLE_IRQ;
- link->conf.Vcc = 50;
- link->conf.IntType = INT_MEMORY_AND_IO;
- link->conf.ConfigIndex = 1;
- link->conf.Present = PRESENT_OPTION;
-
- /* Netwave specific entries in the device structure */
- dev->hard_start_xmit = &netwave_start_xmit;
- dev->set_config = &netwave_config;
- dev->get_stats = &netwave_get_stats;
- dev->set_multicast_list = &set_multicast_list;
- /* wireless extensions */
-#ifdef WIRELESS_EXT
- dev->get_wireless_stats = &netwave_get_wireless_stats;
-#endif
- dev->do_ioctl = &netwave_ioctl;
-
- dev->tx_timeout = &netwave_watchdog;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- ether_setup(dev);
- dev->open = &netwave_open;
- dev->stop = &netwave_close;
- link->irq.Instance = dev;
-
- /* Register with Card Services */
- link->next = dev_list;
- dev_list = link;
- client_reg.dev_info = &dev_info;
- client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
- client_reg.EventMask =
- CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
- CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
- CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
- client_reg.event_handler = &netwave_event;
- client_reg.Version = 0x0210;
- client_reg.event_callback_args.client_data = link;
- ret = CardServices(RegisterClient, &link->handle, &client_reg);
- if (ret != 0) {
- cs_error(link->handle, RegisterClient, ret);
- netwave_detach(link);
- return NULL;
- }
-
- return link;
-} /* netwave_attach */
-
-/*
- * Function netwave_detach (link)
- *
- * This deletes a driver "instance". The device is de-registered
- * with Card Services. If it has been released, all local data
- * structures are freed. Otherwise, the structures will be freed
- * when the device is released.
- */
-static void netwave_detach(dev_link_t *link)
-{
- netwave_private *priv = link->priv;
- dev_link_t **linkp;
-
- DEBUG(0, "netwave_detach(0x%p)\n", link);
-
- /*
- If the device is currently configured and active, we won't
- actually delete it yet. Instead, it is marked so that when
- the release() function is called, that will trigger a proper
- detach().
- */
- del_timer(&link->release);
- if (link->state & DEV_CONFIG) {
- netwave_release((u_long) link);
- if (link->state & DEV_STALE_CONFIG) {
- DEBUG(1, "netwave_cs: detach postponed, '%s' still "
- "locked\n", link->dev->dev_name);
- link->state |= DEV_STALE_LINK;
- return;
- }
- }
-
- /* Break the link with Card Services */
- if (link->handle)
- CardServices(DeregisterClient, link->handle);
-
- /* Locate device structure */
- for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
- if (*linkp == link) break;
- if (*linkp == NULL)
- {
- DEBUG(1, "netwave_cs: detach fail, '%s' not in list\n",
- link->dev->dev_name);
- return;
- }
-
- /* Unlink device structure, free pieces */
- *linkp = link->next;
- if (link->dev)
- unregister_netdev(&priv->dev);
- kfree(priv);
-
-} /* netwave_detach */
-
-/*
- * Function netwave_flush_stale_links (void)
- *
- * This deletes all driver "instances" that need to be deleted.
- * Sometimes, netwave_detach can't be performed following a call from
- * cardmgr (device still open) and the device is put in a STALE_LINK
- * state.
- * This function is in charge of making the cleanup...
- */
-static void netwave_flush_stale_links(void)
-{
- dev_link_t * link; /* Current node in linked list */
- dev_link_t * next; /* Next node in linked list */
-
- DEBUG(1, "netwave_flush_stale_links(0x%p)\n", dev_list);
-
- /* Go through the list */
- for (link = dev_list; link; link = next) {
- next = link->next;
- /* Check if in need of being removed */
- if(link->state & DEV_STALE_LINK)
- netwave_detach(link);
- }
-} /* netwave_flush_stale_links */
-
-/*
- * Function netwave_ioctl (dev, rq, cmd)
- *
- * Perform ioctl : config & info stuff
- * This is the stuff that are treated the wireless extensions (iwconfig)
- *
- */
-static int netwave_ioctl(struct net_device *dev, /* ioctl device */
- struct ifreq *rq, /* Data passed */
- int cmd) /* Ioctl number */
-{
- unsigned long flags;
- int ret = 0;
-#ifdef WIRELESS_EXT
- ioaddr_t iobase = dev->base_addr;
- netwave_private *priv = (netwave_private *) dev->priv;
- u_char *ramBase = priv->ramBase;
- struct iwreq *wrq = (struct iwreq *) rq;
-#endif
-
- DEBUG(0, "%s: ->netwave_ioctl(cmd=0x%X)\n", dev->name, cmd);
-
- /* Disable interrupts & save flags */
- save_flags(flags);
- cli();
-
- /* Look what is the request */
- switch(cmd) {
- /* --------------- WIRELESS EXTENSIONS --------------- */
-#ifdef WIRELESS_EXT
- case SIOCGIWNAME:
- /* Get name */
- strcpy(wrq->u.name, "Netwave");
- break;
- case SIOCSIWNWID:
- /* Set domain */
-#if WIRELESS_EXT > 8
- if(!wrq->u.nwid.disabled) {
- domain = wrq->u.nwid.value;
-#else /* WIRELESS_EXT > 8 */
- if(wrq->u.nwid.on) {
- domain = wrq->u.nwid.nwid;
-#endif /* WIRELESS_EXT > 8 */
- printk( KERN_DEBUG "Setting domain to 0x%x%02x\n",
- (domain >> 8) & 0x01, domain & 0xff);
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0);
- writeb( domain & 0xff, ramBase + NETWAVE_EREG_CB + 1);
- writeb((domain >>8 ) & 0x01,ramBase + NETWAVE_EREG_CB+2);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
- } break;
- case SIOCGIWNWID:
- /* Read domain*/
-#if WIRELESS_EXT > 8
- wrq->u.nwid.value = domain;
- wrq->u.nwid.disabled = 0;
- wrq->u.nwid.fixed = 1;
-#else /* WIRELESS_EXT > 8 */
- wrq->u.nwid.nwid = domain;
- wrq->u.nwid.on = 1;
-#endif /* WIRELESS_EXT > 8 */
- break;
-#if WIRELESS_EXT > 8 /* Note : The API did change... */
- case SIOCGIWENCODE:
- /* Get scramble key */
- if(wrq->u.encoding.pointer != (caddr_t) 0)
- {
- char key[2];
- key[1] = scramble_key & 0xff;
- key[0] = (scramble_key>>8) & 0xff;
- wrq->u.encoding.flags = IW_ENCODE_ENABLED;
- wrq->u.encoding.length = 2;
- if(copy_to_user(wrq->u.encoding.pointer, key, 2))
- ret = -EFAULT;
- }
- break;
- case SIOCSIWENCODE:
- /* Set scramble key */
- if(wrq->u.encoding.pointer != (caddr_t) 0)
- {
- char key[2];
- if(copy_from_user(key, wrq->u.encoding.pointer, 2))
- {
- ret = -EFAULT;
- break;
- }
- scramble_key = (key[0] << 8) | key[1];
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
- writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
- writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
- }
- break;
- case SIOCGIWMODE:
- /* Mode of operation */
- if(domain & 0x100)
- wrq->u.mode = IW_MODE_INFRA;
- else
- wrq->u.mode = IW_MODE_ADHOC;
- break;
-#else /* WIRELESS_EXT > 8 */
- case SIOCGIWENCODE:
- /* Get scramble key */
- wrq->u.encoding.code = scramble_key;
- wrq->u.encoding.method = 1;
- break;
- case SIOCSIWENCODE:
- /* Set scramble key */
- scramble_key = wrq->u.encoding.code;
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
- writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
- writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
- break;
-#endif /* WIRELESS_EXT > 8 */
- case SIOCGIWRANGE:
- /* Basic checking... */
- if(wrq->u.data.pointer != (caddr_t) 0) {
- struct iw_range range;
-
- /* Set the length (very important for backward compatibility) */
- wrq->u.data.length = sizeof(struct iw_range);
-
- /* Set all the info we don't care or don't know about to zero */
- memset(&range, 0, sizeof(range));
-
-#if WIRELESS_EXT > 10
- /* Set the Wireless Extension versions */
- range.we_version_compiled = WIRELESS_EXT;
- range.we_version_source = 9; /* Nothing for us in v10 and v11 */
-#endif /* WIRELESS_EXT > 10 */
-
- /* Set information in the range struct */
- range.throughput = 450 * 1000; /* don't argue on this ! */
- range.min_nwid = 0x0000;
- range.max_nwid = 0x01FF;
-
- range.num_channels = range.num_frequency = 0;
-
- range.sensitivity = 0x3F;
- range.max_qual.qual = 255;
- range.max_qual.level = 255;
- range.max_qual.noise = 0;
-
-#if WIRELESS_EXT > 7
- range.num_bitrates = 1;
- range.bitrate[0] = 1000000; /* 1 Mb/s */
-#endif /* WIRELESS_EXT > 7 */
-
-#if WIRELESS_EXT > 8
- range.encoding_size[0] = 2; /* 16 bits scrambling */
- range.num_encoding_sizes = 1;
- range.max_encoding_tokens = 1; /* Only one key possible */
-#endif /* WIRELESS_EXT > 8 */
-
- /* Copy structure to the user buffer */
- if(copy_to_user(wrq->u.data.pointer, &range,
- sizeof(struct iw_range)))
- ret = -EFAULT;
- }
- break;
- case SIOCGIWPRIV:
- /* Basic checking... */
- if(wrq->u.data.pointer != (caddr_t) 0) {
- struct iw_priv_args priv[] =
- { /* cmd, set_args, get_args, name */
- { SIOCGIPSNAP, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 0,
- sizeof(struct site_survey),
- "getsitesurvey" },
- };
-
- /* Set the number of ioctl available */
- wrq->u.data.length = 1;
-
- /* Copy structure to the user buffer */
- if(copy_to_user(wrq->u.data.pointer, (u_char *) priv,
- sizeof(priv)))
- ret = -EFAULT;
- }
- break;
- case SIOCGIPSNAP:
- if(wrq->u.data.pointer != (caddr_t) 0) {
- /* Take snapshot of environment */
- netwave_snapshot( priv, ramBase, iobase);
- wrq->u.data.length = priv->nss.length;
- /* Copy structure to the user buffer */
- if(copy_to_user(wrq->u.data.pointer,
- (u_char *) &priv->nss,
- sizeof( struct site_survey)))
- {
- printk(KERN_DEBUG "Bad buffer!\n");
- break;
- }
-
- priv->lastExec = jiffies;
- }
- break;
-#endif
- default:
- ret = -EOPNOTSUPP;
- }
-
- /* ReEnable interrupts & restore flags */
- restore_flags(flags);
-
- return ret;
-}
-
-/*
- * Function netwave_pcmcia_config (link)
- *
- * netwave_pcmcia_config() is scheduled to run after a CARD_INSERTION
- * event is received, to configure the PCMCIA socket, and to make the
- * device available to the system.
- *
- */
-
-#define CS_CHECK(fn, args...) \
-while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
-
-static void netwave_pcmcia_config(dev_link_t *link) {
- client_handle_t handle = link->handle;
- netwave_private *priv = link->priv;
- struct net_device *dev = &priv->dev;
- tuple_t tuple;
- cisparse_t parse;
- int i, j, last_ret, last_fn;
- u_char buf[64];
- win_req_t req;
- memreq_t mem;
- u_char *ramBase = NULL;
-
- DEBUG(0, "netwave_pcmcia_config(0x%p)\n", link);
-
- /*
- This reads the card's CONFIG tuple to find its configuration
- registers.
- */
- tuple.Attributes = 0;
- tuple.TupleData = (cisdata_t *) buf;
- tuple.TupleDataMax = 64;
- tuple.TupleOffset = 0;
- tuple.DesiredTuple = CISTPL_CONFIG;
- CS_CHECK(GetFirstTuple, handle, &tuple);
- CS_CHECK(GetTupleData, handle, &tuple);
- CS_CHECK(ParseTuple, handle, &tuple, &parse);
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
-
- /* Configure card */
- link->state |= DEV_CONFIG;
-
- /*
- * Try allocating IO ports. This tries a few fixed addresses.
- * If you want, you can also read the card's config table to
- * pick addresses -- see the serial driver for an example.
- */
- for (i = j = 0x0; j < 0x400; j += 0x20) {
- link->io.BasePort1 = j ^ 0x300;
- i = CardServices(RequestIO, link->handle, &link->io);
- if (i == CS_SUCCESS) break;
- }
- if (i != CS_SUCCESS) {
- cs_error(link->handle, RequestIO, i);
- goto failed;
- }
-
- /*
- * Now allocate an interrupt line. Note that this does not
- * actually assign a handler to the interrupt.
- */
- CS_CHECK(RequestIRQ, handle, &link->irq);
-
- /*
- * This actually configures the PCMCIA socket -- setting up
- * the I/O windows and the interrupt mapping.
- */
- CS_CHECK(RequestConfiguration, handle, &link->conf);
-
- /*
- * Allocate a 32K memory window. Note that the dev_link_t
- * structure provides space for one window handle -- if your
- * device needs several windows, you'll need to keep track of
- * the handles in your private data structure, link->priv.
- */
- DEBUG(1, "Setting mem speed of %d\n", mem_speed);
-
- req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
- req.Base = 0; req.Size = 0x8000;
- req.AccessSpeed = mem_speed;
- link->win = (window_handle_t)link->handle;
- CS_CHECK(RequestWindow, &link->win, &req);
- mem.CardOffset = 0x20000; mem.Page = 0;
- CS_CHECK(MapMemPage, link->win, &mem);
-
- /* Store base address of the common window frame */
- ramBase = ioremap(req.Base, 0x8000);
- ((netwave_private*)dev->priv)->ramBase = ramBase;
-
- dev->irq = link->irq.AssignedIRQ;
- dev->base_addr = link->io.BasePort1;
- if (register_netdev(dev) != 0) {
- printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n");
- goto failed;
- }
-
- strcpy(priv->node.dev_name, dev->name);
- link->dev = &priv->node;
- link->state &= ~DEV_CONFIG_PENDING;
-
- /* Reset card before reading physical address */
- netwave_doreset(dev->base_addr, ramBase);
-
- /* Read the ethernet address and fill in the Netwave registers. */
- for (i = 0; i < 6; i++)
- dev->dev_addr[i] = readb(ramBase + NETWAVE_EREG_PA + i);
-
- printk(KERN_INFO "%s: Netwave: port %#3lx, irq %d, mem %lx id "
- "%c%c, hw_addr ", dev->name, dev->base_addr, dev->irq,
- (u_long) ramBase, (int) readb(ramBase+NETWAVE_EREG_NI),
- (int) readb(ramBase+NETWAVE_EREG_NI+1));
- for (i = 0; i < 6; i++)
- printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
-
- /* get revision words */
- printk(KERN_DEBUG "Netwave_reset: revision %04x %04x\n",
- get_uint16(ramBase + NETWAVE_EREG_ARW),
- get_uint16(ramBase + NETWAVE_EREG_ARW+2));
- return;
-
-cs_failed:
- cs_error(link->handle, last_fn, last_ret);
-failed:
- netwave_release((u_long)link);
-} /* netwave_pcmcia_config */
-
-/*
- * Function netwave_release (arg)
- *
- * After a card is removed, netwave_release() will unregister the net
- * device, and release the PCMCIA configuration. If the device is
- * still open, this will be postponed until it is closed.
- */
-static void netwave_release(u_long arg) {
- dev_link_t *link = (dev_link_t *)arg;
- netwave_private *priv = link->priv;
-
- DEBUG(0, "netwave_release(0x%p)\n", link);
-
- /*
- If the device is currently in use, we won't release until it
- is actually closed.
- */
- if (link->open) {
- printk(KERN_DEBUG "netwave_cs: release postponed, '%s' still open\n",
- link->dev->dev_name);
- link->state |= DEV_STALE_CONFIG;
- return;
- }
-
- /* Don't bother checking to see if these succeed or not */
- if (link->win) {
- iounmap(priv->ramBase);
- CardServices(ReleaseWindow, link->win);
- }
- CardServices(ReleaseConfiguration, link->handle);
- CardServices(ReleaseIO, link->handle, &link->io);
- CardServices(ReleaseIRQ, link->handle, &link->irq);
-
- link->state &= ~(DEV_CONFIG | DEV_STALE_CONFIG);
-
-} /* netwave_release */
-
-/*
- * Function netwave_event (event, priority, args)
- *
- * The card status event handler. Mostly, this schedules other
- * stuff to run after an event is received. A CARD_REMOVAL event
- * also sets some flags to discourage the net drivers from trying
- * to talk to the card any more.
- *
- * When a CARD_REMOVAL event is received, we immediately set a flag
- * to block future accesses to this device. All the functions that
- * actually access the device should check this flag to make sure
- * the card is still present.
- *
- */
-static int netwave_event(event_t event, int priority,
- event_callback_args_t *args) {
- dev_link_t *link = args->client_data;
- netwave_private *priv = link->priv;
- struct net_device *dev = &priv->dev;
-
- DEBUG(1, "netwave_event(0x%06x)\n", event);
-
- switch (event) {
- case CS_EVENT_REGISTRATION_COMPLETE:
- DEBUG(0, "netwave_cs: registration complete\n");
- break;
-
- case CS_EVENT_CARD_REMOVAL:
- link->state &= ~DEV_PRESENT;
- if (link->state & DEV_CONFIG) {
- netif_device_detach(dev);
- mod_timer(&link->release, jiffies + HZ/20);
- }
- break;
- case CS_EVENT_CARD_INSERTION:
- link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
- netwave_pcmcia_config( link);
- break;
- case CS_EVENT_PM_SUSPEND:
- link->state |= DEV_SUSPEND;
- /* Fall through... */
- case CS_EVENT_RESET_PHYSICAL:
- if (link->state & DEV_CONFIG) {
- if (link->open)
- netif_device_detach(dev);
- CardServices(ReleaseConfiguration, link->handle);
- }
- break;
- case CS_EVENT_PM_RESUME:
- link->state &= ~DEV_SUSPEND;
- /* Fall through... */
- case CS_EVENT_CARD_RESET:
- if (link->state & DEV_CONFIG) {
- CardServices(RequestConfiguration, link->handle, &link->conf);
- if (link->open) {
- netwave_reset(dev);
- netif_device_attach(dev);
- }
- }
- break;
- }
- return 0;
-} /* netwave_event */
-
-/*
- * Function netwave_doreset (ioBase, ramBase)
- *
- * Proper hardware reset of the card.
- */
-static void netwave_doreset(ioaddr_t ioBase, u_char* ramBase) {
- /* Reset card */
- wait_WOC(ioBase);
- outb(0x80, ioBase + NETWAVE_REG_PMR);
- writeb(0x08, ramBase + NETWAVE_EREG_ASCC); /* Bit 3 is WOC */
- outb(0x0, ioBase + NETWAVE_REG_PMR); /* release reset */
-}
-
-/*
- * Function netwave_reset (dev)
- *
- * Reset and restore all of the netwave registers
- */
-static void netwave_reset(struct net_device *dev) {
- /* u_char state; */
- netwave_private *priv = (netwave_private*) dev->priv;
- u_char *ramBase = priv->ramBase;
- ioaddr_t iobase = dev->base_addr;
-
- DEBUG(0, "netwave_reset: Done with hardware reset\n");
-
- priv->timeoutCounter = 0;
-
- /* Reset card */
- netwave_doreset(iobase, ramBase);
- printk(KERN_DEBUG "netwave_reset: Done with hardware reset\n");
-
- /* Write a NOP to check the card */
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_NOP, ramBase + NETWAVE_EREG_CB + 0);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
-
- /* Set receive conf */
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0);
- writeb(rxConfRxEna + rxConfBcast, ramBase + NETWAVE_EREG_CB + 1);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
-
- /* Set transmit conf */
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_STC, ramBase + NETWAVE_EREG_CB + 0);
- writeb(txConfTxEna, ramBase + NETWAVE_EREG_CB + 1);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
-
- /* Now set the MU Domain */
- printk(KERN_DEBUG "Setting domain to 0x%x%02x\n", (domain >> 8) & 0x01, domain & 0xff);
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0);
- writeb(domain & 0xff, ramBase + NETWAVE_EREG_CB + 1);
- writeb((domain>>8) & 0x01, ramBase + NETWAVE_EREG_CB + 2);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
-
- /* Set scramble key */
- printk(KERN_DEBUG "Setting scramble key to 0x%x\n", scramble_key);
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
- writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
- writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
-
- /* Enable interrupts, bit 4 high to keep unused
- * source from interrupting us, bit 2 high to
- * set interrupt enable, 567 to enable TxDN,
- * RxErr and RxRdy
- */
- wait_WOC(iobase);
- outb(imrConfIENA+imrConfRFU1, iobase + NETWAVE_REG_IMR);
-
- /* Hent 4 bytes fra 0x170. Skal vaere 0a,29,88,36
- * waitWOC
- * skriv 80 til d000:3688
- * sjekk om det ble 80
- */
-
- /* Enable Receiver */
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_ER, ramBase + NETWAVE_EREG_CB + 0);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
-
- /* Set the IENA bit in COR */
- wait_WOC(iobase);
- outb(corConfIENA + corConfLVLREQ, iobase + NETWAVE_REG_COR);
-}
-
-/*
- * Function netwave_config (dev, map)
- *
- * Configure device, this work is done by netwave_pcmcia_config when a
- * card is inserted
- */
-static int netwave_config(struct net_device *dev, struct ifmap *map) {
- return 0;
-}
-
-/*
- * Function netwave_hw_xmit (data, len, dev)
- */
-static int netwave_hw_xmit(unsigned char* data, int len,
- struct net_device* dev) {
- unsigned long flags;
- unsigned int TxFreeList,
- curBuff,
- MaxData,
- DataOffset;
- int tmpcount;
-
- netwave_private *priv = (netwave_private *) dev->priv;
- u_char* ramBase = priv->ramBase;
- ioaddr_t iobase = dev->base_addr;
-
- /* Disable interrupts & save flags */
- save_flags(flags);
- cli();
-
- /* Check if there are transmit buffers available */
- wait_WOC(iobase);
- if ((inb(iobase+NETWAVE_REG_ASR) & NETWAVE_ASR_TXBA) == 0) {
- /* No buffers available */
- printk(KERN_DEBUG "netwave_hw_xmit: %s - no xmit buffers available.\n",
- dev->name);
- restore_flags(flags);
- return 1;
- }
-
- priv->stats.tx_bytes += len;
-
- DEBUG(3, "Transmitting with SPCQ %x SPU %x LIF %x ISPLQ %x\n",
- readb(ramBase + NETWAVE_EREG_SPCQ),
- readb(ramBase + NETWAVE_EREG_SPU),
- readb(ramBase + NETWAVE_EREG_LIF),
- readb(ramBase + NETWAVE_EREG_ISPLQ));
-
- /* Now try to insert it into the adapters free memory */
- wait_WOC(iobase);
- TxFreeList = get_uint16(ramBase + NETWAVE_EREG_TDP);
- MaxData = get_uint16(ramBase + NETWAVE_EREG_TDP+2);
- DataOffset = get_uint16(ramBase + NETWAVE_EREG_TDP+4);
-
- DEBUG(3, "TxFreeList %x, MaxData %x, DataOffset %x\n",
- TxFreeList, MaxData, DataOffset);
-
- /* Copy packet to the adapter fragment buffers */
- curBuff = TxFreeList;
- tmpcount = 0;
- while (tmpcount < len) {
- int tmplen = len - tmpcount;
- copy_to_pc(ramBase + curBuff + DataOffset, data + tmpcount,
- (tmplen < MaxData) ? tmplen : MaxData);
- tmpcount += MaxData;
-
- /* Advance to next buffer */
- curBuff = get_uint16(ramBase + curBuff);
- }
-
- /* Now issue transmit list */
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_TL, ramBase + NETWAVE_EREG_CB + 0);
- writeb(len & 0xff, ramBase + NETWAVE_EREG_CB + 1);
- writeb((len>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
-
- restore_flags( flags);
- return 0;
-}
-
-static int netwave_start_xmit(struct sk_buff *skb, struct net_device *dev) {
- /* This flag indicate that the hardware can't perform a transmission.
- * Theoritically, NET3 check it before sending a packet to the driver,
- * but in fact it never do that and pool continuously.
- * As the watchdog will abort too long transmissions, we are quite safe...
- */
-
- netif_stop_queue(dev);
-
- {
- short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- unsigned char* buf = skb->data;
-
- if (netwave_hw_xmit( buf, length, dev) == 1) {
- /* Some error, let's make them call us another time? */
- netif_start_queue(dev);
- }
- dev->trans_start = jiffies;
- }
- dev_kfree_skb(skb);
-
- return 0;
-} /* netwave_start_xmit */
-
-/*
- * Function netwave_interrupt (irq, dev_id, regs)
- *
- * This function is the interrupt handler for the Netwave card. This
- * routine will be called whenever:
- * 1. A packet is received.
- * 2. A packet has successfully been transferred and the unit is
- * ready to transmit another packet.
- * 3. A command has completed execution.
- */
-static void netwave_interrupt(int irq, void* dev_id, struct pt_regs *regs) {
- ioaddr_t iobase;
- u_char *ramBase;
- struct net_device *dev = (struct net_device *)dev_id;
- struct netwave_private *priv = dev->priv;
- dev_link_t *link = &priv->link;
- int i;
-
- if (!netif_device_present(dev))
- return;
-
- iobase = dev->base_addr;
- ramBase = priv->ramBase;
-
- /* Now find what caused the interrupt, check while interrupts ready */
- for (i = 0; i < 10; i++) {
- u_char status;
-
- wait_WOC(iobase);
- if (!(inb(iobase+NETWAVE_REG_CCSR) & 0x02))
- break; /* None of the interrupt sources asserted */
-
- status = inb(iobase + NETWAVE_REG_ASR);
-
- if (!DEV_OK(link)) {
- DEBUG(1, "netwave_interrupt: Interrupt with status 0x%x "
- "from removed or suspended card!\n", status);
- break;
- }
-
- /* RxRdy */
- if (status & 0x80) {
- netwave_rx(dev);
- /* wait_WOC(iobase); */
- /* RxRdy cannot be reset directly by the host */
- }
- /* RxErr */
- if (status & 0x40) {
- u_char rser;
-
- rser = readb(ramBase + NETWAVE_EREG_RSER);
-
- if (rser & 0x04) {
- ++priv->stats.rx_dropped;
- ++priv->stats.rx_crc_errors;
- }
- if (rser & 0x02)
- ++priv->stats.rx_frame_errors;
-
- /* Clear the RxErr bit in RSER. RSER+4 is the
- * write part. Also clear the RxCRC (0x04) and
- * RxBig (0x02) bits if present */
- wait_WOC(iobase);
- writeb(0x40 | (rser & 0x06), ramBase + NETWAVE_EREG_RSER + 4);
-
- /* Write bit 6 high to ASCC to clear RxErr in ASR,
- * WOC must be set first!
- */
- wait_WOC(iobase);
- writeb(0x40, ramBase + NETWAVE_EREG_ASCC);
-
- /* Remember to count up priv->stats on error packets */
- ++priv->stats.rx_errors;
- }
- /* TxDN */
- if (status & 0x20) {
- int txStatus;
-
- txStatus = readb(ramBase + NETWAVE_EREG_TSER);
- DEBUG(3, "Transmit done. TSER = %x id %x\n",
- txStatus, readb(ramBase + NETWAVE_EREG_TSER + 1));
-
- if (txStatus & 0x20) {
- /* Transmitting was okay, clear bits */
- wait_WOC(iobase);
- writeb(0x2f, ramBase + NETWAVE_EREG_TSER + 4);
- ++priv->stats.tx_packets;
- }
-
- if (txStatus & 0xd0) {
- if (txStatus & 0x80) {
- ++priv->stats.collisions; /* Because of /proc/net/dev*/
- /* ++priv->stats.tx_aborted_errors; */
- /* printk("Collision. %ld\n", jiffies - dev->trans_start); */
- }
- if (txStatus & 0x40)
- ++priv->stats.tx_carrier_errors;
- /* 0x80 TxGU Transmit giveup - nine times and no luck
- * 0x40 TxNOAP No access point. Discarded packet.
- * 0x10 TxErr Transmit error. Always set when
- * TxGU and TxNOAP is set. (Those are the only ones
- * to set TxErr).
- */
- DEBUG(3, "netwave_interrupt: TxDN with error status %x\n",
- txStatus);
-
- /* Clear out TxGU, TxNOAP, TxErr and TxTrys */
- wait_WOC(iobase);
- writeb(0xdf & txStatus, ramBase+NETWAVE_EREG_TSER+4);
- ++priv->stats.tx_errors;
- }
- DEBUG(3, "New status is TSER %x ASR %x\n",
- readb(ramBase + NETWAVE_EREG_TSER),
- inb(iobase + NETWAVE_REG_ASR));
-
- netif_wake_queue(dev);
- }
- /* TxBA, this would trigger on all error packets received */
- /* if (status & 0x01) {
- DEBUG(4, "Transmit buffers available, %x\n", status);
- }
- */
- }
-} /* netwave_interrupt */
-
-/*
- * Function netwave_watchdog (a)
- *
- * Watchdog : when we start a transmission, we set a timer in the
- * kernel. If the transmission complete, this timer is disabled. If
- * it expire, we reset the card.
- *
- */
-static void netwave_watchdog(struct net_device *dev) {
-
- DEBUG(1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name);
- netwave_reset(dev);
- dev->trans_start = jiffies;
- netif_wake_queue(dev);
-} /* netwave_watchdog */
-
-static struct net_device_stats *netwave_get_stats(struct net_device *dev) {
- netwave_private *priv = (netwave_private*)dev->priv;
-
- update_stats(dev);
-
- DEBUG(2, "netwave: SPCQ %x SPU %x LIF %x ISPLQ %x MHS %x rxtx %x"
- " %x tx %x %x %x %x\n",
- readb(priv->ramBase + NETWAVE_EREG_SPCQ),
- readb(priv->ramBase + NETWAVE_EREG_SPU),
- readb(priv->ramBase + NETWAVE_EREG_LIF),
- readb(priv->ramBase + NETWAVE_EREG_ISPLQ),
- readb(priv->ramBase + NETWAVE_EREG_MHS),
- readb(priv->ramBase + NETWAVE_EREG_EC + 0xe),
- readb(priv->ramBase + NETWAVE_EREG_EC + 0xf),
- readb(priv->ramBase + NETWAVE_EREG_EC + 0x18),
- readb(priv->ramBase + NETWAVE_EREG_EC + 0x19),
- readb(priv->ramBase + NETWAVE_EREG_EC + 0x1a),
- readb(priv->ramBase + NETWAVE_EREG_EC + 0x1b));
-
- return &priv->stats;
-}
-
-static void update_stats(struct net_device *dev) {
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
-/* netwave_private *priv = (netwave_private*) dev->priv;
- priv->stats.rx_packets = readb(priv->ramBase + 0x18e);
- priv->stats.tx_packets = readb(priv->ramBase + 0x18f); */
-
- restore_flags(flags);
-}
-
-static int netwave_rx(struct net_device *dev) {
- netwave_private *priv = (netwave_private*)(dev->priv);
- u_char *ramBase = priv->ramBase;
- ioaddr_t iobase = dev->base_addr;
- u_char rxStatus;
- struct sk_buff *skb = NULL;
- unsigned int curBuffer,
- rcvList;
- int rcvLen;
- int tmpcount = 0;
- int dataCount, dataOffset;
- int i;
- u_char *ptr;
-
- DEBUG(3, "xinw_rx: Receiving ... \n");
-
- /* Receive max 10 packets for now. */
- for (i = 0; i < 10; i++) {
- /* Any packets? */
- wait_WOC(iobase);
- rxStatus = readb(ramBase + NETWAVE_EREG_RSER);
- if ( !( rxStatus & 0x80)) /* No more packets */
- break;
-
- /* Check if multicast/broadcast or other */
- /* multicast = (rxStatus & 0x20); */
-
- /* The receive list pointer and length of the packet */
- wait_WOC(iobase);
- rcvLen = get_int16( ramBase + NETWAVE_EREG_RDP);
- rcvList = get_uint16( ramBase + NETWAVE_EREG_RDP + 2);
-
- if (rcvLen < 0) {
- printk(KERN_DEBUG "netwave_rx: Receive packet with len %d\n",
- rcvLen);
- return 0;
- }
-
- skb = dev_alloc_skb(rcvLen+5);
- if (skb == NULL) {
- DEBUG(1, "netwave_rx: Could not allocate an sk_buff of "
- "length %d\n", rcvLen);
- ++priv->stats.rx_dropped;
- /* Tell the adapter to skip the packet */
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
- return 0;
- }
-
- skb_reserve( skb, 2); /* Align IP on 16 byte */
- skb_put( skb, rcvLen);
- skb->dev = dev;
-
- /* Copy packet fragments to the skb data area */
- ptr = (u_char*) skb->data;
- curBuffer = rcvList;
- tmpcount = 0;
- while ( tmpcount < rcvLen) {
- /* Get length and offset of current buffer */
- dataCount = get_uint16( ramBase+curBuffer+2);
- dataOffset = get_uint16( ramBase+curBuffer+4);
-
- copy_from_pc( ptr + tmpcount,
- ramBase+curBuffer+dataOffset, dataCount);
-
- tmpcount += dataCount;
-
- /* Point to next buffer */
- curBuffer = get_uint16(ramBase + curBuffer);
- }
-
- skb->protocol = eth_type_trans(skb,dev);
- /* Queue packet for network layer */
- netif_rx(skb);
-
- dev->last_rx = jiffies;
- priv->stats.rx_packets++;
- priv->stats.rx_bytes += rcvLen;
-
- /* Got the packet, tell the adapter to skip it */
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
- DEBUG(3, "Packet reception ok\n");
- }
- return 0;
-}
-
-static int netwave_open(struct net_device *dev) {
- netwave_private *priv = dev->priv;
- dev_link_t *link = &priv->link;
-
- DEBUG(1, "netwave_open: starting.\n");
-
- if (!DEV_OK(link))
- return -ENODEV;
-
- link->open++;
- MOD_INC_USE_COUNT;
-
- netif_start_queue(dev);
- netwave_reset(dev);
-
- return 0;
-}
-
-static int netwave_close(struct net_device *dev) {
- netwave_private *priv = (netwave_private *)dev->priv;
- dev_link_t *link = &priv->link;
-
- DEBUG(1, "netwave_close: finishing.\n");
-
- link->open--;
- netif_stop_queue(dev);
- if (link->state & DEV_STALE_CONFIG)
- mod_timer(&link->release, jiffies + HZ/20);
-
- MOD_DEC_USE_COUNT;
- return 0;
-}
-
-static int __init init_netwave_cs(void) {
- servinfo_t serv;
-
- DEBUG(0, "%s\n", version);
-
- CardServices(GetCardServicesInfo, &serv);
- if (serv.Revision != CS_RELEASE_CODE) {
- printk("netwave_cs: Card Services release does not match!\n");
- return -1;
- }
-
- register_pccard_driver(&dev_info, &netwave_attach, &netwave_detach);
-
- return 0;
-}
-
-static void __exit exit_netwave_cs(void) {
- DEBUG(1, "netwave_cs: unloading\n");
-
- unregister_pccard_driver(&dev_info);
-
- /* Do some cleanup of the device list */
- netwave_flush_stale_links();
- if(dev_list != NULL) /* Critical situation */
- printk("netwave_cs: devices remaining when removing module\n");
-}
-
-module_init(init_netwave_cs);
-module_exit(exit_netwave_cs);
-
-/* Set or clear the multicast filter for this adaptor.
- num_addrs == -1 Promiscuous mode, receive all packets
- num_addrs == 0 Normal mode, clear multicast list
- num_addrs > 0 Multicast mode, receive normal and MC packets, and do
- best-effort filtering.
- */
-static void set_multicast_list(struct net_device *dev)
-{
- ioaddr_t iobase = dev->base_addr;
- u_char* ramBase = ((netwave_private*) dev->priv)->ramBase;
- u_char rcvMode = 0;
-
-#ifdef PCMCIA_DEBUG
- if (pc_debug > 2) {
- static int old;
- if (old != dev->mc_count) {
- old = dev->mc_count;
- DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
- dev->name, dev->mc_count);
- }
- }
-#endif
-
- if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) {
- /* Multicast Mode */
- rcvMode = rxConfRxEna + rxConfAMP + rxConfBcast;
- } else if (dev->flags & IFF_PROMISC) {
- /* Promiscous mode */
- rcvMode = rxConfRxEna + rxConfPro + rxConfAMP + rxConfBcast;
- } else {
- /* Normal mode */
- rcvMode = rxConfRxEna + rxConfBcast;
- }
-
- /* printk("netwave set_multicast_list: rcvMode to %x\n", rcvMode);*/
- /* Now set receive mode */
- wait_WOC(iobase);
- writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0);
- writeb(rcvMode, ramBase + NETWAVE_EREG_CB + 1);
- writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
-}
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * Wavelan Pcmcia driver
- *
- * Jean II - HPLB '96
- *
- * Reorganization and extension of the driver.
- * Original copyright follow. See wavelan_cs.h for details.
- *
- * This file contain the declarations of the Wavelan hardware. Note that
- * the Pcmcia Wavelan include a i82593 controller (see definitions in
- * file i82593.h).
- *
- * The main difference between the pcmcia hardware and the ISA one is
- * the Ethernet Controller (i82593 instead of i82586). The i82593 allow
- * only one send buffer. The PSA (Parameter Storage Area : EEprom for
- * permanent storage of various info) is memory mapped, but not the
- * MMI (Modem Management Interface).
- */
-
-/*
- * Definitions for the AT&T GIS (formerly NCR) WaveLAN PCMCIA card:
- * An Ethernet-like radio transceiver controlled by an Intel 82593
- * coprocessor.
- *
- *
- ****************************************************************************
- * Copyright 1995
- * Anthony D. Joseph
- * Massachusetts Institute of Technology
- *
- * Permission to use, copy, modify, and distribute this program
- * for any purpose and without fee is hereby granted, provided
- * that this copyright and permission notice appear on all copies
- * and supporting documentation, the name of M.I.T. not be used
- * in advertising or publicity pertaining to distribution of the
- * program without specific prior permission, and notice be given
- * in supporting documentation that copying and distribution is
- * by permission of M.I.T. M.I.T. makes no representations about
- * the suitability of this software for any purpose. It is pro-
- * vided "as is" without express or implied warranty.
- ****************************************************************************
- *
- *
- * Credits:
- * Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht for
- * providing extremely useful information about WaveLAN PCMCIA hardware
- *
- * This driver is based upon several other drivers, in particular:
- * David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
- * Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
- * Anders Klemets' PCMCIA WaveLAN adapter driver
- * Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
- */
-
-#ifndef _WAVELAN_H
-#define _WAVELAN_H
-
-/************************** MAGIC NUMBERS ***************************/
-
-/* The detection of the wavelan card is made by reading the MAC address
- * from the card and checking it. If you have a non AT&T product (OEM,
- * like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this
- * part to accomodate your hardware...
- */
-const unsigned char MAC_ADDRESSES[][3] =
-{
- { 0x08, 0x00, 0x0E }, /* AT&T Wavelan (standard) & DEC RoamAbout */
- { 0x08, 0x00, 0x6A }, /* AT&T Wavelan (alternate) */
- { 0x00, 0x00, 0xE1 }, /* Hitachi Wavelan */
- { 0x00, 0x60, 0x1D } /* Lucent Wavelan (another one) */
- /* Add your card here and send me the patch ! */
-};
-
-/*
- * Constants used to convert channels to frequencies
- */
-
-/* Frequency available in the 2.0 modem, in units of 250 kHz
- * (as read in the offset register of the dac area).
- * Used to map channel numbers used by `wfreqsel' to frequencies
- */
-const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
- 0xD0, 0xF0, 0xF8, 0x150 };
-
-/* Frequencies of the 1.0 modem (fixed frequencies).
- * Use to map the PSA `subband' to a frequency
- * Note : all frequencies apart from the first one need to be multiplied by 10
- */
-const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
-
-
-/*************************** PC INTERFACE ****************************/
-
-/* WaveLAN host interface definitions */
-
-#define LCCR(base) (base) /* LAN Controller Command Register */
-#define LCSR(base) (base) /* LAN Controller Status Register */
-#define HACR(base) (base+0x1) /* Host Adapter Command Register */
-#define HASR(base) (base+0x1) /* Host Adapter Status Register */
-#define PIORL(base) (base+0x2) /* Program I/O Register Low */
-#define RPLL(base) (base+0x2) /* Receive Pointer Latched Low */
-#define PIORH(base) (base+0x3) /* Program I/O Register High */
-#define RPLH(base) (base+0x3) /* Receive Pointer Latched High */
-#define PIOP(base) (base+0x4) /* Program I/O Port */
-#define MMR(base) (base+0x6) /* MMI Address Register */
-#define MMD(base) (base+0x7) /* MMI Data Register */
-
-/* Host Adaptor Command Register bit definitions */
-
-#define HACR_LOF (1 << 3) /* Lock Out Flag, toggle every 250ms */
-#define HACR_PWR_STAT (1 << 4) /* Power State, 1=active, 0=sleep */
-#define HACR_TX_DMA_RESET (1 << 5) /* Reset transmit DMA ptr on high */
-#define HACR_RX_DMA_RESET (1 << 6) /* Reset receive DMA ptr on high */
-#define HACR_ROM_WEN (1 << 7) /* EEPROM write enabled when true */
-
-#define HACR_RESET (HACR_TX_DMA_RESET | HACR_RX_DMA_RESET)
-#define HACR_DEFAULT (HACR_PWR_STAT)
-
-/* Host Adapter Status Register bit definitions */
-
-#define HASR_MMI_BUSY (1 << 2) /* MMI is busy when true */
-#define HASR_LOF (1 << 3) /* Lock out flag status */
-#define HASR_NO_CLK (1 << 4) /* active when modem not connected */
-
-/* Miscellaneous bit definitions */
-
-#define PIORH_SEL_TX (1 << 5) /* PIOR points to 0=rx/1=tx buffer */
-#define MMR_MMI_WR (1 << 0) /* Next MMI cycle is 0=read, 1=write */
-#define PIORH_MASK 0x1f /* only low 5 bits are significant */
-#define RPLH_MASK 0x1f /* only low 5 bits are significant */
-#define MMI_ADDR_MASK 0x7e /* Bits 1-6 of MMR are significant */
-
-/* Attribute Memory map */
-
-#define CIS_ADDR 0x0000 /* Card Information Status Register */
-#define PSA_ADDR 0x0e00 /* Parameter Storage Area address */
-#define EEPROM_ADDR 0x1000 /* EEPROM address (unused ?) */
-#define COR_ADDR 0x4000 /* Configuration Option Register */
-
-/* Configuration Option Register bit definitions */
-
-#define COR_CONFIG (1 << 0) /* Config Index, 0 when unconfigured */
-#define COR_SW_RESET (1 << 7) /* Software Reset on true */
-#define COR_LEVEL_IRQ (1 << 6) /* Level IRQ */
-
-/* Local Memory map */
-
-#define RX_BASE 0x0000 /* Receive memory, 8 kB */
-#define TX_BASE 0x2000 /* Transmit memory, 2 kB */
-#define UNUSED_BASE 0x2800 /* Unused, 22 kB */
-#define RX_SIZE (TX_BASE-RX_BASE) /* Size of receive area */
-#define RX_SIZE_SHIFT 6 /* Bits to shift in stop register */
-
-#define TRUE 1
-#define FALSE 0
-
-#define MOD_ENAL 1
-#define MOD_PROM 2
-
-/* Size of a MAC address */
-#define WAVELAN_ADDR_SIZE 6
-
-/* Maximum size of Wavelan packet */
-#define WAVELAN_MTU 1500
-
-#define MAXDATAZ (6 + 6 + 2 + WAVELAN_MTU)
-
-/********************** PARAMETER STORAGE AREA **********************/
-
-/*
- * Parameter Storage Area (PSA).
- */
-typedef struct psa_t psa_t;
-struct psa_t
-{
- /* For the PCMCIA Adapter, locations 0x00-0x0F are unused and fixed at 00 */
- unsigned char psa_io_base_addr_1; /* [0x00] Base address 1 ??? */
- unsigned char psa_io_base_addr_2; /* [0x01] Base address 2 */
- unsigned char psa_io_base_addr_3; /* [0x02] Base address 3 */
- unsigned char psa_io_base_addr_4; /* [0x03] Base address 4 */
- unsigned char psa_rem_boot_addr_1; /* [0x04] Remote Boot Address 1 */
- unsigned char psa_rem_boot_addr_2; /* [0x05] Remote Boot Address 2 */
- unsigned char psa_rem_boot_addr_3; /* [0x06] Remote Boot Address 3 */
- unsigned char psa_holi_params; /* [0x07] HOst Lan Interface (HOLI) Parameters */
- unsigned char psa_int_req_no; /* [0x08] Interrupt Request Line */
- unsigned char psa_unused0[7]; /* [0x09-0x0F] unused */
-
- unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x10-0x15] Universal (factory) MAC Address */
- unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x16-1B] Local MAC Address */
- unsigned char psa_univ_local_sel; /* [0x1C] Universal Local Selection */
-#define PSA_UNIVERSAL 0 /* Universal (factory) */
-#define PSA_LOCAL 1 /* Local */
- unsigned char psa_comp_number; /* [0x1D] Compatability Number: */
-#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */
-#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */
-#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */
-#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */
-#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz or 2.0 */
- unsigned char psa_thr_pre_set; /* [0x1E] Modem Threshold Preset */
- unsigned char psa_feature_select; /* [0x1F] Call code required (1=on) */
-#define PSA_FEATURE_CALL_CODE 0x01 /* Call code required (Japan) */
- unsigned char psa_subband; /* [0x20] Subband */
-#define PSA_SUBBAND_915 0 /* 915 MHz or 2.0 */
-#define PSA_SUBBAND_2425 1 /* 2425 MHz */
-#define PSA_SUBBAND_2460 2 /* 2460 MHz */
-#define PSA_SUBBAND_2484 3 /* 2484 MHz */
-#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */
- unsigned char psa_quality_thr; /* [0x21] Modem Quality Threshold */
- unsigned char psa_mod_delay; /* [0x22] Modem Delay ??? (reserved) */
- unsigned char psa_nwid[2]; /* [0x23-0x24] Network ID */
- unsigned char psa_nwid_select; /* [0x25] Network ID Select On Off */
- unsigned char psa_encryption_select; /* [0x26] Encryption On Off */
- unsigned char psa_encryption_key[8]; /* [0x27-0x2E] Encryption Key */
- unsigned char psa_databus_width; /* [0x2F] AT bus width select 8/16 */
- unsigned char psa_call_code[8]; /* [0x30-0x37] (Japan) Call Code */
- unsigned char psa_nwid_prefix[2]; /* [0x38-0x39] Roaming domain */
- unsigned char psa_reserved[2]; /* [0x3A-0x3B] Reserved - fixed 00 */
- unsigned char psa_conf_status; /* [0x3C] Conf Status, bit 0=1:config*/
- unsigned char psa_crc[2]; /* [0x3D] CRC-16 over PSA */
- unsigned char psa_crc_status; /* [0x3F] CRC Valid Flag */
-};
-
-/* Size for structure checking (if padding is correct) */
-#define PSA_SIZE 64
-
-/* Calculate offset of a field in the above structure
- * Warning : only even addresses are used */
-#define psaoff(p,f) ((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL))
-
-/******************** MODEM MANAGEMENT INTERFACE ********************/
-
-/*
- * Modem Management Controller (MMC) write structure.
- */
-typedef struct mmw_t mmw_t;
-struct mmw_t
-{
- unsigned char mmw_encr_key[8]; /* encryption key */
- unsigned char mmw_encr_enable; /* enable/disable encryption */
-#define MMW_ENCR_ENABLE_MODE 0x02 /* Mode of security option */
-#define MMW_ENCR_ENABLE_EN 0x01 /* Enable security option */
- unsigned char mmw_unused0[1]; /* unused */
- unsigned char mmw_des_io_invert; /* Encryption option */
-#define MMW_DES_IO_INVERT_RES 0x0F /* Reserved */
-#define MMW_DES_IO_INVERT_CTRL 0xF0 /* Control ??? (set to 0) */
- unsigned char mmw_unused1[5]; /* unused */
- unsigned char mmw_loopt_sel; /* looptest selection */
-#define MMW_LOOPT_SEL_DIS_NWID 0x40 /* disable NWID filtering */
-#define MMW_LOOPT_SEL_INT 0x20 /* activate Attention Request */
-#define MMW_LOOPT_SEL_LS 0x10 /* looptest w/o collision avoidance */
-#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */
-#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */
-#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */
-#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */
- unsigned char mmw_jabber_enable; /* jabber timer enable */
- /* Abort transmissions > 200 ms */
- unsigned char mmw_freeze; /* freeze / unfreeeze signal level */
- /* 0 : signal level & qual updated for every new message, 1 : frozen */
- unsigned char mmw_anten_sel; /* antenna selection */
-#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */
-#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algo. enable */
- unsigned char mmw_ifs; /* inter frame spacing */
- /* min time between transmission in bit periods (.5 us) - bit 0 ignored */
- unsigned char mmw_mod_delay; /* modem delay (synchro) */
- unsigned char mmw_jam_time; /* jamming time (after collision) */
- unsigned char mmw_unused2[1]; /* unused */
- unsigned char mmw_thr_pre_set; /* level threshold preset */
- /* Discard all packet with signal < this value (4) */
- unsigned char mmw_decay_prm; /* decay parameters */
- unsigned char mmw_decay_updat_prm; /* decay update parameterz */
- unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */
- /* Discard all packet with quality < this value (3) */
- unsigned char mmw_netw_id_l; /* NWID low order byte */
- unsigned char mmw_netw_id_h; /* NWID high order byte */
- /* Network ID or Domain : create virtual net on the air */
-
- /* 2.0 Hardware extension - frequency selection support */
- unsigned char mmw_mode_select; /* for analog tests (set to 0) */
- unsigned char mmw_unused3[1]; /* unused */
- unsigned char mmw_fee_ctrl; /* frequency eeprom control */
-#define MMW_FEE_CTRL_PRE 0x10 /* Enable protected instructions */
-#define MMW_FEE_CTRL_DWLD 0x08 /* Download eeprom to mmc */
-#define MMW_FEE_CTRL_CMD 0x07 /* EEprom commands : */
-#define MMW_FEE_CTRL_READ 0x06 /* Read */
-#define MMW_FEE_CTRL_WREN 0x04 /* Write enable */
-#define MMW_FEE_CTRL_WRITE 0x05 /* Write data to address */
-#define MMW_FEE_CTRL_WRALL 0x04 /* Write data to all addresses */
-#define MMW_FEE_CTRL_WDS 0x04 /* Write disable */
-#define MMW_FEE_CTRL_PRREAD 0x16 /* Read addr from protect register */
-#define MMW_FEE_CTRL_PREN 0x14 /* Protect register enable */
-#define MMW_FEE_CTRL_PRCLEAR 0x17 /* Unprotect all registers */
-#define MMW_FEE_CTRL_PRWRITE 0x15 /* Write addr in protect register */
-#define MMW_FEE_CTRL_PRDS 0x14 /* Protect register disable */
- /* Never issue this command (PRDS) : it's irreversible !!! */
-
- unsigned char mmw_fee_addr; /* EEprom address */
-#define MMW_FEE_ADDR_CHANNEL 0xF0 /* Select the channel */
-#define MMW_FEE_ADDR_OFFSET 0x0F /* Offset in channel data */
-#define MMW_FEE_ADDR_EN 0xC0 /* FEE_CTRL enable operations */
-#define MMW_FEE_ADDR_DS 0x00 /* FEE_CTRL disable operations */
-#define MMW_FEE_ADDR_ALL 0x40 /* FEE_CTRL all operations */
-#define MMW_FEE_ADDR_CLEAR 0xFF /* FEE_CTRL clear operations */
-
- unsigned char mmw_fee_data_l; /* Write data to EEprom */
- unsigned char mmw_fee_data_h; /* high octet */
- unsigned char mmw_ext_ant; /* Setting for external antenna */
-#define MMW_EXT_ANT_EXTANT 0x01 /* Select external antenna */
-#define MMW_EXT_ANT_POL 0x02 /* Polarity of the antenna */
-#define MMW_EXT_ANT_INTERNAL 0x00 /* Internal antenna */
-#define MMW_EXT_ANT_EXTERNAL 0x03 /* External antenna */
-#define MMW_EXT_ANT_IQ_TEST 0x1C /* IQ test pattern (set to 0) */
-};
-
-/* Size for structure checking (if padding is correct) */
-#define MMW_SIZE 37
-
-/* Calculate offset of a field in the above structure */
-#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
-
-
-/*
- * Modem Management Controller (MMC) read structure.
- */
-typedef struct mmr_t mmr_t;
-struct mmr_t
-{
- unsigned char mmr_unused0[8]; /* unused */
- unsigned char mmr_des_status; /* encryption status */
- unsigned char mmr_des_avail; /* encryption available (0x55 read) */
-#define MMR_DES_AVAIL_DES 0x55 /* DES available */
-#define MMR_DES_AVAIL_AES 0x33 /* AES (AT&T) available */
- unsigned char mmr_des_io_invert; /* des I/O invert register */
- unsigned char mmr_unused1[5]; /* unused */
- unsigned char mmr_dce_status; /* DCE status */
-#define MMR_DCE_STATUS_RX_BUSY 0x01 /* receiver busy */
-#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */
-#define MMR_DCE_STATUS_TX_BUSY 0x04 /* transmitter on */
-#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */
-#define MMR_DCE_STATUS 0x0F /* mask to get the bits */
- unsigned char mmr_dsp_id; /* DSP id (AA = Daedalus rev A) */
- unsigned char mmr_unused2[2]; /* unused */
- unsigned char mmr_correct_nwid_l; /* # of correct NWID's rxd (low) */
- unsigned char mmr_correct_nwid_h; /* # of correct NWID's rxd (high) */
- /* Warning : Read high order octet first !!! */
- unsigned char mmr_wrong_nwid_l; /* # of wrong NWID's rxd (low) */
- unsigned char mmr_wrong_nwid_h; /* # of wrong NWID's rxd (high) */
- unsigned char mmr_thr_pre_set; /* level threshold preset */
-#define MMR_THR_PRE_SET 0x3F /* level threshold preset */
-#define MMR_THR_PRE_SET_CUR 0x80 /* Current signal above it */
- unsigned char mmr_signal_lvl; /* signal level */
-#define MMR_SIGNAL_LVL 0x3F /* signal level */
-#define MMR_SIGNAL_LVL_VALID 0x80 /* Updated since last read */
- unsigned char mmr_silence_lvl; /* silence level (noise) */
-#define MMR_SILENCE_LVL 0x3F /* silence level */
-#define MMR_SILENCE_LVL_VALID 0x80 /* Updated since last read */
- unsigned char mmr_sgnl_qual; /* signal quality */
-#define MMR_SGNL_QUAL 0x0F /* signal quality */
-#define MMR_SGNL_QUAL_ANT 0x80 /* current antenna used */
- unsigned char mmr_netw_id_l; /* NWID low order byte ??? */
- unsigned char mmr_unused3[3]; /* unused */
-
- /* 2.0 Hardware extension - frequency selection support */
- unsigned char mmr_fee_status; /* Status of frequency eeprom */
-#define MMR_FEE_STATUS_ID 0xF0 /* Modem revision id */
-#define MMR_FEE_STATUS_DWLD 0x08 /* Download in progress */
-#define MMR_FEE_STATUS_BUSY 0x04 /* EEprom busy */
- unsigned char mmr_unused4[1]; /* unused */
- unsigned char mmr_fee_data_l; /* Read data from eeprom (low) */
- unsigned char mmr_fee_data_h; /* Read data from eeprom (high) */
-};
-
-/* Size for structure checking (if padding is correct) */
-#define MMR_SIZE 36
-
-/* Calculate offset of a field in the above structure */
-#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
-
-
-/* Make the two above structures one */
-typedef union mm_t
-{
- struct mmw_t w; /* Write to the mmc */
- struct mmr_t r; /* Read from the mmc */
-} mm_t;
-
-#endif /* _WAVELAN_H */
+++ /dev/null
-/*
- * Wavelan Pcmcia driver
- *
- * Jean II - HPLB '96
- *
- * Reorganisation and extension of the driver.
- * Original copyright follow. See wavelan_cs.h for details.
- *
- * This code is derived from Anthony D. Joseph's code and all the changes here
- * are also under the original copyright below.
- *
- * This code supports version 2.00 of WaveLAN/PCMCIA cards (2.4GHz), and
- * can work on Linux 2.0.36 with support of David Hinds' PCMCIA Card Services
- *
- * Joe Finney (joe@comp.lancs.ac.uk) at Lancaster University in UK added
- * critical code in the routine to initialize the Modem Management Controller.
- *
- * Thanks to Alan Cox and Bruce Janson for their advice.
- *
- * -- Yunzhou Li (scip4166@nus.sg)
- *
-#ifdef WAVELAN_ROAMING
- * Roaming support added 07/22/98 by Justin Seger (jseger@media.mit.edu)
- * based on patch by Joe Finney from Lancaster University.
-#endif
- *
- * Lucent (formerly AT&T GIS, formerly NCR) WaveLAN PCMCIA card: An
- * Ethernet-like radio transceiver controlled by an Intel 82593 coprocessor.
- *
- * A non-shared memory PCMCIA ethernet driver for linux
- *
- * ISA version modified to support PCMCIA by Anthony Joseph (adj@lcs.mit.edu)
- *
- *
- * Joseph O'Sullivan & John Langford (josullvn@cs.cmu.edu & jcl@cs.cmu.edu)
- *
- * Apr 2 '98 made changes to bring the i82593 control/int handling in line
- * with offical specs...
- *
- ****************************************************************************
- * Copyright 1995
- * Anthony D. Joseph
- * Massachusetts Institute of Technology
- *
- * Permission to use, copy, modify, and distribute this program
- * for any purpose and without fee is hereby granted, provided
- * that this copyright and permission notice appear on all copies
- * and supporting documentation, the name of M.I.T. not be used
- * in advertising or publicity pertaining to distribution of the
- * program without specific prior permission, and notice be given
- * in supporting documentation that copying and distribution is
- * by permission of M.I.T. M.I.T. makes no representations about
- * the suitability of this software for any purpose. It is pro-
- * vided "as is" without express or implied warranty.
- ****************************************************************************
- *
- */
-
-#include "wavelan_cs.h" /* Private header */
-
-/************************* MISC SUBROUTINES **************************/
-/*
- * Subroutines which won't fit in one of the following category
- * (wavelan modem or i82593)
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Wrapper for disabling interrupts.
- * (note : inline, so optimised away)
- */
-static inline void
-wv_splhi(net_local * lp,
- unsigned long * pflags)
-{
- spin_lock_irqsave(&lp->spinlock, *pflags);
- /* Note : above does the cli(); itself */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wrapper for re-enabling interrupts.
- */
-static inline void
-wv_splx(net_local * lp,
- unsigned long * pflags)
-{
- spin_unlock_irqrestore(&lp->spinlock, *pflags);
-
- /* Note : enabling interrupts on the hardware is done in wv_ru_start()
- * via : outb(OP1_INT_ENABLE, LCCR(base));
- */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wrapper for reporting error to cardservices
- */
-static void cs_error(client_handle_t handle, int func, int ret)
-{
- error_info_t err = { func, ret };
- CardServices(ReportError, handle, &err);
-}
-
-#ifdef STRUCT_CHECK
-/*------------------------------------------------------------------*/
-/*
- * Sanity routine to verify the sizes of the various WaveLAN interface
- * structures.
- */
-static char *
-wv_structuct_check(void)
-{
-#define SC(t,s,n) if (sizeof(t) != s) return(n);
-
- SC(psa_t, PSA_SIZE, "psa_t");
- SC(mmw_t, MMW_SIZE, "mmw_t");
- SC(mmr_t, MMR_SIZE, "mmr_t");
-
-#undef SC
-
- return((char *) NULL);
-} /* wv_structuct_check */
-#endif /* STRUCT_CHECK */
-
-/******************* MODEM MANAGEMENT SUBROUTINES *******************/
-/*
- * Useful subroutines to manage the modem of the wavelan
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Read from card's Host Adaptor Status Register.
- */
-static inline u_char
-hasr_read(u_long base)
-{
- return(inb(HASR(base)));
-} /* hasr_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Write to card's Host Adapter Command Register.
- */
-static inline void
-hacr_write(u_long base,
- u_char hacr)
-{
- outb(hacr, HACR(base));
-} /* hacr_write */
-
-/*------------------------------------------------------------------*/
-/*
- * Write to card's Host Adapter Command Register. Include a delay for
- * those times when it is needed.
- */
-static inline void
-hacr_write_slow(u_long base,
- u_char hacr)
-{
- hacr_write(base, hacr);
- /* delay might only be needed sometimes */
- mdelay(1);
-} /* hacr_write_slow */
-
-/*------------------------------------------------------------------*/
-/*
- * Read the Parameter Storage Area from the WaveLAN card's memory
- */
-static void
-psa_read(device * dev,
- int o, /* offset in PSA */
- u_char * b, /* buffer to fill */
- int n) /* size to read */
-{
- u_char * ptr = ((u_char *)dev->mem_start) + PSA_ADDR + (o << 1);
-
- while(n-- > 0)
- {
- *b++ = readb(ptr);
- /* Due to a lack of address decode pins, the WaveLAN PCMCIA card
- * only supports reading even memory addresses. That means the
- * increment here MUST be two.
- * Because of that, we can't use memcpy_fromio()...
- */
- ptr += 2;
- }
-} /* psa_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Write the Paramter Storage Area to the WaveLAN card's memory
- */
-static void
-psa_write(device * dev,
- int o, /* Offset in psa */
- u_char * b, /* Buffer in memory */
- int n) /* Length of buffer */
-{
- u_char * ptr = ((u_char *) dev->mem_start) + PSA_ADDR + (o << 1);
- int count = 0;
- ioaddr_t base = dev->base_addr;
- /* As there seem to have no flag PSA_BUSY as in the ISA model, we are
- * oblige to verify this address to know when the PSA is ready... */
- volatile u_char * verify = ((u_char *) dev->mem_start) + PSA_ADDR +
- (psaoff(0, psa_comp_number) << 1);
-
- /* Authorize writting to PSA */
- hacr_write(base, HACR_PWR_STAT | HACR_ROM_WEN);
-
- while(n-- > 0)
- {
- /* write to PSA */
- writeb(*b++, ptr);
- ptr += 2;
-
- /* I don't have the spec, so I don't know what the correct
- * sequence to write is. This hack seem to work for me... */
- count = 0;
- while((readb(verify) != PSA_COMP_PCMCIA_915) && (count++ < 100))
- mdelay(1);
- }
-
- /* Put the host interface back in standard state */
- hacr_write(base, HACR_DEFAULT);
-} /* psa_write */
-
-#ifdef SET_PSA_CRC
-/*------------------------------------------------------------------*/
-/*
- * Calculate the PSA CRC
- * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
- * NOTE: By specifying a length including the CRC position the
- * returned value should be zero. (i.e. a correct checksum in the PSA)
- *
- * The Windows drivers don't use the CRC, but the AP and the PtP tool
- * depend on it.
- */
-static u_short
-psa_crc(unsigned char * psa, /* The PSA */
- int size) /* Number of short for CRC */
-{
- int byte_cnt; /* Loop on the PSA */
- u_short crc_bytes = 0; /* Data in the PSA */
- int bit_cnt; /* Loop on the bits of the short */
-
- for(byte_cnt = 0; byte_cnt < size; byte_cnt++ )
- {
- crc_bytes ^= psa[byte_cnt]; /* Its an xor */
-
- for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ )
- {
- if(crc_bytes & 0x0001)
- crc_bytes = (crc_bytes >> 1) ^ 0xA001;
- else
- crc_bytes >>= 1 ;
- }
- }
-
- return crc_bytes;
-} /* psa_crc */
-#endif /* SET_PSA_CRC */
-
-/*------------------------------------------------------------------*/
-/*
- * update the checksum field in the Wavelan's PSA
- */
-static void
-update_psa_checksum(device * dev)
-{
-#ifdef SET_PSA_CRC
- psa_t psa;
- u_short crc;
-
- /* read the parameter storage area */
- psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
-
- /* update the checksum */
- crc = psa_crc((unsigned char *) &psa,
- sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1])
- - sizeof(psa.psa_crc_status));
-
- psa.psa_crc[0] = crc & 0xFF;
- psa.psa_crc[1] = (crc & 0xFF00) >> 8;
-
- /* Write it ! */
- psa_write(dev, (char *)&psa.psa_crc - (char *)&psa,
- (unsigned char *)&psa.psa_crc, 2);
-
-#ifdef DEBUG_IOCTL_INFO
- printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
- dev->name, psa.psa_crc[0], psa.psa_crc[1]);
-
- /* Check again (luxury !) */
- crc = psa_crc((unsigned char *) &psa,
- sizeof(psa) - sizeof(psa.psa_crc_status));
-
- if(crc != 0)
- printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name);
-#endif /* DEBUG_IOCTL_INFO */
-#endif /* SET_PSA_CRC */
-} /* update_psa_checksum */
-
-/*------------------------------------------------------------------*/
-/*
- * Write 1 byte to the MMC.
- */
-static inline void
-mmc_out(u_long base,
- u_short o,
- u_char d)
-{
- /* Wait for MMC to go idle */
- while(inb(HASR(base)) & HASR_MMI_BUSY)
- ;
-
- outb((u_char)((o << 1) | MMR_MMI_WR), MMR(base));
- outb(d, MMD(base));
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to write bytes to the Modem Management Controller.
- * We start by the end because it is the way it should be !
- */
-static inline void
-mmc_write(u_long base,
- u_char o,
- u_char * b,
- int n)
-{
- o += n;
- b += n;
-
- while(n-- > 0 )
- mmc_out(base, --o, *(--b));
-} /* mmc_write */
-
-/*------------------------------------------------------------------*/
-/*
- * Read 1 byte from the MMC.
- * Optimised version for 1 byte, avoid using memory...
- */
-static inline u_char
-mmc_in(u_long base,
- u_short o)
-{
- while(inb(HASR(base)) & HASR_MMI_BUSY)
- ;
- outb(o << 1, MMR(base)); /* Set the read address */
-
- outb(0, MMD(base)); /* Required dummy write */
-
- while(inb(HASR(base)) & HASR_MMI_BUSY)
- ;
- return (u_char) (inb(MMD(base))); /* Now do the actual read */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to read bytes from the Modem Management Controller.
- * The implementation is complicated by a lack of address lines,
- * which prevents decoding of the low-order bit.
- * (code has just been moved in the above function)
- * We start by the end because it is the way it should be !
- */
-static inline void
-mmc_read(u_long base,
- u_char o,
- u_char * b,
- int n)
-{
- o += n;
- b += n;
-
- while(n-- > 0)
- *(--b) = mmc_in(base, --o);
-} /* mmc_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Get the type of encryption available...
- */
-static inline int
-mmc_encr(u_long base) /* i/o port of the card */
-{
- int temp;
-
- temp = mmc_in(base, mmroff(0, mmr_des_avail));
- if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
- return 0;
- else
- return temp;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wait for the frequency EEprom to complete a command...
- * I hope this one will be optimally inlined...
- */
-static inline void
-fee_wait(u_long base, /* i/o port of the card */
- int delay, /* Base delay to wait for */
- int number) /* Number of time to wait */
-{
- int count = 0; /* Wait only a limited time */
-
- while((count++ < number) &&
- (mmc_in(base, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY))
- udelay(delay);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Read bytes from the Frequency EEprom (frequency select cards).
- */
-static void
-fee_read(u_long base, /* i/o port of the card */
- u_short o, /* destination offset */
- u_short * b, /* data buffer */
- int n) /* number of registers */
-{
- b += n; /* Position at the end of the area */
-
- /* Write the address */
- mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
-
- /* Loop on all buffer */
- while(n-- > 0)
- {
- /* Write the read command */
- mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ);
-
- /* Wait until EEprom is ready (should be quick !) */
- fee_wait(base, 10, 100);
-
- /* Read the value */
- *--b = ((mmc_in(base, mmroff(0, mmr_fee_data_h)) << 8) |
- mmc_in(base, mmroff(0, mmr_fee_data_l)));
- }
-}
-
-#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */
-
-/*------------------------------------------------------------------*/
-/*
- * Write bytes from the Frequency EEprom (frequency select cards).
- * This is a bit complicated, because the frequency eeprom has to
- * be unprotected and the write enabled.
- * Jean II
- */
-static void
-fee_write(u_long base, /* i/o port of the card */
- u_short o, /* destination offset */
- u_short * b, /* data buffer */
- int n) /* number of registers */
-{
- b += n; /* Position at the end of the area */
-
-#ifdef EEPROM_IS_PROTECTED /* disabled */
-#ifdef DOESNT_SEEM_TO_WORK /* disabled */
- /* Ask to read the protected register */
- mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
-
- fee_wait(base, 10, 100);
-
- /* Read the protected register */
- printk("Protected 2 : %02X-%02X\n",
- mmc_in(base, mmroff(0, mmr_fee_data_h)),
- mmc_in(base, mmroff(0, mmr_fee_data_l)));
-#endif /* DOESNT_SEEM_TO_WORK */
-
- /* Enable protected register */
- mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
- mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
-
- fee_wait(base, 10, 100);
-
- /* Unprotect area */
- mmc_out(base, mmwoff(0, mmw_fee_addr), o + n);
- mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
-#ifdef DOESNT_SEEM_TO_WORK /* disabled */
- /* Or use : */
- mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
-#endif /* DOESNT_SEEM_TO_WORK */
-
- fee_wait(base, 10, 100);
-#endif /* EEPROM_IS_PROTECTED */
-
- /* Write enable */
- mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
- mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
-
- fee_wait(base, 10, 100);
-
- /* Write the EEprom address */
- mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
-
- /* Loop on all buffer */
- while(n-- > 0)
- {
- /* Write the value */
- mmc_out(base, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
- mmc_out(base, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
-
- /* Write the write command */
- mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE);
-
- /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */
- mdelay(10);
- fee_wait(base, 10, 100);
- }
-
- /* Write disable */
- mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
- mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
-
- fee_wait(base, 10, 100);
-
-#ifdef EEPROM_IS_PROTECTED /* disabled */
- /* Reprotect EEprom */
- mmc_out(base, mmwoff(0, mmw_fee_addr), 0x00);
- mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
-
- fee_wait(base, 10, 100);
-#endif /* EEPROM_IS_PROTECTED */
-}
-#endif /* WIRELESS_EXT */
-
-/******************* WaveLAN Roaming routines... ********************/
-
-#ifdef WAVELAN_ROAMING /* Conditional compile, see wavelan_cs.h */
-
-unsigned char WAVELAN_BEACON_ADDRESS[]= {0x09,0x00,0x0e,0x20,0x03,0x00};
-
-void wv_roam_init(struct net_device *dev)
-{
- net_local *lp= (net_local *)dev->priv;
-
- /* Do not remove this unless you have a good reason */
- printk(KERN_NOTICE "%s: Warning, you have enabled roaming on"
- " device %s !\n", dev->name, dev->name);
- printk(KERN_NOTICE "Roaming is currently an experimental unsupported feature"
- " of the Wavelan driver.\n");
- printk(KERN_NOTICE "It may work, but may also make the driver behave in"
- " erratic ways or crash.\n");
-
- lp->wavepoint_table.head=NULL; /* Initialise WavePoint table */
- lp->wavepoint_table.num_wavepoints=0;
- lp->wavepoint_table.locked=0;
- lp->curr_point=NULL; /* No default WavePoint */
- lp->cell_search=0;
-
- lp->cell_timer.data=(long)lp; /* Start cell expiry timer */
- lp->cell_timer.function=wl_cell_expiry;
- lp->cell_timer.expires=jiffies+CELL_TIMEOUT;
- add_timer(&lp->cell_timer);
-
- wv_nwid_filter(NWID_PROMISC,lp) ; /* Enter NWID promiscuous mode */
- /* to build up a good WavePoint */
- /* table... */
- printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name);
-}
-
-void wv_roam_cleanup(struct net_device *dev)
-{
- wavepoint_history *ptr,*old_ptr;
- net_local *lp= (net_local *)dev->priv;
-
- printk(KERN_DEBUG "WaveLAN: Roaming Disabled on device %s\n",dev->name);
-
- /* Fixme : maybe we should check that the timer exist before deleting it */
- del_timer(&lp->cell_timer); /* Remove cell expiry timer */
- ptr=lp->wavepoint_table.head; /* Clear device's WavePoint table */
- while(ptr!=NULL)
- {
- old_ptr=ptr;
- ptr=ptr->next;
- wl_del_wavepoint(old_ptr,lp);
- }
-}
-
-/* Enable/Disable NWID promiscuous mode on a given device */
-void wv_nwid_filter(unsigned char mode, net_local *lp)
-{
- mm_t m;
- unsigned long flags;
-
-#ifdef WAVELAN_ROAMING_DEBUG
- printk(KERN_DEBUG "WaveLAN: NWID promisc %s, device %s\n",(mode==NWID_PROMISC) ? "on" : "off", lp->dev->name);
-#endif
-
- /* Disable interrupts & save flags */
- wv_splhi(lp, &flags);
-
- m.w.mmw_loopt_sel = (mode==NWID_PROMISC) ? MMW_LOOPT_SEL_DIS_NWID : 0x00;
- mmc_write(lp->dev->base_addr, (char *)&m.w.mmw_loopt_sel - (char *)&m, (unsigned char *)&m.w.mmw_loopt_sel, 1);
-
- if(mode==NWID_PROMISC)
- lp->cell_search=1;
- else
- lp->cell_search=0;
-
- /* ReEnable interrupts & restore flags */
- wv_splx(lp, &flags);
-}
-
-/* Find a record in the WavePoint table matching a given NWID */
-wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
-{
- wavepoint_history *ptr=lp->wavepoint_table.head;
-
- while(ptr!=NULL){
- if(ptr->nwid==nwid)
- return ptr;
- ptr=ptr->next;
- }
- return NULL;
-}
-
-/* Create a new wavepoint table entry */
-wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp)
-{
- wavepoint_history *new_wavepoint;
-
-#ifdef WAVELAN_ROAMING_DEBUG
- printk(KERN_DEBUG "WaveLAN: New Wavepoint, NWID:%.4X\n",nwid);
-#endif
-
- if(lp->wavepoint_table.num_wavepoints==MAX_WAVEPOINTS)
- return NULL;
-
- new_wavepoint=(wavepoint_history *) kmalloc(sizeof(wavepoint_history),GFP_ATOMIC);
- if(new_wavepoint==NULL)
- return NULL;
-
- new_wavepoint->nwid=nwid; /* New WavePoints NWID */
- new_wavepoint->average_fast=0; /* Running Averages..*/
- new_wavepoint->average_slow=0;
- new_wavepoint->qualptr=0; /* Start of ringbuffer */
- new_wavepoint->last_seq=seq-1; /* Last sequence no.seen */
- memset(new_wavepoint->sigqual,0,WAVEPOINT_HISTORY);/* Empty ringbuffer */
-
- new_wavepoint->next=lp->wavepoint_table.head;/* Add to wavepoint table */
- new_wavepoint->prev=NULL;
-
- if(lp->wavepoint_table.head!=NULL)
- lp->wavepoint_table.head->prev=new_wavepoint;
-
- lp->wavepoint_table.head=new_wavepoint;
-
- lp->wavepoint_table.num_wavepoints++; /* no. of visible wavepoints */
-
- return new_wavepoint;
-}
-
-/* Remove a wavepoint entry from WavePoint table */
-void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
-{
- if(wavepoint==NULL)
- return;
-
- if(lp->curr_point==wavepoint)
- lp->curr_point=NULL;
-
- if(wavepoint->prev!=NULL)
- wavepoint->prev->next=wavepoint->next;
-
- if(wavepoint->next!=NULL)
- wavepoint->next->prev=wavepoint->prev;
-
- if(lp->wavepoint_table.head==wavepoint)
- lp->wavepoint_table.head=wavepoint->next;
-
- lp->wavepoint_table.num_wavepoints--;
- kfree(wavepoint);
-}
-
-/* Timer callback function - checks WavePoint table for stale entries */
-void wl_cell_expiry(unsigned long data)
-{
- net_local *lp=(net_local *)data;
- wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point;
-
-#if WAVELAN_ROAMING_DEBUG > 1
- printk(KERN_DEBUG "WaveLAN: Wavepoint timeout, dev %s\n",lp->dev->name);
-#endif
-
- if(lp->wavepoint_table.locked)
- {
-#if WAVELAN_ROAMING_DEBUG > 1
- printk(KERN_DEBUG "WaveLAN: Wavepoint table locked...\n");
-#endif
-
- lp->cell_timer.expires=jiffies+1; /* If table in use, come back later */
- add_timer(&lp->cell_timer);
- return;
- }
-
- while(wavepoint!=NULL)
- {
- if(wavepoint->last_seen < jiffies-CELL_TIMEOUT)
- {
-#ifdef WAVELAN_ROAMING_DEBUG
- printk(KERN_DEBUG "WaveLAN: Bye bye %.4X\n",wavepoint->nwid);
-#endif
-
- old_point=wavepoint;
- wavepoint=wavepoint->next;
- wl_del_wavepoint(old_point,lp);
- }
- else
- wavepoint=wavepoint->next;
- }
- lp->cell_timer.expires=jiffies+CELL_TIMEOUT;
- add_timer(&lp->cell_timer);
-}
-
-/* Update SNR history of a wavepoint */
-void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq)
-{
- int i=0,num_missed=0,ptr=0;
- int average_fast=0,average_slow=0;
-
- num_missed=(seq-wavepoint->last_seq)%WAVEPOINT_HISTORY;/* Have we missed
- any beacons? */
- if(num_missed)
- for(i=0;i<num_missed;i++)
- {
- wavepoint->sigqual[wavepoint->qualptr++]=0; /* If so, enter them as 0's */
- wavepoint->qualptr %=WAVEPOINT_HISTORY; /* in the ringbuffer. */
- }
- wavepoint->last_seen=jiffies; /* Add beacon to history */
- wavepoint->last_seq=seq;
- wavepoint->sigqual[wavepoint->qualptr++]=sigqual;
- wavepoint->qualptr %=WAVEPOINT_HISTORY;
- ptr=(wavepoint->qualptr-WAVEPOINT_FAST_HISTORY+WAVEPOINT_HISTORY)%WAVEPOINT_HISTORY;
-
- for(i=0;i<WAVEPOINT_FAST_HISTORY;i++) /* Update running averages */
- {
- average_fast+=wavepoint->sigqual[ptr++];
- ptr %=WAVEPOINT_HISTORY;
- }
-
- average_slow=average_fast;
- for(i=WAVEPOINT_FAST_HISTORY;i<WAVEPOINT_HISTORY;i++)
- {
- average_slow+=wavepoint->sigqual[ptr++];
- ptr %=WAVEPOINT_HISTORY;
- }
-
- wavepoint->average_fast=average_fast/WAVEPOINT_FAST_HISTORY;
- wavepoint->average_slow=average_slow/WAVEPOINT_HISTORY;
-}
-
-/* Perform a handover to a new WavePoint */
-void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
-{
- ioaddr_t base = lp->dev->base_addr;
- mm_t m;
- unsigned long flags;
-
- if(wavepoint==lp->curr_point) /* Sanity check... */
- {
- wv_nwid_filter(!NWID_PROMISC,lp);
- return;
- }
-
-#ifdef WAVELAN_ROAMING_DEBUG
- printk(KERN_DEBUG "WaveLAN: Doing handover to %.4X, dev %s\n",wavepoint->nwid,lp->dev->name);
-#endif
-
- /* Disable interrupts & save flags */
- wv_splhi(lp, &flags);
-
- m.w.mmw_netw_id_l = wavepoint->nwid & 0xFF;
- m.w.mmw_netw_id_h = (wavepoint->nwid & 0xFF00) >> 8;
-
- mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2);
-
- /* ReEnable interrupts & restore flags */
- wv_splx(lp, &flags);
-
- wv_nwid_filter(!NWID_PROMISC,lp);
- lp->curr_point=wavepoint;
-}
-
-/* Called when a WavePoint beacon is received */
-static inline void wl_roam_gather(device * dev,
- u_char * hdr, /* Beacon header */
- u_char * stats) /* SNR, Signal quality
- of packet */
-{
- wavepoint_beacon *beacon= (wavepoint_beacon *)hdr; /* Rcvd. Beacon */
- unsigned short nwid=ntohs(beacon->nwid);
- unsigned short sigqual=stats[2] & MMR_SGNL_QUAL; /* SNR of beacon */
- wavepoint_history *wavepoint=NULL; /* WavePoint table entry */
- net_local *lp=(net_local *)dev->priv; /* Device info */
-
-#if 0
- /* Some people don't need this, some other may need it */
- nwid=nwid^ntohs(beacon->domain_id);
-#endif
-
-#if WAVELAN_ROAMING_DEBUG > 1
- printk(KERN_DEBUG "WaveLAN: beacon, dev %s:\n",dev->name);
- printk(KERN_DEBUG "Domain: %.4X NWID: %.4X SigQual=%d\n",ntohs(beacon->domain_id),nwid,sigqual);
-#endif
-
- lp->wavepoint_table.locked=1; /* <Mutex> */
-
- wavepoint=wl_roam_check(nwid,lp); /* Find WavePoint table entry */
- if(wavepoint==NULL) /* If no entry, Create a new one... */
- {
- wavepoint=wl_new_wavepoint(nwid,beacon->seq,lp);
- if(wavepoint==NULL)
- goto out;
- }
- if(lp->curr_point==NULL) /* If this is the only WavePoint, */
- wv_roam_handover(wavepoint, lp); /* Jump on it! */
-
- wl_update_history(wavepoint, sigqual, beacon->seq); /* Update SNR history
- stats. */
-
- if(lp->curr_point->average_slow < SEARCH_THRESH_LOW) /* If our current */
- if(!lp->cell_search) /* WavePoint is getting faint, */
- wv_nwid_filter(NWID_PROMISC,lp); /* start looking for a new one */
-
- if(wavepoint->average_slow >
- lp->curr_point->average_slow + WAVELAN_ROAMING_DELTA)
- wv_roam_handover(wavepoint, lp); /* Handover to a better WavePoint */
-
- if(lp->curr_point->average_slow > SEARCH_THRESH_HIGH) /* If our SNR is */
- if(lp->cell_search) /* getting better, drop out of cell search mode */
- wv_nwid_filter(!NWID_PROMISC,lp);
-
-out:
- lp->wavepoint_table.locked=0; /* </MUTEX> :-) */
-}
-
-/* Test this MAC frame a WavePoint beacon */
-static inline int WAVELAN_BEACON(unsigned char *data)
-{
- wavepoint_beacon *beacon= (wavepoint_beacon *)data;
- static wavepoint_beacon beacon_template={0xaa,0xaa,0x03,0x08,0x00,0x0e,0x20,0x03,0x00};
-
- if(memcmp(beacon,&beacon_template,9)==0)
- return 1;
- else
- return 0;
-}
-#endif /* WAVELAN_ROAMING */
-
-/************************ I82593 SUBROUTINES *************************/
-/*
- * Useful subroutines to manage the Ethernet controller
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to synchronously send a command to the i82593 chip.
- * Should be called with interrupts disabled.
- * (called by wv_packet_write(), wv_ru_stop(), wv_ru_start(),
- * wv_82593_config() & wv_diag())
- */
-static int
-wv_82593_cmd(device * dev,
- char * str,
- int cmd,
- int result)
-{
- ioaddr_t base = dev->base_addr;
- int status;
- int wait_completed;
- long spin;
-
- /* Spin until the chip finishes executing its current command (if any) */
- spin = 1000;
- do
- {
- /* Time calibration of the loop */
- udelay(10);
-
- /* Read the interrupt register */
- outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
- status = inb(LCSR(base));
- }
- while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0));
-
- /* If the interrupt hasn't be posted */
- if(spin <= 0)
- {
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO "wv_82593_cmd: %s timeout (previous command), status 0x%02x\n",
- str, status);
-#endif
- return(FALSE);
- }
-
- /* Issue the command to the controller */
- outb(cmd, LCCR(base));
-
- /* If we don't have to check the result of the command
- * Note : this mean that the irq handler will deal with that */
- if(result == SR0_NO_RESULT)
- return(TRUE);
-
- /* We are waiting for command completion */
- wait_completed = TRUE;
-
- /* Busy wait while the LAN controller executes the command. */
- spin = 1000;
- do
- {
- /* Time calibration of the loop */
- udelay(10);
-
- /* Read the interrupt register */
- outb(CR0_STATUS_0 | OP0_NOP, LCCR(base));
- status = inb(LCSR(base));
-
- /* Check if there was an interrupt posted */
- if((status & SR0_INTERRUPT))
- {
- /* Acknowledge the interrupt */
- outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
-
- /* Check if interrupt is a command completion */
- if(((status & SR0_BOTH_RX_TX) != SR0_BOTH_RX_TX) &&
- ((status & SR0_BOTH_RX_TX) != 0x0) &&
- !(status & SR0_RECEPTION))
- {
- /* Signal command completion */
- wait_completed = FALSE;
- }
- else
- {
- /* Note : Rx interrupts will be handled later, because we can
- * handle multiple Rx packets at once */
-#ifdef DEBUG_INTERRUPT_INFO
- printk(KERN_INFO "wv_82593_cmd: not our interrupt\n");
-#endif
- }
- }
- }
- while(wait_completed && (spin-- > 0));
-
- /* If the interrupt hasn't be posted */
- if(wait_completed)
- {
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO "wv_82593_cmd: %s timeout, status 0x%02x\n",
- str, status);
-#endif
- return(FALSE);
- }
-
- /* Check the return code returned by the card (see above) against
- * the expected return code provided by the caller */
- if((status & SR0_EVENT_MASK) != result)
- {
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO "wv_82593_cmd: %s failed, status = 0x%x\n",
- str, status);
-#endif
- return(FALSE);
- }
-
- return(TRUE);
-} /* wv_82593_cmd */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does a 593 op-code number 7, and obtains the diagnose
- * status for the WaveLAN.
- */
-static inline int
-wv_diag(device * dev)
-{
- int ret = FALSE;
-
- if(wv_82593_cmd(dev, "wv_diag(): diagnose",
- OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED))
- ret = TRUE;
-
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_INFO "wavelan_cs: i82593 Self Test failed!\n");
-#endif
- return(ret);
-} /* wv_diag */
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to read len bytes from the i82593's ring buffer, starting at
- * chip address addr. The results read from the chip are stored in buf.
- * The return value is the address to use for next the call.
- */
-static int
-read_ringbuf(device * dev,
- int addr,
- char * buf,
- int len)
-{
- ioaddr_t base = dev->base_addr;
- int ring_ptr = addr;
- int chunk_len;
- char * buf_ptr = buf;
-
- /* Get all the buffer */
- while(len > 0)
- {
- /* Position the Program I/O Register at the ring buffer pointer */
- outb(ring_ptr & 0xff, PIORL(base));
- outb(((ring_ptr >> 8) & PIORH_MASK), PIORH(base));
-
- /* First, determine how much we can read without wrapping around the
- ring buffer */
- if((addr + len) < (RX_BASE + RX_SIZE))
- chunk_len = len;
- else
- chunk_len = RX_BASE + RX_SIZE - addr;
- insb(PIOP(base), buf_ptr, chunk_len);
- buf_ptr += chunk_len;
- len -= chunk_len;
- ring_ptr = (ring_ptr - RX_BASE + chunk_len) % RX_SIZE + RX_BASE;
- }
- return(ring_ptr);
-} /* read_ringbuf */
-
-/*------------------------------------------------------------------*/
-/*
- * Reconfigure the i82593, or at least ask for it...
- * Because wv_82593_config use the transmission buffer, we must do it
- * when we are sure that there is no transmission, so we do it now
- * or in wavelan_packet_xmit() (I can't find any better place,
- * wavelan_interrupt is not an option...), so you may experience
- * some delay sometime...
- */
-static inline void
-wv_82593_reconfig(device * dev)
-{
- net_local * lp = (net_local *)dev->priv;
- dev_link_t * link = ((net_local *) dev->priv)->link;
- unsigned long flags;
-
- /* Arm the flag, will be cleard in wv_82593_config() */
- lp->reconfig_82593 = TRUE;
-
- /* Check if we can do it now ! */
- if((link->open) && (netif_running(dev)) && !(netif_queue_stopped(dev)))
- {
- wv_splhi(lp, &flags); /* Disable interrupts */
- wv_82593_config(dev);
- wv_splx(lp, &flags); /* Re-enable interrupts */
- }
- else
- {
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG
- "%s: wv_82593_reconfig(): delayed (state = %lX, link = %d)\n",
- dev->name, dev->state, link->open);
-#endif
- }
-}
-
-/********************* DEBUG & INFO SUBROUTINES *********************/
-/*
- * This routines are used in the code to show debug informations.
- * Most of the time, it dump the content of hardware structures...
- */
-
-#ifdef DEBUG_PSA_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted contents of the Parameter Storage Area.
- */
-static void
-wv_psa_show(psa_t * p)
-{
- printk(KERN_DEBUG "##### wavelan psa contents: #####\n");
- printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
- p->psa_io_base_addr_1,
- p->psa_io_base_addr_2,
- p->psa_io_base_addr_3,
- p->psa_io_base_addr_4);
- printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
- p->psa_rem_boot_addr_1,
- p->psa_rem_boot_addr_2,
- p->psa_rem_boot_addr_3);
- printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
- printk("psa_int_req_no: %d\n", p->psa_int_req_no);
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
- p->psa_unused0[0],
- p->psa_unused0[1],
- p->psa_unused0[2],
- p->psa_unused0[3],
- p->psa_unused0[4],
- p->psa_unused0[5],
- p->psa_unused0[6]);
-#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
- p->psa_univ_mac_addr[0],
- p->psa_univ_mac_addr[1],
- p->psa_univ_mac_addr[2],
- p->psa_univ_mac_addr[3],
- p->psa_univ_mac_addr[4],
- p->psa_univ_mac_addr[5]);
- printk(KERN_DEBUG "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
- p->psa_local_mac_addr[0],
- p->psa_local_mac_addr[1],
- p->psa_local_mac_addr[2],
- p->psa_local_mac_addr[3],
- p->psa_local_mac_addr[4],
- p->psa_local_mac_addr[5]);
- printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel);
- printk("psa_comp_number: %d, ", p->psa_comp_number);
- printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
- printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
- p->psa_feature_select);
- printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
- printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
- printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
- printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], p->psa_nwid[1]);
- printk("psa_nwid_select: %d\n", p->psa_nwid_select);
- printk(KERN_DEBUG "psa_encryption_select: %d, ", p->psa_encryption_select);
- printk("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
- p->psa_encryption_key[0],
- p->psa_encryption_key[1],
- p->psa_encryption_key[2],
- p->psa_encryption_key[3],
- p->psa_encryption_key[4],
- p->psa_encryption_key[5],
- p->psa_encryption_key[6],
- p->psa_encryption_key[7]);
- printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
- printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
- p->psa_call_code[0]);
- printk("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
- p->psa_call_code[0],
- p->psa_call_code[1],
- p->psa_call_code[2],
- p->psa_call_code[3],
- p->psa_call_code[4],
- p->psa_call_code[5],
- p->psa_call_code[6],
- p->psa_call_code[7]);
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
- p->psa_reserved[0],
- p->psa_reserved[1],
- p->psa_reserved[2],
- p->psa_reserved[3]);
-#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
- printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
- printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
-} /* wv_psa_show */
-#endif /* DEBUG_PSA_SHOW */
-
-#ifdef DEBUG_MMC_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the Modem Management Controller.
- * This function need to be completed...
- */
-static void
-wv_mmc_show(device * dev)
-{
- ioaddr_t base = dev->base_addr;
- net_local * lp = (net_local *)dev->priv;
- mmr_t m;
-
- /* Basic check */
- if(hasr_read(base) & HASR_NO_CLK)
- {
- printk(KERN_WARNING "%s: wv_mmc_show: modem not connected\n",
- dev->name);
- return;
- }
-
- wv_splhi(lp, &flags);
-
- /* Read the mmc */
- mmc_out(base, mmwoff(0, mmw_freeze), 1);
- mmc_read(base, 0, (u_char *)&m, sizeof(m));
- mmc_out(base, mmwoff(0, mmw_freeze), 0);
-
-#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */
- /* Don't forget to update statistics */
- lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
-#endif /* WIRELESS_EXT */
-
- wv_splx(lp, &flags);
-
- printk(KERN_DEBUG "##### wavelan modem status registers: #####\n");
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
- m.mmr_unused0[0],
- m.mmr_unused0[1],
- m.mmr_unused0[2],
- m.mmr_unused0[3],
- m.mmr_unused0[4],
- m.mmr_unused0[5],
- m.mmr_unused0[6],
- m.mmr_unused0[7]);
-#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG "Encryption algorythm: %02X - Status: %02X\n",
- m.mmr_des_avail, m.mmr_des_status);
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
- m.mmr_unused1[0],
- m.mmr_unused1[1],
- m.mmr_unused1[2],
- m.mmr_unused1[3],
- m.mmr_unused1[4]);
-#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
- m.mmr_dce_status,
- (m.mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? "energy detected,":"",
- (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
- "loop test indicated," : "",
- (m.mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? "transmitter on," : "",
- (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
- "jabber timer expired," : "");
- printk(KERN_DEBUG "Dsp ID: %02X\n",
- m.mmr_dsp_id);
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
- m.mmr_unused2[0],
- m.mmr_unused2[1]);
-#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
- (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
- (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
- printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
- m.mmr_thr_pre_set & MMR_THR_PRE_SET,
- (m.mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : "below");
- printk(KERN_DEBUG "signal_lvl: %d [%s], ",
- m.mmr_signal_lvl & MMR_SIGNAL_LVL,
- (m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : "no new msg");
- printk("silence_lvl: %d [%s], ", m.mmr_silence_lvl & MMR_SILENCE_LVL,
- (m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : "no new update");
- printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL,
- (m.mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : "Antenna 0");
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
-#endif /* DEBUG_SHOW_UNUSED */
-} /* wv_mmc_show */
-#endif /* DEBUG_MMC_SHOW */
-
-#ifdef DEBUG_I82593_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the i82593's receive unit.
- */
-static void
-wv_ru_show(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
-
- printk(KERN_DEBUG "##### wavelan i82593 receiver status: #####\n");
- printk(KERN_DEBUG "ru: rfp %d stop %d", lp->rfp, lp->stop);
- /*
- * Not implemented yet...
- */
- printk("\n");
-} /* wv_ru_show */
-#endif /* DEBUG_I82593_SHOW */
-
-#ifdef DEBUG_DEVICE_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the WaveLAN PCMCIA device driver.
- */
-static void
-wv_dev_show(device * dev)
-{
- printk(KERN_DEBUG "dev:");
- printk(" state=%lX,", dev->state);
- printk(" trans_start=%ld,", dev->trans_start);
- printk(" flags=0x%x,", dev->flags);
- printk("\n");
-} /* wv_dev_show */
-
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the WaveLAN PCMCIA device driver's
- * private information.
- */
-static void
-wv_local_show(device * dev)
-{
- net_local *lp;
-
- lp = (net_local *)dev->priv;
-
- printk(KERN_DEBUG "local:");
- /*
- * Not implemented yet...
- */
- printk("\n");
-} /* wv_local_show */
-#endif /* DEBUG_DEVICE_SHOW */
-
-#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO)
-/*------------------------------------------------------------------*/
-/*
- * Dump packet header (and content if necessary) on the screen
- */
-static inline void
-wv_packet_info(u_char * p, /* Packet to dump */
- int length, /* Length of the packet */
- char * msg1, /* Name of the device */
- char * msg2) /* Name of the function */
-{
- int i;
- int maxi;
-
- printk(KERN_DEBUG "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n",
- msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length);
- printk(KERN_DEBUG "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n",
- msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13]);
-
-#ifdef DEBUG_PACKET_DUMP
-
- printk(KERN_DEBUG "data=\"");
-
- if((maxi = length) > DEBUG_PACKET_DUMP)
- maxi = DEBUG_PACKET_DUMP;
- for(i = 14; i < maxi; i++)
- if(p[i] >= ' ' && p[i] <= '~')
- printk(" %c", p[i]);
- else
- printk("%02X", p[i]);
- if(maxi < length)
- printk("..");
- printk("\"\n");
- printk(KERN_DEBUG "\n");
-#endif /* DEBUG_PACKET_DUMP */
-}
-#endif /* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */
-
-/*------------------------------------------------------------------*/
-/*
- * This is the information which is displayed by the driver at startup
- * There is a lot of flag to configure it at your will...
- */
-static inline void
-wv_init_info(device * dev)
-{
- ioaddr_t base = dev->base_addr;
- psa_t psa;
- int i;
-
- /* Read the parameter storage area */
- psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
-
-#ifdef DEBUG_PSA_SHOW
- wv_psa_show(&psa);
-#endif
-#ifdef DEBUG_MMC_SHOW
- wv_mmc_show(dev);
-#endif
-#ifdef DEBUG_I82593_SHOW
- wv_ru_show(dev);
-#endif
-
-#ifdef DEBUG_BASIC_SHOW
- /* Now, let's go for the basic stuff */
- printk(KERN_NOTICE "%s: WaveLAN: port %#x, irq %d, hw_addr",
- dev->name, base, dev->irq);
- for(i = 0; i < WAVELAN_ADDR_SIZE; i++)
- printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]);
-
- /* Print current network id */
- if(psa.psa_nwid_select)
- printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], psa.psa_nwid[1]);
- else
- printk(", nwid off");
-
- /* If 2.00 card */
- if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
- (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
- {
- unsigned short freq;
-
- /* Ask the EEprom to read the frequency from the first area */
- fee_read(base, 0x00 /* 1st area - frequency... */,
- &freq, 1);
-
- /* Print frequency */
- printk(", 2.00, %ld", (freq >> 6) + 2400L);
-
- /* Hack !!! */
- if(freq & 0x20)
- printk(".5");
- }
- else
- {
- printk(", PCMCIA, ");
- switch (psa.psa_subband)
- {
- case PSA_SUBBAND_915:
- printk("915");
- break;
- case PSA_SUBBAND_2425:
- printk("2425");
- break;
- case PSA_SUBBAND_2460:
- printk("2460");
- break;
- case PSA_SUBBAND_2484:
- printk("2484");
- break;
- case PSA_SUBBAND_2430_5:
- printk("2430.5");
- break;
- default:
- printk("???");
- }
- }
-
- printk(" MHz\n");
-#endif /* DEBUG_BASIC_SHOW */
-
-#ifdef DEBUG_VERSION_SHOW
- /* Print version information */
- printk(KERN_NOTICE "%s", version);
-#endif
-} /* wv_init_info */
-
-/********************* IOCTL, STATS & RECONFIG *********************/
-/*
- * We found here routines that are called by Linux on differents
- * occasions after the configuration and not for transmitting data
- * These may be called when the user use ifconfig, /proc/net/dev
- * or wireless extensions
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Get the current ethernet statistics. This may be called with the
- * card open or closed.
- * Used when the user read /proc/net/dev
- */
-static en_stats *
-wavelan_get_stats(device * dev)
-{
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
-#endif
-
- return(&((net_local *) dev->priv)->stats);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Set or clear the multicast filter for this adaptor.
- * num_addrs == -1 Promiscuous mode, receive all packets
- * num_addrs == 0 Normal mode, clear multicast list
- * num_addrs > 0 Multicast mode, receive normal and MC packets,
- * and do best-effort filtering.
- */
-
-static void
-wavelan_set_multicast_list(device * dev)
-{
- net_local * lp = (net_local *) dev->priv;
-
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name);
-#endif
-
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n",
- dev->name, dev->flags, dev->mc_count);
-#endif
-
- if(dev->flags & IFF_PROMISC)
- {
- /*
- * Enable promiscuous mode: receive all packets.
- */
- if(!lp->promiscuous)
- {
- lp->promiscuous = 1;
- lp->allmulticast = 0;
- lp->mc_count = 0;
-
- wv_82593_reconfig(dev);
-
- /* Tell the kernel that we are doing a really bad job... */
- dev->flags |= IFF_PROMISC;
- }
- }
- else
- /* If all multicast addresses
- * or too much multicast addresses for the hardware filter */
- if((dev->flags & IFF_ALLMULTI) ||
- (dev->mc_count > I82593_MAX_MULTICAST_ADDRESSES))
- {
- /*
- * Disable promiscuous mode, but active the all multicast mode
- */
- if(!lp->allmulticast)
- {
- lp->promiscuous = 0;
- lp->allmulticast = 1;
- lp->mc_count = 0;
-
- wv_82593_reconfig(dev);
-
- /* Tell the kernel that we are doing a really bad job... */
- dev->flags |= IFF_ALLMULTI;
- }
- }
- else
- /* If there is some multicast addresses to send */
- if(dev->mc_list != (struct dev_mc_list *) NULL)
- {
- /*
- * Disable promiscuous mode, but receive all packets
- * in multicast list
- */
-#ifdef MULTICAST_AVOID
- if(lp->promiscuous || lp->allmulticast ||
- (dev->mc_count != lp->mc_count))
-#endif
- {
- lp->promiscuous = 0;
- lp->allmulticast = 0;
- lp->mc_count = dev->mc_count;
-
- wv_82593_reconfig(dev);
- }
- }
- else
- {
- /*
- * Switch to normal mode: disable promiscuous mode and
- * clear the multicast list.
- */
- if(lp->promiscuous || lp->mc_count == 0)
- {
- lp->promiscuous = 0;
- lp->allmulticast = 0;
- lp->mc_count = 0;
-
- wv_82593_reconfig(dev);
- }
- }
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This function doesn't exist...
- * (Note : it was a nice way to test the reconfigure stuff...)
- */
-#ifdef SET_MAC_ADDRESS
-static int
-wavelan_set_mac_address(device * dev,
- void * addr)
-{
- struct sockaddr * mac = addr;
-
- /* Copy the address */
- memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE);
-
- /* Reconfig the beast */
- wv_82593_reconfig(dev);
-
- return 0;
-}
-#endif /* SET_MAC_ADDRESS */
-
-#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */
-
-/*------------------------------------------------------------------*/
-/*
- * Frequency setting (for hardware able of it)
- * It's a bit complicated and you don't really want to look into it...
- * (called in wavelan_ioctl)
- */
-static inline int
-wv_set_frequency(u_long base, /* i/o port of the card */
- iw_freq * frequency)
-{
- const int BAND_NUM = 10; /* Number of bands */
- long freq = 0L; /* offset to 2.4 GHz in .5 MHz */
-#ifdef DEBUG_IOCTL_INFO
- int i;
-#endif
-
- /* Setting by frequency */
- /* Theoritically, you may set any frequency between
- * the two limits with a 0.5 MHz precision. In practice,
- * I don't want you to have trouble with local
- * regulations... */
- if((frequency->e == 1) &&
- (frequency->m >= (int) 2.412e8) && (frequency->m <= (int) 2.487e8))
- {
- freq = ((frequency->m / 10000) - 24000L) / 5;
- }
-
- /* Setting by channel (same as wfreqsel) */
- /* Warning : each channel is 22MHz wide, so some of the channels
- * will interfere... */
- if((frequency->e == 0) &&
- (frequency->m >= 0) && (frequency->m < BAND_NUM))
- {
- /* Get frequency offset. */
- freq = channel_bands[frequency->m] >> 1;
- }
-
- /* Verify if the frequency is allowed */
- if(freq != 0L)
- {
- u_short table[10]; /* Authorized frequency table */
-
- /* Read the frequency table */
- fee_read(base, 0x71 /* frequency table */,
- table, 10);
-
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG "Frequency table :");
- for(i = 0; i < 10; i++)
- {
- printk(" %04X",
- table[i]);
- }
- printk("\n");
-#endif
-
- /* Look in the table if the frequency is allowed */
- if(!(table[9 - ((freq - 24) / 16)] &
- (1 << ((freq - 24) % 16))))
- return -EINVAL; /* not allowed */
- }
- else
- return -EINVAL;
-
- /* If we get a usable frequency */
- if(freq != 0L)
- {
- unsigned short area[16];
- unsigned short dac[2];
- unsigned short area_verify[16];
- unsigned short dac_verify[2];
- /* Corresponding gain (in the power adjust value table)
- * see AT&T Wavelan Data Manual, REF 407-024689/E, page 3-8
- * & WCIN062D.DOC, page 6.2.9 */
- unsigned short power_limit[] = { 40, 80, 120, 160, 0 };
- int power_band = 0; /* Selected band */
- unsigned short power_adjust; /* Correct value */
-
- /* Search for the gain */
- power_band = 0;
- while((freq > power_limit[power_band]) &&
- (power_limit[++power_band] != 0))
- ;
-
- /* Read the first area */
- fee_read(base, 0x00,
- area, 16);
-
- /* Read the DAC */
- fee_read(base, 0x60,
- dac, 2);
-
- /* Read the new power adjust value */
- fee_read(base, 0x6B - (power_band >> 1),
- &power_adjust, 1);
- if(power_band & 0x1)
- power_adjust >>= 8;
- else
- power_adjust &= 0xFF;
-
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
- for(i = 0; i < 16; i++)
- {
- printk(" %04X",
- area[i]);
- }
- printk("\n");
-
- printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
- dac[0], dac[1]);
-#endif
-
- /* Frequency offset (for info only...) */
- area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
-
- /* Receiver Principle main divider coefficient */
- area[3] = (freq >> 1) + 2400L - 352L;
- area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
-
- /* Transmitter Main divider coefficient */
- area[13] = (freq >> 1) + 2400L;
- area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
-
- /* Others part of the area are flags, bit streams or unused... */
-
- /* Set the value in the DAC */
- dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
- dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
-
- /* Write the first area */
- fee_write(base, 0x00,
- area, 16);
-
- /* Write the DAC */
- fee_write(base, 0x60,
- dac, 2);
-
- /* We now should verify here that the EEprom writting was ok */
-
- /* ReRead the first area */
- fee_read(base, 0x00,
- area_verify, 16);
-
- /* ReRead the DAC */
- fee_read(base, 0x60,
- dac_verify, 2);
-
- /* Compare */
- if(memcmp(area, area_verify, 16 * 2) ||
- memcmp(dac, dac_verify, 2 * 2))
- {
-#ifdef DEBUG_IOCTL_ERROR
- printk(KERN_INFO "Wavelan: wv_set_frequency : unable to write new frequency to EEprom (??)\n");
-#endif
- return -EOPNOTSUPP;
- }
-
- /* We must download the frequency parameters to the
- * synthetisers (from the EEprom - area 1)
- * Note : as the EEprom is auto decremented, we set the end
- * if the area... */
- mmc_out(base, mmwoff(0, mmw_fee_addr), 0x0F);
- mmc_out(base, mmwoff(0, mmw_fee_ctrl),
- MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
-
- /* Wait until the download is finished */
- fee_wait(base, 100, 100);
-
- /* We must now download the power adjust value (gain) to
- * the synthetisers (from the EEprom - area 7 - DAC) */
- mmc_out(base, mmwoff(0, mmw_fee_addr), 0x61);
- mmc_out(base, mmwoff(0, mmw_fee_ctrl),
- MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
-
- /* Wait until the download is finished */
- fee_wait(base, 100, 100);
-
-#ifdef DEBUG_IOCTL_INFO
- /* Verification of what we have done... */
-
- printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
- for(i = 0; i < 16; i++)
- {
- printk(" %04X",
- area_verify[i]);
- }
- printk("\n");
-
- printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
- dac_verify[0], dac_verify[1]);
-#endif
-
- return 0;
- }
- else
- return -EINVAL; /* Bah, never get there... */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Give the list of available frequencies
- */
-static inline int
-wv_frequency_list(u_long base, /* i/o port of the card */
- iw_freq * list, /* List of frequency to fill */
- int max) /* Maximum number of frequencies */
-{
- u_short table[10]; /* Authorized frequency table */
- long freq = 0L; /* offset to 2.4 GHz in .5 MHz + 12 MHz */
- int i; /* index in the table */
-#if WIRELESS_EXT > 7
- const int BAND_NUM = 10; /* Number of bands */
- int c = 0; /* Channel number */
-#endif /* WIRELESS_EXT */
-
- /* Read the frequency table */
- fee_read(base, 0x71 /* frequency table */,
- table, 10);
-
- /* Look all frequencies */
- i = 0;
- for(freq = 0; freq < 150; freq++)
- /* Look in the table if the frequency is allowed */
- if(table[9 - (freq / 16)] & (1 << (freq % 16)))
- {
-#if WIRELESS_EXT > 7
- /* Compute approximate channel number */
- while((((channel_bands[c] >> 1) - 24) < freq) &&
- (c < BAND_NUM))
- c++;
- list[i].i = c; /* Set the list index */
-#endif /* WIRELESS_EXT */
-
- /* put in the list */
- list[i].m = (((freq + 24) * 5) + 24000L) * 10000;
- list[i++].e = 1;
-
- /* Check number */
- if(i >= max)
- return(i);
- }
-
- return(i);
-}
-
-#ifdef WIRELESS_SPY
-/*------------------------------------------------------------------*/
-/*
- * Gather wireless spy statistics : for each packet, compare the source
- * address with out list, and if match, get the stats...
- * Sorry, but this function really need wireless extensions...
- */
-static inline void
-wl_spy_gather(device * dev,
- u_char * mac, /* MAC address */
- u_char * stats) /* Statistics to gather */
-{
- net_local * lp = (net_local *) dev->priv;
- int i;
-
- /* Look all addresses */
- for(i = 0; i < lp->spy_number; i++)
- /* If match */
- if(!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE))
- {
- /* Update statistics */
- lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL;
- lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL;
- lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL;
- lp->spy_stat[i].updated = 0x7;
- }
-}
-#endif /* WIRELESS_SPY */
-
-#ifdef HISTOGRAM
-/*------------------------------------------------------------------*/
-/*
- * This function calculate an histogram on the signal level.
- * As the noise is quite constant, it's like doing it on the SNR.
- * We have defined a set of interval (lp->his_range), and each time
- * the level goes in that interval, we increment the count (lp->his_sum).
- * With this histogram you may detect if one wavelan is really weak,
- * or you may also calculate the mean and standard deviation of the level...
- */
-static inline void
-wl_his_gather(device * dev,
- u_char * stats) /* Statistics to gather */
-{
- net_local * lp = (net_local *) dev->priv;
- u_char level = stats[0] & MMR_SIGNAL_LVL;
- int i;
-
- /* Find the correct interval */
- i = 0;
- while((i < (lp->his_number - 1)) && (level >= lp->his_range[i++]))
- ;
-
- /* Increment interval counter */
- (lp->his_sum[i])++;
-}
-#endif /* HISTOGRAM */
-
-/*------------------------------------------------------------------*/
-/*
- * Perform ioctl : config & info stuff
- * This is here that are treated the wireless extensions (iwconfig)
- */
-static int
-wavelan_ioctl(struct net_device * dev, /* Device on wich the ioctl apply */
- struct ifreq * rq, /* Data passed */
- int cmd) /* Ioctl number */
-{
- ioaddr_t base = dev->base_addr;
- net_local * lp = (net_local *)dev->priv; /* lp is not unused */
- struct iwreq * wrq = (struct iwreq *) rq;
- psa_t psa;
- mm_t m;
- unsigned long flags;
- int ret = 0;
-
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd);
-#endif
-
- /* Disable interrupts & save flags */
- wv_splhi(lp, &flags);
-
- /* Look what is the request */
- switch(cmd)
- {
- /* --------------- WIRELESS EXTENSIONS --------------- */
-
- case SIOCGIWNAME:
- strcpy(wrq->u.name, "Wavelan");
- break;
-
- case SIOCSIWNWID:
- /* Set NWID in wavelan */
-#if WIRELESS_EXT > 8
- if(!wrq->u.nwid.disabled)
- {
- /* Set NWID in psa */
- psa.psa_nwid[0] = (wrq->u.nwid.value & 0xFF00) >> 8;
- psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF;
-#else /* WIRELESS_EXT > 8 */
- if(wrq->u.nwid.on)
- {
- /* Set NWID in psa */
- psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8;
- psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF;
-#endif /* WIRELESS_EXT > 8 */
- psa.psa_nwid_select = 0x01;
- psa_write(dev, (char *)psa.psa_nwid - (char *)&psa,
- (unsigned char *)psa.psa_nwid, 3);
-
- /* Set NWID in mmc */
- m.w.mmw_netw_id_l = psa.psa_nwid[1];
- m.w.mmw_netw_id_h = psa.psa_nwid[0];
- mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m,
- (unsigned char *)&m.w.mmw_netw_id_l, 2);
- mmc_out(base, mmwoff(0, mmw_loopt_sel), 0x00);
- }
- else
- {
- /* Disable nwid in the psa */
- psa.psa_nwid_select = 0x00;
- psa_write(dev, (char *)&psa.psa_nwid_select - (char *)&psa,
- (unsigned char *)&psa.psa_nwid_select, 1);
-
- /* Disable nwid in the mmc (no filtering) */
- mmc_out(base, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID);
- }
- /* update the Wavelan checksum */
- update_psa_checksum(dev);
- break;
-
- case SIOCGIWNWID:
- /* Read the NWID */
- psa_read(dev, (char *)psa.psa_nwid - (char *)&psa,
- (unsigned char *)psa.psa_nwid, 3);
-#if WIRELESS_EXT > 8
- wrq->u.nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
- wrq->u.nwid.disabled = !(psa.psa_nwid_select);
- wrq->u.nwid.fixed = 1; /* Superfluous */
-#else /* WIRELESS_EXT > 8 */
- wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
- wrq->u.nwid.on = psa.psa_nwid_select;
-#endif /* WIRELESS_EXT > 8 */
- break;
-
- case SIOCSIWFREQ:
- /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
- if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
- (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
- ret = wv_set_frequency(base, &(wrq->u.freq));
- else
- ret = -EOPNOTSUPP;
- break;
-
- case SIOCGIWFREQ:
- /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
- * (does it work for everybody ??? - especially old cards...) */
- if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
- (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
- {
- unsigned short freq;
-
- /* Ask the EEprom to read the frequency from the first area */
- fee_read(base, 0x00 /* 1st area - frequency... */,
- &freq, 1);
- wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000;
- wrq->u.freq.e = 1;
- }
- else
- {
- psa_read(dev, (char *)&psa.psa_subband - (char *)&psa,
- (unsigned char *)&psa.psa_subband, 1);
-
- if(psa.psa_subband <= 4)
- {
- wrq->u.freq.m = fixed_bands[psa.psa_subband];
- wrq->u.freq.e = (psa.psa_subband != 0);
- }
- else
- ret = -EOPNOTSUPP;
- }
- break;
-
- case SIOCSIWSENS:
- /* Set the level threshold */
-#if WIRELESS_EXT > 7
- /* We should complain loudly if wrq->u.sens.fixed = 0, because we
- * can't set auto mode... */
- psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F;
-#else /* WIRELESS_EXT > 7 */
- psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F;
-#endif /* WIRELESS_EXT > 7 */
- psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
- (unsigned char *)&psa.psa_thr_pre_set, 1);
- /* update the Wavelan checksum */
- update_psa_checksum(dev);
- mmc_out(base, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set);
- break;
-
- case SIOCGIWSENS:
- /* Read the level threshold */
- psa_read(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
- (unsigned char *)&psa.psa_thr_pre_set, 1);
-#if WIRELESS_EXT > 7
- wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F;
- wrq->u.sens.fixed = 1;
-#else /* WIRELESS_EXT > 7 */
- wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F;
-#endif /* WIRELESS_EXT > 7 */
- break;
-
-#if WIRELESS_EXT > 8
- case SIOCSIWENCODE:
- /* Set encryption key */
- if(!mmc_encr(base))
- {
- ret = -EOPNOTSUPP;
- break;
- }
-
- /* Basic checking... */
- if(wrq->u.encoding.pointer != (caddr_t) 0)
- {
- /* Check the size of the key */
- if(wrq->u.encoding.length != 8)
- {
- ret = -EINVAL;
- break;
- }
-
- /* Copy the key in the driver */
- if(copy_from_user(psa.psa_encryption_key, wrq->u.encoding.pointer,
- wrq->u.encoding.length))
- {
- ret = -EFAULT;
- break;
- }
-
- psa.psa_encryption_select = 1;
- psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
- (unsigned char *) &psa.psa_encryption_select, 8+1);
-
- mmc_out(base, mmwoff(0, mmw_encr_enable),
- MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
- mmc_write(base, mmwoff(0, mmw_encr_key),
- (unsigned char *) &psa.psa_encryption_key, 8);
- }
-
- if(wrq->u.encoding.flags & IW_ENCODE_DISABLED)
- { /* disable encryption */
- psa.psa_encryption_select = 0;
- psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
- (unsigned char *) &psa.psa_encryption_select, 1);
-
- mmc_out(base, mmwoff(0, mmw_encr_enable), 0);
- }
- /* update the Wavelan checksum */
- update_psa_checksum(dev);
- break;
-
- case SIOCGIWENCODE:
- /* Read the encryption key */
- if(!mmc_encr(base))
- {
- ret = -EOPNOTSUPP;
- break;
- }
-
- /* only super-user can see encryption key */
- if(!capable(CAP_NET_ADMIN))
- {
- ret = -EPERM;
- break;
- }
-
- /* Basic checking... */
- if(wrq->u.encoding.pointer != (caddr_t) 0)
- {
- psa_read(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
- (unsigned char *) &psa.psa_encryption_select, 1+8);
-
- /* encryption is enabled ? */
- if(psa.psa_encryption_select)
- wrq->u.encoding.flags = IW_ENCODE_ENABLED;
- else
- wrq->u.encoding.flags = IW_ENCODE_DISABLED;
- wrq->u.encoding.flags |= mmc_encr(base);
-
- /* Copy the key to the user buffer */
- wrq->u.encoding.length = 8;
- if(copy_to_user(wrq->u.encoding.pointer, psa.psa_encryption_key, 8))
- ret = -EFAULT;
- }
- break;
-#endif /* WIRELESS_EXT > 8 */
-
-#ifdef WAVELAN_ROAMING_EXT
-#if WIRELESS_EXT > 5
- case SIOCSIWESSID:
- /* Check if disable */
- if(wrq->u.data.flags == 0)
- lp->filter_domains = 0;
- else
- /* Basic checking... */
- if(wrq->u.data.pointer != (caddr_t) 0)
- {
- char essid[IW_ESSID_MAX_SIZE + 1];
- char * endp;
-
- /* Check the size of the string */
- if(wrq->u.data.length > IW_ESSID_MAX_SIZE + 1)
- {
- ret = -E2BIG;
- break;
- }
-
- /* Copy the string in the driver */
- if(copy_from_user(essid, wrq->u.data.pointer, wrq->u.data.length))
- {
- ret = -EFAULT;
- break;
- }
- essid[IW_ESSID_MAX_SIZE] = '\0';
-
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG "SetEssid : ``%s''\n", essid);
-#endif /* DEBUG_IOCTL_INFO */
-
- /* Convert to a number (note : Wavelan specific) */
- lp->domain_id = simple_strtoul(essid, &endp, 16);
- /* Has it worked ? */
- if(endp > essid)
- lp->filter_domains = 1;
- else
- {
- lp->filter_domains = 0;
- ret = -EINVAL;
- }
- }
- break;
-
- case SIOCGIWESSID:
- /* Basic checking... */
- if(wrq->u.data.pointer != (caddr_t) 0)
- {
- char essid[IW_ESSID_MAX_SIZE + 1];
-
- /* Is the domain ID active ? */
- wrq->u.data.flags = lp->filter_domains;
-
- /* Copy Domain ID into a string (Wavelan specific) */
- /* Sound crazy, be we can't have a snprintf in the kernel !!! */
- sprintf(essid, "%lX", lp->domain_id);
- essid[IW_ESSID_MAX_SIZE] = '\0';
-
- /* Set the length */
- wrq->u.data.length = strlen(essid) + 1;
-
- /* Copy structure to the user buffer */
- if(copy_to_user(wrq->u.data.pointer, essid, wrq->u.data.length))
- ret = -EFAULT;
- }
- break;
-
- case SIOCSIWAP:
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG "Set AP to : %02X:%02X:%02X:%02X:%02X:%02X\n",
- wrq->u.ap_addr.sa_data[0],
- wrq->u.ap_addr.sa_data[1],
- wrq->u.ap_addr.sa_data[2],
- wrq->u.ap_addr.sa_data[3],
- wrq->u.ap_addr.sa_data[4],
- wrq->u.ap_addr.sa_data[5]);
-#endif /* DEBUG_IOCTL_INFO */
-
- ret = -EOPNOTSUPP; /* Not supported yet */
- break;
-
- case SIOCGIWAP:
- /* Should get the real McCoy instead of own Ethernet address */
- memcpy(wrq->u.ap_addr.sa_data, dev->dev_addr, WAVELAN_ADDR_SIZE);
- wrq->u.ap_addr.sa_family = ARPHRD_ETHER;
-
- ret = -EOPNOTSUPP; /* Not supported yet */
- break;
-#endif /* WIRELESS_EXT > 5 */
-#endif /* WAVELAN_ROAMING_EXT */
-
-#if WIRELESS_EXT > 8
-#ifdef WAVELAN_ROAMING
- case SIOCSIWMODE:
- switch(wrq->u.mode)
- {
- case IW_MODE_ADHOC:
- if(do_roaming)
- {
- wv_roam_cleanup(dev);
- do_roaming = 0;
- }
- break;
- case IW_MODE_INFRA:
- if(!do_roaming)
- {
- wv_roam_init(dev);
- do_roaming = 1;
- }
- break;
- default:
- ret = -EINVAL;
- }
- break;
-
- case SIOCGIWMODE:
- if(do_roaming)
- wrq->u.mode = IW_MODE_INFRA;
- else
- wrq->u.mode = IW_MODE_ADHOC;
- break;
-#endif /* WAVELAN_ROAMING */
-#endif /* WIRELESS_EXT > 8 */
-
- case SIOCGIWRANGE:
- /* Basic checking... */
- if(wrq->u.data.pointer != (caddr_t) 0)
- {
- struct iw_range range;
-
- /* Set the length (very important for backward compatibility) */
- wrq->u.data.length = sizeof(struct iw_range);
-
- /* Set all the info we don't care or don't know about to zero */
- memset(&range, 0, sizeof(range));
-
-#if WIRELESS_EXT > 10
- /* Set the Wireless Extension versions */
- range.we_version_compiled = WIRELESS_EXT;
- range.we_version_source = 9; /* Nothing for us in v10 and v11 */
-#endif /* WIRELESS_EXT > 10 */
-
- /* Set information in the range struct */
- range.throughput = 1.4 * 1000 * 1000; /* don't argue on this ! */
- range.min_nwid = 0x0000;
- range.max_nwid = 0xFFFF;
-
- /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
- if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
- (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
- {
- range.num_channels = 10;
- range.num_frequency = wv_frequency_list(base, range.freq,
- IW_MAX_FREQUENCIES);
- }
- else
- range.num_channels = range.num_frequency = 0;
-
- range.sensitivity = 0x3F;
- range.max_qual.qual = MMR_SGNL_QUAL;
- range.max_qual.level = MMR_SIGNAL_LVL;
- range.max_qual.noise = MMR_SILENCE_LVL;
-#if WIRELESS_EXT > 11
- range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */
- /* Need to get better values for those two */
- range.avg_qual.level = 30;
- range.avg_qual.noise = 8;
-#endif /* WIRELESS_EXT > 11 */
-
-#if WIRELESS_EXT > 7
- range.num_bitrates = 1;
- range.bitrate[0] = 2000000; /* 2 Mb/s */
-#endif /* WIRELESS_EXT > 7 */
-
-#if WIRELESS_EXT > 8
- /* Encryption supported ? */
- if(mmc_encr(base))
- {
- range.encoding_size[0] = 8; /* DES = 64 bits key */
- range.num_encoding_sizes = 1;
- range.max_encoding_tokens = 1; /* Only one key possible */
- }
- else
- {
- range.num_encoding_sizes = 0;
- range.max_encoding_tokens = 0;
- }
-#endif /* WIRELESS_EXT > 8 */
-
- /* Copy structure to the user buffer */
- if(copy_to_user(wrq->u.data.pointer, &range,
- sizeof(struct iw_range)))
- ret = -EFAULT;
- }
- break;
-
- case SIOCGIWPRIV:
- /* Basic checking... */
- if(wrq->u.data.pointer != (caddr_t) 0)
- {
- struct iw_priv_args priv[] =
- { /* cmd, set_args, get_args, name */
- { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" },
- { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" },
- { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16, 0, "sethisto" },
- { SIOCGIPHISTO, 0, IW_PRIV_TYPE_INT | 16, "gethisto" },
- { SIOCSIPROAM, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1 , 0, "setroam" },
- { SIOCGIPROAM, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getroam" },
- };
-
- /* Set the number of ioctl available */
- wrq->u.data.length = 6;
-
- /* Copy structure to the user buffer */
- if(copy_to_user(wrq->u.data.pointer, (u_char *) priv,
- sizeof(priv)))
- ret = -EFAULT;
- }
- break;
-
-#ifdef WIRELESS_SPY
- case SIOCSIWSPY:
- /* Set the spy list */
-
- /* Check the number of addresses */
- if(wrq->u.data.length > IW_MAX_SPY)
- {
- ret = -E2BIG;
- break;
- }
- lp->spy_number = wrq->u.data.length;
-
- /* If there is some addresses to copy */
- if(lp->spy_number > 0)
- {
- struct sockaddr address[IW_MAX_SPY];
- int i;
-
- /* Copy addresses to the driver */
- if(copy_from_user(address, wrq->u.data.pointer,
- sizeof(struct sockaddr) * lp->spy_number))
- {
- ret = -EFAULT;
- break;
- }
-
- /* Copy addresses to the lp structure */
- for(i = 0; i < lp->spy_number; i++)
- {
- memcpy(lp->spy_address[i], address[i].sa_data,
- WAVELAN_ADDR_SIZE);
- }
-
- /* Reset structure... */
- memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY);
-
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG "SetSpy - Set of new addresses is :\n");
- for(i = 0; i < wrq->u.data.length; i++)
- printk(KERN_DEBUG "%02X:%02X:%02X:%02X:%02X:%02X\n",
- lp->spy_address[i][0],
- lp->spy_address[i][1],
- lp->spy_address[i][2],
- lp->spy_address[i][3],
- lp->spy_address[i][4],
- lp->spy_address[i][5]);
-#endif /* DEBUG_IOCTL_INFO */
- }
-
- break;
-
- case SIOCGIWSPY:
- /* Get the spy list and spy stats */
-
- /* Set the number of addresses */
- wrq->u.data.length = lp->spy_number;
-
- /* If the user want to have the addresses back... */
- if((lp->spy_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
- {
- struct sockaddr address[IW_MAX_SPY];
- int i;
-
- /* Copy addresses from the lp structure */
- for(i = 0; i < lp->spy_number; i++)
- {
- memcpy(address[i].sa_data, lp->spy_address[i],
- WAVELAN_ADDR_SIZE);
- address[i].sa_family = ARPHRD_ETHER;
- }
-
- /* Copy addresses to the user buffer */
- if(copy_to_user(wrq->u.data.pointer, address,
- sizeof(struct sockaddr) * lp->spy_number))
- {
- ret = -EFAULT;
- break;
- }
-
- /* Copy stats to the user buffer (just after) */
- if(copy_to_user(wrq->u.data.pointer +
- (sizeof(struct sockaddr) * lp->spy_number),
- lp->spy_stat, sizeof(iw_qual) * lp->spy_number))
- {
- ret = -EFAULT;
- break;
- }
-
- /* Reset updated flags */
- for(i = 0; i < lp->spy_number; i++)
- lp->spy_stat[i].updated = 0x0;
- } /* if(pointer != NULL) */
-
- break;
-#endif /* WIRELESS_SPY */
-
- /* ------------------ PRIVATE IOCTL ------------------ */
-
- case SIOCSIPQTHR:
- if(!capable(CAP_NET_ADMIN))
- {
- ret = -EPERM;
- break;
- }
- psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
- psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
- (unsigned char *)&psa.psa_quality_thr, 1);
- /* update the Wavelan checksum */
- update_psa_checksum(dev);
- mmc_out(base, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr);
- break;
-
- case SIOCGIPQTHR:
- psa_read(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
- (unsigned char *)&psa.psa_quality_thr, 1);
- *(wrq->u.name) = psa.psa_quality_thr & 0x0F;
- break;
-
-#ifdef WAVELAN_ROAMING
- case SIOCSIPROAM:
- /* Note : should check if user == root */
- if(do_roaming && (*wrq->u.name)==0)
- wv_roam_cleanup(dev);
- else if(do_roaming==0 && (*wrq->u.name)!=0)
- wv_roam_init(dev);
-
- do_roaming = (*wrq->u.name);
-
- break;
-
- case SIOCGIPROAM:
- *(wrq->u.name) = do_roaming;
- break;
-#endif /* WAVELAN_ROAMING */
-
-#ifdef HISTOGRAM
- case SIOCSIPHISTO:
- /* Verif if the user is root */
- if(!capable(CAP_NET_ADMIN))
- {
- ret = -EPERM;
- }
-
- /* Check the number of intervals */
- if(wrq->u.data.length > 16)
- {
- ret = -E2BIG;
- break;
- }
- lp->his_number = wrq->u.data.length;
-
- /* If there is some addresses to copy */
- if(lp->his_number > 0)
- {
- /* Copy interval ranges to the driver */
- if(copy_from_user(lp->his_range, wrq->u.data.pointer,
- sizeof(char) * lp->his_number))
- {
- ret = -EFAULT;
- break;
- }
-
- /* Reset structure... */
- memset(lp->his_sum, 0x00, sizeof(long) * 16);
- }
- break;
-
- case SIOCGIPHISTO:
- /* Set the number of intervals */
- wrq->u.data.length = lp->his_number;
-
- /* Give back the distribution statistics */
- if((lp->his_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
- {
- /* Copy data to the user buffer */
- if(copy_to_user(wrq->u.data.pointer, lp->his_sum,
- sizeof(long) * lp->his_number))
- ret = -EFAULT;
-
- } /* if(pointer != NULL) */
- break;
-#endif /* HISTOGRAM */
-
- /* ------------------- OTHER IOCTL ------------------- */
-
- default:
- ret = -EOPNOTSUPP;
- }
-
- /* ReEnable interrupts & restore flags */
- wv_splx(lp, &flags);
-
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
-#endif
- return ret;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Get wireless statistics
- * Called by /proc/net/wireless...
- */
-static iw_stats *
-wavelan_get_wireless_stats(device * dev)
-{
- ioaddr_t base = dev->base_addr;
- net_local * lp = (net_local *) dev->priv;
- mmr_t m;
- iw_stats * wstats;
- unsigned long flags;
-
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name);
-#endif
-
- /* Disable interrupts & save flags */
- wv_splhi(lp, &flags);
-
- wstats = &lp->wstats;
-
- /* Get data from the mmc */
- mmc_out(base, mmwoff(0, mmw_freeze), 1);
-
- mmc_read(base, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1);
- mmc_read(base, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, 2);
- mmc_read(base, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, 4);
-
- mmc_out(base, mmwoff(0, mmw_freeze), 0);
-
- /* Copy data to wireless stuff */
- wstats->status = m.mmr_dce_status & MMR_DCE_STATUS;
- wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
- wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
- wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
- wstats->qual.updated = (((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) |
- ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) |
- ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5));
- wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
- wstats->discard.code = 0L;
- wstats->discard.misc = 0L;
-
- /* ReEnable interrupts & restore flags */
- wv_splx(lp, &flags);
-
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name);
-#endif
- return &lp->wstats;
-}
-#endif /* WIRELESS_EXT */
-
-/************************* PACKET RECEPTION *************************/
-/*
- * This part deal with receiving the packets.
- * The interrupt handler get an interrupt when a packet has been
- * successfully received and called this part...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Calculate the starting address of the frame pointed to by the receive
- * frame pointer and verify that the frame seem correct
- * (called by wv_packet_rcv())
- */
-static inline int
-wv_start_of_frame(device * dev,
- int rfp, /* end of frame */
- int wrap) /* start of buffer */
-{
- ioaddr_t base = dev->base_addr;
- int rp;
- int len;
-
- rp = (rfp - 5 + RX_SIZE) % RX_SIZE;
- outb(rp & 0xff, PIORL(base));
- outb(((rp >> 8) & PIORH_MASK), PIORH(base));
- len = inb(PIOP(base));
- len |= inb(PIOP(base)) << 8;
-
- /* Sanity checks on size */
- /* Frame too big */
- if(len > MAXDATAZ + 100)
- {
-#ifdef DEBUG_RX_ERROR
- printk(KERN_INFO "%s: wv_start_of_frame: Received frame too large, rfp %d len 0x%x\n",
- dev->name, rfp, len);
-#endif
- return(-1);
- }
-
- /* Frame too short */
- if(len < 7)
- {
-#ifdef DEBUG_RX_ERROR
- printk(KERN_INFO "%s: wv_start_of_frame: Received null frame, rfp %d len 0x%x\n",
- dev->name, rfp, len);
-#endif
- return(-1);
- }
-
- /* Wrap around buffer */
- if(len > ((wrap - (rfp - len) + RX_SIZE) % RX_SIZE)) /* magic formula ! */
- {
-#ifdef DEBUG_RX_ERROR
- printk(KERN_INFO "%s: wv_start_of_frame: wrap around buffer, wrap %d rfp %d len 0x%x\n",
- dev->name, wrap, rfp, len);
-#endif
- return(-1);
- }
-
- return((rp - len + RX_SIZE) % RX_SIZE);
-} /* wv_start_of_frame */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does the actual copy of data (including the ethernet
- * header structure) from the WaveLAN card to an sk_buff chain that
- * will be passed up to the network interface layer. NOTE: We
- * currently don't handle trailer protocols (neither does the rest of
- * the network interface), so if that is needed, it will (at least in
- * part) be added here. The contents of the receive ring buffer are
- * copied to a message chain that is then passed to the kernel.
- *
- * Note: if any errors occur, the packet is "dropped on the floor"
- * (called by wv_packet_rcv())
- */
-static inline void
-wv_packet_read(device * dev,
- int fd_p,
- int sksize)
-{
- net_local * lp = (net_local *) dev->priv;
- struct sk_buff * skb;
-
-#ifdef DEBUG_RX_TRACE
- printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n",
- dev->name, fd_p, sksize);
-#endif
-
- /* Allocate some buffer for the new packet */
- if((skb = dev_alloc_skb(sksize+2)) == (struct sk_buff *) NULL)
- {
-#ifdef DEBUG_RX_ERROR
- printk(KERN_INFO "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC)\n",
- dev->name, sksize);
-#endif
- lp->stats.rx_dropped++;
- /*
- * Not only do we want to return here, but we also need to drop the
- * packet on the floor to clear the interrupt.
- */
- return;
- }
-
- skb->dev = dev;
-
- skb_reserve(skb, 2);
- fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize);
- skb->protocol = eth_type_trans(skb, dev);
-
-#ifdef DEBUG_RX_INFO
- wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
-#endif /* DEBUG_RX_INFO */
-
- /* Statistics gathering & stuff associated.
- * It seem a bit messy with all the define, but it's really simple... */
- if(
-#ifdef WIRELESS_SPY
- (lp->spy_number > 0) ||
-#endif /* WIRELESS_SPY */
-#ifdef HISTOGRAM
- (lp->his_number > 0) ||
-#endif /* HISTOGRAM */
-#ifdef WAVELAN_ROAMING
- (do_roaming) ||
-#endif /* WAVELAN_ROAMING */
- 0)
- {
- u_char stats[3]; /* Signal level, Noise level, Signal quality */
-
- /* read signal level, silence level and signal quality bytes */
- fd_p = read_ringbuf(dev, (fd_p + 4) % RX_SIZE + RX_BASE,
- stats, 3);
-#ifdef DEBUG_RX_INFO
- printk(KERN_DEBUG "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n",
- dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F);
-#endif
-
-#ifdef WAVELAN_ROAMING
- if(do_roaming)
- if(WAVELAN_BEACON(skb->data))
- wl_roam_gather(dev, skb->data, stats);
-#endif /* WAVELAN_ROAMING */
-
-#ifdef WIRELESS_SPY
- wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats);
-#endif /* WIRELESS_SPY */
-#ifdef HISTOGRAM
- wl_his_gather(dev, stats);
-#endif /* HISTOGRAM */
- }
-
- /*
- * Hand the packet to the Network Module
- */
- netif_rx(skb);
-
- /* Keep stats up to date */
- dev->last_rx = jiffies;
- lp->stats.rx_packets++;
- lp->stats.rx_bytes += sksize;
-
-#ifdef DEBUG_RX_TRACE
- printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
-#endif
- return;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine is called by the interrupt handler to initiate a
- * packet transfer from the card to the network interface layer above
- * this driver. This routine checks if a buffer has been successfully
- * received by the WaveLAN card. If so, the routine wv_packet_read is
- * called to do the actual transfer of the card's data including the
- * ethernet header into a packet consisting of an sk_buff chain.
- * (called by wavelan_interrupt())
- * Note : the spinlock is already grabbed for us and irq are disabled.
- */
-static inline void
-wv_packet_rcv(device * dev)
-{
- ioaddr_t base = dev->base_addr;
- net_local * lp = (net_local *) dev->priv;
- int newrfp;
- int rp;
- int len;
- int f_start;
- int status;
- int i593_rfp;
- int stat_ptr;
- u_char c[4];
-
-#ifdef DEBUG_RX_TRACE
- printk(KERN_DEBUG "%s: ->wv_packet_rcv()\n", dev->name);
-#endif
-
- /* Get the new receive frame pointer from the i82593 chip */
- outb(CR0_STATUS_2 | OP0_NOP, LCCR(base));
- i593_rfp = inb(LCSR(base));
- i593_rfp |= inb(LCSR(base)) << 8;
- i593_rfp %= RX_SIZE;
-
- /* Get the new receive frame pointer from the WaveLAN card.
- * It is 3 bytes more than the increment of the i82593 receive
- * frame pointer, for each packet. This is because it includes the
- * 3 roaming bytes added by the mmc.
- */
- newrfp = inb(RPLL(base));
- newrfp |= inb(RPLH(base)) << 8;
- newrfp %= RX_SIZE;
-
-#ifdef DEBUG_RX_INFO
- printk(KERN_DEBUG "%s: wv_packet_rcv(): i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
- dev->name, i593_rfp, lp->stop, newrfp, lp->rfp);
-#endif
-
-#ifdef DEBUG_RX_ERROR
- /* If no new frame pointer... */
- if(lp->overrunning || newrfp == lp->rfp)
- printk(KERN_INFO "%s: wv_packet_rcv(): no new frame: i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
- dev->name, i593_rfp, lp->stop, newrfp, lp->rfp);
-#endif
-
- /* Read all frames (packets) received */
- while(newrfp != lp->rfp)
- {
- /* A frame is composed of the packet, followed by a status word,
- * the length of the frame (word) and the mmc info (SNR & qual).
- * It's because the length is at the end that we can only scan
- * frames backward. */
-
- /* Find the first frame by skipping backwards over the frames */
- rp = newrfp; /* End of last frame */
- while(((f_start = wv_start_of_frame(dev, rp, newrfp)) != lp->rfp) &&
- (f_start != -1))
- rp = f_start;
-
- /* If we had a problem */
- if(f_start == -1)
- {
-#ifdef DEBUG_RX_ERROR
- printk(KERN_INFO "wavelan_cs: cannot find start of frame ");
- printk(" i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
- i593_rfp, lp->stop, newrfp, lp->rfp);
-#endif
- lp->rfp = rp; /* Get to the last usable frame */
- continue;
- }
-
- /* f_start point to the beggining of the first frame received
- * and rp to the beggining of the next one */
-
- /* Read status & length of the frame */
- stat_ptr = (rp - 7 + RX_SIZE) % RX_SIZE;
- stat_ptr = read_ringbuf(dev, stat_ptr, c, 4);
- status = c[0] | (c[1] << 8);
- len = c[2] | (c[3] << 8);
-
- /* Check status */
- if((status & RX_RCV_OK) != RX_RCV_OK)
- {
- lp->stats.rx_errors++;
- if(status & RX_NO_SFD)
- lp->stats.rx_frame_errors++;
- if(status & RX_CRC_ERR)
- lp->stats.rx_crc_errors++;
- if(status & RX_OVRRUN)
- lp->stats.rx_over_errors++;
-
-#ifdef DEBUG_RX_FAIL
- printk(KERN_DEBUG "%s: wv_packet_rcv(): packet not received ok, status = 0x%x\n",
- dev->name, status);
-#endif
- }
- else
- /* Read the packet and transmit to Linux */
- wv_packet_read(dev, f_start, len - 2);
-
- /* One frame has been processed, skip it */
- lp->rfp = rp;
- }
-
- /*
- * Update the frame stop register, but set it to less than
- * the full 8K to allow space for 3 bytes of signal strength
- * per packet.
- */
- lp->stop = (i593_rfp + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
- outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base));
- outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base));
- outb(OP1_SWIT_TO_PORT_0, LCCR(base));
-
-#ifdef DEBUG_RX_TRACE
- printk(KERN_DEBUG "%s: <-wv_packet_rcv()\n", dev->name);
-#endif
-}
-
-/*********************** PACKET TRANSMISSION ***********************/
-/*
- * This part deal with sending packet through the wavelan
- * We copy the packet to the send buffer and then issue the send
- * command to the i82593. The result of this operation will be
- * checked in wavelan_interrupt()
- */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine fills in the appropriate registers and memory
- * locations on the WaveLAN card and starts the card off on
- * the transmit.
- * (called in wavelan_packet_xmit())
- */
-static inline void
-wv_packet_write(device * dev,
- void * buf,
- short length)
-{
- net_local * lp = (net_local *) dev->priv;
- ioaddr_t base = dev->base_addr;
- unsigned long flags;
- int clen = length;
- register u_short xmtdata_base = TX_BASE;
-
-#ifdef DEBUG_TX_TRACE
- printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length);
-#endif
-
- wv_splhi(lp, &flags);
-
- /* Check if we need some padding */
- if(clen < ETH_ZLEN)
- clen = ETH_ZLEN;
-
- /* Write the length of data buffer followed by the buffer */
- outb(xmtdata_base & 0xff, PIORL(base));
- outb(((xmtdata_base >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
- outb(clen & 0xff, PIOP(base)); /* lsb */
- outb(clen >> 8, PIOP(base)); /* msb */
-
- /* Send the data */
- outsb(PIOP(base), buf, clen);
-
- /* Indicate end of transmit chain */
- outb(OP0_NOP, PIOP(base));
- /* josullvn@cs.cmu.edu: need to send a second NOP for alignment... */
- outb(OP0_NOP, PIOP(base));
-
- /* Reset the transmit DMA pointer */
- hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
- hacr_write(base, HACR_DEFAULT);
- /* Send the transmit command */
- wv_82593_cmd(dev, "wv_packet_write(): transmit",
- OP0_TRANSMIT, SR0_NO_RESULT);
-
- /* Keep stats up to date */
- lp->stats.tx_bytes += length;
-
- wv_splx(lp, &flags);
-
-#ifdef DEBUG_TX_INFO
- wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write");
-#endif /* DEBUG_TX_INFO */
-
-#ifdef DEBUG_TX_TRACE
- printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine is called when we want to send a packet (NET3 callback)
- * In this routine, we check if the harware is ready to accept
- * the packet. We also prevent reentrance. Then, we call the function
- * to send the packet...
- */
-static int
-wavelan_packet_xmit(struct sk_buff * skb,
- device * dev)
-{
- net_local * lp = (net_local *)dev->priv;
- unsigned long flags;
-
-#ifdef DEBUG_TX_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
- (unsigned) skb);
-#endif
-
- /*
- * Block a timer-based transmit from overlapping a previous transmit.
- * In other words, prevent reentering this routine.
- */
- netif_stop_queue(dev);
-
- /* If somebody has asked to reconfigure the controller,
- * we can do it now */
- if(lp->reconfig_82593)
- {
- wv_splhi(lp, &flags); /* Disable interrupts */
- wv_82593_config(dev);
- wv_splx(lp, &flags); /* Re-enable interrupts */
- /* Note : the configure procedure was totally synchronous,
- * so the Tx buffer is now free */
- }
-
-#ifdef DEBUG_TX_ERROR
- if (skb->next)
- printk(KERN_INFO "skb has next\n");
-#endif
-
- wv_packet_write(dev, skb->data, skb->len);
-
- dev_kfree_skb(skb);
-
-#ifdef DEBUG_TX_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
-#endif
- return(0);
-}
-
-/********************** HARDWARE CONFIGURATION **********************/
-/*
- * This part do the real job of starting and configuring the hardware.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to initialize the Modem Management Controller.
- * (called by wv_hw_config())
- */
-static inline int
-wv_mmc_init(device * dev)
-{
- ioaddr_t base = dev->base_addr;
- psa_t psa;
- mmw_t m;
- int configured;
- int i; /* Loop counter */
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name);
-#endif
-
- /* Read the parameter storage area */
- psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
-
- /*
- * Check the first three octets of the MAC addr for the manufacturer's code.
- * Note: If you get the error message below, you've got a
- * non-NCR/AT&T/Lucent PCMCIA cards, see wavelan_cs.h for detail on
- * how to configure your card...
- */
- for(i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
- if((psa.psa_univ_mac_addr[0] == MAC_ADDRESSES[i][0]) &&
- (psa.psa_univ_mac_addr[1] == MAC_ADDRESSES[i][1]) &&
- (psa.psa_univ_mac_addr[2] == MAC_ADDRESSES[i][2]))
- break;
-
- /* If we have not found it... */
- if(i == (sizeof(MAC_ADDRESSES) / sizeof(char) / 3))
- {
-#ifdef DEBUG_CONFIG_ERRORS
- printk(KERN_WARNING "%s: wv_mmc_init(): Invalid MAC address: %02X:%02X:%02X:...\n",
- dev->name, psa.psa_univ_mac_addr[0],
- psa.psa_univ_mac_addr[1], psa.psa_univ_mac_addr[2]);
-#endif
- return FALSE;
- }
-
- /* Get the MAC address */
- memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE);
-
-#ifdef USE_PSA_CONFIG
- configured = psa.psa_conf_status & 1;
-#else
- configured = 0;
-#endif
-
- /* Is the PSA is not configured */
- if(!configured)
- {
- /* User will be able to configure NWID after (with iwconfig) */
- psa.psa_nwid[0] = 0;
- psa.psa_nwid[1] = 0;
-
- /* As NWID is not set : no NWID checking */
- psa.psa_nwid_select = 0;
-
- /* Disable encryption */
- psa.psa_encryption_select = 0;
-
- /* Set to standard values
- * 0x04 for AT,
- * 0x01 for MCA,
- * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document)
- */
- if (psa.psa_comp_number & 1)
- psa.psa_thr_pre_set = 0x01;
- else
- psa.psa_thr_pre_set = 0x04;
- psa.psa_quality_thr = 0x03;
-
- /* It is configured */
- psa.psa_conf_status |= 1;
-
-#ifdef USE_PSA_CONFIG
- /* Write the psa */
- psa_write(dev, (char *)psa.psa_nwid - (char *)&psa,
- (unsigned char *)psa.psa_nwid, 4);
- psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
- (unsigned char *)&psa.psa_thr_pre_set, 1);
- psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
- (unsigned char *)&psa.psa_quality_thr, 1);
- psa_write(dev, (char *)&psa.psa_conf_status - (char *)&psa,
- (unsigned char *)&psa.psa_conf_status, 1);
- /* update the Wavelan checksum */
- update_psa_checksum(dev);
-#endif /* USE_PSA_CONFIG */
- }
-
- /* Zero the mmc structure */
- memset(&m, 0x00, sizeof(m));
-
- /* Copy PSA info to the mmc */
- m.mmw_netw_id_l = psa.psa_nwid[1];
- m.mmw_netw_id_h = psa.psa_nwid[0];
-
- if(psa.psa_nwid_select & 1)
- m.mmw_loopt_sel = 0x00;
- else
- m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
-
- memcpy(&m.mmw_encr_key, &psa.psa_encryption_key,
- sizeof(m.mmw_encr_key));
-
- if(psa.psa_encryption_select)
- m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE;
- else
- m.mmw_encr_enable = 0;
-
- m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
- m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
-
- /*
- * Set default modem control parameters.
- * See NCR document 407-0024326 Rev. A.
- */
- m.mmw_jabber_enable = 0x01;
- m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
- m.mmw_ifs = 0x20;
- m.mmw_mod_delay = 0x04;
- m.mmw_jam_time = 0x38;
-
- m.mmw_des_io_invert = 0;
- m.mmw_freeze = 0;
- m.mmw_decay_prm = 0;
- m.mmw_decay_updat_prm = 0;
-
- /* Write all info to mmc */
- mmc_write(base, 0, (u_char *)&m, sizeof(m));
-
- /* The following code start the modem of the 2.00 frequency
- * selectable cards at power on. It's not strictly needed for the
- * following boots...
- * The original patch was by Joe Finney for the PCMCIA driver, but
- * I've cleaned it a bit and add documentation.
- * Thanks to Loeke Brederveld from Lucent for the info.
- */
-
- /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
- * (does it work for everybody ??? - especially old cards...) */
- /* Note : WFREQSEL verify that it is able to read from EEprom
- * a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID
- * is 0xA (Xilinx version) or 0xB (Ariadne version).
- * My test is more crude but do work... */
- if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
- (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
- {
- /* We must download the frequency parameters to the
- * synthetisers (from the EEprom - area 1)
- * Note : as the EEprom is auto decremented, we set the end
- * if the area... */
- m.mmw_fee_addr = 0x0F;
- m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
- mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m,
- (unsigned char *)&m.mmw_fee_ctrl, 2);
-
- /* Wait until the download is finished */
- fee_wait(base, 100, 100);
-
-#ifdef DEBUG_CONFIG_INFO
- /* The frequency was in the last word downloaded... */
- mmc_read(base, (char *)&m.mmw_fee_data_l - (char *)&m,
- (unsigned char *)&m.mmw_fee_data_l, 2);
-
- /* Print some info for the user */
- printk(KERN_DEBUG "%s: Wavelan 2.00 recognised (frequency select) : Current frequency = %ld\n",
- dev->name,
- ((m.mmw_fee_data_h << 4) |
- (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L);
-#endif
-
- /* We must now download the power adjust value (gain) to
- * the synthetisers (from the EEprom - area 7 - DAC) */
- m.mmw_fee_addr = 0x61;
- m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
- mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m,
- (unsigned char *)&m.mmw_fee_ctrl, 2);
-
- /* Wait until the download is finished */
- } /* if 2.00 card */
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name);
-#endif
- return TRUE;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to gracefully turn off reception, and wait for any commands
- * to complete.
- * (called in wv_ru_start() and wavelan_close() and wavelan_event())
- */
-static int
-wv_ru_stop(device * dev)
-{
- ioaddr_t base = dev->base_addr;
- net_local * lp = (net_local *) dev->priv;
- unsigned long flags;
- int status;
- int spin;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name);
-#endif
-
- wv_splhi(lp, &flags);
-
- /* First, send the LAN controller a stop receive command */
- wv_82593_cmd(dev, "wv_graceful_shutdown(): stop-rcv",
- OP0_STOP_RCV, SR0_NO_RESULT);
-
- /* Then, spin until the receive unit goes idle */
- spin = 300;
- do
- {
- udelay(10);
- outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
- status = inb(LCSR(base));
- }
- while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin-- > 0));
-
- /* Now, spin until the chip finishes executing its current command */
- do
- {
- udelay(10);
- outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
- status = inb(LCSR(base));
- }
- while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0));
-
- wv_splx(lp, &flags);
-
- /* If there was a problem */
- if(spin <= 0)
- {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_INFO "%s: wv_ru_stop(): The chip doesn't want to stop...\n",
- dev->name);
-#endif
- return FALSE;
- }
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_ru_stop()\n", dev->name);
-#endif
- return TRUE;
-} /* wv_ru_stop */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine starts the receive unit running. First, it checks if
- * the card is actually ready. Then the card is instructed to receive
- * packets again.
- * (called in wv_hw_reset() & wavelan_open())
- */
-static int
-wv_ru_start(device * dev)
-{
- ioaddr_t base = dev->base_addr;
- net_local * lp = (net_local *) dev->priv;
- unsigned long flags;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
-#endif
-
- /*
- * We need to start from a quiescent state. To do so, we could check
- * if the card is already running, but instead we just try to shut
- * it down. First, we disable reception (in case it was already enabled).
- */
- if(!wv_ru_stop(dev))
- return FALSE;
-
- wv_splhi(lp, &flags);
-
- /* Now we know that no command is being executed. */
-
- /* Set the receive frame pointer and stop pointer */
- lp->rfp = 0;
- outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base));
-
- /* Reset ring management. This sets the receive frame pointer to 1 */
- outb(OP1_RESET_RING_MNGMT, LCCR(base));
-
-#if 0
- /* XXX the i82593 manual page 6-4 seems to indicate that the stop register
- should be set as below */
- /* outb(CR1_STOP_REG_UPDATE|((RX_SIZE - 0x40)>> RX_SIZE_SHIFT),LCCR(base));*/
-#elif 0
- /* but I set it 0 instead */
- lp->stop = 0;
-#else
- /* but I set it to 3 bytes per packet less than 8K */
- lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
-#endif
- outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base));
- outb(OP1_INT_ENABLE, LCCR(base));
- outb(OP1_SWIT_TO_PORT_0, LCCR(base));
-
- /* Reset receive DMA pointer */
- hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
- hacr_write_slow(base, HACR_DEFAULT);
-
- /* Receive DMA on channel 1 */
- wv_82593_cmd(dev, "wv_ru_start(): rcv-enable",
- CR0_CHNL | OP0_RCV_ENABLE, SR0_NO_RESULT);
-
-#ifdef DEBUG_I82593_SHOW
- {
- int status;
- int opri;
- int spin = 10000;
-
- /* spin until the chip starts receiving */
- do
- {
- outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
- status = inb(LCSR(base));
- if(spin-- <= 0)
- break;
- }
- while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_ACTIVE) &&
- ((status & SR3_RCV_STATE_MASK) != SR3_RCV_READY));
- printk(KERN_DEBUG "rcv status is 0x%x [i:%d]\n",
- (status & SR3_RCV_STATE_MASK), i);
- }
-#endif
-
- wv_splx(lp, &flags);
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
-#endif
- return TRUE;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does a standard config of the WaveLAN controller (i82593).
- * In the ISA driver, this is integrated in wavelan_hardware_reset()
- * (called by wv_hw_config(), wv_82593_reconfig() & wavelan_packet_xmit())
- */
-static int
-wv_82593_config(device * dev)
-{
- ioaddr_t base = dev->base_addr;
- net_local * lp = (net_local *) dev->priv;
- struct i82593_conf_block cfblk;
- int ret = TRUE;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_82593_config()\n", dev->name);
-#endif
-
- /* Create & fill i82593 config block
- *
- * Now conform to Wavelan document WCIN085B
- */
- memset(&cfblk, 0x00, sizeof(struct i82593_conf_block));
- cfblk.d6mod = FALSE; /* Run in i82593 advanced mode */
- cfblk.fifo_limit = 5; /* = 56 B rx and 40 B tx fifo thresholds */
- cfblk.forgnesi = FALSE; /* 0=82C501, 1=AMD7992B compatibility */
- cfblk.fifo_32 = 1;
- cfblk.throttle_enb = FALSE;
- cfblk.contin = TRUE; /* enable continuous mode */
- cfblk.cntrxint = FALSE; /* enable continuous mode receive interrupts */
- cfblk.addr_len = WAVELAN_ADDR_SIZE;
- cfblk.acloc = TRUE; /* Disable source addr insertion by i82593 */
- cfblk.preamb_len = 0; /* 2 bytes preamble (SFD) */
- cfblk.loopback = FALSE;
- cfblk.lin_prio = 0; /* conform to 802.3 backoff algoritm */
- cfblk.exp_prio = 5; /* conform to 802.3 backoff algoritm */
- cfblk.bof_met = 1; /* conform to 802.3 backoff algoritm */
- cfblk.ifrm_spc = 0x20; /* 32 bit times interframe spacing */
- cfblk.slottim_low = 0x20; /* 32 bit times slot time */
- cfblk.slottim_hi = 0x0;
- cfblk.max_retr = 15;
- cfblk.prmisc = ((lp->promiscuous) ? TRUE: FALSE); /* Promiscuous mode */
- cfblk.bc_dis = FALSE; /* Enable broadcast reception */
- cfblk.crs_1 = TRUE; /* Transmit without carrier sense */
- cfblk.nocrc_ins = FALSE; /* i82593 generates CRC */
- cfblk.crc_1632 = FALSE; /* 32-bit Autodin-II CRC */
- cfblk.crs_cdt = FALSE; /* CD not to be interpreted as CS */
- cfblk.cs_filter = 0; /* CS is recognized immediately */
- cfblk.crs_src = FALSE; /* External carrier sense */
- cfblk.cd_filter = 0; /* CD is recognized immediately */
- cfblk.min_fr_len = ETH_ZLEN >> 2; /* Minimum frame length 64 bytes */
- cfblk.lng_typ = FALSE; /* Length field > 1500 = type field */
- cfblk.lng_fld = TRUE; /* Disable 802.3 length field check */
- cfblk.rxcrc_xf = TRUE; /* Don't transfer CRC to memory */
- cfblk.artx = TRUE; /* Disable automatic retransmission */
- cfblk.sarec = TRUE; /* Disable source addr trig of CD */
- cfblk.tx_jabber = TRUE; /* Disable jabber jam sequence */
- cfblk.hash_1 = FALSE; /* Use bits 0-5 in mc address hash */
- cfblk.lbpkpol = TRUE; /* Loopback pin active high */
- cfblk.fdx = FALSE; /* Disable full duplex operation */
- cfblk.dummy_6 = 0x3f; /* all ones */
- cfblk.mult_ia = FALSE; /* No multiple individual addresses */
- cfblk.dis_bof = FALSE; /* Disable the backoff algorithm ?! */
- cfblk.dummy_1 = TRUE; /* set to 1 */
- cfblk.tx_ifs_retrig = 3; /* Hmm... Disabled */
-#ifdef MULTICAST_ALL
- cfblk.mc_all = (lp->allmulticast ? TRUE: FALSE); /* Allow all multicasts */
-#else
- cfblk.mc_all = FALSE; /* No multicast all mode */
-#endif
- cfblk.rcv_mon = 0; /* Monitor mode disabled */
- cfblk.frag_acpt = TRUE; /* Do not accept fragments */
- cfblk.tstrttrs = FALSE; /* No start transmission threshold */
- cfblk.fretx = TRUE; /* FIFO automatic retransmission */
- cfblk.syncrqs = FALSE; /* Synchronous DRQ deassertion... */
- cfblk.sttlen = TRUE; /* 6 byte status registers */
- cfblk.rx_eop = TRUE; /* Signal EOP on packet reception */
- cfblk.tx_eop = TRUE; /* Signal EOP on packet transmission */
- cfblk.rbuf_size = RX_SIZE>>11; /* Set receive buffer size */
- cfblk.rcvstop = TRUE; /* Enable Receive Stop Register */
-
-#ifdef DEBUG_I82593_SHOW
- {
- u_char *c = (u_char *) &cfblk;
- int i;
- printk(KERN_DEBUG "wavelan_cs: config block:");
- for(i = 0; i < sizeof(struct i82593_conf_block); i++,c++)
- {
- if((i % 16) == 0) printk("\n" KERN_DEBUG);
- printk("%02x ", *c);
- }
- printk("\n");
- }
-#endif
-
- /* Copy the config block to the i82593 */
- outb(TX_BASE & 0xff, PIORL(base));
- outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
- outb(sizeof(struct i82593_conf_block) & 0xff, PIOP(base)); /* lsb */
- outb(sizeof(struct i82593_conf_block) >> 8, PIOP(base)); /* msb */
- outsb(PIOP(base), (char *) &cfblk, sizeof(struct i82593_conf_block));
-
- /* reset transmit DMA pointer */
- hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
- hacr_write(base, HACR_DEFAULT);
- if(!wv_82593_cmd(dev, "wv_82593_config(): configure",
- OP0_CONFIGURE, SR0_CONFIGURE_DONE))
- ret = FALSE;
-
- /* Initialize adapter's ethernet MAC address */
- outb(TX_BASE & 0xff, PIORL(base));
- outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
- outb(WAVELAN_ADDR_SIZE, PIOP(base)); /* byte count lsb */
- outb(0, PIOP(base)); /* byte count msb */
- outsb(PIOP(base), &dev->dev_addr[0], WAVELAN_ADDR_SIZE);
-
- /* reset transmit DMA pointer */
- hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
- hacr_write(base, HACR_DEFAULT);
- if(!wv_82593_cmd(dev, "wv_82593_config(): ia-setup",
- OP0_IA_SETUP, SR0_IA_SETUP_DONE))
- ret = FALSE;
-
-#ifdef WAVELAN_ROAMING
- /* If roaming is enabled, join the "Beacon Request" multicast group... */
- /* But only if it's not in there already! */
- if(do_roaming)
- dev_mc_add(dev,WAVELAN_BEACON_ADDRESS, WAVELAN_ADDR_SIZE, 1);
-#endif /* WAVELAN_ROAMING */
-
- /* If any multicast address to set */
- if(lp->mc_count)
- {
- struct dev_mc_list * dmi;
- int addrs_len = WAVELAN_ADDR_SIZE * lp->mc_count;
-
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG "%s: wv_hw_config(): set %d multicast addresses:\n",
- dev->name, lp->mc_count);
- for(dmi=dev->mc_list; dmi; dmi=dmi->next)
- printk(KERN_DEBUG " %02x:%02x:%02x:%02x:%02x:%02x\n",
- dmi->dmi_addr[0], dmi->dmi_addr[1], dmi->dmi_addr[2],
- dmi->dmi_addr[3], dmi->dmi_addr[4], dmi->dmi_addr[5] );
-#endif
-
- /* Initialize adapter's ethernet multicast addresses */
- outb(TX_BASE & 0xff, PIORL(base));
- outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
- outb(addrs_len & 0xff, PIOP(base)); /* byte count lsb */
- outb((addrs_len >> 8), PIOP(base)); /* byte count msb */
- for(dmi=dev->mc_list; dmi; dmi=dmi->next)
- outsb(PIOP(base), dmi->dmi_addr, dmi->dmi_addrlen);
-
- /* reset transmit DMA pointer */
- hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
- hacr_write(base, HACR_DEFAULT);
- if(!wv_82593_cmd(dev, "wv_82593_config(): mc-setup",
- OP0_MC_SETUP, SR0_MC_SETUP_DONE))
- ret = FALSE;
- lp->mc_count = dev->mc_count; /* remember to avoid repeated reset */
- }
-
- /* Job done, clear the flag */
- lp->reconfig_82593 = FALSE;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name);
-#endif
- return(ret);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Read the Access Configuration Register, perform a software reset,
- * and then re-enable the card's software.
- *
- * If I understand correctly : reset the pcmcia interface of the
- * wavelan.
- * (called by wv_config())
- */
-static inline int
-wv_pcmcia_reset(device * dev)
-{
- int i;
- conf_reg_t reg = { 0, CS_READ, CISREG_COR, 0 };
- dev_link_t * link = ((net_local *) dev->priv)->link;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_pcmcia_reset()\n", dev->name);
-#endif
-
- i = CardServices(AccessConfigurationRegister, link->handle, ®);
- if(i != CS_SUCCESS)
- {
- cs_error(link->handle, AccessConfigurationRegister, i);
- return FALSE;
- }
-
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG "%s: wavelan_pcmcia_reset(): Config reg is 0x%x\n",
- dev->name, (u_int) reg.Value);
-#endif
-
- reg.Action = CS_WRITE;
- reg.Value = reg.Value | COR_SW_RESET;
- i = CardServices(AccessConfigurationRegister, link->handle, ®);
- if(i != CS_SUCCESS)
- {
- cs_error(link->handle, AccessConfigurationRegister, i);
- return FALSE;
- }
-
- reg.Action = CS_WRITE;
- reg.Value = COR_LEVEL_IRQ | COR_CONFIG;
- i = CardServices(AccessConfigurationRegister, link->handle, ®);
- if(i != CS_SUCCESS)
- {
- cs_error(link->handle, AccessConfigurationRegister, i);
- return FALSE;
- }
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_pcmcia_reset()\n", dev->name);
-#endif
- return TRUE;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * wavelan_hw_config() is called after a CARD_INSERTION event is
- * received, to configure the wavelan hardware.
- * Note that the reception will be enabled in wavelan->open(), so the
- * device is configured but idle...
- * Performs the following actions:
- * 1. A pcmcia software reset (using wv_pcmcia_reset())
- * 2. A power reset (reset DMA)
- * 3. Reset the LAN controller
- * 4. Initialize the radio modem (using wv_mmc_init)
- * 5. Configure LAN controller (using wv_82593_config)
- * 6. Perform a diagnostic on the LAN controller
- * (called by wavelan_event() & wv_hw_reset())
- */
-static int
-wv_hw_config(device * dev)
-{
- net_local * lp = (net_local *) dev->priv;
- ioaddr_t base = dev->base_addr;
- unsigned long flags;
- int ret = FALSE;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_hw_config()\n", dev->name);
-#endif
-
-#ifdef STRUCT_CHECK
- if(wv_structuct_check() != (char *) NULL)
- {
- printk(KERN_WARNING "%s: wv_hw_config: structure/compiler botch: \"%s\"\n",
- dev->name, wv_structuct_check());
- return FALSE;
- }
-#endif /* STRUCT_CHECK == 1 */
-
- /* Reset the pcmcia interface */
- if(wv_pcmcia_reset(dev) == FALSE)
- return FALSE;
-
- /* Disable interrupts */
- wv_splhi(lp, &flags);
-
- /* Disguised goto ;-) */
- do
- {
- /* Power UP the module + reset the modem + reset host adapter
- * (in fact, reset DMA channels) */
- hacr_write_slow(base, HACR_RESET);
- hacr_write(base, HACR_DEFAULT);
-
- /* Check if the module has been powered up... */
- if(hasr_read(base) & HASR_NO_CLK)
- {
-#ifdef DEBUG_CONFIG_ERRORS
- printk(KERN_WARNING "%s: wv_hw_config(): modem not connected or not a wavelan card\n",
- dev->name);
-#endif
- break;
- }
-
- /* initialize the modem */
- if(wv_mmc_init(dev) == FALSE)
- {
-#ifdef DEBUG_CONFIG_ERRORS
- printk(KERN_WARNING "%s: wv_hw_config(): Can't configure the modem\n",
- dev->name);
-#endif
- break;
- }
-
- /* reset the LAN controller (i82593) */
- outb(OP0_RESET, LCCR(base));
- mdelay(1); /* A bit crude ! */
-
- /* Initialize the LAN controller */
- if(wv_82593_config(dev) == FALSE)
- {
-#ifdef DEBUG_CONFIG_ERRORS
- printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n",
- dev->name);
-#endif
- break;
- }
-
- /* Diagnostic */
- if(wv_diag(dev) == FALSE)
- {
-#ifdef DEBUG_CONFIG_ERRORS
- printk(KERN_INFO "%s: wv_hw_config(): i82593 diagnostic failed\n",
- dev->name);
-#endif
- break;
- }
-
- /*
- * insert code for loopback test here
- */
-
- /* The device is now configured */
- lp->configured = 1;
- ret = TRUE;
- }
- while(0);
-
- /* Re-enable interrupts */
- wv_splx(lp, &flags);
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name);
-#endif
- return(ret);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Totally reset the wavelan and restart it.
- * Performs the following actions:
- * 1. Call wv_hw_config()
- * 2. Start the LAN controller's receive unit
- * (called by wavelan_event(), wavelan_watchdog() and wavelan_open())
- */
-static inline void
-wv_hw_reset(device * dev)
-{
- net_local * lp = (net_local *) dev->priv;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_hw_reset()\n", dev->name);
-#endif
-
- lp->nresets++;
- lp->configured = 0;
-
- /* Call wv_hw_config() for most of the reset & init stuff */
- if(wv_hw_config(dev) == FALSE)
- return;
-
- /* start receive unit */
- wv_ru_start(dev);
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * wv_pcmcia_config() is called after a CARD_INSERTION event is
- * received, to configure the PCMCIA socket, and to make the ethernet
- * device available to the system.
- * (called by wavelan_event())
- */
-static inline int
-wv_pcmcia_config(dev_link_t * link)
-{
- client_handle_t handle;
- tuple_t tuple;
- cisparse_t parse;
- struct net_device * dev;
- int i;
- u_char buf[64];
- win_req_t req;
- memreq_t mem;
-
- handle = link->handle;
- dev = (device *) link->priv;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "->wv_pcmcia_config(0x%p)\n", link);
-#endif
-
- /*
- * This reads the card's CONFIG tuple to find its configuration
- * registers.
- */
- do
- {
- tuple.Attributes = 0;
- tuple.DesiredTuple = CISTPL_CONFIG;
- i = CardServices(GetFirstTuple, handle, &tuple);
- if(i != CS_SUCCESS)
- break;
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleDataMax = 64;
- tuple.TupleOffset = 0;
- i = CardServices(GetTupleData, handle, &tuple);
- if(i != CS_SUCCESS)
- break;
- i = CardServices(ParseTuple, handle, &tuple, &parse);
- if(i != CS_SUCCESS)
- break;
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
- }
- while(0);
- if(i != CS_SUCCESS)
- {
- cs_error(link->handle, ParseTuple, i);
- link->state &= ~DEV_CONFIG_PENDING;
- return FALSE;
- }
-
- /* Configure card */
- link->state |= DEV_CONFIG;
- do
- {
- i = CardServices(RequestIO, link->handle, &link->io);
- if(i != CS_SUCCESS)
- {
- cs_error(link->handle, RequestIO, i);
- break;
- }
-
- /*
- * Now allocate an interrupt line. Note that this does not
- * actually assign a handler to the interrupt.
- */
- i = CardServices(RequestIRQ, link->handle, &link->irq);
- if(i != CS_SUCCESS)
- {
- cs_error(link->handle, RequestIRQ, i);
- break;
- }
-
- /*
- * This actually configures the PCMCIA socket -- setting up
- * the I/O windows and the interrupt mapping.
- */
- link->conf.ConfigIndex = 1;
- i = CardServices(RequestConfiguration, link->handle, &link->conf);
- if(i != CS_SUCCESS)
- {
- cs_error(link->handle, RequestConfiguration, i);
- break;
- }
-
- /*
- * Allocate a small memory window. Note that the dev_link_t
- * structure provides space for one window handle -- if your
- * device needs several windows, you'll need to keep track of
- * the handles in your private data structure, link->priv.
- */
- req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
- req.Base = req.Size = 0;
- req.AccessSpeed = mem_speed;
- link->win = (window_handle_t)link->handle;
- i = CardServices(RequestWindow, &link->win, &req);
- if(i != CS_SUCCESS)
- {
- cs_error(link->handle, RequestWindow, i);
- break;
- }
-
- dev->rmem_start = dev->mem_start =
- (u_long)ioremap(req.Base, req.Size);
- dev->rmem_end = dev->mem_end = dev->mem_start + req.Size;
-
- mem.CardOffset = 0; mem.Page = 0;
- i = CardServices(MapMemPage, link->win, &mem);
- if(i != CS_SUCCESS)
- {
- cs_error(link->handle, MapMemPage, i);
- break;
- }
-
- /* Feed device with this info... */
- dev->irq = link->irq.AssignedIRQ;
- dev->base_addr = link->io.BasePort1;
- netif_start_queue(dev);
-
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG "wv_pcmcia_config: MEMSTART 0x%x IRQ %d IOPORT 0x%x\n",
- (u_int) dev->mem_start, dev->irq, (u_int) dev->base_addr);
-#endif
-
- i = register_netdev(dev);
- if(i != 0)
- {
-#ifdef DEBUG_CONFIG_ERRORS
- printk(KERN_INFO "wv_pcmcia_config(): register_netdev() failed\n");
-#endif
- break;
- }
- }
- while(0); /* Humm... Disguised goto !!! */
-
- link->state &= ~DEV_CONFIG_PENDING;
- /* If any step failed, release any partially configured state */
- if(i != 0)
- {
- wv_pcmcia_release((u_long) link);
- return FALSE;
- }
-
- strcpy(((net_local *) dev->priv)->node.dev_name, dev->name);
- link->dev = &((net_local *) dev->priv)->node;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "<-wv_pcmcia_config()\n");
-#endif
- return TRUE;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * After a card is removed, wv_pcmcia_release() will unregister the net
- * device, and release the PCMCIA configuration. If the device is
- * still open, this will be postponed until it is closed.
- */
-static void
-wv_pcmcia_release(u_long arg) /* Address of the interface struct */
-{
- dev_link_t * link = (dev_link_t *) arg;
- device * dev = (device *) link->priv;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link);
-#endif
-
- /* If the device is currently in use, we won't release until it is
- * actually closed. */
- if(link->open)
- {
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG "%s: wv_pcmcia_release: release postponed, device still open\n",
- dev->name);
-#endif
- link->state |= DEV_STALE_CONFIG;
- return;
- }
-
- /* Don't bother checking to see if these succeed or not */
- iounmap((u_char *)dev->mem_start);
- CardServices(ReleaseWindow, link->win);
- CardServices(ReleaseConfiguration, link->handle);
- CardServices(ReleaseIO, link->handle, &link->io);
- CardServices(ReleaseIRQ, link->handle, &link->irq);
-
- link->state &= ~(DEV_CONFIG | DEV_STALE_CONFIG);
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name);
-#endif
-} /* wv_pcmcia_release */
-
-/*------------------------------------------------------------------*/
-/*
- * Sometimes, wavelan_detach can't be performed following a call from
- * cardmgr (device still open, pcmcia_release not done) and the device
- * is put in a STALE_LINK state and remains in memory.
- *
- * This function run through our current list of device and attempt
- * another time to remove them. We hope that since last time the
- * device has properly been closed.
- *
- * (called by wavelan_attach() & cleanup_module())
- */
-static void
-wv_flush_stale_links(void)
-{
- dev_link_t * link; /* Current node in linked list */
- dev_link_t * next; /* Next node in linked list */
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "-> wv_flush_stale_links(0x%p)\n", dev_list);
-#endif
-
- /* Go through the list */
- for (link = dev_list; link; link = next)
- {
- next = link->next;
-
- /* Check if in need of being removed */
- if((link->state & DEV_STALE_LINK) ||
- (! (link->state & DEV_PRESENT)))
- wavelan_detach(link);
-
- }
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "<- wv_flush_stale_links()\n");
-#endif
-}
-
-/************************ INTERRUPT HANDLING ************************/
-
-/*
- * This function is the interrupt handler for the WaveLAN card. This
- * routine will be called whenever:
- * 1. A packet is received.
- * 2. A packet has successfully been transferred and the unit is
- * ready to transmit another packet.
- * 3. A command has completed execution.
- */
-static void
-wavelan_interrupt(int irq,
- void * dev_id,
- struct pt_regs * regs)
-{
- device * dev;
- net_local * lp;
- ioaddr_t base;
- int status0;
- u_int tx_status;
-
- if((dev = (device *)dev_id) == (device *) NULL)
- {
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_WARNING "wavelan_interrupt(): irq %d for unknown device.\n",
- irq);
-#endif
- return;
- }
-
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
-#endif
-
- lp = (net_local *) dev->priv;
- base = dev->base_addr;
-
-#ifdef DEBUG_INTERRUPT_INFO
- /* Check state of our spinlock (it should be cleared) */
- if(spin_is_locked(&lp->spinlock))
- printk(KERN_DEBUG
- "%s: wavelan_interrupt(): spinlock is already locked !!!\n",
- dev->name);
-#endif
-
- /* Prevent reentrancy. We need to do that because we may have
- * multiple interrupt handler running concurently.
- * It is safe because wv_splhi() disable interrupts before aquiring
- * the spinlock. */
- spin_lock(&lp->spinlock);
-
- /* Treat all pending interrupts */
- while(1)
- {
- /* ---------------- INTERRUPT CHECKING ---------------- */
- /*
- * Look for the interrupt and verify the validity
- */
- outb(CR0_STATUS_0 | OP0_NOP, LCCR(base));
- status0 = inb(LCSR(base));
-
-#ifdef DEBUG_INTERRUPT_INFO
- printk(KERN_DEBUG "status0 0x%x [%s => 0x%x]", status0,
- (status0&SR0_INTERRUPT)?"int":"no int",status0&~SR0_INTERRUPT);
- if(status0&SR0_INTERRUPT)
- {
- printk(" [%s => %d]\n", (status0 & SR0_CHNL) ? "chnl" :
- ((status0 & SR0_EXECUTION) ? "cmd" :
- ((status0 & SR0_RECEPTION) ? "recv" : "unknown")),
- (status0 & SR0_EVENT_MASK));
- }
- else
- printk("\n");
-#endif
-
- /* Return if no actual interrupt from i82593 (normal exit) */
- if(!(status0 & SR0_INTERRUPT))
- break;
-
- /* If interrupt is both Rx and Tx or none...
- * This code in fact is there to catch the spurious interrupt
- * when you remove the wavelan pcmcia card from the socket */
- if(((status0 & SR0_BOTH_RX_TX) == SR0_BOTH_RX_TX) ||
- ((status0 & SR0_BOTH_RX_TX) == 0x0))
- {
-#ifdef DEBUG_INTERRUPT_INFO
- printk(KERN_INFO "%s: wv_interrupt(): bogus interrupt (or from dead card) : %X\n",
- dev->name, status0);
-#endif
- /* Acknowledge the interrupt */
- outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
- break;
- }
-
- /* ----------------- RECEIVING PACKET ----------------- */
- /*
- * When the wavelan signal the reception of a new packet,
- * we call wv_packet_rcv() to copy if from the buffer and
- * send it to NET3
- */
- if(status0 & SR0_RECEPTION)
- {
-#ifdef DEBUG_INTERRUPT_INFO
- printk(KERN_DEBUG "%s: wv_interrupt(): receive\n", dev->name);
-#endif
-
- if((status0 & SR0_EVENT_MASK) == SR0_STOP_REG_HIT)
- {
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO "%s: wv_interrupt(): receive buffer overflow\n",
- dev->name);
-#endif
- lp->stats.rx_over_errors++;
- lp->overrunning = 1;
- }
-
- /* Get the packet */
- wv_packet_rcv(dev);
- lp->overrunning = 0;
-
- /* Acknowledge the interrupt */
- outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
- continue;
- }
-
- /* ---------------- COMMAND COMPLETION ---------------- */
- /*
- * Interrupts issued when the i82593 has completed a command.
- * Most likely : transmission done
- */
-
- /* If a transmission has been done */
- if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE ||
- (status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE ||
- (status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE)
- {
-#ifdef DEBUG_TX_ERROR
- if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE)
- printk(KERN_INFO "%s: wv_interrupt(): packet transmitted without CRC.\n",
- dev->name);
-#endif
-
- /* Get transmission status */
- tx_status = inb(LCSR(base));
- tx_status |= (inb(LCSR(base)) << 8);
-#ifdef DEBUG_INTERRUPT_INFO
- printk(KERN_DEBUG "%s: wv_interrupt(): transmission done\n",
- dev->name);
- {
- u_int rcv_bytes;
- u_char status3;
- rcv_bytes = inb(LCSR(base));
- rcv_bytes |= (inb(LCSR(base)) << 8);
- status3 = inb(LCSR(base));
- printk(KERN_DEBUG "tx_status 0x%02x rcv_bytes 0x%02x status3 0x%x\n",
- tx_status, rcv_bytes, (u_int) status3);
- }
-#endif
- /* Check for possible errors */
- if((tx_status & TX_OK) != TX_OK)
- {
- lp->stats.tx_errors++;
-
- if(tx_status & TX_FRTL)
- {
-#ifdef DEBUG_TX_ERROR
- printk(KERN_INFO "%s: wv_interrupt(): frame too long\n",
- dev->name);
-#endif
- }
- if(tx_status & TX_UND_RUN)
- {
-#ifdef DEBUG_TX_FAIL
- printk(KERN_DEBUG "%s: wv_interrupt(): DMA underrun\n",
- dev->name);
-#endif
- lp->stats.tx_aborted_errors++;
- }
- if(tx_status & TX_LOST_CTS)
- {
-#ifdef DEBUG_TX_FAIL
- printk(KERN_DEBUG "%s: wv_interrupt(): no CTS\n", dev->name);
-#endif
- lp->stats.tx_carrier_errors++;
- }
- if(tx_status & TX_LOST_CRS)
- {
-#ifdef DEBUG_TX_FAIL
- printk(KERN_DEBUG "%s: wv_interrupt(): no carrier\n",
- dev->name);
-#endif
- lp->stats.tx_carrier_errors++;
- }
- if(tx_status & TX_HRT_BEAT)
- {
-#ifdef DEBUG_TX_FAIL
- printk(KERN_DEBUG "%s: wv_interrupt(): heart beat\n", dev->name);
-#endif
- lp->stats.tx_heartbeat_errors++;
- }
- if(tx_status & TX_DEFER)
- {
-#ifdef DEBUG_TX_FAIL
- printk(KERN_DEBUG "%s: wv_interrupt(): channel jammed\n",
- dev->name);
-#endif
- }
- /* Ignore late collisions since they're more likely to happen
- * here (the WaveLAN design prevents the LAN controller from
- * receiving while it is transmitting). We take action only when
- * the maximum retransmit attempts is exceeded.
- */
- if(tx_status & TX_COLL)
- {
- if(tx_status & TX_MAX_COL)
- {
-#ifdef DEBUG_TX_FAIL
- printk(KERN_DEBUG "%s: wv_interrupt(): channel congestion\n",
- dev->name);
-#endif
- if(!(tx_status & TX_NCOL_MASK))
- {
- lp->stats.collisions += 0x10;
- }
- }
- }
- } /* if(!(tx_status & TX_OK)) */
-
- lp->stats.collisions += (tx_status & TX_NCOL_MASK);
- lp->stats.tx_packets++;
-
- netif_wake_queue(dev);
- outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */
- }
- else /* if interrupt = transmit done or retransmit done */
- {
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO "wavelan_cs: unknown interrupt, status0 = %02x\n",
- status0);
-#endif
- outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */
- }
- } /* while(1) */
-
- spin_unlock(&lp->spinlock);
-
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
-#endif
-} /* wv_interrupt */
-
-/*------------------------------------------------------------------*/
-/*
- * Watchdog: when we start a transmission, a timer is set for us in the
- * kernel. If the transmission completes, this timer is disabled. If
- * the timer expires, we are called and we try to unlock the hardware.
- *
- * Note : This watchdog is move clever than the one in the ISA driver,
- * because it try to abort the current command before reseting
- * everything...
- * On the other hand, it's a bit simpler, because we don't have to
- * deal with the multiple Tx buffers...
- */
-static void
-wavelan_watchdog(device * dev)
-{
- net_local * lp = (net_local *) dev->priv;
- ioaddr_t base = dev->base_addr;
- unsigned long flags;
- int aborted = FALSE;
-
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
-#endif
-
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n",
- dev->name);
-#endif
-
- wv_splhi(lp, &flags);
-
- /* Ask to abort the current command */
- outb(OP0_ABORT, LCCR(base));
-
- /* Wait for the end of the command (a bit hackish) */
- if(wv_82593_cmd(dev, "wavelan_watchdog(): abort",
- OP0_NOP | CR0_STATUS_3, SR0_EXECUTION_ABORTED))
- aborted = TRUE;
-
- /* Release spinlock here so that wv_hw_reset() can grab it */
- wv_splx(lp, &flags);
-
- /* Check if we were successful in aborting it */
- if(!aborted)
- {
- /* It seem that it wasn't enough */
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO "%s: wavelan_watchdog: abort failed, trying reset\n",
- dev->name);
-#endif
- wv_hw_reset(dev);
- }
-
-#ifdef DEBUG_PSA_SHOW
- {
- psa_t psa;
- psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
- wv_psa_show(&psa);
- }
-#endif
-#ifdef DEBUG_MMC_SHOW
- wv_mmc_show(dev);
-#endif
-#ifdef DEBUG_I82593_SHOW
- wv_ru_show(dev);
-#endif
-
- /* We are no more waiting for something... */
- netif_wake_queue(dev);
-
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
-#endif
-}
-
-/********************* CONFIGURATION CALLBACKS *********************/
-/*
- * Here are the functions called by the pcmcia package (cardmgr) and
- * linux networking (NET3) for initialization, configuration and
- * deinstallations of the Wavelan Pcmcia Hardware.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Configure and start up the WaveLAN PCMCIA adaptor.
- * Called by NET3 when it "open" the device.
- */
-static int
-wavelan_open(device * dev)
-{
- dev_link_t * link = ((net_local *) dev->priv)->link;
- net_local * lp = (net_local *)dev->priv;
- ioaddr_t base = dev->base_addr;
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name,
- (unsigned int) dev);
-#endif
-
- /* Check if the modem is powered up (wavelan_close() power it down */
- if(hasr_read(base) & HASR_NO_CLK)
- {
- /* Power up (power up time is 250us) */
- hacr_write(base, HACR_DEFAULT);
-
- /* Check if the module has been powered up... */
- if(hasr_read(base) & HASR_NO_CLK)
- {
-#ifdef DEBUG_CONFIG_ERRORS
- printk(KERN_WARNING "%s: wavelan_open(): modem not connected\n",
- dev->name);
-#endif
- return FALSE;
- }
- }
-
- /* Start reception and declare the driver ready */
- if(!lp->configured)
- return FALSE;
- if(!wv_ru_start(dev))
- wv_hw_reset(dev); /* If problem : reset */
- netif_start_queue(dev);
-
- /* Mark the device as used */
- link->open++;
- MOD_INC_USE_COUNT;
-
-#ifdef WAVELAN_ROAMING
- if(do_roaming)
- wv_roam_init(dev);
-#endif /* WAVELAN_ROAMING */
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
-#endif
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Shutdown the WaveLAN PCMCIA adaptor.
- * Called by NET3 when it "close" the device.
- */
-static int
-wavelan_close(device * dev)
-{
- dev_link_t * link = ((net_local *) dev->priv)->link;
- ioaddr_t base = dev->base_addr;
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name,
- (unsigned int) dev);
-#endif
-
- /* If the device isn't open, then nothing to do */
- if(!link->open)
- {
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG "%s: wavelan_close(): device not open\n", dev->name);
-#endif
- return 0;
- }
-
-#ifdef WAVELAN_ROAMING
- /* Cleanup of roaming stuff... */
- if(do_roaming)
- wv_roam_cleanup(dev);
-#endif /* WAVELAN_ROAMING */
-
- link->open--;
- MOD_DEC_USE_COUNT;
-
- /* If the card is still present */
- if(netif_running(dev))
- {
- netif_stop_queue(dev);
-
- /* Stop receiving new messages and wait end of transmission */
- wv_ru_stop(dev);
-
- /* Power down the module */
- hacr_write(base, HACR_DEFAULT & (~HACR_PWR_STAT));
- }
- else
- /* The card is no more there (flag is activated in wv_pcmcia_release) */
- if(link->state & DEV_STALE_CONFIG)
- wv_pcmcia_release((u_long)link);
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
-#endif
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * wavelan_attach() creates an "instance" of the driver, allocating
- * local data structures for one device (one interface). The device
- * is registered with Card Services.
- *
- * The dev_link structure is initialized, but we don't actually
- * configure the card at this point -- we wait until we receive a
- * card insertion event.
- */
-static dev_link_t *
-wavelan_attach(void)
-{
- client_reg_t client_reg; /* Register with cardmgr */
- dev_link_t * link; /* Info for cardmgr */
- device * dev; /* Interface generic data */
- net_local * lp; /* Interface specific data */
- int i, ret;
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "-> wavelan_attach()\n");
-#endif
-
- /* Perform some cleanup */
- wv_flush_stale_links();
-
- /* Initialize the dev_link_t structure */
- link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
- if (!link) return NULL;
- memset(link, 0, sizeof(struct dev_link_t));
-
- /* Unused for the Wavelan */
- link->release.function = &wv_pcmcia_release;
- link->release.data = (u_long) link;
-
- /* The io structure describes IO port mapping */
- link->io.NumPorts1 = 8;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = 3;
-
- /* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
- if (irq_list[0] == -1)
- link->irq.IRQInfo2 = irq_mask;
- else
- for (i = 0; i < 4; i++)
- link->irq.IRQInfo2 |= 1 << irq_list[i];
- link->irq.Handler = wavelan_interrupt;
-
- /* General socket configuration */
- link->conf.Attributes = CONF_ENABLE_IRQ;
- link->conf.Vcc = 50;
- link->conf.IntType = INT_MEMORY_AND_IO;
-
- /* Chain drivers */
- link->next = dev_list;
- dev_list = link;
-
- /* Allocate the generic data structure */
- dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
- if (!dev) {
- kfree(link);
- return NULL;
- }
- memset(dev, 0x00, sizeof(struct net_device));
- link->priv = link->irq.Instance = dev;
-
- /* Allocate the wavelan-specific data structure. */
- dev->priv = lp = (net_local *) kmalloc(sizeof(net_local), GFP_KERNEL);
- if (!lp) {
- kfree(link);
- kfree(dev);
- return NULL;
- }
- memset(lp, 0x00, sizeof(net_local));
-
- /* Init specific data */
- lp->configured = 0;
- lp->reconfig_82593 = FALSE;
- lp->nresets = 0;
- /* Multicast stuff */
- lp->promiscuous = 0;
- lp->allmulticast = 0;
- lp->mc_count = 0;
-
- /* Init spinlock */
- spin_lock_init(&lp->spinlock);
-
- /* back links */
- lp->link = link;
- lp->dev = dev;
-
- /* Standard setup for generic data */
- ether_setup(dev);
-
- /* wavelan NET3 callbacks */
- dev->open = &wavelan_open;
- dev->stop = &wavelan_close;
- dev->hard_start_xmit = &wavelan_packet_xmit;
- dev->get_stats = &wavelan_get_stats;
- dev->set_multicast_list = &wavelan_set_multicast_list;
-#ifdef SET_MAC_ADDRESS
- dev->set_mac_address = &wavelan_set_mac_address;
-#endif /* SET_MAC_ADDRESS */
-
- /* Set the watchdog timer */
- dev->tx_timeout = &wavelan_watchdog;
- dev->watchdog_timeo = WATCHDOG_JIFFIES;
-
-#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */
- dev->do_ioctl = wavelan_ioctl; /* wireless extensions */
- dev->get_wireless_stats = wavelan_get_wireless_stats;
-#endif
-
- /* Other specific data */
- dev->mtu = WAVELAN_MTU;
-
- /* Register with Card Services */
- client_reg.dev_info = &dev_info;
- client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
- client_reg.EventMask =
- CS_EVENT_REGISTRATION_COMPLETE |
- CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
- CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
- CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
- client_reg.event_handler = &wavelan_event;
- client_reg.Version = 0x0210;
- client_reg.event_callback_args.client_data = link;
-
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG "wavelan_attach(): almost done, calling CardServices\n");
-#endif
-
- ret = CardServices(RegisterClient, &link->handle, &client_reg);
- if(ret != 0)
- {
- cs_error(link->handle, RegisterClient, ret);
- wavelan_detach(link);
- return NULL;
- }
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "<- wavelan_attach()\n");
-#endif
-
- return link;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This deletes a driver "instance". The device is de-registered with
- * Card Services. If it has been released, all local data structures
- * are freed. Otherwise, the structures will be freed when the device
- * is released.
- */
-static void
-wavelan_detach(dev_link_t * link)
-{
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "-> wavelan_detach(0x%p)\n", link);
-#endif
-
- /*
- * If the device is currently configured and active, we won't
- * actually delete it yet. Instead, it is marked so that when the
- * release() function is called, that will trigger a proper
- * detach().
- */
- if(link->state & DEV_CONFIG)
- {
- /* Some others haven't done their job : give them another chance */
- wv_pcmcia_release((u_long) link);
- if(link->state & DEV_STALE_CONFIG)
- {
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG "wavelan_detach: detach postponed,"
- " '%s' still locked\n", link->dev->dev_name);
-#endif
- link->state |= DEV_STALE_LINK;
- return;
- }
- }
-
- /* Break the link with Card Services */
- if(link->handle)
- CardServices(DeregisterClient, link->handle);
-
- /* Remove the interface data from the linked list */
- if(dev_list == link)
- dev_list = link->next;
- else
- {
- dev_link_t * prev = dev_list;
-
- while((prev != (dev_link_t *) NULL) && (prev->next != link))
- prev = prev->next;
-
- if(prev == (dev_link_t *) NULL)
- {
-#ifdef DEBUG_CONFIG_ERRORS
- printk(KERN_WARNING "wavelan_detach : Attempting to remove a nonexistent device.\n");
-#endif
- return;
- }
-
- prev->next = link->next;
- }
-
- /* Free pieces */
- if(link->priv)
- {
- device * dev = (device *) link->priv;
-
- /* Remove ourselves from the kernel list of ethernet devices */
- /* Warning : can't be called from interrupt, timer or wavelan_close() */
- if(link->dev != NULL)
- unregister_netdev(dev);
- link->dev = NULL;
-
- if(dev->priv)
- {
- /* Sound strange, but safe... */
- ((net_local *) dev->priv)->link = (dev_link_t *) NULL;
- ((net_local *) dev->priv)->dev = (device *) NULL;
- kfree(dev->priv);
- }
- kfree(link->priv);
- }
- kfree(link);
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "<- wavelan_detach()\n");
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * The card status event handler. Mostly, this schedules other stuff
- * to run after an event is received. A CARD_REMOVAL event also sets
- * some flags to discourage the net drivers from trying to talk to the
- * card any more.
- */
-static int
-wavelan_event(event_t event, /* The event received */
- int priority,
- event_callback_args_t * args)
-{
- dev_link_t * link = (dev_link_t *) args->client_data;
- device * dev = (device *) link->priv;
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "->wavelan_event(): %s\n",
- ((event == CS_EVENT_REGISTRATION_COMPLETE)?"registration complete" :
- ((event == CS_EVENT_CARD_REMOVAL) ? "card removal" :
- ((event == CS_EVENT_CARD_INSERTION) ? "card insertion" :
- ((event == CS_EVENT_PM_SUSPEND) ? "pm suspend" :
- ((event == CS_EVENT_RESET_PHYSICAL) ? "physical reset" :
- ((event == CS_EVENT_PM_RESUME) ? "pm resume" :
- ((event == CS_EVENT_CARD_RESET) ? "card reset" :
- "unknown"))))))));
-#endif
-
- switch(event)
- {
- case CS_EVENT_REGISTRATION_COMPLETE:
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG "wavelan_cs: registration complete\n");
-#endif
- break;
-
- case CS_EVENT_CARD_REMOVAL:
- /* Oups ! The card is no more there */
- link->state &= ~DEV_PRESENT;
- if(link->state & DEV_CONFIG)
- {
- /* Accept no more transmissions */
- netif_device_detach(dev);
-
- /* Release the card */
- wv_pcmcia_release((u_long) link);
- }
- break;
-
- case CS_EVENT_CARD_INSERTION:
- /* Reset and configure the card */
- link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
- if(wv_pcmcia_config(link) &&
- wv_hw_config(dev))
- wv_init_info(dev);
- else
- dev->irq = 0;
- break;
-
- case CS_EVENT_PM_SUSPEND:
- /* NB: wavelan_close will be called, but too late, so we are
- * obliged to close nicely the wavelan here. David, could you
- * close the device before suspending them ? And, by the way,
- * could you, on resume, add a "route add -net ..." after the
- * ifconfig up ??? Thanks... */
-
- /* Stop receiving new messages and wait end of transmission */
- wv_ru_stop(dev);
-
- /* Power down the module */
- hacr_write(dev->base_addr, HACR_DEFAULT & (~HACR_PWR_STAT));
-
- /* The card is now suspended */
- link->state |= DEV_SUSPEND;
- /* Fall through... */
- case CS_EVENT_RESET_PHYSICAL:
- if(link->state & DEV_CONFIG)
- {
- if(link->open)
- netif_device_detach(dev);
- CardServices(ReleaseConfiguration, link->handle);
- }
- break;
-
- case CS_EVENT_PM_RESUME:
- link->state &= ~DEV_SUSPEND;
- /* Fall through... */
- case CS_EVENT_CARD_RESET:
- if(link->state & DEV_CONFIG)
- {
- CardServices(RequestConfiguration, link->handle, &link->conf);
- if(link->open) /* If RESET -> True, If RESUME -> False ??? */
- {
- wv_hw_reset(dev);
- netif_device_attach(dev);
- }
- }
- break;
- }
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "<-wavelan_event()\n");
-#endif
- return 0;
-}
-
-/****************************** MODULE ******************************/
-/*
- * Module entry points : insertion & removal
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Module insertion : initialisation of the module.
- * Register the card with cardmgr...
- */
-static int __init
-init_wavelan_cs(void)
-{
- servinfo_t serv;
-
-#ifdef DEBUG_MODULE_TRACE
- printk(KERN_DEBUG "-> init_wavelan_cs()\n");
-#ifdef DEBUG_VERSION_SHOW
- printk(KERN_DEBUG "%s", version);
-#endif
-#endif
-
- CardServices(GetCardServicesInfo, &serv);
- if(serv.Revision != CS_RELEASE_CODE)
- {
-#ifdef DEBUG_CONFIG_ERRORS
- printk(KERN_WARNING "init_wavelan_cs: Card Services release does not match!\n");
-#endif
- return -1;
- }
-
- register_pccard_driver(&dev_info, &wavelan_attach, &wavelan_detach);
-
-#ifdef DEBUG_MODULE_TRACE
- printk(KERN_DEBUG "<- init_wavelan_cs()\n");
-#endif
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Module removal
- */
-static void __exit
-exit_wavelan_cs(void)
-{
-#ifdef DEBUG_MODULE_TRACE
- printk(KERN_DEBUG "-> cleanup_module()\n");
-#endif
-#ifdef DEBUG_BASIC_SHOW
- printk(KERN_NOTICE "wavelan_cs: unloading\n");
-#endif
-
- /* Do some cleanup of the device list */
- wv_flush_stale_links();
-
- /* If there remain some devices... */
-#ifdef DEBUG_CONFIG_ERRORS
- if(dev_list != NULL)
- {
- /* Honestly, if this happen we are in a deep s**t */
- printk(KERN_INFO "wavelan_cs: devices remaining when removing module\n");
- printk(KERN_INFO "Please flush your disks and reboot NOW !\n");
- }
-#endif
-
- unregister_pccard_driver(&dev_info);
-
-#ifdef DEBUG_MODULE_TRACE
- printk(KERN_DEBUG "<- cleanup_module()\n");
-#endif
-}
-
-module_init(init_wavelan_cs);
-module_exit(exit_wavelan_cs);
+++ /dev/null
-/*
- * Wavelan Pcmcia driver
- *
- * Jean II - HPLB '96
- *
- * Reorganisation and extension of the driver.
- *
- * This file contain all definition and declarations necessary for the
- * wavelan pcmcia driver. This file is a private header, so it should
- * be included only on wavelan_cs.c !!!
- */
-
-#ifndef WAVELAN_CS_H
-#define WAVELAN_CS_H
-
-/************************** DOCUMENTATION **************************/
-/*
- * This driver provide a Linux interface to the Wavelan Pcmcia hardware
- * The Wavelan is a product of Lucent (http://www.wavelan.com/).
- * This division was formerly part of NCR and then AT&T.
- * Wavelan are also distributed by DEC (RoamAbout DS)...
- *
- * To know how to use this driver, read the PCMCIA HOWTO.
- * If you want to exploit the many other fonctionalities, look comments
- * in the code...
- *
- * This driver is the result of the effort of many peoples (see below).
- */
-
-/* ------------------------ SPECIFIC NOTES ------------------------ */
-/*
- * Web page
- * --------
- * I try to maintain a web page with the Wireless LAN Howto at :
- * http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
- *
- * SMP
- * ---
- * We now are SMP compliant (I eventually fixed the remaining bugs).
- * The driver has been tested on a dual P6-150 and survived my usual
- * set of torture tests.
- * Anyway, I spent enough time chasing interrupt re-entrancy during
- * errors or reconfigure, and I designed the locked/unlocked sections
- * of the driver with great care, and with the recent addition of
- * the spinlock (thanks to the new API), we should be quite close to
- * the truth.
- * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast),
- * but better safe than sorry (especially at 2 Mb/s ;-).
- *
- * I have also looked into disabling only our interrupt on the card
- * (via HACR) instead of all interrupts in the processor (via cli),
- * so that other driver are not impacted, and it look like it's
- * possible, but it's very tricky to do right (full of races). As
- * the gain would be mostly for SMP systems, it can wait...
- *
- * Debugging and options
- * ---------------------
- * You will find below a set of '#define" allowing a very fine control
- * on the driver behaviour and the debug messages printed.
- * The main options are :
- * o WAVELAN_ROAMING, for the experimental roaming support.
- * o SET_PSA_CRC, to have your card correctly recognised by
- * an access point and the Point-to-Point diagnostic tool.
- * o USE_PSA_CONFIG, to read configuration from the PSA (EEprom)
- * (otherwise we always start afresh with some defaults)
- *
- * wavelan_cs.o is darn too big
- * -------------------------
- * That's true ! There is a very simple way to reduce the driver
- * object by 33% (yes !). Comment out the following line :
- * #include <linux/wireless.h>
- * Other compile options can also reduce the size of it...
- *
- * MAC address and hardware detection :
- * ----------------------------------
- * The detection code of the wavelan chech that the first 3
- * octets of the MAC address fit the company code. This type of
- * detection work well for AT&T cards (because the AT&T code is
- * hardcoded in wavelan.h), but of course will fail for other
- * manufacturer.
- *
- * If you are sure that your card is derived from the wavelan,
- * here is the way to configure it :
- * 1) Get your MAC address
- * a) With your card utilities (wfreqsel, instconf, ...)
- * b) With the driver :
- * o compile the kernel with DEBUG_CONFIG_INFO enabled
- * o Boot and look the card messages
- * 2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h)
- * 3) Compile & verify
- * 4) Send me the MAC code - I will include it in the next version...
- *
- */
-
-/* --------------------- WIRELESS EXTENSIONS --------------------- */
-/*
- * This driver is the first one to support "wireless extensions".
- * This set of extensions provide you some way to control the wireless
- * caracteristics of the hardware in a standard way and support for
- * applications for taking advantage of it (like Mobile IP).
- *
- * You will need to enable the CONFIG_NET_RADIO define in the kernel
- * configuration to enable the wireless extensions (this is the one
- * giving access to the radio network device choice).
- *
- * It might also be a good idea as well to fetch the wireless tools to
- * configure the device and play a bit.
- */
-
-/* ---------------------------- FILES ---------------------------- */
-/*
- * wavelan_cs.c : The actual code for the driver - C functions
- *
- * wavelan_cs.h : Private header : local types / vars for the driver
- *
- * wavelan.h : Description of the hardware interface & structs
- *
- * i82593.h : Description if the Ethernet controller
- */
-
-/* --------------------------- HISTORY --------------------------- */
-/*
- * The history of the Wavelan drivers is as complicated as history of
- * the Wavelan itself (NCR -> AT&T -> Lucent).
- *
- * All started with Anders Klemets <klemets@paul.rutgers.edu>,
- * writting a Wavelan ISA driver for the MACH microkernel. Girish
- * Welling <welling@paul.rutgers.edu> had also worked on it.
- * Keith Moore modify this for the Pcmcia hardware.
- *
- * Robert Morris <rtm@das.harvard.edu> port these two drivers to BSDI
- * and add specific Pcmcia support (there is currently no equivalent
- * of the PCMCIA package under BSD...).
- *
- * Jim Binkley <jrb@cs.pdx.edu> port both BSDI drivers to FreeBSD.
- *
- * Bruce Janson <bruce@cs.usyd.edu.au> port the BSDI ISA driver to Linux.
- *
- * Anthony D. Joseph <adj@lcs.mit.edu> started modify Bruce driver
- * (with help of the BSDI PCMCIA driver) for PCMCIA.
- * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work.
- * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
- * correctly 2.00 cards (2.4 GHz with frequency selection).
- * David Hinds <dahinds@users.sourceforge.net> integrated the whole in his
- * Pcmcia package (+ bug corrections).
- *
- * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
- * patchs to the Pcmcia driver. After, I added code in the ISA driver
- * for Wireless Extensions and full support of frequency selection
- * cards. Now, I'm doing the same to the Pcmcia driver + some
- * reorganisation.
- * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
- * much needed informations on the Wavelan hardware.
- */
-
-/* By the way : for the copyright & legal stuff :
- * Almost everybody wrote code under GNU or BSD license (or alike),
- * and want that their original copyright remain somewhere in the
- * code (for myself, I go with the GPL).
- * Nobody want to take responsibility for anything, except the fame...
- */
-
-/* --------------------------- CREDITS --------------------------- */
-/*
- * Credits:
- * Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht and
- * Loeke Brederveld of Lucent for providing extremely useful
- * information about WaveLAN PCMCIA hardware
- *
- * This driver is based upon several other drivers, in particular:
- * David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
- * Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
- * Anders Klemets' PCMCIA WaveLAN adapter driver
- * Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
- *
- * Additional Credits:
- *
- * This software was originally developed under Linux 1.2.3
- * (Slackware 2.0 distribution).
- * And then under Linux 2.0.x (Debian 1.1 -> 2.2 - pcmcia 2.8.18+)
- * with an HP OmniBook 4000 and then a 5500.
- *
- * It is based on other device drivers and information either written
- * or supplied by:
- * James Ashton (jaa101@syseng.anu.edu.au),
- * Ajay Bakre (bakre@paul.rutgers.edu),
- * Donald Becker (becker@super.org),
- * Jim Binkley <jrb@cs.pdx.edu>,
- * Loeke Brederveld <lbrederv@wavelan.com>,
- * Allan Creighton (allanc@cs.su.oz.au),
- * Brent Elphick <belphick@uwaterloo.ca>,
- * Joe Finney <joe@comp.lancs.ac.uk>,
- * Matthew Geier (matthew@cs.su.oz.au),
- * Remo di Giovanni (remo@cs.su.oz.au),
- * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
- * David Hinds <dahinds@users.sourceforge.net>,
- * Jan Hoogendoorn (c/o marteijn@lucent.com),
- * Bruce Janson <bruce@cs.usyd.edu.au>,
- * Anthony D. Joseph <adj@lcs.mit.edu>,
- * Anders Klemets (klemets@paul.rutgers.edu),
- * Yunzhou Li <yunzhou@strat.iol.unh.edu>,
- * Marc Meertens (mmeertens@lucent.com),
- * Keith Moore,
- * Robert Morris (rtm@das.harvard.edu),
- * Ian Parkin (ian@cs.su.oz.au),
- * John Rosenberg (johnr@cs.su.oz.au),
- * George Rossi (george@phm.gov.au),
- * Arthur Scott (arthur@cs.su.oz.au),
- * Stanislav Sinyagin <stas@isf.ru>
- * Peter Storey,
- * Jean Tourrilhes <jt@hpl.hp.com>,
- * Girish Welling (welling@paul.rutgers.edu)
- * Clark Woodworth <clark@hiway1.exit109.com>
- * Yongguang Zhang <ygz@isl.hrl.hac.com>...
- */
-
-/* ------------------------- IMPROVEMENTS ------------------------- */
-/*
- * I proudly present :
- *
- * Changes made in 2.8.22 :
- * ----------------------
- * - improved wv_set_multicast_list
- * - catch spurious interrupt
- * - correct release of the device
- *
- * Changes mades in release :
- * ------------------------
- * - Reorganisation of the code, function name change
- * - Creation of private header (wavelan_cs.h)
- * - Reorganised debug messages
- * - More comments, history, ...
- * - Configure earlier (in "insert" instead of "open")
- * and do things only once
- * - mmc_init : configure the PSA if not done
- * - mmc_init : 2.00 detection better code for 2.00 init
- * - better info at startup
- * - Correct a HUGE bug (volatile & uncalibrated busy loop)
- * in wv_82593_cmd => config speedup
- * - Stop receiving & power down on close (and power up on open)
- * use "ifconfig down" & "ifconfig up ; route add -net ..."
- * - Send packets : add watchdog instead of pooling
- * - Receive : check frame wrap around & try to recover some frames
- * - wavelan_set_multicast_list : avoid reset
- * - add wireless extensions (ioctl & get_wireless_stats)
- * get/set nwid/frequency on fly, info for /proc/net/wireless
- * - Suppress useless stuff from lp (net_local), but add link
- * - More inlines
- * - Lot of others minor details & cleanups
- *
- * Changes made in second release :
- * ------------------------------
- * - Optimise wv_85893_reconfig stuff, fix potential problems
- * - Change error values for ioctl
- * - Non blocking wv_ru_stop() + call wv_reset() in case of problems
- * - Remove development printk from wavelan_watchdog()
- * - Remove of the watchdog to wavelan_close instead of wavelan_release
- * fix potential problems...
- * - Start debugging suspend stuff (but it's still a bit weird)
- * - Debug & optimize dump header/packet in Rx & Tx (debug)
- * - Use "readb" and "writeb" to be kernel 2.1 compliant
- * - Better handling of bogus interrupts
- * - Wireless extension : SETSPY and GETSPY
- * - Remove old stuff (stats - for those needing it, just ask me...)
- * - Make wireless extensions optional
- *
- * Changes made in third release :
- * -----------------------------
- * - cleanups & typos
- * - modif wireless ext (spy -> only one pointer)
- * - new private ioctl to set/get quality & level threshold
- * - Init : correct default value of level threshold for pcmcia
- * - kill watchdog in hw_reset
- * - more 2.1 support (copy_to/from_user instead of memcpy_to/fromfs)
- * - Add message level (debug stuff in /var/adm/debug & errors not
- * displayed at console and still in /var/adm/messages)
- *
- * Changes made in fourth release :
- * ------------------------------
- * - multicast support (yes !) thanks to Yongguang Zhang.
- *
- * Changes made in fifth release (2.9.0) :
- * -------------------------------------
- * - Revisited multicast code (it was mostly wrong).
- * - protect code in wv_82593_reconfig with dev->tbusy (oups !)
- *
- * Changes made in sixth release (2.9.1a) :
- * --------------------------------------
- * - Change the detection code for multi manufacturer code support
- * - Correct bug (hang kernel) in init when we were "rejecting" a card
- *
- * Changes made in seventh release (2.9.1b) :
- * ----------------------------------------
- * - Update to wireless extensions changes
- * - Silly bug in card initial configuration (psa_conf_status)
- *
- * Changes made in eigth release :
- * -----------------------------
- * - Small bug in debug code (probably not the last one...)
- * - 1.2.13 support (thanks to Clark Woodworth)
- *
- * Changes made for release in 2.9.2b :
- * ----------------------------------
- * - Level threshold is now a standard wireless extension (version 4 !)
- * - modules parameters types for kernel > 2.1.17
- * - updated man page
- * - Others cleanup from David Hinds
- *
- * Changes made for release in 2.9.5 :
- * ---------------------------------
- * - byte count stats (courtesy of David Hinds)
- * - Remove dev_tint stuff (courtesy of David Hinds)
- * - Others cleanup from David Hinds
- * - Encryption setting from Brent Elphick (thanks a lot !)
- * - 'base' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
- *
- * Changes made for release in 2.9.6 :
- * ---------------------------------
- * - fix bug : no longuer disable watchdog in case of bogus interrupt
- * - increase timeout in config code for picky hardware
- * - mask unused bits in status (Wireless Extensions)
- *
- * Changes integrated by Justin Seger <jseger@MIT.EDU> & David Hinds :
- * -----------------------------------------------------------------
- * - Roaming "hack" from Joe Finney <joe@comp.lancs.ac.uk>
- * - PSA CRC code from Bob Gray <rgray@bald.cs.dartmouth.edu>
- * - Better initialisation of the i82593 controller
- * from Joseph K. O'Sullivan <josullvn+@cs.cmu.edu>
- *
- * Changes made for release in 3.0.10 :
- * ----------------------------------
- * - Fix eject "hang" of the driver under 2.2.X :
- * o create wv_flush_stale_links()
- * o Rename wavelan_release to wv_pcmcia_release & move up
- * o move unregister_netdev to wavelan_detach()
- * o wavelan_release() no longer call wavelan_detach()
- * o Suppress "release" timer
- * o Other cleanups & fixes
- * - New MAC address in the probe
- * - Reorg PSA_CRC code (endian neutral & cleaner)
- * - Correct initialisation of the i82593 from Lucent manual
- * - Put back the watchdog, with larger timeout
- * - TRANSMIT_NO_CRC is a "normal" error, so recover from it
- * from Derrick J Brashear <shadow@dementia.org>
- * - Better handling of TX and RX normal failure conditions
- * - #ifdef out all the roaming code
- * - Add ESSID & "AP current address" ioctl stubs
- * - General cleanup of the code
- *
- * Changes made for release in 3.0.13 :
- * ----------------------------------
- * - Re-enable compilation of roaming code by default, but with
- * do_roaming = 0
- * - Nuke `nwid=nwid^ntohs(beacon->domain_id)' in wl_roam_gather
- * at the demand of John Carol Langford <jcl@gs176.sp.cs.cmu.edu>
- * - Introduced WAVELAN_ROAMING_EXT for incomplete ESSID stuff.
- *
- * Changes made for release in 3.0.15 :
- * ----------------------------------
- * - Change e-mail and web page addresses
- * - Watchdog timer is now correctly expressed in HZ, not in jiffies
- * - Add channel number to the list of frequencies in range
- * - Add the (short) list of bit-rates in range
- * - Developp a new sensitivity... (sens.value & sens.fixed)
- *
- * Changes made for release in 3.1.2 :
- * ---------------------------------
- * - Fix check for root permission (break instead of exit)
- * - New nwid & encoding setting (Wireless Extension 9)
- *
- * Changes made for release in 3.1.12 :
- * ----------------------------------
- * - reworked wv_82593_cmd to avoid using the IRQ handler and doing
- * ugly things with interrupts.
- * - Add IRQ protection in 82593_config/ru_start/ru_stop/watchdog
- * - Update to new network API (softnet - 2.3.43) :
- * o replace dev->tbusy (David + me)
- * o replace dev->tstart (David + me)
- * o remove dev->interrupt (David)
- * o add SMP locking via spinlock in splxx (me)
- * o add spinlock in interrupt handler (me)
- * o use kernel watchdog instead of ours (me)
- * o verify that all the changes make sense and work (me)
- * - Re-sync kernel/pcmcia versions (not much actually)
- * - A few other cleanups (David & me)...
- *
- * Changes made for release in 3.1.22 :
- * ----------------------------------
- * - Check that SMP works, remove annoying log message
- *
- * Changes made for release in 3.1.24 :
- * ----------------------------------
- * - Fix unfrequent card lockup when watchdog was reseting the hardware :
- * o control first busy loop in wv_82593_cmd()
- * o Extend spinlock protection in wv_hw_config()
- *
- * Wishes & dreams:
- * ----------------
- * - Cleanup and integrate the roaming code
- * (std debug, set DomainID, decay avg and co...)
- */
-
-/***************************** INCLUDES *****************************/
-
-/* Linux headers that we need */
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/in.h>
-#include <linux/delay.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/fcntl.h>
-
-#ifdef CONFIG_NET_PCMCIA_RADIO
-#include <linux/wireless.h> /* Wireless extensions */
-#endif
-
-/* Pcmcia headers that we need */
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/version.h>
-
-/* Wavelan declarations */
-#include "i82593.h" /* Definitions for the Intel chip */
-
-#include "wavelan.h" /* Others bits of the hardware */
-
-/************************** DRIVER OPTIONS **************************/
-/*
- * `#define' or `#undef' the following constant to change the behaviour
- * of the driver...
- */
-#define WAVELAN_ROAMING /* Include experimental roaming code */
-#undef WAVELAN_ROAMING_EXT /* Enable roaming wireless extensions */
-#undef SET_PSA_CRC /* Set the CRC in PSA (slower) */
-#define USE_PSA_CONFIG /* Use info from the PSA */
-#undef STRUCT_CHECK /* Verify padding of structures */
-#undef EEPROM_IS_PROTECTED /* Doesn't seem to be necessary */
-#define MULTICAST_AVOID /* Avoid extra multicast (I'm sceptical) */
-#undef SET_MAC_ADDRESS /* Experimental */
-
-#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */
-/* Warning : these stuff will slow down the driver... */
-#define WIRELESS_SPY /* Enable spying addresses */
-#undef HISTOGRAM /* Enable histogram of sig level... */
-#endif
-
-/****************************** DEBUG ******************************/
-
-#undef DEBUG_MODULE_TRACE /* Module insertion/removal */
-#undef DEBUG_CALLBACK_TRACE /* Calls made by Linux */
-#undef DEBUG_INTERRUPT_TRACE /* Calls to handler */
-#undef DEBUG_INTERRUPT_INFO /* type of interrupt & so on */
-#define DEBUG_INTERRUPT_ERROR /* problems */
-#undef DEBUG_CONFIG_TRACE /* Trace the config functions */
-#undef DEBUG_CONFIG_INFO /* What's going on... */
-#define DEBUG_CONFIG_ERRORS /* Errors on configuration */
-#undef DEBUG_TX_TRACE /* Transmission calls */
-#undef DEBUG_TX_INFO /* Header of the transmitted packet */
-#undef DEBUG_TX_FAIL /* Normal failure conditions */
-#define DEBUG_TX_ERROR /* Unexpected conditions */
-#undef DEBUG_RX_TRACE /* Transmission calls */
-#undef DEBUG_RX_INFO /* Header of the transmitted packet */
-#undef DEBUG_RX_FAIL /* Normal failure conditions */
-#define DEBUG_RX_ERROR /* Unexpected conditions */
-#undef DEBUG_PACKET_DUMP 32 /* Dump packet on the screen */
-#undef DEBUG_IOCTL_TRACE /* Misc call by Linux */
-#undef DEBUG_IOCTL_INFO /* Various debug info */
-#define DEBUG_IOCTL_ERROR /* What's going wrong */
-#define DEBUG_BASIC_SHOW /* Show basic startup info */
-#undef DEBUG_VERSION_SHOW /* Print version info */
-#undef DEBUG_PSA_SHOW /* Dump psa to screen */
-#undef DEBUG_MMC_SHOW /* Dump mmc to screen */
-#undef DEBUG_SHOW_UNUSED /* Show also unused fields */
-#undef DEBUG_I82593_SHOW /* Show i82593 status */
-#undef DEBUG_DEVICE_SHOW /* Show device parameters */
-
-/************************ CONSTANTS & MACROS ************************/
-
-#ifdef DEBUG_VERSION_SHOW
-static const char *version = "wavelan_cs.c : v23 (SMP + wireless extensions) 20/12/00\n";
-#endif
-
-/* Watchdog temporisation */
-#define WATCHDOG_JIFFIES (256*HZ/100)
-
-/* Fix a bug in some old wireless extension definitions */
-#ifndef IW_ESSID_MAX_SIZE
-#define IW_ESSID_MAX_SIZE 32
-#endif
-
-/* ------------------------ PRIVATE IOCTL ------------------------ */
-
-/* Wireless Extension Backward compatibility - Jean II
- * If the new wireless device private ioctl range is not defined,
- * default to standard device private ioctl range */
-#ifndef SIOCIWFIRSTPRIV
-#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
-#endif /* SIOCIWFIRSTPRIV */
-
-#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */
-#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */
-#define SIOCSIPROAM SIOCIWFIRSTPRIV + 2 /* Set roaming state */
-#define SIOCGIPROAM SIOCIWFIRSTPRIV + 3 /* Get roaming state */
-
-#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */
-#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 7 /* Get histogram values */
-
-/*************************** WaveLAN Roaming **************************/
-#ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */
-
-#define WAVELAN_ROAMING_DEBUG 0 /* 1 = Trace of handover decisions */
- /* 2 = Info on each beacon rcvd... */
-#define MAX_WAVEPOINTS 7 /* Max visible at one time */
-#define WAVEPOINT_HISTORY 5 /* SNR sample history slow search */
-#define WAVEPOINT_FAST_HISTORY 2 /* SNR sample history fast search */
-#define SEARCH_THRESH_LOW 10 /* SNR to enter cell search */
-#define SEARCH_THRESH_HIGH 13 /* SNR to leave cell search */
-#define WAVELAN_ROAMING_DELTA 1 /* Hysteresis value (+/- SNR) */
-#define CELL_TIMEOUT 2*HZ /* in jiffies */
-
-#define FAST_CELL_SEARCH 1 /* Boolean values... */
-#define NWID_PROMISC 1 /* for code clarity. */
-
-typedef struct wavepoint_beacon
-{
- unsigned char dsap, /* Unused */
- ssap, /* Unused */
- ctrl, /* Unused */
- O,U,I, /* Unused */
- spec_id1, /* Unused */
- spec_id2, /* Unused */
- pdu_type, /* Unused */
- seq; /* WavePoint beacon sequence number */
- unsigned short domain_id, /* WavePoint Domain ID */
- nwid; /* WavePoint NWID */
-} wavepoint_beacon;
-
-typedef struct wavepoint_history
-{
- unsigned short nwid; /* WavePoint's NWID */
- int average_slow; /* SNR running average */
- int average_fast; /* SNR running average */
- unsigned char sigqual[WAVEPOINT_HISTORY]; /* Ringbuffer of recent SNR's */
- unsigned char qualptr; /* Index into ringbuffer */
- unsigned char last_seq; /* Last seq. no seen for WavePoint */
- struct wavepoint_history *next; /* Next WavePoint in table */
- struct wavepoint_history *prev; /* Previous WavePoint in table */
- unsigned long last_seen; /* Time of last beacon recvd, jiffies */
-} wavepoint_history;
-
-struct wavepoint_table
-{
- wavepoint_history *head; /* Start of ringbuffer */
- int num_wavepoints; /* No. of WavePoints visible */
- unsigned char locked; /* Table lock */
-};
-
-#endif /* WAVELAN_ROAMING */
-
-/****************************** TYPES ******************************/
-
-/* Shortcuts */
-typedef struct net_device device;
-typedef struct net_device_stats en_stats;
-typedef struct iw_statistics iw_stats;
-typedef struct iw_quality iw_qual;
-typedef struct iw_freq iw_freq;
-typedef struct net_local net_local;
-typedef struct timer_list timer_list;
-
-/* Basic types */
-typedef u_char mac_addr[WAVELAN_ADDR_SIZE]; /* Hardware address */
-
-/*
- * Static specific data for the interface.
- *
- * For each network interface, Linux keep data in two structure. "device"
- * keep the generic data (same format for everybody) and "net_local" keep
- * the additional specific data.
- * Note that some of this specific data is in fact generic (en_stats, for
- * example).
- */
-struct net_local
-{
- dev_node_t node; /* ???? What is this stuff ???? */
- device * dev; /* Reverse link... */
- spinlock_t spinlock; /* Serialize access to the hardware (SMP) */
- dev_link_t * link; /* pcmcia structure */
- en_stats stats; /* Ethernet interface statistics */
- int nresets; /* Number of hw resets */
- u_char configured; /* If it is configured */
- u_char reconfig_82593; /* Need to reconfigure the controller */
- u_char promiscuous; /* Promiscuous mode */
- u_char allmulticast; /* All Multicast mode */
- int mc_count; /* Number of multicast addresses */
-
- int stop; /* Current i82593 Stop Hit Register */
- int rfp; /* Last DMA machine receive pointer */
- int overrunning; /* Receiver overrun flag */
-
-#ifdef WIRELESS_EXT
- iw_stats wstats; /* Wireless specific stats */
-#endif
-
-#ifdef WIRELESS_SPY
- int spy_number; /* Number of addresses to spy */
- mac_addr spy_address[IW_MAX_SPY]; /* The addresses to spy */
- iw_qual spy_stat[IW_MAX_SPY]; /* Statistics gathered */
-#endif /* WIRELESS_SPY */
-#ifdef HISTOGRAM
- int his_number; /* Number of intervals */
- u_char his_range[16]; /* Boundaries of interval ]n-1; n] */
- u_long his_sum[16]; /* Sum in interval */
-#endif /* HISTOGRAM */
-#ifdef WAVELAN_ROAMING
- u_long domain_id; /* Domain ID we lock on for roaming */
- int filter_domains; /* Check Domain ID of beacon found */
- struct wavepoint_table wavepoint_table; /* Table of visible WavePoints*/
- wavepoint_history * curr_point; /* Current wavepoint */
- int cell_search; /* Searching for new cell? */
- struct timer_list cell_timer; /* Garbage collection */
-#endif /* WAVELAN_ROAMING */
-};
-
-/**************************** PROTOTYPES ****************************/
-
-#ifdef WAVELAN_ROAMING
-/* ---------------------- ROAMING SUBROUTINES -----------------------*/
-
-wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp);
-wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local *lp);
-void wl_del_wavepoint(wavepoint_history *wavepoint, net_local *lp);
-void wl_cell_expiry(unsigned long data);
-wavepoint_history *wl_best_sigqual(int fast_search, net_local *lp);
-void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq);
-void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp);
-void wv_nwid_filter(unsigned char mode, net_local *lp);
-void wv_roam_init(struct net_device *dev);
-void wv_roam_cleanup(struct net_device *dev);
-#endif /* WAVELAN_ROAMING */
-
-/* ----------------------- MISC SUBROUTINES ------------------------ */
-static inline void
- wv_splhi(net_local *, /* Disable interrupts */
- unsigned long *); /* flags */
-static inline void
- wv_splx(net_local *, /* ReEnable interrupts */
- unsigned long *); /* flags */
-static void
- cs_error(client_handle_t, /* Report error to cardmgr */
- int,
- int);
-/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
-static inline u_char /* data */
- hasr_read(u_long); /* Read the host interface : base address */
-static inline void
- hacr_write(u_long, /* Write to host interface : base address */
- u_char), /* data */
- hacr_write_slow(u_long,
- u_char);
-static void
- psa_read(device *, /* Read the Parameter Storage Area */
- int, /* offset in PSA */
- u_char *, /* buffer to fill */
- int), /* size to read */
- psa_write(device *, /* Write to the PSA */
- int, /* Offset in psa */
- u_char *, /* Buffer in memory */
- int); /* Length of buffer */
-static inline void
- mmc_out(u_long, /* Write 1 byte to the Modem Manag Control */
- u_short,
- u_char),
- mmc_write(u_long, /* Write n bytes to the MMC */
- u_char,
- u_char *,
- int);
-static inline u_char /* Read 1 byte from the MMC */
- mmc_in(u_long,
- u_short);
-static inline void
- mmc_read(u_long, /* Read n bytes from the MMC */
- u_char,
- u_char *,
- int),
- fee_wait(u_long, /* Wait for frequency EEprom : base address */
- int, /* Base delay to wait for */
- int); /* Number of time to wait */
-static void
- fee_read(u_long, /* Read the frequency EEprom : base address */
- u_short, /* destination offset */
- u_short *, /* data buffer */
- int); /* number of registers */
-/* ---------------------- I82593 SUBROUTINES ----------------------- */
-static int
- wv_82593_cmd(device *, /* synchronously send a command to i82593 */
- char *,
- int,
- int);
-static inline int
- wv_diag(device *); /* Diagnostique the i82593 */
-static int
- read_ringbuf(device *, /* Read a receive buffer */
- int,
- char *,
- int);
-static inline void
- wv_82593_reconfig(device *); /* Reconfigure the controller */
-/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */
-static inline void
- wv_init_info(device *); /* display startup info */
-/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
-static en_stats *
- wavelan_get_stats(device *); /* Give stats /proc/net/dev */
-/* ----------------------- PACKET RECEPTION ----------------------- */
-static inline int
- wv_start_of_frame(device *, /* Seek beggining of current frame */
- int, /* end of frame */
- int); /* start of buffer */
-static inline void
- wv_packet_read(device *, /* Read a packet from a frame */
- int,
- int),
- wv_packet_rcv(device *); /* Read all packets waiting */
-/* --------------------- PACKET TRANSMISSION --------------------- */
-static inline void
- wv_packet_write(device *, /* Write a packet to the Tx buffer */
- void *,
- short);
-static int
- wavelan_packet_xmit(struct sk_buff *, /* Send a packet */
- device *);
-/* -------------------- HARDWARE CONFIGURATION -------------------- */
-static inline int
- wv_mmc_init(device *); /* Initialize the modem */
-static int
- wv_ru_stop(device *), /* Stop the i82593 receiver unit */
- wv_ru_start(device *); /* Start the i82593 receiver unit */
-static int
- wv_82593_config(device *); /* Configure the i82593 */
-static inline int
- wv_pcmcia_reset(device *); /* Reset the pcmcia interface */
-static int
- wv_hw_config(device *); /* Reset & configure the whole hardware */
-static inline void
- wv_hw_reset(device *); /* Same, + start receiver unit */
-static inline int
- wv_pcmcia_config(dev_link_t *); /* Configure the pcmcia interface */
-static void
- wv_pcmcia_release(u_long), /* Remove a device */
- wv_flush_stale_links(void); /* "detach" all possible devices */
-/* ---------------------- INTERRUPT HANDLING ---------------------- */
-static void
- wavelan_interrupt(int, /* Interrupt handler */
- void *,
- struct pt_regs *);
-static void
- wavelan_watchdog(device *); /* Transmission watchdog */
-/* ------------------- CONFIGURATION CALLBACKS ------------------- */
-static int
- wavelan_open(device *), /* Open the device */
- wavelan_close(device *); /* Close the device */
-static dev_link_t *
- wavelan_attach(void); /* Create a new device */
-static void
- wavelan_detach(dev_link_t *); /* Destroy a removed device */
-static int
- wavelan_event(event_t, /* Manage pcmcia events */
- int,
- event_callback_args_t *);
-
-/**************************** VARIABLES ****************************/
-
-static dev_info_t dev_info = "wavelan_cs";
-static dev_link_t *dev_list = NULL; /* Linked list of devices */
-
-/*
- * Parameters that can be set with 'insmod'
- * The exact syntax is 'insmod wavelan_cs.o <var>=<value>'
- */
-
-/* Bit map of interrupts to choose from */
-/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4 and 3 */
-static int irq_mask = 0xdeb8;
-static int irq_list[4] = { -1 };
-
-/* Shared memory speed, in ns */
-static int mem_speed = 0;
-
-/* New module interface */
-MODULE_PARM(irq_mask, "i");
-MODULE_PARM(irq_list, "1-4i");
-MODULE_PARM(mem_speed, "i");
-
-#ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */
-/* Enable roaming mode ? No ! Please keep this to 0 */
-static int do_roaming = 0;
-MODULE_PARM(do_roaming, "i");
-#endif /* WAVELAN_ROAMING */
-
-MODULE_LICENSE("GPL");
-
-#endif /* WAVELAN_CS_H */
-
+++ /dev/null
-/*
- * WaveLAN ISA driver
- *
- * Jean II - HPLB '96
- *
- * Reorganisation and extension of the driver.
- * Original copyright follows (also see the end of this file).
- * See wavelan.p.h for details.
- *
- *
- *
- * AT&T GIS (nee NCR) WaveLAN card:
- * An Ethernet-like radio transceiver
- * controlled by an Intel 82586 coprocessor.
- */
-
-#include "wavelan.p.h" /* Private header */
-
-/************************* MISC SUBROUTINES **************************/
-/*
- * Subroutines which won't fit in one of the following category
- * (WaveLAN modem or i82586)
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Wrapper for disabling interrupts and locking the driver.
- * (note : inline, so optimised away)
- */
-static inline void wv_splhi(net_local * lp,
- unsigned long * pflags)
-{
- spin_lock_irqsave(&lp->spinlock, *pflags);
- /* Note : above does the cli(); itself */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wrapper for re-enabling interrupts and un-locking the driver.
- */
-static inline void wv_splx(net_local * lp,
- unsigned long * pflags)
-{
- spin_unlock_irqrestore(&lp->spinlock, *pflags);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Translate irq number to PSA irq parameter
- */
-static u8 wv_irq_to_psa(int irq)
-{
- if (irq < 0 || irq >= NELS(irqvals))
- return 0;
-
- return irqvals[irq];
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Translate PSA irq parameter to irq number
- */
-static int __init wv_psa_to_irq(u8 irqval)
-{
- int irq;
-
- for (irq = 0; irq < NELS(irqvals); irq++)
- if (irqvals[irq] == irqval)
- return irq;
-
- return -1;
-}
-
-#ifdef STRUCT_CHECK
-/*------------------------------------------------------------------*/
-/*
- * Sanity routine to verify the sizes of the various WaveLAN interface
- * structures.
- */
-static char *wv_struct_check(void)
-{
-#define SC(t,s,n) if (sizeof(t) != s) return(n);
-
- SC(psa_t, PSA_SIZE, "psa_t");
- SC(mmw_t, MMW_SIZE, "mmw_t");
- SC(mmr_t, MMR_SIZE, "mmr_t");
- SC(ha_t, HA_SIZE, "ha_t");
-
-#undef SC
-
- return ((char *) NULL);
-} /* wv_struct_check */
-#endif /* STRUCT_CHECK */
-
-/********************* HOST ADAPTER SUBROUTINES *********************/
-/*
- * Useful subroutines to manage the WaveLAN ISA interface
- *
- * One major difference with the PCMCIA hardware (except the port mapping)
- * is that we have to keep the state of the Host Control Register
- * because of the interrupt enable & bus size flags.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Read from card's Host Adaptor Status Register.
- */
-static inline u16 hasr_read(unsigned long ioaddr)
-{
- return (inw(HASR(ioaddr)));
-} /* hasr_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Write to card's Host Adapter Command Register.
- */
-static inline void hacr_write(unsigned long ioaddr, u16 hacr)
-{
- outw(hacr, HACR(ioaddr));
-} /* hacr_write */
-
-/*------------------------------------------------------------------*/
-/*
- * Write to card's Host Adapter Command Register. Include a delay for
- * those times when it is needed.
- */
-static inline void hacr_write_slow(unsigned long ioaddr, u16 hacr)
-{
- hacr_write(ioaddr, hacr);
- /* delay might only be needed sometimes */
- mdelay(1);
-} /* hacr_write_slow */
-
-/*------------------------------------------------------------------*/
-/*
- * Set the channel attention bit.
- */
-static inline void set_chan_attn(unsigned long ioaddr, u16 hacr)
-{
- hacr_write(ioaddr, hacr | HACR_CA);
-} /* set_chan_attn */
-
-/*------------------------------------------------------------------*/
-/*
- * Reset, and then set host adaptor into default mode.
- */
-static inline void wv_hacr_reset(unsigned long ioaddr)
-{
- hacr_write_slow(ioaddr, HACR_RESET);
- hacr_write(ioaddr, HACR_DEFAULT);
-} /* wv_hacr_reset */
-
-/*------------------------------------------------------------------*/
-/*
- * Set the I/O transfer over the ISA bus to 8-bit mode
- */
-static inline void wv_16_off(unsigned long ioaddr, u16 hacr)
-{
- hacr &= ~HACR_16BITS;
- hacr_write(ioaddr, hacr);
-} /* wv_16_off */
-
-/*------------------------------------------------------------------*/
-/*
- * Set the I/O transfer over the ISA bus to 8-bit mode
- */
-static inline void wv_16_on(unsigned long ioaddr, u16 hacr)
-{
- hacr |= HACR_16BITS;
- hacr_write(ioaddr, hacr);
-} /* wv_16_on */
-
-/*------------------------------------------------------------------*/
-/*
- * Disable interrupts on the WaveLAN hardware.
- * (called by wv_82586_stop())
- */
-static inline void wv_ints_off(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
-
- lp->hacr &= ~HACR_INTRON;
- hacr_write(ioaddr, lp->hacr);
-} /* wv_ints_off */
-
-/*------------------------------------------------------------------*/
-/*
- * Enable interrupts on the WaveLAN hardware.
- * (called by wv_hw_reset())
- */
-static inline void wv_ints_on(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
-
- lp->hacr |= HACR_INTRON;
- hacr_write(ioaddr, lp->hacr);
-} /* wv_ints_on */
-
-/******************* MODEM MANAGEMENT SUBROUTINES *******************/
-/*
- * Useful subroutines to manage the modem of the WaveLAN
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Read the Parameter Storage Area from the WaveLAN card's memory
- */
-/*
- * Read bytes from the PSA.
- */
-static void psa_read(unsigned long ioaddr, u16 hacr, int o, /* offset in PSA */
- u8 * b, /* buffer to fill */
- int n)
-{ /* size to read */
- wv_16_off(ioaddr, hacr);
-
- while (n-- > 0) {
- outw(o, PIOR2(ioaddr));
- o++;
- *b++ = inb(PIOP2(ioaddr));
- }
-
- wv_16_on(ioaddr, hacr);
-} /* psa_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Write the Parameter Storage Area to the WaveLAN card's memory.
- */
-static void psa_write(unsigned long ioaddr, u16 hacr, int o, /* Offset in PSA */
- u8 * b, /* Buffer in memory */
- int n)
-{ /* Length of buffer */
- int count = 0;
-
- wv_16_off(ioaddr, hacr);
-
- while (n-- > 0) {
- outw(o, PIOR2(ioaddr));
- o++;
-
- outb(*b, PIOP2(ioaddr));
- b++;
-
- /* Wait for the memory to finish its write cycle */
- count = 0;
- while ((count++ < 100) &&
- (hasr_read(ioaddr) & HASR_PSA_BUSY)) mdelay(1);
- }
-
- wv_16_on(ioaddr, hacr);
-} /* psa_write */
-
-#ifdef SET_PSA_CRC
-/*------------------------------------------------------------------*/
-/*
- * Calculate the PSA CRC
- * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
- * NOTE: By specifying a length including the CRC position the
- * returned value should be zero. (i.e. a correct checksum in the PSA)
- *
- * The Windows drivers don't use the CRC, but the AP and the PtP tool
- * depend on it.
- */
-static inline u16 psa_crc(u8 * psa, /* The PSA */
- int size)
-{ /* Number of short for CRC */
- int byte_cnt; /* Loop on the PSA */
- u16 crc_bytes = 0; /* Data in the PSA */
- int bit_cnt; /* Loop on the bits of the short */
-
- for (byte_cnt = 0; byte_cnt < size; byte_cnt++) {
- crc_bytes ^= psa[byte_cnt]; /* Its an xor */
-
- for (bit_cnt = 1; bit_cnt < 9; bit_cnt++) {
- if (crc_bytes & 0x0001)
- crc_bytes = (crc_bytes >> 1) ^ 0xA001;
- else
- crc_bytes >>= 1;
- }
- }
-
- return crc_bytes;
-} /* psa_crc */
-#endif /* SET_PSA_CRC */
-
-/*------------------------------------------------------------------*/
-/*
- * update the checksum field in the Wavelan's PSA
- */
-static void update_psa_checksum(device * dev, unsigned long ioaddr, u16 hacr)
-{
-#ifdef SET_PSA_CRC
- psa_t psa;
- u16 crc;
-
- /* read the parameter storage area */
- psa_read(ioaddr, hacr, 0, (unsigned char *) &psa, sizeof(psa));
-
- /* update the checksum */
- crc = psa_crc((unsigned char *) &psa,
- sizeof(psa) - sizeof(psa.psa_crc[0]) -
- sizeof(psa.psa_crc[1])
- - sizeof(psa.psa_crc_status));
-
- psa.psa_crc[0] = crc & 0xFF;
- psa.psa_crc[1] = (crc & 0xFF00) >> 8;
-
- /* Write it ! */
- psa_write(ioaddr, hacr, (char *) &psa.psa_crc - (char *) &psa,
- (unsigned char *) &psa.psa_crc, 2);
-
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
- dev->name, psa.psa_crc[0], psa.psa_crc[1]);
-
- /* Check again (luxury !) */
- crc = psa_crc((unsigned char *) &psa,
- sizeof(psa) - sizeof(psa.psa_crc_status));
-
- if (crc != 0)
- printk(KERN_WARNING
- "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n",
- dev->name);
-#endif /* DEBUG_IOCTL_INFO */
-#endif /* SET_PSA_CRC */
-} /* update_psa_checksum */
-
-/*------------------------------------------------------------------*/
-/*
- * Write 1 byte to the MMC.
- */
-static inline void mmc_out(unsigned long ioaddr, u16 o, u8 d)
-{
- /* Wait for MMC to go idle */
- while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
-
- outw((u16) (((u16) d << 8) | (o << 1) | 1), MMCR(ioaddr));
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to write bytes to the Modem Management Controller.
- * We start at the end because it is the way it should be!
- */
-static inline void mmc_write(unsigned long ioaddr, u8 o, u8 * b, int n)
-{
- o += n;
- b += n;
-
- while (n-- > 0)
- mmc_out(ioaddr, --o, *(--b));
-} /* mmc_write */
-
-/*------------------------------------------------------------------*/
-/*
- * Read a byte from the MMC.
- * Optimised version for 1 byte, avoid using memory.
- */
-static inline u8 mmc_in(unsigned long ioaddr, u16 o)
-{
- while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
- outw(o << 1, MMCR(ioaddr));
-
- while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
- return (u8) (inw(MMCR(ioaddr)) >> 8);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to read bytes from the Modem Management Controller.
- * The implementation is complicated by a lack of address lines,
- * which prevents decoding of the low-order bit.
- * (code has just been moved in the above function)
- * We start at the end because it is the way it should be!
- */
-static inline void mmc_read(unsigned long ioaddr, u8 o, u8 * b, int n)
-{
- o += n;
- b += n;
-
- while (n-- > 0)
- *(--b) = mmc_in(ioaddr, --o);
-} /* mmc_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Get the type of encryption available.
- */
-static inline int mmc_encr(unsigned long ioaddr)
-{ /* I/O port of the card */
- int temp;
-
- temp = mmc_in(ioaddr, mmroff(0, mmr_des_avail));
- if ((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
- return 0;
- else
- return temp;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wait for the frequency EEPROM to complete a command.
- * I hope this one will be optimally inlined.
- */
-static inline void fee_wait(unsigned long ioaddr, /* I/O port of the card */
- int delay, /* Base delay to wait for */
- int number)
-{ /* Number of time to wait */
- int count = 0; /* Wait only a limited time */
-
- while ((count++ < number) &&
- (mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
- MMR_FEE_STATUS_BUSY)) udelay(delay);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Read bytes from the Frequency EEPROM (frequency select cards).
- */
-static void fee_read(unsigned long ioaddr, /* I/O port of the card */
- u16 o, /* destination offset */
- u16 * b, /* data buffer */
- int n)
-{ /* number of registers */
- b += n; /* Position at the end of the area */
-
- /* Write the address */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
-
- /* Loop on all buffer */
- while (n-- > 0) {
- /* Write the read command */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
- MMW_FEE_CTRL_READ);
-
- /* Wait until EEPROM is ready (should be quick). */
- fee_wait(ioaddr, 10, 100);
-
- /* Read the value. */
- *--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) |
- mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
- }
-}
-
-#ifdef WIRELESS_EXT /* if the wireless extension exists in the kernel */
-
-/*------------------------------------------------------------------*/
-/*
- * Write bytes from the Frequency EEPROM (frequency select cards).
- * This is a bit complicated, because the frequency EEPROM has to
- * be unprotected and the write enabled.
- * Jean II
- */
-static void fee_write(unsigned long ioaddr, /* I/O port of the card */
- u16 o, /* destination offset */
- u16 * b, /* data buffer */
- int n)
-{ /* number of registers */
- b += n; /* Position at the end of the area. */
-
-#ifdef EEPROM_IS_PROTECTED /* disabled */
-#ifdef DOESNT_SEEM_TO_WORK /* disabled */
- /* Ask to read the protected register */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
-
- fee_wait(ioaddr, 10, 100);
-
- /* Read the protected register. */
- printk("Protected 2: %02X-%02X\n",
- mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)),
- mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
-#endif /* DOESNT_SEEM_TO_WORK */
-
- /* Enable protected register. */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
- mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
-
- fee_wait(ioaddr, 10, 100);
-
- /* Unprotect area. */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n);
- mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
-#ifdef DOESNT_SEEM_TO_WORK /* disabled */
- /* or use: */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
-#endif /* DOESNT_SEEM_TO_WORK */
-
- fee_wait(ioaddr, 10, 100);
-#endif /* EEPROM_IS_PROTECTED */
-
- /* Write enable. */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
- mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
-
- fee_wait(ioaddr, 10, 100);
-
- /* Write the EEPROM address. */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
-
- /* Loop on all buffer */
- while (n-- > 0) {
- /* Write the value. */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
- mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
-
- /* Write the write command. */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
- MMW_FEE_CTRL_WRITE);
-
- /* WaveLAN documentation says to wait at least 10 ms for EEBUSY = 0 */
- mdelay(10);
- fee_wait(ioaddr, 10, 100);
- }
-
- /* Write disable. */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
- mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
-
- fee_wait(ioaddr, 10, 100);
-
-#ifdef EEPROM_IS_PROTECTED /* disabled */
- /* Reprotect EEPROM. */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00);
- mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
-
- fee_wait(ioaddr, 10, 100);
-#endif /* EEPROM_IS_PROTECTED */
-}
-#endif /* WIRELESS_EXT */
-
-/************************ I82586 SUBROUTINES *************************/
-/*
- * Useful subroutines to manage the Ethernet controller
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Read bytes from the on-board RAM.
- * Why does inlining this function make it fail?
- */
-static /*inline */ void obram_read(unsigned long ioaddr,
- u16 o, u8 * b, int n)
-{
- outw(o, PIOR1(ioaddr));
- insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Write bytes to the on-board RAM.
- */
-static inline void obram_write(unsigned long ioaddr, u16 o, u8 * b, int n)
-{
- outw(o, PIOR1(ioaddr));
- outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Acknowledge the reading of the status issued by the i82586.
- */
-static void wv_ack(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
- u16 scb_cs;
- int i;
-
- obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
- (unsigned char *) &scb_cs, sizeof(scb_cs));
- scb_cs &= SCB_ST_INT;
-
- if (scb_cs == 0)
- return;
-
- obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
- (unsigned char *) &scb_cs, sizeof(scb_cs));
-
- set_chan_attn(ioaddr, lp->hacr);
-
- for (i = 1000; i > 0; i--) {
- obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
- (unsigned char *) &scb_cs, sizeof(scb_cs));
- if (scb_cs == 0)
- break;
-
- udelay(10);
- }
- udelay(100);
-
-#ifdef DEBUG_CONFIG_ERROR
- if (i <= 0)
- printk(KERN_INFO
- "%s: wv_ack(): board not accepting command.\n",
- dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Set channel attention bit and busy wait until command has
- * completed, then acknowledge completion of the command.
- */
-static inline int wv_synchronous_cmd(device * dev, const char *str)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
- u16 scb_cmd;
- ach_t cb;
- int i;
-
- scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
- obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
- (unsigned char *) &scb_cmd, sizeof(scb_cmd));
-
- set_chan_attn(ioaddr, lp->hacr);
-
- for (i = 1000; i > 0; i--) {
- obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb,
- sizeof(cb));
- if (cb.ac_status & AC_SFLD_C)
- break;
-
- udelay(10);
- }
- udelay(100);
-
- if (i <= 0 || !(cb.ac_status & AC_SFLD_OK)) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_INFO "%s: %s failed; status = 0x%x\n",
- dev->name, str, cb.ac_status);
-#endif
-#ifdef DEBUG_I82586_SHOW
- wv_scb_show(ioaddr);
-#endif
- return -1;
- }
-
- /* Ack the status */
- wv_ack(dev);
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Configuration commands completion interrupt.
- * Check if done, and if OK.
- */
-static inline int
-wv_config_complete(device * dev, unsigned long ioaddr, net_local * lp)
-{
- unsigned short mcs_addr;
- unsigned short status;
- int ret;
-
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name);
-#endif
-
- mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t)
- + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t);
-
- /* Read the status of the last command (set mc list). */
- obram_read(ioaddr, acoff(mcs_addr, ac_status),
- (unsigned char *) &status, sizeof(status));
-
- /* If not completed -> exit */
- if ((status & AC_SFLD_C) == 0)
- ret = 0; /* Not ready to be scrapped */
- else {
-#ifdef DEBUG_CONFIG_ERROR
- unsigned short cfg_addr;
- unsigned short ias_addr;
-
- /* Check mc_config command */
- if ((status & AC_SFLD_OK) != AC_SFLD_OK)
- printk(KERN_INFO
- "%s: wv_config_complete(): set_multicast_address failed; status = 0x%x\n",
- dev->name, status);
-
- /* check ia-config command */
- ias_addr = mcs_addr - sizeof(ac_ias_t);
- obram_read(ioaddr, acoff(ias_addr, ac_status),
- (unsigned char *) &status, sizeof(status));
- if ((status & AC_SFLD_OK) != AC_SFLD_OK)
- printk(KERN_INFO
- "%s: wv_config_complete(): set_MAC_address failed; status = 0x%x\n",
- dev->name, status);
-
- /* Check config command. */
- cfg_addr = ias_addr - sizeof(ac_cfg_t);
- obram_read(ioaddr, acoff(cfg_addr, ac_status),
- (unsigned char *) &status, sizeof(status));
- if ((status & AC_SFLD_OK) != AC_SFLD_OK)
- printk(KERN_INFO
- "%s: wv_config_complete(): configure failed; status = 0x%x\n",
- dev->name, status);
-#endif /* DEBUG_CONFIG_ERROR */
-
- ret = 1; /* Ready to be scrapped */
- }
-
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name,
- ret);
-#endif
- return ret;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Command completion interrupt.
- * Reclaim as many freed tx buffers as we can.
- * (called in wavelan_interrupt()).
- * Note : the spinlock is already grabbed for us.
- */
-static int wv_complete(device * dev, unsigned long ioaddr, net_local * lp)
-{
- int nreaped = 0;
-
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name);
-#endif
-
- /* Loop on all the transmit buffers */
- while (lp->tx_first_in_use != I82586NULL) {
- unsigned short tx_status;
-
- /* Read the first transmit buffer */
- obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status),
- (unsigned char *) &tx_status,
- sizeof(tx_status));
-
- /* If not completed -> exit */
- if ((tx_status & AC_SFLD_C) == 0)
- break;
-
- /* Hack for reconfiguration */
- if (tx_status == 0xFFFF)
- if (!wv_config_complete(dev, ioaddr, lp))
- break; /* Not completed */
-
- /* We now remove this buffer */
- nreaped++;
- --lp->tx_n_in_use;
-
-/*
-if (lp->tx_n_in_use > 0)
- printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
-*/
-
- /* Was it the last one? */
- if (lp->tx_n_in_use <= 0)
- lp->tx_first_in_use = I82586NULL;
- else {
- /* Next one in the chain */
- lp->tx_first_in_use += TXBLOCKZ;
- if (lp->tx_first_in_use >=
- OFFSET_CU +
- NTXBLOCKS * TXBLOCKZ) lp->tx_first_in_use -=
- NTXBLOCKS * TXBLOCKZ;
- }
-
- /* Hack for reconfiguration */
- if (tx_status == 0xFFFF)
- continue;
-
- /* Now, check status of the finished command */
- if (tx_status & AC_SFLD_OK) {
- int ncollisions;
-
- lp->stats.tx_packets++;
- ncollisions = tx_status & AC_SFLD_MAXCOL;
- lp->stats.collisions += ncollisions;
-#ifdef DEBUG_TX_INFO
- if (ncollisions > 0)
- printk(KERN_DEBUG
- "%s: wv_complete(): tx completed after %d collisions.\n",
- dev->name, ncollisions);
-#endif
- } else {
- lp->stats.tx_errors++;
- if (tx_status & AC_SFLD_S10) {
- lp->stats.tx_carrier_errors++;
-#ifdef DEBUG_TX_FAIL
- printk(KERN_DEBUG
- "%s: wv_complete(): tx error: no CS.\n",
- dev->name);
-#endif
- }
- if (tx_status & AC_SFLD_S9) {
- lp->stats.tx_carrier_errors++;
-#ifdef DEBUG_TX_FAIL
- printk(KERN_DEBUG
- "%s: wv_complete(): tx error: lost CTS.\n",
- dev->name);
-#endif
- }
- if (tx_status & AC_SFLD_S8) {
- lp->stats.tx_fifo_errors++;
-#ifdef DEBUG_TX_FAIL
- printk(KERN_DEBUG
- "%s: wv_complete(): tx error: slow DMA.\n",
- dev->name);
-#endif
- }
- if (tx_status & AC_SFLD_S6) {
- lp->stats.tx_heartbeat_errors++;
-#ifdef DEBUG_TX_FAIL
- printk(KERN_DEBUG
- "%s: wv_complete(): tx error: heart beat.\n",
- dev->name);
-#endif
- }
- if (tx_status & AC_SFLD_S5) {
- lp->stats.tx_aborted_errors++;
-#ifdef DEBUG_TX_FAIL
- printk(KERN_DEBUG
- "%s: wv_complete(): tx error: too many collisions.\n",
- dev->name);
-#endif
- }
- }
-
-#ifdef DEBUG_TX_INFO
- printk(KERN_DEBUG
- "%s: wv_complete(): tx completed, tx_status 0x%04x\n",
- dev->name, tx_status);
-#endif
- }
-
-#ifdef DEBUG_INTERRUPT_INFO
- if (nreaped > 1)
- printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n",
- dev->name, nreaped);
-#endif
-
- /*
- * Inform upper layers.
- */
- if (lp->tx_n_in_use < NTXBLOCKS - 1) {
- netif_wake_queue(dev);
- }
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name);
-#endif
- return nreaped;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Reconfigure the i82586, or at least ask for it.
- * Because wv_82586_config uses a transmission buffer, we must do it
- * when we are sure that there is one left, so we do it now
- * or in wavelan_packet_xmit() (I can't find any better place,
- * wavelan_interrupt is not an option), so you may experience
- * delays sometimes.
- */
-static inline void wv_82586_reconfig(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long flags;
-
- /* Arm the flag, will be cleard in wv_82586_config() */
- lp->reconfig_82586 = 1;
-
- /* Check if we can do it now ! */
- if((netif_running(dev)) && !(netif_queue_stopped(dev))) {
- wv_splhi(lp, &flags);
- /* May fail */
- wv_82586_config(dev);
- wv_splx(lp, &flags);
- }
- else {
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG
- "%s: wv_82586_reconfig(): delayed (state = %lX)\n",
- dev->name, dev->state);
-#endif
- }
-}
-
-/********************* DEBUG & INFO SUBROUTINES *********************/
-/*
- * This routine is used in the code to show information for debugging.
- * Most of the time, it dumps the contents of hardware structures.
- */
-
-#ifdef DEBUG_PSA_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted contents of the Parameter Storage Area.
- */
-static void wv_psa_show(psa_t * p)
-{
- printk(KERN_DEBUG "##### WaveLAN PSA contents: #####\n");
- printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
- p->psa_io_base_addr_1,
- p->psa_io_base_addr_2,
- p->psa_io_base_addr_3, p->psa_io_base_addr_4);
- printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
- p->psa_rem_boot_addr_1,
- p->psa_rem_boot_addr_2, p->psa_rem_boot_addr_3);
- printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
- printk("psa_int_req_no: %d\n", p->psa_int_req_no);
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG
- "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
- p->psa_unused0[0], p->psa_unused0[1], p->psa_unused0[2],
- p->psa_unused0[3], p->psa_unused0[4], p->psa_unused0[5],
- p->psa_unused0[6]);
-#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG
- "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
- p->psa_univ_mac_addr[0], p->psa_univ_mac_addr[1],
- p->psa_univ_mac_addr[2], p->psa_univ_mac_addr[3],
- p->psa_univ_mac_addr[4], p->psa_univ_mac_addr[5]);
- printk(KERN_DEBUG
- "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
- p->psa_local_mac_addr[0], p->psa_local_mac_addr[1],
- p->psa_local_mac_addr[2], p->psa_local_mac_addr[3],
- p->psa_local_mac_addr[4], p->psa_local_mac_addr[5]);
- printk(KERN_DEBUG "psa_univ_local_sel: %d, ",
- p->psa_univ_local_sel);
- printk("psa_comp_number: %d, ", p->psa_comp_number);
- printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
- printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
- p->psa_feature_select);
- printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
- printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
- printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
- printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0],
- p->psa_nwid[1]);
- printk("psa_nwid_select: %d\n", p->psa_nwid_select);
- printk(KERN_DEBUG "psa_encryption_select: %d, ",
- p->psa_encryption_select);
- printk
- ("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
- p->psa_encryption_key[0], p->psa_encryption_key[1],
- p->psa_encryption_key[2], p->psa_encryption_key[3],
- p->psa_encryption_key[4], p->psa_encryption_key[5],
- p->psa_encryption_key[6], p->psa_encryption_key[7]);
- printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
- printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
- p->psa_call_code[0]);
- printk
- ("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
- p->psa_call_code[0], p->psa_call_code[1], p->psa_call_code[2],
- p->psa_call_code[3], p->psa_call_code[4], p->psa_call_code[5],
- p->psa_call_code[6], p->psa_call_code[7]);
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
- p->psa_reserved[0],
- p->psa_reserved[1], p->psa_reserved[2], p->psa_reserved[3]);
-#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
- printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
- printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
-} /* wv_psa_show */
-#endif /* DEBUG_PSA_SHOW */
-
-#ifdef DEBUG_MMC_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the Modem Management Controller.
- * This function needs to be completed.
- */
-static void wv_mmc_show(device * dev)
-{
- unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv;
- mmr_t m;
-
- /* Basic check */
- if (hasr_read(ioaddr) & HASR_NO_CLK) {
- printk(KERN_WARNING
- "%s: wv_mmc_show: modem not connected\n",
- dev->name);
- return;
- }
-
- /* Read the mmc */
- mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
- mmc_read(ioaddr, 0, (u8 *) & m, sizeof(m));
- mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
-
-#ifdef WIRELESS_EXT /* if wireless extension exists in the kernel */
- /* Don't forget to update statistics */
- lp->wstats.discard.nwid +=
- (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
-#endif /* WIRELESS_EXT */
-
- printk(KERN_DEBUG "##### WaveLAN modem status registers: #####\n");
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG
- "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
- m.mmr_unused0[0], m.mmr_unused0[1], m.mmr_unused0[2],
- m.mmr_unused0[3], m.mmr_unused0[4], m.mmr_unused0[5],
- m.mmr_unused0[6], m.mmr_unused0[7]);
-#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG "Encryption algorithm: %02X - Status: %02X\n",
- m.mmr_des_avail, m.mmr_des_status);
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
- m.mmr_unused1[0],
- m.mmr_unused1[1],
- m.mmr_unused1[2], m.mmr_unused1[3], m.mmr_unused1[4]);
-#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
- m.mmr_dce_status,
- (m.
- mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ?
- "energy detected," : "",
- (m.
- mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
- "loop test indicated," : "",
- (m.
- mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ?
- "transmitter on," : "",
- (m.
- mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
- "jabber timer expired," : "");
- printk(KERN_DEBUG "Dsp ID: %02X\n", m.mmr_dsp_id);
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
- m.mmr_unused2[0], m.mmr_unused2[1]);
-#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
- (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
- (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
- printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
- m.mmr_thr_pre_set & MMR_THR_PRE_SET,
- (m.
- mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" :
- "below");
- printk(KERN_DEBUG "signal_lvl: %d [%s], ",
- m.mmr_signal_lvl & MMR_SIGNAL_LVL,
- (m.
- mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" :
- "no new msg");
- printk("silence_lvl: %d [%s], ",
- m.mmr_silence_lvl & MMR_SILENCE_LVL,
- (m.
- mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" :
- "no new update");
- printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL,
- (m.
- mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" :
- "Antenna 0");
-#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
-#endif /* DEBUG_SHOW_UNUSED */
-} /* wv_mmc_show */
-#endif /* DEBUG_MMC_SHOW */
-
-#ifdef DEBUG_I82586_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the last block of the i82586 memory.
- */
-static void wv_scb_show(unsigned long ioaddr)
-{
- scb_t scb;
-
- obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
- sizeof(scb));
-
- printk(KERN_DEBUG "##### WaveLAN system control block: #####\n");
-
- printk(KERN_DEBUG "status: ");
- printk("stat 0x%x[%s%s%s%s] ",
- (scb.
- scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA |
- SCB_ST_RNR)) >> 12,
- (scb.
- scb_status & SCB_ST_CX) ? "command completion interrupt," :
- "", (scb.scb_status & SCB_ST_FR) ? "frame received," : "",
- (scb.
- scb_status & SCB_ST_CNA) ? "command unit not active," : "",
- (scb.
- scb_status & SCB_ST_RNR) ? "receiving unit not ready," :
- "");
- printk("cus 0x%x[%s%s%s] ", (scb.scb_status & SCB_ST_CUS) >> 8,
- ((scb.scb_status & SCB_ST_CUS) ==
- SCB_ST_CUS_IDLE) ? "idle" : "",
- ((scb.scb_status & SCB_ST_CUS) ==
- SCB_ST_CUS_SUSP) ? "suspended" : "",
- ((scb.scb_status & SCB_ST_CUS) ==
- SCB_ST_CUS_ACTV) ? "active" : "");
- printk("rus 0x%x[%s%s%s%s]\n", (scb.scb_status & SCB_ST_RUS) >> 4,
- ((scb.scb_status & SCB_ST_RUS) ==
- SCB_ST_RUS_IDLE) ? "idle" : "",
- ((scb.scb_status & SCB_ST_RUS) ==
- SCB_ST_RUS_SUSP) ? "suspended" : "",
- ((scb.scb_status & SCB_ST_RUS) ==
- SCB_ST_RUS_NRES) ? "no resources" : "",
- ((scb.scb_status & SCB_ST_RUS) ==
- SCB_ST_RUS_RDY) ? "ready" : "");
-
- printk(KERN_DEBUG "command: ");
- printk("ack 0x%x[%s%s%s%s] ",
- (scb.
- scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR |
- SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
- (scb.
- scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
- (scb.
- scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
- (scb.
- scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "",
- (scb.
- scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : "");
- printk("cuc 0x%x[%s%s%s%s%s] ",
- (scb.scb_command & SCB_CMD_CUC) >> 8,
- ((scb.scb_command & SCB_CMD_CUC) ==
- SCB_CMD_CUC_NOP) ? "nop" : "",
- ((scb.scb_command & SCB_CMD_CUC) ==
- SCB_CMD_CUC_GO) ? "start cbl_offset" : "",
- ((scb.scb_command & SCB_CMD_CUC) ==
- SCB_CMD_CUC_RES) ? "resume execution" : "",
- ((scb.scb_command & SCB_CMD_CUC) ==
- SCB_CMD_CUC_SUS) ? "suspend execution" : "",
- ((scb.scb_command & SCB_CMD_CUC) ==
- SCB_CMD_CUC_ABT) ? "abort execution" : "");
- printk("ruc 0x%x[%s%s%s%s%s]\n",
- (scb.scb_command & SCB_CMD_RUC) >> 4,
- ((scb.scb_command & SCB_CMD_RUC) ==
- SCB_CMD_RUC_NOP) ? "nop" : "",
- ((scb.scb_command & SCB_CMD_RUC) ==
- SCB_CMD_RUC_GO) ? "start rfa_offset" : "",
- ((scb.scb_command & SCB_CMD_RUC) ==
- SCB_CMD_RUC_RES) ? "resume reception" : "",
- ((scb.scb_command & SCB_CMD_RUC) ==
- SCB_CMD_RUC_SUS) ? "suspend reception" : "",
- ((scb.scb_command & SCB_CMD_RUC) ==
- SCB_CMD_RUC_ABT) ? "abort reception" : "");
-
- printk(KERN_DEBUG "cbl_offset 0x%x ", scb.scb_cbl_offset);
- printk("rfa_offset 0x%x\n", scb.scb_rfa_offset);
-
- printk(KERN_DEBUG "crcerrs %d ", scb.scb_crcerrs);
- printk("alnerrs %d ", scb.scb_alnerrs);
- printk("rscerrs %d ", scb.scb_rscerrs);
- printk("ovrnerrs %d\n", scb.scb_ovrnerrs);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the i82586's receive unit.
- */
-static void wv_ru_show(device * dev)
-{
- /* net_local *lp = (net_local *) dev->priv; */
-
- printk(KERN_DEBUG
- "##### WaveLAN i82586 receiver unit status: #####\n");
- printk(KERN_DEBUG "ru:");
- /*
- * Not implemented yet
- */
- printk("\n");
-} /* wv_ru_show */
-
-/*------------------------------------------------------------------*/
-/*
- * Display info about one control block of the i82586 memory.
- */
-static void wv_cu_show_one(device * dev, net_local * lp, int i, u16 p)
-{
- unsigned long ioaddr;
- ac_tx_t actx;
-
- ioaddr = dev->base_addr;
-
- printk("%d: 0x%x:", i, p);
-
- obram_read(ioaddr, p, (unsigned char *) &actx, sizeof(actx));
- printk(" status=0x%x,", actx.tx_h.ac_status);
- printk(" command=0x%x,", actx.tx_h.ac_command);
-
- /*
- {
- tbd_t tbd;
-
- obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd));
- printk(" tbd_status=0x%x,", tbd.tbd_status);
- }
- */
-
- printk("|");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Print status of the command unit of the i82586.
- */
-static void wv_cu_show(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned int i;
- u16 p;
-
- printk(KERN_DEBUG
- "##### WaveLAN i82586 command unit status: #####\n");
-
- printk(KERN_DEBUG);
- for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++) {
- wv_cu_show_one(dev, lp, i, p);
-
- p += TXBLOCKZ;
- if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
- p -= NTXBLOCKS * TXBLOCKZ;
- }
- printk("\n");
-}
-#endif /* DEBUG_I82586_SHOW */
-
-#ifdef DEBUG_DEVICE_SHOW
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the WaveLAN PCMCIA device driver.
- */
-static void wv_dev_show(device * dev)
-{
- printk(KERN_DEBUG "dev:");
- printk(" state=%lX,", dev->state);
- printk(" trans_start=%ld,", dev->trans_start);
- printk(" flags=0x%x,", dev->flags);
- printk("\n");
-} /* wv_dev_show */
-
-/*------------------------------------------------------------------*/
-/*
- * Print the formatted status of the WaveLAN PCMCIA device driver's
- * private information.
- */
-static void wv_local_show(device * dev)
-{
- net_local *lp;
-
- lp = (net_local *) dev->priv;
-
- printk(KERN_DEBUG "local:");
- printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
- printk(" hacr=0x%x,", lp->hacr);
- printk(" rx_head=0x%x,", lp->rx_head);
- printk(" rx_last=0x%x,", lp->rx_last);
- printk(" tx_first_free=0x%x,", lp->tx_first_free);
- printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use);
- printk("\n");
-} /* wv_local_show */
-#endif /* DEBUG_DEVICE_SHOW */
-
-#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO)
-/*------------------------------------------------------------------*/
-/*
- * Dump packet header (and content if necessary) on the screen
- */
-static inline void wv_packet_info(u8 * p, /* Packet to dump */
- int length, /* Length of the packet */
- char *msg1, /* Name of the device */
- char *msg2)
-{ /* Name of the function */
- int i;
- int maxi;
-
- printk(KERN_DEBUG
- "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n",
- msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length);
- printk(KERN_DEBUG
- "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n",
- msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12],
- p[13]);
-
-#ifdef DEBUG_PACKET_DUMP
-
- printk(KERN_DEBUG "data=\"");
-
- if ((maxi = length) > DEBUG_PACKET_DUMP)
- maxi = DEBUG_PACKET_DUMP;
- for (i = 14; i < maxi; i++)
- if (p[i] >= ' ' && p[i] <= '~')
- printk(" %c", p[i]);
- else
- printk("%02X", p[i]);
- if (maxi < length)
- printk("..");
- printk("\"\n");
- printk(KERN_DEBUG "\n");
-#endif /* DEBUG_PACKET_DUMP */
-}
-#endif /* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */
-
-/*------------------------------------------------------------------*/
-/*
- * This is the information which is displayed by the driver at startup.
- * There are lots of flags for configuring it to your liking.
- */
-static inline void wv_init_info(device * dev)
-{
- short ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv;
- psa_t psa;
- int i;
-
- /* Read the parameter storage area */
- psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
-
-#ifdef DEBUG_PSA_SHOW
- wv_psa_show(&psa);
-#endif
-#ifdef DEBUG_MMC_SHOW
- wv_mmc_show(dev);
-#endif
-#ifdef DEBUG_I82586_SHOW
- wv_cu_show(dev);
-#endif
-
-#ifdef DEBUG_BASIC_SHOW
- /* Now, let's go for the basic stuff. */
- printk(KERN_NOTICE "%s: WaveLAN at %#x,", dev->name, ioaddr);
- for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
- printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]);
- printk(", IRQ %d", dev->irq);
-
- /* Print current network ID. */
- if (psa.psa_nwid_select)
- printk(", nwid 0x%02X-%02X", psa.psa_nwid[0],
- psa.psa_nwid[1]);
- else
- printk(", nwid off");
-
- /* If 2.00 card */
- if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
- (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
- unsigned short freq;
-
- /* Ask the EEPROM to read the frequency from the first area. */
- fee_read(ioaddr, 0x00, &freq, 1);
-
- /* Print frequency */
- printk(", 2.00, %ld", (freq >> 6) + 2400L);
-
- /* Hack! */
- if (freq & 0x20)
- printk(".5");
- } else {
- printk(", PC");
- switch (psa.psa_comp_number) {
- case PSA_COMP_PC_AT_915:
- case PSA_COMP_PC_AT_2400:
- printk("-AT");
- break;
- case PSA_COMP_PC_MC_915:
- case PSA_COMP_PC_MC_2400:
- printk("-MC");
- break;
- case PSA_COMP_PCMCIA_915:
- printk("MCIA");
- break;
- default:
- printk("?");
- }
- printk(", ");
- switch (psa.psa_subband) {
- case PSA_SUBBAND_915:
- printk("915");
- break;
- case PSA_SUBBAND_2425:
- printk("2425");
- break;
- case PSA_SUBBAND_2460:
- printk("2460");
- break;
- case PSA_SUBBAND_2484:
- printk("2484");
- break;
- case PSA_SUBBAND_2430_5:
- printk("2430.5");
- break;
- default:
- printk("?");
- }
- }
-
- printk(" MHz\n");
-#endif /* DEBUG_BASIC_SHOW */
-
-#ifdef DEBUG_VERSION_SHOW
- /* Print version information */
- printk(KERN_NOTICE "%s", version);
-#endif
-} /* wv_init_info */
-
-/********************* IOCTL, STATS & RECONFIG *********************/
-/*
- * We found here routines that are called by Linux on different
- * occasions after the configuration and not for transmitting data
- * These may be called when the user use ifconfig, /proc/net/dev
- * or wireless extensions
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Get the current Ethernet statistics. This may be called with the
- * card open or closed.
- * Used when the user read /proc/net/dev
- */
-static en_stats *wavelan_get_stats(device * dev)
-{
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
-#endif
-
- return (&((net_local *) dev->priv)->stats);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Set or clear the multicast filter for this adaptor.
- * num_addrs == -1 Promiscuous mode, receive all packets
- * num_addrs == 0 Normal mode, clear multicast list
- * num_addrs > 0 Multicast mode, receive normal and MC packets,
- * and do best-effort filtering.
- */
-static void wavelan_set_multicast_list(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
-
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n",
- dev->name);
-#endif
-
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG
- "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n",
- dev->name, dev->flags, dev->mc_count);
-#endif
-
- /* Are we asking for promiscuous mode,
- * or all multicast addresses (we don't have that!)
- * or too many multicast addresses for the hardware filter? */
- if ((dev->flags & IFF_PROMISC) ||
- (dev->flags & IFF_ALLMULTI) ||
- (dev->mc_count > I82586_MAX_MULTICAST_ADDRESSES)) {
- /*
- * Enable promiscuous mode: receive all packets.
- */
- if (!lp->promiscuous) {
- lp->promiscuous = 1;
- lp->mc_count = 0;
-
- wv_82586_reconfig(dev);
-
- /* Tell the kernel that we are doing a really bad job. */
- dev->flags |= IFF_PROMISC;
- }
- } else
- /* Are there multicast addresses to send? */
- if (dev->mc_list != (struct dev_mc_list *) NULL) {
- /*
- * Disable promiscuous mode, but receive all packets
- * in multicast list
- */
-#ifdef MULTICAST_AVOID
- if (lp->promiscuous || (dev->mc_count != lp->mc_count))
-#endif
- {
- lp->promiscuous = 0;
- lp->mc_count = dev->mc_count;
-
- wv_82586_reconfig(dev);
- }
- } else {
- /*
- * Switch to normal mode: disable promiscuous mode and
- * clear the multicast list.
- */
- if (lp->promiscuous || lp->mc_count == 0) {
- lp->promiscuous = 0;
- lp->mc_count = 0;
-
- wv_82586_reconfig(dev);
- }
- }
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n",
- dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This function doesn't exist.
- * (Note : it was a nice way to test the reconfigure stuff...)
- */
-#ifdef SET_MAC_ADDRESS
-static int wavelan_set_mac_address(device * dev, void *addr)
-{
- struct sockaddr *mac = addr;
-
- /* Copy the address. */
- memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE);
-
- /* Reconfigure the beast. */
- wv_82586_reconfig(dev);
-
- return 0;
-}
-#endif /* SET_MAC_ADDRESS */
-
-#ifdef WIRELESS_EXT /* if wireless extensions exist in the kernel */
-
-/*------------------------------------------------------------------*/
-/*
- * Frequency setting (for hardware capable of it)
- * It's a bit complicated and you don't really want to look into it.
- * (called in wavelan_ioctl)
- */
-static inline int wv_set_frequency(unsigned long ioaddr, /* I/O port of the card */
- iw_freq * frequency)
-{
- const int BAND_NUM = 10; /* Number of bands */
- long freq = 0L; /* offset to 2.4 GHz in .5 MHz */
-#ifdef DEBUG_IOCTL_INFO
- int i;
-#endif
-
- /* Setting by frequency */
- /* Theoretically, you may set any frequency between
- * the two limits with a 0.5 MHz precision. In practice,
- * I don't want you to have trouble with local regulations.
- */
- if ((frequency->e == 1) &&
- (frequency->m >= (int) 2.412e8)
- && (frequency->m <= (int) 2.487e8)) {
- freq = ((frequency->m / 10000) - 24000L) / 5;
- }
-
- /* Setting by channel (same as wfreqsel) */
- /* Warning: each channel is 22 MHz wide, so some of the channels
- * will interfere. */
- if ((frequency->e == 0) && (frequency->m < BAND_NUM)) {
- /* Get frequency offset. */
- freq = channel_bands[frequency->m] >> 1;
- }
-
- /* Verify that the frequency is allowed. */
- if (freq != 0L) {
- u16 table[10]; /* Authorized frequency table */
-
- /* Read the frequency table. */
- fee_read(ioaddr, 0x71, table, 10);
-
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG "Frequency table: ");
- for (i = 0; i < 10; i++) {
- printk(" %04X", table[i]);
- }
- printk("\n");
-#endif
-
- /* Look in the table to see whether the frequency is allowed. */
- if (!(table[9 - ((freq - 24) / 16)] &
- (1 << ((freq - 24) % 16)))) return -EINVAL; /* not allowed */
- } else
- return -EINVAL;
-
- /* if we get a usable frequency */
- if (freq != 0L) {
- unsigned short area[16];
- unsigned short dac[2];
- unsigned short area_verify[16];
- unsigned short dac_verify[2];
- /* Corresponding gain (in the power adjust value table)
- * See AT&T WaveLAN Data Manual, REF 407-024689/E, page 3-8
- * and WCIN062D.DOC, page 6.2.9. */
- unsigned short power_limit[] = { 40, 80, 120, 160, 0 };
- int power_band = 0; /* Selected band */
- unsigned short power_adjust; /* Correct value */
-
- /* Search for the gain. */
- power_band = 0;
- while ((freq > power_limit[power_band]) &&
- (power_limit[++power_band] != 0));
-
- /* Read the first area. */
- fee_read(ioaddr, 0x00, area, 16);
-
- /* Read the DAC. */
- fee_read(ioaddr, 0x60, dac, 2);
-
- /* Read the new power adjust value. */
- fee_read(ioaddr, 0x6B - (power_band >> 1), &power_adjust,
- 1);
- if (power_band & 0x1)
- power_adjust >>= 8;
- else
- power_adjust &= 0xFF;
-
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG "WaveLAN EEPROM Area 1: ");
- for (i = 0; i < 16; i++) {
- printk(" %04X", area[i]);
- }
- printk("\n");
-
- printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n",
- dac[0], dac[1]);
-#endif
-
- /* Frequency offset (for info only) */
- area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
-
- /* Receiver Principle main divider coefficient */
- area[3] = (freq >> 1) + 2400L - 352L;
- area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
-
- /* Transmitter Main divider coefficient */
- area[13] = (freq >> 1) + 2400L;
- area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
-
- /* Other parts of the area are flags, bit streams or unused. */
-
- /* Set the value in the DAC. */
- dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
- dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
-
- /* Write the first area. */
- fee_write(ioaddr, 0x00, area, 16);
-
- /* Write the DAC. */
- fee_write(ioaddr, 0x60, dac, 2);
-
- /* We now should verify here that the writing of the EEPROM went OK. */
-
- /* Reread the first area. */
- fee_read(ioaddr, 0x00, area_verify, 16);
-
- /* Reread the DAC. */
- fee_read(ioaddr, 0x60, dac_verify, 2);
-
- /* Compare. */
- if (memcmp(area, area_verify, 16 * 2) ||
- memcmp(dac, dac_verify, 2 * 2)) {
-#ifdef DEBUG_IOCTL_ERROR
- printk(KERN_INFO
- "WaveLAN: wv_set_frequency: unable to write new frequency to EEPROM(?).\n");
-#endif
- return -EOPNOTSUPP;
- }
-
- /* We must download the frequency parameters to the
- * synthesizers (from the EEPROM - area 1)
- * Note: as the EEPROM is automatically decremented, we set the end
- * if the area... */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F);
- mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
- MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
-
- /* Wait until the download is finished. */
- fee_wait(ioaddr, 100, 100);
-
- /* We must now download the power adjust value (gain) to
- * the synthesizers (from the EEPROM - area 7 - DAC). */
- mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61);
- mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
- MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
-
- /* Wait for the download to finish. */
- fee_wait(ioaddr, 100, 100);
-
-#ifdef DEBUG_IOCTL_INFO
- /* Verification of what we have done */
-
- printk(KERN_DEBUG "WaveLAN EEPROM Area 1: ");
- for (i = 0; i < 16; i++) {
- printk(" %04X", area_verify[i]);
- }
- printk("\n");
-
- printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n",
- dac_verify[0], dac_verify[1]);
-#endif
-
- return 0;
- } else
- return -EINVAL; /* Bah, never get there... */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Give the list of available frequencies.
- */
-static inline int wv_frequency_list(unsigned long ioaddr, /* I/O port of the card */
- iw_freq * list, /* List of frequencies to fill */
- int max)
-{ /* Maximum number of frequencies */
- u16 table[10]; /* Authorized frequency table */
- long freq = 0L; /* offset to 2.4 GHz in .5 MHz + 12 MHz */
- int i; /* index in the table */
- int c = 0; /* Channel number */
-
- /* Read the frequency table. */
- fee_read(ioaddr, 0x71 /* frequency table */ , table, 10);
-
- /* Check all frequencies. */
- i = 0;
- for (freq = 0; freq < 150; freq++)
- /* Look in the table if the frequency is allowed */
- if (table[9 - (freq / 16)] & (1 << (freq % 16))) {
- /* Compute approximate channel number */
- while ((((channel_bands[c] >> 1) - 24) < freq) &&
- (c < NELS(channel_bands)))
- c++;
- list[i].i = c; /* Set the list index */
-
- /* put in the list */
- list[i].m = (((freq + 24) * 5) + 24000L) * 10000;
- list[i++].e = 1;
-
- /* Check number. */
- if (i >= max)
- return (i);
- }
-
- return (i);
-}
-
-#ifdef WIRELESS_SPY
-/*------------------------------------------------------------------*/
-/*
- * Gather wireless spy statistics: for each packet, compare the source
- * address with our list, and if they match, get the statistics.
- * Sorry, but this function really needs the wireless extensions.
- */
-static inline void wl_spy_gather(device * dev, u8 * mac, /* MAC address */
- u8 * stats)
-{ /* Statistics to gather */
- net_local *lp = (net_local *) dev->priv;
- int i;
-
- /* Check all addresses. */
- for (i = 0; i < lp->spy_number; i++)
- /* If match */
- if (!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE)) {
- /* Update statistics */
- lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL;
- lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL;
- lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL;
- lp->spy_stat[i].updated = 0x7;
- }
-}
-#endif /* WIRELESS_SPY */
-
-#ifdef HISTOGRAM
-/*------------------------------------------------------------------*/
-/*
- * This function calculates a histogram of the signal level.
- * As the noise is quite constant, it's like doing it on the SNR.
- * We have defined a set of interval (lp->his_range), and each time
- * the level goes in that interval, we increment the count (lp->his_sum).
- * With this histogram you may detect if one WaveLAN is really weak,
- * or you may also calculate the mean and standard deviation of the level.
- */
-static inline void wl_his_gather(device * dev, u8 * stats)
-{ /* Statistics to gather */
- net_local *lp = (net_local *) dev->priv;
- u8 level = stats[0] & MMR_SIGNAL_LVL;
- int i;
-
- /* Find the correct interval. */
- i = 0;
- while ((i < (lp->his_number - 1))
- && (level >= lp->his_range[i++]));
-
- /* Increment interval counter. */
- (lp->his_sum[i])++;
-}
-#endif /* HISTOGRAM */
-
-/*------------------------------------------------------------------*/
-/*
- * Perform ioctl for configuration and information.
- * It is here that the wireless extensions are treated (iwconfig).
- */
-static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is applied */
- struct ifreq *rq, /* data passed */
- int cmd)
-{ /* ioctl number */
- unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
- struct iwreq *wrq = (struct iwreq *) rq;
- psa_t psa;
- mm_t m;
- unsigned long flags;
- int ret = 0;
- int err = 0;
-
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name,
- cmd);
-#endif
-
- /* Disable interrupts and save flags. */
- wv_splhi(lp, &flags);
-
- /* Look what is the request */
- switch (cmd) {
- /* --------------- WIRELESS EXTENSIONS --------------- */
-
- case SIOCGIWNAME:
- strcpy(wrq->u.name, "WaveLAN");
- break;
-
- case SIOCSIWNWID:
- /* Set NWID in WaveLAN. */
- if (!wrq->u.nwid.disabled) {
- /* Set NWID in psa */
- psa.psa_nwid[0] =
- (wrq->u.nwid.value & 0xFF00) >> 8;
- psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF;
- psa.psa_nwid_select = 0x01;
- psa_write(ioaddr, lp->hacr,
- (char *) psa.psa_nwid - (char *) &psa,
- (unsigned char *) psa.psa_nwid, 3);
-
- /* Set NWID in mmc. */
- m.w.mmw_netw_id_l = psa.psa_nwid[1];
- m.w.mmw_netw_id_h = psa.psa_nwid[0];
- mmc_write(ioaddr,
- (char *) &m.w.mmw_netw_id_l -
- (char *) &m,
- (unsigned char *) &m.w.mmw_netw_id_l, 2);
- mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00);
- } else {
- /* Disable NWID in the psa. */
- psa.psa_nwid_select = 0x00;
- psa_write(ioaddr, lp->hacr,
- (char *) &psa.psa_nwid_select -
- (char *) &psa,
- (unsigned char *) &psa.psa_nwid_select,
- 1);
-
- /* Disable NWID in the mmc (no filtering). */
- mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel),
- MMW_LOOPT_SEL_DIS_NWID);
- }
- /* update the Wavelan checksum */
- update_psa_checksum(dev, ioaddr, lp->hacr);
- break;
-
- case SIOCGIWNWID:
- /* Read the NWID. */
- psa_read(ioaddr, lp->hacr,
- (char *) psa.psa_nwid - (char *) &psa,
- (unsigned char *) psa.psa_nwid, 3);
- wrq->u.nwid.value =
- (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
- wrq->u.nwid.disabled = !(psa.psa_nwid_select);
- wrq->u.nwid.fixed = 1; /* Superfluous */
- break;
-
- case SIOCSIWFREQ:
- /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */
- if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
- (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
- ret = wv_set_frequency(ioaddr, &(wrq->u.freq));
- else
- ret = -EOPNOTSUPP;
- break;
-
- case SIOCGIWFREQ:
- /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable).
- * Does it work for everybody, especially old cards? */
- if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
- (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
- unsigned short freq;
-
- /* Ask the EEPROM to read the frequency from the first area. */
- fee_read(ioaddr, 0x00, &freq, 1);
- wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000;
- wrq->u.freq.e = 1;
- } else {
- psa_read(ioaddr, lp->hacr,
- (char *) &psa.psa_subband - (char *) &psa,
- (unsigned char *) &psa.psa_subband, 1);
-
- if (psa.psa_subband <= 4) {
- wrq->u.freq.m =
- fixed_bands[psa.psa_subband];
- wrq->u.freq.e = (psa.psa_subband != 0);
- } else
- ret = -EOPNOTSUPP;
- }
- break;
-
- case SIOCSIWSENS:
- /* Set the level threshold. */
- /* We should complain loudly if wrq->u.sens.fixed = 0, because we
- * can't set auto mode... */
- psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F;
- psa_write(ioaddr, lp->hacr,
- (char *) &psa.psa_thr_pre_set - (char *) &psa,
- (unsigned char *) &psa.psa_thr_pre_set, 1);
- /* update the Wavelan checksum */
- update_psa_checksum(dev, ioaddr, lp->hacr);
- mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set),
- psa.psa_thr_pre_set);
- break;
-
- case SIOCGIWSENS:
- /* Read the level threshold. */
- psa_read(ioaddr, lp->hacr,
- (char *) &psa.psa_thr_pre_set - (char *) &psa,
- (unsigned char *) &psa.psa_thr_pre_set, 1);
- wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F;
- wrq->u.sens.fixed = 1;
- break;
-
- case SIOCSIWENCODE:
- /* Set encryption key */
- if (!mmc_encr(ioaddr)) {
- ret = -EOPNOTSUPP;
- break;
- }
-
- /* Basic checking... */
- if (wrq->u.encoding.pointer != (caddr_t) 0) {
- /* Check the size of the key */
- if (wrq->u.encoding.length != 8) {
- ret = -EINVAL;
- break;
- }
-
- /* Copy the key in the driver */
- wv_splx(lp, &flags);
- err = copy_from_user(psa.psa_encryption_key,
- wrq->u.encoding.pointer,
- wrq->u.encoding.length);
- wv_splhi(lp, &flags);
- if (err) {
- ret = -EFAULT;
- break;
- }
-
- psa.psa_encryption_select = 1;
- psa_write(ioaddr, lp->hacr,
- (char *) &psa.psa_encryption_select -
- (char *) &psa,
- (unsigned char *) &psa.
- psa_encryption_select, 8 + 1);
-
- mmc_out(ioaddr, mmwoff(0, mmw_encr_enable),
- MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
- mmc_write(ioaddr, mmwoff(0, mmw_encr_key),
- (unsigned char *) &psa.
- psa_encryption_key, 8);
- }
-
- if (wrq->u.encoding.flags & IW_ENCODE_DISABLED) { /* disable encryption */
- psa.psa_encryption_select = 0;
- psa_write(ioaddr, lp->hacr,
- (char *) &psa.psa_encryption_select -
- (char *) &psa,
- (unsigned char *) &psa.
- psa_encryption_select, 1);
-
- mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
- }
- /* update the Wavelan checksum */
- update_psa_checksum(dev, ioaddr, lp->hacr);
- break;
-
- case SIOCGIWENCODE:
- /* Read the encryption key */
- if (!mmc_encr(ioaddr)) {
- ret = -EOPNOTSUPP;
- break;
- }
-
- /* only super-user can see encryption key */
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- break;
- }
-
- /* Basic checking... */
- if (wrq->u.encoding.pointer != (caddr_t) 0) {
- /* Verify the user buffer */
- ret =
- verify_area(VERIFY_WRITE,
- wrq->u.encoding.pointer, 8);
- if (ret)
- break;
-
- psa_read(ioaddr, lp->hacr,
- (char *) &psa.psa_encryption_select -
- (char *) &psa,
- (unsigned char *) &psa.
- psa_encryption_select, 1 + 8);
-
- /* encryption is enabled ? */
- if (psa.psa_encryption_select)
- wrq->u.encoding.flags = IW_ENCODE_ENABLED;
- else
- wrq->u.encoding.flags = IW_ENCODE_DISABLED;
- wrq->u.encoding.flags |= mmc_encr(ioaddr);
-
- /* Copy the key to the user buffer */
- wrq->u.encoding.length = 8;
- wv_splx(lp, &flags);
- if (copy_to_user(wrq->u.encoding.pointer,
- psa.psa_encryption_key, 8))
- ret = -EFAULT;
- wv_splhi(lp, &flags);
- }
- break;
-
- case SIOCGIWRANGE:
- /* basic checking */
- if (wrq->u.data.pointer != (caddr_t) 0) {
- struct iw_range range;
-
- /* Set the length (very important for backward
- * compatibility) */
- wrq->u.data.length = sizeof(struct iw_range);
-
- /* Set all the info we don't care or don't know
- * about to zero */
- memset(&range, 0, sizeof(range));
-
- /* Set the Wireless Extension versions */
- range.we_version_compiled = WIRELESS_EXT;
- range.we_version_source = 9;
-
- /* Set information in the range struct. */
- range.throughput = 1.6 * 1000 * 1000; /* don't argue on this ! */
- range.min_nwid = 0x0000;
- range.max_nwid = 0xFFFF;
-
- /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */
- if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
- (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
- range.num_channels = 10;
- range.num_frequency =
- wv_frequency_list(ioaddr, range.freq,
- IW_MAX_FREQUENCIES);
- } else
- range.num_channels = range.num_frequency =
- 0;
-
- range.sensitivity = 0x3F;
- range.max_qual.qual = MMR_SGNL_QUAL;
- range.max_qual.level = MMR_SIGNAL_LVL;
- range.max_qual.noise = MMR_SILENCE_LVL;
- range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */
- /* Need to get better values for those two */
- range.avg_qual.level = 30;
- range.avg_qual.noise = 8;
-
- range.num_bitrates = 1;
- range.bitrate[0] = 2000000; /* 2 Mb/s */
-
- /* Encryption supported ? */
- if (mmc_encr(ioaddr)) {
- range.encoding_size[0] = 8; /* DES = 64 bits key */
- range.num_encoding_sizes = 1;
- range.max_encoding_tokens = 1; /* Only one key possible */
- } else {
- range.num_encoding_sizes = 0;
- range.max_encoding_tokens = 0;
- }
-
- /* Copy structure to the user buffer. */
- wv_splx(lp, &flags);
- if (copy_to_user(wrq->u.data.pointer,
- &range,
- sizeof(struct iw_range)))
- ret = -EFAULT;
- wv_splhi(lp, &flags);
- }
- break;
-
- case SIOCGIWPRIV:
- /* Basic checking */
- if (wrq->u.data.pointer != (caddr_t) 0) {
- struct iw_priv_args priv[] = {
- /* { cmd,
- set_args,
- get_args,
- name } */
- { SIOCSIPQTHR,
- IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
- 0,
- "setqualthr" },
- { SIOCGIPQTHR,
- 0,
- IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
- "getqualthr" },
- { SIOCSIPHISTO,
- IW_PRIV_TYPE_BYTE | 16,
- 0,
- "sethisto" },
- { SIOCGIPHISTO,
- 0,
- IW_PRIV_TYPE_INT | 16,
- "gethisto" },
- };
-
- /* Set the number of available ioctls. */
- wrq->u.data.length = 4;
-
- /* Copy structure to the user buffer. */
- wv_splx(lp, &flags);
- if (copy_to_user(wrq->u.data.pointer,
- (u8 *) priv,
- sizeof(priv)))
- ret = -EFAULT;
- wv_splhi(lp, &flags);
- }
- break;
-
-#ifdef WIRELESS_SPY
- case SIOCSIWSPY:
- /* Set the spy list */
-
- /* Check the number of addresses. */
- if (wrq->u.data.length > IW_MAX_SPY) {
- ret = -E2BIG;
- break;
- }
- lp->spy_number = wrq->u.data.length;
-
- /* Are there are addresses to copy? */
- if (lp->spy_number > 0) {
- struct sockaddr address[IW_MAX_SPY];
- int i;
-
- /* Copy addresses to the driver. */
- wv_splx(lp, &flags);
- err = copy_from_user(address,
- wrq->u.data.pointer,
- sizeof(struct sockaddr)
- * lp->spy_number);
- wv_splhi(lp, &flags);
- if (err) {
- ret = -EFAULT;
- break;
- }
-
- /* Copy addresses to the lp structure. */
- for (i = 0; i < lp->spy_number; i++) {
- memcpy(lp->spy_address[i],
- address[i].sa_data,
- WAVELAN_ADDR_SIZE);
- }
-
- /* Reset structure. */
- memset(lp->spy_stat, 0x00,
- sizeof(iw_qual) * IW_MAX_SPY);
-
-#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG
- "SetSpy: set of new addresses is: \n");
- for (i = 0; i < wrq->u.data.length; i++)
- printk(KERN_DEBUG
- "%02X:%02X:%02X:%02X:%02X:%02X \n",
- lp->spy_address[i][0],
- lp->spy_address[i][1],
- lp->spy_address[i][2],
- lp->spy_address[i][3],
- lp->spy_address[i][4],
- lp->spy_address[i][5]);
-#endif /* DEBUG_IOCTL_INFO */
- }
-
- break;
-
- case SIOCGIWSPY:
- /* Get the spy list and spy stats. */
-
- /* Set the number of addresses */
- wrq->u.data.length = lp->spy_number;
-
- /* Does the user want to have the addresses back? */
- if ((lp->spy_number > 0)
- && (wrq->u.data.pointer != (caddr_t) 0)) {
- struct sockaddr address[IW_MAX_SPY];
- int i;
-
- /* Copy addresses from the lp structure. */
- for (i = 0; i < lp->spy_number; i++) {
- memcpy(address[i].sa_data,
- lp->spy_address[i],
- WAVELAN_ADDR_SIZE);
- address[i].sa_family = AF_UNIX;
- }
-
- /* Copy addresses to the user buffer. */
- wv_splx(lp, &flags);
- err = copy_to_user(wrq->u.data.pointer,
- address,
- sizeof(struct sockaddr)
- * lp->spy_number);
-
- /* Copy stats to the user buffer (just after). */
- err |= copy_to_user(wrq->u.data.pointer
- + (sizeof(struct sockaddr)
- * lp->spy_number),
- lp->spy_stat,
- sizeof(iw_qual) * lp->spy_number);
- wv_splhi(lp, &flags);
- if (err) {
- ret = -EFAULT;
- break;
- }
-
- /* Reset updated flags. */
- for (i = 0; i < lp->spy_number; i++)
- lp->spy_stat[i].updated = 0x0;
- }
- /* if(pointer != NULL) */
- break;
-#endif /* WIRELESS_SPY */
-
- /* ------------------ PRIVATE IOCTL ------------------ */
-
- case SIOCSIPQTHR:
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- break;
- }
- psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
- psa_write(ioaddr, lp->hacr,
- (char *) &psa.psa_quality_thr - (char *) &psa,
- (unsigned char *) &psa.psa_quality_thr, 1);
- /* update the Wavelan checksum */
- update_psa_checksum(dev, ioaddr, lp->hacr);
- mmc_out(ioaddr, mmwoff(0, mmw_quality_thr),
- psa.psa_quality_thr);
- break;
-
- case SIOCGIPQTHR:
- psa_read(ioaddr, lp->hacr,
- (char *) &psa.psa_quality_thr - (char *) &psa,
- (unsigned char *) &psa.psa_quality_thr, 1);
- *(wrq->u.name) = psa.psa_quality_thr & 0x0F;
- break;
-
-#ifdef HISTOGRAM
- case SIOCSIPHISTO:
- /* Verify that the user is root. */
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- break;
- }
-
- /* Check the number of intervals. */
- if (wrq->u.data.length > 16) {
- ret = -E2BIG;
- break;
- }
- lp->his_number = wrq->u.data.length;
-
- /* Are there addresses to copy? */
- if (lp->his_number > 0) {
- /* Copy interval ranges to the driver */
- wv_splx(lp, &flags);
- err = copy_from_user(lp->his_range,
- wrq->u.data.pointer,
- sizeof(char) * lp->his_number);
- wv_splhi(lp, &flags);
- if (err) {
- ret = -EFAULT;
- break;
- }
-
- /* Reset structure. */
- memset(lp->his_sum, 0x00, sizeof(long) * 16);
- }
- break;
-
- case SIOCGIPHISTO:
- /* Set the number of intervals. */
- wrq->u.data.length = lp->his_number;
-
- /* Give back the distribution statistics */
- if ((lp->his_number > 0)
- && (wrq->u.data.pointer != (caddr_t) 0)) {
- /* Copy data to the user buffer. */
- wv_splx(lp, &flags);
- if (copy_to_user(wrq->u.data.pointer,
- lp->his_sum,
- sizeof(long) * lp->his_number);
- ret = -EFAULT;
- wv_splhi(lp, &flags);
-
- } /* if(pointer != NULL) */
- break;
-#endif /* HISTOGRAM */
-
- /* ------------------- OTHER IOCTL ------------------- */
-
- default:
- ret = -EOPNOTSUPP;
- } /* switch (cmd) */
-
- /* Enable interrupts and restore flags. */
- wv_splx(lp, &flags);
-
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
-#endif
- return ret;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Get wireless statistics.
- * Called by /proc/net/wireless
- */
-static iw_stats *wavelan_get_wireless_stats(device * dev)
-{
- unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv;
- mmr_t m;
- iw_stats *wstats;
- unsigned long flags;
-
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n",
- dev->name);
-#endif
-
- /* Check */
- if (lp == (net_local *) NULL)
- return (iw_stats *) NULL;
-
- /* Disable interrupts and save flags. */
- wv_splhi(lp, &flags);
-
- wstats = &lp->wstats;
-
- /* Get data from the mmc. */
- mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
-
- mmc_read(ioaddr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1);
- mmc_read(ioaddr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l,
- 2);
- mmc_read(ioaddr, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set,
- 4);
-
- mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
-
- /* Copy data to wireless stuff. */
- wstats->status = m.mmr_dce_status & MMR_DCE_STATUS;
- wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
- wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
- wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
- wstats->qual.updated = (((m. mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7)
- | ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6)
- | ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5));
- wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
- wstats->discard.code = 0L;
- wstats->discard.misc = 0L;
-
- /* Enable interrupts and restore flags. */
- wv_splx(lp, &flags);
-
-#ifdef DEBUG_IOCTL_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n",
- dev->name);
-#endif
- return &lp->wstats;
-}
-#endif /* WIRELESS_EXT */
-
-/************************* PACKET RECEPTION *************************/
-/*
- * This part deals with receiving the packets.
- * The interrupt handler gets an interrupt when a packet has been
- * successfully received and calls this part.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does the actual copying of data (including the Ethernet
- * header structure) from the WaveLAN card to an sk_buff chain that
- * will be passed up to the network interface layer. NOTE: we
- * currently don't handle trailer protocols (neither does the rest of
- * the network interface), so if that is needed, it will (at least in
- * part) be added here. The contents of the receive ring buffer are
- * copied to a message chain that is then passed to the kernel.
- *
- * Note: if any errors occur, the packet is "dropped on the floor".
- * (called by wv_packet_rcv())
- */
-static inline void
-wv_packet_read(device * dev, u16 buf_off, int sksize)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
- struct sk_buff *skb;
-
-#ifdef DEBUG_RX_TRACE
- printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n",
- dev->name, buf_off, sksize);
-#endif
-
- /* Allocate buffer for the data */
- if ((skb = dev_alloc_skb(sksize)) == (struct sk_buff *) NULL) {
-#ifdef DEBUG_RX_ERROR
- printk(KERN_INFO
- "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC).\n",
- dev->name, sksize);
-#endif
- lp->stats.rx_dropped++;
- return;
- }
-
- skb->dev = dev;
-
- /* Copy the packet to the buffer. */
- obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize);
- skb->protocol = eth_type_trans(skb, dev);
-
-#ifdef DEBUG_RX_INFO
- wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
-#endif /* DEBUG_RX_INFO */
-
- /* Statistics-gathering and associated stuff.
- * It seem a bit messy with all the define, but it's really simple... */
-#if defined(WIRELESS_SPY) || defined(HISTOGRAM)
- if (
-#ifdef WIRELESS_SPY
- (lp->spy_number > 0) ||
-#endif /* WIRELESS_SPY */
-#ifdef HISTOGRAM
- (lp->his_number > 0) ||
-#endif /* HISTOGRAM */
- 0) {
- u8 stats[3]; /* signal level, noise level, signal quality */
-
- /* Read signal level, silence level and signal quality bytes. */
- /* Note: in the PCMCIA hardware, these are part of the frame. It seems
- * that for the ISA hardware, it's nowhere to be found in the frame,
- * so I'm obliged to do this (it has a side effect on /proc/net/wireless).
- * Any ideas?
- */
- mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
- mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3);
- mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
-
-#ifdef DEBUG_RX_INFO
- printk(KERN_DEBUG
- "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n",
- dev->name, stats[0] & 0x3F, stats[1] & 0x3F,
- stats[2] & 0x0F);
-#endif
-
- /* Spying stuff */
-#ifdef WIRELESS_SPY
- wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE,
- stats);
-#endif /* WIRELESS_SPY */
-#ifdef HISTOGRAM
- wl_his_gather(dev, stats);
-#endif /* HISTOGRAM */
- }
-#endif /* defined(WIRELESS_SPY) || defined(HISTOGRAM) */
-
- /*
- * Hand the packet to the network module.
- */
- netif_rx(skb);
-
- /* Keep statistics up to date */
- dev->last_rx = jiffies;
- lp->stats.rx_packets++;
- lp->stats.rx_bytes += sksize;
-
-#ifdef DEBUG_RX_TRACE
- printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Transfer as many packets as we can
- * from the device RAM.
- * (called in wavelan_interrupt()).
- * Note : the spinlock is already grabbed for us.
- */
-static inline void wv_receive(device * dev)
-{
- unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv;
- fd_t fd;
- rbd_t rbd;
- int nreaped = 0;
-
-#ifdef DEBUG_RX_TRACE
- printk(KERN_DEBUG "%s: ->wv_receive()\n", dev->name);
-#endif
-
- /* Loop on each received packet. */
- for (;;) {
- obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd,
- sizeof(fd));
-
- /* Note about the status :
- * It start up to be 0 (the value we set). Then, when the RU
- * grab the buffer to prepare for reception, it sets the
- * FD_STATUS_B flag. When the RU has finished receiving the
- * frame, it clears FD_STATUS_B, set FD_STATUS_C to indicate
- * completion and set the other flags to indicate the eventual
- * errors. FD_STATUS_OK indicates that the reception was OK.
- */
-
- /* If the current frame is not complete, we have reached the end. */
- if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C)
- break; /* This is how we exit the loop. */
-
- nreaped++;
-
- /* Check whether frame was correctly received. */
- if ((fd.fd_status & FD_STATUS_OK) == FD_STATUS_OK) {
- /* Does the frame contain a pointer to the data? Let's check. */
- if (fd.fd_rbd_offset != I82586NULL) {
- /* Read the receive buffer descriptor */
- obram_read(ioaddr, fd.fd_rbd_offset,
- (unsigned char *) &rbd,
- sizeof(rbd));
-
-#ifdef DEBUG_RX_ERROR
- if ((rbd.rbd_status & RBD_STATUS_EOF) !=
- RBD_STATUS_EOF) printk(KERN_INFO
- "%s: wv_receive(): missing EOF flag.\n",
- dev->name);
-
- if ((rbd.rbd_status & RBD_STATUS_F) !=
- RBD_STATUS_F) printk(KERN_INFO
- "%s: wv_receive(): missing F flag.\n",
- dev->name);
-#endif /* DEBUG_RX_ERROR */
-
- /* Read the packet and transmit to Linux */
- wv_packet_read(dev, rbd.rbd_bufl,
- rbd.
- rbd_status &
- RBD_STATUS_ACNT);
- }
-#ifdef DEBUG_RX_ERROR
- else /* if frame has no data */
- printk(KERN_INFO
- "%s: wv_receive(): frame has no data.\n",
- dev->name);
-#endif
- } else { /* If reception was no successful */
-
- lp->stats.rx_errors++;
-
-#ifdef DEBUG_RX_INFO
- printk(KERN_DEBUG
- "%s: wv_receive(): frame not received successfully (%X).\n",
- dev->name, fd.fd_status);
-#endif
-
-#ifdef DEBUG_RX_ERROR
- if ((fd.fd_status & FD_STATUS_S6) != 0)
- printk(KERN_INFO
- "%s: wv_receive(): no EOF flag.\n",
- dev->name);
-#endif
-
- if ((fd.fd_status & FD_STATUS_S7) != 0) {
- lp->stats.rx_length_errors++;
-#ifdef DEBUG_RX_FAIL
- printk(KERN_DEBUG
- "%s: wv_receive(): frame too short.\n",
- dev->name);
-#endif
- }
-
- if ((fd.fd_status & FD_STATUS_S8) != 0) {
- lp->stats.rx_over_errors++;
-#ifdef DEBUG_RX_FAIL
- printk(KERN_DEBUG
- "%s: wv_receive(): rx DMA overrun.\n",
- dev->name);
-#endif
- }
-
- if ((fd.fd_status & FD_STATUS_S9) != 0) {
- lp->stats.rx_fifo_errors++;
-#ifdef DEBUG_RX_FAIL
- printk(KERN_DEBUG
- "%s: wv_receive(): ran out of resources.\n",
- dev->name);
-#endif
- }
-
- if ((fd.fd_status & FD_STATUS_S10) != 0) {
- lp->stats.rx_frame_errors++;
-#ifdef DEBUG_RX_FAIL
- printk(KERN_DEBUG
- "%s: wv_receive(): alignment error.\n",
- dev->name);
-#endif
- }
-
- if ((fd.fd_status & FD_STATUS_S11) != 0) {
- lp->stats.rx_crc_errors++;
-#ifdef DEBUG_RX_FAIL
- printk(KERN_DEBUG
- "%s: wv_receive(): CRC error.\n",
- dev->name);
-#endif
- }
- }
-
- fd.fd_status = 0;
- obram_write(ioaddr, fdoff(lp->rx_head, fd_status),
- (unsigned char *) &fd.fd_status,
- sizeof(fd.fd_status));
-
- fd.fd_command = FD_COMMAND_EL;
- obram_write(ioaddr, fdoff(lp->rx_head, fd_command),
- (unsigned char *) &fd.fd_command,
- sizeof(fd.fd_command));
-
- fd.fd_command = 0;
- obram_write(ioaddr, fdoff(lp->rx_last, fd_command),
- (unsigned char *) &fd.fd_command,
- sizeof(fd.fd_command));
-
- lp->rx_last = lp->rx_head;
- lp->rx_head = fd.fd_link_offset;
- } /* for(;;) -> loop on all frames */
-
-#ifdef DEBUG_RX_INFO
- if (nreaped > 1)
- printk(KERN_DEBUG "%s: wv_receive(): reaped %d\n",
- dev->name, nreaped);
-#endif
-#ifdef DEBUG_RX_TRACE
- printk(KERN_DEBUG "%s: <-wv_receive()\n", dev->name);
-#endif
-}
-
-/*********************** PACKET TRANSMISSION ***********************/
-/*
- * This part deals with sending packets through the WaveLAN.
- *
- */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine fills in the appropriate registers and memory
- * locations on the WaveLAN card and starts the card off on
- * the transmit.
- *
- * The principle:
- * Each block contains a transmit command, a NOP command,
- * a transmit block descriptor and a buffer.
- * The CU read the transmit block which point to the tbd,
- * read the tbd and the content of the buffer.
- * When it has finish with it, it goes to the next command
- * which in our case is the NOP. The NOP points on itself,
- * so the CU stop here.
- * When we add the next block, we modify the previous nop
- * to make it point on the new tx command.
- * Simple, isn't it ?
- *
- * (called in wavelan_packet_xmit())
- */
-static inline int wv_packet_write(device * dev, void *buf, short length)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
- unsigned short txblock;
- unsigned short txpred;
- unsigned short tx_addr;
- unsigned short nop_addr;
- unsigned short tbd_addr;
- unsigned short buf_addr;
- ac_tx_t tx;
- ac_nop_t nop;
- tbd_t tbd;
- int clen = length;
- unsigned long flags;
-
-#ifdef DEBUG_TX_TRACE
- printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name,
- length);
-#endif
-
- /* Do we need some padding? */
- if (clen < ETH_ZLEN)
- clen = ETH_ZLEN;
-
- wv_splhi(lp, &flags);
-
- /* Check nothing bad has happened */
- if (lp->tx_n_in_use == (NTXBLOCKS - 1)) {
-#ifdef DEBUG_TX_ERROR
- printk(KERN_INFO "%s: wv_packet_write(): Tx queue full.\n",
- dev->name);
-#endif
- wv_splx(lp, &flags);
- return 1;
- }
-
- /* Calculate addresses of next block and previous block. */
- txblock = lp->tx_first_free;
- txpred = txblock - TXBLOCKZ;
- if (txpred < OFFSET_CU)
- txpred += NTXBLOCKS * TXBLOCKZ;
- lp->tx_first_free += TXBLOCKZ;
- if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
- lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
-
- lp->tx_n_in_use++;
-
- /* Calculate addresses of the different parts of the block. */
- tx_addr = txblock;
- nop_addr = tx_addr + sizeof(tx);
- tbd_addr = nop_addr + sizeof(nop);
- buf_addr = tbd_addr + sizeof(tbd);
-
- /*
- * Transmit command
- */
- tx.tx_h.ac_status = 0;
- obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
- (unsigned char *) &tx.tx_h.ac_status,
- sizeof(tx.tx_h.ac_status));
-
- /*
- * NOP command
- */
- nop.nop_h.ac_status = 0;
- obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
- (unsigned char *) &nop.nop_h.ac_status,
- sizeof(nop.nop_h.ac_status));
- nop.nop_h.ac_link = nop_addr;
- obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
- (unsigned char *) &nop.nop_h.ac_link,
- sizeof(nop.nop_h.ac_link));
-
- /*
- * Transmit buffer descriptor
- */
- tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen);
- tbd.tbd_next_bd_offset = I82586NULL;
- tbd.tbd_bufl = buf_addr;
- tbd.tbd_bufh = 0;
- obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, sizeof(tbd));
-
- /*
- * Data
- */
- obram_write(ioaddr, buf_addr, buf, length);
-
- /*
- * Overwrite the predecessor NOP link
- * so that it points to this txblock.
- */
- nop_addr = txpred + sizeof(tx);
- nop.nop_h.ac_status = 0;
- obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
- (unsigned char *) &nop.nop_h.ac_status,
- sizeof(nop.nop_h.ac_status));
- nop.nop_h.ac_link = txblock;
- obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
- (unsigned char *) &nop.nop_h.ac_link,
- sizeof(nop.nop_h.ac_link));
-
- /* Keep stats up to date. */
- lp->stats.tx_bytes += length;
-
- if (lp->tx_first_in_use == I82586NULL)
- lp->tx_first_in_use = txblock;
-
- if (lp->tx_n_in_use < NTXBLOCKS - 1)
- netif_wake_queue(dev);
-
- wv_splx(lp, &flags);
-
-#ifdef DEBUG_TX_INFO
- wv_packet_info((u8 *) buf, length, dev->name,
- "wv_packet_write");
-#endif /* DEBUG_TX_INFO */
-
-#ifdef DEBUG_TX_TRACE
- printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
-#endif
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine is called when we want to send a packet (NET3 callback)
- * In this routine, we check if the harware is ready to accept
- * the packet. We also prevent reentrance. Then we call the function
- * to send the packet.
- */
-static int wavelan_packet_xmit(struct sk_buff *skb, device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long flags;
-
-#ifdef DEBUG_TX_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
- (unsigned) skb);
-#endif
-
- /*
- * Block a timer-based transmit from overlapping.
- * In other words, prevent reentering this routine.
- */
- netif_stop_queue(dev);
-
- /* If somebody has asked to reconfigure the controller,
- * we can do it now.
- */
- if (lp->reconfig_82586) {
- wv_splhi(lp, &flags);
- wv_82586_config(dev);
- wv_splx(lp, &flags);
- /* Check that we can continue */
- if (lp->tx_n_in_use == (NTXBLOCKS - 1))
- return 1;
- }
-#ifdef DEBUG_TX_ERROR
- if (skb->next)
- printk(KERN_INFO "skb has next\n");
-#endif
-
- /* Write packet on the card */
- if(wv_packet_write(dev, skb->data, skb->len))
- return 1; /* We failed */
-
- dev_kfree_skb(skb);
-
-#ifdef DEBUG_TX_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
-#endif
- return 0;
-}
-
-/*********************** HARDWARE CONFIGURATION ***********************/
-/*
- * This part does the real job of starting and configuring the hardware.
- */
-
-/*--------------------------------------------------------------------*/
-/*
- * Routine to initialize the Modem Management Controller.
- * (called by wv_hw_reset())
- */
-static inline int wv_mmc_init(device * dev)
-{
- unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv;
- psa_t psa;
- mmw_t m;
- int configured;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name);
-#endif
-
- /* Read the parameter storage area. */
- psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
-
-#ifdef USE_PSA_CONFIG
- configured = psa.psa_conf_status & 1;
-#else
- configured = 0;
-#endif
-
- /* Is the PSA is not configured */
- if (!configured) {
- /* User will be able to configure NWID later (with iwconfig). */
- psa.psa_nwid[0] = 0;
- psa.psa_nwid[1] = 0;
-
- /* no NWID checking since NWID is not set */
- psa.psa_nwid_select = 0;
-
- /* Disable encryption */
- psa.psa_encryption_select = 0;
-
- /* Set to standard values:
- * 0x04 for AT,
- * 0x01 for MCA,
- * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document)
- */
- if (psa.psa_comp_number & 1)
- psa.psa_thr_pre_set = 0x01;
- else
- psa.psa_thr_pre_set = 0x04;
- psa.psa_quality_thr = 0x03;
-
- /* It is configured */
- psa.psa_conf_status |= 1;
-
-#ifdef USE_PSA_CONFIG
- /* Write the psa. */
- psa_write(ioaddr, lp->hacr,
- (char *) psa.psa_nwid - (char *) &psa,
- (unsigned char *) psa.psa_nwid, 4);
- psa_write(ioaddr, lp->hacr,
- (char *) &psa.psa_thr_pre_set - (char *) &psa,
- (unsigned char *) &psa.psa_thr_pre_set, 1);
- psa_write(ioaddr, lp->hacr,
- (char *) &psa.psa_quality_thr - (char *) &psa,
- (unsigned char *) &psa.psa_quality_thr, 1);
- psa_write(ioaddr, lp->hacr,
- (char *) &psa.psa_conf_status - (char *) &psa,
- (unsigned char *) &psa.psa_conf_status, 1);
- /* update the Wavelan checksum */
- update_psa_checksum(dev, ioaddr, lp->hacr);
-#endif
- }
-
- /* Zero the mmc structure. */
- memset(&m, 0x00, sizeof(m));
-
- /* Copy PSA info to the mmc. */
- m.mmw_netw_id_l = psa.psa_nwid[1];
- m.mmw_netw_id_h = psa.psa_nwid[0];
-
- if (psa.psa_nwid_select & 1)
- m.mmw_loopt_sel = 0x00;
- else
- m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
-
- memcpy(&m.mmw_encr_key, &psa.psa_encryption_key,
- sizeof(m.mmw_encr_key));
-
- if (psa.psa_encryption_select)
- m.mmw_encr_enable =
- MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE;
- else
- m.mmw_encr_enable = 0;
-
- m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
- m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
-
- /*
- * Set default modem control parameters.
- * See NCR document 407-0024326 Rev. A.
- */
- m.mmw_jabber_enable = 0x01;
- m.mmw_freeze = 0;
- m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
- m.mmw_ifs = 0x20;
- m.mmw_mod_delay = 0x04;
- m.mmw_jam_time = 0x38;
-
- m.mmw_des_io_invert = 0;
- m.mmw_decay_prm = 0;
- m.mmw_decay_updat_prm = 0;
-
- /* Write all info to MMC. */
- mmc_write(ioaddr, 0, (u8 *) & m, sizeof(m));
-
- /* The following code starts the modem of the 2.00 frequency
- * selectable cards at power on. It's not strictly needed for the
- * following boots.
- * The original patch was by Joe Finney for the PCMCIA driver, but
- * I've cleaned it up a bit and added documentation.
- * Thanks to Loeke Brederveld from Lucent for the info.
- */
-
- /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
- * Does it work for everybody, especially old cards? */
- /* Note: WFREQSEL verifies that it is able to read a sensible
- * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID
- * is 0xA (Xilinx version) or 0xB (Ariadne version).
- * My test is more crude but does work. */
- if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
- (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
- /* We must download the frequency parameters to the
- * synthesizers (from the EEPROM - area 1)
- * Note: as the EEPROM is automatically decremented, we set the end
- * if the area... */
- m.mmw_fee_addr = 0x0F;
- m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
- mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m,
- (unsigned char *) &m.mmw_fee_ctrl, 2);
-
- /* Wait until the download is finished. */
- fee_wait(ioaddr, 100, 100);
-
-#ifdef DEBUG_CONFIG_INFO
- /* The frequency was in the last word downloaded. */
- mmc_read(ioaddr, (char *) &m.mmw_fee_data_l - (char *) &m,
- (unsigned char *) &m.mmw_fee_data_l, 2);
-
- /* Print some info for the user. */
- printk(KERN_DEBUG
- "%s: WaveLAN 2.00 recognised (frequency select). Current frequency = %ld\n",
- dev->name,
- ((m.
- mmw_fee_data_h << 4) | (m.mmw_fee_data_l >> 4)) *
- 5 / 2 + 24000L);
-#endif
-
- /* We must now download the power adjust value (gain) to
- * the synthesizers (from the EEPROM - area 7 - DAC). */
- m.mmw_fee_addr = 0x61;
- m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
- mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m,
- (unsigned char *) &m.mmw_fee_ctrl, 2);
-
- /* Wait until the download is finished. */
- }
- /* if 2.00 card */
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name);
-#endif
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Construct the fd and rbd structures.
- * Start the receive unit.
- * (called by wv_hw_reset())
- */
-static inline int wv_ru_start(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
- u16 scb_cs;
- fd_t fd;
- rbd_t rbd;
- u16 rx;
- u16 rx_next;
- int i;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
-#endif
-
- obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
- (unsigned char *) &scb_cs, sizeof(scb_cs));
- if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY)
- return 0;
-
- lp->rx_head = OFFSET_RU;
-
- for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next) {
- rx_next =
- (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ;
-
- fd.fd_status = 0;
- fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0;
- fd.fd_link_offset = rx_next;
- fd.fd_rbd_offset = rx + sizeof(fd);
- obram_write(ioaddr, rx, (unsigned char *) &fd, sizeof(fd));
-
- rbd.rbd_status = 0;
- rbd.rbd_next_rbd_offset = I82586NULL;
- rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd);
- rbd.rbd_bufh = 0;
- rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ);
- obram_write(ioaddr, rx + sizeof(fd),
- (unsigned char *) &rbd, sizeof(rbd));
-
- lp->rx_last = rx;
- }
-
- obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset),
- (unsigned char *) &lp->rx_head, sizeof(lp->rx_head));
-
- scb_cs = SCB_CMD_RUC_GO;
- obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
- (unsigned char *) &scb_cs, sizeof(scb_cs));
-
- set_chan_attn(ioaddr, lp->hacr);
-
- for (i = 1000; i > 0; i--) {
- obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
- (unsigned char *) &scb_cs, sizeof(scb_cs));
- if (scb_cs == 0)
- break;
-
- udelay(10);
- }
-
- if (i <= 0) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_INFO
- "%s: wavelan_ru_start(): board not accepting command.\n",
- dev->name);
-#endif
- return -1;
- }
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
-#endif
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Initialise the transmit blocks.
- * Start the command unit executing the NOP
- * self-loop of the first transmit block.
- *
- * Here we create the list of send buffers used to transmit packets
- * between the PC and the command unit. For each buffer, we create a
- * buffer descriptor (pointing on the buffer), a transmit command
- * (pointing to the buffer descriptor) and a NOP command.
- * The transmit command is linked to the NOP, and the NOP to itself.
- * When we will have finished executing the transmit command, we will
- * then loop on the NOP. By releasing the NOP link to a new command,
- * we may send another buffer.
- *
- * (called by wv_hw_reset())
- */
-static inline int wv_cu_start(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
- int i;
- u16 txblock;
- u16 first_nop;
- u16 scb_cs;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_cu_start()\n", dev->name);
-#endif
-
- lp->tx_first_free = OFFSET_CU;
- lp->tx_first_in_use = I82586NULL;
-
- for (i = 0, txblock = OFFSET_CU;
- i < NTXBLOCKS; i++, txblock += TXBLOCKZ) {
- ac_tx_t tx;
- ac_nop_t nop;
- tbd_t tbd;
- unsigned short tx_addr;
- unsigned short nop_addr;
- unsigned short tbd_addr;
- unsigned short buf_addr;
-
- tx_addr = txblock;
- nop_addr = tx_addr + sizeof(tx);
- tbd_addr = nop_addr + sizeof(nop);
- buf_addr = tbd_addr + sizeof(tbd);
-
- tx.tx_h.ac_status = 0;
- tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I;
- tx.tx_h.ac_link = nop_addr;
- tx.tx_tbd_offset = tbd_addr;
- obram_write(ioaddr, tx_addr, (unsigned char *) &tx,
- sizeof(tx));
-
- nop.nop_h.ac_status = 0;
- nop.nop_h.ac_command = acmd_nop;
- nop.nop_h.ac_link = nop_addr;
- obram_write(ioaddr, nop_addr, (unsigned char *) &nop,
- sizeof(nop));
-
- tbd.tbd_status = TBD_STATUS_EOF;
- tbd.tbd_next_bd_offset = I82586NULL;
- tbd.tbd_bufl = buf_addr;
- tbd.tbd_bufh = 0;
- obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd,
- sizeof(tbd));
- }
-
- first_nop =
- OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t);
- obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset),
- (unsigned char *) &first_nop, sizeof(first_nop));
-
- scb_cs = SCB_CMD_CUC_GO;
- obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
- (unsigned char *) &scb_cs, sizeof(scb_cs));
-
- set_chan_attn(ioaddr, lp->hacr);
-
- for (i = 1000; i > 0; i--) {
- obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
- (unsigned char *) &scb_cs, sizeof(scb_cs));
- if (scb_cs == 0)
- break;
-
- udelay(10);
- }
-
- if (i <= 0) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_INFO
- "%s: wavelan_cu_start(): board not accepting command.\n",
- dev->name);
-#endif
- return -1;
- }
-
- lp->tx_n_in_use = 0;
- netif_start_queue(dev);
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_cu_start()\n", dev->name);
-#endif
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does a standard configuration of the WaveLAN
- * controller (i82586).
- *
- * It initialises the scp, iscp and scb structure
- * The first two are just pointers to the next.
- * The last one is used for basic configuration and for basic
- * communication (interrupt status).
- *
- * (called by wv_hw_reset())
- */
-static inline int wv_82586_start(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
- scp_t scp; /* system configuration pointer */
- iscp_t iscp; /* intermediate scp */
- scb_t scb; /* system control block */
- ach_t cb; /* Action command header */
- u8 zeroes[512];
- int i;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_82586_start()\n", dev->name);
-#endif
-
- /*
- * Clear the onboard RAM.
- */
- memset(&zeroes[0], 0x00, sizeof(zeroes));
- for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes))
- obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes));
-
- /*
- * Construct the command unit structures:
- * scp, iscp, scb, cb.
- */
- memset(&scp, 0x00, sizeof(scp));
- scp.scp_sysbus = SCP_SY_16BBUS;
- scp.scp_iscpl = OFFSET_ISCP;
- obram_write(ioaddr, OFFSET_SCP, (unsigned char *) &scp,
- sizeof(scp));
-
- memset(&iscp, 0x00, sizeof(iscp));
- iscp.iscp_busy = 1;
- iscp.iscp_offset = OFFSET_SCB;
- obram_write(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp,
- sizeof(iscp));
-
- /* Our first command is to reset the i82586. */
- memset(&scb, 0x00, sizeof(scb));
- scb.scb_command = SCB_CMD_RESET;
- scb.scb_cbl_offset = OFFSET_CU;
- scb.scb_rfa_offset = OFFSET_RU;
- obram_write(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
- sizeof(scb));
-
- set_chan_attn(ioaddr, lp->hacr);
-
- /* Wait for command to finish. */
- for (i = 1000; i > 0; i--) {
- obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp,
- sizeof(iscp));
-
- if (iscp.iscp_busy == (unsigned short) 0)
- break;
-
- udelay(10);
- }
-
- if (i <= 0) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_INFO
- "%s: wv_82586_start(): iscp_busy timeout.\n",
- dev->name);
-#endif
- return -1;
- }
-
- /* Check command completion. */
- for (i = 15; i > 0; i--) {
- obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
- sizeof(scb));
-
- if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA))
- break;
-
- udelay(10);
- }
-
- if (i <= 0) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_INFO
- "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n",
- dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status);
-#endif
- return -1;
- }
-
- wv_ack(dev);
-
- /* Set the action command header. */
- memset(&cb, 0x00, sizeof(cb));
- cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
- cb.ac_link = OFFSET_CU;
- obram_write(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb));
-
- if (wv_synchronous_cmd(dev, "diag()") == -1)
- return -1;
-
- obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb));
- if (cb.ac_status & AC_SFLD_FAIL) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_INFO
- "%s: wv_82586_start(): i82586 Self Test failed.\n",
- dev->name);
-#endif
- return -1;
- }
-#ifdef DEBUG_I82586_SHOW
- wv_scb_show(ioaddr);
-#endif
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_82586_start()\n", dev->name);
-#endif
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine does a standard configuration of the WaveLAN
- * controller (i82586).
- *
- * This routine is a violent hack. We use the first free transmit block
- * to make our configuration. In the buffer area, we create the three
- * configuration commands (linked). We make the previous NOP point to
- * the beginning of the buffer instead of the tx command. After, we go
- * as usual to the NOP command.
- * Note that only the last command (mc_set) will generate an interrupt.
- *
- * (called by wv_hw_reset(), wv_82586_reconfig(), wavelan_packet_xmit())
- */
-static void wv_82586_config(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
- unsigned short txblock;
- unsigned short txpred;
- unsigned short tx_addr;
- unsigned short nop_addr;
- unsigned short tbd_addr;
- unsigned short cfg_addr;
- unsigned short ias_addr;
- unsigned short mcs_addr;
- ac_tx_t tx;
- ac_nop_t nop;
- ac_cfg_t cfg; /* Configure action */
- ac_ias_t ias; /* IA-setup action */
- ac_mcs_t mcs; /* Multicast setup */
- struct dev_mc_list *dmi;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name);
-#endif
-
- /* Check nothing bad has happened */
- if (lp->tx_n_in_use == (NTXBLOCKS - 1)) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_INFO "%s: wv_82586_config(): Tx queue full.\n",
- dev->name);
-#endif
- return;
- }
-
- /* Calculate addresses of next block and previous block. */
- txblock = lp->tx_first_free;
- txpred = txblock - TXBLOCKZ;
- if (txpred < OFFSET_CU)
- txpred += NTXBLOCKS * TXBLOCKZ;
- lp->tx_first_free += TXBLOCKZ;
- if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
- lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
-
- lp->tx_n_in_use++;
-
- /* Calculate addresses of the different parts of the block. */
- tx_addr = txblock;
- nop_addr = tx_addr + sizeof(tx);
- tbd_addr = nop_addr + sizeof(nop);
- cfg_addr = tbd_addr + sizeof(tbd_t); /* beginning of the buffer */
- ias_addr = cfg_addr + sizeof(cfg);
- mcs_addr = ias_addr + sizeof(ias);
-
- /*
- * Transmit command
- */
- tx.tx_h.ac_status = 0xFFFF; /* Fake completion value */
- obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
- (unsigned char *) &tx.tx_h.ac_status,
- sizeof(tx.tx_h.ac_status));
-
- /*
- * NOP command
- */
- nop.nop_h.ac_status = 0;
- obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
- (unsigned char *) &nop.nop_h.ac_status,
- sizeof(nop.nop_h.ac_status));
- nop.nop_h.ac_link = nop_addr;
- obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
- (unsigned char *) &nop.nop_h.ac_link,
- sizeof(nop.nop_h.ac_link));
-
- /* Create a configure action. */
- memset(&cfg, 0x00, sizeof(cfg));
-
- /*
- * For Linux we invert AC_CFG_ALOC() so as to conform
- * to the way that net packets reach us from above.
- * (See also ac_tx_t.)
- *
- * Updated from Wavelan Manual WCIN085B
- */
- cfg.cfg_byte_cnt =
- AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
- cfg.cfg_fifolim = AC_CFG_FIFOLIM(4);
- cfg.cfg_byte8 = AC_CFG_SAV_BF(1) | AC_CFG_SRDY(0);
- cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
- AC_CFG_ILPBCK(0) |
- AC_CFG_PRELEN(AC_CFG_PLEN_2) |
- AC_CFG_ALOC(1) | AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
- cfg.cfg_byte10 = AC_CFG_BOFMET(1) |
- AC_CFG_ACR(6) | AC_CFG_LINPRIO(0);
- cfg.cfg_ifs = 0x20;
- cfg.cfg_slotl = 0x0C;
- cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) | AC_CFG_SLTTMHI(0);
- cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
- AC_CFG_BTSTF(0) |
- AC_CFG_CRC16(0) |
- AC_CFG_NCRC(0) |
- AC_CFG_TNCRS(1) |
- AC_CFG_MANCH(0) |
- AC_CFG_BCDIS(0) | AC_CFG_PRM(lp->promiscuous);
- cfg.cfg_byte15 = AC_CFG_ICDS(0) |
- AC_CFG_CDTF(0) | AC_CFG_ICSS(0) | AC_CFG_CSTF(0);
-/*
- cfg.cfg_min_frm_len = AC_CFG_MNFRM(64);
-*/
- cfg.cfg_min_frm_len = AC_CFG_MNFRM(8);
-
- cfg.cfg_h.ac_command = (AC_CFLD_CMD & acmd_configure);
- cfg.cfg_h.ac_link = ias_addr;
- obram_write(ioaddr, cfg_addr, (unsigned char *) &cfg, sizeof(cfg));
-
- /* Set up the MAC address */
- memset(&ias, 0x00, sizeof(ias));
- ias.ias_h.ac_command = (AC_CFLD_CMD & acmd_ia_setup);
- ias.ias_h.ac_link = mcs_addr;
- memcpy(&ias.ias_addr[0], (unsigned char *) &dev->dev_addr[0],
- sizeof(ias.ias_addr));
- obram_write(ioaddr, ias_addr, (unsigned char *) &ias, sizeof(ias));
-
- /* Initialize adapter's Ethernet multicast addresses */
- memset(&mcs, 0x00, sizeof(mcs));
- mcs.mcs_h.ac_command = AC_CFLD_I | (AC_CFLD_CMD & acmd_mc_setup);
- mcs.mcs_h.ac_link = nop_addr;
- mcs.mcs_cnt = WAVELAN_ADDR_SIZE * lp->mc_count;
- obram_write(ioaddr, mcs_addr, (unsigned char *) &mcs, sizeof(mcs));
-
- /* Any address to set? */
- if (lp->mc_count) {
- for (dmi = dev->mc_list; dmi; dmi = dmi->next)
- outsw(PIOP1(ioaddr), (u16 *) dmi->dmi_addr,
- WAVELAN_ADDR_SIZE >> 1);
-
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG
- "%s: wv_82586_config(): set %d multicast addresses:\n",
- dev->name, lp->mc_count);
- for (dmi = dev->mc_list; dmi; dmi = dmi->next)
- printk(KERN_DEBUG
- " %02x:%02x:%02x:%02x:%02x:%02x\n",
- dmi->dmi_addr[0], dmi->dmi_addr[1],
- dmi->dmi_addr[2], dmi->dmi_addr[3],
- dmi->dmi_addr[4], dmi->dmi_addr[5]);
-#endif
- }
-
- /*
- * Overwrite the predecessor NOP link
- * so that it points to the configure action.
- */
- nop_addr = txpred + sizeof(tx);
- nop.nop_h.ac_status = 0;
- obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
- (unsigned char *) &nop.nop_h.ac_status,
- sizeof(nop.nop_h.ac_status));
- nop.nop_h.ac_link = cfg_addr;
- obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
- (unsigned char *) &nop.nop_h.ac_link,
- sizeof(nop.nop_h.ac_link));
-
- /* Job done, clear the flag */
- lp->reconfig_82586 = 0;
-
- if (lp->tx_first_in_use == I82586NULL)
- lp->tx_first_in_use = txblock;
-
- if (lp->tx_n_in_use == (NTXBLOCKS - 1))
- netif_stop_queue(dev);
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_82586_config()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * This routine, called by wavelan_close(), gracefully stops the
- * WaveLAN controller (i82586).
- * (called by wavelan_close())
- */
-static inline void wv_82586_stop(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
- u16 scb_cmd;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name);
-#endif
-
- /* Suspend both command unit and receive unit. */
- scb_cmd =
- (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC &
- SCB_CMD_RUC_SUS);
- obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
- (unsigned char *) &scb_cmd, sizeof(scb_cmd));
- set_chan_attn(ioaddr, lp->hacr);
-
- /* No more interrupts */
- wv_ints_off(dev);
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_82586_stop()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Totally reset the WaveLAN and restart it.
- * Performs the following actions:
- * 1. A power reset (reset DMA)
- * 2. Initialize the radio modem (using wv_mmc_init)
- * 3. Reset & Configure LAN controller (using wv_82586_start)
- * 4. Start the LAN controller's command unit
- * 5. Start the LAN controller's receive unit
- * (called by wavelan_interrupt(), wavelan_watchdog() & wavelan_open())
- */
-static int wv_hw_reset(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long ioaddr = dev->base_addr;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name,
- (unsigned int) dev);
-#endif
-
- /* Increase the number of resets done. */
- lp->nresets++;
-
- wv_hacr_reset(ioaddr);
- lp->hacr = HACR_DEFAULT;
-
- if ((wv_mmc_init(dev) < 0) || (wv_82586_start(dev) < 0))
- return -1;
-
- /* Enable the card to send interrupts. */
- wv_ints_on(dev);
-
- /* Start card functions */
- if (wv_cu_start(dev) < 0)
- return -1;
-
- /* Setup the controller and parameters */
- wv_82586_config(dev);
-
- /* Finish configuration with the receive unit */
- if (wv_ru_start(dev) < 0)
- return -1;
-
-#ifdef DEBUG_CONFIG_TRACE
- printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name);
-#endif
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Check if there is a WaveLAN at the specific base address.
- * As a side effect, this reads the MAC address.
- * (called in wavelan_probe() and init_module())
- */
-static int wv_check_ioaddr(unsigned long ioaddr, u8 * mac)
-{
- int i; /* Loop counter */
-
- /* Check if the base address if available. */
- if (check_region(ioaddr, sizeof(ha_t)))
- return -EADDRINUSE; /* ioaddr already used */
-
- /* Reset host interface */
- wv_hacr_reset(ioaddr);
-
- /* Read the MAC address from the parameter storage area. */
- psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr),
- mac, 6);
-
- /*
- * Check the first three octets of the address for the manufacturer's code.
- * Note: if this can't find your WaveLAN card, you've got a
- * non-NCR/AT&T/Lucent ISA card. See wavelan.p.h for detail on
- * how to configure your card.
- */
- for (i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
- if ((mac[0] == MAC_ADDRESSES[i][0]) &&
- (mac[1] == MAC_ADDRESSES[i][1]) &&
- (mac[2] == MAC_ADDRESSES[i][2]))
- return 0;
-
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_WARNING
- "WaveLAN (0x%3X): your MAC address might be %02X:%02X:%02X.\n",
- ioaddr, mac[0], mac[1], mac[2]);
-#endif
- return -ENODEV;
-}
-
-/************************ INTERRUPT HANDLING ************************/
-
-/*
- * This function is the interrupt handler for the WaveLAN card. This
- * routine will be called whenever:
- */
-static void wavelan_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- device *dev;
- unsigned long ioaddr;
- net_local *lp;
- u16 hasr;
- u16 status;
- u16 ack_cmd;
-
- dev = dev_id;
-
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
-#endif
-
- lp = (net_local *) dev->priv;
- ioaddr = dev->base_addr;
-
-#ifdef DEBUG_INTERRUPT_INFO
- /* Check state of our spinlock */
- if(spin_is_locked(&lp->spinlock))
- printk(KERN_DEBUG
- "%s: wavelan_interrupt(): spinlock is already locked !!!\n",
- dev->name);
-#endif
-
- /* Prevent reentrancy. We need to do that because we may have
- * multiple interrupt handler running concurrently.
- * It is safe because wv_splhi() disables interrupts before acquiring
- * the spinlock. */
- spin_lock(&lp->spinlock);
-
- /* Check modem interrupt */
- if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR) {
- u8 dce_status;
-
- /*
- * Interrupt from the modem management controller.
- * This will clear it -- ignored for now.
- */
- mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status,
- sizeof(dce_status));
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO
- "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n",
- dev->name, dce_status);
-#endif
- }
-
- /* Check if not controller interrupt */
- if ((hasr & HASR_82586_INTR) == 0) {
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO
- "%s: wavelan_interrupt(): interrupt not coming from i82586\n",
- dev->name);
-#endif
- spin_unlock (&lp->spinlock);
- return;
- }
-
- /* Read interrupt data. */
- obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
- (unsigned char *) &status, sizeof(status));
-
- /*
- * Acknowledge the interrupt(s).
- */
- ack_cmd = status & SCB_ST_INT;
- obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
- (unsigned char *) &ack_cmd, sizeof(ack_cmd));
- set_chan_attn(ioaddr, lp->hacr);
-
-#ifdef DEBUG_INTERRUPT_INFO
- printk(KERN_DEBUG "%s: wavelan_interrupt(): status 0x%04x.\n",
- dev->name, status);
-#endif
-
- /* Command completed. */
- if ((status & SCB_ST_CX) == SCB_ST_CX) {
-#ifdef DEBUG_INTERRUPT_INFO
- printk(KERN_DEBUG
- "%s: wavelan_interrupt(): command completed.\n",
- dev->name);
-#endif
- wv_complete(dev, ioaddr, lp);
- }
-
- /* Frame received. */
- if ((status & SCB_ST_FR) == SCB_ST_FR) {
-#ifdef DEBUG_INTERRUPT_INFO
- printk(KERN_DEBUG
- "%s: wavelan_interrupt(): received packet.\n",
- dev->name);
-#endif
- wv_receive(dev);
- }
-
- /* Check the state of the command unit. */
- if (((status & SCB_ST_CNA) == SCB_ST_CNA) ||
- (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) &&
- (netif_running(dev)))) {
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO
- "%s: wavelan_interrupt(): CU inactive -- restarting\n",
- dev->name);
-#endif
- wv_hw_reset(dev);
- }
-
- /* Check the state of the command unit. */
- if (((status & SCB_ST_RNR) == SCB_ST_RNR) ||
- (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) &&
- (netif_running(dev)))) {
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO
- "%s: wavelan_interrupt(): RU not ready -- restarting\n",
- dev->name);
-#endif
- wv_hw_reset(dev);
- }
-
- /* Release spinlock */
- spin_unlock (&lp->spinlock);
-
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
-#endif
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Watchdog: when we start a transmission, a timer is set for us in the
- * kernel. If the transmission completes, this timer is disabled. If
- * the timer expires, we are called and we try to unlock the hardware.
- */
-static void wavelan_watchdog(device * dev)
-{
- net_local * lp = (net_local *)dev->priv;
- u_long ioaddr = dev->base_addr;
- unsigned long flags;
- unsigned int nreaped;
-
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
-#endif
-
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n",
- dev->name);
-#endif
-
- /* Check that we came here for something */
- if (lp->tx_n_in_use <= 0) {
- return;
- }
-
- wv_splhi(lp, &flags);
-
- /* Try to see if some buffers are not free (in case we missed
- * an interrupt */
- nreaped = wv_complete(dev, ioaddr, lp);
-
-#ifdef DEBUG_INTERRUPT_INFO
- printk(KERN_DEBUG
- "%s: wavelan_watchdog(): %d reaped, %d remain.\n",
- dev->name, nreaped, lp->tx_n_in_use);
-#endif
-
-#ifdef DEBUG_PSA_SHOW
- {
- psa_t psa;
- psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
- wv_psa_show(&psa);
- }
-#endif
-#ifdef DEBUG_MMC_SHOW
- wv_mmc_show(dev);
-#endif
-#ifdef DEBUG_I82586_SHOW
- wv_cu_show(dev);
-#endif
-
- /* If no buffer has been freed */
- if (nreaped == 0) {
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO
- "%s: wavelan_watchdog(): cleanup failed, trying reset\n",
- dev->name);
-#endif
- wv_hw_reset(dev);
- }
-
- /* At this point, we should have some free Tx buffer ;-) */
- if (lp->tx_n_in_use < NTXBLOCKS - 1)
- netif_wake_queue(dev);
-
- wv_splx(lp, &flags);
-
-#ifdef DEBUG_INTERRUPT_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
-#endif
-}
-
-/********************* CONFIGURATION CALLBACKS *********************/
-/*
- * Here are the functions called by the Linux networking code (NET3)
- * for initialization, configuration and deinstallations of the
- * WaveLAN ISA hardware.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Configure and start up the WaveLAN PCMCIA adaptor.
- * Called by NET3 when it "opens" the device.
- */
-static int wavelan_open(device * dev)
-{
- net_local * lp = (net_local *)dev->priv;
- unsigned long flags;
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name,
- (unsigned int) dev);
-#endif
-
- /* Check irq */
- if (dev->irq == 0) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_WARNING "%s: wavelan_open(): no IRQ\n",
- dev->name);
-#endif
- return -ENXIO;
- }
-
- if (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", dev) != 0)
- {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n",
- dev->name);
-#endif
- return -EAGAIN;
- }
-
- wv_splhi(lp, &flags);
-
- if (wv_hw_reset(dev) != -1) {
- netif_start_queue(dev);
- } else {
- free_irq(dev->irq, dev);
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_INFO
- "%s: wavelan_open(): impossible to start the card\n",
- dev->name);
-#endif
- wv_splx(lp, &flags);
- return -EAGAIN;
- }
- wv_splx(lp, &flags);
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
-#endif
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Shut down the WaveLAN ISA card.
- * Called by NET3 when it "closes" the device.
- */
-static int wavelan_close(device * dev)
-{
- net_local *lp = (net_local *) dev->priv;
- unsigned long flags;
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name,
- (unsigned int) dev);
-#endif
-
- netif_stop_queue(dev);
-
- /*
- * Flush the Tx and disable Rx.
- */
- wv_splhi(lp, &flags);
- wv_82586_stop(dev);
- wv_splx(lp, &flags);
-
- free_irq(dev->irq, dev);
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
-#endif
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Probe an I/O address, and if the WaveLAN is there configure the
- * device structure
- * (called by wavelan_probe() and via init_module()).
- */
-static int __init wavelan_config(device * dev)
-{
- unsigned long ioaddr = dev->base_addr;
- u8 irq_mask;
- int irq;
- net_local *lp;
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "%s: ->wavelan_config(dev=0x%x, ioaddr=0x%lx)\n",
- dev->name, (unsigned int) dev, ioaddr);
-#endif
-
- /* Check IRQ argument on command line. */
- if (dev->irq != 0) {
- irq_mask = wv_irq_to_psa(dev->irq);
-
- if (irq_mask == 0) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_WARNING
- "%s: wavelan_config(): invalid IRQ %d ignored.\n",
- dev->name, dev->irq);
-#endif
- dev->irq = 0;
- } else {
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG
- "%s: wavelan_config(): changing IRQ to %d\n",
- dev->name, dev->irq);
-#endif
- psa_write(ioaddr, HACR_DEFAULT,
- psaoff(0, psa_int_req_no), &irq_mask, 1);
- /* update the Wavelan checksum */
- update_psa_checksum(dev, ioaddr, HACR_DEFAULT);
- wv_hacr_reset(ioaddr);
- }
- }
-
- psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no),
- &irq_mask, 1);
- if ((irq = wv_psa_to_irq(irq_mask)) == -1) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_INFO
- "%s: wavelan_config(): could not wavelan_map_irq(%d).\n",
- dev->name, irq_mask);
-#endif
- return -EAGAIN;
- }
-
- dev->irq = irq;
-
- if (!request_region(ioaddr, sizeof(ha_t), "wavelan"))
- return -EBUSY;
-
- dev->mem_start = 0x0000;
- dev->mem_end = 0x0000;
- dev->if_port = 0;
-
- /* Initialize device structures */
- dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
- if (dev->priv == NULL) {
- release_region(ioaddr, sizeof(ha_t));
- return -ENOMEM;
- }
- memset(dev->priv, 0x00, sizeof(net_local));
- lp = (net_local *) dev->priv;
-
- /* Back link to the device structure. */
- lp->dev = dev;
- /* Add the device at the beginning of the linked list. */
- lp->next = wavelan_list;
- wavelan_list = lp;
-
- lp->hacr = HACR_DEFAULT;
-
- /* Multicast stuff */
- lp->promiscuous = 0;
- lp->mc_count = 0;
-
- /* Init spinlock */
- spin_lock_init(&lp->spinlock);
-
- /*
- * Fill in the fields of the device structure
- * with generic Ethernet values.
- */
- ether_setup(dev);
-
- SET_MODULE_OWNER(dev);
- dev->open = wavelan_open;
- dev->stop = wavelan_close;
- dev->hard_start_xmit = wavelan_packet_xmit;
- dev->get_stats = wavelan_get_stats;
- dev->set_multicast_list = &wavelan_set_multicast_list;
- dev->tx_timeout = &wavelan_watchdog;
- dev->watchdog_timeo = WATCHDOG_JIFFIES;
-#ifdef SET_MAC_ADDRESS
- dev->set_mac_address = &wavelan_set_mac_address;
-#endif /* SET_MAC_ADDRESS */
-
-#ifdef WIRELESS_EXT /* if wireless extension exists in the kernel */
- dev->do_ioctl = wavelan_ioctl;
- dev->get_wireless_stats = wavelan_get_wireless_stats;
-#endif
-
- dev->mtu = WAVELAN_MTU;
-
- /* Display nice information. */
- wv_init_info(dev);
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_config()\n", dev->name);
-#endif
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Check for a network adaptor of this type. Return '0' iff one
- * exists. There seem to be different interpretations of
- * the initial value of dev->base_addr.
- * We follow the example in drivers/net/ne.c.
- * (called in "Space.c")
- */
-int __init wavelan_probe(device * dev)
-{
- short base_addr;
- mac_addr mac; /* MAC address (check existence of WaveLAN) */
- int i;
- int r;
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG
- "%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n",
- dev->name, (unsigned int) dev,
- (unsigned int) dev->base_addr);
-#endif
-
-#ifdef STRUCT_CHECK
- if (wv_struct_check() != (char *) NULL) {
- printk(KERN_WARNING
- "%s: wavelan_probe(): structure/compiler botch: \"%s\"\n",
- dev->name, wv_struct_check());
- return -ENODEV;
- }
-#endif /* STRUCT_CHECK */
-
- /* Check the value of the command line parameter for base address. */
- base_addr = dev->base_addr;
-
- /* Don't probe at all. */
- if (base_addr < 0) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_WARNING
- "%s: wavelan_probe(): invalid base address\n",
- dev->name);
-#endif
- return -ENXIO;
- }
-
- /* Check a single specified location. */
- if (base_addr > 0x100) {
- /* Check if there is something at this base address */
- if ((r = wv_check_ioaddr(base_addr, mac)) == 0) {
- memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */
- r = wavelan_config(dev);
- }
-#ifdef DEBUG_CONFIG_INFO
- if (r != 0)
- printk(KERN_DEBUG
- "%s: wavelan_probe(): no device at specified base address (0x%X) or address already in use\n",
- dev->name, base_addr);
-#endif
-
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name);
-#endif
- return r;
- }
-
- /* Scan all possible addresses of the WaveLAN hardware. */
- for (i = 0; i < NELS(iobase); i++) {
- /* Check whether there is something at this base address. */
- if (wv_check_ioaddr(iobase[i], mac) == 0) {
- dev->base_addr = iobase[i]; /* Copy base address. */
- memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */
- if (wavelan_config(dev) == 0) {
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG
- "%s: <-wavelan_probe()\n",
- dev->name);
-#endif
- return 0;
- }
- }
- }
-
- /* We may have touched base_addr. Another driver may not like it. */
- dev->base_addr = base_addr;
-
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG "%s: wavelan_probe(): no device found\n",
- dev->name);
-#endif
-
- return -ENODEV;
-}
-
-/****************************** MODULE ******************************/
-/*
- * Module entry point: insertion and removal
- */
-
-#ifdef MODULE
-/*------------------------------------------------------------------*/
-/*
- * Insertion of the module
- * I'm now quite proud of the multi-device support.
- */
-int init_module(void)
-{
- mac_addr mac; /* MAC address (check WaveLAN existence) */
- int ret = -EIO; /* Return error if no cards found */
- int i;
-
-#ifdef DEBUG_MODULE_TRACE
- printk(KERN_DEBUG "-> init_module()\n");
-#endif
-
- /* If probing is asked */
- if (io[0] == 0) {
-#ifdef DEBUG_CONFIG_ERROR
- printk(KERN_WARNING
- "WaveLAN init_module(): doing device probing (bad !)\n");
- printk(KERN_WARNING
- "Specify base addresses while loading module to correct the problem\n");
-#endif
-
- /* Copy the basic set of address to be probed. */
- for (i = 0; i < NELS(iobase); i++)
- io[i] = iobase[i];
- }
-
-
- /* Loop on all possible base addresses. */
- i = -1;
- while ((io[++i] != 0) && (i < NELS(io))) {
- /* Check if there is something at this base address. */
- if (wv_check_ioaddr(io[i], mac) == 0) {
- device *dev;
-
- /* Create device and set basic arguments. */
- dev =
- kmalloc(sizeof(struct net_device), GFP_KERNEL);
- if (dev == NULL) {
- ret = -ENOMEM;
- break;
- }
- memset(dev, 0x00, sizeof(struct net_device));
- memcpy(dev->name, name[i], IFNAMSIZ); /* Copy name */
- dev->base_addr = io[i];
- dev->irq = irq[i];
- dev->init = &wavelan_config;
- memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */
-
- /* Try to create the device. */
- if (register_netdev(dev) != 0) {
- /* Deallocate everything. */
- /* Note: if dev->priv is mallocated, there is no way to fail. */
- kfree(dev);
- } else {
- /* If at least one device OK, we do not fail */
- ret = 0;
- }
- } /* if there is something at the address */
- } /* Loop on all addresses. */
-
-#ifdef DEBUG_CONFIG_ERROR
- if (wavelan_list == (net_local *) NULL)
- printk(KERN_WARNING
- "WaveLAN init_module(): no device found\n");
-#endif
-
-#ifdef DEBUG_MODULE_TRACE
- printk(KERN_DEBUG "<- init_module()\n");
-#endif
- return ret;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Removal of the module
- */
-void cleanup_module(void)
-{
-#ifdef DEBUG_MODULE_TRACE
- printk(KERN_DEBUG "-> cleanup_module()\n");
-#endif
-
- /* Loop on all devices and release them. */
- while (wavelan_list != (net_local *) NULL) {
- device *dev = wavelan_list->dev;
-
-#ifdef DEBUG_CONFIG_INFO
- printk(KERN_DEBUG
- "%s: cleanup_module(): removing device at 0x%x\n",
- dev->name, (unsigned int) dev);
-#endif
-
- /* Release the ioport region. */
- release_region(dev->base_addr, sizeof(ha_t));
-
- /* Definitely remove the device. */
- unregister_netdev(dev);
-
- /* Unlink the device. */
- wavelan_list = wavelan_list->next;
-
- /* Free pieces. */
- kfree(dev->priv);
- kfree(dev);
- }
-
-#ifdef DEBUG_MODULE_TRACE
- printk(KERN_DEBUG "<- cleanup_module()\n");
-#endif
-}
-#endif /* MODULE */
-MODULE_LICENSE("GPL");
-
-/*
- * This software may only be used and distributed
- * according to the terms of the GNU General Public License.
- *
- * This software was developed as a component of the
- * Linux operating system.
- * It is based on other device drivers and information
- * either written or supplied by:
- * Ajay Bakre (bakre@paul.rutgers.edu),
- * Donald Becker (becker@scyld.com),
- * Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com),
- * Anders Klemets (klemets@it.kth.se),
- * Vladimir V. Kolpakov (w@stier.koenig.ru),
- * Marc Meertens (Marc.Meertens@Utrecht.NCR.com),
- * Pauline Middelink (middelin@polyware.iaf.nl),
- * Robert Morris (rtm@das.harvard.edu),
- * Jean Tourrilhes (jt@hplb.hpl.hp.com),
- * Girish Welling (welling@paul.rutgers.edu),
- *
- * Thanks go also to:
- * James Ashton (jaa101@syseng.anu.edu.au),
- * Alan Cox (alan@redhat.com),
- * Allan Creighton (allanc@cs.usyd.edu.au),
- * Matthew Geier (matthew@cs.usyd.edu.au),
- * Remo di Giovanni (remo@cs.usyd.edu.au),
- * Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de),
- * Vipul Gupta (vgupta@cs.binghamton.edu),
- * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
- * Tim Nicholson (tim@cs.usyd.edu.au),
- * Ian Parkin (ian@cs.usyd.edu.au),
- * John Rosenberg (johnr@cs.usyd.edu.au),
- * George Rossi (george@phm.gov.au),
- * Arthur Scott (arthur@cs.usyd.edu.au),
- * Peter Storey,
- * for their assistance and advice.
- *
- * Please send bug reports, updates, comments to:
- *
- * Bruce Janson Email: bruce@cs.usyd.edu.au
- * Basser Department of Computer Science Phone: +61-2-9351-3423
- * University of Sydney, N.S.W., 2006, AUSTRALIA Fax: +61-2-9351-3838
- */
+++ /dev/null
-/*
- * WaveLAN ISA driver
- *
- * Jean II - HPLB '96
- *
- * Reorganisation and extension of the driver.
- * Original copyright follows. See wavelan.p.h for details.
- *
- * This file contains the declarations for the WaveLAN hardware. Note that
- * the WaveLAN ISA includes a i82586 controller (see definitions in
- * file i82586.h).
- *
- * The main difference between the ISA hardware and the PCMCIA one is
- * the Ethernet controller (i82586 instead of i82593).
- * The i82586 allows multiple transmit buffers. The PSA needs to be accessed
- * through the host interface.
- */
-
-#ifndef _WAVELAN_H
-#define _WAVELAN_H
-
-/************************** MAGIC NUMBERS ***************************/
-
-/* Detection of the WaveLAN card is done by reading the MAC
- * address from the card and checking it. If you have a non-AT&T
- * product (OEM, like DEC RoamAbout, Digital Ocean, or Epson),
- * you might need to modify this part to accommodate your hardware.
- */
-static const char MAC_ADDRESSES[][3] =
-{
- { 0x08, 0x00, 0x0E }, /* AT&T WaveLAN (standard) & DEC RoamAbout */
- { 0x08, 0x00, 0x6A }, /* AT&T WaveLAN (alternate) */
- { 0x00, 0x00, 0xE1 }, /* Hitachi Wavelan */
- { 0x00, 0x60, 0x1D } /* Lucent Wavelan (another one) */
- /* Add your card here and send me the patch! */
-};
-
-#define WAVELAN_ADDR_SIZE 6 /* Size of a MAC address */
-
-#define WAVELAN_MTU 1500 /* Maximum size of WaveLAN packet */
-
-#define MAXDATAZ (WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU)
-
-/*
- * Constants used to convert channels to frequencies
- */
-
-/* Frequency available in the 2.0 modem, in units of 250 kHz
- * (as read in the offset register of the dac area).
- * Used to map channel numbers used by `wfreqsel' to frequencies
- */
-static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
- 0xD0, 0xF0, 0xF8, 0x150 };
-
-/* Frequencies of the 1.0 modem (fixed frequencies).
- * Use to map the PSA `subband' to a frequency
- * Note : all frequencies apart from the first one need to be multiplied by 10
- */
-static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
-
-
-
-/*************************** PC INTERFACE ****************************/
-
-/*
- * Host Adaptor structure.
- * (base is board port address).
- */
-typedef union hacs_u hacs_u;
-union hacs_u
-{
- unsigned short hu_command; /* Command register */
-#define HACR_RESET 0x0001 /* Reset board */
-#define HACR_CA 0x0002 /* Set Channel Attention for 82586 */
-#define HACR_16BITS 0x0004 /* 16-bit operation (0 => 8bits) */
-#define HACR_OUT0 0x0008 /* General purpose output pin 0 */
- /* not used - must be 1 */
-#define HACR_OUT1 0x0010 /* General purpose output pin 1 */
- /* not used - must be 1 */
-#define HACR_82586_INT_ENABLE 0x0020 /* Enable 82586 interrupts */
-#define HACR_MMC_INT_ENABLE 0x0040 /* Enable MMC interrupts */
-#define HACR_INTR_CLR_ENABLE 0x0080 /* Enable interrupt status read/clear */
- unsigned short hu_status; /* Status Register */
-#define HASR_82586_INTR 0x0001 /* Interrupt request from 82586 */
-#define HASR_MMC_INTR 0x0002 /* Interrupt request from MMC */
-#define HASR_MMC_BUSY 0x0004 /* MMC busy indication */
-#define HASR_PSA_BUSY 0x0008 /* LAN parameter storage area busy */
-};
-
-typedef struct ha_t ha_t;
-struct ha_t
-{
- hacs_u ha_cs; /* Command and status registers */
-#define ha_command ha_cs.hu_command
-#define ha_status ha_cs.hu_status
- unsigned short ha_mmcr; /* Modem Management Ctrl Register */
- unsigned short ha_pior0; /* Program I/O Address Register Port 0 */
- unsigned short ha_piop0; /* Program I/O Port 0 */
- unsigned short ha_pior1; /* Program I/O Address Register Port 1 */
- unsigned short ha_piop1; /* Program I/O Port 1 */
- unsigned short ha_pior2; /* Program I/O Address Register Port 2 */
- unsigned short ha_piop2; /* Program I/O Port 2 */
-};
-
-#define HA_SIZE 16
-
-#define hoff(p,f) (unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0)
-#define HACR(p) hoff(p, ha_command)
-#define HASR(p) hoff(p, ha_status)
-#define MMCR(p) hoff(p, ha_mmcr)
-#define PIOR0(p) hoff(p, ha_pior0)
-#define PIOP0(p) hoff(p, ha_piop0)
-#define PIOR1(p) hoff(p, ha_pior1)
-#define PIOP1(p) hoff(p, ha_piop1)
-#define PIOR2(p) hoff(p, ha_pior2)
-#define PIOP2(p) hoff(p, ha_piop2)
-
-/*
- * Program I/O Mode Register values.
- */
-#define STATIC_PIO 0 /* Mode 1: static mode */
- /* RAM access ??? */
-#define AUTOINCR_PIO 1 /* Mode 2: auto increment mode */
- /* RAM access ??? */
-#define AUTODECR_PIO 2 /* Mode 3: auto decrement mode */
- /* RAM access ??? */
-#define PARAM_ACCESS_PIO 3 /* Mode 4: LAN parameter access mode */
- /* Parameter access. */
-#define PIO_MASK 3 /* register mask */
-#define PIOM(cmd,piono) ((u_short)cmd << 10 << (piono * 2))
-
-#define HACR_DEFAULT (HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2))
-#define HACR_INTRON (HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE)
-
-/************************** MEMORY LAYOUT **************************/
-
-/*
- * Onboard 64 k RAM layout.
- * (Offsets from 0x0000.)
- */
-#define OFFSET_RU 0x0000 /* 75% memory */
-#define OFFSET_CU 0xC000 /* 25% memory */
-#define OFFSET_SCB (OFFSET_ISCP - sizeof(scb_t))
-#define OFFSET_ISCP (OFFSET_SCP - sizeof(iscp_t))
-#define OFFSET_SCP I82586_SCP_ADDR
-
-#define RXBLOCKZ (sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ)
-#define TXBLOCKZ (sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ)
-
-#define NRXBLOCKS ((OFFSET_CU - OFFSET_RU) / RXBLOCKZ)
-#define NTXBLOCKS ((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ)
-
-/********************** PARAMETER STORAGE AREA **********************/
-
-/*
- * Parameter Storage Area (PSA).
- */
-typedef struct psa_t psa_t;
-struct psa_t
-{
- unsigned char psa_io_base_addr_1; /* [0x00] Base address 1 ??? */
- unsigned char psa_io_base_addr_2; /* [0x01] Base address 2 */
- unsigned char psa_io_base_addr_3; /* [0x02] Base address 3 */
- unsigned char psa_io_base_addr_4; /* [0x03] Base address 4 */
- unsigned char psa_rem_boot_addr_1; /* [0x04] Remote Boot Address 1 */
- unsigned char psa_rem_boot_addr_2; /* [0x05] Remote Boot Address 2 */
- unsigned char psa_rem_boot_addr_3; /* [0x06] Remote Boot Address 3 */
- unsigned char psa_holi_params; /* [0x07] HOst Lan Interface (HOLI) Parameters */
- unsigned char psa_int_req_no; /* [0x08] Interrupt Request Line */
- unsigned char psa_unused0[7]; /* [0x09-0x0F] unused */
-
- unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x10-0x15] Universal (factory) MAC Address */
- unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x16-1B] Local MAC Address */
- unsigned char psa_univ_local_sel; /* [0x1C] Universal Local Selection */
-#define PSA_UNIVERSAL 0 /* Universal (factory) */
-#define PSA_LOCAL 1 /* Local */
- unsigned char psa_comp_number; /* [0x1D] Compatibility Number: */
-#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */
-#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */
-#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */
-#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */
-#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz or 2.0 */
- unsigned char psa_thr_pre_set; /* [0x1E] Modem Threshold Preset */
- unsigned char psa_feature_select; /* [0x1F] Call code required (1=on) */
-#define PSA_FEATURE_CALL_CODE 0x01 /* Call code required (Japan) */
- unsigned char psa_subband; /* [0x20] Subband */
-#define PSA_SUBBAND_915 0 /* 915 MHz or 2.0 */
-#define PSA_SUBBAND_2425 1 /* 2425 MHz */
-#define PSA_SUBBAND_2460 2 /* 2460 MHz */
-#define PSA_SUBBAND_2484 3 /* 2484 MHz */
-#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */
- unsigned char psa_quality_thr; /* [0x21] Modem Quality Threshold */
- unsigned char psa_mod_delay; /* [0x22] Modem Delay (?) (reserved) */
- unsigned char psa_nwid[2]; /* [0x23-0x24] Network ID */
- unsigned char psa_nwid_select; /* [0x25] Network ID Select On/Off */
- unsigned char psa_encryption_select; /* [0x26] Encryption On/Off */
- unsigned char psa_encryption_key[8]; /* [0x27-0x2E] Encryption Key */
- unsigned char psa_databus_width; /* [0x2F] AT bus width select 8/16 */
- unsigned char psa_call_code[8]; /* [0x30-0x37] (Japan) Call Code */
- unsigned char psa_nwid_prefix[2]; /* [0x38-0x39] Roaming domain */
- unsigned char psa_reserved[2]; /* [0x3A-0x3B] Reserved - fixed 00 */
- unsigned char psa_conf_status; /* [0x3C] Conf Status, bit 0=1:config*/
- unsigned char psa_crc[2]; /* [0x3D] CRC-16 over PSA */
- unsigned char psa_crc_status; /* [0x3F] CRC Valid Flag */
-};
-
-#define PSA_SIZE 64
-
-/* Calculate offset of a field in the above structure.
- * Warning: only even addresses are used. */
-#define psaoff(p,f) ((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL))
-
-/******************** MODEM MANAGEMENT INTERFACE ********************/
-
-/*
- * Modem Management Controller (MMC) write structure.
- */
-typedef struct mmw_t mmw_t;
-struct mmw_t
-{
- unsigned char mmw_encr_key[8]; /* encryption key */
- unsigned char mmw_encr_enable; /* Enable or disable encryption. */
-#define MMW_ENCR_ENABLE_MODE 0x02 /* mode of security option */
-#define MMW_ENCR_ENABLE_EN 0x01 /* Enable security option. */
- unsigned char mmw_unused0[1]; /* unused */
- unsigned char mmw_des_io_invert; /* encryption option */
-#define MMW_DES_IO_INVERT_RES 0x0F /* reserved */
-#define MMW_DES_IO_INVERT_CTRL 0xF0 /* control (?) (set to 0) */
- unsigned char mmw_unused1[5]; /* unused */
- unsigned char mmw_loopt_sel; /* looptest selection */
-#define MMW_LOOPT_SEL_DIS_NWID 0x40 /* Disable NWID filtering. */
-#define MMW_LOOPT_SEL_INT 0x20 /* Activate Attention Request. */
-#define MMW_LOOPT_SEL_LS 0x10 /* looptest, no collision avoidance */
-#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */
-#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */
-#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */
-#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */
- unsigned char mmw_jabber_enable; /* jabber timer enable */
- /* Abort transmissions > 200 ms */
- unsigned char mmw_freeze; /* freeze or unfreeze signal level */
- /* 0 : signal level & qual updated for every new message, 1 : frozen */
- unsigned char mmw_anten_sel; /* antenna selection */
-#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */
-#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algo. enable */
- unsigned char mmw_ifs; /* inter frame spacing */
- /* min time between transmission in bit periods (.5 us) - bit 0 ignored */
- unsigned char mmw_mod_delay; /* modem delay (synchro) */
- unsigned char mmw_jam_time; /* jamming time (after collision) */
- unsigned char mmw_unused2[1]; /* unused */
- unsigned char mmw_thr_pre_set; /* level threshold preset */
- /* Discard all packet with signal < this value (4) */
- unsigned char mmw_decay_prm; /* decay parameters */
- unsigned char mmw_decay_updat_prm; /* decay update parameters */
- unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */
- /* Discard all packet with quality < this value (3) */
- unsigned char mmw_netw_id_l; /* NWID low order byte */
- unsigned char mmw_netw_id_h; /* NWID high order byte */
- /* Network ID or Domain : create virtual net on the air */
-
- /* 2.0 Hardware extension - frequency selection support */
- unsigned char mmw_mode_select; /* for analog tests (set to 0) */
- unsigned char mmw_unused3[1]; /* unused */
- unsigned char mmw_fee_ctrl; /* frequency EEPROM control */
-#define MMW_FEE_CTRL_PRE 0x10 /* Enable protected instructions. */
-#define MMW_FEE_CTRL_DWLD 0x08 /* Download EEPROM to mmc. */
-#define MMW_FEE_CTRL_CMD 0x07 /* EEPROM commands: */
-#define MMW_FEE_CTRL_READ 0x06 /* Read */
-#define MMW_FEE_CTRL_WREN 0x04 /* Write enable */
-#define MMW_FEE_CTRL_WRITE 0x05 /* Write data to address. */
-#define MMW_FEE_CTRL_WRALL 0x04 /* Write data to all addresses. */
-#define MMW_FEE_CTRL_WDS 0x04 /* Write disable */
-#define MMW_FEE_CTRL_PRREAD 0x16 /* Read addr from protect register */
-#define MMW_FEE_CTRL_PREN 0x14 /* Protect register enable */
-#define MMW_FEE_CTRL_PRCLEAR 0x17 /* Unprotect all registers. */
-#define MMW_FEE_CTRL_PRWRITE 0x15 /* Write address in protect register */
-#define MMW_FEE_CTRL_PRDS 0x14 /* Protect register disable */
- /* Never issue the PRDS command: it's irreversible! */
-
- unsigned char mmw_fee_addr; /* EEPROM address */
-#define MMW_FEE_ADDR_CHANNEL 0xF0 /* Select the channel. */
-#define MMW_FEE_ADDR_OFFSET 0x0F /* Offset in channel data */
-#define MMW_FEE_ADDR_EN 0xC0 /* FEE_CTRL enable operations */
-#define MMW_FEE_ADDR_DS 0x00 /* FEE_CTRL disable operations */
-#define MMW_FEE_ADDR_ALL 0x40 /* FEE_CTRL all operations */
-#define MMW_FEE_ADDR_CLEAR 0xFF /* FEE_CTRL clear operations */
-
- unsigned char mmw_fee_data_l; /* Write data to EEPROM. */
- unsigned char mmw_fee_data_h; /* high octet */
- unsigned char mmw_ext_ant; /* Setting for external antenna */
-#define MMW_EXT_ANT_EXTANT 0x01 /* Select external antenna */
-#define MMW_EXT_ANT_POL 0x02 /* Polarity of the antenna */
-#define MMW_EXT_ANT_INTERNAL 0x00 /* Internal antenna */
-#define MMW_EXT_ANT_EXTERNAL 0x03 /* External antenna */
-#define MMW_EXT_ANT_IQ_TEST 0x1C /* IQ test pattern (set to 0) */
-};
-
-#define MMW_SIZE 37
-
-#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
-
-/*
- * Modem Management Controller (MMC) read structure.
- */
-typedef struct mmr_t mmr_t;
-struct mmr_t
-{
- unsigned char mmr_unused0[8]; /* unused */
- unsigned char mmr_des_status; /* encryption status */
- unsigned char mmr_des_avail; /* encryption available (0x55 read) */
-#define MMR_DES_AVAIL_DES 0x55 /* DES available */
-#define MMR_DES_AVAIL_AES 0x33 /* AES (AT&T) available */
- unsigned char mmr_des_io_invert; /* des I/O invert register */
- unsigned char mmr_unused1[5]; /* unused */
- unsigned char mmr_dce_status; /* DCE status */
-#define MMR_DCE_STATUS_RX_BUSY 0x01 /* receiver busy */
-#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */
-#define MMR_DCE_STATUS_TX_BUSY 0x04 /* transmitter on */
-#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */
-#define MMR_DCE_STATUS 0x0F /* mask to get the bits */
- unsigned char mmr_dsp_id; /* DSP ID (AA = Daedalus rev A) */
- unsigned char mmr_unused2[2]; /* unused */
- unsigned char mmr_correct_nwid_l; /* # of correct NWIDs rxd (low) */
- unsigned char mmr_correct_nwid_h; /* # of correct NWIDs rxd (high) */
- /* Warning: read high-order octet first! */
- unsigned char mmr_wrong_nwid_l; /* # of wrong NWIDs rxd (low) */
- unsigned char mmr_wrong_nwid_h; /* # of wrong NWIDs rxd (high) */
- unsigned char mmr_thr_pre_set; /* level threshold preset */
-#define MMR_THR_PRE_SET 0x3F /* level threshold preset */
-#define MMR_THR_PRE_SET_CUR 0x80 /* Current signal above it */
- unsigned char mmr_signal_lvl; /* signal level */
-#define MMR_SIGNAL_LVL 0x3F /* signal level */
-#define MMR_SIGNAL_LVL_VALID 0x80 /* Updated since last read */
- unsigned char mmr_silence_lvl; /* silence level (noise) */
-#define MMR_SILENCE_LVL 0x3F /* silence level */
-#define MMR_SILENCE_LVL_VALID 0x80 /* Updated since last read */
- unsigned char mmr_sgnl_qual; /* signal quality */
-#define MMR_SGNL_QUAL 0x0F /* signal quality */
-#define MMR_SGNL_QUAL_ANT 0x80 /* current antenna used */
- unsigned char mmr_netw_id_l; /* NWID low order byte (?) */
- unsigned char mmr_unused3[3]; /* unused */
-
- /* 2.0 Hardware extension - frequency selection support */
- unsigned char mmr_fee_status; /* Status of frequency EEPROM */
-#define MMR_FEE_STATUS_ID 0xF0 /* Modem revision ID */
-#define MMR_FEE_STATUS_DWLD 0x08 /* Download in progress */
-#define MMR_FEE_STATUS_BUSY 0x04 /* EEPROM busy */
- unsigned char mmr_unused4[1]; /* unused */
- unsigned char mmr_fee_data_l; /* Read data from EEPROM (low) */
- unsigned char mmr_fee_data_h; /* Read data from EEPROM (high) */
-};
-
-#define MMR_SIZE 36
-
-#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
-
-/* Make the two above structures one */
-typedef union mm_t
-{
- struct mmw_t w; /* Write to the mmc */
- struct mmr_t r; /* Read from the mmc */
-} mm_t;
-
-#endif /* _WAVELAN_H */
-
-/*
- * This software may only be used and distributed
- * according to the terms of the GNU General Public License.
- *
- * For more details, see wavelan.c.
- */
+++ /dev/null
-/*
- * WaveLAN ISA driver
- *
- * Jean II - HPLB '96
- *
- * Reorganisation and extension of the driver.
- *
- * This file contains all definitions and declarations necessary for the
- * WaveLAN ISA driver. This file is a private header, so it should
- * be included only in wavelan.c!
- */
-
-#ifndef WAVELAN_P_H
-#define WAVELAN_P_H
-
-/************************** DOCUMENTATION ***************************/
-/*
- * This driver provides a Linux interface to the WaveLAN ISA hardware.
- * The WaveLAN is a product of Lucent (http://www.wavelan.com/).
- * This division was formerly part of NCR and then AT&T.
- * WaveLANs are also distributed by DEC (RoamAbout DS) and Digital Ocean.
- *
- * To learn how to use this driver, read the NET3 HOWTO.
- * If you want to exploit the many other functionalities, read the comments
- * in the code.
- *
- * This driver is the result of the effort of many people (see below).
- */
-
-/* ------------------------ SPECIFIC NOTES ------------------------ */
-/*
- * Web page
- * --------
- * I try to maintain a web page with the Wireless LAN Howto at :
- * http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
- *
- * SMP
- * ---
- * We now are SMP compliant (I eventually fixed the remaining bugs).
- * The driver has been tested on a dual P6-150 and survived my usual
- * set of torture tests.
- * Anyway, I spent enough time chasing interrupt re-entrancy during
- * errors or reconfigure, and I designed the locked/unlocked sections
- * of the driver with great care, and with the recent addition of
- * the spinlock (thanks to the new API), we should be quite close to
- * the truth.
- * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast),
- * but better safe than sorry (especially at 2 Mb/s ;-).
- *
- * I have also looked into disabling only our interrupt on the card
- * (via HACR) instead of all interrupts in the processor (via cli),
- * so that other driver are not impacted, and it look like it's
- * possible, but it's very tricky to do right (full of races). As
- * the gain would be mostly for SMP systems, it can wait...
- *
- * Debugging and options
- * ---------------------
- * You will find below a set of '#define" allowing a very fine control
- * on the driver behaviour and the debug messages printed.
- * The main options are :
- * o SET_PSA_CRC, to have your card correctly recognised by
- * an access point and the Point-to-Point diagnostic tool.
- * o USE_PSA_CONFIG, to read configuration from the PSA (EEprom)
- * (otherwise we always start afresh with some defaults)
- *
- * wavelan.o is too darned big
- * ---------------------------
- * That's true! There is a very simple way to reduce the driver
- * object by 33%! Comment out the following line:
- * #include <linux/wireless.h>
- * Other compile options can also reduce the size of it...
- *
- * MAC address and hardware detection:
- * -----------------------------------
- * The detection code for the WaveLAN checks that the first three
- * octets of the MAC address fit the company code. This type of
- * detection works well for AT&T cards (because the AT&T code is
- * hardcoded in wavelan.h), but of course will fail for other
- * manufacturers.
- *
- * If you are sure that your card is derived from the WaveLAN,
- * here is the way to configure it:
- * 1) Get your MAC address
- * a) With your card utilities (wfreqsel, instconf, etc.)
- * b) With the driver:
- * o compile the kernel with DEBUG_CONFIG_INFO enabled
- * o Boot and look the card messages
- * 2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h)
- * 3) Compile and verify
- * 4) Send me the MAC code. I will include it in the next version.
- *
- */
-
-/* --------------------- WIRELESS EXTENSIONS --------------------- */
-/*
- * This driver is the first to support "wireless extensions".
- * This set of extensions provides a standard way to control the wireless
- * characteristics of the hardware. Applications such as mobile IP may
- * take advantage of it.
- *
- * You will need to enable the CONFIG_NET_RADIO define in the kernel
- * configuration to enable the wireless extensions (this is the one
- * giving access to the radio network device choice).
- *
- * It might also be a good idea as well to fetch the wireless tools to
- * configure the device and play a bit.
- */
-
-/* ---------------------------- FILES ---------------------------- */
-/*
- * wavelan.c: actual code for the driver: C functions
- *
- * wavelan.p.h: private header: local types and variables for driver
- *
- * wavelan.h: description of the hardware interface and structs
- *
- * i82586.h: description of the Ethernet controller
- */
-
-/* --------------------------- HISTORY --------------------------- */
-/*
- * This is based on information in the drivers' headers. It may not be
- * accurate, and I guarantee only my best effort.
- *
- * The history of the WaveLAN drivers is as complicated as the history of
- * the WaveLAN itself (NCR -> AT&T -> Lucent).
- *
- * It all started with Anders Klemets <klemets@paul.rutgers.edu>
- * writing a WaveLAN ISA driver for the Mach microkernel. Girish
- * Welling <welling@paul.rutgers.edu> had also worked on it.
- * Keith Moore modified this for the PCMCIA hardware.
- *
- * Robert Morris <rtm@das.harvard.edu> ported these two drivers to BSDI
- * and added specific PCMCIA support (there is currently no equivalent
- * of the PCMCIA package under BSD).
- *
- * Jim Binkley <jrb@cs.pdx.edu> ported both BSDI drivers to FreeBSD.
- *
- * Bruce Janson <bruce@cs.usyd.edu.au> ported the BSDI ISA driver to Linux.
- *
- * Anthony D. Joseph <adj@lcs.mit.edu> started to modify Bruce's driver
- * (with help of the BSDI PCMCIA driver) for PCMCIA.
- * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished this work.
- * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
- * 2.00 cards correctly (2.4 GHz with frequency selection).
- * David Hinds <dhinds@hyper.stanford.edu> integrated the whole in his
- * PCMCIA package (and bug corrections).
- *
- * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
- * patches to the PCMCIA driver. Later, I added code in the ISA driver
- * for Wireless Extensions and full support of frequency selection
- * cards. Then, I did the same to the PCMCIA driver, and did some
- * reorganisation. Finally, I came back to the ISA driver to
- * upgrade it at the same level as the PCMCIA one and reorganise
- * the code.
- * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
- * much needed information on the WaveLAN hardware.
- */
-
-/* The original copyrights and literature mention others' names and
- * credits. I don't know what their part in this development was.
- */
-
-/* By the way, for the copyright and legal stuff:
- * almost everybody wrote code under the GNU or BSD license (or similar),
- * and want their original copyright to remain somewhere in the
- * code (for myself, I go with the GPL).
- * Nobody wants to take responsibility for anything, except the fame.
- */
-
-/* --------------------------- CREDITS --------------------------- */
-/*
- * This software was developed as a component of the
- * Linux operating system.
- * It is based on other device drivers and information
- * either written or supplied by:
- * Ajay Bakre <bakre@paul.rutgers.edu>,
- * Donald Becker <becker@cesdis.gsfc.nasa.gov>,
- * Loeke Brederveld <Loeke.Brederveld@Utrecht.NCR.com>,
- * Brent Elphick <belphick@uwaterloo.ca>,
- * Anders Klemets <klemets@it.kth.se>,
- * Vladimir V. Kolpakov <w@stier.koenig.ru>,
- * Marc Meertens <Marc.Meertens@Utrecht.NCR.com>,
- * Pauline Middelink <middelin@polyware.iaf.nl>,
- * Robert Morris <rtm@das.harvard.edu>,
- * Jean Tourrilhes <jt@hpl.hp.com>,
- * Girish Welling <welling@paul.rutgers.edu>,
- * Clark Woodworth <clark@hiway1.exit109.com>
- * Yongguang Zhang <ygz@isl.hrl.hac.com>
- *
- * Thanks go also to:
- * James Ashton <jaa101@syseng.anu.edu.au>,
- * Alan Cox <alan@redhat.com>,
- * Allan Creighton <allanc@cs.usyd.edu.au>,
- * Matthew Geier <matthew@cs.usyd.edu.au>,
- * Remo di Giovanni <remo@cs.usyd.edu.au>,
- * Eckhard Grah <grah@wrcs1.urz.uni-wuppertal.de>,
- * Vipul Gupta <vgupta@cs.binghamton.edu>,
- * Mark Hagan <mhagan@wtcpost.daytonoh.NCR.COM>,
- * Tim Nicholson <tim@cs.usyd.edu.au>,
- * Ian Parkin <ian@cs.usyd.edu.au>,
- * John Rosenberg <johnr@cs.usyd.edu.au>,
- * George Rossi <george@phm.gov.au>,
- * Arthur Scott <arthur@cs.usyd.edu.au>,
- * Stanislav Sinyagin <stas@isf.ru>
- * and Peter Storey for their assistance and advice.
- *
- * Additional Credits:
- *
- * My development has been done initially under Debian 1.1 (Linux 2.0.x)
- * and now under Debian 2.2, initially with an HP Vectra XP/60, and now
- * an HP Vectra XP/90.
- *
- */
-
-/* ------------------------- IMPROVEMENTS ------------------------- */
-/*
- * I proudly present:
- *
- * Changes made in first pre-release:
- * ----------------------------------
- * - reorganisation of the code, function name change
- * - creation of private header (wavelan.p.h)
- * - reorganised debug messages
- * - more comments, history, etc.
- * - mmc_init: configure the PSA if not done
- * - mmc_init: correct default value of level threshold for PCMCIA
- * - mmc_init: 2.00 detection better code for 2.00 initialization
- * - better info at startup
- * - IRQ setting (note: this setting is permanent)
- * - watchdog: change strategy (and solve module removal problems)
- * - add wireless extensions (ioctl and get_wireless_stats)
- * get/set nwid/frequency on fly, info for /proc/net/wireless
- * - more wireless extensions: SETSPY and GETSPY
- * - make wireless extensions optional
- * - private ioctl to set/get quality and level threshold, histogram
- * - remove /proc/net/wavelan
- * - suppress useless stuff from lp (net_local)
- * - kernel 2.1 support (copy_to/from_user instead of memcpy_to/fromfs)
- * - add message level (debug stuff in /var/adm/debug and errors not
- * displayed at console and still in /var/adm/messages)
- * - multi device support
- * - start fixing the probe (init code)
- * - more inlines
- * - man page
- * - many other minor details and cleanups
- *
- * Changes made in second pre-release:
- * -----------------------------------
- * - clean up init code (probe and module init)
- * - better multiple device support (module)
- * - name assignment (module)
- *
- * Changes made in third pre-release:
- * ----------------------------------
- * - be more conservative on timers
- * - preliminary support for multicast (I still lack some details)
- *
- * Changes made in fourth pre-release:
- * -----------------------------------
- * - multicast (revisited and finished)
- * - avoid reset in set_multicast_list (a really big hack)
- * if somebody could apply this code for other i82586 based drivers
- * - share onboard memory 75% RU and 25% CU (instead of 50/50)
- *
- * Changes made for release in 2.1.15:
- * -----------------------------------
- * - change the detection code for multi manufacturer code support
- *
- * Changes made for release in 2.1.17:
- * -----------------------------------
- * - update to wireless extensions changes
- * - silly bug in card initial configuration (psa_conf_status)
- *
- * Changes made for release in 2.1.27 & 2.0.30:
- * --------------------------------------------
- * - small bug in debug code (probably not the last one...)
- * - remove extern keyword for wavelan_probe()
- * - level threshold is now a standard wireless extension (version 4 !)
- * - modules parameters types (new module interface)
- *
- * Changes made for release in 2.1.36:
- * -----------------------------------
- * - byte count stats (courtesy of David Hinds)
- * - remove dev_tint stuff (courtesy of David Hinds)
- * - encryption setting from Brent Elphick (thanks a lot!)
- * - 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
- *
- * Other changes (not by me) :
- * -------------------------
- * - Spelling and gramar "rectification".
- *
- * Changes made for release in 2.0.37 & 2.2.2 :
- * ------------------------------------------
- * - Correct status in /proc/net/wireless
- * - Set PSA CRC to make PtP diagnostic tool happy (Bob Gray)
- * - Module init code don't fail if we found at least one card in
- * the address list (Karlis Peisenieks)
- * - Missing parenthesis (Christopher Peterson)
- * - Correct i82586 configuration parameters
- * - Encryption initialisation bug (Robert McCormack)
- * - New mac addresses detected in the probe
- * - Increase watchdog for busy environments
- *
- * Changes made for release in 2.0.38 & 2.2.7 :
- * ------------------------------------------
- * - Correct the reception logic to better report errors and avoid
- * sending bogus packet up the stack
- * - Delay RU config to avoid corrupting first received packet
- * - Change config completion code (to actually check something)
- * - Avoid reading out of bound in skbuf to transmit
- * - Rectify a lot of (useless) debugging code
- * - Change the way to `#ifdef SET_PSA_CRC'
- *
- * Changes made for release in 2.2.11 & 2.3.13 :
- * -------------------------------------------
- * - Change e-mail and web page addresses
- * - Watchdog timer is now correctly expressed in HZ, not in jiffies
- * - Add channel number to the list of frequencies in range
- * - Add the (short) list of bit-rates in range
- * - Developp a new sensitivity... (sens.value & sens.fixed)
- *
- * Changes made for release in 2.2.14 & 2.3.23 :
- * -------------------------------------------
- * - Fix check for root permission (break instead of exit)
- * - New nwid & encoding setting (Wireless Extension 9)
- *
- * Changes made for release in 2.3.49 :
- * ----------------------------------
- * - Indentation reformating (Alan)
- * - Update to new network API (softnet - 2.3.43) :
- * o replace dev->tbusy (Alan)
- * o replace dev->tstart (Alan)
- * o remove dev->interrupt (Alan)
- * o add SMP locking via spinlock in splxx (me)
- * o add spinlock in interrupt handler (me)
- * o use kernel watchdog instead of ours (me)
- * o increase watchdog timeout (kernel is more sensitive) (me)
- * o verify that all the changes make sense and work (me)
- * - Fixup a potential gotcha when reconfiguring and thighten a bit
- * the interactions with Tx queue.
- *
- * Changes made for release in 2.4.0 :
- * ---------------------------------
- * - Fix spinlock stupid bugs that I left in. The driver is now SMP
- * compliant and doesn't lockup at startup.
- *
- * Wishes & dreams:
- * ----------------
- * - roaming (see Pcmcia driver)
- */
-
-/***************************** INCLUDES *****************************/
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/stat.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/uaccess.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <linux/timer.h>
-#include <linux/init.h>
-
-#include <linux/wireless.h> /* Wireless extensions */
-
-/* WaveLAN declarations */
-#include "i82586.h"
-#include "wavelan.h"
-
-/************************** DRIVER OPTIONS **************************/
-/*
- * `#define' or `#undef' the following constant to change the behaviour
- * of the driver...
- */
-#undef SET_PSA_CRC /* Calculate and set the CRC on PSA (slower) */
-#define USE_PSA_CONFIG /* Use info from the PSA. */
-#undef STRUCT_CHECK /* Verify padding of structures. */
-#undef EEPROM_IS_PROTECTED /* doesn't seem to be necessary */
-#define MULTICAST_AVOID /* Avoid extra multicast (I'm sceptical). */
-#undef SET_MAC_ADDRESS /* Experimental */
-
-#ifdef WIRELESS_EXT /* If wireless extensions exist in the kernel */
-/* Warning: this stuff will slow down the driver. */
-#define WIRELESS_SPY /* Enable spying addresses. */
-#undef HISTOGRAM /* Enable histogram of signal level. */
-#endif
-
-/****************************** DEBUG ******************************/
-
-#undef DEBUG_MODULE_TRACE /* module insertion/removal */
-#undef DEBUG_CALLBACK_TRACE /* calls made by Linux */
-#undef DEBUG_INTERRUPT_TRACE /* calls to handler */
-#undef DEBUG_INTERRUPT_INFO /* type of interrupt and so on */
-#define DEBUG_INTERRUPT_ERROR /* problems */
-#undef DEBUG_CONFIG_TRACE /* Trace the config functions. */
-#undef DEBUG_CONFIG_INFO /* what's going on */
-#define DEBUG_CONFIG_ERROR /* errors on configuration */
-#undef DEBUG_TX_TRACE /* transmission calls */
-#undef DEBUG_TX_INFO /* header of the transmitted packet */
-#undef DEBUG_TX_FAIL /* Normal failure conditions */
-#define DEBUG_TX_ERROR /* Unexpected conditions */
-#undef DEBUG_RX_TRACE /* transmission calls */
-#undef DEBUG_RX_INFO /* header of the received packet */
-#undef DEBUG_RX_FAIL /* Normal failure conditions */
-#define DEBUG_RX_ERROR /* Unexpected conditions */
-
-#undef DEBUG_PACKET_DUMP /* Dump packet on the screen if defined to 32. */
-#undef DEBUG_IOCTL_TRACE /* misc. call by Linux */
-#undef DEBUG_IOCTL_INFO /* various debugging info */
-#define DEBUG_IOCTL_ERROR /* what's going wrong */
-#define DEBUG_BASIC_SHOW /* Show basic startup info. */
-#undef DEBUG_VERSION_SHOW /* Print version info. */
-#undef DEBUG_PSA_SHOW /* Dump PSA to screen. */
-#undef DEBUG_MMC_SHOW /* Dump mmc to screen. */
-#undef DEBUG_SHOW_UNUSED /* Show unused fields too. */
-#undef DEBUG_I82586_SHOW /* Show i82586 status. */
-#undef DEBUG_DEVICE_SHOW /* Show device parameters. */
-
-/************************ CONSTANTS & MACROS ************************/
-
-#ifdef DEBUG_VERSION_SHOW
-static const char *version = "wavelan.c : v23 (SMP + wireless extensions) 05/10/00\n";
-#endif
-
-/* Watchdog temporisation */
-#define WATCHDOG_JIFFIES (512*HZ/100)
-
-/* Macro to get the number of elements in an array */
-#define NELS(a) (sizeof(a) / sizeof(a[0]))
-
-/* ------------------------ PRIVATE IOCTL ------------------------ */
-
-#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */
-#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */
-#define SIOCSIPLTHR SIOCIWFIRSTPRIV + 2 /* Set level threshold */
-#define SIOCGIPLTHR SIOCIWFIRSTPRIV + 3 /* Get level threshold */
-
-#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */
-#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 7 /* Get histogram values */
-
-/****************************** TYPES ******************************/
-
-/* Shortcuts */
-typedef struct net_device device;
-typedef struct net_device_stats en_stats;
-typedef struct iw_statistics iw_stats;
-typedef struct iw_quality iw_qual;
-typedef struct iw_freq iw_freq;
-typedef struct net_local net_local;
-typedef struct timer_list timer_list;
-
-/* Basic types */
-typedef u_char mac_addr[WAVELAN_ADDR_SIZE]; /* Hardware address */
-
-/*
- * Static specific data for the interface.
- *
- * For each network interface, Linux keeps data in two structures: "device"
- * keeps the generic data (same format for everybody) and "net_local" keeps
- * additional specific data.
- * Note that some of this specific data is in fact generic (en_stats, for
- * example).
- */
-struct net_local
-{
- net_local * next; /* linked list of the devices */
- device * dev; /* reverse link */
- spinlock_t spinlock; /* Serialize access to the hardware (SMP) */
- en_stats stats; /* Ethernet interface statistics */
- int nresets; /* number of hardware resets */
- u_char reconfig_82586; /* We need to reconfigure the controller. */
- u_char promiscuous; /* promiscuous mode */
- int mc_count; /* number of multicast addresses */
- u_short hacr; /* current host interface state */
-
- int tx_n_in_use;
- u_short rx_head;
- u_short rx_last;
- u_short tx_first_free;
- u_short tx_first_in_use;
-
-#ifdef WIRELESS_EXT
- iw_stats wstats; /* Wireless-specific statistics */
-#endif
-
-#ifdef WIRELESS_SPY
- int spy_number; /* number of addresses to spy */
- mac_addr spy_address[IW_MAX_SPY]; /* the addresses to spy */
- iw_qual spy_stat[IW_MAX_SPY]; /* statistics gathered */
-#endif /* WIRELESS_SPY */
-
-#ifdef HISTOGRAM
- int his_number; /* number of intervals */
- u_char his_range[16]; /* boundaries of interval ]n-1; n] */
- u_long his_sum[16]; /* sum in interval */
-#endif /* HISTOGRAM */
-};
-
-/**************************** PROTOTYPES ****************************/
-
-/* ----------------------- MISC. SUBROUTINES ------------------------ */
-static inline void
- wv_splhi(net_local *, /* Disable interrupts, lock driver */
- unsigned long *); /* flags */
-static inline void
- wv_splx(net_local *, /* Enable interrupts, unlock driver */
- unsigned long *); /* flags */
-static u_char
- wv_irq_to_psa(int);
-static int
- wv_psa_to_irq(u_char);
-/* ------------------- HOST ADAPTER SUBROUTINES ------------------- */
-static inline u_short /* data */
- hasr_read(u_long); /* Read the host interface: base address */
-static inline void
- hacr_write(u_long, /* Write to host interface: base address */
- u_short), /* data */
- hacr_write_slow(u_long,
- u_short),
- set_chan_attn(u_long, /* ioaddr */
- u_short), /* hacr */
- wv_hacr_reset(u_long), /* ioaddr */
- wv_16_off(u_long, /* ioaddr */
- u_short), /* hacr */
- wv_16_on(u_long, /* ioaddr */
- u_short), /* hacr */
- wv_ints_off(device *),
- wv_ints_on(device *);
-/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
-static void
- psa_read(u_long, /* Read the Parameter Storage Area. */
- u_short, /* hacr */
- int, /* offset in PSA */
- u_char *, /* buffer to fill */
- int), /* size to read */
- psa_write(u_long, /* Write to the PSA. */
- u_short, /* hacr */
- int, /* offset in PSA */
- u_char *, /* buffer in memory */
- int); /* length of buffer */
-static inline void
- mmc_out(u_long, /* Write 1 byte to the Modem Manag Control. */
- u_short,
- u_char),
- mmc_write(u_long, /* Write n bytes to the MMC. */
- u_char,
- u_char *,
- int);
-static inline u_char /* Read 1 byte from the MMC. */
- mmc_in(u_long,
- u_short);
-static inline void
- mmc_read(u_long, /* Read n bytes from the MMC. */
- u_char,
- u_char *,
- int),
- fee_wait(u_long, /* Wait for frequency EEPROM: base address */
- int, /* base delay to wait for */
- int); /* time to wait */
-static void
- fee_read(u_long, /* Read the frequency EEPROM: base address */
- u_short, /* destination offset */
- u_short *, /* data buffer */
- int); /* number of registers */
-/* ---------------------- I82586 SUBROUTINES ----------------------- */
-static /*inline*/ void
- obram_read(u_long, /* ioaddr */
- u_short, /* o */
- u_char *, /* b */
- int); /* n */
-static inline void
- obram_write(u_long, /* ioaddr */
- u_short, /* o */
- u_char *, /* b */
- int); /* n */
-static void
- wv_ack(device *);
-static inline int
- wv_synchronous_cmd(device *,
- const char *),
- wv_config_complete(device *,
- u_long,
- net_local *);
-static int
- wv_complete(device *,
- u_long,
- net_local *);
-static inline void
- wv_82586_reconfig(device *);
-/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */
-#ifdef DEBUG_I82586_SHOW
-static void
- wv_scb_show(unsigned short);
-#endif
-static inline void
- wv_init_info(device *); /* display startup info */
-/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
-static en_stats *
- wavelan_get_stats(device *); /* Give stats /proc/net/dev */
-static void
- wavelan_set_multicast_list(device *);
-/* ----------------------- PACKET RECEPTION ----------------------- */
-static inline void
- wv_packet_read(device *, /* Read a packet from a frame. */
- u_short,
- int),
- wv_receive(device *); /* Read all packets waiting. */
-/* --------------------- PACKET TRANSMISSION --------------------- */
-static inline int
- wv_packet_write(device *, /* Write a packet to the Tx buffer. */
- void *,
- short);
-static int
- wavelan_packet_xmit(struct sk_buff *, /* Send a packet. */
- device *);
-/* -------------------- HARDWARE CONFIGURATION -------------------- */
-static inline int
- wv_mmc_init(device *), /* Initialize the modem. */
- wv_ru_start(device *), /* Start the i82586 receiver unit. */
- wv_cu_start(device *), /* Start the i82586 command unit. */
- wv_82586_start(device *); /* Start the i82586. */
-static void
- wv_82586_config(device *); /* Configure the i82586. */
-static inline void
- wv_82586_stop(device *);
-static int
- wv_hw_reset(device *), /* Reset the WaveLAN hardware. */
- wv_check_ioaddr(u_long, /* ioaddr */
- u_char *); /* mac address (read) */
-/* ---------------------- INTERRUPT HANDLING ---------------------- */
-static void
- wavelan_interrupt(int, /* interrupt handler */
- void *,
- struct pt_regs *);
-static void
- wavelan_watchdog(device *); /* transmission watchdog */
-/* ------------------- CONFIGURATION CALLBACKS ------------------- */
-static int
- wavelan_open(device *), /* Open the device. */
- wavelan_close(device *), /* Close the device. */
- wavelan_config(device *); /* Configure one device. */
-extern int
- wavelan_probe(device *); /* See Space.c. */
-
-/**************************** VARIABLES ****************************/
-
-/*
- * This is the root of the linked list of WaveLAN drivers
- * It is use to verify that we don't reuse the same base address
- * for two different drivers and to clean up when removing the module.
- */
-static net_local * wavelan_list = (net_local *) NULL;
-
-/*
- * This table is used to translate the PSA value to IRQ number
- * and vice versa.
- */
-static u_char irqvals[] =
-{
- 0, 0, 0, 0x01,
- 0x02, 0x04, 0, 0x08,
- 0, 0, 0x10, 0x20,
- 0x40, 0, 0, 0x80,
-};
-
-/*
- * Table of the available I/O addresses (base addresses) for WaveLAN
- */
-static unsigned short iobase[] =
-{
-#if 0
- /* Leave out 0x3C0 for now -- seems to clash with some video
- * controllers.
- * Leave out the others too -- we will always use 0x390 and leave
- * 0x300 for the Ethernet device.
- * Jean II: 0x3E0 is fine as well.
- */
- 0x300, 0x390, 0x3E0, 0x3C0
-#endif /* 0 */
- 0x390, 0x3E0
-};
-
-#ifdef MODULE
-/* Parameters set by insmod */
-static int io[4];
-static int irq[4];
-static char name[4][IFNAMSIZ];
-MODULE_PARM(io, "1-4i");
-MODULE_PARM(irq, "1-4i");
-MODULE_PARM(name, "1-4c" __MODULE_STRING(IFNAMSIZ));
-MODULE_PARM_DESC(io, "WaveLAN I/O base address(es),required");
-MODULE_PARM_DESC(irq, "WaveLAN IRQ number(s)");
-MODULE_PARM_DESC(name, "WaveLAN interface neme(s)");
-#endif /* MODULE */
-
-#endif /* WAVELAN_P_H */
# Wireless LAN device configuration
#
+comment 'Wireless ISA/PCI cards support'
+
+# Good old obsolete Wavelan.
+tristate ' AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support' CONFIG_WAVELAN
+
+# 802.11b cards
if [ "$CONFIG_ISA" = "y" -o "$CONFIG_PCI" = "y" ]; then
tristate ' Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards' CONFIG_AIRO
fi
# If Pcmcia is compiled in, offer Pcmcia cards...
if [ "$CONFIG_PCMCIA" != "n" ]; then
- comment 'Wireless Pcmcia cards support'
+ comment 'Wireless Pcmcia/Cardbus cards support'
+
+# Obsolete cards
+ dep_tristate ' Xircom Netwave AirSurfer Pcmcia wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA
+ dep_tristate ' AT&T/Lucent old Wavelan Pcmcia wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA
+# 802.11b cards
dep_tristate ' Hermes PCMCIA card support' CONFIG_PCMCIA_HERMES $CONFIG_HERMES
tristate ' Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards' CONFIG_AIRO_CS
fi
# Things that need to export symbols
export-objs := airo.o orinoco.o hermes.o
+# Obsolete cards
+obj-$(CONFIG_WAVELAN) += wavelan.o
+obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o
+obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o
+
obj-$(CONFIG_HERMES) += orinoco.o hermes.o
obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o
obj-$(CONFIG_APPLE_AIRPORT) += airport.o
--- /dev/null
+/*
+ * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor.
+ *
+ * See:
+ * Intel Microcommunications 1991
+ * p1-1 to p1-37
+ * Intel order No. 231658
+ * ISBN 1-55512-119-5
+ *
+ * Unfortunately, the above chapter mentions neither
+ * the System Configuration Pointer (SCP) nor the
+ * Intermediate System Configuration Pointer (ISCP),
+ * so we probably need to look elsewhere for the
+ * whole story -- some recommend the "Intel LAN
+ * Components manual" but I have neither a copy
+ * nor a full reference. But "elsewhere" may be
+ * in the same publication...
+ * The description of a later device, the
+ * "82596CA High-Performance 32-Bit Local Area Network
+ * Coprocessor", (ibid. p1-38 to p1-109) does mention
+ * the SCP and ISCP and also has an i82586 compatibility
+ * mode. Even more useful is "AP-235 An 82586 Data Link
+ * Driver" (ibid. p1-337 to p1-417).
+ */
+
+#define I82586_MEMZ (64 * 1024)
+
+#define I82586_SCP_ADDR (I82586_MEMZ - sizeof(scp_t))
+
+#define ADDR_LEN 6
+#define I82586NULL 0xFFFF
+
+#define toff(t,p,f) (unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * System Configuration Pointer (SCP).
+ */
+typedef struct scp_t scp_t;
+struct scp_t
+{
+ unsigned short scp_sysbus; /* 82586 bus width: */
+#define SCP_SY_16BBUS (0x0 << 0) /* 16 bits */
+#define SCP_SY_8BBUS (0x1 << 0) /* 8 bits. */
+ unsigned short scp_junk[2]; /* Unused */
+ unsigned short scp_iscpl; /* lower 16 bits of ISCP_ADDR */
+ unsigned short scp_iscph; /* upper 16 bits of ISCP_ADDR */
+};
+
+/*
+ * Intermediate System Configuration Pointer (ISCP).
+ */
+typedef struct iscp_t iscp_t;
+struct iscp_t
+{
+ unsigned short iscp_busy; /* set by CPU before first CA, */
+ /* cleared by 82586 after read. */
+ unsigned short iscp_offset; /* offset of SCB */
+ unsigned short iscp_basel; /* base of SCB */
+ unsigned short iscp_baseh; /* " */
+};
+
+/*
+ * System Control Block (SCB).
+ * The 82586 writes its status to scb_status and then
+ * raises an interrupt to alert the CPU.
+ * The CPU writes a command to scb_command and
+ * then issues a Channel Attention (CA) to alert the 82586.
+ */
+typedef struct scb_t scb_t;
+struct scb_t
+{
+ unsigned short scb_status; /* Status of 82586 */
+#define SCB_ST_INT (0xF << 12) /* Some of: */
+#define SCB_ST_CX (0x1 << 15) /* Cmd completed */
+#define SCB_ST_FR (0x1 << 14) /* Frame received */
+#define SCB_ST_CNA (0x1 << 13) /* Cmd unit not active */
+#define SCB_ST_RNR (0x1 << 12) /* Rcv unit not ready */
+#define SCB_ST_JUNK0 (0x1 << 11) /* 0 */
+#define SCB_ST_CUS (0x7 << 8) /* Cmd unit status */
+#define SCB_ST_CUS_IDLE (0 << 8) /* Idle */
+#define SCB_ST_CUS_SUSP (1 << 8) /* Suspended */
+#define SCB_ST_CUS_ACTV (2 << 8) /* Active */
+#define SCB_ST_JUNK1 (0x1 << 7) /* 0 */
+#define SCB_ST_RUS (0x7 << 4) /* Rcv unit status */
+#define SCB_ST_RUS_IDLE (0 << 4) /* Idle */
+#define SCB_ST_RUS_SUSP (1 << 4) /* Suspended */
+#define SCB_ST_RUS_NRES (2 << 4) /* No resources */
+#define SCB_ST_RUS_RDY (4 << 4) /* Ready */
+ unsigned short scb_command; /* Next command */
+#define SCB_CMD_ACK_CX (0x1 << 15) /* Ack cmd completion */
+#define SCB_CMD_ACK_FR (0x1 << 14) /* Ack frame received */
+#define SCB_CMD_ACK_CNA (0x1 << 13) /* Ack CU not active */
+#define SCB_CMD_ACK_RNR (0x1 << 12) /* Ack RU not ready */
+#define SCB_CMD_JUNKX (0x1 << 11) /* Unused */
+#define SCB_CMD_CUC (0x7 << 8) /* Command Unit command */
+#define SCB_CMD_CUC_NOP (0 << 8) /* Nop */
+#define SCB_CMD_CUC_GO (1 << 8) /* Start cbl_offset */
+#define SCB_CMD_CUC_RES (2 << 8) /* Resume execution */
+#define SCB_CMD_CUC_SUS (3 << 8) /* Suspend " */
+#define SCB_CMD_CUC_ABT (4 << 8) /* Abort " */
+#define SCB_CMD_RESET (0x1 << 7) /* Reset chip (hardware) */
+#define SCB_CMD_RUC (0x7 << 4) /* Receive Unit command */
+#define SCB_CMD_RUC_NOP (0 << 4) /* Nop */
+#define SCB_CMD_RUC_GO (1 << 4) /* Start rfa_offset */
+#define SCB_CMD_RUC_RES (2 << 4) /* Resume reception */
+#define SCB_CMD_RUC_SUS (3 << 4) /* Suspend " */
+#define SCB_CMD_RUC_ABT (4 << 4) /* Abort " */
+ unsigned short scb_cbl_offset; /* Offset of first command unit */
+ /* Action Command */
+ unsigned short scb_rfa_offset; /* Offset of first Receive */
+ /* Frame Descriptor in the */
+ /* Receive Frame Area */
+ unsigned short scb_crcerrs; /* Properly aligned frames */
+ /* received with a CRC error */
+ unsigned short scb_alnerrs; /* Misaligned frames received */
+ /* with a CRC error */
+ unsigned short scb_rscerrs; /* Frames lost due to no space */
+ unsigned short scb_ovrnerrs; /* Frames lost due to slow bus */
+};
+
+#define scboff(p,f) toff(scb_t, p, f)
+
+/*
+ * The eight Action Commands.
+ */
+typedef enum acmd_e acmd_e;
+enum acmd_e
+{
+ acmd_nop = 0, /* Do nothing */
+ acmd_ia_setup = 1, /* Load an (ethernet) address into the */
+ /* 82586 */
+ acmd_configure = 2, /* Update the 82586 operating parameters */
+ acmd_mc_setup = 3, /* Load a list of (ethernet) multicast */
+ /* addresses into the 82586 */
+ acmd_transmit = 4, /* Transmit a frame */
+ acmd_tdr = 5, /* Perform a Time Domain Reflectometer */
+ /* test on the serial link */
+ acmd_dump = 6, /* Copy 82586 registers to memory */
+ acmd_diagnose = 7, /* Run an internal self test */
+};
+
+/*
+ * Generic Action Command header.
+ */
+typedef struct ach_t ach_t;
+struct ach_t
+{
+ unsigned short ac_status; /* Command status: */
+#define AC_SFLD_C (0x1 << 15) /* Command completed */
+#define AC_SFLD_B (0x1 << 14) /* Busy executing */
+#define AC_SFLD_OK (0x1 << 13) /* Completed error free */
+#define AC_SFLD_A (0x1 << 12) /* Command aborted */
+#define AC_SFLD_FAIL (0x1 << 11) /* Selftest failed */
+#define AC_SFLD_S10 (0x1 << 10) /* No carrier sense */
+ /* during transmission */
+#define AC_SFLD_S9 (0x1 << 9) /* Tx unsuccessful: */
+ /* (stopped) lost CTS */
+#define AC_SFLD_S8 (0x1 << 8) /* Tx unsuccessful: */
+ /* (stopped) slow DMA */
+#define AC_SFLD_S7 (0x1 << 7) /* Tx deferred: */
+ /* other link traffic */
+#define AC_SFLD_S6 (0x1 << 6) /* Heart Beat: collision */
+ /* detect after last tx */
+#define AC_SFLD_S5 (0x1 << 5) /* Tx stopped: */
+ /* excessive collisions */
+#define AC_SFLD_MAXCOL (0xF << 0) /* Collision count */
+ unsigned short ac_command; /* Command specifier: */
+#define AC_CFLD_EL (0x1 << 15) /* End of command list */
+#define AC_CFLD_S (0x1 << 14) /* Suspend on completion */
+#define AC_CFLD_I (0x1 << 13) /* Interrupt on completion */
+#define AC_CFLD_CMD (0x7 << 0) /* acmd_e */
+ unsigned short ac_link; /* Next Action Command */
+};
+
+#define acoff(p,f) toff(ach_t, p, f)
+
+/*
+ * The Nop Action Command.
+ */
+typedef struct ac_nop_t ac_nop_t;
+struct ac_nop_t
+{
+ ach_t nop_h;
+};
+
+/*
+ * The IA-Setup Action Command.
+ */
+typedef struct ac_ias_t ac_ias_t;
+struct ac_ias_t
+{
+ ach_t ias_h;
+ unsigned char ias_addr[ADDR_LEN]; /* The (ethernet) address */
+};
+
+/*
+ * The Configure Action Command.
+ */
+typedef struct ac_cfg_t ac_cfg_t;
+struct ac_cfg_t
+{
+ ach_t cfg_h;
+ unsigned char cfg_byte_cnt; /* Size foll data: 4-12 */
+#define AC_CFG_BYTE_CNT(v) (((v) & 0xF) << 0)
+ unsigned char cfg_fifolim; /* FIFO threshold */
+#define AC_CFG_FIFOLIM(v) (((v) & 0xF) << 0)
+ unsigned char cfg_byte8;
+#define AC_CFG_SAV_BF(v) (((v) & 0x1) << 7) /* Save rxd bad frames */
+#define AC_CFG_SRDY(v) (((v) & 0x1) << 6) /* SRDY/ARDY pin means */
+ /* external sync. */
+ unsigned char cfg_byte9;
+#define AC_CFG_ELPBCK(v) (((v) & 0x1) << 7) /* External loopback */
+#define AC_CFG_ILPBCK(v) (((v) & 0x1) << 6) /* Internal loopback */
+#define AC_CFG_PRELEN(v) (((v) & 0x3) << 4) /* Preamble length */
+#define AC_CFG_PLEN_2 0 /* 2 bytes */
+#define AC_CFG_PLEN_4 1 /* 4 bytes */
+#define AC_CFG_PLEN_8 2 /* 8 bytes */
+#define AC_CFG_PLEN_16 3 /* 16 bytes */
+#define AC_CFG_ALOC(v) (((v) & 0x1) << 3) /* Addr/len data is */
+ /* explicit in buffers */
+#define AC_CFG_ADDRLEN(v) (((v) & 0x7) << 0) /* Bytes per address */
+ unsigned char cfg_byte10;
+#define AC_CFG_BOFMET(v) (((v) & 0x1) << 7) /* Use alternate expo. */
+ /* backoff method */
+#define AC_CFG_ACR(v) (((v) & 0x7) << 4) /* Accelerated cont. res. */
+#define AC_CFG_LINPRIO(v) (((v) & 0x7) << 0) /* Linear priority */
+ unsigned char cfg_ifs; /* Interframe spacing */
+ unsigned char cfg_slotl; /* Slot time (low byte) */
+ unsigned char cfg_byte13;
+#define AC_CFG_RETRYNUM(v) (((v) & 0xF) << 4) /* Max. collision retry */
+#define AC_CFG_SLTTMHI(v) (((v) & 0x7) << 0) /* Slot time (high bits) */
+ unsigned char cfg_byte14;
+#define AC_CFG_FLGPAD(v) (((v) & 0x1) << 7) /* Pad with HDLC flags */
+#define AC_CFG_BTSTF(v) (((v) & 0x1) << 6) /* Do HDLC bitstuffing */
+#define AC_CFG_CRC16(v) (((v) & 0x1) << 5) /* 16 bit CCITT CRC */
+#define AC_CFG_NCRC(v) (((v) & 0x1) << 4) /* Insert no CRC */
+#define AC_CFG_TNCRS(v) (((v) & 0x1) << 3) /* Tx even if no carrier */
+#define AC_CFG_MANCH(v) (((v) & 0x1) << 2) /* Manchester coding */
+#define AC_CFG_BCDIS(v) (((v) & 0x1) << 1) /* Disable broadcast */
+#define AC_CFG_PRM(v) (((v) & 0x1) << 0) /* Promiscuous mode */
+ unsigned char cfg_byte15;
+#define AC_CFG_ICDS(v) (((v) & 0x1) << 7) /* Internal collision */
+ /* detect source */
+#define AC_CFG_CDTF(v) (((v) & 0x7) << 4) /* Collision detect */
+ /* filter in bit times */
+#define AC_CFG_ICSS(v) (((v) & 0x1) << 3) /* Internal carrier */
+ /* sense source */
+#define AC_CFG_CSTF(v) (((v) & 0x7) << 0) /* Carrier sense */
+ /* filter in bit times */
+ unsigned short cfg_min_frm_len;
+#define AC_CFG_MNFRM(v) (((v) & 0xFF) << 0) /* Min. bytes/frame (<= 255) */
+};
+
+/*
+ * The MC-Setup Action Command.
+ */
+typedef struct ac_mcs_t ac_mcs_t;
+struct ac_mcs_t
+{
+ ach_t mcs_h;
+ unsigned short mcs_cnt; /* No. of bytes of MC addresses */
+#if 0
+ unsigned char mcs_data[ADDR_LEN]; /* The first MC address .. */
+ ...
+#endif
+};
+
+#define I82586_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */
+
+/*
+ * The Transmit Action Command.
+ */
+typedef struct ac_tx_t ac_tx_t;
+struct ac_tx_t
+{
+ ach_t tx_h;
+ unsigned short tx_tbd_offset; /* Address of list of buffers. */
+#if 0
+Linux packets are passed down with the destination MAC address
+and length/type field already prepended to the data,
+so we do not need to insert it. Consistent with this
+we must also set the AC_CFG_ALOC(..) flag during the
+ac_cfg_t action command.
+ unsigned char tx_addr[ADDR_LEN]; /* The frame dest. address */
+ unsigned short tx_length; /* The frame length */
+#endif /* 0 */
+};
+
+/*
+ * The Time Domain Reflectometer Action Command.
+ */
+typedef struct ac_tdr_t ac_tdr_t;
+struct ac_tdr_t
+{
+ ach_t tdr_h;
+ unsigned short tdr_result; /* Result. */
+#define AC_TDR_LNK_OK (0x1 << 15) /* No link problem */
+#define AC_TDR_XCVR_PRB (0x1 << 14) /* Txcvr cable problem */
+#define AC_TDR_ET_OPN (0x1 << 13) /* Open on the link */
+#define AC_TDR_ET_SRT (0x1 << 12) /* Short on the link */
+#define AC_TDR_TIME (0x7FF << 0) /* Distance to problem */
+ /* site in transmit */
+ /* clock cycles */
+};
+
+/*
+ * The Dump Action Command.
+ */
+typedef struct ac_dmp_t ac_dmp_t;
+struct ac_dmp_t
+{
+ ach_t dmp_h;
+ unsigned short dmp_offset; /* Result. */
+};
+
+/*
+ * Size of the result of the dump command.
+ */
+#define DUMPBYTES 170
+
+/*
+ * The Diagnose Action Command.
+ */
+typedef struct ac_dgn_t ac_dgn_t;
+struct ac_dgn_t
+{
+ ach_t dgn_h;
+};
+
+/*
+ * Transmit Buffer Descriptor (TBD).
+ */
+typedef struct tbd_t tbd_t;
+struct tbd_t
+{
+ unsigned short tbd_status; /* Written by the CPU */
+#define TBD_STATUS_EOF (0x1 << 15) /* This TBD is the */
+ /* last for this frame */
+#define TBD_STATUS_ACNT (0x3FFF << 0) /* Actual count of data */
+ /* bytes in this buffer */
+ unsigned short tbd_next_bd_offset; /* Next in list */
+ unsigned short tbd_bufl; /* Buffer address (low) */
+ unsigned short tbd_bufh; /* " " (high) */
+};
+
+/*
+ * Receive Buffer Descriptor (RBD).
+ */
+typedef struct rbd_t rbd_t;
+struct rbd_t
+{
+ unsigned short rbd_status; /* Written by the 82586 */
+#define RBD_STATUS_EOF (0x1 << 15) /* This RBD is the */
+ /* last for this frame */
+#define RBD_STATUS_F (0x1 << 14) /* ACNT field is valid */
+#define RBD_STATUS_ACNT (0x3FFF << 0) /* Actual no. of data */
+ /* bytes in this buffer */
+ unsigned short rbd_next_rbd_offset; /* Next rbd in list */
+ unsigned short rbd_bufl; /* Data pointer (low) */
+ unsigned short rbd_bufh; /* " " (high) */
+ unsigned short rbd_el_size; /* EL+Data buf. size */
+#define RBD_EL (0x1 << 15) /* This BD is the */
+ /* last in the list */
+#define RBD_SIZE (0x3FFF << 0) /* No. of bytes the */
+ /* buffer can hold */
+};
+
+#define rbdoff(p,f) toff(rbd_t, p, f)
+
+/*
+ * Frame Descriptor (FD).
+ */
+typedef struct fd_t fd_t;
+struct fd_t
+{
+ unsigned short fd_status; /* Written by the 82586 */
+#define FD_STATUS_C (0x1 << 15) /* Completed storing frame */
+#define FD_STATUS_B (0x1 << 14) /* FD was consumed by RU */
+#define FD_STATUS_OK (0x1 << 13) /* Frame rxd successfully */
+#define FD_STATUS_S11 (0x1 << 11) /* CRC error */
+#define FD_STATUS_S10 (0x1 << 10) /* Alignment error */
+#define FD_STATUS_S9 (0x1 << 9) /* Ran out of resources */
+#define FD_STATUS_S8 (0x1 << 8) /* Rx DMA overrun */
+#define FD_STATUS_S7 (0x1 << 7) /* Frame too short */
+#define FD_STATUS_S6 (0x1 << 6) /* No EOF flag */
+ unsigned short fd_command; /* Command */
+#define FD_COMMAND_EL (0x1 << 15) /* Last FD in list */
+#define FD_COMMAND_S (0x1 << 14) /* Suspend RU after rx */
+ unsigned short fd_link_offset; /* Next FD */
+ unsigned short fd_rbd_offset; /* First RBD (data) */
+ /* Prepared by CPU, */
+ /* updated by 82586 */
+#if 0
+I think the rest is unused since we
+have set AC_CFG_ALOC(..). However, just
+in case, we leave the space.
+#endif /* 0 */
+ unsigned char fd_dest[ADDR_LEN]; /* Destination address */
+ /* Written by 82586 */
+ unsigned char fd_src[ADDR_LEN]; /* Source address */
+ /* Written by 82586 */
+ unsigned short fd_length; /* Frame length or type */
+ /* Written by 82586 */
+};
+
+#define fdoff(p,f) toff(fd_t, p, f)
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU General Public License.
+ *
+ * For more details, see wavelan.c.
+ */
--- /dev/null
+/*
+ * Definitions for Intel 82593 CSMA/CD Core LAN Controller
+ * The definitions are taken from the 1992 users manual with Intel
+ * order number 297125-001.
+ *
+ * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp
+ *
+ * Copyright 1994, Anders Klemets <klemets@it.kth.se>
+ *
+ * This software may be freely distributed for noncommercial purposes
+ * as long as this notice is retained.
+ *
+ * HISTORY
+ * i82593.h,v
+ * Revision 1.1 1996/07/17 15:23:12 root
+ * Initial revision
+ *
+ * Revision 1.3 1995/04/05 15:13:58 adj
+ * Initial alpha release
+ *
+ * Revision 1.2 1994/06/16 23:57:31 klemets
+ * Mirrored all the fields in the configuration block.
+ *
+ * Revision 1.1 1994/06/02 20:25:34 klemets
+ * Initial revision
+ *
+ *
+ */
+#ifndef _I82593_H
+#define _I82593_H
+
+/* Intel 82593 CSMA/CD Core LAN Controller */
+
+/* Port 0 Command Register definitions */
+
+/* Execution operations */
+#define OP0_NOP 0 /* CHNL = 0 */
+#define OP0_SWIT_TO_PORT_1 0 /* CHNL = 1 */
+#define OP0_IA_SETUP 1
+#define OP0_CONFIGURE 2
+#define OP0_MC_SETUP 3
+#define OP0_TRANSMIT 4
+#define OP0_TDR 5
+#define OP0_DUMP 6
+#define OP0_DIAGNOSE 7
+#define OP0_TRANSMIT_NO_CRC 9
+#define OP0_RETRANSMIT 12
+#define OP0_ABORT 13
+/* Reception operations */
+#define OP0_RCV_ENABLE 8
+#define OP0_RCV_DISABLE 10
+#define OP0_STOP_RCV 11
+/* Status pointer control operations */
+#define OP0_FIX_PTR 15 /* CHNL = 1 */
+#define OP0_RLS_PTR 15 /* CHNL = 0 */
+#define OP0_RESET 14
+
+#define CR0_CHNL (1 << 4) /* 0=Channel 0, 1=Channel 1 */
+#define CR0_STATUS_0 0x00
+#define CR0_STATUS_1 0x20
+#define CR0_STATUS_2 0x40
+#define CR0_STATUS_3 0x60
+#define CR0_INT_ACK (1 << 7) /* 0=No ack, 1=acknowledge */
+
+/* Port 0 Status Register definitions */
+
+#define SR0_NO_RESULT 0 /* dummy */
+#define SR0_EVENT_MASK 0x0f
+#define SR0_IA_SETUP_DONE 1
+#define SR0_CONFIGURE_DONE 2
+#define SR0_MC_SETUP_DONE 3
+#define SR0_TRANSMIT_DONE 4
+#define SR0_TDR_DONE 5
+#define SR0_DUMP_DONE 6
+#define SR0_DIAGNOSE_PASSED 7
+#define SR0_TRANSMIT_NO_CRC_DONE 9
+#define SR0_RETRANSMIT_DONE 12
+#define SR0_EXECUTION_ABORTED 13
+#define SR0_END_OF_FRAME 8
+#define SR0_RECEPTION_ABORTED 10
+#define SR0_DIAGNOSE_FAILED 15
+#define SR0_STOP_REG_HIT 11
+
+#define SR0_CHNL (1 << 4)
+#define SR0_EXECUTION (1 << 5)
+#define SR0_RECEPTION (1 << 6)
+#define SR0_INTERRUPT (1 << 7)
+#define SR0_BOTH_RX_TX (SR0_EXECUTION | SR0_RECEPTION)
+
+#define SR3_EXEC_STATE_MASK 0x03
+#define SR3_EXEC_IDLE 0
+#define SR3_TX_ABORT_IN_PROGRESS 1
+#define SR3_EXEC_ACTIVE 2
+#define SR3_ABORT_IN_PROGRESS 3
+#define SR3_EXEC_CHNL (1 << 2)
+#define SR3_STP_ON_NO_RSRC (1 << 3)
+#define SR3_RCVING_NO_RSRC (1 << 4)
+#define SR3_RCV_STATE_MASK 0x60
+#define SR3_RCV_IDLE 0x00
+#define SR3_RCV_READY 0x20
+#define SR3_RCV_ACTIVE 0x40
+#define SR3_RCV_STOP_IN_PROG 0x60
+#define SR3_RCV_CHNL (1 << 7)
+
+/* Port 1 Command Register definitions */
+
+#define OP1_NOP 0
+#define OP1_SWIT_TO_PORT_0 1
+#define OP1_INT_DISABLE 2
+#define OP1_INT_ENABLE 3
+#define OP1_SET_TS 5
+#define OP1_RST_TS 7
+#define OP1_POWER_DOWN 8
+#define OP1_RESET_RING_MNGMT 11
+#define OP1_RESET 14
+#define OP1_SEL_RST 15
+
+#define CR1_STATUS_4 0x00
+#define CR1_STATUS_5 0x20
+#define CR1_STATUS_6 0x40
+#define CR1_STOP_REG_UPDATE (1 << 7)
+
+/* Receive frame status bits */
+
+#define RX_RCLD (1 << 0)
+#define RX_IA_MATCH (1 << 1)
+#define RX_NO_AD_MATCH (1 << 2)
+#define RX_NO_SFD (1 << 3)
+#define RX_SRT_FRM (1 << 7)
+#define RX_OVRRUN (1 << 8)
+#define RX_ALG_ERR (1 << 10)
+#define RX_CRC_ERR (1 << 11)
+#define RX_LEN_ERR (1 << 12)
+#define RX_RCV_OK (1 << 13)
+#define RX_TYP_LEN (1 << 15)
+
+/* Transmit status bits */
+
+#define TX_NCOL_MASK 0x0f
+#define TX_FRTL (1 << 4)
+#define TX_MAX_COL (1 << 5)
+#define TX_HRT_BEAT (1 << 6)
+#define TX_DEFER (1 << 7)
+#define TX_UND_RUN (1 << 8)
+#define TX_LOST_CTS (1 << 9)
+#define TX_LOST_CRS (1 << 10)
+#define TX_LTCOL (1 << 11)
+#define TX_OK (1 << 13)
+#define TX_COLL (1 << 15)
+
+struct i82593_conf_block {
+ u_char fifo_limit : 4,
+ forgnesi : 1,
+ fifo_32 : 1,
+ d6mod : 1,
+ throttle_enb : 1;
+ u_char throttle : 6,
+ cntrxint : 1,
+ contin : 1;
+ u_char addr_len : 3,
+ acloc : 1,
+ preamb_len : 2,
+ loopback : 2;
+ u_char lin_prio : 3,
+ tbofstop : 1,
+ exp_prio : 3,
+ bof_met : 1;
+ u_char : 4,
+ ifrm_spc : 4;
+ u_char : 5,
+ slottim_low : 3;
+ u_char slottim_hi : 3,
+ : 1,
+ max_retr : 4;
+ u_char prmisc : 1,
+ bc_dis : 1,
+ : 1,
+ crs_1 : 1,
+ nocrc_ins : 1,
+ crc_1632 : 1,
+ : 1,
+ crs_cdt : 1;
+ u_char cs_filter : 3,
+ crs_src : 1,
+ cd_filter : 3,
+ : 1;
+ u_char : 2,
+ min_fr_len : 6;
+ u_char lng_typ : 1,
+ lng_fld : 1,
+ rxcrc_xf : 1,
+ artx : 1,
+ sarec : 1,
+ tx_jabber : 1, /* why is this called max_len in the manual? */
+ hash_1 : 1,
+ lbpkpol : 1;
+ u_char : 6,
+ fdx : 1,
+ : 1;
+ u_char dummy_6 : 6, /* supposed to be ones */
+ mult_ia : 1,
+ dis_bof : 1;
+ u_char dummy_1 : 1, /* supposed to be one */
+ tx_ifs_retrig : 2,
+ mc_all : 1,
+ rcv_mon : 2,
+ frag_acpt : 1,
+ tstrttrs : 1;
+ u_char fretx : 1,
+ runt_eop : 1,
+ hw_sw_pin : 1,
+ big_endn : 1,
+ syncrqs : 1,
+ sttlen : 1,
+ tx_eop : 1,
+ rx_eop : 1;
+ u_char rbuf_size : 5,
+ rcvstop : 1,
+ : 2;
+};
+
+#define I82593_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */
+
+#endif /* _I82593_H */
--- /dev/null
+/*********************************************************************
+ *
+ * Filename: netwave_cs.c
+ * Version: 0.4.1
+ * Description: Netwave AirSurfer Wireless LAN PC Card driver
+ * Status: Experimental.
+ * Authors: John Markus Bjørndalen <johnm@cs.uit.no>
+ * Dag Brattli <dagb@cs.uit.no>
+ * David Hinds <dahinds@users.sourceforge.net>
+ * Created at: A long time ago!
+ * Modified at: Mon Nov 10 11:54:37 1997
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1997 University of Tromsø, Norway
+ *
+ * Revision History:
+ *
+ * 08-Nov-97 15:14:47 John Markus Bjørndalen <johnm@cs.uit.no>
+ * - Fixed some bugs in netwave_rx and cleaned it up a bit.
+ * (One of the bugs would have destroyed packets when receiving
+ * multiple packets per interrupt).
+ * - Cleaned up parts of newave_hw_xmit.
+ * - A few general cleanups.
+ * 24-Oct-97 13:17:36 Dag Brattli <dagb@cs.uit.no>
+ * - Fixed netwave_rx receive function (got updated docs)
+ * Others:
+ * - Changed name from xircnw to netwave, take a look at
+ * http://www.netwave-wireless.com
+ * - Some reorganizing of the code
+ * - Removed possible race condition between interrupt handler and transmit
+ * function
+ * - Started to add wireless extensions, but still needs some coding
+ * - Added watchdog for better handling of transmission timeouts
+ * (hopefully this works better)
+ ********************************************************************/
+
+/* To have statistics (just packets sent) define this */
+#undef NETWAVE_STATS
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/mem_op.h>
+
+#define NETWAVE_REGOFF 0x8000
+/* The Netwave IO registers, offsets to iobase */
+#define NETWAVE_REG_COR 0x0
+#define NETWAVE_REG_CCSR 0x2
+#define NETWAVE_REG_ASR 0x4
+#define NETWAVE_REG_IMR 0xa
+#define NETWAVE_REG_PMR 0xc
+#define NETWAVE_REG_IOLOW 0x6
+#define NETWAVE_REG_IOHI 0x7
+#define NETWAVE_REG_IOCONTROL 0x8
+#define NETWAVE_REG_DATA 0xf
+/* The Netwave Extended IO registers, offsets to RamBase */
+#define NETWAVE_EREG_ASCC 0x114
+#define NETWAVE_EREG_RSER 0x120
+#define NETWAVE_EREG_RSERW 0x124
+#define NETWAVE_EREG_TSER 0x130
+#define NETWAVE_EREG_TSERW 0x134
+#define NETWAVE_EREG_CB 0x100
+#define NETWAVE_EREG_SPCQ 0x154
+#define NETWAVE_EREG_SPU 0x155
+#define NETWAVE_EREG_LIF 0x14e
+#define NETWAVE_EREG_ISPLQ 0x156
+#define NETWAVE_EREG_HHC 0x158
+#define NETWAVE_EREG_NI 0x16e
+#define NETWAVE_EREG_MHS 0x16b
+#define NETWAVE_EREG_TDP 0x140
+#define NETWAVE_EREG_RDP 0x150
+#define NETWAVE_EREG_PA 0x160
+#define NETWAVE_EREG_EC 0x180
+#define NETWAVE_EREG_CRBP 0x17a
+#define NETWAVE_EREG_ARW 0x166
+
+/*
+ * Commands used in the extended command buffer
+ * NETWAVE_EREG_CB (0x100-0x10F)
+ */
+#define NETWAVE_CMD_NOP 0x00
+#define NETWAVE_CMD_SRC 0x01
+#define NETWAVE_CMD_STC 0x02
+#define NETWAVE_CMD_AMA 0x03
+#define NETWAVE_CMD_DMA 0x04
+#define NETWAVE_CMD_SAMA 0x05
+#define NETWAVE_CMD_ER 0x06
+#define NETWAVE_CMD_DR 0x07
+#define NETWAVE_CMD_TL 0x08
+#define NETWAVE_CMD_SRP 0x09
+#define NETWAVE_CMD_SSK 0x0a
+#define NETWAVE_CMD_SMD 0x0b
+#define NETWAVE_CMD_SAPD 0x0c
+#define NETWAVE_CMD_SSS 0x11
+/* End of Command marker */
+#define NETWAVE_CMD_EOC 0x00
+
+/* ASR register bits */
+#define NETWAVE_ASR_RXRDY 0x80
+#define NETWAVE_ASR_TXBA 0x01
+
+#define TX_TIMEOUT ((32*HZ)/100)
+
+static const unsigned int imrConfRFU1 = 0x10; /* RFU interrupt mask, keep high */
+static const unsigned int imrConfIENA = 0x02; /* Interrupt enable */
+
+static const unsigned int corConfIENA = 0x01; /* Interrupt enable */
+static const unsigned int corConfLVLREQ = 0x40; /* Keep high */
+
+static const unsigned int rxConfRxEna = 0x80; /* Receive Enable */
+static const unsigned int rxConfMAC = 0x20; /* MAC host receive mode*/
+static const unsigned int rxConfPro = 0x10; /* Promiscuous */
+static const unsigned int rxConfAMP = 0x08; /* Accept Multicast Packets */
+static const unsigned int rxConfBcast = 0x04; /* Accept Broadcast Packets */
+
+static const unsigned int txConfTxEna = 0x80; /* Transmit Enable */
+static const unsigned int txConfMAC = 0x20; /* Host sends MAC mode */
+static const unsigned int txConfEUD = 0x10; /* Enable Uni-Data packets */
+static const unsigned int txConfKey = 0x02; /* Scramble data packets */
+static const unsigned int txConfLoop = 0x01; /* Loopback mode */
+
+/*
+ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
+ you do not define PCMCIA_DEBUG at all, all the debug code will be
+ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
+ be present but disabled -- but it can then be enabled for specific
+ modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"netwave_cs.c 0.3.0 Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n";
+#else
+#define DEBUG(n, args...)
+#endif
+
+static dev_info_t dev_info = "netwave_cs";
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Choose the domain, default is 0x100 */
+static u_int domain = 0x100;
+
+/* Scramble key, range from 0x0 to 0xffff.
+ * 0x0 is no scrambling.
+ */
+static u_int scramble_key = 0x0;
+
+/* Shared memory speed, in ns. The documentation states that
+ * the card should not be read faster than every 400ns.
+ * This timing should be provided by the HBA. If it becomes a
+ * problem, try setting mem_speed to 400.
+ */
+static int mem_speed;
+
+/* Bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(domain, "i");
+MODULE_PARM(scramble_key, "i");
+MODULE_PARM(mem_speed, "i");
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/*====================================================================*/
+
+/* PCMCIA (Card Services) related functions */
+static void netwave_release(u_long arg); /* Card removal */
+static int netwave_event(event_t event, int priority,
+ event_callback_args_t *args);
+static void netwave_pcmcia_config(dev_link_t *arg); /* Runs after card
+ insertion */
+static dev_link_t *netwave_attach(void); /* Create instance */
+static void netwave_detach(dev_link_t *); /* Destroy instance */
+static void netwave_flush_stale_links(void); /* Destroy all staled instances */
+
+/* Hardware configuration */
+static void netwave_doreset(ioaddr_t iobase, u_char* ramBase);
+static void netwave_reset(struct net_device *dev);
+
+/* Misc device stuff */
+static int netwave_open(struct net_device *dev); /* Open the device */
+static int netwave_close(struct net_device *dev); /* Close the device */
+static int netwave_config(struct net_device *dev, struct ifmap *map);
+
+/* Packet transmission and Packet reception */
+static int netwave_start_xmit( struct sk_buff *skb, struct net_device *dev);
+static int netwave_rx( struct net_device *dev);
+
+/* Interrupt routines */
+static void netwave_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void netwave_watchdog(struct net_device *);
+
+/* Statistics */
+static void update_stats(struct net_device *dev);
+static struct net_device_stats *netwave_get_stats(struct net_device *dev);
+
+/* Wireless extensions */
+#ifdef WIRELESS_EXT
+static struct iw_statistics* netwave_get_wireless_stats(struct net_device *dev);
+#endif
+static int netwave_ioctl(struct net_device *, struct ifreq *, int);
+
+static void set_multicast_list(struct net_device *dev);
+
+/*
+ A linked list of "instances" of the skeleton device. Each actual
+ PCMCIA card corresponds to one device instance, and is described
+ by one dev_link_t structure (defined in ds.h).
+
+ You may not want to use a linked list for this -- for example, the
+ memory card driver uses an array of dev_link_t pointers, where minor
+ device numbers are used to derive the corresponding array index.
+*/
+static dev_link_t *dev_list;
+
+/*
+ A dev_link_t structure has fields for most things that are needed
+ to keep track of a socket, but there will usually be some device
+ specific information that also needs to be kept track of. The
+ 'priv' pointer in a dev_link_t structure can be used to point to
+ a device-specific private data structure, like this.
+
+ A driver needs to provide a dev_node_t structure for each device
+ on a card. In some cases, there is only one device per card (for
+ example, ethernet cards, modems). In other cases, there may be
+ many actual or logical devices (SCSI adapters, memory cards with
+ multiple partitions). The dev_node_t structures need to be kept
+ in a linked list starting at the 'dev' field of a dev_link_t
+ structure. We allocate them in the card's private data structure,
+ because they generally can't be allocated dynamically.
+*/
+
+/* Wireless Extension Backward compatibility - Jean II
+ * If the new wireless device private ioctl range is not defined,
+ * default to standard device private ioctl range */
+#ifndef SIOCIWFIRSTPRIV
+#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
+#endif /* SIOCIWFIRSTPRIV */
+
+#define SIOCGIPSNAP SIOCIWFIRSTPRIV /* Site Survey Snapshot */
+/*#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1*/
+
+#define MAX_ESA 10
+
+typedef struct net_addr {
+ u_char addr48[6];
+} net_addr;
+
+struct site_survey {
+ u_short length;
+ u_char struct_revision;
+ u_char roaming_state;
+
+ u_char sp_existsFlag;
+ u_char sp_link_quality;
+ u_char sp_max_link_quality;
+ u_char linkQualityGoodFairBoundary;
+ u_char linkQualityFairPoorBoundary;
+ u_char sp_utilization;
+ u_char sp_goodness;
+ u_char sp_hotheadcount;
+ u_char roaming_condition;
+
+ net_addr sp;
+ u_char numAPs;
+ net_addr nearByAccessPoints[MAX_ESA];
+};
+
+typedef struct netwave_private {
+ dev_link_t link;
+ struct net_device dev;
+ dev_node_t node;
+ u_char *ramBase;
+ int timeoutCounter;
+ int lastExec;
+ struct timer_list watchdog; /* To avoid blocking state */
+ struct site_survey nss;
+ struct net_device_stats stats;
+#ifdef WIRELESS_EXT
+ struct iw_statistics iw_stats; /* Wireless stats */
+#endif
+} netwave_private;
+
+#ifdef NETWAVE_STATS
+static struct net_device_stats *netwave_get_stats(struct net_device *dev);
+#endif
+
+/*
+ * The Netwave card is little-endian, so won't work for big endian
+ * systems.
+ */
+static inline unsigned short get_uint16(u_char* staddr)
+{
+ return readw(staddr); /* Return only 16 bits */
+}
+
+static inline short get_int16(u_char* staddr)
+{
+ return readw(staddr);
+}
+
+/**************************************************************************/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+}
+
+/*
+ * Wait until the WOC (Write Operation Complete) bit in the
+ * ASR (Adapter Status Register) is asserted.
+ * This should have aborted if it takes too long time.
+ */
+static inline void wait_WOC(unsigned int iobase)
+{
+ /* Spin lock */
+ while ((inb(iobase + NETWAVE_REG_ASR) & 0x8) != 0x8) ;
+}
+
+#ifdef WIRELESS_EXT
+static void netwave_snapshot(netwave_private *priv, u_char *ramBase,
+ ioaddr_t iobase) {
+ u_short resultBuffer;
+
+ /* if time since last snapshot is > 1 sec. (100 jiffies?) then take
+ * new snapshot, else return cached data. This is the recommended rate.
+ */
+ if ( jiffies - priv->lastExec > 100) {
+ /* Take site survey snapshot */
+ /*printk( KERN_DEBUG "Taking new snapshot. %ld\n", jiffies -
+ priv->lastExec); */
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_SSS, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+ wait_WOC(iobase);
+
+ /* Get result and copy to cach */
+ resultBuffer = readw(ramBase + NETWAVE_EREG_CRBP);
+ copy_from_pc( &priv->nss, ramBase+resultBuffer,
+ sizeof(struct site_survey));
+ }
+}
+#endif
+
+#ifdef WIRELESS_EXT
+/*
+ * Function netwave_get_wireless_stats (dev)
+ *
+ * Wireless extensions statistics
+ *
+ */
+static struct iw_statistics *netwave_get_wireless_stats(struct net_device *dev)
+{
+ unsigned long flags;
+ ioaddr_t iobase = dev->base_addr;
+ netwave_private *priv = (netwave_private *) dev->priv;
+ u_char *ramBase = priv->ramBase;
+ struct iw_statistics* wstats;
+
+ wstats = &priv->iw_stats;
+
+ save_flags(flags);
+ cli();
+
+ netwave_snapshot( priv, ramBase, iobase);
+
+ wstats->status = priv->nss.roaming_state;
+ wstats->qual.qual = readb( ramBase + NETWAVE_EREG_SPCQ);
+ wstats->qual.level = readb( ramBase + NETWAVE_EREG_ISPLQ);
+ wstats->qual.noise = readb( ramBase + NETWAVE_EREG_SPU) & 0x3f;
+ wstats->discard.nwid = 0L;
+ wstats->discard.code = 0L;
+ wstats->discard.misc = 0L;
+
+ restore_flags(flags);
+
+ return &priv->iw_stats;
+}
+#endif
+
+/*
+ * Function netwave_attach (void)
+ *
+ * Creates an "instance" of the driver, allocating local data
+ * structures for one device. The device is registered with Card
+ * Services.
+ *
+ * The dev_link structure is initialized, but we don't actually
+ * configure the card at this point -- we wait until we receive a
+ * card insertion event.
+ */
+static dev_link_t *netwave_attach(void)
+{
+ client_reg_t client_reg;
+ dev_link_t *link;
+ struct net_device *dev;
+ netwave_private *priv;
+ int i, ret;
+
+ DEBUG(0, "netwave_attach()\n");
+
+ /* Perform some cleanup */
+ netwave_flush_stale_links();
+
+ /* Initialize the dev_link_t structure */
+ priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) return NULL;
+ memset(priv, 0, sizeof(*priv));
+ link = &priv->link; dev = &priv->dev;
+ link->priv = dev->priv = priv;
+ link->release.function = &netwave_release;
+ link->release.data = (u_long)link;
+
+ /* The io structure describes IO port mapping */
+ link->io.NumPorts1 = 16;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ /* link->io.NumPorts2 = 16;
+ link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; */
+ link->io.IOAddrLines = 5;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+ if (irq_list[0] == -1)
+ link->irq.IRQInfo2 = irq_mask;
+ else
+ for (i = 0; i < 4; i++)
+ link->irq.IRQInfo2 |= 1 << irq_list[i];
+ link->irq.Handler = &netwave_interrupt;
+
+ /* General socket configuration */
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ link->conf.ConfigIndex = 1;
+ link->conf.Present = PRESENT_OPTION;
+
+ /* Netwave specific entries in the device structure */
+ dev->hard_start_xmit = &netwave_start_xmit;
+ dev->set_config = &netwave_config;
+ dev->get_stats = &netwave_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ /* wireless extensions */
+#ifdef WIRELESS_EXT
+ dev->get_wireless_stats = &netwave_get_wireless_stats;
+#endif
+ dev->do_ioctl = &netwave_ioctl;
+
+ dev->tx_timeout = &netwave_watchdog;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ ether_setup(dev);
+ dev->open = &netwave_open;
+ dev->stop = &netwave_close;
+ link->irq.Instance = dev;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.EventMask =
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.event_handler = &netwave_event;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = CardServices(RegisterClient, &link->handle, &client_reg);
+ if (ret != 0) {
+ cs_error(link->handle, RegisterClient, ret);
+ netwave_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* netwave_attach */
+
+/*
+ * Function netwave_detach (link)
+ *
+ * This deletes a driver "instance". The device is de-registered
+ * with Card Services. If it has been released, all local data
+ * structures are freed. Otherwise, the structures will be freed
+ * when the device is released.
+ */
+static void netwave_detach(dev_link_t *link)
+{
+ netwave_private *priv = link->priv;
+ dev_link_t **linkp;
+
+ DEBUG(0, "netwave_detach(0x%p)\n", link);
+
+ /*
+ If the device is currently configured and active, we won't
+ actually delete it yet. Instead, it is marked so that when
+ the release() function is called, that will trigger a proper
+ detach().
+ */
+ del_timer(&link->release);
+ if (link->state & DEV_CONFIG) {
+ netwave_release((u_long) link);
+ if (link->state & DEV_STALE_CONFIG) {
+ DEBUG(1, "netwave_cs: detach postponed, '%s' still "
+ "locked\n", link->dev->dev_name);
+ link->state |= DEV_STALE_LINK;
+ return;
+ }
+ }
+
+ /* Break the link with Card Services */
+ if (link->handle)
+ CardServices(DeregisterClient, link->handle);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+ if (*linkp == NULL)
+ {
+ DEBUG(1, "netwave_cs: detach fail, '%s' not in list\n",
+ link->dev->dev_name);
+ return;
+ }
+
+ /* Unlink device structure, free pieces */
+ *linkp = link->next;
+ if (link->dev)
+ unregister_netdev(&priv->dev);
+ kfree(priv);
+
+} /* netwave_detach */
+
+/*
+ * Function netwave_flush_stale_links (void)
+ *
+ * This deletes all driver "instances" that need to be deleted.
+ * Sometimes, netwave_detach can't be performed following a call from
+ * cardmgr (device still open) and the device is put in a STALE_LINK
+ * state.
+ * This function is in charge of making the cleanup...
+ */
+static void netwave_flush_stale_links(void)
+{
+ dev_link_t * link; /* Current node in linked list */
+ dev_link_t * next; /* Next node in linked list */
+
+ DEBUG(1, "netwave_flush_stale_links(0x%p)\n", dev_list);
+
+ /* Go through the list */
+ for (link = dev_list; link; link = next) {
+ next = link->next;
+ /* Check if in need of being removed */
+ if(link->state & DEV_STALE_LINK)
+ netwave_detach(link);
+ }
+} /* netwave_flush_stale_links */
+
+/*
+ * Function netwave_ioctl (dev, rq, cmd)
+ *
+ * Perform ioctl : config & info stuff
+ * This is the stuff that are treated the wireless extensions (iwconfig)
+ *
+ */
+static int netwave_ioctl(struct net_device *dev, /* ioctl device */
+ struct ifreq *rq, /* Data passed */
+ int cmd) /* Ioctl number */
+{
+ unsigned long flags;
+ int ret = 0;
+#ifdef WIRELESS_EXT
+ ioaddr_t iobase = dev->base_addr;
+ netwave_private *priv = (netwave_private *) dev->priv;
+ u_char *ramBase = priv->ramBase;
+ struct iwreq *wrq = (struct iwreq *) rq;
+#endif
+
+ DEBUG(0, "%s: ->netwave_ioctl(cmd=0x%X)\n", dev->name, cmd);
+
+ /* Disable interrupts & save flags */
+ save_flags(flags);
+ cli();
+
+ /* Look what is the request */
+ switch(cmd) {
+ /* --------------- WIRELESS EXTENSIONS --------------- */
+#ifdef WIRELESS_EXT
+ case SIOCGIWNAME:
+ /* Get name */
+ strcpy(wrq->u.name, "Netwave");
+ break;
+ case SIOCSIWNWID:
+ /* Set domain */
+#if WIRELESS_EXT > 8
+ if(!wrq->u.nwid.disabled) {
+ domain = wrq->u.nwid.value;
+#else /* WIRELESS_EXT > 8 */
+ if(wrq->u.nwid.on) {
+ domain = wrq->u.nwid.nwid;
+#endif /* WIRELESS_EXT > 8 */
+ printk( KERN_DEBUG "Setting domain to 0x%x%02x\n",
+ (domain >> 8) & 0x01, domain & 0xff);
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0);
+ writeb( domain & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+ writeb((domain >>8 ) & 0x01,ramBase + NETWAVE_EREG_CB+2);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+ } break;
+ case SIOCGIWNWID:
+ /* Read domain*/
+#if WIRELESS_EXT > 8
+ wrq->u.nwid.value = domain;
+ wrq->u.nwid.disabled = 0;
+ wrq->u.nwid.fixed = 1;
+#else /* WIRELESS_EXT > 8 */
+ wrq->u.nwid.nwid = domain;
+ wrq->u.nwid.on = 1;
+#endif /* WIRELESS_EXT > 8 */
+ break;
+#if WIRELESS_EXT > 8 /* Note : The API did change... */
+ case SIOCGIWENCODE:
+ /* Get scramble key */
+ if(wrq->u.encoding.pointer != (caddr_t) 0)
+ {
+ char key[2];
+ key[1] = scramble_key & 0xff;
+ key[0] = (scramble_key>>8) & 0xff;
+ wrq->u.encoding.flags = IW_ENCODE_ENABLED;
+ wrq->u.encoding.length = 2;
+ if(copy_to_user(wrq->u.encoding.pointer, key, 2))
+ ret = -EFAULT;
+ }
+ break;
+ case SIOCSIWENCODE:
+ /* Set scramble key */
+ if(wrq->u.encoding.pointer != (caddr_t) 0)
+ {
+ char key[2];
+ if(copy_from_user(key, wrq->u.encoding.pointer, 2))
+ {
+ ret = -EFAULT;
+ break;
+ }
+ scramble_key = (key[0] << 8) | key[1];
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+ writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+ }
+ break;
+ case SIOCGIWMODE:
+ /* Mode of operation */
+ if(domain & 0x100)
+ wrq->u.mode = IW_MODE_INFRA;
+ else
+ wrq->u.mode = IW_MODE_ADHOC;
+ break;
+#else /* WIRELESS_EXT > 8 */
+ case SIOCGIWENCODE:
+ /* Get scramble key */
+ wrq->u.encoding.code = scramble_key;
+ wrq->u.encoding.method = 1;
+ break;
+ case SIOCSIWENCODE:
+ /* Set scramble key */
+ scramble_key = wrq->u.encoding.code;
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+ writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+ break;
+#endif /* WIRELESS_EXT > 8 */
+ case SIOCGIWRANGE:
+ /* Basic checking... */
+ if(wrq->u.data.pointer != (caddr_t) 0) {
+ struct iw_range range;
+
+ /* Set the length (very important for backward compatibility) */
+ wrq->u.data.length = sizeof(struct iw_range);
+
+ /* Set all the info we don't care or don't know about to zero */
+ memset(&range, 0, sizeof(range));
+
+#if WIRELESS_EXT > 10
+ /* Set the Wireless Extension versions */
+ range.we_version_compiled = WIRELESS_EXT;
+ range.we_version_source = 9; /* Nothing for us in v10 and v11 */
+#endif /* WIRELESS_EXT > 10 */
+
+ /* Set information in the range struct */
+ range.throughput = 450 * 1000; /* don't argue on this ! */
+ range.min_nwid = 0x0000;
+ range.max_nwid = 0x01FF;
+
+ range.num_channels = range.num_frequency = 0;
+
+ range.sensitivity = 0x3F;
+ range.max_qual.qual = 255;
+ range.max_qual.level = 255;
+ range.max_qual.noise = 0;
+
+#if WIRELESS_EXT > 7
+ range.num_bitrates = 1;
+ range.bitrate[0] = 1000000; /* 1 Mb/s */
+#endif /* WIRELESS_EXT > 7 */
+
+#if WIRELESS_EXT > 8
+ range.encoding_size[0] = 2; /* 16 bits scrambling */
+ range.num_encoding_sizes = 1;
+ range.max_encoding_tokens = 1; /* Only one key possible */
+#endif /* WIRELESS_EXT > 8 */
+
+ /* Copy structure to the user buffer */
+ if(copy_to_user(wrq->u.data.pointer, &range,
+ sizeof(struct iw_range)))
+ ret = -EFAULT;
+ }
+ break;
+ case SIOCGIWPRIV:
+ /* Basic checking... */
+ if(wrq->u.data.pointer != (caddr_t) 0) {
+ struct iw_priv_args priv[] =
+ { /* cmd, set_args, get_args, name */
+ { SIOCGIPSNAP, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 0,
+ sizeof(struct site_survey),
+ "getsitesurvey" },
+ };
+
+ /* Set the number of ioctl available */
+ wrq->u.data.length = 1;
+
+ /* Copy structure to the user buffer */
+ if(copy_to_user(wrq->u.data.pointer, (u_char *) priv,
+ sizeof(priv)))
+ ret = -EFAULT;
+ }
+ break;
+ case SIOCGIPSNAP:
+ if(wrq->u.data.pointer != (caddr_t) 0) {
+ /* Take snapshot of environment */
+ netwave_snapshot( priv, ramBase, iobase);
+ wrq->u.data.length = priv->nss.length;
+ /* Copy structure to the user buffer */
+ if(copy_to_user(wrq->u.data.pointer,
+ (u_char *) &priv->nss,
+ sizeof( struct site_survey)))
+ {
+ printk(KERN_DEBUG "Bad buffer!\n");
+ break;
+ }
+
+ priv->lastExec = jiffies;
+ }
+ break;
+#endif
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ /* ReEnable interrupts & restore flags */
+ restore_flags(flags);
+
+ return ret;
+}
+
+/*
+ * Function netwave_pcmcia_config (link)
+ *
+ * netwave_pcmcia_config() is scheduled to run after a CARD_INSERTION
+ * event is received, to configure the PCMCIA socket, and to make the
+ * device available to the system.
+ *
+ */
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+static void netwave_pcmcia_config(dev_link_t *link) {
+ client_handle_t handle = link->handle;
+ netwave_private *priv = link->priv;
+ struct net_device *dev = &priv->dev;
+ tuple_t tuple;
+ cisparse_t parse;
+ int i, j, last_ret, last_fn;
+ u_char buf[64];
+ win_req_t req;
+ memreq_t mem;
+ u_char *ramBase = NULL;
+
+ DEBUG(0, "netwave_pcmcia_config(0x%p)\n", link);
+
+ /*
+ This reads the card's CONFIG tuple to find its configuration
+ registers.
+ */
+ tuple.Attributes = 0;
+ tuple.TupleData = (cisdata_t *) buf;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ CS_CHECK(GetFirstTuple, handle, &tuple);
+ CS_CHECK(GetTupleData, handle, &tuple);
+ CS_CHECK(ParseTuple, handle, &tuple, &parse);
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ /*
+ * Try allocating IO ports. This tries a few fixed addresses.
+ * If you want, you can also read the card's config table to
+ * pick addresses -- see the serial driver for an example.
+ */
+ for (i = j = 0x0; j < 0x400; j += 0x20) {
+ link->io.BasePort1 = j ^ 0x300;
+ i = CardServices(RequestIO, link->handle, &link->io);
+ if (i == CS_SUCCESS) break;
+ }
+ if (i != CS_SUCCESS) {
+ cs_error(link->handle, RequestIO, i);
+ goto failed;
+ }
+
+ /*
+ * Now allocate an interrupt line. Note that this does not
+ * actually assign a handler to the interrupt.
+ */
+ CS_CHECK(RequestIRQ, handle, &link->irq);
+
+ /*
+ * This actually configures the PCMCIA socket -- setting up
+ * the I/O windows and the interrupt mapping.
+ */
+ CS_CHECK(RequestConfiguration, handle, &link->conf);
+
+ /*
+ * Allocate a 32K memory window. Note that the dev_link_t
+ * structure provides space for one window handle -- if your
+ * device needs several windows, you'll need to keep track of
+ * the handles in your private data structure, link->priv.
+ */
+ DEBUG(1, "Setting mem speed of %d\n", mem_speed);
+
+ req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+ req.Base = 0; req.Size = 0x8000;
+ req.AccessSpeed = mem_speed;
+ link->win = (window_handle_t)link->handle;
+ CS_CHECK(RequestWindow, &link->win, &req);
+ mem.CardOffset = 0x20000; mem.Page = 0;
+ CS_CHECK(MapMemPage, link->win, &mem);
+
+ /* Store base address of the common window frame */
+ ramBase = ioremap(req.Base, 0x8000);
+ ((netwave_private*)dev->priv)->ramBase = ramBase;
+
+ dev->irq = link->irq.AssignedIRQ;
+ dev->base_addr = link->io.BasePort1;
+ if (register_netdev(dev) != 0) {
+ printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n");
+ goto failed;
+ }
+
+ strcpy(priv->node.dev_name, dev->name);
+ link->dev = &priv->node;
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ /* Reset card before reading physical address */
+ netwave_doreset(dev->base_addr, ramBase);
+
+ /* Read the ethernet address and fill in the Netwave registers. */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = readb(ramBase + NETWAVE_EREG_PA + i);
+
+ printk(KERN_INFO "%s: Netwave: port %#3lx, irq %d, mem %lx id "
+ "%c%c, hw_addr ", dev->name, dev->base_addr, dev->irq,
+ (u_long) ramBase, (int) readb(ramBase+NETWAVE_EREG_NI),
+ (int) readb(ramBase+NETWAVE_EREG_NI+1));
+ for (i = 0; i < 6; i++)
+ printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+
+ /* get revision words */
+ printk(KERN_DEBUG "Netwave_reset: revision %04x %04x\n",
+ get_uint16(ramBase + NETWAVE_EREG_ARW),
+ get_uint16(ramBase + NETWAVE_EREG_ARW+2));
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+failed:
+ netwave_release((u_long)link);
+} /* netwave_pcmcia_config */
+
+/*
+ * Function netwave_release (arg)
+ *
+ * After a card is removed, netwave_release() will unregister the net
+ * device, and release the PCMCIA configuration. If the device is
+ * still open, this will be postponed until it is closed.
+ */
+static void netwave_release(u_long arg) {
+ dev_link_t *link = (dev_link_t *)arg;
+ netwave_private *priv = link->priv;
+
+ DEBUG(0, "netwave_release(0x%p)\n", link);
+
+ /*
+ If the device is currently in use, we won't release until it
+ is actually closed.
+ */
+ if (link->open) {
+ printk(KERN_DEBUG "netwave_cs: release postponed, '%s' still open\n",
+ link->dev->dev_name);
+ link->state |= DEV_STALE_CONFIG;
+ return;
+ }
+
+ /* Don't bother checking to see if these succeed or not */
+ if (link->win) {
+ iounmap(priv->ramBase);
+ CardServices(ReleaseWindow, link->win);
+ }
+ CardServices(ReleaseConfiguration, link->handle);
+ CardServices(ReleaseIO, link->handle, &link->io);
+ CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+ link->state &= ~(DEV_CONFIG | DEV_STALE_CONFIG);
+
+} /* netwave_release */
+
+/*
+ * Function netwave_event (event, priority, args)
+ *
+ * The card status event handler. Mostly, this schedules other
+ * stuff to run after an event is received. A CARD_REMOVAL event
+ * also sets some flags to discourage the net drivers from trying
+ * to talk to the card any more.
+ *
+ * When a CARD_REMOVAL event is received, we immediately set a flag
+ * to block future accesses to this device. All the functions that
+ * actually access the device should check this flag to make sure
+ * the card is still present.
+ *
+ */
+static int netwave_event(event_t event, int priority,
+ event_callback_args_t *args) {
+ dev_link_t *link = args->client_data;
+ netwave_private *priv = link->priv;
+ struct net_device *dev = &priv->dev;
+
+ DEBUG(1, "netwave_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_REGISTRATION_COMPLETE:
+ DEBUG(0, "netwave_cs: registration complete\n");
+ break;
+
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ netif_device_detach(dev);
+ mod_timer(&link->release, jiffies + HZ/20);
+ }
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ netwave_pcmcia_config( link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG) {
+ if (link->open)
+ netif_device_detach(dev);
+ CardServices(ReleaseConfiguration, link->handle);
+ }
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (link->state & DEV_CONFIG) {
+ CardServices(RequestConfiguration, link->handle, &link->conf);
+ if (link->open) {
+ netwave_reset(dev);
+ netif_device_attach(dev);
+ }
+ }
+ break;
+ }
+ return 0;
+} /* netwave_event */
+
+/*
+ * Function netwave_doreset (ioBase, ramBase)
+ *
+ * Proper hardware reset of the card.
+ */
+static void netwave_doreset(ioaddr_t ioBase, u_char* ramBase) {
+ /* Reset card */
+ wait_WOC(ioBase);
+ outb(0x80, ioBase + NETWAVE_REG_PMR);
+ writeb(0x08, ramBase + NETWAVE_EREG_ASCC); /* Bit 3 is WOC */
+ outb(0x0, ioBase + NETWAVE_REG_PMR); /* release reset */
+}
+
+/*
+ * Function netwave_reset (dev)
+ *
+ * Reset and restore all of the netwave registers
+ */
+static void netwave_reset(struct net_device *dev) {
+ /* u_char state; */
+ netwave_private *priv = (netwave_private*) dev->priv;
+ u_char *ramBase = priv->ramBase;
+ ioaddr_t iobase = dev->base_addr;
+
+ DEBUG(0, "netwave_reset: Done with hardware reset\n");
+
+ priv->timeoutCounter = 0;
+
+ /* Reset card */
+ netwave_doreset(iobase, ramBase);
+ printk(KERN_DEBUG "netwave_reset: Done with hardware reset\n");
+
+ /* Write a NOP to check the card */
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_NOP, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+
+ /* Set receive conf */
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(rxConfRxEna + rxConfBcast, ramBase + NETWAVE_EREG_CB + 1);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
+
+ /* Set transmit conf */
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_STC, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(txConfTxEna, ramBase + NETWAVE_EREG_CB + 1);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
+
+ /* Now set the MU Domain */
+ printk(KERN_DEBUG "Setting domain to 0x%x%02x\n", (domain >> 8) & 0x01, domain & 0xff);
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(domain & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+ writeb((domain>>8) & 0x01, ramBase + NETWAVE_EREG_CB + 2);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+
+ /* Set scramble key */
+ printk(KERN_DEBUG "Setting scramble key to 0x%x\n", scramble_key);
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+ writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+
+ /* Enable interrupts, bit 4 high to keep unused
+ * source from interrupting us, bit 2 high to
+ * set interrupt enable, 567 to enable TxDN,
+ * RxErr and RxRdy
+ */
+ wait_WOC(iobase);
+ outb(imrConfIENA+imrConfRFU1, iobase + NETWAVE_REG_IMR);
+
+ /* Hent 4 bytes fra 0x170. Skal vaere 0a,29,88,36
+ * waitWOC
+ * skriv 80 til d000:3688
+ * sjekk om det ble 80
+ */
+
+ /* Enable Receiver */
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_ER, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+
+ /* Set the IENA bit in COR */
+ wait_WOC(iobase);
+ outb(corConfIENA + corConfLVLREQ, iobase + NETWAVE_REG_COR);
+}
+
+/*
+ * Function netwave_config (dev, map)
+ *
+ * Configure device, this work is done by netwave_pcmcia_config when a
+ * card is inserted
+ */
+static int netwave_config(struct net_device *dev, struct ifmap *map) {
+ return 0;
+}
+
+/*
+ * Function netwave_hw_xmit (data, len, dev)
+ */
+static int netwave_hw_xmit(unsigned char* data, int len,
+ struct net_device* dev) {
+ unsigned long flags;
+ unsigned int TxFreeList,
+ curBuff,
+ MaxData,
+ DataOffset;
+ int tmpcount;
+
+ netwave_private *priv = (netwave_private *) dev->priv;
+ u_char* ramBase = priv->ramBase;
+ ioaddr_t iobase = dev->base_addr;
+
+ /* Disable interrupts & save flags */
+ save_flags(flags);
+ cli();
+
+ /* Check if there are transmit buffers available */
+ wait_WOC(iobase);
+ if ((inb(iobase+NETWAVE_REG_ASR) & NETWAVE_ASR_TXBA) == 0) {
+ /* No buffers available */
+ printk(KERN_DEBUG "netwave_hw_xmit: %s - no xmit buffers available.\n",
+ dev->name);
+ restore_flags(flags);
+ return 1;
+ }
+
+ priv->stats.tx_bytes += len;
+
+ DEBUG(3, "Transmitting with SPCQ %x SPU %x LIF %x ISPLQ %x\n",
+ readb(ramBase + NETWAVE_EREG_SPCQ),
+ readb(ramBase + NETWAVE_EREG_SPU),
+ readb(ramBase + NETWAVE_EREG_LIF),
+ readb(ramBase + NETWAVE_EREG_ISPLQ));
+
+ /* Now try to insert it into the adapters free memory */
+ wait_WOC(iobase);
+ TxFreeList = get_uint16(ramBase + NETWAVE_EREG_TDP);
+ MaxData = get_uint16(ramBase + NETWAVE_EREG_TDP+2);
+ DataOffset = get_uint16(ramBase + NETWAVE_EREG_TDP+4);
+
+ DEBUG(3, "TxFreeList %x, MaxData %x, DataOffset %x\n",
+ TxFreeList, MaxData, DataOffset);
+
+ /* Copy packet to the adapter fragment buffers */
+ curBuff = TxFreeList;
+ tmpcount = 0;
+ while (tmpcount < len) {
+ int tmplen = len - tmpcount;
+ copy_to_pc(ramBase + curBuff + DataOffset, data + tmpcount,
+ (tmplen < MaxData) ? tmplen : MaxData);
+ tmpcount += MaxData;
+
+ /* Advance to next buffer */
+ curBuff = get_uint16(ramBase + curBuff);
+ }
+
+ /* Now issue transmit list */
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_TL, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(len & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+ writeb((len>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+
+ restore_flags( flags);
+ return 0;
+}
+
+static int netwave_start_xmit(struct sk_buff *skb, struct net_device *dev) {
+ /* This flag indicate that the hardware can't perform a transmission.
+ * Theoritically, NET3 check it before sending a packet to the driver,
+ * but in fact it never do that and pool continuously.
+ * As the watchdog will abort too long transmissions, we are quite safe...
+ */
+
+ netif_stop_queue(dev);
+
+ {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char* buf = skb->data;
+
+ if (netwave_hw_xmit( buf, length, dev) == 1) {
+ /* Some error, let's make them call us another time? */
+ netif_start_queue(dev);
+ }
+ dev->trans_start = jiffies;
+ }
+ dev_kfree_skb(skb);
+
+ return 0;
+} /* netwave_start_xmit */
+
+/*
+ * Function netwave_interrupt (irq, dev_id, regs)
+ *
+ * This function is the interrupt handler for the Netwave card. This
+ * routine will be called whenever:
+ * 1. A packet is received.
+ * 2. A packet has successfully been transferred and the unit is
+ * ready to transmit another packet.
+ * 3. A command has completed execution.
+ */
+static void netwave_interrupt(int irq, void* dev_id, struct pt_regs *regs) {
+ ioaddr_t iobase;
+ u_char *ramBase;
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct netwave_private *priv = dev->priv;
+ dev_link_t *link = &priv->link;
+ int i;
+
+ if (!netif_device_present(dev))
+ return;
+
+ iobase = dev->base_addr;
+ ramBase = priv->ramBase;
+
+ /* Now find what caused the interrupt, check while interrupts ready */
+ for (i = 0; i < 10; i++) {
+ u_char status;
+
+ wait_WOC(iobase);
+ if (!(inb(iobase+NETWAVE_REG_CCSR) & 0x02))
+ break; /* None of the interrupt sources asserted */
+
+ status = inb(iobase + NETWAVE_REG_ASR);
+
+ if (!DEV_OK(link)) {
+ DEBUG(1, "netwave_interrupt: Interrupt with status 0x%x "
+ "from removed or suspended card!\n", status);
+ break;
+ }
+
+ /* RxRdy */
+ if (status & 0x80) {
+ netwave_rx(dev);
+ /* wait_WOC(iobase); */
+ /* RxRdy cannot be reset directly by the host */
+ }
+ /* RxErr */
+ if (status & 0x40) {
+ u_char rser;
+
+ rser = readb(ramBase + NETWAVE_EREG_RSER);
+
+ if (rser & 0x04) {
+ ++priv->stats.rx_dropped;
+ ++priv->stats.rx_crc_errors;
+ }
+ if (rser & 0x02)
+ ++priv->stats.rx_frame_errors;
+
+ /* Clear the RxErr bit in RSER. RSER+4 is the
+ * write part. Also clear the RxCRC (0x04) and
+ * RxBig (0x02) bits if present */
+ wait_WOC(iobase);
+ writeb(0x40 | (rser & 0x06), ramBase + NETWAVE_EREG_RSER + 4);
+
+ /* Write bit 6 high to ASCC to clear RxErr in ASR,
+ * WOC must be set first!
+ */
+ wait_WOC(iobase);
+ writeb(0x40, ramBase + NETWAVE_EREG_ASCC);
+
+ /* Remember to count up priv->stats on error packets */
+ ++priv->stats.rx_errors;
+ }
+ /* TxDN */
+ if (status & 0x20) {
+ int txStatus;
+
+ txStatus = readb(ramBase + NETWAVE_EREG_TSER);
+ DEBUG(3, "Transmit done. TSER = %x id %x\n",
+ txStatus, readb(ramBase + NETWAVE_EREG_TSER + 1));
+
+ if (txStatus & 0x20) {
+ /* Transmitting was okay, clear bits */
+ wait_WOC(iobase);
+ writeb(0x2f, ramBase + NETWAVE_EREG_TSER + 4);
+ ++priv->stats.tx_packets;
+ }
+
+ if (txStatus & 0xd0) {
+ if (txStatus & 0x80) {
+ ++priv->stats.collisions; /* Because of /proc/net/dev*/
+ /* ++priv->stats.tx_aborted_errors; */
+ /* printk("Collision. %ld\n", jiffies - dev->trans_start); */
+ }
+ if (txStatus & 0x40)
+ ++priv->stats.tx_carrier_errors;
+ /* 0x80 TxGU Transmit giveup - nine times and no luck
+ * 0x40 TxNOAP No access point. Discarded packet.
+ * 0x10 TxErr Transmit error. Always set when
+ * TxGU and TxNOAP is set. (Those are the only ones
+ * to set TxErr).
+ */
+ DEBUG(3, "netwave_interrupt: TxDN with error status %x\n",
+ txStatus);
+
+ /* Clear out TxGU, TxNOAP, TxErr and TxTrys */
+ wait_WOC(iobase);
+ writeb(0xdf & txStatus, ramBase+NETWAVE_EREG_TSER+4);
+ ++priv->stats.tx_errors;
+ }
+ DEBUG(3, "New status is TSER %x ASR %x\n",
+ readb(ramBase + NETWAVE_EREG_TSER),
+ inb(iobase + NETWAVE_REG_ASR));
+
+ netif_wake_queue(dev);
+ }
+ /* TxBA, this would trigger on all error packets received */
+ /* if (status & 0x01) {
+ DEBUG(4, "Transmit buffers available, %x\n", status);
+ }
+ */
+ }
+} /* netwave_interrupt */
+
+/*
+ * Function netwave_watchdog (a)
+ *
+ * Watchdog : when we start a transmission, we set a timer in the
+ * kernel. If the transmission complete, this timer is disabled. If
+ * it expire, we reset the card.
+ *
+ */
+static void netwave_watchdog(struct net_device *dev) {
+
+ DEBUG(1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name);
+ netwave_reset(dev);
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+} /* netwave_watchdog */
+
+static struct net_device_stats *netwave_get_stats(struct net_device *dev) {
+ netwave_private *priv = (netwave_private*)dev->priv;
+
+ update_stats(dev);
+
+ DEBUG(2, "netwave: SPCQ %x SPU %x LIF %x ISPLQ %x MHS %x rxtx %x"
+ " %x tx %x %x %x %x\n",
+ readb(priv->ramBase + NETWAVE_EREG_SPCQ),
+ readb(priv->ramBase + NETWAVE_EREG_SPU),
+ readb(priv->ramBase + NETWAVE_EREG_LIF),
+ readb(priv->ramBase + NETWAVE_EREG_ISPLQ),
+ readb(priv->ramBase + NETWAVE_EREG_MHS),
+ readb(priv->ramBase + NETWAVE_EREG_EC + 0xe),
+ readb(priv->ramBase + NETWAVE_EREG_EC + 0xf),
+ readb(priv->ramBase + NETWAVE_EREG_EC + 0x18),
+ readb(priv->ramBase + NETWAVE_EREG_EC + 0x19),
+ readb(priv->ramBase + NETWAVE_EREG_EC + 0x1a),
+ readb(priv->ramBase + NETWAVE_EREG_EC + 0x1b));
+
+ return &priv->stats;
+}
+
+static void update_stats(struct net_device *dev) {
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+/* netwave_private *priv = (netwave_private*) dev->priv;
+ priv->stats.rx_packets = readb(priv->ramBase + 0x18e);
+ priv->stats.tx_packets = readb(priv->ramBase + 0x18f); */
+
+ restore_flags(flags);
+}
+
+static int netwave_rx(struct net_device *dev) {
+ netwave_private *priv = (netwave_private*)(dev->priv);
+ u_char *ramBase = priv->ramBase;
+ ioaddr_t iobase = dev->base_addr;
+ u_char rxStatus;
+ struct sk_buff *skb = NULL;
+ unsigned int curBuffer,
+ rcvList;
+ int rcvLen;
+ int tmpcount = 0;
+ int dataCount, dataOffset;
+ int i;
+ u_char *ptr;
+
+ DEBUG(3, "xinw_rx: Receiving ... \n");
+
+ /* Receive max 10 packets for now. */
+ for (i = 0; i < 10; i++) {
+ /* Any packets? */
+ wait_WOC(iobase);
+ rxStatus = readb(ramBase + NETWAVE_EREG_RSER);
+ if ( !( rxStatus & 0x80)) /* No more packets */
+ break;
+
+ /* Check if multicast/broadcast or other */
+ /* multicast = (rxStatus & 0x20); */
+
+ /* The receive list pointer and length of the packet */
+ wait_WOC(iobase);
+ rcvLen = get_int16( ramBase + NETWAVE_EREG_RDP);
+ rcvList = get_uint16( ramBase + NETWAVE_EREG_RDP + 2);
+
+ if (rcvLen < 0) {
+ printk(KERN_DEBUG "netwave_rx: Receive packet with len %d\n",
+ rcvLen);
+ return 0;
+ }
+
+ skb = dev_alloc_skb(rcvLen+5);
+ if (skb == NULL) {
+ DEBUG(1, "netwave_rx: Could not allocate an sk_buff of "
+ "length %d\n", rcvLen);
+ ++priv->stats.rx_dropped;
+ /* Tell the adapter to skip the packet */
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+ return 0;
+ }
+
+ skb_reserve( skb, 2); /* Align IP on 16 byte */
+ skb_put( skb, rcvLen);
+ skb->dev = dev;
+
+ /* Copy packet fragments to the skb data area */
+ ptr = (u_char*) skb->data;
+ curBuffer = rcvList;
+ tmpcount = 0;
+ while ( tmpcount < rcvLen) {
+ /* Get length and offset of current buffer */
+ dataCount = get_uint16( ramBase+curBuffer+2);
+ dataOffset = get_uint16( ramBase+curBuffer+4);
+
+ copy_from_pc( ptr + tmpcount,
+ ramBase+curBuffer+dataOffset, dataCount);
+
+ tmpcount += dataCount;
+
+ /* Point to next buffer */
+ curBuffer = get_uint16(ramBase + curBuffer);
+ }
+
+ skb->protocol = eth_type_trans(skb,dev);
+ /* Queue packet for network layer */
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += rcvLen;
+
+ /* Got the packet, tell the adapter to skip it */
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+ DEBUG(3, "Packet reception ok\n");
+ }
+ return 0;
+}
+
+static int netwave_open(struct net_device *dev) {
+ netwave_private *priv = dev->priv;
+ dev_link_t *link = &priv->link;
+
+ DEBUG(1, "netwave_open: starting.\n");
+
+ if (!DEV_OK(link))
+ return -ENODEV;
+
+ link->open++;
+ MOD_INC_USE_COUNT;
+
+ netif_start_queue(dev);
+ netwave_reset(dev);
+
+ return 0;
+}
+
+static int netwave_close(struct net_device *dev) {
+ netwave_private *priv = (netwave_private *)dev->priv;
+ dev_link_t *link = &priv->link;
+
+ DEBUG(1, "netwave_close: finishing.\n");
+
+ link->open--;
+ netif_stop_queue(dev);
+ if (link->state & DEV_STALE_CONFIG)
+ mod_timer(&link->release, jiffies + HZ/20);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int __init init_netwave_cs(void) {
+ servinfo_t serv;
+
+ DEBUG(0, "%s\n", version);
+
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk("netwave_cs: Card Services release does not match!\n");
+ return -1;
+ }
+
+ register_pccard_driver(&dev_info, &netwave_attach, &netwave_detach);
+
+ return 0;
+}
+
+static void __exit exit_netwave_cs(void) {
+ DEBUG(1, "netwave_cs: unloading\n");
+
+ unregister_pccard_driver(&dev_info);
+
+ /* Do some cleanup of the device list */
+ netwave_flush_stale_links();
+ if(dev_list != NULL) /* Critical situation */
+ printk("netwave_cs: devices remaining when removing module\n");
+}
+
+module_init(init_netwave_cs);
+module_exit(exit_netwave_cs);
+
+/* Set or clear the multicast filter for this adaptor.
+ num_addrs == -1 Promiscuous mode, receive all packets
+ num_addrs == 0 Normal mode, clear multicast list
+ num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ best-effort filtering.
+ */
+static void set_multicast_list(struct net_device *dev)
+{
+ ioaddr_t iobase = dev->base_addr;
+ u_char* ramBase = ((netwave_private*) dev->priv)->ramBase;
+ u_char rcvMode = 0;
+
+#ifdef PCMCIA_DEBUG
+ if (pc_debug > 2) {
+ static int old;
+ if (old != dev->mc_count) {
+ old = dev->mc_count;
+ DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+ dev->name, dev->mc_count);
+ }
+ }
+#endif
+
+ if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) {
+ /* Multicast Mode */
+ rcvMode = rxConfRxEna + rxConfAMP + rxConfBcast;
+ } else if (dev->flags & IFF_PROMISC) {
+ /* Promiscous mode */
+ rcvMode = rxConfRxEna + rxConfPro + rxConfAMP + rxConfBcast;
+ } else {
+ /* Normal mode */
+ rcvMode = rxConfRxEna + rxConfBcast;
+ }
+
+ /* printk("netwave set_multicast_list: rcvMode to %x\n", rcvMode);*/
+ /* Now set receive mode */
+ wait_WOC(iobase);
+ writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0);
+ writeb(rcvMode, ramBase + NETWAVE_EREG_CB + 1);
+ writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
+}
+MODULE_LICENSE("GPL");
1) Bring other kernel Wireless LAN drivers here
Already done :
o hermes.c/orinoco.c -> Wavelan IEEE driver + Airport driver
- Drivers I have control over :
+ o airo.c/airo_cs.c -> Ben's Aironet driver
o wavelan.c -> old Wavelan ISA driver
- o wavelan_cs.c -> old Wavelan Pcmcia driver (warning : header)
+ o wavelan_cs.c -> old Wavelan Pcmcia driver
o netwave_cs.c -> Netwave Pcmcia driver
Drivers likely to go :
o ray_cs.c -> Raytheon/Aviator driver (maintainer MIA)
Drivers I have absolutely no control over :
o arlan.c -> old Aironet Arlan 655 (need to ask Elmer)
o aironet4500_xxx.c -> Elmer's Aironet driver (need to ask Elmer)
- o airo.c/airo_cs.c -> Ben's Aironet driver (not yet in kernel)
o strip.c -> Metricom's stuff. Not a wlan. Hum...
ETA : Kernel 2.5.X
2) Bring new Wireless LAN driver not yet in the kernel there
See my web page for details
+3) Misc
+ o Mark wavelan, wavelan_cs, netwave_cs drivers as obsolete
+ o Maybe arlan.c, ray_cs.c and strip.c also deserve to be obsolete
+ o Use new Probe/module stuff in wavelan.c
+ o New Wireless Extension API (pending)
+
Jean II
--- /dev/null
+/*
+ * WaveLAN ISA driver
+ *
+ * Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyright follows (also see the end of this file).
+ * See wavelan.p.h for details.
+ *
+ *
+ *
+ * AT&T GIS (nee NCR) WaveLAN card:
+ * An Ethernet-like radio transceiver
+ * controlled by an Intel 82586 coprocessor.
+ */
+
+#include "wavelan.p.h" /* Private header */
+
+/************************* MISC SUBROUTINES **************************/
+/*
+ * Subroutines which won't fit in one of the following category
+ * (WaveLAN modem or i82586)
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for disabling interrupts and locking the driver.
+ * (note : inline, so optimised away)
+ */
+static inline void wv_splhi(net_local * lp,
+ unsigned long * pflags)
+{
+ spin_lock_irqsave(&lp->spinlock, *pflags);
+ /* Note : above does the cli(); itself */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for re-enabling interrupts and un-locking the driver.
+ */
+static inline void wv_splx(net_local * lp,
+ unsigned long * pflags)
+{
+ spin_unlock_irqrestore(&lp->spinlock, *pflags);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Translate irq number to PSA irq parameter
+ */
+static u8 wv_irq_to_psa(int irq)
+{
+ if (irq < 0 || irq >= NELS(irqvals))
+ return 0;
+
+ return irqvals[irq];
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Translate PSA irq parameter to irq number
+ */
+static int __init wv_psa_to_irq(u8 irqval)
+{
+ int irq;
+
+ for (irq = 0; irq < NELS(irqvals); irq++)
+ if (irqvals[irq] == irqval)
+ return irq;
+
+ return -1;
+}
+
+#ifdef STRUCT_CHECK
+/*------------------------------------------------------------------*/
+/*
+ * Sanity routine to verify the sizes of the various WaveLAN interface
+ * structures.
+ */
+static char *wv_struct_check(void)
+{
+#define SC(t,s,n) if (sizeof(t) != s) return(n);
+
+ SC(psa_t, PSA_SIZE, "psa_t");
+ SC(mmw_t, MMW_SIZE, "mmw_t");
+ SC(mmr_t, MMR_SIZE, "mmr_t");
+ SC(ha_t, HA_SIZE, "ha_t");
+
+#undef SC
+
+ return ((char *) NULL);
+} /* wv_struct_check */
+#endif /* STRUCT_CHECK */
+
+/********************* HOST ADAPTER SUBROUTINES *********************/
+/*
+ * Useful subroutines to manage the WaveLAN ISA interface
+ *
+ * One major difference with the PCMCIA hardware (except the port mapping)
+ * is that we have to keep the state of the Host Control Register
+ * because of the interrupt enable & bus size flags.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read from card's Host Adaptor Status Register.
+ */
+static inline u16 hasr_read(unsigned long ioaddr)
+{
+ return (inw(HASR(ioaddr)));
+} /* hasr_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register.
+ */
+static inline void hacr_write(unsigned long ioaddr, u16 hacr)
+{
+ outw(hacr, HACR(ioaddr));
+} /* hacr_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register. Include a delay for
+ * those times when it is needed.
+ */
+static inline void hacr_write_slow(unsigned long ioaddr, u16 hacr)
+{
+ hacr_write(ioaddr, hacr);
+ /* delay might only be needed sometimes */
+ mdelay(1);
+} /* hacr_write_slow */
+
+/*------------------------------------------------------------------*/
+/*
+ * Set the channel attention bit.
+ */
+static inline void set_chan_attn(unsigned long ioaddr, u16 hacr)
+{
+ hacr_write(ioaddr, hacr | HACR_CA);
+} /* set_chan_attn */
+
+/*------------------------------------------------------------------*/
+/*
+ * Reset, and then set host adaptor into default mode.
+ */
+static inline void wv_hacr_reset(unsigned long ioaddr)
+{
+ hacr_write_slow(ioaddr, HACR_RESET);
+ hacr_write(ioaddr, HACR_DEFAULT);
+} /* wv_hacr_reset */
+
+/*------------------------------------------------------------------*/
+/*
+ * Set the I/O transfer over the ISA bus to 8-bit mode
+ */
+static inline void wv_16_off(unsigned long ioaddr, u16 hacr)
+{
+ hacr &= ~HACR_16BITS;
+ hacr_write(ioaddr, hacr);
+} /* wv_16_off */
+
+/*------------------------------------------------------------------*/
+/*
+ * Set the I/O transfer over the ISA bus to 8-bit mode
+ */
+static inline void wv_16_on(unsigned long ioaddr, u16 hacr)
+{
+ hacr |= HACR_16BITS;
+ hacr_write(ioaddr, hacr);
+} /* wv_16_on */
+
+/*------------------------------------------------------------------*/
+/*
+ * Disable interrupts on the WaveLAN hardware.
+ * (called by wv_82586_stop())
+ */
+static inline void wv_ints_off(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+
+ lp->hacr &= ~HACR_INTRON;
+ hacr_write(ioaddr, lp->hacr);
+} /* wv_ints_off */
+
+/*------------------------------------------------------------------*/
+/*
+ * Enable interrupts on the WaveLAN hardware.
+ * (called by wv_hw_reset())
+ */
+static inline void wv_ints_on(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+
+ lp->hacr |= HACR_INTRON;
+ hacr_write(ioaddr, lp->hacr);
+} /* wv_ints_on */
+
+/******************* MODEM MANAGEMENT SUBROUTINES *******************/
+/*
+ * Useful subroutines to manage the modem of the WaveLAN
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read the Parameter Storage Area from the WaveLAN card's memory
+ */
+/*
+ * Read bytes from the PSA.
+ */
+static void psa_read(unsigned long ioaddr, u16 hacr, int o, /* offset in PSA */
+ u8 * b, /* buffer to fill */
+ int n)
+{ /* size to read */
+ wv_16_off(ioaddr, hacr);
+
+ while (n-- > 0) {
+ outw(o, PIOR2(ioaddr));
+ o++;
+ *b++ = inb(PIOP2(ioaddr));
+ }
+
+ wv_16_on(ioaddr, hacr);
+} /* psa_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write the Parameter Storage Area to the WaveLAN card's memory.
+ */
+static void psa_write(unsigned long ioaddr, u16 hacr, int o, /* Offset in PSA */
+ u8 * b, /* Buffer in memory */
+ int n)
+{ /* Length of buffer */
+ int count = 0;
+
+ wv_16_off(ioaddr, hacr);
+
+ while (n-- > 0) {
+ outw(o, PIOR2(ioaddr));
+ o++;
+
+ outb(*b, PIOP2(ioaddr));
+ b++;
+
+ /* Wait for the memory to finish its write cycle */
+ count = 0;
+ while ((count++ < 100) &&
+ (hasr_read(ioaddr) & HASR_PSA_BUSY)) mdelay(1);
+ }
+
+ wv_16_on(ioaddr, hacr);
+} /* psa_write */
+
+#ifdef SET_PSA_CRC
+/*------------------------------------------------------------------*/
+/*
+ * Calculate the PSA CRC
+ * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
+ * NOTE: By specifying a length including the CRC position the
+ * returned value should be zero. (i.e. a correct checksum in the PSA)
+ *
+ * The Windows drivers don't use the CRC, but the AP and the PtP tool
+ * depend on it.
+ */
+static inline u16 psa_crc(u8 * psa, /* The PSA */
+ int size)
+{ /* Number of short for CRC */
+ int byte_cnt; /* Loop on the PSA */
+ u16 crc_bytes = 0; /* Data in the PSA */
+ int bit_cnt; /* Loop on the bits of the short */
+
+ for (byte_cnt = 0; byte_cnt < size; byte_cnt++) {
+ crc_bytes ^= psa[byte_cnt]; /* Its an xor */
+
+ for (bit_cnt = 1; bit_cnt < 9; bit_cnt++) {
+ if (crc_bytes & 0x0001)
+ crc_bytes = (crc_bytes >> 1) ^ 0xA001;
+ else
+ crc_bytes >>= 1;
+ }
+ }
+
+ return crc_bytes;
+} /* psa_crc */
+#endif /* SET_PSA_CRC */
+
+/*------------------------------------------------------------------*/
+/*
+ * update the checksum field in the Wavelan's PSA
+ */
+static void update_psa_checksum(device * dev, unsigned long ioaddr, u16 hacr)
+{
+#ifdef SET_PSA_CRC
+ psa_t psa;
+ u16 crc;
+
+ /* read the parameter storage area */
+ psa_read(ioaddr, hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+ /* update the checksum */
+ crc = psa_crc((unsigned char *) &psa,
+ sizeof(psa) - sizeof(psa.psa_crc[0]) -
+ sizeof(psa.psa_crc[1])
+ - sizeof(psa.psa_crc_status));
+
+ psa.psa_crc[0] = crc & 0xFF;
+ psa.psa_crc[1] = (crc & 0xFF00) >> 8;
+
+ /* Write it ! */
+ psa_write(ioaddr, hacr, (char *) &psa.psa_crc - (char *) &psa,
+ (unsigned char *) &psa.psa_crc, 2);
+
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
+ dev->name, psa.psa_crc[0], psa.psa_crc[1]);
+
+ /* Check again (luxury !) */
+ crc = psa_crc((unsigned char *) &psa,
+ sizeof(psa) - sizeof(psa.psa_crc_status));
+
+ if (crc != 0)
+ printk(KERN_WARNING
+ "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n",
+ dev->name);
+#endif /* DEBUG_IOCTL_INFO */
+#endif /* SET_PSA_CRC */
+} /* update_psa_checksum */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write 1 byte to the MMC.
+ */
+static inline void mmc_out(unsigned long ioaddr, u16 o, u8 d)
+{
+ /* Wait for MMC to go idle */
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
+
+ outw((u16) (((u16) d << 8) | (o << 1) | 1), MMCR(ioaddr));
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to write bytes to the Modem Management Controller.
+ * We start at the end because it is the way it should be!
+ */
+static inline void mmc_write(unsigned long ioaddr, u8 o, u8 * b, int n)
+{
+ o += n;
+ b += n;
+
+ while (n-- > 0)
+ mmc_out(ioaddr, --o, *(--b));
+} /* mmc_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read a byte from the MMC.
+ * Optimised version for 1 byte, avoid using memory.
+ */
+static inline u8 mmc_in(unsigned long ioaddr, u16 o)
+{
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
+ outw(o << 1, MMCR(ioaddr));
+
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY);
+ return (u8) (inw(MMCR(ioaddr)) >> 8);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to read bytes from the Modem Management Controller.
+ * The implementation is complicated by a lack of address lines,
+ * which prevents decoding of the low-order bit.
+ * (code has just been moved in the above function)
+ * We start at the end because it is the way it should be!
+ */
+static inline void mmc_read(unsigned long ioaddr, u8 o, u8 * b, int n)
+{
+ o += n;
+ b += n;
+
+ while (n-- > 0)
+ *(--b) = mmc_in(ioaddr, --o);
+} /* mmc_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the type of encryption available.
+ */
+static inline int mmc_encr(unsigned long ioaddr)
+{ /* I/O port of the card */
+ int temp;
+
+ temp = mmc_in(ioaddr, mmroff(0, mmr_des_avail));
+ if ((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
+ return 0;
+ else
+ return temp;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wait for the frequency EEPROM to complete a command.
+ * I hope this one will be optimally inlined.
+ */
+static inline void fee_wait(unsigned long ioaddr, /* I/O port of the card */
+ int delay, /* Base delay to wait for */
+ int number)
+{ /* Number of time to wait */
+ int count = 0; /* Wait only a limited time */
+
+ while ((count++ < number) &&
+ (mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+ MMR_FEE_STATUS_BUSY)) udelay(delay);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the Frequency EEPROM (frequency select cards).
+ */
+static void fee_read(unsigned long ioaddr, /* I/O port of the card */
+ u16 o, /* destination offset */
+ u16 * b, /* data buffer */
+ int n)
+{ /* number of registers */
+ b += n; /* Position at the end of the area */
+
+ /* Write the address */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+ /* Loop on all buffer */
+ while (n-- > 0) {
+ /* Write the read command */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+ MMW_FEE_CTRL_READ);
+
+ /* Wait until EEPROM is ready (should be quick). */
+ fee_wait(ioaddr, 10, 100);
+
+ /* Read the value. */
+ *--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) |
+ mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
+ }
+}
+
+#ifdef WIRELESS_EXT /* if the wireless extension exists in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes from the Frequency EEPROM (frequency select cards).
+ * This is a bit complicated, because the frequency EEPROM has to
+ * be unprotected and the write enabled.
+ * Jean II
+ */
+static void fee_write(unsigned long ioaddr, /* I/O port of the card */
+ u16 o, /* destination offset */
+ u16 * b, /* data buffer */
+ int n)
+{ /* number of registers */
+ b += n; /* Position at the end of the area. */
+
+#ifdef EEPROM_IS_PROTECTED /* disabled */
+#ifdef DOESNT_SEEM_TO_WORK /* disabled */
+ /* Ask to read the protected register */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
+
+ fee_wait(ioaddr, 10, 100);
+
+ /* Read the protected register. */
+ printk("Protected 2: %02X-%02X\n",
+ mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)),
+ mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
+#endif /* DOESNT_SEEM_TO_WORK */
+
+ /* Enable protected register. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
+
+ fee_wait(ioaddr, 10, 100);
+
+ /* Unprotect area. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+#ifdef DOESNT_SEEM_TO_WORK /* disabled */
+ /* or use: */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
+#endif /* DOESNT_SEEM_TO_WORK */
+
+ fee_wait(ioaddr, 10, 100);
+#endif /* EEPROM_IS_PROTECTED */
+
+ /* Write enable. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
+
+ fee_wait(ioaddr, 10, 100);
+
+ /* Write the EEPROM address. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+ /* Loop on all buffer */
+ while (n-- > 0) {
+ /* Write the value. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
+
+ /* Write the write command. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+ MMW_FEE_CTRL_WRITE);
+
+ /* WaveLAN documentation says to wait at least 10 ms for EEBUSY = 0 */
+ mdelay(10);
+ fee_wait(ioaddr, 10, 100);
+ }
+
+ /* Write disable. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
+
+ fee_wait(ioaddr, 10, 100);
+
+#ifdef EEPROM_IS_PROTECTED /* disabled */
+ /* Reprotect EEPROM. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+
+ fee_wait(ioaddr, 10, 100);
+#endif /* EEPROM_IS_PROTECTED */
+}
+#endif /* WIRELESS_EXT */
+
+/************************ I82586 SUBROUTINES *************************/
+/*
+ * Useful subroutines to manage the Ethernet controller
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the on-board RAM.
+ * Why does inlining this function make it fail?
+ */
+static /*inline */ void obram_read(unsigned long ioaddr,
+ u16 o, u8 * b, int n)
+{
+ outw(o, PIOR1(ioaddr));
+ insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes to the on-board RAM.
+ */
+static inline void obram_write(unsigned long ioaddr, u16 o, u8 * b, int n)
+{
+ outw(o, PIOR1(ioaddr));
+ outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Acknowledge the reading of the status issued by the i82586.
+ */
+static void wv_ack(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ u16 scb_cs;
+ int i;
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
+ (unsigned char *) &scb_cs, sizeof(scb_cs));
+ scb_cs &= SCB_ST_INT;
+
+ if (scb_cs == 0)
+ return;
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--) {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(10);
+ }
+ udelay(100);
+
+#ifdef DEBUG_CONFIG_ERROR
+ if (i <= 0)
+ printk(KERN_INFO
+ "%s: wv_ack(): board not accepting command.\n",
+ dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set channel attention bit and busy wait until command has
+ * completed, then acknowledge completion of the command.
+ */
+static inline int wv_synchronous_cmd(device * dev, const char *str)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ u16 scb_cmd;
+ ach_t cb;
+ int i;
+
+ scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &scb_cmd, sizeof(scb_cmd));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--) {
+ obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb,
+ sizeof(cb));
+ if (cb.ac_status & AC_SFLD_C)
+ break;
+
+ udelay(10);
+ }
+ udelay(100);
+
+ if (i <= 0 || !(cb.ac_status & AC_SFLD_OK)) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO "%s: %s failed; status = 0x%x\n",
+ dev->name, str, cb.ac_status);
+#endif
+#ifdef DEBUG_I82586_SHOW
+ wv_scb_show(ioaddr);
+#endif
+ return -1;
+ }
+
+ /* Ack the status */
+ wv_ack(dev);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Configuration commands completion interrupt.
+ * Check if done, and if OK.
+ */
+static inline int
+wv_config_complete(device * dev, unsigned long ioaddr, net_local * lp)
+{
+ unsigned short mcs_addr;
+ unsigned short status;
+ int ret;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name);
+#endif
+
+ mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t)
+ + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t);
+
+ /* Read the status of the last command (set mc list). */
+ obram_read(ioaddr, acoff(mcs_addr, ac_status),
+ (unsigned char *) &status, sizeof(status));
+
+ /* If not completed -> exit */
+ if ((status & AC_SFLD_C) == 0)
+ ret = 0; /* Not ready to be scrapped */
+ else {
+#ifdef DEBUG_CONFIG_ERROR
+ unsigned short cfg_addr;
+ unsigned short ias_addr;
+
+ /* Check mc_config command */
+ if ((status & AC_SFLD_OK) != AC_SFLD_OK)
+ printk(KERN_INFO
+ "%s: wv_config_complete(): set_multicast_address failed; status = 0x%x\n",
+ dev->name, status);
+
+ /* check ia-config command */
+ ias_addr = mcs_addr - sizeof(ac_ias_t);
+ obram_read(ioaddr, acoff(ias_addr, ac_status),
+ (unsigned char *) &status, sizeof(status));
+ if ((status & AC_SFLD_OK) != AC_SFLD_OK)
+ printk(KERN_INFO
+ "%s: wv_config_complete(): set_MAC_address failed; status = 0x%x\n",
+ dev->name, status);
+
+ /* Check config command. */
+ cfg_addr = ias_addr - sizeof(ac_cfg_t);
+ obram_read(ioaddr, acoff(cfg_addr, ac_status),
+ (unsigned char *) &status, sizeof(status));
+ if ((status & AC_SFLD_OK) != AC_SFLD_OK)
+ printk(KERN_INFO
+ "%s: wv_config_complete(): configure failed; status = 0x%x\n",
+ dev->name, status);
+#endif /* DEBUG_CONFIG_ERROR */
+
+ ret = 1; /* Ready to be scrapped */
+ }
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name,
+ ret);
+#endif
+ return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Command completion interrupt.
+ * Reclaim as many freed tx buffers as we can.
+ * (called in wavelan_interrupt()).
+ * Note : the spinlock is already grabbed for us.
+ */
+static int wv_complete(device * dev, unsigned long ioaddr, net_local * lp)
+{
+ int nreaped = 0;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name);
+#endif
+
+ /* Loop on all the transmit buffers */
+ while (lp->tx_first_in_use != I82586NULL) {
+ unsigned short tx_status;
+
+ /* Read the first transmit buffer */
+ obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status),
+ (unsigned char *) &tx_status,
+ sizeof(tx_status));
+
+ /* If not completed -> exit */
+ if ((tx_status & AC_SFLD_C) == 0)
+ break;
+
+ /* Hack for reconfiguration */
+ if (tx_status == 0xFFFF)
+ if (!wv_config_complete(dev, ioaddr, lp))
+ break; /* Not completed */
+
+ /* We now remove this buffer */
+ nreaped++;
+ --lp->tx_n_in_use;
+
+/*
+if (lp->tx_n_in_use > 0)
+ printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
+*/
+
+ /* Was it the last one? */
+ if (lp->tx_n_in_use <= 0)
+ lp->tx_first_in_use = I82586NULL;
+ else {
+ /* Next one in the chain */
+ lp->tx_first_in_use += TXBLOCKZ;
+ if (lp->tx_first_in_use >=
+ OFFSET_CU +
+ NTXBLOCKS * TXBLOCKZ) lp->tx_first_in_use -=
+ NTXBLOCKS * TXBLOCKZ;
+ }
+
+ /* Hack for reconfiguration */
+ if (tx_status == 0xFFFF)
+ continue;
+
+ /* Now, check status of the finished command */
+ if (tx_status & AC_SFLD_OK) {
+ int ncollisions;
+
+ lp->stats.tx_packets++;
+ ncollisions = tx_status & AC_SFLD_MAXCOL;
+ lp->stats.collisions += ncollisions;
+#ifdef DEBUG_TX_INFO
+ if (ncollisions > 0)
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx completed after %d collisions.\n",
+ dev->name, ncollisions);
+#endif
+ } else {
+ lp->stats.tx_errors++;
+ if (tx_status & AC_SFLD_S10) {
+ lp->stats.tx_carrier_errors++;
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx error: no CS.\n",
+ dev->name);
+#endif
+ }
+ if (tx_status & AC_SFLD_S9) {
+ lp->stats.tx_carrier_errors++;
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx error: lost CTS.\n",
+ dev->name);
+#endif
+ }
+ if (tx_status & AC_SFLD_S8) {
+ lp->stats.tx_fifo_errors++;
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx error: slow DMA.\n",
+ dev->name);
+#endif
+ }
+ if (tx_status & AC_SFLD_S6) {
+ lp->stats.tx_heartbeat_errors++;
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx error: heart beat.\n",
+ dev->name);
+#endif
+ }
+ if (tx_status & AC_SFLD_S5) {
+ lp->stats.tx_aborted_errors++;
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx error: too many collisions.\n",
+ dev->name);
+#endif
+ }
+ }
+
+#ifdef DEBUG_TX_INFO
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx completed, tx_status 0x%04x\n",
+ dev->name, tx_status);
+#endif
+ }
+
+#ifdef DEBUG_INTERRUPT_INFO
+ if (nreaped > 1)
+ printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n",
+ dev->name, nreaped);
+#endif
+
+ /*
+ * Inform upper layers.
+ */
+ if (lp->tx_n_in_use < NTXBLOCKS - 1) {
+ netif_wake_queue(dev);
+ }
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name);
+#endif
+ return nreaped;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Reconfigure the i82586, or at least ask for it.
+ * Because wv_82586_config uses a transmission buffer, we must do it
+ * when we are sure that there is one left, so we do it now
+ * or in wavelan_packet_xmit() (I can't find any better place,
+ * wavelan_interrupt is not an option), so you may experience
+ * delays sometimes.
+ */
+static inline void wv_82586_reconfig(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long flags;
+
+ /* Arm the flag, will be cleard in wv_82586_config() */
+ lp->reconfig_82586 = 1;
+
+ /* Check if we can do it now ! */
+ if((netif_running(dev)) && !(netif_queue_stopped(dev))) {
+ wv_splhi(lp, &flags);
+ /* May fail */
+ wv_82586_config(dev);
+ wv_splx(lp, &flags);
+ }
+ else {
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG
+ "%s: wv_82586_reconfig(): delayed (state = %lX)\n",
+ dev->name, dev->state);
+#endif
+ }
+}
+
+/********************* DEBUG & INFO SUBROUTINES *********************/
+/*
+ * This routine is used in the code to show information for debugging.
+ * Most of the time, it dumps the contents of hardware structures.
+ */
+
+#ifdef DEBUG_PSA_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted contents of the Parameter Storage Area.
+ */
+static void wv_psa_show(psa_t * p)
+{
+ printk(KERN_DEBUG "##### WaveLAN PSA contents: #####\n");
+ printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
+ p->psa_io_base_addr_1,
+ p->psa_io_base_addr_2,
+ p->psa_io_base_addr_3, p->psa_io_base_addr_4);
+ printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
+ p->psa_rem_boot_addr_1,
+ p->psa_rem_boot_addr_2, p->psa_rem_boot_addr_3);
+ printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
+ printk("psa_int_req_no: %d\n", p->psa_int_req_no);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG
+ "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ p->psa_unused0[0], p->psa_unused0[1], p->psa_unused0[2],
+ p->psa_unused0[3], p->psa_unused0[4], p->psa_unused0[5],
+ p->psa_unused0[6]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG
+ "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ p->psa_univ_mac_addr[0], p->psa_univ_mac_addr[1],
+ p->psa_univ_mac_addr[2], p->psa_univ_mac_addr[3],
+ p->psa_univ_mac_addr[4], p->psa_univ_mac_addr[5]);
+ printk(KERN_DEBUG
+ "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ p->psa_local_mac_addr[0], p->psa_local_mac_addr[1],
+ p->psa_local_mac_addr[2], p->psa_local_mac_addr[3],
+ p->psa_local_mac_addr[4], p->psa_local_mac_addr[5]);
+ printk(KERN_DEBUG "psa_univ_local_sel: %d, ",
+ p->psa_univ_local_sel);
+ printk("psa_comp_number: %d, ", p->psa_comp_number);
+ printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
+ printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
+ p->psa_feature_select);
+ printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
+ printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
+ printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
+ printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0],
+ p->psa_nwid[1]);
+ printk("psa_nwid_select: %d\n", p->psa_nwid_select);
+ printk(KERN_DEBUG "psa_encryption_select: %d, ",
+ p->psa_encryption_select);
+ printk
+ ("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ p->psa_encryption_key[0], p->psa_encryption_key[1],
+ p->psa_encryption_key[2], p->psa_encryption_key[3],
+ p->psa_encryption_key[4], p->psa_encryption_key[5],
+ p->psa_encryption_key[6], p->psa_encryption_key[7]);
+ printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
+ printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
+ p->psa_call_code[0]);
+ printk
+ ("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ p->psa_call_code[0], p->psa_call_code[1], p->psa_call_code[2],
+ p->psa_call_code[3], p->psa_call_code[4], p->psa_call_code[5],
+ p->psa_call_code[6], p->psa_call_code[7]);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
+ p->psa_reserved[0],
+ p->psa_reserved[1], p->psa_reserved[2], p->psa_reserved[3]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
+ printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
+ printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
+} /* wv_psa_show */
+#endif /* DEBUG_PSA_SHOW */
+
+#ifdef DEBUG_MMC_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the Modem Management Controller.
+ * This function needs to be completed.
+ */
+static void wv_mmc_show(device * dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ net_local *lp = (net_local *) dev->priv;
+ mmr_t m;
+
+ /* Basic check */
+ if (hasr_read(ioaddr) & HASR_NO_CLK) {
+ printk(KERN_WARNING
+ "%s: wv_mmc_show: modem not connected\n",
+ dev->name);
+ return;
+ }
+
+ /* Read the mmc */
+ mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+ mmc_read(ioaddr, 0, (u8 *) & m, sizeof(m));
+ mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+#ifdef WIRELESS_EXT /* if wireless extension exists in the kernel */
+ /* Don't forget to update statistics */
+ lp->wstats.discard.nwid +=
+ (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+#endif /* WIRELESS_EXT */
+
+ printk(KERN_DEBUG "##### WaveLAN modem status registers: #####\n");
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG
+ "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ m.mmr_unused0[0], m.mmr_unused0[1], m.mmr_unused0[2],
+ m.mmr_unused0[3], m.mmr_unused0[4], m.mmr_unused0[5],
+ m.mmr_unused0[6], m.mmr_unused0[7]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "Encryption algorithm: %02X - Status: %02X\n",
+ m.mmr_des_avail, m.mmr_des_status);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
+ m.mmr_unused1[0],
+ m.mmr_unused1[1],
+ m.mmr_unused1[2], m.mmr_unused1[3], m.mmr_unused1[4]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
+ m.mmr_dce_status,
+ (m.
+ mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ?
+ "energy detected," : "",
+ (m.
+ mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
+ "loop test indicated," : "",
+ (m.
+ mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ?
+ "transmitter on," : "",
+ (m.
+ mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
+ "jabber timer expired," : "");
+ printk(KERN_DEBUG "Dsp ID: %02X\n", m.mmr_dsp_id);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
+ m.mmr_unused2[0], m.mmr_unused2[1]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
+ (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
+ (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
+ printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
+ m.mmr_thr_pre_set & MMR_THR_PRE_SET,
+ (m.
+ mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" :
+ "below");
+ printk(KERN_DEBUG "signal_lvl: %d [%s], ",
+ m.mmr_signal_lvl & MMR_SIGNAL_LVL,
+ (m.
+ mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" :
+ "no new msg");
+ printk("silence_lvl: %d [%s], ",
+ m.mmr_silence_lvl & MMR_SILENCE_LVL,
+ (m.
+ mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" :
+ "no new update");
+ printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL,
+ (m.
+ mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" :
+ "Antenna 0");
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
+#endif /* DEBUG_SHOW_UNUSED */
+} /* wv_mmc_show */
+#endif /* DEBUG_MMC_SHOW */
+
+#ifdef DEBUG_I82586_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the last block of the i82586 memory.
+ */
+static void wv_scb_show(unsigned long ioaddr)
+{
+ scb_t scb;
+
+ obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
+ sizeof(scb));
+
+ printk(KERN_DEBUG "##### WaveLAN system control block: #####\n");
+
+ printk(KERN_DEBUG "status: ");
+ printk("stat 0x%x[%s%s%s%s] ",
+ (scb.
+ scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA |
+ SCB_ST_RNR)) >> 12,
+ (scb.
+ scb_status & SCB_ST_CX) ? "command completion interrupt," :
+ "", (scb.scb_status & SCB_ST_FR) ? "frame received," : "",
+ (scb.
+ scb_status & SCB_ST_CNA) ? "command unit not active," : "",
+ (scb.
+ scb_status & SCB_ST_RNR) ? "receiving unit not ready," :
+ "");
+ printk("cus 0x%x[%s%s%s] ", (scb.scb_status & SCB_ST_CUS) >> 8,
+ ((scb.scb_status & SCB_ST_CUS) ==
+ SCB_ST_CUS_IDLE) ? "idle" : "",
+ ((scb.scb_status & SCB_ST_CUS) ==
+ SCB_ST_CUS_SUSP) ? "suspended" : "",
+ ((scb.scb_status & SCB_ST_CUS) ==
+ SCB_ST_CUS_ACTV) ? "active" : "");
+ printk("rus 0x%x[%s%s%s%s]\n", (scb.scb_status & SCB_ST_RUS) >> 4,
+ ((scb.scb_status & SCB_ST_RUS) ==
+ SCB_ST_RUS_IDLE) ? "idle" : "",
+ ((scb.scb_status & SCB_ST_RUS) ==
+ SCB_ST_RUS_SUSP) ? "suspended" : "",
+ ((scb.scb_status & SCB_ST_RUS) ==
+ SCB_ST_RUS_NRES) ? "no resources" : "",
+ ((scb.scb_status & SCB_ST_RUS) ==
+ SCB_ST_RUS_RDY) ? "ready" : "");
+
+ printk(KERN_DEBUG "command: ");
+ printk("ack 0x%x[%s%s%s%s] ",
+ (scb.
+ scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR |
+ SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
+ (scb.
+ scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
+ (scb.
+ scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
+ (scb.
+ scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "",
+ (scb.
+ scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : "");
+ printk("cuc 0x%x[%s%s%s%s%s] ",
+ (scb.scb_command & SCB_CMD_CUC) >> 8,
+ ((scb.scb_command & SCB_CMD_CUC) ==
+ SCB_CMD_CUC_NOP) ? "nop" : "",
+ ((scb.scb_command & SCB_CMD_CUC) ==
+ SCB_CMD_CUC_GO) ? "start cbl_offset" : "",
+ ((scb.scb_command & SCB_CMD_CUC) ==
+ SCB_CMD_CUC_RES) ? "resume execution" : "",
+ ((scb.scb_command & SCB_CMD_CUC) ==
+ SCB_CMD_CUC_SUS) ? "suspend execution" : "",
+ ((scb.scb_command & SCB_CMD_CUC) ==
+ SCB_CMD_CUC_ABT) ? "abort execution" : "");
+ printk("ruc 0x%x[%s%s%s%s%s]\n",
+ (scb.scb_command & SCB_CMD_RUC) >> 4,
+ ((scb.scb_command & SCB_CMD_RUC) ==
+ SCB_CMD_RUC_NOP) ? "nop" : "",
+ ((scb.scb_command & SCB_CMD_RUC) ==
+ SCB_CMD_RUC_GO) ? "start rfa_offset" : "",
+ ((scb.scb_command & SCB_CMD_RUC) ==
+ SCB_CMD_RUC_RES) ? "resume reception" : "",
+ ((scb.scb_command & SCB_CMD_RUC) ==
+ SCB_CMD_RUC_SUS) ? "suspend reception" : "",
+ ((scb.scb_command & SCB_CMD_RUC) ==
+ SCB_CMD_RUC_ABT) ? "abort reception" : "");
+
+ printk(KERN_DEBUG "cbl_offset 0x%x ", scb.scb_cbl_offset);
+ printk("rfa_offset 0x%x\n", scb.scb_rfa_offset);
+
+ printk(KERN_DEBUG "crcerrs %d ", scb.scb_crcerrs);
+ printk("alnerrs %d ", scb.scb_alnerrs);
+ printk("rscerrs %d ", scb.scb_rscerrs);
+ printk("ovrnerrs %d\n", scb.scb_ovrnerrs);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the i82586's receive unit.
+ */
+static void wv_ru_show(device * dev)
+{
+ /* net_local *lp = (net_local *) dev->priv; */
+
+ printk(KERN_DEBUG
+ "##### WaveLAN i82586 receiver unit status: #####\n");
+ printk(KERN_DEBUG "ru:");
+ /*
+ * Not implemented yet
+ */
+ printk("\n");
+} /* wv_ru_show */
+
+/*------------------------------------------------------------------*/
+/*
+ * Display info about one control block of the i82586 memory.
+ */
+static void wv_cu_show_one(device * dev, net_local * lp, int i, u16 p)
+{
+ unsigned long ioaddr;
+ ac_tx_t actx;
+
+ ioaddr = dev->base_addr;
+
+ printk("%d: 0x%x:", i, p);
+
+ obram_read(ioaddr, p, (unsigned char *) &actx, sizeof(actx));
+ printk(" status=0x%x,", actx.tx_h.ac_status);
+ printk(" command=0x%x,", actx.tx_h.ac_command);
+
+ /*
+ {
+ tbd_t tbd;
+
+ obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd));
+ printk(" tbd_status=0x%x,", tbd.tbd_status);
+ }
+ */
+
+ printk("|");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Print status of the command unit of the i82586.
+ */
+static void wv_cu_show(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned int i;
+ u16 p;
+
+ printk(KERN_DEBUG
+ "##### WaveLAN i82586 command unit status: #####\n");
+
+ printk(KERN_DEBUG);
+ for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++) {
+ wv_cu_show_one(dev, lp, i, p);
+
+ p += TXBLOCKZ;
+ if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ p -= NTXBLOCKS * TXBLOCKZ;
+ }
+ printk("\n");
+}
+#endif /* DEBUG_I82586_SHOW */
+
+#ifdef DEBUG_DEVICE_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver.
+ */
+static void wv_dev_show(device * dev)
+{
+ printk(KERN_DEBUG "dev:");
+ printk(" state=%lX,", dev->state);
+ printk(" trans_start=%ld,", dev->trans_start);
+ printk(" flags=0x%x,", dev->flags);
+ printk("\n");
+} /* wv_dev_show */
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver's
+ * private information.
+ */
+static void wv_local_show(device * dev)
+{
+ net_local *lp;
+
+ lp = (net_local *) dev->priv;
+
+ printk(KERN_DEBUG "local:");
+ printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
+ printk(" hacr=0x%x,", lp->hacr);
+ printk(" rx_head=0x%x,", lp->rx_head);
+ printk(" rx_last=0x%x,", lp->rx_last);
+ printk(" tx_first_free=0x%x,", lp->tx_first_free);
+ printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use);
+ printk("\n");
+} /* wv_local_show */
+#endif /* DEBUG_DEVICE_SHOW */
+
+#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO)
+/*------------------------------------------------------------------*/
+/*
+ * Dump packet header (and content if necessary) on the screen
+ */
+static inline void wv_packet_info(u8 * p, /* Packet to dump */
+ int length, /* Length of the packet */
+ char *msg1, /* Name of the device */
+ char *msg2)
+{ /* Name of the function */
+ int i;
+ int maxi;
+
+ printk(KERN_DEBUG
+ "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n",
+ msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length);
+ printk(KERN_DEBUG
+ "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n",
+ msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12],
+ p[13]);
+
+#ifdef DEBUG_PACKET_DUMP
+
+ printk(KERN_DEBUG "data=\"");
+
+ if ((maxi = length) > DEBUG_PACKET_DUMP)
+ maxi = DEBUG_PACKET_DUMP;
+ for (i = 14; i < maxi; i++)
+ if (p[i] >= ' ' && p[i] <= '~')
+ printk(" %c", p[i]);
+ else
+ printk("%02X", p[i]);
+ if (maxi < length)
+ printk("..");
+ printk("\"\n");
+ printk(KERN_DEBUG "\n");
+#endif /* DEBUG_PACKET_DUMP */
+}
+#endif /* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */
+
+/*------------------------------------------------------------------*/
+/*
+ * This is the information which is displayed by the driver at startup.
+ * There are lots of flags for configuring it to your liking.
+ */
+static inline void wv_init_info(device * dev)
+{
+ short ioaddr = dev->base_addr;
+ net_local *lp = (net_local *) dev->priv;
+ psa_t psa;
+ int i;
+
+ /* Read the parameter storage area */
+ psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+#ifdef DEBUG_PSA_SHOW
+ wv_psa_show(&psa);
+#endif
+#ifdef DEBUG_MMC_SHOW
+ wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82586_SHOW
+ wv_cu_show(dev);
+#endif
+
+#ifdef DEBUG_BASIC_SHOW
+ /* Now, let's go for the basic stuff. */
+ printk(KERN_NOTICE "%s: WaveLAN at %#x,", dev->name, ioaddr);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]);
+ printk(", IRQ %d", dev->irq);
+
+ /* Print current network ID. */
+ if (psa.psa_nwid_select)
+ printk(", nwid 0x%02X-%02X", psa.psa_nwid[0],
+ psa.psa_nwid[1]);
+ else
+ printk(", nwid off");
+
+ /* If 2.00 card */
+ if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+ (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
+ unsigned short freq;
+
+ /* Ask the EEPROM to read the frequency from the first area. */
+ fee_read(ioaddr, 0x00, &freq, 1);
+
+ /* Print frequency */
+ printk(", 2.00, %ld", (freq >> 6) + 2400L);
+
+ /* Hack! */
+ if (freq & 0x20)
+ printk(".5");
+ } else {
+ printk(", PC");
+ switch (psa.psa_comp_number) {
+ case PSA_COMP_PC_AT_915:
+ case PSA_COMP_PC_AT_2400:
+ printk("-AT");
+ break;
+ case PSA_COMP_PC_MC_915:
+ case PSA_COMP_PC_MC_2400:
+ printk("-MC");
+ break;
+ case PSA_COMP_PCMCIA_915:
+ printk("MCIA");
+ break;
+ default:
+ printk("?");
+ }
+ printk(", ");
+ switch (psa.psa_subband) {
+ case PSA_SUBBAND_915:
+ printk("915");
+ break;
+ case PSA_SUBBAND_2425:
+ printk("2425");
+ break;
+ case PSA_SUBBAND_2460:
+ printk("2460");
+ break;
+ case PSA_SUBBAND_2484:
+ printk("2484");
+ break;
+ case PSA_SUBBAND_2430_5:
+ printk("2430.5");
+ break;
+ default:
+ printk("?");
+ }
+ }
+
+ printk(" MHz\n");
+#endif /* DEBUG_BASIC_SHOW */
+
+#ifdef DEBUG_VERSION_SHOW
+ /* Print version information */
+ printk(KERN_NOTICE "%s", version);
+#endif
+} /* wv_init_info */
+
+/********************* IOCTL, STATS & RECONFIG *********************/
+/*
+ * We found here routines that are called by Linux on different
+ * occasions after the configuration and not for transmitting data
+ * These may be called when the user use ifconfig, /proc/net/dev
+ * or wireless extensions
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the current Ethernet statistics. This may be called with the
+ * card open or closed.
+ * Used when the user read /proc/net/dev
+ */
+static en_stats *wavelan_get_stats(device * dev)
+{
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
+#endif
+
+ return (&((net_local *) dev->priv)->stats);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, clear multicast list
+ * num_addrs > 0 Multicast mode, receive normal and MC packets,
+ * and do best-effort filtering.
+ */
+static void wavelan_set_multicast_list(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n",
+ dev->name);
+#endif
+
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG
+ "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n",
+ dev->name, dev->flags, dev->mc_count);
+#endif
+
+ /* Are we asking for promiscuous mode,
+ * or all multicast addresses (we don't have that!)
+ * or too many multicast addresses for the hardware filter? */
+ if ((dev->flags & IFF_PROMISC) ||
+ (dev->flags & IFF_ALLMULTI) ||
+ (dev->mc_count > I82586_MAX_MULTICAST_ADDRESSES)) {
+ /*
+ * Enable promiscuous mode: receive all packets.
+ */
+ if (!lp->promiscuous) {
+ lp->promiscuous = 1;
+ lp->mc_count = 0;
+
+ wv_82586_reconfig(dev);
+
+ /* Tell the kernel that we are doing a really bad job. */
+ dev->flags |= IFF_PROMISC;
+ }
+ } else
+ /* Are there multicast addresses to send? */
+ if (dev->mc_list != (struct dev_mc_list *) NULL) {
+ /*
+ * Disable promiscuous mode, but receive all packets
+ * in multicast list
+ */
+#ifdef MULTICAST_AVOID
+ if (lp->promiscuous || (dev->mc_count != lp->mc_count))
+#endif
+ {
+ lp->promiscuous = 0;
+ lp->mc_count = dev->mc_count;
+
+ wv_82586_reconfig(dev);
+ }
+ } else {
+ /*
+ * Switch to normal mode: disable promiscuous mode and
+ * clear the multicast list.
+ */
+ if (lp->promiscuous || lp->mc_count == 0) {
+ lp->promiscuous = 0;
+ lp->mc_count = 0;
+
+ wv_82586_reconfig(dev);
+ }
+ }
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n",
+ dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This function doesn't exist.
+ * (Note : it was a nice way to test the reconfigure stuff...)
+ */
+#ifdef SET_MAC_ADDRESS
+static int wavelan_set_mac_address(device * dev, void *addr)
+{
+ struct sockaddr *mac = addr;
+
+ /* Copy the address. */
+ memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE);
+
+ /* Reconfigure the beast. */
+ wv_82586_reconfig(dev);
+
+ return 0;
+}
+#endif /* SET_MAC_ADDRESS */
+
+#ifdef WIRELESS_EXT /* if wireless extensions exist in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Frequency setting (for hardware capable of it)
+ * It's a bit complicated and you don't really want to look into it.
+ * (called in wavelan_ioctl)
+ */
+static inline int wv_set_frequency(unsigned long ioaddr, /* I/O port of the card */
+ iw_freq * frequency)
+{
+ const int BAND_NUM = 10; /* Number of bands */
+ long freq = 0L; /* offset to 2.4 GHz in .5 MHz */
+#ifdef DEBUG_IOCTL_INFO
+ int i;
+#endif
+
+ /* Setting by frequency */
+ /* Theoretically, you may set any frequency between
+ * the two limits with a 0.5 MHz precision. In practice,
+ * I don't want you to have trouble with local regulations.
+ */
+ if ((frequency->e == 1) &&
+ (frequency->m >= (int) 2.412e8)
+ && (frequency->m <= (int) 2.487e8)) {
+ freq = ((frequency->m / 10000) - 24000L) / 5;
+ }
+
+ /* Setting by channel (same as wfreqsel) */
+ /* Warning: each channel is 22 MHz wide, so some of the channels
+ * will interfere. */
+ if ((frequency->e == 0) && (frequency->m < BAND_NUM)) {
+ /* Get frequency offset. */
+ freq = channel_bands[frequency->m] >> 1;
+ }
+
+ /* Verify that the frequency is allowed. */
+ if (freq != 0L) {
+ u16 table[10]; /* Authorized frequency table */
+
+ /* Read the frequency table. */
+ fee_read(ioaddr, 0x71, table, 10);
+
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG "Frequency table: ");
+ for (i = 0; i < 10; i++) {
+ printk(" %04X", table[i]);
+ }
+ printk("\n");
+#endif
+
+ /* Look in the table to see whether the frequency is allowed. */
+ if (!(table[9 - ((freq - 24) / 16)] &
+ (1 << ((freq - 24) % 16)))) return -EINVAL; /* not allowed */
+ } else
+ return -EINVAL;
+
+ /* if we get a usable frequency */
+ if (freq != 0L) {
+ unsigned short area[16];
+ unsigned short dac[2];
+ unsigned short area_verify[16];
+ unsigned short dac_verify[2];
+ /* Corresponding gain (in the power adjust value table)
+ * See AT&T WaveLAN Data Manual, REF 407-024689/E, page 3-8
+ * and WCIN062D.DOC, page 6.2.9. */
+ unsigned short power_limit[] = { 40, 80, 120, 160, 0 };
+ int power_band = 0; /* Selected band */
+ unsigned short power_adjust; /* Correct value */
+
+ /* Search for the gain. */
+ power_band = 0;
+ while ((freq > power_limit[power_band]) &&
+ (power_limit[++power_band] != 0));
+
+ /* Read the first area. */
+ fee_read(ioaddr, 0x00, area, 16);
+
+ /* Read the DAC. */
+ fee_read(ioaddr, 0x60, dac, 2);
+
+ /* Read the new power adjust value. */
+ fee_read(ioaddr, 0x6B - (power_band >> 1), &power_adjust,
+ 1);
+ if (power_band & 0x1)
+ power_adjust >>= 8;
+ else
+ power_adjust &= 0xFF;
+
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG "WaveLAN EEPROM Area 1: ");
+ for (i = 0; i < 16; i++) {
+ printk(" %04X", area[i]);
+ }
+ printk("\n");
+
+ printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n",
+ dac[0], dac[1]);
+#endif
+
+ /* Frequency offset (for info only) */
+ area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
+
+ /* Receiver Principle main divider coefficient */
+ area[3] = (freq >> 1) + 2400L - 352L;
+ area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+ /* Transmitter Main divider coefficient */
+ area[13] = (freq >> 1) + 2400L;
+ area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+ /* Other parts of the area are flags, bit streams or unused. */
+
+ /* Set the value in the DAC. */
+ dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
+ dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
+
+ /* Write the first area. */
+ fee_write(ioaddr, 0x00, area, 16);
+
+ /* Write the DAC. */
+ fee_write(ioaddr, 0x60, dac, 2);
+
+ /* We now should verify here that the writing of the EEPROM went OK. */
+
+ /* Reread the first area. */
+ fee_read(ioaddr, 0x00, area_verify, 16);
+
+ /* Reread the DAC. */
+ fee_read(ioaddr, 0x60, dac_verify, 2);
+
+ /* Compare. */
+ if (memcmp(area, area_verify, 16 * 2) ||
+ memcmp(dac, dac_verify, 2 * 2)) {
+#ifdef DEBUG_IOCTL_ERROR
+ printk(KERN_INFO
+ "WaveLAN: wv_set_frequency: unable to write new frequency to EEPROM(?).\n");
+#endif
+ return -EOPNOTSUPP;
+ }
+
+ /* We must download the frequency parameters to the
+ * synthesizers (from the EEPROM - area 1)
+ * Note: as the EEPROM is automatically decremented, we set the end
+ * if the area... */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+ MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+ /* Wait until the download is finished. */
+ fee_wait(ioaddr, 100, 100);
+
+ /* We must now download the power adjust value (gain) to
+ * the synthesizers (from the EEPROM - area 7 - DAC). */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+ MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+ /* Wait for the download to finish. */
+ fee_wait(ioaddr, 100, 100);
+
+#ifdef DEBUG_IOCTL_INFO
+ /* Verification of what we have done */
+
+ printk(KERN_DEBUG "WaveLAN EEPROM Area 1: ");
+ for (i = 0; i < 16; i++) {
+ printk(" %04X", area_verify[i]);
+ }
+ printk("\n");
+
+ printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n",
+ dac_verify[0], dac_verify[1]);
+#endif
+
+ return 0;
+ } else
+ return -EINVAL; /* Bah, never get there... */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Give the list of available frequencies.
+ */
+static inline int wv_frequency_list(unsigned long ioaddr, /* I/O port of the card */
+ iw_freq * list, /* List of frequencies to fill */
+ int max)
+{ /* Maximum number of frequencies */
+ u16 table[10]; /* Authorized frequency table */
+ long freq = 0L; /* offset to 2.4 GHz in .5 MHz + 12 MHz */
+ int i; /* index in the table */
+ int c = 0; /* Channel number */
+
+ /* Read the frequency table. */
+ fee_read(ioaddr, 0x71 /* frequency table */ , table, 10);
+
+ /* Check all frequencies. */
+ i = 0;
+ for (freq = 0; freq < 150; freq++)
+ /* Look in the table if the frequency is allowed */
+ if (table[9 - (freq / 16)] & (1 << (freq % 16))) {
+ /* Compute approximate channel number */
+ while ((((channel_bands[c] >> 1) - 24) < freq) &&
+ (c < NELS(channel_bands)))
+ c++;
+ list[i].i = c; /* Set the list index */
+
+ /* put in the list */
+ list[i].m = (((freq + 24) * 5) + 24000L) * 10000;
+ list[i++].e = 1;
+
+ /* Check number. */
+ if (i >= max)
+ return (i);
+ }
+
+ return (i);
+}
+
+#ifdef WIRELESS_SPY
+/*------------------------------------------------------------------*/
+/*
+ * Gather wireless spy statistics: for each packet, compare the source
+ * address with our list, and if they match, get the statistics.
+ * Sorry, but this function really needs the wireless extensions.
+ */
+static inline void wl_spy_gather(device * dev, u8 * mac, /* MAC address */
+ u8 * stats)
+{ /* Statistics to gather */
+ net_local *lp = (net_local *) dev->priv;
+ int i;
+
+ /* Check all addresses. */
+ for (i = 0; i < lp->spy_number; i++)
+ /* If match */
+ if (!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE)) {
+ /* Update statistics */
+ lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL;
+ lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL;
+ lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL;
+ lp->spy_stat[i].updated = 0x7;
+ }
+}
+#endif /* WIRELESS_SPY */
+
+#ifdef HISTOGRAM
+/*------------------------------------------------------------------*/
+/*
+ * This function calculates a histogram of the signal level.
+ * As the noise is quite constant, it's like doing it on the SNR.
+ * We have defined a set of interval (lp->his_range), and each time
+ * the level goes in that interval, we increment the count (lp->his_sum).
+ * With this histogram you may detect if one WaveLAN is really weak,
+ * or you may also calculate the mean and standard deviation of the level.
+ */
+static inline void wl_his_gather(device * dev, u8 * stats)
+{ /* Statistics to gather */
+ net_local *lp = (net_local *) dev->priv;
+ u8 level = stats[0] & MMR_SIGNAL_LVL;
+ int i;
+
+ /* Find the correct interval. */
+ i = 0;
+ while ((i < (lp->his_number - 1))
+ && (level >= lp->his_range[i++]));
+
+ /* Increment interval counter. */
+ (lp->his_sum[i])++;
+}
+#endif /* HISTOGRAM */
+
+/*------------------------------------------------------------------*/
+/*
+ * Perform ioctl for configuration and information.
+ * It is here that the wireless extensions are treated (iwconfig).
+ */
+static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is applied */
+ struct ifreq *rq, /* data passed */
+ int cmd)
+{ /* ioctl number */
+ unsigned long ioaddr = dev->base_addr;
+ net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ struct iwreq *wrq = (struct iwreq *) rq;
+ psa_t psa;
+ mm_t m;
+ unsigned long flags;
+ int ret = 0;
+ int err = 0;
+
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name,
+ cmd);
+#endif
+
+ /* Disable interrupts and save flags. */
+ wv_splhi(lp, &flags);
+
+ /* Look what is the request */
+ switch (cmd) {
+ /* --------------- WIRELESS EXTENSIONS --------------- */
+
+ case SIOCGIWNAME:
+ strcpy(wrq->u.name, "WaveLAN");
+ break;
+
+ case SIOCSIWNWID:
+ /* Set NWID in WaveLAN. */
+ if (!wrq->u.nwid.disabled) {
+ /* Set NWID in psa */
+ psa.psa_nwid[0] =
+ (wrq->u.nwid.value & 0xFF00) >> 8;
+ psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF;
+ psa.psa_nwid_select = 0x01;
+ psa_write(ioaddr, lp->hacr,
+ (char *) psa.psa_nwid - (char *) &psa,
+ (unsigned char *) psa.psa_nwid, 3);
+
+ /* Set NWID in mmc. */
+ m.w.mmw_netw_id_l = psa.psa_nwid[1];
+ m.w.mmw_netw_id_h = psa.psa_nwid[0];
+ mmc_write(ioaddr,
+ (char *) &m.w.mmw_netw_id_l -
+ (char *) &m,
+ (unsigned char *) &m.w.mmw_netw_id_l, 2);
+ mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00);
+ } else {
+ /* Disable NWID in the psa. */
+ psa.psa_nwid_select = 0x00;
+ psa_write(ioaddr, lp->hacr,
+ (char *) &psa.psa_nwid_select -
+ (char *) &psa,
+ (unsigned char *) &psa.psa_nwid_select,
+ 1);
+
+ /* Disable NWID in the mmc (no filtering). */
+ mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel),
+ MMW_LOOPT_SEL_DIS_NWID);
+ }
+ /* update the Wavelan checksum */
+ update_psa_checksum(dev, ioaddr, lp->hacr);
+ break;
+
+ case SIOCGIWNWID:
+ /* Read the NWID. */
+ psa_read(ioaddr, lp->hacr,
+ (char *) psa.psa_nwid - (char *) &psa,
+ (unsigned char *) psa.psa_nwid, 3);
+ wrq->u.nwid.value =
+ (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+ wrq->u.nwid.disabled = !(psa.psa_nwid_select);
+ wrq->u.nwid.fixed = 1; /* Superfluous */
+ break;
+
+ case SIOCSIWFREQ:
+ /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */
+ if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+ (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+ ret = wv_set_frequency(ioaddr, &(wrq->u.freq));
+ else
+ ret = -EOPNOTSUPP;
+ break;
+
+ case SIOCGIWFREQ:
+ /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable).
+ * Does it work for everybody, especially old cards? */
+ if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+ (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
+ unsigned short freq;
+
+ /* Ask the EEPROM to read the frequency from the first area. */
+ fee_read(ioaddr, 0x00, &freq, 1);
+ wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000;
+ wrq->u.freq.e = 1;
+ } else {
+ psa_read(ioaddr, lp->hacr,
+ (char *) &psa.psa_subband - (char *) &psa,
+ (unsigned char *) &psa.psa_subband, 1);
+
+ if (psa.psa_subband <= 4) {
+ wrq->u.freq.m =
+ fixed_bands[psa.psa_subband];
+ wrq->u.freq.e = (psa.psa_subband != 0);
+ } else
+ ret = -EOPNOTSUPP;
+ }
+ break;
+
+ case SIOCSIWSENS:
+ /* Set the level threshold. */
+ /* We should complain loudly if wrq->u.sens.fixed = 0, because we
+ * can't set auto mode... */
+ psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F;
+ psa_write(ioaddr, lp->hacr,
+ (char *) &psa.psa_thr_pre_set - (char *) &psa,
+ (unsigned char *) &psa.psa_thr_pre_set, 1);
+ /* update the Wavelan checksum */
+ update_psa_checksum(dev, ioaddr, lp->hacr);
+ mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set),
+ psa.psa_thr_pre_set);
+ break;
+
+ case SIOCGIWSENS:
+ /* Read the level threshold. */
+ psa_read(ioaddr, lp->hacr,
+ (char *) &psa.psa_thr_pre_set - (char *) &psa,
+ (unsigned char *) &psa.psa_thr_pre_set, 1);
+ wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F;
+ wrq->u.sens.fixed = 1;
+ break;
+
+ case SIOCSIWENCODE:
+ /* Set encryption key */
+ if (!mmc_encr(ioaddr)) {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ /* Basic checking... */
+ if (wrq->u.encoding.pointer != (caddr_t) 0) {
+ /* Check the size of the key */
+ if (wrq->u.encoding.length != 8) {
+ ret = -EINVAL;
+ break;
+ }
+
+ /* Copy the key in the driver */
+ wv_splx(lp, &flags);
+ err = copy_from_user(psa.psa_encryption_key,
+ wrq->u.encoding.pointer,
+ wrq->u.encoding.length);
+ wv_splhi(lp, &flags);
+ if (err) {
+ ret = -EFAULT;
+ break;
+ }
+
+ psa.psa_encryption_select = 1;
+ psa_write(ioaddr, lp->hacr,
+ (char *) &psa.psa_encryption_select -
+ (char *) &psa,
+ (unsigned char *) &psa.
+ psa_encryption_select, 8 + 1);
+
+ mmc_out(ioaddr, mmwoff(0, mmw_encr_enable),
+ MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
+ mmc_write(ioaddr, mmwoff(0, mmw_encr_key),
+ (unsigned char *) &psa.
+ psa_encryption_key, 8);
+ }
+
+ if (wrq->u.encoding.flags & IW_ENCODE_DISABLED) { /* disable encryption */
+ psa.psa_encryption_select = 0;
+ psa_write(ioaddr, lp->hacr,
+ (char *) &psa.psa_encryption_select -
+ (char *) &psa,
+ (unsigned char *) &psa.
+ psa_encryption_select, 1);
+
+ mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
+ }
+ /* update the Wavelan checksum */
+ update_psa_checksum(dev, ioaddr, lp->hacr);
+ break;
+
+ case SIOCGIWENCODE:
+ /* Read the encryption key */
+ if (!mmc_encr(ioaddr)) {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ /* only super-user can see encryption key */
+ if (!capable(CAP_NET_ADMIN)) {
+ ret = -EPERM;
+ break;
+ }
+
+ /* Basic checking... */
+ if (wrq->u.encoding.pointer != (caddr_t) 0) {
+ /* Verify the user buffer */
+ ret =
+ verify_area(VERIFY_WRITE,
+ wrq->u.encoding.pointer, 8);
+ if (ret)
+ break;
+
+ psa_read(ioaddr, lp->hacr,
+ (char *) &psa.psa_encryption_select -
+ (char *) &psa,
+ (unsigned char *) &psa.
+ psa_encryption_select, 1 + 8);
+
+ /* encryption is enabled ? */
+ if (psa.psa_encryption_select)
+ wrq->u.encoding.flags = IW_ENCODE_ENABLED;
+ else
+ wrq->u.encoding.flags = IW_ENCODE_DISABLED;
+ wrq->u.encoding.flags |= mmc_encr(ioaddr);
+
+ /* Copy the key to the user buffer */
+ wrq->u.encoding.length = 8;
+ wv_splx(lp, &flags);
+ if (copy_to_user(wrq->u.encoding.pointer,
+ psa.psa_encryption_key, 8))
+ ret = -EFAULT;
+ wv_splhi(lp, &flags);
+ }
+ break;
+
+ case SIOCGIWRANGE:
+ /* basic checking */
+ if (wrq->u.data.pointer != (caddr_t) 0) {
+ struct iw_range range;
+
+ /* Set the length (very important for backward
+ * compatibility) */
+ wrq->u.data.length = sizeof(struct iw_range);
+
+ /* Set all the info we don't care or don't know
+ * about to zero */
+ memset(&range, 0, sizeof(range));
+
+ /* Set the Wireless Extension versions */
+ range.we_version_compiled = WIRELESS_EXT;
+ range.we_version_source = 9;
+
+ /* Set information in the range struct. */
+ range.throughput = 1.6 * 1000 * 1000; /* don't argue on this ! */
+ range.min_nwid = 0x0000;
+ range.max_nwid = 0xFFFF;
+
+ /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */
+ if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+ (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
+ range.num_channels = 10;
+ range.num_frequency =
+ wv_frequency_list(ioaddr, range.freq,
+ IW_MAX_FREQUENCIES);
+ } else
+ range.num_channels = range.num_frequency =
+ 0;
+
+ range.sensitivity = 0x3F;
+ range.max_qual.qual = MMR_SGNL_QUAL;
+ range.max_qual.level = MMR_SIGNAL_LVL;
+ range.max_qual.noise = MMR_SILENCE_LVL;
+ range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */
+ /* Need to get better values for those two */
+ range.avg_qual.level = 30;
+ range.avg_qual.noise = 8;
+
+ range.num_bitrates = 1;
+ range.bitrate[0] = 2000000; /* 2 Mb/s */
+
+ /* Encryption supported ? */
+ if (mmc_encr(ioaddr)) {
+ range.encoding_size[0] = 8; /* DES = 64 bits key */
+ range.num_encoding_sizes = 1;
+ range.max_encoding_tokens = 1; /* Only one key possible */
+ } else {
+ range.num_encoding_sizes = 0;
+ range.max_encoding_tokens = 0;
+ }
+
+ /* Copy structure to the user buffer. */
+ wv_splx(lp, &flags);
+ if (copy_to_user(wrq->u.data.pointer,
+ &range,
+ sizeof(struct iw_range)))
+ ret = -EFAULT;
+ wv_splhi(lp, &flags);
+ }
+ break;
+
+ case SIOCGIWPRIV:
+ /* Basic checking */
+ if (wrq->u.data.pointer != (caddr_t) 0) {
+ struct iw_priv_args priv[] = {
+ /* { cmd,
+ set_args,
+ get_args,
+ name } */
+ { SIOCSIPQTHR,
+ IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+ 0,
+ "setqualthr" },
+ { SIOCGIPQTHR,
+ 0,
+ IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+ "getqualthr" },
+ { SIOCSIPHISTO,
+ IW_PRIV_TYPE_BYTE | 16,
+ 0,
+ "sethisto" },
+ { SIOCGIPHISTO,
+ 0,
+ IW_PRIV_TYPE_INT | 16,
+ "gethisto" },
+ };
+
+ /* Set the number of available ioctls. */
+ wrq->u.data.length = 4;
+
+ /* Copy structure to the user buffer. */
+ wv_splx(lp, &flags);
+ if (copy_to_user(wrq->u.data.pointer,
+ (u8 *) priv,
+ sizeof(priv)))
+ ret = -EFAULT;
+ wv_splhi(lp, &flags);
+ }
+ break;
+
+#ifdef WIRELESS_SPY
+ case SIOCSIWSPY:
+ /* Set the spy list */
+
+ /* Check the number of addresses. */
+ if (wrq->u.data.length > IW_MAX_SPY) {
+ ret = -E2BIG;
+ break;
+ }
+ lp->spy_number = wrq->u.data.length;
+
+ /* Are there are addresses to copy? */
+ if (lp->spy_number > 0) {
+ struct sockaddr address[IW_MAX_SPY];
+ int i;
+
+ /* Copy addresses to the driver. */
+ wv_splx(lp, &flags);
+ err = copy_from_user(address,
+ wrq->u.data.pointer,
+ sizeof(struct sockaddr)
+ * lp->spy_number);
+ wv_splhi(lp, &flags);
+ if (err) {
+ ret = -EFAULT;
+ break;
+ }
+
+ /* Copy addresses to the lp structure. */
+ for (i = 0; i < lp->spy_number; i++) {
+ memcpy(lp->spy_address[i],
+ address[i].sa_data,
+ WAVELAN_ADDR_SIZE);
+ }
+
+ /* Reset structure. */
+ memset(lp->spy_stat, 0x00,
+ sizeof(iw_qual) * IW_MAX_SPY);
+
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG
+ "SetSpy: set of new addresses is: \n");
+ for (i = 0; i < wrq->u.data.length; i++)
+ printk(KERN_DEBUG
+ "%02X:%02X:%02X:%02X:%02X:%02X \n",
+ lp->spy_address[i][0],
+ lp->spy_address[i][1],
+ lp->spy_address[i][2],
+ lp->spy_address[i][3],
+ lp->spy_address[i][4],
+ lp->spy_address[i][5]);
+#endif /* DEBUG_IOCTL_INFO */
+ }
+
+ break;
+
+ case SIOCGIWSPY:
+ /* Get the spy list and spy stats. */
+
+ /* Set the number of addresses */
+ wrq->u.data.length = lp->spy_number;
+
+ /* Does the user want to have the addresses back? */
+ if ((lp->spy_number > 0)
+ && (wrq->u.data.pointer != (caddr_t) 0)) {
+ struct sockaddr address[IW_MAX_SPY];
+ int i;
+
+ /* Copy addresses from the lp structure. */
+ for (i = 0; i < lp->spy_number; i++) {
+ memcpy(address[i].sa_data,
+ lp->spy_address[i],
+ WAVELAN_ADDR_SIZE);
+ address[i].sa_family = AF_UNIX;
+ }
+
+ /* Copy addresses to the user buffer. */
+ wv_splx(lp, &flags);
+ err = copy_to_user(wrq->u.data.pointer,
+ address,
+ sizeof(struct sockaddr)
+ * lp->spy_number);
+
+ /* Copy stats to the user buffer (just after). */
+ err |= copy_to_user(wrq->u.data.pointer
+ + (sizeof(struct sockaddr)
+ * lp->spy_number),
+ lp->spy_stat,
+ sizeof(iw_qual) * lp->spy_number);
+ wv_splhi(lp, &flags);
+ if (err) {
+ ret = -EFAULT;
+ break;
+ }
+
+ /* Reset updated flags. */
+ for (i = 0; i < lp->spy_number; i++)
+ lp->spy_stat[i].updated = 0x0;
+ }
+ /* if(pointer != NULL) */
+ break;
+#endif /* WIRELESS_SPY */
+
+ /* ------------------ PRIVATE IOCTL ------------------ */
+
+ case SIOCSIPQTHR:
+ if (!capable(CAP_NET_ADMIN)) {
+ ret = -EPERM;
+ break;
+ }
+ psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
+ psa_write(ioaddr, lp->hacr,
+ (char *) &psa.psa_quality_thr - (char *) &psa,
+ (unsigned char *) &psa.psa_quality_thr, 1);
+ /* update the Wavelan checksum */
+ update_psa_checksum(dev, ioaddr, lp->hacr);
+ mmc_out(ioaddr, mmwoff(0, mmw_quality_thr),
+ psa.psa_quality_thr);
+ break;
+
+ case SIOCGIPQTHR:
+ psa_read(ioaddr, lp->hacr,
+ (char *) &psa.psa_quality_thr - (char *) &psa,
+ (unsigned char *) &psa.psa_quality_thr, 1);
+ *(wrq->u.name) = psa.psa_quality_thr & 0x0F;
+ break;
+
+#ifdef HISTOGRAM
+ case SIOCSIPHISTO:
+ /* Verify that the user is root. */
+ if (!capable(CAP_NET_ADMIN)) {
+ ret = -EPERM;
+ break;
+ }
+
+ /* Check the number of intervals. */
+ if (wrq->u.data.length > 16) {
+ ret = -E2BIG;
+ break;
+ }
+ lp->his_number = wrq->u.data.length;
+
+ /* Are there addresses to copy? */
+ if (lp->his_number > 0) {
+ /* Copy interval ranges to the driver */
+ wv_splx(lp, &flags);
+ err = copy_from_user(lp->his_range,
+ wrq->u.data.pointer,
+ sizeof(char) * lp->his_number);
+ wv_splhi(lp, &flags);
+ if (err) {
+ ret = -EFAULT;
+ break;
+ }
+
+ /* Reset structure. */
+ memset(lp->his_sum, 0x00, sizeof(long) * 16);
+ }
+ break;
+
+ case SIOCGIPHISTO:
+ /* Set the number of intervals. */
+ wrq->u.data.length = lp->his_number;
+
+ /* Give back the distribution statistics */
+ if ((lp->his_number > 0)
+ && (wrq->u.data.pointer != (caddr_t) 0)) {
+ /* Copy data to the user buffer. */
+ wv_splx(lp, &flags);
+ if (copy_to_user(wrq->u.data.pointer,
+ lp->his_sum,
+ sizeof(long) * lp->his_number);
+ ret = -EFAULT;
+ wv_splhi(lp, &flags);
+
+ } /* if(pointer != NULL) */
+ break;
+#endif /* HISTOGRAM */
+
+ /* ------------------- OTHER IOCTL ------------------- */
+
+ default:
+ ret = -EOPNOTSUPP;
+ } /* switch (cmd) */
+
+ /* Enable interrupts and restore flags. */
+ wv_splx(lp, &flags);
+
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
+#endif
+ return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Get wireless statistics.
+ * Called by /proc/net/wireless
+ */
+static iw_stats *wavelan_get_wireless_stats(device * dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ net_local *lp = (net_local *) dev->priv;
+ mmr_t m;
+ iw_stats *wstats;
+ unsigned long flags;
+
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n",
+ dev->name);
+#endif
+
+ /* Check */
+ if (lp == (net_local *) NULL)
+ return (iw_stats *) NULL;
+
+ /* Disable interrupts and save flags. */
+ wv_splhi(lp, &flags);
+
+ wstats = &lp->wstats;
+
+ /* Get data from the mmc. */
+ mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+
+ mmc_read(ioaddr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1);
+ mmc_read(ioaddr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l,
+ 2);
+ mmc_read(ioaddr, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set,
+ 4);
+
+ mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+ /* Copy data to wireless stuff. */
+ wstats->status = m.mmr_dce_status & MMR_DCE_STATUS;
+ wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
+ wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
+ wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
+ wstats->qual.updated = (((m. mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7)
+ | ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6)
+ | ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5));
+ wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+ wstats->discard.code = 0L;
+ wstats->discard.misc = 0L;
+
+ /* Enable interrupts and restore flags. */
+ wv_splx(lp, &flags);
+
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n",
+ dev->name);
+#endif
+ return &lp->wstats;
+}
+#endif /* WIRELESS_EXT */
+
+/************************* PACKET RECEPTION *************************/
+/*
+ * This part deals with receiving the packets.
+ * The interrupt handler gets an interrupt when a packet has been
+ * successfully received and calls this part.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does the actual copying of data (including the Ethernet
+ * header structure) from the WaveLAN card to an sk_buff chain that
+ * will be passed up to the network interface layer. NOTE: we
+ * currently don't handle trailer protocols (neither does the rest of
+ * the network interface), so if that is needed, it will (at least in
+ * part) be added here. The contents of the receive ring buffer are
+ * copied to a message chain that is then passed to the kernel.
+ *
+ * Note: if any errors occur, the packet is "dropped on the floor".
+ * (called by wv_packet_rcv())
+ */
+static inline void
+wv_packet_read(device * dev, u16 buf_off, int sksize)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ struct sk_buff *skb;
+
+#ifdef DEBUG_RX_TRACE
+ printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n",
+ dev->name, buf_off, sksize);
+#endif
+
+ /* Allocate buffer for the data */
+ if ((skb = dev_alloc_skb(sksize)) == (struct sk_buff *) NULL) {
+#ifdef DEBUG_RX_ERROR
+ printk(KERN_INFO
+ "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC).\n",
+ dev->name, sksize);
+#endif
+ lp->stats.rx_dropped++;
+ return;
+ }
+
+ skb->dev = dev;
+
+ /* Copy the packet to the buffer. */
+ obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize);
+ skb->protocol = eth_type_trans(skb, dev);
+
+#ifdef DEBUG_RX_INFO
+ wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
+#endif /* DEBUG_RX_INFO */
+
+ /* Statistics-gathering and associated stuff.
+ * It seem a bit messy with all the define, but it's really simple... */
+#if defined(WIRELESS_SPY) || defined(HISTOGRAM)
+ if (
+#ifdef WIRELESS_SPY
+ (lp->spy_number > 0) ||
+#endif /* WIRELESS_SPY */
+#ifdef HISTOGRAM
+ (lp->his_number > 0) ||
+#endif /* HISTOGRAM */
+ 0) {
+ u8 stats[3]; /* signal level, noise level, signal quality */
+
+ /* Read signal level, silence level and signal quality bytes. */
+ /* Note: in the PCMCIA hardware, these are part of the frame. It seems
+ * that for the ISA hardware, it's nowhere to be found in the frame,
+ * so I'm obliged to do this (it has a side effect on /proc/net/wireless).
+ * Any ideas?
+ */
+ mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+ mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3);
+ mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+#ifdef DEBUG_RX_INFO
+ printk(KERN_DEBUG
+ "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n",
+ dev->name, stats[0] & 0x3F, stats[1] & 0x3F,
+ stats[2] & 0x0F);
+#endif
+
+ /* Spying stuff */
+#ifdef WIRELESS_SPY
+ wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE,
+ stats);
+#endif /* WIRELESS_SPY */
+#ifdef HISTOGRAM
+ wl_his_gather(dev, stats);
+#endif /* HISTOGRAM */
+ }
+#endif /* defined(WIRELESS_SPY) || defined(HISTOGRAM) */
+
+ /*
+ * Hand the packet to the network module.
+ */
+ netif_rx(skb);
+
+ /* Keep statistics up to date */
+ dev->last_rx = jiffies;
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += sksize;
+
+#ifdef DEBUG_RX_TRACE
+ printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Transfer as many packets as we can
+ * from the device RAM.
+ * (called in wavelan_interrupt()).
+ * Note : the spinlock is already grabbed for us.
+ */
+static inline void wv_receive(device * dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ net_local *lp = (net_local *) dev->priv;
+ fd_t fd;
+ rbd_t rbd;
+ int nreaped = 0;
+
+#ifdef DEBUG_RX_TRACE
+ printk(KERN_DEBUG "%s: ->wv_receive()\n", dev->name);
+#endif
+
+ /* Loop on each received packet. */
+ for (;;) {
+ obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd,
+ sizeof(fd));
+
+ /* Note about the status :
+ * It start up to be 0 (the value we set). Then, when the RU
+ * grab the buffer to prepare for reception, it sets the
+ * FD_STATUS_B flag. When the RU has finished receiving the
+ * frame, it clears FD_STATUS_B, set FD_STATUS_C to indicate
+ * completion and set the other flags to indicate the eventual
+ * errors. FD_STATUS_OK indicates that the reception was OK.
+ */
+
+ /* If the current frame is not complete, we have reached the end. */
+ if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C)
+ break; /* This is how we exit the loop. */
+
+ nreaped++;
+
+ /* Check whether frame was correctly received. */
+ if ((fd.fd_status & FD_STATUS_OK) == FD_STATUS_OK) {
+ /* Does the frame contain a pointer to the data? Let's check. */
+ if (fd.fd_rbd_offset != I82586NULL) {
+ /* Read the receive buffer descriptor */
+ obram_read(ioaddr, fd.fd_rbd_offset,
+ (unsigned char *) &rbd,
+ sizeof(rbd));
+
+#ifdef DEBUG_RX_ERROR
+ if ((rbd.rbd_status & RBD_STATUS_EOF) !=
+ RBD_STATUS_EOF) printk(KERN_INFO
+ "%s: wv_receive(): missing EOF flag.\n",
+ dev->name);
+
+ if ((rbd.rbd_status & RBD_STATUS_F) !=
+ RBD_STATUS_F) printk(KERN_INFO
+ "%s: wv_receive(): missing F flag.\n",
+ dev->name);
+#endif /* DEBUG_RX_ERROR */
+
+ /* Read the packet and transmit to Linux */
+ wv_packet_read(dev, rbd.rbd_bufl,
+ rbd.
+ rbd_status &
+ RBD_STATUS_ACNT);
+ }
+#ifdef DEBUG_RX_ERROR
+ else /* if frame has no data */
+ printk(KERN_INFO
+ "%s: wv_receive(): frame has no data.\n",
+ dev->name);
+#endif
+ } else { /* If reception was no successful */
+
+ lp->stats.rx_errors++;
+
+#ifdef DEBUG_RX_INFO
+ printk(KERN_DEBUG
+ "%s: wv_receive(): frame not received successfully (%X).\n",
+ dev->name, fd.fd_status);
+#endif
+
+#ifdef DEBUG_RX_ERROR
+ if ((fd.fd_status & FD_STATUS_S6) != 0)
+ printk(KERN_INFO
+ "%s: wv_receive(): no EOF flag.\n",
+ dev->name);
+#endif
+
+ if ((fd.fd_status & FD_STATUS_S7) != 0) {
+ lp->stats.rx_length_errors++;
+#ifdef DEBUG_RX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_receive(): frame too short.\n",
+ dev->name);
+#endif
+ }
+
+ if ((fd.fd_status & FD_STATUS_S8) != 0) {
+ lp->stats.rx_over_errors++;
+#ifdef DEBUG_RX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_receive(): rx DMA overrun.\n",
+ dev->name);
+#endif
+ }
+
+ if ((fd.fd_status & FD_STATUS_S9) != 0) {
+ lp->stats.rx_fifo_errors++;
+#ifdef DEBUG_RX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_receive(): ran out of resources.\n",
+ dev->name);
+#endif
+ }
+
+ if ((fd.fd_status & FD_STATUS_S10) != 0) {
+ lp->stats.rx_frame_errors++;
+#ifdef DEBUG_RX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_receive(): alignment error.\n",
+ dev->name);
+#endif
+ }
+
+ if ((fd.fd_status & FD_STATUS_S11) != 0) {
+ lp->stats.rx_crc_errors++;
+#ifdef DEBUG_RX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_receive(): CRC error.\n",
+ dev->name);
+#endif
+ }
+ }
+
+ fd.fd_status = 0;
+ obram_write(ioaddr, fdoff(lp->rx_head, fd_status),
+ (unsigned char *) &fd.fd_status,
+ sizeof(fd.fd_status));
+
+ fd.fd_command = FD_COMMAND_EL;
+ obram_write(ioaddr, fdoff(lp->rx_head, fd_command),
+ (unsigned char *) &fd.fd_command,
+ sizeof(fd.fd_command));
+
+ fd.fd_command = 0;
+ obram_write(ioaddr, fdoff(lp->rx_last, fd_command),
+ (unsigned char *) &fd.fd_command,
+ sizeof(fd.fd_command));
+
+ lp->rx_last = lp->rx_head;
+ lp->rx_head = fd.fd_link_offset;
+ } /* for(;;) -> loop on all frames */
+
+#ifdef DEBUG_RX_INFO
+ if (nreaped > 1)
+ printk(KERN_DEBUG "%s: wv_receive(): reaped %d\n",
+ dev->name, nreaped);
+#endif
+#ifdef DEBUG_RX_TRACE
+ printk(KERN_DEBUG "%s: <-wv_receive()\n", dev->name);
+#endif
+}
+
+/*********************** PACKET TRANSMISSION ***********************/
+/*
+ * This part deals with sending packets through the WaveLAN.
+ *
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine fills in the appropriate registers and memory
+ * locations on the WaveLAN card and starts the card off on
+ * the transmit.
+ *
+ * The principle:
+ * Each block contains a transmit command, a NOP command,
+ * a transmit block descriptor and a buffer.
+ * The CU read the transmit block which point to the tbd,
+ * read the tbd and the content of the buffer.
+ * When it has finish with it, it goes to the next command
+ * which in our case is the NOP. The NOP points on itself,
+ * so the CU stop here.
+ * When we add the next block, we modify the previous nop
+ * to make it point on the new tx command.
+ * Simple, isn't it ?
+ *
+ * (called in wavelan_packet_xmit())
+ */
+static inline int wv_packet_write(device * dev, void *buf, short length)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ unsigned short txblock;
+ unsigned short txpred;
+ unsigned short tx_addr;
+ unsigned short nop_addr;
+ unsigned short tbd_addr;
+ unsigned short buf_addr;
+ ac_tx_t tx;
+ ac_nop_t nop;
+ tbd_t tbd;
+ int clen = length;
+ unsigned long flags;
+
+#ifdef DEBUG_TX_TRACE
+ printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name,
+ length);
+#endif
+
+ /* Do we need some padding? */
+ if (clen < ETH_ZLEN)
+ clen = ETH_ZLEN;
+
+ wv_splhi(lp, &flags);
+
+ /* Check nothing bad has happened */
+ if (lp->tx_n_in_use == (NTXBLOCKS - 1)) {
+#ifdef DEBUG_TX_ERROR
+ printk(KERN_INFO "%s: wv_packet_write(): Tx queue full.\n",
+ dev->name);
+#endif
+ wv_splx(lp, &flags);
+ return 1;
+ }
+
+ /* Calculate addresses of next block and previous block. */
+ txblock = lp->tx_first_free;
+ txpred = txblock - TXBLOCKZ;
+ if (txpred < OFFSET_CU)
+ txpred += NTXBLOCKS * TXBLOCKZ;
+ lp->tx_first_free += TXBLOCKZ;
+ if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
+
+ lp->tx_n_in_use++;
+
+ /* Calculate addresses of the different parts of the block. */
+ tx_addr = txblock;
+ nop_addr = tx_addr + sizeof(tx);
+ tbd_addr = nop_addr + sizeof(nop);
+ buf_addr = tbd_addr + sizeof(tbd);
+
+ /*
+ * Transmit command
+ */
+ tx.tx_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
+ (unsigned char *) &tx.tx_h.ac_status,
+ sizeof(tx.tx_h.ac_status));
+
+ /*
+ * NOP command
+ */
+ nop.nop_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+ (unsigned char *) &nop.nop_h.ac_status,
+ sizeof(nop.nop_h.ac_status));
+ nop.nop_h.ac_link = nop_addr;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+ (unsigned char *) &nop.nop_h.ac_link,
+ sizeof(nop.nop_h.ac_link));
+
+ /*
+ * Transmit buffer descriptor
+ */
+ tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen);
+ tbd.tbd_next_bd_offset = I82586NULL;
+ tbd.tbd_bufl = buf_addr;
+ tbd.tbd_bufh = 0;
+ obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, sizeof(tbd));
+
+ /*
+ * Data
+ */
+ obram_write(ioaddr, buf_addr, buf, length);
+
+ /*
+ * Overwrite the predecessor NOP link
+ * so that it points to this txblock.
+ */
+ nop_addr = txpred + sizeof(tx);
+ nop.nop_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+ (unsigned char *) &nop.nop_h.ac_status,
+ sizeof(nop.nop_h.ac_status));
+ nop.nop_h.ac_link = txblock;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+ (unsigned char *) &nop.nop_h.ac_link,
+ sizeof(nop.nop_h.ac_link));
+
+ /* Keep stats up to date. */
+ lp->stats.tx_bytes += length;
+
+ if (lp->tx_first_in_use == I82586NULL)
+ lp->tx_first_in_use = txblock;
+
+ if (lp->tx_n_in_use < NTXBLOCKS - 1)
+ netif_wake_queue(dev);
+
+ wv_splx(lp, &flags);
+
+#ifdef DEBUG_TX_INFO
+ wv_packet_info((u8 *) buf, length, dev->name,
+ "wv_packet_write");
+#endif /* DEBUG_TX_INFO */
+
+#ifdef DEBUG_TX_TRACE
+ printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
+#endif
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine is called when we want to send a packet (NET3 callback)
+ * In this routine, we check if the harware is ready to accept
+ * the packet. We also prevent reentrance. Then we call the function
+ * to send the packet.
+ */
+static int wavelan_packet_xmit(struct sk_buff *skb, device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long flags;
+
+#ifdef DEBUG_TX_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
+ (unsigned) skb);
+#endif
+
+ /*
+ * Block a timer-based transmit from overlapping.
+ * In other words, prevent reentering this routine.
+ */
+ netif_stop_queue(dev);
+
+ /* If somebody has asked to reconfigure the controller,
+ * we can do it now.
+ */
+ if (lp->reconfig_82586) {
+ wv_splhi(lp, &flags);
+ wv_82586_config(dev);
+ wv_splx(lp, &flags);
+ /* Check that we can continue */
+ if (lp->tx_n_in_use == (NTXBLOCKS - 1))
+ return 1;
+ }
+#ifdef DEBUG_TX_ERROR
+ if (skb->next)
+ printk(KERN_INFO "skb has next\n");
+#endif
+
+ /* Write packet on the card */
+ if(wv_packet_write(dev, skb->data, skb->len))
+ return 1; /* We failed */
+
+ dev_kfree_skb(skb);
+
+#ifdef DEBUG_TX_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
+#endif
+ return 0;
+}
+
+/*********************** HARDWARE CONFIGURATION ***********************/
+/*
+ * This part does the real job of starting and configuring the hardware.
+ */
+
+/*--------------------------------------------------------------------*/
+/*
+ * Routine to initialize the Modem Management Controller.
+ * (called by wv_hw_reset())
+ */
+static inline int wv_mmc_init(device * dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ net_local *lp = (net_local *) dev->priv;
+ psa_t psa;
+ mmw_t m;
+ int configured;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name);
+#endif
+
+ /* Read the parameter storage area. */
+ psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+#ifdef USE_PSA_CONFIG
+ configured = psa.psa_conf_status & 1;
+#else
+ configured = 0;
+#endif
+
+ /* Is the PSA is not configured */
+ if (!configured) {
+ /* User will be able to configure NWID later (with iwconfig). */
+ psa.psa_nwid[0] = 0;
+ psa.psa_nwid[1] = 0;
+
+ /* no NWID checking since NWID is not set */
+ psa.psa_nwid_select = 0;
+
+ /* Disable encryption */
+ psa.psa_encryption_select = 0;
+
+ /* Set to standard values:
+ * 0x04 for AT,
+ * 0x01 for MCA,
+ * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document)
+ */
+ if (psa.psa_comp_number & 1)
+ psa.psa_thr_pre_set = 0x01;
+ else
+ psa.psa_thr_pre_set = 0x04;
+ psa.psa_quality_thr = 0x03;
+
+ /* It is configured */
+ psa.psa_conf_status |= 1;
+
+#ifdef USE_PSA_CONFIG
+ /* Write the psa. */
+ psa_write(ioaddr, lp->hacr,
+ (char *) psa.psa_nwid - (char *) &psa,
+ (unsigned char *) psa.psa_nwid, 4);
+ psa_write(ioaddr, lp->hacr,
+ (char *) &psa.psa_thr_pre_set - (char *) &psa,
+ (unsigned char *) &psa.psa_thr_pre_set, 1);
+ psa_write(ioaddr, lp->hacr,
+ (char *) &psa.psa_quality_thr - (char *) &psa,
+ (unsigned char *) &psa.psa_quality_thr, 1);
+ psa_write(ioaddr, lp->hacr,
+ (char *) &psa.psa_conf_status - (char *) &psa,
+ (unsigned char *) &psa.psa_conf_status, 1);
+ /* update the Wavelan checksum */
+ update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
+ }
+
+ /* Zero the mmc structure. */
+ memset(&m, 0x00, sizeof(m));
+
+ /* Copy PSA info to the mmc. */
+ m.mmw_netw_id_l = psa.psa_nwid[1];
+ m.mmw_netw_id_h = psa.psa_nwid[0];
+
+ if (psa.psa_nwid_select & 1)
+ m.mmw_loopt_sel = 0x00;
+ else
+ m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
+
+ memcpy(&m.mmw_encr_key, &psa.psa_encryption_key,
+ sizeof(m.mmw_encr_key));
+
+ if (psa.psa_encryption_select)
+ m.mmw_encr_enable =
+ MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE;
+ else
+ m.mmw_encr_enable = 0;
+
+ m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
+ m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
+
+ /*
+ * Set default modem control parameters.
+ * See NCR document 407-0024326 Rev. A.
+ */
+ m.mmw_jabber_enable = 0x01;
+ m.mmw_freeze = 0;
+ m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
+ m.mmw_ifs = 0x20;
+ m.mmw_mod_delay = 0x04;
+ m.mmw_jam_time = 0x38;
+
+ m.mmw_des_io_invert = 0;
+ m.mmw_decay_prm = 0;
+ m.mmw_decay_updat_prm = 0;
+
+ /* Write all info to MMC. */
+ mmc_write(ioaddr, 0, (u8 *) & m, sizeof(m));
+
+ /* The following code starts the modem of the 2.00 frequency
+ * selectable cards at power on. It's not strictly needed for the
+ * following boots.
+ * The original patch was by Joe Finney for the PCMCIA driver, but
+ * I've cleaned it up a bit and added documentation.
+ * Thanks to Loeke Brederveld from Lucent for the info.
+ */
+
+ /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+ * Does it work for everybody, especially old cards? */
+ /* Note: WFREQSEL verifies that it is able to read a sensible
+ * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID
+ * is 0xA (Xilinx version) or 0xB (Ariadne version).
+ * My test is more crude but does work. */
+ if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+ (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) {
+ /* We must download the frequency parameters to the
+ * synthesizers (from the EEPROM - area 1)
+ * Note: as the EEPROM is automatically decremented, we set the end
+ * if the area... */
+ m.mmw_fee_addr = 0x0F;
+ m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+ mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m,
+ (unsigned char *) &m.mmw_fee_ctrl, 2);
+
+ /* Wait until the download is finished. */
+ fee_wait(ioaddr, 100, 100);
+
+#ifdef DEBUG_CONFIG_INFO
+ /* The frequency was in the last word downloaded. */
+ mmc_read(ioaddr, (char *) &m.mmw_fee_data_l - (char *) &m,
+ (unsigned char *) &m.mmw_fee_data_l, 2);
+
+ /* Print some info for the user. */
+ printk(KERN_DEBUG
+ "%s: WaveLAN 2.00 recognised (frequency select). Current frequency = %ld\n",
+ dev->name,
+ ((m.
+ mmw_fee_data_h << 4) | (m.mmw_fee_data_l >> 4)) *
+ 5 / 2 + 24000L);
+#endif
+
+ /* We must now download the power adjust value (gain) to
+ * the synthesizers (from the EEPROM - area 7 - DAC). */
+ m.mmw_fee_addr = 0x61;
+ m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+ mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m,
+ (unsigned char *) &m.mmw_fee_ctrl, 2);
+
+ /* Wait until the download is finished. */
+ }
+ /* if 2.00 card */
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name);
+#endif
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Construct the fd and rbd structures.
+ * Start the receive unit.
+ * (called by wv_hw_reset())
+ */
+static inline int wv_ru_start(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ u16 scb_cs;
+ fd_t fd;
+ rbd_t rbd;
+ u16 rx;
+ u16 rx_next;
+ int i;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
+#endif
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
+ (unsigned char *) &scb_cs, sizeof(scb_cs));
+ if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY)
+ return 0;
+
+ lp->rx_head = OFFSET_RU;
+
+ for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next) {
+ rx_next =
+ (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ;
+
+ fd.fd_status = 0;
+ fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0;
+ fd.fd_link_offset = rx_next;
+ fd.fd_rbd_offset = rx + sizeof(fd);
+ obram_write(ioaddr, rx, (unsigned char *) &fd, sizeof(fd));
+
+ rbd.rbd_status = 0;
+ rbd.rbd_next_rbd_offset = I82586NULL;
+ rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd);
+ rbd.rbd_bufh = 0;
+ rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ);
+ obram_write(ioaddr, rx + sizeof(fd),
+ (unsigned char *) &rbd, sizeof(rbd));
+
+ lp->rx_last = rx;
+ }
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset),
+ (unsigned char *) &lp->rx_head, sizeof(lp->rx_head));
+
+ scb_cs = SCB_CMD_RUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--) {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(10);
+ }
+
+ if (i <= 0) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO
+ "%s: wavelan_ru_start(): board not accepting command.\n",
+ dev->name);
+#endif
+ return -1;
+ }
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
+#endif
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Initialise the transmit blocks.
+ * Start the command unit executing the NOP
+ * self-loop of the first transmit block.
+ *
+ * Here we create the list of send buffers used to transmit packets
+ * between the PC and the command unit. For each buffer, we create a
+ * buffer descriptor (pointing on the buffer), a transmit command
+ * (pointing to the buffer descriptor) and a NOP command.
+ * The transmit command is linked to the NOP, and the NOP to itself.
+ * When we will have finished executing the transmit command, we will
+ * then loop on the NOP. By releasing the NOP link to a new command,
+ * we may send another buffer.
+ *
+ * (called by wv_hw_reset())
+ */
+static inline int wv_cu_start(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ int i;
+ u16 txblock;
+ u16 first_nop;
+ u16 scb_cs;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_cu_start()\n", dev->name);
+#endif
+
+ lp->tx_first_free = OFFSET_CU;
+ lp->tx_first_in_use = I82586NULL;
+
+ for (i = 0, txblock = OFFSET_CU;
+ i < NTXBLOCKS; i++, txblock += TXBLOCKZ) {
+ ac_tx_t tx;
+ ac_nop_t nop;
+ tbd_t tbd;
+ unsigned short tx_addr;
+ unsigned short nop_addr;
+ unsigned short tbd_addr;
+ unsigned short buf_addr;
+
+ tx_addr = txblock;
+ nop_addr = tx_addr + sizeof(tx);
+ tbd_addr = nop_addr + sizeof(nop);
+ buf_addr = tbd_addr + sizeof(tbd);
+
+ tx.tx_h.ac_status = 0;
+ tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I;
+ tx.tx_h.ac_link = nop_addr;
+ tx.tx_tbd_offset = tbd_addr;
+ obram_write(ioaddr, tx_addr, (unsigned char *) &tx,
+ sizeof(tx));
+
+ nop.nop_h.ac_status = 0;
+ nop.nop_h.ac_command = acmd_nop;
+ nop.nop_h.ac_link = nop_addr;
+ obram_write(ioaddr, nop_addr, (unsigned char *) &nop,
+ sizeof(nop));
+
+ tbd.tbd_status = TBD_STATUS_EOF;
+ tbd.tbd_next_bd_offset = I82586NULL;
+ tbd.tbd_bufl = buf_addr;
+ tbd.tbd_bufh = 0;
+ obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd,
+ sizeof(tbd));
+ }
+
+ first_nop =
+ OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t);
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset),
+ (unsigned char *) &first_nop, sizeof(first_nop));
+
+ scb_cs = SCB_CMD_CUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--) {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(10);
+ }
+
+ if (i <= 0) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO
+ "%s: wavelan_cu_start(): board not accepting command.\n",
+ dev->name);
+#endif
+ return -1;
+ }
+
+ lp->tx_n_in_use = 0;
+ netif_start_queue(dev);
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_cu_start()\n", dev->name);
+#endif
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a standard configuration of the WaveLAN
+ * controller (i82586).
+ *
+ * It initialises the scp, iscp and scb structure
+ * The first two are just pointers to the next.
+ * The last one is used for basic configuration and for basic
+ * communication (interrupt status).
+ *
+ * (called by wv_hw_reset())
+ */
+static inline int wv_82586_start(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ scp_t scp; /* system configuration pointer */
+ iscp_t iscp; /* intermediate scp */
+ scb_t scb; /* system control block */
+ ach_t cb; /* Action command header */
+ u8 zeroes[512];
+ int i;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_82586_start()\n", dev->name);
+#endif
+
+ /*
+ * Clear the onboard RAM.
+ */
+ memset(&zeroes[0], 0x00, sizeof(zeroes));
+ for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes))
+ obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes));
+
+ /*
+ * Construct the command unit structures:
+ * scp, iscp, scb, cb.
+ */
+ memset(&scp, 0x00, sizeof(scp));
+ scp.scp_sysbus = SCP_SY_16BBUS;
+ scp.scp_iscpl = OFFSET_ISCP;
+ obram_write(ioaddr, OFFSET_SCP, (unsigned char *) &scp,
+ sizeof(scp));
+
+ memset(&iscp, 0x00, sizeof(iscp));
+ iscp.iscp_busy = 1;
+ iscp.iscp_offset = OFFSET_SCB;
+ obram_write(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp,
+ sizeof(iscp));
+
+ /* Our first command is to reset the i82586. */
+ memset(&scb, 0x00, sizeof(scb));
+ scb.scb_command = SCB_CMD_RESET;
+ scb.scb_cbl_offset = OFFSET_CU;
+ scb.scb_rfa_offset = OFFSET_RU;
+ obram_write(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
+ sizeof(scb));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ /* Wait for command to finish. */
+ for (i = 1000; i > 0; i--) {
+ obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp,
+ sizeof(iscp));
+
+ if (iscp.iscp_busy == (unsigned short) 0)
+ break;
+
+ udelay(10);
+ }
+
+ if (i <= 0) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO
+ "%s: wv_82586_start(): iscp_busy timeout.\n",
+ dev->name);
+#endif
+ return -1;
+ }
+
+ /* Check command completion. */
+ for (i = 15; i > 0; i--) {
+ obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
+ sizeof(scb));
+
+ if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA))
+ break;
+
+ udelay(10);
+ }
+
+ if (i <= 0) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO
+ "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n",
+ dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status);
+#endif
+ return -1;
+ }
+
+ wv_ack(dev);
+
+ /* Set the action command header. */
+ memset(&cb, 0x00, sizeof(cb));
+ cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
+ cb.ac_link = OFFSET_CU;
+ obram_write(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb));
+
+ if (wv_synchronous_cmd(dev, "diag()") == -1)
+ return -1;
+
+ obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb));
+ if (cb.ac_status & AC_SFLD_FAIL) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO
+ "%s: wv_82586_start(): i82586 Self Test failed.\n",
+ dev->name);
+#endif
+ return -1;
+ }
+#ifdef DEBUG_I82586_SHOW
+ wv_scb_show(ioaddr);
+#endif
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_82586_start()\n", dev->name);
+#endif
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a standard configuration of the WaveLAN
+ * controller (i82586).
+ *
+ * This routine is a violent hack. We use the first free transmit block
+ * to make our configuration. In the buffer area, we create the three
+ * configuration commands (linked). We make the previous NOP point to
+ * the beginning of the buffer instead of the tx command. After, we go
+ * as usual to the NOP command.
+ * Note that only the last command (mc_set) will generate an interrupt.
+ *
+ * (called by wv_hw_reset(), wv_82586_reconfig(), wavelan_packet_xmit())
+ */
+static void wv_82586_config(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ unsigned short txblock;
+ unsigned short txpred;
+ unsigned short tx_addr;
+ unsigned short nop_addr;
+ unsigned short tbd_addr;
+ unsigned short cfg_addr;
+ unsigned short ias_addr;
+ unsigned short mcs_addr;
+ ac_tx_t tx;
+ ac_nop_t nop;
+ ac_cfg_t cfg; /* Configure action */
+ ac_ias_t ias; /* IA-setup action */
+ ac_mcs_t mcs; /* Multicast setup */
+ struct dev_mc_list *dmi;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name);
+#endif
+
+ /* Check nothing bad has happened */
+ if (lp->tx_n_in_use == (NTXBLOCKS - 1)) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO "%s: wv_82586_config(): Tx queue full.\n",
+ dev->name);
+#endif
+ return;
+ }
+
+ /* Calculate addresses of next block and previous block. */
+ txblock = lp->tx_first_free;
+ txpred = txblock - TXBLOCKZ;
+ if (txpred < OFFSET_CU)
+ txpred += NTXBLOCKS * TXBLOCKZ;
+ lp->tx_first_free += TXBLOCKZ;
+ if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
+
+ lp->tx_n_in_use++;
+
+ /* Calculate addresses of the different parts of the block. */
+ tx_addr = txblock;
+ nop_addr = tx_addr + sizeof(tx);
+ tbd_addr = nop_addr + sizeof(nop);
+ cfg_addr = tbd_addr + sizeof(tbd_t); /* beginning of the buffer */
+ ias_addr = cfg_addr + sizeof(cfg);
+ mcs_addr = ias_addr + sizeof(ias);
+
+ /*
+ * Transmit command
+ */
+ tx.tx_h.ac_status = 0xFFFF; /* Fake completion value */
+ obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
+ (unsigned char *) &tx.tx_h.ac_status,
+ sizeof(tx.tx_h.ac_status));
+
+ /*
+ * NOP command
+ */
+ nop.nop_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+ (unsigned char *) &nop.nop_h.ac_status,
+ sizeof(nop.nop_h.ac_status));
+ nop.nop_h.ac_link = nop_addr;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+ (unsigned char *) &nop.nop_h.ac_link,
+ sizeof(nop.nop_h.ac_link));
+
+ /* Create a configure action. */
+ memset(&cfg, 0x00, sizeof(cfg));
+
+ /*
+ * For Linux we invert AC_CFG_ALOC() so as to conform
+ * to the way that net packets reach us from above.
+ * (See also ac_tx_t.)
+ *
+ * Updated from Wavelan Manual WCIN085B
+ */
+ cfg.cfg_byte_cnt =
+ AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
+ cfg.cfg_fifolim = AC_CFG_FIFOLIM(4);
+ cfg.cfg_byte8 = AC_CFG_SAV_BF(1) | AC_CFG_SRDY(0);
+ cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
+ AC_CFG_ILPBCK(0) |
+ AC_CFG_PRELEN(AC_CFG_PLEN_2) |
+ AC_CFG_ALOC(1) | AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
+ cfg.cfg_byte10 = AC_CFG_BOFMET(1) |
+ AC_CFG_ACR(6) | AC_CFG_LINPRIO(0);
+ cfg.cfg_ifs = 0x20;
+ cfg.cfg_slotl = 0x0C;
+ cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) | AC_CFG_SLTTMHI(0);
+ cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
+ AC_CFG_BTSTF(0) |
+ AC_CFG_CRC16(0) |
+ AC_CFG_NCRC(0) |
+ AC_CFG_TNCRS(1) |
+ AC_CFG_MANCH(0) |
+ AC_CFG_BCDIS(0) | AC_CFG_PRM(lp->promiscuous);
+ cfg.cfg_byte15 = AC_CFG_ICDS(0) |
+ AC_CFG_CDTF(0) | AC_CFG_ICSS(0) | AC_CFG_CSTF(0);
+/*
+ cfg.cfg_min_frm_len = AC_CFG_MNFRM(64);
+*/
+ cfg.cfg_min_frm_len = AC_CFG_MNFRM(8);
+
+ cfg.cfg_h.ac_command = (AC_CFLD_CMD & acmd_configure);
+ cfg.cfg_h.ac_link = ias_addr;
+ obram_write(ioaddr, cfg_addr, (unsigned char *) &cfg, sizeof(cfg));
+
+ /* Set up the MAC address */
+ memset(&ias, 0x00, sizeof(ias));
+ ias.ias_h.ac_command = (AC_CFLD_CMD & acmd_ia_setup);
+ ias.ias_h.ac_link = mcs_addr;
+ memcpy(&ias.ias_addr[0], (unsigned char *) &dev->dev_addr[0],
+ sizeof(ias.ias_addr));
+ obram_write(ioaddr, ias_addr, (unsigned char *) &ias, sizeof(ias));
+
+ /* Initialize adapter's Ethernet multicast addresses */
+ memset(&mcs, 0x00, sizeof(mcs));
+ mcs.mcs_h.ac_command = AC_CFLD_I | (AC_CFLD_CMD & acmd_mc_setup);
+ mcs.mcs_h.ac_link = nop_addr;
+ mcs.mcs_cnt = WAVELAN_ADDR_SIZE * lp->mc_count;
+ obram_write(ioaddr, mcs_addr, (unsigned char *) &mcs, sizeof(mcs));
+
+ /* Any address to set? */
+ if (lp->mc_count) {
+ for (dmi = dev->mc_list; dmi; dmi = dmi->next)
+ outsw(PIOP1(ioaddr), (u16 *) dmi->dmi_addr,
+ WAVELAN_ADDR_SIZE >> 1);
+
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG
+ "%s: wv_82586_config(): set %d multicast addresses:\n",
+ dev->name, lp->mc_count);
+ for (dmi = dev->mc_list; dmi; dmi = dmi->next)
+ printk(KERN_DEBUG
+ " %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dmi->dmi_addr[0], dmi->dmi_addr[1],
+ dmi->dmi_addr[2], dmi->dmi_addr[3],
+ dmi->dmi_addr[4], dmi->dmi_addr[5]);
+#endif
+ }
+
+ /*
+ * Overwrite the predecessor NOP link
+ * so that it points to the configure action.
+ */
+ nop_addr = txpred + sizeof(tx);
+ nop.nop_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+ (unsigned char *) &nop.nop_h.ac_status,
+ sizeof(nop.nop_h.ac_status));
+ nop.nop_h.ac_link = cfg_addr;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+ (unsigned char *) &nop.nop_h.ac_link,
+ sizeof(nop.nop_h.ac_link));
+
+ /* Job done, clear the flag */
+ lp->reconfig_82586 = 0;
+
+ if (lp->tx_first_in_use == I82586NULL)
+ lp->tx_first_in_use = txblock;
+
+ if (lp->tx_n_in_use == (NTXBLOCKS - 1))
+ netif_stop_queue(dev);
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_82586_config()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine, called by wavelan_close(), gracefully stops the
+ * WaveLAN controller (i82586).
+ * (called by wavelan_close())
+ */
+static inline void wv_82586_stop(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ u16 scb_cmd;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name);
+#endif
+
+ /* Suspend both command unit and receive unit. */
+ scb_cmd =
+ (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC &
+ SCB_CMD_RUC_SUS);
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &scb_cmd, sizeof(scb_cmd));
+ set_chan_attn(ioaddr, lp->hacr);
+
+ /* No more interrupts */
+ wv_ints_off(dev);
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_82586_stop()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Totally reset the WaveLAN and restart it.
+ * Performs the following actions:
+ * 1. A power reset (reset DMA)
+ * 2. Initialize the radio modem (using wv_mmc_init)
+ * 3. Reset & Configure LAN controller (using wv_82586_start)
+ * 4. Start the LAN controller's command unit
+ * 5. Start the LAN controller's receive unit
+ * (called by wavelan_interrupt(), wavelan_watchdog() & wavelan_open())
+ */
+static int wv_hw_reset(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name,
+ (unsigned int) dev);
+#endif
+
+ /* Increase the number of resets done. */
+ lp->nresets++;
+
+ wv_hacr_reset(ioaddr);
+ lp->hacr = HACR_DEFAULT;
+
+ if ((wv_mmc_init(dev) < 0) || (wv_82586_start(dev) < 0))
+ return -1;
+
+ /* Enable the card to send interrupts. */
+ wv_ints_on(dev);
+
+ /* Start card functions */
+ if (wv_cu_start(dev) < 0)
+ return -1;
+
+ /* Setup the controller and parameters */
+ wv_82586_config(dev);
+
+ /* Finish configuration with the receive unit */
+ if (wv_ru_start(dev) < 0)
+ return -1;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name);
+#endif
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Check if there is a WaveLAN at the specific base address.
+ * As a side effect, this reads the MAC address.
+ * (called in wavelan_probe() and init_module())
+ */
+static int wv_check_ioaddr(unsigned long ioaddr, u8 * mac)
+{
+ int i; /* Loop counter */
+
+ /* Check if the base address if available. */
+ if (check_region(ioaddr, sizeof(ha_t)))
+ return -EADDRINUSE; /* ioaddr already used */
+
+ /* Reset host interface */
+ wv_hacr_reset(ioaddr);
+
+ /* Read the MAC address from the parameter storage area. */
+ psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr),
+ mac, 6);
+
+ /*
+ * Check the first three octets of the address for the manufacturer's code.
+ * Note: if this can't find your WaveLAN card, you've got a
+ * non-NCR/AT&T/Lucent ISA card. See wavelan.p.h for detail on
+ * how to configure your card.
+ */
+ for (i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
+ if ((mac[0] == MAC_ADDRESSES[i][0]) &&
+ (mac[1] == MAC_ADDRESSES[i][1]) &&
+ (mac[2] == MAC_ADDRESSES[i][2]))
+ return 0;
+
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_WARNING
+ "WaveLAN (0x%3X): your MAC address might be %02X:%02X:%02X.\n",
+ ioaddr, mac[0], mac[1], mac[2]);
+#endif
+ return -ENODEV;
+}
+
+/************************ INTERRUPT HANDLING ************************/
+
+/*
+ * This function is the interrupt handler for the WaveLAN card. This
+ * routine will be called whenever:
+ */
+static void wavelan_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ device *dev;
+ unsigned long ioaddr;
+ net_local *lp;
+ u16 hasr;
+ u16 status;
+ u16 ack_cmd;
+
+ dev = dev_id;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
+#endif
+
+ lp = (net_local *) dev->priv;
+ ioaddr = dev->base_addr;
+
+#ifdef DEBUG_INTERRUPT_INFO
+ /* Check state of our spinlock */
+ if(spin_is_locked(&lp->spinlock))
+ printk(KERN_DEBUG
+ "%s: wavelan_interrupt(): spinlock is already locked !!!\n",
+ dev->name);
+#endif
+
+ /* Prevent reentrancy. We need to do that because we may have
+ * multiple interrupt handler running concurrently.
+ * It is safe because wv_splhi() disables interrupts before acquiring
+ * the spinlock. */
+ spin_lock(&lp->spinlock);
+
+ /* Check modem interrupt */
+ if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR) {
+ u8 dce_status;
+
+ /*
+ * Interrupt from the modem management controller.
+ * This will clear it -- ignored for now.
+ */
+ mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status,
+ sizeof(dce_status));
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO
+ "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n",
+ dev->name, dce_status);
+#endif
+ }
+
+ /* Check if not controller interrupt */
+ if ((hasr & HASR_82586_INTR) == 0) {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO
+ "%s: wavelan_interrupt(): interrupt not coming from i82586\n",
+ dev->name);
+#endif
+ spin_unlock (&lp->spinlock);
+ return;
+ }
+
+ /* Read interrupt data. */
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
+ (unsigned char *) &status, sizeof(status));
+
+ /*
+ * Acknowledge the interrupt(s).
+ */
+ ack_cmd = status & SCB_ST_INT;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &ack_cmd, sizeof(ack_cmd));
+ set_chan_attn(ioaddr, lp->hacr);
+
+#ifdef DEBUG_INTERRUPT_INFO
+ printk(KERN_DEBUG "%s: wavelan_interrupt(): status 0x%04x.\n",
+ dev->name, status);
+#endif
+
+ /* Command completed. */
+ if ((status & SCB_ST_CX) == SCB_ST_CX) {
+#ifdef DEBUG_INTERRUPT_INFO
+ printk(KERN_DEBUG
+ "%s: wavelan_interrupt(): command completed.\n",
+ dev->name);
+#endif
+ wv_complete(dev, ioaddr, lp);
+ }
+
+ /* Frame received. */
+ if ((status & SCB_ST_FR) == SCB_ST_FR) {
+#ifdef DEBUG_INTERRUPT_INFO
+ printk(KERN_DEBUG
+ "%s: wavelan_interrupt(): received packet.\n",
+ dev->name);
+#endif
+ wv_receive(dev);
+ }
+
+ /* Check the state of the command unit. */
+ if (((status & SCB_ST_CNA) == SCB_ST_CNA) ||
+ (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) &&
+ (netif_running(dev)))) {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO
+ "%s: wavelan_interrupt(): CU inactive -- restarting\n",
+ dev->name);
+#endif
+ wv_hw_reset(dev);
+ }
+
+ /* Check the state of the command unit. */
+ if (((status & SCB_ST_RNR) == SCB_ST_RNR) ||
+ (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) &&
+ (netif_running(dev)))) {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO
+ "%s: wavelan_interrupt(): RU not ready -- restarting\n",
+ dev->name);
+#endif
+ wv_hw_reset(dev);
+ }
+
+ /* Release spinlock */
+ spin_unlock (&lp->spinlock);
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Watchdog: when we start a transmission, a timer is set for us in the
+ * kernel. If the transmission completes, this timer is disabled. If
+ * the timer expires, we are called and we try to unlock the hardware.
+ */
+static void wavelan_watchdog(device * dev)
+{
+ net_local * lp = (net_local *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+ unsigned long flags;
+ unsigned int nreaped;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
+#endif
+
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n",
+ dev->name);
+#endif
+
+ /* Check that we came here for something */
+ if (lp->tx_n_in_use <= 0) {
+ return;
+ }
+
+ wv_splhi(lp, &flags);
+
+ /* Try to see if some buffers are not free (in case we missed
+ * an interrupt */
+ nreaped = wv_complete(dev, ioaddr, lp);
+
+#ifdef DEBUG_INTERRUPT_INFO
+ printk(KERN_DEBUG
+ "%s: wavelan_watchdog(): %d reaped, %d remain.\n",
+ dev->name, nreaped, lp->tx_n_in_use);
+#endif
+
+#ifdef DEBUG_PSA_SHOW
+ {
+ psa_t psa;
+ psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+ wv_psa_show(&psa);
+ }
+#endif
+#ifdef DEBUG_MMC_SHOW
+ wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82586_SHOW
+ wv_cu_show(dev);
+#endif
+
+ /* If no buffer has been freed */
+ if (nreaped == 0) {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO
+ "%s: wavelan_watchdog(): cleanup failed, trying reset\n",
+ dev->name);
+#endif
+ wv_hw_reset(dev);
+ }
+
+ /* At this point, we should have some free Tx buffer ;-) */
+ if (lp->tx_n_in_use < NTXBLOCKS - 1)
+ netif_wake_queue(dev);
+
+ wv_splx(lp, &flags);
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
+#endif
+}
+
+/********************* CONFIGURATION CALLBACKS *********************/
+/*
+ * Here are the functions called by the Linux networking code (NET3)
+ * for initialization, configuration and deinstallations of the
+ * WaveLAN ISA hardware.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Configure and start up the WaveLAN PCMCIA adaptor.
+ * Called by NET3 when it "opens" the device.
+ */
+static int wavelan_open(device * dev)
+{
+ net_local * lp = (net_local *)dev->priv;
+ unsigned long flags;
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name,
+ (unsigned int) dev);
+#endif
+
+ /* Check irq */
+ if (dev->irq == 0) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_WARNING "%s: wavelan_open(): no IRQ\n",
+ dev->name);
+#endif
+ return -ENXIO;
+ }
+
+ if (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", dev) != 0)
+ {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n",
+ dev->name);
+#endif
+ return -EAGAIN;
+ }
+
+ wv_splhi(lp, &flags);
+
+ if (wv_hw_reset(dev) != -1) {
+ netif_start_queue(dev);
+ } else {
+ free_irq(dev->irq, dev);
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO
+ "%s: wavelan_open(): impossible to start the card\n",
+ dev->name);
+#endif
+ wv_splx(lp, &flags);
+ return -EAGAIN;
+ }
+ wv_splx(lp, &flags);
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
+#endif
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Shut down the WaveLAN ISA card.
+ * Called by NET3 when it "closes" the device.
+ */
+static int wavelan_close(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long flags;
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name,
+ (unsigned int) dev);
+#endif
+
+ netif_stop_queue(dev);
+
+ /*
+ * Flush the Tx and disable Rx.
+ */
+ wv_splhi(lp, &flags);
+ wv_82586_stop(dev);
+ wv_splx(lp, &flags);
+
+ free_irq(dev->irq, dev);
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
+#endif
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Probe an I/O address, and if the WaveLAN is there configure the
+ * device structure
+ * (called by wavelan_probe() and via init_module()).
+ */
+static int __init wavelan_config(device * dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ u8 irq_mask;
+ int irq;
+ net_local *lp;
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_config(dev=0x%x, ioaddr=0x%lx)\n",
+ dev->name, (unsigned int) dev, ioaddr);
+#endif
+
+ /* Check IRQ argument on command line. */
+ if (dev->irq != 0) {
+ irq_mask = wv_irq_to_psa(dev->irq);
+
+ if (irq_mask == 0) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_WARNING
+ "%s: wavelan_config(): invalid IRQ %d ignored.\n",
+ dev->name, dev->irq);
+#endif
+ dev->irq = 0;
+ } else {
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG
+ "%s: wavelan_config(): changing IRQ to %d\n",
+ dev->name, dev->irq);
+#endif
+ psa_write(ioaddr, HACR_DEFAULT,
+ psaoff(0, psa_int_req_no), &irq_mask, 1);
+ /* update the Wavelan checksum */
+ update_psa_checksum(dev, ioaddr, HACR_DEFAULT);
+ wv_hacr_reset(ioaddr);
+ }
+ }
+
+ psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no),
+ &irq_mask, 1);
+ if ((irq = wv_psa_to_irq(irq_mask)) == -1) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO
+ "%s: wavelan_config(): could not wavelan_map_irq(%d).\n",
+ dev->name, irq_mask);
+#endif
+ return -EAGAIN;
+ }
+
+ dev->irq = irq;
+
+ if (!request_region(ioaddr, sizeof(ha_t), "wavelan"))
+ return -EBUSY;
+
+ dev->mem_start = 0x0000;
+ dev->mem_end = 0x0000;
+ dev->if_port = 0;
+
+ /* Initialize device structures */
+ dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
+ if (dev->priv == NULL) {
+ release_region(ioaddr, sizeof(ha_t));
+ return -ENOMEM;
+ }
+ memset(dev->priv, 0x00, sizeof(net_local));
+ lp = (net_local *) dev->priv;
+
+ /* Back link to the device structure. */
+ lp->dev = dev;
+ /* Add the device at the beginning of the linked list. */
+ lp->next = wavelan_list;
+ wavelan_list = lp;
+
+ lp->hacr = HACR_DEFAULT;
+
+ /* Multicast stuff */
+ lp->promiscuous = 0;
+ lp->mc_count = 0;
+
+ /* Init spinlock */
+ spin_lock_init(&lp->spinlock);
+
+ /*
+ * Fill in the fields of the device structure
+ * with generic Ethernet values.
+ */
+ ether_setup(dev);
+
+ SET_MODULE_OWNER(dev);
+ dev->open = wavelan_open;
+ dev->stop = wavelan_close;
+ dev->hard_start_xmit = wavelan_packet_xmit;
+ dev->get_stats = wavelan_get_stats;
+ dev->set_multicast_list = &wavelan_set_multicast_list;
+ dev->tx_timeout = &wavelan_watchdog;
+ dev->watchdog_timeo = WATCHDOG_JIFFIES;
+#ifdef SET_MAC_ADDRESS
+ dev->set_mac_address = &wavelan_set_mac_address;
+#endif /* SET_MAC_ADDRESS */
+
+#ifdef WIRELESS_EXT /* if wireless extension exists in the kernel */
+ dev->do_ioctl = wavelan_ioctl;
+ dev->get_wireless_stats = wavelan_get_wireless_stats;
+#endif
+
+ dev->mtu = WAVELAN_MTU;
+
+ /* Display nice information. */
+ wv_init_info(dev);
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_config()\n", dev->name);
+#endif
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Check for a network adaptor of this type. Return '0' iff one
+ * exists. There seem to be different interpretations of
+ * the initial value of dev->base_addr.
+ * We follow the example in drivers/net/ne.c.
+ * (called in "Space.c")
+ */
+int __init wavelan_probe(device * dev)
+{
+ short base_addr;
+ mac_addr mac; /* MAC address (check existence of WaveLAN) */
+ int i;
+ int r;
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG
+ "%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n",
+ dev->name, (unsigned int) dev,
+ (unsigned int) dev->base_addr);
+#endif
+
+#ifdef STRUCT_CHECK
+ if (wv_struct_check() != (char *) NULL) {
+ printk(KERN_WARNING
+ "%s: wavelan_probe(): structure/compiler botch: \"%s\"\n",
+ dev->name, wv_struct_check());
+ return -ENODEV;
+ }
+#endif /* STRUCT_CHECK */
+
+ /* Check the value of the command line parameter for base address. */
+ base_addr = dev->base_addr;
+
+ /* Don't probe at all. */
+ if (base_addr < 0) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_WARNING
+ "%s: wavelan_probe(): invalid base address\n",
+ dev->name);
+#endif
+ return -ENXIO;
+ }
+
+ /* Check a single specified location. */
+ if (base_addr > 0x100) {
+ /* Check if there is something at this base address */
+ if ((r = wv_check_ioaddr(base_addr, mac)) == 0) {
+ memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */
+ r = wavelan_config(dev);
+ }
+#ifdef DEBUG_CONFIG_INFO
+ if (r != 0)
+ printk(KERN_DEBUG
+ "%s: wavelan_probe(): no device at specified base address (0x%X) or address already in use\n",
+ dev->name, base_addr);
+#endif
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name);
+#endif
+ return r;
+ }
+
+ /* Scan all possible addresses of the WaveLAN hardware. */
+ for (i = 0; i < NELS(iobase); i++) {
+ /* Check whether there is something at this base address. */
+ if (wv_check_ioaddr(iobase[i], mac) == 0) {
+ dev->base_addr = iobase[i]; /* Copy base address. */
+ memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */
+ if (wavelan_config(dev) == 0) {
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG
+ "%s: <-wavelan_probe()\n",
+ dev->name);
+#endif
+ return 0;
+ }
+ }
+ }
+
+ /* We may have touched base_addr. Another driver may not like it. */
+ dev->base_addr = base_addr;
+
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG "%s: wavelan_probe(): no device found\n",
+ dev->name);
+#endif
+
+ return -ENODEV;
+}
+
+/****************************** MODULE ******************************/
+/*
+ * Module entry point: insertion and removal
+ */
+
+#ifdef MODULE
+/*------------------------------------------------------------------*/
+/*
+ * Insertion of the module
+ * I'm now quite proud of the multi-device support.
+ */
+int init_module(void)
+{
+ mac_addr mac; /* MAC address (check WaveLAN existence) */
+ int ret = -EIO; /* Return error if no cards found */
+ int i;
+
+#ifdef DEBUG_MODULE_TRACE
+ printk(KERN_DEBUG "-> init_module()\n");
+#endif
+
+ /* If probing is asked */
+ if (io[0] == 0) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_WARNING
+ "WaveLAN init_module(): doing device probing (bad !)\n");
+ printk(KERN_WARNING
+ "Specify base addresses while loading module to correct the problem\n");
+#endif
+
+ /* Copy the basic set of address to be probed. */
+ for (i = 0; i < NELS(iobase); i++)
+ io[i] = iobase[i];
+ }
+
+
+ /* Loop on all possible base addresses. */
+ i = -1;
+ while ((io[++i] != 0) && (i < NELS(io))) {
+ /* Check if there is something at this base address. */
+ if (wv_check_ioaddr(io[i], mac) == 0) {
+ device *dev;
+
+ /* Create device and set basic arguments. */
+ dev =
+ kmalloc(sizeof(struct net_device), GFP_KERNEL);
+ if (dev == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+ memset(dev, 0x00, sizeof(struct net_device));
+ memcpy(dev->name, name[i], IFNAMSIZ); /* Copy name */
+ dev->base_addr = io[i];
+ dev->irq = irq[i];
+ dev->init = &wavelan_config;
+ memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */
+
+ /* Try to create the device. */
+ if (register_netdev(dev) != 0) {
+ /* Deallocate everything. */
+ /* Note: if dev->priv is mallocated, there is no way to fail. */
+ kfree(dev);
+ } else {
+ /* If at least one device OK, we do not fail */
+ ret = 0;
+ }
+ } /* if there is something at the address */
+ } /* Loop on all addresses. */
+
+#ifdef DEBUG_CONFIG_ERROR
+ if (wavelan_list == (net_local *) NULL)
+ printk(KERN_WARNING
+ "WaveLAN init_module(): no device found\n");
+#endif
+
+#ifdef DEBUG_MODULE_TRACE
+ printk(KERN_DEBUG "<- init_module()\n");
+#endif
+ return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Removal of the module
+ */
+void cleanup_module(void)
+{
+#ifdef DEBUG_MODULE_TRACE
+ printk(KERN_DEBUG "-> cleanup_module()\n");
+#endif
+
+ /* Loop on all devices and release them. */
+ while (wavelan_list != (net_local *) NULL) {
+ device *dev = wavelan_list->dev;
+
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG
+ "%s: cleanup_module(): removing device at 0x%x\n",
+ dev->name, (unsigned int) dev);
+#endif
+
+ /* Release the ioport region. */
+ release_region(dev->base_addr, sizeof(ha_t));
+
+ /* Definitely remove the device. */
+ unregister_netdev(dev);
+
+ /* Unlink the device. */
+ wavelan_list = wavelan_list->next;
+
+ /* Free pieces. */
+ kfree(dev->priv);
+ kfree(dev);
+ }
+
+#ifdef DEBUG_MODULE_TRACE
+ printk(KERN_DEBUG "<- cleanup_module()\n");
+#endif
+}
+#endif /* MODULE */
+MODULE_LICENSE("GPL");
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU General Public License.
+ *
+ * This software was developed as a component of the
+ * Linux operating system.
+ * It is based on other device drivers and information
+ * either written or supplied by:
+ * Ajay Bakre (bakre@paul.rutgers.edu),
+ * Donald Becker (becker@scyld.com),
+ * Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com),
+ * Anders Klemets (klemets@it.kth.se),
+ * Vladimir V. Kolpakov (w@stier.koenig.ru),
+ * Marc Meertens (Marc.Meertens@Utrecht.NCR.com),
+ * Pauline Middelink (middelin@polyware.iaf.nl),
+ * Robert Morris (rtm@das.harvard.edu),
+ * Jean Tourrilhes (jt@hplb.hpl.hp.com),
+ * Girish Welling (welling@paul.rutgers.edu),
+ *
+ * Thanks go also to:
+ * James Ashton (jaa101@syseng.anu.edu.au),
+ * Alan Cox (alan@redhat.com),
+ * Allan Creighton (allanc@cs.usyd.edu.au),
+ * Matthew Geier (matthew@cs.usyd.edu.au),
+ * Remo di Giovanni (remo@cs.usyd.edu.au),
+ * Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de),
+ * Vipul Gupta (vgupta@cs.binghamton.edu),
+ * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
+ * Tim Nicholson (tim@cs.usyd.edu.au),
+ * Ian Parkin (ian@cs.usyd.edu.au),
+ * John Rosenberg (johnr@cs.usyd.edu.au),
+ * George Rossi (george@phm.gov.au),
+ * Arthur Scott (arthur@cs.usyd.edu.au),
+ * Peter Storey,
+ * for their assistance and advice.
+ *
+ * Please send bug reports, updates, comments to:
+ *
+ * Bruce Janson Email: bruce@cs.usyd.edu.au
+ * Basser Department of Computer Science Phone: +61-2-9351-3423
+ * University of Sydney, N.S.W., 2006, AUSTRALIA Fax: +61-2-9351-3838
+ */
--- /dev/null
+/*
+ * WaveLAN ISA driver
+ *
+ * Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyright follows. See wavelan.p.h for details.
+ *
+ * This file contains the declarations for the WaveLAN hardware. Note that
+ * the WaveLAN ISA includes a i82586 controller (see definitions in
+ * file i82586.h).
+ *
+ * The main difference between the ISA hardware and the PCMCIA one is
+ * the Ethernet controller (i82586 instead of i82593).
+ * The i82586 allows multiple transmit buffers. The PSA needs to be accessed
+ * through the host interface.
+ */
+
+#ifndef _WAVELAN_H
+#define _WAVELAN_H
+
+/************************** MAGIC NUMBERS ***************************/
+
+/* Detection of the WaveLAN card is done by reading the MAC
+ * address from the card and checking it. If you have a non-AT&T
+ * product (OEM, like DEC RoamAbout, Digital Ocean, or Epson),
+ * you might need to modify this part to accommodate your hardware.
+ */
+static const char MAC_ADDRESSES[][3] =
+{
+ { 0x08, 0x00, 0x0E }, /* AT&T WaveLAN (standard) & DEC RoamAbout */
+ { 0x08, 0x00, 0x6A }, /* AT&T WaveLAN (alternate) */
+ { 0x00, 0x00, 0xE1 }, /* Hitachi Wavelan */
+ { 0x00, 0x60, 0x1D } /* Lucent Wavelan (another one) */
+ /* Add your card here and send me the patch! */
+};
+
+#define WAVELAN_ADDR_SIZE 6 /* Size of a MAC address */
+
+#define WAVELAN_MTU 1500 /* Maximum size of WaveLAN packet */
+
+#define MAXDATAZ (WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU)
+
+/*
+ * Constants used to convert channels to frequencies
+ */
+
+/* Frequency available in the 2.0 modem, in units of 250 kHz
+ * (as read in the offset register of the dac area).
+ * Used to map channel numbers used by `wfreqsel' to frequencies
+ */
+static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
+ 0xD0, 0xF0, 0xF8, 0x150 };
+
+/* Frequencies of the 1.0 modem (fixed frequencies).
+ * Use to map the PSA `subband' to a frequency
+ * Note : all frequencies apart from the first one need to be multiplied by 10
+ */
+static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
+
+
+
+/*************************** PC INTERFACE ****************************/
+
+/*
+ * Host Adaptor structure.
+ * (base is board port address).
+ */
+typedef union hacs_u hacs_u;
+union hacs_u
+{
+ unsigned short hu_command; /* Command register */
+#define HACR_RESET 0x0001 /* Reset board */
+#define HACR_CA 0x0002 /* Set Channel Attention for 82586 */
+#define HACR_16BITS 0x0004 /* 16-bit operation (0 => 8bits) */
+#define HACR_OUT0 0x0008 /* General purpose output pin 0 */
+ /* not used - must be 1 */
+#define HACR_OUT1 0x0010 /* General purpose output pin 1 */
+ /* not used - must be 1 */
+#define HACR_82586_INT_ENABLE 0x0020 /* Enable 82586 interrupts */
+#define HACR_MMC_INT_ENABLE 0x0040 /* Enable MMC interrupts */
+#define HACR_INTR_CLR_ENABLE 0x0080 /* Enable interrupt status read/clear */
+ unsigned short hu_status; /* Status Register */
+#define HASR_82586_INTR 0x0001 /* Interrupt request from 82586 */
+#define HASR_MMC_INTR 0x0002 /* Interrupt request from MMC */
+#define HASR_MMC_BUSY 0x0004 /* MMC busy indication */
+#define HASR_PSA_BUSY 0x0008 /* LAN parameter storage area busy */
+};
+
+typedef struct ha_t ha_t;
+struct ha_t
+{
+ hacs_u ha_cs; /* Command and status registers */
+#define ha_command ha_cs.hu_command
+#define ha_status ha_cs.hu_status
+ unsigned short ha_mmcr; /* Modem Management Ctrl Register */
+ unsigned short ha_pior0; /* Program I/O Address Register Port 0 */
+ unsigned short ha_piop0; /* Program I/O Port 0 */
+ unsigned short ha_pior1; /* Program I/O Address Register Port 1 */
+ unsigned short ha_piop1; /* Program I/O Port 1 */
+ unsigned short ha_pior2; /* Program I/O Address Register Port 2 */
+ unsigned short ha_piop2; /* Program I/O Port 2 */
+};
+
+#define HA_SIZE 16
+
+#define hoff(p,f) (unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0)
+#define HACR(p) hoff(p, ha_command)
+#define HASR(p) hoff(p, ha_status)
+#define MMCR(p) hoff(p, ha_mmcr)
+#define PIOR0(p) hoff(p, ha_pior0)
+#define PIOP0(p) hoff(p, ha_piop0)
+#define PIOR1(p) hoff(p, ha_pior1)
+#define PIOP1(p) hoff(p, ha_piop1)
+#define PIOR2(p) hoff(p, ha_pior2)
+#define PIOP2(p) hoff(p, ha_piop2)
+
+/*
+ * Program I/O Mode Register values.
+ */
+#define STATIC_PIO 0 /* Mode 1: static mode */
+ /* RAM access ??? */
+#define AUTOINCR_PIO 1 /* Mode 2: auto increment mode */
+ /* RAM access ??? */
+#define AUTODECR_PIO 2 /* Mode 3: auto decrement mode */
+ /* RAM access ??? */
+#define PARAM_ACCESS_PIO 3 /* Mode 4: LAN parameter access mode */
+ /* Parameter access. */
+#define PIO_MASK 3 /* register mask */
+#define PIOM(cmd,piono) ((u_short)cmd << 10 << (piono * 2))
+
+#define HACR_DEFAULT (HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2))
+#define HACR_INTRON (HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE)
+
+/************************** MEMORY LAYOUT **************************/
+
+/*
+ * Onboard 64 k RAM layout.
+ * (Offsets from 0x0000.)
+ */
+#define OFFSET_RU 0x0000 /* 75% memory */
+#define OFFSET_CU 0xC000 /* 25% memory */
+#define OFFSET_SCB (OFFSET_ISCP - sizeof(scb_t))
+#define OFFSET_ISCP (OFFSET_SCP - sizeof(iscp_t))
+#define OFFSET_SCP I82586_SCP_ADDR
+
+#define RXBLOCKZ (sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ)
+#define TXBLOCKZ (sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ)
+
+#define NRXBLOCKS ((OFFSET_CU - OFFSET_RU) / RXBLOCKZ)
+#define NTXBLOCKS ((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ)
+
+/********************** PARAMETER STORAGE AREA **********************/
+
+/*
+ * Parameter Storage Area (PSA).
+ */
+typedef struct psa_t psa_t;
+struct psa_t
+{
+ unsigned char psa_io_base_addr_1; /* [0x00] Base address 1 ??? */
+ unsigned char psa_io_base_addr_2; /* [0x01] Base address 2 */
+ unsigned char psa_io_base_addr_3; /* [0x02] Base address 3 */
+ unsigned char psa_io_base_addr_4; /* [0x03] Base address 4 */
+ unsigned char psa_rem_boot_addr_1; /* [0x04] Remote Boot Address 1 */
+ unsigned char psa_rem_boot_addr_2; /* [0x05] Remote Boot Address 2 */
+ unsigned char psa_rem_boot_addr_3; /* [0x06] Remote Boot Address 3 */
+ unsigned char psa_holi_params; /* [0x07] HOst Lan Interface (HOLI) Parameters */
+ unsigned char psa_int_req_no; /* [0x08] Interrupt Request Line */
+ unsigned char psa_unused0[7]; /* [0x09-0x0F] unused */
+
+ unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x10-0x15] Universal (factory) MAC Address */
+ unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x16-1B] Local MAC Address */
+ unsigned char psa_univ_local_sel; /* [0x1C] Universal Local Selection */
+#define PSA_UNIVERSAL 0 /* Universal (factory) */
+#define PSA_LOCAL 1 /* Local */
+ unsigned char psa_comp_number; /* [0x1D] Compatibility Number: */
+#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */
+#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */
+#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */
+#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */
+#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz or 2.0 */
+ unsigned char psa_thr_pre_set; /* [0x1E] Modem Threshold Preset */
+ unsigned char psa_feature_select; /* [0x1F] Call code required (1=on) */
+#define PSA_FEATURE_CALL_CODE 0x01 /* Call code required (Japan) */
+ unsigned char psa_subband; /* [0x20] Subband */
+#define PSA_SUBBAND_915 0 /* 915 MHz or 2.0 */
+#define PSA_SUBBAND_2425 1 /* 2425 MHz */
+#define PSA_SUBBAND_2460 2 /* 2460 MHz */
+#define PSA_SUBBAND_2484 3 /* 2484 MHz */
+#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */
+ unsigned char psa_quality_thr; /* [0x21] Modem Quality Threshold */
+ unsigned char psa_mod_delay; /* [0x22] Modem Delay (?) (reserved) */
+ unsigned char psa_nwid[2]; /* [0x23-0x24] Network ID */
+ unsigned char psa_nwid_select; /* [0x25] Network ID Select On/Off */
+ unsigned char psa_encryption_select; /* [0x26] Encryption On/Off */
+ unsigned char psa_encryption_key[8]; /* [0x27-0x2E] Encryption Key */
+ unsigned char psa_databus_width; /* [0x2F] AT bus width select 8/16 */
+ unsigned char psa_call_code[8]; /* [0x30-0x37] (Japan) Call Code */
+ unsigned char psa_nwid_prefix[2]; /* [0x38-0x39] Roaming domain */
+ unsigned char psa_reserved[2]; /* [0x3A-0x3B] Reserved - fixed 00 */
+ unsigned char psa_conf_status; /* [0x3C] Conf Status, bit 0=1:config*/
+ unsigned char psa_crc[2]; /* [0x3D] CRC-16 over PSA */
+ unsigned char psa_crc_status; /* [0x3F] CRC Valid Flag */
+};
+
+#define PSA_SIZE 64
+
+/* Calculate offset of a field in the above structure.
+ * Warning: only even addresses are used. */
+#define psaoff(p,f) ((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL))
+
+/******************** MODEM MANAGEMENT INTERFACE ********************/
+
+/*
+ * Modem Management Controller (MMC) write structure.
+ */
+typedef struct mmw_t mmw_t;
+struct mmw_t
+{
+ unsigned char mmw_encr_key[8]; /* encryption key */
+ unsigned char mmw_encr_enable; /* Enable or disable encryption. */
+#define MMW_ENCR_ENABLE_MODE 0x02 /* mode of security option */
+#define MMW_ENCR_ENABLE_EN 0x01 /* Enable security option. */
+ unsigned char mmw_unused0[1]; /* unused */
+ unsigned char mmw_des_io_invert; /* encryption option */
+#define MMW_DES_IO_INVERT_RES 0x0F /* reserved */
+#define MMW_DES_IO_INVERT_CTRL 0xF0 /* control (?) (set to 0) */
+ unsigned char mmw_unused1[5]; /* unused */
+ unsigned char mmw_loopt_sel; /* looptest selection */
+#define MMW_LOOPT_SEL_DIS_NWID 0x40 /* Disable NWID filtering. */
+#define MMW_LOOPT_SEL_INT 0x20 /* Activate Attention Request. */
+#define MMW_LOOPT_SEL_LS 0x10 /* looptest, no collision avoidance */
+#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */
+#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */
+#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */
+#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */
+ unsigned char mmw_jabber_enable; /* jabber timer enable */
+ /* Abort transmissions > 200 ms */
+ unsigned char mmw_freeze; /* freeze or unfreeze signal level */
+ /* 0 : signal level & qual updated for every new message, 1 : frozen */
+ unsigned char mmw_anten_sel; /* antenna selection */
+#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */
+#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algo. enable */
+ unsigned char mmw_ifs; /* inter frame spacing */
+ /* min time between transmission in bit periods (.5 us) - bit 0 ignored */
+ unsigned char mmw_mod_delay; /* modem delay (synchro) */
+ unsigned char mmw_jam_time; /* jamming time (after collision) */
+ unsigned char mmw_unused2[1]; /* unused */
+ unsigned char mmw_thr_pre_set; /* level threshold preset */
+ /* Discard all packet with signal < this value (4) */
+ unsigned char mmw_decay_prm; /* decay parameters */
+ unsigned char mmw_decay_updat_prm; /* decay update parameters */
+ unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */
+ /* Discard all packet with quality < this value (3) */
+ unsigned char mmw_netw_id_l; /* NWID low order byte */
+ unsigned char mmw_netw_id_h; /* NWID high order byte */
+ /* Network ID or Domain : create virtual net on the air */
+
+ /* 2.0 Hardware extension - frequency selection support */
+ unsigned char mmw_mode_select; /* for analog tests (set to 0) */
+ unsigned char mmw_unused3[1]; /* unused */
+ unsigned char mmw_fee_ctrl; /* frequency EEPROM control */
+#define MMW_FEE_CTRL_PRE 0x10 /* Enable protected instructions. */
+#define MMW_FEE_CTRL_DWLD 0x08 /* Download EEPROM to mmc. */
+#define MMW_FEE_CTRL_CMD 0x07 /* EEPROM commands: */
+#define MMW_FEE_CTRL_READ 0x06 /* Read */
+#define MMW_FEE_CTRL_WREN 0x04 /* Write enable */
+#define MMW_FEE_CTRL_WRITE 0x05 /* Write data to address. */
+#define MMW_FEE_CTRL_WRALL 0x04 /* Write data to all addresses. */
+#define MMW_FEE_CTRL_WDS 0x04 /* Write disable */
+#define MMW_FEE_CTRL_PRREAD 0x16 /* Read addr from protect register */
+#define MMW_FEE_CTRL_PREN 0x14 /* Protect register enable */
+#define MMW_FEE_CTRL_PRCLEAR 0x17 /* Unprotect all registers. */
+#define MMW_FEE_CTRL_PRWRITE 0x15 /* Write address in protect register */
+#define MMW_FEE_CTRL_PRDS 0x14 /* Protect register disable */
+ /* Never issue the PRDS command: it's irreversible! */
+
+ unsigned char mmw_fee_addr; /* EEPROM address */
+#define MMW_FEE_ADDR_CHANNEL 0xF0 /* Select the channel. */
+#define MMW_FEE_ADDR_OFFSET 0x0F /* Offset in channel data */
+#define MMW_FEE_ADDR_EN 0xC0 /* FEE_CTRL enable operations */
+#define MMW_FEE_ADDR_DS 0x00 /* FEE_CTRL disable operations */
+#define MMW_FEE_ADDR_ALL 0x40 /* FEE_CTRL all operations */
+#define MMW_FEE_ADDR_CLEAR 0xFF /* FEE_CTRL clear operations */
+
+ unsigned char mmw_fee_data_l; /* Write data to EEPROM. */
+ unsigned char mmw_fee_data_h; /* high octet */
+ unsigned char mmw_ext_ant; /* Setting for external antenna */
+#define MMW_EXT_ANT_EXTANT 0x01 /* Select external antenna */
+#define MMW_EXT_ANT_POL 0x02 /* Polarity of the antenna */
+#define MMW_EXT_ANT_INTERNAL 0x00 /* Internal antenna */
+#define MMW_EXT_ANT_EXTERNAL 0x03 /* External antenna */
+#define MMW_EXT_ANT_IQ_TEST 0x1C /* IQ test pattern (set to 0) */
+};
+
+#define MMW_SIZE 37
+
+#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * Modem Management Controller (MMC) read structure.
+ */
+typedef struct mmr_t mmr_t;
+struct mmr_t
+{
+ unsigned char mmr_unused0[8]; /* unused */
+ unsigned char mmr_des_status; /* encryption status */
+ unsigned char mmr_des_avail; /* encryption available (0x55 read) */
+#define MMR_DES_AVAIL_DES 0x55 /* DES available */
+#define MMR_DES_AVAIL_AES 0x33 /* AES (AT&T) available */
+ unsigned char mmr_des_io_invert; /* des I/O invert register */
+ unsigned char mmr_unused1[5]; /* unused */
+ unsigned char mmr_dce_status; /* DCE status */
+#define MMR_DCE_STATUS_RX_BUSY 0x01 /* receiver busy */
+#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */
+#define MMR_DCE_STATUS_TX_BUSY 0x04 /* transmitter on */
+#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */
+#define MMR_DCE_STATUS 0x0F /* mask to get the bits */
+ unsigned char mmr_dsp_id; /* DSP ID (AA = Daedalus rev A) */
+ unsigned char mmr_unused2[2]; /* unused */
+ unsigned char mmr_correct_nwid_l; /* # of correct NWIDs rxd (low) */
+ unsigned char mmr_correct_nwid_h; /* # of correct NWIDs rxd (high) */
+ /* Warning: read high-order octet first! */
+ unsigned char mmr_wrong_nwid_l; /* # of wrong NWIDs rxd (low) */
+ unsigned char mmr_wrong_nwid_h; /* # of wrong NWIDs rxd (high) */
+ unsigned char mmr_thr_pre_set; /* level threshold preset */
+#define MMR_THR_PRE_SET 0x3F /* level threshold preset */
+#define MMR_THR_PRE_SET_CUR 0x80 /* Current signal above it */
+ unsigned char mmr_signal_lvl; /* signal level */
+#define MMR_SIGNAL_LVL 0x3F /* signal level */
+#define MMR_SIGNAL_LVL_VALID 0x80 /* Updated since last read */
+ unsigned char mmr_silence_lvl; /* silence level (noise) */
+#define MMR_SILENCE_LVL 0x3F /* silence level */
+#define MMR_SILENCE_LVL_VALID 0x80 /* Updated since last read */
+ unsigned char mmr_sgnl_qual; /* signal quality */
+#define MMR_SGNL_QUAL 0x0F /* signal quality */
+#define MMR_SGNL_QUAL_ANT 0x80 /* current antenna used */
+ unsigned char mmr_netw_id_l; /* NWID low order byte (?) */
+ unsigned char mmr_unused3[3]; /* unused */
+
+ /* 2.0 Hardware extension - frequency selection support */
+ unsigned char mmr_fee_status; /* Status of frequency EEPROM */
+#define MMR_FEE_STATUS_ID 0xF0 /* Modem revision ID */
+#define MMR_FEE_STATUS_DWLD 0x08 /* Download in progress */
+#define MMR_FEE_STATUS_BUSY 0x04 /* EEPROM busy */
+ unsigned char mmr_unused4[1]; /* unused */
+ unsigned char mmr_fee_data_l; /* Read data from EEPROM (low) */
+ unsigned char mmr_fee_data_h; /* Read data from EEPROM (high) */
+};
+
+#define MMR_SIZE 36
+
+#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/* Make the two above structures one */
+typedef union mm_t
+{
+ struct mmw_t w; /* Write to the mmc */
+ struct mmr_t r; /* Read from the mmc */
+} mm_t;
+
+#endif /* _WAVELAN_H */
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU General Public License.
+ *
+ * For more details, see wavelan.c.
+ */
--- /dev/null
+/*
+ * WaveLAN ISA driver
+ *
+ * Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ *
+ * This file contains all definitions and declarations necessary for the
+ * WaveLAN ISA driver. This file is a private header, so it should
+ * be included only in wavelan.c!
+ */
+
+#ifndef WAVELAN_P_H
+#define WAVELAN_P_H
+
+/************************** DOCUMENTATION ***************************/
+/*
+ * This driver provides a Linux interface to the WaveLAN ISA hardware.
+ * The WaveLAN is a product of Lucent (http://www.wavelan.com/).
+ * This division was formerly part of NCR and then AT&T.
+ * WaveLANs are also distributed by DEC (RoamAbout DS) and Digital Ocean.
+ *
+ * To learn how to use this driver, read the NET3 HOWTO.
+ * If you want to exploit the many other functionalities, read the comments
+ * in the code.
+ *
+ * This driver is the result of the effort of many people (see below).
+ */
+
+/* ------------------------ SPECIFIC NOTES ------------------------ */
+/*
+ * Web page
+ * --------
+ * I try to maintain a web page with the Wireless LAN Howto at :
+ * http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
+ *
+ * SMP
+ * ---
+ * We now are SMP compliant (I eventually fixed the remaining bugs).
+ * The driver has been tested on a dual P6-150 and survived my usual
+ * set of torture tests.
+ * Anyway, I spent enough time chasing interrupt re-entrancy during
+ * errors or reconfigure, and I designed the locked/unlocked sections
+ * of the driver with great care, and with the recent addition of
+ * the spinlock (thanks to the new API), we should be quite close to
+ * the truth.
+ * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast),
+ * but better safe than sorry (especially at 2 Mb/s ;-).
+ *
+ * I have also looked into disabling only our interrupt on the card
+ * (via HACR) instead of all interrupts in the processor (via cli),
+ * so that other driver are not impacted, and it look like it's
+ * possible, but it's very tricky to do right (full of races). As
+ * the gain would be mostly for SMP systems, it can wait...
+ *
+ * Debugging and options
+ * ---------------------
+ * You will find below a set of '#define" allowing a very fine control
+ * on the driver behaviour and the debug messages printed.
+ * The main options are :
+ * o SET_PSA_CRC, to have your card correctly recognised by
+ * an access point and the Point-to-Point diagnostic tool.
+ * o USE_PSA_CONFIG, to read configuration from the PSA (EEprom)
+ * (otherwise we always start afresh with some defaults)
+ *
+ * wavelan.o is too darned big
+ * ---------------------------
+ * That's true! There is a very simple way to reduce the driver
+ * object by 33%! Comment out the following line:
+ * #include <linux/wireless.h>
+ * Other compile options can also reduce the size of it...
+ *
+ * MAC address and hardware detection:
+ * -----------------------------------
+ * The detection code for the WaveLAN checks that the first three
+ * octets of the MAC address fit the company code. This type of
+ * detection works well for AT&T cards (because the AT&T code is
+ * hardcoded in wavelan.h), but of course will fail for other
+ * manufacturers.
+ *
+ * If you are sure that your card is derived from the WaveLAN,
+ * here is the way to configure it:
+ * 1) Get your MAC address
+ * a) With your card utilities (wfreqsel, instconf, etc.)
+ * b) With the driver:
+ * o compile the kernel with DEBUG_CONFIG_INFO enabled
+ * o Boot and look the card messages
+ * 2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h)
+ * 3) Compile and verify
+ * 4) Send me the MAC code. I will include it in the next version.
+ *
+ */
+
+/* --------------------- WIRELESS EXTENSIONS --------------------- */
+/*
+ * This driver is the first to support "wireless extensions".
+ * This set of extensions provides a standard way to control the wireless
+ * characteristics of the hardware. Applications such as mobile IP may
+ * take advantage of it.
+ *
+ * You will need to enable the CONFIG_NET_RADIO define in the kernel
+ * configuration to enable the wireless extensions (this is the one
+ * giving access to the radio network device choice).
+ *
+ * It might also be a good idea as well to fetch the wireless tools to
+ * configure the device and play a bit.
+ */
+
+/* ---------------------------- FILES ---------------------------- */
+/*
+ * wavelan.c: actual code for the driver: C functions
+ *
+ * wavelan.p.h: private header: local types and variables for driver
+ *
+ * wavelan.h: description of the hardware interface and structs
+ *
+ * i82586.h: description of the Ethernet controller
+ */
+
+/* --------------------------- HISTORY --------------------------- */
+/*
+ * This is based on information in the drivers' headers. It may not be
+ * accurate, and I guarantee only my best effort.
+ *
+ * The history of the WaveLAN drivers is as complicated as the history of
+ * the WaveLAN itself (NCR -> AT&T -> Lucent).
+ *
+ * It all started with Anders Klemets <klemets@paul.rutgers.edu>
+ * writing a WaveLAN ISA driver for the Mach microkernel. Girish
+ * Welling <welling@paul.rutgers.edu> had also worked on it.
+ * Keith Moore modified this for the PCMCIA hardware.
+ *
+ * Robert Morris <rtm@das.harvard.edu> ported these two drivers to BSDI
+ * and added specific PCMCIA support (there is currently no equivalent
+ * of the PCMCIA package under BSD).
+ *
+ * Jim Binkley <jrb@cs.pdx.edu> ported both BSDI drivers to FreeBSD.
+ *
+ * Bruce Janson <bruce@cs.usyd.edu.au> ported the BSDI ISA driver to Linux.
+ *
+ * Anthony D. Joseph <adj@lcs.mit.edu> started to modify Bruce's driver
+ * (with help of the BSDI PCMCIA driver) for PCMCIA.
+ * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished this work.
+ * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
+ * 2.00 cards correctly (2.4 GHz with frequency selection).
+ * David Hinds <dhinds@hyper.stanford.edu> integrated the whole in his
+ * PCMCIA package (and bug corrections).
+ *
+ * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
+ * patches to the PCMCIA driver. Later, I added code in the ISA driver
+ * for Wireless Extensions and full support of frequency selection
+ * cards. Then, I did the same to the PCMCIA driver, and did some
+ * reorganisation. Finally, I came back to the ISA driver to
+ * upgrade it at the same level as the PCMCIA one and reorganise
+ * the code.
+ * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
+ * much needed information on the WaveLAN hardware.
+ */
+
+/* The original copyrights and literature mention others' names and
+ * credits. I don't know what their part in this development was.
+ */
+
+/* By the way, for the copyright and legal stuff:
+ * almost everybody wrote code under the GNU or BSD license (or similar),
+ * and want their original copyright to remain somewhere in the
+ * code (for myself, I go with the GPL).
+ * Nobody wants to take responsibility for anything, except the fame.
+ */
+
+/* --------------------------- CREDITS --------------------------- */
+/*
+ * This software was developed as a component of the
+ * Linux operating system.
+ * It is based on other device drivers and information
+ * either written or supplied by:
+ * Ajay Bakre <bakre@paul.rutgers.edu>,
+ * Donald Becker <becker@cesdis.gsfc.nasa.gov>,
+ * Loeke Brederveld <Loeke.Brederveld@Utrecht.NCR.com>,
+ * Brent Elphick <belphick@uwaterloo.ca>,
+ * Anders Klemets <klemets@it.kth.se>,
+ * Vladimir V. Kolpakov <w@stier.koenig.ru>,
+ * Marc Meertens <Marc.Meertens@Utrecht.NCR.com>,
+ * Pauline Middelink <middelin@polyware.iaf.nl>,
+ * Robert Morris <rtm@das.harvard.edu>,
+ * Jean Tourrilhes <jt@hpl.hp.com>,
+ * Girish Welling <welling@paul.rutgers.edu>,
+ * Clark Woodworth <clark@hiway1.exit109.com>
+ * Yongguang Zhang <ygz@isl.hrl.hac.com>
+ *
+ * Thanks go also to:
+ * James Ashton <jaa101@syseng.anu.edu.au>,
+ * Alan Cox <alan@redhat.com>,
+ * Allan Creighton <allanc@cs.usyd.edu.au>,
+ * Matthew Geier <matthew@cs.usyd.edu.au>,
+ * Remo di Giovanni <remo@cs.usyd.edu.au>,
+ * Eckhard Grah <grah@wrcs1.urz.uni-wuppertal.de>,
+ * Vipul Gupta <vgupta@cs.binghamton.edu>,
+ * Mark Hagan <mhagan@wtcpost.daytonoh.NCR.COM>,
+ * Tim Nicholson <tim@cs.usyd.edu.au>,
+ * Ian Parkin <ian@cs.usyd.edu.au>,
+ * John Rosenberg <johnr@cs.usyd.edu.au>,
+ * George Rossi <george@phm.gov.au>,
+ * Arthur Scott <arthur@cs.usyd.edu.au>,
+ * Stanislav Sinyagin <stas@isf.ru>
+ * and Peter Storey for their assistance and advice.
+ *
+ * Additional Credits:
+ *
+ * My development has been done initially under Debian 1.1 (Linux 2.0.x)
+ * and now under Debian 2.2, initially with an HP Vectra XP/60, and now
+ * an HP Vectra XP/90.
+ *
+ */
+
+/* ------------------------- IMPROVEMENTS ------------------------- */
+/*
+ * I proudly present:
+ *
+ * Changes made in first pre-release:
+ * ----------------------------------
+ * - reorganisation of the code, function name change
+ * - creation of private header (wavelan.p.h)
+ * - reorganised debug messages
+ * - more comments, history, etc.
+ * - mmc_init: configure the PSA if not done
+ * - mmc_init: correct default value of level threshold for PCMCIA
+ * - mmc_init: 2.00 detection better code for 2.00 initialization
+ * - better info at startup
+ * - IRQ setting (note: this setting is permanent)
+ * - watchdog: change strategy (and solve module removal problems)
+ * - add wireless extensions (ioctl and get_wireless_stats)
+ * get/set nwid/frequency on fly, info for /proc/net/wireless
+ * - more wireless extensions: SETSPY and GETSPY
+ * - make wireless extensions optional
+ * - private ioctl to set/get quality and level threshold, histogram
+ * - remove /proc/net/wavelan
+ * - suppress useless stuff from lp (net_local)
+ * - kernel 2.1 support (copy_to/from_user instead of memcpy_to/fromfs)
+ * - add message level (debug stuff in /var/adm/debug and errors not
+ * displayed at console and still in /var/adm/messages)
+ * - multi device support
+ * - start fixing the probe (init code)
+ * - more inlines
+ * - man page
+ * - many other minor details and cleanups
+ *
+ * Changes made in second pre-release:
+ * -----------------------------------
+ * - clean up init code (probe and module init)
+ * - better multiple device support (module)
+ * - name assignment (module)
+ *
+ * Changes made in third pre-release:
+ * ----------------------------------
+ * - be more conservative on timers
+ * - preliminary support for multicast (I still lack some details)
+ *
+ * Changes made in fourth pre-release:
+ * -----------------------------------
+ * - multicast (revisited and finished)
+ * - avoid reset in set_multicast_list (a really big hack)
+ * if somebody could apply this code for other i82586 based drivers
+ * - share onboard memory 75% RU and 25% CU (instead of 50/50)
+ *
+ * Changes made for release in 2.1.15:
+ * -----------------------------------
+ * - change the detection code for multi manufacturer code support
+ *
+ * Changes made for release in 2.1.17:
+ * -----------------------------------
+ * - update to wireless extensions changes
+ * - silly bug in card initial configuration (psa_conf_status)
+ *
+ * Changes made for release in 2.1.27 & 2.0.30:
+ * --------------------------------------------
+ * - small bug in debug code (probably not the last one...)
+ * - remove extern keyword for wavelan_probe()
+ * - level threshold is now a standard wireless extension (version 4 !)
+ * - modules parameters types (new module interface)
+ *
+ * Changes made for release in 2.1.36:
+ * -----------------------------------
+ * - byte count stats (courtesy of David Hinds)
+ * - remove dev_tint stuff (courtesy of David Hinds)
+ * - encryption setting from Brent Elphick (thanks a lot!)
+ * - 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
+ *
+ * Other changes (not by me) :
+ * -------------------------
+ * - Spelling and gramar "rectification".
+ *
+ * Changes made for release in 2.0.37 & 2.2.2 :
+ * ------------------------------------------
+ * - Correct status in /proc/net/wireless
+ * - Set PSA CRC to make PtP diagnostic tool happy (Bob Gray)
+ * - Module init code don't fail if we found at least one card in
+ * the address list (Karlis Peisenieks)
+ * - Missing parenthesis (Christopher Peterson)
+ * - Correct i82586 configuration parameters
+ * - Encryption initialisation bug (Robert McCormack)
+ * - New mac addresses detected in the probe
+ * - Increase watchdog for busy environments
+ *
+ * Changes made for release in 2.0.38 & 2.2.7 :
+ * ------------------------------------------
+ * - Correct the reception logic to better report errors and avoid
+ * sending bogus packet up the stack
+ * - Delay RU config to avoid corrupting first received packet
+ * - Change config completion code (to actually check something)
+ * - Avoid reading out of bound in skbuf to transmit
+ * - Rectify a lot of (useless) debugging code
+ * - Change the way to `#ifdef SET_PSA_CRC'
+ *
+ * Changes made for release in 2.2.11 & 2.3.13 :
+ * -------------------------------------------
+ * - Change e-mail and web page addresses
+ * - Watchdog timer is now correctly expressed in HZ, not in jiffies
+ * - Add channel number to the list of frequencies in range
+ * - Add the (short) list of bit-rates in range
+ * - Developp a new sensitivity... (sens.value & sens.fixed)
+ *
+ * Changes made for release in 2.2.14 & 2.3.23 :
+ * -------------------------------------------
+ * - Fix check for root permission (break instead of exit)
+ * - New nwid & encoding setting (Wireless Extension 9)
+ *
+ * Changes made for release in 2.3.49 :
+ * ----------------------------------
+ * - Indentation reformating (Alan)
+ * - Update to new network API (softnet - 2.3.43) :
+ * o replace dev->tbusy (Alan)
+ * o replace dev->tstart (Alan)
+ * o remove dev->interrupt (Alan)
+ * o add SMP locking via spinlock in splxx (me)
+ * o add spinlock in interrupt handler (me)
+ * o use kernel watchdog instead of ours (me)
+ * o increase watchdog timeout (kernel is more sensitive) (me)
+ * o verify that all the changes make sense and work (me)
+ * - Fixup a potential gotcha when reconfiguring and thighten a bit
+ * the interactions with Tx queue.
+ *
+ * Changes made for release in 2.4.0 :
+ * ---------------------------------
+ * - Fix spinlock stupid bugs that I left in. The driver is now SMP
+ * compliant and doesn't lockup at startup.
+ *
+ * Wishes & dreams:
+ * ----------------
+ * - roaming (see Pcmcia driver)
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/stat.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+
+#include <linux/wireless.h> /* Wireless extensions */
+
+/* WaveLAN declarations */
+#include "i82586.h"
+#include "wavelan.h"
+
+/************************** DRIVER OPTIONS **************************/
+/*
+ * `#define' or `#undef' the following constant to change the behaviour
+ * of the driver...
+ */
+#undef SET_PSA_CRC /* Calculate and set the CRC on PSA (slower) */
+#define USE_PSA_CONFIG /* Use info from the PSA. */
+#undef STRUCT_CHECK /* Verify padding of structures. */
+#undef EEPROM_IS_PROTECTED /* doesn't seem to be necessary */
+#define MULTICAST_AVOID /* Avoid extra multicast (I'm sceptical). */
+#undef SET_MAC_ADDRESS /* Experimental */
+
+#ifdef WIRELESS_EXT /* If wireless extensions exist in the kernel */
+/* Warning: this stuff will slow down the driver. */
+#define WIRELESS_SPY /* Enable spying addresses. */
+#undef HISTOGRAM /* Enable histogram of signal level. */
+#endif
+
+/****************************** DEBUG ******************************/
+
+#undef DEBUG_MODULE_TRACE /* module insertion/removal */
+#undef DEBUG_CALLBACK_TRACE /* calls made by Linux */
+#undef DEBUG_INTERRUPT_TRACE /* calls to handler */
+#undef DEBUG_INTERRUPT_INFO /* type of interrupt and so on */
+#define DEBUG_INTERRUPT_ERROR /* problems */
+#undef DEBUG_CONFIG_TRACE /* Trace the config functions. */
+#undef DEBUG_CONFIG_INFO /* what's going on */
+#define DEBUG_CONFIG_ERROR /* errors on configuration */
+#undef DEBUG_TX_TRACE /* transmission calls */
+#undef DEBUG_TX_INFO /* header of the transmitted packet */
+#undef DEBUG_TX_FAIL /* Normal failure conditions */
+#define DEBUG_TX_ERROR /* Unexpected conditions */
+#undef DEBUG_RX_TRACE /* transmission calls */
+#undef DEBUG_RX_INFO /* header of the received packet */
+#undef DEBUG_RX_FAIL /* Normal failure conditions */
+#define DEBUG_RX_ERROR /* Unexpected conditions */
+
+#undef DEBUG_PACKET_DUMP /* Dump packet on the screen if defined to 32. */
+#undef DEBUG_IOCTL_TRACE /* misc. call by Linux */
+#undef DEBUG_IOCTL_INFO /* various debugging info */
+#define DEBUG_IOCTL_ERROR /* what's going wrong */
+#define DEBUG_BASIC_SHOW /* Show basic startup info. */
+#undef DEBUG_VERSION_SHOW /* Print version info. */
+#undef DEBUG_PSA_SHOW /* Dump PSA to screen. */
+#undef DEBUG_MMC_SHOW /* Dump mmc to screen. */
+#undef DEBUG_SHOW_UNUSED /* Show unused fields too. */
+#undef DEBUG_I82586_SHOW /* Show i82586 status. */
+#undef DEBUG_DEVICE_SHOW /* Show device parameters. */
+
+/************************ CONSTANTS & MACROS ************************/
+
+#ifdef DEBUG_VERSION_SHOW
+static const char *version = "wavelan.c : v23 (SMP + wireless extensions) 05/10/00\n";
+#endif
+
+/* Watchdog temporisation */
+#define WATCHDOG_JIFFIES (512*HZ/100)
+
+/* Macro to get the number of elements in an array */
+#define NELS(a) (sizeof(a) / sizeof(a[0]))
+
+/* ------------------------ PRIVATE IOCTL ------------------------ */
+
+#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */
+#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */
+#define SIOCSIPLTHR SIOCIWFIRSTPRIV + 2 /* Set level threshold */
+#define SIOCGIPLTHR SIOCIWFIRSTPRIV + 3 /* Get level threshold */
+
+#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */
+#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 7 /* Get histogram values */
+
+/****************************** TYPES ******************************/
+
+/* Shortcuts */
+typedef struct net_device device;
+typedef struct net_device_stats en_stats;
+typedef struct iw_statistics iw_stats;
+typedef struct iw_quality iw_qual;
+typedef struct iw_freq iw_freq;
+typedef struct net_local net_local;
+typedef struct timer_list timer_list;
+
+/* Basic types */
+typedef u_char mac_addr[WAVELAN_ADDR_SIZE]; /* Hardware address */
+
+/*
+ * Static specific data for the interface.
+ *
+ * For each network interface, Linux keeps data in two structures: "device"
+ * keeps the generic data (same format for everybody) and "net_local" keeps
+ * additional specific data.
+ * Note that some of this specific data is in fact generic (en_stats, for
+ * example).
+ */
+struct net_local
+{
+ net_local * next; /* linked list of the devices */
+ device * dev; /* reverse link */
+ spinlock_t spinlock; /* Serialize access to the hardware (SMP) */
+ en_stats stats; /* Ethernet interface statistics */
+ int nresets; /* number of hardware resets */
+ u_char reconfig_82586; /* We need to reconfigure the controller. */
+ u_char promiscuous; /* promiscuous mode */
+ int mc_count; /* number of multicast addresses */
+ u_short hacr; /* current host interface state */
+
+ int tx_n_in_use;
+ u_short rx_head;
+ u_short rx_last;
+ u_short tx_first_free;
+ u_short tx_first_in_use;
+
+#ifdef WIRELESS_EXT
+ iw_stats wstats; /* Wireless-specific statistics */
+#endif
+
+#ifdef WIRELESS_SPY
+ int spy_number; /* number of addresses to spy */
+ mac_addr spy_address[IW_MAX_SPY]; /* the addresses to spy */
+ iw_qual spy_stat[IW_MAX_SPY]; /* statistics gathered */
+#endif /* WIRELESS_SPY */
+
+#ifdef HISTOGRAM
+ int his_number; /* number of intervals */
+ u_char his_range[16]; /* boundaries of interval ]n-1; n] */
+ u_long his_sum[16]; /* sum in interval */
+#endif /* HISTOGRAM */
+};
+
+/**************************** PROTOTYPES ****************************/
+
+/* ----------------------- MISC. SUBROUTINES ------------------------ */
+static inline void
+ wv_splhi(net_local *, /* Disable interrupts, lock driver */
+ unsigned long *); /* flags */
+static inline void
+ wv_splx(net_local *, /* Enable interrupts, unlock driver */
+ unsigned long *); /* flags */
+static u_char
+ wv_irq_to_psa(int);
+static int
+ wv_psa_to_irq(u_char);
+/* ------------------- HOST ADAPTER SUBROUTINES ------------------- */
+static inline u_short /* data */
+ hasr_read(u_long); /* Read the host interface: base address */
+static inline void
+ hacr_write(u_long, /* Write to host interface: base address */
+ u_short), /* data */
+ hacr_write_slow(u_long,
+ u_short),
+ set_chan_attn(u_long, /* ioaddr */
+ u_short), /* hacr */
+ wv_hacr_reset(u_long), /* ioaddr */
+ wv_16_off(u_long, /* ioaddr */
+ u_short), /* hacr */
+ wv_16_on(u_long, /* ioaddr */
+ u_short), /* hacr */
+ wv_ints_off(device *),
+ wv_ints_on(device *);
+/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
+static void
+ psa_read(u_long, /* Read the Parameter Storage Area. */
+ u_short, /* hacr */
+ int, /* offset in PSA */
+ u_char *, /* buffer to fill */
+ int), /* size to read */
+ psa_write(u_long, /* Write to the PSA. */
+ u_short, /* hacr */
+ int, /* offset in PSA */
+ u_char *, /* buffer in memory */
+ int); /* length of buffer */
+static inline void
+ mmc_out(u_long, /* Write 1 byte to the Modem Manag Control. */
+ u_short,
+ u_char),
+ mmc_write(u_long, /* Write n bytes to the MMC. */
+ u_char,
+ u_char *,
+ int);
+static inline u_char /* Read 1 byte from the MMC. */
+ mmc_in(u_long,
+ u_short);
+static inline void
+ mmc_read(u_long, /* Read n bytes from the MMC. */
+ u_char,
+ u_char *,
+ int),
+ fee_wait(u_long, /* Wait for frequency EEPROM: base address */
+ int, /* base delay to wait for */
+ int); /* time to wait */
+static void
+ fee_read(u_long, /* Read the frequency EEPROM: base address */
+ u_short, /* destination offset */
+ u_short *, /* data buffer */
+ int); /* number of registers */
+/* ---------------------- I82586 SUBROUTINES ----------------------- */
+static /*inline*/ void
+ obram_read(u_long, /* ioaddr */
+ u_short, /* o */
+ u_char *, /* b */
+ int); /* n */
+static inline void
+ obram_write(u_long, /* ioaddr */
+ u_short, /* o */
+ u_char *, /* b */
+ int); /* n */
+static void
+ wv_ack(device *);
+static inline int
+ wv_synchronous_cmd(device *,
+ const char *),
+ wv_config_complete(device *,
+ u_long,
+ net_local *);
+static int
+ wv_complete(device *,
+ u_long,
+ net_local *);
+static inline void
+ wv_82586_reconfig(device *);
+/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */
+#ifdef DEBUG_I82586_SHOW
+static void
+ wv_scb_show(unsigned short);
+#endif
+static inline void
+ wv_init_info(device *); /* display startup info */
+/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
+static en_stats *
+ wavelan_get_stats(device *); /* Give stats /proc/net/dev */
+static void
+ wavelan_set_multicast_list(device *);
+/* ----------------------- PACKET RECEPTION ----------------------- */
+static inline void
+ wv_packet_read(device *, /* Read a packet from a frame. */
+ u_short,
+ int),
+ wv_receive(device *); /* Read all packets waiting. */
+/* --------------------- PACKET TRANSMISSION --------------------- */
+static inline int
+ wv_packet_write(device *, /* Write a packet to the Tx buffer. */
+ void *,
+ short);
+static int
+ wavelan_packet_xmit(struct sk_buff *, /* Send a packet. */
+ device *);
+/* -------------------- HARDWARE CONFIGURATION -------------------- */
+static inline int
+ wv_mmc_init(device *), /* Initialize the modem. */
+ wv_ru_start(device *), /* Start the i82586 receiver unit. */
+ wv_cu_start(device *), /* Start the i82586 command unit. */
+ wv_82586_start(device *); /* Start the i82586. */
+static void
+ wv_82586_config(device *); /* Configure the i82586. */
+static inline void
+ wv_82586_stop(device *);
+static int
+ wv_hw_reset(device *), /* Reset the WaveLAN hardware. */
+ wv_check_ioaddr(u_long, /* ioaddr */
+ u_char *); /* mac address (read) */
+/* ---------------------- INTERRUPT HANDLING ---------------------- */
+static void
+ wavelan_interrupt(int, /* interrupt handler */
+ void *,
+ struct pt_regs *);
+static void
+ wavelan_watchdog(device *); /* transmission watchdog */
+/* ------------------- CONFIGURATION CALLBACKS ------------------- */
+static int
+ wavelan_open(device *), /* Open the device. */
+ wavelan_close(device *), /* Close the device. */
+ wavelan_config(device *); /* Configure one device. */
+extern int
+ wavelan_probe(device *); /* See Space.c. */
+
+/**************************** VARIABLES ****************************/
+
+/*
+ * This is the root of the linked list of WaveLAN drivers
+ * It is use to verify that we don't reuse the same base address
+ * for two different drivers and to clean up when removing the module.
+ */
+static net_local * wavelan_list = (net_local *) NULL;
+
+/*
+ * This table is used to translate the PSA value to IRQ number
+ * and vice versa.
+ */
+static u_char irqvals[] =
+{
+ 0, 0, 0, 0x01,
+ 0x02, 0x04, 0, 0x08,
+ 0, 0, 0x10, 0x20,
+ 0x40, 0, 0, 0x80,
+};
+
+/*
+ * Table of the available I/O addresses (base addresses) for WaveLAN
+ */
+static unsigned short iobase[] =
+{
+#if 0
+ /* Leave out 0x3C0 for now -- seems to clash with some video
+ * controllers.
+ * Leave out the others too -- we will always use 0x390 and leave
+ * 0x300 for the Ethernet device.
+ * Jean II: 0x3E0 is fine as well.
+ */
+ 0x300, 0x390, 0x3E0, 0x3C0
+#endif /* 0 */
+ 0x390, 0x3E0
+};
+
+#ifdef MODULE
+/* Parameters set by insmod */
+static int io[4];
+static int irq[4];
+static char name[4][IFNAMSIZ];
+MODULE_PARM(io, "1-4i");
+MODULE_PARM(irq, "1-4i");
+MODULE_PARM(name, "1-4c" __MODULE_STRING(IFNAMSIZ));
+MODULE_PARM_DESC(io, "WaveLAN I/O base address(es),required");
+MODULE_PARM_DESC(irq, "WaveLAN IRQ number(s)");
+MODULE_PARM_DESC(name, "WaveLAN interface neme(s)");
+#endif /* MODULE */
+
+#endif /* WAVELAN_P_H */
--- /dev/null
+/*
+ * Wavelan Pcmcia driver
+ *
+ * Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyright follow. See wavelan_cs.p.h for details.
+ *
+ * This code is derived from Anthony D. Joseph's code and all the changes here
+ * are also under the original copyright below.
+ *
+ * This code supports version 2.00 of WaveLAN/PCMCIA cards (2.4GHz), and
+ * can work on Linux 2.0.36 with support of David Hinds' PCMCIA Card Services
+ *
+ * Joe Finney (joe@comp.lancs.ac.uk) at Lancaster University in UK added
+ * critical code in the routine to initialize the Modem Management Controller.
+ *
+ * Thanks to Alan Cox and Bruce Janson for their advice.
+ *
+ * -- Yunzhou Li (scip4166@nus.sg)
+ *
+#ifdef WAVELAN_ROAMING
+ * Roaming support added 07/22/98 by Justin Seger (jseger@media.mit.edu)
+ * based on patch by Joe Finney from Lancaster University.
+#endif
+ *
+ * Lucent (formerly AT&T GIS, formerly NCR) WaveLAN PCMCIA card: An
+ * Ethernet-like radio transceiver controlled by an Intel 82593 coprocessor.
+ *
+ * A non-shared memory PCMCIA ethernet driver for linux
+ *
+ * ISA version modified to support PCMCIA by Anthony Joseph (adj@lcs.mit.edu)
+ *
+ *
+ * Joseph O'Sullivan & John Langford (josullvn@cs.cmu.edu & jcl@cs.cmu.edu)
+ *
+ * Apr 2 '98 made changes to bring the i82593 control/int handling in line
+ * with offical specs...
+ *
+ ****************************************************************************
+ * Copyright 1995
+ * Anthony D. Joseph
+ * Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this program
+ * for any purpose and without fee is hereby granted, provided
+ * that this copyright and permission notice appear on all copies
+ * and supporting documentation, the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * program without specific prior permission, and notice be given
+ * in supporting documentation that copying and distribution is
+ * by permission of M.I.T. M.I.T. makes no representations about
+ * the suitability of this software for any purpose. It is pro-
+ * vided "as is" without express or implied warranty.
+ ****************************************************************************
+ *
+ */
+
+#include "wavelan_cs.p.h" /* Private header */
+
+/************************* MISC SUBROUTINES **************************/
+/*
+ * Subroutines which won't fit in one of the following category
+ * (wavelan modem or i82593)
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for disabling interrupts.
+ * (note : inline, so optimised away)
+ */
+static inline void
+wv_splhi(net_local * lp,
+ unsigned long * pflags)
+{
+ spin_lock_irqsave(&lp->spinlock, *pflags);
+ /* Note : above does the cli(); itself */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for re-enabling interrupts.
+ */
+static inline void
+wv_splx(net_local * lp,
+ unsigned long * pflags)
+{
+ spin_unlock_irqrestore(&lp->spinlock, *pflags);
+
+ /* Note : enabling interrupts on the hardware is done in wv_ru_start()
+ * via : outb(OP1_INT_ENABLE, LCCR(base));
+ */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for reporting error to cardservices
+ */
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+}
+
+#ifdef STRUCT_CHECK
+/*------------------------------------------------------------------*/
+/*
+ * Sanity routine to verify the sizes of the various WaveLAN interface
+ * structures.
+ */
+static char *
+wv_structuct_check(void)
+{
+#define SC(t,s,n) if (sizeof(t) != s) return(n);
+
+ SC(psa_t, PSA_SIZE, "psa_t");
+ SC(mmw_t, MMW_SIZE, "mmw_t");
+ SC(mmr_t, MMR_SIZE, "mmr_t");
+
+#undef SC
+
+ return((char *) NULL);
+} /* wv_structuct_check */
+#endif /* STRUCT_CHECK */
+
+/******************* MODEM MANAGEMENT SUBROUTINES *******************/
+/*
+ * Useful subroutines to manage the modem of the wavelan
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read from card's Host Adaptor Status Register.
+ */
+static inline u_char
+hasr_read(u_long base)
+{
+ return(inb(HASR(base)));
+} /* hasr_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register.
+ */
+static inline void
+hacr_write(u_long base,
+ u_char hacr)
+{
+ outb(hacr, HACR(base));
+} /* hacr_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register. Include a delay for
+ * those times when it is needed.
+ */
+static inline void
+hacr_write_slow(u_long base,
+ u_char hacr)
+{
+ hacr_write(base, hacr);
+ /* delay might only be needed sometimes */
+ mdelay(1);
+} /* hacr_write_slow */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read the Parameter Storage Area from the WaveLAN card's memory
+ */
+static void
+psa_read(device * dev,
+ int o, /* offset in PSA */
+ u_char * b, /* buffer to fill */
+ int n) /* size to read */
+{
+ u_char * ptr = ((u_char *)dev->mem_start) + PSA_ADDR + (o << 1);
+
+ while(n-- > 0)
+ {
+ *b++ = readb(ptr);
+ /* Due to a lack of address decode pins, the WaveLAN PCMCIA card
+ * only supports reading even memory addresses. That means the
+ * increment here MUST be two.
+ * Because of that, we can't use memcpy_fromio()...
+ */
+ ptr += 2;
+ }
+} /* psa_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write the Paramter Storage Area to the WaveLAN card's memory
+ */
+static void
+psa_write(device * dev,
+ int o, /* Offset in psa */
+ u_char * b, /* Buffer in memory */
+ int n) /* Length of buffer */
+{
+ u_char * ptr = ((u_char *) dev->mem_start) + PSA_ADDR + (o << 1);
+ int count = 0;
+ ioaddr_t base = dev->base_addr;
+ /* As there seem to have no flag PSA_BUSY as in the ISA model, we are
+ * oblige to verify this address to know when the PSA is ready... */
+ volatile u_char * verify = ((u_char *) dev->mem_start) + PSA_ADDR +
+ (psaoff(0, psa_comp_number) << 1);
+
+ /* Authorize writting to PSA */
+ hacr_write(base, HACR_PWR_STAT | HACR_ROM_WEN);
+
+ while(n-- > 0)
+ {
+ /* write to PSA */
+ writeb(*b++, ptr);
+ ptr += 2;
+
+ /* I don't have the spec, so I don't know what the correct
+ * sequence to write is. This hack seem to work for me... */
+ count = 0;
+ while((readb(verify) != PSA_COMP_PCMCIA_915) && (count++ < 100))
+ mdelay(1);
+ }
+
+ /* Put the host interface back in standard state */
+ hacr_write(base, HACR_DEFAULT);
+} /* psa_write */
+
+#ifdef SET_PSA_CRC
+/*------------------------------------------------------------------*/
+/*
+ * Calculate the PSA CRC
+ * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
+ * NOTE: By specifying a length including the CRC position the
+ * returned value should be zero. (i.e. a correct checksum in the PSA)
+ *
+ * The Windows drivers don't use the CRC, but the AP and the PtP tool
+ * depend on it.
+ */
+static u_short
+psa_crc(unsigned char * psa, /* The PSA */
+ int size) /* Number of short for CRC */
+{
+ int byte_cnt; /* Loop on the PSA */
+ u_short crc_bytes = 0; /* Data in the PSA */
+ int bit_cnt; /* Loop on the bits of the short */
+
+ for(byte_cnt = 0; byte_cnt < size; byte_cnt++ )
+ {
+ crc_bytes ^= psa[byte_cnt]; /* Its an xor */
+
+ for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ )
+ {
+ if(crc_bytes & 0x0001)
+ crc_bytes = (crc_bytes >> 1) ^ 0xA001;
+ else
+ crc_bytes >>= 1 ;
+ }
+ }
+
+ return crc_bytes;
+} /* psa_crc */
+#endif /* SET_PSA_CRC */
+
+/*------------------------------------------------------------------*/
+/*
+ * update the checksum field in the Wavelan's PSA
+ */
+static void
+update_psa_checksum(device * dev)
+{
+#ifdef SET_PSA_CRC
+ psa_t psa;
+ u_short crc;
+
+ /* read the parameter storage area */
+ psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+
+ /* update the checksum */
+ crc = psa_crc((unsigned char *) &psa,
+ sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1])
+ - sizeof(psa.psa_crc_status));
+
+ psa.psa_crc[0] = crc & 0xFF;
+ psa.psa_crc[1] = (crc & 0xFF00) >> 8;
+
+ /* Write it ! */
+ psa_write(dev, (char *)&psa.psa_crc - (char *)&psa,
+ (unsigned char *)&psa.psa_crc, 2);
+
+#ifdef DEBUG_IOCTL_INFO
+ printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
+ dev->name, psa.psa_crc[0], psa.psa_crc[1]);
+
+ /* Check again (luxury !) */
+ crc = psa_crc((unsigned char *) &psa,
+ sizeof(psa) - sizeof(psa.psa_crc_status));
+
+ if(crc != 0)
+ printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name);
+#endif /* DEBUG_IOCTL_INFO */
+#endif /* SET_PSA_CRC */
+} /* update_psa_checksum */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write 1 byte to the MMC.
+ */
+static inline void
+mmc_out(u_long base,
+ u_short o,
+ u_char d)
+{
+ /* Wait for MMC to go idle */
+ while(inb(HASR(base)) & HASR_MMI_BUSY)
+ ;
+
+ outb((u_char)((o << 1) | MMR_MMI_WR), MMR(base));
+ outb(d, MMD(base));
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to write bytes to the Modem Management Controller.
+ * We start by the end because it is the way it should be !
+ */
+static inline void
+mmc_write(u_long base,
+ u_char o,
+ u_char * b,
+ int n)
+{
+ o += n;
+ b += n;
+
+ while(n-- > 0 )
+ mmc_out(base, --o, *(--b));
+} /* mmc_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read 1 byte from the MMC.
+ * Optimised version for 1 byte, avoid using memory...
+ */
+static inline u_char
+mmc_in(u_long base,
+ u_short o)
+{
+ while(inb(HASR(base)) & HASR_MMI_BUSY)
+ ;
+ outb(o << 1, MMR(base)); /* Set the read address */
+
+ outb(0, MMD(base)); /* Required dummy write */
+
+ while(inb(HASR(base)) & HASR_MMI_BUSY)
+ ;
+ return (u_char) (inb(MMD(base))); /* Now do the actual read */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to read bytes from the Modem Management Controller.
+ * The implementation is complicated by a lack of address lines,
+ * which prevents decoding of the low-order bit.
+ * (code has just been moved in the above function)
+ * We start by the end because it is the way it should be !
+ */
+static inline void
+mmc_read(u_long base,
+ u_char o,
+ u_char * b,
+ int n)
+{
+ o += n;
+ b += n;
+
+ while(n-- > 0)
+ *(--b) = mmc_in(base, --o);
+} /* mmc_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the type of encryption available...
+ */
+static inline int
+mmc_encr(u_long base) /* i/o port of the card */
+{
+ int temp;
+
+ temp = mmc_in(base, mmroff(0, mmr_des_avail));
+ if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
+ return 0;
+ else
+ return temp;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wait for the frequency EEprom to complete a command...
+ * I hope this one will be optimally inlined...
+ */
+static inline void
+fee_wait(u_long base, /* i/o port of the card */
+ int delay, /* Base delay to wait for */
+ int number) /* Number of time to wait */
+{
+ int count = 0; /* Wait only a limited time */
+
+ while((count++ < number) &&
+ (mmc_in(base, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY))
+ udelay(delay);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the Frequency EEprom (frequency select cards).
+ */
+static void
+fee_read(u_long base, /* i/o port of the card */
+ u_short o, /* destination offset */
+ u_short * b, /* data buffer */
+ int n) /* number of registers */
+{
+ b += n; /* Position at the end of the area */
+
+ /* Write the address */
+ mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+ /* Loop on all buffer */
+ while(n-- > 0)
+ {
+ /* Write the read command */
+ mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ);
+
+ /* Wait until EEprom is ready (should be quick !) */
+ fee_wait(base, 10, 100);
+
+ /* Read the value */
+ *--b = ((mmc_in(base, mmroff(0, mmr_fee_data_h)) << 8) |
+ mmc_in(base, mmroff(0, mmr_fee_data_l)));
+ }
+}
+
+#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes from the Frequency EEprom (frequency select cards).
+ * This is a bit complicated, because the frequency eeprom has to
+ * be unprotected and the write enabled.
+ * Jean II
+ */
+static void
+fee_write(u_long base, /* i/o port of the card */
+ u_short o, /* destination offset */
+ u_short * b, /* data buffer */
+ int n) /* number of registers */
+{
+ b += n; /* Position at the end of the area */
+
+#ifdef EEPROM_IS_PROTECTED /* disabled */
+#ifdef DOESNT_SEEM_TO_WORK /* disabled */
+ /* Ask to read the protected register */
+ mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
+
+ fee_wait(base, 10, 100);
+
+ /* Read the protected register */
+ printk("Protected 2 : %02X-%02X\n",
+ mmc_in(base, mmroff(0, mmr_fee_data_h)),
+ mmc_in(base, mmroff(0, mmr_fee_data_l)));
+#endif /* DOESNT_SEEM_TO_WORK */
+
+ /* Enable protected register */
+ mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+ mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
+
+ fee_wait(base, 10, 100);
+
+ /* Unprotect area */
+ mmc_out(base, mmwoff(0, mmw_fee_addr), o + n);
+ mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+#ifdef DOESNT_SEEM_TO_WORK /* disabled */
+ /* Or use : */
+ mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
+#endif /* DOESNT_SEEM_TO_WORK */
+
+ fee_wait(base, 10, 100);
+#endif /* EEPROM_IS_PROTECTED */
+
+ /* Write enable */
+ mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+ mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
+
+ fee_wait(base, 10, 100);
+
+ /* Write the EEprom address */
+ mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+ /* Loop on all buffer */
+ while(n-- > 0)
+ {
+ /* Write the value */
+ mmc_out(base, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
+ mmc_out(base, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
+
+ /* Write the write command */
+ mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE);
+
+ /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */
+ mdelay(10);
+ fee_wait(base, 10, 100);
+ }
+
+ /* Write disable */
+ mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
+ mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
+
+ fee_wait(base, 10, 100);
+
+#ifdef EEPROM_IS_PROTECTED /* disabled */
+ /* Reprotect EEprom */
+ mmc_out(base, mmwoff(0, mmw_fee_addr), 0x00);
+ mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+
+ fee_wait(base, 10, 100);
+#endif /* EEPROM_IS_PROTECTED */
+}
+#endif /* WIRELESS_EXT */
+
+/******************* WaveLAN Roaming routines... ********************/
+
+#ifdef WAVELAN_ROAMING /* Conditional compile, see wavelan_cs.h */
+
+unsigned char WAVELAN_BEACON_ADDRESS[]= {0x09,0x00,0x0e,0x20,0x03,0x00};
+
+void wv_roam_init(struct net_device *dev)
+{
+ net_local *lp= (net_local *)dev->priv;
+
+ /* Do not remove this unless you have a good reason */
+ printk(KERN_NOTICE "%s: Warning, you have enabled roaming on"
+ " device %s !\n", dev->name, dev->name);
+ printk(KERN_NOTICE "Roaming is currently an experimental unsupported feature"
+ " of the Wavelan driver.\n");
+ printk(KERN_NOTICE "It may work, but may also make the driver behave in"
+ " erratic ways or crash.\n");
+
+ lp->wavepoint_table.head=NULL; /* Initialise WavePoint table */
+ lp->wavepoint_table.num_wavepoints=0;
+ lp->wavepoint_table.locked=0;
+ lp->curr_point=NULL; /* No default WavePoint */
+ lp->cell_search=0;
+
+ lp->cell_timer.data=(long)lp; /* Start cell expiry timer */
+ lp->cell_timer.function=wl_cell_expiry;
+ lp->cell_timer.expires=jiffies+CELL_TIMEOUT;
+ add_timer(&lp->cell_timer);
+
+ wv_nwid_filter(NWID_PROMISC,lp) ; /* Enter NWID promiscuous mode */
+ /* to build up a good WavePoint */
+ /* table... */
+ printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name);
+}
+
+void wv_roam_cleanup(struct net_device *dev)
+{
+ wavepoint_history *ptr,*old_ptr;
+ net_local *lp= (net_local *)dev->priv;
+
+ printk(KERN_DEBUG "WaveLAN: Roaming Disabled on device %s\n",dev->name);
+
+ /* Fixme : maybe we should check that the timer exist before deleting it */
+ del_timer(&lp->cell_timer); /* Remove cell expiry timer */
+ ptr=lp->wavepoint_table.head; /* Clear device's WavePoint table */
+ while(ptr!=NULL)
+ {
+ old_ptr=ptr;
+ ptr=ptr->next;
+ wl_del_wavepoint(old_ptr,lp);
+ }
+}
+
+/* Enable/Disable NWID promiscuous mode on a given device */
+void wv_nwid_filter(unsigned char mode, net_local *lp)
+{
+ mm_t m;
+ unsigned long flags;
+
+#ifdef WAVELAN_ROAMING_DEBUG
+ printk(KERN_DEBUG "WaveLAN: NWID promisc %s, device %s\n",(mode==NWID_PROMISC) ? "on" : "off", lp->dev->name);
+#endif
+
+ /* Disable interrupts & save flags */
+ wv_splhi(lp, &flags);
+
+ m.w.mmw_loopt_sel = (mode==NWID_PROMISC) ? MMW_LOOPT_SEL_DIS_NWID : 0x00;
+ mmc_write(lp->dev->base_addr, (char *)&m.w.mmw_loopt_sel - (char *)&m, (unsigned char *)&m.w.mmw_loopt_sel, 1);
+
+ if(mode==NWID_PROMISC)
+ lp->cell_search=1;
+ else
+ lp->cell_search=0;
+
+ /* ReEnable interrupts & restore flags */
+ wv_splx(lp, &flags);
+}
+
+/* Find a record in the WavePoint table matching a given NWID */
+wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
+{
+ wavepoint_history *ptr=lp->wavepoint_table.head;
+
+ while(ptr!=NULL){
+ if(ptr->nwid==nwid)
+ return ptr;
+ ptr=ptr->next;
+ }
+ return NULL;
+}
+
+/* Create a new wavepoint table entry */
+wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp)
+{
+ wavepoint_history *new_wavepoint;
+
+#ifdef WAVELAN_ROAMING_DEBUG
+ printk(KERN_DEBUG "WaveLAN: New Wavepoint, NWID:%.4X\n",nwid);
+#endif
+
+ if(lp->wavepoint_table.num_wavepoints==MAX_WAVEPOINTS)
+ return NULL;
+
+ new_wavepoint=(wavepoint_history *) kmalloc(sizeof(wavepoint_history),GFP_ATOMIC);
+ if(new_wavepoint==NULL)
+ return NULL;
+
+ new_wavepoint->nwid=nwid; /* New WavePoints NWID */
+ new_wavepoint->average_fast=0; /* Running Averages..*/
+ new_wavepoint->average_slow=0;
+ new_wavepoint->qualptr=0; /* Start of ringbuffer */
+ new_wavepoint->last_seq=seq-1; /* Last sequence no.seen */
+ memset(new_wavepoint->sigqual,0,WAVEPOINT_HISTORY);/* Empty ringbuffer */
+
+ new_wavepoint->next=lp->wavepoint_table.head;/* Add to wavepoint table */
+ new_wavepoint->prev=NULL;
+
+ if(lp->wavepoint_table.head!=NULL)
+ lp->wavepoint_table.head->prev=new_wavepoint;
+
+ lp->wavepoint_table.head=new_wavepoint;
+
+ lp->wavepoint_table.num_wavepoints++; /* no. of visible wavepoints */
+
+ return new_wavepoint;
+}
+
+/* Remove a wavepoint entry from WavePoint table */
+void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
+{
+ if(wavepoint==NULL)
+ return;
+
+ if(lp->curr_point==wavepoint)
+ lp->curr_point=NULL;
+
+ if(wavepoint->prev!=NULL)
+ wavepoint->prev->next=wavepoint->next;
+
+ if(wavepoint->next!=NULL)
+ wavepoint->next->prev=wavepoint->prev;
+
+ if(lp->wavepoint_table.head==wavepoint)
+ lp->wavepoint_table.head=wavepoint->next;
+
+ lp->wavepoint_table.num_wavepoints--;
+ kfree(wavepoint);
+}
+
+/* Timer callback function - checks WavePoint table for stale entries */
+void wl_cell_expiry(unsigned long data)
+{
+ net_local *lp=(net_local *)data;
+ wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point;
+
+#if WAVELAN_ROAMING_DEBUG > 1
+ printk(KERN_DEBUG "WaveLAN: Wavepoint timeout, dev %s\n",lp->dev->name);
+#endif
+
+ if(lp->wavepoint_table.locked)
+ {
+#if WAVELAN_ROAMING_DEBUG > 1
+ printk(KERN_DEBUG "WaveLAN: Wavepoint table locked...\n");
+#endif
+
+ lp->cell_timer.expires=jiffies+1; /* If table in use, come back later */
+ add_timer(&lp->cell_timer);
+ return;
+ }
+
+ while(wavepoint!=NULL)
+ {
+ if(wavepoint->last_seen < jiffies-CELL_TIMEOUT)
+ {
+#ifdef WAVELAN_ROAMING_DEBUG
+ printk(KERN_DEBUG "WaveLAN: Bye bye %.4X\n",wavepoint->nwid);
+#endif
+
+ old_point=wavepoint;
+ wavepoint=wavepoint->next;
+ wl_del_wavepoint(old_point,lp);
+ }
+ else
+ wavepoint=wavepoint->next;
+ }
+ lp->cell_timer.expires=jiffies+CELL_TIMEOUT;
+ add_timer(&lp->cell_timer);
+}
+
+/* Update SNR history of a wavepoint */
+void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq)
+{
+ int i=0,num_missed=0,ptr=0;
+ int average_fast=0,average_slow=0;
+
+ num_missed=(seq-wavepoint->last_seq)%WAVEPOINT_HISTORY;/* Have we missed
+ any beacons? */
+ if(num_missed)
+ for(i=0;i<num_missed;i++)
+ {
+ wavepoint->sigqual[wavepoint->qualptr++]=0; /* If so, enter them as 0's */
+ wavepoint->qualptr %=WAVEPOINT_HISTORY; /* in the ringbuffer. */
+ }
+ wavepoint->last_seen=jiffies; /* Add beacon to history */
+ wavepoint->last_seq=seq;
+ wavepoint->sigqual[wavepoint->qualptr++]=sigqual;
+ wavepoint->qualptr %=WAVEPOINT_HISTORY;
+ ptr=(wavepoint->qualptr-WAVEPOINT_FAST_HISTORY+WAVEPOINT_HISTORY)%WAVEPOINT_HISTORY;
+
+ for(i=0;i<WAVEPOINT_FAST_HISTORY;i++) /* Update running averages */
+ {
+ average_fast+=wavepoint->sigqual[ptr++];
+ ptr %=WAVEPOINT_HISTORY;
+ }
+
+ average_slow=average_fast;
+ for(i=WAVEPOINT_FAST_HISTORY;i<WAVEPOINT_HISTORY;i++)
+ {
+ average_slow+=wavepoint->sigqual[ptr++];
+ ptr %=WAVEPOINT_HISTORY;
+ }
+
+ wavepoint->average_fast=average_fast/WAVEPOINT_FAST_HISTORY;
+ wavepoint->average_slow=average_slow/WAVEPOINT_HISTORY;
+}
+
+/* Perform a handover to a new WavePoint */
+void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
+{
+ ioaddr_t base = lp->dev->base_addr;
+ mm_t m;
+ unsigned long flags;
+
+ if(wavepoint==lp->curr_point) /* Sanity check... */
+ {
+ wv_nwid_filter(!NWID_PROMISC,lp);
+ return;
+ }
+
+#ifdef WAVELAN_ROAMING_DEBUG
+ printk(KERN_DEBUG "WaveLAN: Doing handover to %.4X, dev %s\n",wavepoint->nwid,lp->dev->name);
+#endif
+
+ /* Disable interrupts & save flags */
+ wv_splhi(lp, &flags);
+
+ m.w.mmw_netw_id_l = wavepoint->nwid & 0xFF;
+ m.w.mmw_netw_id_h = (wavepoint->nwid & 0xFF00) >> 8;
+
+ mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2);
+
+ /* ReEnable interrupts & restore flags */
+ wv_splx(lp, &flags);
+
+ wv_nwid_filter(!NWID_PROMISC,lp);
+ lp->curr_point=wavepoint;
+}
+
+/* Called when a WavePoint beacon is received */
+static inline void wl_roam_gather(device * dev,
+ u_char * hdr, /* Beacon header */
+ u_char * stats) /* SNR, Signal quality
+ of packet */
+{
+ wavepoint_beacon *beacon= (wavepoint_beacon *)hdr; /* Rcvd. Beacon */
+ unsigned short nwid=ntohs(beacon->nwid);
+ unsigned short sigqual=stats[2] & MMR_SGNL_QUAL; /* SNR of beacon */
+ wavepoint_history *wavepoint=NULL; /* WavePoint table entry */
+ net_local *lp=(net_local *)dev->priv; /* Device info */
+
+#if 0
+ /* Some people don't need this, some other may need it */
+ nwid=nwid^ntohs(beacon->domain_id);
+#endif
+
+#if WAVELAN_ROAMING_DEBUG > 1
+ printk(KERN_DEBUG "WaveLAN: beacon, dev %s:\n",dev->name);
+ printk(KERN_DEBUG "Domain: %.4X NWID: %.4X SigQual=%d\n",ntohs(beacon->domain_id),nwid,sigqual);
+#endif
+
+ lp->wavepoint_table.locked=1; /* <Mutex> */
+
+ wavepoint=wl_roam_check(nwid,lp); /* Find WavePoint table entry */
+ if(wavepoint==NULL) /* If no entry, Create a new one... */
+ {
+ wavepoint=wl_new_wavepoint(nwid,beacon->seq,lp);
+ if(wavepoint==NULL)
+ goto out;
+ }
+ if(lp->curr_point==NULL) /* If this is the only WavePoint, */
+ wv_roam_handover(wavepoint, lp); /* Jump on it! */
+
+ wl_update_history(wavepoint, sigqual, beacon->seq); /* Update SNR history
+ stats. */
+
+ if(lp->curr_point->average_slow < SEARCH_THRESH_LOW) /* If our current */
+ if(!lp->cell_search) /* WavePoint is getting faint, */
+ wv_nwid_filter(NWID_PROMISC,lp); /* start looking for a new one */
+
+ if(wavepoint->average_slow >
+ lp->curr_point->average_slow + WAVELAN_ROAMING_DELTA)
+ wv_roam_handover(wavepoint, lp); /* Handover to a better WavePoint */
+
+ if(lp->curr_point->average_slow > SEARCH_THRESH_HIGH) /* If our SNR is */
+ if(lp->cell_search) /* getting better, drop out of cell search mode */
+ wv_nwid_filter(!NWID_PROMISC,lp);
+
+out:
+ lp->wavepoint_table.locked=0; /* </MUTEX> :-) */
+}
+
+/* Test this MAC frame a WavePoint beacon */
+static inline int WAVELAN_BEACON(unsigned char *data)
+{
+ wavepoint_beacon *beacon= (wavepoint_beacon *)data;
+ static wavepoint_beacon beacon_template={0xaa,0xaa,0x03,0x08,0x00,0x0e,0x20,0x03,0x00};
+
+ if(memcmp(beacon,&beacon_template,9)==0)
+ return 1;
+ else
+ return 0;
+}
+#endif /* WAVELAN_ROAMING */
+
+/************************ I82593 SUBROUTINES *************************/
+/*
+ * Useful subroutines to manage the Ethernet controller
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to synchronously send a command to the i82593 chip.
+ * Should be called with interrupts disabled.
+ * (called by wv_packet_write(), wv_ru_stop(), wv_ru_start(),
+ * wv_82593_config() & wv_diag())
+ */
+static int
+wv_82593_cmd(device * dev,
+ char * str,
+ int cmd,
+ int result)
+{
+ ioaddr_t base = dev->base_addr;
+ int status;
+ int wait_completed;
+ long spin;
+
+ /* Spin until the chip finishes executing its current command (if any) */
+ spin = 1000;
+ do
+ {
+ /* Time calibration of the loop */
+ udelay(10);
+
+ /* Read the interrupt register */
+ outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+ status = inb(LCSR(base));
+ }
+ while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0));
+
+ /* If the interrupt hasn't be posted */
+ if(spin <= 0)
+ {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO "wv_82593_cmd: %s timeout (previous command), status 0x%02x\n",
+ str, status);
+#endif
+ return(FALSE);
+ }
+
+ /* Issue the command to the controller */
+ outb(cmd, LCCR(base));
+
+ /* If we don't have to check the result of the command
+ * Note : this mean that the irq handler will deal with that */
+ if(result == SR0_NO_RESULT)
+ return(TRUE);
+
+ /* We are waiting for command completion */
+ wait_completed = TRUE;
+
+ /* Busy wait while the LAN controller executes the command. */
+ spin = 1000;
+ do
+ {
+ /* Time calibration of the loop */
+ udelay(10);
+
+ /* Read the interrupt register */
+ outb(CR0_STATUS_0 | OP0_NOP, LCCR(base));
+ status = inb(LCSR(base));
+
+ /* Check if there was an interrupt posted */
+ if((status & SR0_INTERRUPT))
+ {
+ /* Acknowledge the interrupt */
+ outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
+
+ /* Check if interrupt is a command completion */
+ if(((status & SR0_BOTH_RX_TX) != SR0_BOTH_RX_TX) &&
+ ((status & SR0_BOTH_RX_TX) != 0x0) &&
+ !(status & SR0_RECEPTION))
+ {
+ /* Signal command completion */
+ wait_completed = FALSE;
+ }
+ else
+ {
+ /* Note : Rx interrupts will be handled later, because we can
+ * handle multiple Rx packets at once */
+#ifdef DEBUG_INTERRUPT_INFO
+ printk(KERN_INFO "wv_82593_cmd: not our interrupt\n");
+#endif
+ }
+ }
+ }
+ while(wait_completed && (spin-- > 0));
+
+ /* If the interrupt hasn't be posted */
+ if(wait_completed)
+ {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO "wv_82593_cmd: %s timeout, status 0x%02x\n",
+ str, status);
+#endif
+ return(FALSE);
+ }
+
+ /* Check the return code returned by the card (see above) against
+ * the expected return code provided by the caller */
+ if((status & SR0_EVENT_MASK) != result)
+ {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO "wv_82593_cmd: %s failed, status = 0x%x\n",
+ str, status);
+#endif
+ return(FALSE);
+ }
+
+ return(TRUE);
+} /* wv_82593_cmd */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a 593 op-code number 7, and obtains the diagnose
+ * status for the WaveLAN.
+ */
+static inline int
+wv_diag(device * dev)
+{
+ int ret = FALSE;
+
+ if(wv_82593_cmd(dev, "wv_diag(): diagnose",
+ OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED))
+ ret = TRUE;
+
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO "wavelan_cs: i82593 Self Test failed!\n");
+#endif
+ return(ret);
+} /* wv_diag */
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to read len bytes from the i82593's ring buffer, starting at
+ * chip address addr. The results read from the chip are stored in buf.
+ * The return value is the address to use for next the call.
+ */
+static int
+read_ringbuf(device * dev,
+ int addr,
+ char * buf,
+ int len)
+{
+ ioaddr_t base = dev->base_addr;
+ int ring_ptr = addr;
+ int chunk_len;
+ char * buf_ptr = buf;
+
+ /* Get all the buffer */
+ while(len > 0)
+ {
+ /* Position the Program I/O Register at the ring buffer pointer */
+ outb(ring_ptr & 0xff, PIORL(base));
+ outb(((ring_ptr >> 8) & PIORH_MASK), PIORH(base));
+
+ /* First, determine how much we can read without wrapping around the
+ ring buffer */
+ if((addr + len) < (RX_BASE + RX_SIZE))
+ chunk_len = len;
+ else
+ chunk_len = RX_BASE + RX_SIZE - addr;
+ insb(PIOP(base), buf_ptr, chunk_len);
+ buf_ptr += chunk_len;
+ len -= chunk_len;
+ ring_ptr = (ring_ptr - RX_BASE + chunk_len) % RX_SIZE + RX_BASE;
+ }
+ return(ring_ptr);
+} /* read_ringbuf */
+
+/*------------------------------------------------------------------*/
+/*
+ * Reconfigure the i82593, or at least ask for it...
+ * Because wv_82593_config use the transmission buffer, we must do it
+ * when we are sure that there is no transmission, so we do it now
+ * or in wavelan_packet_xmit() (I can't find any better place,
+ * wavelan_interrupt is not an option...), so you may experience
+ * some delay sometime...
+ */
+static inline void
+wv_82593_reconfig(device * dev)
+{
+ net_local * lp = (net_local *)dev->priv;
+ dev_link_t * link = ((net_local *) dev->priv)->link;
+ unsigned long flags;
+
+ /* Arm the flag, will be cleard in wv_82593_config() */
+ lp->reconfig_82593 = TRUE;
+
+ /* Check if we can do it now ! */
+ if((link->open) && (netif_running(dev)) && !(netif_queue_stopped(dev)))
+ {
+ wv_splhi(lp, &flags); /* Disable interrupts */
+ wv_82593_config(dev);
+ wv_splx(lp, &flags); /* Re-enable interrupts */
+ }
+ else
+ {
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG
+ "%s: wv_82593_reconfig(): delayed (state = %lX, link = %d)\n",
+ dev->name, dev->state, link->open);
+#endif
+ }
+}
+
+/********************* DEBUG & INFO SUBROUTINES *********************/
+/*
+ * This routines are used in the code to show debug informations.
+ * Most of the time, it dump the content of hardware structures...
+ */
+
+#ifdef DEBUG_PSA_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted contents of the Parameter Storage Area.
+ */
+static void
+wv_psa_show(psa_t * p)
+{
+ printk(KERN_DEBUG "##### wavelan psa contents: #####\n");
+ printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
+ p->psa_io_base_addr_1,
+ p->psa_io_base_addr_2,
+ p->psa_io_base_addr_3,
+ p->psa_io_base_addr_4);
+ printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
+ p->psa_rem_boot_addr_1,
+ p->psa_rem_boot_addr_2,
+ p->psa_rem_boot_addr_3);
+ printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
+ printk("psa_int_req_no: %d\n", p->psa_int_req_no);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ p->psa_unused0[0],
+ p->psa_unused0[1],
+ p->psa_unused0[2],
+ p->psa_unused0[3],
+ p->psa_unused0[4],
+ p->psa_unused0[5],
+ p->psa_unused0[6]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ p->psa_univ_mac_addr[0],
+ p->psa_univ_mac_addr[1],
+ p->psa_univ_mac_addr[2],
+ p->psa_univ_mac_addr[3],
+ p->psa_univ_mac_addr[4],
+ p->psa_univ_mac_addr[5]);
+ printk(KERN_DEBUG "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ p->psa_local_mac_addr[0],
+ p->psa_local_mac_addr[1],
+ p->psa_local_mac_addr[2],
+ p->psa_local_mac_addr[3],
+ p->psa_local_mac_addr[4],
+ p->psa_local_mac_addr[5]);
+ printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel);
+ printk("psa_comp_number: %d, ", p->psa_comp_number);
+ printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
+ printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
+ p->psa_feature_select);
+ printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
+ printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
+ printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
+ printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], p->psa_nwid[1]);
+ printk("psa_nwid_select: %d\n", p->psa_nwid_select);
+ printk(KERN_DEBUG "psa_encryption_select: %d, ", p->psa_encryption_select);
+ printk("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ p->psa_encryption_key[0],
+ p->psa_encryption_key[1],
+ p->psa_encryption_key[2],
+ p->psa_encryption_key[3],
+ p->psa_encryption_key[4],
+ p->psa_encryption_key[5],
+ p->psa_encryption_key[6],
+ p->psa_encryption_key[7]);
+ printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
+ printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
+ p->psa_call_code[0]);
+ printk("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ p->psa_call_code[0],
+ p->psa_call_code[1],
+ p->psa_call_code[2],
+ p->psa_call_code[3],
+ p->psa_call_code[4],
+ p->psa_call_code[5],
+ p->psa_call_code[6],
+ p->psa_call_code[7]);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
+ p->psa_reserved[0],
+ p->psa_reserved[1],
+ p->psa_reserved[2],
+ p->psa_reserved[3]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
+ printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
+ printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
+} /* wv_psa_show */
+#endif /* DEBUG_PSA_SHOW */
+
+#ifdef DEBUG_MMC_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the Modem Management Controller.
+ * This function need to be completed...
+ */
+static void
+wv_mmc_show(device * dev)
+{
+ ioaddr_t base = dev->base_addr;
+ net_local * lp = (net_local *)dev->priv;
+ mmr_t m;
+
+ /* Basic check */
+ if(hasr_read(base) & HASR_NO_CLK)
+ {
+ printk(KERN_WARNING "%s: wv_mmc_show: modem not connected\n",
+ dev->name);
+ return;
+ }
+
+ wv_splhi(lp, &flags);
+
+ /* Read the mmc */
+ mmc_out(base, mmwoff(0, mmw_freeze), 1);
+ mmc_read(base, 0, (u_char *)&m, sizeof(m));
+ mmc_out(base, mmwoff(0, mmw_freeze), 0);
+
+#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */
+ /* Don't forget to update statistics */
+ lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+#endif /* WIRELESS_EXT */
+
+ wv_splx(lp, &flags);
+
+ printk(KERN_DEBUG "##### wavelan modem status registers: #####\n");
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ m.mmr_unused0[0],
+ m.mmr_unused0[1],
+ m.mmr_unused0[2],
+ m.mmr_unused0[3],
+ m.mmr_unused0[4],
+ m.mmr_unused0[5],
+ m.mmr_unused0[6],
+ m.mmr_unused0[7]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "Encryption algorythm: %02X - Status: %02X\n",
+ m.mmr_des_avail, m.mmr_des_status);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
+ m.mmr_unused1[0],
+ m.mmr_unused1[1],
+ m.mmr_unused1[2],
+ m.mmr_unused1[3],
+ m.mmr_unused1[4]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
+ m.mmr_dce_status,
+ (m.mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? "energy detected,":"",
+ (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
+ "loop test indicated," : "",
+ (m.mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? "transmitter on," : "",
+ (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
+ "jabber timer expired," : "");
+ printk(KERN_DEBUG "Dsp ID: %02X\n",
+ m.mmr_dsp_id);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
+ m.mmr_unused2[0],
+ m.mmr_unused2[1]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
+ (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
+ (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
+ printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
+ m.mmr_thr_pre_set & MMR_THR_PRE_SET,
+ (m.mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : "below");
+ printk(KERN_DEBUG "signal_lvl: %d [%s], ",
+ m.mmr_signal_lvl & MMR_SIGNAL_LVL,
+ (m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : "no new msg");
+ printk("silence_lvl: %d [%s], ", m.mmr_silence_lvl & MMR_SILENCE_LVL,
+ (m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : "no new update");
+ printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL,
+ (m.mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : "Antenna 0");
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
+#endif /* DEBUG_SHOW_UNUSED */
+} /* wv_mmc_show */
+#endif /* DEBUG_MMC_SHOW */
+
+#ifdef DEBUG_I82593_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the i82593's receive unit.
+ */
+static void
+wv_ru_show(device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+
+ printk(KERN_DEBUG "##### wavelan i82593 receiver status: #####\n");
+ printk(KERN_DEBUG "ru: rfp %d stop %d", lp->rfp, lp->stop);
+ /*
+ * Not implemented yet...
+ */
+ printk("\n");
+} /* wv_ru_show */
+#endif /* DEBUG_I82593_SHOW */
+
+#ifdef DEBUG_DEVICE_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver.
+ */
+static void
+wv_dev_show(device * dev)
+{
+ printk(KERN_DEBUG "dev:");
+ printk(" state=%lX,", dev->state);
+ printk(" trans_start=%ld,", dev->trans_start);
+ printk(" flags=0x%x,", dev->flags);
+ printk("\n");
+} /* wv_dev_show */
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver's
+ * private information.
+ */
+static void
+wv_local_show(device * dev)
+{
+ net_local *lp;
+
+ lp = (net_local *)dev->priv;
+
+ printk(KERN_DEBUG "local:");
+ /*
+ * Not implemented yet...
+ */
+ printk("\n");
+} /* wv_local_show */
+#endif /* DEBUG_DEVICE_SHOW */
+
+#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO)
+/*------------------------------------------------------------------*/
+/*
+ * Dump packet header (and content if necessary) on the screen
+ */
+static inline void
+wv_packet_info(u_char * p, /* Packet to dump */
+ int length, /* Length of the packet */
+ char * msg1, /* Name of the device */
+ char * msg2) /* Name of the function */
+{
+ int i;
+ int maxi;
+
+ printk(KERN_DEBUG "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n",
+ msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length);
+ printk(KERN_DEBUG "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n",
+ msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13]);
+
+#ifdef DEBUG_PACKET_DUMP
+
+ printk(KERN_DEBUG "data=\"");
+
+ if((maxi = length) > DEBUG_PACKET_DUMP)
+ maxi = DEBUG_PACKET_DUMP;
+ for(i = 14; i < maxi; i++)
+ if(p[i] >= ' ' && p[i] <= '~')
+ printk(" %c", p[i]);
+ else
+ printk("%02X", p[i]);
+ if(maxi < length)
+ printk("..");
+ printk("\"\n");
+ printk(KERN_DEBUG "\n");
+#endif /* DEBUG_PACKET_DUMP */
+}
+#endif /* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */
+
+/*------------------------------------------------------------------*/
+/*
+ * This is the information which is displayed by the driver at startup
+ * There is a lot of flag to configure it at your will...
+ */
+static inline void
+wv_init_info(device * dev)
+{
+ ioaddr_t base = dev->base_addr;
+ psa_t psa;
+ int i;
+
+ /* Read the parameter storage area */
+ psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+
+#ifdef DEBUG_PSA_SHOW
+ wv_psa_show(&psa);
+#endif
+#ifdef DEBUG_MMC_SHOW
+ wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82593_SHOW
+ wv_ru_show(dev);
+#endif
+
+#ifdef DEBUG_BASIC_SHOW
+ /* Now, let's go for the basic stuff */
+ printk(KERN_NOTICE "%s: WaveLAN: port %#x, irq %d, hw_addr",
+ dev->name, base, dev->irq);
+ for(i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]);
+
+ /* Print current network id */
+ if(psa.psa_nwid_select)
+ printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], psa.psa_nwid[1]);
+ else
+ printk(", nwid off");
+
+ /* If 2.00 card */
+ if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+ (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+ {
+ unsigned short freq;
+
+ /* Ask the EEprom to read the frequency from the first area */
+ fee_read(base, 0x00 /* 1st area - frequency... */,
+ &freq, 1);
+
+ /* Print frequency */
+ printk(", 2.00, %ld", (freq >> 6) + 2400L);
+
+ /* Hack !!! */
+ if(freq & 0x20)
+ printk(".5");
+ }
+ else
+ {
+ printk(", PCMCIA, ");
+ switch (psa.psa_subband)
+ {
+ case PSA_SUBBAND_915:
+ printk("915");
+ break;
+ case PSA_SUBBAND_2425:
+ printk("2425");
+ break;
+ case PSA_SUBBAND_2460:
+ printk("2460");
+ break;
+ case PSA_SUBBAND_2484:
+ printk("2484");
+ break;
+ case PSA_SUBBAND_2430_5:
+ printk("2430.5");
+ break;
+ default:
+ printk("???");
+ }
+ }
+
+ printk(" MHz\n");
+#endif /* DEBUG_BASIC_SHOW */
+
+#ifdef DEBUG_VERSION_SHOW
+ /* Print version information */
+ printk(KERN_NOTICE "%s", version);
+#endif
+} /* wv_init_info */
+
+/********************* IOCTL, STATS & RECONFIG *********************/
+/*
+ * We found here routines that are called by Linux on differents
+ * occasions after the configuration and not for transmitting data
+ * These may be called when the user use ifconfig, /proc/net/dev
+ * or wireless extensions
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the current ethernet statistics. This may be called with the
+ * card open or closed.
+ * Used when the user read /proc/net/dev
+ */
+static en_stats *
+wavelan_get_stats(device * dev)
+{
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
+#endif
+
+ return(&((net_local *) dev->priv)->stats);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, clear multicast list
+ * num_addrs > 0 Multicast mode, receive normal and MC packets,
+ * and do best-effort filtering.
+ */
+
+static void
+wavelan_set_multicast_list(device * dev)
+{
+ net_local * lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name);
+#endif
+
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n",
+ dev->name, dev->flags, dev->mc_count);
+#endif
+
+ if(dev->flags & IFF_PROMISC)
+ {
+ /*
+ * Enable promiscuous mode: receive all packets.
+ */
+ if(!lp->promiscuous)
+ {
+ lp->promiscuous = 1;
+ lp->allmulticast = 0;
+ lp->mc_count = 0;
+
+ wv_82593_reconfig(dev);
+
+ /* Tell the kernel that we are doing a really bad job... */
+ dev->flags |= IFF_PROMISC;
+ }
+ }
+ else
+ /* If all multicast addresses
+ * or too much multicast addresses for the hardware filter */
+ if((dev->flags & IFF_ALLMULTI) ||
+ (dev->mc_count > I82593_MAX_MULTICAST_ADDRESSES))
+ {
+ /*
+ * Disable promiscuous mode, but active the all multicast mode
+ */
+ if(!lp->allmulticast)
+ {
+ lp->promiscuous = 0;
+ lp->allmulticast = 1;
+ lp->mc_count = 0;
+
+ wv_82593_reconfig(dev);
+
+ /* Tell the kernel that we are doing a really bad job... */
+ dev->flags |= IFF_ALLMULTI;
+ }
+ }
+ else
+ /* If there is some multicast addresses to send */
+ if(dev->mc_list != (struct dev_mc_list *) NULL)
+ {
+ /*
+ * Disable promiscuous mode, but receive all packets
+ * in multicast list
+ */
+#ifdef MULTICAST_AVOID
+ if(lp->promiscuous || lp->allmulticast ||
+ (dev->mc_count != lp->mc_count))
+#endif
+ {
+ lp->promiscuous = 0;
+ lp->allmulticast = 0;
+ lp->mc_count = dev->mc_count;
+
+ wv_82593_reconfig(dev);
+ }
+ }
+ else
+ {
+ /*
+ * Switch to normal mode: disable promiscuous mode and
+ * clear the multicast list.
+ */
+ if(lp->promiscuous || lp->mc_count == 0)
+ {
+ lp->promiscuous = 0;
+ lp->allmulticast = 0;
+ lp->mc_count = 0;
+
+ wv_82593_reconfig(dev);
+ }
+ }
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This function doesn't exist...
+ * (Note : it was a nice way to test the reconfigure stuff...)
+ */
+#ifdef SET_MAC_ADDRESS
+static int
+wavelan_set_mac_address(device * dev,
+ void * addr)
+{
+ struct sockaddr * mac = addr;
+
+ /* Copy the address */
+ memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE);
+
+ /* Reconfig the beast */
+ wv_82593_reconfig(dev);
+
+ return 0;
+}
+#endif /* SET_MAC_ADDRESS */
+
+#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Frequency setting (for hardware able of it)
+ * It's a bit complicated and you don't really want to look into it...
+ * (called in wavelan_ioctl)
+ */
+static inline int
+wv_set_frequency(u_long base, /* i/o port of the card */
+ iw_freq * frequency)
+{
+ const int BAND_NUM = 10; /* Number of bands */
+ long freq = 0L; /* offset to 2.4 GHz in .5 MHz */
+#ifdef DEBUG_IOCTL_INFO
+ int i;
+#endif
+
+ /* Setting by frequency */
+ /* Theoritically, you may set any frequency between
+ * the two limits with a 0.5 MHz precision. In practice,
+ * I don't want you to have trouble with local
+ * regulations... */
+ if((frequency->e == 1) &&
+ (frequency->m >= (int) 2.412e8) && (frequency->m <= (int) 2.487e8))
+ {
+ freq = ((frequency->m / 10000) - 24000L) / 5;
+ }
+
+ /* Setting by channel (same as wfreqsel) */
+ /* Warning : each channel is 22MHz wide, so some of the channels
+ * will interfere... */
+ if((frequency->e == 0) &&
+ (frequency->m >= 0) && (frequency->m < BAND_NUM))
+ {
+ /* Get frequency offset. */
+ freq = channel_bands[frequency->m] >> 1;
+ }
+
+ /* Verify if the frequency is allowed */
+ if(freq != 0L)
+ {
+ u_short table[10]; /* Authorized frequency table */
+
+ /* Read the frequency table */
+ fee_read(base, 0x71 /* frequency table */,
+ table, 10);
+
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG "Frequency table :");
+ for(i = 0; i < 10; i++)
+ {
+ printk(" %04X",
+ table[i]);
+ }
+ printk("\n");
+#endif
+
+ /* Look in the table if the frequency is allowed */
+ if(!(table[9 - ((freq - 24) / 16)] &
+ (1 << ((freq - 24) % 16))))
+ return -EINVAL; /* not allowed */
+ }
+ else
+ return -EINVAL;
+
+ /* If we get a usable frequency */
+ if(freq != 0L)
+ {
+ unsigned short area[16];
+ unsigned short dac[2];
+ unsigned short area_verify[16];
+ unsigned short dac_verify[2];
+ /* Corresponding gain (in the power adjust value table)
+ * see AT&T Wavelan Data Manual, REF 407-024689/E, page 3-8
+ * & WCIN062D.DOC, page 6.2.9 */
+ unsigned short power_limit[] = { 40, 80, 120, 160, 0 };
+ int power_band = 0; /* Selected band */
+ unsigned short power_adjust; /* Correct value */
+
+ /* Search for the gain */
+ power_band = 0;
+ while((freq > power_limit[power_band]) &&
+ (power_limit[++power_band] != 0))
+ ;
+
+ /* Read the first area */
+ fee_read(base, 0x00,
+ area, 16);
+
+ /* Read the DAC */
+ fee_read(base, 0x60,
+ dac, 2);
+
+ /* Read the new power adjust value */
+ fee_read(base, 0x6B - (power_band >> 1),
+ &power_adjust, 1);
+ if(power_band & 0x1)
+ power_adjust >>= 8;
+ else
+ power_adjust &= 0xFF;
+
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+ for(i = 0; i < 16; i++)
+ {
+ printk(" %04X",
+ area[i]);
+ }
+ printk("\n");
+
+ printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+ dac[0], dac[1]);
+#endif
+
+ /* Frequency offset (for info only...) */
+ area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
+
+ /* Receiver Principle main divider coefficient */
+ area[3] = (freq >> 1) + 2400L - 352L;
+ area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+ /* Transmitter Main divider coefficient */
+ area[13] = (freq >> 1) + 2400L;
+ area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+ /* Others part of the area are flags, bit streams or unused... */
+
+ /* Set the value in the DAC */
+ dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
+ dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
+
+ /* Write the first area */
+ fee_write(base, 0x00,
+ area, 16);
+
+ /* Write the DAC */
+ fee_write(base, 0x60,
+ dac, 2);
+
+ /* We now should verify here that the EEprom writting was ok */
+
+ /* ReRead the first area */
+ fee_read(base, 0x00,
+ area_verify, 16);
+
+ /* ReRead the DAC */
+ fee_read(base, 0x60,
+ dac_verify, 2);
+
+ /* Compare */
+ if(memcmp(area, area_verify, 16 * 2) ||
+ memcmp(dac, dac_verify, 2 * 2))
+ {
+#ifdef DEBUG_IOCTL_ERROR
+ printk(KERN_INFO "Wavelan: wv_set_frequency : unable to write new frequency to EEprom (??)\n");
+#endif
+ return -EOPNOTSUPP;
+ }
+
+ /* We must download the frequency parameters to the
+ * synthetisers (from the EEprom - area 1)
+ * Note : as the EEprom is auto decremented, we set the end
+ * if the area... */
+ mmc_out(base, mmwoff(0, mmw_fee_addr), 0x0F);
+ mmc_out(base, mmwoff(0, mmw_fee_ctrl),
+ MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+ /* Wait until the download is finished */
+ fee_wait(base, 100, 100);
+
+ /* We must now download the power adjust value (gain) to
+ * the synthetisers (from the EEprom - area 7 - DAC) */
+ mmc_out(base, mmwoff(0, mmw_fee_addr), 0x61);
+ mmc_out(base, mmwoff(0, mmw_fee_ctrl),
+ MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+ /* Wait until the download is finished */
+ fee_wait(base, 100, 100);
+
+#ifdef DEBUG_IOCTL_INFO
+ /* Verification of what we have done... */
+
+ printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+ for(i = 0; i < 16; i++)
+ {
+ printk(" %04X",
+ area_verify[i]);
+ }
+ printk("\n");
+
+ printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+ dac_verify[0], dac_verify[1]);
+#endif
+
+ return 0;
+ }
+ else
+ return -EINVAL; /* Bah, never get there... */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Give the list of available frequencies
+ */
+static inline int
+wv_frequency_list(u_long base, /* i/o port of the card */
+ iw_freq * list, /* List of frequency to fill */
+ int max) /* Maximum number of frequencies */
+{
+ u_short table[10]; /* Authorized frequency table */
+ long freq = 0L; /* offset to 2.4 GHz in .5 MHz + 12 MHz */
+ int i; /* index in the table */
+#if WIRELESS_EXT > 7
+ const int BAND_NUM = 10; /* Number of bands */
+ int c = 0; /* Channel number */
+#endif /* WIRELESS_EXT */
+
+ /* Read the frequency table */
+ fee_read(base, 0x71 /* frequency table */,
+ table, 10);
+
+ /* Look all frequencies */
+ i = 0;
+ for(freq = 0; freq < 150; freq++)
+ /* Look in the table if the frequency is allowed */
+ if(table[9 - (freq / 16)] & (1 << (freq % 16)))
+ {
+#if WIRELESS_EXT > 7
+ /* Compute approximate channel number */
+ while((((channel_bands[c] >> 1) - 24) < freq) &&
+ (c < BAND_NUM))
+ c++;
+ list[i].i = c; /* Set the list index */
+#endif /* WIRELESS_EXT */
+
+ /* put in the list */
+ list[i].m = (((freq + 24) * 5) + 24000L) * 10000;
+ list[i++].e = 1;
+
+ /* Check number */
+ if(i >= max)
+ return(i);
+ }
+
+ return(i);
+}
+
+#ifdef WIRELESS_SPY
+/*------------------------------------------------------------------*/
+/*
+ * Gather wireless spy statistics : for each packet, compare the source
+ * address with out list, and if match, get the stats...
+ * Sorry, but this function really need wireless extensions...
+ */
+static inline void
+wl_spy_gather(device * dev,
+ u_char * mac, /* MAC address */
+ u_char * stats) /* Statistics to gather */
+{
+ net_local * lp = (net_local *) dev->priv;
+ int i;
+
+ /* Look all addresses */
+ for(i = 0; i < lp->spy_number; i++)
+ /* If match */
+ if(!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE))
+ {
+ /* Update statistics */
+ lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL;
+ lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL;
+ lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL;
+ lp->spy_stat[i].updated = 0x7;
+ }
+}
+#endif /* WIRELESS_SPY */
+
+#ifdef HISTOGRAM
+/*------------------------------------------------------------------*/
+/*
+ * This function calculate an histogram on the signal level.
+ * As the noise is quite constant, it's like doing it on the SNR.
+ * We have defined a set of interval (lp->his_range), and each time
+ * the level goes in that interval, we increment the count (lp->his_sum).
+ * With this histogram you may detect if one wavelan is really weak,
+ * or you may also calculate the mean and standard deviation of the level...
+ */
+static inline void
+wl_his_gather(device * dev,
+ u_char * stats) /* Statistics to gather */
+{
+ net_local * lp = (net_local *) dev->priv;
+ u_char level = stats[0] & MMR_SIGNAL_LVL;
+ int i;
+
+ /* Find the correct interval */
+ i = 0;
+ while((i < (lp->his_number - 1)) && (level >= lp->his_range[i++]))
+ ;
+
+ /* Increment interval counter */
+ (lp->his_sum[i])++;
+}
+#endif /* HISTOGRAM */
+
+/*------------------------------------------------------------------*/
+/*
+ * Perform ioctl : config & info stuff
+ * This is here that are treated the wireless extensions (iwconfig)
+ */
+static int
+wavelan_ioctl(struct net_device * dev, /* Device on wich the ioctl apply */
+ struct ifreq * rq, /* Data passed */
+ int cmd) /* Ioctl number */
+{
+ ioaddr_t base = dev->base_addr;
+ net_local * lp = (net_local *)dev->priv; /* lp is not unused */
+ struct iwreq * wrq = (struct iwreq *) rq;
+ psa_t psa;
+ mm_t m;
+ unsigned long flags;
+ int ret = 0;
+
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd);
+#endif
+
+ /* Disable interrupts & save flags */
+ wv_splhi(lp, &flags);
+
+ /* Look what is the request */
+ switch(cmd)
+ {
+ /* --------------- WIRELESS EXTENSIONS --------------- */
+
+ case SIOCGIWNAME:
+ strcpy(wrq->u.name, "Wavelan");
+ break;
+
+ case SIOCSIWNWID:
+ /* Set NWID in wavelan */
+#if WIRELESS_EXT > 8
+ if(!wrq->u.nwid.disabled)
+ {
+ /* Set NWID in psa */
+ psa.psa_nwid[0] = (wrq->u.nwid.value & 0xFF00) >> 8;
+ psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF;
+#else /* WIRELESS_EXT > 8 */
+ if(wrq->u.nwid.on)
+ {
+ /* Set NWID in psa */
+ psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8;
+ psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF;
+#endif /* WIRELESS_EXT > 8 */
+ psa.psa_nwid_select = 0x01;
+ psa_write(dev, (char *)psa.psa_nwid - (char *)&psa,
+ (unsigned char *)psa.psa_nwid, 3);
+
+ /* Set NWID in mmc */
+ m.w.mmw_netw_id_l = psa.psa_nwid[1];
+ m.w.mmw_netw_id_h = psa.psa_nwid[0];
+ mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m,
+ (unsigned char *)&m.w.mmw_netw_id_l, 2);
+ mmc_out(base, mmwoff(0, mmw_loopt_sel), 0x00);
+ }
+ else
+ {
+ /* Disable nwid in the psa */
+ psa.psa_nwid_select = 0x00;
+ psa_write(dev, (char *)&psa.psa_nwid_select - (char *)&psa,
+ (unsigned char *)&psa.psa_nwid_select, 1);
+
+ /* Disable nwid in the mmc (no filtering) */
+ mmc_out(base, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID);
+ }
+ /* update the Wavelan checksum */
+ update_psa_checksum(dev);
+ break;
+
+ case SIOCGIWNWID:
+ /* Read the NWID */
+ psa_read(dev, (char *)psa.psa_nwid - (char *)&psa,
+ (unsigned char *)psa.psa_nwid, 3);
+#if WIRELESS_EXT > 8
+ wrq->u.nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+ wrq->u.nwid.disabled = !(psa.psa_nwid_select);
+ wrq->u.nwid.fixed = 1; /* Superfluous */
+#else /* WIRELESS_EXT > 8 */
+ wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+ wrq->u.nwid.on = psa.psa_nwid_select;
+#endif /* WIRELESS_EXT > 8 */
+ break;
+
+ case SIOCSIWFREQ:
+ /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
+ if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+ (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+ ret = wv_set_frequency(base, &(wrq->u.freq));
+ else
+ ret = -EOPNOTSUPP;
+ break;
+
+ case SIOCGIWFREQ:
+ /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+ * (does it work for everybody ??? - especially old cards...) */
+ if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+ (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+ {
+ unsigned short freq;
+
+ /* Ask the EEprom to read the frequency from the first area */
+ fee_read(base, 0x00 /* 1st area - frequency... */,
+ &freq, 1);
+ wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000;
+ wrq->u.freq.e = 1;
+ }
+ else
+ {
+ psa_read(dev, (char *)&psa.psa_subband - (char *)&psa,
+ (unsigned char *)&psa.psa_subband, 1);
+
+ if(psa.psa_subband <= 4)
+ {
+ wrq->u.freq.m = fixed_bands[psa.psa_subband];
+ wrq->u.freq.e = (psa.psa_subband != 0);
+ }
+ else
+ ret = -EOPNOTSUPP;
+ }
+ break;
+
+ case SIOCSIWSENS:
+ /* Set the level threshold */
+#if WIRELESS_EXT > 7
+ /* We should complain loudly if wrq->u.sens.fixed = 0, because we
+ * can't set auto mode... */
+ psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F;
+#else /* WIRELESS_EXT > 7 */
+ psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F;
+#endif /* WIRELESS_EXT > 7 */
+ psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+ (unsigned char *)&psa.psa_thr_pre_set, 1);
+ /* update the Wavelan checksum */
+ update_psa_checksum(dev);
+ mmc_out(base, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set);
+ break;
+
+ case SIOCGIWSENS:
+ /* Read the level threshold */
+ psa_read(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+ (unsigned char *)&psa.psa_thr_pre_set, 1);
+#if WIRELESS_EXT > 7
+ wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F;
+ wrq->u.sens.fixed = 1;
+#else /* WIRELESS_EXT > 7 */
+ wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F;
+#endif /* WIRELESS_EXT > 7 */
+ break;
+
+#if WIRELESS_EXT > 8
+ case SIOCSIWENCODE:
+ /* Set encryption key */
+ if(!mmc_encr(base))
+ {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ /* Basic checking... */
+ if(wrq->u.encoding.pointer != (caddr_t) 0)
+ {
+ /* Check the size of the key */
+ if(wrq->u.encoding.length != 8)
+ {
+ ret = -EINVAL;
+ break;
+ }
+
+ /* Copy the key in the driver */
+ if(copy_from_user(psa.psa_encryption_key, wrq->u.encoding.pointer,
+ wrq->u.encoding.length))
+ {
+ ret = -EFAULT;
+ break;
+ }
+
+ psa.psa_encryption_select = 1;
+ psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
+ (unsigned char *) &psa.psa_encryption_select, 8+1);
+
+ mmc_out(base, mmwoff(0, mmw_encr_enable),
+ MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
+ mmc_write(base, mmwoff(0, mmw_encr_key),
+ (unsigned char *) &psa.psa_encryption_key, 8);
+ }
+
+ if(wrq->u.encoding.flags & IW_ENCODE_DISABLED)
+ { /* disable encryption */
+ psa.psa_encryption_select = 0;
+ psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
+ (unsigned char *) &psa.psa_encryption_select, 1);
+
+ mmc_out(base, mmwoff(0, mmw_encr_enable), 0);
+ }
+ /* update the Wavelan checksum */
+ update_psa_checksum(dev);
+ break;
+
+ case SIOCGIWENCODE:
+ /* Read the encryption key */
+ if(!mmc_encr(base))
+ {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ /* only super-user can see encryption key */
+ if(!capable(CAP_NET_ADMIN))
+ {
+ ret = -EPERM;
+ break;
+ }
+
+ /* Basic checking... */
+ if(wrq->u.encoding.pointer != (caddr_t) 0)
+ {
+ psa_read(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
+ (unsigned char *) &psa.psa_encryption_select, 1+8);
+
+ /* encryption is enabled ? */
+ if(psa.psa_encryption_select)
+ wrq->u.encoding.flags = IW_ENCODE_ENABLED;
+ else
+ wrq->u.encoding.flags = IW_ENCODE_DISABLED;
+ wrq->u.encoding.flags |= mmc_encr(base);
+
+ /* Copy the key to the user buffer */
+ wrq->u.encoding.length = 8;
+ if(copy_to_user(wrq->u.encoding.pointer, psa.psa_encryption_key, 8))
+ ret = -EFAULT;
+ }
+ break;
+#endif /* WIRELESS_EXT > 8 */
+
+#ifdef WAVELAN_ROAMING_EXT
+#if WIRELESS_EXT > 5
+ case SIOCSIWESSID:
+ /* Check if disable */
+ if(wrq->u.data.flags == 0)
+ lp->filter_domains = 0;
+ else
+ /* Basic checking... */
+ if(wrq->u.data.pointer != (caddr_t) 0)
+ {
+ char essid[IW_ESSID_MAX_SIZE + 1];
+ char * endp;
+
+ /* Check the size of the string */
+ if(wrq->u.data.length > IW_ESSID_MAX_SIZE + 1)
+ {
+ ret = -E2BIG;
+ break;
+ }
+
+ /* Copy the string in the driver */
+ if(copy_from_user(essid, wrq->u.data.pointer, wrq->u.data.length))
+ {
+ ret = -EFAULT;
+ break;
+ }
+ essid[IW_ESSID_MAX_SIZE] = '\0';
+
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG "SetEssid : ``%s''\n", essid);
+#endif /* DEBUG_IOCTL_INFO */
+
+ /* Convert to a number (note : Wavelan specific) */
+ lp->domain_id = simple_strtoul(essid, &endp, 16);
+ /* Has it worked ? */
+ if(endp > essid)
+ lp->filter_domains = 1;
+ else
+ {
+ lp->filter_domains = 0;
+ ret = -EINVAL;
+ }
+ }
+ break;
+
+ case SIOCGIWESSID:
+ /* Basic checking... */
+ if(wrq->u.data.pointer != (caddr_t) 0)
+ {
+ char essid[IW_ESSID_MAX_SIZE + 1];
+
+ /* Is the domain ID active ? */
+ wrq->u.data.flags = lp->filter_domains;
+
+ /* Copy Domain ID into a string (Wavelan specific) */
+ /* Sound crazy, be we can't have a snprintf in the kernel !!! */
+ sprintf(essid, "%lX", lp->domain_id);
+ essid[IW_ESSID_MAX_SIZE] = '\0';
+
+ /* Set the length */
+ wrq->u.data.length = strlen(essid) + 1;
+
+ /* Copy structure to the user buffer */
+ if(copy_to_user(wrq->u.data.pointer, essid, wrq->u.data.length))
+ ret = -EFAULT;
+ }
+ break;
+
+ case SIOCSIWAP:
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG "Set AP to : %02X:%02X:%02X:%02X:%02X:%02X\n",
+ wrq->u.ap_addr.sa_data[0],
+ wrq->u.ap_addr.sa_data[1],
+ wrq->u.ap_addr.sa_data[2],
+ wrq->u.ap_addr.sa_data[3],
+ wrq->u.ap_addr.sa_data[4],
+ wrq->u.ap_addr.sa_data[5]);
+#endif /* DEBUG_IOCTL_INFO */
+
+ ret = -EOPNOTSUPP; /* Not supported yet */
+ break;
+
+ case SIOCGIWAP:
+ /* Should get the real McCoy instead of own Ethernet address */
+ memcpy(wrq->u.ap_addr.sa_data, dev->dev_addr, WAVELAN_ADDR_SIZE);
+ wrq->u.ap_addr.sa_family = ARPHRD_ETHER;
+
+ ret = -EOPNOTSUPP; /* Not supported yet */
+ break;
+#endif /* WIRELESS_EXT > 5 */
+#endif /* WAVELAN_ROAMING_EXT */
+
+#if WIRELESS_EXT > 8
+#ifdef WAVELAN_ROAMING
+ case SIOCSIWMODE:
+ switch(wrq->u.mode)
+ {
+ case IW_MODE_ADHOC:
+ if(do_roaming)
+ {
+ wv_roam_cleanup(dev);
+ do_roaming = 0;
+ }
+ break;
+ case IW_MODE_INFRA:
+ if(!do_roaming)
+ {
+ wv_roam_init(dev);
+ do_roaming = 1;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+
+ case SIOCGIWMODE:
+ if(do_roaming)
+ wrq->u.mode = IW_MODE_INFRA;
+ else
+ wrq->u.mode = IW_MODE_ADHOC;
+ break;
+#endif /* WAVELAN_ROAMING */
+#endif /* WIRELESS_EXT > 8 */
+
+ case SIOCGIWRANGE:
+ /* Basic checking... */
+ if(wrq->u.data.pointer != (caddr_t) 0)
+ {
+ struct iw_range range;
+
+ /* Set the length (very important for backward compatibility) */
+ wrq->u.data.length = sizeof(struct iw_range);
+
+ /* Set all the info we don't care or don't know about to zero */
+ memset(&range, 0, sizeof(range));
+
+#if WIRELESS_EXT > 10
+ /* Set the Wireless Extension versions */
+ range.we_version_compiled = WIRELESS_EXT;
+ range.we_version_source = 9; /* Nothing for us in v10 and v11 */
+#endif /* WIRELESS_EXT > 10 */
+
+ /* Set information in the range struct */
+ range.throughput = 1.4 * 1000 * 1000; /* don't argue on this ! */
+ range.min_nwid = 0x0000;
+ range.max_nwid = 0xFFFF;
+
+ /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
+ if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+ (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+ {
+ range.num_channels = 10;
+ range.num_frequency = wv_frequency_list(base, range.freq,
+ IW_MAX_FREQUENCIES);
+ }
+ else
+ range.num_channels = range.num_frequency = 0;
+
+ range.sensitivity = 0x3F;
+ range.max_qual.qual = MMR_SGNL_QUAL;
+ range.max_qual.level = MMR_SIGNAL_LVL;
+ range.max_qual.noise = MMR_SILENCE_LVL;
+#if WIRELESS_EXT > 11
+ range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */
+ /* Need to get better values for those two */
+ range.avg_qual.level = 30;
+ range.avg_qual.noise = 8;
+#endif /* WIRELESS_EXT > 11 */
+
+#if WIRELESS_EXT > 7
+ range.num_bitrates = 1;
+ range.bitrate[0] = 2000000; /* 2 Mb/s */
+#endif /* WIRELESS_EXT > 7 */
+
+#if WIRELESS_EXT > 8
+ /* Encryption supported ? */
+ if(mmc_encr(base))
+ {
+ range.encoding_size[0] = 8; /* DES = 64 bits key */
+ range.num_encoding_sizes = 1;
+ range.max_encoding_tokens = 1; /* Only one key possible */
+ }
+ else
+ {
+ range.num_encoding_sizes = 0;
+ range.max_encoding_tokens = 0;
+ }
+#endif /* WIRELESS_EXT > 8 */
+
+ /* Copy structure to the user buffer */
+ if(copy_to_user(wrq->u.data.pointer, &range,
+ sizeof(struct iw_range)))
+ ret = -EFAULT;
+ }
+ break;
+
+ case SIOCGIWPRIV:
+ /* Basic checking... */
+ if(wrq->u.data.pointer != (caddr_t) 0)
+ {
+ struct iw_priv_args priv[] =
+ { /* cmd, set_args, get_args, name */
+ { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" },
+ { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" },
+ { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16, 0, "sethisto" },
+ { SIOCGIPHISTO, 0, IW_PRIV_TYPE_INT | 16, "gethisto" },
+ { SIOCSIPROAM, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1 , 0, "setroam" },
+ { SIOCGIPROAM, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getroam" },
+ };
+
+ /* Set the number of ioctl available */
+ wrq->u.data.length = 6;
+
+ /* Copy structure to the user buffer */
+ if(copy_to_user(wrq->u.data.pointer, (u_char *) priv,
+ sizeof(priv)))
+ ret = -EFAULT;
+ }
+ break;
+
+#ifdef WIRELESS_SPY
+ case SIOCSIWSPY:
+ /* Set the spy list */
+
+ /* Check the number of addresses */
+ if(wrq->u.data.length > IW_MAX_SPY)
+ {
+ ret = -E2BIG;
+ break;
+ }
+ lp->spy_number = wrq->u.data.length;
+
+ /* If there is some addresses to copy */
+ if(lp->spy_number > 0)
+ {
+ struct sockaddr address[IW_MAX_SPY];
+ int i;
+
+ /* Copy addresses to the driver */
+ if(copy_from_user(address, wrq->u.data.pointer,
+ sizeof(struct sockaddr) * lp->spy_number))
+ {
+ ret = -EFAULT;
+ break;
+ }
+
+ /* Copy addresses to the lp structure */
+ for(i = 0; i < lp->spy_number; i++)
+ {
+ memcpy(lp->spy_address[i], address[i].sa_data,
+ WAVELAN_ADDR_SIZE);
+ }
+
+ /* Reset structure... */
+ memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY);
+
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG "SetSpy - Set of new addresses is :\n");
+ for(i = 0; i < wrq->u.data.length; i++)
+ printk(KERN_DEBUG "%02X:%02X:%02X:%02X:%02X:%02X\n",
+ lp->spy_address[i][0],
+ lp->spy_address[i][1],
+ lp->spy_address[i][2],
+ lp->spy_address[i][3],
+ lp->spy_address[i][4],
+ lp->spy_address[i][5]);
+#endif /* DEBUG_IOCTL_INFO */
+ }
+
+ break;
+
+ case SIOCGIWSPY:
+ /* Get the spy list and spy stats */
+
+ /* Set the number of addresses */
+ wrq->u.data.length = lp->spy_number;
+
+ /* If the user want to have the addresses back... */
+ if((lp->spy_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
+ {
+ struct sockaddr address[IW_MAX_SPY];
+ int i;
+
+ /* Copy addresses from the lp structure */
+ for(i = 0; i < lp->spy_number; i++)
+ {
+ memcpy(address[i].sa_data, lp->spy_address[i],
+ WAVELAN_ADDR_SIZE);
+ address[i].sa_family = ARPHRD_ETHER;
+ }
+
+ /* Copy addresses to the user buffer */
+ if(copy_to_user(wrq->u.data.pointer, address,
+ sizeof(struct sockaddr) * lp->spy_number))
+ {
+ ret = -EFAULT;
+ break;
+ }
+
+ /* Copy stats to the user buffer (just after) */
+ if(copy_to_user(wrq->u.data.pointer +
+ (sizeof(struct sockaddr) * lp->spy_number),
+ lp->spy_stat, sizeof(iw_qual) * lp->spy_number))
+ {
+ ret = -EFAULT;
+ break;
+ }
+
+ /* Reset updated flags */
+ for(i = 0; i < lp->spy_number; i++)
+ lp->spy_stat[i].updated = 0x0;
+ } /* if(pointer != NULL) */
+
+ break;
+#endif /* WIRELESS_SPY */
+
+ /* ------------------ PRIVATE IOCTL ------------------ */
+
+ case SIOCSIPQTHR:
+ if(!capable(CAP_NET_ADMIN))
+ {
+ ret = -EPERM;
+ break;
+ }
+ psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
+ psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
+ (unsigned char *)&psa.psa_quality_thr, 1);
+ /* update the Wavelan checksum */
+ update_psa_checksum(dev);
+ mmc_out(base, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr);
+ break;
+
+ case SIOCGIPQTHR:
+ psa_read(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
+ (unsigned char *)&psa.psa_quality_thr, 1);
+ *(wrq->u.name) = psa.psa_quality_thr & 0x0F;
+ break;
+
+#ifdef WAVELAN_ROAMING
+ case SIOCSIPROAM:
+ /* Note : should check if user == root */
+ if(do_roaming && (*wrq->u.name)==0)
+ wv_roam_cleanup(dev);
+ else if(do_roaming==0 && (*wrq->u.name)!=0)
+ wv_roam_init(dev);
+
+ do_roaming = (*wrq->u.name);
+
+ break;
+
+ case SIOCGIPROAM:
+ *(wrq->u.name) = do_roaming;
+ break;
+#endif /* WAVELAN_ROAMING */
+
+#ifdef HISTOGRAM
+ case SIOCSIPHISTO:
+ /* Verif if the user is root */
+ if(!capable(CAP_NET_ADMIN))
+ {
+ ret = -EPERM;
+ }
+
+ /* Check the number of intervals */
+ if(wrq->u.data.length > 16)
+ {
+ ret = -E2BIG;
+ break;
+ }
+ lp->his_number = wrq->u.data.length;
+
+ /* If there is some addresses to copy */
+ if(lp->his_number > 0)
+ {
+ /* Copy interval ranges to the driver */
+ if(copy_from_user(lp->his_range, wrq->u.data.pointer,
+ sizeof(char) * lp->his_number))
+ {
+ ret = -EFAULT;
+ break;
+ }
+
+ /* Reset structure... */
+ memset(lp->his_sum, 0x00, sizeof(long) * 16);
+ }
+ break;
+
+ case SIOCGIPHISTO:
+ /* Set the number of intervals */
+ wrq->u.data.length = lp->his_number;
+
+ /* Give back the distribution statistics */
+ if((lp->his_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
+ {
+ /* Copy data to the user buffer */
+ if(copy_to_user(wrq->u.data.pointer, lp->his_sum,
+ sizeof(long) * lp->his_number))
+ ret = -EFAULT;
+
+ } /* if(pointer != NULL) */
+ break;
+#endif /* HISTOGRAM */
+
+ /* ------------------- OTHER IOCTL ------------------- */
+
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ /* ReEnable interrupts & restore flags */
+ wv_splx(lp, &flags);
+
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
+#endif
+ return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Get wireless statistics
+ * Called by /proc/net/wireless...
+ */
+static iw_stats *
+wavelan_get_wireless_stats(device * dev)
+{
+ ioaddr_t base = dev->base_addr;
+ net_local * lp = (net_local *) dev->priv;
+ mmr_t m;
+ iw_stats * wstats;
+ unsigned long flags;
+
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name);
+#endif
+
+ /* Disable interrupts & save flags */
+ wv_splhi(lp, &flags);
+
+ wstats = &lp->wstats;
+
+ /* Get data from the mmc */
+ mmc_out(base, mmwoff(0, mmw_freeze), 1);
+
+ mmc_read(base, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1);
+ mmc_read(base, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, 2);
+ mmc_read(base, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, 4);
+
+ mmc_out(base, mmwoff(0, mmw_freeze), 0);
+
+ /* Copy data to wireless stuff */
+ wstats->status = m.mmr_dce_status & MMR_DCE_STATUS;
+ wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
+ wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
+ wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
+ wstats->qual.updated = (((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) |
+ ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) |
+ ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5));
+ wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+ wstats->discard.code = 0L;
+ wstats->discard.misc = 0L;
+
+ /* ReEnable interrupts & restore flags */
+ wv_splx(lp, &flags);
+
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name);
+#endif
+ return &lp->wstats;
+}
+#endif /* WIRELESS_EXT */
+
+/************************* PACKET RECEPTION *************************/
+/*
+ * This part deal with receiving the packets.
+ * The interrupt handler get an interrupt when a packet has been
+ * successfully received and called this part...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Calculate the starting address of the frame pointed to by the receive
+ * frame pointer and verify that the frame seem correct
+ * (called by wv_packet_rcv())
+ */
+static inline int
+wv_start_of_frame(device * dev,
+ int rfp, /* end of frame */
+ int wrap) /* start of buffer */
+{
+ ioaddr_t base = dev->base_addr;
+ int rp;
+ int len;
+
+ rp = (rfp - 5 + RX_SIZE) % RX_SIZE;
+ outb(rp & 0xff, PIORL(base));
+ outb(((rp >> 8) & PIORH_MASK), PIORH(base));
+ len = inb(PIOP(base));
+ len |= inb(PIOP(base)) << 8;
+
+ /* Sanity checks on size */
+ /* Frame too big */
+ if(len > MAXDATAZ + 100)
+ {
+#ifdef DEBUG_RX_ERROR
+ printk(KERN_INFO "%s: wv_start_of_frame: Received frame too large, rfp %d len 0x%x\n",
+ dev->name, rfp, len);
+#endif
+ return(-1);
+ }
+
+ /* Frame too short */
+ if(len < 7)
+ {
+#ifdef DEBUG_RX_ERROR
+ printk(KERN_INFO "%s: wv_start_of_frame: Received null frame, rfp %d len 0x%x\n",
+ dev->name, rfp, len);
+#endif
+ return(-1);
+ }
+
+ /* Wrap around buffer */
+ if(len > ((wrap - (rfp - len) + RX_SIZE) % RX_SIZE)) /* magic formula ! */
+ {
+#ifdef DEBUG_RX_ERROR
+ printk(KERN_INFO "%s: wv_start_of_frame: wrap around buffer, wrap %d rfp %d len 0x%x\n",
+ dev->name, wrap, rfp, len);
+#endif
+ return(-1);
+ }
+
+ return((rp - len + RX_SIZE) % RX_SIZE);
+} /* wv_start_of_frame */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does the actual copy of data (including the ethernet
+ * header structure) from the WaveLAN card to an sk_buff chain that
+ * will be passed up to the network interface layer. NOTE: We
+ * currently don't handle trailer protocols (neither does the rest of
+ * the network interface), so if that is needed, it will (at least in
+ * part) be added here. The contents of the receive ring buffer are
+ * copied to a message chain that is then passed to the kernel.
+ *
+ * Note: if any errors occur, the packet is "dropped on the floor"
+ * (called by wv_packet_rcv())
+ */
+static inline void
+wv_packet_read(device * dev,
+ int fd_p,
+ int sksize)
+{
+ net_local * lp = (net_local *) dev->priv;
+ struct sk_buff * skb;
+
+#ifdef DEBUG_RX_TRACE
+ printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n",
+ dev->name, fd_p, sksize);
+#endif
+
+ /* Allocate some buffer for the new packet */
+ if((skb = dev_alloc_skb(sksize+2)) == (struct sk_buff *) NULL)
+ {
+#ifdef DEBUG_RX_ERROR
+ printk(KERN_INFO "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC)\n",
+ dev->name, sksize);
+#endif
+ lp->stats.rx_dropped++;
+ /*
+ * Not only do we want to return here, but we also need to drop the
+ * packet on the floor to clear the interrupt.
+ */
+ return;
+ }
+
+ skb->dev = dev;
+
+ skb_reserve(skb, 2);
+ fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize);
+ skb->protocol = eth_type_trans(skb, dev);
+
+#ifdef DEBUG_RX_INFO
+ wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
+#endif /* DEBUG_RX_INFO */
+
+ /* Statistics gathering & stuff associated.
+ * It seem a bit messy with all the define, but it's really simple... */
+ if(
+#ifdef WIRELESS_SPY
+ (lp->spy_number > 0) ||
+#endif /* WIRELESS_SPY */
+#ifdef HISTOGRAM
+ (lp->his_number > 0) ||
+#endif /* HISTOGRAM */
+#ifdef WAVELAN_ROAMING
+ (do_roaming) ||
+#endif /* WAVELAN_ROAMING */
+ 0)
+ {
+ u_char stats[3]; /* Signal level, Noise level, Signal quality */
+
+ /* read signal level, silence level and signal quality bytes */
+ fd_p = read_ringbuf(dev, (fd_p + 4) % RX_SIZE + RX_BASE,
+ stats, 3);
+#ifdef DEBUG_RX_INFO
+ printk(KERN_DEBUG "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n",
+ dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F);
+#endif
+
+#ifdef WAVELAN_ROAMING
+ if(do_roaming)
+ if(WAVELAN_BEACON(skb->data))
+ wl_roam_gather(dev, skb->data, stats);
+#endif /* WAVELAN_ROAMING */
+
+#ifdef WIRELESS_SPY
+ wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats);
+#endif /* WIRELESS_SPY */
+#ifdef HISTOGRAM
+ wl_his_gather(dev, stats);
+#endif /* HISTOGRAM */
+ }
+
+ /*
+ * Hand the packet to the Network Module
+ */
+ netif_rx(skb);
+
+ /* Keep stats up to date */
+ dev->last_rx = jiffies;
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += sksize;
+
+#ifdef DEBUG_RX_TRACE
+ printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
+#endif
+ return;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine is called by the interrupt handler to initiate a
+ * packet transfer from the card to the network interface layer above
+ * this driver. This routine checks if a buffer has been successfully
+ * received by the WaveLAN card. If so, the routine wv_packet_read is
+ * called to do the actual transfer of the card's data including the
+ * ethernet header into a packet consisting of an sk_buff chain.
+ * (called by wavelan_interrupt())
+ * Note : the spinlock is already grabbed for us and irq are disabled.
+ */
+static inline void
+wv_packet_rcv(device * dev)
+{
+ ioaddr_t base = dev->base_addr;
+ net_local * lp = (net_local *) dev->priv;
+ int newrfp;
+ int rp;
+ int len;
+ int f_start;
+ int status;
+ int i593_rfp;
+ int stat_ptr;
+ u_char c[4];
+
+#ifdef DEBUG_RX_TRACE
+ printk(KERN_DEBUG "%s: ->wv_packet_rcv()\n", dev->name);
+#endif
+
+ /* Get the new receive frame pointer from the i82593 chip */
+ outb(CR0_STATUS_2 | OP0_NOP, LCCR(base));
+ i593_rfp = inb(LCSR(base));
+ i593_rfp |= inb(LCSR(base)) << 8;
+ i593_rfp %= RX_SIZE;
+
+ /* Get the new receive frame pointer from the WaveLAN card.
+ * It is 3 bytes more than the increment of the i82593 receive
+ * frame pointer, for each packet. This is because it includes the
+ * 3 roaming bytes added by the mmc.
+ */
+ newrfp = inb(RPLL(base));
+ newrfp |= inb(RPLH(base)) << 8;
+ newrfp %= RX_SIZE;
+
+#ifdef DEBUG_RX_INFO
+ printk(KERN_DEBUG "%s: wv_packet_rcv(): i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
+ dev->name, i593_rfp, lp->stop, newrfp, lp->rfp);
+#endif
+
+#ifdef DEBUG_RX_ERROR
+ /* If no new frame pointer... */
+ if(lp->overrunning || newrfp == lp->rfp)
+ printk(KERN_INFO "%s: wv_packet_rcv(): no new frame: i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
+ dev->name, i593_rfp, lp->stop, newrfp, lp->rfp);
+#endif
+
+ /* Read all frames (packets) received */
+ while(newrfp != lp->rfp)
+ {
+ /* A frame is composed of the packet, followed by a status word,
+ * the length of the frame (word) and the mmc info (SNR & qual).
+ * It's because the length is at the end that we can only scan
+ * frames backward. */
+
+ /* Find the first frame by skipping backwards over the frames */
+ rp = newrfp; /* End of last frame */
+ while(((f_start = wv_start_of_frame(dev, rp, newrfp)) != lp->rfp) &&
+ (f_start != -1))
+ rp = f_start;
+
+ /* If we had a problem */
+ if(f_start == -1)
+ {
+#ifdef DEBUG_RX_ERROR
+ printk(KERN_INFO "wavelan_cs: cannot find start of frame ");
+ printk(" i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
+ i593_rfp, lp->stop, newrfp, lp->rfp);
+#endif
+ lp->rfp = rp; /* Get to the last usable frame */
+ continue;
+ }
+
+ /* f_start point to the beggining of the first frame received
+ * and rp to the beggining of the next one */
+
+ /* Read status & length of the frame */
+ stat_ptr = (rp - 7 + RX_SIZE) % RX_SIZE;
+ stat_ptr = read_ringbuf(dev, stat_ptr, c, 4);
+ status = c[0] | (c[1] << 8);
+ len = c[2] | (c[3] << 8);
+
+ /* Check status */
+ if((status & RX_RCV_OK) != RX_RCV_OK)
+ {
+ lp->stats.rx_errors++;
+ if(status & RX_NO_SFD)
+ lp->stats.rx_frame_errors++;
+ if(status & RX_CRC_ERR)
+ lp->stats.rx_crc_errors++;
+ if(status & RX_OVRRUN)
+ lp->stats.rx_over_errors++;
+
+#ifdef DEBUG_RX_FAIL
+ printk(KERN_DEBUG "%s: wv_packet_rcv(): packet not received ok, status = 0x%x\n",
+ dev->name, status);
+#endif
+ }
+ else
+ /* Read the packet and transmit to Linux */
+ wv_packet_read(dev, f_start, len - 2);
+
+ /* One frame has been processed, skip it */
+ lp->rfp = rp;
+ }
+
+ /*
+ * Update the frame stop register, but set it to less than
+ * the full 8K to allow space for 3 bytes of signal strength
+ * per packet.
+ */
+ lp->stop = (i593_rfp + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
+ outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base));
+ outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base));
+ outb(OP1_SWIT_TO_PORT_0, LCCR(base));
+
+#ifdef DEBUG_RX_TRACE
+ printk(KERN_DEBUG "%s: <-wv_packet_rcv()\n", dev->name);
+#endif
+}
+
+/*********************** PACKET TRANSMISSION ***********************/
+/*
+ * This part deal with sending packet through the wavelan
+ * We copy the packet to the send buffer and then issue the send
+ * command to the i82593. The result of this operation will be
+ * checked in wavelan_interrupt()
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine fills in the appropriate registers and memory
+ * locations on the WaveLAN card and starts the card off on
+ * the transmit.
+ * (called in wavelan_packet_xmit())
+ */
+static inline void
+wv_packet_write(device * dev,
+ void * buf,
+ short length)
+{
+ net_local * lp = (net_local *) dev->priv;
+ ioaddr_t base = dev->base_addr;
+ unsigned long flags;
+ int clen = length;
+ register u_short xmtdata_base = TX_BASE;
+
+#ifdef DEBUG_TX_TRACE
+ printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length);
+#endif
+
+ wv_splhi(lp, &flags);
+
+ /* Check if we need some padding */
+ if(clen < ETH_ZLEN)
+ clen = ETH_ZLEN;
+
+ /* Write the length of data buffer followed by the buffer */
+ outb(xmtdata_base & 0xff, PIORL(base));
+ outb(((xmtdata_base >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+ outb(clen & 0xff, PIOP(base)); /* lsb */
+ outb(clen >> 8, PIOP(base)); /* msb */
+
+ /* Send the data */
+ outsb(PIOP(base), buf, clen);
+
+ /* Indicate end of transmit chain */
+ outb(OP0_NOP, PIOP(base));
+ /* josullvn@cs.cmu.edu: need to send a second NOP for alignment... */
+ outb(OP0_NOP, PIOP(base));
+
+ /* Reset the transmit DMA pointer */
+ hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+ hacr_write(base, HACR_DEFAULT);
+ /* Send the transmit command */
+ wv_82593_cmd(dev, "wv_packet_write(): transmit",
+ OP0_TRANSMIT, SR0_NO_RESULT);
+
+ /* Keep stats up to date */
+ lp->stats.tx_bytes += length;
+
+ wv_splx(lp, &flags);
+
+#ifdef DEBUG_TX_INFO
+ wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write");
+#endif /* DEBUG_TX_INFO */
+
+#ifdef DEBUG_TX_TRACE
+ printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine is called when we want to send a packet (NET3 callback)
+ * In this routine, we check if the harware is ready to accept
+ * the packet. We also prevent reentrance. Then, we call the function
+ * to send the packet...
+ */
+static int
+wavelan_packet_xmit(struct sk_buff * skb,
+ device * dev)
+{
+ net_local * lp = (net_local *)dev->priv;
+ unsigned long flags;
+
+#ifdef DEBUG_TX_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
+ (unsigned) skb);
+#endif
+
+ /*
+ * Block a timer-based transmit from overlapping a previous transmit.
+ * In other words, prevent reentering this routine.
+ */
+ netif_stop_queue(dev);
+
+ /* If somebody has asked to reconfigure the controller,
+ * we can do it now */
+ if(lp->reconfig_82593)
+ {
+ wv_splhi(lp, &flags); /* Disable interrupts */
+ wv_82593_config(dev);
+ wv_splx(lp, &flags); /* Re-enable interrupts */
+ /* Note : the configure procedure was totally synchronous,
+ * so the Tx buffer is now free */
+ }
+
+#ifdef DEBUG_TX_ERROR
+ if (skb->next)
+ printk(KERN_INFO "skb has next\n");
+#endif
+
+ wv_packet_write(dev, skb->data, skb->len);
+
+ dev_kfree_skb(skb);
+
+#ifdef DEBUG_TX_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
+#endif
+ return(0);
+}
+
+/********************** HARDWARE CONFIGURATION **********************/
+/*
+ * This part do the real job of starting and configuring the hardware.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to initialize the Modem Management Controller.
+ * (called by wv_hw_config())
+ */
+static inline int
+wv_mmc_init(device * dev)
+{
+ ioaddr_t base = dev->base_addr;
+ psa_t psa;
+ mmw_t m;
+ int configured;
+ int i; /* Loop counter */
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name);
+#endif
+
+ /* Read the parameter storage area */
+ psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+
+ /*
+ * Check the first three octets of the MAC addr for the manufacturer's code.
+ * Note: If you get the error message below, you've got a
+ * non-NCR/AT&T/Lucent PCMCIA cards, see wavelan_cs.h for detail on
+ * how to configure your card...
+ */
+ for(i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
+ if((psa.psa_univ_mac_addr[0] == MAC_ADDRESSES[i][0]) &&
+ (psa.psa_univ_mac_addr[1] == MAC_ADDRESSES[i][1]) &&
+ (psa.psa_univ_mac_addr[2] == MAC_ADDRESSES[i][2]))
+ break;
+
+ /* If we have not found it... */
+ if(i == (sizeof(MAC_ADDRESSES) / sizeof(char) / 3))
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_WARNING "%s: wv_mmc_init(): Invalid MAC address: %02X:%02X:%02X:...\n",
+ dev->name, psa.psa_univ_mac_addr[0],
+ psa.psa_univ_mac_addr[1], psa.psa_univ_mac_addr[2]);
+#endif
+ return FALSE;
+ }
+
+ /* Get the MAC address */
+ memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE);
+
+#ifdef USE_PSA_CONFIG
+ configured = psa.psa_conf_status & 1;
+#else
+ configured = 0;
+#endif
+
+ /* Is the PSA is not configured */
+ if(!configured)
+ {
+ /* User will be able to configure NWID after (with iwconfig) */
+ psa.psa_nwid[0] = 0;
+ psa.psa_nwid[1] = 0;
+
+ /* As NWID is not set : no NWID checking */
+ psa.psa_nwid_select = 0;
+
+ /* Disable encryption */
+ psa.psa_encryption_select = 0;
+
+ /* Set to standard values
+ * 0x04 for AT,
+ * 0x01 for MCA,
+ * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document)
+ */
+ if (psa.psa_comp_number & 1)
+ psa.psa_thr_pre_set = 0x01;
+ else
+ psa.psa_thr_pre_set = 0x04;
+ psa.psa_quality_thr = 0x03;
+
+ /* It is configured */
+ psa.psa_conf_status |= 1;
+
+#ifdef USE_PSA_CONFIG
+ /* Write the psa */
+ psa_write(dev, (char *)psa.psa_nwid - (char *)&psa,
+ (unsigned char *)psa.psa_nwid, 4);
+ psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+ (unsigned char *)&psa.psa_thr_pre_set, 1);
+ psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
+ (unsigned char *)&psa.psa_quality_thr, 1);
+ psa_write(dev, (char *)&psa.psa_conf_status - (char *)&psa,
+ (unsigned char *)&psa.psa_conf_status, 1);
+ /* update the Wavelan checksum */
+ update_psa_checksum(dev);
+#endif /* USE_PSA_CONFIG */
+ }
+
+ /* Zero the mmc structure */
+ memset(&m, 0x00, sizeof(m));
+
+ /* Copy PSA info to the mmc */
+ m.mmw_netw_id_l = psa.psa_nwid[1];
+ m.mmw_netw_id_h = psa.psa_nwid[0];
+
+ if(psa.psa_nwid_select & 1)
+ m.mmw_loopt_sel = 0x00;
+ else
+ m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
+
+ memcpy(&m.mmw_encr_key, &psa.psa_encryption_key,
+ sizeof(m.mmw_encr_key));
+
+ if(psa.psa_encryption_select)
+ m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE;
+ else
+ m.mmw_encr_enable = 0;
+
+ m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
+ m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
+
+ /*
+ * Set default modem control parameters.
+ * See NCR document 407-0024326 Rev. A.
+ */
+ m.mmw_jabber_enable = 0x01;
+ m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
+ m.mmw_ifs = 0x20;
+ m.mmw_mod_delay = 0x04;
+ m.mmw_jam_time = 0x38;
+
+ m.mmw_des_io_invert = 0;
+ m.mmw_freeze = 0;
+ m.mmw_decay_prm = 0;
+ m.mmw_decay_updat_prm = 0;
+
+ /* Write all info to mmc */
+ mmc_write(base, 0, (u_char *)&m, sizeof(m));
+
+ /* The following code start the modem of the 2.00 frequency
+ * selectable cards at power on. It's not strictly needed for the
+ * following boots...
+ * The original patch was by Joe Finney for the PCMCIA driver, but
+ * I've cleaned it a bit and add documentation.
+ * Thanks to Loeke Brederveld from Lucent for the info.
+ */
+
+ /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+ * (does it work for everybody ??? - especially old cards...) */
+ /* Note : WFREQSEL verify that it is able to read from EEprom
+ * a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID
+ * is 0xA (Xilinx version) or 0xB (Ariadne version).
+ * My test is more crude but do work... */
+ if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+ (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+ {
+ /* We must download the frequency parameters to the
+ * synthetisers (from the EEprom - area 1)
+ * Note : as the EEprom is auto decremented, we set the end
+ * if the area... */
+ m.mmw_fee_addr = 0x0F;
+ m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+ mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m,
+ (unsigned char *)&m.mmw_fee_ctrl, 2);
+
+ /* Wait until the download is finished */
+ fee_wait(base, 100, 100);
+
+#ifdef DEBUG_CONFIG_INFO
+ /* The frequency was in the last word downloaded... */
+ mmc_read(base, (char *)&m.mmw_fee_data_l - (char *)&m,
+ (unsigned char *)&m.mmw_fee_data_l, 2);
+
+ /* Print some info for the user */
+ printk(KERN_DEBUG "%s: Wavelan 2.00 recognised (frequency select) : Current frequency = %ld\n",
+ dev->name,
+ ((m.mmw_fee_data_h << 4) |
+ (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L);
+#endif
+
+ /* We must now download the power adjust value (gain) to
+ * the synthetisers (from the EEprom - area 7 - DAC) */
+ m.mmw_fee_addr = 0x61;
+ m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+ mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m,
+ (unsigned char *)&m.mmw_fee_ctrl, 2);
+
+ /* Wait until the download is finished */
+ } /* if 2.00 card */
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name);
+#endif
+ return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to gracefully turn off reception, and wait for any commands
+ * to complete.
+ * (called in wv_ru_start() and wavelan_close() and wavelan_event())
+ */
+static int
+wv_ru_stop(device * dev)
+{
+ ioaddr_t base = dev->base_addr;
+ net_local * lp = (net_local *) dev->priv;
+ unsigned long flags;
+ int status;
+ int spin;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name);
+#endif
+
+ wv_splhi(lp, &flags);
+
+ /* First, send the LAN controller a stop receive command */
+ wv_82593_cmd(dev, "wv_graceful_shutdown(): stop-rcv",
+ OP0_STOP_RCV, SR0_NO_RESULT);
+
+ /* Then, spin until the receive unit goes idle */
+ spin = 300;
+ do
+ {
+ udelay(10);
+ outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+ status = inb(LCSR(base));
+ }
+ while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin-- > 0));
+
+ /* Now, spin until the chip finishes executing its current command */
+ do
+ {
+ udelay(10);
+ outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+ status = inb(LCSR(base));
+ }
+ while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0));
+
+ wv_splx(lp, &flags);
+
+ /* If there was a problem */
+ if(spin <= 0)
+ {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO "%s: wv_ru_stop(): The chip doesn't want to stop...\n",
+ dev->name);
+#endif
+ return FALSE;
+ }
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_ru_stop()\n", dev->name);
+#endif
+ return TRUE;
+} /* wv_ru_stop */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine starts the receive unit running. First, it checks if
+ * the card is actually ready. Then the card is instructed to receive
+ * packets again.
+ * (called in wv_hw_reset() & wavelan_open())
+ */
+static int
+wv_ru_start(device * dev)
+{
+ ioaddr_t base = dev->base_addr;
+ net_local * lp = (net_local *) dev->priv;
+ unsigned long flags;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
+#endif
+
+ /*
+ * We need to start from a quiescent state. To do so, we could check
+ * if the card is already running, but instead we just try to shut
+ * it down. First, we disable reception (in case it was already enabled).
+ */
+ if(!wv_ru_stop(dev))
+ return FALSE;
+
+ wv_splhi(lp, &flags);
+
+ /* Now we know that no command is being executed. */
+
+ /* Set the receive frame pointer and stop pointer */
+ lp->rfp = 0;
+ outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base));
+
+ /* Reset ring management. This sets the receive frame pointer to 1 */
+ outb(OP1_RESET_RING_MNGMT, LCCR(base));
+
+#if 0
+ /* XXX the i82593 manual page 6-4 seems to indicate that the stop register
+ should be set as below */
+ /* outb(CR1_STOP_REG_UPDATE|((RX_SIZE - 0x40)>> RX_SIZE_SHIFT),LCCR(base));*/
+#elif 0
+ /* but I set it 0 instead */
+ lp->stop = 0;
+#else
+ /* but I set it to 3 bytes per packet less than 8K */
+ lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
+#endif
+ outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base));
+ outb(OP1_INT_ENABLE, LCCR(base));
+ outb(OP1_SWIT_TO_PORT_0, LCCR(base));
+
+ /* Reset receive DMA pointer */
+ hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+ hacr_write_slow(base, HACR_DEFAULT);
+
+ /* Receive DMA on channel 1 */
+ wv_82593_cmd(dev, "wv_ru_start(): rcv-enable",
+ CR0_CHNL | OP0_RCV_ENABLE, SR0_NO_RESULT);
+
+#ifdef DEBUG_I82593_SHOW
+ {
+ int status;
+ int opri;
+ int spin = 10000;
+
+ /* spin until the chip starts receiving */
+ do
+ {
+ outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+ status = inb(LCSR(base));
+ if(spin-- <= 0)
+ break;
+ }
+ while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_ACTIVE) &&
+ ((status & SR3_RCV_STATE_MASK) != SR3_RCV_READY));
+ printk(KERN_DEBUG "rcv status is 0x%x [i:%d]\n",
+ (status & SR3_RCV_STATE_MASK), i);
+ }
+#endif
+
+ wv_splx(lp, &flags);
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
+#endif
+ return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a standard config of the WaveLAN controller (i82593).
+ * In the ISA driver, this is integrated in wavelan_hardware_reset()
+ * (called by wv_hw_config(), wv_82593_reconfig() & wavelan_packet_xmit())
+ */
+static int
+wv_82593_config(device * dev)
+{
+ ioaddr_t base = dev->base_addr;
+ net_local * lp = (net_local *) dev->priv;
+ struct i82593_conf_block cfblk;
+ int ret = TRUE;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_82593_config()\n", dev->name);
+#endif
+
+ /* Create & fill i82593 config block
+ *
+ * Now conform to Wavelan document WCIN085B
+ */
+ memset(&cfblk, 0x00, sizeof(struct i82593_conf_block));
+ cfblk.d6mod = FALSE; /* Run in i82593 advanced mode */
+ cfblk.fifo_limit = 5; /* = 56 B rx and 40 B tx fifo thresholds */
+ cfblk.forgnesi = FALSE; /* 0=82C501, 1=AMD7992B compatibility */
+ cfblk.fifo_32 = 1;
+ cfblk.throttle_enb = FALSE;
+ cfblk.contin = TRUE; /* enable continuous mode */
+ cfblk.cntrxint = FALSE; /* enable continuous mode receive interrupts */
+ cfblk.addr_len = WAVELAN_ADDR_SIZE;
+ cfblk.acloc = TRUE; /* Disable source addr insertion by i82593 */
+ cfblk.preamb_len = 0; /* 2 bytes preamble (SFD) */
+ cfblk.loopback = FALSE;
+ cfblk.lin_prio = 0; /* conform to 802.3 backoff algoritm */
+ cfblk.exp_prio = 5; /* conform to 802.3 backoff algoritm */
+ cfblk.bof_met = 1; /* conform to 802.3 backoff algoritm */
+ cfblk.ifrm_spc = 0x20; /* 32 bit times interframe spacing */
+ cfblk.slottim_low = 0x20; /* 32 bit times slot time */
+ cfblk.slottim_hi = 0x0;
+ cfblk.max_retr = 15;
+ cfblk.prmisc = ((lp->promiscuous) ? TRUE: FALSE); /* Promiscuous mode */
+ cfblk.bc_dis = FALSE; /* Enable broadcast reception */
+ cfblk.crs_1 = TRUE; /* Transmit without carrier sense */
+ cfblk.nocrc_ins = FALSE; /* i82593 generates CRC */
+ cfblk.crc_1632 = FALSE; /* 32-bit Autodin-II CRC */
+ cfblk.crs_cdt = FALSE; /* CD not to be interpreted as CS */
+ cfblk.cs_filter = 0; /* CS is recognized immediately */
+ cfblk.crs_src = FALSE; /* External carrier sense */
+ cfblk.cd_filter = 0; /* CD is recognized immediately */
+ cfblk.min_fr_len = ETH_ZLEN >> 2; /* Minimum frame length 64 bytes */
+ cfblk.lng_typ = FALSE; /* Length field > 1500 = type field */
+ cfblk.lng_fld = TRUE; /* Disable 802.3 length field check */
+ cfblk.rxcrc_xf = TRUE; /* Don't transfer CRC to memory */
+ cfblk.artx = TRUE; /* Disable automatic retransmission */
+ cfblk.sarec = TRUE; /* Disable source addr trig of CD */
+ cfblk.tx_jabber = TRUE; /* Disable jabber jam sequence */
+ cfblk.hash_1 = FALSE; /* Use bits 0-5 in mc address hash */
+ cfblk.lbpkpol = TRUE; /* Loopback pin active high */
+ cfblk.fdx = FALSE; /* Disable full duplex operation */
+ cfblk.dummy_6 = 0x3f; /* all ones */
+ cfblk.mult_ia = FALSE; /* No multiple individual addresses */
+ cfblk.dis_bof = FALSE; /* Disable the backoff algorithm ?! */
+ cfblk.dummy_1 = TRUE; /* set to 1 */
+ cfblk.tx_ifs_retrig = 3; /* Hmm... Disabled */
+#ifdef MULTICAST_ALL
+ cfblk.mc_all = (lp->allmulticast ? TRUE: FALSE); /* Allow all multicasts */
+#else
+ cfblk.mc_all = FALSE; /* No multicast all mode */
+#endif
+ cfblk.rcv_mon = 0; /* Monitor mode disabled */
+ cfblk.frag_acpt = TRUE; /* Do not accept fragments */
+ cfblk.tstrttrs = FALSE; /* No start transmission threshold */
+ cfblk.fretx = TRUE; /* FIFO automatic retransmission */
+ cfblk.syncrqs = FALSE; /* Synchronous DRQ deassertion... */
+ cfblk.sttlen = TRUE; /* 6 byte status registers */
+ cfblk.rx_eop = TRUE; /* Signal EOP on packet reception */
+ cfblk.tx_eop = TRUE; /* Signal EOP on packet transmission */
+ cfblk.rbuf_size = RX_SIZE>>11; /* Set receive buffer size */
+ cfblk.rcvstop = TRUE; /* Enable Receive Stop Register */
+
+#ifdef DEBUG_I82593_SHOW
+ {
+ u_char *c = (u_char *) &cfblk;
+ int i;
+ printk(KERN_DEBUG "wavelan_cs: config block:");
+ for(i = 0; i < sizeof(struct i82593_conf_block); i++,c++)
+ {
+ if((i % 16) == 0) printk("\n" KERN_DEBUG);
+ printk("%02x ", *c);
+ }
+ printk("\n");
+ }
+#endif
+
+ /* Copy the config block to the i82593 */
+ outb(TX_BASE & 0xff, PIORL(base));
+ outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+ outb(sizeof(struct i82593_conf_block) & 0xff, PIOP(base)); /* lsb */
+ outb(sizeof(struct i82593_conf_block) >> 8, PIOP(base)); /* msb */
+ outsb(PIOP(base), (char *) &cfblk, sizeof(struct i82593_conf_block));
+
+ /* reset transmit DMA pointer */
+ hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+ hacr_write(base, HACR_DEFAULT);
+ if(!wv_82593_cmd(dev, "wv_82593_config(): configure",
+ OP0_CONFIGURE, SR0_CONFIGURE_DONE))
+ ret = FALSE;
+
+ /* Initialize adapter's ethernet MAC address */
+ outb(TX_BASE & 0xff, PIORL(base));
+ outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+ outb(WAVELAN_ADDR_SIZE, PIOP(base)); /* byte count lsb */
+ outb(0, PIOP(base)); /* byte count msb */
+ outsb(PIOP(base), &dev->dev_addr[0], WAVELAN_ADDR_SIZE);
+
+ /* reset transmit DMA pointer */
+ hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+ hacr_write(base, HACR_DEFAULT);
+ if(!wv_82593_cmd(dev, "wv_82593_config(): ia-setup",
+ OP0_IA_SETUP, SR0_IA_SETUP_DONE))
+ ret = FALSE;
+
+#ifdef WAVELAN_ROAMING
+ /* If roaming is enabled, join the "Beacon Request" multicast group... */
+ /* But only if it's not in there already! */
+ if(do_roaming)
+ dev_mc_add(dev,WAVELAN_BEACON_ADDRESS, WAVELAN_ADDR_SIZE, 1);
+#endif /* WAVELAN_ROAMING */
+
+ /* If any multicast address to set */
+ if(lp->mc_count)
+ {
+ struct dev_mc_list * dmi;
+ int addrs_len = WAVELAN_ADDR_SIZE * lp->mc_count;
+
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG "%s: wv_hw_config(): set %d multicast addresses:\n",
+ dev->name, lp->mc_count);
+ for(dmi=dev->mc_list; dmi; dmi=dmi->next)
+ printk(KERN_DEBUG " %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dmi->dmi_addr[0], dmi->dmi_addr[1], dmi->dmi_addr[2],
+ dmi->dmi_addr[3], dmi->dmi_addr[4], dmi->dmi_addr[5] );
+#endif
+
+ /* Initialize adapter's ethernet multicast addresses */
+ outb(TX_BASE & 0xff, PIORL(base));
+ outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+ outb(addrs_len & 0xff, PIOP(base)); /* byte count lsb */
+ outb((addrs_len >> 8), PIOP(base)); /* byte count msb */
+ for(dmi=dev->mc_list; dmi; dmi=dmi->next)
+ outsb(PIOP(base), dmi->dmi_addr, dmi->dmi_addrlen);
+
+ /* reset transmit DMA pointer */
+ hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+ hacr_write(base, HACR_DEFAULT);
+ if(!wv_82593_cmd(dev, "wv_82593_config(): mc-setup",
+ OP0_MC_SETUP, SR0_MC_SETUP_DONE))
+ ret = FALSE;
+ lp->mc_count = dev->mc_count; /* remember to avoid repeated reset */
+ }
+
+ /* Job done, clear the flag */
+ lp->reconfig_82593 = FALSE;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name);
+#endif
+ return(ret);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read the Access Configuration Register, perform a software reset,
+ * and then re-enable the card's software.
+ *
+ * If I understand correctly : reset the pcmcia interface of the
+ * wavelan.
+ * (called by wv_config())
+ */
+static inline int
+wv_pcmcia_reset(device * dev)
+{
+ int i;
+ conf_reg_t reg = { 0, CS_READ, CISREG_COR, 0 };
+ dev_link_t * link = ((net_local *) dev->priv)->link;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_pcmcia_reset()\n", dev->name);
+#endif
+
+ i = CardServices(AccessConfigurationRegister, link->handle, ®);
+ if(i != CS_SUCCESS)
+ {
+ cs_error(link->handle, AccessConfigurationRegister, i);
+ return FALSE;
+ }
+
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG "%s: wavelan_pcmcia_reset(): Config reg is 0x%x\n",
+ dev->name, (u_int) reg.Value);
+#endif
+
+ reg.Action = CS_WRITE;
+ reg.Value = reg.Value | COR_SW_RESET;
+ i = CardServices(AccessConfigurationRegister, link->handle, ®);
+ if(i != CS_SUCCESS)
+ {
+ cs_error(link->handle, AccessConfigurationRegister, i);
+ return FALSE;
+ }
+
+ reg.Action = CS_WRITE;
+ reg.Value = COR_LEVEL_IRQ | COR_CONFIG;
+ i = CardServices(AccessConfigurationRegister, link->handle, ®);
+ if(i != CS_SUCCESS)
+ {
+ cs_error(link->handle, AccessConfigurationRegister, i);
+ return FALSE;
+ }
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_pcmcia_reset()\n", dev->name);
+#endif
+ return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * wavelan_hw_config() is called after a CARD_INSERTION event is
+ * received, to configure the wavelan hardware.
+ * Note that the reception will be enabled in wavelan->open(), so the
+ * device is configured but idle...
+ * Performs the following actions:
+ * 1. A pcmcia software reset (using wv_pcmcia_reset())
+ * 2. A power reset (reset DMA)
+ * 3. Reset the LAN controller
+ * 4. Initialize the radio modem (using wv_mmc_init)
+ * 5. Configure LAN controller (using wv_82593_config)
+ * 6. Perform a diagnostic on the LAN controller
+ * (called by wavelan_event() & wv_hw_reset())
+ */
+static int
+wv_hw_config(device * dev)
+{
+ net_local * lp = (net_local *) dev->priv;
+ ioaddr_t base = dev->base_addr;
+ unsigned long flags;
+ int ret = FALSE;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_hw_config()\n", dev->name);
+#endif
+
+#ifdef STRUCT_CHECK
+ if(wv_structuct_check() != (char *) NULL)
+ {
+ printk(KERN_WARNING "%s: wv_hw_config: structure/compiler botch: \"%s\"\n",
+ dev->name, wv_structuct_check());
+ return FALSE;
+ }
+#endif /* STRUCT_CHECK == 1 */
+
+ /* Reset the pcmcia interface */
+ if(wv_pcmcia_reset(dev) == FALSE)
+ return FALSE;
+
+ /* Disable interrupts */
+ wv_splhi(lp, &flags);
+
+ /* Disguised goto ;-) */
+ do
+ {
+ /* Power UP the module + reset the modem + reset host adapter
+ * (in fact, reset DMA channels) */
+ hacr_write_slow(base, HACR_RESET);
+ hacr_write(base, HACR_DEFAULT);
+
+ /* Check if the module has been powered up... */
+ if(hasr_read(base) & HASR_NO_CLK)
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_WARNING "%s: wv_hw_config(): modem not connected or not a wavelan card\n",
+ dev->name);
+#endif
+ break;
+ }
+
+ /* initialize the modem */
+ if(wv_mmc_init(dev) == FALSE)
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_WARNING "%s: wv_hw_config(): Can't configure the modem\n",
+ dev->name);
+#endif
+ break;
+ }
+
+ /* reset the LAN controller (i82593) */
+ outb(OP0_RESET, LCCR(base));
+ mdelay(1); /* A bit crude ! */
+
+ /* Initialize the LAN controller */
+ if(wv_82593_config(dev) == FALSE)
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n",
+ dev->name);
+#endif
+ break;
+ }
+
+ /* Diagnostic */
+ if(wv_diag(dev) == FALSE)
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_INFO "%s: wv_hw_config(): i82593 diagnostic failed\n",
+ dev->name);
+#endif
+ break;
+ }
+
+ /*
+ * insert code for loopback test here
+ */
+
+ /* The device is now configured */
+ lp->configured = 1;
+ ret = TRUE;
+ }
+ while(0);
+
+ /* Re-enable interrupts */
+ wv_splx(lp, &flags);
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name);
+#endif
+ return(ret);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Totally reset the wavelan and restart it.
+ * Performs the following actions:
+ * 1. Call wv_hw_config()
+ * 2. Start the LAN controller's receive unit
+ * (called by wavelan_event(), wavelan_watchdog() and wavelan_open())
+ */
+static inline void
+wv_hw_reset(device * dev)
+{
+ net_local * lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_hw_reset()\n", dev->name);
+#endif
+
+ lp->nresets++;
+ lp->configured = 0;
+
+ /* Call wv_hw_config() for most of the reset & init stuff */
+ if(wv_hw_config(dev) == FALSE)
+ return;
+
+ /* start receive unit */
+ wv_ru_start(dev);
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * wv_pcmcia_config() is called after a CARD_INSERTION event is
+ * received, to configure the PCMCIA socket, and to make the ethernet
+ * device available to the system.
+ * (called by wavelan_event())
+ */
+static inline int
+wv_pcmcia_config(dev_link_t * link)
+{
+ client_handle_t handle;
+ tuple_t tuple;
+ cisparse_t parse;
+ struct net_device * dev;
+ int i;
+ u_char buf[64];
+ win_req_t req;
+ memreq_t mem;
+
+ handle = link->handle;
+ dev = (device *) link->priv;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "->wv_pcmcia_config(0x%p)\n", link);
+#endif
+
+ /*
+ * This reads the card's CONFIG tuple to find its configuration
+ * registers.
+ */
+ do
+ {
+ tuple.Attributes = 0;
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ i = CardServices(GetFirstTuple, handle, &tuple);
+ if(i != CS_SUCCESS)
+ break;
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ i = CardServices(GetTupleData, handle, &tuple);
+ if(i != CS_SUCCESS)
+ break;
+ i = CardServices(ParseTuple, handle, &tuple, &parse);
+ if(i != CS_SUCCESS)
+ break;
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+ }
+ while(0);
+ if(i != CS_SUCCESS)
+ {
+ cs_error(link->handle, ParseTuple, i);
+ link->state &= ~DEV_CONFIG_PENDING;
+ return FALSE;
+ }
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+ do
+ {
+ i = CardServices(RequestIO, link->handle, &link->io);
+ if(i != CS_SUCCESS)
+ {
+ cs_error(link->handle, RequestIO, i);
+ break;
+ }
+
+ /*
+ * Now allocate an interrupt line. Note that this does not
+ * actually assign a handler to the interrupt.
+ */
+ i = CardServices(RequestIRQ, link->handle, &link->irq);
+ if(i != CS_SUCCESS)
+ {
+ cs_error(link->handle, RequestIRQ, i);
+ break;
+ }
+
+ /*
+ * This actually configures the PCMCIA socket -- setting up
+ * the I/O windows and the interrupt mapping.
+ */
+ link->conf.ConfigIndex = 1;
+ i = CardServices(RequestConfiguration, link->handle, &link->conf);
+ if(i != CS_SUCCESS)
+ {
+ cs_error(link->handle, RequestConfiguration, i);
+ break;
+ }
+
+ /*
+ * Allocate a small memory window. Note that the dev_link_t
+ * structure provides space for one window handle -- if your
+ * device needs several windows, you'll need to keep track of
+ * the handles in your private data structure, link->priv.
+ */
+ req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+ req.Base = req.Size = 0;
+ req.AccessSpeed = mem_speed;
+ link->win = (window_handle_t)link->handle;
+ i = CardServices(RequestWindow, &link->win, &req);
+ if(i != CS_SUCCESS)
+ {
+ cs_error(link->handle, RequestWindow, i);
+ break;
+ }
+
+ dev->rmem_start = dev->mem_start =
+ (u_long)ioremap(req.Base, req.Size);
+ dev->rmem_end = dev->mem_end = dev->mem_start + req.Size;
+
+ mem.CardOffset = 0; mem.Page = 0;
+ i = CardServices(MapMemPage, link->win, &mem);
+ if(i != CS_SUCCESS)
+ {
+ cs_error(link->handle, MapMemPage, i);
+ break;
+ }
+
+ /* Feed device with this info... */
+ dev->irq = link->irq.AssignedIRQ;
+ dev->base_addr = link->io.BasePort1;
+ netif_start_queue(dev);
+
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG "wv_pcmcia_config: MEMSTART 0x%x IRQ %d IOPORT 0x%x\n",
+ (u_int) dev->mem_start, dev->irq, (u_int) dev->base_addr);
+#endif
+
+ i = register_netdev(dev);
+ if(i != 0)
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_INFO "wv_pcmcia_config(): register_netdev() failed\n");
+#endif
+ break;
+ }
+ }
+ while(0); /* Humm... Disguised goto !!! */
+
+ link->state &= ~DEV_CONFIG_PENDING;
+ /* If any step failed, release any partially configured state */
+ if(i != 0)
+ {
+ wv_pcmcia_release((u_long) link);
+ return FALSE;
+ }
+
+ strcpy(((net_local *) dev->priv)->node.dev_name, dev->name);
+ link->dev = &((net_local *) dev->priv)->node;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "<-wv_pcmcia_config()\n");
+#endif
+ return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * After a card is removed, wv_pcmcia_release() will unregister the net
+ * device, and release the PCMCIA configuration. If the device is
+ * still open, this will be postponed until it is closed.
+ */
+static void
+wv_pcmcia_release(u_long arg) /* Address of the interface struct */
+{
+ dev_link_t * link = (dev_link_t *) arg;
+ device * dev = (device *) link->priv;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link);
+#endif
+
+ /* If the device is currently in use, we won't release until it is
+ * actually closed. */
+ if(link->open)
+ {
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG "%s: wv_pcmcia_release: release postponed, device still open\n",
+ dev->name);
+#endif
+ link->state |= DEV_STALE_CONFIG;
+ return;
+ }
+
+ /* Don't bother checking to see if these succeed or not */
+ iounmap((u_char *)dev->mem_start);
+ CardServices(ReleaseWindow, link->win);
+ CardServices(ReleaseConfiguration, link->handle);
+ CardServices(ReleaseIO, link->handle, &link->io);
+ CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+ link->state &= ~(DEV_CONFIG | DEV_STALE_CONFIG);
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name);
+#endif
+} /* wv_pcmcia_release */
+
+/*------------------------------------------------------------------*/
+/*
+ * Sometimes, wavelan_detach can't be performed following a call from
+ * cardmgr (device still open, pcmcia_release not done) and the device
+ * is put in a STALE_LINK state and remains in memory.
+ *
+ * This function run through our current list of device and attempt
+ * another time to remove them. We hope that since last time the
+ * device has properly been closed.
+ *
+ * (called by wavelan_attach() & cleanup_module())
+ */
+static void
+wv_flush_stale_links(void)
+{
+ dev_link_t * link; /* Current node in linked list */
+ dev_link_t * next; /* Next node in linked list */
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "-> wv_flush_stale_links(0x%p)\n", dev_list);
+#endif
+
+ /* Go through the list */
+ for (link = dev_list; link; link = next)
+ {
+ next = link->next;
+
+ /* Check if in need of being removed */
+ if((link->state & DEV_STALE_LINK) ||
+ (! (link->state & DEV_PRESENT)))
+ wavelan_detach(link);
+
+ }
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "<- wv_flush_stale_links()\n");
+#endif
+}
+
+/************************ INTERRUPT HANDLING ************************/
+
+/*
+ * This function is the interrupt handler for the WaveLAN card. This
+ * routine will be called whenever:
+ * 1. A packet is received.
+ * 2. A packet has successfully been transferred and the unit is
+ * ready to transmit another packet.
+ * 3. A command has completed execution.
+ */
+static void
+wavelan_interrupt(int irq,
+ void * dev_id,
+ struct pt_regs * regs)
+{
+ device * dev;
+ net_local * lp;
+ ioaddr_t base;
+ int status0;
+ u_int tx_status;
+
+ if((dev = (device *)dev_id) == (device *) NULL)
+ {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_WARNING "wavelan_interrupt(): irq %d for unknown device.\n",
+ irq);
+#endif
+ return;
+ }
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
+#endif
+
+ lp = (net_local *) dev->priv;
+ base = dev->base_addr;
+
+#ifdef DEBUG_INTERRUPT_INFO
+ /* Check state of our spinlock (it should be cleared) */
+ if(spin_is_locked(&lp->spinlock))
+ printk(KERN_DEBUG
+ "%s: wavelan_interrupt(): spinlock is already locked !!!\n",
+ dev->name);
+#endif
+
+ /* Prevent reentrancy. We need to do that because we may have
+ * multiple interrupt handler running concurently.
+ * It is safe because wv_splhi() disable interrupts before aquiring
+ * the spinlock. */
+ spin_lock(&lp->spinlock);
+
+ /* Treat all pending interrupts */
+ while(1)
+ {
+ /* ---------------- INTERRUPT CHECKING ---------------- */
+ /*
+ * Look for the interrupt and verify the validity
+ */
+ outb(CR0_STATUS_0 | OP0_NOP, LCCR(base));
+ status0 = inb(LCSR(base));
+
+#ifdef DEBUG_INTERRUPT_INFO
+ printk(KERN_DEBUG "status0 0x%x [%s => 0x%x]", status0,
+ (status0&SR0_INTERRUPT)?"int":"no int",status0&~SR0_INTERRUPT);
+ if(status0&SR0_INTERRUPT)
+ {
+ printk(" [%s => %d]\n", (status0 & SR0_CHNL) ? "chnl" :
+ ((status0 & SR0_EXECUTION) ? "cmd" :
+ ((status0 & SR0_RECEPTION) ? "recv" : "unknown")),
+ (status0 & SR0_EVENT_MASK));
+ }
+ else
+ printk("\n");
+#endif
+
+ /* Return if no actual interrupt from i82593 (normal exit) */
+ if(!(status0 & SR0_INTERRUPT))
+ break;
+
+ /* If interrupt is both Rx and Tx or none...
+ * This code in fact is there to catch the spurious interrupt
+ * when you remove the wavelan pcmcia card from the socket */
+ if(((status0 & SR0_BOTH_RX_TX) == SR0_BOTH_RX_TX) ||
+ ((status0 & SR0_BOTH_RX_TX) == 0x0))
+ {
+#ifdef DEBUG_INTERRUPT_INFO
+ printk(KERN_INFO "%s: wv_interrupt(): bogus interrupt (or from dead card) : %X\n",
+ dev->name, status0);
+#endif
+ /* Acknowledge the interrupt */
+ outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
+ break;
+ }
+
+ /* ----------------- RECEIVING PACKET ----------------- */
+ /*
+ * When the wavelan signal the reception of a new packet,
+ * we call wv_packet_rcv() to copy if from the buffer and
+ * send it to NET3
+ */
+ if(status0 & SR0_RECEPTION)
+ {
+#ifdef DEBUG_INTERRUPT_INFO
+ printk(KERN_DEBUG "%s: wv_interrupt(): receive\n", dev->name);
+#endif
+
+ if((status0 & SR0_EVENT_MASK) == SR0_STOP_REG_HIT)
+ {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO "%s: wv_interrupt(): receive buffer overflow\n",
+ dev->name);
+#endif
+ lp->stats.rx_over_errors++;
+ lp->overrunning = 1;
+ }
+
+ /* Get the packet */
+ wv_packet_rcv(dev);
+ lp->overrunning = 0;
+
+ /* Acknowledge the interrupt */
+ outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
+ continue;
+ }
+
+ /* ---------------- COMMAND COMPLETION ---------------- */
+ /*
+ * Interrupts issued when the i82593 has completed a command.
+ * Most likely : transmission done
+ */
+
+ /* If a transmission has been done */
+ if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE ||
+ (status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE ||
+ (status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE)
+ {
+#ifdef DEBUG_TX_ERROR
+ if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE)
+ printk(KERN_INFO "%s: wv_interrupt(): packet transmitted without CRC.\n",
+ dev->name);
+#endif
+
+ /* Get transmission status */
+ tx_status = inb(LCSR(base));
+ tx_status |= (inb(LCSR(base)) << 8);
+#ifdef DEBUG_INTERRUPT_INFO
+ printk(KERN_DEBUG "%s: wv_interrupt(): transmission done\n",
+ dev->name);
+ {
+ u_int rcv_bytes;
+ u_char status3;
+ rcv_bytes = inb(LCSR(base));
+ rcv_bytes |= (inb(LCSR(base)) << 8);
+ status3 = inb(LCSR(base));
+ printk(KERN_DEBUG "tx_status 0x%02x rcv_bytes 0x%02x status3 0x%x\n",
+ tx_status, rcv_bytes, (u_int) status3);
+ }
+#endif
+ /* Check for possible errors */
+ if((tx_status & TX_OK) != TX_OK)
+ {
+ lp->stats.tx_errors++;
+
+ if(tx_status & TX_FRTL)
+ {
+#ifdef DEBUG_TX_ERROR
+ printk(KERN_INFO "%s: wv_interrupt(): frame too long\n",
+ dev->name);
+#endif
+ }
+ if(tx_status & TX_UND_RUN)
+ {
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG "%s: wv_interrupt(): DMA underrun\n",
+ dev->name);
+#endif
+ lp->stats.tx_aborted_errors++;
+ }
+ if(tx_status & TX_LOST_CTS)
+ {
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG "%s: wv_interrupt(): no CTS\n", dev->name);
+#endif
+ lp->stats.tx_carrier_errors++;
+ }
+ if(tx_status & TX_LOST_CRS)
+ {
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG "%s: wv_interrupt(): no carrier\n",
+ dev->name);
+#endif
+ lp->stats.tx_carrier_errors++;
+ }
+ if(tx_status & TX_HRT_BEAT)
+ {
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG "%s: wv_interrupt(): heart beat\n", dev->name);
+#endif
+ lp->stats.tx_heartbeat_errors++;
+ }
+ if(tx_status & TX_DEFER)
+ {
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG "%s: wv_interrupt(): channel jammed\n",
+ dev->name);
+#endif
+ }
+ /* Ignore late collisions since they're more likely to happen
+ * here (the WaveLAN design prevents the LAN controller from
+ * receiving while it is transmitting). We take action only when
+ * the maximum retransmit attempts is exceeded.
+ */
+ if(tx_status & TX_COLL)
+ {
+ if(tx_status & TX_MAX_COL)
+ {
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG "%s: wv_interrupt(): channel congestion\n",
+ dev->name);
+#endif
+ if(!(tx_status & TX_NCOL_MASK))
+ {
+ lp->stats.collisions += 0x10;
+ }
+ }
+ }
+ } /* if(!(tx_status & TX_OK)) */
+
+ lp->stats.collisions += (tx_status & TX_NCOL_MASK);
+ lp->stats.tx_packets++;
+
+ netif_wake_queue(dev);
+ outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */
+ }
+ else /* if interrupt = transmit done or retransmit done */
+ {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO "wavelan_cs: unknown interrupt, status0 = %02x\n",
+ status0);
+#endif
+ outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */
+ }
+ } /* while(1) */
+
+ spin_unlock(&lp->spinlock);
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
+#endif
+} /* wv_interrupt */
+
+/*------------------------------------------------------------------*/
+/*
+ * Watchdog: when we start a transmission, a timer is set for us in the
+ * kernel. If the transmission completes, this timer is disabled. If
+ * the timer expires, we are called and we try to unlock the hardware.
+ *
+ * Note : This watchdog is move clever than the one in the ISA driver,
+ * because it try to abort the current command before reseting
+ * everything...
+ * On the other hand, it's a bit simpler, because we don't have to
+ * deal with the multiple Tx buffers...
+ */
+static void
+wavelan_watchdog(device * dev)
+{
+ net_local * lp = (net_local *) dev->priv;
+ ioaddr_t base = dev->base_addr;
+ unsigned long flags;
+ int aborted = FALSE;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
+#endif
+
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n",
+ dev->name);
+#endif
+
+ wv_splhi(lp, &flags);
+
+ /* Ask to abort the current command */
+ outb(OP0_ABORT, LCCR(base));
+
+ /* Wait for the end of the command (a bit hackish) */
+ if(wv_82593_cmd(dev, "wavelan_watchdog(): abort",
+ OP0_NOP | CR0_STATUS_3, SR0_EXECUTION_ABORTED))
+ aborted = TRUE;
+
+ /* Release spinlock here so that wv_hw_reset() can grab it */
+ wv_splx(lp, &flags);
+
+ /* Check if we were successful in aborting it */
+ if(!aborted)
+ {
+ /* It seem that it wasn't enough */
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO "%s: wavelan_watchdog: abort failed, trying reset\n",
+ dev->name);
+#endif
+ wv_hw_reset(dev);
+ }
+
+#ifdef DEBUG_PSA_SHOW
+ {
+ psa_t psa;
+ psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+ wv_psa_show(&psa);
+ }
+#endif
+#ifdef DEBUG_MMC_SHOW
+ wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82593_SHOW
+ wv_ru_show(dev);
+#endif
+
+ /* We are no more waiting for something... */
+ netif_wake_queue(dev);
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
+#endif
+}
+
+/********************* CONFIGURATION CALLBACKS *********************/
+/*
+ * Here are the functions called by the pcmcia package (cardmgr) and
+ * linux networking (NET3) for initialization, configuration and
+ * deinstallations of the Wavelan Pcmcia Hardware.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Configure and start up the WaveLAN PCMCIA adaptor.
+ * Called by NET3 when it "open" the device.
+ */
+static int
+wavelan_open(device * dev)
+{
+ dev_link_t * link = ((net_local *) dev->priv)->link;
+ net_local * lp = (net_local *)dev->priv;
+ ioaddr_t base = dev->base_addr;
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name,
+ (unsigned int) dev);
+#endif
+
+ /* Check if the modem is powered up (wavelan_close() power it down */
+ if(hasr_read(base) & HASR_NO_CLK)
+ {
+ /* Power up (power up time is 250us) */
+ hacr_write(base, HACR_DEFAULT);
+
+ /* Check if the module has been powered up... */
+ if(hasr_read(base) & HASR_NO_CLK)
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_WARNING "%s: wavelan_open(): modem not connected\n",
+ dev->name);
+#endif
+ return FALSE;
+ }
+ }
+
+ /* Start reception and declare the driver ready */
+ if(!lp->configured)
+ return FALSE;
+ if(!wv_ru_start(dev))
+ wv_hw_reset(dev); /* If problem : reset */
+ netif_start_queue(dev);
+
+ /* Mark the device as used */
+ link->open++;
+ MOD_INC_USE_COUNT;
+
+#ifdef WAVELAN_ROAMING
+ if(do_roaming)
+ wv_roam_init(dev);
+#endif /* WAVELAN_ROAMING */
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
+#endif
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Shutdown the WaveLAN PCMCIA adaptor.
+ * Called by NET3 when it "close" the device.
+ */
+static int
+wavelan_close(device * dev)
+{
+ dev_link_t * link = ((net_local *) dev->priv)->link;
+ ioaddr_t base = dev->base_addr;
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name,
+ (unsigned int) dev);
+#endif
+
+ /* If the device isn't open, then nothing to do */
+ if(!link->open)
+ {
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG "%s: wavelan_close(): device not open\n", dev->name);
+#endif
+ return 0;
+ }
+
+#ifdef WAVELAN_ROAMING
+ /* Cleanup of roaming stuff... */
+ if(do_roaming)
+ wv_roam_cleanup(dev);
+#endif /* WAVELAN_ROAMING */
+
+ link->open--;
+ MOD_DEC_USE_COUNT;
+
+ /* If the card is still present */
+ if(netif_running(dev))
+ {
+ netif_stop_queue(dev);
+
+ /* Stop receiving new messages and wait end of transmission */
+ wv_ru_stop(dev);
+
+ /* Power down the module */
+ hacr_write(base, HACR_DEFAULT & (~HACR_PWR_STAT));
+ }
+ else
+ /* The card is no more there (flag is activated in wv_pcmcia_release) */
+ if(link->state & DEV_STALE_CONFIG)
+ wv_pcmcia_release((u_long)link);
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
+#endif
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * wavelan_attach() creates an "instance" of the driver, allocating
+ * local data structures for one device (one interface). The device
+ * is registered with Card Services.
+ *
+ * The dev_link structure is initialized, but we don't actually
+ * configure the card at this point -- we wait until we receive a
+ * card insertion event.
+ */
+static dev_link_t *
+wavelan_attach(void)
+{
+ client_reg_t client_reg; /* Register with cardmgr */
+ dev_link_t * link; /* Info for cardmgr */
+ device * dev; /* Interface generic data */
+ net_local * lp; /* Interface specific data */
+ int i, ret;
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "-> wavelan_attach()\n");
+#endif
+
+ /* Perform some cleanup */
+ wv_flush_stale_links();
+
+ /* Initialize the dev_link_t structure */
+ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+ if (!link) return NULL;
+ memset(link, 0, sizeof(struct dev_link_t));
+
+ /* Unused for the Wavelan */
+ link->release.function = &wv_pcmcia_release;
+ link->release.data = (u_long) link;
+
+ /* The io structure describes IO port mapping */
+ link->io.NumPorts1 = 8;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = 3;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+ if (irq_list[0] == -1)
+ link->irq.IRQInfo2 = irq_mask;
+ else
+ for (i = 0; i < 4; i++)
+ link->irq.IRQInfo2 |= 1 << irq_list[i];
+ link->irq.Handler = wavelan_interrupt;
+
+ /* General socket configuration */
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ /* Chain drivers */
+ link->next = dev_list;
+ dev_list = link;
+
+ /* Allocate the generic data structure */
+ dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+ if (!dev) {
+ kfree(link);
+ return NULL;
+ }
+ memset(dev, 0x00, sizeof(struct net_device));
+ link->priv = link->irq.Instance = dev;
+
+ /* Allocate the wavelan-specific data structure. */
+ dev->priv = lp = (net_local *) kmalloc(sizeof(net_local), GFP_KERNEL);
+ if (!lp) {
+ kfree(link);
+ kfree(dev);
+ return NULL;
+ }
+ memset(lp, 0x00, sizeof(net_local));
+
+ /* Init specific data */
+ lp->configured = 0;
+ lp->reconfig_82593 = FALSE;
+ lp->nresets = 0;
+ /* Multicast stuff */
+ lp->promiscuous = 0;
+ lp->allmulticast = 0;
+ lp->mc_count = 0;
+
+ /* Init spinlock */
+ spin_lock_init(&lp->spinlock);
+
+ /* back links */
+ lp->link = link;
+ lp->dev = dev;
+
+ /* Standard setup for generic data */
+ ether_setup(dev);
+
+ /* wavelan NET3 callbacks */
+ dev->open = &wavelan_open;
+ dev->stop = &wavelan_close;
+ dev->hard_start_xmit = &wavelan_packet_xmit;
+ dev->get_stats = &wavelan_get_stats;
+ dev->set_multicast_list = &wavelan_set_multicast_list;
+#ifdef SET_MAC_ADDRESS
+ dev->set_mac_address = &wavelan_set_mac_address;
+#endif /* SET_MAC_ADDRESS */
+
+ /* Set the watchdog timer */
+ dev->tx_timeout = &wavelan_watchdog;
+ dev->watchdog_timeo = WATCHDOG_JIFFIES;
+
+#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */
+ dev->do_ioctl = wavelan_ioctl; /* wireless extensions */
+ dev->get_wireless_stats = wavelan_get_wireless_stats;
+#endif
+
+ /* Other specific data */
+ dev->mtu = WAVELAN_MTU;
+
+ /* Register with Card Services */
+ client_reg.dev_info = &dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.EventMask =
+ CS_EVENT_REGISTRATION_COMPLETE |
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.event_handler = &wavelan_event;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG "wavelan_attach(): almost done, calling CardServices\n");
+#endif
+
+ ret = CardServices(RegisterClient, &link->handle, &client_reg);
+ if(ret != 0)
+ {
+ cs_error(link->handle, RegisterClient, ret);
+ wavelan_detach(link);
+ return NULL;
+ }
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "<- wavelan_attach()\n");
+#endif
+
+ return link;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This deletes a driver "instance". The device is de-registered with
+ * Card Services. If it has been released, all local data structures
+ * are freed. Otherwise, the structures will be freed when the device
+ * is released.
+ */
+static void
+wavelan_detach(dev_link_t * link)
+{
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "-> wavelan_detach(0x%p)\n", link);
+#endif
+
+ /*
+ * If the device is currently configured and active, we won't
+ * actually delete it yet. Instead, it is marked so that when the
+ * release() function is called, that will trigger a proper
+ * detach().
+ */
+ if(link->state & DEV_CONFIG)
+ {
+ /* Some others haven't done their job : give them another chance */
+ wv_pcmcia_release((u_long) link);
+ if(link->state & DEV_STALE_CONFIG)
+ {
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG "wavelan_detach: detach postponed,"
+ " '%s' still locked\n", link->dev->dev_name);
+#endif
+ link->state |= DEV_STALE_LINK;
+ return;
+ }
+ }
+
+ /* Break the link with Card Services */
+ if(link->handle)
+ CardServices(DeregisterClient, link->handle);
+
+ /* Remove the interface data from the linked list */
+ if(dev_list == link)
+ dev_list = link->next;
+ else
+ {
+ dev_link_t * prev = dev_list;
+
+ while((prev != (dev_link_t *) NULL) && (prev->next != link))
+ prev = prev->next;
+
+ if(prev == (dev_link_t *) NULL)
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_WARNING "wavelan_detach : Attempting to remove a nonexistent device.\n");
+#endif
+ return;
+ }
+
+ prev->next = link->next;
+ }
+
+ /* Free pieces */
+ if(link->priv)
+ {
+ device * dev = (device *) link->priv;
+
+ /* Remove ourselves from the kernel list of ethernet devices */
+ /* Warning : can't be called from interrupt, timer or wavelan_close() */
+ if(link->dev != NULL)
+ unregister_netdev(dev);
+ link->dev = NULL;
+
+ if(dev->priv)
+ {
+ /* Sound strange, but safe... */
+ ((net_local *) dev->priv)->link = (dev_link_t *) NULL;
+ ((net_local *) dev->priv)->dev = (device *) NULL;
+ kfree(dev->priv);
+ }
+ kfree(link->priv);
+ }
+ kfree(link);
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "<- wavelan_detach()\n");
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * The card status event handler. Mostly, this schedules other stuff
+ * to run after an event is received. A CARD_REMOVAL event also sets
+ * some flags to discourage the net drivers from trying to talk to the
+ * card any more.
+ */
+static int
+wavelan_event(event_t event, /* The event received */
+ int priority,
+ event_callback_args_t * args)
+{
+ dev_link_t * link = (dev_link_t *) args->client_data;
+ device * dev = (device *) link->priv;
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "->wavelan_event(): %s\n",
+ ((event == CS_EVENT_REGISTRATION_COMPLETE)?"registration complete" :
+ ((event == CS_EVENT_CARD_REMOVAL) ? "card removal" :
+ ((event == CS_EVENT_CARD_INSERTION) ? "card insertion" :
+ ((event == CS_EVENT_PM_SUSPEND) ? "pm suspend" :
+ ((event == CS_EVENT_RESET_PHYSICAL) ? "physical reset" :
+ ((event == CS_EVENT_PM_RESUME) ? "pm resume" :
+ ((event == CS_EVENT_CARD_RESET) ? "card reset" :
+ "unknown"))))))));
+#endif
+
+ switch(event)
+ {
+ case CS_EVENT_REGISTRATION_COMPLETE:
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG "wavelan_cs: registration complete\n");
+#endif
+ break;
+
+ case CS_EVENT_CARD_REMOVAL:
+ /* Oups ! The card is no more there */
+ link->state &= ~DEV_PRESENT;
+ if(link->state & DEV_CONFIG)
+ {
+ /* Accept no more transmissions */
+ netif_device_detach(dev);
+
+ /* Release the card */
+ wv_pcmcia_release((u_long) link);
+ }
+ break;
+
+ case CS_EVENT_CARD_INSERTION:
+ /* Reset and configure the card */
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ if(wv_pcmcia_config(link) &&
+ wv_hw_config(dev))
+ wv_init_info(dev);
+ else
+ dev->irq = 0;
+ break;
+
+ case CS_EVENT_PM_SUSPEND:
+ /* NB: wavelan_close will be called, but too late, so we are
+ * obliged to close nicely the wavelan here. David, could you
+ * close the device before suspending them ? And, by the way,
+ * could you, on resume, add a "route add -net ..." after the
+ * ifconfig up ??? Thanks... */
+
+ /* Stop receiving new messages and wait end of transmission */
+ wv_ru_stop(dev);
+
+ /* Power down the module */
+ hacr_write(dev->base_addr, HACR_DEFAULT & (~HACR_PWR_STAT));
+
+ /* The card is now suspended */
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if(link->state & DEV_CONFIG)
+ {
+ if(link->open)
+ netif_device_detach(dev);
+ CardServices(ReleaseConfiguration, link->handle);
+ }
+ break;
+
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if(link->state & DEV_CONFIG)
+ {
+ CardServices(RequestConfiguration, link->handle, &link->conf);
+ if(link->open) /* If RESET -> True, If RESUME -> False ??? */
+ {
+ wv_hw_reset(dev);
+ netif_device_attach(dev);
+ }
+ }
+ break;
+ }
+
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "<-wavelan_event()\n");
+#endif
+ return 0;
+}
+
+/****************************** MODULE ******************************/
+/*
+ * Module entry points : insertion & removal
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Module insertion : initialisation of the module.
+ * Register the card with cardmgr...
+ */
+static int __init
+init_wavelan_cs(void)
+{
+ servinfo_t serv;
+
+#ifdef DEBUG_MODULE_TRACE
+ printk(KERN_DEBUG "-> init_wavelan_cs()\n");
+#ifdef DEBUG_VERSION_SHOW
+ printk(KERN_DEBUG "%s", version);
+#endif
+#endif
+
+ CardServices(GetCardServicesInfo, &serv);
+ if(serv.Revision != CS_RELEASE_CODE)
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_WARNING "init_wavelan_cs: Card Services release does not match!\n");
+#endif
+ return -1;
+ }
+
+ register_pccard_driver(&dev_info, &wavelan_attach, &wavelan_detach);
+
+#ifdef DEBUG_MODULE_TRACE
+ printk(KERN_DEBUG "<- init_wavelan_cs()\n");
+#endif
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Module removal
+ */
+static void __exit
+exit_wavelan_cs(void)
+{
+#ifdef DEBUG_MODULE_TRACE
+ printk(KERN_DEBUG "-> cleanup_module()\n");
+#endif
+#ifdef DEBUG_BASIC_SHOW
+ printk(KERN_NOTICE "wavelan_cs: unloading\n");
+#endif
+
+ /* Do some cleanup of the device list */
+ wv_flush_stale_links();
+
+ /* If there remain some devices... */
+#ifdef DEBUG_CONFIG_ERRORS
+ if(dev_list != NULL)
+ {
+ /* Honestly, if this happen we are in a deep s**t */
+ printk(KERN_INFO "wavelan_cs: devices remaining when removing module\n");
+ printk(KERN_INFO "Please flush your disks and reboot NOW !\n");
+ }
+#endif
+
+ unregister_pccard_driver(&dev_info);
+
+#ifdef DEBUG_MODULE_TRACE
+ printk(KERN_DEBUG "<- cleanup_module()\n");
+#endif
+}
+
+module_init(init_wavelan_cs);
+module_exit(exit_wavelan_cs);
--- /dev/null
+/*
+ * Wavelan Pcmcia driver
+ *
+ * Jean II - HPLB '96
+ *
+ * Reorganization and extension of the driver.
+ * Original copyright follow. See wavelan_cs.h for details.
+ *
+ * This file contain the declarations of the Wavelan hardware. Note that
+ * the Pcmcia Wavelan include a i82593 controller (see definitions in
+ * file i82593.h).
+ *
+ * The main difference between the pcmcia hardware and the ISA one is
+ * the Ethernet Controller (i82593 instead of i82586). The i82593 allow
+ * only one send buffer. The PSA (Parameter Storage Area : EEprom for
+ * permanent storage of various info) is memory mapped, but not the
+ * MMI (Modem Management Interface).
+ */
+
+/*
+ * Definitions for the AT&T GIS (formerly NCR) WaveLAN PCMCIA card:
+ * An Ethernet-like radio transceiver controlled by an Intel 82593
+ * coprocessor.
+ *
+ *
+ ****************************************************************************
+ * Copyright 1995
+ * Anthony D. Joseph
+ * Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this program
+ * for any purpose and without fee is hereby granted, provided
+ * that this copyright and permission notice appear on all copies
+ * and supporting documentation, the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * program without specific prior permission, and notice be given
+ * in supporting documentation that copying and distribution is
+ * by permission of M.I.T. M.I.T. makes no representations about
+ * the suitability of this software for any purpose. It is pro-
+ * vided "as is" without express or implied warranty.
+ ****************************************************************************
+ *
+ *
+ * Credits:
+ * Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht for
+ * providing extremely useful information about WaveLAN PCMCIA hardware
+ *
+ * This driver is based upon several other drivers, in particular:
+ * David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
+ * Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
+ * Anders Klemets' PCMCIA WaveLAN adapter driver
+ * Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
+ */
+
+#ifndef _WAVELAN_CS_H
+#define _WAVELAN_CS_H
+
+/************************** MAGIC NUMBERS ***************************/
+
+/* The detection of the wavelan card is made by reading the MAC address
+ * from the card and checking it. If you have a non AT&T product (OEM,
+ * like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this
+ * part to accomodate your hardware...
+ */
+const unsigned char MAC_ADDRESSES[][3] =
+{
+ { 0x08, 0x00, 0x0E }, /* AT&T Wavelan (standard) & DEC RoamAbout */
+ { 0x08, 0x00, 0x6A }, /* AT&T Wavelan (alternate) */
+ { 0x00, 0x00, 0xE1 }, /* Hitachi Wavelan */
+ { 0x00, 0x60, 0x1D } /* Lucent Wavelan (another one) */
+ /* Add your card here and send me the patch ! */
+};
+
+/*
+ * Constants used to convert channels to frequencies
+ */
+
+/* Frequency available in the 2.0 modem, in units of 250 kHz
+ * (as read in the offset register of the dac area).
+ * Used to map channel numbers used by `wfreqsel' to frequencies
+ */
+const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
+ 0xD0, 0xF0, 0xF8, 0x150 };
+
+/* Frequencies of the 1.0 modem (fixed frequencies).
+ * Use to map the PSA `subband' to a frequency
+ * Note : all frequencies apart from the first one need to be multiplied by 10
+ */
+const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
+
+
+/*************************** PC INTERFACE ****************************/
+
+/* WaveLAN host interface definitions */
+
+#define LCCR(base) (base) /* LAN Controller Command Register */
+#define LCSR(base) (base) /* LAN Controller Status Register */
+#define HACR(base) (base+0x1) /* Host Adapter Command Register */
+#define HASR(base) (base+0x1) /* Host Adapter Status Register */
+#define PIORL(base) (base+0x2) /* Program I/O Register Low */
+#define RPLL(base) (base+0x2) /* Receive Pointer Latched Low */
+#define PIORH(base) (base+0x3) /* Program I/O Register High */
+#define RPLH(base) (base+0x3) /* Receive Pointer Latched High */
+#define PIOP(base) (base+0x4) /* Program I/O Port */
+#define MMR(base) (base+0x6) /* MMI Address Register */
+#define MMD(base) (base+0x7) /* MMI Data Register */
+
+/* Host Adaptor Command Register bit definitions */
+
+#define HACR_LOF (1 << 3) /* Lock Out Flag, toggle every 250ms */
+#define HACR_PWR_STAT (1 << 4) /* Power State, 1=active, 0=sleep */
+#define HACR_TX_DMA_RESET (1 << 5) /* Reset transmit DMA ptr on high */
+#define HACR_RX_DMA_RESET (1 << 6) /* Reset receive DMA ptr on high */
+#define HACR_ROM_WEN (1 << 7) /* EEPROM write enabled when true */
+
+#define HACR_RESET (HACR_TX_DMA_RESET | HACR_RX_DMA_RESET)
+#define HACR_DEFAULT (HACR_PWR_STAT)
+
+/* Host Adapter Status Register bit definitions */
+
+#define HASR_MMI_BUSY (1 << 2) /* MMI is busy when true */
+#define HASR_LOF (1 << 3) /* Lock out flag status */
+#define HASR_NO_CLK (1 << 4) /* active when modem not connected */
+
+/* Miscellaneous bit definitions */
+
+#define PIORH_SEL_TX (1 << 5) /* PIOR points to 0=rx/1=tx buffer */
+#define MMR_MMI_WR (1 << 0) /* Next MMI cycle is 0=read, 1=write */
+#define PIORH_MASK 0x1f /* only low 5 bits are significant */
+#define RPLH_MASK 0x1f /* only low 5 bits are significant */
+#define MMI_ADDR_MASK 0x7e /* Bits 1-6 of MMR are significant */
+
+/* Attribute Memory map */
+
+#define CIS_ADDR 0x0000 /* Card Information Status Register */
+#define PSA_ADDR 0x0e00 /* Parameter Storage Area address */
+#define EEPROM_ADDR 0x1000 /* EEPROM address (unused ?) */
+#define COR_ADDR 0x4000 /* Configuration Option Register */
+
+/* Configuration Option Register bit definitions */
+
+#define COR_CONFIG (1 << 0) /* Config Index, 0 when unconfigured */
+#define COR_SW_RESET (1 << 7) /* Software Reset on true */
+#define COR_LEVEL_IRQ (1 << 6) /* Level IRQ */
+
+/* Local Memory map */
+
+#define RX_BASE 0x0000 /* Receive memory, 8 kB */
+#define TX_BASE 0x2000 /* Transmit memory, 2 kB */
+#define UNUSED_BASE 0x2800 /* Unused, 22 kB */
+#define RX_SIZE (TX_BASE-RX_BASE) /* Size of receive area */
+#define RX_SIZE_SHIFT 6 /* Bits to shift in stop register */
+
+#define TRUE 1
+#define FALSE 0
+
+#define MOD_ENAL 1
+#define MOD_PROM 2
+
+/* Size of a MAC address */
+#define WAVELAN_ADDR_SIZE 6
+
+/* Maximum size of Wavelan packet */
+#define WAVELAN_MTU 1500
+
+#define MAXDATAZ (6 + 6 + 2 + WAVELAN_MTU)
+
+/********************** PARAMETER STORAGE AREA **********************/
+
+/*
+ * Parameter Storage Area (PSA).
+ */
+typedef struct psa_t psa_t;
+struct psa_t
+{
+ /* For the PCMCIA Adapter, locations 0x00-0x0F are unused and fixed at 00 */
+ unsigned char psa_io_base_addr_1; /* [0x00] Base address 1 ??? */
+ unsigned char psa_io_base_addr_2; /* [0x01] Base address 2 */
+ unsigned char psa_io_base_addr_3; /* [0x02] Base address 3 */
+ unsigned char psa_io_base_addr_4; /* [0x03] Base address 4 */
+ unsigned char psa_rem_boot_addr_1; /* [0x04] Remote Boot Address 1 */
+ unsigned char psa_rem_boot_addr_2; /* [0x05] Remote Boot Address 2 */
+ unsigned char psa_rem_boot_addr_3; /* [0x06] Remote Boot Address 3 */
+ unsigned char psa_holi_params; /* [0x07] HOst Lan Interface (HOLI) Parameters */
+ unsigned char psa_int_req_no; /* [0x08] Interrupt Request Line */
+ unsigned char psa_unused0[7]; /* [0x09-0x0F] unused */
+
+ unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x10-0x15] Universal (factory) MAC Address */
+ unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x16-1B] Local MAC Address */
+ unsigned char psa_univ_local_sel; /* [0x1C] Universal Local Selection */
+#define PSA_UNIVERSAL 0 /* Universal (factory) */
+#define PSA_LOCAL 1 /* Local */
+ unsigned char psa_comp_number; /* [0x1D] Compatability Number: */
+#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */
+#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */
+#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */
+#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */
+#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz or 2.0 */
+ unsigned char psa_thr_pre_set; /* [0x1E] Modem Threshold Preset */
+ unsigned char psa_feature_select; /* [0x1F] Call code required (1=on) */
+#define PSA_FEATURE_CALL_CODE 0x01 /* Call code required (Japan) */
+ unsigned char psa_subband; /* [0x20] Subband */
+#define PSA_SUBBAND_915 0 /* 915 MHz or 2.0 */
+#define PSA_SUBBAND_2425 1 /* 2425 MHz */
+#define PSA_SUBBAND_2460 2 /* 2460 MHz */
+#define PSA_SUBBAND_2484 3 /* 2484 MHz */
+#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */
+ unsigned char psa_quality_thr; /* [0x21] Modem Quality Threshold */
+ unsigned char psa_mod_delay; /* [0x22] Modem Delay ??? (reserved) */
+ unsigned char psa_nwid[2]; /* [0x23-0x24] Network ID */
+ unsigned char psa_nwid_select; /* [0x25] Network ID Select On Off */
+ unsigned char psa_encryption_select; /* [0x26] Encryption On Off */
+ unsigned char psa_encryption_key[8]; /* [0x27-0x2E] Encryption Key */
+ unsigned char psa_databus_width; /* [0x2F] AT bus width select 8/16 */
+ unsigned char psa_call_code[8]; /* [0x30-0x37] (Japan) Call Code */
+ unsigned char psa_nwid_prefix[2]; /* [0x38-0x39] Roaming domain */
+ unsigned char psa_reserved[2]; /* [0x3A-0x3B] Reserved - fixed 00 */
+ unsigned char psa_conf_status; /* [0x3C] Conf Status, bit 0=1:config*/
+ unsigned char psa_crc[2]; /* [0x3D] CRC-16 over PSA */
+ unsigned char psa_crc_status; /* [0x3F] CRC Valid Flag */
+};
+
+/* Size for structure checking (if padding is correct) */
+#define PSA_SIZE 64
+
+/* Calculate offset of a field in the above structure
+ * Warning : only even addresses are used */
+#define psaoff(p,f) ((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL))
+
+/******************** MODEM MANAGEMENT INTERFACE ********************/
+
+/*
+ * Modem Management Controller (MMC) write structure.
+ */
+typedef struct mmw_t mmw_t;
+struct mmw_t
+{
+ unsigned char mmw_encr_key[8]; /* encryption key */
+ unsigned char mmw_encr_enable; /* enable/disable encryption */
+#define MMW_ENCR_ENABLE_MODE 0x02 /* Mode of security option */
+#define MMW_ENCR_ENABLE_EN 0x01 /* Enable security option */
+ unsigned char mmw_unused0[1]; /* unused */
+ unsigned char mmw_des_io_invert; /* Encryption option */
+#define MMW_DES_IO_INVERT_RES 0x0F /* Reserved */
+#define MMW_DES_IO_INVERT_CTRL 0xF0 /* Control ??? (set to 0) */
+ unsigned char mmw_unused1[5]; /* unused */
+ unsigned char mmw_loopt_sel; /* looptest selection */
+#define MMW_LOOPT_SEL_DIS_NWID 0x40 /* disable NWID filtering */
+#define MMW_LOOPT_SEL_INT 0x20 /* activate Attention Request */
+#define MMW_LOOPT_SEL_LS 0x10 /* looptest w/o collision avoidance */
+#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */
+#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */
+#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */
+#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */
+ unsigned char mmw_jabber_enable; /* jabber timer enable */
+ /* Abort transmissions > 200 ms */
+ unsigned char mmw_freeze; /* freeze / unfreeeze signal level */
+ /* 0 : signal level & qual updated for every new message, 1 : frozen */
+ unsigned char mmw_anten_sel; /* antenna selection */
+#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */
+#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algo. enable */
+ unsigned char mmw_ifs; /* inter frame spacing */
+ /* min time between transmission in bit periods (.5 us) - bit 0 ignored */
+ unsigned char mmw_mod_delay; /* modem delay (synchro) */
+ unsigned char mmw_jam_time; /* jamming time (after collision) */
+ unsigned char mmw_unused2[1]; /* unused */
+ unsigned char mmw_thr_pre_set; /* level threshold preset */
+ /* Discard all packet with signal < this value (4) */
+ unsigned char mmw_decay_prm; /* decay parameters */
+ unsigned char mmw_decay_updat_prm; /* decay update parameterz */
+ unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */
+ /* Discard all packet with quality < this value (3) */
+ unsigned char mmw_netw_id_l; /* NWID low order byte */
+ unsigned char mmw_netw_id_h; /* NWID high order byte */
+ /* Network ID or Domain : create virtual net on the air */
+
+ /* 2.0 Hardware extension - frequency selection support */
+ unsigned char mmw_mode_select; /* for analog tests (set to 0) */
+ unsigned char mmw_unused3[1]; /* unused */
+ unsigned char mmw_fee_ctrl; /* frequency eeprom control */
+#define MMW_FEE_CTRL_PRE 0x10 /* Enable protected instructions */
+#define MMW_FEE_CTRL_DWLD 0x08 /* Download eeprom to mmc */
+#define MMW_FEE_CTRL_CMD 0x07 /* EEprom commands : */
+#define MMW_FEE_CTRL_READ 0x06 /* Read */
+#define MMW_FEE_CTRL_WREN 0x04 /* Write enable */
+#define MMW_FEE_CTRL_WRITE 0x05 /* Write data to address */
+#define MMW_FEE_CTRL_WRALL 0x04 /* Write data to all addresses */
+#define MMW_FEE_CTRL_WDS 0x04 /* Write disable */
+#define MMW_FEE_CTRL_PRREAD 0x16 /* Read addr from protect register */
+#define MMW_FEE_CTRL_PREN 0x14 /* Protect register enable */
+#define MMW_FEE_CTRL_PRCLEAR 0x17 /* Unprotect all registers */
+#define MMW_FEE_CTRL_PRWRITE 0x15 /* Write addr in protect register */
+#define MMW_FEE_CTRL_PRDS 0x14 /* Protect register disable */
+ /* Never issue this command (PRDS) : it's irreversible !!! */
+
+ unsigned char mmw_fee_addr; /* EEprom address */
+#define MMW_FEE_ADDR_CHANNEL 0xF0 /* Select the channel */
+#define MMW_FEE_ADDR_OFFSET 0x0F /* Offset in channel data */
+#define MMW_FEE_ADDR_EN 0xC0 /* FEE_CTRL enable operations */
+#define MMW_FEE_ADDR_DS 0x00 /* FEE_CTRL disable operations */
+#define MMW_FEE_ADDR_ALL 0x40 /* FEE_CTRL all operations */
+#define MMW_FEE_ADDR_CLEAR 0xFF /* FEE_CTRL clear operations */
+
+ unsigned char mmw_fee_data_l; /* Write data to EEprom */
+ unsigned char mmw_fee_data_h; /* high octet */
+ unsigned char mmw_ext_ant; /* Setting for external antenna */
+#define MMW_EXT_ANT_EXTANT 0x01 /* Select external antenna */
+#define MMW_EXT_ANT_POL 0x02 /* Polarity of the antenna */
+#define MMW_EXT_ANT_INTERNAL 0x00 /* Internal antenna */
+#define MMW_EXT_ANT_EXTERNAL 0x03 /* External antenna */
+#define MMW_EXT_ANT_IQ_TEST 0x1C /* IQ test pattern (set to 0) */
+};
+
+/* Size for structure checking (if padding is correct) */
+#define MMW_SIZE 37
+
+/* Calculate offset of a field in the above structure */
+#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+
+
+/*
+ * Modem Management Controller (MMC) read structure.
+ */
+typedef struct mmr_t mmr_t;
+struct mmr_t
+{
+ unsigned char mmr_unused0[8]; /* unused */
+ unsigned char mmr_des_status; /* encryption status */
+ unsigned char mmr_des_avail; /* encryption available (0x55 read) */
+#define MMR_DES_AVAIL_DES 0x55 /* DES available */
+#define MMR_DES_AVAIL_AES 0x33 /* AES (AT&T) available */
+ unsigned char mmr_des_io_invert; /* des I/O invert register */
+ unsigned char mmr_unused1[5]; /* unused */
+ unsigned char mmr_dce_status; /* DCE status */
+#define MMR_DCE_STATUS_RX_BUSY 0x01 /* receiver busy */
+#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */
+#define MMR_DCE_STATUS_TX_BUSY 0x04 /* transmitter on */
+#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */
+#define MMR_DCE_STATUS 0x0F /* mask to get the bits */
+ unsigned char mmr_dsp_id; /* DSP id (AA = Daedalus rev A) */
+ unsigned char mmr_unused2[2]; /* unused */
+ unsigned char mmr_correct_nwid_l; /* # of correct NWID's rxd (low) */
+ unsigned char mmr_correct_nwid_h; /* # of correct NWID's rxd (high) */
+ /* Warning : Read high order octet first !!! */
+ unsigned char mmr_wrong_nwid_l; /* # of wrong NWID's rxd (low) */
+ unsigned char mmr_wrong_nwid_h; /* # of wrong NWID's rxd (high) */
+ unsigned char mmr_thr_pre_set; /* level threshold preset */
+#define MMR_THR_PRE_SET 0x3F /* level threshold preset */
+#define MMR_THR_PRE_SET_CUR 0x80 /* Current signal above it */
+ unsigned char mmr_signal_lvl; /* signal level */
+#define MMR_SIGNAL_LVL 0x3F /* signal level */
+#define MMR_SIGNAL_LVL_VALID 0x80 /* Updated since last read */
+ unsigned char mmr_silence_lvl; /* silence level (noise) */
+#define MMR_SILENCE_LVL 0x3F /* silence level */
+#define MMR_SILENCE_LVL_VALID 0x80 /* Updated since last read */
+ unsigned char mmr_sgnl_qual; /* signal quality */
+#define MMR_SGNL_QUAL 0x0F /* signal quality */
+#define MMR_SGNL_QUAL_ANT 0x80 /* current antenna used */
+ unsigned char mmr_netw_id_l; /* NWID low order byte ??? */
+ unsigned char mmr_unused3[3]; /* unused */
+
+ /* 2.0 Hardware extension - frequency selection support */
+ unsigned char mmr_fee_status; /* Status of frequency eeprom */
+#define MMR_FEE_STATUS_ID 0xF0 /* Modem revision id */
+#define MMR_FEE_STATUS_DWLD 0x08 /* Download in progress */
+#define MMR_FEE_STATUS_BUSY 0x04 /* EEprom busy */
+ unsigned char mmr_unused4[1]; /* unused */
+ unsigned char mmr_fee_data_l; /* Read data from eeprom (low) */
+ unsigned char mmr_fee_data_h; /* Read data from eeprom (high) */
+};
+
+/* Size for structure checking (if padding is correct) */
+#define MMR_SIZE 36
+
+/* Calculate offset of a field in the above structure */
+#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+
+
+/* Make the two above structures one */
+typedef union mm_t
+{
+ struct mmw_t w; /* Write to the mmc */
+ struct mmr_t r; /* Read from the mmc */
+} mm_t;
+
+#endif /* _WAVELAN_CS_H */
--- /dev/null
+/*
+ * Wavelan Pcmcia driver
+ *
+ * Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ *
+ * This file contain all definition and declarations necessary for the
+ * wavelan pcmcia driver. This file is a private header, so it should
+ * be included only on wavelan_cs.c !!!
+ */
+
+#ifndef WAVELAN_CS_P_H
+#define WAVELAN_CS_P_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * This driver provide a Linux interface to the Wavelan Pcmcia hardware
+ * The Wavelan is a product of Lucent (http://www.wavelan.com/).
+ * This division was formerly part of NCR and then AT&T.
+ * Wavelan are also distributed by DEC (RoamAbout DS)...
+ *
+ * To know how to use this driver, read the PCMCIA HOWTO.
+ * If you want to exploit the many other fonctionalities, look comments
+ * in the code...
+ *
+ * This driver is the result of the effort of many peoples (see below).
+ */
+
+/* ------------------------ SPECIFIC NOTES ------------------------ */
+/*
+ * Web page
+ * --------
+ * I try to maintain a web page with the Wireless LAN Howto at :
+ * http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
+ *
+ * SMP
+ * ---
+ * We now are SMP compliant (I eventually fixed the remaining bugs).
+ * The driver has been tested on a dual P6-150 and survived my usual
+ * set of torture tests.
+ * Anyway, I spent enough time chasing interrupt re-entrancy during
+ * errors or reconfigure, and I designed the locked/unlocked sections
+ * of the driver with great care, and with the recent addition of
+ * the spinlock (thanks to the new API), we should be quite close to
+ * the truth.
+ * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast),
+ * but better safe than sorry (especially at 2 Mb/s ;-).
+ *
+ * I have also looked into disabling only our interrupt on the card
+ * (via HACR) instead of all interrupts in the processor (via cli),
+ * so that other driver are not impacted, and it look like it's
+ * possible, but it's very tricky to do right (full of races). As
+ * the gain would be mostly for SMP systems, it can wait...
+ *
+ * Debugging and options
+ * ---------------------
+ * You will find below a set of '#define" allowing a very fine control
+ * on the driver behaviour and the debug messages printed.
+ * The main options are :
+ * o WAVELAN_ROAMING, for the experimental roaming support.
+ * o SET_PSA_CRC, to have your card correctly recognised by
+ * an access point and the Point-to-Point diagnostic tool.
+ * o USE_PSA_CONFIG, to read configuration from the PSA (EEprom)
+ * (otherwise we always start afresh with some defaults)
+ *
+ * wavelan_cs.o is darn too big
+ * -------------------------
+ * That's true ! There is a very simple way to reduce the driver
+ * object by 33% (yes !). Comment out the following line :
+ * #include <linux/wireless.h>
+ * Other compile options can also reduce the size of it...
+ *
+ * MAC address and hardware detection :
+ * ----------------------------------
+ * The detection code of the wavelan chech that the first 3
+ * octets of the MAC address fit the company code. This type of
+ * detection work well for AT&T cards (because the AT&T code is
+ * hardcoded in wavelan_cs.h), but of course will fail for other
+ * manufacturer.
+ *
+ * If you are sure that your card is derived from the wavelan,
+ * here is the way to configure it :
+ * 1) Get your MAC address
+ * a) With your card utilities (wfreqsel, instconf, ...)
+ * b) With the driver :
+ * o compile the kernel with DEBUG_CONFIG_INFO enabled
+ * o Boot and look the card messages
+ * 2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan_cs.h)
+ * 3) Compile & verify
+ * 4) Send me the MAC code - I will include it in the next version...
+ *
+ */
+
+/* --------------------- WIRELESS EXTENSIONS --------------------- */
+/*
+ * This driver is the first one to support "wireless extensions".
+ * This set of extensions provide you some way to control the wireless
+ * caracteristics of the hardware in a standard way and support for
+ * applications for taking advantage of it (like Mobile IP).
+ *
+ * You will need to enable the CONFIG_NET_RADIO define in the kernel
+ * configuration to enable the wireless extensions (this is the one
+ * giving access to the radio network device choice).
+ *
+ * It might also be a good idea as well to fetch the wireless tools to
+ * configure the device and play a bit.
+ */
+
+/* ---------------------------- FILES ---------------------------- */
+/*
+ * wavelan_cs.c : The actual code for the driver - C functions
+ *
+ * wavelan_cs.p.h : Private header : local types / vars for the driver
+ *
+ * wavelan_cs.h : Description of the hardware interface & structs
+ *
+ * i82593.h : Description if the Ethernet controller
+ */
+
+/* --------------------------- HISTORY --------------------------- */
+/*
+ * The history of the Wavelan drivers is as complicated as history of
+ * the Wavelan itself (NCR -> AT&T -> Lucent).
+ *
+ * All started with Anders Klemets <klemets@paul.rutgers.edu>,
+ * writting a Wavelan ISA driver for the MACH microkernel. Girish
+ * Welling <welling@paul.rutgers.edu> had also worked on it.
+ * Keith Moore modify this for the Pcmcia hardware.
+ *
+ * Robert Morris <rtm@das.harvard.edu> port these two drivers to BSDI
+ * and add specific Pcmcia support (there is currently no equivalent
+ * of the PCMCIA package under BSD...).
+ *
+ * Jim Binkley <jrb@cs.pdx.edu> port both BSDI drivers to FreeBSD.
+ *
+ * Bruce Janson <bruce@cs.usyd.edu.au> port the BSDI ISA driver to Linux.
+ *
+ * Anthony D. Joseph <adj@lcs.mit.edu> started modify Bruce driver
+ * (with help of the BSDI PCMCIA driver) for PCMCIA.
+ * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work.
+ * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
+ * correctly 2.00 cards (2.4 GHz with frequency selection).
+ * David Hinds <dahinds@users.sourceforge.net> integrated the whole in his
+ * Pcmcia package (+ bug corrections).
+ *
+ * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
+ * patchs to the Pcmcia driver. After, I added code in the ISA driver
+ * for Wireless Extensions and full support of frequency selection
+ * cards. Now, I'm doing the same to the Pcmcia driver + some
+ * reorganisation.
+ * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
+ * much needed informations on the Wavelan hardware.
+ */
+
+/* By the way : for the copyright & legal stuff :
+ * Almost everybody wrote code under GNU or BSD license (or alike),
+ * and want that their original copyright remain somewhere in the
+ * code (for myself, I go with the GPL).
+ * Nobody want to take responsibility for anything, except the fame...
+ */
+
+/* --------------------------- CREDITS --------------------------- */
+/*
+ * Credits:
+ * Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht and
+ * Loeke Brederveld of Lucent for providing extremely useful
+ * information about WaveLAN PCMCIA hardware
+ *
+ * This driver is based upon several other drivers, in particular:
+ * David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
+ * Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
+ * Anders Klemets' PCMCIA WaveLAN adapter driver
+ * Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
+ *
+ * Additional Credits:
+ *
+ * This software was originally developed under Linux 1.2.3
+ * (Slackware 2.0 distribution).
+ * And then under Linux 2.0.x (Debian 1.1 -> 2.2 - pcmcia 2.8.18+)
+ * with an HP OmniBook 4000 and then a 5500.
+ *
+ * It is based on other device drivers and information either written
+ * or supplied by:
+ * James Ashton (jaa101@syseng.anu.edu.au),
+ * Ajay Bakre (bakre@paul.rutgers.edu),
+ * Donald Becker (becker@super.org),
+ * Jim Binkley <jrb@cs.pdx.edu>,
+ * Loeke Brederveld <lbrederv@wavelan.com>,
+ * Allan Creighton (allanc@cs.su.oz.au),
+ * Brent Elphick <belphick@uwaterloo.ca>,
+ * Joe Finney <joe@comp.lancs.ac.uk>,
+ * Matthew Geier (matthew@cs.su.oz.au),
+ * Remo di Giovanni (remo@cs.su.oz.au),
+ * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
+ * David Hinds <dahinds@users.sourceforge.net>,
+ * Jan Hoogendoorn (c/o marteijn@lucent.com),
+ * Bruce Janson <bruce@cs.usyd.edu.au>,
+ * Anthony D. Joseph <adj@lcs.mit.edu>,
+ * Anders Klemets (klemets@paul.rutgers.edu),
+ * Yunzhou Li <yunzhou@strat.iol.unh.edu>,
+ * Marc Meertens (mmeertens@lucent.com),
+ * Keith Moore,
+ * Robert Morris (rtm@das.harvard.edu),
+ * Ian Parkin (ian@cs.su.oz.au),
+ * John Rosenberg (johnr@cs.su.oz.au),
+ * George Rossi (george@phm.gov.au),
+ * Arthur Scott (arthur@cs.su.oz.au),
+ * Stanislav Sinyagin <stas@isf.ru>
+ * Peter Storey,
+ * Jean Tourrilhes <jt@hpl.hp.com>,
+ * Girish Welling (welling@paul.rutgers.edu)
+ * Clark Woodworth <clark@hiway1.exit109.com>
+ * Yongguang Zhang <ygz@isl.hrl.hac.com>...
+ */
+
+/* ------------------------- IMPROVEMENTS ------------------------- */
+/*
+ * I proudly present :
+ *
+ * Changes made in 2.8.22 :
+ * ----------------------
+ * - improved wv_set_multicast_list
+ * - catch spurious interrupt
+ * - correct release of the device
+ *
+ * Changes mades in release :
+ * ------------------------
+ * - Reorganisation of the code, function name change
+ * - Creation of private header (wavelan_cs.h)
+ * - Reorganised debug messages
+ * - More comments, history, ...
+ * - Configure earlier (in "insert" instead of "open")
+ * and do things only once
+ * - mmc_init : configure the PSA if not done
+ * - mmc_init : 2.00 detection better code for 2.00 init
+ * - better info at startup
+ * - Correct a HUGE bug (volatile & uncalibrated busy loop)
+ * in wv_82593_cmd => config speedup
+ * - Stop receiving & power down on close (and power up on open)
+ * use "ifconfig down" & "ifconfig up ; route add -net ..."
+ * - Send packets : add watchdog instead of pooling
+ * - Receive : check frame wrap around & try to recover some frames
+ * - wavelan_set_multicast_list : avoid reset
+ * - add wireless extensions (ioctl & get_wireless_stats)
+ * get/set nwid/frequency on fly, info for /proc/net/wireless
+ * - Suppress useless stuff from lp (net_local), but add link
+ * - More inlines
+ * - Lot of others minor details & cleanups
+ *
+ * Changes made in second release :
+ * ------------------------------
+ * - Optimise wv_85893_reconfig stuff, fix potential problems
+ * - Change error values for ioctl
+ * - Non blocking wv_ru_stop() + call wv_reset() in case of problems
+ * - Remove development printk from wavelan_watchdog()
+ * - Remove of the watchdog to wavelan_close instead of wavelan_release
+ * fix potential problems...
+ * - Start debugging suspend stuff (but it's still a bit weird)
+ * - Debug & optimize dump header/packet in Rx & Tx (debug)
+ * - Use "readb" and "writeb" to be kernel 2.1 compliant
+ * - Better handling of bogus interrupts
+ * - Wireless extension : SETSPY and GETSPY
+ * - Remove old stuff (stats - for those needing it, just ask me...)
+ * - Make wireless extensions optional
+ *
+ * Changes made in third release :
+ * -----------------------------
+ * - cleanups & typos
+ * - modif wireless ext (spy -> only one pointer)
+ * - new private ioctl to set/get quality & level threshold
+ * - Init : correct default value of level threshold for pcmcia
+ * - kill watchdog in hw_reset
+ * - more 2.1 support (copy_to/from_user instead of memcpy_to/fromfs)
+ * - Add message level (debug stuff in /var/adm/debug & errors not
+ * displayed at console and still in /var/adm/messages)
+ *
+ * Changes made in fourth release :
+ * ------------------------------
+ * - multicast support (yes !) thanks to Yongguang Zhang.
+ *
+ * Changes made in fifth release (2.9.0) :
+ * -------------------------------------
+ * - Revisited multicast code (it was mostly wrong).
+ * - protect code in wv_82593_reconfig with dev->tbusy (oups !)
+ *
+ * Changes made in sixth release (2.9.1a) :
+ * --------------------------------------
+ * - Change the detection code for multi manufacturer code support
+ * - Correct bug (hang kernel) in init when we were "rejecting" a card
+ *
+ * Changes made in seventh release (2.9.1b) :
+ * ----------------------------------------
+ * - Update to wireless extensions changes
+ * - Silly bug in card initial configuration (psa_conf_status)
+ *
+ * Changes made in eigth release :
+ * -----------------------------
+ * - Small bug in debug code (probably not the last one...)
+ * - 1.2.13 support (thanks to Clark Woodworth)
+ *
+ * Changes made for release in 2.9.2b :
+ * ----------------------------------
+ * - Level threshold is now a standard wireless extension (version 4 !)
+ * - modules parameters types for kernel > 2.1.17
+ * - updated man page
+ * - Others cleanup from David Hinds
+ *
+ * Changes made for release in 2.9.5 :
+ * ---------------------------------
+ * - byte count stats (courtesy of David Hinds)
+ * - Remove dev_tint stuff (courtesy of David Hinds)
+ * - Others cleanup from David Hinds
+ * - Encryption setting from Brent Elphick (thanks a lot !)
+ * - 'base' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
+ *
+ * Changes made for release in 2.9.6 :
+ * ---------------------------------
+ * - fix bug : no longuer disable watchdog in case of bogus interrupt
+ * - increase timeout in config code for picky hardware
+ * - mask unused bits in status (Wireless Extensions)
+ *
+ * Changes integrated by Justin Seger <jseger@MIT.EDU> & David Hinds :
+ * -----------------------------------------------------------------
+ * - Roaming "hack" from Joe Finney <joe@comp.lancs.ac.uk>
+ * - PSA CRC code from Bob Gray <rgray@bald.cs.dartmouth.edu>
+ * - Better initialisation of the i82593 controller
+ * from Joseph K. O'Sullivan <josullvn+@cs.cmu.edu>
+ *
+ * Changes made for release in 3.0.10 :
+ * ----------------------------------
+ * - Fix eject "hang" of the driver under 2.2.X :
+ * o create wv_flush_stale_links()
+ * o Rename wavelan_release to wv_pcmcia_release & move up
+ * o move unregister_netdev to wavelan_detach()
+ * o wavelan_release() no longer call wavelan_detach()
+ * o Suppress "release" timer
+ * o Other cleanups & fixes
+ * - New MAC address in the probe
+ * - Reorg PSA_CRC code (endian neutral & cleaner)
+ * - Correct initialisation of the i82593 from Lucent manual
+ * - Put back the watchdog, with larger timeout
+ * - TRANSMIT_NO_CRC is a "normal" error, so recover from it
+ * from Derrick J Brashear <shadow@dementia.org>
+ * - Better handling of TX and RX normal failure conditions
+ * - #ifdef out all the roaming code
+ * - Add ESSID & "AP current address" ioctl stubs
+ * - General cleanup of the code
+ *
+ * Changes made for release in 3.0.13 :
+ * ----------------------------------
+ * - Re-enable compilation of roaming code by default, but with
+ * do_roaming = 0
+ * - Nuke `nwid=nwid^ntohs(beacon->domain_id)' in wl_roam_gather
+ * at the demand of John Carol Langford <jcl@gs176.sp.cs.cmu.edu>
+ * - Introduced WAVELAN_ROAMING_EXT for incomplete ESSID stuff.
+ *
+ * Changes made for release in 3.0.15 :
+ * ----------------------------------
+ * - Change e-mail and web page addresses
+ * - Watchdog timer is now correctly expressed in HZ, not in jiffies
+ * - Add channel number to the list of frequencies in range
+ * - Add the (short) list of bit-rates in range
+ * - Developp a new sensitivity... (sens.value & sens.fixed)
+ *
+ * Changes made for release in 3.1.2 :
+ * ---------------------------------
+ * - Fix check for root permission (break instead of exit)
+ * - New nwid & encoding setting (Wireless Extension 9)
+ *
+ * Changes made for release in 3.1.12 :
+ * ----------------------------------
+ * - reworked wv_82593_cmd to avoid using the IRQ handler and doing
+ * ugly things with interrupts.
+ * - Add IRQ protection in 82593_config/ru_start/ru_stop/watchdog
+ * - Update to new network API (softnet - 2.3.43) :
+ * o replace dev->tbusy (David + me)
+ * o replace dev->tstart (David + me)
+ * o remove dev->interrupt (David)
+ * o add SMP locking via spinlock in splxx (me)
+ * o add spinlock in interrupt handler (me)
+ * o use kernel watchdog instead of ours (me)
+ * o verify that all the changes make sense and work (me)
+ * - Re-sync kernel/pcmcia versions (not much actually)
+ * - A few other cleanups (David & me)...
+ *
+ * Changes made for release in 3.1.22 :
+ * ----------------------------------
+ * - Check that SMP works, remove annoying log message
+ *
+ * Changes made for release in 3.1.24 :
+ * ----------------------------------
+ * - Fix unfrequent card lockup when watchdog was reseting the hardware :
+ * o control first busy loop in wv_82593_cmd()
+ * o Extend spinlock protection in wv_hw_config()
+ *
+ * Wishes & dreams:
+ * ----------------
+ * - Cleanup and integrate the roaming code
+ * (std debug, set DomainID, decay avg and co...)
+ */
+
+/***************************** INCLUDES *****************************/
+
+/* Linux headers that we need */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h> /* Wireless extensions */
+#endif
+
+/* Pcmcia headers that we need */
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/version.h>
+
+/* Wavelan declarations */
+#include "i82593.h" /* Definitions for the Intel chip */
+
+#include "wavelan_cs.h" /* Others bits of the hardware */
+
+/************************** DRIVER OPTIONS **************************/
+/*
+ * `#define' or `#undef' the following constant to change the behaviour
+ * of the driver...
+ */
+#define WAVELAN_ROAMING /* Include experimental roaming code */
+#undef WAVELAN_ROAMING_EXT /* Enable roaming wireless extensions */
+#undef SET_PSA_CRC /* Set the CRC in PSA (slower) */
+#define USE_PSA_CONFIG /* Use info from the PSA */
+#undef STRUCT_CHECK /* Verify padding of structures */
+#undef EEPROM_IS_PROTECTED /* Doesn't seem to be necessary */
+#define MULTICAST_AVOID /* Avoid extra multicast (I'm sceptical) */
+#undef SET_MAC_ADDRESS /* Experimental */
+
+#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */
+/* Warning : these stuff will slow down the driver... */
+#define WIRELESS_SPY /* Enable spying addresses */
+#undef HISTOGRAM /* Enable histogram of sig level... */
+#endif
+
+/****************************** DEBUG ******************************/
+
+#undef DEBUG_MODULE_TRACE /* Module insertion/removal */
+#undef DEBUG_CALLBACK_TRACE /* Calls made by Linux */
+#undef DEBUG_INTERRUPT_TRACE /* Calls to handler */
+#undef DEBUG_INTERRUPT_INFO /* type of interrupt & so on */
+#define DEBUG_INTERRUPT_ERROR /* problems */
+#undef DEBUG_CONFIG_TRACE /* Trace the config functions */
+#undef DEBUG_CONFIG_INFO /* What's going on... */
+#define DEBUG_CONFIG_ERRORS /* Errors on configuration */
+#undef DEBUG_TX_TRACE /* Transmission calls */
+#undef DEBUG_TX_INFO /* Header of the transmitted packet */
+#undef DEBUG_TX_FAIL /* Normal failure conditions */
+#define DEBUG_TX_ERROR /* Unexpected conditions */
+#undef DEBUG_RX_TRACE /* Transmission calls */
+#undef DEBUG_RX_INFO /* Header of the transmitted packet */
+#undef DEBUG_RX_FAIL /* Normal failure conditions */
+#define DEBUG_RX_ERROR /* Unexpected conditions */
+#undef DEBUG_PACKET_DUMP 32 /* Dump packet on the screen */
+#undef DEBUG_IOCTL_TRACE /* Misc call by Linux */
+#undef DEBUG_IOCTL_INFO /* Various debug info */
+#define DEBUG_IOCTL_ERROR /* What's going wrong */
+#define DEBUG_BASIC_SHOW /* Show basic startup info */
+#undef DEBUG_VERSION_SHOW /* Print version info */
+#undef DEBUG_PSA_SHOW /* Dump psa to screen */
+#undef DEBUG_MMC_SHOW /* Dump mmc to screen */
+#undef DEBUG_SHOW_UNUSED /* Show also unused fields */
+#undef DEBUG_I82593_SHOW /* Show i82593 status */
+#undef DEBUG_DEVICE_SHOW /* Show device parameters */
+
+/************************ CONSTANTS & MACROS ************************/
+
+#ifdef DEBUG_VERSION_SHOW
+static const char *version = "wavelan_cs.c : v23 (SMP + wireless extensions) 20/12/00\n";
+#endif
+
+/* Watchdog temporisation */
+#define WATCHDOG_JIFFIES (256*HZ/100)
+
+/* Fix a bug in some old wireless extension definitions */
+#ifndef IW_ESSID_MAX_SIZE
+#define IW_ESSID_MAX_SIZE 32
+#endif
+
+/* ------------------------ PRIVATE IOCTL ------------------------ */
+
+/* Wireless Extension Backward compatibility - Jean II
+ * If the new wireless device private ioctl range is not defined,
+ * default to standard device private ioctl range */
+#ifndef SIOCIWFIRSTPRIV
+#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
+#endif /* SIOCIWFIRSTPRIV */
+
+#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */
+#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */
+#define SIOCSIPROAM SIOCIWFIRSTPRIV + 2 /* Set roaming state */
+#define SIOCGIPROAM SIOCIWFIRSTPRIV + 3 /* Get roaming state */
+
+#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */
+#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 7 /* Get histogram values */
+
+/*************************** WaveLAN Roaming **************************/
+#ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */
+
+#define WAVELAN_ROAMING_DEBUG 0 /* 1 = Trace of handover decisions */
+ /* 2 = Info on each beacon rcvd... */
+#define MAX_WAVEPOINTS 7 /* Max visible at one time */
+#define WAVEPOINT_HISTORY 5 /* SNR sample history slow search */
+#define WAVEPOINT_FAST_HISTORY 2 /* SNR sample history fast search */
+#define SEARCH_THRESH_LOW 10 /* SNR to enter cell search */
+#define SEARCH_THRESH_HIGH 13 /* SNR to leave cell search */
+#define WAVELAN_ROAMING_DELTA 1 /* Hysteresis value (+/- SNR) */
+#define CELL_TIMEOUT 2*HZ /* in jiffies */
+
+#define FAST_CELL_SEARCH 1 /* Boolean values... */
+#define NWID_PROMISC 1 /* for code clarity. */
+
+typedef struct wavepoint_beacon
+{
+ unsigned char dsap, /* Unused */
+ ssap, /* Unused */
+ ctrl, /* Unused */
+ O,U,I, /* Unused */
+ spec_id1, /* Unused */
+ spec_id2, /* Unused */
+ pdu_type, /* Unused */
+ seq; /* WavePoint beacon sequence number */
+ unsigned short domain_id, /* WavePoint Domain ID */
+ nwid; /* WavePoint NWID */
+} wavepoint_beacon;
+
+typedef struct wavepoint_history
+{
+ unsigned short nwid; /* WavePoint's NWID */
+ int average_slow; /* SNR running average */
+ int average_fast; /* SNR running average */
+ unsigned char sigqual[WAVEPOINT_HISTORY]; /* Ringbuffer of recent SNR's */
+ unsigned char qualptr; /* Index into ringbuffer */
+ unsigned char last_seq; /* Last seq. no seen for WavePoint */
+ struct wavepoint_history *next; /* Next WavePoint in table */
+ struct wavepoint_history *prev; /* Previous WavePoint in table */
+ unsigned long last_seen; /* Time of last beacon recvd, jiffies */
+} wavepoint_history;
+
+struct wavepoint_table
+{
+ wavepoint_history *head; /* Start of ringbuffer */
+ int num_wavepoints; /* No. of WavePoints visible */
+ unsigned char locked; /* Table lock */
+};
+
+#endif /* WAVELAN_ROAMING */
+
+/****************************** TYPES ******************************/
+
+/* Shortcuts */
+typedef struct net_device device;
+typedef struct net_device_stats en_stats;
+typedef struct iw_statistics iw_stats;
+typedef struct iw_quality iw_qual;
+typedef struct iw_freq iw_freq;
+typedef struct net_local net_local;
+typedef struct timer_list timer_list;
+
+/* Basic types */
+typedef u_char mac_addr[WAVELAN_ADDR_SIZE]; /* Hardware address */
+
+/*
+ * Static specific data for the interface.
+ *
+ * For each network interface, Linux keep data in two structure. "device"
+ * keep the generic data (same format for everybody) and "net_local" keep
+ * the additional specific data.
+ * Note that some of this specific data is in fact generic (en_stats, for
+ * example).
+ */
+struct net_local
+{
+ dev_node_t node; /* ???? What is this stuff ???? */
+ device * dev; /* Reverse link... */
+ spinlock_t spinlock; /* Serialize access to the hardware (SMP) */
+ dev_link_t * link; /* pcmcia structure */
+ en_stats stats; /* Ethernet interface statistics */
+ int nresets; /* Number of hw resets */
+ u_char configured; /* If it is configured */
+ u_char reconfig_82593; /* Need to reconfigure the controller */
+ u_char promiscuous; /* Promiscuous mode */
+ u_char allmulticast; /* All Multicast mode */
+ int mc_count; /* Number of multicast addresses */
+
+ int stop; /* Current i82593 Stop Hit Register */
+ int rfp; /* Last DMA machine receive pointer */
+ int overrunning; /* Receiver overrun flag */
+
+#ifdef WIRELESS_EXT
+ iw_stats wstats; /* Wireless specific stats */
+#endif
+
+#ifdef WIRELESS_SPY
+ int spy_number; /* Number of addresses to spy */
+ mac_addr spy_address[IW_MAX_SPY]; /* The addresses to spy */
+ iw_qual spy_stat[IW_MAX_SPY]; /* Statistics gathered */
+#endif /* WIRELESS_SPY */
+#ifdef HISTOGRAM
+ int his_number; /* Number of intervals */
+ u_char his_range[16]; /* Boundaries of interval ]n-1; n] */
+ u_long his_sum[16]; /* Sum in interval */
+#endif /* HISTOGRAM */
+#ifdef WAVELAN_ROAMING
+ u_long domain_id; /* Domain ID we lock on for roaming */
+ int filter_domains; /* Check Domain ID of beacon found */
+ struct wavepoint_table wavepoint_table; /* Table of visible WavePoints*/
+ wavepoint_history * curr_point; /* Current wavepoint */
+ int cell_search; /* Searching for new cell? */
+ struct timer_list cell_timer; /* Garbage collection */
+#endif /* WAVELAN_ROAMING */
+};
+
+/**************************** PROTOTYPES ****************************/
+
+#ifdef WAVELAN_ROAMING
+/* ---------------------- ROAMING SUBROUTINES -----------------------*/
+
+wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp);
+wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local *lp);
+void wl_del_wavepoint(wavepoint_history *wavepoint, net_local *lp);
+void wl_cell_expiry(unsigned long data);
+wavepoint_history *wl_best_sigqual(int fast_search, net_local *lp);
+void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq);
+void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp);
+void wv_nwid_filter(unsigned char mode, net_local *lp);
+void wv_roam_init(struct net_device *dev);
+void wv_roam_cleanup(struct net_device *dev);
+#endif /* WAVELAN_ROAMING */
+
+/* ----------------------- MISC SUBROUTINES ------------------------ */
+static inline void
+ wv_splhi(net_local *, /* Disable interrupts */
+ unsigned long *); /* flags */
+static inline void
+ wv_splx(net_local *, /* ReEnable interrupts */
+ unsigned long *); /* flags */
+static void
+ cs_error(client_handle_t, /* Report error to cardmgr */
+ int,
+ int);
+/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
+static inline u_char /* data */
+ hasr_read(u_long); /* Read the host interface : base address */
+static inline void
+ hacr_write(u_long, /* Write to host interface : base address */
+ u_char), /* data */
+ hacr_write_slow(u_long,
+ u_char);
+static void
+ psa_read(device *, /* Read the Parameter Storage Area */
+ int, /* offset in PSA */
+ u_char *, /* buffer to fill */
+ int), /* size to read */
+ psa_write(device *, /* Write to the PSA */
+ int, /* Offset in psa */
+ u_char *, /* Buffer in memory */
+ int); /* Length of buffer */
+static inline void
+ mmc_out(u_long, /* Write 1 byte to the Modem Manag Control */
+ u_short,
+ u_char),
+ mmc_write(u_long, /* Write n bytes to the MMC */
+ u_char,
+ u_char *,
+ int);
+static inline u_char /* Read 1 byte from the MMC */
+ mmc_in(u_long,
+ u_short);
+static inline void
+ mmc_read(u_long, /* Read n bytes from the MMC */
+ u_char,
+ u_char *,
+ int),
+ fee_wait(u_long, /* Wait for frequency EEprom : base address */
+ int, /* Base delay to wait for */
+ int); /* Number of time to wait */
+static void
+ fee_read(u_long, /* Read the frequency EEprom : base address */
+ u_short, /* destination offset */
+ u_short *, /* data buffer */
+ int); /* number of registers */
+/* ---------------------- I82593 SUBROUTINES ----------------------- */
+static int
+ wv_82593_cmd(device *, /* synchronously send a command to i82593 */
+ char *,
+ int,
+ int);
+static inline int
+ wv_diag(device *); /* Diagnostique the i82593 */
+static int
+ read_ringbuf(device *, /* Read a receive buffer */
+ int,
+ char *,
+ int);
+static inline void
+ wv_82593_reconfig(device *); /* Reconfigure the controller */
+/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */
+static inline void
+ wv_init_info(device *); /* display startup info */
+/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
+static en_stats *
+ wavelan_get_stats(device *); /* Give stats /proc/net/dev */
+/* ----------------------- PACKET RECEPTION ----------------------- */
+static inline int
+ wv_start_of_frame(device *, /* Seek beggining of current frame */
+ int, /* end of frame */
+ int); /* start of buffer */
+static inline void
+ wv_packet_read(device *, /* Read a packet from a frame */
+ int,
+ int),
+ wv_packet_rcv(device *); /* Read all packets waiting */
+/* --------------------- PACKET TRANSMISSION --------------------- */
+static inline void
+ wv_packet_write(device *, /* Write a packet to the Tx buffer */
+ void *,
+ short);
+static int
+ wavelan_packet_xmit(struct sk_buff *, /* Send a packet */
+ device *);
+/* -------------------- HARDWARE CONFIGURATION -------------------- */
+static inline int
+ wv_mmc_init(device *); /* Initialize the modem */
+static int
+ wv_ru_stop(device *), /* Stop the i82593 receiver unit */
+ wv_ru_start(device *); /* Start the i82593 receiver unit */
+static int
+ wv_82593_config(device *); /* Configure the i82593 */
+static inline int
+ wv_pcmcia_reset(device *); /* Reset the pcmcia interface */
+static int
+ wv_hw_config(device *); /* Reset & configure the whole hardware */
+static inline void
+ wv_hw_reset(device *); /* Same, + start receiver unit */
+static inline int
+ wv_pcmcia_config(dev_link_t *); /* Configure the pcmcia interface */
+static void
+ wv_pcmcia_release(u_long), /* Remove a device */
+ wv_flush_stale_links(void); /* "detach" all possible devices */
+/* ---------------------- INTERRUPT HANDLING ---------------------- */
+static void
+ wavelan_interrupt(int, /* Interrupt handler */
+ void *,
+ struct pt_regs *);
+static void
+ wavelan_watchdog(device *); /* Transmission watchdog */
+/* ------------------- CONFIGURATION CALLBACKS ------------------- */
+static int
+ wavelan_open(device *), /* Open the device */
+ wavelan_close(device *); /* Close the device */
+static dev_link_t *
+ wavelan_attach(void); /* Create a new device */
+static void
+ wavelan_detach(dev_link_t *); /* Destroy a removed device */
+static int
+ wavelan_event(event_t, /* Manage pcmcia events */
+ int,
+ event_callback_args_t *);
+
+/**************************** VARIABLES ****************************/
+
+static dev_info_t dev_info = "wavelan_cs";
+static dev_link_t *dev_list = NULL; /* Linked list of devices */
+
+/*
+ * Parameters that can be set with 'insmod'
+ * The exact syntax is 'insmod wavelan_cs.o <var>=<value>'
+ */
+
+/* Bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4 and 3 */
+static int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+/* Shared memory speed, in ns */
+static int mem_speed = 0;
+
+/* New module interface */
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM(mem_speed, "i");
+
+#ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */
+/* Enable roaming mode ? No ! Please keep this to 0 */
+static int do_roaming = 0;
+MODULE_PARM(do_roaming, "i");
+#endif /* WAVELAN_ROAMING */
+
+MODULE_LICENSE("GPL");
+
+#endif /* WAVELAN_CS_P_H */
+
static int emu10k1_audio_open(struct inode *inode, struct file *file)
{
- int minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
struct emu10k1_card *card = NULL;
struct list_head *entry;
struct emu10k1_wavedevice *wave_dev;
static int emu10k1_midi_open(struct inode *inode, struct file *file)
{
- int minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
struct emu10k1_card *card = NULL;
struct emu10k1_mididevice *midi_dev;
struct list_head *entry;
static int emu10k1_mixer_open(struct inode *inode, struct file *file)
{
- int minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
struct emu10k1_card *card = NULL;
struct list_head *entry;
obj-y += hcd/ehci-hcd.o
endif
+ifeq ($(CONFIG_USB_OHCI_HCD),y)
+ obj-y += hcd/ohci-hcd.o
+endif
+
obj-$(CONFIG_USB_UHCI) += usb-uhci.o
obj-$(CONFIG_USB_UHCI_ALT) += uhci.o
obj-$(CONFIG_USB_OHCI) += usb-ohci.o
mod-subdirs := serial hcd
subdir-$(CONFIG_USB_EHCI_HCD) += hcd
+subdir-$(CONFIG_USB_OHCI_HCD) += hcd
subdir-$(CONFIG_USB_SERIAL) += serial
subdir-$(CONFIG_USB_STORAGE) += storage
#define FLG_CONNECTED 32
struct my_data_urb {
- urb_t urb;
- iso_packet_descriptor_t isoframe[DESCFRAMES];
+ struct urb urb;
+ struct usb_iso_packet_descriptor isoframe[DESCFRAMES];
};
struct my_sync_urb {
- urb_t urb;
- iso_packet_descriptor_t isoframe[SYNCFRAMES];
+ struct urb urb;
+ struct usb_iso_packet_descriptor isoframe[SYNCFRAMES];
};
typedef struct
{
struct auerchain *chain; /* pointer to the chain to which this element belongs */
- urb_t * urbp; /* pointer to attached urb */
+ struct urb * urbp; /* pointer to attached urb */
void *context; /* saved URB context */
usb_complete_t complete; /* saved URB completion function */
struct list_head list; /* to include element into a list */
unsigned int len; /* number of characters in data buffer */
unsigned int retries; /* for urb retries */
struct usb_ctrlrequest *dr; /* for setup data in control messages */
- urb_t * urbp; /* USB urb */
+ struct urb * urbp; /* USB urb */
struct auerbufctl *list; /* pointer to list */
struct list_head buff_list; /* reference to next buffer in list */
} auerbuf_t,*pauerbuf_t;
int open_count; /* count the number of open character channels */
char dev_desc[AUSI_DLEN];/* for storing a textual description */
unsigned int maxControlLength; /* max. Length of control paket (without header) */
- urb_t * inturbp; /* interrupt urb */
+ struct urb * inturbp; /* interrupt urb */
char * intbufp; /* data buffer for interrupt urb */
unsigned int irqsize; /* size of interrupt endpoint 1 */
struct auerchain controlchain; /* for chaining of control messages */
/*-------------------------------------------------------------------*/
/* Forwards */
-static void auerswald_ctrlread_complete (urb_t * urb);
+static void auerswald_ctrlread_complete (struct urb * urb);
static void auerswald_removeservice (pauerswald_t cp, pauerscon_t scp);
/* -------------------------- */
/* completion function for chained urbs */
-static void auerchain_complete (urb_t * urb)
+static void auerchain_complete (struct urb * urb)
{
unsigned long flags;
int result;
this function may be called from completion context or from user space!
early = 1 -> submit in front of chain
*/
-static int auerchain_submit_urb_list (pauerchain_t acp, urb_t * urb, int early)
+static int auerchain_submit_urb_list (pauerchain_t acp, struct urb * urb, int early)
{
int result;
unsigned long flags;
/* submit function for chained urbs
this function may be called from completion context or from user space!
*/
-static int auerchain_submit_urb (pauerchain_t acp, urb_t * urb)
+static int auerchain_submit_urb (pauerchain_t acp, struct urb * urb)
{
return auerchain_submit_urb_list (acp, urb, 0);
}
the result is 0 if the urb is cancelled, or -EINPROGRESS if
USB_ASYNC_UNLINK is set and the function is successfully started.
*/
-static int auerchain_unlink_urb (pauerchain_t acp, urb_t * urb)
+static int auerchain_unlink_urb (pauerchain_t acp, struct urb * urb)
{
unsigned long flags;
- urb_t * urbp;
+ struct urb * urbp;
pauerchainelement_t acep;
struct list_head *tmp;
static void auerchain_unlink_all (pauerchain_t acp)
{
unsigned long flags;
- urb_t * urbp;
+ struct urb * urbp;
pauerchainelement_t acep;
dbg ("auerchain_unlink_all called");
/* completion handler for synchronous chained URBs */
-static void auerchain_blocking_completion (urb_t *urb)
+static void auerchain_blocking_completion (struct urb *urb)
{
wait_queue_head_t *wakeup = (wait_queue_head_t *)urb->context;
wake_up (wakeup);
/* Starts chained urb and waits for completion or timeout */
-static int auerchain_start_wait_urb (pauerchain_t acp, urb_t *urb, int timeout, int* actual_length)
+static int auerchain_start_wait_urb (pauerchain_t acp, struct urb *urb, int timeout, int* actual_length)
{
DECLARE_WAITQUEUE (wait, current);
DECLARE_WAIT_QUEUE_HEAD (wqh);
{
int ret;
struct usb_ctrlrequest *dr;
- urb_t *urb;
+ struct urb *urb;
int length;
dbg ("auerchain_control_msg");
}
/* Completion of asynchronous write block */
-static void auerchar_ctrlwrite_complete (urb_t * urb)
+static void auerchar_ctrlwrite_complete (struct urb * urb)
{
pauerbuf_t bp = (pauerbuf_t) urb->context;
pauerswald_t cp = ((pauerswald_t)((char *)(bp->list)-(unsigned long)(&((pauerswald_t)0)->bufctl)));
}
/* Completion handler for dummy retry packet */
-static void auerswald_ctrlread_wretcomplete (urb_t * urb)
+static void auerswald_ctrlread_wretcomplete (struct urb * urb)
{
pauerbuf_t bp = (pauerbuf_t) urb->context;
pauerswald_t cp;
}
/* completion handler for receiving of control messages */
-static void auerswald_ctrlread_complete (urb_t * urb)
+static void auerswald_ctrlread_complete (struct urb * urb)
{
unsigned int serviceid;
pauerswald_t cp;
messages from the USB device.
*/
/* int completion handler. */
-static void auerswald_int_complete (urb_t * urb)
+static void auerswald_int_complete (struct urb * urb)
{
unsigned long flags;
unsigned int channelid;
unsigned int signr;
void *userbuffer;
void *userurb;
- urb_t urb;
+ struct urb urb;
};
static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
static struct async *alloc_async(unsigned int numisoframes)
{
- unsigned int assize = sizeof(struct async) + numisoframes * sizeof(iso_packet_descriptor_t);
+ unsigned int assize = sizeof(struct async) + numisoframes * sizeof(struct usb_iso_packet_descriptor);
struct async *as = kmalloc(assize, GFP_KERNEL);
if (!as)
return NULL;
return retval;
}
}
- dev->driver_data = hcd;
+ pci_set_drvdata(dev, hcd);
hcd->driver = driver;
hcd->description = driver->description;
hcd->pdev = dev;
struct usb_hcd *hcd;
struct usb_device *hub;
- hcd = (struct usb_hcd *) dev->driver_data;
+ hcd = pci_get_drvdata(dev);
if (!hcd)
return;
info ("remove: %s, state %x", hcd->bus_name, hcd->state);
struct usb_hcd *hcd;
int retval;
- hcd = (struct usb_hcd *) dev->driver_data;
+ hcd = pci_get_drvdata(dev);
info ("suspend %s to state %d", hcd->bus_name, state);
pci_save_state (dev, hcd->pci_state);
struct usb_hcd *hcd;
int retval;
- hcd = (struct usb_hcd *) dev->driver_data;
+ hcd = pci_get_drvdata(dev);
info ("resume %s", hcd->bus_name);
/* guard against multiple resumes (APM bug?) */
# USB Host Controller Drivers
#
dep_tristate ' EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
-# dep_tristate ' OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+dep_tristate ' OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
# dep_tristate ' UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
O_TARGET :=
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
-# obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
+obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
# obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
# Extract lists of the multi-part drivers.
if (!(urb->transfer_flags & EHCI_STATE_UNLINK)
&& ehci->hcd.state != USB_STATE_HALT) {
int i;
- iso_packet_descriptor_t *desc;
+ struct usb_iso_packet_descriptor *desc;
struct ehci_itd *first_itd = urb->hcpriv;
/* update status for this frame's transfers */
--- /dev/null
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under GPL
+ * $Id: ohci-dbg.c,v 1.2 2002/01/19 00:15:45 dbrownell Exp $
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+
+#define pipestring(pipe) ({ char *temp; \
+ switch (usb_pipetype (pipe)) { \
+ case PIPE_CONTROL: temp = "CTRL"; break; \
+ case PIPE_BULK: temp = "BULK"; break; \
+ case PIPE_INTERRUPT: temp = "INTR"; break; \
+ default: temp = "ISOC"; break; \
+ }; temp;})
+
+/* debug| print the main components of an URB
+ * small: 0) header + data packets 1) just header
+ */
+static void urb_print (struct urb * urb, char * str, int small)
+{
+ unsigned int pipe= urb->pipe;
+
+ if (!urb->dev || !urb->dev->bus) {
+ dbg("%s URB: no dev", str);
+ return;
+ }
+
+#ifndef OHCI_VERBOSE_DEBUG
+ if (urb->status != 0)
+#endif
+ dbg("%s:[%4x] dev:%d,ep=%d-%c,%s,flags:%4x,len:%d/%d,stat:%d",
+ str,
+ usb_get_current_frame_number (urb->dev),
+ usb_pipedevice (pipe),
+ usb_pipeendpoint (pipe),
+ usb_pipeout (pipe)? 'O': 'I',
+ pipestring (pipe),
+ urb->transfer_flags,
+ urb->actual_length,
+ urb->transfer_buffer_length,
+ urb->status);
+
+#ifdef OHCI_VERBOSE_DEBUG
+ if (!small) {
+ int i, len;
+
+ if (usb_pipecontrol (pipe)) {
+ printk (KERN_DEBUG __FILE__ ": cmd(8):");
+ for (i = 0; i < 8 ; i++)
+ printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
+ printk ("\n");
+ }
+ if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
+ printk (KERN_DEBUG __FILE__ ": data(%d/%d):",
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ len = usb_pipeout (pipe)?
+ urb->transfer_buffer_length: urb->actual_length;
+ for (i = 0; i < 16 && i < len; i++)
+ printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
+ printk ("%s stat:%d\n", i < len? "...": "", urb->status);
+ }
+ }
+#endif
+}
+
+static inline struct ed *
+dma_to_ed (struct ohci_hcd *hc, dma_addr_t ed_dma);
+
+/* print non-empty branches of the periodic ed tree */
+void ep_print_int_eds (struct ohci_hcd *ohci, char * str)
+{
+ int i, j;
+ __u32 * ed_p;
+ for (i= 0; i < 32; i++) {
+ j = 5;
+ ed_p = &(ohci->hcca->int_table [i]);
+ if (*ed_p == 0)
+ continue;
+ printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):",
+ str, i, i);
+ while (*ed_p != 0 && j--) {
+ struct ed *ed = dma_to_ed (ohci, le32_to_cpup(ed_p));
+ printk (" ed: %4x;", ed->hwINFO);
+ ed_p = &ed->hwNextED;
+ }
+ printk ("\n");
+ }
+}
+
+
+static void ohci_dump_intr_mask (char *label, __u32 mask)
+{
+ dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
+ label,
+ mask,
+ (mask & OHCI_INTR_MIE) ? " MIE" : "",
+ (mask & OHCI_INTR_OC) ? " OC" : "",
+ (mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+ (mask & OHCI_INTR_FNO) ? " FNO" : "",
+ (mask & OHCI_INTR_UE) ? " UE" : "",
+ (mask & OHCI_INTR_RD) ? " RD" : "",
+ (mask & OHCI_INTR_SF) ? " SF" : "",
+ (mask & OHCI_INTR_WDH) ? " WDH" : "",
+ (mask & OHCI_INTR_SO) ? " SO" : ""
+ );
+}
+
+static void maybe_print_eds (char *label, __u32 value)
+{
+ if (value)
+ dbg ("%s %08x", label, value);
+}
+
+static char *hcfs2string (int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET: return "reset";
+ case OHCI_USB_RESUME: return "resume";
+ case OHCI_USB_OPER: return "operational";
+ case OHCI_USB_SUSPEND: return "suspend";
+ }
+ return "?";
+}
+
+// dump control and status registers
+static void ohci_dump_status (struct ohci_hcd *controller)
+{
+ struct ohci_regs *regs = controller->regs;
+ __u32 temp;
+
+ temp = readl (®s->revision) & 0xff;
+ if (temp != 0x10)
+ dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
+
+ temp = readl (®s->control);
+ dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp,
+ (temp & OHCI_CTRL_RWE) ? " RWE" : "",
+ (temp & OHCI_CTRL_RWC) ? " RWC" : "",
+ (temp & OHCI_CTRL_IR) ? " IR" : "",
+ hcfs2string (temp & OHCI_CTRL_HCFS),
+ (temp & OHCI_CTRL_BLE) ? " BLE" : "",
+ (temp & OHCI_CTRL_CLE) ? " CLE" : "",
+ (temp & OHCI_CTRL_IE) ? " IE" : "",
+ (temp & OHCI_CTRL_PLE) ? " PLE" : "",
+ temp & OHCI_CTRL_CBSR
+ );
+
+ temp = readl (®s->cmdstatus);
+ dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp,
+ (temp & OHCI_SOC) >> 16,
+ (temp & OHCI_OCR) ? " OCR" : "",
+ (temp & OHCI_BLF) ? " BLF" : "",
+ (temp & OHCI_CLF) ? " CLF" : "",
+ (temp & OHCI_HCR) ? " HCR" : ""
+ );
+
+ ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus));
+ ohci_dump_intr_mask ("intrenable", readl (®s->intrenable));
+ // intrdisable always same as intrenable
+ // ohci_dump_intr_mask ("intrdisable", readl (®s->intrdisable));
+
+ maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent));
+
+ maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead));
+ maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent));
+
+ maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead));
+ maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent));
+
+ maybe_print_eds ("donehead", readl (®s->donehead));
+}
+
+static void ohci_dump_roothub (struct ohci_hcd *controller, int verbose)
+{
+ __u32 temp, ndp, i;
+
+ temp = roothub_a (controller);
+ ndp = (temp & RH_A_NDP);
+
+ if (verbose) {
+ dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp,
+ ((temp & RH_A_POTPGT) >> 24) & 0xff,
+ (temp & RH_A_NOCP) ? " NOCP" : "",
+ (temp & RH_A_OCPM) ? " OCPM" : "",
+ (temp & RH_A_DT) ? " DT" : "",
+ (temp & RH_A_NPS) ? " NPS" : "",
+ (temp & RH_A_PSM) ? " PSM" : "",
+ ndp
+ );
+ temp = roothub_b (controller);
+ dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
+ temp,
+ (temp & RH_B_PPCM) >> 16,
+ (temp & RH_B_DR)
+ );
+ temp = roothub_status (controller);
+ dbg ("roothub.status: %08x%s%s%s%s%s%s",
+ temp,
+ (temp & RH_HS_CRWE) ? " CRWE" : "",
+ (temp & RH_HS_OCIC) ? " OCIC" : "",
+ (temp & RH_HS_LPSC) ? " LPSC" : "",
+ (temp & RH_HS_DRWE) ? " DRWE" : "",
+ (temp & RH_HS_OCI) ? " OCI" : "",
+ (temp & RH_HS_LPS) ? " LPS" : ""
+ );
+ }
+
+ for (i = 0; i < ndp; i++) {
+ temp = roothub_portstatus (controller, i);
+ dbg_port (controller, "", i, temp);
+ }
+}
+
+static void ohci_dump (struct ohci_hcd *controller, int verbose)
+{
+ dbg ("OHCI controller %s state", controller->hcd.bus_name);
+
+ // dumps some of the state we know about
+ ohci_dump_status (controller);
+ if (verbose)
+ ep_print_int_eds (controller, "hcca");
+ dbg ("hcca frame #%04x", controller->hcca->frame_no);
+ ohci_dump_roothub (controller, 1);
+}
+
+
+#endif
+
--- /dev/null
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * [ Initialisation is based on Linus' ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ *
+ *
+ * History:
+ *
+ * 2002/01/18 package as a patch for 2.5.3; this should match the
+ * 2.4.17 kernel modulo some bugs being fixed.
+ *
+ * 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes
+ * from post-2.4.5 patches.
+ * 2001/09/20 USB_ZERO_PACKET support; hcca_dma portability, OPTi warning
+ * 2001/09/07 match PCI PM changes, errnos from Linus' tree
+ * 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify;
+ * pbook pci quirks gone (please fix pbook pci sw!) (db)
+ *
+ * 2001/04/08 Identify version on module load (gb)
+ * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
+ pci_map_single (db)
+ * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
+ * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
+ *
+ * 2000/09/26 fixed races in removing the private portion of the urb
+ * 2000/09/07 disable bulk and control lists when unlinking the last
+ * endpoint descriptor in order to avoid unrecoverable errors on
+ * the Lucent chips. (rwc@sgi)
+ * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
+ * urb unlink probs, indentation fixes
+ * 2000/08/11 various oops fixes mostly affecting iso and cleanup from
+ * device unplugs.
+ * 2000/06/28 use PCI hotplug framework, for better power management
+ * and for Cardbus support (David Brownell)
+ * 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling
+ * when the controller loses power; handle UE; cleanup; ...
+ *
+ * v5.2 1999/12/07 URB 3rd preview,
+ * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
+ * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume
+ * i386: HUB, Keyboard, Mouse, Printer
+ *
+ * v4.3 1999/10/27 multiple HCs, bulk_request
+ * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
+ * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
+ * v4.0 1999/08/18
+ * v3.0 1999/06/25
+ * v2.1 1999/05/09 code clean up
+ * v2.0 1999/05/04
+ * v1.0 1999/04/27 initial release
+ *
+ * This file is licenced under GPL
+ * $Id: ohci-hcd.c,v 1.7 2002/01/19 00:20:56 dbrownell Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h> /* for in_interrupt () */
+
+#ifndef CONFIG_USB_DEBUG
+ #define CONFIG_USB_DEBUG /* this is still experimental! */
+#endif
+
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+
+#include <linux/usb.h>
+#include "../hcd.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#ifdef CONFIG_PMAC_PBOOK
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/pci-bridge.h>
+#ifndef CONFIG_PM
+# define CONFIG_PM
+#endif
+#endif
+
+/*
+ * TO DO:
+ *
+ * - "disabled" should be the hcd state
+ * - bandwidth alloc to generic code
+ * - lots more testing!!
+ */
+
+#define DRIVER_VERSION "$Revision: 1.7 $"
+#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
+#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
+
+/*-------------------------------------------------------------------------*/
+
+#define OHCI_USE_NPS // force NoPowerSwitching mode
+// #define OHCI_VERBOSE_DEBUG /* not always helpful */
+
+/* For initializing controller (mask in an HCFS mode too) */
+#define OHCI_CONTROL_INIT \
+ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+
+#define OHCI_UNLINK_TIMEOUT (HZ / 10)
+
+/*-------------------------------------------------------------------------*/
+
+#include "ohci.h"
+
+#include "ohci-hub.c"
+#include "ohci-dbg.c"
+#include "ohci-mem.c"
+#include "ohci-q.c"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * queue up an urb for anything except the root hub
+ */
+static int ohci_urb_enqueue (
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ int mem_flags
+) {
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct ed *ed;
+ urb_priv_t *urb_priv;
+ unsigned int pipe = urb->pipe;
+ int i, size = 0;
+ unsigned long flags;
+ int bustime = 0;
+
+#ifdef OHCI_VERBOSE_DEBUG
+ urb_print (urb, "SUB", usb_pipein (pipe));
+#endif
+
+ /* every endpoint has a ed, locate and fill it */
+ if (! (ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) {
+ usb_dec_dev_use (urb->dev);
+ return -ENOMEM;
+ }
+
+ /* for the private part of the URB we need the number of TDs (size) */
+ switch (usb_pipetype (pipe)) {
+ case PIPE_CONTROL:
+ /* 1 TD for setup, 1 for ACK, plus ... */
+ size = 2;
+ /* FALLTHROUGH */
+ case PIPE_BULK:
+ /* one TD for every 4096 Bytes (can be upto 8K) */
+ size += urb->transfer_buffer_length / 4096;
+ /* ... and for any remaining bytes ... */
+ if ((urb->transfer_buffer_length % 4096) != 0)
+ size++;
+ /* ... and maybe a zero length packet to wrap it up */
+ if (size == 0)
+ size++;
+ else if ((urb->transfer_flags & USB_ZERO_PACKET) != 0
+ && (urb->transfer_buffer_length
+ % usb_maxpacket (urb->dev, pipe,
+ usb_pipeout (pipe))) != 0)
+ size++;
+ break;
+ case PIPE_ISOCHRONOUS: /* number of packets from URB */
+ size = urb->number_of_packets;
+ if (size <= 0) {
+ usb_dec_dev_use (urb->dev);
+ return -EINVAL;
+ }
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc [i].actual_length = 0;
+ urb->iso_frame_desc [i].status = -EXDEV;
+ }
+ break;
+ case PIPE_INTERRUPT: /* one TD */
+ size = 1;
+ break;
+ }
+
+ /* allocate the private part of the URB */
+ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *),
+ mem_flags);
+ if (!urb_priv) {
+ usb_dec_dev_use (urb->dev);
+ return -ENOMEM;
+ }
+ memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));
+
+ /* fill the private part of the URB */
+ urb_priv->length = size;
+ urb_priv->ed = ed;
+
+ /* allocate the TDs (updating hash chains) */
+ spin_lock_irqsave (&ohci->lock, flags);
+ for (i = 0; i < size; i++) {
+ urb_priv->td [i] = td_alloc (ohci, SLAB_ATOMIC);
+ if (!urb_priv->td [i]) {
+ urb_priv->length = i;
+ urb_free_priv (ohci, urb_priv);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ usb_dec_dev_use (urb->dev);
+ return -ENOMEM;
+ }
+ }
+
+// FIXME: much of this switch should be generic, move to hcd code ...
+
+ /* allocate and claim bandwidth if needed; ISO
+ * needs start frame index if it was't provided.
+ */
+ switch (usb_pipetype (pipe)) {
+ case PIPE_ISOCHRONOUS:
+ if (urb->transfer_flags & USB_ISO_ASAP) {
+ urb->start_frame = ( (ed->state == ED_OPER)
+ ? (ed->last_iso + 1)
+ : (le16_to_cpu (ohci->hcca->frame_no)
+ + 10)) & 0xffff;
+ }
+ /* FALLTHROUGH */
+ case PIPE_INTERRUPT:
+ if (urb->bandwidth == 0) {
+ bustime = usb_check_bandwidth (urb->dev, urb);
+ }
+ if (bustime < 0) {
+ urb_free_priv (ohci, urb_priv);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ usb_dec_dev_use (urb->dev);
+ return bustime;
+ }
+ usb_claim_bandwidth (urb->dev, urb,
+ bustime, usb_pipeisoc (urb->pipe));
+ }
+
+ urb->hcpriv = urb_priv;
+
+ /* link the ed into a chain if is not already */
+ if (ed->state != ED_OPER)
+ ep_link (ohci, ed);
+
+ /* fill the TDs and link it to the ed */
+ td_submit_urb (urb);
+
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ return 0;
+}
+
+/*
+ * decouple the URB from the HC queues (TDs, urb_priv); it's
+ * already marked for deletion. reporting is always done
+ * asynchronously, and we might be dealing with an urb that's
+ * almost completed anyway...
+ */
+static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ unsigned long flags;
+
+#ifdef DEBUG
+ urb_print (urb, "UNLINK", 1);
+#endif
+
+ if (!ohci->disabled) {
+ urb_priv_t *urb_priv;
+
+ /* flag the urb's data for deletion in some upcoming
+ * SF interrupt's delete list processing
+ */
+ spin_lock_irqsave (&ohci->lock, flags);
+ urb_priv = urb->hcpriv;
+
+ if (!urb_priv || (urb_priv->state == URB_DEL)) {
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return 0;
+ }
+
+ urb_priv->state = URB_DEL;
+ ed_unlink (urb->dev, urb_priv->ed);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ } else {
+ /*
+ * with HC dead, we won't respect hc queue pointers
+ * any more ... just clean up every urb's memory.
+ */
+ finish_urb (ohci, urb);
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ohci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct hcd_dev *dev = (struct hcd_dev *) udev->hcpriv;
+ int i;
+ unsigned long flags;
+
+ /* free any eds, and dummy tds, still hanging around */
+ spin_lock_irqsave (&ohci->lock, flags);
+ for (i = 0; i < 32; i++) {
+ struct ed *ed = dev->ep [i];
+ struct td *tdTailP;
+
+ if (!ed)
+ continue;
+
+ ed->state &= ~ED_URB_DEL;
+ if (ohci->disabled && ed->state == ED_OPER)
+ ed->state = ED_UNLINK;
+ switch (ed->state) {
+ case ED_NEW:
+ break;
+ case ED_UNLINK:
+ tdTailP = dma_to_td (ohci,
+ le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
+ td_free (ohci, tdTailP); /* free dummy td */
+ hash_free_ed (ohci, ed);
+ break;
+
+ case ED_OPER:
+ default:
+ err ("illegal ED %d state in free_config, %d",
+ i, ed->state);
+#ifdef DEBUG
+ BUG ();
+#endif
+ }
+ ed_free (ohci, ed);
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+}
+
+static int ohci_get_frame (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ dbg ("%s: ohci_get_frame", hcd->bus_name);
+ return le16_to_cpu (ohci->hcca->frame_no);
+}
+
+/*-------------------------------------------------------------------------*
+ * HC functions
+ *-------------------------------------------------------------------------*/
+
+/* reset the HC and BUS */
+
+static int hc_reset (struct ohci_hcd *ohci)
+{
+ int timeout = 30;
+ int smm_timeout = 50; /* 0,5 sec */
+
+ if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
+ writel (OHCI_INTR_OC, &ohci->regs->intrenable);
+ writel (OHCI_OCR, &ohci->regs->cmdstatus);
+ dbg ("USB HC TakeOver from SMM");
+ while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
+ wait_ms (10);
+ if (--smm_timeout == 0) {
+ err ("USB HC TakeOver failed!");
+ return -1;
+ }
+ }
+ }
+
+ /* Disable HC interrupts */
+ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+ dbg ("USB HC reset_hc %s: ctrl = 0x%x ;",
+ ohci->hcd.bus_name,
+ readl (&ohci->regs->control));
+
+ /* Reset USB (needed by some controllers) */
+ writel (0, &ohci->regs->control);
+
+ /* HC Reset requires max 10 ms delay */
+ writel (OHCI_HCR, &ohci->regs->cmdstatus);
+ while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+ if (--timeout == 0) {
+ err ("USB HC reset timed out!");
+ return -1;
+ }
+ udelay (1);
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * enable interrupts
+ * connect the virtual root hub
+ */
+static int hc_start (struct ohci_hcd *ohci)
+{
+ __u32 mask;
+ unsigned int fminterval;
+ struct usb_device *udev;
+
+ spin_lock_init (&ohci->lock);
+ ohci->disabled = 1;
+ ohci->sleeping = 0;
+
+ /* Tell the controller where the control and bulk lists are
+ * The lists are empty now. */
+
+ writel (0, &ohci->regs->ed_controlhead);
+ writel (0, &ohci->regs->ed_bulkhead);
+
+ /* a reset clears this */
+ writel ((u32) ohci->hcca_dma, &ohci->regs->hcca);
+
+ fminterval = 0x2edf;
+ writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
+ fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
+ writel (fminterval, &ohci->regs->fminterval);
+ writel (0x628, &ohci->regs->lsthresh);
+
+ /* start controller operations */
+ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci->disabled = 0;
+ writel (ohci->hc_control, &ohci->regs->control);
+
+ /* Choose the interrupts we care about now, others later on demand */
+ mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
+ writel (mask, &ohci->regs->intrstatus);
+ writel (mask, &ohci->regs->intrenable);
+
+#ifdef OHCI_USE_NPS
+ /* required for AMD-756 and some Mac platforms */
+ writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM,
+ &ohci->regs->roothub.a);
+ writel (RH_HS_LPSC, &ohci->regs->roothub.status);
+#endif /* OHCI_USE_NPS */
+
+ // POTPGT delay is bits 24-31, in 2 ms units.
+ mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
+
+ /* connect the virtual root hub */
+ ohci->hcd.bus->root_hub = udev = usb_alloc_dev (NULL, ohci->hcd.bus);
+ ohci->hcd.state = USB_STATE_READY;
+ if (!udev) {
+ ohci->disabled = 1;
+// FIXME cleanup
+ return -ENOMEM;
+ }
+
+ usb_connect (udev);
+ udev->speed = USB_SPEED_FULL;
+ if (usb_new_device (udev) != 0) {
+ usb_free_dev (udev);
+ ohci->disabled = 1;
+// FIXME cleanup
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* an interrupt happens */
+
+static void ohci_irq (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct ohci_regs *regs = ohci->regs;
+ int ints;
+
+ if ((ohci->hcca->done_head != 0)
+ && ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
+ ints = OHCI_INTR_WDH;
+ } else if ((ints = (readl (®s->intrstatus)
+ & readl (®s->intrenable))) == 0) {
+ return;
+ }
+
+ // dbg ("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no));
+
+ if (ints & OHCI_INTR_UE) {
+ ohci->disabled++;
+ err ("OHCI Unrecoverable Error, %s disabled", hcd->bus_name);
+ // e.g. due to PCI Master/Target Abort
+
+#ifdef DEBUG
+ ohci_dump (ohci, 1);
+#endif
+ hc_reset (ohci);
+ }
+
+ if (ints & OHCI_INTR_WDH) {
+ writel (OHCI_INTR_WDH, ®s->intrdisable);
+ dl_done_list (ohci, dl_reverse_done_list (ohci));
+ writel (OHCI_INTR_WDH, ®s->intrenable);
+ }
+
+ if (ints & OHCI_INTR_SO) {
+ dbg ("USB Schedule overrun");
+ writel (OHCI_INTR_SO, ®s->intrenable);
+ }
+
+ // FIXME: this assumes SOF (1/ms) interrupts don't get lost...
+ if (ints & OHCI_INTR_SF) {
+ unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1;
+ writel (OHCI_INTR_SF, ®s->intrdisable);
+ if (ohci->ed_rm_list [!frame] != NULL) {
+ dl_del_list (ohci, !frame);
+ }
+ if (ohci->ed_rm_list [frame] != NULL)
+ writel (OHCI_INTR_SF, ®s->intrenable);
+ }
+
+ writel (ints, ®s->intrstatus);
+ writel (OHCI_INTR_MIE, ®s->intrenable);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ohci_stop (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ dbg ("%s: stop %s controller%s",
+ hcd->bus_name,
+ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
+ ohci->disabled ? " (disabled)" : ""
+ );
+#ifdef DEBUG
+ ohci_dump (ohci, 1);
+#endif
+
+ if (!ohci->disabled)
+ hc_reset (ohci);
+
+ ohci_mem_cleanup (ohci);
+
+#ifdef CONFIG_PCI
+ pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca,
+ ohci->hcca, ohci->hcca_dma);
+#endif
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_start (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+#ifdef CONFIG_PCI
+ if (hcd->pdev) {
+ ohci->hcca = pci_alloc_consistent (hcd->pdev,
+ sizeof *ohci->hcca, &ohci->hcca_dma);
+ if (!ohci->hcca)
+ return -ENOMEM;
+
+ /* AMD 756, for most chips (early revs), corrupts register
+ * values on read ... so enable the vendor workaround.
+ */
+ if (hcd->pdev->vendor == 0x1022
+ && hcd->pdev->device == 0x740c) {
+ ohci->flags = OHCI_QUIRK_AMD756;
+ info ("%s: AMD756 erratum 4 workaround",
+ hcd->bus_name);
+ }
+
+ /* Apple's OHCI driver has a lot of bizarre workarounds
+ * for this chip. Evidently control and bulk lists
+ * can get confused. (B&W G3 models, and ...)
+ */
+ else if (hcd->pdev->vendor == 0x1045
+ && hcd->pdev->device == 0xc861) {
+ info ("%s: WARNING: OPTi workarounds unavailable",
+ hcd->bus_name);
+ }
+ }
+#else
+# error "where's hcca coming from?"
+#endif /* CONFIG_PCI */
+
+ memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
+ if ((ret = ohci_mem_init (ohci)) < 0) {
+ ohci_stop (hcd);
+ return ret;
+ }
+ ohci->regs = hcd->regs;
+
+ if (hc_reset (ohci) < 0) {
+ ohci_stop (hcd);
+ return -ENODEV;
+ }
+
+ if (hc_start (ohci) < 0) {
+ err ("can't start %s", ohci->hcd.bus_name);
+ ohci_stop (hcd);
+ return -EBUSY;
+ }
+
+#ifdef DEBUG
+ ohci_dump (ohci, 1);
+#endif
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+
+static int ohci_suspend (struct usb_hcd *hcd, u32 state)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ unsigned long flags;
+ u16 cmd;
+
+ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
+ dbg ("can't suspend %s (state is %s)", hcd->bus_name,
+ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
+ return -EIO;
+ }
+
+ /* act as if usb suspend can always be used */
+ dbg ("%s: suspend to %d", hcd->bus_name, state);
+ ohci->sleeping = 1;
+
+ /* First stop processing */
+ spin_lock_irqsave (&ohci->lock, flags);
+ ohci->hc_control &=
+ ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
+ writel (ohci->hc_control, &ohci->regs->control);
+ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+ (void) readl (&ohci->regs->intrstatus);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ /* Wait a frame or two */
+ mdelay (1);
+ if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
+ mdelay (1);
+
+ #ifdef CONFIG_PMAC_PBOOK
+ if (_machine == _MACH_Pmac)
+ disable_irq (ohci->irq);
+ /* else, 2.4 assumes shared irqs -- don't disable */
+ #endif
+
+ /* Enable remote wakeup */
+ writel (readl (&ohci->regs->intrenable) | OHCI_INTR_RD,
+ &ohci->regs->intrenable);
+
+ /* Suspend chip and let things settle down a bit */
+ ohci->hc_control = OHCI_USB_SUSPEND;
+ writel (ohci->hc_control, &ohci->regs->control);
+ (void) readl (&ohci->regs->control);
+ mdelay (500); /* No schedule here ! */
+
+ switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
+ case OHCI_USB_RESET:
+ dbg ("%s suspend->reset ?", hcd->bus_name);
+ break;
+ case OHCI_USB_RESUME:
+ dbg ("%s suspend->resume ?", hcd->bus_name);
+ break;
+ case OHCI_USB_OPER:
+ dbg ("%s suspend->operational ?", hcd->bus_name);
+ break;
+ case OHCI_USB_SUSPEND:
+ dbg ("%s suspended", hcd->bus_name);
+ break;
+ }
+
+ /* In some rare situations, Apple's OHCI have happily trashed
+ * memory during sleep. We disable its bus master bit during
+ * suspend
+ */
+ pci_read_config_word (hcd->pdev, PCI_COMMAND, &cmd);
+ cmd &= ~PCI_COMMAND_MASTER;
+ pci_write_config_word (hcd->pdev, PCI_COMMAND, cmd);
+#ifdef CONFIG_PMAC_PBOOK
+ {
+ struct device_node *of_node;
+
+ /* Disable USB PAD & cell clock */
+ of_node = pci_device_to_OF_node (hcd->pdev);
+ if (of_node)
+ pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
+ }
+#endif
+ return 0;
+}
+
+
+// FIXME: this restart logic should be generic,
+// and handle full hcd state cleanup
+
+/* controller died; cleanup debris, then restart */
+/* must not be called from interrupt context */
+
+static int hc_restart (struct ohci_hcd *ohci)
+{
+ int temp;
+ int i;
+
+ ohci->disabled = 1;
+ ohci->sleeping = 0;
+ if (ohci->hcd.bus->root_hub)
+ usb_disconnect (&ohci->hcd.bus->root_hub);
+
+ /* empty the interrupt branches */
+ for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load [i] = 0;
+ for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
+
+ /* no EDs to remove */
+ ohci->ed_rm_list [0] = NULL;
+ ohci->ed_rm_list [1] = NULL;
+
+ /* empty control and bulk lists */
+ ohci->ed_isotail = NULL;
+ ohci->ed_controltail = NULL;
+ ohci->ed_bulktail = NULL;
+
+ if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
+ err ("can't restart %s, %d", ohci->hcd.bus_name, temp);
+ return temp;
+ } else
+ dbg ("restart %s completed", ohci->hcd.bus_name);
+ return 0;
+}
+
+static int ohci_resume (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int temp;
+ int retval = 0;
+ unsigned long flags;
+
+#ifdef CONFIG_PMAC_PBOOK
+ {
+ struct device_node *of_node;
+
+ /* Re-enable USB PAD & cell clock */
+ of_node = pci_device_to_OF_node (hcd->pdev);
+ if (of_node)
+ pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1);
+ }
+#endif
+ /* did we suspend, or were we powered off? */
+ ohci->hc_control = readl (&ohci->regs->control);
+ temp = ohci->hc_control & OHCI_CTRL_HCFS;
+
+#ifdef DEBUG
+ /* the registers may look crazy here */
+ ohci_dump_status (ohci);
+#endif
+
+ /* Re-enable bus mastering */
+ pci_set_master (ohci->hcd.pdev);
+
+ switch (temp) {
+
+ case OHCI_USB_RESET: // lost power
+ info ("USB restart: %s", hcd->bus_name);
+ retval = hc_restart (ohci);
+ break;
+
+ case OHCI_USB_SUSPEND: // host wakeup
+ case OHCI_USB_RESUME: // remote wakeup
+ info ("USB continue: %s from %s wakeup", hcd->bus_name,
+ (temp == OHCI_USB_SUSPEND)
+ ? "host" : "remote");
+ ohci->hc_control = OHCI_USB_RESUME;
+ writel (ohci->hc_control, &ohci->regs->control);
+ (void) readl (&ohci->regs->control);
+ mdelay (20); /* no schedule here ! */
+ /* Some controllers (lucent) need a longer delay here */
+ mdelay (15);
+
+ temp = readl (&ohci->regs->control);
+ temp = ohci->hc_control & OHCI_CTRL_HCFS;
+ if (temp != OHCI_USB_RESUME) {
+ err ("controller %s won't resume", hcd->bus_name);
+ ohci->disabled = 1;
+ retval = -EIO;
+ break;
+ }
+
+ /* Some chips likes being resumed first */
+ writel (OHCI_USB_OPER, &ohci->regs->control);
+ (void) readl (&ohci->regs->control);
+ mdelay (3);
+
+ /* Then re-enable operations */
+ spin_lock_irqsave (&ohci->lock, flags);
+ ohci->disabled = 0;
+ ohci->sleeping = 0;
+ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ if (!ohci->ed_rm_list [0] && !ohci->ed_rm_list [1]) {
+ if (ohci->ed_controltail)
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ if (ohci->ed_bulktail)
+ ohci->hc_control |= OHCI_CTRL_BLE;
+ }
+ hcd->state = USB_STATE_READY;
+ writel (ohci->hc_control, &ohci->regs->control);
+
+ /* trigger a start-frame interrupt (why?) */
+ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+ writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+
+ /* Check for a pending done list */
+ writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);
+ (void) readl (&ohci->regs->intrdisable);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ #ifdef CONFIG_PMAC_PBOOK
+ if (_machine == _MACH_Pmac)
+ enable_irq (ohci->irq);
+ #endif
+ if (ohci->hcca->done_head)
+ dl_done_list (ohci, dl_reverse_done_list (ohci));
+ writel (OHCI_INTR_WDH, &ohci->regs->intrenable);
+
+// writel (OHCI_BLF, &ohci->regs->cmdstatus);
+// writel (OHCI_CLF, &ohci->regs->cmdstatus);
+ohci_dump_status (ohci);
+dbg ("sleeping = %d, disabled = %d", ohci->sleeping, ohci->disabled);
+ break;
+
+ default:
+ warn ("odd PCI resume for %s", hcd->bus_name);
+ }
+ return retval;
+}
+
+#endif /* CONFIG_PM */
+
+
+/*-------------------------------------------------------------------------*/
+
+static const char hcd_name [] = "ohci-hcd";
+
+static const struct hc_driver ohci_driver = {
+ description: hcd_name,
+
+ /*
+ * generic hardware linkage
+ */
+ irq: ohci_irq,
+ flags: HCD_MEMORY | HCD_USB11,
+
+ /*
+ * basic lifecycle operations
+ */
+ start: ohci_start,
+#ifdef CONFIG_PM
+ suspend: ohci_suspend,
+ resume: ohci_resume,
+#endif
+ stop: ohci_stop,
+
+ /*
+ * memory lifecycle (except per-request)
+ */
+ hcd_alloc: ohci_hcd_alloc,
+ hcd_free: ohci_hcd_free,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ urb_enqueue: ohci_urb_enqueue,
+ urb_dequeue: ohci_urb_dequeue,
+ free_config: ohci_free_config,
+
+ /*
+ * scheduling support
+ */
+ get_frame_number: ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ hub_status_data: ohci_hub_status_data,
+ hub_control: ohci_hub_control,
+};
+
+#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION (DRIVER_INFO);
+MODULE_LICENSE ("GPL");
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PCI
+
+/* There do exist non-PCI implementations of OHCI ...
+ * Examples include the SA-1111 (ARM) and some MIPS
+ * and related hardware.
+ */
+
+static const struct pci_device_id __devinitdata pci_ids [] = { {
+
+ /* handle any USB OHCI controller */
+ class: (PCI_CLASS_SERIAL_USB << 8) | 0x10,
+ class_mask: ~0,
+ driver_data: (unsigned long) &ohci_driver,
+
+ /* no matter who makes it */
+ vendor: PCI_ANY_ID,
+ device: PCI_ANY_ID,
+ subvendor: PCI_ANY_ID,
+ subdevice: PCI_ANY_ID,
+
+ }, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver ohci_pci_driver = {
+ name: (char *) hcd_name,
+ id_table: pci_ids,
+
+ probe: usb_hcd_pci_probe,
+ remove: usb_hcd_pci_remove,
+
+#ifdef CONFIG_PM
+ suspend: usb_hcd_pci_suspend,
+ resume: usb_hcd_pci_resume,
+#endif
+};
+
+
+static int __init ohci_hcd_init (void)
+{
+ dbg (DRIVER_INFO);
+ dbg ("block sizes: ed %d td %d",
+ sizeof (struct ed), sizeof (struct td));
+ return pci_module_init (&ohci_pci_driver);
+}
+module_init (ohci_hcd_init);
+
+/*-------------------------------------------------------------------------*/
+
+static void __exit ohci_hcd_cleanup (void)
+{
+ pci_unregister_driver (&ohci_pci_driver);
+}
+module_exit (ohci_hcd_cleanup);
+
+#endif /* CONFIG_PCI */
+
--- /dev/null
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under GPL
+ * $Id: ohci-hub.c,v 1.2 2002/01/19 00:21:49 dbrownell Exp $
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * OHCI Root Hub ... the nonsharable stuff
+ *
+ * Registers don't need cpu_to_le32, that happens transparently
+ */
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect. AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define read_roothub(hc, register, mask) ({ \
+ u32 temp = readl (&hc->regs->roothub.register); \
+ if (hc->flags & OHCI_QUIRK_AMD756) \
+ while (temp & mask) \
+ temp = readl (&hc->regs->roothub.register); \
+ temp; })
+
+static u32 roothub_a (struct ohci_hcd *hc)
+ { return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci_hcd *hc)
+ { return readl (&hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci_hcd *hc)
+ { return readl (&hc->regs->roothub.status); }
+static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
+ { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
+
+/*-------------------------------------------------------------------------*/
+
+#define dbg_port(hc,label,num,value) \
+ dbg ("%s: %s roothub.portstatus [%d] " \
+ "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", \
+ hc->hcd.bus_name, label, num, temp, \
+ (temp & RH_PS_PRSC) ? " PRSC" : "", \
+ (temp & RH_PS_OCIC) ? " OCIC" : "", \
+ (temp & RH_PS_PSSC) ? " PSSC" : "", \
+ (temp & RH_PS_PESC) ? " PESC" : "", \
+ (temp & RH_PS_CSC) ? " CSC" : "", \
+ \
+ (temp & RH_PS_LSDA) ? " LSDA" : "", \
+ (temp & RH_PS_PPS) ? " PPS" : "", \
+ (temp & RH_PS_PRS) ? " PRS" : "", \
+ (temp & RH_PS_POCI) ? " POCI" : "", \
+ (temp & RH_PS_PSS) ? " PSS" : "", \
+ \
+ (temp & RH_PS_PES) ? " PES" : "", \
+ (temp & RH_PS_CCS) ? " CCS" : "" \
+ );
+
+
+/*-------------------------------------------------------------------------*/
+
+/* build "status change" packet (one or two bytes) from HC registers */
+
+static int
+ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ports, i, changed = 0, length = 1;
+
+ ports = roothub_a (ohci) & RH_A_NDP;
+ if (ports > MAX_ROOT_PORTS) {
+ err ("%s: bogus NDP=%d", hcd->bus_name, ports);
+ err ("rereads as NDP=%d",
+ readl (&ohci->regs->roothub.a) & RH_A_NDP);
+ /* retry later; "should not happen" */
+ return 0;
+ }
+
+ /* init status */
+ if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
+ buf [0] = changed = 1;
+ else
+ buf [0] = 0;
+ if (ports > 7) {
+ buf [1] = 0;
+ length++;
+ }
+
+ /* look at each port */
+ for (i = 0; i < ports; i++) {
+ u32 status = roothub_portstatus (ohci, i);
+
+ status &= RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
+ | RH_PS_OCIC | RH_PS_PRSC;
+ if (status) {
+ changed = 1;
+ set_bit (i + 1, buf);
+ }
+ }
+ return changed ? length : 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ohci_hub_descriptor (
+ struct ohci_hcd *ohci,
+ struct usb_hub_descriptor *desc
+) {
+ u32 rh = roothub_a (ohci);
+ int ports = rh & RH_A_NDP;
+ u16 temp;
+
+ desc->bDescriptorType = 0x29;
+ desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = ports;
+ temp = 1 + (ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+
+ temp = 0;
+ if (rh & RH_A_PSM) /* per-port power switching? */
+ temp |= 0x0001;
+ if (rh & RH_A_NOCP) /* no overcurrent reporting? */
+ temp |= 0x0010;
+ else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */
+ temp |= 0x0008;
+ desc->wHubCharacteristics = cpu_to_le16 (temp);
+
+ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ rh = roothub_b (ohci);
+ desc->bitmap [0] = rh & RH_B_DR;
+ if (ports > 7) {
+ desc->bitmap [1] = (rh & RH_B_DR) >> 8;
+ desc->bitmap [2] = desc->bitmap [3] = 0xff;
+ } else
+ desc->bitmap [1] = 0xff;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hub_control (
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ports;
+ u32 temp;
+ int retval = 0;
+
+ // if (port request)
+ ports = roothub_a (ohci) & RH_A_NDP;
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ writel (RH_HS_OCIC, &ohci->regs->roothub.status);
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ temp = RH_PS_CCS;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ temp = RH_PS_PESC;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ temp = RH_PS_POCI;
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ temp = RH_PS_PSSC;
+ break;
+ case USB_PORT_FEAT_POWER:
+ temp = RH_PS_LSDA;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ temp = RH_PS_CSC;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ temp = RH_PS_OCIC;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ temp = RH_PS_PRSC;
+ break;
+ default:
+ goto error;
+ }
+ writel (temp, &ohci->regs->roothub.portstatus [wIndex]);
+ // readl (&ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case GetHubDescriptor:
+ ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
+ break;
+ case GetHubStatus:
+ temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
+ *(u32 *) buf = cpu_to_le32 (temp);
+ break;
+ case GetPortStatus:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = roothub_portstatus (ohci, wIndex);
+ *(u32 *) buf = cpu_to_le32 (temp);
+
+#ifndef OHCI_VERBOSE_DEBUG
+ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
+#endif
+ dbg_port (ohci, "GetStatus", wIndex + 1, temp);
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ // FIXME: this can be cleared, yes?
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ writel (RH_PS_PSS,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case USB_PORT_FEAT_POWER:
+ writel (RH_PS_PPS,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case USB_PORT_FEAT_RESET:
+ temp = readl (&ohci->regs->roothub.portstatus [wIndex]);
+ if (temp & RH_PS_CCS)
+ writel (RH_PS_PRS,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ default:
+ goto error;
+ }
+ break;
+
+ default:
+error:
+ /* "protocol stall" on error */
+ retval = -EPIPE;
+ }
+ return retval;
+}
+
--- /dev/null
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under GPL
+ * $Id: ohci-mem.c,v 1.2 2002/01/19 00:22:13 dbrownell Exp $
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * There's basically three types of memory:
+ * - data used only by the HCD ... kmalloc is fine
+ * - async and periodic schedules, shared by HC and HCD ... these
+ * need to use pci_pool or pci_alloc_consistent
+ * - driver buffers, read/written by HC ... single shot DMA mapped
+ *
+ * There's also PCI "register" data, which is memory mapped.
+ * No memory seen by this driver is pagable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_hcd *ohci_hcd_alloc (void)
+{
+ struct ohci_hcd *ohci;
+
+ ohci = (struct ohci_hcd *) kmalloc (sizeof *ohci, GFP_KERNEL);
+ if (ohci != 0) {
+ memset (ohci, 0, sizeof (struct ohci_hcd));
+ return &ohci->hcd;
+ }
+ return 0;
+}
+
+static void ohci_hcd_free (struct usb_hcd *hcd)
+{
+ kfree (hcd_to_ohci (hcd));
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+# define OHCI_MEM_FLAGS SLAB_POISON
+#else
+# define OHCI_MEM_FLAGS 0
+#endif
+
+#ifndef CONFIG_PCI
+# error "usb-ohci currently requires PCI-based controllers"
+ /* to support non-PCI OHCIs, you need custom bus/mem/... glue */
+#endif
+
+
+/* Recover a TD/ED using its collision chain */
+static inline void *
+dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma)
+{
+ struct hash_t * scan = entry->head;
+ while (scan && scan->dma != dma)
+ scan = scan->next;
+ return scan->virt;
+}
+
+static inline struct ed *
+dma_to_ed (struct ohci_hcd *hc, dma_addr_t ed_dma)
+{
+ return (struct ed *) dma_to_ed_td(&(hc->ed_hash [ED_HASH_FUNC(ed_dma)]),
+ ed_dma);
+}
+
+static inline struct td *
+dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
+{
+ return (struct td *) dma_to_ed_td(&(hc->td_hash [TD_HASH_FUNC(td_dma)]),
+ td_dma);
+}
+
+// FIXME: when updating the hashtables this way, mem_flags is unusable...
+
+/* Add a hash entry for a TD/ED; return true on success */
+static int
+hash_add_ed_td (
+ struct hash_list_t *entry,
+ void *virt,
+ dma_addr_t dma,
+ int mem_flags
+)
+{
+ struct hash_t * scan;
+
+ scan = (struct hash_t *) kmalloc (sizeof *scan, mem_flags);
+ if (!scan)
+ return 0;
+
+ if (!entry->tail) {
+ entry->head = entry->tail = scan;
+ } else {
+ entry->tail->next = scan;
+ entry->tail = scan;
+ }
+
+ scan->virt = virt;
+ scan->dma = dma;
+ scan->next = NULL;
+ return 1;
+}
+
+static inline int
+hash_add_ed (struct ohci_hcd *hc, struct ed *ed, int mem_flags)
+{
+ return hash_add_ed_td (&(hc->ed_hash [ED_HASH_FUNC (ed->dma)]),
+ ed, ed->dma, mem_flags);
+}
+
+static inline int
+hash_add_td (struct ohci_hcd *hc, struct td *td, int mem_flags)
+{
+ return hash_add_ed_td (&(hc->td_hash [TD_HASH_FUNC (td->td_dma)]),
+ td, td->td_dma, mem_flags);
+}
+
+
+static void
+hash_free_ed_td (struct hash_list_t *entry, void *virt)
+{
+ struct hash_t *scan, *prev;
+ scan = prev = entry->head;
+
+ // Find and unlink hash entry
+ while (scan && scan->virt != virt) {
+ prev = scan;
+ scan = scan->next;
+ }
+ if (scan) {
+ if (scan == entry->head) {
+ if (entry->head == entry->tail)
+ entry->head = entry->tail = NULL;
+ else
+ entry->head = scan->next;
+ } else if (scan == entry->tail) {
+ entry->tail = prev;
+ prev->next = NULL;
+ } else
+ prev->next = scan->next;
+ kfree(scan);
+ }
+}
+
+static inline void
+hash_free_ed (struct ohci_hcd *hc, struct ed * ed)
+{
+ hash_free_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), ed);
+}
+
+static inline void
+hash_free_td (struct ohci_hcd *hc, struct td * td)
+{
+ hash_free_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), td);
+}
+
+
+static int ohci_mem_init (struct ohci_hcd *ohci)
+{
+ ohci->td_cache = pci_pool_create ("ohci_td", ohci->hcd.pdev,
+ sizeof (struct td),
+ 32 /* byte alignment */,
+ 0 /* no page-crossing issues */,
+ GFP_KERNEL | OHCI_MEM_FLAGS);
+ if (!ohci->td_cache)
+ return -ENOMEM;
+ ohci->ed_cache = pci_pool_create ("ohci_ed", ohci->hcd.pdev,
+ sizeof (struct ed),
+ 16 /* byte alignment */,
+ 0 /* no page-crossing issues */,
+ GFP_KERNEL | OHCI_MEM_FLAGS);
+ if (!ohci->ed_cache) {
+ pci_pool_destroy (ohci->td_cache);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void ohci_mem_cleanup (struct ohci_hcd *ohci)
+{
+ if (ohci->td_cache) {
+ pci_pool_destroy (ohci->td_cache);
+ ohci->td_cache = 0;
+ }
+ if (ohci->ed_cache) {
+ pci_pool_destroy (ohci->ed_cache);
+ ohci->ed_cache = 0;
+ }
+}
+
+/* TDs ... */
+static struct td *
+td_alloc (struct ohci_hcd *hc, int mem_flags)
+{
+ dma_addr_t dma;
+ struct td *td;
+
+ td = pci_pool_alloc (hc->td_cache, mem_flags, &dma);
+ if (td) {
+ td->td_dma = dma;
+ /* hash it for later reverse mapping */
+ if (!hash_add_td (hc, td, mem_flags)) {
+ pci_pool_free (hc->td_cache, td, dma);
+ return NULL;
+ }
+ }
+ return td;
+}
+
+static inline void
+td_free (struct ohci_hcd *hc, struct td *td)
+{
+ hash_free_td (hc, td);
+ pci_pool_free (hc->td_cache, td, td->td_dma);
+}
+
+
+/* EDs ... */
+static struct ed *
+ed_alloc (struct ohci_hcd *hc, int mem_flags)
+{
+ dma_addr_t dma;
+ struct ed *ed;
+
+ ed = pci_pool_alloc (hc->ed_cache, mem_flags, &dma);
+ if (ed) {
+ memset (ed, 0, sizeof (*ed));
+ ed->dma = dma;
+ /* hash it for later reverse mapping */
+ if (!hash_add_ed (hc, ed, mem_flags)) {
+ pci_pool_free (hc->ed_cache, ed, dma);
+ return NULL;
+ }
+ }
+ return ed;
+}
+
+static inline void
+ed_free (struct ohci_hcd *hc, struct ed *ed)
+{
+ hash_free_ed (hc, ed);
+ pci_pool_free (hc->ed_cache, ed, ed->dma);
+}
+
--- /dev/null
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under GPL
+ * $Id: ohci-q.c,v 1.6 2002/01/19 00:23:15 dbrownell Exp $
+ */
+
+static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
+{
+ int last = urb_priv->length - 1;
+
+ if (last >= 0) {
+ int i;
+ struct td *td = urb_priv->td [0];
+#ifdef CONFIG_PCI
+ int len = td->urb->transfer_buffer_length;
+ int dir = usb_pipeout (td->urb->pipe)
+ ? PCI_DMA_TODEVICE
+ : PCI_DMA_FROMDEVICE;
+
+ /* unmap CTRL URB setup buffer (always td 0) */
+ if (usb_pipecontrol (td->urb->pipe)) {
+ pci_unmap_single (hc->hcd.pdev,
+ td->data_dma, 8, PCI_DMA_TODEVICE);
+
+ /* CTRL data buffer starts at td 1 if len > 0 */
+ if (len && last > 0)
+ td = urb_priv->td [1];
+ }
+ /* else: ISOC, BULK, INTR data buffer starts at td 0 */
+
+ /* unmap data buffer */
+ if (len && td->data_dma)
+ pci_unmap_single (hc->hcd.pdev,
+ td->data_dma, len, dir);
+#else
+# warning "assuming no buffer unmapping is needed"
+#endif
+
+ for (i = 0; i <= last; i++) {
+ td = urb_priv->td [i];
+ if (td)
+ td_free (hc, td);
+ }
+ }
+
+ kfree (urb_priv);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * URB goes back to driver, and isn't reissued.
+ * It's completely gone from HC data structures, so no locking
+ * is needed ... or desired! (Giveback can call back to hcd.)
+ */
+static inline void finish_urb (struct ohci_hcd *ohci, struct urb *urb)
+{
+ if (urb->hcpriv) {
+ urb_free_priv (ohci, urb->hcpriv);
+ urb->hcpriv = NULL;
+ }
+ usb_hcd_giveback_urb (&ohci->hcd, urb);
+}
+
+static void td_submit_urb (struct urb *urb);
+
+/*
+ * URB is reported to driver, is reissued if it's periodic.
+ */
+static int return_urb (struct ohci_hcd *hc, struct urb *urb)
+{
+ urb_priv_t *urb_priv = urb->hcpriv;
+ struct urb *urbt;
+ unsigned long flags;
+ int i;
+
+#ifdef DEBUG
+ if (!urb_priv) {
+ err ("already unlinked!");
+ BUG ();
+ }
+
+ /* just to be sure */
+ if (!urb->complete) {
+ err ("no completion!");
+ BUG ();
+ }
+#endif
+
+#ifdef OHCI_VERBOSE_DEBUG
+ urb_print (urb, "RET", usb_pipeout (urb->pipe));
+#endif
+
+// FIXME: but if urb->status says it was was unlinked ...
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_INTERRUPT:
+#ifdef CONFIG_PCI
+ pci_unmap_single (hc->hcd.pdev,
+ urb_priv->td [0]->data_dma,
+ urb->transfer_buffer_length,
+ usb_pipeout (urb->pipe)
+ ? PCI_DMA_TODEVICE
+ : PCI_DMA_FROMDEVICE);
+#endif
+ urb->complete (urb);
+
+ /* implicitly requeued */
+ urb->actual_length = 0;
+ urb->status = -EINPROGRESS;
+ if (urb_priv->state != URB_DEL) {
+ spin_lock_irqsave (&hc->lock, flags);
+ td_submit_urb (urb);
+ spin_unlock_irqrestore (&hc->lock, flags);
+ }
+ break;
+
+ case PIPE_ISOCHRONOUS:
+ for (urbt = urb->next;
+ urbt && (urbt != urb);
+ urbt = urbt->next)
+ continue;
+ if (urbt) { /* send the reply and requeue URB */
+#ifdef CONFIG_PCI
+// FIXME this style unmap is only done on this route ...
+ pci_unmap_single (hc->hcd.pdev,
+ urb_priv->td [0]->data_dma,
+ urb->transfer_buffer_length,
+ usb_pipeout (urb->pipe)
+ ? PCI_DMA_TODEVICE
+ : PCI_DMA_FROMDEVICE);
+#endif
+ urb->complete (urb);
+ spin_lock_irqsave (&hc->lock, flags);
+ urb->actual_length = 0;
+ urb->status = -EINPROGRESS;
+ urb->start_frame = urb_priv->ed->last_iso + 1;
+ if (urb_priv->state != URB_DEL) {
+ for (i = 0; i < urb->number_of_packets;
+ i++) {
+ urb->iso_frame_desc [i]
+ .actual_length = 0;
+ urb->iso_frame_desc [i]
+ .status = -EXDEV;
+ }
+ td_submit_urb (urb);
+ }
+// FIXME if not deleted, should have been "finished"
+ spin_unlock_irqrestore (&hc->lock, flags);
+
+ } else { /* not reissued */
+ finish_urb (hc, urb);
+ }
+ break;
+
+ /*
+ * C/B requests that get here are never reissued.
+ */
+ case PIPE_BULK:
+ case PIPE_CONTROL:
+ finish_urb (hc, urb);
+ break;
+ }
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/
+
+/* search for the right branch to insert an interrupt ed into the int tree
+ * do some load balancing;
+ * returns the branch and
+ * sets the interval to interval = 2^integer (ld (interval))
+ */
+static int ep_int_balance (struct ohci_hcd *ohci, int interval, int load)
+{
+ int i, branch = 0;
+
+ /* search for the least loaded interrupt endpoint branch */
+ for (i = 0; i < NUM_INTS ; i++)
+ if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i])
+ branch = i;
+
+ branch = branch % interval;
+ for (i = branch; i < NUM_INTS; i += interval)
+ ohci->ohci_int_load [i] += load;
+
+ return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* 2^int ( ld (inter)) */
+
+static int ep_2_n_interval (int inter)
+{
+ int i;
+
+ for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++)
+ continue;
+ return 1 << i;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the int tree is a binary tree
+ * in order to process it sequentially the indexes of the branches have
+ * to be mapped the mapping reverses the bits of a word of num_bits length
+ */
+static int ep_rev (int num_bits, int word)
+{
+ int i, wout = 0;
+
+ for (i = 0; i < num_bits; i++)
+ wout |= (( (word >> i) & 1) << (num_bits - i - 1));
+ return wout;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* link an ed into one of the HC chains */
+
+static int ep_link (struct ohci_hcd *ohci, struct ed *edi)
+{
+ int int_branch, i;
+ int inter, interval, load;
+ __u32 *ed_p;
+ volatile struct ed *ed = edi;
+
+ ed->state = ED_OPER;
+
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ ed->hwNextED = 0;
+ if (ohci->ed_controltail == NULL) {
+ writel (ed->dma, &ohci->regs->ed_controlhead);
+ } else {
+ ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
+ }
+ ed->ed_prev = ohci->ed_controltail;
+ if (!ohci->ed_controltail
+ && !ohci->ed_rm_list [0]
+ && !ohci->ed_rm_list [1]
+ && !ohci->sleeping
+ ) {
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ ohci->ed_controltail = edi;
+ break;
+
+ case PIPE_BULK:
+ ed->hwNextED = 0;
+ if (ohci->ed_bulktail == NULL) {
+ writel (ed->dma, &ohci->regs->ed_bulkhead);
+ } else {
+ ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
+ }
+ ed->ed_prev = ohci->ed_bulktail;
+ if (!ohci->ed_bulktail
+ && !ohci->ed_rm_list [0]
+ && !ohci->ed_rm_list [1]
+ && !ohci->sleeping
+ ) {
+ ohci->hc_control |= OHCI_CTRL_BLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ ohci->ed_bulktail = edi;
+ break;
+
+ case PIPE_INTERRUPT:
+ load = ed->int_load;
+ interval = ep_2_n_interval (ed->int_period);
+ ed->int_interval = interval;
+ int_branch = ep_int_balance (ohci, interval, load);
+ ed->int_branch = int_branch;
+
+ for (i = 0; i < ep_rev (6, interval); i += inter) {
+ inter = 1;
+ for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + int_branch]);
+ (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval);
+ ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED))
+ inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
+ ed->hwNextED = *ed_p;
+ *ed_p = cpu_to_le32 (ed->dma);
+ }
+#ifdef DEBUG
+ ep_print_int_eds (ohci, "LINK_INT");
+#endif
+ break;
+
+ case PIPE_ISOCHRONOUS:
+ ed->hwNextED = 0;
+ ed->int_interval = 1;
+ if (ohci->ed_isotail != NULL) {
+ ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
+ ed->ed_prev = ohci->ed_isotail;
+ } else {
+ for ( i = 0; i < NUM_INTS; i += inter) {
+ inter = 1;
+ for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i)]);
+ *ed_p != 0;
+ ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED))
+ inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
+ *ed_p = cpu_to_le32 (ed->dma);
+ }
+ ed->ed_prev = NULL;
+ }
+ ohci->ed_isotail = edi;
+#ifdef DEBUG
+ ep_print_int_eds (ohci, "LINK_ISO");
+#endif
+ break;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* unlink an ed from one of the HC chains.
+ * just the link to the ed is unlinked.
+ * the link from the ed still points to another operational ed or 0
+ * so the HC can eventually finish the processing of the unlinked ed
+ */
+static int ep_unlink (struct ohci_hcd *ohci, struct ed *ed)
+{
+ int int_branch;
+ int i;
+ int inter;
+ int interval;
+ __u32 *ed_p;
+
+ ed->hwINFO |= __constant_cpu_to_le32 (OHCI_ED_SKIP);
+
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_CLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ writel (le32_to_cpup (&ed->hwNextED),
+ &ohci->regs->ed_controlhead);
+ } else {
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ if (ohci->ed_controltail == ed) {
+ ohci->ed_controltail = ed->ed_prev;
+ } else {
+ (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
+ ->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ case PIPE_BULK:
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_BLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ writel (le32_to_cpup (&ed->hwNextED),
+ &ohci->regs->ed_bulkhead);
+ } else {
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ if (ohci->ed_bulktail == ed) {
+ ohci->ed_bulktail = ed->ed_prev;
+ } else {
+ (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
+ ->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ case PIPE_INTERRUPT:
+ int_branch = ed->int_branch;
+ interval = ed->int_interval;
+
+ for (i = 0; i < ep_rev (6, interval); i += inter) {
+ for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + int_branch]), inter = 1;
+ (*ed_p != 0) && (*ed_p != ed->hwNextED);
+ ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED),
+ inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval)) {
+ if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
+ *ed_p = ed->hwNextED;
+ break;
+ }
+ }
+ }
+ for (i = int_branch; i < NUM_INTS; i += interval)
+ ohci->ohci_int_load [i] -= ed->int_load;
+#ifdef DEBUG
+ ep_print_int_eds (ohci, "UNLINK_INT");
+#endif
+ break;
+
+ case PIPE_ISOCHRONOUS:
+ if (ohci->ed_isotail == ed)
+ ohci->ed_isotail = ed->ed_prev;
+ if (ed->hwNextED != 0)
+ (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
+ ->ed_prev = ed->ed_prev;
+
+ if (ed->ed_prev != NULL) {
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ } else {
+ for (i = 0; i < NUM_INTS; i++) {
+ for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i)]);
+ *ed_p != 0;
+ ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) {
+ // inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
+ if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
+ *ed_p = ed->hwNextED;
+ break;
+ }
+ }
+ }
+ }
+#ifdef DEBUG
+ ep_print_int_eds (ohci, "UNLINK_ISO");
+#endif
+ break;
+ }
+ ed->state = ED_UNLINK;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* (re)init an endpoint; this _should_ be done once at the
+ * usb_set_configuration command, but the USB stack is a bit stateless
+ * so we do it at every transaction.
+ * if the state of the ed is ED_NEW then a dummy td is added and the
+ * state is changed to ED_UNLINK
+ * in all other cases the state is left unchanged
+ * the ed info fields are set even though most of them should
+ * not change
+ */
+static struct ed *ep_add_ed (
+ struct usb_device *udev,
+ unsigned int pipe,
+ int interval,
+ int load,
+ int mem_flags
+) {
+ struct ohci_hcd *ohci = hcd_to_ohci (udev->bus->hcpriv);
+ struct hcd_dev *dev = (struct hcd_dev *) udev->hcpriv;
+ struct td *td;
+ struct ed *ed;
+ unsigned ep;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ ep = usb_pipeendpoint (pipe) << 1;
+ if (!usb_pipecontrol (pipe) && usb_pipeout (pipe))
+ ep |= 1;
+ if (!(ed = dev->ep [ep])) {
+ ed = ed_alloc (ohci, SLAB_ATOMIC);
+ if (!ed) {
+ /* out of memory */
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return NULL;
+ }
+ dev->ep [ep] = ed;
+ }
+
+ if (ed->state & ED_URB_DEL) {
+ /* pending unlink request */
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return NULL;
+ }
+
+ if (ed->state == ED_NEW) {
+ ed->hwINFO = __constant_cpu_to_le32 (OHCI_ED_SKIP);
+ /* dummy td; end of td list for ed */
+ td = td_alloc (ohci, SLAB_ATOMIC);
+ if (!td) {
+ /* out of memory */
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return NULL;
+ }
+ ed->hwTailP = cpu_to_le32 (td->td_dma);
+ ed->hwHeadP = ed->hwTailP;
+ ed->state = ED_UNLINK;
+ ed->type = usb_pipetype (pipe);
+ }
+
+ ohci->dev [usb_pipedevice (pipe)] = udev;
+
+// FIXME: don't do this if it's linked to the HC,
+// we might clobber data toggle or other state ...
+
+ ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)
+ | usb_pipeendpoint (pipe) << 7
+ | (usb_pipeisoc (pipe)? 0x8000: 0)
+ | (usb_pipecontrol (pipe)
+ ? 0: (usb_pipeout (pipe)? 0x800: 0x1000))
+ | (udev->speed == USB_SPEED_LOW) << 13
+ | usb_maxpacket (udev, pipe, usb_pipeout (pipe))
+ << 16);
+
+ if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
+ ed->int_period = interval;
+ ed->int_load = load;
+ }
+
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return ed;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* request unlinking of an endpoint from an operational HC.
+ * put the ep on the rm_list and stop the bulk or ctrl list
+ * real work is done at the next start frame (SF) hardware interrupt
+ */
+static void ed_unlink (struct usb_device *usb_dev, struct ed *ed)
+{
+ unsigned int frame;
+ struct ohci_hcd *ohci = hcd_to_ohci (usb_dev->bus->hcpriv);
+
+ /* already pending? */
+ if (ed->state & ED_URB_DEL)
+ return;
+ ed->state |= ED_URB_DEL;
+
+ ed->hwINFO |= __constant_cpu_to_le32 (OHCI_ED_SKIP);
+
+ switch (ed->type) {
+ case PIPE_CONTROL: /* stop control list */
+ ohci->hc_control &= ~OHCI_CTRL_CLE;
+ writel (ohci->hc_control,
+ &ohci->regs->control);
+ break;
+ case PIPE_BULK: /* stop bulk list */
+ ohci->hc_control &= ~OHCI_CTRL_BLE;
+ writel (ohci->hc_control,
+ &ohci->regs->control);
+ break;
+ }
+
+ frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1;
+ ed->ed_rm_list = ohci->ed_rm_list [frame];
+ ohci->ed_rm_list [frame] = ed;
+
+ /* enable SOF interrupt */
+ if (!ohci->sleeping) {
+ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+ writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+ }
+}
+
+/*-------------------------------------------------------------------------*
+ * TD handling functions
+ *-------------------------------------------------------------------------*/
+
+/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
+
+static void
+td_fill (struct ohci_hcd *ohci, unsigned int info,
+ dma_addr_t data, int len,
+ struct urb *urb, int index)
+{
+ volatile struct td *td, *td_pt;
+ urb_priv_t *urb_priv = urb->hcpriv;
+
+ if (index >= urb_priv->length) {
+ err ("internal OHCI error: TD index > length");
+ return;
+ }
+
+ /* use this td as the next dummy */
+ td_pt = urb_priv->td [index];
+ td_pt->hwNextTD = 0;
+
+ /* fill the old dummy TD */
+ td = urb_priv->td [index] = dma_to_td (ohci,
+ le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf);
+
+ td->ed = urb_priv->ed;
+ td->next_dl_td = NULL;
+ td->index = index;
+ td->urb = urb;
+ td->data_dma = data;
+ if (!len)
+ data = 0;
+
+ td->hwINFO = cpu_to_le32 (info);
+ if ((td->ed->type) == PIPE_ISOCHRONOUS) {
+ td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
+ td->ed->last_iso = info & 0xffff;
+ } else {
+ td->hwCBP = cpu_to_le32 (data);
+ }
+ if (data)
+ td->hwBE = cpu_to_le32 (data + len - 1);
+ else
+ td->hwBE = 0;
+ td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
+ td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
+
+ /* append to queue */
+ td->ed->hwTailP = td->hwNextTD;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* prepare all TDs of a transfer */
+
+static void td_submit_urb (struct urb *urb)
+{
+ urb_priv_t *urb_priv = urb->hcpriv;
+ struct ohci_hcd *ohci = hcd_to_ohci (urb->dev->bus->hcpriv);
+ dma_addr_t data;
+ int data_len = urb->transfer_buffer_length;
+ int cnt = 0;
+ __u32 info = 0;
+ unsigned int toggle = 0;
+
+ /* OHCI handles the DATA-toggles itself, we just use the
+ * USB-toggle bits for resetting
+ */
+ if (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+ usb_pipeout (urb->pipe))) {
+ toggle = TD_T_TOGGLE;
+ } else {
+ toggle = TD_T_DATA0;
+ usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+ usb_pipeout (urb->pipe), 1);
+ }
+
+ urb_priv->td_cnt = 0;
+
+ if (data_len) {
+#ifdef CONFIG_PCI
+ data = pci_map_single (ohci->hcd.pdev,
+ urb->transfer_buffer, data_len,
+ usb_pipeout (urb->pipe)
+ ? PCI_DMA_TODEVICE
+ : PCI_DMA_FROMDEVICE
+ );
+#else
+# error "what dma addr to use"
+#endif
+ } else
+ data = 0;
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_BULK:
+ info = usb_pipeout (urb->pipe)
+ ? TD_CC | TD_DP_OUT
+ : TD_CC | TD_DP_IN ;
+ while (data_len > 4096) {
+ td_fill (ohci,
+ info | (cnt? TD_T_TOGGLE:toggle),
+ data, 4096, urb, cnt);
+ data += 4096; data_len -= 4096; cnt++;
+ }
+ info = usb_pipeout (urb->pipe)?
+ TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
+ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle),
+ data, data_len, urb, cnt);
+ cnt++;
+ if ((urb->transfer_flags & USB_ZERO_PACKET)
+ && cnt < urb_priv->length) {
+ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle),
+ 0, 0, urb, cnt);
+ cnt++;
+ }
+ /* start bulk list */
+ if (!ohci->sleeping)
+ writel (OHCI_BLF, &ohci->regs->cmdstatus);
+ break;
+
+ case PIPE_INTERRUPT:
+ info = TD_CC | toggle;
+ info |= usb_pipeout (urb->pipe)
+ ? TD_DP_OUT
+ : TD_R | TD_DP_IN;
+ td_fill (ohci, info, data, data_len, urb, cnt++);
+ break;
+
+ case PIPE_CONTROL:
+ info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+ td_fill (ohci, info,
+#ifdef CONFIG_PCI
+ pci_map_single (ohci->hcd.pdev,
+ urb->setup_packet, 8,
+ PCI_DMA_TODEVICE),
+#else
+# error "what dma addr to use"
+#endif
+ 8, urb, cnt++);
+ if (data_len > 0) {
+ info = TD_CC | TD_R | TD_T_DATA1;
+ info |= usb_pipeout (urb->pipe)
+ ? TD_DP_OUT
+ : TD_DP_IN;
+ /* NOTE: mishandles transfers >8K, some >4K */
+ td_fill (ohci, info, data, data_len,
+ urb, cnt++);
+ }
+ info = usb_pipeout (urb->pipe)
+ ? TD_CC | TD_DP_IN | TD_T_DATA1
+ : TD_CC | TD_DP_OUT | TD_T_DATA1;
+ td_fill (ohci, info, data, 0, urb, cnt++);
+ /* start control list */
+ if (!ohci->sleeping)
+ writel (OHCI_CLF, &ohci->regs->cmdstatus);
+ break;
+
+ case PIPE_ISOCHRONOUS:
+ for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
+ td_fill (ohci, TD_CC | TD_ISO
+ | ((urb->start_frame + cnt) & 0xffff),
+ data + urb->iso_frame_desc [cnt].offset,
+ urb->iso_frame_desc [cnt].length, urb, cnt);
+ }
+ break;
+ }
+ if (urb_priv->length != cnt)
+ dbg ("TD LENGTH %d != CNT %d", urb_priv->length, cnt);
+}
+
+/*-------------------------------------------------------------------------*
+ * Done List handling functions
+ *-------------------------------------------------------------------------*/
+
+/* calculate the transfer length and update the urb */
+
+static void dl_transfer_length (struct td *td)
+{
+ __u32 tdINFO, tdBE, tdCBP;
+ __u16 tdPSW;
+ struct urb *urb = td->urb;
+ urb_priv_t *urb_priv = urb->hcpriv;
+ int dlen = 0;
+ int cc = 0;
+
+ tdINFO = le32_to_cpup (&td->hwINFO);
+ tdBE = le32_to_cpup (&td->hwBE);
+ tdCBP = le32_to_cpup (&td->hwCBP);
+
+
+ if (tdINFO & TD_ISO) {
+ tdPSW = le16_to_cpu (td->hwPSW [0]);
+ cc = (tdPSW >> 12) & 0xF;
+ if (cc < 0xE) {
+ if (usb_pipeout (urb->pipe)) {
+ dlen = urb->iso_frame_desc [td->index].length;
+ } else {
+ dlen = tdPSW & 0x3ff;
+ }
+ urb->actual_length += dlen;
+ urb->iso_frame_desc [td->index].actual_length = dlen;
+ if (! (urb->transfer_flags & USB_DISABLE_SPD)
+ && (cc == TD_DATAUNDERRUN))
+ cc = TD_CC_NOERROR;
+
+ urb->iso_frame_desc [td->index].status
+ = cc_to_error [cc];
+ }
+ } else { /* BULK, INT, CONTROL DATA */
+ if (! (usb_pipetype (urb->pipe) == PIPE_CONTROL &&
+ ((td->index == 0)
+ || (td->index == urb_priv->length - 1)))) {
+ if (tdBE != 0) {
+ urb->actual_length += (td->hwCBP == 0)
+ ? (tdBE - td->data_dma + 1)
+ : (tdCBP - td->data_dma);
+ }
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* replies to the request have to be on a FIFO basis so
+ * we unreverse the hc-reversed done-list
+ */
+static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
+{
+ __u32 td_list_hc;
+ struct td *td_rev = NULL;
+ struct td *td_list = NULL;
+ urb_priv_t *urb_priv = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0;
+ ohci->hcca->done_head = 0;
+
+ while (td_list_hc) {
+ td_list = dma_to_td (ohci, td_list_hc);
+
+ if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
+ urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
+ dbg (" USB-error/status: %x : %p",
+ TD_CC_GET (le32_to_cpup (&td_list->hwINFO)),
+ td_list);
+ if (td_list->ed->hwHeadP
+ & __constant_cpu_to_le32 (0x1)) {
+ if (urb_priv && ((td_list->index + 1)
+ < urb_priv->length)) {
+ td_list->ed->hwHeadP =
+ (urb_priv->td [urb_priv->length - 1]->hwNextTD
+ & __constant_cpu_to_le32 (0xfffffff0))
+ | (td_list->ed->hwHeadP
+ & __constant_cpu_to_le32 (0x2));
+ urb_priv->td_cnt += urb_priv->length
+ - td_list->index - 1;
+ } else
+ td_list->ed->hwHeadP &=
+ __constant_cpu_to_le32 (0xfffffff2);
+ }
+ }
+
+ td_list->next_dl_td = td_rev;
+ td_rev = td_list;
+ td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0;
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return td_list;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* there are some pending requests to unlink
+ * - some URBs/TDs if urb_priv->state == URB_DEL
+ */
+static void dl_del_list (struct ohci_hcd *ohci, unsigned int frame)
+{
+ unsigned long flags;
+ struct ed *ed;
+ __u32 edINFO;
+ __u32 tdINFO;
+ struct td *td = NULL, *td_next = NULL,
+ *tdHeadP = NULL, *tdTailP;
+ __u32 *td_p;
+ int ctrl = 0, bulk = 0;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ for (ed = ohci->ed_rm_list [frame]; ed != NULL; ed = ed->ed_rm_list) {
+
+ tdTailP = dma_to_td (ohci,
+ le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
+ tdHeadP = dma_to_td (ohci,
+ le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+ edINFO = le32_to_cpup (&ed->hwINFO);
+ td_p = &ed->hwHeadP;
+
+ for (td = tdHeadP; td != tdTailP; td = td_next) {
+ struct urb *urb = td->urb;
+ urb_priv_t *urb_priv = td->urb->hcpriv;
+
+ td_next = dma_to_td (ohci,
+ le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
+ if ((urb_priv->state == URB_DEL)) {
+ tdINFO = le32_to_cpup (&td->hwINFO);
+ if (TD_CC_GET (tdINFO) < 0xE)
+ dl_transfer_length (td);
+ *td_p = td->hwNextTD | (*td_p
+ & __constant_cpu_to_le32 (0x3));
+
+ /* URB is done; clean up */
+ if (++ (urb_priv->td_cnt) == urb_priv->length)
+// FIXME: we shouldn't hold ohci->lock here, else the
+// completion function can't talk to this hcd ...
+ finish_urb (ohci, urb);
+ } else {
+ td_p = &td->hwNextTD;
+ }
+ }
+
+ ed->state &= ~ED_URB_DEL;
+ tdHeadP = dma_to_td (ohci,
+ le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+
+ if (tdHeadP == tdTailP) {
+ if (ed->state == ED_OPER)
+ ep_unlink (ohci, ed);
+ td_free (ohci, tdTailP);
+ ed->hwINFO = __constant_cpu_to_le32 (OHCI_ED_SKIP);
+ ed->state = ED_NEW;
+ } else
+ ed->hwINFO &= ~__constant_cpu_to_le32 (OHCI_ED_SKIP);
+
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ ctrl = 1;
+ break;
+ case PIPE_BULK:
+ bulk = 1;
+ break;
+ }
+ }
+
+ /* maybe reenable control and bulk lists */
+ if (!ohci->disabled) {
+ if (ctrl) /* reset control list */
+ writel (0, &ohci->regs->ed_controlcurrent);
+ if (bulk) /* reset bulk list */
+ writel (0, &ohci->regs->ed_bulkcurrent);
+ if (!ohci->ed_rm_list [!frame]) {
+ if (ohci->ed_controltail)
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ if (ohci->ed_bulktail)
+ ohci->hc_control |= OHCI_CTRL_BLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ }
+
+ ohci->ed_rm_list [frame] = NULL;
+ spin_unlock_irqrestore (&ohci->lock, flags);
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * process normal completions (error or success) and some unlinked eds
+ * this is the main path for handing urbs back to drivers
+ */
+static void dl_done_list (struct ohci_hcd *ohci, struct td *td_list)
+{
+ struct td *td_list_next = NULL;
+ struct ed *ed;
+ int cc = 0;
+ struct urb *urb;
+ urb_priv_t *urb_priv;
+ __u32 tdINFO, edHeadP, edTailP;
+
+ unsigned long flags;
+
+ while (td_list) {
+ td_list_next = td_list->next_dl_td;
+
+ urb = td_list->urb;
+ urb_priv = urb->hcpriv;
+ tdINFO = le32_to_cpup (&td_list->hwINFO);
+
+ ed = td_list->ed;
+
+ dl_transfer_length (td_list);
+
+ /* error code of transfer */
+ cc = TD_CC_GET (tdINFO);
+ if (cc == TD_CC_STALL)
+ usb_endpoint_halt (urb->dev,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipeout (urb->pipe));
+
+ if (! (urb->transfer_flags & USB_DISABLE_SPD)
+ && (cc == TD_DATAUNDERRUN))
+ cc = TD_CC_NOERROR;
+
+ if (++ (urb_priv->td_cnt) == urb_priv->length) {
+ /*
+ * Except for periodic transfers, both branches do
+ * the same thing. Periodic urbs get reissued until
+ * they're "deleted" with usb_unlink_urb.
+ */
+ if ((ed->state & (ED_OPER | ED_UNLINK))
+ && (urb_priv->state != URB_DEL)) {
+ spin_lock (&urb->lock);
+ if (urb->status == -EINPROGRESS)
+ urb->status = cc_to_error [cc];
+ spin_unlock (&urb->lock);
+ return_urb (ohci, urb);
+ } else
+ finish_urb (ohci, urb);
+ }
+
+ spin_lock_irqsave (&ohci->lock, flags);
+ if (ed->state != ED_NEW) {
+ edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0;
+ edTailP = le32_to_cpup (&ed->hwTailP);
+
+// FIXME: ED_UNLINK is very fuzzy w.r.t. what the hc knows...
+
+ /* unlink eds if they are not busy */
+ if ((edHeadP == edTailP) && (ed->state == ED_OPER))
+ ep_unlink (ohci, ed);
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ td_list = td_list_next;
+ }
+}
+
--- /dev/null
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under GPL
+ * $Id: ohci.h,v 1.5 2002/01/19 00:24:01 dbrownell Exp $
+ */
+
+static const int cc_to_error [16] = {
+
+/* map OHCI status to errno values */
+ /* No Error */ 0,
+ /* CRC Error */ -EILSEQ,
+ /* Bit Stuff */ -EPROTO,
+ /* Data Togg */ -EILSEQ,
+ /* Stall */ -EPIPE,
+ /* DevNotResp */ -ETIMEDOUT,
+ /* PIDCheck */ -EPROTO,
+ /* UnExpPID */ -EPROTO,
+ /* DataOver */ -EOVERFLOW,
+ /* DataUnder */ -EREMOTEIO,
+ /* (for hw) */ -EIO,
+ /* (for hw) */ -EIO,
+ /* BufferOver */ -ECOMM,
+ /* BuffUnder */ -ENOSR,
+ /* (for HCD) */ -EALREADY,
+ /* (for HCD) */ -EALREADY
+};
+
+
+/* ED States */
+
+#define ED_NEW 0x00 /* unused, no dummy td */
+#define ED_UNLINK 0x01 /* dummy td, maybe linked to hc */
+#define ED_OPER 0x02 /* dummy td, _is_ linked to hc */
+
+#define ED_URB_DEL 0x08 /* masked in */
+
+/* usb_ohci_ed */
+struct ed {
+ /* first fields are hardware-specified */
+ __u32 hwINFO;
+ __u32 hwTailP;
+ __u32 hwHeadP;
+ __u32 hwNextED;
+
+ struct ed * ed_prev;
+ __u8 int_period;
+ __u8 int_branch;
+ __u8 int_load;
+ __u8 int_interval;
+ __u8 state; // ED_{NEW,UNLINK,OPER}
+ __u8 type;
+ __u16 last_iso;
+ struct ed * ed_rm_list;
+
+ dma_addr_t dma;
+ __u32 unused [3];
+} __attribute((aligned(16)));
+
+
+/* TD info field */
+#define TD_CC 0xf0000000
+#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
+#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
+#define TD_EC 0x0C000000
+#define TD_T 0x03000000
+#define TD_T_DATA0 0x02000000
+#define TD_T_DATA1 0x03000000
+#define TD_T_TOGGLE 0x00000000
+#define TD_R 0x00040000
+#define TD_DI 0x00E00000
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+#define TD_DP 0x00180000
+#define TD_DP_SETUP 0x00000000
+#define TD_DP_IN 0x00100000
+#define TD_DP_OUT 0x00080000
+
+#define TD_ISO 0x00010000
+#define TD_DEL 0x00020000
+
+/* CC Codes */
+#define TD_CC_NOERROR 0x00
+#define TD_CC_CRC 0x01
+#define TD_CC_BITSTUFFING 0x02
+#define TD_CC_DATATOGGLEM 0x03
+#define TD_CC_STALL 0x04
+#define TD_DEVNOTRESP 0x05
+#define TD_PIDCHECKFAIL 0x06
+#define TD_UNEXPECTEDPID 0x07
+#define TD_DATAOVERRUN 0x08
+#define TD_DATAUNDERRUN 0x09
+ /* 0x0A, 0x0B reserved for hardware */
+#define TD_BUFFEROVERRUN 0x0C
+#define TD_BUFFERUNDERRUN 0x0D
+ /* 0x0E, 0x0F reserved for HCD */
+#define TD_NOTACCESSED 0x0F
+
+
+#define MAXPSW 1
+
+struct td {
+ /* first hardware fields are in all tds */
+ __u32 hwINFO;
+ __u32 hwCBP; /* Current Buffer Pointer */
+ __u32 hwNextTD; /* Next TD Pointer */
+ __u32 hwBE; /* Memory Buffer End Pointer */
+
+ __u16 hwPSW [MAXPSW]; /* PSW is only for ISO */
+
+ __u8 unused;
+ __u8 index;
+ struct ed *ed;
+ struct td *next_dl_td;
+ struct urb *urb;
+
+ dma_addr_t td_dma;
+ dma_addr_t data_dma;
+ __u32 unused2 [2];
+} __attribute((aligned(32))); /* iso needs 32 */
+
+#define OHCI_ED_SKIP (1 << 14)
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined in the OHCI spec. The host controller is
+ * told the base address of it. It must be 256-byte aligned.
+ */
+#define NUM_INTS 32 /* part of the OHCI standard */
+struct ohci_hcca {
+ __u32 int_table [NUM_INTS]; /* Interrupt ED table */
+ __u16 frame_no; /* current frame number */
+ __u16 pad1; /* set to 0 on each frame_no change */
+ __u32 done_head; /* info returned for an interrupt */
+ u8 reserved_for_hc [116];
+} __attribute((aligned(256)));
+
+
+/*
+ * Maximum number of root hub ports.
+ */
+#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O
+ * region. This is Memory Mapped I/O. You must use the readl() and
+ * writel() macros defined in asm/io.h to access these!!
+ */
+struct ohci_regs {
+ /* control and status registers */
+ __u32 revision;
+ __u32 control;
+ __u32 cmdstatus;
+ __u32 intrstatus;
+ __u32 intrenable;
+ __u32 intrdisable;
+
+ /* memory pointers */
+ __u32 hcca;
+ __u32 ed_periodcurrent;
+ __u32 ed_controlhead;
+ __u32 ed_controlcurrent;
+ __u32 ed_bulkhead;
+ __u32 ed_bulkcurrent;
+ __u32 donehead;
+
+ /* frame counters */
+ __u32 fminterval;
+ __u32 fmremaining;
+ __u32 fmnumber;
+ __u32 periodicstart;
+ __u32 lsthresh;
+
+ /* Root hub ports */
+ struct ohci_roothub_regs {
+ __u32 a;
+ __u32 b;
+ __u32 status;
+ __u32 portstatus [MAX_ROOT_PORTS];
+ } roothub;
+
+ /* and some optional registers for legacy compatibility */
+} __attribute((aligned(32)));
+
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
+#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
+#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
+#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
+#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
+#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
+#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
+#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
+#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+# define OHCI_USB_RESET (0 << 6)
+# define OHCI_USB_RESUME (1 << 6)
+# define OHCI_USB_OPER (2 << 6)
+# define OHCI_USB_SUSPEND (3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR (1 << 0) /* host controller reset */
+#define OHCI_CLF (1 << 1) /* control list filled */
+#define OHCI_BLF (1 << 2) /* bulk list filled */
+#define OHCI_OCR (1 << 3) /* ownership change request */
+#define OHCI_SOC (3 << 16) /* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
+#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
+#define OHCI_INTR_SF (1 << 2) /* start frame */
+#define OHCI_INTR_RD (1 << 3) /* resume detect */
+#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
+#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
+#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
+
+
+/* OHCI ROOT HUB REGISTER MASKS */
+
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS 0x00000001 /* current connect status */
+#define RH_PS_PES 0x00000002 /* port enable status*/
+#define RH_PS_PSS 0x00000004 /* port suspend status */
+#define RH_PS_POCI 0x00000008 /* port over current indicator */
+#define RH_PS_PRS 0x00000010 /* port reset status */
+#define RH_PS_PPS 0x00000100 /* port power status */
+#define RH_PS_LSDA 0x00000200 /* low speed device attached */
+#define RH_PS_CSC 0x00010000 /* connect status change */
+#define RH_PS_PESC 0x00020000 /* port enable status change */
+#define RH_PS_PSSC 0x00040000 /* port suspend status change */
+#define RH_PS_OCIC 0x00080000 /* over current indicator change */
+#define RH_PS_PRSC 0x00100000 /* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS 0x00000001 /* local power status */
+#define RH_HS_OCI 0x00000002 /* over current indicator */
+#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
+#define RH_HS_LPSC 0x00010000 /* local power status change */
+#define RH_HS_OCIC 0x00020000 /* over current indicator change */
+#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR 0x0000ffff /* device removable flags */
+#define RH_B_PPCM 0xffff0000 /* port power control mask */
+
+/* roothub.a masks */
+#define RH_A_NDP (0xff << 0) /* number of downstream ports */
+#define RH_A_PSM (1 << 8) /* power switching mode */
+#define RH_A_NPS (1 << 9) /* no power switching */
+#define RH_A_DT (1 << 10) /* device type (mbz) */
+#define RH_A_OCPM (1 << 11) /* over current protection mode */
+#define RH_A_NOCP (1 << 12) /* no over current protection */
+#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
+
+
+/* urb */
+typedef struct urb_priv
+{
+ struct ed *ed;
+ __u16 length; // # tds in this request
+ __u16 td_cnt; // tds already serviced
+ int state;
+ struct td *td [0]; // all TDs in this request
+
+} urb_priv_t;
+
+#define URB_DEL 1
+
+
+/* Hash struct used for TD/ED hashing */
+struct hash_t {
+ void *virt;
+ dma_addr_t dma;
+ struct hash_t *next; // chaining for collision cases
+};
+
+/* List of TD/ED hash entries */
+struct hash_list_t {
+ struct hash_t *head;
+ struct hash_t *tail;
+};
+
+#define TD_HASH_SIZE 64 /* power'o'two */
+#define ED_HASH_SIZE 64 /* power'o'two */
+
+#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE)
+#define ED_HASH_FUNC(ed_dma) ((ed_dma ^ (ed_dma >> 5)) % ED_HASH_SIZE)
+
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+
+struct ohci_hcd {
+ spinlock_t lock;
+
+ /*
+ * I/O memory used to communicate with the HC (uncached);
+ */
+ struct ohci_regs *regs;
+
+ /*
+ * main memory used to communicate with the HC (uncached)
+ */
+ struct ohci_hcca *hcca;
+ dma_addr_t hcca_dma;
+
+ struct ed *ed_rm_list [2]; /* to be removed */
+
+ struct ed *ed_bulktail; /* last in bulk list */
+ struct ed *ed_controltail; /* last in ctrl list */
+ struct ed *ed_isotail; /* last in iso list */
+
+#ifdef CONFIG_PCI
+ struct pci_pool *td_cache;
+ struct pci_pool *ed_cache;
+ struct hash_list_t td_hash [TD_HASH_SIZE];
+ struct hash_list_t ed_hash [ED_HASH_SIZE];
+#endif
+
+ /*
+ * driver state
+ */
+ int disabled; /* e.g. got a UE, we're hung */
+ int sleeping;
+ int ohci_int_load [NUM_INTS];
+ u32 hc_control; /* copy of hc control reg */
+ struct usb_device *dev [128];
+
+ unsigned long flags; /* for HC bugs */
+#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
+
+ /*
+ * framework state
+ */
+ struct usb_hcd hcd;
+};
+
+#define hcd_to_ohci(hcd_ptr) list_entry(hcd_ptr, struct ohci_hcd, hcd)
+
struct ibmcam_sbuf {
char *data;
- urb_t *urb;
+ struct urb *urb;
};
struct ibmcam_frame {
/*-------------------------------------------------------------------*
* completion handler for compatibility wrappers (sync control/bulk) *
*-------------------------------------------------------------------*/
-static void usb_api_blocking_completion(urb_t *urb)
+static void usb_api_blocking_completion(struct urb *urb)
{
struct usb_api_data *awd = (struct usb_api_data *)urb->context;
*-------------------------------------------------------------------*/
// Starts urb and waits for completion or timeout
-static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length)
+static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
{
DECLARE_WAITQUEUE(wait, current);
struct usb_api_data awd;
struct usb_ctrlrequest *cmd, void *data, int len,
int timeout)
{
- urb_t *urb;
+ struct urb *urb;
int retv;
int length;
**********************************************************************/
static int
-ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
+ov511_move_data(struct usb_ov511 *ov511, struct urb *urb)
{
unsigned char *cdata;
int data_size, num, offset, i, totlen = 0;
}
static int
-ov518_move_data(struct usb_ov511 *ov511, urb_t *urb)
+ov518_move_data(struct usb_ov511 *ov511, struct urb *urb)
{
unsigned char *cdata;
int i, data_size, totlen = 0;
static int
ov511_init_isoc(struct usb_ov511 *ov511)
{
- urb_t *urb;
+ struct urb *urb;
int fx, err, n, size;
PDEBUG(3, "*** Initializing capture ***");
struct ov511_sbuf {
char *data;
- urb_t *urb;
+ struct urb *urb;
};
enum {
static int update_eth_regs_async( pegasus_t * );
/* Aargh!!! I _really_ hate such tweaks */
-static void ctrl_callback( urb_t *urb )
+static void ctrl_callback( struct urb *urb )
{
pegasus_t *pegasus = urb->context;
*/
static int se401_start_stream(struct usb_se401 *se401)
{
- urb_t *urb;
+ struct urb *urb;
int err=0, i;
se401->streaming=1;
char *fbuf; /* Videodev buffer area */
- urb_t *urb[SE401_NUMSBUF];
- urb_t *inturb;
+ struct urb *urb[SE401_NUMSBUF];
+ struct urb *inturb;
int button;
int buttonpressed;
static __devinitdata struct usb_device_id id_table_8U232AM [] = {
{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
+ { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) },
{ } /* Terminating entry */
};
static __devinitdata struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
+ { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) },
{ } /* Terminating entry */
};
#define FTDI_VID 0x0403 /* Vendor Id */
#define FTDI_SIO_PID 0x8372 /* Product Id SIO application of 8U100AX */
#define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */
+#define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */
+#define FTDI_NF_RIC_PID 0x0001 /* Product Id */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
{
struct edgeport_serial *edge_serial = (struct edgeport_serial *)edge_port->port->serial->private;
int status = 0;
- urb_t *urb;
+ struct urb *urb;
int timeout;
usb_serial_debug_data (__FILE__, __FUNCTION__, length, buffer);
const keyspan_device_details *device_details;
- urb_t *instat_urb;
+ struct urb *instat_urb;
char instat_buf[INSTAT_BUFLEN];
/* XXX this one probably will need a lock */
- urb_t *glocont_urb;
+ struct urb *glocont_urb;
char glocont_buf[GLOCONT_BUFLEN];
};
const keyspan_device_details *device_details;
/* Input endpoints and buffer for this port */
- urb_t *in_urbs[2];
+ struct urb *in_urbs[2];
char in_buffer[2][64];
/* Output endpoints and buffer for this port */
- urb_t *out_urbs[2];
+ struct urb *out_urbs[2];
char out_buffer[2][64];
/* Input ack endpoint */
- urb_t *inack_urb;
+ struct urb *inack_urb;
char inack_buffer[1];
/* Output control endpoint */
- urb_t *outcont_urb;
+ struct urb *outcont_urb;
char outcont_buffer[64];
/* Settings for the port */
const keyspan_device_details *d_details;
int flip;
int left, todo;
- urb_t *this_urb;
+ struct urb *this_urb;
int err;
p_priv = (struct keyspan_port_private *)(port->private);
struct usb_serial *serial = port->serial;
const keyspan_device_details *d_details;
int i, already_active, err;
- urb_t *urb;
+ struct urb *urb;
s_priv = (struct keyspan_serial_private *)(serial->private);
p_priv = (struct keyspan_port_private *)(port->private);
return (0);
}
-static inline void stop_urb(urb_t *urb)
+static inline void stop_urb(struct urb *urb)
{
if (urb && urb->status == -EINPROGRESS) {
urb->transfer_flags &= ~USB_ASYNC_UNLINK;
}
/* Helper functions used by keyspan_setup_urbs */
-static urb_t *keyspan_setup_urb(struct usb_serial *serial, int endpoint,
- int dir, void *ctx, char *buf, int len,
- void (*callback)(urb_t *))
+static struct urb *keyspan_setup_urb(struct usb_serial *serial, int endpoint,
+ int dir, void *ctx, char *buf, int len,
+ void (*callback)(struct urb *))
{
- urb_t *urb;
+ struct urb *urb;
if (endpoint == -1)
return NULL; /* endpoint not needed */
}
static struct callbacks {
- void (*instat_callback)(urb_t *);
- void (*glocont_callback)(urb_t *);
- void (*indat_callback)(urb_t *);
- void (*outdat_callback)(urb_t *);
- void (*inack_callback)(urb_t *);
- void (*outcont_callback)(urb_t *);
+ void (*instat_callback)(struct urb *);
+ void (*glocont_callback)(struct urb *);
+ void (*indat_callback)(struct urb *);
+ void (*outdat_callback)(struct urb *);
+ void (*inack_callback)(struct urb *);
+ void (*outcont_callback)(struct urb *);
} keyspan_callbacks[] = {
{
/* msg_usa26 callbacks */
struct keyspan_port_private *p_priv;
const keyspan_device_details *d_details;
int outcont_urb;
- urb_t *this_urb;
+ struct urb *this_urb;
int err;
dbg ("%s reset=%d\n", __FUNCTION__, reset_port);
struct keyspan_serial_private *s_priv;
struct keyspan_port_private *p_priv;
const keyspan_device_details *d_details;
- urb_t *this_urb;
+ struct urb *this_urb;
int err;
s_priv = (struct keyspan_serial_private *)(serial->private);
struct keyspan_port_private *p_priv;
const keyspan_device_details *d_details;
int glocont_urb;
- urb_t *this_urb;
+ struct urb *this_urb;
int err;
int device_port;
/* This is the completion handler which will wake us up when an URB
* completes.
*/
-static void usb_stor_blocking_completion(urb_t *urb)
+static void usb_stor_blocking_completion(struct urb *urb)
{
struct completion *urb_done_ptr = (struct completion *)urb->context;
static int stv680_start_stream (struct usb_stv *stv680)
{
- urb_t *urb;
+ struct urb *urb;
int err = 0, i;
stv680->streaming = 1;
int removed; /* device disconnected */
int streaming; /* Are we streaming video? */
char *fbuf; /* Videodev buffer area */
- urb_t *urb[STV680_NUMSBUF]; /* # of queued bulk transfers */
+ struct urb *urb[STV680_NUMSBUF]; /* # of queued bulk transfers */
int curframe; /* Current receiving frame */
struct stv680_frame frame[STV680_NUMFRAMES]; /* # frames supported by v4l part */
int readcount;
lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
- uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1);
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe),
+ uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1));
/* All qh's in the queue need to link to the next queue */
urbp->qh->link = eurbp->qh->link;
/* Fix up the toggle for the next URB's */
if (!urbp->queued)
+ /* We set the toggle when we unlink */
toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
else {
/* If we're in the middle of the queue, grab the toggle */
/* Control and Isochronous ignore the toggle, so this */
/* is safe for all types */
- if (!(td->status & TD_CTRL_ACTIVE) &&
- (uhci_actual_length(td->status) < uhci_expected_length(td->info) ||
+ if ((!(td->status & TD_CTRL_ACTIVE) &&
+ (uhci_actual_length(td->status) < uhci_expected_length(td->info)) ||
tmp == head)) {
usb_settoggle(urb->dev, uhci_endpoint(td->info),
uhci_packetout(td->info),
kfree (urb_priv);
}
-static void urb_rm_priv_locked (urb_t * urb)
+static void urb_rm_priv_locked (struct urb * urb)
{
urb_priv_t * urb_priv = urb->hcpriv;
}
}
-static void urb_rm_priv (urb_t * urb)
+static void urb_rm_priv (struct urb * urb)
{
unsigned long flags;
/* debug| print the main components of an URB
* small: 0) header + data packets 1) just header */
-static void urb_print (urb_t * urb, char * str, int small)
+static void urb_print (struct urb * urb, char * str, int small)
{
unsigned int pipe= urb->pipe;
/* return a request to the completion handler */
-static int sohci_return_urb (struct ohci *hc, urb_t * urb)
+static int sohci_return_urb (struct ohci *hc, struct urb * urb)
{
urb_priv_t * urb_priv = urb->hcpriv;
- urb_t * urbt;
+ struct urb * urbt;
unsigned long flags;
int i;
/* get a transfer request */
-static int sohci_submit_urb (urb_t * urb)
+static int sohci_submit_urb (struct urb * urb)
{
ohci_t * ohci;
ed_t * ed;
/* deactivate all TDs and remove the private part of the URB */
/* interrupt callers must use async unlink mode */
-static int sohci_unlink_urb (urb_t * urb)
+static int sohci_unlink_urb (struct urb * urb)
{
unsigned long flags;
ohci_t * ohci;
static void
td_fill (ohci_t * ohci, unsigned int info,
dma_addr_t data, int len,
- urb_t * urb, int index)
+ struct urb * urb, int index)
{
volatile td_t * td, * td_pt;
urb_priv_t * urb_priv = urb->hcpriv;
/* prepare all TDs of a transfer */
-static void td_submit_urb (urb_t * urb)
+static void td_submit_urb (struct urb * urb)
{
urb_priv_t * urb_priv = urb->hcpriv;
ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv;
{
__u32 tdINFO, tdBE, tdCBP;
__u16 tdPSW;
- urb_t * urb = td->urb;
+ struct urb * urb = td->urb;
urb_priv_t * urb_priv = urb->hcpriv;
int dlen = 0;
int cc = 0;
/* handle an urb that is being unlinked */
-static void dl_del_urb (urb_t * urb)
+static void dl_del_urb (struct urb * urb)
{
wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait;
td_p = &ed->hwHeadP;
for (td = tdHeadP; td != tdTailP; td = td_next) {
- urb_t * urb = td->urb;
+ struct urb * urb = td->urb;
urb_priv_t * urb_priv = td->urb->hcpriv;
td_next = dma_to_td (ohci, le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
td_t * td_list_next = NULL;
ed_t * ed;
int cc = 0;
- urb_t * urb;
+ struct urb * urb;
urb_priv_t * urb_priv;
__u32 tdINFO, edHeadP, edTailP;
{
int len;
- urb_t * urb = (urb_t *) ptr;
+ struct urb * urb = (struct urb *) ptr;
ohci_t * ohci = urb->dev->bus->hcpriv;
if (ohci->disabled)
/* Root Hub INTs are polled by this timer */
-static int rh_init_int_timer (urb_t * urb)
+static int rh_init_int_timer (struct urb * urb)
{
ohci_t * ohci = urb->dev->bus->hcpriv;
/* request to virtual root hub */
-static int rh_submit_urb (urb_t * urb)
+static int rh_submit_urb (struct urb * urb)
{
struct usb_device * usb_dev = urb->dev;
ohci_t * ohci = usb_dev->bus->hcpriv;
/*-------------------------------------------------------------------------*/
-static int rh_unlink_urb (urb_t * urb)
+static int rh_unlink_urb (struct urb * urb)
{
ohci_t * ohci = urb->dev->bus->hcpriv;
__u8 index;
struct ed * ed;
struct td * next_dl_td;
- urb_t * urb;
+ struct urb * urb;
dma_addr_t td_dma;
dma_addr_t data_dma;
static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load, int mem_flags);
static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed);
/* td */
-static void td_fill(ohci_t * ohci, unsigned int info, dma_addr_t data, int len, urb_t * urb, int index);
-static void td_submit_urb(urb_t * urb);
+static void td_fill(ohci_t * ohci, unsigned int info, dma_addr_t data, int len, struct urb * urb, int index);
+static void td_submit_urb(struct urb * urb);
/* root hub */
-static int rh_submit_urb(urb_t * urb);
-static int rh_unlink_urb(urb_t * urb);
-static int rh_init_int_timer(urb_t * urb);
+static int rh_submit_urb(struct urb * urb);
+static int rh_unlink_urb(struct urb * urb);
+static int rh_init_int_timer(struct urb * urb);
/*-------------------------------------------------------------------------*/
// Suppress HC interrupt error messages for 5s
#define ERROR_SUPPRESSION_TIME (HZ*5)
-_static int rh_submit_urb (urb_t *urb);
-_static int rh_unlink_urb (urb_t *urb);
+_static int rh_submit_urb (struct urb *urb);
+_static int rh_unlink_urb (struct urb *urb);
_static int delete_qh (uhci_t *s, uhci_desc_t *qh);
-_static int process_transfer (uhci_t *s, urb_t *urb, int mode);
-_static int process_interrupt (uhci_t *s, urb_t *urb);
-_static int process_iso (uhci_t *s, urb_t *urb, int force);
+_static int process_transfer (uhci_t *s, struct urb *urb, int mode);
+_static int process_interrupt (uhci_t *s, struct urb *urb);
+_static int process_iso (uhci_t *s, struct urb *urb, int force);
// How much URBs with ->next are walked
#define MAX_NEXT_COUNT 2048
}
/*-------------------------------------------------------------------*/
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-_static void enable_desc_loop(uhci_t *s, urb_t *urb)
+_static void enable_desc_loop(uhci_t *s, struct urb *urb)
{
int flags;
spin_unlock_irqrestore (&s->qh_lock, flags);
}
/*-------------------------------------------------------------------*/
-_static void disable_desc_loop(uhci_t *s, urb_t *urb)
+_static void disable_desc_loop(uhci_t *s, struct urb *urb)
{
int flags;
}
#endif
/*-------------------------------------------------------------------*/
-_static void queue_urb_unlocked (uhci_t *s, urb_t *urb)
+_static void queue_urb_unlocked (uhci_t *s, struct urb *urb)
{
struct list_head *p=&urb->urb_list;
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
uhci_switch_timer_int(s);
}
/*-------------------------------------------------------------------*/
-_static void queue_urb (uhci_t *s, urb_t *urb)
+_static void queue_urb (uhci_t *s, struct urb *urb)
{
unsigned long flags=0;
spin_unlock_irqrestore (&s->urb_list_lock, flags);
}
/*-------------------------------------------------------------------*/
-_static void dequeue_urb (uhci_t *s, urb_t *urb)
+_static void dequeue_urb (uhci_t *s, struct urb *urb)
{
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
int type;
// LOW LEVEL STUFF
// assembles QHs und TDs for control, bulk and iso
/*-------------------------------------------------------------------*/
-_static int uhci_submit_control_urb (urb_t *urb)
+_static int uhci_submit_control_urb (struct urb *urb)
{
uhci_desc_t *qh, *td;
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
-_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb)
+_static int uhci_submit_bulk_urb (struct urb *urb, struct urb *bulk_urb)
{
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL;
looks a bit complicated because of all the bulk queueing goodies
*/
-_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode)
+_static void uhci_clean_transfer (uhci_t *s, struct urb *urb, uhci_desc_t *qh, int mode)
{
uhci_desc_t *bqh, *nqh, *prevqh, *prevtd;
int now;
urb, priv->prev_queued_urb, priv->next_queued_urb, qh, bqh, priv->next_qh);
if (mode != CLEAN_TRANSFER_DELETION_MARK) { // no work for cleanup at unlink-completion
- urb_t *nurb;
+ struct urb *nurb;
unsigned long flags;
nurb = priv->next_queued_urb;
}
/*-------------------------------------------------------------------*/
// Release bandwidth for Interrupt or Isoc. transfers
-_static void uhci_release_bandwidth(urb_t *urb)
+_static void uhci_release_bandwidth(struct urb *urb)
{
if (urb->bandwidth) {
switch (usb_pipetype(urb->pipe)) {
}
}
-_static void uhci_urb_dma_sync(uhci_t *s, urb_t *urb, urb_priv_t *urb_priv)
+_static void uhci_urb_dma_sync(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv)
{
if (urb_priv->setup_packet_dma)
pci_dma_sync_single(s->uhci_pci, urb_priv->setup_packet_dma,
PCI_DMA_TODEVICE);
}
-_static void uhci_urb_dma_unmap(uhci_t *s, urb_t *urb, urb_priv_t *urb_priv)
+_static void uhci_urb_dma_unmap(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv)
{
if (urb_priv->setup_packet_dma) {
pci_unmap_single(s->uhci_pci, urb_priv->setup_packet_dma,
mode: UNLINK_ASYNC_STORE_URB: unlink and move URB into unlinked list
UNLINK_ASYNC_DONT_STORE: unlink, don't move URB into unlinked list
*/
-_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb, int mode)
+_static int uhci_unlink_urb_async (uhci_t *s, struct urb *urb, int mode)
{
uhci_desc_t *qh;
urb_priv_t *urb_priv;
}
/*-------------------------------------------------------------------*/
// kills an urb by unlinking descriptors and waiting for at least one frame
-_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
+_static int uhci_unlink_urb_sync (uhci_t *s, struct urb *urb)
{
uhci_desc_t *qh;
urb_priv_t *urb_priv;
_static void uhci_cleanup_unlink(uhci_t *s, int force)
{
struct list_head *q;
- urb_t *urb;
+ struct urb *urb;
struct usb_device *dev;
int now, type;
urb_priv_t *urb_priv;
while (q != &s->urb_unlinked) {
- urb = list_entry (q, urb_t, urb_list);
+ urb = list_entry (q, struct urb, urb_list);
urb_priv = (urb_priv_t*)urb->hcpriv;
q = urb->urb_list.next;
}
/*-------------------------------------------------------------------*/
-_static int uhci_unlink_urb (urb_t *urb)
+_static int uhci_unlink_urb (struct urb *urb)
{
uhci_t *s;
unsigned long flags=0;
// In case of ASAP iso transfer, search the URB-list for already queued URBs
// for this EP and calculate the earliest start frame for the new
// URB (easy seamless URB continuation!)
-_static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end)
+_static int find_iso_limits (struct urb *urb, unsigned int *start, unsigned int *end)
{
- urb_t *u, *last_urb = NULL;
+ struct urb *u, *last_urb = NULL;
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
struct list_head *p;
int ret=-1;
p=s->urb_list.prev;
for (; p != &s->urb_list; p = p->prev) {
- u = list_entry (p, urb_t, urb_list);
+ u = list_entry (p, struct urb, urb_list);
// look for pending URBs with identical pipe handle
// works only because iso doesn't toggle the data bit!
if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) {
/*-------------------------------------------------------------------*/
// adjust start_frame according to scheduling constraints (ASAP etc)
-_static int iso_find_start (urb_t *urb)
+_static int iso_find_start (struct urb *urb)
{
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
unsigned int now;
// ASAP-flag set implicitely
// if period==0, the transfer is only done once
-_static int uhci_submit_int_urb (urb_t *urb)
+_static int uhci_submit_int_urb (struct urb *urb)
{
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
urb_priv_t *urb_priv = urb->hcpriv;
return 0;
}
/*-------------------------------------------------------------------*/
-_static int uhci_submit_iso_urb (urb_t *urb)
+_static int uhci_submit_iso_urb (struct urb *urb)
{
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
urb_priv_t *urb_priv = urb->hcpriv;
/*-------------------------------------------------------------------*/
// returns: 0 (no transfer queued), urb* (this urb already queued)
-_static urb_t* search_dev_ep (uhci_t *s, urb_t *urb)
+_static struct urb* search_dev_ep (uhci_t *s, struct urb *urb)
{
struct list_head *p;
- urb_t *tmp;
+ struct urb *tmp;
unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);
dbg("search_dev_ep:");
p=s->urb_list.next;
for (; p != &s->urb_list; p = p->next) {
- tmp = list_entry (p, urb_t, urb_list);
+ tmp = list_entry (p, struct urb, urb_list);
dbg("urb: %p", tmp);
// we can accept this urb if it is not queued at this time
// or if non-iso transfer requests should be scheduled for the same device and pipe
return 0;
}
/*-------------------------------------------------------------------*/
-_static int uhci_submit_urb (urb_t *urb)
+_static int uhci_submit_urb (struct urb *urb)
{
uhci_t *s;
urb_priv_t *urb_priv;
int ret = 0, type;
unsigned long flags;
- urb_t *queued_urb=NULL;
+ struct urb *queued_urb=NULL;
int bustime;
if (!urb->dev || !urb->dev->bus)
_static void uhci_check_timeouts(uhci_t *s)
{
struct list_head *p,*p2;
- urb_t *urb;
+ struct urb *urb;
int type;
p = s->urb_list.prev;
p2 = p;
p = p->prev;
- urb = list_entry (p2, urb_t, urb_list);
+ urb = list_entry (p2, struct urb, urb_list);
type = usb_pipetype (urb->pipe);
hcpriv = (urb_priv_t*)urb->hcpriv;
/*-------------------------------------------------------------------------*/
/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
-_static int rh_send_irq (urb_t *urb)
+_static int rh_send_irq (struct urb *urb)
{
int len = 1;
int i;
/*-------------------------------------------------------------------------*/
/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
-_static int rh_init_int_timer (urb_t *urb);
+_static int rh_init_int_timer (struct urb *urb);
_static void rh_int_timer_do (unsigned long ptr)
{
int len;
- urb_t *urb = (urb_t*) ptr;
+ struct urb *urb = (struct urb *) ptr;
uhci_t *uhci = urb->dev->bus->hcpriv;
if (uhci->rh.send) {
/*-------------------------------------------------------------------------*/
/* Root Hub INTs are polled by this timer, polling interval 20ms */
-_static int rh_init_int_timer (urb_t *urb)
+_static int rh_init_int_timer (struct urb *urb)
{
uhci_t *uhci = urb->dev->bus->hcpriv;
*************************/
-_static int rh_submit_urb (urb_t *urb)
+_static int rh_submit_urb (struct urb *urb)
{
struct usb_device *usb_dev = urb->dev;
uhci_t *uhci = usb_dev->bus->hcpriv;
}
/*-------------------------------------------------------------------------*/
-_static int rh_unlink_urb (urb_t *urb)
+_static int rh_unlink_urb (struct urb *urb)
{
uhci_t *uhci = urb->dev->bus->hcpriv;
unsigned long flags;
struct list_head *p;
struct list_head *p2;
- urb_t *urb;
+ struct urb *urb;
spin_lock_irqsave (&s->urb_list_lock, flags);
p = s->urb_list.prev;
while (p != &s->urb_list) {
p2 = p;
p = p->prev ;
- urb = list_entry (p2, urb_t, urb_list);
+ urb = list_entry (p2, struct urb, urb_list);
dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev);
//urb->transfer_flags |=USB_ASYNC_UNLINK;
uhci_unlink_urb
};
-_static void correct_data_toggles(urb_t *urb)
+_static void correct_data_toggles(struct urb *urb)
{
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe),
!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)));
* PROCESS_TRANSFER_DONT_UNLINK: QHs already unlinked (for async unlink_urb)
*/
-_static int process_transfer (uhci_t *s, urb_t *urb, int mode)
+_static int process_transfer (uhci_t *s, struct urb *urb, int mode)
{
int ret = 0;
urb_priv_t *urb_priv = urb->hcpriv;
if (usb_pipetype (urb->pipe) == PIPE_BULK ) { /* toggle correction for short bulk transfers (nonqueued/queued) */
urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
- urb_t *next_queued_urb=priv->next_queued_urb;
+ struct urb *next_queued_urb=priv->next_queued_urb;
if (next_queued_urb) {
urb_priv_t *next_priv=(urb_priv_t*)next_queued_urb->hcpriv;
return ret;
}
-_static int process_interrupt (uhci_t *s, urb_t *urb)
+_static int process_interrupt (uhci_t *s, struct urb *urb)
{
int i, ret = -EINPROGRESS;
urb_priv_t *urb_priv = urb->hcpriv;
// mode: PROCESS_ISO_REGULAR: processing only for done TDs, unlink TDs
// mode: PROCESS_ISO_FORCE: force processing, don't unlink TDs (already unlinked)
-_static int process_iso (uhci_t *s, urb_t *urb, int mode)
+_static int process_iso (uhci_t *s, struct urb *urb, int mode)
{
int i;
int ret = 0;
_static int process_urb (uhci_t *s, struct list_head *p)
{
int ret = 0;
- urb_t *urb;
+ struct urb *urb;
- urb=list_entry (p, urb_t, urb_list);
+ urb=list_entry (p, struct urb, urb_list);
//dbg("process_urb: found queued urb: %p", urb);
switch (usb_pipetype (urb->pipe)) {
#endif
if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) { // process_interrupt does completion on its own
- urb_t *next_urb = urb->next;
+ struct urb *next_urb = urb->next;
int is_ring = 0;
int contains_killed = 0;
int loop_count=0;
dma_addr_t setup_packet_dma;
dma_addr_t transfer_buffer_dma;
unsigned long started;
- urb_t *next_queued_urb; // next queued urb for this EP
- urb_t *prev_queued_urb;
+ struct urb *next_queued_urb; // next queued urb for this EP
+ struct urb *prev_queued_urb;
uhci_desc_t *bottom_qh;
uhci_desc_t *next_qh; // next helper QH
char use_loop;
*
* The driver should call usb_free_urb() when it is finished with the urb.
*/
-urb_t *usb_alloc_urb(int iso_packets)
+struct urb *usb_alloc_urb(int iso_packets)
{
- urb_t *urb;
+ struct urb *urb;
- urb = (urb_t *)kmalloc(sizeof(urb_t) + iso_packets * sizeof(iso_packet_descriptor_t),
- in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ urb = (struct urb *)kmalloc(sizeof(struct urb) +
+ iso_packets * sizeof(struct usb_iso_packet_descriptor),
+ in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
if (!urb) {
err("alloc_urb: kmalloc failed");
return NULL;
* cleaned up with a call to usb_free_urb() when the driver is finished
* with it.
*/
-void usb_free_urb(urb_t* urb)
+void usb_free_urb(struct urb *urb)
{
if (urb)
kfree(urb);
* the periodic request, and bandwidth reservation is being done for
* this controller, submitting such a periodic request will fail.
*/
-int usb_submit_urb(urb_t *urb)
+int usb_submit_urb(struct urb *urb)
{
if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
return urb->dev->bus->op->submit_urb(urb);
* and the completion function will see status -ECONNRESET. Failure is
* indicated by any other return value.
*/
-int usb_unlink_urb(urb_t *urb)
+int usb_unlink_urb(struct urb *urb)
{
if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
return urb->dev->bus->op->unlink_urb(urb);
int done;
};
-static void usb_api_blocking_completion(urb_t *urb)
+static void usb_api_blocking_completion(struct urb *urb)
{
struct usb_api_data *awd = (struct usb_api_data *)urb->context;
}
// Starts urb and waits for completion or timeout
-static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length)
+static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
{
DECLARE_WAITQUEUE(wait, current);
struct usb_api_data awd;
int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe,
struct usb_ctrlrequest *cmd, void *data, int len, int timeout)
{
- urb_t *urb;
+ struct urb *urb;
int retv;
int length;
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout)
{
- urb_t *urb;
+ struct urb *urb;
if (len < 0)
return -EINVAL;
/*
* Make all of the blocks of data contiguous
*/
-static int usbvideo_CompressIsochronous(uvd_t *uvd, urb_t *urb)
+static int usbvideo_CompressIsochronous(uvd_t *uvd, struct urb *urb)
{
char *cdata;
int i, totlen = 0;
/* We double buffer the Iso lists */
for (i=0; i < USBVIDEO_NUMSBUF; i++) {
int j, k;
- urb_t *urb = uvd->sbuf[i].urb;
+ struct urb *urb = uvd->sbuf[i].urb;
urb->dev = dev;
urb->context = uvd;
urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp);
/* This structure represents one Isoc request - URB and buffer */
typedef struct {
char *data;
- urb_t *urb;
+ struct urb *urb;
} usbvideo_sbuf_t;
typedef struct {
int err = 0;
dbg("vicam_v4l_open");
-
- MOD_INC_USE_COUNT;
+
down(&vicam->sem);
- if (vicam->open_count) /* Maybe not needed? */
- err = -EBUSY;
+ vicam->fbuf = rvmalloc(vicam->maxframesize * VICAM_NUMFRAMES);
+ if (!vicam->fbuf)
+ err=-ENOMEM;
else {
- vicam->fbuf = rvmalloc(vicam->maxframesize * VICAM_NUMFRAMES);
- if (!vicam->fbuf)
- err=-ENOMEM;
- else {
- vicam->open_count = 1;
- }
+ vicam->open_count = 1;
+ }
#ifdef BLINKING
- vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0);
- info ("led on");
- vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0);
+ vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0);
+ info ("led on");
+ vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0);
#endif
- }
up(&vicam->sem);
- if (err)
- MOD_DEC_USE_COUNT;
+
return err;
}
up(&vicam->sem);
/* Why does se401.c have a usbdevice check here? */
/* If device is unplugged while open, I guess we only may unregister now */
- MOD_DEC_USE_COUNT;
}
static long vicam_v4l_read(struct video_device *vdev, char *user_buf, unsigned long buflen, int noblock)
/* FIXME - vicam_template - important */
static struct video_device vicam_template = {
+ owner: THIS_MODULE,
name: "vicam USB camera",
type: VID_TYPE_CAPTURE,
hardware: VID_HARDWARE_SE401, /* need to ask for own id */
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver vicam_driver = {
+ owner: THIS_MODULE,
name: "vicam",
probe: vicam_probe,
disconnect: vicam_disconnect,
module_init(usb_vicam_init);
module_exit(usb_vicam_exit);
+
/* v4l stuff */
char *camera_name;
char *fbuf;
- urb_t *urb[VICAM_NUMSBUF];
+ struct urb *urb[VICAM_NUMSBUF];
int sizes;
int *width;
int *height;
void
mode_to_prot(struct inode *inode)
{
- u32 prot = AFFS_INODE->i_protect;
+ u32 prot = AFFS_I(inode)->i_protect;
mode_t mode = inode->i_mode;
if (!(mode & S_IXUSR))
if (mode & S_IWOTH)
prot |= FIBF_OTR_WRITE;
- AFFS_INODE->i_protect = prot;
+ AFFS_I(inode)->i_protect = prot;
}
void
pr_debug("AFFS: balloc(inode=%lu,goal=%u): ", inode->i_ino, goal);
- if (inode->u.affs_i.i_pa_cnt) {
- pr_debug("%d\n", inode->u.affs_i.i_lastalloc+1);
- inode->u.affs_i.i_pa_cnt--;
- return ++inode->u.affs_i.i_lastalloc;
+ if (AFFS_I(inode)->i_pa_cnt) {
+ pr_debug("%d\n", AFFS_I(inode)->i_lastalloc+1);
+ AFFS_I(inode)->i_pa_cnt--;
+ return ++AFFS_I(inode)->i_lastalloc;
}
if (!goal || goal > AFFS_SB->s_partition_size) {
if (goal)
affs_warning(sb, "affs_balloc", "invalid goal %d", goal);
- //if (!inode->u.affs_i.i_last_block)
+ //if (!AFFS_I(inode)->i_last_block)
// affs_warning(sb, "affs_balloc", "no last alloc block");
goal = AFFS_SB->s_reserved;
}
bit = ffs(tmp) - 1;
blk += bit + AFFS_SB->s_reserved;
mask2 = mask = 1 << (bit & 31);
- inode->u.affs_i.i_lastalloc = blk;
+ AFFS_I(inode)->i_lastalloc = blk;
/* prealloc as much as possible within this word */
while ((mask2 <<= 1)) {
if (!(tmp & mask2))
break;
- inode->u.affs_i.i_pa_cnt++;
+ AFFS_I(inode)->i_pa_cnt++;
mask |= mask2;
}
- bm->bm_free -= inode->u.affs_i.i_pa_cnt + 1;
+ bm->bm_free -= AFFS_I(inode)->i_pa_cnt + 1;
*data = cpu_to_be32(tmp & ~mask);
{
if (atomic_read(&filp->f_count) != 1)
return 0;
- pr_debug("AFFS: open(%d)\n", AFFS_INODE->i_opencnt);
- AFFS_INODE->i_opencnt++;
+ pr_debug("AFFS: open(%d)\n", AFFS_I(inode)->i_opencnt);
+ AFFS_I(inode)->i_opencnt++;
return 0;
}
{
if (atomic_read(&filp->f_count) != 0)
return 0;
- pr_debug("AFFS: release(%d)\n", AFFS_INODE->i_opencnt);
- AFFS_INODE->i_opencnt--;
- if (!AFFS_INODE->i_opencnt)
+ pr_debug("AFFS: release(%d)\n", AFFS_I(inode)->i_opencnt);
+ AFFS_I(inode)->i_opencnt--;
+ if (!AFFS_I(inode)->i_opencnt)
affs_free_prealloc(inode);
return 0;
u32 lc_max;
int i, j, key;
- if (!AFFS_INODE->i_lc) {
+ if (!AFFS_I(inode)->i_lc) {
char *ptr = (char *)get_zeroed_page(GFP_NOFS);
if (!ptr)
return -ENOMEM;
- AFFS_INODE->i_lc = (u32 *)ptr;
- AFFS_INODE->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2);
+ AFFS_I(inode)->i_lc = (u32 *)ptr;
+ AFFS_I(inode)->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2);
}
- lc_max = AFFS_LC_SIZE << AFFS_INODE->i_lc_shift;
+ lc_max = AFFS_LC_SIZE << AFFS_I(inode)->i_lc_shift;
- if (AFFS_INODE->i_extcnt > lc_max) {
+ if (AFFS_I(inode)->i_extcnt > lc_max) {
u32 lc_shift, lc_mask, tmp, off;
/* need to recalculate linear cache, start from old size */
- lc_shift = AFFS_INODE->i_lc_shift;
- tmp = (AFFS_INODE->i_extcnt / AFFS_LC_SIZE) >> lc_shift;
+ lc_shift = AFFS_I(inode)->i_lc_shift;
+ tmp = (AFFS_I(inode)->i_extcnt / AFFS_LC_SIZE) >> lc_shift;
for (; tmp; tmp >>= 1)
lc_shift++;
lc_mask = (1 << lc_shift) - 1;
/* fix idx and old size to new shift */
- lc_idx >>= (lc_shift - AFFS_INODE->i_lc_shift);
- AFFS_INODE->i_lc_size >>= (lc_shift - AFFS_INODE->i_lc_shift);
+ lc_idx >>= (lc_shift - AFFS_I(inode)->i_lc_shift);
+ AFFS_I(inode)->i_lc_size >>= (lc_shift - AFFS_I(inode)->i_lc_shift);
/* first shrink old cache to make more space */
- off = 1 << (lc_shift - AFFS_INODE->i_lc_shift);
+ off = 1 << (lc_shift - AFFS_I(inode)->i_lc_shift);
for (i = 1, j = off; j < AFFS_LC_SIZE; i++, j += off)
- AFFS_INODE->i_ac[i] = AFFS_INODE->i_ac[j];
+ AFFS_I(inode)->i_ac[i] = AFFS_I(inode)->i_ac[j];
- AFFS_INODE->i_lc_shift = lc_shift;
- AFFS_INODE->i_lc_mask = lc_mask;
+ AFFS_I(inode)->i_lc_shift = lc_shift;
+ AFFS_I(inode)->i_lc_mask = lc_mask;
}
/* fill cache to the needed index */
- i = AFFS_INODE->i_lc_size;
- AFFS_INODE->i_lc_size = lc_idx + 1;
+ i = AFFS_I(inode)->i_lc_size;
+ AFFS_I(inode)->i_lc_size = lc_idx + 1;
for (; i <= lc_idx; i++) {
if (!i) {
- AFFS_INODE->i_lc[0] = inode->i_ino;
+ AFFS_I(inode)->i_lc[0] = inode->i_ino;
continue;
}
- key = AFFS_INODE->i_lc[i - 1];
- j = AFFS_INODE->i_lc_mask + 1;
+ key = AFFS_I(inode)->i_lc[i - 1];
+ j = AFFS_I(inode)->i_lc_mask + 1;
// unlock cache
for (; j > 0; j--) {
bh = affs_bread(sb, key);
affs_brelse(bh);
}
// lock cache
- AFFS_INODE->i_lc[i] = key;
+ AFFS_I(inode)->i_lc[i] = key;
}
return 0;
affs_adjust_checksum(bh, blocknr - tmp);
mark_buffer_dirty_inode(bh, inode);
- AFFS_INODE->i_extcnt++;
+ AFFS_I(inode)->i_extcnt++;
mark_inode_dirty(inode);
return new_bh;
affs_get_extblock(struct inode *inode, u32 ext)
{
/* inline the simplest case: same extended block as last time */
- struct buffer_head *bh = AFFS_INODE->i_ext_bh;
- if (ext == AFFS_INODE->i_ext_last)
+ struct buffer_head *bh = AFFS_I(inode)->i_ext_bh;
+ if (ext == AFFS_I(inode)->i_ext_last)
atomic_inc(&bh->b_count);
else
/* we have to do more (not inlined) */
u32 lc_idx, lc_off, ac_idx;
u32 tmp, idx;
- if (ext == AFFS_INODE->i_ext_last + 1) {
+ if (ext == AFFS_I(inode)->i_ext_last + 1) {
/* read the next extended block from the current one */
- bh = AFFS_INODE->i_ext_bh;
+ bh = AFFS_I(inode)->i_ext_bh;
ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
- if (ext < AFFS_INODE->i_extcnt)
+ if (ext < AFFS_I(inode)->i_extcnt)
goto read_ext;
- if (ext > AFFS_INODE->i_extcnt)
+ if (ext > AFFS_I(inode)->i_extcnt)
BUG();
bh = affs_alloc_extblock(inode, bh, ext);
if (IS_ERR(bh))
goto read_ext;
}
- if (ext >= AFFS_INODE->i_extcnt) {
+ if (ext >= AFFS_I(inode)->i_extcnt) {
struct buffer_head *prev_bh;
/* allocate a new extended block */
- if (ext > AFFS_INODE->i_extcnt)
+ if (ext > AFFS_I(inode)->i_extcnt)
BUG();
/* get previous extended block */
again:
/* check if there is an extended cache and whether it's large enough */
- lc_idx = ext >> AFFS_INODE->i_lc_shift;
- lc_off = ext & AFFS_INODE->i_lc_mask;
+ lc_idx = ext >> AFFS_I(inode)->i_lc_shift;
+ lc_off = ext & AFFS_I(inode)->i_lc_mask;
- if (lc_idx >= AFFS_INODE->i_lc_size) {
+ if (lc_idx >= AFFS_I(inode)->i_lc_size) {
int err;
err = affs_grow_extcache(inode, lc_idx);
/* every n'th key we find in the linear cache */
if (!lc_off) {
- ext_key = AFFS_INODE->i_lc[lc_idx];
+ ext_key = AFFS_I(inode)->i_lc[lc_idx];
goto read_ext;
}
/* maybe it's still in the associative cache */
ac_idx = (ext - lc_idx - 1) & AFFS_AC_MASK;
- if (AFFS_INODE->i_ac[ac_idx].ext == ext) {
- ext_key = AFFS_INODE->i_ac[ac_idx].key;
+ if (AFFS_I(inode)->i_ac[ac_idx].ext == ext) {
+ ext_key = AFFS_I(inode)->i_ac[ac_idx].key;
goto read_ext;
}
idx = ac_idx;
while (--tmp, --lc_off > 0) {
idx = (idx - 1) & AFFS_AC_MASK;
- if (AFFS_INODE->i_ac[idx].ext == tmp) {
- ext_key = AFFS_INODE->i_ac[idx].key;
+ if (AFFS_I(inode)->i_ac[idx].ext == tmp) {
+ ext_key = AFFS_I(inode)->i_ac[idx].key;
goto find_ext;
}
}
/* fall back to the linear cache */
- ext_key = AFFS_INODE->i_lc[lc_idx];
+ ext_key = AFFS_I(inode)->i_lc[lc_idx];
find_ext:
/* read all extended blocks until we find the one we need */
//unlock cache
/* store it in the associative cache */
// recalculate ac_idx?
- AFFS_INODE->i_ac[ac_idx].ext = ext;
- AFFS_INODE->i_ac[ac_idx].key = ext_key;
+ AFFS_I(inode)->i_ac[ac_idx].ext = ext;
+ AFFS_I(inode)->i_ac[ac_idx].key = ext_key;
read_ext:
/* finally read the right extended block */
store_ext:
/* release old cached extended block and store the new one */
- affs_brelse(AFFS_INODE->i_ext_bh);
- AFFS_INODE->i_ext_last = ext;
- AFFS_INODE->i_ext_bh = bh;
+ affs_brelse(AFFS_I(inode)->i_ext_bh);
+ AFFS_I(inode)->i_ext_last = ext;
+ AFFS_I(inode)->i_ext_bh = bh;
atomic_inc(&bh->b_count);
return bh;
if (block < 0)
goto err_small;
- if (block >= AFFS_INODE->i_blkcnt) {
- if (block > AFFS_INODE->i_blkcnt || !create)
+ if (block >= AFFS_I(inode)->i_blkcnt) {
+ if (block > AFFS_I(inode)->i_blkcnt || !create)
goto err_big;
} else
create = 0;
if (!blocknr)
goto err_alloc;
bh_result->b_state |= (1UL << BH_New);
- AFFS_INODE->mmu_private += AFFS_SB->s_data_blksize;
- AFFS_INODE->i_blkcnt++;
+ AFFS_I(inode)->mmu_private += AFFS_SB->s_data_blksize;
+ AFFS_I(inode)->i_blkcnt++;
/* store new block */
if (bh_result->b_blocknr)
static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
{
return cont_prepare_write(page, from, to, affs_get_block,
- &page->mapping->host->u.affs_i.mmu_private);
+ &AFFS_I(page->mapping->host)->mmu_private);
}
static int _affs_bmap(struct address_space *mapping, long block)
{
bidx++;
}
affs_brelse(bh);
- inode->i_size = AFFS_INODE->mmu_private = size;
+ inode->i_size = AFFS_I(inode)->mmu_private = size;
return 0;
out:
- inode->i_size = AFFS_INODE->mmu_private = size;
+ inode->i_size = AFFS_I(inode)->mmu_private = size;
return PTR_ERR(bh);
}
affs_brelse(bh);
tmp = (page->index << PAGE_CACHE_SHIFT) + from;
if (tmp > inode->i_size)
- inode->i_size = AFFS_INODE->mmu_private = tmp;
+ inode->i_size = AFFS_I(inode)->mmu_private = tmp;
return written;
pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
- while (inode->u.affs_i.i_pa_cnt) {
- inode->u.affs_i.i_pa_cnt--;
- affs_free_block(sb, ++inode->u.affs_i.i_lastalloc);
+ while (AFFS_I(inode)->i_pa_cnt) {
+ AFFS_I(inode)->i_pa_cnt--;
+ affs_free_block(sb, ++AFFS_I(inode)->i_lastalloc);
}
}
ext = last_blk / AFFS_SB->s_hashsize;
}
- if (inode->i_size > AFFS_INODE->mmu_private) {
+ if (inode->i_size > AFFS_I(inode)->mmu_private) {
struct address_space *mapping = inode->i_mapping;
struct page *page;
u32 size = inode->i_size - 1;
page_cache_release(page);
mark_inode_dirty(inode);
return;
- } else if (inode->i_size == AFFS_INODE->mmu_private)
+ } else if (inode->i_size == AFFS_I(inode)->mmu_private)
return;
// lock cache
ext_bh = affs_get_extblock(inode, ext);
- if (AFFS_INODE->i_lc) {
+ if (AFFS_I(inode)->i_lc) {
/* clear linear cache */
- for (i = (ext + 1) >> AFFS_INODE->i_lc_shift; i < AFFS_LC_SIZE; i++)
- AFFS_INODE->i_lc[i] = 0;
+ for (i = (ext + 1) >> AFFS_I(inode)->i_lc_shift; i < AFFS_LC_SIZE; i++)
+ AFFS_I(inode)->i_lc[i] = 0;
/* clear associative cache */
for (i = 0; i < AFFS_AC_SIZE; i++)
- if (AFFS_INODE->i_ac[i].ext >= ext)
- AFFS_INODE->i_ac[i].ext = 0;
+ if (AFFS_I(inode)->i_ac[i].ext >= ext)
+ AFFS_I(inode)->i_ac[i].ext = 0;
}
ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension);
- blkcnt = AFFS_INODE->i_blkcnt;
+ blkcnt = AFFS_I(inode)->i_blkcnt;
i = 0;
blk = last_blk;
if (inode->i_size) {
affs_brelse(ext_bh);
if (inode->i_size) {
- AFFS_INODE->i_blkcnt = last_blk + 1;
- AFFS_INODE->i_extcnt = ext + 1;
+ AFFS_I(inode)->i_blkcnt = last_blk + 1;
+ AFFS_I(inode)->i_extcnt = ext + 1;
} else {
- AFFS_INODE->i_blkcnt = 0;
- AFFS_INODE->i_extcnt = 1;
+ AFFS_I(inode)->i_blkcnt = 0;
+ AFFS_I(inode)->i_extcnt = 1;
}
- AFFS_INODE->mmu_private = inode->i_size;
+ AFFS_I(inode)->mmu_private = inode->i_size;
// unlock cache
while (ext_key) {
inode->i_size = 0;
inode->i_nlink = 1;
inode->i_mode = 0;
- memset(AFFS_INODE, 0, sizeof(struct affs_inode_info));
- init_MUTEX(&AFFS_INODE->i_link_lock);
- init_MUTEX(&AFFS_INODE->i_ext_lock);
- AFFS_INODE->i_extcnt = 1;
- AFFS_INODE->i_ext_last = ~1;
- AFFS_INODE->i_protect = prot;
+ AFFS_I(inode)->i_extcnt = 1;
+ AFFS_I(inode)->i_ext_last = ~1;
+ AFFS_I(inode)->i_protect = prot;
+ AFFS_I(inode)->i_opencnt = 0;
+ AFFS_I(inode)->i_blkcnt = 0;
+ AFFS_I(inode)->i_lc = NULL;
+ AFFS_I(inode)->i_lc_size = 0;
+ AFFS_I(inode)->i_lc_shift = 0;
+ AFFS_I(inode)->i_lc_mask = 0;
+ AFFS_I(inode)->i_ac = NULL;
+ AFFS_I(inode)->i_ext_bh = NULL;
+ AFFS_I(inode)->mmu_private = 0;
+ AFFS_I(inode)->i_lastalloc = 0;
+ AFFS_I(inode)->i_pa_cnt = 0;
if (AFFS_SB->s_flags & SF_SETMODE)
inode->i_mode = AFFS_SB->s_mode;
case ST_FILE:
size = be32_to_cpu(tail->size);
inode->i_mode |= S_IFREG;
- AFFS_INODE->mmu_private = inode->i_size = size;
+ AFFS_I(inode)->mmu_private = inode->i_size = size;
if (inode->i_size) {
- AFFS_INODE->i_blkcnt = (size - 1) /
+ AFFS_I(inode)->i_blkcnt = (size - 1) /
AFFS_SB->s_data_blksize + 1;
- AFFS_INODE->i_extcnt = (AFFS_INODE->i_blkcnt - 1) /
+ AFFS_I(inode)->i_extcnt = (AFFS_I(inode)->i_blkcnt - 1) /
AFFS_SB->s_hashsize + 1;
}
if (tail->link_chain)
if (tail->stype == be32_to_cpu(ST_ROOT)) {
secs_to_datestamp(inode->i_mtime,&AFFS_ROOT_TAIL(sb, bh)->root_change);
} else {
- tail->protect = cpu_to_be32(AFFS_INODE->i_protect);
+ tail->protect = cpu_to_be32(AFFS_I(inode)->i_protect);
tail->size = cpu_to_be32(inode->i_size);
secs_to_datestamp(inode->i_mtime,&tail->change);
if (!(inode->i_ino == AFFS_SB->s_root_block)) {
lock_kernel();
affs_free_prealloc(inode);
if (atomic_read(&inode->i_count) == 1) {
- if (inode->i_size != AFFS_INODE->mmu_private)
+ if (inode->i_size != AFFS_I(inode)->mmu_private)
affs_truncate(inode);
//if (inode->i_nlink)
// affs_clear_inode(inode);
void
affs_clear_inode(struct inode *inode)
{
- unsigned long cache_page = (unsigned long) inode->u.affs_i.i_lc;
+ unsigned long cache_page = (unsigned long) AFFS_I(inode)->i_lc;
pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
if (cache_page) {
pr_debug("AFFS: freeing ext cache\n");
- inode->u.affs_i.i_lc = NULL;
- inode->u.affs_i.i_ac = NULL;
+ AFFS_I(inode)->i_lc = NULL;
+ AFFS_I(inode)->i_ac = NULL;
free_page(cache_page);
}
- affs_brelse(AFFS_INODE->i_ext_bh);
- AFFS_INODE->i_ext_last = ~1;
- AFFS_INODE->i_ext_bh = NULL;
+ affs_brelse(AFFS_I(inode)->i_ext_bh);
+ AFFS_I(inode)->i_ext_last = ~1;
+ AFFS_I(inode)->i_ext_bh = NULL;
}
struct inode *
inode->i_ino = block;
inode->i_nlink = 1;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
- memset(AFFS_INODE, 0, sizeof(struct affs_inode_info));
- AFFS_INODE->i_extcnt = 1;
- AFFS_INODE->i_ext_last = ~1;
- init_MUTEX(&AFFS_INODE->i_link_lock);
- init_MUTEX(&AFFS_INODE->i_ext_lock);
+ AFFS_I(inode)->i_opencnt = 0;
+ AFFS_I(inode)->i_blkcnt = 0;
+ AFFS_I(inode)->i_lc = NULL;
+ AFFS_I(inode)->i_lc_size = 0;
+ AFFS_I(inode)->i_lc_shift = 0;
+ AFFS_I(inode)->i_lc_mask = 0;
+ AFFS_I(inode)->i_ac = NULL;
+ AFFS_I(inode)->i_ext_bh = NULL;
+ AFFS_I(inode)->mmu_private = 0;
+ AFFS_I(inode)->i_protect = 0;
+ AFFS_I(inode)->i_lastalloc = 0;
+ AFFS_I(inode)->i_pa_cnt = 0;
+ AFFS_I(inode)->i_extcnt = 1;
+ AFFS_I(inode)->i_ext_last = ~1;
insert_inode_hash(inode);
pr_debug("AFFS: write_super() at %lu, clean=%d\n", CURRENT_TIME, clean);
}
+static kmem_cache_t * affs_inode_cachep;
+
+static struct inode *affs_alloc_inode(struct super_block *sb)
+{
+ struct affs_inode_info *ei;
+ ei = (struct affs_inode_info *)kmem_cache_alloc(affs_inode_cachep, SLAB_KERNEL);
+ if (!ei)
+ return NULL;
+ return &ei->vfs_inode;
+}
+
+static void affs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(affs_inode_cachep, AFFS_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct affs_inode_info *ei = (struct affs_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR) {
+ init_MUTEX(&ei->i_link_lock);
+ init_MUTEX(&ei->i_ext_lock);
+ inode_init_once(&ei->vfs_inode);
+ }
+}
+
+static int init_inodecache(void)
+{
+ affs_inode_cachep = kmem_cache_create("affs_inode_cache",
+ sizeof(struct affs_inode_info),
+ 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (affs_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(affs_inode_cachep))
+ printk(KERN_INFO "affs_inode_cache: not all structures were freed\n");
+}
+
static struct super_operations affs_sops = {
+ alloc_inode: affs_alloc_inode,
+ destroy_inode: affs_destroy_inode,
read_inode: affs_read_inode,
write_inode: affs_write_inode,
put_inode: affs_put_inode,
static int __init init_affs_fs(void)
{
- return register_filesystem(&affs_fs_type);
+ int err = init_inodecache();
+ if (err)
+ goto out1;
+ err = register_filesystem(&affs_fs_type);
+ if (err)
+ goto out;
+ return 0;
+out:
+ destroy_inodecache();
+out1:
+ return err;
}
static void __exit exit_affs_fs(void)
{
unregister_filesystem(&affs_fs_type);
+ destroy_inodecache();
}
EXPORT_NO_SYMBOLS;
static void coda_put_super(struct super_block *);
static int coda_statfs(struct super_block *sb, struct statfs *buf);
+static kmem_cache_t * coda_inode_cachep;
+
+static struct inode *coda_alloc_inode(struct super_block *sb)
+{
+ struct coda_inode_info *ei;
+ ei = (struct coda_inode_info *)kmem_cache_alloc(coda_inode_cachep, SLAB_KERNEL);
+ memset(&ei->c_fid, 0, sizeof(struct ViceFid));
+ ei->c_flags = 0;
+ INIT_LIST_HEAD(&ei->c_cilist);
+ ei->c_container = NULL;
+ ei->c_contcount = 0;
+ memset(&ei->c_cached_cred, 0, sizeof(struct coda_cred));
+ ei->c_cached_perm = 0;
+ if (!ei)
+ return NULL;
+ return &ei->vfs_inode;
+}
+
+static void coda_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(coda_inode_cachep, ITOC(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct coda_inode_info *ei = (struct coda_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ inode_init_once(&ei->vfs_inode);
+}
+
+int coda_init_inodecache(void)
+{
+ coda_inode_cachep = kmem_cache_create("coda_inode_cache",
+ sizeof(struct coda_inode_info),
+ 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (coda_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+void coda_destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(coda_inode_cachep))
+ printk(KERN_INFO "coda_inode_cache: not all structures were freed\n");
+}
+
/* exported operations */
struct super_operations coda_super_operations =
{
+ alloc_inode: coda_alloc_inode,
+ destroy_inode: coda_destroy_inode,
read_inode: coda_read_inode,
clear_inode: coda_clear_inode,
put_super: coda_put_super,
if (!sbi) BUG();
-#if 0
- /* check if the inode is already initialized */
- if (inode->u.generic_ip) {
- printk("coda_read_inode: initialized inode");
- return;
- }
-
- inode->u.generic_ip = cii_alloc();
- if (!inode->u.generic_ip) {
- CDEBUG(D_CNODE, "coda_read_inode: failed to allocate inode info\n");
- make_bad_inode(inode);
- return;
- }
- memset(inode->u.generic_ip, 0, sizeof(struct coda_inode_info));
-#endif
-
cii = ITOC(inode);
- if (!coda_isnullfid(&cii->c_fid)) {
- printk("coda_read_inode: initialized inode");
- return;
- }
-
list_add(&cii->c_cilist, &sbi->sbi_cihead);
}
list_del_init(&cii->c_cilist);
inode->i_mapping = &inode->i_data;
coda_cache_clear_inode(inode);
-
-#if 0
- cii_free(inode->u.generic_ip);
- inode->u.generic_ip = NULL;
-#endif
}
int coda_notify_change(struct dentry *de, struct iattr *iattr)
MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
MODULE_LICENSE("GPL");
+extern int coda_init_inodecache(void);
+extern void coda_destroy_inodecache(void);
static int __init init_coda(void)
{
int status;
printk(KERN_INFO "Coda Kernel/Venus communications, v5.3.15, coda@cs.cmu.edu\n");
+ status = coda_init_inodecache();
+ if (status)
+ goto out2;
status = init_coda_psdev();
if ( status ) {
printk("Problem (%d) in init_coda_psdev\n", status);
- return status;
+ goto out1;
}
status = register_filesystem(&coda_fs_type);
if (status) {
printk("coda: failed to register filesystem!\n");
- devfs_unregister(devfs_handle);
- devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
- coda_sysctl_clean();
+ goto out;
}
+ return 0;
+out:
+ devfs_unregister(devfs_handle);
+ devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
+ coda_sysctl_clean();
+out1:
+ coda_destroy_inodecache();
+out2:
return status;
}
devfs_unregister(devfs_handle);
devfs_unregister_chrdev(CODA_PSDEV_MAJOR, "coda_psdev");
coda_sysctl_clean();
+ coda_destroy_inodecache();
}
module_init(init_coda);
/* devfs (Device FileSystem) driver.
- Copyright (C) 1998-2001 Richard Gooch
+ Copyright (C) 1998-2002 Richard Gooch
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
20020113 Richard Gooch <rgooch@atnf.csiro.au>
Fixed (rare, old) race in <devfs_lookup>.
v1.9
+ 20020120 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed deadlock bug in <devfs_d_revalidate_wait>.
+ Tag VFS deletable in <devfs_mk_symlink> if handle ignored.
+ v1.10
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <asm/bitops.h>
#include <asm/atomic.h>
-#define DEVFS_VERSION "1.9 (20020113)"
+#define DEVFS_VERSION "1.10 (20020120)"
#define DEVFS_NAME "devfs"
umode_t mode;
unsigned short namelen; /* I think 64k+ filenames are a way off... */
unsigned char hide:1;
- unsigned char vfs_created:1; /* Whether created by driver or VFS */
+ unsigned char vfs_deletable:1;/* Whether the VFS may delete the entry */
char name[1]; /* This is just a dummy: the allocated array
is bigger. This is NULL-terminated */
};
DPRINTK (DEBUG_REGISTER, "(%s)\n", name);
err = devfs_do_symlink (dir, name, flags, link, &de, info);
if (err) return err;
- if (handle != NULL) *handle = de;
+ if (handle == NULL) de->vfs_deletable = TRUE;
+ else *handle = de;
devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
return 0;
} /* End Function devfs_mk_symlink */
{
PRINTK ("(%s): using old entry in dir: %p \"%s\"\n",
name, dir, dir->name);
- old->vfs_created = FALSE;
+ old->vfs_deletable = FALSE;
devfs_put (dir);
return old;
}
struct devfs_lookup_struct *lookup_info = dentry->d_fsdata;
DECLARE_WAITQUEUE (wait, current);
- if ( !dentry->d_inode && is_devfsd_or_child (fs_info) )
+ if ( is_devfsd_or_child (fs_info) )
{
devfs_handle_t de = lookup_info->de;
struct inode *inode;
- DPRINTK (DEBUG_I_LOOKUP, "(%s): dentry: %p de: %p by: \"%s\"\n",
- dentry->d_name.name, dentry, de, current->comm);
+ DPRINTK (DEBUG_I_LOOKUP,
+ "(%s): dentry: %p inode: %p de: %p by: \"%s\"\n",
+ dentry->d_name.name, dentry, dentry->d_inode, de,
+ current->comm);
+ if (dentry->d_inode) return 1;
if (de == NULL)
{
read_lock (&parent->u.dir.lock);
de = get_devfs_entry_from_vfs_inode (inode);
DPRINTK (DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de);
if (de == NULL) return -ENOENT;
- if (!de->vfs_created) return -EPERM;
+ if (!de->vfs_deletable) return -EPERM;
write_lock (&de->parent->u.dir.lock);
unhooked = _devfs_unhook (de);
write_unlock (&de->parent->u.dir.lock);
DPRINTK (DEBUG_DISABLED, "(%s): errcode from <devfs_do_symlink>: %d\n",
dentry->d_name.name, err);
if (err < 0) return err;
- de->vfs_created = TRUE;
+ de->vfs_deletable = TRUE;
de->inode.uid = current->euid;
de->inode.gid = current->egid;
de->inode.atime = CURRENT_TIME;
if (parent == NULL) return -ENOENT;
de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
if (!de) return -ENOMEM;
- de->vfs_created = TRUE;
+ de->vfs_deletable = TRUE;
if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 )
return err;
de->inode.uid = current->euid;
de = get_devfs_entry_from_vfs_inode (inode);
if (de == NULL) return -ENOENT;
if ( !S_ISDIR (de->mode) ) return -ENOTDIR;
- if (!de->vfs_created) return -EPERM;
+ if (!de->vfs_deletable) return -EPERM;
/* First ensure the directory is empty and will stay thay way */
write_lock (&de->u.dir.lock);
de->u.dir.no_more_additions = TRUE;
if (parent == NULL) return -ENOENT;
de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
if (!de) return -ENOMEM;
- de->vfs_created = TRUE;
+ de->vfs_deletable = TRUE;
if ( S_ISBLK (mode) || S_ISCHR (mode) )
{
de->u.fcb.u.device.major = MAJOR (rdev);
#include <linux/efs_fs.h>
#include <linux/efs_vh.h>
#include <linux/efs_fs_sb.h>
+#include <linux/slab.h>
static DECLARE_FSTYPE_DEV(efs_fs_type, "efs", efs_read_super);
+static kmem_cache_t * efs_inode_cachep;
+
+static struct inode *efs_alloc_inode(struct super_block *sb)
+{
+ struct efs_inode_info *ei;
+ ei = (struct efs_inode_info *)kmem_cache_alloc(efs_inode_cachep, SLAB_KERNEL);
+ if (!ei)
+ return NULL;
+ return &ei->vfs_inode;
+}
+
+static void efs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(efs_inode_cachep, INODE_INFO(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct efs_inode_info *ei = (struct efs_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ inode_init_once(&ei->vfs_inode);
+}
+
+static int init_inodecache(void)
+{
+ efs_inode_cachep = kmem_cache_create("efs_inode_cache",
+ sizeof(struct efs_inode_info),
+ 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (efs_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(efs_inode_cachep))
+ printk(KERN_INFO "efs_inode_cache: not all structures were freed\n");
+}
+
static struct super_operations efs_superblock_operations = {
+ alloc_inode: efs_alloc_inode,
+ destroy_inode: efs_destroy_inode,
read_inode: efs_read_inode,
statfs: efs_statfs,
};
static int __init init_efs_fs(void) {
+ int err;
printk("EFS: "EFS_VERSION" - http://aeschi.ch.eu.org/efs/\n");
- return register_filesystem(&efs_fs_type);
+ err = init_inodecache();
+ if (err)
+ goto out1;
+ err = register_filesystem(&efs_fs_type);
+ if (err)
+ goto out;
+ return 0;
+out:
+ destroy_inodecache();
+out1:
+ return err;
}
static void __exit exit_efs_fs(void) {
unregister_filesystem(&efs_fs_type);
+ destroy_inodecache();
}
EXPORT_NO_SYMBOLS;
*/
#include <linux/config.h>
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
#include <linux/locks.h>
#include <linux/quotaops.h>
* Universite Pierre et Marie Curie (Paris VI)
*/
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
-
+#include "ext2.h"
static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
* and moved here. AV
*/
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
#include <linux/pagemap.h>
typedef struct ext2_dir_entry_2 ext2_dirent;
unsigned long start, n;
unsigned long npages = dir_pages(dir);
struct page *page = NULL;
+ struct ext2_inode_info *ei = EXT2_I(dir);
ext2_dirent * de;
/* OFFSET_CACHE */
*res_page = NULL;
- start = dir->u.ext2_i.i_dir_start_lookup;
+ start = ei->i_dir_start_lookup;
if (start >= npages)
start = 0;
n = start;
found:
*res_page = page;
- dir->u.ext2_i.i_dir_start_lookup = n;
+ ei->i_dir_start_lookup = n;
return de;
}
--- /dev/null
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+
+/*
+ * second extended file system inode data in memory
+ */
+struct ext2_inode_info {
+ __u32 i_data[15];
+ __u32 i_flags;
+ __u32 i_faddr;
+ __u8 i_frag_no;
+ __u8 i_frag_size;
+ __u16 i_osync;
+ __u32 i_file_acl;
+ __u32 i_dir_acl;
+ __u32 i_dtime;
+ __u32 i_block_group;
+ __u32 i_next_alloc_block;
+ __u32 i_next_alloc_goal;
+ __u32 i_prealloc_block;
+ __u32 i_prealloc_count;
+ __u32 i_dir_start_lookup;
+ struct inode vfs_inode;
+};
+
+/*
+ * Function prototypes
+ */
+
+/*
+ * Ok, these declarations are also in <linux/kernel.h> but none of the
+ * ext2 source programs needs to include it so they are duplicated here.
+ */
+
+static inline struct ext2_inode_info *EXT2_I(struct inode *inode)
+{
+ return list_entry(inode, struct ext2_inode_info, vfs_inode);
+}
+
+/* balloc.c */
+extern int ext2_bg_has_super(struct super_block *sb, int group);
+extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
+extern int ext2_new_block (struct inode *, unsigned long,
+ __u32 *, __u32 *, int *);
+extern void ext2_free_blocks (struct inode *, unsigned long,
+ unsigned long);
+extern unsigned long ext2_count_free_blocks (struct super_block *);
+extern void ext2_check_blocks_bitmap (struct super_block *);
+extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
+ unsigned int block_group,
+ struct buffer_head ** bh);
+
+/* dir.c */
+extern int ext2_add_link (struct dentry *, struct inode *);
+extern ino_t ext2_inode_by_name(struct inode *, struct dentry *);
+extern int ext2_make_empty(struct inode *, struct inode *);
+extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct dentry *, struct page **);
+extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *);
+extern int ext2_empty_dir (struct inode *);
+extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **);
+extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *);
+
+/* fsync.c */
+extern int ext2_sync_file (struct file *, struct dentry *, int);
+extern int ext2_fsync_inode (struct inode *, int);
+
+/* ialloc.c */
+extern struct inode * ext2_new_inode (struct inode *, int);
+extern void ext2_free_inode (struct inode *);
+extern unsigned long ext2_count_free_inodes (struct super_block *);
+extern void ext2_check_inodes_bitmap (struct super_block *);
+extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
+
+/* inode.c */
+extern void ext2_read_inode (struct inode *);
+extern void ext2_write_inode (struct inode *, int);
+extern void ext2_put_inode (struct inode *);
+extern void ext2_delete_inode (struct inode *);
+extern int ext2_sync_inode (struct inode *);
+extern void ext2_discard_prealloc (struct inode *);
+extern void ext2_truncate (struct inode *);
+
+/* ioctl.c */
+extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
+ unsigned long);
+
+/* super.c */
+extern void ext2_error (struct super_block *, const char *, const char *, ...)
+ __attribute__ ((format (printf, 3, 4)));
+extern NORET_TYPE void ext2_panic (struct super_block *, const char *,
+ const char *, ...)
+ __attribute__ ((NORET_AND format (printf, 3, 4)));
+extern void ext2_warning (struct super_block *, const char *, const char *, ...)
+ __attribute__ ((format (printf, 3, 4)));
+extern void ext2_update_dynamic_rev (struct super_block *sb);
+extern void ext2_put_super (struct super_block *);
+extern void ext2_write_super (struct super_block *);
+extern int ext2_remount (struct super_block *, int *, char *);
+extern struct super_block * ext2_read_super (struct super_block *,void *,int);
+extern int ext2_statfs (struct super_block *, struct statfs *);
+
+/*
+ * Inodes and files operations
+ */
+
+/* dir.c */
+extern struct file_operations ext2_dir_operations;
+
+/* file.c */
+extern struct inode_operations ext2_file_inode_operations;
+extern struct file_operations ext2_file_operations;
+
+/* inode.c */
+extern struct address_space_operations ext2_aops;
+
+/* namei.c */
+extern struct inode_operations ext2_dir_inode_operations;
+
+/* symlink.c */
+extern struct inode_operations ext2_fast_symlink_inode_operations;
* (jj@sunsite.ms.mff.cuni.cz)
*/
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
#include <linux/sched.h>
/*
* we can depend on generic_block_fdatasync() to sync the data blocks.
*/
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
#include <linux/locks.h>
#include <linux/smp_lock.h>
*/
#include <linux/config.h>
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
#include <linux/locks.h>
#include <linux/quotaops.h>
return group;
}
-struct inode * ext2_new_inode (const struct inode * dir, int mode)
+struct inode * ext2_new_inode(struct inode * dir, int mode)
{
struct super_block * sb;
struct buffer_head * bh;
struct inode * inode;
struct ext2_group_desc * desc;
struct ext2_super_block * es;
+ struct ext2_inode_info *ei;
int err;
sb = dir->i_sb;
if (!inode)
return ERR_PTR(-ENOMEM);
+ ei = EXT2_I(inode);
lock_super (sb);
es = sb->u.ext2_sb.s_es;
repeat:
if (S_ISDIR(mode))
- group = find_group_dir(sb, dir->u.ext2_i.i_block_group);
+ group = find_group_dir(sb, EXT2_I(dir)->i_block_group);
else
- group = find_group_other(sb, dir->u.ext2_i.i_block_group);
+ group = find_group_other(sb, EXT2_I(dir)->i_block_group);
err = -ENOSPC;
if (group == -1)
inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
- inode->u.ext2_i.i_new_inode = 1;
- inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags;
+ memset(ei->i_data, 0, sizeof(ei->i_data));
+ ei->i_flags = EXT2_I(dir)->i_flags;
if (S_ISLNK(mode))
- inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL);
- inode->u.ext2_i.i_block_group = group;
- if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
+ ei->i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL);
+ ei->i_faddr = 0;
+ ei->i_frag_no = 0;
+ ei->i_osync = 0;
+ ei->i_file_acl = 0;
+ ei->i_dir_acl = 0;
+ ei->i_dtime = 0;
+ ei->i_block_group = group;
+ ei->i_next_alloc_block = 0;
+ ei->i_next_alloc_goal = 0;
+ ei->i_prealloc_block = 0;
+ ei->i_prealloc_count = 0;
+ ei->i_dir_start_lookup = 0;
+ if (ei->i_flags & EXT2_SYNC_FL)
inode->i_flags |= S_SYNC;
- insert_inode_hash(inode);
inode->i_generation = sb->u.ext2_sb.s_next_generation++;
+ insert_inode_hash(inode);
mark_inode_dirty(inode);
unlock_super (sb);
* Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000
*/
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
#include <linux/locks.h>
#include <linux/smp_lock.h>
#include <linux/sched.h>
inode->i_ino == EXT2_ACL_IDX_INO ||
inode->i_ino == EXT2_ACL_DATA_INO)
goto no_delete;
- inode->u.ext2_i.i_dtime = CURRENT_TIME;
+ EXT2_I(inode)->i_dtime = CURRENT_TIME;
mark_inode_dirty(inode);
ext2_update_inode(inode, IS_SYNC(inode));
inode->i_size = 0;
void ext2_discard_prealloc (struct inode * inode)
{
#ifdef EXT2_PREALLOCATE
+ struct ext2_inode_info *ei = EXT2_I(inode);
lock_kernel();
/* Writer: ->i_prealloc* */
- if (inode->u.ext2_i.i_prealloc_count) {
- unsigned short total = inode->u.ext2_i.i_prealloc_count;
- unsigned long block = inode->u.ext2_i.i_prealloc_block;
- inode->u.ext2_i.i_prealloc_count = 0;
- inode->u.ext2_i.i_prealloc_block = 0;
+ if (ei->i_prealloc_count) {
+ unsigned short total = ei->i_prealloc_count;
+ unsigned long block = ei->i_prealloc_block;
+ ei->i_prealloc_count = 0;
+ ei->i_prealloc_block = 0;
/* Writer: end */
ext2_free_blocks (inode, block, total);
}
#ifdef EXT2_PREALLOCATE
+ struct ext2_inode_info *ei = EXT2_I(inode);
/* Writer: ->i_prealloc* */
- if (inode->u.ext2_i.i_prealloc_count &&
- (goal == inode->u.ext2_i.i_prealloc_block ||
- goal + 1 == inode->u.ext2_i.i_prealloc_block))
+ if (ei->i_prealloc_count &&
+ (goal == ei->i_prealloc_block ||
+ goal + 1 == ei->i_prealloc_block))
{
- result = inode->u.ext2_i.i_prealloc_block++;
- inode->u.ext2_i.i_prealloc_count--;
+ result = ei->i_prealloc_block++;
+ ei->i_prealloc_count--;
/* Writer: end */
ext2_debug ("preallocation hit (%lu/%lu).\n",
++alloc_hits, ++alloc_attempts);
alloc_hits, ++alloc_attempts);
if (S_ISREG(inode->i_mode))
result = ext2_new_block (inode, goal,
- &inode->u.ext2_i.i_prealloc_count,
- &inode->u.ext2_i.i_prealloc_block, err);
+ &ei->i_prealloc_count,
+ &ei->i_prealloc_block, err);
else
result = ext2_new_block (inode, goal, 0, 0, err);
}
*err = 0;
/* i_data is not going away, no lock needed */
- add_chain (chain, NULL, inode->u.ext2_i.i_data + *offsets);
+ add_chain (chain, NULL, EXT2_I(inode)->i_data + *offsets);
if (!p->key)
goto no_block;
while (--depth) {
static inline unsigned long ext2_find_near(struct inode *inode, Indirect *ind)
{
- u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.ext2_i.i_data;
+ struct ext2_inode_info *ei = EXT2_I(inode);
+ u32 *start = ind->bh ? (u32*) ind->bh->b_data : ei->i_data;
u32 *p;
/* Try to find previous block */
* It is going to be refered from inode itself? OK, just put it into
* the same cylinder group then.
*/
- return (inode->u.ext2_i.i_block_group *
- EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
+ return (ei->i_block_group * EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_first_data_block);
}
Indirect *partial,
unsigned long *goal)
{
+ struct ext2_inode_info *ei = EXT2_I(inode);
/* Writer: ->i_next_alloc* */
- if (block == inode->u.ext2_i.i_next_alloc_block + 1) {
- inode->u.ext2_i.i_next_alloc_block++;
- inode->u.ext2_i.i_next_alloc_goal++;
+ if (block == ei->i_next_alloc_block + 1) {
+ ei->i_next_alloc_block++;
+ ei->i_next_alloc_goal++;
}
/* Writer: end */
/* Reader: pointers, ->i_next_alloc* */
* try the heuristic for sequential allocation,
* failing that at least try to get decent locality.
*/
- if (block == inode->u.ext2_i.i_next_alloc_block)
- *goal = inode->u.ext2_i.i_next_alloc_goal;
+ if (block == ei->i_next_alloc_block)
+ *goal = ei->i_next_alloc_goal;
if (!*goal)
*goal = ext2_find_near(inode, partial);
return 0;
mark_buffer_uptodate(bh, 1);
unlock_buffer(bh);
mark_buffer_dirty_inode(bh, inode);
- if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {
+ if (IS_SYNC(inode) || EXT2_I(inode)->i_osync) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
Indirect *where,
int num)
{
+ struct ext2_inode_info *ei = EXT2_I(inode);
int i;
/* Verify that place we are splicing to is still there and vacant */
/* That's it */
*where->p = where->key;
- inode->u.ext2_i.i_next_alloc_block = block;
- inode->u.ext2_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key);
+ ei->i_next_alloc_block = block;
+ ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key);
/* Writer: end */
/* had we spliced it onto indirect block? */
if (where->bh) {
mark_buffer_dirty_inode(where->bh, inode);
- if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {
+ if (IS_SYNC(inode) || ei->i_osync) {
ll_rw_block (WRITE, 1, &where->bh);
wait_on_buffer(where->bh);
}
}
- if (IS_SYNC(inode) || inode->u.ext2_i.i_osync)
+ if (IS_SYNC(inode) || ei->i_osync)
ext2_sync_inode (inode);
else
mark_inode_dirty(inode);
void ext2_truncate (struct inode * inode)
{
- u32 *i_data = inode->u.ext2_i.i_data;
+ u32 *i_data = EXT2_I(inode)->i_data;
int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
int offsets[4];
Indirect chain[4];
unsigned long block;
unsigned long offset;
struct ext2_group_desc * gdp;
+ struct ext2_inode_info *ei = EXT2_I(inode);
if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
inode->i_ino != EXT2_ACL_DATA_INO &&
inode->i_atime = le32_to_cpu(raw_inode->i_atime);
inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
- inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
+ ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
/* We now have enough fields to check if the inode was active or not.
* This is needed because nfsd might try to access dead inodes
* the test is that same one that e2fsck uses
* NeilBrown 1999oct15
*/
- if (inode->i_nlink == 0 && (inode->i_mode == 0 || inode->u.ext2_i.i_dtime)) {
+ if (inode->i_nlink == 0 && (inode->i_mode == 0 || ei->i_dtime)) {
/* this inode is deleted */
brelse (bh);
goto bad_inode;
inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
inode->i_version = ++event;
- inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags);
- inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
- inode->u.ext2_i.i_frag_no = raw_inode->i_frag;
- inode->u.ext2_i.i_frag_size = raw_inode->i_fsize;
- inode->u.ext2_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
+ ei->i_flags = le32_to_cpu(raw_inode->i_flags);
+ ei->i_faddr = le32_to_cpu(raw_inode->i_faddr);
+ ei->i_frag_no = raw_inode->i_frag;
+ ei->i_frag_size = raw_inode->i_fsize;
+ ei->i_osync = 0;
+ ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
if (S_ISREG(inode->i_mode))
inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
else
- inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
+ ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
+ ei->i_dtime = 0;
inode->i_generation = le32_to_cpu(raw_inode->i_generation);
- inode->u.ext2_i.i_prealloc_count = 0;
- inode->u.ext2_i.i_block_group = block_group;
+ ei->i_next_alloc_block = 0;
+ ei->i_next_alloc_goal = 0;
+ ei->i_prealloc_count = 0;
+ ei->i_block_group = block_group;
+ ei->i_dir_start_lookup = 0;
/*
* NOTE! The in-memory inode i_data array is in little-endian order
* even on big-endian machines: we do NOT byteswap the block numbers!
*/
for (block = 0; block < EXT2_N_BLOCKS; block++)
- inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
+ ei->i_data[block] = raw_inode->i_block[block];
if (inode->i_ino == EXT2_ACL_IDX_INO ||
inode->i_ino == EXT2_ACL_DATA_INO)
le32_to_cpu(raw_inode->i_block[0]));
brelse (bh);
inode->i_attr_flags = 0;
- if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) {
+ if (ei->i_flags & EXT2_SYNC_FL) {
inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS;
inode->i_flags |= S_SYNC;
}
- if (inode->u.ext2_i.i_flags & EXT2_APPEND_FL) {
+ if (ei->i_flags & EXT2_APPEND_FL) {
inode->i_attr_flags |= ATTR_FLAG_APPEND;
inode->i_flags |= S_APPEND;
}
- if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL) {
+ if (ei->i_flags & EXT2_IMMUTABLE_FL) {
inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE;
inode->i_flags |= S_IMMUTABLE;
}
- if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL) {
+ if (ei->i_flags & EXT2_NOATIME_FL) {
inode->i_attr_flags |= ATTR_FLAG_NOATIME;
inode->i_flags |= S_NOATIME;
}
unsigned long offset;
int err = 0;
struct ext2_group_desc * gdp;
+ struct ext2_inode_info *ei = EXT2_I(inode);
if ((inode->i_ino != EXT2_ROOT_INO &&
inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
* Fix up interoperability with old kernels. Otherwise, old inodes get
* re-used with the upper 16 bits of the uid/gid intact
*/
- if(!inode->u.ext2_i.i_dtime) {
+ if(!ei->i_dtime) {
raw_inode->i_uid_high = cpu_to_le16(high_16_bits(inode->i_uid));
raw_inode->i_gid_high = cpu_to_le16(high_16_bits(inode->i_gid));
} else {
raw_inode->i_ctime = cpu_to_le32(inode->i_ctime);
raw_inode->i_mtime = cpu_to_le32(inode->i_mtime);
raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
- raw_inode->i_dtime = cpu_to_le32(inode->u.ext2_i.i_dtime);
- raw_inode->i_flags = cpu_to_le32(inode->u.ext2_i.i_flags);
- raw_inode->i_faddr = cpu_to_le32(inode->u.ext2_i.i_faddr);
- raw_inode->i_frag = inode->u.ext2_i.i_frag_no;
- raw_inode->i_fsize = inode->u.ext2_i.i_frag_size;
- raw_inode->i_file_acl = cpu_to_le32(inode->u.ext2_i.i_file_acl);
+ raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
+ raw_inode->i_flags = cpu_to_le32(ei->i_flags);
+ raw_inode->i_faddr = cpu_to_le32(ei->i_faddr);
+ raw_inode->i_frag = ei->i_frag_no;
+ raw_inode->i_fsize = ei->i_frag_size;
+ raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl);
if (S_ISDIR(inode->i_mode))
- raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl);
+ raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl);
else {
raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);
if (inode->i_size > 0x7fffffffULL) {
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
else for (block = 0; block < EXT2_N_BLOCKS; block++)
- raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
+ raw_inode->i_block[block] = ei->i_data[block];
mark_buffer_dirty(bh);
if (do_sync) {
ll_rw_block (WRITE, 1, &bh);
* Universite Pierre et Marie Curie (Paris VI)
*/
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
#include <linux/sched.h>
#include <asm/uaccess.h>
int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
unsigned long arg)
{
+ struct ext2_inode_info *ei = EXT2_I(inode);
unsigned int flags;
ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
switch (cmd) {
case EXT2_IOC_GETFLAGS:
- flags = inode->u.ext2_i.i_flags & EXT2_FL_USER_VISIBLE;
+ flags = ei->i_flags & EXT2_FL_USER_VISIBLE;
return put_user(flags, (int *) arg);
case EXT2_IOC_SETFLAGS: {
unsigned int oldflags;
if (get_user(flags, (int *) arg))
return -EFAULT;
- oldflags = inode->u.ext2_i.i_flags;
+ oldflags = ei->i_flags;
/*
* The IMMUTABLE and APPEND_ONLY flags can only be changed by
flags = flags & EXT2_FL_USER_MODIFIABLE;
flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
- inode->u.ext2_i.i_flags = flags;
+ ei->i_flags = flags;
if (flags & EXT2_SYNC_FL)
inode->i_flags |= S_SYNC;
* David S. Miller (davem@caip.rutgers.edu), 1995
*/
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
#include <linux/pagemap.h>
/*
if (IS_ERR(inode))
goto out;
- if (l > sizeof (inode->u.ext2_i.i_data)) {
+ if (l > sizeof (EXT2_I(inode)->i_data)) {
/* slow symlink */
inode->i_op = &page_symlink_inode_operations;
inode->i_mapping->a_ops = &ext2_aops;
} else {
/* fast symlink */
inode->i_op = &ext2_fast_symlink_inode_operations;
- memcpy((char*)&inode->u.ext2_i.i_data,symname,l);
+ memcpy((char*)(EXT2_I(inode)->i_data),symname,l);
inode->i_size = l-1;
}
mark_inode_dirty(inode);
#include <linux/config.h>
#include <linux/module.h>
#include <linux/string.h>
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/locks.h>
return;
}
+static kmem_cache_t * ext2_inode_cachep;
+
+static struct inode *ext2_alloc_inode(struct super_block *sb)
+{
+ struct ext2_inode_info *ei;
+ ei = (struct ext2_inode_info *)kmem_cache_alloc(ext2_inode_cachep, SLAB_KERNEL);
+ if (!ei)
+ return NULL;
+ return &ei->vfs_inode;
+}
+
+static void ext2_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct ext2_inode_info *ei = (struct ext2_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ inode_init_once(&ei->vfs_inode);
+}
+
+static int init_inodecache(void)
+{
+ ext2_inode_cachep = kmem_cache_create("ext2_inode_cache",
+ sizeof(struct ext2_inode_info),
+ 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (ext2_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(ext2_inode_cachep))
+ printk(KERN_INFO "ext2_inode_cache: not all structures were freed\n");
+}
+
static struct super_operations ext2_sops = {
+ alloc_inode: ext2_alloc_inode,
+ destroy_inode: ext2_destroy_inode,
read_inode: ext2_read_inode,
write_inode: ext2_write_inode,
put_inode: ext2_put_inode,
static int __init init_ext2_fs(void)
{
- return register_filesystem(&ext2_fs_type);
+ int err = init_inodecache();
+ if (err)
+ goto out1;
+ err = register_filesystem(&ext2_fs_type);
+ if (err)
+ goto out;
+ return 0;
+out:
+ destroy_inodecache();
+out1:
+ return err;
}
static void __exit exit_ext2_fs(void)
{
unregister_filesystem(&ext2_fs_type);
+ destroy_inodecache();
}
EXPORT_NO_SYMBOLS;
* ext2 symlink handling code
*/
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen)
{
- char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
- return vfs_readlink(dentry, buffer, buflen, s);
+ struct ext2_inode_info *ei = EXT2_I(dentry->d_inode);
+ return vfs_readlink(dentry, buffer, buflen, (char *)ei->i_data);
}
static int ext2_follow_link(struct dentry *dentry, struct nameidata *nd)
{
- char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
- return vfs_follow_link(nd, s);
+ struct ext2_inode_info *ei = EXT2_I(dentry->d_inode);
+ return vfs_follow_link(nd, (char *)ei->i_data);
}
struct inode_operations ext2_fast_symlink_inode_operations = {
* group to find a free inode.
*/
struct inode * ext3_new_inode (handle_t *handle,
- const struct inode * dir, int mode)
+ struct inode * dir, int mode)
{
struct super_block * sb;
struct buffer_head * bh;
struct ext3_group_desc * gdp;
struct ext3_group_desc * tmp;
struct ext3_super_block * es;
+ struct ext3_inode_info *ei;
int err = 0;
/* Cannot create files in a deleted directory */
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
- init_rwsem(&inode->u.ext3_i.truncate_sem);
+ ei = EXT3_I(inode);
lock_super (sb);
es = sb->u.ext3_sb.s_es;
/*
* Try to place the inode in its parent directory
*/
- i = dir->u.ext3_i.i_block_group;
+ i = EXT3_I(dir)->i_block_group;
tmp = ext3_get_group_desc (sb, i, &bh2);
if (tmp && le16_to_cpu(tmp->bg_free_inodes_count))
gdp = tmp;
/*
* That failed: try linear search for a free inode
*/
- i = dir->u.ext3_i.i_block_group + 1;
+ i = EXT3_I(dir)->i_block_group + 1;
for (j = 2; j < sb->u.ext3_sb.s_groups_count; j++) {
if (++i >= sb->u.ext3_sb.s_groups_count)
i = 0;
inode->i_blksize = PAGE_SIZE;
inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
- inode->u.ext3_i.i_flags = dir->u.ext3_i.i_flags & ~EXT3_INDEX_FL;
+
+ memset(ei->i_data, 0, sizeof(ei->i_data));
+ ei->i_next_alloc_block = 0;
+ ei->i_next_alloc_goal = 0;
+ ei->i_dir_start_lookup = 0;
+ ei->i_disksize = 0;
+
+ ei->i_flags = EXT3_I(dir)->i_flags & ~EXT3_INDEX_FL;
if (S_ISLNK(mode))
- inode->u.ext3_i.i_flags &= ~(EXT3_IMMUTABLE_FL|EXT3_APPEND_FL);
+ ei->i_flags &= ~(EXT3_IMMUTABLE_FL|EXT3_APPEND_FL);
#ifdef EXT3_FRAGMENTS
- inode->u.ext3_i.i_faddr = 0;
- inode->u.ext3_i.i_frag_no = 0;
- inode->u.ext3_i.i_frag_size = 0;
+ ei->i_faddr = 0;
+ ei->i_frag_no = 0;
+ ei->i_frag_size = 0;
#endif
- inode->u.ext3_i.i_file_acl = 0;
- inode->u.ext3_i.i_dir_acl = 0;
- inode->u.ext3_i.i_dtime = 0;
- INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+ ei->i_file_acl = 0;
+ ei->i_dir_acl = 0;
+ ei->i_dtime = 0;
#ifdef EXT3_PREALLOCATE
- inode->u.ext3_i.i_prealloc_count = 0;
+ ei->i_prealloc_count = 0;
#endif
- inode->u.ext3_i.i_block_group = i;
+ ei->i_block_group = i;
- if (inode->u.ext3_i.i_flags & EXT3_SYNC_FL)
+ if (ei->i_flags & EXT3_SYNC_FL)
inode->i_flags |= S_SYNC;
if (IS_SYNC(inode))
handle->h_sync = 1;
insert_inode_hash(inode);
inode->i_generation = event++;
- inode->u.ext3_i.i_state = EXT3_STATE_NEW;
+ ei->i_state = EXT3_STATE_NEW;
err = ext3_mark_inode_dirty(handle, inode);
if (err) goto fail;
* (Well, we could do this if we need to, but heck - it works)
*/
ext3_orphan_del(handle, inode);
- inode->u.ext3_i.i_dtime = CURRENT_TIME;
+ EXT3_I(inode)->i_dtime = CURRENT_TIME;
/*
* One subtle ordering requirement: if anything has gone wrong
void ext3_discard_prealloc (struct inode * inode)
{
#ifdef EXT3_PREALLOCATE
+ struct ext3_inode_info *ei = EXT3_I(inode);
lock_kernel();
/* Writer: ->i_prealloc* */
- if (inode->u.ext3_i.i_prealloc_count) {
- unsigned short total = inode->u.ext3_i.i_prealloc_count;
- unsigned long block = inode->u.ext3_i.i_prealloc_block;
- inode->u.ext3_i.i_prealloc_count = 0;
- inode->u.ext3_i.i_prealloc_block = 0;
+ if (ei->i_prealloc_count) {
+ unsigned short total = ei->i_prealloc_count;
+ unsigned long block = ei->i_prealloc_block;
+ ei->i_prealloc_count = 0;
+ ei->i_prealloc_block = 0;
/* Writer: end */
ext3_free_blocks (inode, block, total);
}
unsigned long result;
#ifdef EXT3_PREALLOCATE
+ struct ext3_inode_info *ei = EXT3_I(inode);
/* Writer: ->i_prealloc* */
- if (inode->u.ext3_i.i_prealloc_count &&
- (goal == inode->u.ext3_i.i_prealloc_block ||
- goal + 1 == inode->u.ext3_i.i_prealloc_block))
+ if (ei->i_prealloc_count &&
+ (goal == ei->i_prealloc_block ||
+ goal + 1 == ei->i_prealloc_block))
{
- result = inode->u.ext3_i.i_prealloc_block++;
- inode->u.ext3_i.i_prealloc_count--;
+ result = ei->i_prealloc_block++;
+ ei->i_prealloc_count--;
/* Writer: end */
ext3_debug ("preallocation hit (%lu/%lu).\n",
++alloc_hits, ++alloc_attempts);
alloc_hits, ++alloc_attempts);
if (S_ISREG(inode->i_mode))
result = ext3_new_block (inode, goal,
- &inode->u.ext3_i.i_prealloc_count,
- &inode->u.ext3_i.i_prealloc_block, err);
+ &ei->i_prealloc_count,
+ &ei->i_prealloc_block, err);
else
result = ext3_new_block (inode, goal, 0, 0, err);
/*
*err = 0;
/* i_data is not going away, no lock needed */
- add_chain (chain, NULL, inode->u.ext3_i.i_data + *offsets);
+ add_chain (chain, NULL, EXT3_I(inode)->i_data + *offsets);
if (!p->key)
goto no_block;
while (--depth) {
static inline unsigned long ext3_find_near(struct inode *inode, Indirect *ind)
{
- u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.ext3_i.i_data;
+ struct ext3_inode_info *ei = EXT3_I(inode);
+ u32 *start = ind->bh ? (u32*) ind->bh->b_data : ei->i_data;
u32 *p;
/* Try to find previous block */
* It is going to be refered from inode itself? OK, just put it into
* the same cylinder group then.
*/
- return (inode->u.ext3_i.i_block_group *
- EXT3_BLOCKS_PER_GROUP(inode->i_sb)) +
+ return (ei->i_block_group * EXT3_BLOCKS_PER_GROUP(inode->i_sb)) +
le32_to_cpu(inode->i_sb->u.ext3_sb.s_es->s_first_data_block);
}
static int ext3_find_goal(struct inode *inode, long block, Indirect chain[4],
Indirect *partial, unsigned long *goal)
{
+ struct ext3_inode_info *ei = EXT3_I(inode);
/* Writer: ->i_next_alloc* */
- if (block == inode->u.ext3_i.i_next_alloc_block + 1) {
- inode->u.ext3_i.i_next_alloc_block++;
- inode->u.ext3_i.i_next_alloc_goal++;
+ if (block == ei->i_next_alloc_block + 1) {
+ ei->i_next_alloc_block++;
+ ei->i_next_alloc_goal++;
}
#ifdef SEARCH_FROM_ZERO
- inode->u.ext3_i.i_next_alloc_block = 0;
- inode->u.ext3_i.i_next_alloc_goal = 0;
+ ei->i_next_alloc_block = 0;
+ ei->i_next_alloc_goal = 0;
#endif
/* Writer: end */
/* Reader: pointers, ->i_next_alloc* */
* try the heuristic for sequential allocation,
* failing that at least try to get decent locality.
*/
- if (block == inode->u.ext3_i.i_next_alloc_block)
- *goal = inode->u.ext3_i.i_next_alloc_goal;
+ if (block == ei->i_next_alloc_block)
+ *goal = ei->i_next_alloc_goal;
if (!*goal)
*goal = ext3_find_near(inode, partial);
#ifdef SEARCH_FROM_ZERO
{
int i;
int err = 0;
+ struct ext3_inode_info *ei = EXT3_I(inode);
/*
* If we're splicing into a [td]indirect block (as opposed to the
/* That's it */
*where->p = where->key;
- inode->u.ext3_i.i_next_alloc_block = block;
- inode->u.ext3_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key);
+ ei->i_next_alloc_block = block;
+ ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key);
#ifdef SEARCH_FROM_ZERO
- inode->u.ext3_i.i_next_alloc_block = 0;
- inode->u.ext3_i.i_next_alloc_goal = 0;
+ ei->i_next_alloc_block = 0;
+ ei->i_next_alloc_goal = 0;
#endif
/* Writer: end */
unsigned long goal;
int left;
int depth = ext3_block_to_path(inode, iblock, offsets);
+ struct ext3_inode_info *ei = EXT3_I(inode);
loff_t new_size;
J_ASSERT(handle != NULL || create == 0);
/*
* Block out ext3_truncate while we alter the tree
*/
- down_read(&inode->u.ext3_i.truncate_sem);
+ down_read(&ei->truncate_sem);
err = ext3_alloc_branch(handle, inode, left, goal,
offsets+(partial-chain), partial);
if (!err)
err = ext3_splice_branch(handle, inode, iblock, chain,
partial, left);
- up_read(&inode->u.ext3_i.truncate_sem);
+ up_read(&ei->truncate_sem);
if (err == -EAGAIN)
goto changed;
if (err)
* truncate is in progress. It is racy between multiple parallel
* instances of get_block, but we have the BKL.
*/
- if (new_size > inode->u.ext3_i.i_disksize)
- inode->u.ext3_i.i_disksize = new_size;
+ if (new_size > ei->i_disksize)
+ ei->i_disksize = new_size;
bh_result->b_state |= (1UL << BH_New);
goto got_it;
struct buffer_head *tmp_bh;
for (i = 1;
- inode->u.ext3_i.i_prealloc_count &&
+ EXT3_I(inode)->i_prealloc_count &&
i < EXT3_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks;
i++) {
/*
kunmap(page);
}
}
- if (inode->i_size > inode->u.ext3_i.i_disksize) {
- inode->u.ext3_i.i_disksize = inode->i_size;
+ if (inode->i_size > EXT3_I(inode)->i_disksize) {
+ EXT3_I(inode)->i_disksize = inode->i_size;
ret2 = ext3_mark_inode_dirty(handle, inode);
if (!ret)
ret = ret2;
void ext3_truncate(struct inode * inode)
{
handle_t *handle;
- u32 *i_data = inode->u.ext3_i.i_data;
+ struct ext3_inode_info *ei = EXT3_I(inode);
+ u32 *i_data = ei->i_data;
int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
int offsets[4];
Indirect chain[4];
* on-disk inode. We do this via i_disksize, which is the value which
* ext3 *really* writes onto the disk inode.
*/
- inode->u.ext3_i.i_disksize = inode->i_size;
+ ei->i_disksize = inode->i_size;
/*
* From here we block out all ext3_get_block() callers who want to
* modify the block allocation tree.
*/
- down_write(&inode->u.ext3_i.truncate_sem);
+ down_write(&ei->truncate_sem);
if (n == 1) { /* direct blocks */
ext3_free_data(handle, inode, NULL, i_data+offsets[0],
case EXT3_TIND_BLOCK:
;
}
- up_write(&inode->u.ext3_i.truncate_sem);
+ up_write(&ei->truncate_sem);
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
ext3_mark_inode_dirty(handle, inode);
{
struct ext3_iloc iloc;
struct ext3_inode *raw_inode;
+ struct ext3_inode_info *ei = EXT3_I(inode);
struct buffer_head *bh;
int block;
goto bad_inode;
bh = iloc.bh;
raw_inode = iloc.raw_inode;
- init_rwsem(&inode->u.ext3_i.truncate_sem);
inode->i_mode = le16_to_cpu(raw_inode->i_mode);
inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
inode->i_atime = le32_to_cpu(raw_inode->i_atime);
inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
- inode->u.ext3_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
+
+ ei->i_state = 0;
+ ei->i_next_alloc_block = 0;
+ ei->i_next_alloc_goal = 0;
+ ei->i_dir_start_lookup = 0;
+ ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
/* We now have enough fields to check if the inode was active or not.
* This is needed because nfsd might try to access dead inodes
* the test is that same one that e2fsck uses
* size */
inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
inode->i_version = ++event;
- inode->u.ext3_i.i_flags = le32_to_cpu(raw_inode->i_flags);
+ ei->i_flags = le32_to_cpu(raw_inode->i_flags);
#ifdef EXT3_FRAGMENTS
- inode->u.ext3_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
- inode->u.ext3_i.i_frag_no = raw_inode->i_frag;
- inode->u.ext3_i.i_frag_size = raw_inode->i_fsize;
+ ei->i_faddr = le32_to_cpu(raw_inode->i_faddr);
+ ei->i_frag_no = raw_inode->i_frag;
+ ei->i_frag_size = raw_inode->i_fsize;
#endif
- inode->u.ext3_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
+ ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
if (!S_ISREG(inode->i_mode)) {
- inode->u.ext3_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
+ ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
} else {
inode->i_size |=
((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
}
- inode->u.ext3_i.i_disksize = inode->i_size;
+ ei->i_disksize = inode->i_size;
inode->i_generation = le32_to_cpu(raw_inode->i_generation);
#ifdef EXT3_PREALLOCATE
- inode->u.ext3_i.i_prealloc_count = 0;
+ ei->i_prealloc_count = 0;
#endif
- inode->u.ext3_i.i_block_group = iloc.block_group;
+ ei->i_block_group = iloc.block_group;
/*
* NOTE! The in-memory inode i_data array is in little-endian order
* even on big-endian machines: we do NOT byteswap the block numbers!
*/
for (block = 0; block < EXT3_N_BLOCKS; block++)
- inode->u.ext3_i.i_data[block] = iloc.raw_inode->i_block[block];
- INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+ ei->i_data[block] = iloc.raw_inode->i_block[block];
+ INIT_LIST_HEAD(&ei->i_orphan);
brelse (iloc.bh);
init_special_inode(inode, inode->i_mode,
le32_to_cpu(iloc.raw_inode->i_block[0]));
/* inode->i_attr_flags = 0; unused */
- if (inode->u.ext3_i.i_flags & EXT3_SYNC_FL) {
+ if (ei->i_flags & EXT3_SYNC_FL) {
/* inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS; unused */
inode->i_flags |= S_SYNC;
}
- if (inode->u.ext3_i.i_flags & EXT3_APPEND_FL) {
+ if (ei->i_flags & EXT3_APPEND_FL) {
/* inode->i_attr_flags |= ATTR_FLAG_APPEND; unused */
inode->i_flags |= S_APPEND;
}
- if (inode->u.ext3_i.i_flags & EXT3_IMMUTABLE_FL) {
+ if (ei->i_flags & EXT3_IMMUTABLE_FL) {
/* inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE; unused */
inode->i_flags |= S_IMMUTABLE;
}
- if (inode->u.ext3_i.i_flags & EXT3_NOATIME_FL) {
+ if (ei->i_flags & EXT3_NOATIME_FL) {
/* inode->i_attr_flags |= ATTR_FLAG_NOATIME; unused */
inode->i_flags |= S_NOATIME;
}
struct ext3_iloc *iloc)
{
struct ext3_inode *raw_inode = iloc->raw_inode;
+ struct ext3_inode_info *ei = EXT3_I(inode);
struct buffer_head *bh = iloc->bh;
int err = 0, rc, block;
* Fix up interoperability with old kernels. Otherwise, old inodes get
* re-used with the upper 16 bits of the uid/gid intact
*/
- if(!inode->u.ext3_i.i_dtime) {
+ if(!ei->i_dtime) {
raw_inode->i_uid_high =
cpu_to_le16(high_16_bits(inode->i_uid));
raw_inode->i_gid_high =
raw_inode->i_gid_high = 0;
}
raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
- raw_inode->i_size = cpu_to_le32(inode->u.ext3_i.i_disksize);
+ raw_inode->i_size = cpu_to_le32(ei->i_disksize);
raw_inode->i_atime = cpu_to_le32(inode->i_atime);
raw_inode->i_ctime = cpu_to_le32(inode->i_ctime);
raw_inode->i_mtime = cpu_to_le32(inode->i_mtime);
raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
- raw_inode->i_dtime = cpu_to_le32(inode->u.ext3_i.i_dtime);
- raw_inode->i_flags = cpu_to_le32(inode->u.ext3_i.i_flags);
+ raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
+ raw_inode->i_flags = cpu_to_le32(ei->i_flags);
#ifdef EXT3_FRAGMENTS
- raw_inode->i_faddr = cpu_to_le32(inode->u.ext3_i.i_faddr);
- raw_inode->i_frag = inode->u.ext3_i.i_frag_no;
- raw_inode->i_fsize = inode->u.ext3_i.i_frag_size;
+ raw_inode->i_faddr = cpu_to_le32(ei->i_faddr);
+ raw_inode->i_frag = ei->i_frag_no;
+ raw_inode->i_fsize = ei->i_frag_size;
#else
/* If we are not tracking these fields in the in-memory inode,
* then preserve them on disk, but still initialise them to zero
* for new inodes. */
- if (EXT3_I(inode)->i_state & EXT3_STATE_NEW) {
+ if (ei->i_state & EXT3_STATE_NEW) {
raw_inode->i_faddr = 0;
raw_inode->i_frag = 0;
raw_inode->i_fsize = 0;
}
#endif
- raw_inode->i_file_acl = cpu_to_le32(inode->u.ext3_i.i_file_acl);
+ raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl);
if (!S_ISREG(inode->i_mode)) {
- raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext3_i.i_dir_acl);
+ raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl);
} else {
raw_inode->i_size_high =
- cpu_to_le32(inode->u.ext3_i.i_disksize >> 32);
- if (inode->u.ext3_i.i_disksize > 0x7fffffffULL) {
+ cpu_to_le32(ei->i_disksize >> 32);
+ if (ei->i_disksize > 0x7fffffffULL) {
struct super_block *sb = inode->i_sb;
if (!EXT3_HAS_RO_COMPAT_FEATURE(sb,
EXT3_FEATURE_RO_COMPAT_LARGE_FILE) ||
raw_inode->i_block[0] =
cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
else for (block = 0; block < EXT3_N_BLOCKS; block++)
- raw_inode->i_block[block] = inode->u.ext3_i.i_data[block];
+ raw_inode->i_block[block] = ei->i_data[block];
BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
rc = ext3_journal_dirty_metadata(handle, bh);
if (!err)
err = rc;
- EXT3_I(inode)->i_state &= ~EXT3_STATE_NEW;
+ ei->i_state &= ~EXT3_STATE_NEW;
out_brelse:
brelse (bh);
}
error = ext3_orphan_add(handle, inode);
- inode->u.ext3_i.i_disksize = attr->ia_size;
+ EXT3_I(inode)->i_disksize = attr->ia_size;
rc = ext3_mark_inode_dirty(handle, inode);
if (!error)
error = rc;
*/
if (val)
- inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+ EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL;
else
- inode->u.ext3_i.i_flags &= ~EXT3_JOURNAL_DATA_FL;
+ EXT3_I(inode)->i_flags &= ~EXT3_JOURNAL_DATA_FL;
journal_unlock_updates(journal);
int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
unsigned long arg)
{
+ struct ext3_inode_info *ei = EXT3_I(inode);
unsigned int flags;
ext3_debug ("cmd = %u, arg = %lu\n", cmd, arg);
switch (cmd) {
case EXT3_IOC_GETFLAGS:
- flags = inode->u.ext3_i.i_flags & EXT3_FL_USER_VISIBLE;
+ flags = ei->i_flags & EXT3_FL_USER_VISIBLE;
return put_user(flags, (int *) arg);
case EXT3_IOC_SETFLAGS: {
handle_t *handle = NULL;
if (get_user(flags, (int *) arg))
return -EFAULT;
- oldflags = inode->u.ext3_i.i_flags;
+ oldflags = ei->i_flags;
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT3_JOURNAL_DATA_FL;
flags = flags & EXT3_FL_USER_MODIFIABLE;
flags |= oldflags & ~EXT3_FL_USER_MODIFIABLE;
- inode->u.ext3_i.i_flags = flags;
+ ei->i_flags = flags;
if (flags & EXT3_SYNC_FL)
inode->i_flags |= S_SYNC;
sb = dir->i_sb;
nblocks = dir->i_size >> EXT3_BLOCK_SIZE_BITS(sb);
- start = dir->u.ext3_i.i_dir_start_lookup;
+ start = EXT3_I(dir)->i_dir_start_lookup;
if (start >= nblocks)
start = 0;
block = start;
i = search_dirblock(bh, dir, dentry,
block << EXT3_BLOCK_SIZE_BITS(sb), res_dir);
if (i == 1) {
- dir->u.ext3_i.i_dir_start_lookup = block;
+ EXT3_I(dir)->i_dir_start_lookup = block;
ret = bh;
goto cleanup_and_exit;
} else {
de = (struct ext3_dir_entry_2 *) bh->b_data;
de->inode = 0;
de->rec_len = le16_to_cpu(sb->s_blocksize);
- dir->u.ext3_i.i_disksize =
+ EXT3_I(dir)->i_disksize =
dir->i_size = offset + sb->s_blocksize;
- dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL;
ext3_mark_inode_dirty(handle, dir);
} else {
* and/or different from the directory change time.
*/
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
- dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL;
ext3_mark_inode_dirty(handle, dir);
dir->i_version = ++event;
BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
inode->i_op = &ext3_dir_inode_operations;
inode->i_fop = &ext3_dir_operations;
- inode->i_size = inode->u.ext3_i.i_disksize = inode->i_sb->s_blocksize;
+ inode->i_size = EXT3_I(inode)->i_disksize = inode->i_sb->s_blocksize;
inode->i_blocks = 0;
dir_block = ext3_bread (handle, inode, 0, 1, &err);
if (!dir_block) {
if (err)
goto out_no_entry;
dir->i_nlink++;
- dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL;
ext3_mark_inode_dirty(handle, dir);
d_instantiate(dentry, inode);
out_stop:
int err = 0, rc;
lock_super(sb);
- if (!list_empty(&inode->u.ext3_i.i_orphan))
+ if (!list_empty(&EXT3_I(inode)->i_orphan))
goto out_unlock;
/* Orphan handling is only valid for files with data blocks
* This is safe: on error we're going to ignore the orphan list
* anyway on the next recovery. */
if (!err)
- list_add(&inode->u.ext3_i.i_orphan, &EXT3_SB(sb)->s_orphan);
+ list_add(&EXT3_I(inode)->i_orphan, &EXT3_SB(sb)->s_orphan);
jbd_debug(4, "superblock will point to %ld\n", inode->i_ino);
jbd_debug(4, "orphan inode %ld will point to %d\n",
int ext3_orphan_del(handle_t *handle, struct inode *inode)
{
struct list_head *prev;
+ struct ext3_inode_info *ei = EXT3_I(inode);
struct ext3_sb_info *sbi;
ino_t ino_next;
struct ext3_iloc iloc;
int err = 0;
lock_super(inode->i_sb);
- if (list_empty(&inode->u.ext3_i.i_orphan)) {
+ if (list_empty(&ei->i_orphan)) {
unlock_super(inode->i_sb);
return 0;
}
ino_next = NEXT_ORPHAN(inode);
- prev = inode->u.ext3_i.i_orphan.prev;
+ prev = ei->i_orphan.prev;
sbi = EXT3_SB(inode->i_sb);
jbd_debug(4, "remove inode %ld from orphan list\n", inode->i_ino);
- list_del(&inode->u.ext3_i.i_orphan);
- INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+ list_del_init(&ei->i_orphan);
/* If we're on an error path, we may not have a valid
* transaction handle with which to update the orphan list on
} else {
struct ext3_iloc iloc2;
struct inode *i_prev =
- list_entry(prev, struct inode, u.ext3_i.i_orphan);
+ &list_entry(prev, struct ext3_inode_info, i_orphan)->vfs_inode;
jbd_debug(4, "orphan inode %ld will point to %ld\n",
i_prev->i_ino, ino_next);
ext3_mark_inode_dirty(handle, inode);
dir->i_nlink--;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL;
ext3_mark_inode_dirty(handle, dir);
end_rmdir:
if (retval)
goto end_unlink;
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL;
ext3_mark_inode_dirty(handle, dir);
inode->i_nlink--;
if (!inode->i_nlink)
if (IS_ERR(inode))
goto out_stop;
- if (l > sizeof (inode->u.ext3_i.i_data)) {
+ if (l > sizeof (EXT3_I(inode)->i_data)) {
inode->i_op = &page_symlink_inode_operations;
inode->i_mapping->a_ops = &ext3_aops;
/*
goto out_no_entry;
} else {
inode->i_op = &ext3_fast_symlink_inode_operations;
- memcpy((char*)&inode->u.ext3_i.i_data,symname,l);
+ memcpy((char*)&EXT3_I(inode)->i_data,symname,l);
inode->i_size = l-1;
}
- inode->u.ext3_i.i_disksize = inode->i_size;
+ EXT3_I(inode)->i_disksize = inode->i_size;
ext3_mark_inode_dirty(handle, inode);
err = ext3_add_nondir(handle, dentry, inode);
out_stop:
new_inode->i_ctime = CURRENT_TIME;
}
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
- old_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ EXT3_I(old_dir)->i_flags &= ~EXT3_INDEX_FL;
if (dir_bh) {
BUFFER_TRACE(dir_bh, "get_write_access");
ext3_journal_get_write_access(handle, dir_bh);
new_inode->i_nlink--;
} else {
new_dir->i_nlink++;
- new_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ EXT3_I(new_dir)->i_flags &= ~EXT3_INDEX_FL;
ext3_mark_inode_dirty(handle, new_dir);
}
}
return ret;
}
-#define orphan_list_entry(l) list_entry((l), struct inode, u.ext3_i.i_orphan)
+static inline struct inode *orphan_list_entry(struct list_head *l)
+{
+ return &list_entry(l, struct ext3_inode_info, i_orphan)->vfs_inode;
+}
static void dump_orphan_list(struct super_block *sb, struct ext3_sb_info *sbi)
{
return;
}
+static kmem_cache_t * ext3_inode_cachep;
+
+static struct inode *ext3_alloc_inode(struct super_block *sb)
+{
+ struct ext3_inode_info *ei;
+ ei = (struct ext3_inode_info *)kmem_cache_alloc(ext3_inode_cachep, SLAB_KERNEL);
+ if (!ei)
+ return NULL;
+ return &ei->vfs_inode;
+}
+
+static void ext3_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(ext3_inode_cachep, EXT3_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct ext3_inode_info *ei = (struct ext3_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR) {
+ INIT_LIST_HEAD(&ei->i_orphan);
+ init_rwsem(&ei->truncate_sem);
+ inode_init_once(&ei->vfs_inode);
+ }
+}
+
+static int init_inodecache(void)
+{
+ ext3_inode_cachep = kmem_cache_create("ext3_inode_cache",
+ sizeof(struct ext3_inode_info),
+ 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (ext3_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(ext3_inode_cachep))
+ printk(KERN_INFO "ext3_inode_cache: not all structures were freed\n");
+}
+
static struct super_operations ext3_sops = {
+ alloc_inode: ext3_alloc_inode,
+ destroy_inode: ext3_destroy_inode,
read_inode: ext3_read_inode, /* BKL held */
write_inode: ext3_write_inode, /* BKL not held. Don't need */
dirty_inode: ext3_dirty_inode, /* BKL not held. We take it */
static int __init init_ext3_fs(void)
{
- return register_filesystem(&ext3_fs_type);
+ int err = init_inodecache();
+ if (err)
+ goto out1;
+ err = register_filesystem(&ext3_fs_type);
+ if (err)
+ goto out;
+ return 0;
+out:
+ destroy_inodecache();
+out1:
+ return err;
}
static void __exit exit_ext3_fs(void)
{
unregister_filesystem(&ext3_fs_type);
+ destroy_inodecache();
}
EXPORT_NO_SYMBOLS;
static int ext3_readlink(struct dentry *dentry, char *buffer, int buflen)
{
- char *s = (char *)dentry->d_inode->u.ext3_i.i_data;
- return vfs_readlink(dentry, buffer, buflen, s);
+ struct ext3_inode_info *ei = EXT3_I(dentry->d_inode);
+ return vfs_readlink(dentry, buffer, buflen, (char*)ei->i_data);
}
static int ext3_follow_link(struct dentry *dentry, struct nameidata *nd)
{
- char *s = (char *)dentry->d_inode->u.ext3_i.i_data;
- return vfs_follow_link(nd, s);
+ struct ext3_inode_info *ei = EXT3_I(dentry->d_inode);
+ return vfs_follow_link(nd, (char*)ei->i_data);
}
struct inode_operations ext3_fast_symlink_inode_operations = {
if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32))
return -ENOSPC;
new_bh = fat_extend_dir(dir);
- if (!new_bh)
- return -ENOSPC;
+ if (IS_ERR(new_bh))
+ return PTR_ERR(new_bh);
fat_brelse(sb, new_bh);
do {
fat_get_entry(dir, &curr, bh, de, ino);
struct msdos_dir_entry *de;
__u16 date, time;
- if ((bh = fat_extend_dir(dir)) == NULL) return -ENOSPC;
+ bh = fat_extend_dir(dir);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+
/* zeroed out, so... */
fat_date_unix2dos(dir->i_mtime,&time,&date);
de = (struct msdos_dir_entry*)&bh->b_data[0];
return -EIO;
}
if (!(iblock % MSDOS_SB(inode->i_sb)->cluster_size)) {
- if (fat_add_cluster(inode) < 0)
- return -ENOSPC;
+ int error;
+
+ error = fat_add_cluster(inode);
+ if (error < 0)
+ return error;
}
MSDOS_I(inode)->mmu_private += sb->s_blocksize;
phys = fat_bmap(inode, iblock);
{
struct super_block *sb = inode->i_sb;
int count, nr, limit, last, curr, file_cluster;
- int cluster_size = MSDOS_SB(sb)->cluster_size;
- int res = -ENOSPC;
+ int cluster_bits = MSDOS_SB(sb)->cluster_bits;
lock_fat(sb);
if (MSDOS_SB(sb)->free_clusters == 0) {
unlock_fat(sb);
- return res;
+ return -ENOSPC;
}
limit = MSDOS_SB(sb)->clusters;
nr = limit; /* to keep GCC happy */
if (count >= limit) {
MSDOS_SB(sb)->free_clusters = 0;
unlock_fat(sb);
- return res;
+ return -ENOSPC;
}
MSDOS_SB(sb)->prev_free = (count + MSDOS_SB(sb)->prev_free + 1) % limit;
*/
last = file_cluster = 0;
if ((curr = MSDOS_I(inode)->i_start) != 0) {
+ int max_cluster = MSDOS_I(inode)->mmu_private >> cluster_bits;
+
fat_cache_lookup(inode, INT_MAX, &last, &curr);
file_cluster = last;
- while (curr && curr != -1){
+ while (curr && curr != -1) {
file_cluster++;
- if (!(curr = fat_access(sb, last = curr,-1))) {
+ if (!(curr = fat_access(sb, last = curr, -1))) {
fat_fs_panic(sb, "File without EOF");
- return res;
+ return -EIO;
+ }
+ if (file_cluster > max_cluster) {
+ fat_fs_panic(sb,"inode %lu: bad cluster counts",
+ inode->i_ino);
+ return -EIO;
}
}
}
MSDOS_I(inode)->i_logstart = nr;
mark_inode_dirty(inode);
}
- if (file_cluster
- != inode->i_blocks / cluster_size / (sb->s_blocksize / 512)) {
+ if (file_cluster != (inode->i_blocks >> (cluster_bits - 9))) {
printk ("file_cluster badly computed!!! %d <> %ld\n",
- file_cluster,
- inode->i_blocks / cluster_size / (sb->s_blocksize / 512));
+ file_cluster, inode->i_blocks >> (cluster_bits - 9));
fat_cache_inval_inode(inode);
}
- inode->i_blocks += (1 << MSDOS_SB(sb)->cluster_bits) / 512;
+ inode->i_blocks += (1 << cluster_bits) >> 9;
return nr;
}
if (MSDOS_SB(sb)->fat_bits != 32) {
if (inode->i_ino == MSDOS_ROOT_INO)
- return res;
+ return ERR_PTR(-ENOSPC);
}
nr = fat_add_cluster(inode);
if (nr < 0)
- return res;
+ return ERR_PTR(nr);
sector = MSDOS_SB(sb)->data_start + (nr - 2) * cluster_size;
last_sector = sector + cluster_size;
- if (MSDOS_SB(sb)->cvf_format && MSDOS_SB(sb)->cvf_format->zero_out_cluster)
+ if (MSDOS_SB(sb)->cvf_format
+ && MSDOS_SB(sb)->cvf_format->zero_out_cluster) {
+ res = ERR_PTR(-EIO);
MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode, nr);
- else {
+ } else {
for ( ; sector < last_sector; sector++) {
-#ifdef DEBUG
- printk("zeroing sector %d\n", sector);
-#endif
if (!(bh = fat_getblk(sb, sector)))
- printk("getblk failed\n");
+ printk("FAT: fat_getblk() failed\n");
else {
memset(bh->b_data, 0, sb->s_blocksize);
fat_set_uptodate(sb, bh, 1);
fat_brelse(sb, bh);
}
}
+ if (res == NULL)
+ res = ERR_PTR(-EIO);
}
if (inode->i_size & (sb->s_blocksize - 1)) {
fat_fs_panic(sb, "Odd directory size");
**res_de)
{
int count, cluster;
- unsigned long dir_size;
+ unsigned long dir_size = 0;
#ifdef DEBUG
printk("raw_scan_nonroot: start=%d\n",start);
#endif
- dir_size = 0;
do {
for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
if ((cluster = raw_scan_sector(sb,(start-2)*
return -1;
}
if (inode) {
- inode->i_hpfs_file_sec = btree->u.external[i].file_secno;
- inode->i_hpfs_disk_sec = btree->u.external[i].disk_secno;
- inode->i_hpfs_n_secs = btree->u.external[i].length;
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
+ hpfs_inode->i_file_sec = btree->u.external[i].file_secno;
+ hpfs_inode->i_disk_sec = btree->u.external[i].disk_secno;
+ hpfs_inode->i_n_secs = btree->u.external[i].length;
}
brelse(bh);
return a;
void hpfs_lock_inode(struct inode *i)
{
- if (i) down(&i->i_hpfs_sem);
+ if (i) {
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
+ down(&hpfs_inode->i_sem);
+ }
}
void hpfs_unlock_inode(struct inode *i)
{
- if (i) up(&i->i_hpfs_sem);
+ if (i) {
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
+ up(&hpfs_inode->i_sem);
+ }
}
void hpfs_lock_2inodes(struct inode *i1, struct inode *i2)
{
- if (!i1) { if (i2) down(&i2->i_hpfs_sem); return; }
- if (!i2) { if (i1) down(&i1->i_hpfs_sem); return; }
+ struct hpfs_inode_info *hpfs_i1 = NULL, *hpfs_i2 = NULL;
+
+ if (!i1) {
+ if (i2) {
+ hpfs_i2 = hpfs_i(i2);
+ down(&hpfs_i2->i_sem);
+ }
+ return;
+ }
+ if (!i2) {
+ if (i1) {
+ hpfs_i1 = hpfs_i(i1);
+ down(&hpfs_i1->i_sem);
+ }
+ return;
+ }
if (i1->i_ino < i2->i_ino) {
- down(&i1->i_hpfs_sem);
- down(&i2->i_hpfs_sem);
+ down(&hpfs_i1->i_sem);
+ down(&hpfs_i2->i_sem);
} else if (i1->i_ino > i2->i_ino) {
- down(&i2->i_hpfs_sem);
- down(&i1->i_hpfs_sem);
- } else down(&i1->i_hpfs_sem);
+ down(&hpfs_i2->i_sem);
+ down(&hpfs_i1->i_sem);
+ } else down(&hpfs_i1->i_sem);
}
void hpfs_unlock_2inodes(struct inode *i1, struct inode *i2)
{
- if (!i1) { if (i2) up(&i2->i_hpfs_sem); return; }
- if (!i2) { if (i1) up(&i1->i_hpfs_sem); return; }
+ struct hpfs_inode_info *hpfs_i1 = NULL, *hpfs_i2 = NULL;
+
+ if (!i1) {
+ if (i2) {
+ hpfs_i2 = hpfs_i(i2);
+ up(&hpfs_i2->i_sem);
+ }
+ return;
+ }
+ if (!i2) {
+ if (i1) {
+ hpfs_i1 = hpfs_i(i1);
+ up(&hpfs_i1->i_sem);
+ }
+ return;
+ }
if (i1->i_ino < i2->i_ino) {
- up(&i2->i_hpfs_sem);
- up(&i1->i_hpfs_sem);
+ up(&hpfs_i2->i_sem);
+ up(&hpfs_i1->i_sem);
} else if (i1->i_ino > i2->i_ino) {
- up(&i1->i_hpfs_sem);
- up(&i2->i_hpfs_sem);
- } else up(&i1->i_hpfs_sem);
+ up(&hpfs_i1->i_sem);
+ up(&hpfs_i2->i_sem);
+ } else up(&hpfs_i1->i_sem);
}
void hpfs_lock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
if (!i2) { hpfs_lock_2inodes(i1, i3); return; }
if (!i3) { hpfs_lock_2inodes(i1, i2); return; }
if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
- down(&i1->i_hpfs_sem);
+ struct hpfs_inode_info *hpfs_i1 = hpfs_i(i1);
+ down(&hpfs_i1->i_sem);
hpfs_lock_2inodes(i2, i3);
} else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
- down(&i2->i_hpfs_sem);
+ struct hpfs_inode_info *hpfs_i2 = hpfs_i(i2);
+ down(&hpfs_i2->i_sem);
hpfs_lock_2inodes(i1, i3);
} else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
- down(&i3->i_hpfs_sem);
+ struct hpfs_inode_info *hpfs_i3 = hpfs_i(i3);
+ down(&hpfs_i3->i_sem);
hpfs_lock_2inodes(i1, i2);
} else if (i1->i_ino != i2->i_ino) hpfs_lock_2inodes(i1, i2);
else hpfs_lock_2inodes(i1, i3);
if (!i2) { hpfs_unlock_2inodes(i1, i3); return; }
if (!i3) { hpfs_unlock_2inodes(i1, i2); return; }
if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
+ struct hpfs_inode_info *hpfs_i1 = hpfs_i(i1);
hpfs_unlock_2inodes(i2, i3);
- up(&i1->i_hpfs_sem);
+ up(&hpfs_i1->i_sem);
} else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
+ struct hpfs_inode_info *hpfs_i2 = hpfs_i(i2);
hpfs_unlock_2inodes(i1, i3);
- up(&i2->i_hpfs_sem);
+ up(&hpfs_i2->i_sem);
} else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
+ struct hpfs_inode_info *hpfs_i3 = hpfs_i(i3);
hpfs_unlock_2inodes(i1, i2);
- up(&i3->i_hpfs_sem);
+ up(&hpfs_i3->i_sem);
} else if (i1->i_ino != i2->i_ino) hpfs_unlock_2inodes(i1, i2);
else hpfs_unlock_2inodes(i1, i3);
}
loff_t pos;
struct quad_buffer_head qbh;
struct inode *i = filp->f_dentry->d_inode;
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct super_block *s = i->i_sb;
/*printk("dir lseek\n");*/
if (new_off == 0 || new_off == 1 || new_off == 11 || new_off == 12 || new_off == 13) goto ok;
hpfs_lock_inode(i);
- pos = ((loff_t) hpfs_de_as_down_as_possible(s, i->i_hpfs_dno) << 4) + 1;
+ pos = ((loff_t) hpfs_de_as_down_as_possible(s, hpfs_inode->i_dno) << 4) + 1;
while (pos != new_off) {
if (map_pos_dirent(i, &pos, &qbh)) hpfs_brelse4(&qbh);
else goto fail;
int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
struct inode *inode = filp->f_dentry->d_inode;
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
struct quad_buffer_head qbh;
struct hpfs_dirent *de;
int lc;
if (inode->i_sb->s_hpfs_chk) {
if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode"))
return -EFSERROR;
- if (hpfs_chk_sectors(inode->i_sb, inode->i_hpfs_dno, 4, "dir_dnode"))
+ if (hpfs_chk_sectors(inode->i_sb, hpfs_inode->i_dno, 4, "dir_dnode"))
return -EFSERROR;
}
if (inode->i_sb->s_hpfs_chk >= 2) {
e = 1;
hpfs_error(inode->i_sb, "not a directory, fnode %08x",inode->i_ino);
}
- if (inode->i_hpfs_dno != fno->u.external[0].disk_secno) {
+ if (hpfs_inode->i_dno != fno->u.external[0].disk_secno) {
e = 1;
- hpfs_error(inode->i_sb, "corrupted inode: i_hpfs_dno == %08x, fnode -> dnode == %08x", inode->i_hpfs_dno, fno->u.external[0].disk_secno);
+ hpfs_error(inode->i_sb, "corrupted inode: i_dno == %08x, fnode -> dnode == %08x", hpfs_inode->i_dno, fno->u.external[0].disk_secno);
}
brelse(bh);
if (e) return -EFSERROR;
filp->f_pos = 11;
}
if (filp->f_pos == 11) {
- if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir, DT_DIR) < 0) {
+ if (filldir(dirent, "..", 2, filp->f_pos, hpfs_inode->i_parent_dir, DT_DIR) < 0) {
hpfs_unlock_inode(inode);
return 0;
}
filp->f_pos = 1;
}
if (filp->f_pos == 1) {
- filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, inode->i_hpfs_dno) << 4) + 1;
+ filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, hpfs_inode->i_dno) << 4) + 1;
hpfs_add_pos(inode, &filp->f_pos);
filp->f_version = inode->i_version;
}
ino_t ino;
int err;
struct inode *result = NULL;
+ struct hpfs_inode_info *hpfs_result;
if ((err = hpfs_chk_name((char *)name, &len))) {
if (err == -ENAMETOOLONG) return ERR_PTR(-ENAMETOOLONG);
* '.' and '..' will never be passed here.
*/
- de = map_dirent(dir, dir->i_hpfs_dno, (char *) name, len, NULL, &qbh);
+ de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *) name, len, NULL, &qbh);
/*
* This is not really a bailout, just means file not found.
hpfs_error(dir->i_sb, "hpfs_lookup: can't get inode");
goto bail1;
}
- if (!de->directory) result->i_hpfs_parent_dir = dir->i_ino;
+ hpfs_result = hpfs_i(result);
+ if (!de->directory) hpfs_result->i_parent_dir = dir->i_ino;
hpfs_unlock_iget(dir->i_sb);
hpfs_decide_conv(result, (char *)name, len);
result->i_ctime = 1;
result->i_mtime = local_to_gmt(dir->i_sb, de->write_date);
result->i_atime = local_to_gmt(dir->i_sb, de->read_date);
- result->i_hpfs_ea_size = de->ea_size;
- if (!result->i_hpfs_ea_mode && de->read_only)
+ hpfs_result->i_ea_size = de->ea_size;
+ if (!hpfs_result->i_ea_mode && de->read_only)
result->i_mode &= ~0222;
if (!de->directory) {
if (result->i_size == -1) {
result->i_size = de->file_size;
result->i_data.a_ops = &hpfs_aops;
- result->u.hpfs_i.mmu_private = result->i_size;
+ hpfs_i(result)->mmu_private = result->i_size;
/*
* i_blocks should count the fnode and any anodes.
* We count 1 for the fnode and don't bother about
void hpfs_add_pos(struct inode *inode, loff_t *pos)
{
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
int i = 0;
loff_t **ppos;
- if (inode->i_hpfs_rddir_off)
- for (; inode->i_hpfs_rddir_off[i]; i++)
- if (inode->i_hpfs_rddir_off[i] == pos) return;
+
+ if (hpfs_inode->i_rddir_off)
+ for (; hpfs_inode->i_rddir_off[i]; i++)
+ if (hpfs_inode->i_rddir_off[i] == pos) return;
if (!(i&0x0f)) {
if (!(ppos = kmalloc((i+0x11) * sizeof(loff_t*), GFP_KERNEL))) {
printk("HPFS: out of memory for position list\n");
return;
}
- if (inode->i_hpfs_rddir_off) {
- memcpy(ppos, inode->i_hpfs_rddir_off, i * sizeof(loff_t));
- kfree(inode->i_hpfs_rddir_off);
+ if (hpfs_inode->i_rddir_off) {
+ memcpy(ppos, hpfs_inode->i_rddir_off, i * sizeof(loff_t));
+ kfree(hpfs_inode->i_rddir_off);
}
- inode->i_hpfs_rddir_off = ppos;
+ hpfs_inode->i_rddir_off = ppos;
}
- inode->i_hpfs_rddir_off[i] = pos;
- inode->i_hpfs_rddir_off[i + 1] = NULL;
+ hpfs_inode->i_rddir_off[i] = pos;
+ hpfs_inode->i_rddir_off[i + 1] = NULL;
}
void hpfs_del_pos(struct inode *inode, loff_t *pos)
{
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
loff_t **i, **j;
- if (!inode->i_hpfs_rddir_off) goto not_f;
- for (i = inode->i_hpfs_rddir_off; *i; i++) if (*i == pos) goto fnd;
+
+ if (!hpfs_inode->i_rddir_off) goto not_f;
+ for (i = hpfs_inode->i_rddir_off; *i; i++) if (*i == pos) goto fnd;
goto not_f;
fnd:
for (j = i + 1; *j; j++) ;
*i = *(j - 1);
*(j - 1) = NULL;
- if (j - 1 == inode->i_hpfs_rddir_off) {
- kfree(inode->i_hpfs_rddir_off);
- inode->i_hpfs_rddir_off = NULL;
+ if (j - 1 == hpfs_inode->i_rddir_off) {
+ kfree(hpfs_inode->i_rddir_off);
+ hpfs_inode->i_rddir_off = NULL;
}
return;
not_f:
static void for_all_poss(struct inode *inode, void (*f)(loff_t *, loff_t, loff_t),
loff_t p1, loff_t p2)
{
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
loff_t **i;
- if (!inode->i_hpfs_rddir_off) return;
- for (i = inode->i_hpfs_rddir_off; *i; i++) (*f)(*i, p1, p2);
+
+ if (!hpfs_inode->i_rddir_off) return;
+ for (i = hpfs_inode->i_rddir_off; *i; i++) (*f)(*i, p1, p2);
return;
}
fnode->u.external[0].disk_secno = rdno;
mark_buffer_dirty(bh);
brelse(bh);
- d->up = ad->up = i->i_hpfs_dno = rdno;
+ d->up = ad->up = hpfs_i(i)->i_dno = rdno;
d->root_dnode = ad->root_dnode = 0;
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
int hpfs_add_dirent(struct inode *i, unsigned char *name, unsigned namelen,
struct hpfs_dirent *new_de, int cdepth)
{
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct dnode *d;
struct hpfs_dirent *de, *de_end;
struct quad_buffer_head qbh;
dnode_secno dno;
int c;
int c1, c2 = 0;
- dno = i->i_hpfs_dno;
+ dno = hpfs_inode->i_dno;
down:
if (i->i_sb->s_hpfs_chk)
if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_dirent")) return 1;
static void delete_empty_dnode(struct inode *i, dnode_secno dno)
{
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct quad_buffer_head qbh;
struct dnode *dnode;
dnode_secno down, up, ndown;
mark_buffer_dirty(bh);
brelse(bh);
}
- i->i_hpfs_dno = down;
+ hpfs_inode->i_dno = down;
for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, (loff_t) 12);
return;
}
if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 5 + h[1], size, data)) goto bail;
fnode->ea_size_l = pos;
ret:
- inode->i_hpfs_ea_size += 5 + strlen(key) + size;
+ hpfs_i(inode)->i_ea_size += 5 + strlen(key) + size;
return;
bail:
if (fnode->ea_secno)
secno hpfs_bmap(struct inode *inode, unsigned file_secno)
{
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
unsigned n, disk_secno;
struct fnode *fnode;
struct buffer_head *bh;
- if (BLOCKS(inode->u.hpfs_i.mmu_private) <= file_secno) return 0;
- n = file_secno - inode->i_hpfs_file_sec;
- if (n < inode->i_hpfs_n_secs) return inode->i_hpfs_disk_sec + n;
+ if (BLOCKS(hpfs_i(inode)->mmu_private) <= file_secno) return 0;
+ n = file_secno - hpfs_inode->i_file_sec;
+ if (n < hpfs_inode->i_n_secs) return hpfs_inode->i_disk_sec + n;
if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) return 0;
disk_secno = hpfs_bplus_lookup(inode->i_sb, inode, &fnode->btree, file_secno, bh);
if (disk_secno == -1) return 0;
void hpfs_truncate(struct inode *i)
{
if (IS_IMMUTABLE(i)) return /*-EPERM*/;
- i->i_hpfs_n_secs = 0;
+ hpfs_i(i)->i_n_secs = 0;
i->i_blocks = 1 + ((i->i_size + 511) >> 9);
- i->u.hpfs_i.mmu_private = i->i_size;
+ hpfs_i(i)->mmu_private = i->i_size;
hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9));
hpfs_write_inode(i);
}
return 0;
}
if (!create) return 0;
- if (iblock<<9 != inode->u.hpfs_i.mmu_private) {
+ if (iblock<<9 != hpfs_i(inode)->mmu_private) {
BUG();
return -EIO;
}
return -ENOSPC;
}
inode->i_blocks++;
- inode->u.hpfs_i.mmu_private += 512;
+ hpfs_i(inode)->mmu_private += 512;
bh_result->b_state |= 1UL << BH_New;
map_bh(bh_result, inode->i_sb, s);
return 0;
static int hpfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
{
return cont_prepare_write(page,from,to,hpfs_get_block,
- &page->mapping->host->u.hpfs_i.mmu_private);
+ &hpfs_i(page->mapping->host)->mmu_private);
}
static int _hpfs_bmap(struct address_space *mapping, long block)
{
if (retval > 0) {
struct inode *inode = file->f_dentry->d_inode;
inode->i_mtime = CURRENT_TIME;
- inode->i_hpfs_dirty = 1;
+ hpfs_i(inode)->i_dirty = 1;
}
return retval;
}
#include <linux/fs.h>
#include <linux/hpfs_fs.h>
+#include <linux/hpfs_fs_i.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/kernel.h>
int hpfs_symlink_readpage(struct file *, struct page *);
int hpfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+static inline struct hpfs_inode_info *hpfs_i(struct inode *inode)
+{
+ return list_entry(inode, struct hpfs_inode_info, vfs_inode);
+}
+
/* super.c */
void hpfs_error(struct super_block *, char *, ...);
struct buffer_head *bh;
struct fnode *fnode;
struct super_block *sb = i->i_sb;
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
unsigned char *ea;
int ea_size;
- init_MUTEX(&i->i_hpfs_sem);
+
i->i_uid = sb->s_hpfs_uid;
i->i_gid = sb->s_hpfs_gid;
i->i_mode = sb->s_hpfs_mode;
- i->i_hpfs_conv = sb->s_hpfs_conv;
+ hpfs_inode->i_conv = sb->s_hpfs_conv;
i->i_blksize = 512;
i->i_size = -1;
i->i_blocks = -1;
- i->i_hpfs_dno = 0;
- i->i_hpfs_n_secs = 0;
- i->i_hpfs_file_sec = 0;
- i->i_hpfs_disk_sec = 0;
- i->i_hpfs_dpos = 0;
- i->i_hpfs_dsubdno = 0;
- i->i_hpfs_ea_mode = 0;
- i->i_hpfs_ea_uid = 0;
- i->i_hpfs_ea_gid = 0;
- i->i_hpfs_ea_size = 0;
+ hpfs_inode->i_dno = 0;
+ hpfs_inode->i_n_secs = 0;
+ hpfs_inode->i_file_sec = 0;
+ hpfs_inode->i_disk_sec = 0;
+ hpfs_inode->i_dpos = 0;
+ hpfs_inode->i_dsubdno = 0;
+ hpfs_inode->i_ea_mode = 0;
+ hpfs_inode->i_ea_uid = 0;
+ hpfs_inode->i_ea_gid = 0;
+ hpfs_inode->i_ea_size = 0;
i->i_version = ++event;
- i->i_hpfs_rddir_off = NULL;
- i->i_hpfs_dirty = 0;
+ hpfs_inode->i_rddir_off = NULL;
+ hpfs_inode->i_dirty = 0;
i->i_atime = 0;
i->i_mtime = 0;
if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) {
if (ea_size == 2) {
i->i_uid = ea[0] + (ea[1] << 8);
- i->i_hpfs_ea_uid = 1;
+ hpfs_inode->i_ea_uid = 1;
}
kfree(ea);
}
if ((ea = hpfs_get_ea(i->i_sb, fnode, "GID", &ea_size))) {
if (ea_size == 2) {
i->i_gid = ea[0] + (ea[1] << 8);
- i->i_hpfs_ea_gid = 1;
+ hpfs_inode->i_ea_gid = 1;
}
kfree(ea);
}
umode_t mode = sb->s_hpfs_mode;
if (ea_size == 2) {
mode = ea[0] + (ea[1] << 8);
- i->i_hpfs_ea_mode = 1;
+ hpfs_inode->i_ea_mode = 1;
}
kfree(ea);
i->i_mode = mode;
i->i_mode |= S_IFDIR;
i->i_op = &hpfs_dir_iops;
i->i_fop = &hpfs_dir_ops;
- i->i_hpfs_parent_dir = fnode->up;
- i->i_hpfs_dno = fnode->u.external[0].disk_secno;
+ hpfs_inode->i_parent_dir = fnode->up;
+ hpfs_inode->i_dno = fnode->u.external[0].disk_secno;
if (sb->s_hpfs_chk >= 2) {
struct buffer_head *bh0;
- if (hpfs_map_fnode(sb, i->i_hpfs_parent_dir, &bh0)) brelse(bh0);
+ if (hpfs_map_fnode(sb, hpfs_inode->i_parent_dir, &bh0)) brelse(bh0);
}
n_dnodes = 0; n_subdirs = 0;
- hpfs_count_dnodes(i->i_sb, i->i_hpfs_dno, &n_dnodes, &n_subdirs, NULL);
+ hpfs_count_dnodes(i->i_sb, hpfs_inode->i_dno, &n_dnodes, &n_subdirs, NULL);
i->i_blocks = 4 * n_dnodes;
i->i_size = 2048 * n_dnodes;
i->i_nlink = 2 + n_subdirs;
} else {
i->i_mode |= S_IFREG;
- if (!i->i_hpfs_ea_mode) i->i_mode &= ~0111;
+ if (!hpfs_inode->i_ea_mode) i->i_mode &= ~0111;
i->i_op = &hpfs_file_iops;
i->i_fop = &hpfs_file_ops;
i->i_nlink = 1;
i->i_size = fnode->file_size;
i->i_blocks = ((i->i_size + 511) >> 9) + 1;
i->i_data.a_ops = &hpfs_aops;
- i->u.hpfs_i.mmu_private = i->i_size;
+ hpfs_i(i)->mmu_private = i->i_size;
}
brelse(bh);
}
void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode)
{
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
if (fnode->acl_size_l || fnode->acl_size_s) {
/* Some unknown structures like ACL may be in fnode,
we'd better not overwrite them */
hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino);
} else if (i->i_sb->s_hpfs_eas >= 2) {
unsigned char ea[4];
- if ((i->i_uid != i->i_sb->s_hpfs_uid) || i->i_hpfs_ea_uid) {
+ if ((i->i_uid != i->i_sb->s_hpfs_uid) || hpfs_inode->i_ea_uid) {
ea[0] = i->i_uid & 0xff;
ea[1] = i->i_uid >> 8;
hpfs_set_ea(i, fnode, "UID", ea, 2);
- i->i_hpfs_ea_uid = 1;
+ hpfs_inode->i_ea_uid = 1;
}
- if ((i->i_gid != i->i_sb->s_hpfs_gid) || i->i_hpfs_ea_gid) {
+ if ((i->i_gid != i->i_sb->s_hpfs_gid) || hpfs_inode->i_ea_gid) {
ea[0] = i->i_gid & 0xff;
ea[1] = i->i_gid >> 8;
hpfs_set_ea(i, fnode, "GID", ea, 2);
- i->i_hpfs_ea_gid = 1;
+ hpfs_inode->i_ea_gid = 1;
}
if (!S_ISLNK(i->i_mode))
if ((i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
| (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))
&& i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
- | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || i->i_hpfs_ea_mode) {
+ | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || hpfs_inode->i_ea_mode) {
ea[0] = i->i_mode & 0xff;
ea[1] = i->i_mode >> 8;
hpfs_set_ea(i, fnode, "MODE", ea, 2);
- i->i_hpfs_ea_mode = 1;
+ hpfs_inode->i_ea_mode = 1;
}
if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) {
int d = kdev_t_to_nr(i->i_rdev);
void hpfs_write_inode(struct inode *i)
{
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct inode *parent;
if (!i->i_nlink) return;
if (i->i_ino == i->i_sb->s_hpfs_root) return;
- if (i->i_hpfs_rddir_off && !atomic_read(&i->i_count)) {
- if (*i->i_hpfs_rddir_off) printk("HPFS: write_inode: some position still there\n");
- kfree(i->i_hpfs_rddir_off);
- i->i_hpfs_rddir_off = NULL;
+ if (hpfs_inode->i_rddir_off && !atomic_read(&i->i_count)) {
+ if (*hpfs_inode->i_rddir_off) printk("HPFS: write_inode: some position still there\n");
+ kfree(hpfs_inode->i_rddir_off);
+ hpfs_inode->i_rddir_off = NULL;
}
- i->i_hpfs_dirty = 0;
+ hpfs_inode->i_dirty = 0;
hpfs_lock_iget(i->i_sb, 1);
- parent = iget(i->i_sb, i->i_hpfs_parent_dir);
+ parent = iget(i->i_sb, hpfs_inode->i_parent_dir);
hpfs_unlock_iget(i->i_sb);
hpfs_lock_inode(parent);
hpfs_write_inode_nolock(i);
void hpfs_write_inode_nolock(struct inode *i)
{
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct buffer_head *bh;
struct fnode *fnode;
struct quad_buffer_head qbh;
de->read_date = gmt_to_local(i->i_sb, i->i_atime);
de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
de->read_only = !(i->i_mode & 0222);
- de->ea_size = i->i_hpfs_ea_size;
+ de->ea_size = hpfs_inode->i_ea_size;
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
}
if (S_ISDIR(i->i_mode)) {
- if ((de = map_dirent(i, i->i_hpfs_dno, "\001\001", 2, NULL, &qbh))) {
+ if ((de = map_dirent(i, hpfs_inode->i_dno, "\001\001", 2, NULL, &qbh))) {
de->write_date = gmt_to_local(i->i_sb, i->i_mtime);
de->read_date = gmt_to_local(i->i_sb, i->i_atime);
de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
de->read_only = !(i->i_mode & 0222);
- de->ea_size = /*i->i_hpfs_ea_size*/0;
+ de->ea_size = /*hpfs_inode->i_ea_size*/0;
de->file_size = 0;
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
void hpfs_write_if_changed(struct inode *inode)
{
- if (inode->i_hpfs_dirty) {
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
+
+ if (hpfs_inode->i_dirty)
hpfs_write_inode(inode);
- }
}
void hpfs_delete_inode(struct inode *inode)
void hpfs_decide_conv(struct inode *inode, unsigned char *name, unsigned len)
{
+ struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
int i;
- if (inode->i_hpfs_conv != CONV_AUTO) return;
+ if (hpfs_inode->i_conv != CONV_AUTO) return;
for (i = 0; *text_postfix[i]; i++) {
int l = strlen(text_postfix[i]);
if (l <= len)
if (!hpfs_compare_names(inode->i_sb, text_prefix[i], l, name, l, 0))
goto text;
}
- inode->i_hpfs_conv = CONV_BINARY;
+ hpfs_inode->i_conv = CONV_BINARY;
return;
text:
- inode->i_hpfs_conv = CONV_TEXT;
+ hpfs_inode->i_conv = CONV_TEXT;
return;
}
struct hpfs_dirent dee;
int err;
if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
- if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail;
if (!(dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1))) goto bail1;
memset(&dee, 0, sizeof dee);
dee.directory = 1;
dir->i_nlink++;
hpfs_lock_iget(dir->i_sb, 1);
if ((result = iget(dir->i_sb, fno))) {
- result->i_hpfs_parent_dir = dir->i_ino;
+ hpfs_i(result)->i_parent_dir = dir->i_ino;
result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
- result->i_hpfs_ea_size = 0;
+ hpfs_i(result)->i_ea_size = 0;
if (dee.read_only) result->i_mode &= ~0222;
if (result->i_uid != current->fsuid ||
result->i_gid != current->fsgid ||
struct hpfs_dirent dee;
int err;
if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
- if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail;
memset(&dee, 0, sizeof dee);
if (!(mode & 0222)) dee.read_only = 1;
dee.archive = 1;
hpfs_lock_iget(dir->i_sb, 2);
if ((result = iget(dir->i_sb, fno))) {
hpfs_decide_conv(result, (char *)name, len);
- result->i_hpfs_parent_dir = dir->i_ino;
+ hpfs_i(result)->i_parent_dir = dir->i_ino;
result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
- result->i_hpfs_ea_size = 0;
+ hpfs_i(result)->i_ea_size = 0;
if (dee.read_only) result->i_mode &= ~0222;
if (result->i_blocks == -1) result->i_blocks = 1;
if (result->i_size == -1) {
result->i_size = 0;
result->i_data.a_ops = &hpfs_aops;
- result->u.hpfs_i.mmu_private = 0;
+ hpfs_i(result)->mmu_private = 0;
}
if (result->i_uid != current->fsuid ||
result->i_gid != current->fsgid ||
int err;
if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
- if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail;
memset(&dee, 0, sizeof dee);
if (!(mode & 0222)) dee.read_only = 1;
dee.archive = 1;
mark_buffer_dirty(bh);
hpfs_lock_iget(dir->i_sb, 2);
if ((result = iget(dir->i_sb, fno))) {
- result->i_hpfs_parent_dir = dir->i_ino;
+ hpfs_i(result)->i_parent_dir = dir->i_ino;
result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
- result->i_hpfs_ea_size = 0;
+ hpfs_i(result)->i_ea_size = 0;
/*if (result->i_blocks == -1) result->i_blocks = 1;
if (result->i_size == -1) result->i_size = 0;*/
result->i_uid = current->fsuid;
int err;
if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
- if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail;
memset(&dee, 0, sizeof dee);
dee.archive = 1;
dee.hidden = name[0] == '.';
brelse(bh);
hpfs_lock_iget(dir->i_sb, 2);
if ((result = iget(dir->i_sb, fno))) {
- result->i_hpfs_parent_dir = dir->i_ino;
+ hpfs_i(result)->i_parent_dir = dir->i_ino;
result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
- result->i_hpfs_ea_size = 0;
+ hpfs_i(result)->i_ea_size = 0;
/*if (result->i_blocks == -1) result->i_blocks = 1;
if (result->i_size == -1) result->i_size = 0;*/
result->i_mode = S_IFLNK | 0777;
hpfs_adjust_length((char *)name, &len);
again:
hpfs_lock_2inodes(dir, inode);
- if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) {
+ if (!(de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh))) {
hpfs_unlock_2inodes(dir, inode);
return -ENOENT;
}
int r;
hpfs_adjust_length((char *)name, &len);
hpfs_lock_2inodes(dir, inode);
- if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) {
+ if (!(de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh))) {
hpfs_unlock_2inodes(dir, inode);
return -ENOENT;
}
hpfs_unlock_2inodes(dir, inode);
return -ENOTDIR;
}
- hpfs_count_dnodes(dir->i_sb, inode->i_hpfs_dno, NULL, NULL, &n_items);
+ hpfs_count_dnodes(dir->i_sb, hpfs_i(inode)->i_dno, NULL, NULL, &n_items);
if (n_items) {
hpfs_brelse4(&qbh);
hpfs_unlock_2inodes(dir, inode);
goto end1;
}
- if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) {
+ if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) {
hpfs_error(i->i_sb, "lookup succeeded but map dirent failed");
err = -ENOENT;
goto end1;
if (new_inode) {
int r;
if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
- if ((nde = map_dirent(new_dir, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1))) {
+ if ((nde = map_dirent(new_dir, hpfs_i(new_dir)->i_dno, (char *)new_name, new_len, NULL, &qbh1))) {
new_inode->i_nlink = 0;
copy_de(nde, &de);
memcpy(nde->name, new_name, new_len);
}
if (new_dir == old_dir)
- if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) {
+ if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) {
hpfs_unlock_creation(i->i_sb);
hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2");
err = -ENOENT;
hpfs_unlock_creation(i->i_sb);
end:
- i->i_hpfs_parent_dir = new_dir->i_ino;
+ hpfs_i(i)->i_parent_dir = new_dir->i_ino;
if (S_ISDIR(i->i_mode)) {
new_dir->i_nlink++;
old_dir->i_nlink--;
mark_buffer_dirty(bh);
brelse(bh);
}
- i->i_hpfs_conv = i->i_sb->s_hpfs_conv;
+ hpfs_i(i)->i_conv = i->i_sb->s_hpfs_conv;
hpfs_decide_conv(i, (char *)new_name, new_len);
end1:
hpfs_unlock_3inodes(old_dir, new_dir, i);
return 0;
}
+static kmem_cache_t * hpfs_inode_cachep;
+
+static struct inode *hpfs_alloc_inode(struct super_block *sb)
+{
+ struct hpfs_inode_info *ei;
+ ei = (struct hpfs_inode_info *)kmem_cache_alloc(hpfs_inode_cachep, SLAB_KERNEL);
+ if (!ei)
+ return NULL;
+ return &ei->vfs_inode;
+}
+
+static void hpfs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(hpfs_inode_cachep, hpfs_i(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR) {
+ init_MUTEX(&ei->i_sem);
+ inode_init_once(&ei->vfs_inode);
+ }
+}
+
+static int init_inodecache(void)
+{
+ hpfs_inode_cachep = kmem_cache_create("hpfs_inode_cache",
+ sizeof(struct hpfs_inode_info),
+ 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (hpfs_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(hpfs_inode_cachep))
+ printk(KERN_INFO "hpfs_inode_cache: not all structures were freed\n");
+}
+
/* Super operations */
static struct super_operations hpfs_sops =
{
+ alloc_inode: hpfs_alloc_inode,
+ destroy_inode: hpfs_destroy_inode,
read_inode: hpfs_read_inode,
delete_inode: hpfs_delete_inode,
put_super: hpfs_put_super,
s->s_root->d_inode->i_atime = local_to_gmt(s, de->read_date);
s->s_root->d_inode->i_mtime = local_to_gmt(s, de->write_date);
s->s_root->d_inode->i_ctime = local_to_gmt(s, de->creation_date);
- s->s_root->d_inode->i_hpfs_ea_size = de->ea_size;
- s->s_root->d_inode->i_hpfs_parent_dir = s->s_root->d_inode->i_ino;
+ hpfs_i(s->s_root->d_inode)->i_ea_size = de->ea_size;
+ hpfs_i(s->s_root->d_inode)->i_parent_dir = s->s_root->d_inode->i_ino;
if (s->s_root->d_inode->i_size == -1) s->s_root->d_inode->i_size = 2048;
if (s->s_root->d_inode->i_blocks == -1) s->s_root->d_inode->i_blocks = 5;
}
static int __init init_hpfs_fs(void)
{
- return register_filesystem(&hpfs_fs_type);
+ int err = init_inodecache();
+ if (err)
+ goto out1;
+ err = register_filesystem(&hpfs_fs_type);
+ if (err)
+ goto out;
+ return 0;
+out:
+ destroy_inodecache();
+out1:
+ return err;
}
static void __exit exit_hpfs_fs(void)
{
unregister_filesystem(&hpfs_fs_type);
+ destroy_inodecache();
}
EXPORT_NO_SYMBOLS;
void presto_e3_journal_file_data(struct inode *inode)
{
#ifdef EXT3_JOURNAL_DATA_FL
- inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+ EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL;
#else
#warning You must have a facility to enable journaled writes for recovery!
#endif
void presto_obdfs_journal_file_data(struct inode *inode)
{
#ifdef EXT3_JOURNAL_DATA_FL
- inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+ EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL;
#else
#warning You must have a facility to enable journaled writes for recovery!
#endif
void presto_reiserfs_journal_file_data(struct inode *inode)
{
#ifdef EXT3_JOURNAL_DATA_FL
- inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+ EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL;
#else
#warning You must have a facility to enable journaled writes for recovery!
#endif
void nfs_zap_caches(struct inode *);
static void nfs_invalidate_inode(struct inode *);
+static struct inode *nfs_alloc_inode(struct super_block *sb);
+static void nfs_destroy_inode(struct inode *);
static void nfs_read_inode(struct inode *);
static void nfs_write_inode(struct inode *,int);
static void nfs_delete_inode(struct inode *);
static int nfs_show_options(struct seq_file *, struct vfsmount *);
static struct super_operations nfs_sops = {
+ alloc_inode: nfs_alloc_inode,
+ destroy_inode: nfs_destroy_inode,
read_inode: nfs_read_inode,
write_inode: nfs_write_inode,
delete_inode: nfs_delete_inode,
/*
* The "read_inode" function doesn't actually do anything:
- * the real data is filled in later in nfs_fhget. Here we
- * just mark the cache times invalid, and zero out i_mode
- * (the latter makes "nfs_refresh_inode" do the right thing
- * wrt pipe inodes)
+ * the real data is filled in later in nfs_fhget.
*/
static void
nfs_read_inode(struct inode * inode)
{
- inode->i_blksize = inode->i_sb->s_blocksize;
- inode->i_mode = 0;
- inode->i_rdev = NODEV;
- /* We can't support UPDATE_ATIME(), since the server will reset it */
- inode->i_flags |= S_NOATIME;
- INIT_LIST_HEAD(&inode->u.nfs_i.read);
- INIT_LIST_HEAD(&inode->u.nfs_i.dirty);
- INIT_LIST_HEAD(&inode->u.nfs_i.commit);
- INIT_LIST_HEAD(&inode->u.nfs_i.writeback);
- NFS_CACHEINV(inode);
- NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
- NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
}
static void
nfs_zap_caches(inode);
}
-/*
- * Fill in inode information from the fattr.
- */
-static void
-nfs_fill_inode(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr)
-{
- /*
- * Check whether the mode has been set, as we only want to
- * do this once. (We don't allow inodes to change types.)
- */
- if (inode->i_mode == 0) {
- NFS_FILEID(inode) = fattr->fileid;
- NFS_FSID(inode) = fattr->fsid;
- inode->i_mode = fattr->mode;
- /* Why so? Because we want revalidate for devices/FIFOs, and
- * that's precisely what we have in nfs_file_inode_operations.
- */
- inode->i_op = &nfs_file_inode_operations;
- if (S_ISREG(inode->i_mode)) {
- inode->i_fop = &nfs_file_operations;
- inode->i_data.a_ops = &nfs_file_aops;
- } else if (S_ISDIR(inode->i_mode)) {
- inode->i_op = &nfs_dir_inode_operations;
- inode->i_fop = &nfs_dir_operations;
- } else if (S_ISLNK(inode->i_mode))
- inode->i_op = &nfs_symlink_inode_operations;
- else
- init_special_inode(inode, inode->i_mode, fattr->rdev);
- memcpy(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh));
- }
- nfs_refresh_inode(inode, fattr);
-}
-
struct nfs_find_desc {
struct nfs_fh *fh;
struct nfs_fattr *fattr;
return 0;
if (NFS_FILEID(inode) != fattr->fileid)
return 0;
- if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0)
+ if (memcmp(NFS_FH(inode), fh, sizeof(struct nfs_fh)) != 0)
return 0;
/* Force an attribute cache update if inode->i_count == 0 */
if (!atomic_read(&inode->i_count))
return 1;
/* Has the filehandle changed? If so is the old one stale? */
- if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0 &&
+ if (memcmp(NFS_FH(inode), fh, sizeof(struct nfs_fh)) != 0 &&
__nfs_revalidate_inode(NFS_SERVER(inode),inode) == -ESTALE)
return 1;
if (!(inode = iget4(sb, ino, nfs_find_actor, &desc)))
goto out_no_inode;
- nfs_fill_inode(inode, fh, fattr);
+ if (NFS_NEW(inode)) {
+ __u64 new_size, new_mtime;
+ loff_t new_isize;
+ time_t new_atime;
+
+ /* We can't support UPDATE_ATIME(), since the server will reset it */
+ NFS_FLAGS(inode) &= ~NFS_INO_NEW;
+ NFS_FILEID(inode) = fattr->fileid;
+ NFS_FSID(inode) = fattr->fsid;
+ memcpy(NFS_FH(inode), fh, sizeof(struct nfs_fh));
+ inode->i_flags |= S_NOATIME;
+ inode->i_mode = fattr->mode;
+ /* Why so? Because we want revalidate for devices/FIFOs, and
+ * that's precisely what we have in nfs_file_inode_operations.
+ */
+ inode->i_op = &nfs_file_inode_operations;
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_fop = &nfs_file_operations;
+ inode->i_data.a_ops = &nfs_file_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &nfs_dir_inode_operations;
+ inode->i_fop = &nfs_dir_operations;
+ } else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &nfs_symlink_inode_operations;
+ else
+ init_special_inode(inode, inode->i_mode, fattr->rdev);
+
+ new_mtime = fattr->mtime;
+ new_size = fattr->size;
+ new_isize = nfs_size_to_loff_t(fattr->size);
+ new_atime = nfs_time_to_secs(fattr->atime);
+
+ NFS_READTIME(inode) = jiffies;
+ NFS_CACHE_CTIME(inode) = fattr->ctime;
+ inode->i_ctime = nfs_time_to_secs(fattr->ctime);
+ inode->i_atime = new_atime;
+ NFS_CACHE_MTIME(inode) = new_mtime;
+ inode->i_mtime = nfs_time_to_secs(new_mtime);
+ NFS_CACHE_ISIZE(inode) = new_size;
+ inode->i_size = new_isize;
+ inode->i_mode = fattr->mode;
+ inode->i_nlink = fattr->nlink;
+ inode->i_uid = fattr->uid;
+ inode->i_gid = fattr->gid;
+ if (fattr->valid & NFS_ATTR_FATTR_V3) {
+ /*
+ * report the blocks in 512byte units
+ */
+ inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
+ inode->i_blksize = inode->i_sb->s_blocksize;
+ } else {
+ inode->i_blocks = fattr->du.nfs2.blocks;
+ inode->i_blksize = fattr->du.nfs2.blocksize;
+ }
+ NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
+ memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+ } else
+ nfs_refresh_inode(inode, fattr);
dprintk("NFS: __nfs_fhget(%s/%Ld ct=%d)\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode),
extern int nfs_init_writepagecache(void);
extern int nfs_destroy_writepagecache(void);
+static kmem_cache_t * nfs_inode_cachep;
+
+static struct inode *nfs_alloc_inode(struct super_block *sb)
+{
+ struct nfs_inode *nfsi;
+ nfsi = (struct nfs_inode *)kmem_cache_alloc(nfs_inode_cachep, SLAB_KERNEL);
+ if (!nfsi)
+ return NULL;
+ nfsi->flags = NFS_INO_NEW;
+ /* do we need the next 4 lines? */
+ nfsi->hash_next = NULL;
+ nfsi->hash_prev = NULL;
+ nfsi->nextscan = 0;
+ nfsi->mm_cred = NULL;
+ return &nfsi->vfs_inode;
+}
+
+static void nfs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(nfs_inode_cachep, NFS_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct nfs_inode *nfsi = (struct nfs_inode *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR) {
+ inode_init_once(&nfsi->vfs_inode);
+ INIT_LIST_HEAD(&nfsi->read);
+ INIT_LIST_HEAD(&nfsi->dirty);
+ INIT_LIST_HEAD(&nfsi->commit);
+ INIT_LIST_HEAD(&nfsi->writeback);
+ nfsi->nread = 0;
+ nfsi->ndirty = 0;
+ nfsi->ncommit = 0;
+ nfsi->npages = 0;
+ }
+}
+
+int nfs_init_inodecache(void)
+{
+ nfs_inode_cachep = kmem_cache_create("nfs_inode_cache",
+ sizeof(struct nfs_inode),
+ 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (nfs_inode_cachep == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void nfs_destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(nfs_inode_cachep))
+ printk(KERN_INFO "nfs_inode_cache: not all structures were freed\n");
+}
+
/*
* Initialize NFS
*/
err = nfs_init_nfspagecache();
if (err)
- return err;
+ goto out4;
+
+ err = nfs_init_inodecache();
+ if (err)
+ goto out3;
err = nfs_init_readpagecache();
if (err)
- return err;
+ goto out2;
err = nfs_init_writepagecache();
if (err)
- return err;
+ goto out1;
#ifdef CONFIG_PROC_FS
rpc_proc_register(&nfs_rpcstat);
#endif
- return register_filesystem(&nfs_fs_type);
+ err = register_filesystem(&nfs_fs_type);
+ if (err)
+ goto out;
+ return 0;
+out:
+ rpc_proc_unregister("nfs");
+ nfs_destroy_writepagecache();
+out1:
+ nfs_destroy_readpagecache();
+out2:
+ nfs_destroy_inodecache();
+out3:
+ nfs_destroy_nfspagecache();
+out4:
+ return err;
}
static void __exit exit_nfs_fs(void)
{
nfs_destroy_writepagecache();
nfs_destroy_readpagecache();
+ nfs_destroy_inodecache();
nfs_destroy_nfspagecache();
#ifdef CONFIG_PROC_FS
rpc_proc_unregister("nfs");
nfs_mark_request_read(struct nfs_page *req)
{
struct inode *inode = req->wb_inode;
+ struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&nfs_wreq_lock);
- nfs_list_add_request(req, &inode->u.nfs_i.read);
- inode->u.nfs_i.nread++;
+ nfs_list_add_request(req, &nfsi->read);
+ nfsi->nread++;
__nfs_add_lru(&NFS_SERVER(inode)->lru_read, req);
spin_unlock(&nfs_wreq_lock);
}
static int
nfs_readpage_async(struct file *file, struct inode *inode, struct page *page)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *new;
new = nfs_create_request(file, inode, page, 0, PAGE_CACHE_SIZE);
return PTR_ERR(new);
nfs_mark_request_read(new);
- if (inode->u.nfs_i.nread >= NFS_SERVER(inode)->rpages ||
+ if (nfsi->nread >= NFS_SERVER(inode)->rpages ||
page_index(page) == (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)
nfs_pagein_inode(inode, 0, 0);
return 0;
int
nfs_scan_lru_read_timeout(struct nfs_server *server, struct list_head *dst)
{
- struct inode *inode;
+ struct nfs_inode *nfsi;
int npages;
npages = nfs_scan_lru_timeout(&server->lru_read, dst, server->rpages);
if (npages) {
- inode = nfs_list_entry(dst->next)->wb_inode;
- inode->u.nfs_i.nread -= npages;
+ nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+ nfsi->nread -= npages;
}
return npages;
}
int
nfs_scan_lru_read(struct nfs_server *server, struct list_head *dst)
{
- struct inode *inode;
+ struct nfs_inode *nfsi;
int npages;
npages = nfs_scan_lru(&server->lru_read, dst, server->rpages);
if (npages) {
- inode = nfs_list_entry(dst->next)->wb_inode;
- inode->u.nfs_i.nread -= npages;
+ nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+ nfsi->nread -= npages;
}
return npages;
}
static int
nfs_scan_read(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
int res;
- res = nfs_scan_list(&inode->u.nfs_i.read, dst, NULL, idx_start, npages);
- inode->u.nfs_i.nread -= res;
- if ((inode->u.nfs_i.nread == 0) != list_empty(&inode->u.nfs_i.read))
+ res = nfs_scan_list(&nfsi->read, dst, NULL, idx_start, npages);
+ nfsi->nread -= res;
+ if ((nfsi->nread == 0) != list_empty(&nfsi->read))
printk(KERN_ERR "NFS: desynchronized value of nfs_i.nread.\n");
return res;
}
static inline void
nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
if (!list_empty(&req->wb_hash))
return;
if (!NFS_WBACK_BUSY(req))
printk(KERN_ERR "NFS: unlocked request attempted hashed!\n");
- if (list_empty(&inode->u.nfs_i.writeback))
+ if (list_empty(&nfsi->writeback))
igrab(inode);
- inode->u.nfs_i.npages++;
- list_add(&req->wb_hash, &inode->u.nfs_i.writeback);
+ nfsi->npages++;
+ list_add(&req->wb_hash, &nfsi->writeback);
req->wb_count++;
}
static inline void
nfs_inode_remove_request(struct nfs_page *req)
{
+ struct nfs_inode *nfsi;
struct inode *inode;
spin_lock(&nfs_wreq_lock);
if (list_empty(&req->wb_hash)) {
inode = req->wb_inode;
list_del(&req->wb_hash);
INIT_LIST_HEAD(&req->wb_hash);
- inode->u.nfs_i.npages--;
- if ((inode->u.nfs_i.npages == 0) != list_empty(&inode->u.nfs_i.writeback))
+ nfsi = NFS_I(inode);
+ nfsi->npages--;
+ if ((nfsi->npages == 0) != list_empty(&nfsi->writeback))
printk(KERN_ERR "NFS: desynchronized value of nfs_i.npages.\n");
- if (list_empty(&inode->u.nfs_i.writeback)) {
+ if (list_empty(&nfsi->writeback)) {
spin_unlock(&nfs_wreq_lock);
iput(inode);
} else
static inline struct nfs_page *
_nfs_find_request(struct inode *inode, struct page *page)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
struct list_head *head, *next;
- head = &inode->u.nfs_i.writeback;
+ head = &nfsi->writeback;
next = head->next;
while (next != head) {
struct nfs_page *req = nfs_inode_wb_entry(next);
nfs_mark_request_dirty(struct nfs_page *req)
{
struct inode *inode = req->wb_inode;
+ struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&nfs_wreq_lock);
- nfs_list_add_request(req, &inode->u.nfs_i.dirty);
- inode->u.nfs_i.ndirty++;
+ nfs_list_add_request(req, &nfsi->dirty);
+ nfsi->ndirty++;
__nfs_del_lru(req);
__nfs_add_lru(&NFS_SERVER(inode)->lru_dirty, req);
spin_unlock(&nfs_wreq_lock);
static inline int
nfs_dirty_request(struct nfs_page *req)
{
- struct inode *inode = req->wb_inode;
- return !list_empty(&req->wb_list) && req->wb_list_head == &inode->u.nfs_i.dirty;
+ struct nfs_inode *nfsi = NFS_I(req->wb_inode);
+ return !list_empty(&req->wb_list) && req->wb_list_head == &nfsi->dirty;
}
#ifdef CONFIG_NFS_V3
nfs_mark_request_commit(struct nfs_page *req)
{
struct inode *inode = req->wb_inode;
+ struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&nfs_wreq_lock);
- nfs_list_add_request(req, &inode->u.nfs_i.commit);
- inode->u.nfs_i.ncommit++;
+ nfs_list_add_request(req, &nfsi->commit);
+ nfsi->ncommit++;
__nfs_del_lru(req);
__nfs_add_lru(&NFS_SERVER(inode)->lru_commit, req);
spin_unlock(&nfs_wreq_lock);
static int
nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
struct list_head *p, *head;
unsigned long idx_end;
unsigned int res = 0;
idx_end = idx_start + npages - 1;
spin_lock(&nfs_wreq_lock);
- head = &inode->u.nfs_i.writeback;
+ head = &nfsi->writeback;
p = head->next;
while (p != head) {
unsigned long pg_idx;
int
nfs_scan_lru_dirty_timeout(struct nfs_server *server, struct list_head *dst)
{
- struct inode *inode;
+ struct nfs_inode *nfsi;
int npages;
npages = nfs_scan_lru_timeout(&server->lru_dirty, dst, server->wpages);
if (npages) {
- inode = nfs_list_entry(dst->next)->wb_inode;
- inode->u.nfs_i.ndirty -= npages;
+ nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+ nfsi->ndirty -= npages;
}
return npages;
}
int
nfs_scan_lru_dirty(struct nfs_server *server, struct list_head *dst)
{
- struct inode *inode;
+ struct nfs_inode *nfsi;
int npages;
npages = nfs_scan_lru(&server->lru_dirty, dst, server->wpages);
if (npages) {
- inode = nfs_list_entry(dst->next)->wb_inode;
- inode->u.nfs_i.ndirty -= npages;
+ nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+ nfsi->ndirty -= npages;
}
return npages;
}
static int
nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
int res;
- res = nfs_scan_list(&inode->u.nfs_i.dirty, dst, file, idx_start, npages);
- inode->u.nfs_i.ndirty -= res;
- if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty))
+ res = nfs_scan_list(&nfsi->dirty, dst, file, idx_start, npages);
+ nfsi->ndirty -= res;
+ if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty))
printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
return res;
}
int
nfs_scan_lru_commit_timeout(struct nfs_server *server, struct list_head *dst)
{
- struct inode *inode;
+ struct nfs_inode *nfsi;
int npages;
npages = nfs_scan_lru_timeout(&server->lru_commit, dst, 1);
if (npages) {
- inode = nfs_list_entry(dst->next)->wb_inode;
- npages += nfs_scan_list(&inode->u.nfs_i.commit, dst, NULL, 0, 0);
- inode->u.nfs_i.ncommit -= npages;
+ nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+ npages += nfs_scan_list(&nfsi->commit, dst, NULL, 0, 0);
+ nfsi->ncommit -= npages;
}
return npages;
}
int
nfs_scan_lru_commit(struct nfs_server *server, struct list_head *dst)
{
- struct inode *inode;
+ struct nfs_inode *nfsi;
int npages;
npages = nfs_scan_lru(&server->lru_commit, dst, 1);
if (npages) {
- inode = nfs_list_entry(dst->next)->wb_inode;
- npages += nfs_scan_list(&inode->u.nfs_i.commit, dst, NULL, 0, 0);
- inode->u.nfs_i.ncommit -= npages;
+ nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode);
+ npages += nfs_scan_list(&nfsi->commit, dst, NULL, 0, 0);
+ nfsi->ncommit -= npages;
}
return npages;
}
static int
nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
int res;
- res = nfs_scan_list(&inode->u.nfs_i.commit, dst, file, idx_start, npages);
- inode->u.nfs_i.ncommit -= res;
- if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit))
+ res = nfs_scan_list(&nfsi->commit, dst, file, idx_start, npages);
+ nfsi->ncommit -= res;
+ if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit))
printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
return res;
}
{
unsigned int dirty, wpages;
- dirty = inode->u.nfs_i.ndirty;
+ dirty = NFS_I(inode)->ndirty;
wpages = NFS_SERVER(inode)->wpages;
#ifdef CONFIG_NFS_V3
if (NFS_PROTO(inode)->version == 2) {
static int
nfs_flush_one(struct list_head *head, struct inode *inode, int how)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
struct rpc_clnt *clnt = NFS_CLIENT(inode);
struct nfs_write_data *data;
struct rpc_task *task;
if (nfsvers < 3)
data->args.stable = NFS_FILE_SYNC;
else if (stable) {
- if (!inode->u.nfs_i.ncommit)
+ if (!nfsi->ncommit)
data->args.stable = NFS_FILE_SYNC;
else
data->args.stable = NFS_DATA_SYNC;
static void qnx4_clear_inode(struct inode *inode)
{
- struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i;
-
- memset(qnx4_ino->i_reserved, 0, sizeof qnx4_ino->i_reserved);
- qnx4_ino->i_size = 0;
- qnx4_ino->i_num_xtnts = 0;
- qnx4_ino->i_mode = 0;
- qnx4_ino->i_status = 0;
+ struct qnx4_inode_entry *qnx4_ino = qnx4_raw_inode(inode);
+ /* What for? */
+ memset(qnx4_ino->di_fname, 0, sizeof qnx4_ino->di_fname);
+ qnx4_ino->di_size = 0;
+ qnx4_ino->di_num_xtnts = 0;
+ qnx4_ino->di_mode = 0;
+ qnx4_ino->di_status = 0;
}
void qnx4_free_inode(struct inode *inode)
for (i = 0; i < 7; i++) {
rc = sync_block(inode,
- (unsigned short *) inode->u.qnx4_i.i_first_xtnt.xtnt_blk + i, wait);
+ (unsigned short *) qnx4_raw_inode(inode)->di_first_xtnt.xtnt_blk + i, wait);
if (rc > 0)
break;
if (rc)
static struct super_block *qnx4_read_super(struct super_block *, void *, int);
static void qnx4_put_super(struct super_block *sb);
+static struct inode *qnx4_alloc_inode(struct super_block *sb);
+static void qnx4_destroy_inode(struct inode *inode);
static void qnx4_read_inode(struct inode *);
static int qnx4_remount(struct super_block *sb, int *flags, char *data);
static int qnx4_statfs(struct super_block *, struct statfs *);
static struct super_operations qnx4_sops =
{
+ alloc_inode: qnx4_alloc_inode,
+ destroy_inode: qnx4_destroy_inode,
read_inode: qnx4_read_inode,
#ifdef CONFIG_QNX4FS_RW
write_inode: qnx4_write_inode,
unsigned long block = 0;
struct buffer_head *bh = 0;
struct qnx4_xblk *xblk = 0;
- struct qnx4_inode_info *qnx4_inode = &inode->u.qnx4_i;
- qnx4_nxtnt_t nxtnt = le16_to_cpu(qnx4_inode->i_num_xtnts);
+ struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode);
+ qnx4_nxtnt_t nxtnt = le16_to_cpu(qnx4_inode->di_num_xtnts);
- if ( iblock < le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_size) ) {
+ if ( iblock < le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size) ) {
// iblock is in the first extent. This is easy.
- block = le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_blk) + iblock - 1;
+ block = le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_blk) + iblock - 1;
} else {
// iblock is beyond first extent. We have to follow the extent chain.
- i_xblk = le32_to_cpu(qnx4_inode->i_xblk);
- offset = iblock - le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_size);
+ i_xblk = le32_to_cpu(qnx4_inode->di_xblk);
+ offset = iblock - le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size);
ix = 0;
while ( --nxtnt > 0 ) {
if ( ix == 0 ) {
{
return block_read_full_page(page,qnx4_get_block);
}
-static int qnx4_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+static int qnx4_prepare_write(struct file *file, struct page *page,
+ unsigned from, unsigned to)
{
- return cont_prepare_write(page,from,to,qnx4_get_block,
- &page->mapping->host->u.qnx4_i.mmu_private);
+ struct qnx4_inode_info *qnx4_inode = qnx4_i(page->mapping->host);
+ return cont_prepare_write(page, from, to, qnx4_get_block,
+ &qnx4_inode->mmu_private);
}
static int qnx4_bmap(struct address_space *mapping, long block)
{
struct qnx4_inode_entry *raw_inode;
int block, ino;
struct super_block *sb = inode->i_sb;
+ struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode);
ino = inode->i_ino;
inode->i_mode = 0;
inode->i_blocks = le32_to_cpu(raw_inode->di_first_xtnt.xtnt_size);
inode->i_blksize = QNX4_DIR_ENTRY_SIZE;
- memcpy(&inode->u.qnx4_i, (struct qnx4_inode_info *) raw_inode, QNX4_DIR_ENTRY_SIZE);
+ memcpy(qnx4_inode, raw_inode, QNX4_DIR_ENTRY_SIZE);
if (S_ISREG(inode->i_mode)) {
inode->i_op = &qnx4_file_inode_operations;
inode->i_fop = &qnx4_file_operations;
inode->i_mapping->a_ops = &qnx4_aops;
- inode->u.qnx4_i.mmu_private = inode->i_size;
+ qnx4_i(inode)->mmu_private = inode->i_size;
} else if (S_ISDIR(inode->i_mode)) {
inode->i_op = &qnx4_dir_inode_operations;
inode->i_fop = &qnx4_dir_operations;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &page_symlink_inode_operations;
inode->i_mapping->a_ops = &qnx4_aops;
- inode->u.qnx4_i.mmu_private = inode->i_size;
+ qnx4_i(inode)->mmu_private = inode->i_size;
} else
printk("qnx4: bad inode %d on dev %s\n",ino,sb->s_id);
brelse(bh);
}
+static kmem_cache_t *qnx4_inode_cachep;
+
+static struct inode *qnx4_alloc_inode(struct super_block *sb)
+{
+ struct qnx4_inode_info *ei;
+ ei = kmem_cache_alloc(qnx4_inode_cachep, SLAB_KERNEL);
+ if (!ei)
+ return NULL;
+ return &ei->vfs_inode;
+}
+
+static void qnx4_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(qnx4_inode_cachep, qnx4_i(inode));
+}
+
+static void init_once(void *foo, kmem_cache_t * cachep,
+ unsigned long flags)
+{
+ struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ inode_init_once(&ei->vfs_inode);
+}
+
+static int init_inodecache(void)
+{
+ qnx4_inode_cachep = kmem_cache_create("qnx4_inode_cache",
+ sizeof(struct qnx4_inode_info),
+ 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (qnx4_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(qnx4_inode_cachep))
+ printk(KERN_INFO
+ "qnx4_inode_cache: not all structures were freed\n");
+}
+
static DECLARE_FSTYPE_DEV(qnx4_fs_type, "qnx4", qnx4_read_super);
static int __init init_qnx4_fs(void)
{
+ int err;
+
+ err = init_inodecache();
+ if (err)
+ return err;
+
+ err = register_filesystem(&qnx4_fs_type);
+ if (err) {
+ destroy_inodecache();
+ return err;
+ }
+
printk("QNX4 filesystem 0.2.2 registered.\n");
- return register_filesystem(&qnx4_fs_type);
+ return 0;
}
static void __exit exit_qnx4_fs(void)
{
unregister_filesystem(&qnx4_fs_type);
+ destroy_inodecache();
}
EXPORT_NO_SYMBOLS;
unlock_super (sb);
return (unsigned)-1;
}
- if (fragment < inode->u.ufs_i.i_lastfrag) {
+ if (fragment < UFS_I(inode)->i_lastfrag) {
UFSD(("EXIT (ALREADY ALLOCATED)\n"))
unlock_super (sb);
return 0;
*p = cpu_to_fs32(sb, result);
*err = 0;
inode->i_blocks += count << uspi->s_nspfshift;
- inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count);
+ UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
NULLIFY_FRAGMENTS
}
unlock_super(sb);
if (result) {
*err = 0;
inode->i_blocks += count << uspi->s_nspfshift;
- inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count);
+ UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
NULLIFY_FRAGMENTS
unlock_super(sb);
UFSD(("EXIT, result %u\n", result))
*p = cpu_to_fs32(sb, result);
*err = 0;
inode->i_blocks += count << uspi->s_nspfshift;
- inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count);
+ UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
NULLIFY_FRAGMENTS
unlock_super(sb);
if (newcount < request)
* For other inodes, search forward from the parent directory's block
* group to find a free inode.
*/
-struct inode * ufs_new_inode (const struct inode * dir, int mode)
+struct inode * ufs_new_inode(struct inode * dir, int mode)
{
struct super_block * sb;
struct ufs_sb_private_info * uspi;
struct ufs_cylinder_group * ucg;
struct inode * inode;
unsigned cg, bit, i, j, start;
+ struct ufs_inode_info *ufsi;
UFSD(("ENTER\n"))
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
+ ufsi = UFS_I(inode);
uspi = sb->u.ufs_sb.s_uspi;
usb1 = ubh_get_usb_first(USPI_UBH);
inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
- inode->u.ufs_i.i_flags = dir->u.ufs_i.i_flags;
- inode->u.ufs_i.i_lastfrag = 0;
+ ufsi->i_flags = UFS_I(dir)->i_flags;
+ ufsi->i_lastfrag = 0;
+ ufsi->i_gen = 0;
+ ufsi->i_shadow = 0;
+ ufsi->i_osync = 0;
+ ufsi->i_oeftflag = 0;
+ memset(&ufsi->i_u1, 0, sizeof(ufsi->i_u1));
insert_inode_hash(inode);
mark_inode_dirty(inode);
int ufs_frag_map(struct inode *inode, int frag)
{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
struct super_block *sb = inode->i_sb;
struct ufs_sb_private_info *uspi = sb->u.ufs_sb.s_uspi;
int mask = uspi->s_apbmask>>uspi->s_fpbshift;
p = offsets;
lock_kernel();
- block = inode->u.ufs_i.i_u1.i_data[*p++];
+ block = ufsi->i_u1.i_data[*p++];
if (!block)
goto out;
while (--depth) {
unsigned int fragment, unsigned int new_fragment,
unsigned int required, int *err, int metadata, long *phys, int *new)
{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
struct super_block * sb;
struct ufs_sb_private_info * uspi;
struct buffer_head * result;
uspi = sb->u.ufs_sb.s_uspi;
block = ufs_fragstoblks (fragment);
blockoff = ufs_fragnum (fragment);
- p = inode->u.ufs_i.i_u1.i_data + block;
+ p = ufsi->i_u1.i_data + block;
goal = 0;
repeat:
tmp = fs32_to_cpu(sb, *p);
- lastfrag = inode->u.ufs_i.i_lastfrag;
+ lastfrag = ufsi->i_lastfrag;
if (tmp && fragment < lastfrag) {
if (metadata) {
result = sb_getblk(sb, uspi->s_sbbase + tmp + blockoff);
* We must reallocate last allocated block
*/
if (lastblockoff) {
- p2 = inode->u.ufs_i.i_u1.i_data + lastblock;
+ p2 = ufsi->i_u1.i_data + lastblock;
tmp = ufs_new_fragments (inode, p2, lastfrag,
fs32_to_cpu(sb, *p2), uspi->s_fpb - lastblockoff, err);
if (!tmp) {
- if (lastfrag != inode->u.ufs_i.i_lastfrag)
+ if (lastfrag != ufsi->i_lastfrag)
goto repeat;
else
return NULL;
}
- lastfrag = inode->u.ufs_i.i_lastfrag;
+ lastfrag = ufsi->i_lastfrag;
}
- goal = fs32_to_cpu(sb, inode->u.ufs_i.i_u1.i_data[lastblock]) + uspi->s_fpb;
+ goal = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock]) + uspi->s_fpb;
tmp = ufs_new_fragments (inode, p, fragment - blockoff,
goal, required + blockoff, err);
}
* We will allocate new block before last allocated block
*/
else /* (lastblock > block) */ {
- if (lastblock && (tmp = fs32_to_cpu(sb, inode->u.ufs_i.i_u1.i_data[lastblock-1])))
+ if (lastblock && (tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock-1])))
goal = tmp + uspi->s_fpb;
tmp = ufs_new_fragments (inode, p, fragment - blockoff,
goal, uspi->s_fpb, err);
}
if (!tmp) {
if ((!blockoff && *p) ||
- (blockoff && lastfrag != inode->u.ufs_i.i_lastfrag))
+ (blockoff && lastfrag != ufsi->i_lastfrag))
goto repeat;
*err = -ENOSPC;
return NULL;
void ufs_read_inode (struct inode * inode)
{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
struct super_block * sb;
struct ufs_sb_private_info * uspi;
struct ufs_inode * ufs_inode;
inode->i_blocks = fs32_to_cpu(sb, ufs_inode->ui_blocks);
inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat) */
inode->i_version = ++event;
-
- inode->u.ufs_i.i_flags = fs32_to_cpu(sb, ufs_inode->ui_flags);
- inode->u.ufs_i.i_gen = fs32_to_cpu(sb, ufs_inode->ui_gen);
- inode->u.ufs_i.i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow);
- inode->u.ufs_i.i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag);
- inode->u.ufs_i.i_lastfrag = (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift;
+ ufsi->i_flags = fs32_to_cpu(sb, ufs_inode->ui_flags);
+ ufsi->i_gen = fs32_to_cpu(sb, ufs_inode->ui_gen);
+ ufsi->i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow);
+ ufsi->i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag);
+ ufsi->i_lastfrag = (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
;
else if (inode->i_blocks) {
for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++)
- inode->u.ufs_i.i_u1.i_data[i] = ufs_inode->ui_u2.ui_addr.ui_db[i];
+ ufsi->i_u1.i_data[i] = ufs_inode->ui_u2.ui_addr.ui_db[i];
}
else {
for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++)
- inode->u.ufs_i.i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i];
+ ufsi->i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i];
}
-
+ ufsi->i_osync = 0;
if (S_ISREG(inode->i_mode)) {
inode->i_op = &ufs_file_inode_operations;
static int ufs_update_inode(struct inode * inode, int do_sync)
{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
struct super_block * sb;
struct ufs_sb_private_info * uspi;
struct buffer_head * bh;
ufs_inode->ui_mtime.tv_sec = cpu_to_fs32(sb, inode->i_mtime);
ufs_inode->ui_mtime.tv_usec = 0;
ufs_inode->ui_blocks = cpu_to_fs32(sb, inode->i_blocks);
- ufs_inode->ui_flags = cpu_to_fs32(sb, inode->u.ufs_i.i_flags);
- ufs_inode->ui_gen = cpu_to_fs32(sb, inode->u.ufs_i.i_gen);
+ ufs_inode->ui_flags = cpu_to_fs32(sb, ufsi->i_flags);
+ ufs_inode->ui_gen = cpu_to_fs32(sb, ufsi->i_gen);
if ((flags & UFS_UID_MASK) == UFS_UID_EFT) {
- ufs_inode->ui_u3.ui_sun.ui_shadow = cpu_to_fs32(sb, inode->u.ufs_i.i_shadow);
- ufs_inode->ui_u3.ui_sun.ui_oeftflag = cpu_to_fs32(sb, inode->u.ufs_i.i_oeftflag);
+ ufs_inode->ui_u3.ui_sun.ui_shadow = cpu_to_fs32(sb, ufsi->i_shadow);
+ ufs_inode->ui_u3.ui_sun.ui_oeftflag = cpu_to_fs32(sb, ufsi->i_oeftflag);
}
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
ufs_inode->ui_u2.ui_addr.ui_db[0] = cpu_to_fs32(sb, kdev_t_to_nr(inode->i_rdev));
else if (inode->i_blocks) {
for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++)
- ufs_inode->ui_u2.ui_addr.ui_db[i] = inode->u.ufs_i.i_u1.i_data[i];
+ ufs_inode->ui_u2.ui_addr.ui_db[i] = ufsi->i_u1.i_data[i];
}
else {
for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++)
- ufs_inode->ui_u2.ui_symlink[i] = inode->u.ufs_i.i_u1.i_symlink[i];
+ ufs_inode->ui_u2.ui_symlink[i] = ufsi->i_u1.i_symlink[i];
}
if (!inode->i_nlink)
void ufs_delete_inode (struct inode * inode)
{
- /*inode->u.ufs_i.i_dtime = CURRENT_TIME;*/
+ /*UFS_I(inode)->i_dtime = CURRENT_TIME;*/
lock_kernel();
mark_inode_dirty(inode);
ufs_update_inode(inode, IS_SYNC(inode));
} else {
/* fast symlink */
inode->i_op = &ufs_fast_symlink_inode_operations;
- memcpy((char*)&inode->u.ufs_i.i_u1.i_data,symname,l);
+ memcpy((char*)&UFS_I(inode)->i_u1.i_data,symname,l);
inode->i_size = l-1;
}
mark_inode_dirty(inode);
return 0;
}
+static kmem_cache_t * ufs_inode_cachep;
+
+static struct inode *ufs_alloc_inode(struct super_block *sb)
+{
+ struct ufs_inode_info *ei;
+ ei = (struct ufs_inode_info *)kmem_cache_alloc(ufs_inode_cachep, SLAB_KERNEL);
+ if (!ei)
+ return NULL;
+ return &ei->vfs_inode;
+}
+
+static void ufs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(ufs_inode_cachep, UFS_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct ufs_inode_info *ei = (struct ufs_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ inode_init_once(&ei->vfs_inode);
+}
+
+static int init_inodecache(void)
+{
+ ufs_inode_cachep = kmem_cache_create("ufs_inode_cache",
+ sizeof(struct ufs_inode_info),
+ 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (ufs_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(ufs_inode_cachep))
+ printk(KERN_INFO "ufs_inode_cache: not all structures were freed\n");
+}
+
static struct super_operations ufs_super_ops = {
+ alloc_inode: ufs_alloc_inode,
+ destroy_inode: ufs_destroy_inode,
read_inode: ufs_read_inode,
write_inode: ufs_write_inode,
delete_inode: ufs_delete_inode,
static int __init init_ufs_fs(void)
{
- return register_filesystem(&ufs_fs_type);
+ int err = init_inodecache();
+ if (err)
+ goto out1;
+ err = register_filesystem(&ufs_fs_type);
+ if (err)
+ goto out;
+ return 0;
+out:
+ destroy_inodecache();
+out1:
+ return err;
}
static void __exit exit_ufs_fs(void)
{
unregister_filesystem(&ufs_fs_type);
+ destroy_inodecache();
}
EXPORT_NO_SYMBOLS;
module_init(init_ufs_fs)
module_exit(exit_ufs_fs)
+MODULE_LICENSE("GPL");
*/
#include <linux/fs.h>
+#include <linux/ufs_fs.h>
static int ufs_readlink(struct dentry *dentry, char *buffer, int buflen)
{
- char *s = (char *)dentry->d_inode->u.ufs_i.i_u1.i_symlink;
- return vfs_readlink(dentry, buffer, buflen, s);
+ struct ufs_inode_info *p = UFS_I(dentry->d_inode);
+ return vfs_readlink(dentry, buffer, buflen, (char*)p->i_u1.i_symlink);
}
static int ufs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
- char *s = (char *)dentry->d_inode->u.ufs_i.i_u1.i_symlink;
- return vfs_follow_link(nd, s);
+ struct ufs_inode_info *p = UFS_I(dentry->d_inode);
+ return vfs_follow_link(nd, (char*)p->i_u1.i_symlink);
}
struct inode_operations ufs_fast_symlink_inode_operations = {
static int ufs_trunc_direct (struct inode * inode)
{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
struct super_block * sb;
struct ufs_sb_private_info * uspi;
struct buffer_head * bh;
retry = 0;
frag1 = DIRECT_FRAGMENT;
- frag4 = min_t(u32, UFS_NDIR_FRAGMENT, inode->u.ufs_i.i_lastfrag);
+ frag4 = min_t(u32, UFS_NDIR_FRAGMENT, ufsi->i_lastfrag);
frag2 = ((frag1 & uspi->s_fpbmask) ? ((frag1 | uspi->s_fpbmask) + 1) : frag1);
frag3 = frag4 & ~uspi->s_fpbmask;
block1 = block2 = 0;
/*
* Free first free fragments
*/
- p = inode->u.ufs_i.i_u1.i_data + ufs_fragstoblks (frag1);
+ p = ufsi->i_u1.i_data + ufs_fragstoblks (frag1);
tmp = fs32_to_cpu(sb, *p);
if (!tmp )
ufs_panic (sb, "ufs_trunc_direct", "internal error");
* Free whole blocks
*/
for (i = block1 ; i < block2; i++) {
- p = inode->u.ufs_i.i_u1.i_data + i;
+ p = ufsi->i_u1.i_data + i;
tmp = fs32_to_cpu(sb, *p);
if (!tmp)
continue;
/*
* Free last free fragments
*/
- p = inode->u.ufs_i.i_u1.i_data + ufs_fragstoblks (frag3);
+ p = ufsi->i_u1.i_data + ufs_fragstoblks (frag3);
tmp = fs32_to_cpu(sb, *p);
if (!tmp )
ufs_panic(sb, "ufs_truncate_direct", "internal error");
static int ufs_trunc_tindirect (struct inode * inode)
{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
struct super_block * sb;
struct ufs_sb_private_info * uspi;
struct ufs_buffer_head * tind_bh;
tindirect_block = (DIRECT_BLOCK > (UFS_NDADDR + uspi->s_apb + uspi->s_2apb))
? ((DIRECT_BLOCK - UFS_NDADDR - uspi->s_apb - uspi->s_2apb) >> uspi->s_2apbshift) : 0;
- p = inode->u.ufs_i.i_u1.i_data + UFS_TIND_BLOCK;
+ p = ufsi->i_u1.i_data + UFS_TIND_BLOCK;
if (!(tmp = fs32_to_cpu(sb, *p)))
return 0;
tind_bh = ubh_bread (sb, tmp, uspi->s_bsize);
void ufs_truncate (struct inode * inode)
{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
struct super_block * sb;
struct ufs_sb_private_info * uspi;
struct buffer_head * bh;
while (1) {
retry = ufs_trunc_direct(inode);
retry |= ufs_trunc_indirect (inode, UFS_IND_BLOCK,
- (u32 *) &inode->u.ufs_i.i_u1.i_data[UFS_IND_BLOCK]);
+ (u32 *) &ufsi->i_u1.i_data[UFS_IND_BLOCK]);
retry |= ufs_trunc_dindirect (inode, UFS_IND_BLOCK + uspi->s_apb,
- (u32 *) &inode->u.ufs_i.i_u1.i_data[UFS_DIND_BLOCK]);
+ (u32 *) &ufsi->i_u1.i_data[UFS_DIND_BLOCK]);
retry |= ufs_trunc_tindirect (inode);
if (!retry)
break;
}
}
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->u.ufs_i.i_lastfrag = DIRECT_FRAGMENT;
+ ufsi->i_lastfrag = DIRECT_FRAGMENT;
mark_inode_dirty(inode);
UFSD(("EXIT\n"))
}
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/locks.h>
+#include <linux/ufs_fs.h>
#include "swab.h"
#include "util.h"
if (!inode)
goto remove_name;
#ifdef UMSDOS_DEBUG_VERBOSE
-if (inode->u.umsdos_i.i_is_hlink)
+if (UMSDOS_I(inode)->i_is_hlink)
printk("umsdos_readdir_x: %s/%s already resolved, ino=%ld\n",
dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino);
#endif
entry.flags));
/* check whether to resolve a hard-link */
if ((entry.flags & UMSDOS_HLINK) &&
- !inode->u.umsdos_i.i_is_hlink) {
+ !UMSDOS_I(inode)->i_is_hlink) {
dret = umsdos_solve_hlink (dret);
ret = PTR_ERR(dret);
if (IS_ERR(dret))
/*
* This part of the initialization depends only on i_patched.
*/
- if (inode->u.umsdos_i.i_patched)
+ if (UMSDOS_I(inode)->i_patched)
goto out;
- inode->u.umsdos_i.i_patched = 1;
+ UMSDOS_I(inode)->i_patched = 1;
if (S_ISREG (entry->mode))
entry->mtime = inode->i_mtime;
inode->i_mode = entry->mode;
/* Check for a hard link */
if ((info.entry.flags & UMSDOS_HLINK) &&
- !inode->u.umsdos_i.i_is_hlink) {
+ !UMSDOS_I(inode)->i_is_hlink) {
dret = umsdos_solve_hlink (dret);
ret = PTR_ERR(dret);
if (IS_ERR(dret))
if (!IS_ERR(dentry_dst)) {
struct inode *inode = dentry_dst->d_inode;
if (inode) {
- inode->u.umsdos_i.i_is_hlink = 1;
+ UMSDOS_I(inode)->i_is_hlink = 1;
#ifdef UMSDOS_DEBUG_VERBOSE
printk ("umsdos_solve_hlink: resolved link %s/%s, ino=%ld\n",
dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name, inode->i_ino);
PRINTK ((KERN_DEBUG
"put inode %p (%lu) pos %lu count=%d\n"
,inode, inode->i_ino
- ,inode->u.umsdos_i.pos
+ ,UMSDOS_I(inode)->pos
,atomic_read(&inode->i_count)));
if (inode == pseudo_root) {
}
if (atomic_read(&inode->i_count) == 1)
- inode->u.umsdos_i.i_patched = 0;
+ UMSDOS_I(inode)->i_patched = 0;
}
void umsdos_setup_dir(struct dentry *dir)
{
struct inode *inode = dir->d_inode;
+ struct umsdos_inode_info *ui = UMSDOS_I(inode);
if (!S_ISDIR(inode->i_mode))
printk(KERN_ERR "umsdos_setup_dir: %s/%s not a dir!\n",
dir->d_parent->d_name.name, dir->d_name.name);
- init_waitqueue_head (&inode->u.umsdos_i.dir_info.p);
- inode->u.umsdos_i.dir_info.looking = 0;
- inode->u.umsdos_i.dir_info.creating = 0;
- inode->u.umsdos_i.dir_info.pid = 0;
+ init_waitqueue_head (&ui->dir_info.p);
+ ui->dir_info.looking = 0;
+ ui->dir_info.creating = 0;
+ ui->dir_info.pid = 0;
inode->i_op = &umsdos_rdir_inode_operations;
inode->i_fop = &umsdos_rdir_operations;
struct inode *inode = dentry->d_inode;
struct dentry *demd;
- inode->u.umsdos_i.pos = f_pos;
+ UMSDOS_I(inode)->pos = f_pos;
/* now check the EMD file */
demd = umsdos_get_emd_dentry(dentry->d_parent);
int offs;
Printk(("UMSDOS_notify_change: entering for %s/%s (%d)\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->u.umsdos_i.i_patched));
+dentry->d_parent->d_name.name, dentry->d_name.name, UMSDOS_I(inode)->i_patched));
if (inode->i_nlink == 0)
goto out;
/* Read only the start of the entry since we don't touch the name */
mapping = demd->d_inode->i_mapping;
- offs = inode->u.umsdos_i.pos & ~PAGE_CACHE_MASK;
+ offs = UMSDOS_I(inode)->pos & ~PAGE_CACHE_MASK;
ret = -ENOMEM;
- page=grab_cache_page(mapping,inode->u.umsdos_i.pos>>PAGE_CACHE_SHIFT);
+ page=grab_cache_page(mapping,UMSDOS_I(inode)->pos>>PAGE_CACHE_SHIFT);
if (!page)
goto out_dput;
ret=mapping->a_ops->prepare_write(NULL,page,offs,offs+UMSDOS_REC_SIZE);
static inline void u_sleep_on (struct inode *dir)
{
- sleep_on (&dir->u.umsdos_i.dir_info.p);
+ sleep_on (&UMSDOS_I(dir)->dir_info.p);
}
static inline void u_wake_up (struct inode *dir)
{
- wake_up (&dir->u.umsdos_i.dir_info.p);
+ wake_up (&UMSDOS_I(dir)->dir_info.p);
}
/*
{
int ret = 0;
- if (dir->u.umsdos_i.dir_info.creating
- && dir->u.umsdos_i.dir_info.pid != current->pid) {
- PRINTK (("creating && dir_info.pid=%lu, current->pid=%u\n", dir->u.umsdos_i.dir_info.pid, current->pid));
+ if (UMSDOS_I(dir)->dir_info.creating
+ && UMSDOS_I(dir)->dir_info.pid != current->pid) {
+ PRINTK (("creating && dir_info.pid=%lu, current->pid=%u\n", UMSDOS_I(dir)->dir_info.pid, current->pid));
u_sleep_on (dir);
ret = 1;
}
*/
static void umsdos_waitlookup (struct inode *dir)
{
- while (dir->u.umsdos_i.dir_info.looking) {
+ while (UMSDOS_I(dir)->dir_info.looking) {
u_sleep_on (dir);
}
}
* if we (the process) own the lock
*/
while (umsdos_waitcreate (dir) != 0);
- dir->u.umsdos_i.dir_info.creating++;
- dir->u.umsdos_i.dir_info.pid = current->pid;
+ UMSDOS_I(dir)->dir_info.creating++;
+ UMSDOS_I(dir)->dir_info.pid = current->pid;
umsdos_waitlookup (dir);
}
if (umsdos_waitcreate (dir1) == 0
&& umsdos_waitcreate (dir2) == 0) {
/* We own both now */
- dir1->u.umsdos_i.dir_info.creating++;
- dir1->u.umsdos_i.dir_info.pid = current->pid;
- dir2->u.umsdos_i.dir_info.creating++;
- dir2->u.umsdos_i.dir_info.pid = current->pid;
+ UMSDOS_I(dir1)->dir_info.creating++;
+ UMSDOS_I(dir1)->dir_info.pid = current->pid;
+ UMSDOS_I(dir2)->dir_info.creating++;
+ UMSDOS_I(dir2)->dir_info.pid = current->pid;
break;
}
}
void umsdos_startlookup (struct inode *dir)
{
while (umsdos_waitcreate (dir) != 0);
- dir->u.umsdos_i.dir_info.looking++;
+ UMSDOS_I(dir)->dir_info.looking++;
}
/*
*/
void umsdos_unlockcreate (struct inode *dir)
{
- dir->u.umsdos_i.dir_info.creating--;
- if (dir->u.umsdos_i.dir_info.creating < 0) {
- printk ("UMSDOS: dir->u.umsdos_i.dir_info.creating < 0: %d"
- ,dir->u.umsdos_i.dir_info.creating);
+ UMSDOS_I(dir)->dir_info.creating--;
+ if (UMSDOS_I(dir)->dir_info.creating < 0) {
+ printk ("UMSDOS: UMSDOS_I(dir)->dir_info.creating < 0: %d"
+ ,UMSDOS_I(dir)->dir_info.creating);
}
u_wake_up (dir);
}
*/
void umsdos_endlookup (struct inode *dir)
{
- dir->u.umsdos_i.dir_info.looking--;
- if (dir->u.umsdos_i.dir_info.looking < 0) {
- printk ("UMSDOS: dir->u.umsdos_i.dir_info.looking < 0: %d"
- ,dir->u.umsdos_i.dir_info.looking);
+ UMSDOS_I(dir)->dir_info.looking--;
+ if (UMSDOS_I(dir)->dir_info.looking < 0) {
+ printk ("UMSDOS: UMSDOS_I(dir)->dir_info.looking < 0: %d"
+ ,UMSDOS_I(dir)->dir_info.looking);
}
u_wake_up (dir);
}
goto cleanup;
}
/* mark the inode as a hardlink */
- oldinode->u.umsdos_i.i_is_hlink = 1;
+ UMSDOS_I(oldinode)->i_is_hlink = 1;
/*
* Capture the path to the hidden link.
* the dentry for its real name, not the visible name.
* N.B. make sure it's the hidden inode ...
*/
- if (!oldinode->u.umsdos_i.i_is_hlink)
+ if (!UMSDOS_I(oldinode)->i_is_hlink)
printk("UMSDOS_link: %s/%s hidden, ino=%ld not hlink??\n",
olddentry->d_parent->d_name.name,
olddentry->d_name.name, oldinode->i_ino);
#ifdef UMSDOS_PARANOIA
-if (!oldinode->u.umsdos_i.i_is_hlink)
+if (!UMSDOS_I(oldinode)->i_is_hlink)
printk("UMSDOS_link: %s/%s, ino=%ld, not marked as hlink!\n",
olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino);
#endif
dentry->d_parent->d_name.name, dentry->d_name.name));
/* only patch if needed (because we get called even for lookup
(not only rlookup) stuff sometimes, like in umsdos_covered() */
- if (dentry->d_inode->u.umsdos_i.i_patched == 0)
+ if (UMSDOS_I(dentry->d_inode)->i_patched == 0)
umsdos_patch_dentry_inode(dentry, 0);
}
/* We use generic_ffs so get it; include guards resolve the possible
mutually inclusion. */
#include <linux/bitops.h>
+#include <linux/compiler.h>
/*
* Some hacks to defeat gcc over-optimizations..
#define set_bit(nr, addr) (void)test_and_set_bit(nr, addr)
+#define __set_bit(nr, addr) (void)__test_and_set_bit(nr, addr)
+
/*
* clear_bit - Clears a bit in memory
* @nr: Bit to clear
#define clear_bit(nr, addr) (void)test_and_clear_bit(nr, addr)
+#define __clear_bit(nr, addr) (void)__test_and_clear_bit(nr, addr)
+
/*
* change_bit - Toggle a bit in memory
* @nr: Bit to clear
* It also implies a memory barrier.
*/
-static __inline__ int test_and_set_bit(int nr, void *addr)
+static inline int test_and_set_bit(int nr, void *addr)
{
unsigned int mask, retval;
unsigned long flags;
return retval;
}
+static inline int __test_and_set_bit(int nr, void *addr)
+{
+ unsigned int mask, retval;
+ unsigned int *adr = (unsigned int *)addr;
+
+ adr += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ retval = (mask & *adr) != 0;
+ *adr |= mask;
+ return retval;
+}
+
/*
* clear_bit() doesn't provide any barrier for the compiler.
*/
* It also implies a memory barrier.
*/
-static __inline__ int test_and_clear_bit(int nr, void *addr)
+static inline int test_and_clear_bit(int nr, void *addr)
{
unsigned int mask, retval;
unsigned long flags;
* but actually fail. You must protect multiple accesses with a lock.
*/
-static __inline__ int __test_and_clear_bit(int nr, void *addr)
+static inline int __test_and_clear_bit(int nr, void *addr)
{
unsigned int mask, retval;
unsigned int *adr = (unsigned int *)addr;
* It also implies a memory barrier.
*/
-static __inline__ int test_and_change_bit(int nr, void *addr)
+static inline int test_and_change_bit(int nr, void *addr)
{
unsigned int mask, retval;
unsigned long flags;
/* WARNING: non atomic and it can be reordered! */
-static __inline__ int __test_and_change_bit(int nr, void *addr)
+static inline int __test_and_change_bit(int nr, void *addr)
{
unsigned int mask, retval;
unsigned int *adr = (unsigned int *)addr;
* This routine doesn't need to be atomic.
*/
-static __inline__ int test_bit(int nr, const void *addr)
+static inline int test_bit(int nr, const void *addr)
{
unsigned int mask;
unsigned int *adr = (unsigned int *)addr;
* number. They differ in that the first function also inverts all bits
* in the input.
*/
-static __inline__ unsigned long cris_swapnwbrlz(unsigned long w)
+static inline unsigned long cris_swapnwbrlz(unsigned long w)
{
/* Let's just say we return the result in the same register as the
input. Saying we clobber the input but can return the result
return res;
}
-static __inline__ unsigned long cris_swapwbrlz(unsigned long w)
+static inline unsigned long cris_swapwbrlz(unsigned long w)
{
unsigned res;
__asm__ ("swapwbr %0 \n\t"
* ffz = Find First Zero in word. Undefined if no zero exists,
* so code should check against ~0UL first..
*/
-static __inline__ unsigned long ffz(unsigned long w)
+static inline unsigned long ffz(unsigned long w)
{
/* The generic_ffs function is used to avoid the asm when the
argument is a constant. */
* Somewhat like ffz but the equivalent of generic_ffs: in contrast to
* ffz we return the first one-bit *plus one*.
*/
-static __inline__ unsigned long ffs(unsigned long w)
+static inline unsigned long ffs(unsigned long w)
{
/* The generic_ffs function is used to avoid the asm when the
argument is a constant. */
* @offset: The bitnumber to start searching at
* @size: The maximum size to search
*/
-static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
+static inline int find_next_zero_bit (void * addr, int size, int offset)
{
unsigned long *p = ((unsigned long *) addr) + (offset >> 5);
unsigned long result = offset & ~31UL;
#define find_first_zero_bit(addr, size) \
find_next_zero_bit((addr), (size), 0)
+/*
+ * hweightN - returns the hamming weight of a N-bit word
+ * @x: the word to weigh
+ *
+ * The Hamming Weight of a number is the total number of bits set in it.
+ */
+
+#define hweight32(x) generic_hweight32(x)
+#define hweight16(x) generic_hweight16(x)
+#define hweight8(x) generic_hweight8(x)
+
#define ext2_set_bit test_and_set_bit
#define ext2_clear_bit test_and_clear_bit
#define ext2_test_bit test_bit
#define minix_test_bit(nr,addr) test_bit(nr,addr)
#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
-#endif /* __KERNEL__ */
+#if 0
+/* TODO: see below */
+#define sched_find_first_zero_bit(addr) find_first_zero_bit(addr, 168)
+
+#else
+/* TODO: left out pending where to put it.. (there are .h dependencies) */
+ /*
+ * Every architecture must define this function. It's the fastest
+ * way of searching a 168-bit bitmap where the first 128 bits are
+ * unlikely to be set. It's guaranteed that at least one of the 168
+ * bits is cleared.
+ */
+#if 0
+#if MAX_RT_PRIO != 128 || MAX_PRIO != 168
+# error update this function.
+#endif
+#else
+#define MAX_RT_PRIO 128
+#define MAX_PRIO 168
+#endif
+
+static inline int sched_find_first_zero_bit(char *bitmap)
+{
+ unsigned int *b = (unsigned int *)bitmap;
+ unsigned int rt;
+
+ rt = b[0] & b[1] & b[2] & b[3];
+ if (unlikely(rt != 0xffffffff))
+ return find_first_zero_bit(bitmap, MAX_RT_PRIO);
+
+ if (b[4] != ~0)
+ return ffz(b[4]) + MAX_RT_PRIO;
+ return ffz(b[5]) + 32 + MAX_RT_PRIO;
+}
+#undef MAX_PRIO
+#undef MAX_RT_PRIO
+#endif
+
+#endif /* __KERNEL__ */
#endif /* _CRIS_BITOPS_H */
typedef unsigned long elf_greg_t;
-/* These probably need fixing. */
-#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t))
+/* Note that NGREG is defined to ELF_NGREG in include/linux/elfcore.h, and is
+ thus exposed to user-space. */
+#define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t))
typedef elf_greg_t elf_gregset_t[ELF_NGREG];
/* A placeholder; CRIS does not have any fp regs. */
(_r)->r1 = 0; (_r)->r0 = 0; (_r)->mof = 0; (_r)->srp = 0; \
} while (0)
-#undef USE_ELF_CORE_DUMP
+#define USE_ELF_CORE_DUMP
+
+/* The additional layer below is because the stack pointer is missing in
+ the pt_regs struct, but needed in a core dump. pr_reg is a elf_gregset_t,
+ and should be filled in according to the layout of the user_regs_struct
+ struct; regs is a pt_regs struct. We dump all registers, though several are
+ obviously unnecessary. That way there's less need for intelligence at
+ the receiving end (i.e. gdb). */
+#define ELF_CORE_COPY_REGS(pr_reg, regs) \
+ pr_reg[0] = regs->r0; \
+ pr_reg[1] = regs->r1; \
+ pr_reg[2] = regs->r2; \
+ pr_reg[3] = regs->r3; \
+ pr_reg[4] = regs->r4; \
+ pr_reg[5] = regs->r5; \
+ pr_reg[6] = regs->r6; \
+ pr_reg[7] = regs->r7; \
+ pr_reg[8] = regs->r8; \
+ pr_reg[9] = regs->r9; \
+ pr_reg[10] = regs->r10; \
+ pr_reg[11] = regs->r11; \
+ pr_reg[12] = regs->r12; \
+ pr_reg[13] = regs->r13; \
+ pr_reg[14] = rdusp(); /* sp */ \
+ pr_reg[15] = regs->irp; /* pc */ \
+ pr_reg[16] = 0; /* p0 */ \
+ pr_reg[17] = rdvr(); /* vr */ \
+ pr_reg[18] = 0; /* p2 */ \
+ pr_reg[19] = 0; /* p3 */ \
+ pr_reg[20] = 0; /* p4 */ \
+ pr_reg[21] = (regs->dccr & 0xffff); /* ccr */ \
+ pr_reg[22] = 0; /* p6 */ \
+ pr_reg[23] = regs->mof; /* mof */ \
+ pr_reg[24] = 0; /* p8 */ \
+ pr_reg[25] = 0; /* ibr */ \
+ pr_reg[26] = 0; /* irp */ \
+ pr_reg[27] = regs->srp; /* srp */ \
+ pr_reg[28] = 0; /* bar */ \
+ pr_reg[29] = regs->dccr; /* dccr */ \
+ pr_reg[30] = 0; /* brp */ \
+ pr_reg[31] = rdusp(); /* usp */ \
+ pr_reg[32] = 0; /* csrinstr */ \
+ pr_reg[33] = 0; /* csraddr */ \
+ pr_reg[34] = 0; /* csrdata */
+
+
#define ELF_EXEC_PAGESIZE 8192
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
--- /dev/null
+/*
+ * ioctl defines for ethernet driver
+ *
+ * Copyright (c) 2001 Axis Communications AB
+ *
+ * Author: Mikael Starvik
+ *
+ */
+
+#ifndef _CRIS_ETHERNET_H
+#define _CRIS_ETHERNET_H
+#define SET_ETH_SPEED_AUTO SIOCDEVPRIVATE /* Auto neg speed */
+#define SET_ETH_SPEED_10 SIOCDEVPRIVATE+1 /* 10 Mbps */
+#define SET_ETH_SPEED_100 SIOCDEVPRIVATE+2 /* 100 Mbps. */
+#define SET_ETH_DUPLEX_AUTO SIOCDEVPRIVATE+3 /* Auto neg duplex */
+#define SET_ETH_DUPLEX_HALF SIOCDEVPRIVATE+4 /* Full duplex */
+#define SET_ETH_DUPLEX_FULL SIOCDEVPRIVATE+5 /* Half duplex */
+#endif /* _CRIS_ETHERNET_H */
/* the asm IRQ handler makes sure the causing IRQ is blocked, then it calls
* do_IRQ (with irq disabled still). after that it unblocks and jumps to
* ret_from_intr (entry.S)
+ *
+ * The reason the IRQ is blocked is to allow an sti() before the handler which
+ * will acknowledge the interrupt is run.
*/
#define BUILD_IRQ(nr,mask) \
"reti\n\t" \
"nop\n");
+/* This is subtle. The timer interrupt is crucial and it should not be disabled for
+ * too long. However, if it had been a normal interrupt as per BUILD_IRQ, it would
+ * have been BLOCK'ed, and then softirq's are run before we return here to UNBLOCK.
+ * If the softirq's take too much time to run, the timer irq won't run and the
+ * watchdog will kill us.
+ *
+ * Furthermore, if a lot of other irq's occur before we return here, the multiple_irq
+ * handler is run and it prioritizes the timer interrupt. However if we had BLOCK'ed
+ * it here, we would not get the multiple_irq at all.
+ *
+ * The non-blocking here is based on the knowledge that the timer interrupt is
+ * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will not
+ * be an sti() before the timer irq handler is run to acknowledge the interrupt.
+ */
+
+#define BUILD_TIMER_IRQ(nr,mask) \
+void IRQ_NAME(nr); \
+void sIRQ_NAME(nr); \
+void BAD_IRQ_NAME(nr); \
+__asm__ ( \
+ ".text\n\t" \
+ "IRQ" #nr "_interrupt:\n\t" \
+ SAVE_ALL \
+ "sIRQ" #nr "_interrupt:\n\t" /* shortcut for the multiple irq handler */ \
+ "moveq "#nr",$r10\n\t" \
+ "move.d $sp,$r11\n\t" \
+ "jsr do_IRQ\n\t" /* irq.c, r10 and r11 are arguments */ \
+ "moveq 0,$r9\n\t" /* make ret_from_intr realise we came from an irq */ \
+ "jump ret_from_intr\n\t" \
+ "bad_IRQ" #nr "_interrupt:\n\t" \
+ "push $r0\n\t" \
+ BLOCK_IRQ(mask,nr) \
+ "pop $r0\n\t" \
+ "reti\n\t" \
+ "nop\n");
#endif /* _ASM_IRQ_H */
* to arm and m68k I think)
*/
-#define virt_to_page(kaddr) (mem_map + (((unsigned long)kaddr - PAGE_OFFSET) >> PAGE_SHIFT))
-#define VALID_PAGE(page) ((page - mem_map) < max_mapnr)
+#define virt_to_page(kaddr) (mem_map + (((unsigned long)(kaddr) - PAGE_OFFSET) >> PAGE_SHIFT))
+#define VALID_PAGE(page) (((page) - mem_map) < max_mapnr)
+
+/* convert a page (based on mem_map and forward) to a physical address
+ * do this by figuring out the virtual address and then use __pa
+ */
+
+#define page_to_phys(page) __pa((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET)
/* from linker script */
* Allocate and free page tables.
*/
-extern __inline__ pgd_t *get_pgd_slow(void)
+static inline pgd_t *get_pgd_slow(void)
{
pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL);
return ret;
}
-extern __inline__ void free_pgd_slow(pgd_t *pgd)
+static inline void free_pgd_slow(pgd_t *pgd)
{
free_page((unsigned long)pgd);
}
-extern __inline__ pgd_t *get_pgd_fast(void)
+static inline pgd_t *get_pgd_fast(void)
{
unsigned long *ret;
return (pgd_t *)ret;
}
-extern __inline__ void free_pgd_fast(pgd_t *pgd)
+static inline void free_pgd_fast(pgd_t *pgd)
{
*(unsigned long *)pgd = (unsigned long) pgd_quicklist;
pgd_quicklist = (unsigned long *) pgd;
* HISTORY:
*
* $Log: pgtable.h,v $
+ * Revision 1.14 2001/12/10 03:08:50 bjornw
+ * Added pgtable_cache_init dummy
+ *
+ * Revision 1.13 2001/11/12 18:05:38 pkj
+ * Added declaration of paging_init().
+ *
* Revision 1.12 2001/08/11 00:28:00 bjornw
* PAGE_CHG_MASK and PAGE_NONE had somewhat untraditional values
*
* the CRIS page table tree.
*/
+extern void paging_init(void);
+
/* The cache doesn't need to be flushed when TLB entries change because
* the cache is mapped to physical memory, not virtual memory
*/
/*
* No page table caches to initialise
*/
-#define pgtable_cache_init() do { } while (0)
+#define pgtable_cache_init() do { } while (0)
#endif /* _CRIS_PGTABLE_H */
#define init_task (init_task_union.task)
#define init_stack (init_task_union.stack)
-#define cpu_relax() do { } while (0)
+#define cpu_relax() do { } while (0)
#endif /* __ASM_CRIS_PROCESSOR_H */
--- /dev/null
+#ifndef __ASM_CRIS_SCATTERLIST_H
+#define __ASM_CRIS_SCATTERLIST_H
+
+struct scatterlist {
+ char * address; /* Location data is to be transferred to */
+ unsigned int length;
+
+ /* The following is i386 highmem junk - not used by us */
+ struct page * page; /* Location for highmem page, if any */
+ unsigned int offset;/* for highmem, page offset */
+
+};
+
+/* i386 junk */
+
+#define ISA_DMA_THRESHOLD (0x1fffffff)
+
+#endif /* !(__ASM_CRIS_SCATTERLIST_H) */
static inline _syscall1(int,close,int,fd)
static inline _syscall1(int,_exit,int,exitcode)
static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
/* the following are just while developing the elinux port! */
* to write an integer number of pages.
*/
+/* User mode registers, used for core dumps. In order to keep ELF_NGREG
+ sensible we let all registers be 32 bits. The csr registers are included
+ for future use. */
+struct user_regs_struct {
+ unsigned long r0; /* General registers. */
+ unsigned long r1;
+ unsigned long r2;
+ unsigned long r3;
+ unsigned long r4;
+ unsigned long r5;
+ unsigned long r6;
+ unsigned long r7;
+ unsigned long r8;
+ unsigned long r9;
+ unsigned long r10;
+ unsigned long r11;
+ unsigned long r12;
+ unsigned long r13;
+ unsigned long sp; /* Stack pointer. */
+ unsigned long pc; /* Program counter. */
+ unsigned long p0; /* Constant zero (only 8 bits). */
+ unsigned long vr; /* Version register (only 8 bits). */
+ unsigned long p2; /* Reserved. */
+ unsigned long p3; /* Reserved. */
+ unsigned long p4; /* Constant zero (only 16 bits). */
+ unsigned long ccr; /* Condition code register (only 16 bits). */
+ unsigned long p6; /* Reserved. */
+ unsigned long mof; /* Multiply overflow register. */
+ unsigned long p8; /* Constant zero. */
+ unsigned long ibr; /* Not accessible. */
+ unsigned long irp; /* Not accessible. */
+ unsigned long srp; /* Subroutine return pointer. */
+ unsigned long bar; /* Not accessible. */
+ unsigned long dccr; /* Dword condition code register. */
+ unsigned long brp; /* Not accessible. */
+ unsigned long usp; /* User-mode stack pointer. Same as sp when
+ in user mode. */
+ unsigned long csrinstr; /* Internal status registers. */
+ unsigned long csraddr;
+ unsigned long csrdata;
+};
+
+
struct user {
- struct pt_regs regs; /* entire machine state */
+ struct user_regs_struct regs; /* entire machine state */
size_t u_tsize; /* text size (pages) */
size_t u_dsize; /* data size (pages) */
size_t u_ssize; /* stack size (pages) */
* into a single vector (CALL_FUNCTION_VECTOR) to save vector space.
* TLB, reschedule and local APIC vectors are performance-critical.
*
- * Vectors 0xf0-0xfa are free (reserved for future Linux use).
+ * Vectors 0xf0-0xf9 are free (reserved for future Linux use).
*/
#define SPURIOUS_APIC_VECTOR 0xff
#define ERROR_APIC_VECTOR 0xfe
#define INVALIDATE_TLB_VECTOR 0xfd
#define RESCHEDULE_VECTOR 0xfc
-#define CALL_FUNCTION_VECTOR 0xfb
+#define TASK_MIGRATION_VECTOR 0xfb
+#define CALL_FUNCTION_VECTOR 0xfa
/*
* Local APIC timer IRQ vector is on a different priority level,
#include <linux/types.h>
+#include <linux/affs_fs_i.h>
+
#define AFFS_SUPER_MAGIC 0xadff
struct affs_date;
#include <linux/a.out.h>
-// move this to linux/coda.h!!!
-#include <linux/time.h>
-
#define AFFS_CACHE_SIZE PAGE_SIZE
//#define AFFS_CACHE_SIZE (4*4)
unsigned char i_pad;
s32 i_parent; /* parent ino */
#endif
+ struct inode vfs_inode;
};
/* short cut to get to the affs specific inode data */
-#define AFFS_INODE (&inode->u.affs_i)
-#define AFFS_DIR (&dir->u.affs_i)
+static inline struct affs_inode_info *AFFS_I(struct inode *inode)
+{
+ return list_entry(inode, struct affs_inode_info, vfs_inode);
+}
#endif
static inline void
affs_lock_link(struct inode *inode)
{
- down(&AFFS_INODE->i_link_lock);
+ down(&AFFS_I(inode)->i_link_lock);
}
static inline void
affs_unlock_link(struct inode *inode)
{
- up(&AFFS_INODE->i_link_lock);
+ up(&AFFS_I(inode)->i_link_lock);
}
static inline void
affs_lock_dir(struct inode *inode)
{
- down(&AFFS_INODE->i_hash_lock);
+ down(&AFFS_I(inode)->i_hash_lock);
}
static inline void
affs_unlock_dir(struct inode *inode)
{
- up(&AFFS_INODE->i_hash_lock);
+ up(&AFFS_I(inode)->i_hash_lock);
}
static inline void
affs_lock_ext(struct inode *inode)
{
- down(&AFFS_INODE->i_ext_lock);
+ down(&AFFS_I(inode)->i_ext_lock);
}
static inline void
affs_unlock_ext(struct inode *inode)
{
- up(&AFFS_INODE->i_ext_lock);
+ up(&AFFS_I(inode)->i_ext_lock);
}
#ifdef __LITTLE_ENDIAN
#if defined(__linux__)
+#include <linux/time.h>
#define cdev_t u_quad_t
#ifndef __KERNEL__
#if !defined(_UQUAD_T_) && (!defined(__GLIBC__) || __GLIBC__ < 2)
unsigned int c_contcount; /* refcount for container file */
struct coda_cred c_cached_cred; /* credentials of cached perms */
unsigned int c_cached_perm; /* cached access permissions */
+ struct inode vfs_inode;
};
/* flags */
/* inode to cnode access functions */
-#define ITOC(inode) (&((inode)->u.coda_i))
+static inline struct coda_inode_info *ITOC(struct inode *inode)
+{
+ return list_entry(inode, struct coda_inode_info, vfs_inode);
+}
static __inline__ struct ViceFid *coda_i2f(struct inode *inode)
{
* Copyright 2000 (C) Stephen Rothwell
*/
+#include <linux/fs.h>
+
struct dnotify_struct {
struct dnotify_struct * dn_next;
int dn_magic;
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
-#ifdef _EFS_USE_GENERIC
-#define INODE_INFO(i) (struct efs_inode_info *) &((i)->u.generic_ip)
-#define SUPER_INFO(s) (struct efs_sb_info *) &((s)->u.generic_sbp)
-#else
-#define INODE_INFO(i) &((i)->u.efs_i)
+static inline struct efs_inode_info *INODE_INFO(struct inode *inode)
+{
+ return list_entry(inode, struct efs_inode_info, vfs_inode);
+}
#define SUPER_INFO(s) &((s)->u.efs_sb)
-#endif
extern struct inode_operations efs_dir_inode_operations;
extern struct file_operations efs_dir_operations;
int lastextent;
efs_extent extents[EFS_DIRECTEXTENTS];
+ struct inode vfs_inode;
};
#endif /* __EFS_FS_I_H__ */
#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
~EXT2_DIR_ROUND)
-#ifdef __KERNEL__
-/*
- * Function prototypes
- */
-
-/*
- * Ok, these declarations are also in <linux/kernel.h> but none of the
- * ext2 source programs needs to include it so they are duplicated here.
- */
-# define NORET_TYPE /**/
-# define ATTRIB_NORET __attribute__((noreturn))
-# define NORET_AND noreturn,
-
-/* balloc.c */
-extern int ext2_bg_has_super(struct super_block *sb, int group);
-extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
-extern int ext2_new_block (struct inode *, unsigned long,
- __u32 *, __u32 *, int *);
-extern void ext2_free_blocks (struct inode *, unsigned long,
- unsigned long);
-extern unsigned long ext2_count_free_blocks (struct super_block *);
-extern void ext2_check_blocks_bitmap (struct super_block *);
-extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
- unsigned int block_group,
- struct buffer_head ** bh);
-
-/* dir.c */
-extern int ext2_add_link (struct dentry *, struct inode *);
-extern ino_t ext2_inode_by_name(struct inode *, struct dentry *);
-extern int ext2_make_empty(struct inode *, struct inode *);
-extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct dentry *, struct page **);
-extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *);
-extern int ext2_empty_dir (struct inode *);
-extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **);
-extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *);
-
-/* fsync.c */
-extern int ext2_sync_file (struct file *, struct dentry *, int);
-extern int ext2_fsync_inode (struct inode *, int);
-
-/* ialloc.c */
-extern struct inode * ext2_new_inode (const struct inode *, int);
-extern void ext2_free_inode (struct inode *);
-extern unsigned long ext2_count_free_inodes (struct super_block *);
-extern void ext2_check_inodes_bitmap (struct super_block *);
-extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
-
-/* inode.c */
-extern void ext2_read_inode (struct inode *);
-extern void ext2_write_inode (struct inode *, int);
-extern void ext2_put_inode (struct inode *);
-extern void ext2_delete_inode (struct inode *);
-extern int ext2_sync_inode (struct inode *);
-extern void ext2_discard_prealloc (struct inode *);
-extern void ext2_truncate (struct inode *);
-
-/* ioctl.c */
-extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
- unsigned long);
-
-/* super.c */
-extern void ext2_error (struct super_block *, const char *, const char *, ...)
- __attribute__ ((format (printf, 3, 4)));
-extern NORET_TYPE void ext2_panic (struct super_block *, const char *,
- const char *, ...)
- __attribute__ ((NORET_AND format (printf, 3, 4)));
-extern void ext2_warning (struct super_block *, const char *, const char *, ...)
- __attribute__ ((format (printf, 3, 4)));
-extern void ext2_update_dynamic_rev (struct super_block *sb);
-extern void ext2_put_super (struct super_block *);
-extern void ext2_write_super (struct super_block *);
-extern int ext2_remount (struct super_block *, int *, char *);
-extern struct super_block * ext2_read_super (struct super_block *,void *,int);
-extern int ext2_statfs (struct super_block *, struct statfs *);
-
-/*
- * Inodes and files operations
- */
-
-/* dir.c */
-extern struct file_operations ext2_dir_operations;
-
-/* file.c */
-extern struct inode_operations ext2_file_inode_operations;
-extern struct file_operations ext2_file_operations;
-
-/* inode.c */
-extern struct address_space_operations ext2_aops;
-
-/* namei.c */
-extern struct inode_operations ext2_dir_inode_operations;
-
-/* symlink.c */
-extern struct inode_operations ext2_fast_symlink_inode_operations;
-
-#endif /* __KERNEL__ */
-
#endif /* _LINUX_EXT2_FS_H */
+++ /dev/null
-/*
- * linux/include/linux/ext2_fs_i.h
- *
- * Copyright (C) 1992, 1993, 1994, 1995
- * Remy Card (card@masi.ibp.fr)
- * Laboratoire MASI - Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * from
- *
- * linux/include/linux/minix_fs_i.h
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-#ifndef _LINUX_EXT2_FS_I
-#define _LINUX_EXT2_FS_I
-
-/*
- * second extended file system inode data in memory
- */
-struct ext2_inode_info {
- __u32 i_data[15];
- __u32 i_flags;
- __u32 i_faddr;
- __u8 i_frag_no;
- __u8 i_frag_size;
- __u16 i_osync;
- __u32 i_file_acl;
- __u32 i_dir_acl;
- __u32 i_dtime;
- __u32 i_block_group;
- __u32 i_next_alloc_block;
- __u32 i_next_alloc_goal;
- __u32 i_prealloc_block;
- __u32 i_prealloc_count;
- __u32 i_dir_start_lookup;
- int i_new_inode:1; /* Is a freshly allocated inode */
-};
-
-#endif /* _LINUX_EXT2_FS_I */
#ifdef __KERNEL__
#define EXT3_SB(sb) (&((sb)->u.ext3_sb))
-#define EXT3_I(inode) (&((inode)->u.ext3_i))
+static inline struct ext3_inode_info *EXT3_I(struct inode *inode)
+{
+ return list_entry(inode, struct ext3_inode_info, vfs_inode);
+}
#else
/* Assume that user mode programs are passing in an ext3fs superblock, not
* a kernel struct super_block. This will allow us to call the feature-test
#define EXT3_SB(sb) (sb)
#endif
-#define NEXT_ORPHAN(inode) (inode)->u.ext3_i.i_dtime
+#define NEXT_ORPHAN(inode) EXT3_I(inode)->i_dtime
/*
* Codes for operating systems
extern int ext3_sync_file (struct file *, struct dentry *, int);
/* ialloc.c */
-extern struct inode * ext3_new_inode (handle_t *, const struct inode *, int);
+extern struct inode * ext3_new_inode (handle_t *, struct inode *, int);
extern void ext3_free_inode (handle_t *, struct inode *);
extern struct inode * ext3_orphan_get (struct super_block *, ino_t);
extern unsigned long ext3_count_free_inodes (struct super_block *);
* by other means, so we have truncate_sem.
*/
struct rw_semaphore truncate_sem;
+ struct inode vfs_inode;
};
#endif /* _LINUX_EXT3_FS_I */
return 1;
if (test_opt(inode->i_sb, DATA_FLAGS) == EXT3_MOUNT_JOURNAL_DATA)
return 1;
- if (inode->u.ext3_i.i_flags & EXT3_JOURNAL_DATA_FL)
+ if (EXT3_I(inode)->i_flags & EXT3_JOURNAL_DATA_FL)
return 1;
return 0;
}
#include <linux/pipe_fs_i.h>
#include <linux/minix_fs_i.h>
-#include <linux/ext2_fs_i.h>
-#include <linux/ext3_fs_i.h>
-#include <linux/hpfs_fs_i.h>
#include <linux/ntfs_fs_i.h>
#include <linux/msdos_fs_i.h>
#include <linux/umsdos_fs_i.h>
#include <linux/iso_fs_i.h>
-#include <linux/nfs_fs_i.h>
#include <linux/sysv_fs_i.h>
-#include <linux/affs_fs_i.h>
-#include <linux/ufs_fs_i.h>
-#include <linux/efs_fs_i.h>
-#include <linux/coda_fs_i.h>
#include <linux/romfs_fs_i.h>
-#include <linux/shmem_fs.h>
#include <linux/smb_fs_i.h>
#include <linux/hfs_fs_i.h>
#include <linux/adfs_fs_i.h>
-#include <linux/qnx4_fs_i.h>
#include <linux/reiserfs_fs_i.h>
#include <linux/bfs_fs_i.h>
#include <linux/udf_fs_i.h>
__u32 i_generation;
union {
struct minix_inode_info minix_i;
- struct ext2_inode_info ext2_i;
- struct ext3_inode_info ext3_i;
- struct hpfs_inode_info hpfs_i;
struct ntfs_inode_info ntfs_i;
struct msdos_inode_info msdos_i;
struct umsdos_inode_info umsdos_i;
struct iso_inode_info isofs_i;
- struct nfs_inode_info nfs_i;
struct sysv_inode_info sysv_i;
- struct affs_inode_info affs_i;
- struct ufs_inode_info ufs_i;
- struct efs_inode_info efs_i;
struct romfs_inode_info romfs_i;
- struct shmem_inode_info shmem_i;
- struct coda_inode_info coda_i;
struct smb_inode_info smbfs_i;
struct hfs_inode_info hfs_i;
struct adfs_inode_info adfs_i;
- struct qnx4_inode_info qnx4_i;
struct reiserfs_inode_info reiserfs_i;
struct bfs_inode_info bfs_i;
struct udf_inode_info udf_i;
} u;
};
+#include <linux/shmem_fs.h>
+/* will die */
+#include <linux/coda_fs_i.h>
+#include <linux/ext3_fs_i.h>
+#include <linux/efs_fs_i.h>
+
struct fown_struct {
int pid; /* pid or -pgrp where SIGIO should be sent */
uid_t uid, euid; /* uid/euid of process setting the owner */
*/
typedef struct files_struct *fl_owner_t;
+/* that will die - we need it for nfs_lock_info */
+#include <linux/nfs_fs_i.h>
+
struct file_lock {
struct file_lock *fl_next; /* singly linked list for this inode */
struct list_head fl_link; /* doubly linked list of all locks */
unsigned i_dirty : 1;
struct semaphore i_sem; /* semaphore */
loff_t **i_rddir_off;
+ struct inode vfs_inode;
};
-#define i_hpfs_dno u.hpfs_i.i_dno
-#define i_hpfs_parent_dir u.hpfs_i.i_parent_dir
-#define i_hpfs_n_secs u.hpfs_i.i_n_secs
-#define i_hpfs_file_sec u.hpfs_i.i_file_sec
-#define i_hpfs_disk_sec u.hpfs_i.i_disk_sec
-#define i_hpfs_dpos u.hpfs_i.i_dpos
-#define i_hpfs_dsubdno u.hpfs_i.i_dsubdno
-#define i_hpfs_ea_size u.hpfs_i.i_ea_size
-#define i_hpfs_conv u.hpfs_i.i_conv
-#define i_hpfs_ea_mode u.hpfs_i.i_ea_mode
-#define i_hpfs_ea_uid u.hpfs_i.i_ea_uid
-#define i_hpfs_ea_gid u.hpfs_i.i_ea_gid
-/*#define i_hpfs_lock u.hpfs_i.i_lock*/
-/*#define i_hpfs_queue u.hpfs_i.i_queue*/
-#define i_hpfs_sem u.hpfs_i.i_sem
-#define i_hpfs_rddir_off u.hpfs_i.i_rddir_off
-#define i_hpfs_dirty u.hpfs_i.i_dirty
-
#endif
struct net_device_stats* (*get_stats)(struct net_device *dev);
struct iw_statistics* (*get_wireless_stats)(struct net_device *dev);
+ /* List of functions to handle Wireless Extensions (instead of ioctl).
+ * See <net/iw_handler.h> for details. Jean II */
+ struct iw_handler_def * wireless_handlers;
+
/*
* This marks the end of the "visible" part of the structure. All
* fields hereafter are internal to the system, and may change at
*/
#define NFS_SUPER_MAGIC 0x6969
-static inline struct nfs_inode_info *NFS_I(struct inode *inode)
+/*
+ * These are the default flags for swap requests
+ */
+#define NFS_RPC_SWAPFLAGS (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS)
+
+/* Flags in the RPC client structure */
+#define NFS_CLNTF_BUFSIZE 0x0001 /* readdir buffer in longwords */
+
+#define NFS_RW_SYNC 0x0001 /* O_SYNC handling */
+#define NFS_RW_SWAP 0x0002 /* This is a swap request */
+
+/*
+ * When flushing a cluster of dirty pages, there can be different
+ * strategies:
+ */
+#define FLUSH_AGING 0 /* only flush old buffers */
+#define FLUSH_SYNC 1 /* file being synced, or contention */
+#define FLUSH_WAIT 2 /* wait for completion */
+#define FLUSH_STABLE 4 /* commit to stable storage */
+
+#ifdef __KERNEL__
+
+/*
+ * nfs fs inode data in memory
+ */
+struct nfs_inode {
+ /*
+ * The 64bit 'inode number'
+ */
+ __u64 fsid;
+ __u64 fileid;
+
+ /*
+ * NFS file handle
+ */
+ struct nfs_fh fh;
+
+ /*
+ * Various flags
+ */
+ unsigned short flags;
+
+ /*
+ * read_cache_jiffies is when we started read-caching this inode,
+ * and read_cache_mtime is the mtime of the inode at that time.
+ * attrtimeo is for how long the cached information is assumed
+ * to be valid. A successful attribute revalidation doubles
+ * attrtimeo (up to acregmax/acdirmax), a failure resets it to
+ * acregmin/acdirmin.
+ *
+ * We need to revalidate the cached attrs for this inode if
+ *
+ * jiffies - read_cache_jiffies > attrtimeo
+ *
+ * and invalidate any cached data/flush out any dirty pages if
+ * we find that
+ *
+ * mtime != read_cache_mtime
+ */
+ unsigned long read_cache_jiffies;
+ __u64 read_cache_ctime;
+ __u64 read_cache_mtime;
+ __u64 read_cache_isize;
+ unsigned long attrtimeo;
+ unsigned long attrtimeo_timestamp;
+
+ /*
+ * This is the cookie verifier used for NFSv3 readdir
+ * operations
+ */
+ __u32 cookieverf[2];
+
+ /*
+ * This is the list of dirty unwritten pages.
+ */
+ struct list_head read;
+ struct list_head dirty;
+ struct list_head commit;
+ struct list_head writeback;
+
+ unsigned int nread,
+ ndirty,
+ ncommit,
+ npages;
+
+ /* Flush daemon info */
+ struct inode *hash_next,
+ *hash_prev;
+ unsigned long nextscan;
+
+ /* Credentials for shared mmap */
+ struct rpc_cred *mm_cred;
+
+ struct inode vfs_inode;
+};
+
+/*
+ * Legal inode flag values
+ */
+#define NFS_INO_STALE 0x0001 /* possible stale inode */
+#define NFS_INO_ADVISE_RDPLUS 0x0002 /* advise readdirplus */
+#define NFS_INO_REVALIDATING 0x0004 /* revalidating attrs */
+#define NFS_IS_SNAPSHOT 0x0010 /* a snapshot file */
+#define NFS_INO_FLUSH 0x0020 /* inode is due for flushing */
+#define NFS_INO_NEW 0x0040 /* hadn't been filled yet */
+
+static inline struct nfs_inode *NFS_I(struct inode *inode)
{
- return &inode->u.nfs_i;
+ return list_entry(inode, struct nfs_inode, vfs_inode);
}
-#define NFS_FH(inode) (&(inode)->u.nfs_i.fh)
+#define NFS_FH(inode) (&NFS_I(inode)->fh)
#define NFS_SERVER(inode) (&(inode)->i_sb->u.nfs_sb.s_server)
#define NFS_CLIENT(inode) (NFS_SERVER(inode)->client)
#define NFS_PROTO(inode) (NFS_SERVER(inode)->rpc_ops)
#define NFS_REQUESTLIST(inode) (NFS_SERVER(inode)->rw_requests)
#define NFS_ADDR(inode) (RPC_PEERADDR(NFS_CLIENT(inode)))
#define NFS_CONGESTED(inode) (RPC_CONGESTED(NFS_CLIENT(inode)))
-#define NFS_COOKIEVERF(inode) ((inode)->u.nfs_i.cookieverf)
-#define NFS_READTIME(inode) ((inode)->u.nfs_i.read_cache_jiffies)
-#define NFS_CACHE_CTIME(inode) ((inode)->u.nfs_i.read_cache_ctime)
-#define NFS_CACHE_MTIME(inode) ((inode)->u.nfs_i.read_cache_mtime)
-#define NFS_CACHE_ISIZE(inode) ((inode)->u.nfs_i.read_cache_isize)
-#define NFS_NEXTSCAN(inode) ((inode)->u.nfs_i.nextscan)
+#define NFS_COOKIEVERF(inode) (NFS_I(inode)->cookieverf)
+#define NFS_READTIME(inode) (NFS_I(inode)->read_cache_jiffies)
+#define NFS_CACHE_CTIME(inode) (NFS_I(inode)->read_cache_ctime)
+#define NFS_CACHE_MTIME(inode) (NFS_I(inode)->read_cache_mtime)
+#define NFS_CACHE_ISIZE(inode) (NFS_I(inode)->read_cache_isize)
+#define NFS_NEXTSCAN(inode) (NFS_I(inode)->nextscan)
#define NFS_CACHEINV(inode) \
do { \
NFS_READTIME(inode) = jiffies - NFS_MAXATTRTIMEO(inode) - 1; \
} while (0)
-#define NFS_ATTRTIMEO(inode) ((inode)->u.nfs_i.attrtimeo)
+#define NFS_ATTRTIMEO(inode) (NFS_I(inode)->attrtimeo)
#define NFS_MINATTRTIMEO(inode) \
(S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \
: NFS_SERVER(inode)->acregmin)
#define NFS_MAXATTRTIMEO(inode) \
(S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmax \
: NFS_SERVER(inode)->acregmax)
-#define NFS_ATTRTIMEO_UPDATE(inode) ((inode)->u.nfs_i.attrtimeo_timestamp)
+#define NFS_ATTRTIMEO_UPDATE(inode) (NFS_I(inode)->attrtimeo_timestamp)
-#define NFS_FLAGS(inode) ((inode)->u.nfs_i.flags)
+#define NFS_FLAGS(inode) (NFS_I(inode)->flags)
#define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATING)
#define NFS_STALE(inode) (NFS_FLAGS(inode) & NFS_INO_STALE)
+#define NFS_NEW(inode) (NFS_FLAGS(inode) & NFS_INO_NEW)
-#define NFS_FILEID(inode) ((inode)->u.nfs_i.fileid)
-#define NFS_FSID(inode) ((inode)->u.nfs_i.fsid)
+#define NFS_FILEID(inode) (NFS_I(inode)->fileid)
+#define NFS_FSID(inode) (NFS_I(inode)->fsid)
/* Inode Flags */
#define NFS_USE_READDIRPLUS(inode) ((NFS_FLAGS(inode) & NFS_INO_ADVISE_RDPLUS) ? 1 : 0)
-/*
- * These are the default flags for swap requests
- */
-#define NFS_RPC_SWAPFLAGS (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS)
-
-/* Flags in the RPC client structure */
-#define NFS_CLNTF_BUFSIZE 0x0001 /* readdir buffer in longwords */
-
-#define NFS_RW_SYNC 0x0001 /* O_SYNC handling */
-#define NFS_RW_SWAP 0x0002 /* This is a swap request */
-
-/*
- * When flushing a cluster of dirty pages, there can be different
- * strategies:
- */
-#define FLUSH_AGING 0 /* only flush old buffers */
-#define FLUSH_SYNC 1 /* file being synced, or contention */
-#define FLUSH_WAIT 2 /* wait for completion */
-#define FLUSH_STABLE 4 /* commit to stable storage */
-
static inline
loff_t page_offset(struct page *page)
{
return page->index;
}
-#ifdef __KERNEL__
/*
* linux/fs/nfs/inode.c
*/
static inline int
nfs_have_read(struct inode *inode)
{
- return !list_empty(&inode->u.nfs_i.read);
+ return !list_empty(&NFS_I(inode)->read);
}
static inline int
nfs_have_writebacks(struct inode *inode)
{
- return !list_empty(&inode->u.nfs_i.writeback);
+ return !list_empty(&NFS_I(inode)->writeback);
}
static inline int
#include <linux/list.h>
#include <linux/nfs.h>
-/*
- * nfs fs inode data in memory
- */
-struct nfs_inode_info {
- /*
- * The 64bit 'inode number'
- */
- __u64 fsid;
- __u64 fileid;
-
- /*
- * NFS file handle
- */
- struct nfs_fh fh;
-
- /*
- * Various flags
- */
- unsigned short flags;
-
- /*
- * read_cache_jiffies is when we started read-caching this inode,
- * and read_cache_mtime is the mtime of the inode at that time.
- * attrtimeo is for how long the cached information is assumed
- * to be valid. A successful attribute revalidation doubles
- * attrtimeo (up to acregmax/acdirmax), a failure resets it to
- * acregmin/acdirmin.
- *
- * We need to revalidate the cached attrs for this inode if
- *
- * jiffies - read_cache_jiffies > attrtimeo
- *
- * and invalidate any cached data/flush out any dirty pages if
- * we find that
- *
- * mtime != read_cache_mtime
- */
- unsigned long read_cache_jiffies;
- __u64 read_cache_ctime;
- __u64 read_cache_mtime;
- __u64 read_cache_isize;
- unsigned long attrtimeo;
- unsigned long attrtimeo_timestamp;
-
- /*
- * This is the cookie verifier used for NFSv3 readdir
- * operations
- */
- __u32 cookieverf[2];
-
- /*
- * This is the list of dirty unwritten pages.
- */
- struct list_head read;
- struct list_head dirty;
- struct list_head commit;
- struct list_head writeback;
-
- unsigned int nread,
- ndirty,
- ncommit,
- npages;
-
- /* Flush daemon info */
- struct inode *hash_next,
- *hash_prev;
- unsigned long nextscan;
-
- /* Credentials for shared mmap */
- struct rpc_cred *mm_cred;
-};
-
-/*
- * Legal inode flag values
- */
-#define NFS_INO_STALE 0x0001 /* possible stale inode */
-#define NFS_INO_ADVISE_RDPLUS 0x0002 /* advise readdirplus */
-#define NFS_INO_REVALIDATING 0x0004 /* revalidating attrs */
-#define NFS_IS_SNAPSHOT 0x0010 /* a snapshot file */
-#define NFS_INO_FLUSH 0x0020 /* inode is due for flushing */
-
/*
* NFS lock info
*/
#define QNX4DEBUG(X) (void) 0
#endif
+struct qnx4_inode_info {
+ struct qnx4_inode_entry raw;
+ unsigned long mmu_private;
+ struct inode vfs_inode;
+};
+
extern struct dentry *qnx4_lookup(struct inode *dir, struct dentry *dentry);
extern unsigned long qnx4_count_free_blocks(struct super_block *sb);
extern unsigned long qnx4_block_map(struct inode *inode, long iblock);
extern int qnx4_sync_inode(struct inode *inode);
extern int qnx4_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh, int create);
+static inline struct qnx4_inode_info *qnx4_i(struct inode *inode)
+{
+ return list_entry(inode, struct qnx4_inode_info, vfs_inode);
+}
+
+static inline struct qnx4_inode_entry *qnx4_raw_inode(struct inode *inode)
+{
+ return &qnx4_i(inode)->raw;
+}
+
#endif /* __KERNEL__ */
#endif
+++ /dev/null
-/*
- * Name : qnx4_fs_i.h
- * Author : Richard Frowijn
- * Function : qnx4 inode definitions
- * Version : 1.0.2
- * Last modified : 2000-01-06
- *
- * History : 23-03-1998 created
- *
- */
-#ifndef _QNX4_FS_I
-#define _QNX4_FS_I
-
-#include <linux/qnxtypes.h>
-
-/*
- * qnx4 fs inode entry
- */
-struct qnx4_inode_info {
- char i_reserved[16]; /* 16 */
- qnx4_off_t i_size; /* 4 */
- qnx4_xtnt_t i_first_xtnt; /* 8 */
- __u32 i_xblk; /* 4 */
- __s32 i_ftime; /* 4 */
- __s32 i_mtime; /* 4 */
- __s32 i_atime; /* 4 */
- __s32 i_ctime; /* 4 */
- qnx4_nxtnt_t i_num_xtnts; /* 2 */
- qnx4_mode_t i_mode; /* 2 */
- qnx4_muid_t i_uid; /* 2 */
- qnx4_mgid_t i_gid; /* 2 */
- qnx4_nlink_t i_nlink; /* 2 */
- __u8 i_zero[4]; /* 4 */
- qnx4_ftype_t i_type; /* 1 */
- __u8 i_status; /* 1 */
- unsigned long mmu_private;
-};
-
-#endif
extern void sched_init(void);
extern void init_idle(void);
+extern void idle_startup_done(void);
extern void show_state(void);
extern void cpu_init (void);
extern void trap_init(void);
extern void update_one_process(struct task_struct *p, unsigned long user,
unsigned long system, int cpu);
extern void scheduler_tick(struct task_struct *p);
+extern void sched_task_migrated(struct task_struct *p);
+extern void smp_migrate_task(int cpu, task_t *task);
#define MAX_SCHEDULE_TIMEOUT LONG_MAX
extern signed long FASTCALL(schedule_timeout(signed long timeout));
unsigned long swapped;
int locked; /* into memory */
struct list_head list;
- struct inode *inode;
+ struct inode vfs_inode;
};
struct shmem_sb_info {
spinlock_t stat_lock;
};
-#define SHMEM_I(inode) (&inode->u.shmem_i)
+static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
+{
+ return list_entry(inode, struct shmem_inode_info, vfs_inode);
+}
#endif
#define UFS_MAXNAMLEN 255
#define UFS_MAXMNTLEN 512
-#define UFS_MAXCSBUFS 31
+/* #define UFS_MAXCSBUFS 31 */
#define UFS_LINK_MAX 32000
/*
#define UFS_SF_APPEND 0x00040000 /* append-only */
#define UFS_SF_NOUNLINK 0x00100000 /* can't be removed or renamed */
+/*
+ * This structure is used for reading disk structures larger
+ * than the size of fragment.
+ */
+struct ufs_buffer_head {
+ unsigned fragment; /* first fragment */
+ unsigned count; /* number of fragments */
+ struct buffer_head * bh[UFS_MAXFRAG]; /* buffers */
+};
+
+struct ufs_cg_private_info {
+ struct ufs_cylinder_group ucg;
+ __u32 c_cgx; /* number of cylidner group */
+ __u16 c_ncyl; /* number of cyl's this cg */
+ __u16 c_niblk; /* number of inode blocks this cg */
+ __u32 c_ndblk; /* number of data blocks this cg */
+ __u32 c_rotor; /* position of last used block */
+ __u32 c_frotor; /* position of last used frag */
+ __u32 c_irotor; /* position of last used inode */
+ __u32 c_btotoff; /* (__u32) block totals per cylinder */
+ __u32 c_boff; /* (short) free block positions */
+ __u32 c_iusedoff; /* (char) used inode map */
+ __u32 c_freeoff; /* (u_char) free block map */
+ __u32 c_nextfreeoff; /* (u_char) next available space */
+ __u32 c_clustersumoff;/* (u_int32) counts of avail clusters */
+ __u32 c_clusteroff; /* (u_int8) free cluster map */
+ __u32 c_nclusterblks; /* number of clusters this cg */
+};
+
+struct ufs_sb_private_info {
+ struct ufs_buffer_head s_ubh; /* buffer containing super block */
+ __u32 s_sblkno; /* offset of super-blocks in filesys */
+ __u32 s_cblkno; /* offset of cg-block in filesys */
+ __u32 s_iblkno; /* offset of inode-blocks in filesys */
+ __u32 s_dblkno; /* offset of first data after cg */
+ __u32 s_cgoffset; /* cylinder group offset in cylinder */
+ __u32 s_cgmask; /* used to calc mod fs_ntrak */
+ __u32 s_size; /* number of blocks (fragments) in fs */
+ __u32 s_dsize; /* number of data blocks in fs */
+ __u32 s_ncg; /* number of cylinder groups */
+ __u32 s_bsize; /* size of basic blocks */
+ __u32 s_fsize; /* size of fragments */
+ __u32 s_fpb; /* fragments per block */
+ __u32 s_minfree; /* minimum percentage of free blocks */
+ __u32 s_bmask; /* `blkoff'' calc of blk offsets */
+ __u32 s_fmask; /* s_fsize mask */
+ __u32 s_bshift; /* `lblkno'' calc of logical blkno */
+ __u32 s_fshift; /* s_fsize shift */
+ __u32 s_fpbshift; /* fragments per block shift */
+ __u32 s_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ __u32 s_sbsize; /* actual size of super block */
+ __u32 s_csmask; /* csum block offset */
+ __u32 s_csshift; /* csum block number */
+ __u32 s_nindir; /* value of NINDIR */
+ __u32 s_inopb; /* value of INOPB */
+ __u32 s_nspf; /* value of NSPF */
+ __u32 s_npsect; /* # sectors/track including spares */
+ __u32 s_interleave; /* hardware sector interleave */
+ __u32 s_trackskew; /* sector 0 skew, per track */
+ __u32 s_csaddr; /* blk addr of cyl grp summary area */
+ __u32 s_cssize; /* size of cyl grp summary area */
+ __u32 s_cgsize; /* cylinder group size */
+ __u32 s_ntrak; /* tracks per cylinder */
+ __u32 s_nsect; /* sectors per track */
+ __u32 s_spc; /* sectors per cylinder */
+ __u32 s_ipg; /* inodes per group */
+ __u32 s_fpg; /* fragments per group */
+ __u32 s_cpc; /* cyl per cycle in postbl */
+ __s32 s_contigsumsize;/* size of cluster summary array, 44bsd */
+ __s64 s_qbmask; /* ~usb_bmask */
+ __s64 s_qfmask; /* ~usb_fmask */
+ __s32 s_postblformat; /* format of positional layout tables */
+ __s32 s_nrpos; /* number of rotational positions */
+ __s32 s_postbloff; /* (__s16) rotation block list head */
+ __s32 s_rotbloff; /* (__u8) blocks for each rotation */
+
+ __u32 s_fpbmask; /* fragments per block mask */
+ __u32 s_apb; /* address per block */
+ __u32 s_2apb; /* address per block^2 */
+ __u32 s_3apb; /* address per block^3 */
+ __u32 s_apbmask; /* address per block mask */
+ __u32 s_apbshift; /* address per block shift */
+ __u32 s_2apbshift; /* address per block shift * 2 */
+ __u32 s_3apbshift; /* address per block shift * 3 */
+ __u32 s_nspfshift; /* number of sector per fragment shift */
+ __u32 s_nspb; /* number of sector per block */
+ __u32 s_inopf; /* inodes per fragment */
+ __u32 s_sbbase; /* offset of NeXTstep superblock */
+ __u32 s_bpf; /* bits per fragment */
+ __u32 s_bpfshift; /* bits per fragment shift*/
+ __u32 s_bpfmask; /* bits per fragment mask */
+
+ __u32 s_maxsymlinklen;/* upper limit on fast symlinks' size */
+};
+
+/*
+ * Sizes of this structures are:
+ * ufs_super_block_first 512
+ * ufs_super_block_second 512
+ * ufs_super_block_third 356
+ */
+struct ufs_super_block_first {
+ __u32 fs_link;
+ __u32 fs_rlink;
+ __u32 fs_sblkno;
+ __u32 fs_cblkno;
+ __u32 fs_iblkno;
+ __u32 fs_dblkno;
+ __u32 fs_cgoffset;
+ __u32 fs_cgmask;
+ __u32 fs_time;
+ __u32 fs_size;
+ __u32 fs_dsize;
+ __u32 fs_ncg;
+ __u32 fs_bsize;
+ __u32 fs_fsize;
+ __u32 fs_frag;
+ __u32 fs_minfree;
+ __u32 fs_rotdelay;
+ __u32 fs_rps;
+ __u32 fs_bmask;
+ __u32 fs_fmask;
+ __u32 fs_bshift;
+ __u32 fs_fshift;
+ __u32 fs_maxcontig;
+ __u32 fs_maxbpg;
+ __u32 fs_fragshift;
+ __u32 fs_fsbtodb;
+ __u32 fs_sbsize;
+ __u32 fs_csmask;
+ __u32 fs_csshift;
+ __u32 fs_nindir;
+ __u32 fs_inopb;
+ __u32 fs_nspf;
+ __u32 fs_optim;
+ union {
+ struct {
+ __u32 fs_npsect;
+ } fs_sun;
+ struct {
+ __s32 fs_state;
+ } fs_sunx86;
+ } fs_u1;
+ __u32 fs_interleave;
+ __u32 fs_trackskew;
+ __u32 fs_id[2];
+ __u32 fs_csaddr;
+ __u32 fs_cssize;
+ __u32 fs_cgsize;
+ __u32 fs_ntrak;
+ __u32 fs_nsect;
+ __u32 fs_spc;
+ __u32 fs_ncyl;
+ __u32 fs_cpg;
+ __u32 fs_ipg;
+ __u32 fs_fpg;
+ struct ufs_csum fs_cstotal;
+ __s8 fs_fmod;
+ __s8 fs_clean;
+ __s8 fs_ronly;
+ __s8 fs_flags;
+ __s8 fs_fsmnt[UFS_MAXMNTLEN - 212];
+
+};
+
+struct ufs_super_block_second {
+ __s8 fs_fsmnt[212];
+ __u32 fs_cgrotor;
+ __u32 fs_csp[UFS_MAXCSBUFS];
+ __u32 fs_maxcluster;
+ __u32 fs_cpc;
+ __u16 fs_opostbl[82];
+};
+
+struct ufs_super_block_third {
+ __u16 fs_opostbl[46];
+ union {
+ struct {
+ __s32 fs_sparecon[53];/* reserved for future constants */
+ __s32 fs_reclaim;
+ __s32 fs_sparecon2[1];
+ __s32 fs_state; /* file system state time stamp */
+ __u32 fs_qbmask[2]; /* ~usb_bmask */
+ __u32 fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sun;
+ struct {
+ __s32 fs_sparecon[53];/* reserved for future constants */
+ __s32 fs_reclaim;
+ __s32 fs_sparecon2[1];
+ __u32 fs_npsect; /* # sectors/track including spares */
+ __u32 fs_qbmask[2]; /* ~usb_bmask */
+ __u32 fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sunx86;
+ struct {
+ __s32 fs_sparecon[50];/* reserved for future constants */
+ __s32 fs_contigsumsize;/* size of cluster summary array */
+ __s32 fs_maxsymlinklen;/* max length of an internal symlink */
+ __s32 fs_inodefmt; /* format of on-disk inodes */
+ __u32 fs_maxfilesize[2]; /* max representable file size */
+ __u32 fs_qbmask[2]; /* ~usb_bmask */
+ __u32 fs_qfmask[2]; /* ~usb_fmask */
+ __s32 fs_state; /* file system state time stamp */
+ } fs_44;
+ } fs_u2;
+ __s32 fs_postblformat;
+ __s32 fs_nrpos;
+ __s32 fs_postbloff;
+ __s32 fs_rotbloff;
+ __s32 fs_magic;
+ __u8 fs_space[1];
+};
+
#ifdef __KERNEL__
/* balloc.c */
/* ialloc.c */
extern void ufs_free_inode (struct inode *inode);
-extern struct inode * ufs_new_inode (const struct inode *, int);
+extern struct inode * ufs_new_inode (struct inode *, int);
/* inode.c */
extern int ufs_frag_map (struct inode *, int);
/* truncate.c */
extern void ufs_truncate (struct inode *);
+#include <linux/ufs_fs_i.h>
+
+static inline struct ufs_inode_info *UFS_I(struct inode *inode)
+{
+ return list_entry(inode, struct ufs_inode_info, vfs_inode);
+}
+
#endif /* __KERNEL__ */
#endif /* __LINUX_UFS_FS_H */
__u32 i_oeftflag;
__u16 i_osync;
__u32 i_lastfrag;
+ struct inode vfs_inode;
};
#endif /* _LINUX_UFS_FS_I_H */
#ifndef __LINUX_UFS_FS_SB_H
#define __LINUX_UFS_FS_SB_H
-#include <linux/ufs_fs.h>
-
-/*
- * This structure is used for reading disk structures larger
- * than the size of fragment.
- */
-struct ufs_buffer_head {
- unsigned fragment; /* first fragment */
- unsigned count; /* number of fragments */
- struct buffer_head * bh[UFS_MAXFRAG]; /* buffers */
-};
-
-struct ufs_cg_private_info {
- struct ufs_cylinder_group ucg;
- __u32 c_cgx; /* number of cylidner group */
- __u16 c_ncyl; /* number of cyl's this cg */
- __u16 c_niblk; /* number of inode blocks this cg */
- __u32 c_ndblk; /* number of data blocks this cg */
- __u32 c_rotor; /* position of last used block */
- __u32 c_frotor; /* position of last used frag */
- __u32 c_irotor; /* position of last used inode */
- __u32 c_btotoff; /* (__u32) block totals per cylinder */
- __u32 c_boff; /* (short) free block positions */
- __u32 c_iusedoff; /* (char) used inode map */
- __u32 c_freeoff; /* (u_char) free block map */
- __u32 c_nextfreeoff; /* (u_char) next available space */
- __u32 c_clustersumoff;/* (u_int32) counts of avail clusters */
- __u32 c_clusteroff; /* (u_int8) free cluster map */
- __u32 c_nclusterblks; /* number of clusters this cg */
-};
-
-struct ufs_sb_private_info {
- struct ufs_buffer_head s_ubh; /* buffer containing super block */
- __u32 s_sblkno; /* offset of super-blocks in filesys */
- __u32 s_cblkno; /* offset of cg-block in filesys */
- __u32 s_iblkno; /* offset of inode-blocks in filesys */
- __u32 s_dblkno; /* offset of first data after cg */
- __u32 s_cgoffset; /* cylinder group offset in cylinder */
- __u32 s_cgmask; /* used to calc mod fs_ntrak */
- __u32 s_size; /* number of blocks (fragments) in fs */
- __u32 s_dsize; /* number of data blocks in fs */
- __u32 s_ncg; /* number of cylinder groups */
- __u32 s_bsize; /* size of basic blocks */
- __u32 s_fsize; /* size of fragments */
- __u32 s_fpb; /* fragments per block */
- __u32 s_minfree; /* minimum percentage of free blocks */
- __u32 s_bmask; /* `blkoff'' calc of blk offsets */
- __u32 s_fmask; /* s_fsize mask */
- __u32 s_bshift; /* `lblkno'' calc of logical blkno */
- __u32 s_fshift; /* s_fsize shift */
- __u32 s_fpbshift; /* fragments per block shift */
- __u32 s_fsbtodb; /* fsbtodb and dbtofsb shift constant */
- __u32 s_sbsize; /* actual size of super block */
- __u32 s_csmask; /* csum block offset */
- __u32 s_csshift; /* csum block number */
- __u32 s_nindir; /* value of NINDIR */
- __u32 s_inopb; /* value of INOPB */
- __u32 s_nspf; /* value of NSPF */
- __u32 s_npsect; /* # sectors/track including spares */
- __u32 s_interleave; /* hardware sector interleave */
- __u32 s_trackskew; /* sector 0 skew, per track */
- __u32 s_csaddr; /* blk addr of cyl grp summary area */
- __u32 s_cssize; /* size of cyl grp summary area */
- __u32 s_cgsize; /* cylinder group size */
- __u32 s_ntrak; /* tracks per cylinder */
- __u32 s_nsect; /* sectors per track */
- __u32 s_spc; /* sectors per cylinder */
- __u32 s_ipg; /* inodes per group */
- __u32 s_fpg; /* fragments per group */
- __u32 s_cpc; /* cyl per cycle in postbl */
- __s32 s_contigsumsize;/* size of cluster summary array, 44bsd */
- __s64 s_qbmask; /* ~usb_bmask */
- __s64 s_qfmask; /* ~usb_fmask */
- __s32 s_postblformat; /* format of positional layout tables */
- __s32 s_nrpos; /* number of rotational positions */
- __s32 s_postbloff; /* (__s16) rotation block list head */
- __s32 s_rotbloff; /* (__u8) blocks for each rotation */
-
- __u32 s_fpbmask; /* fragments per block mask */
- __u32 s_apb; /* address per block */
- __u32 s_2apb; /* address per block^2 */
- __u32 s_3apb; /* address per block^3 */
- __u32 s_apbmask; /* address per block mask */
- __u32 s_apbshift; /* address per block shift */
- __u32 s_2apbshift; /* address per block shift * 2 */
- __u32 s_3apbshift; /* address per block shift * 3 */
- __u32 s_nspfshift; /* number of sector per fragment shift */
- __u32 s_nspb; /* number of sector per block */
- __u32 s_inopf; /* inodes per fragment */
- __u32 s_sbbase; /* offset of NeXTstep superblock */
- __u32 s_bpf; /* bits per fragment */
- __u32 s_bpfshift; /* bits per fragment shift*/
- __u32 s_bpfmask; /* bits per fragment mask */
-
- __u32 s_maxsymlinklen;/* upper limit on fast symlinks' size */
-};
-
#define UFS_MAX_GROUP_LOADED 8
#define UFS_CGNO_EMPTY ((unsigned)-1)
+struct ufs_sb_private_info;
+struct ufs_cg_private_info;
+struct ufs_csum;
+#define UFS_MAXCSBUFS 31
+
struct ufs_sb_info {
struct ufs_sb_private_info * s_uspi;
struct ufs_csum * s_csp[UFS_MAXCSBUFS];
unsigned s_mount_opt;
};
-/*
- * Sizes of this structures are:
- * ufs_super_block_first 512
- * ufs_super_block_second 512
- * ufs_super_block_third 356
- */
-struct ufs_super_block_first {
- __u32 fs_link;
- __u32 fs_rlink;
- __u32 fs_sblkno;
- __u32 fs_cblkno;
- __u32 fs_iblkno;
- __u32 fs_dblkno;
- __u32 fs_cgoffset;
- __u32 fs_cgmask;
- __u32 fs_time;
- __u32 fs_size;
- __u32 fs_dsize;
- __u32 fs_ncg;
- __u32 fs_bsize;
- __u32 fs_fsize;
- __u32 fs_frag;
- __u32 fs_minfree;
- __u32 fs_rotdelay;
- __u32 fs_rps;
- __u32 fs_bmask;
- __u32 fs_fmask;
- __u32 fs_bshift;
- __u32 fs_fshift;
- __u32 fs_maxcontig;
- __u32 fs_maxbpg;
- __u32 fs_fragshift;
- __u32 fs_fsbtodb;
- __u32 fs_sbsize;
- __u32 fs_csmask;
- __u32 fs_csshift;
- __u32 fs_nindir;
- __u32 fs_inopb;
- __u32 fs_nspf;
- __u32 fs_optim;
- union {
- struct {
- __u32 fs_npsect;
- } fs_sun;
- struct {
- __s32 fs_state;
- } fs_sunx86;
- } fs_u1;
- __u32 fs_interleave;
- __u32 fs_trackskew;
- __u32 fs_id[2];
- __u32 fs_csaddr;
- __u32 fs_cssize;
- __u32 fs_cgsize;
- __u32 fs_ntrak;
- __u32 fs_nsect;
- __u32 fs_spc;
- __u32 fs_ncyl;
- __u32 fs_cpg;
- __u32 fs_ipg;
- __u32 fs_fpg;
- struct ufs_csum fs_cstotal;
- __s8 fs_fmod;
- __s8 fs_clean;
- __s8 fs_ronly;
- __s8 fs_flags;
- __s8 fs_fsmnt[UFS_MAXMNTLEN - 212];
-
-};
-
-struct ufs_super_block_second {
- __s8 fs_fsmnt[212];
- __u32 fs_cgrotor;
- __u32 fs_csp[UFS_MAXCSBUFS];
- __u32 fs_maxcluster;
- __u32 fs_cpc;
- __u16 fs_opostbl[82];
-};
-
-struct ufs_super_block_third {
- __u16 fs_opostbl[46];
- union {
- struct {
- __s32 fs_sparecon[53];/* reserved for future constants */
- __s32 fs_reclaim;
- __s32 fs_sparecon2[1];
- __s32 fs_state; /* file system state time stamp */
- __u32 fs_qbmask[2]; /* ~usb_bmask */
- __u32 fs_qfmask[2]; /* ~usb_fmask */
- } fs_sun;
- struct {
- __s32 fs_sparecon[53];/* reserved for future constants */
- __s32 fs_reclaim;
- __s32 fs_sparecon2[1];
- __u32 fs_npsect; /* # sectors/track including spares */
- __u32 fs_qbmask[2]; /* ~usb_bmask */
- __u32 fs_qfmask[2]; /* ~usb_fmask */
- } fs_sunx86;
- struct {
- __s32 fs_sparecon[50];/* reserved for future constants */
- __s32 fs_contigsumsize;/* size of cluster summary array */
- __s32 fs_maxsymlinklen;/* max length of an internal symlink */
- __s32 fs_inodefmt; /* format of on-disk inodes */
- __u32 fs_maxfilesize[2]; /* max representable file size */
- __u32 fs_qbmask[2]; /* ~usb_bmask */
- __u32 fs_qfmask[2]; /* ~usb_fmask */
- __s32 fs_state; /* file system state time stamp */
- } fs_44;
- } fs_u2;
- __s32 fs_postblformat;
- __s32 fs_nrpos;
- __s32 fs_postbloff;
- __s32 fs_rotbloff;
- __s32 fs_magic;
- __u8 fs_space[1];
-};
-
-#endif /* __LINUX_UFS_FS_SB_H */
+#endif
/* rdir.c 22/03/95 03.31.42 */
struct dentry *umsdos_rlookup_x (struct inode *dir, struct dentry *dentry, int nopseudo);
struct dentry *UMSDOS_rlookup (struct inode *dir, struct dentry *dentry);
+
+static inline struct umsdos_inode_info *UMSDOS_I(struct inode *inode)
+{
+ return &inode->u.umsdos_i;
+}
/* ... less overhead for QUEUE_BULK */
#define USB_TIMEOUT_KILLED 0x1000 /* only set by HCD! */
-typedef struct
-{
+struct usb_iso_packet_descriptor {
unsigned int offset;
unsigned int length; /* expected length */
unsigned int actual_length;
unsigned int status;
-} iso_packet_descriptor_t;
+};
struct urb;
int timeout; /* (in) timeout, in jiffies */
void *context; /* (in) context for completion */
usb_complete_t complete; /* (in) completion routine */
- iso_packet_descriptor_t iso_frame_desc[0]; /* (in) ISO ONLY */
+ struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */
};
-typedef struct urb urb_t;
-
/**
* usb_fill_control_urb - initializes a control urb
* @urb: pointer to the urb to initialize.
+++ /dev/null
-struct usb_device;
-struct usb_bus;
-
-struct usbdev_inode_info {
- struct list_head dlist;
- struct list_head slist;
- union {
- struct usb_device *dev;
- struct usb_bus *bus;
- } p;
-};
+++ /dev/null
-struct usbdev_sb_info {
- struct list_head slist;
- struct list_head ilist;
- uid_t devuid;
- gid_t devgid;
- umode_t devmode;
- uid_t busuid;
- gid_t busgid;
- umode_t busmode;
- uid_t listuid;
- gid_t listgid;
- umode_t listmode;
-};
/*
* This file define a set of standard wireless extensions
*
- * Version : 12 5.10.01
+ * Version : 13 6.12.01
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved.
*/
#ifndef _LINUX_WIRELESS_H
/************************** DOCUMENTATION **************************/
/*
+ * Initial APIs (1996 -> onward) :
+ * -----------------------------
* Basically, the wireless extensions are for now a set of standard ioctl
* call + /proc/net/wireless
*
* We have the list of command plus a structure descibing the
* data exchanged...
* Note that to add these ioctl, I was obliged to modify :
- * net/core/dev.c (two place + add include)
- * net/ipv4/af_inet.c (one place + add include)
+ * # net/core/dev.c (two place + add include)
+ * # net/ipv4/af_inet.c (one place + add include)
*
* /proc/net/wireless is a copy of /proc/net/dev.
* We have a structure for data passed from the driver to /proc/net/wireless
* Too add this, I've modified :
- * net/core/dev.c (two other places)
- * include/linux/netdevice.h (one place)
- * include/linux/proc_fs.h (one place)
+ * # net/core/dev.c (two other places)
+ * # include/linux/netdevice.h (one place)
+ * # include/linux/proc_fs.h (one place)
*
+ * New driver API (2001 -> onward) :
+ * -------------------------------
+ * This file is only concerned with the user space API and common definitions.
+ * The new driver API is defined and documented in :
+ * # include/net/iw_handler.h
+ *
+ * Note as well that /proc/net/wireless implementation has now moved in :
+ * # include/linux/wireless.c
+ *
+ * Other comments :
+ * --------------
* Do not add here things that are redundant with other mechanisms
* (drivers init, ifconfig, /proc/net/dev, ...) and with are not
* wireless specific.
#include <linux/socket.h> /* for "struct sockaddr" et al */
#include <linux/if.h> /* for IFNAMSIZ and co... */
-/**************************** CONSTANTS ****************************/
-
-/* --------------------------- VERSION --------------------------- */
+/***************************** VERSION *****************************/
/*
* This constant is used to know the availability of the wireless
* extensions and to know which version of wireless extensions it is
* (there is some stuff that will be added in the future...)
* I just plan to increment with each new version.
*/
-#define WIRELESS_EXT 12
+#define WIRELESS_EXT 13
/*
* Changes :
* - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space
* - Add new statistics (frag, retry, beacon)
* - Add average quality (for user space calibration)
+ *
+ * V12 to V13
+ * ----------
+ * - Document creation of new driver API.
+ * - Extract union iwreq_data from struct iwreq (for new driver API).
+ * - Rename SIOCSIWNAME as SIOCSIWCOMMIT
*/
+/**************************** CONSTANTS ****************************/
+
/* -------------------------- IOCTL LIST -------------------------- */
/* Basic operations */
-#define SIOCSIWNAME 0x8B00 /* Unused */
+#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */
#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */
#define SIOCSIWNWID 0x8B02 /* set network id (the cell) */
#define SIOCGIWNWID 0x8B03 /* get network id */
};
/* ------------------------ IOCTL REQUEST ------------------------ */
+/*
+ * This structure defines the payload of an ioctl, and is used
+ * below.
+ *
+ * Note that this structure should fit on the memory footprint
+ * of iwreq (which is the same as ifreq), which mean a max size of
+ * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
+ * You should check this when increasing the structures defined
+ * above in this file...
+ */
+union iwreq_data
+{
+ /* Config - generic */
+ char name[IFNAMSIZ];
+ /* Name : used to verify the presence of wireless extensions.
+ * Name of the protocol/provider... */
+
+ struct iw_point essid; /* Extended network name */
+ struct iw_param nwid; /* network id (or domain - the cell) */
+ struct iw_freq freq; /* frequency or channel :
+ * 0-1000 = channel
+ * > 1000 = frequency in Hz */
+
+ struct iw_param sens; /* signal level threshold */
+ struct iw_param bitrate; /* default bit rate */
+ struct iw_param txpower; /* default transmit power */
+ struct iw_param rts; /* RTS threshold threshold */
+ struct iw_param frag; /* Fragmentation threshold */
+ __u32 mode; /* Operation mode */
+ struct iw_param retry; /* Retry limits & lifetime */
+
+ struct iw_point encoding; /* Encoding stuff : tokens */
+ struct iw_param power; /* PM duration/timeout */
+
+ struct sockaddr ap_addr; /* Access point address */
+
+ struct iw_point data; /* Other large parameters */
+};
+
/*
* The structure to exchange data for ioctl.
* This structure is the same as 'struct ifreq', but (re)defined for
* convenience...
- *
- * Note that it should fit on the same memory footprint !
- * You should check this when increasing the above structures (16 octets)
- * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
+ * Do I need to remind you about structure size (32 octets) ?
*/
struct iwreq
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */
} ifr_ifrn;
- /* Data part */
- union
- {
- /* Config - generic */
- char name[IFNAMSIZ];
- /* Name : used to verify the presence of wireless extensions.
- * Name of the protocol/provider... */
-
- struct iw_point essid; /* Extended network name */
- struct iw_param nwid; /* network id (or domain - the cell) */
- struct iw_freq freq; /* frequency or channel :
- * 0-1000 = channel
- * > 1000 = frequency in Hz */
-
- struct iw_param sens; /* signal level threshold */
- struct iw_param bitrate; /* default bit rate */
- struct iw_param txpower; /* default transmit power */
- struct iw_param rts; /* RTS threshold threshold */
- struct iw_param frag; /* Fragmentation threshold */
- __u32 mode; /* Operation mode */
- struct iw_param retry; /* Retry limits & lifetime */
-
- struct iw_point encoding; /* Encoding stuff : tokens */
- struct iw_param power; /* PM duration/timeout */
-
- struct sockaddr ap_addr; /* Access point address */
-
- struct iw_point data; /* Other large parameters */
- } u;
+ /* Data part (defined just above) */
+ union iwreq_data u;
};
/* -------------------------- IOCTL DATA -------------------------- */
--- /dev/null
+/*
+ * This file define the new driver API for Wireless Extensions
+ *
+ * Version : 2 6.12.01
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 2001 Jean Tourrilhes, All Rights Reserved.
+ */
+
+#ifndef _IW_HANDLER_H
+#define _IW_HANDLER_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * Initial driver API (1996 -> onward) :
+ * -----------------------------------
+ * The initial API just sends the IOCTL request received from user space
+ * to the driver (via the driver ioctl handler). The driver has to
+ * handle all the rest...
+ *
+ * The initial API also defines a specific handler in struct net_device
+ * to handle wireless statistics.
+ *
+ * The initial APIs served us well and has proven a reasonably good design.
+ * However, there is a few shortcommings :
+ * o No events, everything is a request to the driver.
+ * o Large ioctl function in driver with gigantic switch statement
+ * (i.e. spaghetti code).
+ * o Driver has to mess up with copy_to/from_user, and in many cases
+ * does it unproperly. Common mistakes are :
+ * * buffer overflows (no checks or off by one checks)
+ * * call copy_to/from_user with irq disabled
+ * o The user space interface is tied to ioctl because of the use
+ * copy_to/from_user.
+ *
+ * New driver API (2001 -> onward) :
+ * -------------------------------
+ * The new driver API is just a bunch of standard functions (handlers),
+ * each handling a specific Wireless Extension. The driver just export
+ * the list of handler it supports, and those will be called apropriately.
+ *
+ * I tried to keep the main advantage of the previous API (simplicity,
+ * efficiency and light weight), and also I provide a good dose of backward
+ * compatibility (most structures are the same, driver can use both API
+ * simultaneously, ...).
+ * Hopefully, I've also addressed the shortcomming of the initial API.
+ *
+ * The advantage of the new API are :
+ * o Handling of Extensions in driver broken in small contained functions
+ * o Tighter checks of ioctl before calling the driver
+ * o Flexible commit strategy (at least, the start of it)
+ * o Backward compatibility (can be mixed with old API)
+ * o Driver doesn't have to worry about memory and user-space issues
+ * The last point is important for the following reasons :
+ * o You are now able to call the new driver API from any API you
+ * want (including from within other parts of the kernel).
+ * o Common mistakes are avoided (buffer overflow, user space copy
+ * with irq disabled and so on).
+ *
+ * The Drawback of the new API are :
+ * o bloat (especially kernel)
+ * o need to migrate existing drivers to new API
+ * My initial testing shows that the new API adds around 3kB to the kernel
+ * and save between 0 and 5kB from a typical driver.
+ * Also, as all structures and data types are unchanged, the migration is
+ * quite straightforward (but tedious).
+ *
+ * ---
+ *
+ * The new driver API is defined below in this file. User space should
+ * not be aware of what's happening down there...
+ *
+ * A new kernel wrapper is in charge of validating the IOCTLs and calling
+ * the appropriate driver handler. This is implemented in :
+ * # net/core/wireless.c
+ *
+ * The driver export the list of handlers in :
+ * # include/linux/netdevice.h (one place)
+ *
+ * The new driver API is available for WIRELESS_EXT >= 13.
+ * Good luck with migration to the new API ;-)
+ */
+
+/* ---------------------- THE IMPLEMENTATION ---------------------- */
+/*
+ * Some of the choice I've made are pretty controversials. Defining an
+ * API is very much weighting compromises. This goes into some of the
+ * details and the thinking behind the implementation.
+ *
+ * Implementation goals :
+ * --------------------
+ * The implementation goals were as follow :
+ * o Obvious : you should not need a PhD to understand what's happening,
+ * the benefit is easier maintainance.
+ * o Flexible : it should accomodate a wide variety of driver
+ * implementations and be as flexible as the old API.
+ * o Lean : it should be efficient memory wise to minimise the impact
+ * on kernel footprint.
+ * o Transparent to user space : the large number of user space
+ * applications that use Wireless Extensions should not need
+ * any modifications.
+ *
+ * Array of functions versus Struct of functions
+ * ---------------------------------------------
+ * 1) Having an array of functions allow the kernel code to access the
+ * handler in a single lookup, which is much more efficient (think hash
+ * table here).
+ * 2) The only drawback is that driver writer may put their handler in
+ * the wrong slot. This is trivial to test (I set the frequency, the
+ * bitrate changes). Once the handler is in the proper slot, it will be
+ * there forever, because the array is only extended at the end.
+ * 3) Backward/forward compatibility : adding new handler just require
+ * extending the array, so you can put newer driver in older kernel
+ * without having to patch the kernel code (and vice versa).
+ *
+ * All handler are of the same generic type
+ * ----------------------------------------
+ * That's a feature !!!
+ * 1) Having a generic handler allow to have generic code, which is more
+ * efficient. If each of the handler was individually typed I would need
+ * to add a big switch in the kernel (== more bloat). This solution is
+ * more scalable, adding new Wireless Extensions doesn't add new code.
+ * 2) You can use the same handler in different slots of the array. For
+ * hardware, it may be more efficient or logical to handle multiple
+ * Wireless Extensions with a single function, and the API allow you to
+ * do that. (An example would be a single record on the card to control
+ * both bitrate and frequency, the handler would read the old record,
+ * modify it according to info->cmd and rewrite it).
+ *
+ * Functions prototype uses union iwreq_data
+ * -----------------------------------------
+ * Some would have prefered functions defined this way :
+ * static int mydriver_ioctl_setrate(struct net_device *dev,
+ * long rate, int auto)
+ * 1) The kernel code doesn't "validate" the content of iwreq_data, and
+ * can't do it (different hardware may have different notion of what a
+ * valid frequency is), so we don't pretend that we do it.
+ * 2) The above form is not extendable. If I want to add a flag (for
+ * example to distinguish setting max rate and basic rate), I would
+ * break the prototype. Using iwreq_data is more flexible.
+ * 3) Also, the above form is not generic (see above).
+ * 4) I don't expect driver developper using the wrong field of the
+ * union (Doh !), so static typechecking doesn't add much value.
+ * 5) Lastly, you can skip the union by doing :
+ * static int mydriver_ioctl_setrate(struct net_device *dev,
+ * struct iw_request_info *info,
+ * struct iw_param *rrq,
+ * char *extra)
+ * And then adding the handler in the array like this :
+ * (iw_handler) mydriver_ioctl_setrate, // SIOCSIWRATE
+ *
+ * Using functions and not a registry
+ * ----------------------------------
+ * Another implementation option would have been for every instance to
+ * define a registry (a struct containing all the Wireless Extensions)
+ * and only have a function to commit the registry to the hardware.
+ * 1) This approach can be emulated by the current code, but not
+ * vice versa.
+ * 2) Some drivers don't keep any configuration in the driver, for them
+ * adding such a registry would be a significant bloat.
+ * 3) The code to translate from Wireless Extension to native format is
+ * needed anyway, so it would not reduce significantely the amount of code.
+ * 4) The current approach only selectively translate Wireless Extensions
+ * to native format and only selectively set, whereas the registry approach
+ * would require to translate all WE and set all parameters for any single
+ * change.
+ * 5) For many Wireless Extensions, the GET operation return the current
+ * dynamic value, not the value that was set.
+ *
+ * This header is <net/iw_handler.h>
+ * ---------------------------------
+ * 1) This header is kernel space only and should not be exported to
+ * user space. Headers in "include/linux/" are exported, headers in
+ * "include/net/" are not.
+ *
+ * Mixed 32/64 bit issues
+ * ----------------------
+ * The Wireless Extensions are designed to be 64 bit clean, by using only
+ * datatypes with explicit storage size.
+ * There are some issues related to kernel and user space using different
+ * memory model, and in particular 64bit kernel with 32bit user space.
+ * The problem is related to struct iw_point, that contains a pointer
+ * that *may* need to be translated.
+ * This is quite messy. The new API doesn't solve this problem (it can't),
+ * but is a step in the right direction :
+ * 1) Meta data about each ioctl is easily available, so we know what type
+ * of translation is needed.
+ * 2) The move of data between kernel and user space is only done in a single
+ * place in the kernel, so adding specific hooks in there is possible.
+ * 3) In the long term, it allows to move away from using ioctl as the
+ * user space API.
+ *
+ * So many comments and so few code
+ * --------------------------------
+ * That's a feature. Comments won't bloat the resulting kernel binary.
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/wireless.h> /* IOCTL user space API */
+
+/***************************** VERSION *****************************/
+/*
+ * This constant is used to know which version of the driver API is
+ * available. Hopefully, this will be pretty stable and no changes
+ * will be needed...
+ * I just plan to increment with each new version.
+ */
+#define IW_HANDLER_VERSION 2
+
+/**************************** CONSTANTS ****************************/
+
+/* Special error message for the driver to indicate that we
+ * should do a commit after return from the iw_handler */
+#define EIWCOMMIT EINPROGRESS
+
+/* Flags available in struct iw_request_info */
+#define IW_REQUEST_FLAG_NONE 0x0000 /* No flag so far */
+
+/* Type of headers we know about (basically union iwreq_data) */
+#define IW_HEADER_TYPE_NULL 0 /* Not available */
+#define IW_HEADER_TYPE_CHAR 2 /* char [IFNAMSIZ] */
+#define IW_HEADER_TYPE_UINT 4 /* __u32 */
+#define IW_HEADER_TYPE_FREQ 5 /* struct iw_freq */
+#define IW_HEADER_TYPE_POINT 6 /* struct iw_point */
+#define IW_HEADER_TYPE_PARAM 7 /* struct iw_param */
+#define IW_HEADER_TYPE_ADDR 8 /* struct sockaddr */
+
+/* Handling flags */
+/* Most are not implemented. I just use them as a reminder of some
+ * cool features we might need one day ;-) */
+#define IW_DESCR_FLAG_NONE 0x0000 /* Obvious */
+/* Wrapper level flags */
+#define IW_DESCR_FLAG_DUMP 0x0001 /* Not part of the dump command */
+#define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */
+#define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET request is ROOT only */
+/* Driver level flags */
+#define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */
+
+/****************************** TYPES ******************************/
+
+/* ----------------------- WIRELESS HANDLER ----------------------- */
+/*
+ * A wireless handler is just a standard function, that looks like the
+ * ioctl handler.
+ * We also define there how a handler list look like... As the Wireless
+ * Extension space is quite dense, we use a simple array, which is faster
+ * (that's the perfect hash table ;-).
+ */
+
+/*
+ * Meta data about the request passed to the iw_handler.
+ * Most handlers can safely ignore what's in there.
+ * The 'cmd' field might come handy if you want to use the same handler
+ * for multiple command...
+ * This struct is also my long term insurance. I can add new fields here
+ * without breaking the prototype of iw_handler...
+ */
+struct iw_request_info
+{
+ __u16 cmd; /* Wireless Extension command */
+ __u16 flags; /* More to come ;-) */
+};
+
+/*
+ * This is how a function handling a Wireless Extension should look
+ * like (both get and set, standard and private).
+ */
+typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+/*
+ * This define all the handler that the driver export.
+ * As you need only one per driver type, please use a static const
+ * shared by all driver instances... Same for the members...
+ * This will be linked from net_device in <linux/netdevice.h>
+ */
+struct iw_handler_def
+{
+ /* Number of handlers defined (more precisely, index of the
+ * last defined handler + 1) */
+ __u16 num_standard;
+ __u16 num_private;
+ /* Number of private arg description */
+ __u16 num_private_args;
+
+ /* Array of handlers for standard ioctls
+ * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME]
+ */
+ iw_handler * standard;
+
+ /* Array of handlers for private ioctls
+ * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV]
+ */
+ iw_handler * private;
+
+ /* Arguments of private handler. This one is just a list, so you
+ * can put it in any order you want and should not leave holes...
+ * We will automatically export that to user space... */
+ struct iw_priv_args * private_args;
+
+ /* In the long term, get_wireless_stats will move from
+ * 'struct net_device' to here, to minimise bloat. */
+};
+
+/* ----------------------- WIRELESS EVENTS ----------------------- */
+/*
+ * Currently we don't support events, so let's just plan for the
+ * future...
+ */
+
+/*
+ * A Wireless Event.
+ */
+// How do we define short header ? We don't want a flag on length.
+// Probably a flag on event ? Highest bit to zero...
+struct iw_event
+{
+ __u16 length; /* Lenght of this stuff */
+ __u16 event; /* Wireless IOCTL */
+ union iwreq_data header; /* IOCTL fixed payload */
+ char extra[0]; /* Optional IOCTL data */
+};
+
+/* ---------------------- IOCTL DESCRIPTION ---------------------- */
+/*
+ * One of the main goal of the new interface is to deal entirely with
+ * user space/kernel space memory move.
+ * For that, we need to know :
+ * o if iwreq is a pointer or contain the full data
+ * o what is the size of the data to copy
+ *
+ * For private IOCTLs, we use the same rules as used by iwpriv and
+ * defined in struct iw_priv_args.
+ *
+ * For standard IOCTLs, things are quite different and we need to
+ * use the stuctures below. Actually, this struct is also more
+ * efficient, but that's another story...
+ */
+
+/*
+ * Describe how a standard IOCTL looks like.
+ */
+struct iw_ioctl_description
+{
+ __u8 header_type; /* NULL, iw_point or other */
+ __u8 token_type; /* Future */
+ __u16 token_size; /* Granularity of payload */
+ __u16 min_tokens; /* Min acceptable token number */
+ __u16 max_tokens; /* Max acceptable token number */
+ __u32 flags; /* Special handling of the request */
+};
+
+/* Need to think of short header translation table. Later. */
+
+/**************************** PROTOTYPES ****************************/
+/*
+ * Functions part of the Wireless Extensions (defined in net/core/wireless.c).
+ * Those may be called only within the kernel.
+ */
+
+/* First : function strictly used inside the kernel */
+
+/* Handle /proc/net/wireless, called in net/code/dev.c */
+extern int dev_get_wireless_info(char * buffer, char **start, off_t offset,
+ int length);
+
+/* Handle IOCTLs, called in net/code/dev.c */
+extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd);
+
+/* Second : functions that may be called by driver modules */
+/* None yet */
+
+#endif /* _LINUX_WIRELESS_H */
extern void setup_arch(char **);
extern void cpu_idle(void);
-unsigned long wait_init_idle;
-
#ifndef CONFIG_SMP
#ifdef CONFIG_X86_LOCAL_APIC
#else
+static unsigned long __initdata wait_init_idle;
+
+void __init idle_startup_done(void)
+{
+ clear_bit(smp_processor_id(), &wait_init_idle);
+ while (wait_init_idle) {
+ cpu_relax();
+ barrier();
+ }
+}
/* Called by boot processor to activate the rest. */
static void __init smp_init(void)
smp_threads_ready=1;
smp_commence();
+ idle_startup_done();
}
#endif
check_bugs();
printk("POSIX conformance testing by UNIFIX\n");
+ init_idle();
/*
* We count on the initial thread going ok
* Like idlers init is an unlocked kernel thread, which will
*/
smp_init();
- /*
- * Finally, we wait for all other CPU's, and initialize this
- * thread that will become the idle thread for the boot CPU.
- * After this, the scheduler is fully initialized, and we can
- * start creating and running new threads.
- */
- init_idle();
-
/* Do the rest non-__init'ed, we're now alive */
rest_init();
}
if (p->ptrace & PT_PTRACED)
send_sig(SIGSTOP, p, 1);
-#define RUN_CHILD_FIRST 1
-#if RUN_CHILD_FIRST
wake_up_forked_process(p); /* do this last */
-#else
- wake_up_process(p); /* do this last */
-#endif
++total_forks;
if (clone_flags & CLONE_VFORK)
wait_for_completion(&vfork);
-#if RUN_CHILD_FIRST
else
/*
* Let the child process run first, to avoid most of the
* COW overhead when the child exec()s afterwards.
*/
current->need_resched = 1;
-#endif
fork_out:
return retval;
*
* Locking rule: those places that want to lock multiple runqueues
* (such as the load balancing or the process migration code), lock
- * acquire operations must be ordered by the runqueue's cpu id.
- *
- * The RT event id is used to avoid calling into the the RT scheduler
- * if there is a RT task active in an SMP system but there is no
- * RT scheduling activity otherwise.
+ * acquire operations must be ordered by ascending &runqueue.
*/
struct runqueue {
spinlock_t lock;
#define this_rq() cpu_rq(smp_processor_id())
#define task_rq(p) cpu_rq((p)->cpu)
#define cpu_curr(cpu) (cpu_rq(cpu)->curr)
-#define rq_cpu(rq) ((rq) - runqueues)
#define rt_task(p) ((p)->policy != SCHED_OTHER)
-#define lock_task_rq(rq,p,flags) \
-do { \
-repeat_lock_task: \
- rq = task_rq(p); \
- spin_lock_irqsave(&rq->lock, flags); \
- if (unlikely(rq_cpu(rq) != (p)->cpu)) { \
- spin_unlock_irqrestore(&rq->lock, flags); \
- goto repeat_lock_task; \
- } \
-} while (0)
-
-#define unlock_task_rq(rq,p,flags) \
- spin_unlock_irqrestore(&rq->lock, flags)
+static inline runqueue_t *lock_task_rq(task_t *p, unsigned long *flags)
+{
+ struct runqueue *__rq;
+
+repeat_lock_task:
+ __rq = task_rq(p);
+ spin_lock_irqsave(&__rq->lock, *flags);
+ if (unlikely(__rq != task_rq(p))) {
+ spin_unlock_irqrestore(&__rq->lock, *flags);
+ goto repeat_lock_task;
+ }
+ return __rq;
+}
+static inline void unlock_task_rq(runqueue_t *rq, task_t *p,
+ unsigned long *flags)
+{
+ spin_unlock_irqrestore(&rq->lock, *flags);
+}
/*
* Adding/removing a task to/from a priority array:
*/
}
/*
- * A task is 'heavily interactive' if it has reached the
- * bottom 25% of the SCHED_OTHER priority range - in this
+ * A task is 'heavily interactive' if it either has reached the
+ * bottom 25% of the SCHED_OTHER priority range, or if it is below
+ * its default priority by at least 3 priority levels. In this
* case we favor it by reinserting it on the active array,
* even after it expired its current timeslice.
*
+ * A task is a 'CPU hog' if it's either in the upper 25% of the
+ * SCHED_OTHER priority range, or if's not an interactive task.
+ *
* A task can get a priority bonus by being 'somewhat
* interactive' - and it will get a priority penalty for
* being a CPU hog.
*
- * CPU-hog penalties cannot go more than 5 above the default
- * priority level. Priority bonus cannot go below the minimum
- * priority level.
*/
-#define PRIO_INTERACTIVE (MAX_RT_PRIO + MAX_USER_PRIO/3)
-#define TASK_INTERACTIVE(p) ((p)->prio <= PRIO_INTERACTIVE)
+#define PRIO_INTERACTIVE (MAX_RT_PRIO + MAX_USER_PRIO/4)
+#define PRIO_CPU_HOG (MAX_RT_PRIO + MAX_USER_PRIO*3/4)
+
+#define TASK_INTERACTIVE(p) (((p)->prio <= PRIO_INTERACTIVE) || \
+ (((p)->prio < PRIO_CPU_HOG) && \
+ ((p)->prio <= NICE_TO_PRIO((p)->__nice)-3)))
static inline int effective_prio(task_t *p)
{
/*
* Here we scale the actual sleep average [0 .... MAX_SLEEP_AVG]
- * into the 20 ... -20 bonus/penalty range.
+ * into the 19 ... -18 bonus/penalty range.
+ *
+ * We take off the 10% from the full 0...39 priority range so that:
+ *
+ * 1) nice +19 CPU hogs do not preempt nice 0 CPU hogs just yet.
+ * 2) nice -20 interactive tasks do not get preempted by
+ * nice 0 interactive tasks.
+ *
+ * Both properties are important to certain applications.
*/
- bonus = MAX_USER_PRIO * p->sleep_avg / MAX_SLEEP_AVG - MAX_USER_PRIO/2;
+ bonus = MAX_USER_PRIO*9/10 * p->sleep_avg / MAX_SLEEP_AVG -
+ MAX_USER_PRIO*9/10/2;
prio = NICE_TO_PRIO(p->__nice) - bonus;
if (prio < MAX_RT_PRIO)
prio = MAX_RT_PRIO;
static inline void activate_task(task_t *p, runqueue_t *rq)
{
+ unsigned long sleep_time = jiffies - p->sleep_timestamp;
prio_array_t *array = rq->active;
- if (!rt_task(p)) {
+ if (!rt_task(p) && sleep_time) {
/*
* This code gives a bonus to interactive tasks. We update
* an 'average sleep time' value here, based on
* the higher the average gets - and the higher the priority
* boost gets as well.
*/
- p->sleep_avg += jiffies - p->sleep_timestamp;
+ p->sleep_avg += sleep_time;
if (p->sleep_avg > MAX_SLEEP_AVG)
p->sleep_avg = MAX_SLEEP_AVG;
p->prio = effective_prio(p);
cpu_relax();
barrier();
}
- lock_task_rq(rq, p, flags);
+ rq = lock_task_rq(p, &flags);
if (unlikely(rq->curr == p)) {
- unlock_task_rq(rq, p, flags);
+ unlock_task_rq(rq, p, &flags);
goto repeat;
}
- unlock_task_rq(rq, p, flags);
+ unlock_task_rq(rq, p, &flags);
+}
+
+/*
+ * The SMP message passing code calls this function whenever
+ * the new task has arrived at the target CPU. We move the
+ * new task into the local runqueue.
+ *
+ * This function must be called with interrupts disabled.
+ */
+void sched_task_migrated(task_t *new_task)
+{
+ wait_task_inactive(new_task);
+ new_task->cpu = smp_processor_id();
+ wake_up_process(new_task);
}
/*
int success = 0;
runqueue_t *rq;
- lock_task_rq(rq, p, flags);
+ rq = lock_task_rq(p, &flags);
p->state = TASK_RUNNING;
if (!p->array) {
activate_task(p, rq);
resched_task(rq->curr);
success = 1;
}
- unlock_task_rq(rq, p, flags);
+ unlock_task_rq(rq, p, &flags);
return success;
}
-inline int wake_up_process(task_t * p)
+int wake_up_process(task_t * p)
{
return try_to_wake_up(p, 0);
}
p->state = TASK_RUNNING;
if (!rt_task(p)) {
- p->prio += MAX_USER_PRIO/10;
- if (p->prio > MAX_PRIO-1)
- p->prio = MAX_PRIO-1;
+ p->sleep_avg = (p->sleep_avg * 4) / 5;
+ p->prio = effective_prio(p);
}
spin_lock_irq(&rq->lock);
activate_task(p, rq);
return sum;
}
-static inline unsigned long max_rq_len(void)
-{
- unsigned long i, curr, max = 0;
-
- for (i = 0; i < smp_num_cpus; i++) {
- curr = cpu_rq(i)->nr_running;
- if (curr > max)
- max = curr;
- }
- return max;
-}
-
/*
* Current runqueue is empty, or rebalance tick: if there is an
* inbalance (current runqueue is too short) then pull from
* Ok, lets do some actual balancing:
*/
- if (rq_cpu(busiest) < this_cpu) {
+ if (busiest < this_rq) {
spin_unlock(&this_rq->lock);
spin_lock(&busiest->lock);
spin_lock(&this_rq->lock);
static inline void idle_tick(void)
{
- if ((jiffies % IDLE_REBALANCE_TICK) ||
- likely(this_rq()->curr == NULL))
+ if (jiffies % IDLE_REBALANCE_TICK)
return;
spin_lock(&this_rq()->lock);
load_balance(this_rq(), 1);
unsigned long now = jiffies;
runqueue_t *rq = this_rq();
- if (p == rq->idle || !rq->idle)
+ if (p == rq->idle)
return idle_tick();
/* Task might have expired already, but not scheduled off yet */
if (p->array != rq->active) {
* do not update a process's priority until it either
* goes to sleep or uses up its timeslice. This makes
* it possible for interactive tasks to use up their
- * timeslices at their high priority levels.
+ * timeslices at their highest priority levels.
*/
if (p->sleep_avg)
p->sleep_avg--;
*/
asmlinkage void schedule(void)
{
- task_t *prev, *next;
+ task_t *prev = current, *next;
+ runqueue_t *rq = this_rq();
prio_array_t *array;
- runqueue_t *rq;
list_t *queue;
int idx;
if (unlikely(in_interrupt()))
BUG();
-need_resched_back:
- prev = current;
release_kernel_lock(prev, smp_processor_id());
- rq = this_rq();
spin_lock_irq(&rq->lock);
switch (prev->state) {
- case TASK_INTERRUPTIBLE:
- if (unlikely(signal_pending(prev))) {
- prev->state = TASK_RUNNING;
- break;
- }
- default:
- deactivate_task(prev, rq);
- case TASK_RUNNING:
+ case TASK_INTERRUPTIBLE:
+ if (unlikely(signal_pending(prev))) {
+ prev->state = TASK_RUNNING;
+ break;
+ }
+ default:
+ deactivate_task(prev, rq);
+ case TASK_RUNNING:
}
pick_next_task:
if (unlikely(!rq->nr_running)) {
if (likely(prev != next)) {
rq->nr_switches++;
rq->curr = next;
- next->cpu = prev->cpu;
context_switch(prev, next);
/*
* The runqueue pointer might be from another CPU
spin_unlock_irq(&rq->lock);
reacquire_kernel_lock(current);
- if (need_resched())
- goto need_resched_back;
return;
}
*/
void set_cpus_allowed(task_t *p, unsigned long new_mask)
{
- runqueue_t *this_rq = this_rq(), *target_rq;
- unsigned long this_mask = 1UL << smp_processor_id();
- int target_cpu;
-
new_mask &= cpu_online_map;
if (!new_mask)
BUG();
+
p->cpus_allowed = new_mask;
/*
* Can the task run on the current CPU? If not then
* migrate the process off to a proper CPU.
*/
- if (new_mask & this_mask)
+ if (new_mask & (1UL << smp_processor_id()))
return;
- target_cpu = ffz(~new_mask);
- target_rq = cpu_rq(target_cpu);
- if (target_cpu < smp_processor_id()) {
- spin_lock_irq(&target_rq->lock);
- spin_lock(&this_rq->lock);
- } else {
- spin_lock_irq(&this_rq->lock);
- spin_lock(&target_rq->lock);
- }
- dequeue_task(p, p->array);
- this_rq->nr_running--;
- target_rq->nr_running++;
- enqueue_task(p, target_rq->active);
- target_rq->curr->need_resched = 1;
- spin_unlock(&target_rq->lock);
+#if CONFIG_SMP
+ smp_migrate_task(ffz(~new_mask), current);
- /*
- * The easiest solution is to context switch into
- * the idle thread - which will pick the best task
- * afterwards:
- */
- this_rq->nr_switches++;
- this_rq->curr = this_rq->idle;
- this_rq->idle->need_resched = 1;
- context_switch(current, this_rq->idle);
- barrier();
- spin_unlock_irq(&this_rq()->lock);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule();
+#endif
}
void scheduling_functions_end_here(void) { }
* We have to be careful, if called from sys_setpriority(),
* the task might be in the middle of scheduling on another CPU.
*/
- lock_task_rq(rq, p, flags);
+ rq = lock_task_rq(p, &flags);
if (rt_task(p)) {
p->__nice = nice;
goto out_unlock;
if (array) {
enqueue_task(p, array);
/*
- * If the task is runnable and lowered its priority,
+ * If the task is running and lowered its priority,
* or increased its priority then reschedule its CPU:
*/
if ((nice < p->__nice) ||
resched_task(rq->curr);
}
out_unlock:
- unlock_task_rq(rq, p, flags);
+ unlock_task_rq(rq, p, &flags);
}
#ifndef __alpha__
* To be able to change p->policy safely, the apropriate
* runqueue lock must be held.
*/
- lock_task_rq(rq,p,flags);
+ rq = lock_task_rq(p, &flags);
if (policy < 0)
policy = p->policy;
activate_task(p, task_rq(p));
out_unlock:
- unlock_task_rq(rq,p,flags);
+ unlock_task_rq(rq, p, &flags);
out_unlock_tasklist:
read_unlock_irq(&tasklist_lock);
read_unlock(&tasklist_lock);
}
-extern unsigned long wait_init_idle;
-
static inline void double_rq_lock(runqueue_t *rq1, runqueue_t *rq2)
{
if (rq1 == rq2)
spin_lock(&rq1->lock);
else {
- if (rq_cpu(rq1) < rq_cpu(rq2)) {
+ if (rq1 < rq2) {
spin_lock(&rq1->lock);
spin_lock(&rq2->lock);
} else {
this_rq->curr = this_rq->idle = current;
deactivate_task(current, rq);
current->array = NULL;
- current->prio = MAX_PRIO-1;
+ current->prio = MAX_PRIO;
current->state = TASK_RUNNING;
- clear_bit(smp_processor_id(), &wait_init_idle);
double_rq_unlock(this_rq, rq);
- while (wait_init_idle) {
- cpu_relax();
- barrier();
- }
current->need_resched = 1;
- __sti();
+ __restore_flags(flags);
}
extern void init_timervecs(void);
*/
rq = this_rq();
rq->curr = current;
- rq->idle = NULL;
+ rq->idle = current;
wake_up_process(current);
init_timervecs();
/*
* Simple selection loop. We chose the process with the highest
- * number of 'points'. We need the locks to make sure that the
- * list of task structs doesn't change while we look the other way.
+ * number of 'points'. We expect the caller will lock the tasklist.
*
* (not docbooked, we don't want this one cluttering up the manual)
*/
struct task_struct *p = NULL;
struct task_struct *chosen = NULL;
- read_lock(&tasklist_lock);
for_each_task(p) {
if (p->pid) {
int points = badness(p);
}
}
}
- read_unlock(&tasklist_lock);
return chosen;
}
*/
static void oom_kill(void)
{
- struct task_struct *p = select_bad_process(), *q;
+ struct task_struct *p, *q;
+
+ read_lock(&tasklist_lock);
+ p = select_bad_process();
/* Found nothing?!?! Either we hang forever, or we panic. */
if (p == NULL)
panic("Out of memory and no killable processes...\n");
/* kill all processes that share the ->mm (i.e. all threads) */
- read_lock(&tasklist_lock);
for_each_task(q) {
if(q->mm == p->mm) oom_kill_task(q);
}
*/
void out_of_memory(void)
{
- static unsigned long first, last, count;
+ static unsigned long first, last, count, lastkill;
unsigned long now, since;
/*
if (++count < 10)
return;
+ /*
+ * If we just killed a process, wait a while
+ * to give that task a chance to exit. This
+ * avoids killing multiple processes needlessly.
+ */
+ since = now - lastkill;
+ if (since < HZ*5)
+ return;
+
/*
* Ok, really out of memory. Kill something.
*/
+ lastkill = now;
oom_kill();
reset:
#include <linux/pagemap.h>
#include <linux/string.h>
#include <linux/locks.h>
+#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
return 0;
found:
delete_from_swap_cache(page);
- add_to_page_cache(page, info->inode->i_mapping, offset + idx);
+ add_to_page_cache(page, info->vfs_inode.i_mapping, offset + idx);
SetPageDirty(page);
SetPageUptodate(page);
info->swapped--;
inode->i_mapping->a_ops = &shmem_aops;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
info = SHMEM_I(inode);
- info->inode = inode;
spin_lock_init (&info->lock);
sema_init (&info->sem, 1);
+ info->next_index = 0;
+ memset (info->i_direct, 0, sizeof(info->i_direct));
+ info->i_indirect = NULL;
+ info->swapped = 0;
+ info->locked = 0;
switch (mode & S_IFMT) {
default:
init_special_inode(inode, mode, dev);
return sb;
}
+static kmem_cache_t * shmem_inode_cachep;
+static struct inode *shmem_alloc_inode(struct super_block *sb)
+{
+ struct shmem_inode_info *p;
+ p = (struct shmem_inode_info *)kmem_cache_alloc(shmem_inode_cachep, SLAB_KERNEL);
+ if (!p)
+ return NULL;
+ return &p->vfs_inode;
+}
+
+static void shmem_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct shmem_inode_info *p = (struct shmem_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR) {
+ inode_init_once(&p->vfs_inode);
+ }
+}
+
+static int init_inodecache(void)
+{
+ shmem_inode_cachep = kmem_cache_create("shmem_inode_cache",
+ sizeof(struct shmem_inode_info),
+ 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (shmem_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(shmem_inode_cachep))
+ printk(KERN_INFO "shmem_inode_cache: not all structures were freed\n");
+}
static struct address_space_operations shmem_aops = {
writepage: shmem_writepage,
};
static struct super_operations shmem_ops = {
+ alloc_inode: shmem_alloc_inode,
+ destroy_inode: shmem_destroy_inode,
#ifdef CONFIG_TMPFS
statfs: shmem_statfs,
remount_fs: shmem_remount_fs,
int error;
struct vfsmount * res;
- if ((error = register_filesystem(&tmpfs_fs_type))) {
+ error = init_inodecache();
+ if (error)
+ goto out3;
+
+ error = register_filesystem(&tmpfs_fs_type);
+ if (error) {
printk (KERN_ERR "Could not register tmpfs\n");
- return error;
+ goto out2;
}
#ifdef CONFIG_TMPFS
- if ((error = register_filesystem(&shmem_fs_type))) {
+ error = register_filesystem(&shmem_fs_type);
+ if (error) {
printk (KERN_ERR "Could not register shm fs\n");
- return error;
+ goto out1;
}
devfs_mk_dir (NULL, "shm", NULL);
#endif
res = kern_mount(&tmpfs_fs_type);
if (IS_ERR (res)) {
+ error = PTR_ERR(res);
printk (KERN_ERR "could not kern_mount tmpfs\n");
- unregister_filesystem(&tmpfs_fs_type);
- return PTR_ERR(res);
+ goto out;
}
shm_mnt = res;
/* The internal instance should not do size checking */
- if ((error = shmem_set_size(SHMEM_SB(res->mnt_sb), ULONG_MAX, ULONG_MAX)))
- printk (KERN_ERR "could not set limits on internal tmpfs\n");
-
+ shmem_set_size(SHMEM_SB(res->mnt_sb), ULONG_MAX, ULONG_MAX);
return 0;
+
+out:
+#ifdef CONFIG_TMPFS
+ unregister_filesystem(&shmem_fs_type);
+out1:
+#endif
+ unregister_filesystem(&tmpfs_fs_type);
+out2:
+ destroy_inodecache();
+out3:
+ return error;
}
static void __exit exit_shmem_fs(void)
#endif
unregister_filesystem(&tmpfs_fs_type);
mntput(shm_mnt);
+ destroy_inodecache();
}
module_init(init_shmem_fs)
if (page) {
page_cache_get(page);
/* Only cache user (+us), or swap space full? Free it! */
- if (page_count(page) == 2 || vm_swap_full()) {
+ if (page_count(page) - !!page->buffers == 2 || vm_swap_full()) {
delete_from_swap_cache(page);
SetPageDirty(page);
}
obj-$(CONFIG_NETFILTER) += netfilter.o
obj-$(CONFIG_NET_DIVERT) += dv.o
obj-$(CONFIG_NET_PROFILE) += profile.o
+obj-$(CONFIG_NET_RADIO) += wireless.o
+# Ugly. I wish all wireless drivers were moved in drivers/net/wireless
+obj-$(CONFIG_NET_PCMCIA_RADIO) += wireless.o
include $(TOPDIR)/Rules.make
#include <linux/module.h>
#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
#include <linux/wireless.h> /* Note : will define WIRELESS_EXT */
+#include <net/iw_handler.h>
#endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
#ifdef CONFIG_PLIP
extern int plip_init(void);
#endif /* CONFIG_PROC_FS */
-#ifdef WIRELESS_EXT
-#ifdef CONFIG_PROC_FS
-
-/*
- * Print one entry of /proc/net/wireless
- * This is a clone of /proc/net/dev (just above)
- */
-static int sprintf_wireless_stats(char *buffer, struct net_device *dev)
-{
- /* Get stats from the driver */
- struct iw_statistics *stats = (dev->get_wireless_stats ?
- dev->get_wireless_stats(dev) :
- (struct iw_statistics *) NULL);
- int size;
-
- if (stats != (struct iw_statistics *) NULL) {
- size = sprintf(buffer,
- "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d %6d %6d %6d\n",
- dev->name,
- stats->status,
- stats->qual.qual,
- stats->qual.updated & 1 ? '.' : ' ',
- stats->qual.level,
- stats->qual.updated & 2 ? '.' : ' ',
- stats->qual.noise,
- stats->qual.updated & 4 ? '.' : ' ',
- stats->discard.nwid,
- stats->discard.code,
- stats->discard.fragment,
- stats->discard.retries,
- stats->discard.misc,
- stats->miss.beacon);
- stats->qual.updated = 0;
- }
- else
- size = 0;
-
- return size;
-}
-
-/*
- * Print info for /proc/net/wireless (print all entries)
- * This is a clone of /proc/net/dev (just above)
- */
-static int dev_get_wireless_info(char * buffer, char **start, off_t offset,
- int length)
-{
- int len = 0;
- off_t begin = 0;
- off_t pos = 0;
- int size;
-
- struct net_device * dev;
-
- size = sprintf(buffer,
- "Inter-| sta-| Quality | Discarded packets | Missed\n"
- " face | tus | link level noise | nwid crypt frag retry misc | beacon\n"
- );
-
- pos += size;
- len += size;
-
- read_lock(&dev_base_lock);
- for (dev = dev_base; dev != NULL; dev = dev->next) {
- size = sprintf_wireless_stats(buffer + len, dev);
- len += size;
- pos = begin + len;
-
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset + length)
- break;
- }
- read_unlock(&dev_base_lock);
-
- *start = buffer + (offset - begin); /* Start of wanted data */
- len -= (offset - begin); /* Start slop */
- if (len > length)
- len = length; /* Ending slop */
- if (len < 0)
- len = 0;
-
- return len;
-}
-#endif /* CONFIG_PROC_FS */
-
-/*
- * Allow programatic access to /proc/net/wireless even if /proc
- * doesn't exist... Also more efficient...
- */
-static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr)
-{
- /* Get stats from the driver */
- struct iw_statistics *stats = (dev->get_wireless_stats ?
- dev->get_wireless_stats(dev) :
- (struct iw_statistics *) NULL);
-
- if (stats != (struct iw_statistics *) NULL) {
- struct iwreq * wrq = (struct iwreq *)ifr;
-
- /* Copy statistics to the user buffer */
- if(copy_to_user(wrq->u.data.pointer, stats,
- sizeof(struct iw_statistics)))
- return -EFAULT;
-
- /* Check if we need to clear the update flag */
- if(wrq->u.data.flags != 0)
- stats->qual.updated = 0;
- return(0);
- } else
- return -EOPNOTSUPP;
-}
-#endif /* WIRELESS_EXT */
-
/**
* netdev_set_master - set up master/slave pair
* @slave: slave device
notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
return 0;
-#ifdef WIRELESS_EXT
- case SIOCGIWSTATS:
- return dev_iwstats(dev, ifr);
-#endif /* WIRELESS_EXT */
-
/*
* Unknown or private ioctl
*/
return -EOPNOTSUPP;
}
-#ifdef WIRELESS_EXT
- if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
- if (dev->do_ioctl) {
- if (!netif_device_present(dev))
- return -ENODEV;
- return dev->do_ioctl(dev, ifr, cmd);
- }
- return -EOPNOTSUPP;
- }
-#endif /* WIRELESS_EXT */
-
}
return -EINVAL;
}
}
dev_load(ifr.ifr_name);
rtnl_lock();
- ret = dev_ifsioc(&ifr, cmd);
+ /* Follow me in net/core/wireless.c */
+ ret = wireless_process_ioctl(&ifr, cmd);
rtnl_unlock();
if (!ret && IW_IS_GET(cmd) &&
copy_to_user(arg, &ifr, sizeof(struct ifreq)))
proc_net_create("dev", 0, dev_get_info);
create_proc_read_entry("net/softnet_stat", 0, 0, dev_proc_stats, NULL);
#ifdef WIRELESS_EXT
+ /* Available in net/core/wireless.c */
proc_net_create("wireless", 0, dev_get_wireless_info);
#endif /* WIRELESS_EXT */
#endif /* CONFIG_PROC_FS */
--- /dev/null
+/*
+ * This file implement the Wireless Extensions APIs.
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved.
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+
+/************************** DOCUMENTATION **************************/
+/*
+ * API definition :
+ * --------------
+ * See <linux/wireless.h> for details of the APIs and the rest.
+ *
+ * History :
+ * -------
+ *
+ * v1 - 5.12.01 - Jean II
+ * o Created this file.
+ *
+ * v2 - 13.12.01 - Jean II
+ * o Move /proc/net/wireless stuff from net/core/dev.c to here
+ * o Make Wireless Extension IOCTLs go through here
+ * o Added iw_handler handling ;-)
+ * o Added standard ioctl description
+ * o Initial dumb commit strategy based on orinoco.c
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <asm/uaccess.h> /* copy_to_user() */
+#include <linux/config.h> /* Not needed ??? */
+#include <linux/types.h> /* off_t */
+#include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */
+
+#include <linux/wireless.h> /* Pretty obvious */
+#include <net/iw_handler.h> /* New driver API */
+
+/**************************** CONSTANTS ****************************/
+
+/* This will be turned on later on... */
+#undef WE_STRICT_WRITE /* Check write buffer size */
+
+/* Debuging stuff */
+#undef WE_IOCTL_DEBUG /* Debug IOCTL API */
+
+/************************* GLOBAL VARIABLES *************************/
+/*
+ * You should not use global variables, because or re-entrancy.
+ * On our case, it's only const, so it's OK...
+ */
+static const struct iw_ioctl_description standard_ioctl[] = {
+ /* SIOCSIWCOMMIT (internal) */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* SIOCGIWNAME */
+ { IW_HEADER_TYPE_CHAR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+ /* SIOCSIWNWID */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT},
+ /* SIOCGIWNWID */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+ /* SIOCSIWFREQ */
+ { IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT},
+ /* SIOCGIWFREQ */
+ { IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+ /* SIOCSIWMODE */
+ { IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT},
+ /* SIOCGIWMODE */
+ { IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+ /* SIOCSIWSENS */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCGIWSENS */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCSIWRANGE */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* SIOCGIWRANGE */
+ { IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_range), IW_DESCR_FLAG_DUMP},
+ /* SIOCSIWPRIV */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* SIOCGIWPRIV (handled directly by us) */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* SIOCSIWSTATS */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* SIOCGIWSTATS (handled directly by us) */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+ /* SIOCSIWSPY */
+ { IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0},
+ /* SIOCGIWSPY */
+ { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0},
+ /* -- hole -- */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* -- hole -- */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* SIOCSIWAP */
+ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0},
+ /* SIOCGIWAP */
+ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
+ /* -- hole -- */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* SIOCGIWAPLIST */
+ { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, 0},
+ /* -- hole -- */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* -- hole -- */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* SIOCSIWESSID */
+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_EVENT},
+ /* SIOCGIWESSID */
+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_DUMP},
+ /* SIOCSIWNICKN */
+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0},
+ /* SIOCGIWNICKN */
+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0},
+ /* -- hole -- */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* -- hole -- */
+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+ /* SIOCSIWRATE */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCGIWRATE */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCSIWRTS */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCGIWRTS */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCSIWFRAG */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCGIWFRAG */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCSIWTXPOW */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCGIWTXPOW */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCSIWRETRY */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCGIWRETRY */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCSIWENCODE */
+ { IW_HEADER_TYPE_POINT, 4, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT},
+ /* SIOCGIWENCODE */
+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT},
+ /* SIOCSIWPOWER */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+ /* SIOCGIWPOWER */
+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
+};
+
+/* Size (in bytes) of the various private data types */
+char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 };
+
+/************************ COMMON SUBROUTINES ************************/
+/*
+ * Stuff that may be used in various place or doesn't fit in one
+ * of the section below.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Return the driver handler associated with a specific Wireless Extension.
+ * Called from various place, so make sure it remains efficient.
+ */
+static inline iw_handler get_handler(struct net_device *dev,
+ unsigned int cmd)
+{
+ unsigned int index; /* MUST be unsigned */
+
+ /* Check if we have some wireless handlers defined */
+ if(dev->wireless_handlers == NULL)
+ return NULL;
+
+ /* Try as a standard command */
+ index = cmd - SIOCIWFIRST;
+ if(index < dev->wireless_handlers->num_standard)
+ return dev->wireless_handlers->standard[index];
+
+ /* Try as a private command */
+ index = cmd - SIOCIWFIRSTPRIV;
+ if(index < dev->wireless_handlers->num_private)
+ return dev->wireless_handlers->private[index];
+
+ /* Not found */
+ return NULL;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Get statistics out of the driver
+ */
+static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
+{
+ return (dev->get_wireless_stats ?
+ dev->get_wireless_stats(dev) :
+ (struct iw_statistics *) NULL);
+ /* In the future, get_wireless_stats may move from 'struct net_device'
+ * to 'struct iw_handler_def', to de-bloat struct net_device.
+ * Definitely worse a thought... */
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call the commit handler in the driver
+ * (if exist and if conditions are right)
+ *
+ * Note : our current commit strategy is currently pretty dumb,
+ * but we will be able to improve on that...
+ * The goal is to try to agreagate as many changes as possible
+ * before doing the commit. Drivers that will define a commit handler
+ * are usually those that need a reset after changing parameters, so
+ * we want to minimise the number of reset.
+ * A cool idea is to use a timer : at each "set" command, we re-set the
+ * timer, when the timer eventually fires, we call the driver.
+ * Hopefully, more on that later.
+ *
+ * Also, I'm waiting to see how many people will complain about the
+ * netif_running(dev) test. I'm open on that one...
+ * Hopefully, the driver will remember to do a commit in "open()" ;-)
+ */
+static inline int call_commit_handler(struct net_device * dev)
+{
+ if((netif_running(dev)) &&
+ (dev->wireless_handlers->standard[0] != NULL)) {
+ /* Call the commit handler on the driver */
+ return dev->wireless_handlers->standard[0](dev, NULL,
+ NULL, NULL);
+ } else
+ return 0; /* Command completed successfully */
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Number of private arguments
+ */
+static inline int get_priv_size(__u16 args)
+{
+ int num = args & IW_PRIV_SIZE_MASK;
+ int type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+ return num * priv_type_size[type];
+}
+
+
+/******************** /proc/net/wireless SUPPORT ********************/
+/*
+ * The /proc/net/wireless file is a human readable user-space interface
+ * exporting various wireless specific statistics from the wireless devices.
+ * This is the most popular part of the Wireless Extensions ;-)
+ *
+ * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
+ * The content of the file is basically the content of "struct iw_statistics".
+ */
+
+#ifdef CONFIG_PROC_FS
+
+/* ---------------------------------------------------------------- */
+/*
+ * Print one entry (line) of /proc/net/wireless
+ */
+static inline int sprintf_wireless_stats(char *buffer, struct net_device *dev)
+{
+ /* Get stats from the driver */
+ struct iw_statistics *stats;
+ int size;
+
+ stats = get_wireless_stats(dev);
+ if (stats != (struct iw_statistics *) NULL) {
+ size = sprintf(buffer,
+ "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d %6d %6d %6d\n",
+ dev->name,
+ stats->status,
+ stats->qual.qual,
+ stats->qual.updated & 1 ? '.' : ' ',
+ stats->qual.level,
+ stats->qual.updated & 2 ? '.' : ' ',
+ stats->qual.noise,
+ stats->qual.updated & 4 ? '.' : ' ',
+ stats->discard.nwid,
+ stats->discard.code,
+ stats->discard.fragment,
+ stats->discard.retries,
+ stats->discard.misc,
+ stats->miss.beacon);
+ stats->qual.updated = 0;
+ }
+ else
+ size = 0;
+
+ return size;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Print info for /proc/net/wireless (print all entries)
+ */
+int dev_get_wireless_info(char * buffer, char **start, off_t offset,
+ int length)
+{
+ int len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+ int size;
+
+ struct net_device * dev;
+
+ size = sprintf(buffer,
+ "Inter-| sta-| Quality | Discarded packets | Missed\n"
+ " face | tus | link level noise | nwid crypt frag retry misc | beacon\n"
+ );
+
+ pos += size;
+ len += size;
+
+ read_lock(&dev_base_lock);
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ size = sprintf_wireless_stats(buffer + len, dev);
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ break;
+ }
+ read_unlock(&dev_base_lock);
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if (len > length)
+ len = length; /* Ending slop */
+ if (len < 0)
+ len = 0;
+
+ return len;
+}
+#endif /* CONFIG_PROC_FS */
+
+/************************** IOCTL SUPPORT **************************/
+/*
+ * The original user space API to configure all those Wireless Extensions
+ * is through IOCTLs.
+ * In there, we check if we need to call the new driver API (iw_handler)
+ * or just call the driver ioctl handler.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Allow programatic access to /proc/net/wireless even if /proc
+ * doesn't exist... Also more efficient...
+ */
+static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr)
+{
+ /* Get stats from the driver */
+ struct iw_statistics *stats;
+
+ stats = get_wireless_stats(dev);
+ if (stats != (struct iw_statistics *) NULL) {
+ struct iwreq * wrq = (struct iwreq *)ifr;
+
+ /* Copy statistics to the user buffer */
+ if(copy_to_user(wrq->u.data.pointer, stats,
+ sizeof(struct iw_statistics)))
+ return -EFAULT;
+
+ /* Check if we need to clear the update flag */
+ if(wrq->u.data.flags != 0)
+ stats->qual.updated = 0;
+ return 0;
+ } else
+ return -EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Export the driver private handler definition
+ * They will be picked up by tools like iwpriv...
+ */
+static inline int ioctl_export_private(struct net_device * dev,
+ struct ifreq * ifr)
+{
+ struct iwreq * iwr = (struct iwreq *) ifr;
+
+ /* Check if the driver has something to export */
+ if((dev->wireless_handlers->num_private_args == 0) ||
+ (dev->wireless_handlers->private_args == NULL))
+ return -EOPNOTSUPP;
+
+ /* Check NULL pointer */
+ if(iwr->u.data.pointer == NULL)
+ return -EFAULT;
+#ifdef WE_STRICT_WRITE
+ /* Check if there is enough buffer up there */
+ if(iwr->u.data.length < (SIOCIWLASTPRIV - SIOCIWFIRSTPRIV + 1))
+ return -E2BIG;
+#endif /* WE_STRICT_WRITE */
+
+ /* Set the number of available ioctls. */
+ iwr->u.data.length = dev->wireless_handlers->num_private_args;
+
+ /* Copy structure to the user buffer. */
+ if (copy_to_user(iwr->u.data.pointer,
+ dev->wireless_handlers->private_args,
+ sizeof(struct iw_priv_args) * iwr->u.data.length))
+ return -EFAULT;
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a standard Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ */
+static inline int ioctl_standard_call(struct net_device * dev,
+ struct ifreq * ifr,
+ unsigned int cmd,
+ iw_handler handler)
+{
+ struct iwreq * iwr = (struct iwreq *) ifr;
+ const struct iw_ioctl_description * descr;
+ struct iw_request_info info;
+ int ret = -EINVAL;
+
+ /* Get the description of the IOCTL */
+ descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "%s : Found standard handler for 0x%04X\n",
+ ifr->ifr_name, cmd);
+ printk(KERN_DEBUG "Header type : %d, token type : %d, token_size : %d, max_token : %d\n", descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif /* WE_IOCTL_DEBUG */
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have a pointer to user space data or not */
+ if(descr->header_type != IW_HEADER_TYPE_POINT) {
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, &(iwr->u), NULL);
+ } else {
+ char * extra;
+ int err;
+
+ /* Check what user space is giving us */
+ if(IW_IS_SET(cmd)) {
+ /* Check NULL pointer */
+ if((iwr->u.data.pointer == NULL) &&
+ (iwr->u.data.length != 0))
+ return -EFAULT;
+ /* Check if number of token fits within bounds */
+ if(iwr->u.data.length > descr->max_tokens)
+ return -E2BIG;
+ if(iwr->u.data.length < descr->min_tokens)
+ return -EINVAL;
+ } else {
+ /* Check NULL pointer */
+ if(iwr->u.data.pointer == NULL)
+ return -EFAULT;
+#ifdef WE_STRICT_WRITE
+ /* Check if there is enough buffer up there */
+ if(iwr->u.data.length < descr->max_tokens)
+ return -E2BIG;
+#endif /* WE_STRICT_WRITE */
+ }
+
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "Malloc %d bytes\n",
+ descr->max_tokens * descr->token_size);
+#endif /* WE_IOCTL_DEBUG */
+
+ /* Always allocate for max space. Easier, and won't last
+ * long... */
+ extra = kmalloc(descr->max_tokens * descr->token_size,
+ GFP_KERNEL);
+ if (extra == NULL) {
+ return -ENOMEM;
+ }
+
+ /* If it is a SET, get all the extra data in here */
+ if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+ err = copy_from_user(extra, iwr->u.data.pointer,
+ iwr->u.data.length *
+ descr->token_size);
+ if (err) {
+ kfree(extra);
+ return -EFAULT;
+ }
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "Got %d bytes\n",
+ iwr->u.data.length * descr->token_size);
+#endif /* WE_IOCTL_DEBUG */
+ }
+
+ /* Call the handler */
+ ret = handler(dev, &info, &(iwr->u), extra);
+
+ /* If we have something to return to the user */
+ if (!ret && IW_IS_GET(cmd)) {
+ err = copy_to_user(iwr->u.data.pointer, extra,
+ iwr->u.data.length *
+ descr->token_size);
+ if (err)
+ ret = -EFAULT;
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "Wrote %d bytes\n",
+ iwr->u.data.length * descr->token_size);
+#endif /* WE_IOCTL_DEBUG */
+ }
+
+ /* Cleanup - I told you it wasn't that long ;-) */
+ kfree(extra);
+ }
+
+ /* Call commit handler if needed and defined */
+ if(ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ /* Here, we will generate the appropriate event if needed */
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int ioctl_private_call(struct net_device * dev,
+ struct ifreq * ifr,
+ unsigned int cmd,
+ iw_handler handler)
+{
+ struct iwreq * iwr = (struct iwreq *) ifr;
+ struct iw_priv_args * descr = NULL;
+ struct iw_request_info info;
+ int extra_size = 0;
+ int i;
+ int ret = -EINVAL;
+
+ /* Get the description of the IOCTL */
+ for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
+ if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+ descr = &(dev->wireless_handlers->private_args[i]);
+ break;
+ }
+
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "%s : Found private handler for 0x%04X\n",
+ ifr->ifr_name, cmd);
+ if(descr) {
+ printk(KERN_DEBUG "Name %s, set %X, get %X\n",
+ descr->name, descr->set_args, descr->get_args);
+ }
+#endif /* WE_IOCTL_DEBUG */
+
+ /* Compute the size of the set/get arguments */
+ if(descr != NULL) {
+ if(IW_IS_SET(cmd)) {
+ /* Size of set arguments */
+ extra_size = get_priv_size(descr->set_args);
+
+ /* Does it fits in iwr ? */
+ if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+ (extra_size < IFNAMSIZ))
+ extra_size = 0;
+ } else {
+ /* Size of set arguments */
+ extra_size = get_priv_size(descr->get_args);
+
+ /* Does it fits in iwr ? */
+ if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+ (extra_size < IFNAMSIZ))
+ extra_size = 0;
+ }
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have a pointer to user space data or not. */
+ if(extra_size == 0) {
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
+ } else {
+ char * extra;
+ int err;
+
+ /* Check what user space is giving us */
+ if(IW_IS_SET(cmd)) {
+ /* Check NULL pointer */
+ if((iwr->u.data.pointer == NULL) &&
+ (iwr->u.data.length != 0))
+ return -EFAULT;
+
+ /* Does it fits within bounds ? */
+ if(iwr->u.data.length > (descr->set_args &
+ IW_PRIV_SIZE_MASK))
+ return -E2BIG;
+ } else {
+ /* Check NULL pointer */
+ if(iwr->u.data.pointer == NULL)
+ return -EFAULT;
+ }
+
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "Malloc %d bytes\n", extra_size);
+#endif /* WE_IOCTL_DEBUG */
+
+ /* Always allocate for max space. Easier, and won't last
+ * long... */
+ extra = kmalloc(extra_size, GFP_KERNEL);
+ if (extra == NULL) {
+ return -ENOMEM;
+ }
+
+ /* If it is a SET, get all the extra data in here */
+ if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+ err = copy_from_user(extra, iwr->u.data.pointer,
+ extra_size);
+ if (err) {
+ kfree(extra);
+ return -EFAULT;
+ }
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "Got %d elem\n", iwr->u.data.length);
+#endif /* WE_IOCTL_DEBUG */
+ }
+
+ /* Call the handler */
+ ret = handler(dev, &info, &(iwr->u), extra);
+
+ /* If we have something to return to the user */
+ if (!ret && IW_IS_GET(cmd)) {
+ err = copy_to_user(iwr->u.data.pointer, extra,
+ extra_size);
+ if (err)
+ ret = -EFAULT;
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "Wrote %d elem\n",
+ iwr->u.data.length);
+#endif /* WE_IOCTL_DEBUG */
+ }
+
+ /* Cleanup - I told you it wasn't that long ;-) */
+ kfree(extra);
+ }
+
+
+ /* Call commit handler if needed and defined */
+ if(ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main IOCTl dispatcher. Called from the main networking code
+ * (dev_ioctl() in net/core/dev.c).
+ * Check the type of IOCTL and call the appropriate wrapper...
+ */
+int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
+{
+ struct net_device *dev;
+ iw_handler handler;
+
+ /* Permissions are already checked in dev_ioctl() before calling us.
+ * The copy_to/from_user() of ifr is also dealt with in there */
+
+ /* Make sure the device exist */
+ if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
+ return -ENODEV;
+
+ /* A bunch of special cases, then the generic case...
+ * Note that 'cmd' is already filtered in dev_ioctl() with
+ * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
+ switch(cmd)
+ {
+ case SIOCGIWSTATS:
+ /* Get Wireless Stats */
+ return dev_iwstats(dev, ifr);
+
+ case SIOCGIWPRIV:
+ /* Check if we have some wireless handlers defined */
+ if(dev->wireless_handlers != NULL) {
+ /* We export to user space the definition of
+ * the private handler ourselves */
+ return ioctl_export_private(dev, ifr);
+ }
+ // ## Fall-through for old API ##
+ default:
+ /* Generic IOCTL */
+ /* Basic check */
+ if (!netif_device_present(dev))
+ return -ENODEV;
+ /* New driver API : try to find the handler */
+ handler = get_handler(dev, cmd);
+ if(handler != NULL) {
+ /* Standard and private are not the same */
+ if(cmd < SIOCIWFIRSTPRIV)
+ return ioctl_standard_call(dev,
+ ifr,
+ cmd,
+ handler);
+ else
+ return ioctl_private_call(dev,
+ ifr,
+ cmd,
+ handler);
+ }
+ /* Old driver API : call driver ioctl handler */
+ if (dev->do_ioctl) {
+ return dev->do_ioctl(dev, ifr, cmd);
+ }
+ return -EOPNOTSUPP;
+ }
+ /* Not reached */
+ return -EINVAL;
+}
module_init(khttpd_init)
module_exit(khttpd_cleanup)
+
+MODULE_LICENSE("GPL");
module_init(netlink_proto_init);
module_exit(netlink_proto_exit);
+
+MODULE_LICENSE("GPL");