N: David Hinds
E: dhinds@zen.stanford.edu
-W: http://hyper.stanford.edu/~dhinds
+W: http://tao.stanford.edu/~dhinds
D: PCMCIA and CardBus stuff, PCMCIA-HOWTO, PCMCIA client drivers
S: 2019 W. Middlefield Rd #1
S: Mountain View, CA 94043
S: Czech Republic
N: Paul Mackerras
-E: paulus@cs.anu.edu.au
+E: paulus@linuxcare.com
D: Linux port for PCI Power Macintosh
-S: Dept. of Computer Science
-S: Australian National University
-S: Canberra ACT 0200
+S: Linuxcare, Inc.
+S: 24 Marcus Clarke Street
+S: Canberra ACT 2601
S: Australia
N: Pat Mackinlay
- NFS 2.2beta40 ; showmount --version
- Bash 1.14.7 ; bash -version
- Ncpfs 2.2.0 ; ncpmount -v
-- Pcmcia-cs 3.0.7 ; cardmgr -V
+- Pcmcia-cs 3.1.2 ; cardmgr -V
- PPP 2.3.9 ; pppd --version
- Util-linux 2.9i ; chsh -v
Pcmcia-cs
=========
-The 3.0.7 release:
-ftp://hyper.stanford.edu/pub/pcmcia/pcmcia-cs.3.0.7.tar.gz
+The 3.1.2 release:
+ftp://sourceforge.org/pcmcia/pcmcia-cs-3.1.2.tar.gz
Setserial
=========
If unsure, say Y.
-PCMCIA/Cardbus support
+PCMCIA/CardBus support
CONFIG_PCMCIA
- Say Y here if you want to attach PCMCIA's (PC-cards) to your Linux
- computer. These are credit-card size devices such as network cards,
- modems or hard drives popular with laptops.
+ Include kernel support for PCMCIA and CardBus devices. Because
+ PCMCIA support requires additional components that are not part of
+ the kernel (i.e., the pcmcia-cs package), building PCMCIA into the
+ kernel is generally not recommended unless you have a specific
+ need. If unsure, say N.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ When compiled this way, there will be modules called pcmcia_core.o
+ and ds.o. If you want to compile it as a module, say M here and
+ read Documentation/modules.txt.
You will also need David Hinds' pcmcia-cs package (see the file
- Documentation/Changes for location).
+ Documentation/Changes for location). For more information, see the
+ PCMCIA-HOWTO.
CardBus support
CONFIG_CARDBUS
- CardBus is a bus mastering architecture for PC-cards (it allows
- PC-cards to talk to the rest of the stuff inside your computer). If
- unsure, say Y.
+ There are two types of PCMCIA devices: 16-bit PC Cards, and higher
+ performance 32-bit CardBus devices. Use this option to include
+ support for CardBus devices. If unsure, say Y.
+
+i82365/Yenta compatible bridge support
+CONFIG_I82365
+ Include support for PCMCIA and CardBus host bridges that are
+ register compatible with the Intel i82365 and/or the Yenta
+ specification: this includes virtually all modern PCMCIA bridges.
+ If unsure, say Y.
+
+Databook TCIC host bridge support
+CONFIG_TCIC
+ Include support for the Databook TCIC family of PCMCIA host bridges.
+ These are only found on a handful of old systems. If unsure, say N.
System V IPC
CONFIG_SYSVIPC
say M here and read Documentation/modules.txt. The module will be
called x25_asy.o. If unsure, say N.
-PCMCIA ethernet cards (NE2000 compatibles: DE-650, ...)
+PCMCIA network device support
+CONFIG_NET_PCMCIA
+ Say Y if you would like to include support for any PCMCIA network
+ adapters. If unsure, say N.
+
+3Com 3c589 PCMCIA support
+CONFIG_PCMCIA_3C589
+ Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA
+ (PC-card) Ethernet card to your computer.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called 3c589_cs.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt. If unsure,
+ say N.
+
+3Com 3c574 PCMCIA support
+CONFIG_PCMCIA_3C574
+ Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA
+ (PC-card) Fast Ethernet card to your computer.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called 3c574_cs.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt. If unsure,
+ say N.
+
+Fujitsu FMV-J18x PCMCIA support
+CONFIG_PCMCIA_FMVJ18X
+ Say Y here if you intend to attach a Fujitsu FMV-J18x or compatible
+ PCMCIA (PC-card) Ethernet card to your computer.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called fmvj18x_cs.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt. If unsure,
+ say N.
+
+NE2000 compatible PCMCIA support
CONFIG_PCMCIA_PCNET
Say Y here if you intend to attach an NE2000 compatible PCMCIA
- (PC-card) Ethernet networking card to your computer.
+ (PC-card) Ethernet or Fast Ethernet card to your computer.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
module, say M here and read Documentation/modules.txt. If unsure,
say N.
-3Com 3c589 PCMCIA card
-CONFIG_PCMCIA_3C589
- Say Y here if you intend to attach a 3Com 3c589 PCMCIA
- (PC-card) Ethernet networking card to your computer.
+New Media PCMCIA support
+CONFIG_PCMCIA_NMCLAN
+ Say Y here if you intend to attach a New Media Ethernet or LiveWire
+ PCMCIA (PC-card) Ethernet card to your computer.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
- The module will be called 3c589_cs.o. If you want to compile it as a
+ The module will be called nmclan_cs.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt. If unsure,
+ say N.
+
+SMC 91Cxx PCMCIA support
+CONFIG_PCMCIA_SMC91C92
+ Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
+ (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called smc91c92_cs.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt. If unsure,
+ say N.
+
+Xircom 16-bit PCMCIA support
+CONFIG_PCMCIA_XIRC2PS
+ Say Y here if you intend to attach a Xircom 16-bit PCMCIA
+ (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called xirc2ps_cs.o. If you want to compile it as a
module, say M here and read Documentation/modules.txt. If unsure,
say N.
-Aviator/Raytheon 2.4MHz wireless
+Aviator/Raytheon 2.4MHz wireless support
CONFIG_PCMCIA_RAYCS
Say Y here if you intend to attach an Aviator/Raytheon PCMCIA
(PC-card) wireless Ethernet networking card to your computer.
module, say M here and read Documentation/modules.txt. If unsure,
say N.
+Xircom Netwave AirSurfer wireless support
+CONFIG_PCMCIA_NETWAVE
+ Say Y here if you intend to attach a Xircom Netwave AirSurfer PCMCIA
+ (PC-card) wireless Ethernet networking card to your computer.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called netwave_cs.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt. If unsure,
+ say N.
+
+AT&T/Lucent Wavelan wireless support
+CONFIG_PCMCIA_WAVELAN
+ Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA
+ (PC-card) wireless Ethernet networking card to your computer. This
+ driver is for the non-IEEE-802.11 Wavelan cards.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called wavelan_cs.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt. If unsure,
+ say N.
+
PLIP (parallel port) support
CONFIG_PLIP
PLIP (Parallel Line Internet Protocol) is used to create a
W: http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi
S: Maintained
+i386 BOOT CODE
+P: Riley H. Williams
+M: rhw@memalpha.cx
+L: Linux-Kernel@vger.rutgers.edu
+S: Maintained
+
IBM MCA SCSI SUBSYSTEM DRIVER
P: Michael Lang
M: langa2@kph.uni-mainz.de
LINUX FOR POWER MACINTOSH
P: Paul Mackerras
-M: paulus@cs.anu.edu.au
-L: linux-pmac@samba.anu.edu.au
+M: paulus@linuxcare.com
+W: http://www.linuxppc.org/
+L: linuxppc-dev@lists.linuxppc.org
S: Maintained
M68K
P: David Hinds
M: dhinds@zen.stanford.edu
L: linux-kernel@vger.rutgers.edu
+W: http://pcmcia.sourceforge.org
S: Maintained
PCNET32 NETWORK DRIVER
makecrc();
puts("Uncompressing Linux...");
gunzip();
- puts("done.\nNow booting the kernel\n");
+ puts(" done, booting the kernel.\n");
return output_ptr;
}
#else
void show_mem(void)
{
- extern void show_net_buffers(void);
- int i,free = 0,total = 0,reserved = 0;
- int shared = 0;
+ int free = 0, total = 0, reserved = 0;
+ int shared = 0, cached = 0;
+ struct page *page, *end;
printk("Mem-info:\n");
show_free_areas();
printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
- i = MAP_NR(high_memory);
- while (i-- > 0) {
+ for (page = mem_map, end = mem_map + max_mapnr;
+ page < end; page++) {
+ if (PageSkip(page)) {
+ if (page->next_hash < page)
+ break;
+ page = page->next_hash;
+ }
total++;
- if (PageReserved(mem_map+i))
+ if (PageReserved(page))
reserved++;
- else if (!atomic_read(&mem_map[i].count))
+ else if (PageSwapCache(page))
+ cached++;
+ else if (!atomic_read(&page->count))
free++;
else
- shared += atomic_read(&mem_map[i].count) - 1;
+ shared += atomic_read(&page->count) - 1;
}
- printk("%d pages of RAM\n",total);
- printk("%d free pages\n",free);
- printk("%d reserved pages\n",reserved);
- printk("%d pages shared\n",shared);
+ printk("%d pages of RAM\n", total);
+ printk("%d free pages\n", free);
+ printk("%d reserved pages\n", reserved);
+ printk("%d pages shared\n", shared);
+ printk("%d pages swap cached\n", cached);
+#ifndef CONFIG_NO_PGT_CACHE
+ printk("%ld page tables cached\n", pgtable_cache_size);
+#endif
show_buffers();
#ifdef CONFIG_NET
show_net_buffers();
# CONFIG_MCA is not set
#
-# PCMCIA/Cardbus support
+# PCMCIA/CardBus support
#
CONFIG_PCMCIA=y
CONFIG_CARDBUS=y
+CONFIG_I82365=y
+# CONFIG_TCIC is not set
CONFIG_SYSVIPC=y
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_SYSCTL=y
# CONFIG_WAN is not set
#
-# PCMCIA network devices
+# PCMCIA network device support
#
-CONFIG_PCMCIA_PCNET=y
+CONFIG_NET_PCMCIA=y
# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=y
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
CONFIG_PCMCIA_RAYCS=y
+# CONFIG_PCMCIA_NETWAVE is not set
+# CONFIG_PCMCIA_WAVELAN is not set
CONFIG_PCMCIA_NETCARD=y
#
} /* add_memory_region */
-#define LOWMEMSIZE() ((*(unsigned short *)__va(0x413)) * 1024)
+/*
+ * Do NOT EVER look at the BIOS memory size location.
+ * It does not work on many machines.
+ */
+#define LOWMEMSIZE() (0x9f000)
void __init setup_memory_region(void)
{
/*********************************************************** SETUP */
/* From arch/m68k/kernel/setup.c. */
-void __init apus_setup_arch(unsigned long * memory_start_p,
- unsigned long * memory_end_p)
+void __init apus_setup_arch(void)
{
#ifdef CONFIG_APUS
extern char cmd_line[];
#include <linux/version.h>
#include <linux/adb.h>
#include <linux/module.h>
+#include <linux/delay.h>
#include <asm/mmu.h>
#include <asm/processor.h>
void __init
-chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
+chrp_setup_arch(void)
{
extern char cmd_line[];
struct device_node *device;
#ifdef CONFIG_DUMMY_CONSOLE
conswitchp = &dummy_con;
#endif
- *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
+ pmac_find_bridges();
/* Get the event scan rate for the rtas so we know how
* often it expects a heartbeat. -- Cort
irq = *chrp_int_ack_special;
else
irq = i8259_irq( smp_processor_id() );
- /*
- * Acknowledge as soon as possible to allow i8259
- * interrupt nesting */
- openpic_eoi( smp_processor_id() );
+ openpic_eoi( smp_processor_id() );
}
if (irq == OPENPIC_VEC_SPURIOUS)
/*
* acknowledged
*/
irq = -1;
+ /*
+ * I would like to openpic_eoi here but there seem to be timing problems
+ * between the openpic ack and the openpic eoi.
+ * -- Cort
+ */
return irq;
}
/*
* If it's an i8259 irq then we've already done the
* openpic irq. So we just check to make sure the controller
- * is an openpic and if it is then eoi -- Cort
+ * is an openpic and if it is then eoi
+ *
+ * We do it this way since our irq_desc[irq].ctl can change
+ * with RTL and no longer be open_pic -- Cort
*/
- if ( irq_desc[irq].ctl == &open_pic )
+ if ( irq >= open_pic.irq_offset)
openpic_eoi( smp_processor_id() );
}
-#if 0
-void
-chrp_do_IRQ(struct pt_regs *regs,
- int cpu,
- int isfake)
-{
- int irq;
- unsigned long bits = 0;
- int openpic_eoi_done = 0;
-
-#ifdef __SMP__
- {
- unsigned int loops = 1000000;
- while (test_bit(0, &global_irq_lock)) {
- if (smp_processor_id() == global_irq_holder) {
- printk("uh oh, interrupt while we hold global irq lock!\n");
-#ifdef CONFIG_XMON
- xmon(0);
-#endif
- break;
- }
- if (loops-- == 0) {
- printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder);
-#ifdef CONFIG_XMON
- xmon(0);
-#endif
- }
- }
- }
-#endif /* __SMP__ */
-
- irq = openpic_irq(0);
- if (irq == IRQ_8259_CASCADE)
- {
- /*
- * This magic address generates a PCI IACK cycle.
- *
- * This should go in the above mask/ack code soon. -- Cort
- */
- if ( chrp_int_ack_special )
- irq = *chrp_int_ack_special;
- else
- irq = i8259_irq(0);
- /*
- * Acknowledge as soon as possible to allow i8259
- * interrupt nesting */
- openpic_eoi(0);
- openpic_eoi_done = 1;
- }
- if (irq == OPENPIC_VEC_SPURIOUS)
- {
- /*
- * Spurious interrupts should never be
- * acknowledged
- */
- ppc_spurious_interrupts++;
- openpic_eoi_done = 1;
- goto out;
- }
- bits = 1UL << irq;
-
- if (irq < 0)
- {
- printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
- irq, regs->nip);
- ppc_spurious_interrupts++;
- }
- else
- {
- ppc_irq_dispatch_handler( regs, irq );
- }
-out:
- if (!openpic_eoi_done)
- openpic_eoi(0);
-}
-#endif
-
void __init chrp_init_IRQ(void)
{
struct device_node *np;
extern char cmd_line[];
-void __init gemini_setup_arch(unsigned long *memstart, unsigned long *memend)
+void __init gemini_setup_arch(void)
{
unsigned int cpu;
extern char cmd_line[];
/*
- * $Id: idle.c,v 1.67 1999/09/10 05:05:47 paulus Exp $
+ * $Id: idle.c,v 1.68 1999/10/15 18:16:03 cort Exp $
*
* Idle daemon for PowerPC. Idle daemon will handle any action
* that needs to be taken when the system becomes idle.
/* set the POW bit in the MSR, and enable interrupts
* so we wake up sometime! */
+ __sti(); /* this keeps rtl from getting confused -- Cort */
_nmask_and_or_msr(0, MSR_POW | MSR_EE);
-
- /* Disable interrupts again so restore_flags will
- * work. */
- _nmask_and_or_msr(MSR_EE, 0);
}
restore_flags(msr);
default:
irq = ppc_md.get_irq( regs );
if ( irq < 0 )
{
- printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
- irq, regs->nip);
- ppc_spurious_interrupts++;
- return;
+ /* -2 means ignore, already handled */
+ if (irq != -2) {
+ printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
+ irq, regs->nip);
+ ppc_spurious_interrupts++;
+ }
+ goto out;
}
ppc_irq_dispatch_handler( regs, irq );
if ( ppc_md.post_irq )
ppc_md.post_irq( irq );
-
+
+ out:
hardirq_exit( cpu );
}
#include <linux/blk.h>
#include <linux/ioport.h>
#include <linux/ide.h>
+#include <linux/bootmem.h>
#include <asm/mmu.h>
#include <asm/processor.h>
}
void __init
-m8xx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
+m8xx_setup_arch(void)
{
int cpm_page;
extern char cmd_line[];
- cpm_page = *memory_start_p;
- *memory_start_p += PAGE_SIZE;
+ cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE);
printk("Boot arguments: %s\n", cmd_line);
rd_doload = 1;
rd_image_start = 0;
#endif
+#if 0 /* XXX this may need to be updated for the new bootmem stuff,
+ or possibly just deleted (see set_phys_avail() in init.c).
+ - paulus. */
/* initrd_start and size are setup by boot/head.S and kernel/head.S */
if ( initrd_start )
{
}
}
#endif
+#endif
}
void
}
void __init
-mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
+mbx_setup_arch(void)
{
int cpm_page;
extern char cmd_line[];
- cpm_page = *memory_start_p;
- *memory_start_p += PAGE_SIZE;
+ cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE);
sprintf(cmd_line,
"%s root=/dev/nfs nfsroot=/sys/mbxroot",
rd_doload = 1;
rd_image_start = 0;
#endif
+#if 0 /* XXX this may need to be updated for the new bootmem stuff,
+ or possibly just deleted (see set_phys_avail() in init.c).
+ - paulus. */
/* initrd_start and size are setup by boot/head.S and kernel/head.S */
if ( initrd_start )
{
}
}
#endif
+#endif
#ifdef notdef
request_region(0x20,0x20,"pic1");
* snoop from the data cache.
* This is a no-op on the 601 which has a unified cache.
*
- * void flush_page_to_ram(void *page)
+ * void __flush_page_to_ram(void *page)
*/
-_GLOBAL(flush_page_to_ram)
+_GLOBAL(__flush_page_to_ram)
mfspr r5,PVR
rlwinm r5,r5,16,16,31
cmpi 0,r5,1
{
return 0;
}
+
+/* the next two are stolen from the alpha port... */
+void __init
+pcibios_update_resource(struct pci_dev *dev, struct resource *root,
+ struct resource *res, int resource)
+{
+ unsigned long where, size;
+ u32 reg;
+
+ where = PCI_BASE_ADDRESS_0 + (resource * 4);
+ size = res->end - res->start;
+ pci_read_config_dword(dev, where, ®);
+ reg = (reg & size) | (((u32)(res->start - root->start)) & ~size);
+ pci_write_config_dword(dev, where, reg);
+}
+
+void __init
+pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+ /* XXX FIXME - update OF device tree node interrupt property */
+}
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
+#include <linux/bootmem.h>
#include <asm/init.h>
#include <asm/io.h>
struct bridge_data **bridges, *bridge_list;
static int max_bus;
-static void add_bridges(struct device_node *dev, unsigned long *mem_ptr);
+static void add_bridges(struct device_node *dev);
/*
* Magic constants for enabling cache coherency in the bandit/PSX bridge.
bp->io_base);
}
-unsigned long __init pmac_find_bridges(unsigned long mem_start, unsigned long mem_end)
+void __init pmac_find_bridges(void)
{
int bus;
struct bridge_data *bridge;
bridge_list = 0;
max_bus = 0;
- add_bridges(find_devices("bandit"), &mem_start);
- add_bridges(find_devices("chaos"), &mem_start);
- add_bridges(find_devices("pci"), &mem_start);
- bridges = (struct bridge_data **) mem_start;
- mem_start += (max_bus + 1) * sizeof(struct bridge_data *);
+ add_bridges(find_devices("bandit"));
+ add_bridges(find_devices("chaos"));
+ add_bridges(find_devices("pci"));
+ bridges = (struct bridge_data **)
+ alloc_bootmem((max_bus + 1) * sizeof(struct bridge_data *));
memset(bridges, 0, (max_bus + 1) * sizeof(struct bridge_data *));
for (bridge = bridge_list; bridge != NULL; bridge = bridge->next)
for (bus = bridge->bus_number; bus <= bridge->max_bus; ++bus)
bridges[bus] = bridge;
-
- return mem_start;
}
/*
* "pci" (a MPC106) and no bandit or chaos bridges, and contrariwise,
* if we have one or more bandit or chaos bridges, we don't have a MPC106.
*/
-static void __init add_bridges(struct device_node *dev, unsigned long *mem_ptr)
+static void __init add_bridges(struct device_node *dev)
{
int *bus_range;
int len;
printk(KERN_INFO "PCI buses %d..%d", bus_range[0],
bus_range[1]);
printk(" controlled by %s at %x\n", dev->name, addr->address);
- bp = (struct bridge_data *) *mem_ptr;
- *mem_ptr += sizeof(struct bridge_data);
+ bp = (struct bridge_data *) alloc_bootmem(sizeof(*bp));
if (strcmp(dev->name, "pci") != 0) {
bp->cfg_addr = (volatile unsigned int *)
ioremap(addr->address + 0x800000, 0x1000);
xmon(regs);
#endif
smp_message_recv();
- return -1;
+ return -2; /* ignore, already handled */
}
{
}
/* get addresses of second controller */
- irqctrler = (irqctrler->next) ? irqctrler->next : NULL;
+ irqctrler = irqctrler->next;
if (irqctrler && irqctrler->n_addrs > 0) {
addr = (unsigned long)
ioremap(irqctrler->addrs[0].address, 0x40);
pmac_irq_hw[i] = (volatile struct pmac_irq_hw*)
(addr + (4 - i) * 0x10);
}
+ } else {
+ /* older powermacs have a GC (grand central) or ohare at
+ f3000000, with interrupt control registers at f3000020. */
+ addr = (unsigned long) ioremap(0xf3000000, 0x40);
+ pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20);
}
/* disable all interrupts in all controllers */
static volatile u32 *sysctrl_regs;
void __init
-pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)
+pmac_setup_arch(void)
{
struct device_node *cpu;
int *fp;
__ioremap(0xffc00000, 0x400000, pgprot_val(PAGE_READONLY));
ohare_init();
- *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
+ pmac_find_bridges();
init_p2pbridge();
/* Checks "l2cr-value" property in the registry */
EXPORT_SYMBOL(syscall_trace);
EXPORT_SYMBOL(transfer_to_handler);
EXPORT_SYMBOL(do_IRQ);
-EXPORT_SYMBOL(init_task_union);
EXPORT_SYMBOL(MachineCheckException);
EXPORT_SYMBOL(AlignmentException);
EXPORT_SYMBOL(ProgramCheckException);
}
void __init
-prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
+prep_setup_arch()
{
extern char cmd_line[];
unsigned char reg;
}
}
-void __init setup_arch(char **cmdline_p,
- unsigned long * memory_start_p, unsigned long * memory_end_p)
+void __init setup_arch(char **cmdline_p)
{
extern int panic_timeout;
extern char _etext[], _edata[];
extern char *klimit;
- extern unsigned long find_available_memory(void);
- extern unsigned long *end_of_DRAM;
+ extern void do_init_bootmem(void);
#ifdef CONFIG_XMON
extern void xmon_map_scc(void);
xmon(0);
#endif /* CONFIG_XMON */
- /* reboot on panic */
+ /* reboot on panic */
panic_timeout = 180;
init_mm.start_code = PAGE_OFFSET;
init_mm.end_code = (unsigned long) _etext;
init_mm.end_data = (unsigned long) _edata;
- init_mm.brk = (unsigned long) klimit;
+ init_mm.brk = (unsigned long) klimit;
/* Save unparsed command line copy for /proc/cmdline */
strcpy(saved_command_line, cmd_line);
*cmdline_p = cmd_line;
- *memory_start_p = find_available_memory();
- *memory_end_p = (unsigned long) end_of_DRAM;
+ /* set up the bootmem stuff with available memory */
+ do_init_bootmem();
- ppc_md.setup_arch(memory_start_p, memory_end_p);
+ ppc_md.setup_arch();
/* clear the progress line */
if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab);
}
/*
- * $Id: init.c,v 1.193 1999/10/11 18:50:35 geert Exp $
+ * $Id: init.c,v 1.195 1999/10/15 16:39:39 cort Exp $
*
* PowerPC version
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/openpic.h>
+#include <linux/bootmem.h>
#ifdef CONFIG_BLK_DEV_INITRD
#include <linux/blk.h> /* for initrd_* */
#endif
atomic_t next_mmu_context;
unsigned long *end_of_DRAM;
int mem_init_done;
+int init_bootmem_done;
+int boot_mapsize;
+unsigned long totalram_pages = 0;
extern pgd_t swapper_pg_dir[];
extern char _start[], _end[];
extern char etext[], _stext[];
};
struct mem_pieces phys_mem;
struct mem_pieces phys_avail;
-struct mem_pieces prom_mem;
static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int);
+static void set_phys_avail(void);
void *find_mem_piece(unsigned, unsigned);
static void print_mem_pieces(struct mem_pieces *);
#if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_ALL_PPC)
pte = (pte_t *) MMU_get_page();
else if ((pte = (pte_t *) get_zero_page_fast()) == NULL)
if ((pte = (pte_t *) __get_free_page(GFP_KERNEL)))
- clear_page((unsigned long)pte);
+ clear_page(pte);
if (pte) {
pmd_val(*pmd) = (unsigned long)pte;
return pte + offset;
* ZERO_PAGE is a special page that is used for zero-initialized
* data and COW.
*/
-unsigned long empty_bad_page_table;
+pte_t *empty_bad_page_table;
pte_t * __bad_pagetable(void)
{
- __clear_user((void *)empty_bad_page_table, PAGE_SIZE);
- return (pte_t *) empty_bad_page_table;
+ clear_page(empty_bad_page_table);
+ return empty_bad_page_table;
}
-unsigned long empty_bad_page;
+void *empty_bad_page;
pte_t __bad_page(void)
{
- __clear_user((void *)empty_bad_page, PAGE_SIZE);
- return pte_mkdirty(mk_pte(empty_bad_page, PAGE_SHARED));
+ clear_page(empty_bad_page);
+ return pte_mkdirty(mk_pte_phys(__pa(empty_bad_page), PAGE_SHARED));
}
void show_mem(void)
val->totalram = 0;
val->sharedram = 0;
val->freeram = nr_free_pages << PAGE_SHIFT;
- val->bufferram = atomic_read(&buffermem);
+ val->bufferram = atomic_read(&buffermem_pages);
while (i-- > 0) {
if (PageReserved(mem_map+i))
continue;
}
#endif /* CONFIG_8xx */
+/*
+ * Set phys_avail to phys_mem less the kernel text/data/bss.
+ */
+static void __init set_phys_avail(void)
+{
+ unsigned long kstart, ksize;
+
+ /* we can't call the prom any more at this stage, so
+ all of memory is available (after klimit) */
+ phys_avail = phys_mem;
+
+ /*
+ * phys_avail records memory we can use.
+ * Make sure the kernel text/data/bss is not in it.
+ */
+ kstart = __pa(_stext); /* should be 0 */
+ ksize = PAGE_ALIGN(klimit - _stext);
+ remove_mem_piece(&phys_avail, kstart, ksize, 0);
+ remove_mem_piece(&phys_avail, 0, 0x4000, 0);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start) {
+ /*
+ * Remove the initialized ramdisk from the available memory.
+ */
+ remove_mem_piece(&phys_avail, __pa(initrd_start),
+ initrd_end - initrd_start, 1);
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+}
+
/*
* Scan a region for a piece of a given size with the required alignment.
*/
printk("\n");
}
-#if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_PPC_ALL)
+#if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_ALL_PPC)
/*
* Add some memory to an array of pieces
*/
{
int i;
unsigned long v, p, s, f;
-#ifndef CONFIG_8xx
+#ifndef CONFIG_8xx
if (!__map_without_bats) {
unsigned long tot, mem_base, bl, done;
unsigned long max_size = (256<<20);
RAM_PAGE);
}
}
- v = KERNELBASE;
- for (i = 0; i < phys_mem.n_regions; ++i) {
- p = phys_mem.regions[i].address;
- for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) {
- f = _PAGE_PRESENT | _PAGE_ACCESSED;
- if ((char *) v < _stext || (char *) v >= etext)
- f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE;
- else
- /* On the powerpc, no user access
- forces R/W kernel access */
- f |= _PAGE_USER;
- map_page(v, p, f);
- v += PAGE_SIZE;
- p += PAGE_SIZE;
- }
- }
-
-#else /* CONFIG_8xx */
+#endif /* CONFIG_8xx */
for (i = 0; i < phys_mem.n_regions; ++i) {
v = (ulong)__va(phys_mem.regions[i].address);
* don't get ASID compares on kernel space.
*/
f = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED;
-
- /* I don't really need the rest of this code, but
- * I grabbed it because I think the line:
- * f |= _PAGE_USER
- * is incorrect. It needs to be set to bits we
- * don't define to cause a kernel read-only. On
- * the MPC8xx, the PAGE_DIRTY takes care of that
- * for us (along with the RW software state).
- */
if ((char *) v < _stext || (char *) v >= etext)
f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE;
+#ifndef CONFIG_8xx
+ else
+ /* On the powerpc (not 8xx), no user access
+ forces R/W kernel access */
+ f |= _PAGE_USER;
+#endif /* CONFIG_8xx */
map_page(v, p, f);
v += PAGE_SIZE;
p += PAGE_SIZE;
}
- }
-#endif /* CONFIG_8xx */
+ }
}
-/* This can get called from ioremap, so don't make it an __init, OK? */
+/* In fact this is only called until mem_init is done. */
static void __init *MMU_get_page(void)
{
void *p;
if (mem_init_done) {
p = (void *) __get_free_page(GFP_KERNEL);
- if (p == 0)
- panic("couldn't get a page in MMU_get_page");
+ } else if (init_bootmem_done) {
+ p = alloc_bootmem_pages(PAGE_SIZE);
} else {
p = find_mem_piece(PAGE_SIZE, PAGE_SIZE);
}
+ if (p == 0)
+ panic("couldn't get a page in MMU_get_page");
__clear_user(p, PAGE_SIZE);
return p;
}
if ( ppc_md.progress ) ppc_md.progress("MMU:exit", 0x211);
}
+/*
+ * Initialize the bootmem system and give it all the memory we
+ * have available.
+ */
+void __init do_init_bootmem(void)
+{
+ unsigned long start, size;
+ int i;
+
+ /*
+ * Find an area to use for the bootmem bitmap.
+ * We look for the first area which is at least
+ * 128kB in length (128kB is enough for a bitmap
+ * for 4GB of memory, using 4kB pages), plus 1 page
+ * (in case the address isn't page-aligned).
+ */
+ start = 0;
+ size = 0;
+ for (i = 0; i < phys_avail.n_regions; ++i) {
+ unsigned long a = phys_avail.regions[i].address;
+ unsigned long s = phys_avail.regions[i].size;
+ if (s <= size)
+ continue;
+ start = a;
+ size = s;
+ if (s >= 33 * PAGE_SIZE)
+ break;
+ }
+ start = PAGE_ALIGN(start);
+
+ boot_mapsize = init_bootmem(start >> PAGE_SHIFT,
+ __pa(end_of_DRAM) >> PAGE_SHIFT);
+
+ /* remove the bootmem bitmap from the available memory */
+ remove_mem_piece(&phys_avail, start, start + boot_mapsize, 1);
+
+ /* add everything in phys_avail into the bootmem map */
+ for (i = 0; i < phys_avail.n_regions; ++i)
+ free_bootmem(phys_avail.regions[i].address,
+ phys_avail.regions[i].size);
+
+ init_bootmem_done = 1;
+}
+
+#if 0
/*
* Find some memory for setup_arch to return.
* We use the largest chunk of available memory as the area
avail_start = (unsigned long) __va(a);
return avail_start;
}
+#endif /* 0 */
/*
* paging_init() sets up the page tables - in fact we've already done this.
*/
-unsigned long __init paging_init(unsigned long start_mem, unsigned long end_mem)
+void __init paging_init(void)
{
- extern unsigned long free_area_init(unsigned long, unsigned long);
/*
* Grab some memory for bad_page and bad_pagetable to use.
*/
- empty_bad_page = PAGE_ALIGN(start_mem);
- empty_bad_page_table = empty_bad_page + PAGE_SIZE;
- start_mem = empty_bad_page + 2 * PAGE_SIZE;
-
- /* note: free_area_init uses its second argument
- to size the mem_map array. */
- start_mem = free_area_init(start_mem, end_mem);
- return start_mem;
+ empty_bad_page = alloc_bootmem_pages(PAGE_SIZE);
+ empty_bad_page_table = alloc_bootmem_pages(PAGE_SIZE);
+
+ free_area_init(max_low_pfn);
}
-void __init mem_init(unsigned long start_mem, unsigned long end_mem)
+void __init mem_init(void)
{
unsigned long addr;
- int i;
- unsigned long a, lim;
int codepages = 0;
int datapages = 0;
int initpages = 0;
extern unsigned int rtas_data, rtas_size;
- end_mem &= PAGE_MASK;
- high_memory = (void *) end_mem;
- max_mapnr = MAP_NR(high_memory);
-
- /* mark usable pages in the mem_map[] */
- start_mem = PAGE_ALIGN(start_mem);
-
+ max_mapnr = max_low_pfn;
+ high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
num_physpages = max_mapnr; /* RAM is assumed contiguous */
- remove_mem_piece(&phys_avail, __pa(avail_start),
- start_mem - avail_start, 1);
- for (i = 0; i < phys_avail.n_regions; ++i) {
- a = (unsigned long) __va(phys_avail.regions[i].address);
- lim = (a + phys_avail.regions[i].size) & PAGE_MASK;
- a = PAGE_ALIGN(a);
- for (; a < lim; a += PAGE_SIZE)
- clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
- }
+ totalram_pages += free_all_bootmem();
#ifdef CONFIG_BLK_DEV_INITRD
/* if we are booted from BootX with an initial ramdisk,
make sure the ramdisk pages aren't reserved. */
if (initrd_start) {
- for (a = initrd_start; a < initrd_end; a += PAGE_SIZE)
- clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
+ for (addr = initrd_start; addr < initrd_end; addr += PAGE_SIZE)
+ clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags);
}
#endif /* CONFIG_BLK_DEV_INITRD */
- /* free the prom's memory - no-op on prep */
- for (i = 0; i < prom_mem.n_regions; ++i) {
- a = (unsigned long) __va(prom_mem.regions[i].address);
- lim = (a + prom_mem.regions[i].size) & PAGE_MASK;
- a = PAGE_ALIGN(a);
- for (; a < lim; a += PAGE_SIZE)
- clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
- }
-
- prom_trashed = 1;
-
- for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
- if (PageReserved(mem_map + MAP_NR(addr))) {
- if (addr < (ulong) etext)
- codepages++;
- else if (addr >= (unsigned long)&__init_begin
- && addr < (unsigned long)&__init_end)
- initpages++;
- else if (addr < (ulong) start_mem)
- datapages++;
+ for (addr = PAGE_OFFSET; addr < (unsigned long)end_of_DRAM;
+ addr += PAGE_SIZE) {
+ if (!PageReserved(mem_map + MAP_NR(addr)))
continue;
- }
- set_page_count(mem_map + MAP_NR(addr), 1);
-#ifdef CONFIG_BLK_DEV_INITRD
- if (!initrd_start ||
- addr < (initrd_start & PAGE_MASK) || addr >= initrd_end)
-#endif /* CONFIG_BLK_DEV_INITRD */
-#ifndef CONFIG_8xx
- if ( !rtas_data ||
- addr < (rtas_data & PAGE_MASK) ||
- addr >= (rtas_data+rtas_size))
-#endif /* CONFIG_8xx */
- free_page(addr);
+ if (addr < (ulong) etext)
+ codepages++;
+ else if (addr >= (unsigned long)&__init_begin
+ && addr < (unsigned long)&__init_end)
+ initpages++;
+ else if (addr < (ulong) klimit)
+ datapages++;
}
printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n",
codepages << (PAGE_SHIFT-10),
datapages << (PAGE_SHIFT-10),
initpages << (PAGE_SHIFT-10),
- PAGE_OFFSET, end_mem);
+ PAGE_OFFSET, (unsigned long) end_of_DRAM);
mem_init_done = 1;
}
unsigned long __init *pmac_find_end_of_memory(void)
{
unsigned long a, total;
- unsigned long kstart, ksize;
- int i;
/* max amount of RAM we allow -- Cort */
-#define RAM_LIMIT (256<<20)
+#define RAM_LIMIT (768<<20)
memory_node = find_devices("memory");
if (memory_node == NULL) {
phys_mem.n_regions = 1;
}
- if (boot_infos == 0) {
- /* record which bits the prom is using */
- get_mem_prop("available", &phys_avail);
- prom_mem = phys_mem;
- for (i = 0; i < phys_avail.n_regions; ++i)
- remove_mem_piece(&prom_mem,
- phys_avail.regions[i].address,
- phys_avail.regions[i].size, 0);
- } else {
- /* booted from BootX - it's all available (after klimit) */
- phys_avail = phys_mem;
- prom_mem.n_regions = 0;
- }
+ set_phys_avail();
- /*
- * phys_avail records memory we can use now.
- * prom_mem records memory allocated by the prom that we
- * don't want to use now, but we'll reclaim later.
- * Make sure the kernel text/data/bss is in neither.
- */
- kstart = __pa(_stext); /* should be 0 */
- ksize = PAGE_ALIGN(klimit - _stext);
- remove_mem_piece(&phys_avail, kstart, ksize, 0);
- remove_mem_piece(&prom_mem, kstart, ksize, 0);
- remove_mem_piece(&phys_avail, 0, 0x4000, 0);
- remove_mem_piece(&prom_mem, 0, 0x4000, 0);
#undef RAM_LIMIT
return __va(total);
}
*/
unsigned long __init *prep_find_end_of_memory(void)
{
- unsigned long kstart, ksize;
unsigned long total;
total = res->TotalMemory;
printk("Ramsize default to be %ldM\n", total>>20);
}
append_mem_piece(&phys_mem, 0, total);
- phys_avail = phys_mem;
- kstart = __pa(_stext); /* should be 0 */
- ksize = PAGE_ALIGN(klimit - _stext);
- remove_mem_piece(&phys_avail, kstart, ksize, 0);
- remove_mem_piece(&phys_avail, 0, 0x4000, 0);
+ set_phys_avail();
return (__va(total));
}
#if defined(CONFIG_GEMINI)
unsigned long __init *gemini_find_end_of_memory(void)
{
- unsigned long total, kstart, ksize, *ret;
+ unsigned long total, *ret;
unsigned char reg;
reg = readb(GEMINI_MEMCFG);
phys_mem.n_regions = 1;
ret = __va(phys_mem.regions[0].size);
- phys_avail = phys_mem;
- kstart = __pa(_stext);
- ksize = PAGE_ALIGN( _end - _stext );
- remove_mem_piece( &phys_avail, kstart, ksize, 0 );
+ set_phys_avail();
return ret;
}
#endif /* defined(CONFIG_GEMINI) || defined(CONFIG_ALL_PPC) */
}
/* Now register the memory block. */
- {
- unsigned long kstart, ksize;
-
- append_mem_piece(&phys_mem, memory[0].addr, memory[0].size);
- phys_avail = phys_mem;
- kstart = __pa(_stext);
- ksize = PAGE_ALIGN(klimit - _stext);
- remove_mem_piece(&phys_avail, kstart, ksize, 0);
- }
+ append_mem_piece(&phys_mem, memory[0].addr, memory[0].size);
+ set_phys_avail();
/* Remove the memory chunks that are controlled by special
Phase5 hardware. */
*/
unsigned long __init *m8xx_find_end_of_memory(void)
{
- unsigned long kstart, ksize;
bd_t *binfo;
unsigned long *ret;
extern unsigned char __res[];
ret = __va(phys_mem.regions[0].address+
phys_mem.regions[0].size);
- phys_avail = phys_mem;
-
- kstart = __pa(_stext); /* should be 0 */
- ksize = PAGE_ALIGN(_end - _stext);
- remove_mem_piece(&phys_avail, kstart, ksize, 0);
+ set_phys_avail();
return ret;
}
#endif /* ndef CONFIG_8xx */
static int
icside_config_drive(ide_drive_t *drive, int mode)
{
- ide_hwif_t *hwif = HWIF(drive);
int speed, err;
if (mode == 2) {
#define OF(args) args
+#ifndef memzero
#define memzero(s, n) memset ((s), 0, (n))
-
+#endif
typedef unsigned char uch;
typedef unsigned short ush;
*
* Maintainer unknown.
*
- * Drive tuning added from Corel Computer's kernel sources
+ * Drive tuning added from Rebel.com's kernel sources
* -- Russell King (15/11/98) linux@arm.linux.org.uk
*/
prot = (prot & _CACHEMASK040) | _PAGE_NOCACHE_S;
#elif defined(__mips__)
prot = (prot & ~_CACHE_MASK) | _CACHE_UNCACHED;
+#elif defined(__arm__) && defined(CONFIG_CPU_32)
+ /* Turn off caching for all I/O areas */
+ prot &= ~(L_PTE_CACHEABLE | L_PTE_BUFFERABLE);
#endif
return prot;
*/
unsigned char keyboard_type = KB_101;
-#ifndef __alpha__
+#if !defined(__alpha__) && !defined(__arm__)
asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on);
#endif
*/
#if defined(__i386__) || defined(__alpha__) || defined(__powerpc__) \
- || (defined(__mips__) && !defined(CONFIG_SGI))
+ || (defined(__mips__) && !defined(CONFIG_SGI)) \
+ || (defined(__arm__) && defined(CONFIG_HOST_FOOTBRIDGE))
static void
kd_nosound(unsigned long ignored)
ucval = keyboard_type;
goto setchar;
-#ifndef __alpha__
+#if !defined(__alpha__) && !defined(__arm__)
/*
* These cannot be implemented on any machine that implements
* ioperm() in user level (such as Alpha PCs).
*
* Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Receive DMA code by Takashi Oe <toe@unlserve.unl.edu>.
+ *
+ * $Id: macserial.c,v 1.24.2.3 1999/09/10 02:05:58 paulus Exp $
*/
#include <linux/config.h>
#ifdef CONFIG_SERIAL_CONSOLE
#include <linux/console.h>
#endif
+#include <linux/slab.h>
#include <asm/init.h>
#include <asm/io.h>
#ifdef CONFIG_KGDB
#include <asm/kgdb.h>
#endif
+#include <asm/dbdma.h>
#include "macserial.h"
};
#endif
+#define SUPPORT_SERIAL_DMA
+
/*
* It would be nice to dynamically allocate everything that
* depends on NUM_SERIAL, so we could support any number of
static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
static int set_scc_power(struct mac_serial * info, int state);
static int setup_scc(struct mac_serial * info);
+static void dbdma_reset(volatile struct dbdma_regs *dma);
+static void dbdma_flush(volatile struct dbdma_regs *dma);
+static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs);
+static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs);
+static void dma_init(struct mac_serial * info);
+static void rxdma_start(struct mac_serial * info, int current);
+static void rxdma_to_tty(struct mac_serial * info);
static struct tty_struct *serial_table[NUM_CHANNELS];
static struct termios *serial_termios[NUM_CHANNELS];
write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */
}
+/*
+ * Reset a Descriptor-Based DMA channel.
+ */
+static void dbdma_reset(volatile struct dbdma_regs *dma)
+{
+ int i;
+
+ out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16);
+
+ /*
+ * Yes this looks peculiar, but apparently it needs to be this
+ * way on some machines. (We need to make sure the DBDMA
+ * engine has actually got the write above and responded
+ * to it. - paulus)
+ */
+ for (i = 200; i > 0; --i)
+ if (ld_le32(&dma->control) & RUN)
+ udelay(1);
+}
+
+/*
+ * Tells a DBDMA channel to stop and write any buffered data
+ * it might have to memory.
+ */
+static _INLINE_ void dbdma_flush(volatile struct dbdma_regs *dma)
+{
+ int i = 0;
+
+ out_le32(&dma->control, (FLUSH << 16) | FLUSH);
+ while (((in_le32(&dma->status) & FLUSH) != 0) && (i++ < 100))
+ udelay(1);
+}
+
/*
* ----------------------------------------------------------------------
*
mark_bh(MACSERIAL_BH);
}
+/* Work out the flag value for a z8530 status value. */
+static _INLINE_ int stat_to_flag(int stat)
+{
+ int flag;
+
+ if (stat & Rx_OVR) {
+ flag = TTY_OVERRUN;
+ } else if (stat & FRM_ERR) {
+ flag = TTY_FRAME;
+ } else if (stat & PAR_ERR) {
+ flag = TTY_PARITY;
+ } else
+ flag = 0;
+ return flag;
+}
+
static _INLINE_ void receive_chars(struct mac_serial *info,
struct pt_regs *regs)
{
if (flip_max_cnt < tty->flip.count)
flip_max_cnt = tty->flip.count;
}
- if (stat & Rx_OVR) {
- flag = TTY_OVERRUN;
- } else if (stat & FRM_ERR) {
- flag = TTY_FRAME;
- } else if (stat & PAR_ERR) {
- flag = TTY_PARITY;
- } else
- flag = 0;
+ flag = stat_to_flag(stat);
if (flag)
/* reset the error indication */
write_zsreg(info->zs_channel, 0, ERR_RES);
info->read_reg_zero = status;
}
+static _INLINE_ void receive_special_dma(struct mac_serial *info)
+{
+ unsigned char stat, flag;
+ volatile struct dbdma_regs *rd = &info->rx->dma;
+ int where = RX_BUF_SIZE;
+
+ spin_lock(&info->rx_dma_lock);
+ if ((ld_le32(&rd->status) & ACTIVE) != 0)
+ dbdma_flush(rd);
+ if (in_le32(&rd->cmdptr)
+ == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1))
+ where -= in_le16(&info->rx->res_count);
+ where--;
+
+ stat = read_zsreg(info->zs_channel, R1);
+
+ flag = stat_to_flag(stat);
+ if (flag) {
+ info->rx_flag_buf[info->rx_cbuf][where] = flag;
+ /* reset the error indication */
+ write_zsreg(info->zs_channel, 0, ERR_RES);
+ }
+
+ spin_unlock(&info->rx_dma_lock);
+}
+
/*
* This is the serial driver's generic interrupt routine
*/
unsigned char zs_intreg;
int shift;
+ if (!(info->flags & ZILOG_INITIALIZED)) {
+ printk("rs_interrupt: irq %d, port not initialized\n", irq);
+ disable_irq(irq);
+ return;
+ }
+
/* NOTE: The read register 3, which holds the irq status,
* does so for both channels on each chip. Although
* the status value itself must be read from the A
for (;;) {
zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift;
#ifdef SERIAL_DEBUG_INTR
- printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg);
+ printk("rs_interrupt: irq %d, zs_intreg 0x%x\n",
+ irq, (int)zs_intreg);
#endif
if ((zs_intreg & CHAN_IRQMASK) == 0)
break;
- if (!(info->flags & ZILOG_INITIALIZED)) {
- printk("rs_interrupt: irq %d, port not initialized\n", irq);
- break;
+ if (zs_intreg & CHBRxIP) {
+ /* If we are doing DMA, we only ask for interrupts
+ on characters with errors or special conditions. */
+ if (info->dma_initted)
+ receive_special_dma(info);
+ else
+ receive_chars(info, regs);
}
-
- if (zs_intreg & CHBRxIP)
- receive_chars(info, regs);
if (zs_intreg & CHBTxIP)
transmit_chars(info);
if (zs_intreg & CHBEXT)
}
}
+/* Transmit DMA interrupt - not used at present */
+static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+/*
+ * Receive DMA interrupt.
+ */
+static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct mac_serial *info = (struct mac_serial *) dev_id;
+ volatile struct dbdma_cmd *cd;
+
+ if (!info->dma_initted)
+ return;
+ spin_lock(&info->rx_dma_lock);
+ /* First, confirm that this interrupt is, indeed, coming */
+ /* from Rx DMA */
+ cd = info->rx_cmds[info->rx_cbuf] + 2;
+ if ((in_le16(&cd->xfer_status) & (RUN | ACTIVE)) != (RUN | ACTIVE)) {
+ spin_unlock(&info->rx_dma_lock);
+ return;
+ }
+ if (info->rx_fbuf != RX_NO_FBUF) {
+ info->rx_cbuf = info->rx_fbuf;
+ if (++info->rx_fbuf == info->rx_nbuf)
+ info->rx_fbuf = 0;
+ if (info->rx_fbuf == info->rx_ubuf)
+ info->rx_fbuf = RX_NO_FBUF;
+ }
+ spin_unlock(&info->rx_dma_lock);
+}
+
/*
* -------------------------------------------------------------------
* Here ends the serial interrupt routines.
}
}
-static void rs_timer(void)
-{
-}
-
static int startup(struct mac_serial * info, int can_sleep)
{
int delay;
info->flags |= ZILOG_INITIALIZED;
enable_irq(info->irq);
+ if (info->dma_initted) {
+// enable_irq(info->tx_dma_irq);
+ enable_irq(info->rx_dma_irq);
+ }
if (delay) {
if (can_sleep) {
return 0;
}
+static _INLINE_ void rxdma_start(struct mac_serial * info, int current)
+{
+ volatile struct dbdma_regs *rd = &info->rx->dma;
+ volatile struct dbdma_cmd *cd = info->rx_cmds[current];
+
+//printk(KERN_DEBUG "SCC: rxdma_start\n");
+
+ st_le32(&rd->cmdptr, virt_to_bus(cd));
+ out_le32(&rd->control, (RUN << 16) | RUN);
+}
+
+static void rxdma_to_tty(struct mac_serial *info)
+{
+ struct tty_struct *tty = info->tty;
+ volatile struct dbdma_regs *rd = &info->rx->dma;
+ unsigned long flags;
+ int residue, available, space, do_queue;
+
+ if (!tty)
+ return;
+
+ do_queue = 0;
+ spin_lock_irqsave(&info->rx_dma_lock, flags);
+more:
+ space = TTY_FLIPBUF_SIZE - tty->flip.count;
+ if (!space) {
+ do_queue++;
+ goto out;
+ }
+ residue = 0;
+ if (info->rx_ubuf == info->rx_cbuf) {
+ if ((ld_le32(&rd->status) & ACTIVE) != 0) {
+ dbdma_flush(rd);
+ if (in_le32(&rd->cmdptr)
+ == virt_to_bus(info->rx_cmds[info->rx_cbuf]+1))
+ residue = in_le16(&info->rx->res_count);
+ }
+ }
+ available = RX_BUF_SIZE - residue - info->rx_done_bytes;
+ if (available > space)
+ available = space;
+ if (available) {
+ memcpy(tty->flip.char_buf_ptr,
+ info->rx_char_buf[info->rx_ubuf] + info->rx_done_bytes,
+ available);
+ memcpy(tty->flip.flag_buf_ptr,
+ info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,
+ available);
+ tty->flip.char_buf_ptr += available;
+ tty->flip.count += available;
+ tty->flip.flag_buf_ptr += available;
+ memset(info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,
+ 0, available);
+ info->rx_done_bytes += available;
+ do_queue++;
+ }
+ if (info->rx_done_bytes == RX_BUF_SIZE) {
+ volatile struct dbdma_cmd *cd = info->rx_cmds[info->rx_ubuf];
+
+ if (info->rx_ubuf == info->rx_cbuf)
+ goto out;
+ /* mark rx_char_buf[rx_ubuf] free */
+ st_le16(&cd->command, DBDMA_NOP);
+ cd++;
+ st_le32(&cd->cmd_dep, 0);
+ st_le32((unsigned int *)&cd->res_count, 0);
+ cd++;
+ st_le16(&cd->xfer_status, 0);
+
+ if (info->rx_fbuf == RX_NO_FBUF) {
+ info->rx_fbuf = info->rx_ubuf;
+ if (!(ld_le32(&rd->status) & ACTIVE)) {
+ dbdma_reset(&info->rx->dma);
+ rxdma_start(info, info->rx_ubuf);
+ info->rx_cbuf = info->rx_ubuf;
+ }
+ }
+ info->rx_done_bytes = 0;
+ if (++info->rx_ubuf == info->rx_nbuf)
+ info->rx_ubuf = 0;
+ if (info->rx_fbuf == info->rx_ubuf)
+ info->rx_fbuf = RX_NO_FBUF;
+ goto more;
+ }
+out:
+ spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+ if (do_queue)
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static void poll_rxdma(void *private_)
+{
+ struct mac_serial *info = (struct mac_serial *) private_;
+ unsigned long flags;
+
+ rxdma_to_tty(info);
+ spin_lock_irqsave(&info->rx_dma_lock, flags);
+ mod_timer(&info->poll_dma_timer, RX_DMA_TIMER);
+ spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+}
+
+static void dma_init(struct mac_serial * info)
+{
+ int i, size;
+ volatile struct dbdma_cmd *cd;
+ unsigned char *p;
+
+//printk(KERN_DEBUG "SCC: dma_init\n");
+
+ info->rx_nbuf = 8;
+
+ /* various mem set up */
+ size = sizeof(struct dbdma_cmd) * (3 * info->rx_nbuf + 2)
+ + (RX_BUF_SIZE * 2 + sizeof(*info->rx_cmds)
+ + sizeof(*info->rx_char_buf) + sizeof(*info->rx_flag_buf))
+ * info->rx_nbuf;
+ info->dma_priv = kmalloc(size, GFP_KERNEL | GFP_DMA);
+ if (info->dma_priv == NULL)
+ return;
+ memset(info->dma_priv, 0, size);
+
+ info->rx_cmds = (volatile struct dbdma_cmd **)info->dma_priv;
+ info->rx_char_buf = (unsigned char **) (info->rx_cmds + info->rx_nbuf);
+ info->rx_flag_buf = info->rx_char_buf + info->rx_nbuf;
+ p = (unsigned char *) (info->rx_flag_buf + info->rx_nbuf);
+ for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)
+ info->rx_char_buf[i] = p;
+ for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)
+ info->rx_flag_buf[i] = p;
+
+ /* a bit of DMA programming */
+ cd = info->rx_cmds[0] = (volatile struct dbdma_cmd *) DBDMA_ALIGN(p);
+ st_le16(&cd->command, DBDMA_NOP);
+ cd++;
+ st_le16(&cd->req_count, RX_BUF_SIZE);
+ st_le16(&cd->command, INPUT_MORE);
+ st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[0]));
+ cd++;
+ st_le16(&cd->req_count, 4);
+ st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);
+ st_le32(&cd->phy_addr, virt_to_bus(cd-2));
+ st_le32(&cd->cmd_dep, DBDMA_STOP);
+ for (i = 1; i < info->rx_nbuf; i++) {
+ info->rx_cmds[i] = ++cd;
+ st_le16(&cd->command, DBDMA_NOP);
+ cd++;
+ st_le16(&cd->req_count, RX_BUF_SIZE);
+ st_le16(&cd->command, INPUT_MORE);
+ st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[i]));
+ cd++;
+ st_le16(&cd->req_count, 4);
+ st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);
+ st_le32(&cd->phy_addr, virt_to_bus(cd-2));
+ st_le32(&cd->cmd_dep, DBDMA_STOP);
+ }
+ cd++;
+ st_le16(&cd->command, DBDMA_NOP | BR_ALWAYS);
+ st_le32(&cd->cmd_dep, virt_to_bus(info->rx_cmds[0]));
+
+ /* setup DMA to our liking */
+ dbdma_reset(&info->rx->dma);
+ st_le32(&info->rx->dma.intr_sel, 0x10001);
+ st_le32(&info->rx->dma.br_sel, 0x10001);
+ out_le32(&info->rx->dma.wait_sel, 0x10001);
+
+ /* set various flags */
+ info->rx_ubuf = 0;
+ info->rx_cbuf = 0;
+ info->rx_fbuf = info->rx_ubuf + 1;
+ if (info->rx_fbuf == info->rx_nbuf)
+ info->rx_fbuf = RX_NO_FBUF;
+ info->rx_done_bytes = 0;
+
+ /* setup polling */
+ init_timer(&info->poll_dma_timer);
+ info->poll_dma_timer.function = (void *)&poll_rxdma;
+ info->poll_dma_timer.data = (unsigned long)info;
+
+ info->dma_initted = 1;
+}
+
static int setup_scc(struct mac_serial * info)
{
unsigned long flags;
ZS_CLEARFIFO(info->zs_channel);
info->xmit_fifo_size = 1;
+ /*
+ * Reset DMAs
+ */
+ if (info->has_dma)
+ dma_init(info);
+
/*
* Clear the interrupt registers.
*/
/*
* Finally, enable sequencing and interrupts
*/
- info->curregs[1] = (info->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
+ if (!info->dma_initted) {
+ /* interrupt on ext/status changes, all received chars,
+ transmit ready */
+ info->curregs[1] = (info->curregs[1] & ~0x18)
+ | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
+ } else {
+ /* interrupt on ext/status changes, W/Req pin is
+ receive DMA request */
+ info->curregs[1] = (info->curregs[1] & ~(0x18 | TxINT_ENAB))
+ | (EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN);
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ /* enable W/Req pin */
+ info->curregs[1] |= WT_RDY_ENAB;
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ /* enable interrupts on transmit ready and receive errors */
+ info->curregs[1] |= INT_ERR_Rx | TxINT_ENAB;
+ }
info->pendregs[1] = info->curregs[1];
info->curregs[3] |= (RxENABLE | Rx8);
info->pendregs[3] = info->curregs[3];
restore_flags(flags);
+ if (info->dma_initted) {
+ spin_lock_irqsave(&info->rx_dma_lock, flags);
+ rxdma_start(info, 0);
+ info->poll_dma_timer.expires = RX_DMA_TIMER;
+ add_timer(&info->poll_dma_timer);
+ spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+ }
+
return 0;
}
return;
}
-
+
+ if (info->has_dma) {
+ del_timer(&info->poll_dma_timer);
+ dbdma_reset(info->tx_dma);
+ dbdma_reset(&info->rx->dma);
+ disable_irq(info->tx_dma_irq);
+ disable_irq(info->rx_dma_irq);
+ }
disable_irq(info->irq);
info->pendregs[1] = info->curregs[1] = 0;
info->xmit_buf = 0;
}
+ if (info->has_dma && info->dma_priv) {
+ kfree(info->dma_priv);
+ info->dma_priv = NULL;
+ info->dma_initted = 0;
+ }
+
memset(info->curregs, 0, sizeof(info->curregs));
memset(info->curregs, 0, sizeof(info->pendregs));
feature_set(info->dev_node, FEATURE_Modem_Reset);
mdelay(5);
feature_clear(info->dev_node, FEATURE_Modem_Reset);
- delay = 1000; /* wait for 1s before using */
+ delay = 2500; /* wait for 2.5s before using */
}
#ifdef CONFIG_PMAC_PBOOK
if (info->is_pwbk_ir)
if (!tty || !info->xmit_buf || !tmp_buf)
return 0;
- save_flags(flags);
if (from_user) {
down(&tmp_buf_sem);
while (1) {
ret = -EFAULT;
break;
}
+ save_flags(flags);
cli();
c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
up(&tmp_buf_sem);
} else {
while (1) {
+ save_flags(flags);
cli();
c = MIN(count,
MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
&& !info->tx_active)
transmit_chars(info);
- restore_flags(flags);
return ret;
}
static void rs_flush_buffer(struct tty_struct *tty)
{
struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+ unsigned long flags;
if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
return;
- cli();
+ save_flags(flags); cli();
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- sti();
+ restore_flags(flags);
wake_up_interruptible(&tty->write_wait);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
struct mac_serial *info = (struct mac_serial *)tty->driver_data;
unsigned long flags;
#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
printk("throttle %ld....\n",tty->ldisc.chars_in_buffer(tty));
#endif
struct mac_serial *info = (struct mac_serial *)tty->driver_data;
unsigned long flags;
#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
printk("unthrottle %s: %d....\n",tty->ldisc.chars_in_buffer(tty));
#endif
save_flags(flags); cli();
if (tty_hung_up_p(filp)) {
+ MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
info->count = 0;
}
if (info->count) {
+ MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
printk("waiting end of Tx... (timeout:%d)\n", info->closing_wait);
#endif
tty->closing = 1;
- if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
+ if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) {
+ restore_flags(flags);
tty_wait_until_sent(tty, info->closing_wait);
+ save_flags(flags); cli();
+ }
+
/*
* At this point we stop accepting input. To do this, we
* disable the receiver and receive interrupts.
#ifdef SERIAL_DEBUG_OPEN
printk("waiting end of Rx...\n");
#endif
+ restore_flags(flags);
rs_wait_until_sent(tty, info->timeout);
+ save_flags(flags); cli();
}
shutdown(info);
info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
ZILOG_CLOSING);
wake_up_interruptible(&info->close_wait);
+ MOD_DEC_USE_COUNT;
}
/*
int retval, line;
unsigned long page;
+ MOD_INC_USE_COUNT;
line = MINOR(tty->device) - tty->driver.minor_start;
- if ((line < 0) || (line >= zs_channels_found))
+ if ((line < 0) || (line >= zs_channels_found)) {
+ MOD_DEC_USE_COUNT;
return -ENODEV;
+ }
info = zs_soft + line;
#ifdef CONFIG_KGDB
- if (info->kgdb_channel)
+ if (info->kgdb_channel) {
+ MOD_DEC_USE_COUNT;
return -ENODEV;
+ }
#endif
if (serial_paranoia_check(info, tty->device, "rs_open"))
return -ENODEV;
static void show_serial_version(void)
{
- printk("PowerMac Z8530 serial driver version 1.01\n");
+ printk("PowerMac Z8530 serial driver version 2.0\n");
+}
+
+/*
+ * Initialize one channel, both the mac_serial and mac_zschannel
+ * structs. We use the dev_node field of the mac_serial struct.
+ */
+static void
+chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan,
+ struct mac_zschannel *zs_chan_a)
+{
+ struct device_node *ch = zss->dev_node;
+ char *conn;
+ int len;
+
+ zss->irq = ch->intrs[0].line;
+ zss->has_dma = 0;
+#if !defined(CONFIG_KGDB) && defined(SUPPORT_SERIAL_DMA)
+ if (ch->n_addrs == 3 && ch->n_intrs == 3)
+ zss->has_dma = 1;
+#endif
+ zss->dma_initted = 0;
+
+ zs_chan->control = (volatile unsigned char *)
+ ioremap(ch->addrs[0].address, 0x1000);
+ zs_chan->data = zs_chan->control + 0x10;
+ spin_lock_init(&zs_chan->lock);
+ zs_chan->parent = zss;
+ zss->zs_channel = zs_chan;
+ zss->zs_chan_a = zs_chan_a;
+
+ /* setup misc varariables */
+ zss->kgdb_channel = 0;
+ zss->is_cobalt_modem = device_is_compatible(ch, "cobalt");
+
+ /* XXX tested only with wallstreet PowerBook,
+ should do no harm anyway */
+ conn = get_property(ch, "AAPL,connector", &len);
+ zss->is_pwbk_ir = conn && (strcmp(conn, "infrared") == 0);
+
+ if (zss->has_dma) {
+ zss->dma_priv = NULL;
+ /* it seems that the last two addresses are the
+ DMA controllers */
+ zss->tx_dma = (volatile struct dbdma_regs *)
+ ioremap(ch->addrs[ch->n_addrs - 2].address, 0x100);
+ zss->rx = (volatile struct mac_dma *)
+ ioremap(ch->addrs[ch->n_addrs - 1].address, 0x100);
+ zss->tx_dma_irq = ch->intrs[1].line;
+ zss->rx_dma_irq = ch->intrs[2].line;
+ spin_lock_init(&zss->rx_dma_lock);
+ }
}
/* Ask the PROM how many Z8530s we have and initialize their zs_channels */
{
struct device_node *dev, *ch;
struct mac_serial **pp;
- int n, lenp;
- char *conn;
+ int n, chip, nchan;
+ struct mac_zschannel *zs_chan;
+ int chan_a_index;
n = 0;
pp = &zs_chain;
+ zs_chan = zs_channels;
for (dev = find_devices("escc"); dev != 0; dev = dev->next) {
+ nchan = 0;
+ chip = n;
if (n >= NUM_CHANNELS) {
printk("Sorry, can't use %s: no more channels\n",
dev->full_name);
continue;
}
+ chan_a_index = 0;
for (ch = dev->child; ch != 0; ch = ch->sibling) {
+ if (nchan >= 2) {
+ printk(KERN_WARNING "SCC: Only 2 channels per "
+ "chip are supported\n");
+ break;
+ }
if (ch->n_addrs < 1 || (ch ->n_intrs < 1)) {
printk("Can't use %s: %d addrs %d intrs\n",
ch->full_name, ch->n_addrs, ch->n_intrs);
continue;
}
- zs_channels[n].control = (volatile unsigned char *)
- ioremap(ch->addrs[0].address, 0x1000);
- zs_channels[n].data = zs_channels[n].control + 0x10;
- spin_lock_init(&zs_channels[n].lock);
- zs_soft[n].zs_channel = &zs_channels[n];
- zs_soft[n].dev_node = ch;
- zs_soft[n].irq = ch->intrs[0].line;
- zs_soft[n].zs_channel->parent = &zs_soft[n];
- zs_soft[n].is_cobalt_modem = device_is_compatible(ch, "cobalt");
-
- /* XXX tested only with wallstreet PowerBook,
- should do no harm anyway */
- conn = get_property(ch, "AAPL,connector", &lenp);
- zs_soft[n].is_pwbk_ir =
- conn && (strcmp(conn, "infrared") == 0);
-
- /* XXX this assumes the prom puts chan A before B */
- if (n & 1)
- zs_soft[n].zs_chan_a = &zs_channels[n-1];
- else
- zs_soft[n].zs_chan_a = &zs_channels[n];
+ /* The channel with the higher address
+ will be the A side. */
+ if (nchan > 0 &&
+ ch->addrs[0].address
+ > zs_soft[n-1].dev_node->addrs[0].address)
+ chan_a_index = 1;
+
+ /* minimal initialization for now */
+ zs_soft[n].dev_node = ch;
*pp = &zs_soft[n];
pp = &zs_soft[n].zs_next;
+ ++nchan;
++n;
}
+ if (nchan == 0)
+ continue;
+
+ /* set up A side */
+ chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan);
+ ++zs_chan;
+
+ /* set up B side, if it exists */
+ if (nchan > 1)
+ chan_init(&zs_soft[chip + 1 - chan_a_index],
+ zs_chan, zs_chan - 1);
+ ++zs_chan;
}
*pp = 0;
+
zs_channels_found = n;
#ifdef CONFIG_PMAC_PBOOK
if (n)
/* Setup base handler, and timer table. */
init_bh(MACSERIAL_BH, do_serial_bh);
- timer_table[RS_TIMER].fn = rs_timer;
- timer_table[RS_TIMER].expires = 0;
/* Find out how many Z8530 SCCs we have */
if (zs_chain == 0)
/* Register the interrupt handler for each one */
save_flags(flags); cli();
for (i = 0; i < zs_channels_found; ++i) {
+ if (zs_soft[i].has_dma) {
+ if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0,
+ "SCC-txdma", &zs_soft[i]))
+ printk(KERN_ERR "macserial: can't get irq %d\n",
+ zs_soft[i].tx_dma_irq);
+ disable_irq(zs_soft[i].tx_dma_irq);
+ if (request_irq(zs_soft[i].rx_dma_irq, rs_rxdma_irq, 0,
+ "SCC-rxdma", &zs_soft[i]))
+ printk(KERN_ERR "macserial: can't get irq %d\n",
+ zs_soft[i].rx_dma_irq);
+ disable_irq(zs_soft[i].rx_dma_irq);
+ }
if (request_irq(zs_soft[i].irq, rs_interrupt, 0,
"SCC", &zs_soft[i]))
printk(KERN_ERR "macserial: can't get irq %d\n",
/* By default, disable the port */
set_scc_power(info, 0);
}
+ tmp_buf = 0;
return 0;
}
for (info = zs_chain, i = 0; info; info = info->zs_next, i++)
set_scc_power(info, 0);
save_flags(flags); cli();
- for (i = 0; i < zs_channels_found; ++i)
+ for (i = 0; i < zs_channels_found; ++i) {
free_irq(zs_soft[i].irq, &zs_soft[i]);
+ if (zs_soft[i].has_dma) {
+ free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]);
+ free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]);
+ }
+ }
restore_flags(flags);
tty_unregister_driver(&callout_driver);
tty_unregister_driver(&serial_driver);
+
+ if (tmp_buf) {
+ free_page((unsigned long) tmp_buf);
+ tmp_buf = 0;
+ }
+
+#ifdef CONFIG_PMAC_PBOOK
+ if (zs_channels_found)
+ pmu_unregister_sleep_notifier(&serial_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
}
#endif /* MODULE */
if (zs_chain == 0)
return -1;
+ set_scc_power(info, 1);
+
/* Reset the channel */
write_zsreg(info->zs_channel, R9, CHRA);
if (zs_chain == 0)
probe_sccs();
- set_scc_power(&zs_soft[n], 1);
+ set_scc_power(&zs_soft[tty_num], 1);
zs_kgdbchan = zs_soft[tty_num].zs_channel;
zs_soft[tty_num].change_needed = 0;
zs_soft[tty_num].clk_divisor = 16;
zs_soft[tty_num].zs_baud = 38400;
zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */
- zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
/* Turn on transmitter/receiver at 8-bits/char */
kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400);
struct mac_serial* parent;
};
+struct mac_dma {
+ volatile struct dbdma_regs dma;
+ volatile unsigned short res_count;
+ volatile unsigned short command;
+ volatile unsigned int buf_addr;
+};
+
struct mac_serial {
struct mac_serial *zs_next; /* For IRQ servicing chain */
struct mac_zschannel *zs_channel; /* Channel registers */
struct termios callout_termios;
wait_queue_head_t open_wait;
wait_queue_head_t close_wait;
+
+ volatile struct dbdma_regs *tx_dma;
+ int tx_dma_irq;
+ volatile struct dbdma_cmd *tx_cmds;
+ volatile struct mac_dma *rx;
+ int rx_dma_irq;
+ volatile struct dbdma_cmd **rx_cmds;
+ unsigned char **rx_char_buf;
+ unsigned char **rx_flag_buf;
+#define RX_BUF_SIZE 256
+ int rx_nbuf;
+ int rx_done_bytes;
+ int rx_ubuf;
+ int rx_fbuf;
+#define RX_NO_FBUF (-1)
+ int rx_cbuf;
+ spinlock_t rx_dma_lock;
+ int has_dma;
+ int dma_initted;
+ void *dma_priv;
+ struct timer_list poll_dma_timer;
+#define RX_DMA_TIMER (jiffies + 10*HZ/1000)
};
#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
#define INT_ERR_Rx 0x18 /* Int on error only */
-#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
-#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
-#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
+#define WT_RDY_RT 0x20 /* W/Req reflects recv if 1, xmit if 0 */
+#define WT_FN_RDYFN 0x40 /* W/Req pin is DMA request if 1, wait if 0 */
+#define WT_RDY_ENAB 0x80 /* Enable W/Req pin */
/* Write Register #2 (Interrupt Vector) */
/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+/* Write Register 7' (Some enhanced feature control) */
+#define ENEXREAD 0x40 /* Enable read of some write registers */
+
/* Write Register 8 (transmit buffer) */
/* Write Register 9 (Master interrupt control) */
#define SNRZI 0xe0 /* Set NRZI mode */
/* Write Register 15 (external/status interrupt control) */
+#define EN85C30 1 /* Enable some 85c30-enhanced registers */
#define ZCIE 2 /* Zero count IE */
+#define ENSTFIFO 4 /* Enable status FIFO (SDLC) */
#define DCDIE 8 /* DCD IE */
#define SYNCIE 0x10 /* Sync/hunt IE */
#define CTSIE 0x20 /* CTS IE */
#define END_FR 0x80 /* End of Frame (SDLC) */
/* Read Register 2 (channel b only) - Interrupt vector */
+#define CHB_Tx_EMPTY 0x00
+#define CHB_EXT_STAT 0x02
+#define CHB_Rx_AVAIL 0x04
+#define CHB_SPECIAL 0x06
+#define CHA_Tx_EMPTY 0x08
+#define CHA_EXT_STAT 0x0a
+#define CHA_Rx_AVAIL 0x0c
+#define CHA_SPECIAL 0x0e
+#define STATUS_MASK 0x06
/* Read Register 3 (interrupt pending register) ch a only */
#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
struct acpi_table *rsdt;
u32 *rsdt_entry;
int rsdt_entry_count;
- u8 *i;
+ unsigned long i;
// search BIOS memory for RSDP
for (i = ACPI_BIOS_ROM_BASE; i < ACPI_BIOS_ROM_END; i += 16) {
- rsdp = (struct acpi_rsdp *) i;
- if (readl(rsdp->signature) == ACPI_RSDP1_SIG
- && readl(rsdp->signature + 1) == ACPI_RSDP2_SIG) {
+ rsdp = (struct acpi_rsdp *) phys_to_virt(i);
+ if (rsdp->signature[0] == ACPI_RSDP1_SIG &&
+ rsdp->signature[1] == ACPI_RSDP2_SIG) {
char oem[7];
int j;
// strip trailing space and print OEM identifier
- memcpy_fromio(oem, rsdp->oem, 6);
+ memcpy(oem, rsdp->oem, 6);
oem[6] = '\0';
for (j = 5;
j > 0 && (oem[j] == '\0' || oem[j] == ' ');
return -ENODEV;
}
// fetch RSDT from RSDP
- rsdt = acpi_map_table(readl(&rsdp->rsdt));
+ rsdt = acpi_map_table(rsdp->rsdt);
if (!rsdt || rsdt->signature != ACPI_RSDT_SIG) {
printk(KERN_ERR "ACPI: no RSDT found\n");
acpi_unmap_table(rsdt);
ifeq ($(CONFIG_PCMCIA),y)
SUB_DIRS += pcmcia
- MOD_SUB_DIRS += pcmcia
+ MOD_IN_SUB_DIRS += pcmcia
else
ifeq ($(CONFIG_PCMCIA),m)
- MOD_SUB_DIRS += pcmcia
+ MOD_IN_SUB_DIRS += pcmcia
endif
endif
endif
endif
-ifeq ($(CONFIG_PCMCIA_PCNET),y)
-CONFIG_8390_BUILTIN = y
-else
- ifeq ($(CONFIG_PCMCIA_PCNET),m)
- CONFIG_8390_MODULE = y
- endif
-endif
-
# If anything built-in uses the 8390, then build it into the kernel also.
# If not, but a module uses it, build as a module.
ifdef CONFIG_8390_BUILTIN
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
- * Copyright (c) 1998 Corel Computer Corp.
+ * Copyright (c) 1998-1999 Rebel.com
*
* 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.
*
- * Neither Paul VanderSpek nor Corel Computer Corp. admit liability
- * nor provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
+ * Neither Paul VanderSpek nor Rebel.com admit liability nor provide
+ * warranty for any of this software. This material is provided "AS-IS"
+ * and at no charge.
*
* If you find bugs in this file, its very likely that the same bug
* will also be in pc87108.c since the implementations is quite
--- /dev/null
+/* 3c574.c: A PCMCIA ethernet driver for the 3com 3c574 "RoadRunner".
+
+ Written 1993-1998 by
+ Donald Becker, becker@cesdis.gsfc.nasa.gov, (driver core) and
+ David Hinds, dhinds@allegro.stanford.edu (derived from his PC card code).
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+
+ This driver derives from Donald Becker's 3c509 core, which has the
+ following copyright:
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+*/
+
+/* Driver author info must always be in the binary. Version too.. */
+static const char *tc574_version =
+"3c574_cs.c v1.08 9/24/98 Donald Becker/David Hinds, becker@cesdis.gsfc.nasa.gov.\n";
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the 3Com 3c574 PC card Fast Ethernet
+Adapter.
+
+II. Board-specific settings
+
+None -- PC cards are autoconfigured.
+
+III. Driver operation
+
+The 3c574 uses a Boomerang-style interface, without the bus-master capability.
+See the Boomerang driver and documentation for most details.
+
+IV. Notes and chip documentation.
+
+Two added registers are used to enhance PIO performance, RunnerRdCtrl and
+RunnerWrCtrl. These are 11 bit down-counters that are preloaded with the
+count of word (16 bits) reads or writes the driver is about to do to the Rx
+or Tx FIFO. The chip is then able to hide the internal-PCI-bus to PC-card
+translation latency by buffering the I/O operations with an 8 word FIFO.
+Note: No other chip accesses are permitted when this buffer is used.
+
+A second enhancement is that both attribute and common memory space
+0x0800-0x0fff can translated to the PIO FIFO. Thus memory operations (faster
+with *some* PCcard bridges) may be used instead of I/O operations.
+This is enabled by setting the 0x10 bit in the PCMCIA LAN COR.
+
+Some slow PC card bridges work better if they never see a WAIT signal.
+This is configured by setting the 0x20 bit in the PCMCIA LAN COR.
+Only do this after testing that it is reliable and improves performance.
+
+The upper five bits of RunnerRdCtrl are used to window into PCcard
+configuration space registers. Window 0 is the regular Boomerang/Odie
+register set, 1-5 are various PC card control registers, and 16-31 are
+the (reversed!) CIS table.
+
+A final note: writing the InternalConfig register in window 3 with an
+invalid ramWidth is Very Bad.
+
+V. References
+
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+http://www.national.com/pf/DP/DP83840.html
+
+Thanks to Terry Murphy of 3Com for providing development information for
+earlier 3Com products.
+
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.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 <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/mem_op.h>
+
+/* A few values that may be tweaked. */
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(full_duplex, "i");
+#ifdef BROKEN_FEATURES
+MODULE_PARM(use_fifo_buffer, "i");
+MODULE_PARM(use_memory_ops, "i");
+MODULE_PARM(no_wait, "i");
+#endif
+
+/* Now-standard PC card module parameters. */
+static u_int irq_mask = 0xdeb8; /* IRQ3,4,5,7,9,10,11,12,14,15 */
+static int irq_list[4] = { -1 };
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT ((800*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 64;
+
+/* Force full duplex modes? */
+static int full_duplex = 0;
+
+#ifdef BROKEN_FEATURES
+/* Performance features: best left disabled. */
+/* Set to buffer all Tx/RxFIFO accesses. */
+static int use_fifo_buffer = 0;
+/* Set iff memory ops are faster than I/O ops. */
+static int use_memory_ops = 0;
+/* Set iff disabling the WAIT signal is reliable and faster. */
+static int no_wait = 0;
+#endif
+
+/* To minimize the size of the driver source and make the driver more
+ readable not all constants are symbolically defined.
+ You'll need the manual if you want to understand driver details anyway. */
+/* Offsets from base I/O address. */
+#define EL3_DATA 0x00
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+
+/* The top five bits written to EL3_CMD are a command, the lower
+ 11 bits are the parameter, if applicable. */
+enum el3_cmds {
+ TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+ RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+ TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+ FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+ SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+ SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
+ StatsDisable = 22<<11, StopCoax = 23<<11,
+};
+
+enum elxl_status {
+ IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+ TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+ IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 };
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
+};
+
+enum Window0 {
+ Wn0EepromCmd = 10, Wn0EepromData = 12, /* EEPROM command/address, data. */
+ IntrStatus=0x0E, /* Valid in all windows. */
+};
+/* These assumes the larger EEPROM. */
+enum Win0_EEPROM_cmds {
+ EEPROM_Read = 0x200, EEPROM_WRITE = 0x100, EEPROM_ERASE = 0x300,
+ EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
+ EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+ On the "Odie" this window is always mapped at offsets 0x10-0x1f.
+ Except for TxFree, which is overlapped by RunnerWrCtrl. */
+enum Window1 {
+ TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
+ RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B,
+ TxFree = 0x0C, /* Remaining free bytes in Tx buffer. */
+ RunnerRdCtrl = 0x16, RunnerWrCtrl = 0x1c,
+};
+
+enum Window3 { /* Window 3: MAC/config bits. */
+ Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
+};
+union wn3_config {
+ int i;
+ struct w3_config_fields {
+ unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
+ int pad8:8;
+ unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
+ int pad24:7;
+ } u;
+};
+
+enum Window4 { /* Window 4: Xcvr/media bits. */
+ Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
+};
+
+#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
+
+struct el3_private {
+ dev_node_t node;
+ struct net_device_stats stats;
+ u16 advertising, partner; /* NWay media advertisement */
+ unsigned char phys[2]; /* MII device addresses. */
+ unsigned int
+ autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */
+ /* for transceiver monitoring */
+ struct timer_list media;
+ u_short media_status;
+ u_short fast_poll;
+ u_long last_irq;
+};
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+ This only set with the original DP83840 on older 3c905 boards, so the extra
+ code size of a per-interface flag is not worthwhile. */
+static char mii_preamble_required = 0;
+
+#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 =
+"3c574_cs.c 1.000 1998/1/8 Donald Becker, becker@cesdis.gsfc.nasa.gov.\n";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/* Index of functions. */
+
+static void tc574_config(dev_link_t *link);
+static void tc574_release(u_long arg);
+static int tc574_event(event_t event, int priority,
+ event_callback_args_t *args);
+
+static void mdio_sync(ioaddr_t ioaddr, int bits);
+static int mdio_read(ioaddr_t ioaddr, int phy_id, int location);
+static void mdio_write(ioaddr_t ioaddr, int phy_id, int location, int value);
+static u_short read_eeprom(ioaddr_t ioaddr, int index);
+static void wait_for_completion(struct net_device *dev, int cmd);
+
+static void tc574_reset(struct net_device *dev);
+static void media_check(u_long arg);
+static int el3_open(struct net_device *dev);
+static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void update_stats(ioaddr_t addr, struct net_device *dev);
+static struct net_device_stats *el3_get_stats(struct net_device *dev);
+static int el3_rx(struct net_device *dev, int worklimit);
+static int el3_close(struct net_device *dev);
+#ifdef HAVE_PRIVATE_IOCTL
+static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+#endif
+static void set_rx_mode(struct net_device *dev);
+
+static dev_info_t dev_info = "3c574_cs";
+
+static dev_link_t *tc574_attach(void);
+static void tc574_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+
+static void flush_stale_links(void)
+{
+ dev_link_t *link, *next;
+ for (link = dev_list; link; link = next) {
+ next = link->next;
+ if (link->state & DEV_STALE_LINK)
+ tc574_detach(link);
+ }
+}
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+#if CS_RELEASE_CODE < 0x2911
+ CardServices(ReportError, dev_info, (void *)func, (void *)ret);
+#else
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+#endif
+}
+
+/*
+ We never need to do anything when a tc574 device is "initialized"
+ by the net software, because we only register already-found cards.
+*/
+
+static int tc574_init(struct net_device *dev)
+{
+ return 0;
+}
+
+/*
+ tc574_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+*/
+
+static dev_link_t *tc574_attach(void)
+{
+ client_reg_t client_reg;
+ dev_link_t *link;
+ struct net_device *dev;
+ int i, ret;
+
+ DEBUG(0, "3c574_attach()\n");
+ flush_stale_links();
+
+ /* Create the PC card device object. */
+ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+ memset(link, 0, sizeof(struct dev_link_t));
+ link->release.function = &tc574_release;
+ link->release.data = (u_long)link;
+ link->io.NumPorts1 = 32;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ link->io.IOAddrLines = 5;
+ 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 = &el3_interrupt;
+ 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;
+
+ /* Create the network device object. */
+ dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+ memset(dev, 0, sizeof(struct net_device));
+
+ /* Make up a Odie-specific data structure. */
+ dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct el3_private));
+
+ /* The EL3-specific entries in the device structure. */
+ dev->hard_start_xmit = &el3_start_xmit;
+ dev->get_stats = &el3_get_stats;
+#ifdef HAVE_PRIVATE_IOCTL
+ dev->do_ioctl = &private_ioctl;
+#endif
+ dev->set_multicast_list = &set_rx_mode;
+ ether_setup(dev);
+ dev->name = ((struct el3_private *)dev->priv)->node.dev_name;
+ dev->init = &tc574_init;
+ dev->open = &el3_open;
+ dev->stop = &el3_close;
+ dev->tbusy = 1;
+
+ link->priv = dev;
+#if CS_RELEASE_CODE > 0x2911
+ link->irq.Instance = dev;
+#endif
+
+ /* 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 = &tc574_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);
+ tc574_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* tc574_attach */
+
+/*
+
+ 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 tc574_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+ long flags;
+
+ DEBUG(0, "3c574_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+ if (*linkp == NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+ if (link->state & DEV_RELEASE_PENDING) {
+ del_timer(&link->release);
+ link->state &= ~DEV_RELEASE_PENDING;
+ }
+ restore_flags(flags);
+
+ if (link->state & DEV_CONFIG) {
+ tc574_release((u_long)link);
+ if (link->state & DEV_STALE_CONFIG) {
+ link->state |= DEV_STALE_LINK;
+ return;
+ }
+ }
+
+ if (link->handle)
+ CardServices(DeregisterClient, link->handle);
+
+ /* Unlink device structure, free bits */
+ *linkp = link->next;
+ if (link->priv) {
+ struct net_device *dev = link->priv;
+ if (link->dev != NULL)
+ unregister_netdev(dev);
+ if (dev->priv)
+ kfree(dev->priv);
+ kfree(link->priv);
+ }
+ kfree(link);
+
+} /* tc574_detach */
+
+/*
+ tc574_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ ethernet device available to the system.
+*/
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+static void tc574_config(dev_link_t *link)
+{
+ client_handle_t handle;
+ struct net_device *dev;
+ struct el3_private *lp;
+ tuple_t tuple;
+ cisparse_t parse;
+ u_short buf[32];
+ int last_fn, last_ret, i, j;
+ ioaddr_t ioaddr;
+ u16 *phys_addr;
+ char *cardname;
+
+ handle = link->handle;
+ dev = link->priv;
+ phys_addr = (u16 *)dev->dev_addr;
+
+ DEBUG(0, "3c574_config(0x%p)\n", link);
+
+ tuple.Attributes = 0;
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ CS_CHECK(GetFirstTuple, handle, &tuple);
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ 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;
+
+ for (i = j = 0; 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;
+ }
+ CS_CHECK(RequestIRQ, link->handle, &link->irq);
+ CS_CHECK(RequestConfiguration, link->handle, &link->conf);
+
+ dev->mem_start = 0;
+#ifdef BROKEN_FEATURES
+ if (use_memory_ops) {
+ win_req_t req;
+ memreq_t mem;
+ req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM |
+ WIN_ENABLE | WIN_USE_WAIT;
+ req.Base = 0;
+ req.Size = 0x1000;
+ req.AccessSpeed = 0;
+ link->win = (window_handle_t)link->handle;
+ i = CardServices(RequestWindow, &link->win, &req);
+ if (i == CS_SUCCESS) {
+ mem.Page = mem.CardOffset = 0;
+ CardServices(MapMemPage, link->win, &mem);
+ dev->mem_start = (long)(ioremap(req.Base, 0x1000)) + 0x800;
+ } else
+ cs_error(link->handle, RequestWindow, i);
+ }
+#endif
+
+ dev->irq = link->irq.AssignedIRQ;
+ dev->base_addr = link->io.BasePort1;
+
+ dev->tbusy = 0;
+ if (register_netdev(dev) != 0) {
+ printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n");
+ goto failed;
+ }
+
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ ioaddr = dev->base_addr;
+ lp = (struct el3_private *)dev->priv;
+ link->dev = &lp->node;
+
+ /* The 3c574 normally uses an EEPROM for configuration info, including
+ the hardware address. The future products may include a modem chip
+ and put the address in the CIS. */
+ tuple.DesiredTuple = 0x88;
+ if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) {
+ CardServices(GetTupleData, handle, &tuple);
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(buf[i]);
+ } else {
+ EL3WINDOW(0);
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i + 10));
+ if (phys_addr[0] == 0x6060) {
+ printk(KERN_NOTICE "3c574_cs: IO port conflict at 0x%03lx"
+ "-0x%03lx\n", dev->base_addr, dev->base_addr+15);
+ goto failed;
+ }
+ }
+ tuple.DesiredTuple = CISTPL_VERS_1;
+ if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS &&
+ CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS &&
+ CardServices(ParseTuple, handle, &tuple, &parse) == CS_SUCCESS) {
+ cardname = parse.version_1.str + parse.version_1.ofs[1];
+ } else
+ cardname = "3Com 3c574";
+
+ printk(KERN_INFO "%s: %s at io %#3lx, irq %d, hw_addr ",
+ dev->name, cardname, dev->base_addr, dev->irq);
+
+ for (i = 0; i < 6; i++)
+ printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : ".\n"));
+
+ if (dev->mem_start)
+ printk(KERN_INFO" Acceleration window at memory base %#lx.\n",
+ dev->mem_start);
+
+ {
+ u_char mcr, *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
+ union wn3_config config;
+ outw(2<<11, ioaddr + RunnerRdCtrl);
+ mcr = inb(ioaddr + 2);
+ outw(0<<11, ioaddr + RunnerRdCtrl);
+ printk(KERN_INFO " ASIC rev %d,", mcr>>3);
+ EL3WINDOW(3);
+ config.i = inl(ioaddr + Wn3_Config);
+ printk(" %dK FIFO split %s Rx:Tx, %sMII interface.\n",
+ 8 << config.u.ram_size, ram_split[config.u.ram_split],
+ config.u.autoselect ? "autoselect " : "");
+ lp->default_media = config.u.xcvr;
+ lp->autoselect = config.u.autoselect;
+ }
+
+ {
+ int phy, phy_idx = 0;
+
+ /* Roadrunner only: Turn on the MII transceiver */
+ outw(0x8040, ioaddr + Wn3_Options);
+ udelay(1000);
+ outw(0xc040, ioaddr + Wn3_Options);
+ wait_for_completion(dev, TxReset);
+ wait_for_completion(dev, RxReset);
+ udelay(1000);
+ outw(0x8040, ioaddr + Wn3_Options);
+
+ EL3WINDOW(4);
+ for (phy = 1; phy <= 32 && phy_idx < sizeof(lp->phys); phy++) {
+ int mii_status;
+ mdio_sync(ioaddr, 32);
+ mii_status = mdio_read(ioaddr, phy & 0x1f, 1);
+ if (mii_status != 0xffff) {
+ lp->phys[phy_idx++] = phy & 0x1f;
+ DEBUG(0, " MII transceiver at index %d, status %x.\n",
+ phy, mii_status);
+ if ((mii_status & 0x0040) == 0)
+ mii_preamble_required = 1;
+ }
+ }
+ if (phy_idx == 0) {
+ printk(KERN_NOTICE " No MII transceivers found!\n");
+ goto failed;
+ }
+ i = mdio_read(ioaddr, lp->phys[0], 16) | 0x40;
+ mdio_write(ioaddr, lp->phys[0], 16, i);
+ lp->advertising = mdio_read(ioaddr, lp->phys[0], 4);
+ if (full_duplex) {
+ /* Only advertise the FD media types. */
+ lp->advertising &= ~0x02a0;
+ mdio_write(ioaddr, lp->phys[0], 4, lp->advertising);
+ }
+ }
+
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+failed:
+ tc574_release((u_long)link);
+ return;
+
+} /* tc574_config */
+
+/*
+ After a card is removed, tc574_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 tc574_release(u_long arg)
+{
+ dev_link_t *link = (dev_link_t *)arg;
+ struct net_device *dev = link->priv;
+
+ DEBUG(0, "3c574_release(0x%p)\n", link);
+
+ if (link->open) {
+ DEBUG(1, "3c574_cs: release postponed, '%s' still open\n",
+ link->dev->dev_name);
+ link->state |= DEV_STALE_CONFIG;
+ return;
+ }
+
+ CardServices(ReleaseConfiguration, link->handle);
+ CardServices(ReleaseIO, link->handle, &link->io);
+ CardServices(ReleaseIRQ, link->handle, &link->irq);
+ if (link->win) {
+ iounmap((void *)(dev->mem_start - 0x800));
+ CardServices(ReleaseWindow, link->win);
+ }
+
+ link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
+
+} /* tc574_release */
+
+/*
+ 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 tc574_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ struct net_device *dev = link->priv;
+
+ DEBUG(1, "3c574_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ dev->tbusy = 1; dev->start = 0;
+ link->release.expires = jiffies + HZ/20;
+ add_timer(&link->release);
+ }
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ tc574_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) {
+ dev->tbusy = 1; dev->start = 0;
+ }
+ 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) {
+ tc574_reset(dev);
+ dev->tbusy = 0; dev->start = 1;
+ }
+ }
+ break;
+ }
+ return 0;
+} /* tc574_event */
+
+static void dump_status(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ EL3WINDOW(1);
+ printk(KERN_INFO " irq status %04x, rx status %04x, tx status "
+ "%02x, tx free %04x\n", inw(ioaddr+EL3_STATUS),
+ inw(ioaddr+RxStatus), inb(ioaddr+TxStatus),
+ inw(ioaddr+TxFree));
+ EL3WINDOW(4);
+ printk(KERN_INFO " diagnostics: fifo %04x net %04x ethernet %04x"
+ " media %04x\n", inw(ioaddr+0x04), inw(ioaddr+0x06),
+ inw(ioaddr+0x08), inw(ioaddr+0x0a));
+ EL3WINDOW(1);
+}
+
+/*
+ Use this for commands that may take time to finish
+*/
+static void wait_for_completion(struct net_device *dev, int cmd)
+{
+ int i = 1500;
+ outw(cmd, dev->base_addr + EL3_CMD);
+ while (--i > 0)
+ if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break;
+ if (i == 0)
+ printk(KERN_NOTICE "%s: command 0x%04x did not complete!\n",
+ dev->name, cmd);
+}
+
+/* Read a word from the EEPROM using the regular EEPROM access register.
+ Assume that we are in register window zero.
+ */
+static u_short read_eeprom(ioaddr_t ioaddr, int index)
+{
+ int timer;
+ outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd);
+ /* Pause for at least 162 usec for the read to take place. */
+ for (timer = 1620; timer >= 0; timer--) {
+ if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
+ break;
+ }
+ return inw(ioaddr + Wn0EepromData);
+}
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details.
+ The maxium data clock rate is 2.5 Mhz. The timing is easily met by the
+ slow PC card interface. */
+
+#define MDIO_SHIFT_CLK 0x01
+#define MDIO_DIR_WRITE 0x04
+#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
+#define MDIO_DATA_READ 0x02
+#define MDIO_ENB_IN 0x00
+
+/* Generate the preamble required for initial synchronization and
+ a few older transceivers. */
+static void mdio_sync(ioaddr_t ioaddr, int bits)
+{
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+ /* Establish sync by sending at least 32 logic ones. */
+ while (-- bits >= 0) {
+ outw(MDIO_DATA_WRITE1, mdio_addr);
+ outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+ }
+}
+
+static int mdio_read(ioaddr_t ioaddr, int phy_id, int location)
+{
+ int i;
+ int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ unsigned int retval = 0;
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+ if (mii_preamble_required)
+ mdio_sync(ioaddr, 32);
+
+ /* Shift the read command bits out. */
+ for (i = 14; i >= 0; i--) {
+ int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outw(dataval, mdio_addr);
+ outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ outw(MDIO_ENB_IN, mdio_addr);
+ retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+ outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(ioaddr_t ioaddr, int phy_id, int location, int value)
+{
+ int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+ int i;
+
+ if (mii_preamble_required)
+ mdio_sync(ioaddr, 32);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outw(dataval, mdio_addr);
+ outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+ }
+ /* Leave the interface idle. */
+ for (i = 1; i >= 0; i--) {
+ outw(MDIO_ENB_IN, mdio_addr);
+ outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ }
+
+ return;
+}
+
+/* Reset and restore all of the 3c574 registers. */
+static void tc574_reset(struct net_device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ int i, ioaddr = dev->base_addr;
+
+ wait_for_completion(dev, TotalReset|0x10);
+
+#ifdef BROKEN_FEATURES
+ /* Set the PIO ctrl bits in the PC card LAN COR using Runner window 1. */
+ if (dev->mem_start || no_wait) {
+ u8 lan_cor;
+ outw(1<<11, ioaddr + RunnerRdCtrl);
+ lan_cor = inw(ioaddr) & ~0x30;
+ if (dev->mem_start) /* Iff use_memory_ops worked! */
+ lan_cor |= 0x10;
+ if (no_wait)
+ lan_cor |= 0x20;
+ outw(lan_cor, ioaddr);
+ }
+#endif
+ /* Clear any transactions in progress. */
+ outw(0, ioaddr + RunnerWrCtrl);
+ outw(0, ioaddr + RunnerRdCtrl);
+
+ /* Set the station address and mask. */
+ EL3WINDOW(2);
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+ for (; i < 12; i+=2)
+ outw(0, ioaddr + i);
+
+ /* Reset config options */
+ EL3WINDOW(3);
+ outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
+ outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b,
+ ioaddr + Wn3_Config);
+
+ /* Roadrunner only: Turn on the MII transceiver. */
+ outw(0x8040, ioaddr + Wn3_Options);
+ udelay(1000);
+ outw(0xc040, ioaddr + Wn3_Options);
+ wait_for_completion(dev, TxReset);
+ wait_for_completion(dev, RxReset);
+ udelay(1000);
+ outw(0x8040, ioaddr + Wn3_Options);
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ EL3WINDOW(6);
+ for (i = 0; i < 10; i++)
+ inb(ioaddr + i);
+ inw(ioaddr + 10);
+ inw(ioaddr + 12);
+
+ EL3WINDOW(4);
+ /* New: On the Vortex/Odie we must also clear the BadSSD counter.. */
+ inb(ioaddr + 12);
+ /* .. enable any extra statistics bits.. */
+ outw(0x0040, ioaddr + Wn4_NetDiag);
+ /* .. re-sync MII and re-fill what NWay is advertising. */
+ mdio_sync(ioaddr, 32);
+ mdio_write(ioaddr, lp->phys[0], 4, lp->advertising);
+
+ /* Switch to register set 1 for normal use, just for TxFree. */
+ EL3WINDOW(1);
+
+ set_rx_mode(dev);
+ outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ ioaddr + EL3_CMD);
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+ | AdapterFailure | RxEarly, ioaddr + EL3_CMD);
+}
+
+static int el3_open(struct net_device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ dev_link_t *link;
+
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+ if (!DEV_OK(link))
+ return -ENODEV;
+
+ link->open++;
+ MOD_INC_USE_COUNT;
+ dev->interrupt = 0; dev->tbusy = 0; dev->start = 1;
+
+ tc574_reset(dev);
+ lp->media.function = &media_check;
+ lp->media.data = (u_long)dev;
+ lp->media.expires = jiffies + HZ;
+ add_timer(&lp->media);
+
+ DEBUG(2, "%s: opened, status %4.4x.\n",
+ dev->name, inw(dev->base_addr + EL3_STATUS));
+
+ return 0; /* Always succeed */
+}
+
+static void el3_tx_timeout(struct net_device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+
+ printk(KERN_NOTICE "%s: Transmit timed out!\n", dev->name);
+ dump_status(dev);
+ lp->stats.tx_errors++;
+ dev->trans_start = jiffies;
+ /* Issue TX_RESET and TX_START commands. */
+ wait_for_completion(dev, TxReset);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->tbusy = 0;
+}
+
+static void pop_tx_status(struct net_device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ int i;
+
+ /* Clear the Tx status stack. */
+ for (i = 32; i > 0; i--) {
+ u_char tx_status = inb(ioaddr + TxStatus);
+ if (!(tx_status & 0x84)) break;
+ /* reset transmitter on jabber error or underrun */
+ if (tx_status & 0x30)
+ wait_for_completion(dev, TxReset);
+ if (tx_status & 0x38) {
+ DEBUG(1, "%s: transmit error: status 0x%02x\n",
+ dev->name, tx_status);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ lp->stats.tx_aborted_errors++;
+ }
+ outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
+ }
+}
+
+static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+#ifdef BROKEN_FEATURES
+ long flags = 0;
+#endif
+
+ /* Transmitter timeout, serious problems. */
+ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+ if (jiffies - dev->trans_start < TX_TIMEOUT)
+ return 1;
+ el3_tx_timeout(dev);
+ }
+
+ DEBUG(3, "%s: el3_start_xmit(length = %ld) called, "
+ "status %4.4x.\n", dev->name, (long)skb->len,
+ inw(ioaddr + EL3_STATUS));
+
+#ifdef BROKEN_FEATURES
+ if (use_fifo_buffer) {
+ /* Avoid other accesses to the chip while RunnerWrCtrl is non-zero. */
+ save_flags(flags);
+ cli();
+ outw((((skb->len + 7)>>2)<<1), ioaddr + RunnerWrCtrl);
+ DEBUG(0, "TxFree %x, tx length %x, RunnerWrCtrl is %4.4x.\n",
+ inw(ioaddr+TxFree), skb->len, inw(ioaddr+RunnerWrCtrl));
+ }
+
+ /* Put out the doubleword header... */
+ /* ... and the packet rounded to a doubleword. */
+ if (dev->mem_start) {
+ writew(skb->len, (void *)dev->mem_start);
+ writew(0, (void *)dev->mem_start);
+ copy_to_pc((void*)dev->mem_start, skb->data, (skb->len+3)&~3);
+ } else {
+ outw(skb->len, ioaddr + TX_FIFO);
+ outw(0, ioaddr + TX_FIFO);
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
+ }
+
+ if (use_fifo_buffer) {
+ DEBUG(0, " RunnerWr/RdCtrl is %4.4x/%4.4x, TxFree %x.\n",
+ inw(ioaddr + RunnerWrCtrl), inw(ioaddr + RunnerRdCtrl),
+ inw(ioaddr + TxFree));
+ restore_flags(flags);
+ }
+#else
+ outw(skb->len, ioaddr + TX_FIFO);
+ outw(0, ioaddr + TX_FIFO);
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
+#endif
+
+ dev->trans_start = jiffies;
+
+ /* TxFree appears only in Window 1, not offset 0x1c. */
+ if (inw(ioaddr + TxFree) > 1536) {
+ dev->tbusy = 0;
+ } else
+ /* Interrupt us when the FIFO has room for max-sized packet.
+ The threshold is in units of dwords. */
+ outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
+
+ dev_kfree_skb (skb);
+ pop_tx_status(dev);
+
+ return 0;
+}
+
+/* The EL3 interrupt handler. */
+static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct el3_private *lp;
+ ioaddr_t ioaddr, status;
+ int work_budget = max_interrupt_work;
+
+ if ((dev == NULL) || !dev->start)
+ return;
+ lp = (struct el3_private *)dev->priv;
+ ioaddr = dev->base_addr;
+
+#ifdef PCMCIA_DEBUG
+ if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+ printk(KERN_NOTICE "%s: re-entering the interrupt handler.\n",
+ dev->name);
+ return;
+ }
+ DEBUG(3, "%s: interrupt, status %4.4x.\n",
+ dev->name, inw(ioaddr + EL3_STATUS));
+#endif
+
+ while ((status = inw(ioaddr + EL3_STATUS)) &
+ (IntLatch | RxComplete | RxEarly | StatsFull)) {
+ if ((dev->start == 0) || ((status & 0xe000) != 0x2000)) {
+ DEBUG(1, "%s: Interrupt from dead card\n", dev->name);
+ break;
+ }
+
+ if (status & RxComplete)
+ work_budget = el3_rx(dev, work_budget);
+
+ if (status & TxAvailable) {
+ DEBUG(3, " TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ if (status & TxComplete)
+ pop_tx_status(dev);
+
+ if (status & (AdapterFailure | RxEarly | StatsFull)) {
+ /* Handle all uncommon interrupts. */
+ if (status & StatsFull)
+ update_stats(ioaddr, dev);
+ if (status & RxEarly) {
+ work_budget = el3_rx(dev, work_budget);
+ outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+ }
+ if (status & AdapterFailure) {
+ u16 fifo_diag;
+ EL3WINDOW(4);
+ fifo_diag = inw(ioaddr + Wn4_FIFODiag);
+ EL3WINDOW(1);
+ printk(KERN_NOTICE "%s: adapter failure, FIFO diagnostic"
+ " register %04x.\n", dev->name, fifo_diag);
+ if (fifo_diag & 0x0400) {
+ /* Tx overrun */
+ wait_for_completion(dev, TxReset);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ }
+ if (fifo_diag & 0x2000) {
+ /* Rx underrun */
+ wait_for_completion(dev, RxReset);
+ set_rx_mode(dev);
+ outw(RxEnable, ioaddr + EL3_CMD);
+ }
+ outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+ }
+ }
+
+ if (--work_budget < 0) {
+ DEBUG(0, "%s: Too much work in interrupt, "
+ "status %4.4x.\n", dev->name, status);
+ /* Clear all interrupts */
+ outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+ }
+
+#ifdef PCMCIA_DEBUG
+ DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",
+ dev->name, inw(ioaddr + EL3_STATUS));
+ dev->interrupt = 0;
+#endif
+ return;
+}
+
+/*
+ This timer serves two purposes: to check for missed interrupts
+ (and as a last resort, poll the NIC for events), and to monitor
+ the MII, reporting changes in cable status.
+*/
+static void media_check(u_long arg)
+{
+ struct net_device *dev = (struct net_device *)(arg);
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ u_long flags;
+ u_short /* cable, */ media, partner;
+
+ if (dev->start == 0) goto reschedule;
+
+ /* Check for pending interrupt with expired latency timer: with
+ this, we can limp along even if the interrupt is blocked */
+ if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
+ (inb(ioaddr + Timer) == 0xff)) {
+ if (!lp->fast_poll)
+ printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);
+ el3_interrupt(dev->irq, dev, NULL);
+ lp->fast_poll = HZ;
+ }
+ if (lp->fast_poll) {
+ lp->fast_poll--;
+ lp->media.expires = jiffies + 2;
+ add_timer(&lp->media);
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+ EL3WINDOW(4);
+ media = mdio_read(ioaddr, lp->phys[0], 1);
+ partner = mdio_read(ioaddr, lp->phys[0], 5);
+ EL3WINDOW(1);
+ restore_flags(flags);
+
+ if (media != lp->media_status) {
+ if ((media ^ lp->media_status) & 0x0004)
+ printk(KERN_INFO "%s: %s link beat\n", dev->name,
+ (lp->media_status & 0x0004) ? "lost" : "found");
+ if ((media ^ lp->media_status) & 0x0020) {
+ lp->partner = 0;
+ if (lp->media_status & 0x0020) {
+ printk(KERN_INFO "%s: autonegotiation restarted\n",
+ dev->name);
+ } else if (partner) {
+ partner &= lp->advertising;
+ lp->partner = partner;
+ printk(KERN_INFO "%s: autonegotiation complete: "
+ "%sbaseT-%cD selected\n", dev->name,
+ ((partner & 0x0180) ? "100" : "10"),
+ ((partner & 0x0140) ? 'F' : 'H'));
+ } else {
+ printk(KERN_INFO "%s: link partner did not autonegotiate\n",
+ dev->name);
+ }
+
+ EL3WINDOW(3);
+ outb((partner & 0x0140 ? 0x20 : 0) |
+ (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
+ EL3WINDOW(1);
+
+ }
+ if (media & 0x0010)
+ printk(KERN_INFO "%s: remote fault detected\n",
+ dev->name);
+ if (media & 0x0002)
+ printk(KERN_INFO "%s: jabber detected\n", dev->name);
+ lp->media_status = media;
+ }
+
+reschedule:
+ lp->media.expires = jiffies + HZ;
+ add_timer(&lp->media);
+}
+
+static struct net_device_stats *el3_get_stats(struct net_device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+
+ if (dev->start)
+ update_stats(dev->base_addr, dev);
+ return &lp->stats;
+}
+
+/* Update statistics.
+ Suprisingly this need not be run single-threaded, but it effectively is.
+ The counters clear when read, so the adds must merely be atomic.
+ */
+static void update_stats(ioaddr_t ioaddr, struct net_device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ u8 upper_cnt;
+
+ DEBUG(2, "%s: updating the statistics.\n", dev->name);
+
+ if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */
+ return;
+
+ /* Unlike the 3c509 we need not turn off stats updates while reading. */
+ /* Switch to the stats window, and read everything. */
+ EL3WINDOW(6);
+ lp->stats.tx_carrier_errors += inb(ioaddr + 0);
+ lp->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */ inb(ioaddr + 2);
+ lp->stats.collisions += inb(ioaddr + 3);
+ lp->stats.tx_window_errors += inb(ioaddr + 4);
+ lp->stats.rx_fifo_errors += inb(ioaddr + 5);
+ lp->stats.tx_packets += inb(ioaddr + 6);
+ upper_cnt = inb(ioaddr + 9);
+ lp->stats.tx_packets += (upper_cnt&0x30) << 4;
+ /* Rx packets */ inb(ioaddr + 7);
+ /* Tx deferrals */ inb(ioaddr + 8);
+ lp->stats.rx_bytes += inw(ioaddr + 10);
+ lp->stats.tx_bytes += inw(ioaddr + 12);
+
+ /* With Vortex and later we must also clear the BadSSD counter. */
+ EL3WINDOW(4);
+ inb(ioaddr + 12);
+
+ EL3WINDOW(1);
+}
+
+static int el3_rx(struct net_device *dev, int worklimit)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ short rx_status;
+
+ DEBUG(3, "%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
+ while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) &&
+ (--worklimit >= 0)) {
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ short error = rx_status & 0x3800;
+ lp->stats.rx_errors++;
+ switch (error) {
+ case 0x0000: lp->stats.rx_over_errors++; break;
+ case 0x0800: lp->stats.rx_length_errors++; break;
+ case 0x1000: lp->stats.rx_frame_errors++; break;
+ case 0x1800: lp->stats.rx_length_errors++; break;
+ case 0x2000: lp->stats.rx_frame_errors++; break;
+ case 0x2800: lp->stats.rx_crc_errors++; break;
+ }
+ } else {
+ short pkt_len = rx_status & 0x7ff;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len+5);
+
+ DEBUG(3, " Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+ if (skb != NULL) {
+ skb->dev = dev;
+ skb_reserve(skb, 2);
+
+#ifdef BROKEN_FEATURES
+ if (use_fifo_buffer) {
+ outw(((pkt_len+3)>>2)<<1, ioaddr + RunnerRdCtrl);
+ DEBUG(0,"Start Rx %x -- RunnerRdCtrl is %4.4x.\n",
+ pkt_len, inw(ioaddr + RunnerRdCtrl));
+ }
+ if (dev->mem_start) {
+ copy_from_pc(skb_put(skb, pkt_len),
+ (void*)dev->mem_start, (pkt_len+3)&~3);
+ } else {
+ insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
+ ((pkt_len+3)>>2));
+ }
+ if (use_fifo_buffer) {
+ DEBUG(0," RunnerRdCtrl is now %4.4x.\n",
+ inw(ioaddr + RunnerRdCtrl));
+ outw(0, ioaddr + RunnerRdCtrl);
+ DEBUG(0, " Rx packet with data %2.2x:%2.2x:%2.2x\n",
+ skb->head[0], skb->head[1], skb->head[2]);
+ }
+#else
+ insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
+ ((pkt_len+3)>>2));
+#endif
+
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ } else {
+ DEBUG(1, "%s: couldn't allocate a sk_buff of"
+ " size %d.\n", dev->name, pkt_len);
+ lp->stats.rx_dropped++;
+ }
+ }
+ wait_for_completion(dev, RxDiscard);
+ }
+
+ return worklimit;
+}
+
+#ifdef HAVE_PRIVATE_IOCTL
+/* Provide ioctl() calls to examine the MII xcvr state. */
+static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct el3_private *vp = (struct el3_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ u16 *data = (u16 *)&rq->ifr_data;
+ int phy = vp->phys[0] & 0x1f;
+
+ DEBUG(2, "%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
+ dev->name, rq->ifr_ifrn.ifrn_name, cmd,
+ data[0], data[1], data[2], data[3]);
+
+ switch(cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ data[0] = phy;
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
+ {
+ int saved_window;
+ long flags;
+
+ save_flags(flags);
+ cli();
+ saved_window = inw(ioaddr + EL3_CMD) >> 13;
+ EL3WINDOW(4);
+ data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+ EL3WINDOW(saved_window);
+ restore_flags(flags);
+ return 0;
+ }
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+ {
+ int saved_window;
+ long flags;
+
+ if (!suser())
+ return -EPERM;
+ save_flags(flags);
+ cli();
+ saved_window = inw(ioaddr + EL3_CMD) >> 13;
+ EL3WINDOW(4);
+ mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+ EL3WINDOW(saved_window);
+ restore_flags(flags);
+ return 0;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+#endif /* HAVE_PRIVATE_IOCTL */
+
+/* The Odie chip has a 64 bin multicast filter, but the bit layout is not
+ documented. Until it is we revert to receiving all multicast frames when
+ any multicast reception is desired.
+ Note: My other drivers emit a log message whenever promiscuous mode is
+ entered to help detect password sniffers. This is less desirable on
+ typical PC card machines, so we omit the message.
+ */
+
+static void set_rx_mode(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+
+ if (dev->flags & IFF_PROMISC)
+ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+ ioaddr + EL3_CMD);
+ else if (dev->mc_count || (dev->flags & IFF_ALLMULTI))
+ outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+ else
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+}
+
+static int el3_close(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ dev_link_t *link;
+
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+ if (link == NULL)
+ return -ENODEV;
+
+ DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
+
+ if (DEV_OK(link)) {
+ /* Turn off statistics ASAP. We update lp->stats below. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, ioaddr + EL3_CMD);
+ outw(TxDisable, ioaddr + EL3_CMD);
+
+ /* Note: Switching to window 0 may disable the IRQ. */
+ EL3WINDOW(0);
+
+ update_stats(ioaddr, dev);
+ }
+
+ link->open--;
+ dev->start = 0;
+ del_timer(&((struct el3_private *)dev->priv)->media);
+ if (link->state & DEV_STALE_CONFIG) {
+ link->release.expires = jiffies + HZ/20;
+ link->state |= DEV_RELEASE_PENDING;
+ add_timer(&link->release);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static int __init init_3c574_cs(void)
+{
+ servinfo_t serv;
+
+ /* Always emit the version, before any failure. */
+ printk(KERN_INFO"%s", tc574_version);
+ DEBUG(0, "%s\n", version);
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "3c574_cs: Card Services release "
+ "does not match!\n");
+ return -1;
+ }
+ register_pccard_driver(&dev_info, &tc574_attach, &tc574_detach);
+ return 0;
+}
+
+static void __exit exit_3c574_cs(void)
+{
+ DEBUG(0, "3c574_cs: unloading\n");
+ unregister_pccard_driver(&dev_info);
+ while (dev_list != NULL)
+ tc574_detach(dev_list);
+}
+
+module_init(init_3c574_cs);
+module_exit(exit_3c574_cs);
+
+/*
+ * Local variables:
+ * compile-command: "make 3c574_cs.o"
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu
- 3c589_cs.c 1.134 1999/09/15 15:33:09
+ 3c589_cs.c 1.135 1999/10/07 20:14:54
The network driver code is based on Donald Becker's 3c589 code:
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version =
-"3c589_cs.c 1.134 1999/09/15 15:33:09 (David Hinds)";
+"3c589_cs.c 1.135 1999/10/07 20:14:54 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
static int tc589_event(event_t event, int priority,
event_callback_args_t *args);
-static ushort read_eeprom(short ioaddr, int index);
+static u_short read_eeprom(ioaddr_t ioaddr, int index);
static void tc589_reset(struct net_device *dev);
static void media_check(u_long arg);
static int el3_config(struct net_device *dev, struct ifmap *map);
static int el3_open(struct net_device *dev);
static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static void update_stats(int addr, struct net_device *dev);
+static void update_stats(ioaddr_t addr, struct net_device *dev);
static struct net_device_stats *el3_get_stats(struct net_device *dev);
static int el3_rx(struct net_device *dev);
static int el3_close(struct net_device *dev);
struct net_device *dev;
tuple_t tuple;
cisparse_t parse;
- u_short buf[32];
+ u_short buf[32], *phys_addr;
int last_fn, last_ret, i, j, multi = 0;
- short ioaddr, *phys_addr;
+ ioaddr_t ioaddr;
char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
handle = link->handle;
dev = link->priv;
- phys_addr = (short *)dev->dev_addr;
+ phys_addr = (u_short *)dev->dev_addr;
DEBUG(0, "3c589_config(0x%p)\n", link);
Read a word from the EEPROM using the regular EEPROM access register.
Assume that we are in register window zero.
*/
-static ushort read_eeprom(short ioaddr, int index)
+static u_short read_eeprom(ioaddr_t ioaddr, int index)
{
int i;
outw(EEPROM_READ + index, ioaddr + 10);
static void tc589_set_xcvr(struct net_device *dev, int if_port)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
- ushort ioaddr = dev->base_addr;
+ ioaddr_t ioaddr = dev->base_addr;
EL3WINDOW(0);
switch (if_port) {
static void dump_status(struct net_device *dev)
{
- int ioaddr = dev->base_addr;
+ ioaddr_t ioaddr = dev->base_addr;
EL3WINDOW(1);
printk(KERN_INFO " irq status %04x, rx status %04x, tx status "
"%02x tx free %04x\n", inw(ioaddr+EL3_STATUS),
/* Reset and restore all of the 3c589 registers. */
static void tc589_reset(struct net_device *dev)
{
- ushort ioaddr = dev->base_addr;
+ ioaddr_t ioaddr = dev->base_addr;
int i;
EL3WINDOW(0);
static void el3_tx_timeout(struct net_device *dev)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
- int ioaddr = dev->base_addr;
+ ioaddr_t ioaddr = dev->base_addr;
printk(KERN_NOTICE "%s: Transmit timed out!\n", dev->name);
dump_status(dev);
static void pop_tx_status(struct net_device *dev)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
- int ioaddr = dev->base_addr;
+ ioaddr_t ioaddr = dev->base_addr;
int i;
/* Clear the Tx status stack. */
static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
- int ioaddr = dev->base_addr;
+ ioaddr_t ioaddr = dev->base_addr;
/* Transmitter timeout, serious problems. */
if (dev->tbusy) {
{
struct net_device *dev = (struct net_device *)dev_id;
struct el3_private *lp;
- int ioaddr, status;
+ ioaddr_t ioaddr, status;
int i = 0;
if ((dev == NULL) || !dev->start)
{
struct net_device *dev = (struct net_device *)(arg);
struct el3_private *lp = (struct el3_private *)dev->priv;
- int ioaddr = dev->base_addr;
+ ioaddr_t ioaddr = dev->base_addr;
u_short media, errs;
u_long flags;
operation, and it's simpler for the rest of the driver to assume that
window 1 is always valid rather than use a special window-state variable.
*/
-static void update_stats(int ioaddr, struct net_device *dev)
+static void update_stats(ioaddr_t ioaddr, struct net_device *dev)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
static int el3_rx(struct net_device *dev)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
- int ioaddr = dev->base_addr;
+ ioaddr_t ioaddr = dev->base_addr;
int worklimit = 32;
short rx_status;
*/
static void set_multicast_list(struct net_device *dev)
{
- short ioaddr = dev->base_addr;
+ ioaddr_t ioaddr = dev->base_addr;
dev_link_t *link;
for (link = dev_list; link; link = link->next)
if (link->priv == dev) break;
static int el3_close(struct net_device *dev)
{
- int ioaddr = dev->base_addr;
+ ioaddr_t ioaddr = dev->base_addr;
dev_link_t *link;
for (link = dev_list; link; link = link->next)
#
mainmenu_option next_comment
-comment 'PCMCIA network devices'
+comment 'PCMCIA network device support'
-dep_tristate 'PCMCIA ethernet cards (NE2000 compatibles: DE-650, ...)' CONFIG_PCMCIA_PCNET $CONFIG_PCMCIA
-dep_tristate '3Com 3c589 PCMCIA card' CONFIG_PCMCIA_3C589 $CONFIG_PCMCIA
-dep_tristate 'Aviator/Raytheon 2.4MHz wireless' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA
+bool 'PCMCIA network device support' CONFIG_NET_PCMCIA
+if [ "$CONFIG_NET_PCMCIA" = "y" ]; then
+ dep_tristate ' 3Com 3c589 PCMCIA support' CONFIG_PCMCIA_3C589 $CONFIG_PCMCIA
+ dep_tristate ' 3Com 3c574 PCMCIA support' CONFIG_PCMCIA_3C574 $CONFIG_PCMCIA
+ dep_tristate ' Fujitsu FMV-J18x PCMCIA support' CONFIG_PCMCIA_FMVJ18X $CONFIG_PCMCIA
+ dep_tristate ' NE2000 compatible PCMCIA support' CONFIG_PCMCIA_PCNET $CONFIG_PCMCIA
+ dep_tristate ' New Media PCMCIA support' CONFIG_PCMCIA_NMCLAN $CONFIG_PCMCIA
+ dep_tristate ' SMC 91Cxx PCMCIA support' CONFIG_PCMCIA_SMC91C92 $CONFIG_PCMCIA
+ dep_tristate ' Xircom 16-bit PCMCIA support' CONFIG_PCMCIA_XIRC2PS $CONFIG_PCMCIA
-if [ "$CONFIG_PCMCIA_3C589" = "y" -o "$CONFIG_PCMCIA_RAYCS" = "y" -o "$CONFIG_PCMCIA_PCNET" = "y" ]; then
+ # if [ "$CONFIG_CARDBUS" = "y" ]; then
+ # dep_tristate ' 3Com 3c575 CardBus support' CONFIG_PCMCIA_3C575 $CONFIG_PCMCIA
+ # dep_tristate ' DEC Tulip CardBus support' CONFIG_PCMCIA_TULIP $CONFIG_PCMCIA
+ # dep_tristate ' SMC EPIC CardBus support' CONFIG_PCMCIA_EPIC100 $CONFIG_PCMCIA
+ # fi
+
+ 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
+fi
+
+if [ "$CONFIG_PCMCIA_3C589" = "y" -o "$CONFIG_PCMCIA_3C574" = "y" -o \
+ "$CONFIG_PCMCIA_FMVJ18X" = "y" -o "$CONFIG_PCMCIA_PCNET" = "y" -o \
+ "$CONFIG_PCMCIA_NMCLAN" = "y" -o "$CONFIG_PCMCIA_SMC91C92" = "y" -o \
+ "$CONFIG_PCMCIA_XIRC2PS" = "y" -o "$CONFIG_PCMCIA_RAYCS" = "y" -o \
+ "$CONFIG_PCMCIA_NETWAVE" = "y" -o "$CONFIG_PCMCIA_WAVELAN" = "y" ]; then
define_bool CONFIG_PCMCIA_NETCARD y
fi
M_OBJS :=
MOD_LIST_NAME := PCMCIA_MODULES
+#CFLAGS_3c575_cb.o = -DCARDBUS
+#CFLAGS_tulip_cb.o = -DCARDBUS
+
+ifeq ($(CONFIG_PCMCIA_3C589),y)
+ O_OBJS += 3c589_cs.o
+else
+ ifeq ($(CONFIG_PCMCIA_3C589),m)
+ M_OBJS += 3c589_cs.o
+ endif
+endif
+
+ifeq ($(CONFIG_PCMCIA_3C574),y)
+ O_OBJS += 3c574_cs.o
+else
+ ifeq ($(CONFIG_PCMCIA_3C574),m)
+ M_OBJS += 3c574_cs.o
+ endif
+endif
+
+ifeq ($(CONFIG_PCMCIA_FMVJ18X),y)
+ O_OBJS += fmvj18x_cs.o
+else
+ ifeq ($(CONFIG_PCMCIA_FMVJ18X),m)
+ M_OBJS += fmvj18x_cs.o
+ endif
+endif
+
+ifeq ($(CONFIG_PCMCIA_NMCLAN),y)
+ O_OBJS += nmclan_cs.o
+else
+ ifeq ($(CONFIG_PCMCIA_NMCLAN),m)
+ M_OBJS += nmclan_cs.o
+ endif
+endif
+
ifeq ($(CONFIG_PCMCIA_PCNET),y)
O_OBJS += pcnet_cs.o
else
ifeq ($(CONFIG_PCMCIA_PCNET),m)
- MX_OBJS += pcnet_cs.o
+ M_OBJS += pcnet_cs.o
endif
endif
-ifeq ($(CONFIG_PCMCIA_3C589),y)
- O_OBJS += 3c589_cs.o
+ifeq ($(CONFIG_PCMCIA_SMC91C92),y)
+ O_OBJS += smc91c92_cs.o
else
- ifeq ($(CONFIG_PCMCIA_3C589),m)
- MX_OBJS += 3c589_cs.o
+ ifeq ($(CONFIG_PCMCIA_SMC91C92),m)
+ M_OBJS += smc91c92_cs.o
endif
endif
+#ifeq ($(CONFIG_PCMCIA_3C575),y)
+# O_OBJS += 3c575_cb.o
+#else
+# ifeq ($(CONFIG_PCMCIA_3C575),m)
+# M_OBJS += 3c575_cb.o
+# endif
+#endif
+
+#ifeq ($(CONFIG_PCMCIA_TULIP),y)
+# O_OBJS += tulip_cb.o
+#else
+# ifeq ($(CONFIG_PCMCIA_TULIP),m)
+# M_OBJS += tulip_cb.o
+# endif
+#endif
+
+#ifeq ($(CONFIG_PCMCIA_EPIC100),y)
+# O_OBJS += epic100_cb.o
+#else
+# ifeq ($(CONFIG_PCMCIA_EPIC100),m)
+# M_OBJS += epic100_cb.o
+# endif
+#endif
+
ifeq ($(CONFIG_PCMCIA_RAYCS),y)
OX_OBJS += ray_cs.o
else
endif
endif
+ifeq ($(CONFIG_PCMCIA_NETWAVE),y)
+ OX_OBJS += netwave_cs.o
+else
+ ifeq ($(CONFIG_PCMCIA_NETWAVE),m)
+ M_OBJS += netwave_cs.o
+ endif
+endif
+
+ifeq ($(CONFIG_PCMCIA_WAVELAN),y)
+ OX_OBJS += wavelan_cs.o
+else
+ ifeq ($(CONFIG_PCMCIA_WAVELAN),m)
+ M_OBJS += wavelan_cs.o
+ endif
+endif
+
include $(TOPDIR)/Rules.make
+
+#epic100_cb.o: ../epic100.c
+# $(CC) $(CFLAGS) -DCARDBUS -c -o $@ ../epic100.c
--- /dev/null
+/*======================================================================
+ fmvj18x_cs.c,v 1.9 1996/08/06 03:13:53 root Exp
+
+ A fmvj18x (and its compatibles) PCMCIA client driver
+
+ Contributed by Shingo Fujimoto, shingo@flab.fujitsu.co.jp
+
+ TDK LAK-CD021 and CONTEC C-NET(PC)C support added by
+ Nobuhiro Katayama, kata-n@po.iijnet.or.jp
+
+ The PCMCIA client code is based on code written by David Hinds.
+ Network code is based on the "FMV-18x driver" by Yutaka TAMIYA
+ but is actually largely Donald Becker's AT1700 driver, which
+ carries the following attribution:
+
+ Written 1993-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+/*
+ 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)
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*
+ For debugging this driver you may need more information.
+ To enable printing registers or status, set 'fmvj18x_debug=#' option .
+ */
+#ifdef FMVJ18X_DEBUG
+static int fmvj18x_debug = FMVJ18X_DEBUG;
+#else
+static int fmvj18x_debug = 2;
+#endif /* FMVJ18X_DEBUG */
+
+/* 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 };
+
+/* SRAM configuration */
+/* 0:4KB*2 TX buffer else:8KB*2 TX buffer */
+static int sram_config = 0;
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM(sram_config, "i");
+
+/*====================================================================*/
+/*
+ driver version infomation
+ */
+#ifdef PCMCIA_DEBUG
+static char *version =
+ "fmvj18x_cs.c,v 1.9 1996/08/06 03:13:53 root Exp";
+#endif
+
+/*====================================================================*/
+/*
+ PCMCIA event handlers
+ */
+static void fmvj18x_config(dev_link_t *link);
+static void fmvj18x_release(u_long arg);
+static int fmvj18x_event(event_t event, int priority,
+ event_callback_args_t *args);
+static int fmvj18x_init(struct net_device *dev);
+static dev_link_t *fmvj18x_attach(void);
+static void fmvj18x_detach(dev_link_t *);
+
+/*
+ LAN controler(MBH86960A) specific routines
+ */
+static int fjn_config(struct net_device *dev, struct ifmap *map);
+static int fjn_open(struct net_device *dev);
+static int fjn_close(struct net_device *dev);
+static int fjn_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void fjn_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void fjn_rx(struct net_device *dev);
+static void fjn_reset(struct net_device *dev);
+static struct net_device_stats *fjn_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+
+static dev_info_t dev_info = "fmvj18x_cs";
+static dev_link_t *dev_list = NULL;
+
+/*
+ card type
+ */
+typedef enum { MBH10302, MBH10304, TDK, CONTEC, LA501 } cardtype_t;
+
+/*
+ driver specific data structure
+*/
+typedef struct local_info_t {
+ dev_node_t node;
+ struct net_device_stats stats;
+ long open_time;
+ uint tx_started:1;
+ uint tx_queue;
+ u_short tx_queue_len;
+ cardtype_t cardtype;
+ u_short sent;
+ u_char mc_filter[8];
+} local_info_t;
+
+#define MC_FILTERBREAK 64
+
+/*====================================================================*/
+/*
+ ioport offset from the base address
+ */
+#define TX_STATUS 0 /* transmit status register */
+#define RX_STATUS 1 /* receive status register */
+#define TX_INTR 2 /* transmit interrupt mask register */
+#define RX_INTR 3 /* receive interrupt mask register */
+#define TX_MODE 4 /* transmit mode register */
+#define RX_MODE 5 /* receive mode register */
+#define CONFIG_0 6 /* configuration register 0 */
+#define CONFIG_1 7 /* configuration register 1 */
+
+#define NODE_ID 8 /* node ID register (bank 0) */
+#define MAR_ADR 8 /* multicast address registers (bank 1) */
+
+#define DATAPORT 8 /* buffer mem port registers (bank 2) */
+#define TX_START 10 /* transmit start register */
+#define COL_CTRL 11 /* 16 collision control register */
+#define BMPR12 12 /* reserved */
+#define BMPR13 13 /* reserved */
+#define RX_SKIP 14 /* skip received packet register */
+
+#define LAN_CTRL 16 /* LAN card control register */
+
+#define MAC_ID 0x1a /* hardware address */
+
+/*
+ control bits
+ */
+#define ENA_TMT_OK 0x80
+#define ENA_TMT_REC 0x20
+#define ENA_COL 0x04
+#define ENA_16_COL 0x02
+#define ENA_TBUS_ERR 0x01
+
+#define ENA_PKT_RDY 0x80
+#define ENA_BUS_ERR 0x40
+#define ENA_LEN_ERR 0x08
+#define ENA_ALG_ERR 0x04
+#define ENA_CRC_ERR 0x02
+#define ENA_OVR_FLO 0x01
+
+/* flags */
+#define F_TMT_RDY 0x80 /* can accept new packet */
+#define F_NET_BSY 0x40 /* carrier is detected */
+#define F_TMT_OK 0x20 /* send packet successfully */
+#define F_SRT_PKT 0x10 /* short packet error */
+#define F_COL_ERR 0x04 /* collision error */
+#define F_16_COL 0x02 /* 16 collision error */
+#define F_TBUS_ERR 0x01 /* bus read error */
+
+#define F_PKT_RDY 0x80 /* packet(s) in buffer */
+#define F_BUS_ERR 0x40 /* bus read error */
+#define F_LEN_ERR 0x08 /* short packet */
+#define F_ALG_ERR 0x04 /* frame error */
+#define F_CRC_ERR 0x02 /* CRC error */
+#define F_OVR_FLO 0x01 /* overflow error */
+
+#define F_BUF_EMP 0x40 /* receive buffer is empty */
+
+#define F_SKP_PKT 0x05 /* drop packet in buffer */
+
+/* default bitmaps */
+#define D_TX_INTR ( ENA_TMT_OK )
+#define D_RX_INTR ( ENA_PKT_RDY | ENA_LEN_ERR \
+ | ENA_ALG_ERR | ENA_CRC_ERR | ENA_OVR_FLO )
+#define TX_STAT_M ( F_TMT_RDY )
+#define RX_STAT_M ( F_PKT_RDY | F_LEN_ERR \
+ | F_ALG_ERR | F_CRC_ERR | F_OVR_FLO )
+
+/* commands */
+#define D_TX_MODE 0x06 /* no tests, detect carrier */
+#define ID_MATCHED 0x02 /* (RX_MODE) */
+#define RECV_ALL 0x03 /* (RX_MODE) */
+#define CONFIG0_DFL 0x5a /* 16bit bus, 4K x 2 Tx queues */
+#define CONFIG0_DFL_1 0x5e /* 16bit bus, 8K x 2 Tx queues */
+#define CONFIG0_RST 0xda /* Data Link Controler off (CONFIG_0) */
+#define CONFIG0_RST_1 0xde /* Data Link Controler off (CONFIG_0) */
+#define BANK_0 0xa0 /* bank 0 (CONFIG_1) */
+#define BANK_1 0xa4 /* bank 1 (CONFIG_1) */
+#define BANK_2 0xa8 /* bank 2 (CONFIG_1) */
+#define CHIP_OFF 0x80 /* contrl chip power off (CONFIG_1) */
+#define DO_TX 0x80 /* do transmit packet */
+#define SEND_PKT 0x81 /* send a packet */
+#define AUTO_MODE 0x07 /* Auto skip packet on 16 col detected */
+#define MANU_MODE 0x03 /* Stop and skip packet on 16 col */
+#define TDK_AUTO_MODE 0x47 /* Auto skip packet on 16 col detected */
+#define TDK_MANU_MODE 0x43 /* Stop and skip packet on 16 col */
+#define INTR_OFF 0x0d /* LAN controler ignores interrupts */
+#define INTR_ON 0x1d /* LAN controler will catch interrupts */
+
+/*======================================================================
+
+ This bit of code is used to avoid unregistering network devices
+ at inappropriate times. 2.2 and later kernels are fairly picky
+ about when this can happen.
+
+======================================================================*/
+
+static void flush_stale_links(void)
+{
+ dev_link_t *link, *next;
+ for (link = dev_list; link; link = next) {
+ next = link->next;
+ if (link->state & DEV_STALE_LINK)
+ fmvj18x_detach(link);
+ }
+}
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+}
+
+/*====================================================================*/
+
+static dev_link_t *fmvj18x_attach(void)
+{
+ client_reg_t client_reg;
+ dev_link_t *link;
+ struct net_device *dev;
+ int i, ret;
+
+ DEBUG(0, "fmvj18x_attach()\n");
+ flush_stale_links();
+
+ /* Initialize the dev_link_t structure */
+ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+ memset(link, 0, sizeof(struct dev_link_t));
+
+ link->release.function = &fmvj18x_release;
+ link->release.data = (u_long)link;
+
+ /* The io structure describes IO port mapping */
+ link->io.NumPorts1 = 32;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ 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 = &fjn_interrupt;
+
+ /* General socket configuration */
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ /* Make up a FMVJ18x specific data structure */
+ dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+ memset(dev, 0, sizeof(struct net_device));
+ dev->priv = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(local_info_t));
+
+ /* The FMVJ18x specific entries in the device structure. */
+ dev->hard_start_xmit = &fjn_start_xmit;
+ dev->set_config = &fjn_config;
+ dev->get_stats = &fjn_get_stats;
+ dev->set_multicast_list = &set_rx_mode;
+ ether_setup(dev);
+ dev->name = ((local_info_t *)dev->priv)->node.dev_name;
+ dev->init = &fmvj18x_init;
+ dev->open = &fjn_open;
+ dev->stop = &fjn_close;
+ dev->tbusy = 0xFF;
+ link->priv = 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 = &fmvj18x_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);
+ fmvj18x_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* fmvj18x_attach */
+
+/*====================================================================*/
+
+static void fmvj18x_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+ long flags;
+
+ DEBUG(0, "fmvj18x_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+ if (*linkp == NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+ if (link->state & DEV_RELEASE_PENDING) {
+ del_timer(&link->release);
+ link->state &= ~DEV_RELEASE_PENDING;
+ }
+ restore_flags(flags);
+
+ if (link->state & DEV_CONFIG) {
+ fmvj18x_release((u_long)link);
+ if (link->state & DEV_STALE_CONFIG) {
+ link->state |= DEV_STALE_LINK;
+ return;
+ }
+ }
+
+ /* Break the link with Card Services */
+ if (link->handle)
+ CardServices(DeregisterClient, link->handle);
+
+ /* Unlink device structure, free pieces */
+ *linkp = link->next;
+ if (link->priv) {
+ struct net_device *dev = link->priv;
+ if (link->dev != NULL)
+ unregister_netdev(dev);
+ if (dev->priv)
+ kfree(dev->priv);
+ kfree(dev);
+ }
+ kfree(link);
+
+} /* fmvj18x_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+static void fmvj18x_config(dev_link_t *link)
+{
+ client_handle_t handle;
+ tuple_t tuple;
+ cisparse_t parse;
+ struct net_device *dev;
+ u_short buf[32];
+ int i, last_fn, last_ret;
+ ioaddr_t ioaddr;
+ cardtype_t cardtype;
+ char *card_name = "unknown";
+ u_char *node_id;
+
+ handle = link->handle;
+ dev =link->priv;
+
+ DEBUG(0, "fmvj18x_config(0x%p)\n", link);
+
+ /*
+ This reads the card's CONFIG tuple to find its configuration
+ registers.
+ */
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ CS_CHECK(GetFirstTuple, handle, &tuple);
+ tuple.TupleData = (u_char *)buf;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ CS_CHECK(GetTupleData, handle, &tuple);
+ CS_CHECK(ParseTuple, handle, &tuple, &parse);
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ tuple.DesiredTuple = CISTPL_FUNCE;
+ tuple.TupleOffset = 0;
+ if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) {
+ /* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, handle, &tuple);
+ CS_CHECK(GetTupleData, handle, &tuple);
+ CS_CHECK(ParseTuple, handle, &tuple, &parse);
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+ tuple.DesiredTuple = CISTPL_MANFID;
+ CS_CHECK(GetFirstTuple, handle, &tuple);
+ CS_CHECK(GetTupleData, handle, &tuple);
+
+ switch (le16_to_cpu(buf[0])) {
+ case MANFID_TDK:
+ cardtype = TDK;
+ break;
+ case MANFID_CONTEC:
+ cardtype = CONTEC;
+ break;
+ case MANFID_FUJITSU:
+ if (le16_to_cpu(buf[1]) == PRODID_FUJITSU_MBH10302)
+ cardtype = MBH10302;
+ else if (le16_to_cpu(buf[1]) == PRODID_FUJITSU_MBH10304)
+ cardtype = MBH10304;
+ else
+ cardtype = LA501;
+ break;
+ default:
+ cardtype = MBH10304;
+ }
+ } else {
+ /* old type card */
+ cardtype = MBH10302;
+ link->conf.ConfigIndex = 1;
+ }
+ CS_CHECK(RequestIO, link->handle, &link->io);
+ CS_CHECK(RequestIRQ, link->handle, &link->irq);
+ CS_CHECK(RequestConfiguration, link->handle, &link->conf);
+ dev->irq = link->irq.AssignedIRQ;
+ dev->base_addr = link->io.BasePort1;
+ dev->tbusy = 0;
+ if (register_netdev(dev) != 0) {
+ printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n");
+ goto failed;
+ }
+
+ ioaddr = dev->base_addr;
+
+ /* Power On chip and select bank 0 */
+ outb(BANK_0, ioaddr + CONFIG_1);
+ /* Reset controler */
+ if( sram_config == 0 )
+ outb(CONFIG0_RST, ioaddr + CONFIG_0);
+ else
+ outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
+
+ /* Set hardware address */
+ switch (cardtype) {
+ case MBH10304:
+ case TDK:
+ case LA501:
+ case CONTEC:
+ tuple.DesiredTuple = CISTPL_FUNCE;
+ tuple.TupleOffset = 0;
+ CS_CHECK(GetFirstTuple, handle, &tuple);
+ tuple.TupleOffset = 0;
+ CS_CHECK(GetTupleData, handle, &tuple);
+ if (cardtype == MBH10304) {
+ /* MBH10304's CIS_FUNCE is corrupted */
+ node_id = &(tuple.TupleData[5]);
+ card_name = "FMV-J182";
+ } else {
+ while (tuple.TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID ) {
+ CS_CHECK(GetNextTuple, handle, &tuple) ;
+ CS_CHECK(GetTupleData, handle, &tuple) ;
+ }
+ node_id = &(tuple.TupleData[2]);
+ if( cardtype == TDK ) {
+ card_name = "TDK LAK-CD021";
+ } else if( cardtype == LA501 ) {
+ card_name = "LA501";
+ } else {
+ card_name = "C-NET(PC)C";
+ }
+ }
+ /* Read MACID from CIS */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = node_id[i];
+ break;
+ case MBH10302:
+ default:
+ /* Read MACID from register */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(ioaddr + MAC_ID + i);
+ card_name = "FMV-J181";
+ break;
+ }
+
+ link->dev = &((local_info_t *)dev->priv)->node;
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ ((struct local_info_t *)dev->priv)->cardtype = cardtype ;
+ /* print current configuration */
+ printk(KERN_INFO "%s: %s, sram %s, port %#3lx, irq %d, hw_addr ",
+ dev->name, card_name, sram_config == 0 ? "4K TX*2" : "8K TX*2",
+ dev->base_addr, dev->irq);
+ for (i = 0; i < 6; i++)
+ printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+
+ return;
+
+cs_failed:
+ /* All Card Services errors end up here */
+ cs_error(link->handle, last_fn, last_ret);
+failed:
+ fmvj18x_release((u_long)link);
+
+} /* fmvj18x_config */
+
+/*====================================================================*/
+
+static void fmvj18x_release(u_long arg)
+{
+ dev_link_t *link = (dev_link_t *)arg;
+
+ DEBUG(0, "fmvj18x_release(0x%p)\n", link);
+
+ /*
+ If the device is currently in use, we won't release until it
+ is actually closed.
+ */
+ if (link->open) {
+ DEBUG(1, "fmvj18x_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 */
+ 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_RELEASE_PENDING);
+
+} /* fmvj18x_release */
+
+/*====================================================================*/
+
+static int fmvj18x_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ struct net_device *dev = link->priv;
+
+ DEBUG(1, "fmvj18x_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ dev->tbusy = 0xFF;
+ dev->start = 0;
+ link->release.expires = jiffies + HZ/20;
+ add_timer(&link->release);
+ }
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ fmvj18x_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) {
+ dev->tbusy = 0xFF;
+ dev->start = 0;
+ }
+ 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) {
+ dev->tbusy = 0;
+ dev->start = 1;
+ fjn_reset(dev);
+ }
+ }
+ break;
+ }
+ return 0;
+} /* fmvj18x_event */
+
+static int fmvj18x_init(struct net_device *dev)
+{
+ return 0;
+} /* fmvj18x_init */
+
+/*====================================================================*/
+
+static int __init init_fmvj18x_cs(void)
+{
+ servinfo_t serv;
+ DEBUG(0, "%s\n", version);
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "fmvj18x: Card Services release "
+ "does not match!\n");
+ return -1;
+ }
+ register_pccard_driver(&dev_info, &fmvj18x_attach, &fmvj18x_detach);
+ return 0;
+}
+
+static void __exit exit_fmvj18x_cs(void)
+{
+ DEBUG(0, "fmvj18x_cs: unloading\n");
+ unregister_pccard_driver(&dev_info);
+ while (dev_list != NULL)
+ fmvj18x_detach(dev_list);
+}
+
+module_init(init_fmvj18x_cs);
+module_exit(exit_fmvj18x_cs);
+
+/*====================================================================*/
+
+static void fjn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ ioaddr_t ioaddr;
+ local_info_t *lp;
+ unsigned short tx_stat, rx_stat;
+
+ if (dev == NULL) {
+ printk(KERN_NOTICE "fjn_interrupt(): irq %d for "
+ "unknown device.\n", irq);
+ return;
+ }
+ if (dev->interrupt) {
+ printk(KERN_NOTICE "%s: re-entering the interrupt handler.\n",
+ dev->name);
+ return;
+ }
+ dev->interrupt = 1;
+ lp = (struct local_info_t *)dev->priv;
+ ioaddr = dev->base_addr;
+
+ /* avoid multiple interrupts */
+ outw(0x0000, ioaddr + TX_INTR);
+
+ /* wait for a while */
+ udelay(1);
+
+ /* get status */
+ tx_stat = inb(ioaddr + TX_STATUS);
+ rx_stat = inb(ioaddr + RX_STATUS);
+
+ /* clear status */
+ outb(tx_stat, ioaddr + TX_STATUS);
+ outb(rx_stat, ioaddr + RX_STATUS);
+
+ if (fmvj18x_debug > 4) {
+ printk(KERN_DEBUG "%s: interrupt, rx_status %02x.\n",
+ dev->name, rx_stat);
+ printk(KERN_DEBUG " tx_status %02x.\n",
+ tx_stat);
+ }
+
+ if (rx_stat || (inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
+ /* there is packet(s) in rx buffer */
+ fjn_rx(dev);
+ }
+ if (tx_stat & F_TMT_RDY) {
+ lp->stats.tx_packets += lp->sent ;
+ lp->sent = 0 ;
+ if (lp->tx_queue) {
+ outb(DO_TX | lp->tx_queue, ioaddr + TX_START);
+ lp->sent = lp->tx_queue ;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ } else {
+ lp->tx_started = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+ }
+ if (fmvj18x_debug > 4) {
+ printk(KERN_DEBUG "%s: exiting interrupt,\n", dev->name);
+ printk(KERN_DEBUG " tx_status %02x, rx_status %02x.\n",
+ tx_stat, rx_stat);
+ }
+
+ dev->interrupt = 0;
+ outb(D_TX_INTR, ioaddr + TX_INTR);
+ outb(D_RX_INTR, ioaddr + RX_INTR);
+
+ return;
+} /* fjn_interrupt */
+
+/*====================================================================*/
+
+static int fjn_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct local_info_t *lp = (struct local_info_t *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 10)
+ return 1;
+ printk(KERN_NOTICE "%s: transmit timed out with status %04x, %s?\n",
+ dev->name, htons(inw(ioaddr + TX_STATUS)),
+ inb(ioaddr + TX_STATUS) & F_TMT_RDY
+ ? "IRQ conflict" : "network cable problem");
+ printk(KERN_NOTICE "%s: timeout registers: %04x %04x %04x "
+ "%04x %04x %04x %04x %04x.\n",
+ dev->name, htons(inw(ioaddr + 0)),
+ htons(inw(ioaddr + 2)), htons(inw(ioaddr + 4)),
+ htons(inw(ioaddr + 6)), htons(inw(ioaddr + 8)),
+ htons(inw(ioaddr +10)), htons(inw(ioaddr +12)),
+ htons(inw(ioaddr +14)));
+ lp->stats.tx_errors++;
+ /* ToDo: We should try to restart the adaptor... */
+ cli();
+
+ fjn_reset(dev);
+
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ lp->sent = 0;
+ lp->open_time = jiffies;
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ sti();
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
+ printk(KERN_NOTICE "%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ if (length > ETH_FRAME_LEN) {
+ if (fmvj18x_debug)
+ printk(KERN_NOTICE "%s: Attempting to send a large packet"
+ " (%d bytes).\n", dev->name, length);
+ return 1;
+ }
+
+ if (fmvj18x_debug > 4)
+ printk(KERN_DEBUG "%s: Transmitting a packet of length %lu.\n",
+ dev->name, (unsigned long)skb->len);
+ lp->stats.tx_bytes += skb->len;
+
+ /* Disable both interrupts. */
+ outw(0x0000, ioaddr + TX_INTR);
+
+ /* wait for a while */
+ udelay(1);
+
+ outw(length, ioaddr + DATAPORT);
+ outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+
+ lp->tx_queue++;
+ lp->tx_queue_len += ((length+3) & ~1);
+
+ if (lp->tx_started == 0) {
+ /* If the Tx is idle, always trigger a transmit. */
+ outb(DO_TX | lp->tx_queue, ioaddr + TX_START);
+ lp->sent = lp->tx_queue ;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
+ lp->tx_started = 1;
+ dev->tbusy = 0;
+ } else {
+ if( sram_config == 0 ) {
+ if (lp->tx_queue_len < (4096 - (ETH_FRAME_LEN +2)) )
+ /* Yes, there is room for one more packet. */
+ dev->tbusy = 0;
+ } else {
+ if (lp->tx_queue_len < (8192 - (ETH_FRAME_LEN +2)) &&
+ lp->tx_queue < 127 )
+ /* Yes, there is room for one more packet. */
+ dev->tbusy = 0;
+ }
+ }
+
+ /* Re-enable interrupts */
+ outb(D_TX_INTR, ioaddr + TX_INTR);
+ outb(D_RX_INTR, ioaddr + RX_INTR);
+ }
+ dev_kfree_skb (skb);
+
+ return 0;
+} /* fjn_start_xmit */
+
+/*====================================================================*/
+
+static void fjn_reset(struct net_device *dev)
+{
+ struct local_info_t *lp = (struct local_info_t *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ int i;
+
+ if (fmvj18x_debug > 4) {
+ printk(KERN_DEBUG "fjn_reset(%s) called.\n",dev->name);
+ }
+
+ /* Power On chip and select bank 0 */
+ outb(BANK_0, ioaddr + CONFIG_1);
+ /* Reset buffers */
+ if( sram_config == 0 )
+ outb(CONFIG0_RST, ioaddr + CONFIG_0);
+ else
+ outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
+
+ /* Set Tx modes */
+ outb(D_TX_MODE, ioaddr + TX_MODE);
+ /* set Rx modes */
+ outb(ID_MATCHED, ioaddr + RX_MODE);
+
+ /* Set hardware address */
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + NODE_ID + i);
+
+ if (fmvj18x_debug > 4) {
+ printk(KERN_DEBUG "node id: ");
+ for (i = 0; i < 6; i++)
+ printk("%02X ",inb(ioaddr + NODE_ID + i));
+ printk("\n");
+ }
+ /* Switch to bank 1 */
+ outb(BANK_1, ioaddr + CONFIG_1);
+
+ /* set the multicast table to accept none. */
+ for (i = 0; i < 6; i++)
+ outb(0x00, ioaddr + MAR_ADR + i);
+
+ /* Switch to bank 2 (runtime mode) */
+ outb(BANK_2, ioaddr + CONFIG_1);
+
+ /* set 16col ctrl bits */
+ if( lp->cardtype == TDK )
+ outb(TDK_AUTO_MODE, ioaddr + COL_CTRL);
+ else
+ outb(AUTO_MODE, ioaddr + COL_CTRL);
+
+ /* clear Reserved Regs */
+ outb(0x00, ioaddr + BMPR12);
+ outb(0x00, ioaddr + BMPR13);
+
+ /* reset Skip packet reg. */
+ outb(0x01, ioaddr + RX_SKIP);
+
+ /* Enable Tx and Rx */
+ if( sram_config == 0 )
+ outb(CONFIG0_DFL, ioaddr + CONFIG_0);
+ else
+ outb(CONFIG0_DFL_1, ioaddr + CONFIG_0);
+
+ /* Init receive pointer ? */
+ inw(ioaddr + DATAPORT);
+ inw(ioaddr + DATAPORT);
+
+ /* Clear all status */
+ outb(0xff, ioaddr + TX_STATUS);
+ outb(0xff, ioaddr + RX_STATUS);
+
+ if( lp->cardtype != TDK )
+ outb(INTR_OFF, ioaddr + LAN_CTRL);
+
+ /* Turn on Rx interrupts */
+ outb(D_TX_INTR, ioaddr + TX_INTR);
+ outb(D_RX_INTR, ioaddr + RX_INTR);
+
+ /* Turn on interrupts from LAN card controler */
+ if( lp->cardtype != TDK )
+ outb(INTR_ON, ioaddr + LAN_CTRL);
+} /* fjn_reset */
+
+/*====================================================================*/
+
+static void fjn_rx(struct net_device *dev)
+{
+ struct local_info_t *lp = (struct local_info_t *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ int boguscount = 10; /* 5 -> 10: by agy 19940922 */
+
+ if (fmvj18x_debug > 4)
+ printk(KERN_DEBUG "%s: in rx_packet(), rx_status %02x.\n",
+ dev->name, inb(ioaddr + RX_STATUS));
+
+ while ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
+ u_short status = inw(ioaddr + DATAPORT);
+
+ if (fmvj18x_debug > 4)
+ printk(KERN_DEBUG "%s: Rxing packet mode %02x status %04x.\n",
+ dev->name, inb(ioaddr + RX_MODE), status);
+#ifndef final_version
+ if (status == 0) {
+ outb(F_SKP_PKT, ioaddr + RX_SKIP);
+ break;
+ }
+#endif
+ if ((status & 0xF0) != 0x20) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (status & F_LEN_ERR) lp->stats.rx_length_errors++;
+ if (status & F_ALG_ERR) lp->stats.rx_frame_errors++;
+ if (status & F_CRC_ERR) lp->stats.rx_crc_errors++;
+ if (status & F_OVR_FLO) lp->stats.rx_over_errors++;
+ } else {
+ u_short pkt_len = inw(ioaddr + DATAPORT);
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ if (pkt_len > 1550) {
+ printk(KERN_NOTICE "%s: The FMV-18x claimed a very "
+ "large packet, size %d.\n", dev->name, pkt_len);
+ outb(F_SKP_PKT, ioaddr + RX_SKIP);
+ lp->stats.rx_errors++;
+ break;
+ }
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL) {
+ printk(KERN_NOTICE "%s: Memory squeeze, dropping "
+ "packet (len %d).\n", dev->name, pkt_len);
+ outb(F_SKP_PKT, ioaddr + RX_SKIP);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = dev;
+
+ skb_reserve(skb, 2);
+ insw(ioaddr + DATAPORT, skb_put(skb, pkt_len),
+ (pkt_len + 1) >> 1);
+ skb->protocol = eth_type_trans(skb, dev);
+
+ if (fmvj18x_debug > 5) {
+ int i;
+ printk(KERN_DEBUG "%s: Rxed packet of length %d: ",
+ dev->name, pkt_len);
+ for (i = 0; i < 14; i++)
+ printk(" %02x", skb->data[i]);
+ printk(".\n");
+ }
+
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += skb->len;
+ }
+ if (--boguscount <= 0)
+ break;
+ }
+
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(NET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+/*
+ if( lp->cardtype != TDK ) {
+ int i;
+ for (i = 0; i < 20; i++) {
+ if ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == F_BUF_EMP)
+ break;
+ (void)inw(ioaddr + DATAPORT); /+ dummy status read +/
+ outb(F_SKP_PKT, ioaddr + RX_SKIP);
+ }
+
+ if (fmvj18x_debug > 5 && i > 0)
+ printk(KERN_DEBUG "%s: Exint Rx packet with mode %02x after"
+ " %d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i);
+ }
+*/
+
+ return;
+} /* fjn_rx */
+
+/*====================================================================*/
+
+static int fjn_config(struct net_device *dev, struct ifmap *map){
+ return 0;
+} /* fjn_config */
+
+static int fjn_open(struct net_device *dev)
+{
+ struct local_info_t *lp = (struct local_info_t *)dev->priv;
+ dev_link_t *link;
+
+ if (fmvj18x_debug > 4)
+ printk(KERN_DEBUG "fjn_open('%s').\n", dev->name);
+
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+ if (!DEV_OK(link))
+ return -ENODEV;
+
+ link->open++;
+
+ fjn_reset(dev);
+
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ lp->open_time = jiffies;
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+} /* fjn_open */
+
+/*====================================================================*/
+
+static int fjn_close(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ struct local_info_t *lp = (struct local_info_t *)dev->priv;
+ dev_link_t *link;
+
+ if (fmvj18x_debug > 4)
+ printk(KERN_DEBUG "fjn_open('%s').\n", dev->name);
+
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+ if (link == NULL)
+ return -ENODEV;
+
+ if (fmvj18x_debug > 2)
+ printk(KERN_DEBUG "%s: shutting down ethercard.\n", dev->name);
+
+ ((struct local_info_t *)dev->priv)->open_time = 0;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Set configuration register 0 to disable Tx and Rx. */
+ if( sram_config == 0 )
+ outb(CONFIG0_RST ,ioaddr + CONFIG_0);
+ else
+ outb(CONFIG0_RST_1 ,ioaddr + CONFIG_0);
+
+ /* Update the statistics -- ToDo. */
+
+ /* Power-down the chip. Green, green, green! */
+ outb(CHIP_OFF ,ioaddr + CONFIG_1);
+
+ /* Set the ethernet adaptor disable IRQ */
+ if( lp->cardtype != TDK )
+ outb(INTR_OFF, ioaddr + LAN_CTRL);
+
+ link->open--;
+ dev->start = 0;
+ if (link->state & DEV_STALE_CONFIG) {
+ link->release.expires = jiffies + HZ/20;
+ link->state |= DEV_RELEASE_PENDING;
+ add_timer(&link->release);
+ }
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+} /* fjn_close */
+
+/*====================================================================*/
+
+static struct net_device_stats *fjn_get_stats(struct net_device *dev)
+{
+ local_info_t *lp = (local_info_t *)dev->priv;
+ return &lp->stats;
+} /* fjn_get_stats */
+
+/*====================================================================*/
+
+/*
+ Set the multicast/promiscuous mode for this adaptor.
+*/
+
+/* The little-endian AUTODIN II ethernet CRC calculation.
+ N.B. Do not use for bulk data, use a table-based routine instead.
+ This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+ unsigned int crc = 0xffffffff; /* Initial value. */
+ while(--length >= 0) {
+ unsigned char current_octet = *data++;
+ int bit;
+ for (bit = 8; --bit >= 0; current_octet >>= 1) {
+ if ((crc ^ current_octet) & 1) {
+ crc >>= 1;
+ crc ^= ethernet_polynomial_le;
+ } else
+ crc >>= 1;
+ }
+ }
+ return crc;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ struct local_info_t *lp = (struct local_info_t *)dev->priv;
+ unsigned char mc_filter[8]; /* Multicast hash filter */
+ long flags;
+ int i;
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Unconditionally log net taps. */
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */
+ } else if (dev->mc_count > MC_FILTERBREAK
+ || (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ outb(2, ioaddr + RX_MODE); /* Use normal mode. */
+ } else if (dev->mc_count == 0) {
+ memset(mc_filter, 0x00, sizeof(mc_filter));
+ outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */
+ } else {
+ struct dev_mc_list *mclist;
+ int i;
+
+ memset(mc_filter, 0, sizeof(mc_filter));
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next)
+ set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
+ mc_filter);
+ }
+
+ save_flags(flags);
+ cli();
+ if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) {
+ int saved_bank = inb(ioaddr + CONFIG_1);
+ /* Switch to bank 1 and set the multicast table. */
+ outb(0xe4, ioaddr + CONFIG_1);
+ for (i = 0; i < 8; i++)
+ outb(mc_filter[i], ioaddr + 8 + i);
+ memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter));
+ outb(saved_bank, ioaddr + CONFIG_1);
+ }
+ restore_flags(flags);
+}
--- /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 <dhinds@hyper.stanford.edu>
+ * 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/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/malloc.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 20
+#define WATCHDOG_JIFFIES 32
+
+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 */
+
+/*static int netwave_debug = 0;*/
+
+/*
+ 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 = 0;
+
+/* 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(u_long); /* Transmission watchdog */
+
+/* Statistics */
+static void update_stats(struct net_device *dev);
+static struct enet_statistics *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 = NULL;
+
+/*
+ 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.
+*/
+
+#define SIOCGIPSNAP SIOCDEVPRIVATE /* Site Survey Snapshot */
+/*#define SIOCGIPQTHR SIOCDEVPRIVATE + 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_node_t node;
+ u_char *ramBase;
+ int timeoutCounter;
+ int lastExec;
+ struct timer_list watchdog; /* To avoid blocking state */
+ struct site_survey nss;
+ struct enet_statistics stats;
+#ifdef WIRELESS_EXT
+ struct iw_statistics iw_stats; /* Wireless stats */
+#endif
+} netwave_private;
+
+#ifdef NETWAVE_STATS
+static struct enet_statistics *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_init (dev)
+ *
+ * We never need to do anything when a device is "initialized"
+ * by the net software, because we only register already-found cards.
+ */
+int netwave_init(struct net_device *dev)
+{
+ /* We do all the initialization of this in netwave_attach instead */
+ return 0;
+}
+
+/*
+ * 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 */
+ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+ memset(link, 0, sizeof(struct dev_link_t));
+ 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;
+
+ /* Allocate space for private device-specific data */
+ dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+ memset(dev, 0, sizeof(struct net_device));
+
+ dev->priv = kmalloc(sizeof(netwave_private), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(netwave_private));
+
+ /* Set the watchdog timer */
+ priv = (netwave_private *) dev->priv;
+ priv->watchdog.function = &netwave_watchdog;
+ priv->watchdog.data = (unsigned long) dev;
+
+ /* 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;
+
+ ether_setup(dev);
+ dev->name = ((struct netwave_private *)dev->priv)->node.dev_name;
+ dev->init = &netwave_init;
+ dev->open = &netwave_open;
+ dev->stop = &netwave_close;
+ dev->tbusy = 1;
+ link->priv = 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)
+{
+ dev_link_t **linkp;
+ long flags;
+
+ DEBUG(0, "netwave_detach(0x%p)\n", link);
+
+ save_flags(flags);
+ if (link->state & DEV_RELEASE_PENDING) {
+ del_timer(&link->release);
+ link->state &= ~DEV_RELEASE_PENDING;
+ }
+ cli();
+ restore_flags(flags);
+
+ /*
+ 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) {
+ 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->priv) {
+ struct net_device *dev = link->priv;
+ if (link->dev != NULL)
+ unregister_netdev(dev);
+ link->dev = NULL;
+ if (dev->priv)
+ kfree(dev->priv);
+ kfree(link->priv);
+ }
+ kfree(link);
+
+} /* 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 (useless : its constant...) */
+ wrq->u.data.length = sizeof(struct iw_range);
+
+ /* 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;
+ tuple_t tuple;
+ cisparse_t parse;
+ struct net_device *dev;
+ int i, j, last_ret, last_fn;
+ u_char buf[64];
+ win_req_t req;
+ memreq_t mem;
+ u_char *ramBase = NULL;
+ /* modwin_t mod;
+ short iobase, *phys_addr;
+ */
+ handle = link->handle;
+ dev = link->priv;
+
+ 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 (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;
+ dev->tbusy = 0;
+ if (register_netdev(dev) != 0) {
+ printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n");
+ goto failed;
+ }
+
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ link->dev = &((netwave_private *)dev->priv)->node;
+
+ /* 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);
+ return;
+} /* 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;
+ struct net_device *dev = 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(((netwave_private *)dev->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_RELEASE_PENDING | 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;
+ struct net_device *dev = link->priv;
+
+ 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) {
+ dev->tbusy = 1; dev->start = 0;
+ /* ((netwave_private *)link->priv)->block = 1; */
+ link->release.expires = jiffies + 5;
+ add_timer(&link->release);
+ }
+ 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) {
+ dev->tbusy = 1; dev->start = 0;
+ }
+ 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);
+ dev->tbusy = 0; dev->start = 1;
+ }
+ }
+ 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;
+
+ /* If watchdog was activated, kill it ! */
+ del_timer(&priv->watchdog);
+
+ /* 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);
+ 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);
+
+ /* If watchdog not already active, activate it... */
+ if(priv->watchdog.prev == (struct timer_list *) NULL) {
+
+ /* set timer to expire in WATCHDOG_JIFFIES */
+ priv->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+ add_timer(&priv->watchdog);
+ }
+ 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...
+ */
+
+ if (dev->tbusy) {
+ /* Handled by watchdog */
+ return 1;
+
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a 'kick me' function call instead.
+ */
+ /*int tickssofar = jiffies - dev->trans_start;*/
+ /* printk("xmit called with busy. tickssofar %d\n", tickssofar); */
+ /*if (tickssofar < TX_TIMEOUT)
+ return 1;
+ */
+ /* Should also detect if the kernel tries to xmit
+ * on a stopped card.
+ */
+
+ /*if (netwave_debug > 0)
+ printk(KERN_DEBUG "%s timed out.\n", dev->name);
+ netwave_reset(dev);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;*/
+ }
+
+ /* Sending a NULL skb means some higher layer thinks we've missed an
+ * tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
+ * itself.
+ */
+
+ /* Block a timer-based transmit from overlapping. This could
+ * better be done with atomic_swap(1, dev->tbusy, but set_bit()
+ * works as well
+ */
+ if ( test_and_set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ 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? */
+ dev->tbusy = 0;
+ }
+ 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 transfered 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;
+ int i;
+ dev_link_t *link;
+
+ if ((dev == NULL) | (!dev->start))
+ return;
+
+ priv = (netwave_private *)dev->priv;
+
+ if (dev->interrupt) {
+ printk("%s: re-entering the interrupt handler.\n", dev->name);
+ return;
+ }
+ dev->interrupt = 1;
+
+ /* Find the correct dev_link_t */
+ for (link = dev_list; NULL != link; link = link->next)
+ if (dev == link->priv) break;
+
+ 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 ( ! (link->state & DEV_PRESENT) || link->state & DEV_SUSPEND ) {
+ DEBUG( 1, "netwave_interupt: 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));
+
+ /* If watchdog was activated, kill it ! */
+ del_timer(&priv->watchdog);
+
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ /* TxBA, this would trigger on all error packets received */
+ /* if (status & 0x01) {
+ if (netwave_debug > 3)
+ printk(KERN_DEBUG "Transmit buffers available, %x\n", status);
+ }
+ */
+ }
+ /* done.. */
+ dev->interrupt = 0;
+ return;
+} /* 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(u_long a) {
+ struct net_device *dev;
+ ioaddr_t iobase;
+
+ dev = (struct net_device *) a;
+ iobase = dev->base_addr;
+
+ DEBUG( 1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name);
+
+ netwave_reset(dev);
+
+ /* We are not waiting anymore... */
+ dev->tbusy = 0;
+
+} /* netwave_watchdog */
+
+static struct enet_statistics *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);
+
+ /* 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");
+
+ priv->stats.rx_packets++;
+
+ priv->stats.rx_bytes += skb->len;
+ }
+ return 0;
+}
+
+static int netwave_open(struct net_device *dev) {
+ dev_link_t *link;
+
+ DEBUG(1, "netwave_open: starting.\n");
+
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+
+ if (!DEV_OK(link))
+ return -ENODEV;
+
+ link->open++;
+ MOD_INC_USE_COUNT;
+
+ dev->interrupt = 0; dev->tbusy = 0; dev->start = 1;
+ netwave_reset(dev);
+
+ return 0;
+}
+
+static int netwave_close(struct net_device *dev) {
+ dev_link_t *link;
+ netwave_private *priv = (netwave_private *) dev->priv;
+
+ DEBUG(1, "netwave_close: finishing.\n");
+
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+ if (link == NULL)
+ return -ENODEV;
+
+ /* If watchdog was activated, kill it ! */
+ del_timer(&priv->watchdog);
+
+ link->open--;
+ dev->start = 0;
+ if (link->state & DEV_STALE_CONFIG) {
+ link->release.expires = jiffies + 5;
+ link->state |= DEV_RELEASE_PENDING;
+ add_timer(&link->release);
+ }
+
+ 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 = 0;
+ 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);
+}
--- /dev/null
+/* ----------------------------------------------------------------------------
+Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN.
+ nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao
+
+ The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media
+ Access Controller for Ethernet (MACE). It is essentially the Am2150
+ PCMCIA Ethernet card contained in the the Am2150 Demo Kit.
+
+Written by Roger C. Pao <rpao@paonet.org>
+ Copyright 1995 Roger C. Pao
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License.
+
+Ported to Linux 1.3.* network driver environment by
+ Matti Aarnio <mea@utu.fi>
+
+References
+
+ Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993
+ Am79C940 (MACE) Data Sheet, 1994
+ Am79C90 (C-LANCE) Data Sheet, 1994
+ Linux PCMCIA Programmer's Guide v1.17
+ /usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8
+
+ Eric Mears, New Media Corporation
+ Tom Pollard, New Media Corporation
+ Dean Siasoyco, New Media Corporation
+ Ken Lesniak, Silicon Graphics, Inc. <lesniak@boston.sgi.com>
+ Donald Becker <becker@cesdis1.gsfc.nasa.gov>
+ David Hinds <dhinds@hyper.stanford.edu>
+
+ The Linux client driver is based on the 3c589_cs.c client driver by
+ David Hinds.
+
+ The Linux network driver outline is based on the 3c589_cs.c driver,
+ the 8390.c driver, and the example skeleton.c kernel code, which are
+ by Donald Becker.
+
+ The Am2150 network driver hardware interface code is based on the
+ OS/9000 driver for the New Media Ethernet LAN by Eric Mears.
+
+ Special thanks for testing and help in debugging this driver goes
+ to Ken Lesniak.
+
+-------------------------------------------------------------------------------
+Driver Notes and Issues
+-------------------------------------------------------------------------------
+
+1. Developed on a Dell 320SLi
+ PCMCIA Card Services 2.6.2
+ Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386
+
+2. rc.pcmcia may require loading pcmcia_core with io_speed=300:
+ 'insmod pcmcia_core.o io_speed=300'.
+ This will avoid problems with fast systems which causes rx_framecnt
+ to return random values.
+
+3. If hot extraction does not work for you, use 'ifconfig eth0 down'
+ before extraction.
+
+4. There is a bad slow-down problem in this driver.
+
+5. Future: Multicast processing. In the meantime, do _not_ compile your
+ kernel with multicast ip enabled.
+
+-------------------------------------------------------------------------------
+History
+-------------------------------------------------------------------------------
+Log: nmclan_cs.c,v
+ * Revision 0.16 1995/07/01 06:42:17 rpao
+ * Bug fix: nmclan_reset() called CardServices incorrectly.
+ *
+ * Revision 0.15 1995/05/24 08:09:47 rpao
+ * Re-implement MULTI_TX dev->tbusy handling.
+ *
+ * Revision 0.14 1995/05/23 03:19:30 rpao
+ * Added, in nmclan_config(), "tuple.Attributes = 0;".
+ * Modified MACE ID check to ignore chip revision level.
+ * Avoid tx_free_frames race condition between _start_xmit and _interrupt.
+ *
+ * Revision 0.13 1995/05/18 05:56:34 rpao
+ * Statistics changes.
+ * Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list.
+ * Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT. Fixes driver lockup.
+ *
+ * Revision 0.12 1995/05/14 00:12:23 rpao
+ * Statistics overhaul.
+ *
+
+95/05/13 rpao V0.10a
+ Bug fix: MACE statistics counters used wrong I/O ports.
+ Bug fix: mace_interrupt() needed to allow statistics to be
+ processed without RX or TX interrupts pending.
+95/05/11 rpao V0.10
+ Multiple transmit request processing.
+ Modified statistics to use MACE counters where possible.
+95/05/10 rpao V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO.
+ *Released
+95/05/10 rpao V0.08
+ Bug fix: Make all non-exported functions private by using
+ static keyword.
+ Bug fix: Test IntrCnt _before_ reading MACE_IR.
+95/05/10 rpao V0.07 Statistics.
+95/05/09 rpao V0.06 Fix rx_framecnt problem by addition of PCIC wait states.
+
+---------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------------
+Conditional Compilation Options
+---------------------------------------------------------------------------- */
+
+#define MULTI_TX 0
+#define TIMEOUT_TX 1
+#define RESET_ON_TIMEOUT 1
+#define TX_INTERRUPTABLE 1
+#define RESET_XILINX 0
+
+/* ----------------------------------------------------------------------------
+Include Files
+---------------------------------------------------------------------------- */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.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 <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+/* ----------------------------------------------------------------------------
+Defines
+---------------------------------------------------------------------------- */
+
+#define ETHER_ADDR_LEN ETH_ALEN
+ /* 6 bytes in an Ethernet Address */
+#define MACE_LADRF_LEN 8
+ /* 8 bytes in Logical Address Filter */
+
+/* Transmitter Busy Bit Index Defines */
+#define TBUSY_UNSPECIFIED 0
+#define TBUSY_PARTIAL_TX_FRAME 0
+#define TBUSY_NO_FREE_TX_FRAMES 1
+
+/* Loop Control Defines */
+#define MACE_MAX_IR_ITERATIONS 10
+#define MACE_MAX_RX_ITERATIONS 12
+ /*
+ TBD: Dean brought this up, and I assumed the hardware would
+ handle it:
+
+ If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be
+ non-zero when the isr exits. We may not get another interrupt
+ to process the remaining packets for some time.
+ */
+
+/*
+The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA)
+which manages the interface between the MACE and the PCMCIA bus. It
+also includes buffer management for the 32K x 8 SRAM to control up to
+four transmit and 12 receive frames at a time.
+*/
+#define AM2150_MAX_TX_FRAMES 4
+#define AM2150_MAX_RX_FRAMES 12
+
+/* Am2150 Ethernet Card I/O Mapping */
+#define AM2150_RCV 0x00
+#define AM2150_XMT 0x04
+#define AM2150_XMT_SKIP 0x09
+#define AM2150_RCV_NEXT 0x0A
+#define AM2150_RCV_FRAME_COUNT 0x0B
+#define AM2150_MACE_BANK 0x0C
+#define AM2150_MACE_BASE 0x10
+
+/* MACE Registers */
+#define MACE_RCVFIFO 0
+#define MACE_XMTFIFO 1
+#define MACE_XMTFC 2
+#define MACE_XMTFS 3
+#define MACE_XMTRC 4
+#define MACE_RCVFC 5
+#define MACE_RCVFS 6
+#define MACE_FIFOFC 7
+#define MACE_IR 8
+#define MACE_IMR 9
+#define MACE_PR 10
+#define MACE_BIUCC 11
+#define MACE_FIFOCC 12
+#define MACE_MACCC 13
+#define MACE_PLSCC 14
+#define MACE_PHYCC 15
+#define MACE_CHIPIDL 16
+#define MACE_CHIPIDH 17
+#define MACE_IAC 18
+/* Reserved */
+#define MACE_LADRF 20
+#define MACE_PADR 21
+/* Reserved */
+/* Reserved */
+#define MACE_MPC 24
+/* Reserved */
+#define MACE_RNTPC 26
+#define MACE_RCVCC 27
+/* Reserved */
+#define MACE_UTR 29
+#define MACE_RTR1 30
+#define MACE_RTR2 31
+
+/* MACE Bit Masks */
+#define MACE_XMTRC_EXDEF 0x80
+#define MACE_XMTRC_XMTRC 0x0F
+
+#define MACE_XMTFS_XMTSV 0x80
+#define MACE_XMTFS_UFLO 0x40
+#define MACE_XMTFS_LCOL 0x20
+#define MACE_XMTFS_MORE 0x10
+#define MACE_XMTFS_ONE 0x08
+#define MACE_XMTFS_DEFER 0x04
+#define MACE_XMTFS_LCAR 0x02
+#define MACE_XMTFS_RTRY 0x01
+
+#define MACE_RCVFS_RCVSTS 0xF000
+#define MACE_RCVFS_OFLO 0x8000
+#define MACE_RCVFS_CLSN 0x4000
+#define MACE_RCVFS_FRAM 0x2000
+#define MACE_RCVFS_FCS 0x1000
+
+#define MACE_FIFOFC_RCVFC 0xF0
+#define MACE_FIFOFC_XMTFC 0x0F
+
+#define MACE_IR_JAB 0x80
+#define MACE_IR_BABL 0x40
+#define MACE_IR_CERR 0x20
+#define MACE_IR_RCVCCO 0x10
+#define MACE_IR_RNTPCO 0x08
+#define MACE_IR_MPCO 0x04
+#define MACE_IR_RCVINT 0x02
+#define MACE_IR_XMTINT 0x01
+
+#define MACE_MACCC_PROM 0x80
+#define MACE_MACCC_DXMT2PD 0x40
+#define MACE_MACCC_EMBA 0x20
+#define MACE_MACCC_RESERVED 0x10
+#define MACE_MACCC_DRCVPA 0x08
+#define MACE_MACCC_DRCVBC 0x04
+#define MACE_MACCC_ENXMT 0x02
+#define MACE_MACCC_ENRCV 0x01
+
+#define MACE_PHYCC_LNKFL 0x80
+#define MACE_PHYCC_DLNKTST 0x40
+#define MACE_PHYCC_REVPOL 0x20
+#define MACE_PHYCC_DAPC 0x10
+#define MACE_PHYCC_LRT 0x08
+#define MACE_PHYCC_ASEL 0x04
+#define MACE_PHYCC_RWAKE 0x02
+#define MACE_PHYCC_AWAKE 0x01
+
+#define MACE_IAC_ADDRCHG 0x80
+#define MACE_IAC_PHYADDR 0x04
+#define MACE_IAC_LOGADDR 0x02
+
+#define MACE_UTR_RTRE 0x80
+#define MACE_UTR_RTRD 0x40
+#define MACE_UTR_RPA 0x20
+#define MACE_UTR_FCOLL 0x10
+#define MACE_UTR_RCVFCSE 0x08
+#define MACE_UTR_LOOP_INCL_MENDEC 0x06
+#define MACE_UTR_LOOP_NO_MENDEC 0x04
+#define MACE_UTR_LOOP_EXTERNAL 0x02
+#define MACE_UTR_LOOP_NONE 0x00
+#define MACE_UTR_RESERVED 0x01
+
+/* Switch MACE register bank (only 0 and 1 are valid) */
+#define MACEBANK(win_num) outb((win_num), ioaddr + AM2150_MACE_BANK)
+
+#define MACE_IMR_DEFAULT \
+ (0xFF - \
+ ( \
+ MACE_IR_CERR | \
+ MACE_IR_RCVCCO | \
+ MACE_IR_RNTPCO | \
+ MACE_IR_MPCO | \
+ MACE_IR_RCVINT | \
+ MACE_IR_XMTINT \
+ ) \
+ )
+#undef MACE_IMR_DEFAULT
+#define MACE_IMR_DEFAULT 0x00 /* New statistics handling: grab everything */
+
+/* ----------------------------------------------------------------------------
+Type Definitions
+---------------------------------------------------------------------------- */
+
+typedef struct _mace_statistics {
+ /* MACE_XMTFS */
+ int xmtsv;
+ int uflo;
+ int lcol;
+ int more;
+ int one;
+ int defer;
+ int lcar;
+ int rtry;
+
+ /* MACE_XMTRC */
+ int exdef;
+ int xmtrc;
+
+ /* RFS1--Receive Status (RCVSTS) */
+ int oflo;
+ int clsn;
+ int fram;
+ int fcs;
+
+ /* RFS2--Runt Packet Count (RNTPC) */
+ int rfs_rntpc;
+
+ /* RFS3--Receive Collision Count (RCVCC) */
+ int rfs_rcvcc;
+
+ /* MACE_IR */
+ int jab;
+ int babl;
+ int cerr;
+ int rcvcco;
+ int rntpco;
+ int mpco;
+
+ /* MACE_MPC */
+ int mpc;
+
+ /* MACE_RNTPC */
+ int rntpc;
+
+ /* MACE_RCVCC */
+ int rcvcc;
+} mace_statistics;
+
+typedef struct _mace_private {
+ dev_node_t node;
+ struct net_device_stats linux_stats; /* Linux statistics counters */
+ mace_statistics mace_stats; /* MACE chip statistics counters */
+
+ /* restore_multicast_list() state variables */
+ int multicast_ladrf[MACE_LADRF_LEN]; /* Logical address filter */
+ int multicast_num_addrs;
+
+ char tx_free_frames; /* Number of free transmit frame buffers */
+ char tx_irq_disabled; /* MACE TX interrupt disabled */
+} mace_private;
+
+/* ----------------------------------------------------------------------------
+Private Global Variables
+---------------------------------------------------------------------------- */
+
+#ifdef PCMCIA_DEBUG
+static char rcsid[] =
+"nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao";
+static char *version =
+"nmclan_cs 0.16 (Roger C. Pao)";
+#endif
+
+static dev_info_t dev_info="nmclan_cs";
+static dev_link_t *dev_list=NULL;
+
+static char *if_names[]={
+ "Auto",
+ "10baseT",
+ "BNC",
+};
+
+#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)
+#else
+#define DEBUG(n, args...)
+#endif
+
+/* ----------------------------------------------------------------------------
+Parameters
+ These are the parameters that can be set during loading with
+ 'insmod'.
+---------------------------------------------------------------------------- */
+
+static int if_port=0; /* default=auto */
+ /*
+ * 0=auto
+ * 1=10base-T (twisted pair)
+ * 2=10base-2 (BNC)
+ */
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(if_port, "i");
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/* ----------------------------------------------------------------------------
+Function Prototypes
+---------------------------------------------------------------------------- */
+
+static void nmclan_config(dev_link_t *link);
+static void nmclan_release(u_long arg);
+static int nmclan_event(event_t event, int priority,
+ event_callback_args_t *args);
+
+static void nmclan_reset(struct net_device *dev);
+static int mace_config(struct net_device *dev, struct ifmap *map);
+static int mace_open(struct net_device *dev);
+static int mace_close(struct net_device *dev);
+static int mace_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void mace_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static struct net_device_stats *mace_get_stats(struct net_device *dev);
+static int mace_rx(struct net_device *dev, unsigned char RxCnt);
+static void restore_multicast_list(struct net_device *dev);
+
+static void set_multicast_list(struct net_device *dev);
+
+static dev_link_t *nmclan_attach(void);
+static void nmclan_detach(dev_link_t *);
+
+/* ----------------------------------------------------------------------------
+flush_stale_links
+ Clean up stale device structures
+---------------------------------------------------------------------------- */
+
+static void flush_stale_links(void)
+{
+ dev_link_t *link, *next;
+ for (link = dev_list; link; link = next) {
+ next = link->next;
+ if (link->state & DEV_STALE_LINK)
+ nmclan_detach(link);
+ }
+}
+
+/* ----------------------------------------------------------------------------
+cs_error
+ Report a Card Services related error.
+---------------------------------------------------------------------------- */
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+}
+
+/* ----------------------------------------------------------------------------
+nmclan_init
+ We never need to do anything when a nmclan device is "initialized"
+ by the net software, because we only register already-found cards.
+---------------------------------------------------------------------------- */
+static int nmclan_init(struct net_device *dev)
+{
+ return 0;
+} /* nmclan_init */
+
+/* ----------------------------------------------------------------------------
+nmclan_attach
+ Creates an "instance" of the driver, allocating local data
+ structures for one device. The device is registered with Card
+ Services.
+---------------------------------------------------------------------------- */
+static dev_link_t *nmclan_attach(void)
+{
+ client_reg_t client_reg;
+ dev_link_t *link;
+ struct net_device *dev;
+ int i, ret;
+
+ DEBUG(0, "nmclan_attach()\n");
+ DEBUG(1, "%s\n", rcsid);
+ flush_stale_links();
+
+ /* Create new ethernet device */
+ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+ memset(link, 0, sizeof(struct dev_link_t));
+ link->release.function = &nmclan_release;
+ link->release.data = (u_long)link;
+ link->io.NumPorts1 = 32;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 5;
+ 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 = &mace_interrupt;
+ 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;
+
+ dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+ memset(dev, 0, sizeof(struct net_device));
+
+ /* Allocate private data area for this device. */
+ dev->priv = kmalloc(sizeof(mace_private), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(mace_private));
+ ((mace_private *)dev->priv)->tx_free_frames=AM2150_MAX_TX_FRAMES;
+
+ dev->hard_start_xmit = &mace_start_xmit;
+ dev->set_config = &mace_config;
+ dev->get_stats = &mace_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ ether_setup(dev);
+ dev->name = ((mace_private *)dev->priv)->node.dev_name;
+ dev->init = &nmclan_init;
+ dev->open = &mace_open;
+ dev->stop = &mace_close;
+ dev->tbusy = 0xFF;
+ link->priv = 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 = &nmclan_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);
+ nmclan_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* nmclan_attach */
+
+/* ----------------------------------------------------------------------------
+nmclan_detach
+ 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 nmclan_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+
+ DEBUG(0, "nmclan_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+ if (*linkp == NULL)
+ return;
+
+ if (link->state & DEV_CONFIG) {
+ nmclan_release((u_long)link);
+ if (link->state & DEV_STALE_CONFIG) {
+ link->state |= DEV_STALE_LINK;
+ return;
+ }
+ }
+
+ if (link->handle)
+ CardServices(DeregisterClient, link->handle);
+
+ /* Unlink device structure, free bits */
+ *linkp = link->next;
+ if (link->priv) {
+ struct net_device *dev = link->priv;
+ if (link->dev != NULL)
+ unregister_netdev(dev);
+ if (dev->priv)
+ kfree(dev->priv);
+ kfree(link->priv);
+ }
+ kfree(link);
+
+} /* nmclan_detach */
+
+/* ----------------------------------------------------------------------------
+mace_read
+ Reads a MACE register. This is bank independent; however, the
+ caller must ensure that this call is not interruptable. We are
+ assuming that during normal operation, the MACE is always in
+ bank 0.
+---------------------------------------------------------------------------- */
+static int mace_read(ioaddr_t ioaddr, int reg)
+{
+ int data = 0xFF;
+ unsigned long flags;
+
+ switch (reg >> 4) {
+ case 0: /* register 0-15 */
+ data = inb(ioaddr + AM2150_MACE_BASE + reg);
+ break;
+ case 1: /* register 16-31 */
+ save_flags(flags);
+ cli();
+ MACEBANK(1);
+ data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
+ MACEBANK(0);
+ restore_flags(flags);
+ break;
+ }
+ return (data & 0xFF);
+} /* mace_read */
+
+/* ----------------------------------------------------------------------------
+mace_write
+ Writes to a MACE register. This is bank independent; however,
+ the caller must ensure that this call is not interruptable. We
+ are assuming that during normal operation, the MACE is always in
+ bank 0.
+---------------------------------------------------------------------------- */
+static void mace_write(ioaddr_t ioaddr, int reg, int data)
+{
+ unsigned long flags;
+
+ switch (reg >> 4) {
+ case 0: /* register 0-15 */
+ outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg);
+ break;
+ case 1: /* register 16-31 */
+ save_flags(flags);
+ cli();
+ MACEBANK(1);
+ outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
+ MACEBANK(0);
+ restore_flags(flags);
+ break;
+ }
+} /* mace_write */
+
+/* ----------------------------------------------------------------------------
+mace_init
+ Resets the MACE chip.
+---------------------------------------------------------------------------- */
+static void mace_init(ioaddr_t ioaddr, char *enet_addr)
+{
+ int i;
+
+ /* MACE Software reset */
+ mace_write(ioaddr, MACE_BIUCC, 1);
+ while (mace_read(ioaddr, MACE_BIUCC) & 0x01) {
+ /* Wait for reset bit to be cleared automatically after <= 200ns */;
+ }
+ mace_write(ioaddr, MACE_BIUCC, 0);
+
+ /* The Am2150 requires that the MACE FIFOs operate in burst mode. */
+ mace_write(ioaddr, MACE_FIFOCC, 0x0F);
+
+ mace_write(ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */
+ mace_write(ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */
+
+ /*
+ * Bit 2-1 PORTSEL[1-0] Port Select.
+ * 00 AUI/10Base-2
+ * 01 10Base-T
+ * 10 DAI Port (reserved in Am2150)
+ * 11 GPSI
+ * For this card, only the first two are valid.
+ * So, PLSCC should be set to
+ * 0x00 for 10Base-2
+ * 0x02 for 10Base-T
+ * Or just set ASEL in PHYCC below!
+ */
+ switch (if_port) {
+ case 1:
+ mace_write(ioaddr, MACE_PLSCC, 0x02);
+ break;
+ case 2:
+ mace_write(ioaddr, MACE_PLSCC, 0x00);
+ break;
+ default:
+ mace_write(ioaddr, MACE_PHYCC, /* ASEL */ 4);
+ /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden,
+ and the MACE device will automatically select the operating media
+ interface port. */
+ break;
+ }
+
+ mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR);
+ /* Poll ADDRCHG bit */
+ while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
+ ;
+ /* Set PADR register */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ mace_write(ioaddr, MACE_PADR, enet_addr[i]);
+
+ /* MAC Configuration Control Register should be written last */
+ /* Let set_multicast_list set this. */
+ /* mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
+ mace_write(ioaddr, MACE_MACCC, 0x00);
+} /* mace_init */
+
+/* ----------------------------------------------------------------------------
+nmclan_config
+ This routine is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ ethernet device available to the system.
+---------------------------------------------------------------------------- */
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+static void nmclan_config(dev_link_t *link)
+{
+ client_handle_t handle;
+ struct net_device *dev;
+ tuple_t tuple;
+ cisparse_t parse;
+ u_char buf[64];
+ int i, last_ret, last_fn;
+ ioaddr_t ioaddr;
+ u_short *phys_addr;
+
+ handle = link->handle;
+ dev = link->priv;
+ phys_addr = (u_short *)dev->dev_addr;
+
+ DEBUG(0, "nmclan_config(0x%p)\n", link);
+
+ tuple.Attributes = 0;
+ tuple.TupleData = 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;
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ CS_CHECK(RequestIO, handle, &link->io);
+ CS_CHECK(RequestIRQ, handle, &link->irq);
+ CS_CHECK(RequestConfiguration, handle, &link->conf);
+ dev->irq = link->irq.AssignedIRQ;
+ dev->base_addr = link->io.BasePort1;
+ dev->tbusy = 0;
+ i = register_netdev(dev);
+ if (i != 0) {
+ printk(KERN_NOTICE "nmclan_cs: register_netdev() failed\n");
+ goto failed;
+ }
+
+ ioaddr = dev->base_addr;
+
+ /* Read the ethernet address from the CIS. */
+ tuple.DesiredTuple = 0x80 /* CISTPL_CFTABLE_ENTRY_MISC */;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ CS_CHECK(GetFirstTuple, handle, &tuple);
+ CS_CHECK(GetTupleData, handle, &tuple);
+ memcpy(dev->dev_addr, tuple.TupleData, ETHER_ADDR_LEN);
+
+ /* Verify configuration by reading the MACE ID. */
+ {
+ char sig[2];
+
+ sig[0] = mace_read(ioaddr, MACE_CHIPIDL);
+ sig[1] = mace_read(ioaddr, MACE_CHIPIDH);
+ if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) {
+ DEBUG(0, "nmclan_cs configured: mace id=%x %x\n",
+ sig[0], sig[1]);
+ } else {
+ printk(KERN_NOTICE "nmclan_cs: mace id not found: %x %x should"
+ " be 0x40 0x?9\n", sig[0], sig[1]);
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
+ }
+ }
+
+ mace_init(ioaddr, dev->dev_addr);
+
+ /* The if_port symbol can be set when the module is loaded */
+ if (if_port <= 2)
+ dev->if_port = if_port;
+ else
+ printk(KERN_NOTICE "nmclan_cs: invalid if_port requested\n");
+
+ link->dev = &((mace_private *)dev->priv)->node;
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ printk(KERN_INFO "%s: nmclan: port %#3lx, irq %d, %s port, hw_addr ",
+ dev->name, dev->base_addr, dev->irq, if_names[dev->if_port]);
+ for (i = 0; i < 6; i++)
+ printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+failed:
+ nmclan_release((u_long)link);
+ return;
+
+} /* nmclan_config */
+
+/* ----------------------------------------------------------------------------
+nmclan_release
+ After a card is removed, nmclan_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 nmclan_release(u_long arg)
+{
+ dev_link_t *link = (dev_link_t *)arg;
+
+ DEBUG(0, "nmclan_release(0x%p)\n", link);
+
+ if (link->open) {
+ DEBUG(1, "nmclan_cs: release postponed, '%s' "
+ "still open\n", link->dev->dev_name);
+ link->state |= DEV_STALE_CONFIG;
+ return;
+ }
+
+ CardServices(ReleaseConfiguration, link->handle);
+ CardServices(ReleaseIO, link->handle, &link->io);
+ CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+ link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
+
+} /* nmclan_release */
+
+/* ----------------------------------------------------------------------------
+nmclan_event
+ 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 nmclan_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ struct net_device *dev = link->priv;
+
+ DEBUG(1, "nmclan_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ dev->tbusy = 0xFF; dev->start = 0;
+ link->release.expires = jiffies + HZ/20;
+ add_timer(&link->release);
+ }
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ nmclan_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) {
+ dev->tbusy = 0xFF; dev->start = 0;
+ }
+ 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) {
+ dev->tbusy = 0;
+ dev->start = 1;
+ nmclan_reset(dev);
+ }
+ }
+ break;
+ case CS_EVENT_RESET_REQUEST:
+ return 1;
+ break;
+ }
+ return 0;
+} /* nmclan_event */
+
+/* ------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------------
+nmclan_reset
+ Reset and restore all of the Xilinx and MACE registers.
+---------------------------------------------------------------------------- */
+static void nmclan_reset(struct net_device *dev)
+{
+
+#if RESET_XILINX
+ dev_link_t *link;
+ conf_reg_t reg;
+ u_long OrigCorValue;
+
+ /* Find our client handle. */
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+ if (link == NULL) {
+ printk(KERN_NOTICE "nmclan_cs: bad device pointer!\n");
+ return;
+ }
+
+ /* Save original COR value */
+ reg.Function = 0;
+ reg.Action = CS_READ;
+ reg.Offset = CISREG_COR;
+ reg.Value = 0;
+ CardServices(AccessConfigurationRegister, link->handle, ®);
+ OrigCorValue = reg.Value;
+
+ /* Reset Xilinx */
+ reg.Action = CS_WRITE;
+ reg.Offset = CISREG_COR;
+ DEBUG(1, "nmclan_reset: OrigCorValue=0x%lX, resetting...\n",
+ OrigCorValue);
+ reg.Value = COR_SOFT_RESET;
+ CardServices(AccessConfigurationRegister, link->handle, ®);
+ /* Need to wait for 20 ms for PCMCIA to finish reset. */
+
+ /* Restore original COR configuration index */
+ reg.Value = COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK);
+ CardServices(AccessConfigurationRegister, link->handle, ®);
+ /* Xilinx is now completely reset along with the MACE chip. */
+ ((mace_private *)dev->priv)->tx_free_frames=AM2150_MAX_TX_FRAMES;
+
+#endif /* #if RESET_XILINX */
+
+ /* Xilinx is now completely reset along with the MACE chip. */
+ ((mace_private *)dev->priv)->tx_free_frames=AM2150_MAX_TX_FRAMES;
+
+ /* Reinitialize the MACE chip for operation. */
+ mace_init(dev->base_addr, dev->dev_addr);
+ mace_write(dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);
+
+ /* Restore the multicast list and enable TX and RX. */
+ restore_multicast_list(dev);
+} /* nmclan_reset */
+
+/* ----------------------------------------------------------------------------
+mace_config
+ [Someone tell me what this is supposed to do? Is if_port a defined
+ standard? If so, there should be defines to indicate 1=10Base-T,
+ 2=10Base-2, etc. including limited automatic detection.]
+---------------------------------------------------------------------------- */
+static int mace_config(struct net_device *dev, struct ifmap *map)
+{
+ if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+ if (map->port <= 2) {
+ dev->if_port = map->port;
+ printk(KERN_INFO "%s: switched to %s port\n", dev->name,
+ if_names[dev->if_port]);
+ }
+ else
+ return -EINVAL;
+ }
+ return 0;
+} /* mace_config */
+
+/* ----------------------------------------------------------------------------
+mace_open
+ Open device driver.
+---------------------------------------------------------------------------- */
+static int mace_open(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ dev_link_t *link;
+
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+ if (!DEV_OK(link))
+ return -ENODEV;
+
+ link->open++;
+ MOD_INC_USE_COUNT;
+
+ MACEBANK(0);
+
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ nmclan_reset(dev);
+
+ return 0; /* Always succeed */
+} /* mace_open */
+
+/* ----------------------------------------------------------------------------
+mace_close
+ Closes device driver.
+---------------------------------------------------------------------------- */
+static int mace_close(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ dev_link_t *link;
+
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+ if (link == NULL)
+ return -ENODEV;
+
+ DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
+
+ /* Mask off all interrupts from the MACE chip. */
+ outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR);
+
+ link->open--;
+ dev->start = 0;
+ if (link->state & DEV_STALE_CONFIG) {
+ link->release.expires = jiffies + HZ/20;
+ link->state |= DEV_RELEASE_PENDING;
+ add_timer(&link->release);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+} /* mace_close */
+
+/* ----------------------------------------------------------------------------
+mace_start_xmit
+ This routine begins the packet transmit function. When completed,
+ it will generate a transmit interrupt.
+
+ According to /usr/src/linux/net/inet/dev.c, if _start_xmit
+ returns 0, the "packet is now solely the responsibility of the
+ driver." If _start_xmit returns non-zero, the "transmission
+ failed, put skb back into a list."
+---------------------------------------------------------------------------- */
+static int mace_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ mace_private *lp = (mace_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ dev_link_t *link;
+
+#if TIMEOUT_TX
+ /* Transmitter timeout. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+
+ if (tickssofar < (HZ/5))
+ return 1;
+ printk(KERN_NOTICE "%s: transmit timed out -- ", dev->name);
+#if RESET_ON_TIMEOUT
+ printk("resetting card\n");
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+ if (link)
+ CardServices(ResetCard, link->handle);
+#else /* #if RESET_ON_TIMEOUT */
+ printk("NOT resetting card\n");
+#endif /* #if RESET_ON_TIMEOUT */
+ dev->trans_start = jiffies;
+ return 1;
+ }
+#else /* #if TIMEOUT_TX */
+ if (dev->tbusy)
+ return 1;
+#endif /* #if TIMEOUT_TX */
+
+ DEBUG(3, "%s: mace_start_xmit(length = %ld) called.\n",
+ dev->name, (long)skb->len);
+
+ /* Avoid timer-based retransmission conflicts. */
+ if (test_and_set_bit(TBUSY_UNSPECIFIED, (void*)&dev->tbusy) != 0) {
+ printk(KERN_NOTICE "%s: transmitter access conflict.\n",
+ dev->name);
+ return 1;
+ }
+
+#if (!TX_INTERRUPTABLE)
+ /* Disable MACE TX interrupts. */
+ outb(MACE_IMR_DEFAULT | MACE_IR_XMTINT,
+ ioaddr + AM2150_MACE_BASE + MACE_IMR);
+ lp->tx_irq_disabled=1;
+#endif /* #if (!TX_INTERRUPTABLE) */
+
+ {
+ /* This block must not be interrupted by another transmit request!
+ dev->tbusy will take care of timer-based retransmissions from
+ the upper layers. The interrupt handler is guaranteed never to
+ service a transmit interrupt while we are in here.
+ */
+ set_bit(TBUSY_PARTIAL_TX_FRAME, (void*)&dev->tbusy);
+
+ lp->linux_stats.tx_bytes += skb->len;
+ lp->tx_free_frames--;
+ set_bit(TBUSY_NO_FREE_TX_FRAMES, (void*)&dev->tbusy);
+
+ /* WARNING: Write the _exact_ number of bytes written in the header! */
+ /* Put out the word header [must be an outw()] . . . */
+ outw(skb->len, ioaddr + AM2150_XMT);
+ /* . . . and the packet [may be any combination of outw() and outb()] */
+ outsw(ioaddr + AM2150_XMT, skb->data, skb->len >> 1);
+ if (skb->len & 1) {
+ /* Odd byte transfer */
+ outb(skb->data[skb->len-1], ioaddr + AM2150_XMT);
+ }
+
+ dev->trans_start = jiffies;
+
+ if (lp->tx_free_frames > 0) {
+#if MULTI_TX
+ clear_bit(TBUSY_NO_FREE_TX_FRAMES, (void*)&dev->tbusy);
+#endif /* #if MULTI_TX */
+ }
+
+ clear_bit(TBUSY_PARTIAL_TX_FRAME, (void*)&dev->tbusy);
+ }
+
+#if (!TX_INTERRUPTABLE)
+ /* Re-enable MACE TX interrupts. */
+ lp->tx_irq_disabled=0;
+ outb(MACE_IMR_DEFAULT, ioaddr + AM2150_MACE_BASE + MACE_IMR);
+#endif /* #if (!TX_INTERRUPTABLE) */
+
+ dev_kfree_skb(skb);
+
+ return 0;
+} /* mace_start_xmit */
+
+/* ----------------------------------------------------------------------------
+mace_interrupt
+ The interrupt handler.
+---------------------------------------------------------------------------- */
+static void mace_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ mace_private *lp = (mace_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ int status;
+ int IntrCnt = MACE_MAX_IR_ITERATIONS;
+
+ if (dev == NULL) {
+ DEBUG(2, "mace_interrupt(): irq 0x%X for unknown device.\n",
+ irq);
+ return;
+ }
+
+ if (dev->interrupt || lp->tx_irq_disabled) {
+ sti();
+ printk(
+ (lp->tx_irq_disabled?
+ KERN_NOTICE "%s: Interrupt with tx_irq_disabled "
+ "[isr=%02X, imr=%02X]\n":
+ KERN_NOTICE "%s: Re-entering the interrupt handler "
+ "[isr=%02X, imr=%02X]\n"),
+ dev->name,
+ inb(ioaddr + AM2150_MACE_BASE + MACE_IR),
+ inb(ioaddr + AM2150_MACE_BASE + MACE_IMR)
+ );
+ /* WARNING: MACE_IR has been read! */
+ return;
+ }
+ dev->interrupt = 1;
+ sti();
+
+ if (dev->start == 0) {
+ DEBUG(2, "%s: interrupt from dead card\n", dev->name);
+ goto exception;
+ }
+
+ do {
+ /* WARNING: MACE_IR is a READ/CLEAR port! */
+ status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR);
+
+ DEBUG(3, "mace_interrupt: irq 0x%X status 0x%X.\n", irq, status);
+
+ if (status & MACE_IR_RCVINT) {
+ mace_rx(dev, MACE_MAX_RX_ITERATIONS);
+ }
+
+ if (status & MACE_IR_XMTINT) {
+ unsigned char fifofc;
+ unsigned char xmtrc;
+ unsigned char xmtfs;
+
+ fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC);
+ if ((fifofc & MACE_FIFOFC_XMTFC)==0) {
+ lp->linux_stats.tx_errors++;
+ outb(0xFF, ioaddr + AM2150_XMT_SKIP);
+ }
+
+ /* Transmit Retry Count (XMTRC, reg 4) */
+ xmtrc = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTRC);
+ if (xmtrc & MACE_XMTRC_EXDEF) lp->mace_stats.exdef++;
+ lp->mace_stats.xmtrc += (xmtrc & MACE_XMTRC_XMTRC);
+
+ if (
+ (xmtfs = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTFS)) &
+ MACE_XMTFS_XMTSV /* Transmit Status Valid */
+ ) {
+ lp->mace_stats.xmtsv++;
+
+ if (xmtfs & ~MACE_XMTFS_XMTSV) {
+ if (xmtfs & MACE_XMTFS_UFLO) {
+ /* Underflow. Indicates that the Transmit FIFO emptied before
+ the end of frame was reached. */
+ lp->mace_stats.uflo++;
+ }
+ if (xmtfs & MACE_XMTFS_LCOL) {
+ /* Late Collision */
+ lp->mace_stats.lcol++;
+ }
+ if (xmtfs & MACE_XMTFS_MORE) {
+ /* MORE than one retry was needed */
+ lp->mace_stats.more++;
+ }
+ if (xmtfs & MACE_XMTFS_ONE) {
+ /* Exactly ONE retry occurred */
+ lp->mace_stats.one++;
+ }
+ if (xmtfs & MACE_XMTFS_DEFER) {
+ /* Transmission was defered */
+ lp->mace_stats.defer++;
+ }
+ if (xmtfs & MACE_XMTFS_LCAR) {
+ /* Loss of carrier */
+ lp->mace_stats.lcar++;
+ }
+ if (xmtfs & MACE_XMTFS_RTRY) {
+ /* Retry error: transmit aborted after 16 attempts */
+ lp->mace_stats.rtry++;
+ }
+ } /* if (xmtfs & ~MACE_XMTFS_XMTSV) */
+
+ } /* if (xmtfs & MACE_XMTFS_XMTSV) */
+
+ lp->linux_stats.tx_packets++;
+ lp->tx_free_frames++;
+ clear_bit(TBUSY_NO_FREE_TX_FRAMES, (void*)&dev->tbusy);
+ mark_bh(NET_BH);
+ } /* if (status & MACE_IR_XMTINT) */
+
+ if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) {
+ if (status & MACE_IR_JAB) {
+ /* Jabber Error. Excessive transmit duration (20-150ms). */
+ lp->mace_stats.jab++;
+ }
+ if (status & MACE_IR_BABL) {
+ /* Babble Error. >1518 bytes transmitted. */
+ lp->mace_stats.babl++;
+ }
+ if (status & MACE_IR_CERR) {
+ /* Collision Error. CERR indicates the absence of the
+ Signal Quality Error Test message after a packet
+ transmission. */
+ lp->mace_stats.cerr++;
+ }
+ if (status & MACE_IR_RCVCCO) {
+ /* Receive Collision Count Overflow; */
+ lp->mace_stats.rcvcco++;
+ }
+ if (status & MACE_IR_RNTPCO) {
+ /* Runt Packet Count Overflow */
+ lp->mace_stats.rntpco++;
+ }
+ if (status & MACE_IR_MPCO) {
+ /* Missed Packet Count Overflow */
+ lp->mace_stats.mpco++;
+ }
+ } /* if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) */
+
+ } while ((status & ~MACE_IMR_DEFAULT) && (--IntrCnt));
+
+exception:
+ dev->interrupt = 0;
+ return;
+} /* mace_interrupt */
+
+/* ----------------------------------------------------------------------------
+mace_rx
+ Receives packets.
+---------------------------------------------------------------------------- */
+static int mace_rx(struct net_device *dev, unsigned char RxCnt)
+{
+ mace_private *lp = (mace_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ unsigned char rx_framecnt;
+ unsigned short rx_status;
+
+ while (
+ ((rx_framecnt = inb(ioaddr + AM2150_RCV_FRAME_COUNT)) > 0) &&
+ (rx_framecnt <= 12) && /* rx_framecnt==0xFF if card is extracted. */
+ (RxCnt--)
+ ) {
+ rx_status = inw(ioaddr + AM2150_RCV);
+
+ DEBUG(3, "%s: in mace_rx(), framecnt 0x%X, rx_status"
+ " 0x%X.\n", dev->name, rx_framecnt, rx_status);
+
+ if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */
+ lp->linux_stats.rx_errors++;
+ if (rx_status & MACE_RCVFS_OFLO) {
+ lp->mace_stats.oflo++;
+ }
+ if (rx_status & MACE_RCVFS_CLSN) {
+ lp->mace_stats.clsn++;
+ }
+ if (rx_status & MACE_RCVFS_FRAM) {
+ lp->mace_stats.fram++;
+ }
+ if (rx_status & MACE_RCVFS_FCS) {
+ lp->mace_stats.fcs++;
+ }
+ } else {
+ short pkt_len = (rx_status & ~MACE_RCVFS_RCVSTS) - 4;
+ /* Auto Strip is off, always subtract 4 */
+ struct sk_buff *skb;
+
+ lp->mace_stats.rfs_rntpc += inb(ioaddr + AM2150_RCV);
+ /* runt packet count */
+ lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV);
+ /* rcv collision count */
+
+ DEBUG(3, " receiving packet size 0x%X rx_status"
+ " 0x%X.\n", pkt_len, rx_status);
+
+ skb = dev_alloc_skb(pkt_len+2);
+
+ if (skb != NULL) {
+ skb->dev = dev;
+
+ skb_reserve(skb, 2);
+ insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1);
+ if (pkt_len & 1)
+ *(skb->tail-1) = inb(ioaddr + AM2150_RCV);
+ skb->protocol = eth_type_trans(skb, dev);
+
+ netif_rx(skb); /* Send the packet to the upper (protocol) layers. */
+
+ lp->linux_stats.rx_packets++;
+ lp->linux_stats.rx_bytes += skb->len;
+ outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
+ continue;
+ } else {
+ DEBUG(1, "%s: couldn't allocate a sk_buff of size"
+ " %d.\n", dev->name, pkt_len);
+ lp->linux_stats.rx_dropped++;
+ }
+ }
+ outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
+ } /* while */
+
+ return 0;
+} /* mace_rx */
+
+/* ----------------------------------------------------------------------------
+pr_linux_stats
+---------------------------------------------------------------------------- */
+static void pr_linux_stats(struct net_device_stats *pstats)
+{
+ DEBUG(2, "pr_linux_stats\n");
+ DEBUG(2, " rx_packets=%-7ld tx_packets=%ld\n",
+ (long)pstats->rx_packets, (long)pstats->tx_packets);
+ DEBUG(2, " rx_errors=%-7ld tx_errors=%ld\n",
+ (long)pstats->rx_errors, (long)pstats->tx_errors);
+ DEBUG(2, " rx_dropped=%-7ld tx_dropped=%ld\n",
+ (long)pstats->rx_dropped, (long)pstats->tx_dropped);
+ DEBUG(2, " multicast=%-7ld collisions=%ld\n",
+ (long)pstats->multicast, (long)pstats->collisions);
+
+ DEBUG(2, " rx_length_errors=%-7ld rx_over_errors=%ld\n",
+ (long)pstats->rx_length_errors, (long)pstats->rx_over_errors);
+ DEBUG(2, " rx_crc_errors=%-7ld rx_frame_errors=%ld\n",
+ (long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors);
+ DEBUG(2, " rx_fifo_errors=%-7ld rx_missed_errors=%ld\n",
+ (long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors);
+
+ DEBUG(2, " tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n",
+ (long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors);
+ DEBUG(2, " tx_fifo_errors=%-7ld tx_heartbeat_errors=%ld\n",
+ (long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors);
+ DEBUG(2, " tx_window_errors=%ld\n",
+ (long)pstats->tx_window_errors);
+} /* pr_linux_stats */
+
+/* ----------------------------------------------------------------------------
+pr_mace_stats
+---------------------------------------------------------------------------- */
+static void pr_mace_stats(mace_statistics *pstats)
+{
+ DEBUG(2, "pr_mace_stats\n");
+
+ DEBUG(2, " xmtsv=%-7d uflo=%d\n",
+ pstats->xmtsv, pstats->uflo);
+ DEBUG(2, " lcol=%-7d more=%d\n",
+ pstats->lcol, pstats->more);
+ DEBUG(2, " one=%-7d defer=%d\n",
+ pstats->one, pstats->defer);
+ DEBUG(2, " lcar=%-7d rtry=%d\n",
+ pstats->lcar, pstats->rtry);
+
+ /* MACE_XMTRC */
+ DEBUG(2, " exdef=%-7d xmtrc=%d\n",
+ pstats->exdef, pstats->xmtrc);
+
+ /* RFS1--Receive Status (RCVSTS) */
+ DEBUG(2, " oflo=%-7d clsn=%d\n",
+ pstats->oflo, pstats->clsn);
+ DEBUG(2, " fram=%-7d fcs=%d\n",
+ pstats->fram, pstats->fcs);
+
+ /* RFS2--Runt Packet Count (RNTPC) */
+ /* RFS3--Receive Collision Count (RCVCC) */
+ DEBUG(2, " rfs_rntpc=%-7d rfs_rcvcc=%d\n",
+ pstats->rfs_rntpc, pstats->rfs_rcvcc);
+
+ /* MACE_IR */
+ DEBUG(2, " jab=%-7d babl=%d\n",
+ pstats->jab, pstats->babl);
+ DEBUG(2, " cerr=%-7d rcvcco=%d\n",
+ pstats->cerr, pstats->rcvcco);
+ DEBUG(2, " rntpco=%-7d mpco=%d\n",
+ pstats->rntpco, pstats->mpco);
+
+ /* MACE_MPC */
+ DEBUG(2, " mpc=%d\n", pstats->mpc);
+
+ /* MACE_RNTPC */
+ DEBUG(2, " rntpc=%d\n", pstats->rntpc);
+
+ /* MACE_RCVCC */
+ DEBUG(2, " rcvcc=%d\n", pstats->rcvcc);
+
+} /* pr_mace_stats */
+
+/* ----------------------------------------------------------------------------
+update_stats
+ Update statistics. We change to register window 1, so this
+ should be run single-threaded if the device is active. This is
+ expected to be a rare operation, and it's simpler for the rest
+ of the driver to assume that window 0 is always valid rather
+ than use a special window-state variable.
+
+ oflo & uflo should _never_ occur since it would mean the Xilinx
+ was not able to transfer data between the MACE FIFO and the
+ card's SRAM fast enough. If this happens, something is
+ seriously wrong with the hardware.
+---------------------------------------------------------------------------- */
+static void update_stats(ioaddr_t ioaddr, struct net_device *dev)
+{
+ mace_private *lp = (mace_private *)dev->priv;
+
+ lp->mace_stats.rcvcc += mace_read(ioaddr, MACE_RCVCC);
+ lp->mace_stats.rntpc += mace_read(ioaddr, MACE_RNTPC);
+ lp->mace_stats.mpc += mace_read(ioaddr, MACE_MPC);
+ /* At this point, mace_stats is fully updated for this call.
+ We may now update the linux_stats. */
+
+ /* The MACE has no equivalent for linux_stats field which are commented
+ out. */
+
+ /* lp->linux_stats.multicast; */
+ lp->linux_stats.collisions =
+ lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc;
+ /* Collision: The MACE may retry sending a packet 15 times
+ before giving up. The retry count is in XMTRC.
+ Does each retry constitute a collision?
+ If so, why doesn't the RCVCC record these collisions? */
+
+ /* detailed rx_errors: */
+ lp->linux_stats.rx_length_errors =
+ lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc;
+ /* lp->linux_stats.rx_over_errors */
+ lp->linux_stats.rx_crc_errors = lp->mace_stats.fcs;
+ lp->linux_stats.rx_frame_errors = lp->mace_stats.fram;
+ lp->linux_stats.rx_fifo_errors = lp->mace_stats.oflo;
+ lp->linux_stats.rx_missed_errors =
+ lp->mace_stats.mpco * 256 + lp->mace_stats.mpc;
+
+ /* detailed tx_errors */
+ lp->linux_stats.tx_aborted_errors = lp->mace_stats.rtry;
+ lp->linux_stats.tx_carrier_errors = lp->mace_stats.lcar;
+ /* LCAR usually results from bad cabling. */
+ lp->linux_stats.tx_fifo_errors = lp->mace_stats.uflo;
+ lp->linux_stats.tx_heartbeat_errors = lp->mace_stats.cerr;
+ /* lp->linux_stats.tx_window_errors; */
+
+ return;
+} /* update_stats */
+
+/* ----------------------------------------------------------------------------
+mace_get_stats
+ Gathers ethernet statistics from the MACE chip.
+---------------------------------------------------------------------------- */
+static struct net_device_stats *mace_get_stats(struct net_device *dev)
+{
+ mace_private *lp = (mace_private *)dev->priv;
+
+ update_stats(dev->base_addr, dev);
+
+ DEBUG(1, "%s: updating the statistics.\n", dev->name);
+ pr_linux_stats(&lp->linux_stats);
+ pr_mace_stats(&lp->mace_stats);
+
+ return &lp->linux_stats;
+} /* net_device_stats */
+
+/* ----------------------------------------------------------------------------
+updateCRC
+ Modified from Am79C90 data sheet.
+---------------------------------------------------------------------------- */
+
+#if BROKEN_MULTICAST
+
+static void updateCRC(int *CRC, int bit)
+{
+ int poly[]={
+ 1,1,1,0, 1,1,0,1,
+ 1,0,1,1, 1,0,0,0,
+ 1,0,0,0, 0,0,1,1,
+ 0,0,1,0, 0,0,0,0
+ }; /* CRC polynomial. poly[n] = coefficient of the x**n term of the
+ CRC generator polynomial. */
+
+ int j;
+
+ /* shift CRC and control bit (CRC[32]) */
+ for (j = 32; j > 0; j--)
+ CRC[j] = CRC[j-1];
+ CRC[0] = 0;
+
+ /* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */
+ if (bit ^ CRC[32])
+ for (j = 0; j < 32; j++)
+ CRC[j] ^= poly[j];
+} /* updateCRC */
+
+/* ----------------------------------------------------------------------------
+BuildLAF
+ Build logical address filter.
+ Modified from Am79C90 data sheet.
+
+Input
+ ladrf: logical address filter (contents initialized to 0)
+ adr: ethernet address
+---------------------------------------------------------------------------- */
+static void BuildLAF(int *ladrf, int *adr)
+{
+ int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */
+
+ int i, byte; /* temporary array indices */
+ int hashcode; /* the output object */
+
+ CRC[32]=0;
+
+ for (byte = 0; byte < 6; byte++)
+ for (i = 0; i < 8; i++)
+ updateCRC(CRC, (adr[byte] >> i) & 1);
+
+ hashcode = 0;
+ for (i = 0; i < 6; i++)
+ hashcode = (hashcode << 1) + CRC[i];
+
+ byte = hashcode >> 3;
+ ladrf[byte] |= (1 << (hashcode & 7));
+
+#ifdef PCMCIA_DEBUG
+ if (pc_debug > 2) {
+ printk(KERN_DEBUG " adr =");
+ for (i = 0; i < 6; i++)
+ printk(" %02X", adr[i]);
+ printk("\n" KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63]"
+ " =", hashcode);
+ for (i = 0; i < 8; i++)
+ printk(" %02X", ladrf[i]);
+ printk("\n");
+ }
+#endif
+} /* BuildLAF */
+
+/* ----------------------------------------------------------------------------
+restore_multicast_list
+ Restores the multicast filter for MACE chip to the last
+ set_multicast_list() call.
+
+Input
+ multicast_num_addrs
+ multicast_ladrf[]
+---------------------------------------------------------------------------- */
+static void restore_multicast_list(struct net_device *dev)
+{
+ mace_private *lp = (mace_private *)dev->priv;
+ int num_addrs = lp->multicast_num_addrs;
+ int *ladrf = lp->multicast_ladrf;
+ ioaddr_t ioaddr = dev->base_addr;
+ int i;
+
+ DEBUG(2, "%s: restoring Rx mode to %d addresses.\n",
+ dev->name, num_addrs);
+
+ if (num_addrs > 0) {
+
+ DEBUG(1, "Attempt to restore multicast list detected.\n");
+
+ mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR);
+ /* Poll ADDRCHG bit */
+ while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
+ ;
+ /* Set LADRF register */
+ for (i = 0; i < MACE_LADRF_LEN; i++)
+ mace_write(ioaddr, MACE_LADRF, ladrf[i]);
+
+ mace_write(ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL);
+ mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+
+ } else if (num_addrs < 0) {
+
+ /* Promiscuous mode: receive all packets */
+ mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(ioaddr, MACE_MACCC,
+ MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
+ );
+
+ } else {
+
+ /* Normal mode */
+ mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+
+ }
+} /* restore_multicast_list */
+
+/* ----------------------------------------------------------------------------
+set_multicast_list
+ Set or clear the multicast filter for this adaptor.
+
+Input
+ 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.
+Output
+ multicast_num_addrs
+ multicast_ladrf[]
+---------------------------------------------------------------------------- */
+
+static void set_multicast_list(struct net_device *dev)
+{
+ mace_private *lp = (mace_private *)dev->priv;
+ int adr[ETHER_ADDR_LEN] = {0}; /* Ethernet address */
+ int i;
+ struct dev_mc_list *dmi = dev->mc_list;
+
+#ifdef PCMCIA_DEBUG
+ if (pc_debug > 1) {
+ static int old = 0;
+ if (dev->mc_count != old) {
+ old = dev->mc_count;
+ DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+ dev->name, old);
+ }
+ }
+#endif
+
+ /* Set multicast_num_addrs. */
+ lp->multicast_num_addrs = dev->mc_count;
+
+ /* Set multicast_ladrf. */
+ if (num_addrs > 0) {
+ /* Calculate multicast logical address filter */
+ memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN);
+ for (i = 0; i < dev->mc_count; i++) {
+ memcpy(adr, dmi->dmi_addr, ETHER_ADDR_LEN);
+ dmi = dmi->next;
+ BuildLAF(lp->multicast_ladrf, adr);
+ }
+ }
+
+ restore_multicast_list(dev);
+
+} /* set_multicast_list */
+
+#endif /* BROKEN_MULTICAST */
+
+static void restore_multicast_list(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+
+ DEBUG(2, "%s: restoring Rx mode to %d addresses.\n", dev->name,
+ ((mace_private *)(dev->priv))->multicast_num_addrs);
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Promiscuous mode: receive all packets */
+ mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(ioaddr, MACE_MACCC,
+ MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
+ );
+ } else {
+ /* Normal mode */
+ mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+ }
+} /* restore_multicast_list */
+
+static void set_multicast_list(struct net_device *dev)
+{
+ mace_private *lp = (mace_private *)dev->priv;
+
+#ifdef PCMCIA_DEBUG
+ if (pc_debug > 1) {
+ static int old = 0;
+ if (dev->mc_count != old) {
+ old = dev->mc_count;
+ DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+ dev->name, old);
+ }
+ }
+#endif
+
+ lp->multicast_num_addrs = dev->mc_count;
+ restore_multicast_list(dev);
+
+} /* set_multicast_list */
+
+/* ----------------------------------------------------------------------------
+init_nmclan_cs
+---------------------------------------------------------------------------- */
+
+static int __init init_nmclan_cs(void)
+{
+ servinfo_t serv;
+ DEBUG(0, "%s\n", version);
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "nmclan_cs: Card Services release does not match!\n");
+ return -1;
+ }
+ register_pccard_driver(&dev_info, &nmclan_attach, &nmclan_detach);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------
+exit_nmclan_cs
+---------------------------------------------------------------------------- */
+
+static void __exit exit_nmclan_cs(void)
+{
+ DEBUG(0, "nmclan_cs: unloading\n");
+ unregister_pccard_driver(&dev_info);
+ while (dev_list != NULL)
+ nmclan_detach(dev_list);
+}
+
+module_init(init_nmclan_cs);
+module_exit(exit_nmclan_cs);
--- /dev/null
+/*
+ This file contains the firmware of Seven of Diamonds from OSITECH.
+ (Special thanks to Kevin MacPherson of OSITECH)
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+*/
+
+ static const u_char __Xilinx7OD[] = {
+ 0xFF, 0x04, 0xA0, 0x36, 0xF3, 0xEC, 0xFF, 0xFF, 0xFF, 0xDF, 0xFB, 0xFF,
+ 0xF3, 0xFF, 0xFF, 0xFF,
+ 0xEF, 0x3F, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F, 0xFE, 0xFF,
+ 0xCE, 0xFE, 0xFE, 0xFE,
+ 0xFE, 0xDE, 0xBD, 0xDD, 0xFD, 0xFF, 0xFD, 0xCF, 0xF7, 0xBF, 0x7F, 0xFF,
+ 0x7F, 0x3F, 0xFE, 0xBF,
+ 0xFF, 0xFF, 0xFF, 0xBC, 0xFF, 0xFF, 0xBD, 0xB5, 0x7F, 0x7F, 0xBF, 0xBF,
+ 0x7F, 0xFF, 0xEF, 0xFF,
+ 0xFF, 0xFF, 0xFB, 0xFF, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xDE,
+ 0xFE, 0xFE, 0xFA, 0xDE,
+ 0xBD, 0xFD, 0xED, 0xFD, 0xFD, 0xCF, 0xEF, 0xEF, 0xEF, 0xEF, 0xC7, 0xDF,
+ 0xDF, 0xDF, 0xDF, 0xDF,
+ 0xFF, 0x7E, 0xFE, 0xFD, 0x7D, 0x6D, 0xEE, 0xFE, 0x7C, 0xFB, 0xF4, 0xFB,
+ 0xCF, 0xDB, 0xDF, 0xFF,
+ 0xFF, 0xBB, 0x7F, 0xFF, 0x7F, 0xFF, 0xF7, 0xFF, 0x9E, 0xBF, 0x3B, 0xBF,
+ 0xBF, 0x7F, 0x7F, 0x7F,
+ 0x7E, 0x6F, 0xDF, 0xEF, 0xF5, 0xF6, 0xFD, 0xF6, 0xF5, 0xED, 0xEB, 0xFF,
+ 0xEF, 0xEF, 0xEF, 0x7E,
+ 0x7F, 0x7F, 0x6F, 0x7F, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xEF, 0xBF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0x1F, 0x1F, 0xEE, 0xFF, 0xBC,
+ 0xB7, 0xFF, 0xDF, 0xFF,
+ 0xDF, 0xEF, 0x3B, 0xE3, 0xD3, 0xFF, 0xFB, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF,
+ 0xFF, 0xBA, 0xBF, 0x2D,
+ 0xDB, 0xBD, 0xFD, 0xDB, 0xDF, 0xFA, 0xFB, 0xFF, 0xEF, 0xFB, 0xDB, 0xF3,
+ 0xFF, 0xDF, 0xFD, 0x7F,
+ 0xEF, 0xFB, 0xFF, 0xFF, 0xBE, 0xBF, 0x27, 0xBA, 0xFE, 0xFB, 0xDF, 0xFF,
+ 0xF6, 0xFF, 0xFF, 0xEF,
+ 0xFB, 0xDB, 0xF3, 0xD9, 0x9A, 0x3F, 0xFF, 0xAF, 0xBF, 0xFF, 0xFF, 0xBE,
+ 0x3F, 0x37, 0xBD, 0x96,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAE, 0xFB, 0xF3, 0xF3, 0xEB, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xF7, 0xFA, 0xBC, 0xAE, 0xFE, 0xBE, 0xFE, 0xBB, 0x7F, 0xFD, 0xFF,
+ 0x7F, 0xEF, 0xF7, 0xFB,
+ 0xBB, 0xD7, 0xF7, 0x7F, 0xFF, 0xF7, 0xFF, 0xFF, 0xF7, 0xBC, 0xED, 0xFD,
+ 0xBD, 0x9D, 0x7D, 0x7B,
+ 0xFB, 0x7B, 0x7B, 0xFB, 0xAF, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xF7,
+ 0xAA, 0xB9, 0xBF, 0x8F, 0xBF, 0xDF, 0xFF, 0x7F, 0xFF, 0xFF, 0x7F, 0xCF,
+ 0xFB, 0xEB, 0xCB, 0xEB,
+ 0xEE, 0xFF, 0xFF, 0xD7, 0xFF, 0xFF, 0xFF, 0x3E, 0x33, 0x3F, 0x1C, 0x7C,
+ 0xFC, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xCF, 0xD3, 0xF3, 0xE3, 0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xEB, 0xFE, 0x35,
+ 0x3F, 0x3D, 0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xEF, 0x6F, 0xE3,
+ 0xE3, 0xE3, 0xEF, 0xFF,
+ 0xFF, 0xDF, 0xFF, 0xFF, 0xF7, 0xFE, 0x3E, 0x5E, 0xFE, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFD, 0xFF, 0xFF,
+ 0xAF, 0xCF, 0xF2, 0xCB, 0xCF, 0x8E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD,
+ 0xFC, 0x3E, 0x1F, 0x9E,
+ 0xAD, 0xFD, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xEF, 0xFF, 0xB3, 0xF7, 0xE7,
+ 0xF7, 0xFA, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xEE, 0xEB, 0xAB, 0xAF, 0x9F, 0xE3, 0x7F, 0xFF, 0xDE,
+ 0xFF, 0x7F, 0xEE, 0xFF,
+ 0xFF, 0xFB, 0x3A, 0xFA, 0xFF, 0xF2, 0x77, 0xFF, 0xFF, 0xF7, 0xFE, 0xFF,
+ 0xFE, 0xBD, 0xAE, 0xDE,
+ 0x7D, 0x7D, 0xFD, 0xFF, 0xBF, 0xEE, 0xFF, 0xFD, 0xFF, 0xDB, 0xFB, 0xFF,
+ 0xF7, 0xEF, 0xFB, 0xFF,
+ 0xFF, 0xFE, 0xFF, 0x2D, 0xAF, 0xB9, 0xFD, 0x79, 0xFB, 0xFA, 0xFF, 0xBF,
+ 0xEF, 0xFF, 0xFF, 0x91,
+ 0xFA, 0xFB, 0xDF, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFC, 0xCF, 0x37, 0xBF,
+ 0xBF, 0xFF, 0x7F, 0x7F,
+ 0xFF, 0xFF, 0xFF, 0xAF, 0xFF, 0xFF, 0xF3, 0xFB, 0xFB, 0xFF, 0xF5, 0xEF,
+ 0xFF, 0xFF, 0xF7, 0xFA,
+ 0xFF, 0xFF, 0xEE, 0xFA, 0xFE, 0xFB, 0x55, 0xDD, 0xFF, 0x7F, 0xAF, 0xFE,
+ 0xFF, 0xFB, 0xFB, 0xF5,
+ 0xFF, 0xF7, 0xEF, 0xFF, 0xFF, 0xFF, 0xBE, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D,
+ 0x7B, 0x7B, 0x7B, 0x7B,
+ 0xFB, 0xAE, 0xFF, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF7, 0xDA, 0xB7, 0x61,
+ 0xFF, 0xB9, 0x59, 0xF3, 0x73, 0xF3, 0xDF, 0x7F, 0x6F, 0xDF, 0xEF, 0xF7,
+ 0xEB, 0xEB, 0xD7, 0xFF,
+ 0xD7, 0xFF, 0xFF, 0xF7, 0xFE, 0x7F, 0xFB, 0x3E, 0x38, 0x73, 0xF6, 0x7F,
+ 0xFC, 0xFF, 0xFF, 0xCF,
+ 0xFF, 0xB7, 0xFB, 0xB3, 0xB3, 0x67, 0xFF, 0xE7, 0xFD, 0xFF, 0xEF, 0xF6,
+ 0x7F, 0xB7, 0xBC, 0xF5,
+ 0x7B, 0xF6, 0xF7, 0xF5, 0xFF, 0xFF, 0xEF, 0xFF, 0xF7, 0xFF, 0xF7, 0xCE,
+ 0xE7, 0xFF, 0x9F, 0xFF,
+ 0xFF, 0xF5, 0xFE, 0x7D, 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xEF, 0xFF, 0xF6,
+ 0xCB, 0xDB, 0xEE, 0xFE, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xBE,
+ 0x1E, 0x3E, 0xFE, 0xFF,
+ 0x7D, 0xFE, 0xFF, 0xFF, 0xEF, 0xBF, 0xE7, 0xFF, 0xE3, 0xE3, 0xFF, 0xDF,
+ 0xE7, 0xFF, 0xFF, 0xFF,
+ 0xB8, 0xEF, 0xB7, 0x2F, 0xEE, 0xFF, 0xDF, 0xFF, 0xBF, 0xFF, 0x7F, 0xEF,
+ 0xEB, 0xBF, 0xA3, 0xD3,
+ 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xBE, 0xFD, 0x3F, 0xCF, 0xFD,
+ 0xFB, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xAF, 0xFB, 0xBF, 0xBB, 0xBF, 0xDB, 0xFD, 0xFB, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x3E, 0xFE,
+ 0x3F, 0xBA, 0xBA, 0xFE, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xEF, 0xC3, 0x7F,
+ 0xB2, 0x9B, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0x3C, 0xFF, 0x3F, 0x3C, 0xFF, 0xFE, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xAF, 0xF3, 0xFE, 0xF3, 0xE3, 0xEB, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xF7,
+ 0x9A, 0xFE, 0xAF, 0x9E,
+ 0xBE, 0xFE, 0xFF, 0xDF, 0xFF, 0xFF, 0x7B, 0xEF, 0xF7, 0xBF, 0xFB, 0xFB,
+ 0xFB, 0xFF, 0xFF, 0x7F,
+ 0xFF, 0xFF, 0xFF, 0xBC, 0xBD, 0xFD, 0xBD, 0xDD, 0x7D, 0x7B, 0x7B, 0x7B,
+ 0x7B, 0xFB, 0xAE, 0xFF,
+ 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xF7, 0x9A, 0xFF,
+ 0x9F, 0xFF, 0xAF, 0xEF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xCF, 0xF3, 0xFF, 0xEB, 0xFF, 0xEB, 0xFF,
+ 0xFF, 0xBF, 0xFF, 0xFF,
+ 0xEF, 0xFE, 0xFF, 0x37, 0xFC, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xCF, 0xEF, 0xFD, 0xF3,
+ 0xFF, 0xEE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0xFD, 0x2F, 0xFD,
+ 0xFF, 0xFD, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xEF, 0xCF, 0xFF, 0xF3, 0xBF, 0x69, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFE,
+ 0xFB, 0x9F, 0xFF, 0xBF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x87,
+ 0xFE, 0xDA, 0xEF, 0xCF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xEF, 0xBF, 0xEF, 0xEF, 0xFD,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xEF, 0xFD, 0xFF, 0x7B, 0xFF, 0xEB, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xEB, 0xF8, 0xFF, 0xEF,
+ 0xAF, 0xFF, 0xFF, 0xBD, 0xFF, 0xFF, 0xFF, 0x7F, 0xEE, 0x7F, 0xEF, 0xFF,
+ 0xBB, 0xFF, 0xBF, 0xFB,
+ 0xFF, 0xFF, 0xFF, 0xF7, 0xF6, 0xFB, 0xBD, 0xFD, 0xDD, 0xF5, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xAF,
+ 0xFF, 0x5F, 0xF5, 0xDF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6,
+ 0xF3, 0xFF, 0xDE, 0xFE,
+ 0xEF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xDE, 0xDF, 0x5F, 0xDF,
+ 0xFD, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xAF, 0xFF, 0xFF,
+ 0xEF, 0xED, 0xFF, 0xDF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xDA, 0xBD, 0xBE,
+ 0xAE, 0xFE, 0x7F, 0xFD,
+ 0xDF, 0xFF, 0xFF, 0x7F, 0xEF, 0xFF, 0xFB, 0xFB, 0xFB, 0x7F, 0xF7, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xF7,
+ 0xBC, 0xFD, 0xBD, 0xBD, 0xBD, 0xFD, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAE,
+ 0xFF, 0xFF, 0xFD, 0xFF,
+ 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x9F, 0xBF, 0xBF, 0xCF,
+ 0x7F, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xAF, 0xFF, 0xEB, 0xEB, 0xEB, 0xFF, 0xD7, 0xFE, 0xFF, 0xFF,
+ 0xBF, 0xE7, 0xFE, 0xBF,
+ 0x7F, 0xFC, 0xFF, 0xFF, 0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFB,
+ 0xFB, 0xFF, 0xFF, 0xDD,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBD, 0xDF, 0x9D, 0xFD, 0xDF, 0xB9,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xEF, 0xFF, 0xFB, 0xEF, 0xEB, 0xFF, 0xDE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF6, 0x9F, 0xFF, 0xFC,
+ 0xFE, 0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xDF, 0xFA, 0xCD, 0xCF,
+ 0xBF, 0x9F, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xF7, 0xFE, 0xBF, 0xFF, 0xDF, 0xEF, 0x5F, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x7F, 0x6F, 0xFF,
+ 0xBB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0xFF,
+ 0x5F, 0xFF, 0xBF, 0xBF,
+ 0xF9, 0xFF, 0xFF, 0xFF, 0x7F, 0x6E, 0x7B, 0xFF, 0xEF, 0xFD, 0xEB, 0xDF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF7, 0xB6, 0x3E, 0xFC, 0xFD, 0xBF, 0x7E, 0xFB, 0xFF, 0xFF, 0xFF, 0xF7,
+ 0xEF, 0xF7, 0xF3, 0xF7,
+ 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0x35, 0x79, 0xFF,
+ 0xBF, 0xFC, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xEF, 0xFB, 0x53, 0xDF, 0xFF, 0xEB, 0xBF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xBC,
+ 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xF5,
+ 0xFF, 0xF7, 0xFF, 0xFB,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBA, 0xAA, 0xEE, 0xFE, 0x3F, 0x7D,
+ 0xFD, 0xFF, 0xFF, 0xFF,
+ 0x7F, 0xAF, 0x77, 0xFB, 0xFB, 0xFF, 0xFB, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF7, 0xBE, 0xBD, 0xBD,
+ 0xBD, 0xBD, 0xFD, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAE, 0xFF, 0xEF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFC,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x9A, 0xD9, 0xB8, 0xFF, 0xFF, 0x79, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xCF,
+ 0xFB, 0xFF, 0xEB, 0xFF, 0xEB, 0xD7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xDE,
+ 0xF8, 0xFB, 0xFE, 0x3F,
+ 0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xAD, 0xBF, 0xFA, 0xFF, 0x73,
+ 0xDF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x3A, 0xF5, 0xB7, 0xFC, 0x3F, 0xF9, 0xFD, 0xFF, 0xFF, 0xFF,
+ 0x7F, 0xEF, 0xF3, 0xFF,
+ 0xBF, 0xFE, 0xF3, 0x9F, 0xFE, 0xFF, 0xFF, 0xFF, 0xF7, 0x3E, 0xFF, 0xFF,
+ 0xFF, 0xBF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xD3, 0xFE, 0xDB, 0xFF, 0xDB, 0xDF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x3E, 0xFF, 0xBF, 0xFF, 0x7F, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F,
+ 0xF3, 0xFF, 0xED, 0xFF,
+ 0xF7, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xF6, 0x3C, 0xFE, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x9F, 0xEF, 0xEF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x7E, 0xBF,
+ 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBB, 0xEF, 0xDF, 0xF1,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x3E, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xBF,
+ 0xEF, 0xFD, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF,
+ 0xFC, 0x3E, 0xFE, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2E, 0xEF, 0xF3, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xF7, 0xBA, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x7F, 0xAF, 0xFB,
+ 0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xF2, 0xD6, 0xED,
+ 0xBD, 0xBD, 0xBD, 0x7D,
+ 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x92, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F,
+ 0xAF, 0xEB, 0xEB, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFE, 0x2E, 0xFE, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x4F, 0xEF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFE,
+ 0x3C, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xCE,
+ 0xC3, 0xFD, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xEF, 0xCF, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF7, 0xEE, 0x3E, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xDF, 0xE2, 0xFF,
+ 0xFF, 0xFF, 0xFB, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xBE, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x7F, 0xEE,
+ 0x5F, 0xE6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E,
+ 0x7D, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xF3, 0xFB, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xBF, 0xF7, 0x36, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xEF, 0xD3, 0xF6,
+ 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x7F, 0xEE,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xEF, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xBA, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE,
+ 0xFB, 0xFA, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xD6, 0xFD, 0xBD, 0xBD, 0xBD,
+ 0x7D, 0x7B, 0x7B, 0x7B,
+ 0x7B, 0xFB, 0xAE, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xF7, 0xBA, 0xBF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xEB, 0x6B,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFE, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x4F, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF,
+ 0x3E, 0x6E, 0xFC, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xC3, 0xC9, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x3E, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xEF, 0xFB,
+ 0xD5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE,
+ 0xFE, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6F, 0xEF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFB,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xF6, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE,
+ 0xEF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFE, 0xFF, 0xF7, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x7F, 0xFA, 0xEF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xE7, 0xFF, 0xFE,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE, 0xEF, 0xBF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xA7, 0xFF, 0xFC, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x7F,
+ 0xFE, 0xAE, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7,
+ 0xF7, 0xFA, 0xFF, 0xFD,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xAF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xF7, 0xBE, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B,
+ 0x7B, 0x7B, 0xFB, 0xAF,
+ 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCA,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x6F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xE7, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xCF, 0xFE, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xDF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xE7, 0xF2, 0xFC,
+ 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xAE, 0xEF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x7E, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF,
+ 0xFE, 0xFE, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xEF, 0xDD, 0xFE, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xAF, 0xEF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBA, 0xFE,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFA, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF6, 0x9C, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB,
+ 0xAE, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x7A, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xDF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x6F, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xF7, 0xFE,
+ 0xFE, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xEB,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x9E, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xEF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFE, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xCB, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFD,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xBE, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xEF,
+ 0xEF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFB, 0xAF, 0x7F, 0xFF,
+ 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xEF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xBF, 0xFF,
+ 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAE,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xF7, 0xBC, 0xBD,
+ 0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x7F,
+ 0xAF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF,
+ 0xFE, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF,
+ 0xFF, 0xFF, 0xEF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xBF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xEF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFE, 0xFF, 0x9F, 0x9F,
+ 0x9F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0xFF, 0xEF, 0xDF, 0xDF, 0xDF, 0xDF, 0xCF, 0xB7, 0xBF, 0xBF,
+ 0xBF, 0xBF, 0xFF, 0xBC,
+ 0xB9, 0x9D, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xEF, 0xD7,
+ 0xF5, 0xF3, 0xF1, 0xD1,
+ 0x65, 0xE3, 0xE3, 0xE3, 0xA3, 0xFF, 0xFE, 0x7F, 0xFE, 0xDE, 0xDE, 0xFF,
+ 0xBD, 0xBD, 0xBD, 0xBD,
+ 0xDF, 0xEF, 0xFB, 0xF7, 0xF3, 0xF3, 0xF3, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7,
+ 0xFB, 0xFE, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+
+ };
Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu
- pcnet_cs.c 1.99 1999/09/15 15:33:09
+ pcnet_cs.c 1.101 1999/10/21 00:56:19
The network driver code is based on Donald Becker's NE2000 code:
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version =
-"pcnet_cs.c 1.99 1999/09/15 15:33:09 (David Hinds)";
+"pcnet_cs.c 1.101 1999/10/21 00:56:19 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
{
struct net_device *dev = link->priv;
unsigned char prom[32];
- int i, j, ioaddr;
+ ioaddr_t ioaddr;
+ int i, j;
/* This is lifted straight from drivers/net/ne.c */
struct {
static void set_misc_reg(struct net_device *dev)
{
- int nic_base = dev->base_addr;
+ ioaddr_t nic_base = dev->base_addr;
pcnet_dev_t *info = (pcnet_dev_t *)dev;
u_char tmp;
int i;
for (i = 0; i < 20; i++) {
if ((inb(dev->base_addr+0x1c) & 0x01) == 0) break;
- current->state = TASK_INTERRUPTIBLE;
+ __set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/10);
}
}
static void pcnet_reset_8390(struct net_device *dev)
{
- int nic_base = dev->base_addr;
+ ioaddr_t nic_base = dev->base_addr;
int i;
ei_status.txing = ei_status.dmaing = 0;
{
pcnet_dev_t *info = (pcnet_dev_t *)(arg);
struct net_device *dev = &info->dev;
- int nic_base = dev->base_addr;
+ ioaddr_t nic_base = dev->base_addr;
if (dev->start == 0) goto reschedule;
struct e8390_pkt_hdr *hdr,
int ring_page)
{
- int nic_base = dev->base_addr;
+ ioaddr_t nic_base = dev->base_addr;
if (ei_status.dmaing) {
printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input."
static void dma_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset)
{
- int nic_base = dev->base_addr;
+ ioaddr_t nic_base = dev->base_addr;
int xfer_count = count;
char *buf = skb->data;
const unsigned char *buf,
const int start_page)
{
- int nic_base = dev->base_addr;
+ ioaddr_t nic_base = dev->base_addr;
pcnet_dev_t *info = (pcnet_dev_t *)dev;
#ifdef PCMCIA_DEBUG
int retries = 0;
--- /dev/null
+/*======================================================================
+
+ A PCMCIA ethernet driver for SMC91c92-based cards.
+
+ This driver supports Megahertz PCMCIA ethernet cards; and
+ Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem
+ multifunction cards.
+
+ Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu
+
+ smc91c92_cs.c 1.79 1999/10/19 00:38:29
+
+ This driver contains code written by Donald Becker
+ (becker@cesdis.gsfc.nasa.gov), Rowan Hughes (x-csrdh@jcu.edu.au),
+ David Hinds (dhinds@hyper.stanford.edu), and Erik Stahlman
+ (erik@vt.edu). Donald wrote the SMC 91c92 code using parts of
+ Erik's SMC 91c94 driver. Rowan wrote a similar driver, and I've
+ incorporated some parts of his driver here. I (Dave) wrote most
+ of the PCMCIA glue code, and the Ositech support code. Kelly
+ Stephens (kstephen@holli.com) added support for the Motorola
+ Mariner, with help from Allen Brost.
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+/* Ositech Seven of Diamonds firmware */
+#include "ositech.h"
+
+/*====================================================================*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+static const char *version =
+"smc91c92_cs.c 0.09 1996/8/4 Donald Becker, becker@cesdis.gsfc.nasa.gov.\n";
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+#else
+#define DEBUG(n, args...)
+#endif
+
+static char *if_names[] = { "auto", "10baseT", "10base2"};
+
+/* Parameters that can be set with 'insmod' */
+
+/*
+ Transceiver/media type.
+ 0 = auto
+ 1 = 10baseT (and autoselect if #define AUTOSELECT),
+ 2 = AUI/10base2,
+*/
+static int if_port = 0;
+
+/* Bit map of interrupts to choose from. */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(if_port, "i");
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/* Operational parameter that usually are not changed. */
+
+/* Do you want to use 32 bit xfers? This should work on all chips,
+ but could cause trouble with some PCMCIA controllers... */
+#define USE_32_BIT 1
+
+/* Time in jiffies before concluding Tx hung */
+#define TX_TIMEOUT ((400*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK 4
+
+/* Times to check the check the chip before concluding that it doesn't
+ currently have room for another Tx packet. */
+#define MEMORY_WAIT_TIME 8
+
+/* Values that should be specific lengths */
+typedef unsigned short uint16;
+
+static dev_info_t dev_info = "smc91c92_cs";
+
+static dev_link_t *dev_list = NULL;
+
+struct smc_private {
+ u_short manfid;
+ u_short cardid;
+ struct net_device_stats stats;
+ dev_node_t node;
+ struct sk_buff *saved_skb;
+ int packets_waiting;
+ caddr_t base;
+ u_short cfg;
+ struct timer_list media;
+ int watchdog, tx_err;
+ u_short media_status;
+ u_short fast_poll;
+ u_long last_rx;
+};
+
+/* Special definitions for Megahertz multifunction cards */
+#define MEGAHERTZ_ISR 0x0380
+
+/* Special function registers for Motorola Mariner */
+#define MOT_LAN 0x0000
+#define MOT_UART 0x0020
+#define MOT_EEPROM 0x20
+
+#define MOT_NORMAL \
+(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA)
+
+/* Special function registers for Ositech cards */
+#define OSITECH_AUI_CTL 0x0c
+#define OSITECH_PWRDOWN 0x0d
+#define OSITECH_RESET 0x0e
+#define OSITECH_ISR 0x0f
+#define OSITECH_AUI_PWR 0x0c
+#define OSITECH_RESET_ISR 0x0e
+
+#define OSI_AUI_PWR 0x40
+#define OSI_LAN_PWRDOWN 0x02
+#define OSI_MODEM_PWRDOWN 0x01
+#define OSI_LAN_RESET 0x02
+#define OSI_MODEM_RESET 0x01
+
+/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */
+#define BANK_SELECT 14 /* Window select register. */
+#define SMC_SELECT_BANK(x) { outw( x, ioaddr + BANK_SELECT); }
+
+/* Bank 0 registers. */
+#define TCR 0 /* transmit control register */
+#define TCR_CLEAR 0 /* do NOTHING */
+#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */
+#define TCR_PAD_EN 0x0080 /* pads short packets to 64 bytes */
+#define TCR_MONCSN 0x0400 /* Monitor Carrier. */
+#define TCR_FDUPLX 0x0800 /* Full duplex mode. */
+#define TCR_NORMAL TCR_ENABLE | TCR_PAD_EN
+
+#define EPH 2 /* Ethernet Protocol Handler report. */
+#define EPH_TX_SUC 0x0001
+#define EPH_SNGLCOL 0x0002
+#define EPH_MULCOL 0x0004
+#define EPH_LTX_MULT 0x0008
+#define EPH_16COL 0x0010
+#define EPH_SQET 0x0020
+#define EPH_LTX_BRD 0x0040
+#define EPH_TX_DEFR 0x0080
+#define EPH_LAT_COL 0x0200
+#define EPH_LOST_CAR 0x0400
+#define EPH_EXC_DEF 0x0800
+#define EPH_CTR_ROL 0x1000
+#define EPH_RX_OVRN 0x2000
+#define EPH_LINK_OK 0x4000
+#define EPH_TX_UNRN 0x8000
+#define MEMINFO 8 /* Memory Information Register */
+#define MEMCFG 10 /* Memory Configuration Register */
+
+/* Bank 1 registers. */
+#define CONFIG 0
+#define CFG_MII_SELECT 0x8000 /* 91C100 only */
+#define CFG_NO_WAIT 0x1000
+#define CFG_FULL_STEP 0x0400
+#define CFG_SET_SQLCH 0x0200
+#define CFG_AUI_SELECT 0x0100
+#define CFG_16BIT 0x0080
+#define CFG_DIS_LINK 0x0040
+#define CFG_STATIC 0x0030
+#define CFG_IRQ_SEL_1 0x0004
+#define CFG_IRQ_SEL_0 0x0002
+#define BASE_ADDR 2
+#define ADDR0 4
+#define GENERAL 10
+#define CONTROL 12
+#define CTL_STORE 0x0001
+#define CTL_RELOAD 0x0002
+#define CTL_EE_SELECT 0x0004
+#define CTL_TE_ENABLE 0x0020
+#define CTL_CR_ENABLE 0x0040
+#define CTL_LE_ENABLE 0x0080
+#define CTL_AUTO_RELEASE 0x0800
+#define CTL_POWERDOWN 0x2000
+
+/* Bank 2 registers. */
+#define MMU_CMD 0
+#define MC_ALLOC 0x20 /* or with number of 256 byte packets */
+#define MC_RESET 0x40
+#define MC_RELEASE 0x80 /* remove and release the current rx packet */
+#define MC_FREEPKT 0xA0 /* Release packet in PNR register */
+#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */
+#define PNR_ARR 2
+#define FIFO_PORTS 4
+#define FP_RXEMPTY 0x8000
+#define POINTER 6
+#define PTR_AUTO_INC 0x0040
+#define PTR_READ 0x2000
+#define PTR_AUTOINC 0x4000
+#define PTR_RCV 0x8000
+#define DATA_1 8
+#define INTERRUPT 12
+#define IM_RCV_INT 0x1
+#define IM_TX_INT 0x2
+#define IM_TX_EMPTY_INT 0x4
+#define IM_ALLOC_INT 0x8
+#define IM_RX_OVRN_INT 0x10
+#define IM_EPH_INT 0x20
+
+#define RCR 4
+enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002,
+ RxEnable = 0x0100, RxStripCRC = 0x0200};
+#define RCR_SOFTRESET 0x8000 /* resets the chip */
+#define RCR_STRIP_CRC 0x200 /* strips CRC */
+#define RCR_ENABLE 0x100 /* IFF this is set, we can recieve packets */
+#define RCR_ALMUL 0x4 /* receive all multicast packets */
+#define RCR_PROMISC 0x2 /* enable promiscuous mode */
+
+/* the normal settings for the RCR register : */
+#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE)
+#define RCR_CLEAR 0x0 /* set it to a base state */
+#define COUNTER 6
+
+/* BANK 3 -- not the same values as in smc9194! */
+#define MULTICAST0 0
+#define MULTICAST2 2
+#define MULTICAST4 4
+#define MULTICAST6 6
+#define REVISION 0x0a
+
+/* Transmit status bits. */
+#define TS_SUCCESS 0x0001
+#define TS_16COL 0x0010
+#define TS_LATCOL 0x0200
+#define TS_LOSTCAR 0x0400
+
+/* Receive status bits. */
+#define RS_ALGNERR 0x8000
+#define RS_BADCRC 0x2000
+#define RS_ODDFRAME 0x1000
+#define RS_TOOLONG 0x0800
+#define RS_TOOSHORT 0x0400
+#define RS_MULTICAST 0x0001
+#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+#define set_bits(v, p) outw(inw(p)|(v), (p))
+#define mask_bits(v, p) outw(inw(p)&(v), (p))
+
+/*====================================================================*/
+
+static dev_link_t *smc91c92_attach(void);
+static void smc91c92_detach(dev_link_t *);
+static void smc91c92_config(dev_link_t *link);
+static void smc91c92_release(u_long arg);
+static int smc91c92_event(event_t event, int priority,
+ event_callback_args_t *args);
+
+static int smc91c92_open(struct net_device *dev);
+static int smc91c92_close(struct net_device *dev);
+static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void smc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void smc_rx(struct net_device *dev);
+static struct net_device_stats *smc91c92_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static int s9k_config(struct net_device *dev, struct ifmap *map);
+static void smc_set_xcvr(struct net_device *dev, int if_port);
+static void smc_reset(struct net_device *dev);
+static void media_check(u_long arg);
+
+/*======================================================================
+
+ This bit of code is used to avoid unregistering network devices
+ at inappropriate times. 2.2 and later kernels are fairly picky
+ about when this can happen.
+
+======================================================================*/
+
+static void flush_stale_links(void)
+{
+ dev_link_t *link, *next;
+ for (link = dev_list; link; link = next) {
+ next = link->next;
+ if (link->state & DEV_STALE_LINK)
+ smc91c92_detach(link);
+ }
+}
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+}
+
+/*====================================================================*/
+
+static int smc91c92_init(struct net_device *dev)
+{
+ DEBUG(0, "%s: smc91c92_init called!\n", dev->name);
+ return 0;
+}
+
+/*======================================================================
+
+ smc91c92_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+======================================================================*/
+
+static dev_link_t *smc91c92_attach(void)
+{
+ client_reg_t client_reg;
+ dev_link_t *link;
+ struct net_device *dev;
+ int i, ret;
+
+ DEBUG(0, "smc91c92_attach()\n");
+ flush_stale_links();
+
+ /* Create new ethernet device */
+ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+ memset(link, 0, sizeof(struct dev_link_t));
+ link->release.function = &smc91c92_release;
+ link->release.data = (u_long)link;
+ link->io.NumPorts1 = 16;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 4;
+ 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 = &smc_interrupt;
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+ memset(dev, 0, sizeof(struct net_device));
+
+ /* Make up a SMC91-specific-data structure. */
+ dev->priv = kmalloc(sizeof(struct smc_private), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct smc_private));
+
+ /* The SMC91c92-specific entries in the device structure. */
+ dev->hard_start_xmit = &smc_start_xmit;
+ dev->get_stats = &smc91c92_get_stats;
+ dev->set_config = &s9k_config;
+ dev->set_multicast_list = &set_rx_mode;
+ ether_setup(dev);
+ dev->name = ((struct smc_private *)dev->priv)->node.dev_name;
+ dev->init = &smc91c92_init;
+ dev->open = &smc91c92_open;
+ dev->stop = &smc91c92_close;
+ dev->tbusy = 1;
+ link->priv = 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 = &smc91c92_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);
+ smc91c92_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* smc91c92_attach */
+
+/*======================================================================
+
+ 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 smc91c92_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+ long flags;
+
+ DEBUG(0, "smc91c92_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+ if (*linkp == NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+ if (link->state & DEV_RELEASE_PENDING) {
+ del_timer(&link->release);
+ link->state &= ~DEV_RELEASE_PENDING;
+ }
+ restore_flags(flags);
+
+ if (link->state & DEV_CONFIG) {
+ smc91c92_release((u_long)link);
+ if (link->state & DEV_STALE_CONFIG) {
+ link->state |= DEV_STALE_LINK;
+ return;
+ }
+ }
+
+ if (link->handle)
+ CardServices(DeregisterClient, link->handle);
+
+ /* Unlink device structure, free bits */
+ *linkp = link->next;
+ if (link->priv) {
+ struct net_device *dev = link->priv;
+ if (link->dev != NULL)
+ unregister_netdev(dev);
+ if (dev->priv)
+ kfree(dev->priv);
+ kfree(link->priv);
+ }
+ kfree(link);
+
+} /* smc91c92_detach */
+
+/*====================================================================*/
+
+static int cvt_ascii_address(struct net_device *dev, char *s)
+{
+ int i, j, da, c;
+
+ if (strlen(s) != 12)
+ return -1;
+ for (i = 0; i < 6; i++) {
+ da = 0;
+ for (j = 0; j < 2; j++) {
+ c = *s++;
+ da <<= 4;
+ da += ((c >= '0') && (c <= '9')) ?
+ (c - '0') : ((c & 0x0f) + 9);
+ }
+ dev->dev_addr[i] = da;
+ }
+ return 0;
+}
+
+/*====================================================================*/
+
+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple,
+ cisparse_t *parse)
+{
+ int i;
+ i = CardServices(fn, handle, tuple);
+ if (i != CS_SUCCESS) return i;
+ i = CardServices(GetTupleData, handle, tuple);
+ if (i != CS_SUCCESS) return i;
+ return CardServices(ParseTuple, handle, tuple, parse);
+}
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
+
+/*======================================================================
+
+ Configuration stuff for Megahertz cards
+
+ mhz_3288_power() is used to power up a 3288's ethernet chip.
+ mhz_mfc_config() handles socket setup for multifunction (1144
+ and 3288) cards. mhz_setup() gets a card's hardware ethernet
+ address.
+
+======================================================================*/
+
+static int mhz_3288_power(dev_link_t *link)
+{
+ struct net_device *dev = link->priv;
+ struct smc_private *lp = dev->priv;
+ u_char tmp;
+
+ /* Read the ISR twice... */
+ readb(lp->base+MEGAHERTZ_ISR);
+ udelay(5);
+ readb(lp->base+MEGAHERTZ_ISR);
+
+ /* Pause 200ms... */
+ mdelay(200);
+
+ /* Now read and write the COR... */
+ tmp = readb(lp->base + link->conf.ConfigBase + CISREG_COR);
+ udelay(5);
+ writeb(tmp, lp->base + link->conf.ConfigBase + CISREG_COR);
+
+ return 0;
+}
+
+static int mhz_mfc_config(dev_link_t *link)
+{
+ struct net_device *dev = link->priv;
+ struct smc_private *lp = dev->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ u_char buf[255];
+ cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+ win_req_t req;
+ memreq_t mem;
+ int i, k;
+
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ link->irq.Attributes =
+ IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT;
+ link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+ link->io.NumPorts2 = 8;
+
+ tuple.Attributes = tuple.TupleOffset = 0;
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+
+ i = first_tuple(link->handle, &tuple, &parse);
+ /* The Megahertz combo cards have modem-like CIS entries, so
+ we have to explicitly try a bunch of port combinations. */
+ while (i == CS_SUCCESS) {
+ link->conf.ConfigIndex = cf->index;
+ link->io.BasePort2 = cf->io.win[0].base;
+ for (k = 0; k < 0x400; k += 0x10) {
+ if (k & 0x80) continue;
+ link->io.BasePort1 = k ^ 0x300;
+ i = CardServices(RequestIO, link->handle, &link->io);
+ if (i == CS_SUCCESS) break;
+ }
+ if (i == CS_SUCCESS) break;
+ i = next_tuple(link->handle, &tuple, &parse);
+ }
+ if (i != CS_SUCCESS)
+ return i;
+ dev->base_addr = link->io.BasePort1;
+
+ /* Allocate a memory window, for accessing the ISR */
+ req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+ req.Base = 0;
+ req.Size = 0x1000;
+ req.AccessSpeed = 0;
+ link->win = (window_handle_t)link->handle;
+ i = CardServices(RequestWindow, &link->win, &req);
+ if (i != CS_SUCCESS)
+ return i;
+ lp->base = ioremap(req.Base, 0x1000);
+ mem.CardOffset = mem.Page = 0;
+ if (lp->manfid == MANFID_MOTOROLA)
+ mem.CardOffset = link->conf.ConfigBase;
+ i = CardServices(MapMemPage, link->win, &mem);
+
+ if ((i == CS_SUCCESS)
+ && (lp->manfid == MANFID_MEGAHERTZ)
+ && (lp->cardid == PRODID_MEGAHERTZ_EM3288))
+ mhz_3288_power(link);
+
+ return i;
+}
+
+static int mhz_setup(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ struct net_device *dev = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ u_char buf[255], *station_addr;
+
+ tuple.Attributes = tuple.TupleOffset = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+
+ /* Read the station address from the CIS. It is stored as the last
+ (fourth) string in the Version 1 Version/ID tuple. */
+ tuple.DesiredTuple = CISTPL_VERS_1;
+ if (first_tuple(handle, &tuple, &parse) != CS_SUCCESS)
+ return -1;
+ /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
+ if (next_tuple(handle, &tuple, &parse) != CS_SUCCESS)
+ first_tuple(handle, &tuple, &parse);
+ if (parse.version_1.ns > 3) {
+ station_addr = parse.version_1.str + parse.version_1.ofs[3];
+ if (cvt_ascii_address(dev, station_addr) == 0)
+ return 0;
+ }
+
+ /* Another possibility: for the EM3288, in a special tuple */
+ tuple.DesiredTuple = 0x81;
+ if (CardServices(GetFirstTuple, handle, &tuple) != CS_SUCCESS)
+ return -1;
+ if (CardServices(GetTupleData, handle, &tuple) != CS_SUCCESS)
+ return -1;
+ buf[12] = '\0';
+ if (cvt_ascii_address(dev, buf) == 0)
+ return 0;
+
+ return -1;
+}
+
+/*======================================================================
+
+ Configuration stuff for the Motorola Mariner
+
+ mot_config() writes directly to the Mariner configuration
+ registers because the CIS is just bogus.
+
+======================================================================*/
+
+static void mot_config(dev_link_t *link)
+{
+ struct net_device *dev = link->priv;
+ struct smc_private *lp = dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ ioaddr_t iouart = link->io.BasePort2;
+
+ /* Set UART base address and force map with COR bit 1 */
+ writeb(iouart & 0xff, lp->base + MOT_UART + CISREG_IOBASE_0);
+ writeb((iouart >> 8) & 0xff, lp->base + MOT_UART + CISREG_IOBASE_1);
+ writeb(MOT_NORMAL, lp->base + MOT_UART + CISREG_COR);
+
+ /* Set SMC base address and force map with COR bit 1 */
+ writeb(ioaddr & 0xff, lp->base + MOT_LAN + CISREG_IOBASE_0);
+ writeb((ioaddr >> 8) & 0xff, lp->base + MOT_LAN + CISREG_IOBASE_1);
+ writeb(MOT_NORMAL, lp->base + MOT_LAN + CISREG_COR);
+
+ /* Wait for things to settle down */
+ mdelay(100);
+}
+
+static int mot_setup(dev_link_t *link) {
+ struct net_device *dev = link->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ int i, wait, loop;
+ unsigned int addr;
+
+ /* Read Ethernet address from Serial EEPROM */
+
+ for (i = 0; i < 3; i++) {
+ SMC_SELECT_BANK( 2 );
+ outw(MOT_EEPROM + i, ioaddr + POINTER);
+ SMC_SELECT_BANK( 1 );
+ outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL);
+
+ for (loop = 0; loop < 200; loop++) {
+ udelay(10);
+ wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL));
+ if (wait == 0) break;
+ }
+
+ if (wait)
+ return -1;
+
+ addr = inw(ioaddr + GENERAL);
+ dev->dev_addr[2*i] = addr & 0xff;
+ dev->dev_addr[2*i+1] = (addr >> 8) & 0xff;
+ }
+
+ return 0;
+}
+
+/*====================================================================*/
+
+static int smc_config(dev_link_t *link)
+{
+ struct net_device *dev = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ u_char buf[255];
+ cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+ int i;
+
+ tuple.Attributes = tuple.TupleOffset = 0;
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+
+ link->io.NumPorts1 = 16;
+ i = first_tuple(link->handle, &tuple, &parse);
+ while (i != CS_NO_MORE_ITEMS) {
+ if (i == CS_SUCCESS) {
+ link->conf.ConfigIndex = cf->index;
+ link->io.BasePort1 = cf->io.win[0].base;
+ i = CardServices(RequestIO, link->handle, &link->io);
+ if (i == CS_SUCCESS) break;
+ }
+ i = next_tuple(link->handle, &tuple, &parse);
+ }
+ if (i == CS_SUCCESS)
+ dev->base_addr = link->io.BasePort1;
+ return i;
+}
+
+static int smc_setup(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ struct net_device *dev = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ cistpl_lan_node_id_t *node_id;
+ u_char buf[255], *station_addr;
+ int i;
+
+ tuple.Attributes = tuple.TupleOffset = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+
+ /* Check for a LAN function extension tuple */
+ tuple.DesiredTuple = CISTPL_FUNCE;
+ i = first_tuple(handle, &tuple, &parse);
+ while (i == CS_SUCCESS) {
+ if (parse.funce.type == CISTPL_FUNCE_LAN_NODE_ID)
+ break;
+ i = next_tuple(handle, &tuple, &parse);
+ }
+ if (i == CS_SUCCESS) {
+ node_id = (cistpl_lan_node_id_t *)parse.funce.data;
+ if (node_id->nb == 6) {
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = node_id->id[i];
+ return 0;
+ }
+ }
+
+ /* Try the third string in the Version 1 Version/ID tuple. */
+ tuple.DesiredTuple = CISTPL_VERS_1;
+ if (first_tuple(handle, &tuple, &parse) != CS_SUCCESS)
+ return -1;
+ station_addr = parse.version_1.str + parse.version_1.ofs[2];
+ if (cvt_ascii_address(dev, station_addr) == 0)
+ return 0;
+
+ return -1;
+}
+
+/*====================================================================*/
+
+static int osi_config(dev_link_t *link)
+{
+ struct net_device *dev = link->priv;
+ static ioaddr_t com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+ int i, j;
+
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ link->irq.Attributes =
+ IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT;
+ link->io.NumPorts1 = 64;
+ link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+ link->io.NumPorts2 = 8;
+
+ /* Enable Hard Decode, LAN, Modem */
+ link->conf.ConfigIndex = 0x23;
+
+ for (j = 0; j < 4; j++) {
+ link->io.BasePort2 = com[j];
+ i = CardServices(RequestIO, link->handle, &link->io);
+ if (i == CS_SUCCESS) break;
+ }
+ if (i != CS_SUCCESS) {
+ /* Fallback: turn off hard decode */
+ link->conf.ConfigIndex = 0x03;
+ link->io.NumPorts2 = 0;
+ i = CardServices(RequestIO, link->handle, &link->io);
+ }
+ dev->base_addr = link->io.BasePort1 + 0x10;
+ return i;
+}
+
+static int osi_setup(dev_link_t *link, u_short manfid, u_short cardid)
+{
+ client_handle_t handle = link->handle;
+ struct net_device *dev = link->priv;
+ tuple_t tuple;
+ u_char buf[255];
+ int i;
+
+ tuple.Attributes = TUPLE_RETURN_COMMON;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+
+ /* Read the station address from tuple 0x90, subtuple 0x04 */
+ tuple.DesiredTuple = 0x90;
+ i = CardServices(GetFirstTuple, handle, &tuple);
+ while (i == CS_SUCCESS) {
+ i = CardServices(GetTupleData, handle, &tuple);
+ if ((i != CS_SUCCESS) || (buf[0] == 0x04))
+ break;
+ i = CardServices(GetNextTuple, handle, &tuple);
+ }
+ if (i != CS_SUCCESS)
+ return -1;
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = buf[i+2];
+
+ if (manfid != MANFID_OSITECH) return 0;
+
+ if (cardid == PRODID_OSITECH_SEVEN) {
+ /* Download the Seven of Diamonds firmware */
+ for (i = 0; i < sizeof(__Xilinx7OD); i++) {
+ outb(__Xilinx7OD[i], link->io.BasePort1+2);
+ udelay(50);
+ }
+ } else {
+ /* Make sure both functions are powered up */
+ set_bits(0x300, link->io.BasePort1 + OSITECH_AUI_PWR);
+ /* Now, turn on the interrupt for both card functions */
+ set_bits(0x300, link->io.BasePort1 + OSITECH_RESET_ISR);
+ DEBUG(2, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n",
+ inw(link->io.BasePort1 + OSITECH_AUI_PWR),
+ inw(link->io.BasePort1 + OSITECH_RESET_ISR));
+ }
+
+ return 0;
+}
+
+/*======================================================================
+
+ This verifies that the chip is some SMC91cXX variant, and returns
+ the revision code if successful. Otherwise, it returns -ENODEV.
+
+======================================================================*/
+
+static int check_sig(dev_link_t *link)
+{
+ struct net_device *dev = link->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ int width;
+ u_short s;
+
+ SMC_SELECT_BANK(1);
+ if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) {
+ /* Try powering up the chip */
+ outw(0, ioaddr + CONTROL);
+ mdelay(55);
+ }
+
+ /* Try setting bus width */
+ width = (link->io.Attributes1 == IO_DATA_PATH_WIDTH_AUTO);
+ s = inb(ioaddr + CONFIG);
+ if (width)
+ s |= CFG_16BIT;
+ else
+ s &= ~CFG_16BIT;
+ outb(s, ioaddr + CONFIG);
+
+ /* Check Base Address Register to make sure bus width is OK */
+ s = inw(ioaddr + BASE_ADDR);
+ if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) &&
+ ((s >> 8) != (s & 0xff))) {
+ SMC_SELECT_BANK(3);
+ s = inw(ioaddr + REVISION);
+ return (s & 0xff);
+ }
+
+ if (width) {
+ event_callback_args_t args;
+ printk(KERN_INFO "smc91c92_cs: using 8-bit IO window.\n");
+ args.client_data = link;
+ smc91c92_event(CS_EVENT_RESET_PHYSICAL, 0, &args);
+ CardServices(ReleaseIO, link->handle, &link->io);
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ CardServices(RequestIO, link->handle, &link->io);
+ smc91c92_event(CS_EVENT_CARD_RESET, 0, &args);
+ return check_sig(link);
+ }
+ return -ENODEV;
+}
+
+/*======================================================================
+
+ smc91c92_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ ethernet device available to the system.
+
+======================================================================*/
+
+#define CS_EXIT_TEST(ret, svc, label) \
+if (ret != CS_SUCCESS) { cs_error(link->handle, svc, ret); goto label; }
+
+static void smc91c92_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ struct net_device *dev = link->priv;
+ struct smc_private *lp = dev->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ u_short buf[32];
+ char *name;
+ int i, rev;
+
+ DEBUG(0, "smc91c92_config(0x%p)\n", link);
+
+ tuple.Attributes = tuple.TupleOffset = 0;
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleDataMax = sizeof(buf);
+
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ i = first_tuple(handle, &tuple, &parse);
+ CS_EXIT_TEST(i, ParseTuple, config_failed);
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ tuple.DesiredTuple = CISTPL_MANFID;
+ tuple.Attributes = TUPLE_RETURN_COMMON;
+ if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
+ lp->manfid = parse.manfid.manf;
+ lp->cardid = parse.manfid.card;
+ }
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ if (lp->manfid == MANFID_OSITECH) {
+ i = osi_config(link);
+ } else if (lp->manfid == MANFID_MOTOROLA
+ || ((lp->manfid == MANFID_MEGAHERTZ)
+ && ((lp->cardid == PRODID_MEGAHERTZ_VARIOUS)
+ || (lp->cardid == PRODID_MEGAHERTZ_EM3288)))) {
+ i = mhz_mfc_config(link);
+ } else {
+ i = smc_config(link);
+ }
+ CS_EXIT_TEST(i, RequestIO, config_failed);
+
+ i = CardServices(RequestIRQ, link->handle, &link->irq);
+ CS_EXIT_TEST(i, RequestIRQ, config_failed);
+ i = CardServices(RequestConfiguration, link->handle, &link->conf);
+ CS_EXIT_TEST(i, RequestConfiguration, config_failed);
+
+ if (lp->manfid == MANFID_MOTOROLA)
+ mot_config(link);
+
+ dev->irq = link->irq.AssignedIRQ;
+
+ if ((if_port >= 0) && (if_port <= 2))
+ dev->if_port = if_port;
+ else
+ printk(KERN_NOTICE "smc91c92_cs: invalid if_port requested\n");
+ dev->tbusy = 0;
+
+ if (register_netdev(dev) != 0) {
+ printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n");
+ goto config_undo;
+ }
+
+ switch (lp->manfid) {
+ case MANFID_OSITECH:
+ case MANFID_PSION:
+ i = osi_setup(link, lp->manfid, lp->cardid); break;
+ case MANFID_SMC:
+ case MANFID_NEW_MEDIA:
+ i = smc_setup(link); break;
+ case 0x128: /* For broken Megahertz cards */
+ case MANFID_MEGAHERTZ:
+ i = mhz_setup(link); break;
+ case MANFID_MOTOROLA:
+ default: /* get the hw address from EEPROM */
+ i = mot_setup(link); break;
+ }
+
+ if (i != 0) {
+ printk(KERN_NOTICE "smc91c92_cs: Unable to find hardware address.\n");
+ link->state &= ~DEV_CONFIG_PENDING;
+ goto config_undo;
+ }
+
+ link->dev = &lp->node;
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ rev = check_sig(link);
+ name = "???";
+ if (rev > 0)
+ switch (rev >> 4) {
+ case 3: name = "92"; break;
+ case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break;
+ case 5: name = "95"; break;
+ case 7: name = "100"; break;
+ case 8: name = "100-FD"; break;
+ }
+ printk(KERN_INFO "%s: smc91c%s rev %d: io %#3lx, irq %d, "
+ "hw_addr ", dev->name, name, (rev & 0x0f), dev->base_addr,
+ dev->irq);
+ for (i = 0; i < 6; i++)
+ printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+ if (rev > 0) {
+ u_long mir, mcr, mii;
+ ioaddr_t ioaddr = dev->base_addr;
+ SMC_SELECT_BANK(0);
+ mir = inw(ioaddr + MEMINFO) & 0xff;
+ if (mir == 0xff) mir++;
+ /* Get scale factor for memory size */
+ mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200;
+ mir *= 128 * (1<<((mcr >> 9) & 7));
+ if (mir & 0x3ff)
+ printk(KERN_INFO " %lu byte", mir);
+ else
+ printk(KERN_INFO " %lu kb", mir>>10);
+ SMC_SELECT_BANK(1);
+ mii = inw(ioaddr + CONFIG) & CFG_MII_SELECT;
+ printk(" buffer, %s xcvr\n", mii ? "MII" : if_names[dev->if_port]);
+ }
+
+ return;
+
+config_undo:
+ unregister_netdev(dev);
+config_failed: /* CS_EXIT_TEST() calls jump to here... */
+ smc91c92_release((u_long)link);
+
+} /* smc91c92_config */
+
+/*======================================================================
+
+ After a card is removed, smc91c92_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 smc91c92_release(u_long arg)
+{
+ dev_link_t *link = (dev_link_t *)arg;
+ struct net_device *dev = link->priv;
+
+ DEBUG(0, "smc91c92_release(0x%p)\n", link);
+
+ if (link->open) {
+ DEBUG(1, "smc91c92_cs: release postponed, '%s' still open\n",
+ dev->name);
+ link->state |= DEV_STALE_CONFIG;
+ return;
+ }
+
+ CardServices(ReleaseConfiguration, link->handle);
+ CardServices(ReleaseIO, link->handle, &link->io);
+ CardServices(ReleaseIRQ, link->handle, &link->irq);
+ if (link->win) {
+ struct smc_private *lp = dev->priv;
+ iounmap(lp->base);
+ CardServices(ReleaseWindow, link->win);
+ }
+
+ link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
+
+} /* smc91c92_release */
+
+/*======================================================================
+
+ 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 smc91c92_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ struct net_device *dev = link->priv;
+
+ DEBUG(1, "smc91c92_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ dev->tbusy = 1;
+ dev->start = 0;
+ link->release.expires = jiffies + HZ/20;
+ link->state |= DEV_RELEASE_PENDING;
+ add_timer(&link->release);
+ }
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT;
+ smc91c92_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) {
+ dev->tbusy = 1; dev->start = 0;
+ }
+ 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) {
+ struct smc_private *lp = dev->priv;
+ if ((lp->manfid == MANFID_MEGAHERTZ) &&
+ (lp->cardid == PRODID_MEGAHERTZ_EM3288))
+ mhz_3288_power(link);
+ CardServices(RequestConfiguration, link->handle, &link->conf);
+ if (lp->manfid == MANFID_MOTOROLA)
+ mot_config(link);
+ if ((lp->manfid == MANFID_OSITECH) &&
+ (lp->cardid != PRODID_OSITECH_SEVEN)) {
+ /* Power up the card and enable interrupts */
+ set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
+ set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
+ }
+ if (link->open) {
+ smc_reset(dev);
+ dev->tbusy = 0; dev->start = 1;
+ }
+ }
+ break;
+ }
+ return 0;
+} /* smc91c92_event */
+
+/*======================================================================
+
+ The driver core code, most of which should be common with a
+ non-PCMCIA implementation.
+
+======================================================================*/
+
+#ifdef PCMCIA_DEBUG
+static void smc_dump(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ u_short i, w, save;
+ save = inw(ioaddr + BANK_SELECT);
+ for (w = 0; w < 4; w++) {
+ SMC_SELECT_BANK(w);
+ printk(KERN_DEBUG "bank %d: ", w);
+ for (i = 0; i < 14; i += 2)
+ printk(" %04x", inw(ioaddr + i));
+ printk("\n");
+ }
+ outw(save, ioaddr + BANK_SELECT);
+}
+#endif
+
+static int smc91c92_open(struct net_device *dev)
+{
+ struct smc_private *lp = (struct smc_private *)dev->priv;
+ dev_link_t *link;
+
+#ifdef PCMCIA_DEBUG
+ DEBUG(0, "%s: smc91c92_open(%p), ID/Window %4.4x.\n",
+ dev->name, dev, inw(dev->base_addr + BANK_SELECT));
+ if (pc_debug > 1) smc_dump(dev);
+#endif
+
+ /* Check that the PCMCIA card is still here. */
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+ /* Physical device present signature. */
+ if (!DEV_OK(link))
+ return -ENODEV;
+ if (check_sig(link) < 0) {
+ printk("smc91c92_cs: Yikes! Bad chip signature!\n");
+ return -ENODEV;
+ }
+ link->open++;
+ MOD_INC_USE_COUNT;
+
+ dev->interrupt = 0; dev->tbusy = 0; dev->start = 1;
+ lp->saved_skb = 0;
+ lp->packets_waiting = 0;
+
+ smc_reset(dev);
+ lp->media.function = &media_check;
+ lp->media.data = (u_long)dev;
+ lp->media.expires = jiffies + HZ;
+ add_timer(&lp->media);
+
+ return 0;
+} /* smc91c92_open */
+
+/*====================================================================*/
+
+static int smc91c92_close(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ dev_link_t *link;
+
+ DEBUG(0, "%s: smc91c92_close(), status %4.4x.\n",
+ dev->name, inw(ioaddr + BANK_SELECT));
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Shut off all interrupts, and turn off the Tx and Rx sections.
+ Don't bother to check for chip present. */
+ SMC_SELECT_BANK( 2 ); /* Nominally paranoia, but do no assume... */
+ outw(0, ioaddr + INTERRUPT);
+ SMC_SELECT_BANK( 0 );
+ mask_bits(0xff00, ioaddr + RCR);
+ mask_bits(0xff00, ioaddr + TCR);
+
+ /* Put the chip into power-down mode. */
+ SMC_SELECT_BANK( 1 );
+ outw(CTL_POWERDOWN, ioaddr + CONTROL );
+
+ for (link = dev_list; link; link = link->next)
+ if (link->priv == dev) break;
+ if (link == NULL)
+ return -ENODEV;
+
+ link->open--; dev->start = 0;
+ del_timer(&((struct smc_private *)dev->priv)->media);
+ if (link->state & DEV_STALE_CONFIG) {
+ link->release.expires = jiffies + HZ/20;
+ link->state |= DEV_RELEASE_PENDING;
+ add_timer(&link->release);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+} /* smc91c92_close */
+
+/*======================================================================
+
+ Transfer a packet to the hardware and trigger the packet send.
+ This may be called at either from either the Tx queue code
+ or the interrupt handler.
+
+======================================================================*/
+
+static void smc_hardware_send_packet( struct net_device * dev )
+{
+ struct smc_private *lp = (struct smc_private *)dev->priv;
+ struct sk_buff *skb = lp->saved_skb;
+ ioaddr_t ioaddr = dev->base_addr;
+ unsigned char packet_no;
+
+ if ( !skb ) {
+ printk(KERN_ERR "%s: In XMIT with no packet to send.\n", dev->name);
+ return;
+ }
+
+ /* There should be a packet slot waiting. */
+ packet_no = inw(ioaddr + PNR_ARR) >> 8;
+ if ( packet_no & 0x80 ) {
+ /* If not, there is a hardware problem! Likely an ejected card. */
+ printk(KERN_WARNING "%s: 91c92 hardware Tx buffer allocation"
+ " failed, status %#2.2x.\n", dev->name, packet_no);
+ dev_kfree_skb (skb);
+ lp->saved_skb = NULL;
+ dev->tbusy = 0;
+ return;
+ }
+
+ lp->stats.tx_bytes += skb->len;
+ /* The card should use the just-allocated buffer. */
+ outw( packet_no, ioaddr + PNR_ARR );
+ /* point to the beginning of the packet */
+ outw( PTR_AUTOINC , ioaddr + POINTER );
+
+ /* Send the packet length ( +6 for status, length and ctl byte )
+ and the status word ( set to zeros ). */
+ {
+ unsigned char *buf = skb->data;
+ int length = skb->len; /* The chip will pad to ethernet min length. */
+
+ DEBUG(2, "%s: Trying to xmit packet of length %d.\n",
+ dev->name, length);
+
+#ifdef USE_32_BIT
+ outl((length+6) << 16, ioaddr + DATA_1);
+ if (length & 0x2) {
+ outsl(ioaddr + DATA_1, buf, length >> 2 );
+ outw( *((uint16 *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1);
+ } else
+ outsl(ioaddr + DATA_1, buf, length >> 2 );
+#else
+ /* send the packet length: +6 for status words, length, and ctl */
+ outw( 0, ioaddr + DATA_1 );
+ outw(length + 6, ioaddr + DATA_1 );
+ outsw(ioaddr + DATA_1 , buf, length >> 1);
+#endif
+
+ /* The odd last byte, if there is one, goes in the control word. */
+ outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1 );
+ }
+
+ /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */
+ outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) |
+ (inw(ioaddr + INTERRUPT) & 0xff00),
+ ioaddr + INTERRUPT);
+
+ /* The chip does the rest of the work. */
+ outw( MC_ENQUEUE , ioaddr + MMU_CMD );
+
+ lp->saved_skb = NULL;
+ dev_kfree_skb (skb);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ return;
+}
+
+/*====================================================================*/
+
+static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smc_private *lp = (struct smc_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ unsigned short num_pages;
+ short time_out, ir;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < TX_TIMEOUT)
+ return 1;
+ printk(KERN_NOTICE "%s: SMC91c92 transmit timed out, "
+ "Tx_status %2.2x status %4.4x.\n",
+ dev->name, inw(ioaddr)&0xff, inw(ioaddr + 2));
+ lp->stats.tx_errors++;
+ smc_reset(dev);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ lp->saved_skb = NULL;
+ }
+
+ DEBUG(2, "%s: smc91c92_start_xmit(length = %d) called,"
+ " status %4.4x.\n", dev->name, skb->len, inw(ioaddr + 2));
+
+ /* Avoid timer-based retransmission conflicts. */
+ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk(KERN_ERR "%s: transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+
+ if ( lp->saved_skb) {
+ /* THIS SHOULD NEVER HAPPEN. */
+ lp->stats.tx_aborted_errors++;
+ printk(KERN_DEBUG "%s: Internal error -- sent packet while busy.\n",
+ dev->name);
+ return 1;
+ }
+ lp->saved_skb = skb;
+
+ num_pages = skb->len >> 8;
+
+ if (num_pages > 7) {
+ printk(KERN_ERR "%s: Far too big packet error.\n", dev->name);
+ dev_kfree_skb (skb);
+ lp->saved_skb = NULL;
+ lp->stats.tx_dropped++;
+ return 0; /* Do not re-queue this packet. */
+ }
+ /* A packet is now waiting. */
+ lp->packets_waiting++;
+
+ SMC_SELECT_BANK( 2 ); /* Paranoia, we should always be in window 2 */
+
+ /* Allocate the memory; send the packet now if we win. */
+ outw( MC_ALLOC | num_pages, ioaddr + MMU_CMD );
+ for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) {
+ ir = inw(ioaddr+INTERRUPT);
+ if (ir & IM_ALLOC_INT) {
+ /* Acknowledge the interrupt, send the packet. */
+ outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT);
+ smc_hardware_send_packet(dev); /* Send the packet now.. */
+ return 0;
+ }
+ }
+
+ /* Otherwise defer until the Tx-space-allocated interrupt. */
+ DEBUG(2, "%s: memory allocation deferred.\n", dev->name);
+ outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
+
+ return 0;
+}
+
+/*======================================================================
+
+ Handle a Tx anomolous event. Entered while in Window 2.
+
+======================================================================*/
+
+static void smc_tx_err( struct net_device * dev )
+{
+ struct smc_private *lp = (struct smc_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ int saved_packet = inw(ioaddr + PNR_ARR) & 0xff;
+ int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f;
+ int tx_status;
+
+ /* select this as the packet to read from */
+ outw( packet_no, ioaddr + PNR_ARR );
+
+ /* read the first word from this packet */
+ outw( PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER );
+
+ tx_status = inw(ioaddr + DATA_1);
+
+ lp->stats.tx_errors++;
+ if (tx_status & TS_LOSTCAR) lp->stats.tx_carrier_errors++;
+ if (tx_status & TS_LATCOL) lp->stats.tx_window_errors++;
+ if (tx_status & TS_16COL) {
+ lp->stats.tx_aborted_errors++;
+ lp->tx_err++;
+ }
+
+ if ( tx_status & TS_SUCCESS ) {
+ printk(KERN_NOTICE "%s: Successful packet caused error "
+ "interrupt?\n", dev->name);
+ }
+ /* re-enable transmit */
+ SMC_SELECT_BANK( 0 );
+ outw( inw(ioaddr + TCR) | TCR_ENABLE, ioaddr + TCR);
+ SMC_SELECT_BANK( 2 );
+
+ outw( MC_FREEPKT, ioaddr + MMU_CMD ); /* Free the packet memory. */
+
+ /* one less packet waiting for me */
+ lp->packets_waiting--;
+
+ outw( saved_packet, ioaddr + PNR_ARR );
+ return;
+}
+
+/*====================================================================*/
+
+static void smc_eph_irq(struct net_device *dev)
+{
+ struct smc_private *lp = dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ unsigned short card_stats, ephs;
+
+ SMC_SELECT_BANK(0);
+ ephs = inw(ioaddr + EPH);
+ DEBUG(2, "%s: Ethernet protocol handler interrupt, status"
+ " %4.4x.\n", dev->name, ephs);
+ /* Could be a counter roll-over warning: update stats. */
+ card_stats = inw( ioaddr + COUNTER );
+ /* single collisions */
+ lp->stats.collisions += card_stats & 0xF;
+ card_stats >>= 4;
+ /* multiple collisions */
+ lp->stats.collisions += card_stats & 0xF;
+#if 0 /* These are for when linux supports these statistics */
+ card_stats >>= 4; /* deferred */
+ card_stats >>= 4; /* excess deferred */
+#endif
+ /* If we had a transmit error we must re-enable the transmitter. */
+ outw( inw(ioaddr + TCR) | TCR_ENABLE, ioaddr + TCR);
+
+ /* Clear a link error interrupt. */
+ SMC_SELECT_BANK(1);
+ outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL);
+ outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
+ ioaddr + CONTROL);
+ SMC_SELECT_BANK(2);
+}
+
+/*====================================================================*/
+
+static void smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ struct smc_private *lp;
+ ioaddr_t ioaddr;
+ u_short saved_bank, saved_pointer, mask, status;
+ char bogus_cnt = INTR_WORK; /* Work we are willing to do. */
+
+ if ((dev == NULL) || !dev->start)
+ return;
+ ioaddr = dev->base_addr;
+
+#ifdef PCMCIA_DEBUG
+ if (dev->interrupt) {
+ printk(KERN_ERR "%s: re-entering the interrupt handler.\n",
+ dev->name);
+ return;
+ }
+ dev->interrupt = 1;
+
+ DEBUG(3, "%s: SMC91c92 interrupt %d at %#x.\n", dev->name,
+ irq, ioaddr);
+#endif
+
+ lp = (struct smc_private *)dev->priv;
+ lp->watchdog = 0;
+ saved_bank = inw(ioaddr + BANK_SELECT);
+ if ((saved_bank & 0xff00) != 0x3300) {
+ /* The device does not exist -- the card could be off-line, or
+ maybe it has been ejected. */
+#ifdef PCMCIA_DEBUG
+ if (dev->start)
+ DEBUG(1, "%s: SMC91c92 interrupt %d for non-existent"
+ "/ejected device.\n", dev->name, irq);
+ dev->interrupt = 0;
+#endif
+ goto irq_done;
+ }
+
+ SMC_SELECT_BANK(2);
+ saved_pointer = inw(ioaddr + POINTER);
+ mask = inw(ioaddr + INTERRUPT) >> 8;
+ /* clear all interrupts */
+ outw( 0, ioaddr + INTERRUPT );
+
+ do { /* read the status flag, and mask it */
+ status = inw(ioaddr + INTERRUPT) & 0xff;
+ DEBUG(3, "%s: Status is %#2.2x (mask %#2.2x).\n", dev->name,
+ status, mask);
+ if ((status & mask) == 0)
+ break;
+
+ if (status & IM_RCV_INT) {
+ /* Got a packet(s). */
+ smc_rx(dev);
+ lp->last_rx = jiffies;
+ }
+ if (status & IM_TX_INT) {
+ smc_tx_err(dev);
+ outw(IM_TX_INT, ioaddr + INTERRUPT);
+ }
+ status &= mask;
+ if (status & IM_TX_EMPTY_INT) {
+ outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT);
+ mask &= ~IM_TX_EMPTY_INT;
+ lp->stats.tx_packets += lp->packets_waiting;
+ lp->packets_waiting = 0;
+ }
+ if (status & IM_ALLOC_INT) {
+ /* Clear this interrupt so it doesn't happen again */
+ mask &= ~IM_ALLOC_INT;
+
+ smc_hardware_send_packet(dev);
+
+ /* enable xmit interrupts based on this */
+ mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );
+
+ /* and let the card send more packets to me */
+ mark_bh( NET_BH );
+ }
+ if (status & IM_RX_OVRN_INT) {
+ lp->stats.rx_errors++;
+ lp->stats.rx_fifo_errors++;
+ outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT);
+ }
+ if (status & IM_EPH_INT)
+ smc_eph_irq(dev);
+ } while ( --bogus_cnt);
+
+ DEBUG(3, " Restoring saved registers mask %2.2x bank %4.4x"
+ " pointer %4.4x.\n", mask, saved_bank, saved_pointer);
+
+ /* restore state register */
+ outw((mask<<8), ioaddr + INTERRUPT);
+ outw(saved_pointer, ioaddr + POINTER);
+ SMC_SELECT_BANK( saved_bank );
+
+#ifdef PCMCIA_DEBUG
+ dev->interrupt = 0;
+ DEBUG(3, "%s: Exiting interrupt IRQ%d.\n", dev->name, irq);
+#endif
+
+irq_done:
+
+ if ((lp->manfid == MANFID_OSITECH) &&
+ (lp->cardid != PRODID_OSITECH_SEVEN)) {
+ /* Retrigger interrupt if needed */
+ mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR);
+ set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR);
+ }
+ if (lp->manfid == MANFID_MOTOROLA) {
+ u_char cor;
+ cor = readb(lp->base + MOT_UART + CISREG_COR);
+ writeb(cor & ~COR_IREQ_ENA, lp->base + MOT_UART + CISREG_COR);
+ writeb(cor, lp->base + MOT_UART + CISREG_COR);
+ cor = readb(lp->base + MOT_LAN + CISREG_COR);
+ writeb(cor & ~COR_IREQ_ENA, lp->base + MOT_LAN + CISREG_COR);
+ writeb(cor, lp->base + MOT_LAN + CISREG_COR);
+ }
+#ifdef DOES_NOT_WORK
+ if (lp->base != NULL) { /* Megahertz MFC's */
+ readb(lp->base+MEGAHERTZ_ISR);
+ readb(lp->base+MEGAHERTZ_ISR);
+ }
+#endif
+}
+
+/*====================================================================*/
+
+static void smc_rx(struct net_device *dev)
+{
+ struct smc_private *lp = (struct smc_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ int rx_status;
+ int packet_length; /* Caution: not frame length, rather words
+ to transfer from the chip. */
+
+ /* Assertion: we are in Window 2. */
+
+ if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) {
+ printk(KERN_ERR "%s: smc_rx() with nothing on Rx FIFO.\n",
+ dev->name);
+ return;
+ }
+
+ /* Reset the read pointer, and read the status and packet length. */
+ outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER );
+ rx_status = inw(ioaddr + DATA_1);
+ packet_length = inw(ioaddr + DATA_1) & 0x07ff;
+
+ DEBUG(2, "%s: Receive status %4.4x length %d.\n",
+ dev->name, rx_status, packet_length);
+
+ if ( !(rx_status & RS_ERRORS )) {
+ /* do stuff to make a new packet */
+ struct sk_buff *skb;
+
+ /* Note: packet_length adds 5 or 6 extra bytes here! */
+ skb = dev_alloc_skb(packet_length+2);
+
+ if ( skb == NULL ) {
+ DEBUG(1, "%s: Low memory, packet dropped.\n", dev->name);
+ lp->stats.rx_dropped++;
+ outw( MC_RELEASE, ioaddr + MMU_CMD );
+ return;
+ }
+
+ packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6);
+ skb_reserve(skb, 2);
+ insw(ioaddr+DATA_1, skb_put(skb, packet_length),
+ (packet_length+1)>>1);
+ skb->protocol = eth_type_trans(skb, dev);
+
+ skb->dev = dev;
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += skb->len;
+ if (rx_status & RS_MULTICAST)
+ lp->stats.multicast++;
+ } else {
+ /* error ... */
+ lp->stats.rx_errors++;
+
+ if (rx_status & RS_ALGNERR) lp->stats.rx_frame_errors++;
+ if (rx_status & (RS_TOOSHORT | RS_TOOLONG))
+ lp->stats.rx_length_errors++;
+ if (rx_status & RS_BADCRC) lp->stats.rx_crc_errors++;
+ }
+ /* Let the MMU free the memory of this packet. */
+ outw(MC_RELEASE, ioaddr + MMU_CMD);
+
+ return;
+}
+
+/*====================================================================*/
+
+static struct net_device_stats *smc91c92_get_stats(struct net_device *dev)
+{
+ struct smc_private *lp = (struct smc_private *)dev->priv;
+ /* Nothing to update - the 91c92 is a pretty primative chip. */
+ return &lp->stats;
+}
+
+/*======================================================================
+
+ Compute the AUTODIN polynomial "CRC32" for ethernet packets.
+
+======================================================================*/
+
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+
+static unsigned ether_crc(int length, unsigned char *data)
+{
+ int crc = 0xffffffff; /* Initial value. */
+
+ while (--length >= 0) {
+ unsigned char current_octet = *data++;
+ int bit;
+ for (bit = 0; bit < 8; bit++, current_octet >>= 1) {
+ crc = (crc << 1) ^
+ ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
+ }
+ }
+ /* The hash index is the either the upper or lower bits of the CRC, so
+ * we return the entire CRC.
+ */
+ return crc;
+}
+
+/*======================================================================
+
+ Calculate values for the hardware multicast filter hash table.
+
+======================================================================*/
+
+static void fill_multicast_tbl(int count, struct dev_mc_list *addrs,
+ unsigned char *multicast_table)
+{
+ struct dev_mc_list *mc_addr;
+
+ for (mc_addr = addrs; mc_addr && --count > 0; mc_addr = mc_addr->next) {
+ unsigned int position = ether_crc(6, mc_addr->dmi_addr);
+#ifndef final_version /* Verify multicast address. */
+ if ( (mc_addr->dmi_addr[0] & 1) == 0)
+ continue;
+#endif
+ multicast_table[position >> 29] |= 1 << ((position >> 26) & 7);
+ }
+}
+
+/*======================================================================
+
+ Set the receive mode.
+
+ This routine is used by both the protocol level to notify us of
+ promiscuous/multicast mode changes, and by the open/reset code to
+ initialize the Rx registers. We always set the multicast list and
+ leave the receiver running.
+
+======================================================================*/
+
+static void set_rx_mode(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ unsigned int multicast_table[ 2 ] = { 0, };
+ long flags;
+ uint16 rx_cfg_setting;
+
+ if (dev->flags & IFF_PROMISC) {
+ printk(KERN_NOTICE "%s: setting Rx mode to promiscuous.\n", dev->name);
+ rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti;
+ } else if (dev->flags & IFF_ALLMULTI)
+ rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti;
+ else {
+ if (dev->mc_count) {
+ fill_multicast_tbl(dev->mc_count, dev->mc_list,
+ (unsigned char *)multicast_table);
+ }
+ rx_cfg_setting = RxStripCRC | RxEnable;
+ }
+
+ /* Load MC table and Rx setting into the chip without interrupts. */
+ save_flags(flags);
+ cli();
+ SMC_SELECT_BANK( 3 );
+ outl(multicast_table[0], ioaddr + MULTICAST0);
+ outl(multicast_table[1], ioaddr + MULTICAST4);
+ SMC_SELECT_BANK(0);
+ outw(rx_cfg_setting, ioaddr + RCR);
+ SMC_SELECT_BANK(2);
+ restore_flags(flags);
+
+ return;
+}
+
+/*======================================================================
+
+ Senses when a card's config changes. Here, it's coax or TP.
+
+======================================================================*/
+
+static int s9k_config(struct net_device *dev, struct ifmap *map)
+{
+ if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+ if (map->port <= 2) {
+ dev->if_port = map->port;
+ printk(KERN_INFO "%s: switched to %s port\n",
+ dev->name, if_names[dev->if_port]);
+ } else
+ return -EINVAL;
+ }
+ smc_reset(dev);
+ return 0;
+}
+
+/*======================================================================
+
+ Reset the chip, reloading every register that might be corrupted.
+
+======================================================================*/
+
+/*
+ Set transceiver type, perhaps to something other than what the user
+ specified in dev->if_port.
+*/
+static void smc_set_xcvr(struct net_device *dev, int if_port)
+{
+ struct smc_private *lp = (struct smc_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ u_short saved_bank;
+
+ saved_bank = inw(ioaddr + BANK_SELECT);
+ SMC_SELECT_BANK(1);
+ if (if_port == 2) {
+ outw(lp->cfg | CFG_AUI_SELECT, ioaddr + CONFIG);
+ if ((lp->manfid == MANFID_OSITECH) &&
+ (lp->cardid != PRODID_OSITECH_SEVEN))
+ set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
+ lp->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002);
+ } else {
+ outw(lp->cfg, ioaddr + CONFIG);
+ if ((lp->manfid == MANFID_OSITECH) &&
+ (lp->cardid != PRODID_OSITECH_SEVEN))
+ mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
+ lp->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001);
+ }
+ SMC_SELECT_BANK(saved_bank);
+}
+
+static void smc_reset(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ struct smc_private *lp = dev->priv;
+ int i;
+
+ DEBUG(0, "%s: smc91c92 reset called.\n", dev->name);
+
+ /* The first interaction must be a write to bring the chip out
+ of sleep mode. */
+ SMC_SELECT_BANK( 0 );
+ /* Reset the chip. */
+ outw( RCR_SOFTRESET, ioaddr + RCR );
+ udelay(10);
+
+ /* Clear the transmit and receive configuration registers. */
+ outw( RCR_CLEAR, ioaddr + RCR );
+ outw( TCR_CLEAR, ioaddr + TCR );
+
+ /* Set the Window 1 control, configuration and station addr registers.
+ No point in writing the I/O base register ;-> */
+ SMC_SELECT_BANK(1);
+ /* Automatically release succesfully transmitted packets,
+ Accept link errors, counter and Tx error interrupts. */
+ outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
+ ioaddr + CONTROL);
+ lp->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT;
+ lp->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC |
+ (lp->manfid == MANFID_OSITECH ? (CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0) : 0);
+ smc_set_xcvr(dev, dev->if_port);
+ if ((lp->manfid == MANFID_OSITECH) &&
+ (lp->cardid != PRODID_OSITECH_SEVEN))
+ outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) |
+ (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00),
+ ioaddr - 0x10 + OSITECH_AUI_PWR);
+
+ /* Fill in the physical address. The databook is wrong about the order! */
+ for (i = 0; i < 6; i += 2)
+ outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i],
+ ioaddr + ADDR0 + i);
+
+ /* Reset the MMU */
+ SMC_SELECT_BANK(2);
+ outw(MC_RESET, ioaddr + MMU_CMD );
+ outw(0, ioaddr + INTERRUPT );
+
+ /* Re-enable the chip. */
+ SMC_SELECT_BANK(0);
+ outw(((lp->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) |
+ TCR_ENABLE | TCR_PAD_EN, ioaddr + TCR);
+ set_rx_mode(dev);
+
+ /* Enable interrupts. */
+ SMC_SELECT_BANK( 2 );
+ outw( (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,
+ ioaddr + INTERRUPT );
+}
+
+/*======================================================================
+
+ Media selection timer routine
+
+======================================================================*/
+
+static void media_check(u_long arg)
+{
+ struct net_device *dev = (struct net_device *)(arg);
+ struct smc_private *lp = (struct smc_private *)dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ u_short i, media, saved_bank;
+
+ if (dev->start == 0) goto reschedule;
+
+ lp = (struct smc_private *)dev->priv;
+ saved_bank = inw(ioaddr + BANK_SELECT);
+ SMC_SELECT_BANK(2);
+ i = inw(ioaddr + INTERRUPT);
+ SMC_SELECT_BANK(0);
+ media = inw(ioaddr + EPH) & EPH_LINK_OK;
+ SMC_SELECT_BANK(1);
+ media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;
+ SMC_SELECT_BANK(saved_bank);
+
+ /* Check for pending interrupt with watchdog flag set: with
+ this, we can limp along even if the interrupt is blocked */
+ if (lp->watchdog++ && ((i>>8) & i)) {
+ if (!lp->fast_poll)
+ printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);
+ smc_interrupt(dev->irq, dev, NULL);
+ lp->fast_poll = HZ;
+ }
+ if (lp->fast_poll) {
+ lp->fast_poll--;
+ lp->media.expires = jiffies + 1;
+ add_timer(&lp->media);
+ return;
+ }
+
+ if (lp->cfg & CFG_MII_SELECT)
+ goto reschedule;
+
+ /* Ignore collisions unless we've had no rx's recently */
+ if (jiffies - lp->last_rx > HZ) {
+ if (lp->tx_err || (lp->media_status & EPH_16COL))
+ media |= EPH_16COL;
+ }
+ lp->tx_err = 0;
+
+ if (media != lp->media_status) {
+ if ((media & lp->media_status & 1) &&
+ ((lp->media_status ^ media) & EPH_LINK_OK))
+ printk(KERN_INFO "%s: %s link beat\n", dev->name,
+ (lp->media_status & EPH_LINK_OK ? "lost" : "found"));
+ else if ((media & lp->media_status & 2) &&
+ ((lp->media_status ^ media) & EPH_16COL))
+ printk(KERN_INFO "%s: coax cable %s\n", dev->name,
+ (media & EPH_16COL ? "problem" : "ok"));
+ if (dev->if_port == 0) {
+ if (media & 1) {
+ if (media & EPH_LINK_OK)
+ printk(KERN_INFO "%s: flipped to 10baseT\n",
+ dev->name);
+ else
+ smc_set_xcvr(dev, 2);
+ } else {
+ if (media & EPH_16COL)
+ smc_set_xcvr(dev, 1);
+ else
+ printk(KERN_INFO "%s: flipped to 10base2\n",
+ dev->name);
+ }
+ }
+ lp->media_status = media;
+ }
+
+reschedule:
+ lp->media.expires = jiffies + HZ;
+ add_timer(&lp->media);
+}
+
+/*====================================================================*/
+
+static int __init init_smc91c92_cs(void)
+{
+ servinfo_t serv;
+ DEBUG(0, "%s\n", version);
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_ERR
+ "smc91c92_cs: Card Services release does not match!\n");
+ return -1;
+ }
+ register_pccard_driver(&dev_info, &smc91c92_attach, &smc91c92_detach);
+ return 0;
+}
+
+static void __exit exit_smc91c92_cs(void)
+{
+ DEBUG(0, "smc91c92_cs: unloading\n");
+ unregister_pccard_driver(&dev_info);
+ while (dev_list != NULL)
+ smc91c92_detach(dev_list);
+}
+
+module_init(init_smc91c92_cs);
+module_exit(exit_smc91c92_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 controler (see definitions in
+ * file i82593.h).
+ *
+ * The main difference between the pcmcia hardware and the ISA one is
+ * the Ethernet Controler (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.
+ */
+static inline unsigned long
+wv_splhi(void)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ return(flags);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for re-enabling interrupts.
+ */
+static inline void
+wv_splx(unsigned long flags)
+{
+ restore_flags(flags);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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 *******************/
+/*
+ * Usefull 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 */
+ udelay(1000L);
+} /* 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))
+ udelay(1000);
+ }
+
+ /* 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 */
+ udelay(10000);
+ 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 unsuported 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=(int)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 x;
+
+#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 */
+ x = wv_splhi();
+
+ 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);
+
+ /* ReEnable interrupts & restore flags */
+ wv_splx(x);
+
+ if(mode==NWID_PROMISC)
+ lp->cell_search=1;
+ else
+ lp->cell_search=0;
+}
+
+/* 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 x;
+
+ 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 */
+ x = wv_splhi();
+
+ 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(x);
+
+ 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 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 *************************/
+/*
+ * Usefull subroutines to manage the Ethernet controler
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to synchronously send a command to the i82593 chip.
+ * Should be called with interrupts enabled.
+ */
+static int
+wv_82593_cmd(device * dev,
+ char * str,
+ int cmd,
+ int result)
+{
+ ioaddr_t base = dev->base_addr;
+ net_local * lp = (net_local *)dev->priv;
+ int status;
+ long spin;
+ u_long opri;
+
+ /* Spin until the chip finishes executing its current command (if any) */
+ do
+ {
+ opri = wv_splhi();
+ outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+ status = inb(LCSR(base));
+ wv_splx(opri);
+ }
+ while((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE);
+
+ /* We are waiting for command completion */
+ wv_wait_completed = TRUE;
+
+ /* Issue the command to the controler */
+ outb(cmd, LCCR(base));
+
+ /* If we don't have to check the result of the command */
+ if(result == SR0_NO_RESULT)
+ {
+ wv_wait_completed = FALSE;
+ return(TRUE);
+ }
+
+ /* Busy wait while the LAN controller executes the command.
+ * Note : wv_wait_completed should be volatile */
+ spin = 0;
+ while(wv_wait_completed && (spin++ < 1000))
+ udelay(10);
+
+ /* If the interrupt handler hasn't be called */
+ if(wv_wait_completed)
+ {
+ outb(OP0_NOP, LCCR(base));
+ status = inb(LCSR(base));
+ if(status & SR0_INTERRUPT)
+ {
+ /* There was an interrupt : call the interrupt handler */
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_WARNING "wv_82593_cmd: interrupt handler not installed or interrupt disabled\n");
+#endif
+
+ wavelan_interrupt(dev->irq, (void *) dev,
+ (struct pt_regs *) NULL);
+ }
+ else
+ {
+ wv_wait_completed = 0; /* XXX */
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO "wv_82593_cmd: %s timeout, status0 0x%02x\n",
+ str, status);
+#endif
+ /* We probably should reset the controller here */
+ return(FALSE);
+ }
+ }
+
+ /* Check the return code provided by the interrupt handler against
+ * the expected return code provided by the caller */
+ if((lp->status & SR0_EVENT_MASK) != result)
+ {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO "wv_82593_cmd: %s failed, status0 = 0x%x\n",
+ str, lp->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)
+{
+ if(wv_82593_cmd(dev, "wv_diag(): diagnose",
+ OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED))
+ return(TRUE);
+
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO "wavelan_cs: i82593 Self Test failed!\n");
+#endif
+ return(FALSE);
+} /* 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;
+
+#ifdef OLDIES
+ /* After having check skb_put (net/core/skbuff.c) in the kernel, it seem
+ * quite safe to remove this... */
+
+ /* If buf is NULL, just increment the ring buffer pointer */
+ if(buf == NULL)
+ return((ring_ptr - RX_BASE + len) % RX_SIZE + RX_BASE);
+#endif
+
+ /* 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;
+
+ /* Check if we can do it now ! */
+ if(!(link->open) || (test_and_set_bit(0, (void *)&dev->tbusy) != 0))
+ {
+ lp->reconfig_82593 = TRUE;
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG "%s: wv_82593_reconfig(): delayed (busy = %ld, link = %d)\n",
+ dev->name, dev->tbusy, link->open);
+#endif
+ }
+ else
+ {
+ lp->reconfig_82593 = FALSE;
+ wv_82593_config(dev);
+ dev->tbusy = 0;
+ }
+}
+
+#ifdef OLDIES
+/*------------------------------------------------------------------*/
+/*
+ * Dumps the current i82593 receive buffer to the console.
+ */
+static void wavelan_dump(device *dev)
+{
+ ioaddr_t base = dev->base_addr;
+ int i, c;
+
+ /* disable receiver so we can use channel 1 */
+ outb(OP0_RCV_DISABLE, LCCR(base));
+
+ /* reset receive DMA pointer */
+ hacr_write_slow(base, HACR_PWR_STAT | HACR_RX_DMA_RESET);
+ hacr_write(base, HACR_DEFAULT);
+
+ /* dump into receive buffer */
+ wv_82593_cmd(dev, "wavelan_dump(): dump", CR0_CHNL|OP0_DUMP, SR0_DUMP_DONE);
+
+ /* set read pointer to start of receive buffer */
+ outb(0, PIORL(base));
+ outb(0, PIORH(base));
+
+ printk(KERN_DEBUG "wavelan_cs: dump:\n");
+ printk(KERN_DEBUG " 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
+ for(i = 0; i < 73; i++){
+ if((i % 16) == 0) {
+ printk("\n0x%02x:", i);
+ if (!i) {
+ printk(" ");
+ continue;
+ }
+ }
+ c = inb(PIOP(base));
+ printk("%02x ", c);
+ }
+ printk("\n");
+
+ /* enable the receiver again */
+ wv_ru_start(dev);
+}
+#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;
+ }
+
+ /* 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 */
+
+ 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(" start=%d,", dev->start);
+ printk(" tbusy=%ld,", dev->tbusy);
+ printk(" interrupt=%d,", dev->interrupt);
+ 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 x;
+ 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 */
+ x = wv_splhi();
+
+ /* 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(!suser())
+ {
+ 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 (useless : its constant...) */
+ wrq->u.data.length = sizeof(struct iw_range);
+
+ /* 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 > 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(!suser())
+ {
+ 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(!suser())
+ {
+ 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(x);
+
+#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 x;
+
+#ifdef DEBUG_IOCTL_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name);
+#endif
+
+ /* Disable interrupts & save flags */
+ x = wv_splhi();
+
+ if(lp == (net_local *) NULL)
+ return (iw_stats *) NULL;
+ 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(x);
+
+#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
+ /* Another glitch : Due to the way the GET_PACKET macro is written,
+ * we are not sure to have the same thing in skb->data. On the other
+ * hand, skb->mac.raw is not defined everywhere...
+ * For versions between 1.2.13 and those where skb->mac.raw appear,
+ * I don't have a clue...
+ */
+ 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 */
+
+ /* Spying stuff */
+#ifdef WIRELESS_SPY
+ /* Same as above */
+ 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 */
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += skb->len;
+
+#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())
+ */
+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 x;
+ 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
+
+ x = wv_splhi();
+
+ /* 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;
+
+ /* If watchdog not already active, activate it... */
+ if(lp->watchdog.prev == (timer_list *) NULL)
+ {
+ /* set timer to expire in WATCHDOG_JIFFIES */
+ lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+ add_timer(&lp->watchdog);
+ }
+
+ wv_splx(x);
+
+#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 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;
+
+#ifdef DEBUG_TX_TRACE
+ printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
+ (unsigned) skb);
+#endif
+
+ /* 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...
+ */
+ if(dev->tbusy)
+ return(1);
+
+ /*
+ * For ethernet, fill in the header.
+ */
+
+ /*
+ * Block a timer-based transmit from overlapping a previous transmit.
+ * In other words, prevent reentering this routine.
+ */
+ if(test_and_set_bit(0, (void *)&dev->tbusy) != 0)
+#ifdef DEBUG_TX_ERROR
+ printk(KERN_INFO "%s: Transmitter access conflict.\n", dev->name);
+#endif
+ else
+ {
+ /* If somebody has asked to reconfigure the controler, we can do it now */
+ if(lp->reconfig_82593)
+ {
+ lp->reconfig_82593 = FALSE;
+ wv_82593_config(dev);
+ }
+
+#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;
+ unsigned long opri;
+ int status;
+ int spin;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name);
+#endif
+
+ /* 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 = 0;
+ do
+ {
+ udelay(10);
+ opri = wv_splhi();
+ outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+ status = inb(LCSR(base));
+ wv_splx(opri);
+ }
+ while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin++ < 300));
+
+ /* Now, spin until the chip finishes executing its current command */
+ do
+ {
+ udelay(10);
+ opri = wv_splhi();
+ outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+ status = inb(LCSR(base));
+ wv_splx(opri);
+ }
+ while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin++ < 300));
+
+ /* If there was a problem */
+ if(spin > 300)
+ {
+#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;
+
+#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;
+
+ /* 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));
+
+ /* but I set it to 3 bytes per packet less than 8K */
+ lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
+ 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 i = 0;
+
+ /* spin until the chip starts receiving */
+ do
+ {
+ opri = wv_splhi();
+ outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+ status = inb(LCSR(base));
+ wv_splx(opri);
+ if(i++ > 10000)
+ 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
+#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 controler (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;
+
+#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))
+ return(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))
+ return(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))
+ return(FALSE);
+ lp->mc_count = dev->mc_count; /* remember to avoid repeated reset */
+ }
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name);
+#endif
+ return(TRUE);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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;
+
+#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;
+
+ /* 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 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
+ return FALSE;
+ }
+
+ /* initialize the modem */
+ if(wv_mmc_init(dev) == FALSE)
+ return FALSE;
+
+ /* reset the LAN controller (i82593) */
+ outb(OP0_RESET, LCCR(base));
+ udelay(1000L); /* A bit crude ! */
+
+ /* Initialize the LAN controler */
+ if((wv_82593_config(dev) == FALSE) ||
+ (wv_diag(dev) == FALSE))
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n", dev->name);
+#endif
+ return FALSE;
+ }
+
+ /*
+ * insert code for loopback test here
+ */
+
+ /* The device is now configured */
+ lp->configured = 1;
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name);
+#endif
+ return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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
+
+ /* If watchdog was activated, kill it ! */
+ if(lp->watchdog.prev != (timer_list *) NULL)
+ del_timer(&lp->watchdog);
+
+ 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 4K 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 = 0; req.Size = 0x1000;
+ 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, 0x1000);
+ 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;
+ dev->tbusy = 0;
+
+#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;
+ }
+
+ /* ???? Could you explain me this, Dave ? */
+ 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_RELEASE_PENDING | DEV_STALE_CONFIG);
+
+#ifdef DEBUG_CONFIG_TRACE
+ printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name);
+#endif
+} /* wv_pcmcia_release */
+
+/*------------------------------------------------------------------*/
+/*
+ * Sometimes, netwave_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 transfered 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;
+
+ /* Prevent reentrance. What should we do here ? */
+#ifdef DEBUG_INTERRUPT_ERROR
+ if(dev->interrupt)
+ printk(KERN_INFO "%s: wavelan_interrupt(): Re-entering the interrupt handler.\n",
+ dev->name);
+#endif
+ dev->interrupt = 1;
+
+ /* 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;
+ }
+
+ lp->status = status0; /* Save current status (for commands) */
+
+ /* ----------------- 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 we are already waiting elsewhere for the command to complete */
+ if(wv_wait_completed)
+ {
+#ifdef DEBUG_INTERRUPT_INFO
+ printk(KERN_DEBUG "%s: wv_interrupt(): command completed\n",
+ dev->name);
+#endif
+
+ /* Signal command completion */
+ wv_wait_completed = 0;
+
+ /* Acknowledge the interrupt */
+ outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
+ continue;
+ }
+
+ /* 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
+
+ /* If watchdog was activated, kill it ! */
+ if(lp->watchdog.prev != (timer_list *) NULL)
+ del_timer(&lp->watchdog);
+
+ /* 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++;
+
+ dev->tbusy = FALSE;
+ mark_bh(NET_BH);
+ 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 */
+ }
+ }
+ dev->interrupt = FALSE;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
+#endif
+} /* wv_interrupt */
+
+/*------------------------------------------------------------------*/
+/*
+ * Watchdog : when we start a transmission, we set a timer in the
+ * kernel. If the transmission complete, this timer is disabled. If
+ * it expire, it try to unlock the hardware.
+ *
+ * Note : this watchdog doesn't work on the same principle as the
+ * watchdog in the ISA driver. I make it this way because the overhead
+ * of add_timer() and del_timer() is nothing and that it avoid calling
+ * the watchdog, saving some CPU... If you want to apply the same
+ * watchdog to the ISA driver, you should be a bit carefull, because
+ * of the many transmit buffers...
+ * This watchdog is also move clever, it try to abort the current
+ * command before reseting everything...
+ */
+static void
+wavelan_watchdog(u_long a)
+{
+ device * dev;
+ net_local * lp;
+ ioaddr_t base;
+ int spin;
+
+ dev = (device *) a;
+ base = dev->base_addr;
+ lp = (net_local *) dev->priv;
+
+#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
+
+ /* We are waiting for command completion */
+ wv_wait_completed = TRUE;
+
+ /* Ask to abort the current command */
+ outb(OP0_ABORT, LCCR(base));
+
+ /* Busy wait while the LAN controller executes the command.
+ * Note : wv_wait_completed should be volatile */
+ spin = 0;
+ while(wv_wait_completed && (spin++ < 250))
+ udelay(10);
+
+ /* If the interrupt handler hasn't be called or invalid status */
+ if((wv_wait_completed) ||
+ ((lp->status & SR0_EVENT_MASK) != SR0_EXECUTION_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... */
+ dev->tbusy = 0;
+
+#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 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 */
+ dev->interrupt = 0;
+ dev->start = 1;
+ dev->tbusy = 0;
+
+ /* 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;
+ net_local * lp = (net_local *)dev->priv;
+ 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 */
+
+ /* If watchdog was activated, kill it ! */
+ if(lp->watchdog.prev != (timer_list *) NULL)
+ del_timer(&lp->watchdog);
+
+ link->open--;
+ MOD_DEC_USE_COUNT;
+
+ /* If the card is still present */
+ if(dev->start)
+ {
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* 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;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * We never need to do anything when a wavelan device is "initialized"
+ * by the net software, because we only register already-found cards.
+ */
+static int
+wavelan_init(device * dev)
+{
+#ifdef DEBUG_CALLBACK_TRACE
+ printk(KERN_DEBUG "<>wavelan_init()\n");
+#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);
+ 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);
+ 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);
+ memset(lp, 0x00, sizeof(net_local));
+
+ /* Init specific data */
+ wv_wait_completed = 0;
+ lp->status = FALSE;
+ lp->configured = 0;
+ lp->reconfig_82593 = FALSE;
+ lp->nresets = 0;
+
+ /* Set the watchdog timer */
+ lp->watchdog.function = wavelan_watchdog;
+ lp->watchdog.data = (unsigned long) dev;
+
+ /* back links */
+ lp->link = link;
+ lp->dev = dev;
+
+ /* Standard setup for generic data */
+ ether_setup(dev);
+
+ /* wavelan NET3 callbacks */
+ dev->init = &wavelan_init;
+ 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 */
+
+#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 */
+ /* Provide storage area for device name */
+ dev->name = ((net_local *)dev->priv)->node.dev_name;
+ dev->tbusy = 1;
+ 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 */
+ dev->tbusy = 1; dev->start = 0;
+
+ /* 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)
+ {
+ dev->tbusy = 1;
+ dev->start = 0;
+ }
+ 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);
+ dev->tbusy = 0;
+ dev->start = 1;
+ }
+ }
+ 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
+ *
+ * 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 controler
+ */
+
+/* --------------------------- 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 <dhinds@hyper.stanford.edu> 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 - pcmcia 2.8.18-23) with
+ * HP OmniBook 4000 & 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 <dhinds@hyper.stanford.edu>,
+ * 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
+ * - Supress 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 Supress "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)
+ *
+ * Wishes & dreams:
+ * ----------------
+ * - Cleanup and integrate the roaming code
+ * (std debug, set DomainID, decay avg and co...)
+ */
+
+/***************************** INCLUDES *****************************/
+
+/* Linux headers that we need */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.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.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 transmited 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 transmited 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 : v21 (wireless extensions) 18/10/99\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 ------------------------ */
+
+#define SIOCSIPQTHR SIOCDEVPRIVATE /* Set quality threshold */
+#define SIOCGIPQTHR SIOCDEVPRIVATE + 1 /* Get quality threshold */
+#define SIOCSIPROAM SIOCDEVPRIVATE + 2 /* Set roaming state */
+#define SIOCGIPROAM SIOCDEVPRIVATE + 3 /* Get roaming state */
+
+#define SIOCSIPHISTO SIOCDEVPRIVATE + 6 /* Set histogram ranges */
+#define SIOCGIPHISTO SIOCDEVPRIVATE + 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... */
+ 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 controler */
+ u_char promiscuous; /* Promiscuous mode */
+ u_char allmulticast; /* All Multicast mode */
+ int mc_count; /* Number of multicast addresses */
+ timer_list watchdog; /* To avoid blocking state */
+
+ u_char status; /* Current i82593 status */
+ 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 unsigned long /* flags */
+ wv_splhi(void); /* Disable interrupts */
+static inline void
+ wv_splx(unsigned long); /* ReEnable interrupts : 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 controler */
+/* ------------------- 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(u_long); /* Transmission watchdog */
+/* ------------------- CONFIGURATION CALLBACKS ------------------- */
+static int
+ wavelan_open(device *), /* Open the device */
+ wavelan_close(device *), /* Close the device */
+ wavelan_init(device *); /* Do nothing */
+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 */
+
+/* WARNING : the following variable MUST be volatile
+ * It is used by wv_82593_cmd to syncronise with wavelan_interrupt */
+static volatile int wv_wait_completed = 0;
+
+/*
+ * 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 */
+
+#endif /* WAVELAN_CS_H */
+
--- /dev/null
+/* [xirc2ps_cs.c wk 14.04.97] (1.31 1998/12/09 19:32:55)
+ * Xircom Creditcard Ethernet Adapter IIps driver
+ *
+ * This driver works for the CE2, CEM28, CEM33, CE3 and CEM56 cards.
+ * The CEM56 has some problems, but it works.
+ * The CEII card with the 14k4 modem and other old cards do not work.
+ *
+ * Written by Werner Koch (werner.koch@guug.de),
+ * based on David Hinds skeleton driver.
+ *
+ * You can get the latest driver revision from
+ * "http://www.d.shuttle.de/isil/xircom/xirc2ps.html"
+ *
+ * Please report bugs to: "xircom-bugs@isil.d.shuttle.de"
+ *
+ * A bug fix for the CEM56 to use modem and ethernet simultaneously
+ * was provided by Koen Van Herck (Koen.Van.Herck@xircom.com).
+ *
+ * If your card locks up you should use the option "lockup_hack=1";
+ * this may solve the problem but violates a kernel timing convention
+ * (Thanks to David Luyer).
+ *
+ * Thanks to David Hinds for the PCMCIA package, Donald Becker for some
+ * advice, Xircom for providing specs and help, 4PC GmbH Duesseldorf for
+ * providing some hardware and last not least to all folks who helped to
+ * develop this driver.
+ *
+ * For those, who are willing to do alpha testing of drivers, I have setup
+ * the mailing list "xircom-devel@isil.d.shuttle.de" (To subscribe send a
+ * message containing the word "subscribe" in the subject or somewhere at
+ * the beginning of a line to "xircom-devel-request@isil.d.shuttle.de").
+ *
+ ************************************************************************
+ * Copyright (c) 1997,1998 Werner Koch (dd9jn)
+ *
+ * This driver 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.
+ *
+ * It 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
+ *
+ *
+ * ALTERNATIVELY, this driver may be distributed under the terms of
+ * the following license, in which case the provisions of this license
+ * are required INSTEAD OF the GNU General Public License. (This clause
+ * is necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Enable the bug fix for CEM56 to use modem and ethernet simultaneously */
+#define CEM56_FIX
+
+#if !defined(PCMCIA_DEBUG) && 0
+ #define PCMCIA_DEBUG 4
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.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 <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+
+#ifndef MANFID_XIRCOM
+ #define MANFID_XIRCOM 0x0105
+#endif
+#ifndef MANFID_COMPAQ
+ #define MANFID_COMPAQ 0x0138
+ #define MANFID_COMPAQ2 0x0183 /* is this correct? */
+#endif
+#ifndef MANFID_INTEL
+ #define MANFID_INTEL 0x0089
+#endif
+
+#include <pcmcia/ds.h>
+
+/* Time in jiffies before concluding Tx hung */
+#define TX_TIMEOUT ((400*HZ)/1000)
+
+/****************
+ * Some constants used to access the hardware
+ */
+
+/* Register offsets and value constans */
+#define XIRCREG_CR 0 /* Command register (wr) */
+enum xirc_cr {
+ TransmitPacket = 0x01,
+ SoftReset = 0x02,
+ EnableIntr = 0x04,
+ ForceIntr = 0x08,
+ ClearTxFIFO = 0x10,
+ ClearRxOvrun = 0x20,
+ RestartTx = 0x40
+};
+#define XIRCREG_ESR 0 /* Ethernet status register (rd) */
+enum xirc_esr {
+ FullPktRcvd = 0x01, /* full packet in receive buffer */
+ PktRejected = 0x04, /* a packet has been rejected */
+ TxPktPend = 0x08, /* TX Packet Pending */
+ IncorPolarity = 0x10,
+ MediaSelect = 0x20 /* set if TP, clear if AUI */
+};
+#define XIRCREG_PR 1 /* Page Register select */
+#define XIRCREG_EDP 4 /* Ethernet Data Port Register */
+#define XIRCREG_ISR 6 /* Ethernet Interrupt Status Register */
+enum xirc_isr {
+ TxBufOvr = 0x01, /* TX Buffer Overflow */
+ PktTxed = 0x02, /* Packet Transmitted */
+ MACIntr = 0x04, /* MAC Interrupt occured */
+ TxResGrant = 0x08, /* Tx Reservation Granted */
+ RxFullPkt = 0x20, /* Rx Full Packet */
+ RxPktRej = 0x40, /* Rx Packet Rejected */
+ ForcedIntr= 0x80 /* Forced Interrupt */
+};
+#define XIRCREG1_IMR0 12 /* Ethernet Interrupt Mask Register (on page 1)*/
+#define XIRCREG1_IMR1 13
+#define XIRCREG0_TSO 8 /* Transmit Space Open Register (on page 0)*/
+#define XIRCREG0_TRS 10 /* Transmit reservation Size Register (page 0)*/
+#define XIRCREG0_DO 12 /* Data Offset Register (page 0) (wr) */
+#define XIRCREG0_RSR 12 /* Receive Status Register (page 0) (rd) */
+enum xirc_rsr {
+ PhyPkt = 0x01, /* set:physical packet, clear: multicast packet */
+ BrdcstPkt = 0x02, /* set if it is a broadcast packet */
+ PktTooLong = 0x04, /* set if packet length > 1518 */
+ AlignErr = 0x10, /* incorrect CRC and last octet not complete */
+ CRCErr = 0x20, /* incorrect CRC and last octet is complete */
+ PktRxOk = 0x80 /* received ok */
+};
+#define XIRCREG0_PTR 13 /* packets transmitted register (rd) */
+#define XIRCREG0_RBC 14 /* receive byte count regsister (rd) */
+#define XIRCREG1_ECR 14 /* ethernet configurationn register */
+enum xirc_ecr {
+ FullDuplex = 0x04, /* enable full duplex mode */
+ LongTPMode = 0x08, /* adjust for longer lengths of TP cable */
+ DisablePolCor = 0x10,/* disable auto polarity correction */
+ DisableLinkPulse = 0x20, /* disable link pulse generation */
+ DisableAutoTx = 0x40, /* disable auto-transmit */
+};
+#define XIRCREG2_RBS 8 /* receive buffer start register */
+#define XIRCREG2_LED 10 /* LED Configuration register */
+/* values for the leds: Bits 2-0 for led 1
+ * 0 disabled Bits 5-3 for led 2
+ * 1 collision
+ * 2 noncollision
+ * 3 link_detected
+ * 4 incor_polarity
+ * 5 jabber
+ * 6 auto_assertion
+ * 7 rx_tx_activity
+ */
+#define XIRCREG2_MSR 12 /* Mohawk specific register */
+
+#define XIRCREG4_GPR0 8 /* General Purpose Register 0 */
+#define XIRCREG4_GPR1 9 /* General Purpose Register 1 */
+#define XIRCREG2_GPR2 13 /* General Purpose Register 2 (page2!)*/
+#define XIRCREG4_BOV 10 /* Bonding Version Register */
+#define XIRCREG4_LMA 12 /* Local Memory Address Register */
+#define XIRCREG4_LMD 14 /* Local Memory Data Port */
+/* MAC register can only by accessed with 8 bit operations */
+#define XIRCREG40_CMD0 8 /* Command Register (wr) */
+enum xirc_cmd { /* Commands */
+ Transmit = 0x01,
+ EnableRecv = 0x04,
+ DisableRecv = 0x08,
+ Abort = 0x10,
+ Online = 0x20,
+ IntrAck = 0x40,
+ Offline = 0x80
+};
+#define XIRCREG5_RHSA0 10 /* Rx Host Start Address */
+#define XIRCREG40_RXST0 9 /* Receive Status Register */
+#define XIRCREG40_TXST0 11 /* Transmit Status Register 0 */
+#define XIRCREG40_TXST1 12 /* Transmit Status Register 10 */
+#define XIRCREG40_RMASK0 13 /* Receive Mask Register */
+#define XIRCREG40_TMASK0 14 /* Transmit Mask Register 0 */
+#define XIRCREG40_TMASK1 15 /* Transmit Mask Register 0 */
+#define XIRCREG42_SWC0 8 /* Software Configuration 0 */
+#define XIRCREG42_SWC1 9 /* Software Configuration 1 */
+#define XIRCREG42_BOC 10 /* Back-Off Configuration */
+#define XIRCREG44_TDR0 8 /* Time Domain Reflectometry 0 */
+#define XIRCREG44_TDR1 9 /* Time Domain Reflectometry 1 */
+#define XIRCREG44_RXBC_LO 10 /* Rx Byte Count 0 (rd) */
+#define XIRCREG44_RXBC_HI 11 /* Rx Byte Count 1 (rd) */
+#define XIRCREG45_REV 15 /* Revision Register (rd) */
+#define XIRCREG50_IA 8 /* Individual Address (8-13) */
+
+static char *if_names[] = { "Auto", "10BaseT", "10Base2", "AUI", "100BaseT" };
+
+/****************
+ * 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");
+#endif
+static char *version =
+"xirc2ps_cs.c 1.31 1998/12/09 19:32:55 (dd9jn+kvh)";
+ /* !--- CVS revision */
+#define KDBG_XIRC KERN_DEBUG "xirc2ps_cs: "
+#define KERR_XIRC KERN_ERR "xirc2ps_cs: "
+#define KWRN_XIRC KERN_WARNING "xirc2ps_cs: "
+#define KNOT_XIRC KERN_NOTICE "xirc2ps_cs: "
+#define KINF_XIRC KERN_INFO "xirc2ps_cs: "
+
+/* card types */
+#define XIR_UNKNOWN 0 /* unknown: not supported */
+#define XIR_CE 1 /* (prodid 1) different hardware: not supported */
+#define XIR_CE2 2 /* (prodid 2) */
+#define XIR_CE3 3 /* (prodid 3) */
+#define XIR_CEM 4 /* (prodid 1) different hardware: not supported */
+#define XIR_CEM2 5 /* (prodid 2) */
+#define XIR_CEM3 6 /* (prodid 3) */
+#define XIR_CEM33 7 /* (prodid 4) */
+#define XIR_CEM56M 8 /* (prodid 5) */
+#define XIR_CEM56 9 /* (prodid 6) */
+#define XIR_CM28 10 /* (prodid 3) modem only: not supported here */
+#define XIR_CM33 11 /* (prodid 4) modem only: not supported here */
+#define XIR_CM56 12 /* (prodid 5) modem only: not supported here */
+#define XIR_CG 13 /* (prodid 1) GSM modem only: not supported */
+#define XIR_CBE 14 /* (prodid 1) cardbus ethernet: not supported */
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+static int if_port = 0;
+MODULE_PARM(if_port, "i");
+
+/* Bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
+static u_long irq_mask = 0xdeb8;
+MODULE_PARM(irq_mask, "i");
+
+static int irq_list[4] = { -1 };
+MODULE_PARM(irq_list, "1-4i");
+
+static int do_sound = 1;
+MODULE_PARM(do_sound, "i");
+
+static int card_type = 0;
+MODULE_PARM(card_type, "i"); /* dummy, not used anymore */
+
+static int lockup_hack = 0;
+MODULE_PARM(lockup_hack, "i"); /* anti lockup hack */
+
+/*====================================================================*/
+
+/* We do not process more than these number of bytes during one
+ * interrupt. (Of course we receive complete packets, so this is not
+ * an exact value).
+ * Something between 2000..22000; first value gives best interrupt latency,
+ * the second enables the usage of the complete on-chip buffer. We use the
+ * high value as the initial value.
+ */
+static unsigned maxrx_bytes = 22000;
+
+/* MII management prototypes */
+static void mii_idle(ioaddr_t ioaddr);
+static void mii_putbit(ioaddr_t ioaddr, unsigned data);
+static int mii_getbit( ioaddr_t ioaddr );
+static void mii_wbits(ioaddr_t ioaddr, unsigned data, int len);
+static unsigned mii_rd(ioaddr_t ioaddr, u_char phyaddr, u_char phyreg);
+static void mii_wr(ioaddr_t ioaddr, u_char phyaddr, u_char phyreg,
+ unsigned data, int len);
+
+/*
+ * The event() function is this driver's Card Services event handler.
+ * It will be called by Card Services when an appropriate card status
+ * event is received. The config() and release() entry points are
+ * used to configure or release a socket, in response to card insertion
+ * and ejection events. They are invoked from the event handler.
+ */
+
+static int has_ce2_string(dev_link_t * link);
+static void xirc2ps_config(dev_link_t * link);
+static void xirc2ps_release(u_long arg);
+static int xirc2ps_event(event_t event, int priority,
+ event_callback_args_t * args);
+
+/****************
+ * The attach() and detach() entry points are used to create and destroy
+ * "instances" of the driver, where each instance represents everything
+ * needed to manage one actual PCMCIA card.
+ */
+
+static dev_link_t *xirc2ps_attach(void);
+static void xirc2ps_detach(dev_link_t *);
+
+/****************
+ * You'll also need to prototype all the functions that will actually
+ * be used to talk to your device. See 'pcmem_cs' for a good example
+ * of a fully self-sufficient driver; the other drivers rely more or
+ * less on other parts of the kernel.
+ */
+
+static void xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+/*
+ * The dev_info variable is the "key" that is used to match up this
+ * device driver with appropriate cards, through the card configuration
+ * database.
+ */
+
+static dev_info_t dev_info = "xirc2ps_cs";
+
+/****************
+ * A linked list of "instances" of the 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 = NULL;
+
+/****************
+ * 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.
+ */
+
+typedef struct local_info_t {
+ dev_node_t node;
+ struct enet_statistics stats;
+ int card_type;
+ int probe_port;
+ int silicon; /* silicon revision. 0=old CE2, 1=Scipper, 4=Mohawk */
+ int mohawk; /* a CE3 type card */
+ int dingo; /* a CEM56 type card */
+ int modem; /* is a multi function card (i.e with a modem) */
+ caddr_t dingo_ccr; /* only used for CEM56 cards */
+ int suspended;
+ unsigned last_ptr_value; /* last packets transmitted value */
+ const char *manf_str;
+} local_info_t;
+
+/****************
+ * Some more prototypes
+ */
+static int do_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static struct enet_statistics *do_get_stats(struct net_device *dev);
+static void set_addresses(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static int do_init(struct net_device *dev);
+static int set_card_type( dev_link_t *link, const void *s );
+static int do_config(struct net_device *dev, struct ifmap *map);
+static int do_open(struct net_device *dev);
+static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void hardreset(struct net_device *dev);
+static void do_reset(struct net_device *dev, int full);
+static int init_mii(struct net_device *dev);
+static void do_powerdown(struct net_device *dev);
+static int do_stop(struct net_device *dev);
+
+/*=============== Helper functions =========================*/
+static void
+flush_stale_links(void)
+{
+ dev_link_t *link, *next;
+ for (link = dev_list; link; link = next) {
+ next = link->next;
+ if (link->state & DEV_STALE_LINK)
+ xirc2ps_detach(link);
+ }
+}
+
+static void
+cs_error(client_handle_t handle, int func, int ret)
+{
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+}
+
+static int
+get_tuple_data(int fn, client_handle_t handle, tuple_t *tuple )
+{
+ int err;
+
+ if( (err=CardServices(fn, handle, tuple)) )
+ return err;
+ return CardServices(GetTupleData, handle, tuple);
+}
+
+static int
+get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+{
+ int err;
+
+ if( (err=get_tuple_data(fn, handle, tuple)) )
+ return err;
+ return CardServices(ParseTuple, handle, tuple, parse);
+}
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
+
+#define SelectPage(pgnr) outb((pgnr), ioaddr + XIRCREG_PR)
+#define GetByte(reg) ((unsigned)inb(ioaddr + (reg)))
+#define GetWord(reg) ((unsigned)inw(ioaddr + (reg)))
+#define PutByte(reg,value) outb((value), ioaddr+(reg))
+#define PutWord(reg,value) outw((value), ioaddr+(reg))
+
+static void
+busy_loop(u_long len)
+{
+ u_long timeout = jiffies + len;
+ u_long flags;
+
+ save_flags(flags);
+ sti();
+ while(timeout >= jiffies)
+ ;
+ restore_flags(flags);
+}
+
+/*====== Functions used for debugging =================================*/
+#if defined(PCMCIA_DEBUG) && 0 /* reading regs may change system status */
+static void
+PrintRegisters(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+
+ if(pc_debug > 1) {
+ int i, page;
+
+ printk(KDBG_XIRC "Register common: ");
+ for(i = 0; i < 8; i++ )
+ printk(" %2.2x", GetByte(i) );
+ printk("\n");
+ for(page = 0; page <= 8; page++) {
+ printk(KDBG_XIRC "Register page %2x: ", page);
+ SelectPage(page);
+ for(i = 8; i < 16; i++)
+ printk(" %2.2x", GetByte(i));
+ printk("\n");
+ }
+ for(page=0x40 ; page <= 0x5f; page++) {
+ if( page == 0x43 || (page >= 0x46 && page <= 0x4f)
+ || (page >= 0x51 && page <=0x5e) )
+ continue;
+ printk(KDBG_XIRC "Register page %2x: ", page);
+ SelectPage(page);
+ for(i = 8; i < 16; i++)
+ printk(" %2.2x", GetByte(i));
+ printk("\n");
+ }
+ }
+}
+#endif /* PCMCIA_DEBUG */
+
+/*============== MII Management functions ===============*/
+
+/****************
+ * Turn around for read
+ */
+static void
+mii_idle(ioaddr_t ioaddr)
+{
+ PutByte(XIRCREG2_GPR2, 0x04|0 ); /* drive MDCK low */
+ udelay(1);
+ PutByte(XIRCREG2_GPR2, 0x04|1 ); /* and drive MDCK high */
+ udelay(1);
+}
+
+/****************
+ * Write a bit to MDI/O
+ */
+static void
+mii_putbit(ioaddr_t ioaddr, unsigned data)
+{
+ #if 1
+ if( data ) {
+ PutByte(XIRCREG2_GPR2, 0x0c|2|0 ); /* set MDIO */
+ udelay(1);
+ PutByte(XIRCREG2_GPR2, 0x0c|2|1 ); /* and drive MDCK high */
+ udelay(1);
+ }
+ else {
+ PutByte(XIRCREG2_GPR2, 0x0c|0|0 ); /* clear MDIO */
+ udelay(1);
+ PutByte(XIRCREG2_GPR2, 0x0c|0|1 ); /* and drive MDCK high */
+ udelay(1);
+ }
+ #else
+ if( data ) {
+ PutWord(XIRCREG2_GPR2-1, 0x0e0e );
+ udelay(1);
+ PutWord(XIRCREG2_GPR2-1, 0x0f0f );
+ udelay(1);
+ }
+ else {
+ PutWord(XIRCREG2_GPR2-1, 0x0c0c );
+ udelay(1);
+ PutWord(XIRCREG2_GPR2-1, 0x0d0d );
+ udelay(1);
+ }
+ #endif
+}
+
+/****************
+ * Get a bit from MDI/O
+ */
+static int
+mii_getbit( ioaddr_t ioaddr )
+{
+ unsigned d;
+
+ PutByte(XIRCREG2_GPR2, 4|0 ); /* drive MDCK low */
+ udelay(1);
+ d = GetByte(XIRCREG2_GPR2); /* read MDIO */
+ PutByte(XIRCREG2_GPR2, 4|1 ); /* drive MDCK high again */
+ udelay(1);
+ return d & 0x20; /* read MDIO */
+}
+
+static void
+mii_wbits(ioaddr_t ioaddr, unsigned data, int len)
+{
+ unsigned m = 1 << (len-1);
+ for( ; m; m >>= 1)
+ mii_putbit( ioaddr, data & m );
+}
+
+static unsigned
+mii_rd(ioaddr_t ioaddr, u_char phyaddr, u_char phyreg)
+{
+ int i;
+ unsigned data=0, m;
+
+ SelectPage(2);
+ for( i=0; i < 32; i++ ) /* 32 bit preamble */
+ mii_putbit(ioaddr, 1);
+ mii_wbits(ioaddr, 0x06, 4); /* Start and opcode for read */
+ mii_wbits(ioaddr, phyaddr, 5); /* PHY address to be accessed */
+ mii_wbits(ioaddr, phyreg, 5); /* PHY register to read */
+ mii_idle(ioaddr); /* turn around */
+ mii_getbit( ioaddr);
+
+ for( m = 1<<15; m; m >>= 1 )
+ if( mii_getbit( ioaddr ) )
+ data |= m;
+ mii_idle(ioaddr);
+ return data;
+}
+
+static void
+mii_wr(ioaddr_t ioaddr, u_char phyaddr, u_char phyreg, unsigned data, int len)
+{
+ int i;
+
+ SelectPage(2);
+ for( i=0; i < 32; i++ ) /* 32 bit preamble */
+ mii_putbit(ioaddr, 1);
+ mii_wbits(ioaddr, 0x05, 4); /* Start and opcode for write */
+ mii_wbits(ioaddr, phyaddr, 5); /* PHY address to be accessed */
+ mii_wbits(ioaddr, phyreg, 5); /* PHY Register to write */
+ mii_putbit(ioaddr, 1); /* turn around */
+ mii_putbit(ioaddr, 0);
+ mii_wbits(ioaddr, data, len); /* And write the data */
+ mii_idle(ioaddr);
+}
+
+#ifdef PCMCIA_DEBUG
+static void
+mii_dump(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ int i;
+
+ /* Note that registers 14, 1d,1e and 1f are reserved and should
+ * not be read according to the DP83840A specs.
+ */
+ printk(KERN_DEBUG "%s: MII register dump:\n", dev->name);
+ for(i=0; i < 32; i++ ) {
+ if( !(i % 8) ) {
+ if( i )
+ printk("\n");
+ printk(KERN_DEBUG "%s:", dev->name );
+ }
+ printk(" %04x", mii_rd(ioaddr, 0, i) );
+ }
+ printk("\n");
+}
+#endif
+
+/*============= Main bulk of functions =========================*/
+
+/****************
+ * xirc2ps_attach() 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 *
+xirc2ps_attach(void)
+{
+ client_reg_t client_reg;
+ dev_link_t *link;
+ struct net_device *dev;
+ local_info_t *local;
+ int err;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "attach()\n");
+ #endif
+ flush_stale_links();
+
+ /* Initialize the dev_link_t structure */
+ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+ memset(link, 0, sizeof(struct dev_link_t));
+ link->release.function = &xirc2ps_release;
+ link->release.data = (u_long) link;
+
+ /* 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;
+
+ /* Allocate space for a device structure */
+ dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+ memset(dev, 0, sizeof(struct net_device));
+ local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+ memset(local, 0, sizeof(local_info_t));
+ dev->priv = local;
+
+ /* Fill in card specific entries */
+ dev->hard_start_xmit = &do_start_xmit;
+ dev->set_config = &do_config;
+ dev->get_stats = &do_get_stats;
+ dev->do_ioctl = &do_ioctl;
+ dev->set_multicast_list = &set_multicast_list;
+ ether_setup(dev);
+ dev->name = local->node.dev_name;
+ dev->init = &do_init;
+ dev->open = &do_open;
+ dev->stop = &do_stop;
+ dev->tbusy = 1;
+ link->priv = 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 = &xirc2ps_event;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ if( (err = CardServices(RegisterClient, &link->handle, &client_reg)) ) {
+ cs_error(link->handle, RegisterClient, err);
+ xirc2ps_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* xirc2ps_attach */
+
+/****************
+ * 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
+xirc2ps_detach(dev_link_t * link)
+{
+ dev_link_t **linkp;
+ long flags;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "detach(0x%p)\n", link);
+ #endif
+
+ /* Locate device structure */
+ for( linkp = &dev_list; *linkp; linkp = &(*linkp)->next )
+ if( *linkp == link)
+ break;
+ if( !*linkp ) {
+ #ifdef PCMCIA_DEBUG
+ printk(KDBG_XIRC "detach(0x%p): dev_link lost\n", link );
+ #endif
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+ if( link->state & DEV_RELEASE_PENDING ) {
+ del_timer(&link->release);
+ link->state &= ~DEV_RELEASE_PENDING;
+ }
+ restore_flags(flags);
+
+ /*
+ * 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) {
+ #ifdef PCMCIA_DEBUG
+ printk(KDBG_XIRC "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);
+
+ /* Unlink device structure, free pieces */
+ *linkp = link->next;
+ if(link->priv) {
+ struct net_device *dev = link->priv;
+ if (link->dev != NULL)
+ unregister_netdev(dev);
+ if( dev->priv )
+ kfree(dev->priv);
+ kfree(link->priv);
+ }
+ kfree(link);
+
+} /* xirc2ps_detach */
+
+/****************
+ * Detect the type of the card. s is the buffer with the data of tuple 0x20
+ * Returns: 0 := not supported
+ * mediaid=11 and prodid=47
+ * Media-Id bits:
+ * Ethernet 0x01
+ * Tokenring 0x02
+ * Arcnet 0x04
+ * Wireless 0x08
+ * Modem 0x10
+ * GSM only 0x20
+ * Prod-Id bits:
+ * Pocket 0x10
+ * External 0x20
+ * Creditcard 0x40
+ * Cardbus 0x80
+ *
+ */
+static int
+set_card_type( dev_link_t *link, const void *s )
+{
+ struct net_device *dev = link->priv;
+ local_info_t *local = dev->priv;
+ #ifdef PCMCIA_DEBUG
+ unsigned cisrev = ((const unsigned char *)s)[2];
+ #endif
+ unsigned mediaid= ((const unsigned char *)s)[3];
+ unsigned prodid = ((const unsigned char *)s)[4];
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "cisrev=%02x mediaid=%02x prodid=%02x\n",
+ cisrev, mediaid, prodid );
+ #endif
+
+ local->mohawk = 0;
+ local->dingo = 0;
+ local->modem = 0;
+ local->card_type = XIR_UNKNOWN;
+ if( !(prodid & 0x40) ) {
+ printk(KNOT_XIRC "Ooops: Not a creditcard\n");
+ return 0;
+ }
+ if( !(mediaid & 0x01) ) {
+ printk(KNOT_XIRC "Not an Ethernet card\n");
+ return 0;
+ }
+ if( mediaid & 0x10 ) {
+ local->modem = 1;
+ switch( prodid & 15 ) {
+ case 1: local->card_type = XIR_CEM ; break;
+ case 2: local->card_type = XIR_CEM2 ; break;
+ case 3: local->card_type = XIR_CEM3 ; break;
+ case 4: local->card_type = XIR_CEM33 ; break;
+ case 5: local->card_type = XIR_CEM56M;
+ local->mohawk = 1;
+ break;
+ case 6:
+ case 7: /* 7 is the RealPort 10/56 */
+ local->card_type = XIR_CEM56 ;
+ local->mohawk = 1;
+ local->dingo = 1;
+ break;
+ }
+ }
+ else {
+ switch( prodid & 15 ) {
+ case 1: local->card_type = has_ce2_string(link)? XIR_CE2 : XIR_CE ;
+ break;
+ case 2: local->card_type = XIR_CE2; break;
+ case 3: local->card_type = XIR_CE3;
+ local->mohawk = 1;
+ break;
+ }
+ }
+ if( local->card_type == XIR_CE || local->card_type == XIR_CEM ) {
+ printk(KNOT_XIRC "Sorry, this is an old CE card\n");
+ return 0;
+ }
+ if( local->card_type == XIR_UNKNOWN )
+ printk(KNOT_XIRC "Warning: Unknown card (mediaid=%02x prodid=%02x)\n",
+ mediaid, prodid );
+
+ return 1;
+}
+
+/****************
+ * There are some CE2 cards out which claim to be a CE card.
+ * This function looks for a "CE2" in the 3rd version field.
+ * Returns: true if this is a CE2
+ */
+static int
+has_ce2_string(dev_link_t * link)
+{
+ client_handle_t handle = link->handle;
+ tuple_t tuple;
+ cisparse_t parse;
+ u_char buf[256];
+
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = 254;
+ tuple.TupleOffset = 0;
+ tuple.DesiredTuple = CISTPL_VERS_1;
+ if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 2 ) {
+ if( strstr(parse.version_1.str + parse.version_1.ofs[2], "CE2") )
+ return 1;
+ }
+ return 0;
+}
+
+/****************
+ * xirc2ps_config() is scheduled to run after a CARD_INSERTION event
+ * is received, to configure the PCMCIA socket, and to make the
+ * ethernet device available to the system.
+ */
+static void
+xirc2ps_config(dev_link_t * link)
+{
+ client_handle_t handle;
+ tuple_t tuple;
+ cisparse_t parse;
+ struct net_device *dev;
+ local_info_t *local;
+ ioaddr_t ioaddr;
+ int err, i;
+ u_char buf[64];
+ cistpl_lan_node_id_t *node_id = (cistpl_lan_node_id_t*)parse.funce.data;
+ cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+
+ handle = link->handle;
+ dev = link->priv;
+ local = dev->priv;
+ local->dingo_ccr = 0;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "config(0x%p)\n", link);
+ #endif
+
+ /*
+ * This reads the card's CONFIG tuple to find its configuration
+ * registers.
+ */
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+
+ /* Is this a valid card */
+ tuple.DesiredTuple = CISTPL_MANFID;
+ if( (err=first_tuple(handle, &tuple, &parse))) {
+ printk(KNOT_XIRC "manfid not found in CIS\n");
+ goto failure;
+ }
+
+ switch( parse.manfid.manf ) {
+ case MANFID_XIRCOM:
+ local->manf_str = "Xircom";
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "found xircom card\n");
+ #endif
+ break;
+ case MANFID_ACCTON:
+ local->manf_str = "Accton";
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "found Accton card\n");
+ #endif
+ break;
+ case MANFID_COMPAQ:
+ case MANFID_COMPAQ2:
+ local->manf_str = "Compaq";
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "found Compaq card\n");
+ #endif
+ break;
+ case MANFID_INTEL:
+ local->manf_str = "Intel";
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "found Intel card\n");
+ #endif
+ break;
+ default:
+ printk(KNOT_XIRC "Unknown Card Manufacturer ID: 0x%04x\n",
+ (unsigned)parse.manfid.manf);
+ goto failure;
+ }
+
+ if( !set_card_type(link, buf ) ) {
+ printk(KNOT_XIRC "this card is not supported\n");
+ goto failure;
+ }
+
+ /* get configuration stuff */
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ if( (err=first_tuple(handle, &tuple, &parse)))
+ goto cis_error;
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ /* get the ethernet address from the CIS */
+ tuple.DesiredTuple = CISTPL_FUNCE;
+ for( err = first_tuple(handle, &tuple, &parse); !err;
+ err = next_tuple(handle, &tuple, &parse) ) {
+ /* Once I saw two CISTPL_FUNCE_LAN_NODE_ID entries:
+ * the first one with a length of zero the second correct -
+ * so I skip all entries with length 0 */
+ if( parse.funce.type == CISTPL_FUNCE_LAN_NODE_ID
+ && ((cistpl_lan_node_id_t *)parse.funce.data)->nb )
+ break;
+ }
+ if( err ) { /* not found: try to get the node-id from tuple 0x89 */
+ tuple.DesiredTuple = 0x89; /* data layout looks like tuple 0x22 */
+ if( !(err = get_tuple_data(GetFirstTuple, handle, &tuple )) ) {
+ if( tuple.TupleDataLen == 8 && *buf == CISTPL_FUNCE_LAN_NODE_ID )
+ memcpy( &parse, buf, 8 );
+ else
+ err = -1;
+ }
+ }
+ if( err ) { /* another try (James Lehmer's CE2 version 4.1)*/
+ tuple.DesiredTuple = CISTPL_FUNCE;
+ for( err = first_tuple(handle, &tuple, &parse); !err;
+ err = next_tuple(handle, &tuple, &parse) ) {
+ if( parse.funce.type == 0x02 && parse.funce.data[0] == 1
+ && parse.funce.data[1] == 6 && tuple.TupleDataLen == 13 ) {
+ buf[1] = 4;
+ memcpy( &parse, buf+1, 8 );
+ break;
+ }
+ }
+ }
+ if( err ) {
+ printk(KNOT_XIRC "node-id not found in CIS\n");
+ goto failure;
+ }
+ node_id = (cistpl_lan_node_id_t *)parse.funce.data;
+ if( node_id->nb != 6 ) {
+ printk(KNOT_XIRC "malformed node-id in CIS\n");
+ goto failure;
+ }
+ for( i=0; i < 6; i++ )
+ dev->dev_addr[i] = node_id->id[i];
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ link->io.IOAddrLines =10;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ link->irq.Attributes = 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 = xirc2ps_interrupt;
+ link->irq.Instance = dev;
+ if( local->modem ) {
+ int pass;
+
+ if( do_sound ) {
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status |= CCSR_AUDIO_ENA;
+ }
+ link->irq.Attributes |= IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED ;
+ link->io.NumPorts2 = 8;
+ link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+ if( local->dingo ) {
+ /* Take the Modem IO port from the CIS and scan for a free
+ * Ethernet port */
+ link->io.NumPorts1 = 16; /* no Mako stuff anymore */
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ for( err = first_tuple(handle, &tuple, &parse); !err;
+ err = next_tuple(handle, &tuple, &parse) ) {
+ if( cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8 ) {
+ for(ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) {
+ link->conf.ConfigIndex = cf->index ;
+ link->io.BasePort2 = cf->io.win[0].base;
+ link->io.BasePort1 = ioaddr;
+ if( !(err=CardServices(RequestIO, link->handle,
+ &link->io)) )
+ goto port_found;
+ }
+ }
+ }
+ }
+ else {
+ link->io.NumPorts1 = 18;
+ /* We do 2 passes here: The first one uses the regular mapping and
+ * the second tries again, thereby considering that the 32 ports are
+ * mirrored every 32 bytes. Actually we use a mirrored port for
+ * the Mako if (on the first pass) the COR bit 5 is set.
+ */
+ for( pass=0; pass < 2; pass++ ) {
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ for( err = first_tuple(handle, &tuple, &parse); !err;
+ err = next_tuple(handle, &tuple, &parse) ){
+ if( cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8 ){
+ link->conf.ConfigIndex = cf->index ;
+ link->io.BasePort2 = cf->io.win[0].base;
+ link->io.BasePort1 = link->io.BasePort2
+ + (pass ? ( cf->index & 0x20 ? -24:8 )
+ : ( cf->index & 0x20 ? 8:-24) );
+ if( !(err=CardServices(RequestIO, link->handle,
+ &link->io)))
+ goto port_found;
+ }
+ }
+ }
+ /* if special option:
+ * try to configure as Ethernet only.
+ * .... */
+ }
+ printk(KNOT_XIRC "no ports available\n");
+ }
+ else {
+ link->irq.Attributes |= IRQ_TYPE_EXCLUSIVE;
+ link->io.NumPorts1 = 16;
+ for(ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) {
+ link->io.BasePort1 = ioaddr;
+ if( !(err=CardServices(RequestIO, link->handle, &link->io)) )
+ goto port_found;
+ }
+ link->io.BasePort1 = 0; /* let CS decide */
+ if( (err=CardServices(RequestIO, link->handle, &link->io)) ) {
+ cs_error(link->handle, RequestIO, err);
+ goto config_error;
+ }
+ }
+ port_found:
+ if( err )
+ goto config_error;
+
+ /****************
+ * Now allocate an interrupt line. Note that this does not
+ * actually assign a handler to the interrupt.
+ */
+ if( (err=CardServices(RequestIRQ, link->handle, &link->irq))) {
+ cs_error(link->handle, RequestIRQ, err);
+ goto config_error;
+ }
+
+ /****************
+ * This actually configures the PCMCIA socket -- setting up
+ * the I/O windows and the interrupt mapping.
+ */
+ if( (err=CardServices(RequestConfiguration,
+ link->handle, &link->conf)) ) {
+ cs_error(link->handle, RequestConfiguration, err);
+ goto config_error;
+ }
+
+ if( local->dingo ) {
+ #ifdef CEM56_FIX
+ conf_reg_t reg;
+ #endif
+ win_req_t req;
+ memreq_t mem;
+
+ #ifdef CEM56_FIX
+ /* Reset the modem's BAR to the correct value
+ * This is necessary because in the RequestConfiguration call,
+ * the base address of the ethernet port (BasePort1) is written
+ * to the BAR registers of the modem.
+ */
+ reg.Action = CS_WRITE;
+ reg.Offset = CISREG_IOBASE_0;
+ reg.Value = link->io.BasePort2 & 0xff;
+ if( (err = CardServices(AccessConfigurationRegister, link->handle,
+ ® )) ) {
+ cs_error(link->handle, AccessConfigurationRegister, err);
+ goto config_error;
+ }
+ reg.Action = CS_WRITE;
+ reg.Offset = CISREG_IOBASE_1;
+ reg.Value = (link->io.BasePort2 >> 8) & 0xff;
+ if( (err = CardServices(AccessConfigurationRegister, link->handle,
+ ® )) ) {
+ cs_error(link->handle, AccessConfigurationRegister, err);
+ goto config_error;
+ }
+ #endif
+
+ /* There is no config entry for the Ethernet part which
+ * is at 0x0800. So we allocate a window into the attribute
+ * memory and write direct to the CIS registers
+ */
+ req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+ req.Base = 0;
+ req.Size = 0x1000; /* 4k window */
+ req.AccessSpeed = 0;
+ link->win = (window_handle_t)link->handle;
+ if( (err = CardServices(RequestWindow, &link->win, &req )) ) {
+ cs_error(link->handle, RequestWindow, err);
+ goto config_error;
+ }
+ local->dingo_ccr = ioremap(req.Base,0x1000) + 0x0800;
+ mem.CardOffset = 0x0;
+ mem.Page = 0;
+ if( (err = CardServices(MapMemPage, link->win, &mem) ) ) {
+ cs_error(link->handle, MapMemPage, err);
+ goto config_error;
+ }
+
+ /* Setup the CCRs; there are no infos in the CIS about the Ethernet
+ * part.
+ */
+ writeb(0x47, local->dingo_ccr + CISREG_COR );
+ ioaddr = link->io.BasePort1;
+ writeb( ioaddr & 0xff , local->dingo_ccr + CISREG_IOBASE_0 );
+ writeb((ioaddr >> 8)&0xff , local->dingo_ccr + CISREG_IOBASE_1 );
+
+ #if 0
+ {
+ u_char tmp;
+ printk(KERN_INFO "ECOR:" );
+ for(i=0; i < 7; i++ ) {
+ tmp = readb(local->dingo_ccr + i*2 );
+ printk(" %02x", tmp );
+ }
+ printk("\n" );
+ printk(KERN_INFO "DCOR:" );
+ for(i=0; i < 4; i++ ) {
+ tmp = readb(local->dingo_ccr + 0x20 + i*2 );
+ printk(" %02x", tmp );
+ }
+ printk("\n" );
+ printk(KERN_INFO "SCOR:" );
+ for(i=0; i < 10; i++ ) {
+ tmp = readb(local->dingo_ccr + 0x40 + i*2 );
+ printk(" %02x", tmp );
+ }
+ printk("\n" );
+ }
+ #endif
+
+ writeb( 0x01 , local->dingo_ccr + 0x20 );
+ writeb( 0x0c , local->dingo_ccr + 0x22 );
+ writeb( 0x00 , local->dingo_ccr + 0x24 );
+ writeb( 0x00 , local->dingo_ccr + 0x26 );
+ writeb( 0x00 , local->dingo_ccr + 0x28 );
+ }
+
+ /* The if_port symbol can be set when the module is loaded */
+ local->probe_port=0;
+ if( !if_port ) {
+ local->probe_port=1;
+ dev->if_port = 1;
+ }
+ else if( (if_port >= 1 && if_port <= 2) || (local->mohawk && if_port==4) )
+ dev->if_port = if_port;
+ else
+ printk(KERN_NOTICE "xirc2ps_cs: invalid if_port requested\n");
+
+ /* we can now register the device with the net subsystem */
+ dev->irq = link->irq.AssignedIRQ;
+ dev->base_addr = link->io.BasePort1;
+ dev->tbusy = 0;
+ if( (err=register_netdev(dev)) ) {
+ printk(KERN_NOTICE "xirc2ps_cs: register_netdev() failed\n");
+ goto config_error;
+ }
+
+ link->state &= ~DEV_CONFIG_PENDING;
+ link->dev = &local->node;
+
+ if( local->dingo )
+ do_reset(dev, 1); /* a kludge to make the cem56 work */
+
+ /* give some infos about the hardware */
+ printk(KERN_INFO "%s: %s: port %#3lx, irq %d, hwaddr",
+ dev->name, local->manf_str,(u_long)dev->base_addr, (int)dev->irq );
+ for(i = 0; i < 6; i++)
+ printk("%c%02X", i?':':' ', dev->dev_addr[i]);
+ printk("\n");
+
+ return;
+
+ config_error:
+ link->state &= ~DEV_CONFIG_PENDING;
+ xirc2ps_release((u_long)link);
+ return;
+
+ cis_error:
+ printk(KERN_NOTICE "xirc2ps_cs: unable to parse CIS\n");
+ failure:
+ link->state &= ~DEV_CONFIG_PENDING;
+} /* xirc2ps_config */
+
+/****************
+ * After a card is removed, xirc2ps_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
+xirc2ps_release( u_long arg)
+{
+ dev_link_t *link = (dev_link_t *) arg;
+ struct net_device *dev = link->priv;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "release(0x%p)\n", link);
+ #endif
+
+ /*
+ * If the device is currently in use, we won't release until it
+ * is actually closed.
+ */
+ if(link->open) {
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "release postponed, '%s' "
+ "still open\n", link->dev->dev_name);
+ #endif
+ link->state |= DEV_STALE_CONFIG;
+ return;
+ }
+
+ if( link->win ) {
+ local_info_t *local = dev->priv;
+ if( local->dingo )
+ iounmap( local->dingo_ccr - 0x0800 );
+ 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_RELEASE_PENDING);
+
+} /* xirc2ps_release */
+
+/*====================================================================*/
+
+/****************
+ * 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
+xirc2ps_event(event_t event, int priority,
+ event_callback_args_t * args)
+{
+ dev_link_t *link = args->client_data;
+ struct net_device *dev = link->priv;
+ local_info_t *lp = dev? dev->priv : NULL;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "event(%d)\n", (int)event );
+ #endif
+
+ switch (event) {
+ case CS_EVENT_REGISTRATION_COMPLETE:
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "registration complete\n");
+ #endif
+ break;
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if(link->state & DEV_CONFIG) {
+ dev->tbusy = 1; dev->start = 0;
+ link->release.expires = jiffies + HZ / 20;
+ add_timer(&link->release);
+ }
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ xirc2ps_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) {
+ dev->tbusy = 1; dev->start = 0;
+ lp->suspended=1;
+ do_powerdown(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) {
+ do_reset(dev,1);
+ lp->suspended=0;
+ dev->tbusy = 0; dev->start = 1;
+ }
+ }
+ break;
+ }
+ return 0;
+} /* xirc2ps_event */
+
+/*====================================================================*/
+
+/****************
+ * This is the Interrupt service route.
+ */
+static void
+xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ local_info_t *lp;
+ ioaddr_t ioaddr;
+ u_char saved_page;
+ unsigned bytes_rcvd;
+ unsigned int_status, eth_status, rx_status, tx_status;
+ unsigned rsr, pktlen;
+ ulong start_ticks = jiffies; /* fixme: jiffies rollover every 497 days
+ * is this something to worry about?
+ * -- on a laptop?
+ */
+
+ if( !dev->start )
+ return;
+
+ if( dev->interrupt ) {
+ printk(KERR_XIRC "re-entering isr on irq %d (dev=%p)\n", irq, dev);
+ return;
+ }
+ dev->interrupt = 1;
+ lp = dev->priv;
+ ioaddr = dev->base_addr;
+ if( lp->mohawk ) { /* must disable the interrupt */
+ PutByte(XIRCREG_CR, 0 );
+ }
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug > 6 )
+ printk(KERN_DEBUG "%s: interrupt %d at %#x.\n", dev->name, irq, ioaddr);
+ #endif
+
+ saved_page = GetByte(XIRCREG_PR);
+ /* Read the ISR to see whats the cause for the interrupt.
+ * This also clears the interrupt flags on CE2 cards
+ */
+ int_status = GetByte(XIRCREG_ISR);
+ bytes_rcvd = 0;
+ loop_entry:
+ if( int_status == 0xff ) { /* card may be ejected */
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug > 3 )
+ printk(KERN_DEBUG "%s: interrupt %d for dead card\n", dev->name, irq );
+ #endif
+ goto leave;
+ }
+ eth_status = GetByte(XIRCREG_ESR);
+
+ SelectPage(0x40);
+ rx_status = GetByte(XIRCREG40_RXST0);
+ PutByte(XIRCREG40_RXST0, (~rx_status & 0xff) );
+ tx_status = GetByte(XIRCREG40_TXST0);
+ tx_status |= GetByte(XIRCREG40_TXST1) << 8;
+ PutByte(XIRCREG40_TXST0, 0 );
+ PutByte(XIRCREG40_TXST1, 0 );
+
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug > 3 )
+ printk(KERN_DEBUG "%s: ISR=%#2.2x ESR=%#2.2x RSR=%#2.2x TSR=%#4.4x\n",
+ dev->name, int_status, eth_status, rx_status, tx_status );
+ #endif
+
+ /***** receive section ******/
+ SelectPage(0);
+ while( eth_status & FullPktRcvd ) {
+ rsr = GetByte(XIRCREG0_RSR);
+ if( bytes_rcvd > maxrx_bytes && (rsr & PktRxOk) ) {
+ /* too many bytes received during this int, drop the rest of the
+ * packets */
+ lp->stats.rx_dropped++;
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug > 3 )
+ printk(KNOT_XIRC "%s: RX drop, too much done\n", dev->name);
+ #endif
+ PutWord(XIRCREG0_DO, 0x8000 ); /* issue cmd: skip_rx_packet */
+ }
+ else if( rsr & PktRxOk ) {
+ struct sk_buff *skb;
+
+ pktlen = GetWord(XIRCREG0_RBC);
+ bytes_rcvd += pktlen;
+
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug > 5 )
+ printk(KDBG_XIRC "rsr=%#02x packet_length=%u\n", rsr, pktlen );
+ #endif
+
+ skb = dev_alloc_skb(pktlen+3); /* 1 extra so we can use insw */
+ if( !skb ) {
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug )
+ printk(KNOT_XIRC "low memory, packet dropped (size=%u)\n",
+ pktlen );
+ #endif
+ lp->stats.rx_dropped++;
+ }
+ else { /* okay get the packet */
+ skb_reserve(skb, 2);
+ if( lp->silicon == 0 ) { /* work around a hardware bug */
+ unsigned rhsa; /* receive start address */
+
+ SelectPage(5);
+ rhsa = GetWord(XIRCREG5_RHSA0);
+ SelectPage(0);
+ rhsa += 3; /* skip control infos */
+ if( rhsa >= 0x8000 )
+ rhsa = 0;
+ if( rhsa + pktlen > 0x8000 ) {
+ unsigned i;
+ u_char *buf = skb_put(skb, pktlen);
+ for(i=0; i < pktlen ; i++, rhsa++ ) {
+ buf[i] = GetByte(XIRCREG_EDP);
+ if( rhsa == 0x8000 ) {
+ rhsa = 0;
+ i--;
+ }
+ }
+ } else {
+ insw(ioaddr+XIRCREG_EDP,
+ skb_put(skb, pktlen), (pktlen+1)>>1 );
+ }
+ }
+ #if 0
+ else if( lp->mohawk ) {
+ /* To use this 32 bit access we should use
+ * a manual optimized loop
+ * Also the words are swapped, we can get more
+ * performance by using 32 bit access and swapping
+ * the words in a register. Will need this for cardbus
+ *
+ * Note: don't forget to change the ALLOC_SKB to .. +3
+ */
+ unsigned i;
+ u_long *p = skb_put(skb, pktlen);
+ register u_long a;
+ ioaddr_t edpreg = ioaddr+XIRCREG_EDP-2;
+ for(i=0; i < len ; i += 4, p++ ) {
+ a = inl(edpreg);
+ __asm__("rorl $16,%0\n\t"
+ :"=q" (a)
+ : "0" (a));
+ *p = a;
+ }
+ }
+ #endif
+ else {
+ insw(ioaddr+XIRCREG_EDP, skb_put(skb, pktlen),
+ (pktlen+1)>>1 );
+ }
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->dev = dev;
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += pktlen;
+ if( !(rsr & PhyPkt) )
+ lp->stats.multicast++;
+ }
+ PutWord(XIRCREG0_DO, 0x8000 ); /* issue cmd: skip_rx_packet */
+ }
+ else {
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug > 5 )
+ printk("rsr=%#02x\n", rsr );
+ #endif
+ }
+ if( rsr & PktTooLong ) {
+ lp->stats.rx_frame_errors++;
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug > 3 )
+ printk(KNOT_XIRC "%s: Packet too long\n", dev->name);
+ #endif
+ }
+ if( rsr & CRCErr ) {
+ lp->stats.rx_crc_errors++;
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug > 3 )
+ printk(KNOT_XIRC "%s: CRC error\n", dev->name);
+ #endif
+ }
+ if( rsr & AlignErr ) {
+ lp->stats.rx_fifo_errors++; /* okay ? */
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug > 3 )
+ printk(KNOT_XIRC "%s: Alignment error\n", dev->name);
+ #endif
+ }
+
+ /* get the new ethernet status */
+ eth_status = GetByte(XIRCREG_ESR);
+ }
+ if( rx_status & 0x10 ) { /* Receive overrun */
+ lp->stats.rx_over_errors++;
+ PutByte(XIRCREG_CR, ClearRxOvrun);
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug > 3 )
+ printk(KDBG_XIRC "receive overrun cleared\n" );
+ #endif
+ }
+
+ /***** transmit section ******/
+ if( int_status & PktTxed ) {
+ unsigned n, nn;
+
+ n = lp->last_ptr_value;
+ nn = GetByte(XIRCREG0_PTR);
+ lp->last_ptr_value = nn;
+ if( nn < n ) /* rollover */
+ lp->stats.tx_packets += 256 - n;
+ else if( n == nn ) { /* happens sometimes - don't know why */
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug )
+ printk(KDBG_XIRC "PTR not changed?\n" );
+ #endif
+ }
+ else
+ lp->stats.tx_packets += lp->last_ptr_value - n;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+ if( tx_status & 0x0002 ) { /* Execessive collissions */
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug )
+ printk(KDBG_XIRC "tx restarted due to execssive collissions\n" );
+ #endif
+ PutByte(XIRCREG_CR, RestartTx ); /* restart transmitter process */
+ }
+ if( tx_status & 0x0040 )
+ lp->stats.tx_aborted_errors++;
+
+ /* recalculate our work chunk so that we limit the duration of this
+ * ISR to about 1/10 of a second.
+ * Calculate only if we received a reasonable amount of bytes.
+ */
+ if( bytes_rcvd > 1000 ) {
+ u_long duration = jiffies - start_ticks;
+
+ if( duration >= HZ/10 ) { /* if more than about 1/10 second */
+ maxrx_bytes = (bytes_rcvd * (HZ/10)) / duration;
+ if( maxrx_bytes < 2000 )
+ maxrx_bytes = 2000;
+ else if( maxrx_bytes > 22000 )
+ maxrx_bytes = 22000;
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug > 1)
+ printk(KDBG_XIRC "set maxrx=%u (rcvd=%u ticks=%lu)\n",
+ maxrx_bytes, bytes_rcvd, duration );
+ #endif
+ }
+ else if( !duration && maxrx_bytes < 22000 ) { /* now much faster*/
+ maxrx_bytes += 2000;
+ if( maxrx_bytes > 22000 )
+ maxrx_bytes = 22000;
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug > 1 )
+ printk(KDBG_XIRC "set maxrx=%u\n", maxrx_bytes );
+ #endif
+ }
+ }
+
+ leave:
+ if( lockup_hack ) {
+ if( int_status != 0xff && (int_status = GetByte(XIRCREG_ISR)) != 0 )
+ goto loop_entry;
+ }
+ SelectPage(saved_page);
+ dev->interrupt = 0;
+ PutByte(XIRCREG_CR, EnableIntr ); /* re-enable interrupts */
+ /* Instead of dropping packets during a receive, we could
+ * force an interrupt with this command:
+ * PutByte(XIRCREG_CR, EnableIntr|ForceIntr );
+ */
+} /* xirc2ps_interrupt */
+
+/*====================================================================*/
+
+static int
+do_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ local_info_t *lp = dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ int okay;
+ unsigned freespace;
+ unsigned pktlen = skb? skb->len : 0;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug>1 )
+ printk(KDBG_XIRC "do_start_xmit(skb=%p, dev=%p) len=%u\n",
+ skb, dev, pktlen );
+ #endif
+
+ /* Transmitter timeout, serious problems */
+ if( dev->tbusy ) {
+ int tickssofar = jiffies - dev->trans_start;
+
+ if( lp->suspended ) {
+ dev_kfree_skb (skb);
+ dev->trans_start = jiffies;
+ lp->stats.tx_dropped++;
+ return 0;
+ }
+ if( tickssofar < TX_TIMEOUT )
+ return 1;
+
+ printk(KERN_NOTICE "%s: transmit timed out\n", dev->name );
+ lp->stats.tx_errors++;
+ /* reset the card */
+ do_reset(dev,1);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ }
+
+ if( test_and_set_bit(0, (void*)&dev->tbusy ) ) {
+ printk(KWRN_XIRC "transmitter access conflict\n");
+ dev_kfree_skb (skb);
+ return 0;
+ }
+
+ /* adjust the packet length to min. required
+ * and hope that the buffer is large enough
+ * to provide some random data.
+ * fixme: For Mohawk we can change this by sending
+ * a larger packetlen than we actually have; the chip will
+ * pad this in his buffer with random bytes
+ */
+ if( pktlen < ETH_ZLEN )
+ pktlen = ETH_ZLEN;
+
+ SelectPage(0);
+ PutWord(XIRCREG0_TRS, (u_short)pktlen+2 );
+ freespace = GetWord(XIRCREG0_TSO);
+ okay = freespace & 0x8000;
+ freespace &= 0x7fff;
+ /* TRS doesn't work - (indeed it is eliminated with sil-rev 1) */
+ okay = pktlen +2 < freespace;
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug > 2 + ( okay ? 2 : 0) )
+ printk(KERN_DEBUG "%s: avail. tx space=%u%s\n", dev->name, freespace,
+ okay? " (okay)":" (not enough)" );
+ #endif
+ if( !okay ) { /* not enough space */
+ dev->tbusy = 1;
+ return 1; /* upper layer may decide to requeue this packet */
+ }
+ /* send the packet */
+ PutWord(XIRCREG_EDP, (u_short)pktlen );
+ outsw(ioaddr+XIRCREG_EDP, skb->data, pktlen>>1 );
+ if( pktlen & 1 )
+ PutByte(XIRCREG_EDP, skb->data[pktlen-1] );
+
+ if( lp->mohawk )
+ PutByte(XIRCREG_CR, TransmitPacket|EnableIntr );
+
+ dev_kfree_skb (skb);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ lp->stats.tx_bytes += pktlen;
+ return 0;
+}
+
+static struct enet_statistics *
+do_get_stats(struct net_device *dev)
+{
+ local_info_t *lp = dev->priv;
+
+ /* lp->stats.rx_missed_errors = GetByte(?) */
+ return &lp->stats;
+}
+
+/****************
+ * Set all addresses: This first one is the individual address,
+ * the next 9 addresses are taken from the multicast list and
+ * the rest is filled with the individual address.
+ */
+static void
+set_addresses(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ local_info_t *lp = dev->priv;
+ struct dev_mc_list *dmi = dev->mc_list;
+ char *addr;
+ int i,j,k,n;
+
+ SelectPage(k=0x50);
+ for(i=0,j=8,n=0; ; i++, j++) {
+ if( i > 5 ) {
+ if( ++n > 9 )
+ break;
+ i = 0;
+ }
+ if( j > 15 ) {
+ j = 8;
+ k++;
+ SelectPage(k);
+ }
+
+ if( n && n <= dev->mc_count && dmi ) {
+ addr = dmi->dmi_addr;
+ dmi = dmi->next;
+ }
+ else
+ addr = dev->dev_addr;
+
+ if( lp->mohawk )
+ PutByte( j, addr[5-i] );
+ else
+ PutByte( j, addr[i] );
+ }
+ SelectPage(0);
+}
+
+/****************
+ * Set or clear the multicast filter for this adaptor.
+ * We can filter up to 9 addresses, if more are requested we set
+ * multicast promiscuous mode.
+ */
+
+static void
+set_multicast_list(struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+
+ SelectPage(0x42);
+ if( dev->flags & IFF_PROMISC ) { /* snoop */
+ PutByte(XIRCREG42_SWC1, 0x06); /* set MPE and PME */
+ }
+ else if( dev->mc_count > 9 || (dev->flags & IFF_ALLMULTI) ) {
+ PutByte(XIRCREG42_SWC1, 0x06); /* set MPE */
+ }
+ else if( dev->mc_count ) { /* the chip can filter 9 addresses perfectly */
+ PutByte(XIRCREG42_SWC1, 0x00);
+ SelectPage(0x40);
+ PutByte(XIRCREG40_CMD0, Offline );
+ set_addresses(dev);
+ SelectPage(0x40);
+ PutByte(XIRCREG40_CMD0, EnableRecv | Online );
+ }
+ else { /* standard usage */
+ PutByte(XIRCREG42_SWC1, 0x00);
+ }
+ SelectPage(0);
+}
+
+/****************
+ * We never need to do anything when a IIps device is "initialized"
+ * by the net software, because we only register already-found cards.
+ */
+static int
+do_init(struct net_device *dev)
+{
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "do_init(%p)\n", dev );
+ #endif
+ return 0;
+}
+
+static int
+do_config(struct net_device *dev, struct ifmap *map)
+{
+ local_info_t *local = dev->priv;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "do_config(%p)\n", dev );
+ #endif
+
+ if( map->port != 255 && map->port != dev->if_port ) {
+ if( map->port <= 4 ) {
+ if( !map->port ) {
+ local->probe_port = 1;
+ dev->if_port = 1;
+ }
+ else {
+ local->probe_port = 0;
+ dev->if_port = map->port;
+ }
+ printk(KERN_INFO "%s: switching to %s port\n",
+ dev->name, if_names[dev->if_port]);
+ do_reset(dev,1); /* not the fine way :-) */
+ }
+ else
+ return -EINVAL;
+ }
+ #ifdef PCMCIA_DEBUG
+ else if( map->port == dev->if_port && local->mohawk ) {
+ /* kludge to print the mii regsiters */
+ mii_dump(dev);
+ }
+ #endif
+ return 0;
+}
+
+/****************
+ * Open the driver
+ */
+static int
+do_open(struct net_device *dev)
+{
+ local_info_t *lp = dev->priv;
+ dev_link_t *link;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "do_open(%p)\n", dev );
+ #endif
+
+ /* Check that the PCMCIA card is still here. */
+ for( link = dev_list; link; link = link->next )
+ if( link->priv == dev )
+ break;
+ /* Physical device present signature. */
+ if( !DEV_OK(link) )
+ return -ENODEV;
+
+ /* okay */
+ link->open++;
+ MOD_INC_USE_COUNT;
+
+ dev->interrupt = 0; dev->tbusy = 0; dev->start = 1;
+ lp->suspended = 0;
+ do_reset(dev,1);
+
+ return 0;
+}
+
+static int
+do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ local_info_t *local = dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ u16 *data = (u16 *)&rq->ifr_data;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug > 1)
+ printk(KERN_DEBUG "%s: ioctl(%-.6s, %#04x) %04x %04x %04x %04x\n",
+ dev->name, rq->ifr_ifrn.ifrn_name, cmd,
+ data[0], data[1], data[2], data[3] );
+ #endif
+
+ if( !local->mohawk )
+ return -EOPNOTSUPP;
+
+ switch( cmd ) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ data[0] = 0; /* we have only this address */
+ /* fall trough */
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
+ data[3] = mii_rd( ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+ break;
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+ if( !suser() )
+ return -EPERM;
+ mii_wr(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2], 16);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static void
+hardreset(struct net_device *dev)
+{
+ local_info_t *local = dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+
+ SelectPage(4);
+ udelay(1);
+ PutByte(XIRCREG4_GPR1, 0); /* clear bit 0: power down */
+ busy_loop(HZ/25); /* wait 40 msec */
+ if( local->mohawk )
+ PutByte(XIRCREG4_GPR1, 1); /* set bit 0: power up */
+ else
+ PutByte(XIRCREG4_GPR1, 1 | 4); /* set bit 0: power up, bit 2: AIC */
+ busy_loop(HZ/50); /* wait 20 msec */
+}
+
+static void
+do_reset(struct net_device *dev, int full)
+{
+ local_info_t *local = dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ unsigned value;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KERN_DEBUG "%s: do_reset(%p,%d)\n",
+ dev? dev->name:"eth?", dev, full );
+ #endif
+
+ hardreset(dev);
+ PutByte(XIRCREG_CR, SoftReset ); /* set */
+ busy_loop(HZ/50); /* wait 20 msec */
+ PutByte(XIRCREG_CR, 0 ); /* clear */
+ busy_loop(HZ/25); /* wait 40 msec */
+ if( local->mohawk ) {
+ SelectPage(4);
+ /* set pin GP1 and GP2 to output (0x0c)
+ * set GP1 to low to power up the ML6692 (0x00)
+ * set GP2 to high to power up the 10Mhz chip (0x02)
+ */
+ PutByte(XIRCREG4_GPR0, 0x0e);
+ }
+
+ /* give the circuits some time to power up */
+ busy_loop(HZ/2); /* about 500ms */
+
+ local->last_ptr_value = 0;
+ local->silicon = local->mohawk ? (GetByte(XIRCREG4_BOV) & 0x70) >> 4
+ : (GetByte(XIRCREG4_BOV) & 0x30) >> 4;
+
+ if( local->probe_port ) {
+ if( !local->mohawk ) {
+ SelectPage(4);
+ PutByte(XIRCREG4_GPR0, 4);
+ local->probe_port = 0;
+ }
+ }
+ else if( dev->if_port == 2 ) { /* enable 10Base2 */
+ SelectPage(0x42);
+ PutByte(XIRCREG42_SWC1, 0xC0);
+ }
+ else { /* enable 10BaseT */
+ SelectPage(0x42);
+ PutByte(XIRCREG42_SWC1, 0x80);
+ }
+ busy_loop(HZ/25); /* wait 40 msec to let it complete */
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug) {
+ SelectPage(0);
+ value = GetByte(XIRCREG_ESR); /* read the ESR */
+ printk(KERN_DEBUG "%s: ESR is: %#02x\n", dev->name, value);
+ }
+ #endif
+
+ /* setup the ECR */
+ SelectPage(1);
+ PutByte(XIRCREG1_IMR0, 0xff ); /* allow all ints */
+ PutByte(XIRCREG1_IMR1, 1 ); /* and Set TxUnderrunDetect */
+ value = GetByte(XIRCREG1_ECR);
+ #if 0
+ if( local->mohawk )
+ value |= DisableLinkPulse;
+ PutByte(XIRCREG1_ECR, value);
+ #endif
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KERN_DEBUG "%s: ECR is: %#02x\n", dev->name, value);
+ #endif
+
+ SelectPage(0x42);
+ PutByte(XIRCREG42_SWC0, 0x20); /* disable source insertion */
+
+ if( local->silicon != 1 ) {
+ /* set the local memory dividing line.
+ * The comments in the sample code say that this is only
+ * settable with the scipper version 2 which is revision 0.
+ * Always for CE3 cards
+ */
+ SelectPage(2);
+ PutWord(XIRCREG2_RBS, 0x2000 );
+ }
+
+ if( full )
+ set_addresses(dev);
+
+ /* Hardware workaround:
+ * The receive byte pointer after reset is off by 1 so we need
+ * to move the offset pointer back to 0.
+ */
+ SelectPage(0);
+ PutWord(XIRCREG0_DO, 0x2000 ); /* change offset command, off=0 */
+
+ /* setup MAC IMRs and clear status registers */
+ SelectPage(0x40); /* Bit 7 ... bit 0 */
+ PutByte(XIRCREG40_RMASK0, 0xff); /* ROK, RAB, rsv, RO, CRC, AE, PTL, MP */
+ PutByte(XIRCREG40_TMASK0, 0xff); /* TOK, TAB, SQE, LL, TU, JAB, EXC, CRS */
+ PutByte(XIRCREG40_TMASK1, 0xb0); /* rsv, rsv, PTD, EXT, rsv,rsv,rsv, rsv*/
+ PutByte(XIRCREG40_RXST0, 0x00); /* ROK, RAB, REN, RO, CRC, AE, PTL, MP */
+ PutByte(XIRCREG40_TXST0, 0x00); /* TOK, TAB, SQE, LL, TU, JAB, EXC, CRS */
+ PutByte(XIRCREG40_TXST1, 0x00); /* TEN, rsv, PTD, EXT, retry_counter:4 */
+
+ if( full && local->mohawk && init_mii(dev) ) {
+ if( dev->if_port == 4 || local->dingo ) { /* and use it */
+ SelectPage(2);
+ value = GetByte(XIRCREG2_MSR);
+ value |= 0x08; /* Select MII */
+ PutByte(XIRCREG2_MSR, value);
+ busy_loop(HZ/50); /* wait 20 msec */
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KERN_DEBUG "%s: MII selected\n", dev->name);
+ #endif
+ }
+ else {
+ SelectPage(2);
+ PutByte(XIRCREG2_MSR, GetByte(XIRCREG2_MSR) | 0x08);
+ busy_loop(HZ/50);
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KERN_DEBUG "%s: MII detected; using 10mbs\n",
+ dev->name);
+ #endif
+ SelectPage(0x42);
+ if( dev->if_port == 2 ) /* enable 10Base2 */
+ PutByte(XIRCREG42_SWC1, 0xC0);
+ else /* enable 10BaseT */
+ PutByte(XIRCREG42_SWC1, 0x80);
+ busy_loop(HZ/25); /* wait 40 msec to let it complete */
+ }
+ }
+ else { /* No MII */
+ SelectPage(0);
+ value = GetByte(XIRCREG_ESR); /* read the ESR */
+ dev->if_port = (value & MediaSelect) ? 1 : 2;
+ }
+
+ /* configure the LEDs */
+ SelectPage(2);
+ if( dev->if_port == 1 || dev->if_port == 4 ) /* TP: Link and Activity */
+ PutByte(XIRCREG2_LED, 0x3b );
+ else /* Coax: Not-Collision and Activity */
+ PutByte(XIRCREG2_LED, 0x3a );
+
+ if (local->dingo)
+ PutByte( 0x0b, 0x04 ); /* 100 Mbit LED */
+
+ /* enable receiver and put the mac online */
+ if( full ) {
+ SelectPage(0x40);
+ PutByte(XIRCREG40_CMD0, EnableRecv | Online );
+ }
+
+ /* setup Ethernet IMR and enable interrupts */
+ SelectPage(1);
+ PutByte(XIRCREG1_IMR0, 0xff );
+ udelay(1);
+ SelectPage(0);
+ PutByte(XIRCREG_CR, EnableIntr );
+ if( local->modem && !local->dingo ) { /* do some magic */
+ if( !(GetByte( 0x10 ) & 0x01 ) )
+ PutByte( 0x10, 0x11 ); /* unmask master-int bit */
+ }
+
+ if( full )
+ printk(KERN_INFO "%s: media %s, silicon revision %d\n", dev->name,
+ if_names[dev->if_port], local->silicon);
+ /* We should switch back to page 0 to avoid a bug in revision 0
+ * where regs with offset below 8 can't be read after an access
+ * to the MAC registers */
+ SelectPage(0);
+}
+
+/****************
+ * Initialize the Media-Independent-Interface
+ * Returns: True if we have a good MII
+ */
+static int
+init_mii(struct net_device *dev)
+{
+ local_info_t *local = dev->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ unsigned control, status, linkpartner;
+ int i;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug>1) {
+ mii_dump(dev);
+ }
+ #endif
+
+ status = mii_rd(ioaddr, 0, 1 );
+ if( (status & 0xff00) != 0x7800 )
+ return 0; /* No MII */
+
+ if( local->probe_port )
+ control = 0x1000; /* auto neg */
+ else if( dev->if_port == 4 )
+ control = 0x2000; /* no auto neg, 100mbs mode */
+ else
+ control = 0x0000; /* no auto neg, 10mbs mode */
+ mii_wr(ioaddr, 0, 0, control, 16 );
+ udelay(100);
+ control = mii_rd(ioaddr, 0, 0 );
+
+ if( control & 0x0400 ) {
+ printk(KERN_NOTICE "%s can't take PHY out of isolation mode\n",
+ dev->name);
+ local->probe_port = 0;
+ return 0;
+ }
+
+ if( local->probe_port ) {
+ /* according to the DP83840A specs the auto negotation process
+ * may take up to 3.5 sec, so we use this also for our ML6692
+ * Fixme: Better to use a timer here!
+ */
+ for(i=0; i < 35; i++ ) {
+ busy_loop(HZ/10); /* wait 100 msec */
+ status = mii_rd(ioaddr, 0, 1 );
+ if( (status & 0x0020) && (status & 0x0004) )
+ break;
+ }
+
+ if( !(status & 0x0020) ) {
+ printk(KERN_NOTICE "%s: auto negotation failed;"
+ " using 10mbs\n", dev->name );
+ control = 0x0000;
+ mii_wr(ioaddr, 0, 0, control, 16 );
+ udelay(100);
+ SelectPage(0);
+ dev->if_port = (GetByte(XIRCREG_ESR) & MediaSelect) ? 1 : 2;
+ }
+ else {
+ linkpartner = mii_rd(ioaddr, 0, 5 );
+ printk(KERN_INFO "%s: MII link partner: %04x\n", dev->name,
+ linkpartner );
+ if( linkpartner & 0x0080 ) { /* 100BaseTx capability */
+ dev->if_port = 4;
+ }
+ else
+ dev->if_port = 1;
+ }
+ local->probe_port = 0;
+ }
+
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug )
+ mii_dump(dev);
+ #endif
+
+ return 1;
+}
+
+static void
+do_powerdown(struct net_device *dev)
+{
+
+ ioaddr_t ioaddr = dev->base_addr;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "do_powerdown(%p)\n", dev );
+ #endif
+
+ SelectPage(4);
+ PutByte(XIRCREG4_GPR1, 0); /* clear bit 0: power down */
+ SelectPage(0);
+}
+
+static int
+do_stop( struct net_device *dev)
+{
+ ioaddr_t ioaddr = dev->base_addr;
+ dev_link_t *link;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "do_stop(%p)\n", dev );
+ #endif
+
+ for(link = dev_list; link; link = link->next)
+ if(link->priv == dev)
+ break;
+ if( !link )
+ return -ENODEV;
+
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "shutting down\n");
+ #endif
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ SelectPage(0);
+ PutByte(XIRCREG_CR, 0 ); /* disable interrupts */
+ SelectPage(0x01);
+ PutByte(XIRCREG1_IMR0, 0x00 ); /* forbid all ints */
+ SelectPage(4);
+ PutByte(XIRCREG4_GPR1, 0); /* clear bit 0: power down */
+ SelectPage(0);
+
+ link->open--; dev->start = 0;
+ if (link->state & DEV_STALE_CONFIG) {
+ link->release.expires = jiffies + HZ/20;
+ link->state |= DEV_RELEASE_PENDING;
+ add_timer(&link->release);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static int __init
+init_xirc2ps_cs(void)
+{
+ servinfo_t serv;
+
+ printk(KERN_INFO "%s\n", version);
+ if( card_type )
+ printk(KINF_XIRC "option card_type is obsolete\n");
+ if( lockup_hack )
+ printk(KINF_XIRC "lockup hack is enabled\n");
+ CardServices(GetCardServicesInfo, &serv);
+ if( serv.Revision != CS_RELEASE_CODE ) {
+ printk(KNOT_XIRC "Card Services release does not match!\n");
+ return -1;
+ }
+ #ifdef PCMCIA_DEBUG
+ if( pc_debug )
+ printk(KDBG_XIRC "pc_debug=%d\n", pc_debug);
+ #endif
+ register_pccard_driver(&dev_info, &xirc2ps_attach, &xirc2ps_detach);
+ return 0;
+}
+
+static void __exit
+exit_xirc2ps_cs(void)
+{
+ #ifdef PCMCIA_DEBUG
+ if(pc_debug)
+ printk(KDBG_XIRC "unloading\n");
+ #endif
+ unregister_pccard_driver(&dev_info);
+ while( dev_list ) {
+ if( dev_list->state & DEV_CONFIG )
+ xirc2ps_release( (u_long)dev_list );
+ if( dev_list ) /* xirc2ps_release() might already have detached... */
+ xirc2ps_detach( dev_list );
+ }
+}
+
+module_init(init_xirc2ps_cs);
+module_exit(exit_xirc2ps_cs);
+
# PCMCIA bus subsystem configuration
#
mainmenu_option next_comment
-comment 'PCMCIA/Cardbus support'
+comment 'PCMCIA/CardBus support'
-tristate 'PCMCIA/Cardbus support' CONFIG_PCMCIA
+tristate 'PCMCIA/CardBus support' CONFIG_PCMCIA
if [ "$CONFIG_PCMCIA" != "n" ]; then
if [ "$CONFIG_PCI" != "n" ]; then
bool ' CardBus support' CONFIG_CARDBUS
fi
+ bool ' i82365/Yenta compatible bridge support' CONFIG_I82365
+ bool ' Databook TCIC host bridge support' CONFIG_TCIC
fi
endmenu
MOD_LIST_NAME := PCMCIA_MODULES
ifeq ($(CONFIG_PCMCIA),y)
- O_OBJS := i82365.o tcic.o cistpl.o rsrc_mgr.o bulkmem.o
+ O_OBJS := cistpl.o rsrc_mgr.o bulkmem.o
OX_OBJS := ds.o cs.o
O_TARGET := pcmcia.o
+ ifeq ($(CONFIG_I82365),y)
+ O_OBJS += i82365.o
+ endif
+ ifeq ($(CONFIG_TCIC),y)
+ O_OBJS += tcic.o
+ endif
ifeq ($(CONFIG_CARDBUS),y)
O_OBJS += cardbus.o
+ OX_OBJS += cb_enabler.o
endif
else
ifeq ($(CONFIG_PCMCIA),m)
- M_OBJS := i82365.o tcic.o pcmcia_core.o
+ M_OBJS := pcmcia_core.o
MX_OBJS := ds.o
MIX_OBJS := cs.o
CORE_OBJS := cistpl.o rsrc_mgr.o bulkmem.o cs.o
+ ifeq ($(CONFIG_I82365),y)
+ M_OBJS += i82365.o
+ endif
+ ifeq ($(CONFIG_TCIC),y)
+ M_OBJS += tcic.o
+ endif
ifeq ($(CONFIG_CARDBUS),y)
CORE_OBJS += cardbus.o
+ MX_OBJS += cb_enabler.o
endif
endif
endif
Cardbus device configuration
- cardbus.c 1.59 1999/09/15 15:32:19
+ cardbus.c 1.61 1999/10/20 22:36:57
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
pci_readl(bus, i, PCI_CLASS_REVISION, &c[i].dev.class);
c[i].dev.class >>= 8;
c[i].dev.hdr_type = hdr;
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_PROC_FS
pci_proc_attach_device(&c[i].dev);
-#endif
+#endif
}
return CS_SUCCESS;
if (*p == &c[0].dev) break;
for (q = *p; q; q = q->next) {
if (q->bus != (*p)->bus) break;
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_PROC_FS
pci_proc_detach_device(q);
-#endif
+#endif
}
if (*p) *p = q;
s->cap.cb_bus->devices = NULL;
s->irq.AssignedIRQ = irq;
}
}
- c[0].dev.irq = irq;
+ for (i = 0; i < fn; i++)
+ c[i].dev.irq = irq;
return CS_SUCCESS;
Cardbus device enabler
- cb_enabler.c 1.23 1999/09/15 15:32:19
+ cb_enabler.c 1.24 1999/10/20 00:19:09
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version =
-"cb_enabler.c 1.23 1999/09/15 15:32:19 (David Hinds)";
+"cb_enabler.c 1.24 1999/10/20 00:19:09 (David Hinds)";
#else
#define DEBUG(n, args...) do { } while (0)
#endif
/*====================================================================*/
+EXPORT_SYMBOL(register_driver);
+EXPORT_SYMBOL(unregister_driver);
+
static int __init init_cb_enabler(void)
{
servinfo_t serv;
PCMCIA Card Services -- core services
- cs.c 1.228 1999/09/15 15:32:19
+ cs.c 1.232 1999/10/20 22:17:24
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
int pc_debug = PCMCIA_DEBUG;
MODULE_PARM(pc_debug, "i");
static const char *version =
-"cs.c 1.228 1999/09/15 15:32:19 (David Hinds)";
+"cs.c 1.232 1999/10/20 22:17:24 (David Hinds)";
#endif
static const char *release = "Linux PCMCIA Card Services " CS_RELEASE;
socket_info_t *s = NULL;
client_t *client;
+#ifdef CONFIG_PROC_FS
+ for (i = 0; i < sockets; i++) {
+ s = socket_table[i];
+ if (s->ss_entry != ss_entry) continue;
+ if (proc_pccard) {
+ char name[3];
+ sprintf(name, "%02d", i);
+#ifdef PCMCIA_DEBUG
+ remove_proc_entry("clients", s->proc);
+#endif
+ }
+ }
+#endif
+
for (;;) {
for (i = 0; i < sockets; i++) {
s = socket_table[i];
if (s->ss_entry == ss_entry) break;
}
- if (i == sockets) {
+ if (i == sockets)
break;
- } else {
-#ifdef CONFIG_PROC_FS
- if (proc_pccard) {
- char name[3];
- sprintf(name, "%02d", i);
-#ifdef PCMCIA_DEBUG
- remove_proc_entry("clients", s->proc);
-#endif
- remove_proc_entry(name, proc_pccard);
- }
-#endif
- while (s->clients) {
- client = s->clients;
- s->clients = s->clients->next;
- kfree(client);
- }
- init_socket(s);
- release_cis_mem(s);
-#ifdef CONFIG_CARDBUS
- cb_release_cis_mem(s);
-#endif
- s->ss_entry = NULL;
- kfree(s);
- socket_table[i] = NULL;
- for (j = i; j < sockets-1; j++)
- socket_table[j] = socket_table[j+1];
- sockets--;
+ shutdown_socket(i);
+ release_cis_mem(s);
+ while (s->clients) {
+ client = s->clients;
+ s->clients = s->clients->next;
+ kfree(client);
}
+ s->ss_entry = NULL;
+ kfree(s);
+ socket_table[i] = NULL;
+ for (j = i; j < sockets-1; j++)
+ socket_table[j] = socket_table[j+1];
+ sockets--;
}
} /* unregister_ss_entry */
{
socket_info_t *s;
window_t *win;
- int w;
+ int w, align;
if (CHECK_HANDLE(*handle))
return CS_BAD_HANDLE;
win->sock = s;
win->base = req->Base;
win->size = req->Size;
+ align = ((s->cap.features & SS_CAP_MEM_ALIGN) ||
+ (req->Attributes & WIN_STRICT_ALIGN));
if (find_mem_region(&win->base, win->size, (*handle)->dev_info,
- ((s->cap.features & SS_CAP_MEM_ALIGN) ?
- req->Size : s->cap.map_size),
+ (align ? req->Size : s->cap.map_size),
(req->Attributes & WIN_MAP_BELOW_1MB) ||
!(s->cap.features & SS_CAP_PAGE_REGS)))
return CS_IN_USE;
Device driver for Intel 82365 and compatible PC Card controllers,
and Yenta-compatible PCI-to-CardBus controllers.
- i82365.c 1.254 1999/09/15 15:32:19
+ i82365.c 1.260 1999/10/21 00:56:07
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static const char *version =
-"i82365.c 1.254 1999/09/15 15:32:19 (David Hinds)";
+"i82365.c 1.260 1999/10/21 00:56:07 (David Hinds)";
#else
#define DEBUG(n, args...) do { } while (0)
#endif
#ifdef CONFIG_ISA
#ifdef CONFIG_PCI
/* PCI card status change interrupts? */
-static int pci_csc = 0;
+static int pci_csc = 1;
/* PCI IO card functional interrupts? */
static int pci_int = 0;
MODULE_PARM(pci_csc, "i");
typedef struct socket_info_t {
u_short type, flags;
socket_cap_t cap;
- u_short ioaddr;
+ ioaddr_t ioaddr;
u_short psock;
u_char cs_irq, intr;
void (*handler)(void *info, u_int events);
/* Default ISA interrupt mask */
#define I365_MASK 0xdeb8 /* irq 15,14,12,11,10,9,7,5,4,3 */
-static void pcic_interrupt_wrapper(u_long);
-static void pcic_interrupt(int irq, void *dev,
- struct pt_regs *regs);
-static int pcic_service(u_int sock, u_int cmd, void *arg);
-#ifdef CONFIG_PROC_FS
-static void pcic_proc_remove(u_short sock);
-#endif
-
#ifdef CONFIG_ISA
static int grab_irq;
static spinlock_t isa_lock = SPIN_LOCK_UNLOCKED;
+#define ISA_LOCK(n, f) \
+ if (!(socket[n].flags & IS_CARDBUS)) spin_lock_irqsave(&isa_lock, f)
+#define ISA_UNLOCK(n, f) \
+ if (!(socket[n].flags & IS_CARDBUS)) spin_unlock_irqrestore(&isa_lock, f)
+#else
+#define ISA_LOCK(n, f) do { } while (0)
+#define ISA_UNLOCK(n, f) do { } while (0)
#endif
+
static struct timer_list poll_timer;
/*====================================================================*/
#ifdef CONFIG_PCI
IS_PD6729, IS_PD6730, IS_OZ6729, IS_OZ6730,
IS_I82092AA, IS_OM82C092G,
- IS_PD6832, IS_OZ6832, IS_OZ6836,
+ IS_PD6832, IS_OZ6832, IS_OZ6836, IS_OZ6812,
IS_RL5C465, IS_RL5C466, IS_RL5C475, IS_RL5C476, IS_RL5C478,
IS_SMC34C90,
IS_TI1130, IS_TI1131, IS_TI1250A, IS_TI1220, IS_TI1221, IS_TI1210,
PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6832 },
{ "O2Micro OZ6836/OZ6860", IS_O2MICRO|IS_CARDBUS|IS_VG_PWR,
PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6836 },
+ { "O2Micro OZ6812", IS_O2MICRO|IS_CARDBUS|IS_VG_PWR,
+ PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6812 },
{ "Ricoh RL5C465", IS_RICOH|IS_CARDBUS|IS_DF_PWR,
PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C465 },
{ "Ricoh RL5C466", IS_RICOH|IS_CARDBUS|IS_DF_PWR,
/* Some PCI shortcuts */
-#ifdef CONFIG_PCI
-
#define pci_readb pcibios_read_config_byte
#define pci_writeb pcibios_write_config_byte
#define pci_readw pcibios_read_config_word
static void cb_get_power(u_short sock, socket_state_t *state);
static void cb_set_power(u_short sock, socket_state_t *state);
-#endif
/*====================================================================*/
else
#endif
{
- u_short port = socket[sock].ioaddr;
+ ioaddr_t port = socket[sock].ioaddr;
u_char val;
reg = I365_REG(socket[sock].psock, reg);
outb(reg, port); val = inb(port+1);
else
#endif
{
- u_short port = socket[sock].ioaddr;
+ ioaddr_t port = socket[sock].ioaddr;
u_char val = I365_REG(socket[sock].psock, reg);
outb(val, port); outb(data, port+1);
}
p->mode_e &= ~O2_MODE_E_MHPG_DMA;
p->mhpg |= O2_MHPG_CINT_ENA | O2_MHPG_CSC_ENA;
p->mhpg &= ~O2_MHPG_CHANNEL;
+ if (t->revision == 0x34)
+ p->mode_c = 0x20;
} else {
if (p->mode_b & O2_MODE_B_IRQ15_RI) mask &= ~0x8000;
}
======================================================================*/
-static void get_host_state(u_short s)
+static void get_bridge_state(u_short s)
{
socket_info_t *t = &socket[s];
if (t->flags & IS_CIRRUS)
#endif
}
-static void set_host_state(u_short s)
+static void set_bridge_state(u_short s)
{
socket_info_t *t = &socket[s];
#ifdef CONFIG_PCI
#endif
}
-static u_int __init set_host_opts(u_short s, u_short ns)
+static u_int __init set_bridge_opts(u_short s, u_short ns)
{
u_short i;
u_int m = 0xffff;
continue;
}
buf[0] = '\0';
- get_host_state(i);
+ get_bridge_state(i);
if (socket[i].flags & IS_CIRRUS)
m = cirrus_set_opts(i, buf);
#ifdef CONFIG_ISA
if (socket[i].flags & IS_CARDBUS)
cb_set_opts(i, buf+strlen(buf));
#endif
- set_host_state(i);
+ set_bridge_state(i);
printk(KERN_INFO " host opts [%d]:%s\n", i,
(*buf) ? buf : " none");
}
if (request_irq(irq, irq_count, (pci?SA_SHIRQ:0), "scan", NULL) != 0)
return 1;
irq_hits = 0; irq_sock = sock;
- current->state = TASK_INTERRUPTIBLE;
+ __set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/100);
if (irq_hits) {
free_irq(irq, NULL);
(cb_set_irq_mode(sock, 0, 0) == 0))
#endif
if (do_scan) {
- set_host_state(sock);
+ set_bridge_state(sock);
i365_set(sock, I365_CSCINT, 0);
for (i = 0; i < 16; i++)
if ((mask0 & (1 << i)) && (test_irq(sock, i, 0) == 0))
u_int i;
cb_set_irq_mode(sock, 1, 0);
- set_host_state(sock);
+ set_bridge_state(sock);
i365_set(sock, I365_CSCINT, 0);
/* Only probe irq's 9..11, to be conservative */
for (i = 9; i < 12; i++) {
static int to_cycles(int ns)
{
return ns/cycle_time;
-} /* speed_convert */
+}
static int to_ns(int cycles)
{
for (i = mask = 0; i < 16; i++)
mask |= (1<<irq_list[i]);
#endif
- mask &= I365_MASK & set_host_opts(base, ns);
+ mask &= I365_MASK & set_bridge_opts(base, ns);
#ifdef CONFIG_ISA
/* Scan for ISA interrupts */
mask = isa_scan(base, mask);
s->cb_virt = ioremap(s->cb_phys, 0x1000);
pci_writel(bus, devfn, PCI_BASE_ADDRESS_0, s->cb_phys);
/* Simple sanity checks */
- if (((readb(s->cb_virt+0x800+I365_IDENT) & 0xf0)
- == 0x80) &&
+ if (!(readb(s->cb_virt+0x800+I365_IDENT) & 0x70) &&
!(readb(s->cb_virt+0x800+I365_CSC) &&
readb(s->cb_virt+0x800+I365_CSC) &&
readb(s->cb_virt+0x800+I365_CSC)))
/* Re-do card type & voltage detection */
cb_writel(sockets-ns, CB_SOCKET_FORCE, CB_SF_CVSTEST);
- current->state = TASK_INTERRUPTIBLE;
+ __set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/5);
/* Set up PCI bus bridge structures if needed */
static void __init isa_probe(void)
{
- int i, j, sock, k;
- int ns, id;
- u_short port;
+ int i, j, sock, k, ns, id;
+ ioaddr_t port;
if (check_region(i365_base, 2) != 0) {
if (sockets == 0)
/*====================================================================*/
-static int __init init_i82365(void)
-{
- servinfo_t serv;
- CardServices(GetCardServicesInfo, &serv);
- if (serv.Revision != CS_RELEASE_CODE) {
- printk(KERN_NOTICE "i82365: Card Services release "
- "does not match!\n");
- return -1;
- }
- DEBUG(0, "%s\n", version);
- printk(KERN_INFO "Intel PCIC probe: ");
- sockets = 0;
-
-#ifdef CONFIG_PCI
- if (do_pci_probe && pcibios_present()) {
- pci_probe(PCI_CLASS_BRIDGE_CARDBUS, add_cb_bridge);
- pci_probe(PCI_CLASS_BRIDGE_PCMCIA, add_pci_bridge);
- }
-#endif
-
-#ifdef CONFIG_ISA
- isa_probe();
-#endif
-
- if (sockets == 0) {
- printk("not found.\n");
- return -ENODEV;
- }
-
- /* Set up interrupt handler(s) */
-#ifdef CONFIG_ISA
- if (grab_irq != 0)
- request_irq(cs_irq, pcic_interrupt, 0, "i82365", NULL);
-#endif
-#ifdef CONFIG_PCI
- if (pci_csc) {
- u_int i, irq, mask = 0;
- for (i = 0; i < sockets; i++) {
- irq = socket[i].cap.pci_irq;
- if (irq && !(mask & (1<<irq)))
- request_irq(irq, pcic_interrupt, SA_SHIRQ, "i82365", NULL);
- mask |= (1<<irq);
- }
- }
-#endif
-
- if (register_ss_entry(sockets, &pcic_service) != 0)
- printk(KERN_NOTICE "i82365: register_ss_entry() failed\n");
-
- /* Finally, schedule a polling interrupt */
- if (poll_interval != 0) {
- poll_timer.function = pcic_interrupt_wrapper;
- poll_timer.data = 0;
- poll_timer.prev = poll_timer.next = NULL;
- poll_timer.expires = jiffies + poll_interval;
- add_timer(&poll_timer);
- }
-
- return 0;
-
-} /* init_i82365 */
-
-/*====================================================================*/
-
-static void __exit exit_i82365(void)
-{
- int i;
-#ifdef CONFIG_PROC_FS
- for (i = 0; i < sockets; i++) pcic_proc_remove(i);
-#endif
- unregister_ss_entry(&pcic_service);
- if (poll_interval != 0)
- del_timer(&poll_timer);
-#ifdef CONFIG_ISA
- if (grab_irq != 0)
- free_irq(cs_irq, NULL);
-#endif
-#ifdef CONFIG_PCI
- if (pci_csc) {
- u_int irq, mask = 0;
- for (i = 0; i < sockets; i++) {
- irq = socket[i].cap.pci_irq;
- if (irq && !(mask & (1<<irq)))
- free_irq(irq, NULL);
- mask |= (1<<irq);
- }
- }
-#endif
- for (i = 0; i < sockets; i++) {
- i365_set(i, I365_CSCINT, 0);
-#ifdef CONFIG_PCI
- if (socket[i].cb_virt) {
- iounmap(socket[i].cb_virt);
- release_mem_region(socket[i].cb_phys, 0x1000);
- } else
-#endif
- release_region(socket[i].ioaddr, 2);
- }
-} /* exit_i82365 */
-
-/*====================================================================*/
-
-static void pcic_interrupt_wrapper(u_long data)
-{
- pcic_interrupt(0, NULL, NULL);
- poll_timer.expires = jiffies + poll_interval;
- add_timer(&poll_timer);
-}
-
static void pcic_interrupt(int irq, void *dev,
struct pt_regs *regs)
{
if ((socket[i].cs_irq != irq) &&
(socket[i].cap.pci_irq != irq))
continue;
-#ifdef CONFIG_ISA
- if (!(socket[i].flags & IS_CARDBUS))
- spin_lock_irqsave(&isa_lock, flags);
-#endif
+ ISA_LOCK(i, flags);
csc = i365_get(i, I365_CSC);
+#ifdef CONFIG_PCI
+ if ((socket[i].flags & IS_CARDBUS) &&
+ (cb_readl(i,CB_SOCKET_EVENT) & (CB_SE_CCD1|CB_SE_CCD2))) {
+ cb_writel(i, CB_SOCKET_EVENT, CB_SE_CCD1|CB_SE_CCD2);
+ csc |= I365_CSC_DETECT;
+ }
+#endif
if ((csc == 0) || (!socket[i].handler) ||
(i365_get(i, I365_IDENT) & 0x70)) {
-#ifdef CONFIG_ISA
- if (!(socket[i].flags & IS_CARDBUS))
- spin_unlock_irqrestore(&isa_lock, flags);
-#endif
+ ISA_UNLOCK(i, flags);
continue;
}
events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0;
events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
events |= (csc & I365_CSC_READY) ? SS_READY : 0;
}
-#ifdef CONFIG_ISA
- if (!(socket[i].flags & IS_CARDBUS))
- spin_unlock_irqrestore(&isa_lock, flags);
-#endif
+ ISA_UNLOCK(i, flags);
DEBUG(2, "i82365: socket %d event 0x%02x\n", i, events);
if (events)
socket[i].handler(socket[i].info, events);
DEBUG(4, "i82365: interrupt done\n");
} /* pcic_interrupt */
+static void pcic_interrupt_wrapper(u_long data)
+{
+ pcic_interrupt(0, NULL, NULL);
+ poll_timer.expires = jiffies + poll_interval;
+ add_timer(&poll_timer);
+}
+
/*====================================================================*/
static int pcic_register_callback(u_short sock, ss_callback_t *call)
(t->cap.pci_irq == state->io_irq));
t->bcr &= ~CB_BCR_CB_RESET;
#endif
- set_host_state(sock);
+ set_bridge_state(sock);
/* IO card, RESET flag, IO interrupt */
reg = t->intr;
}
i365_set(sock, I365_CSCINT, reg);
i365_get(sock, I365_CSC);
+#ifdef CONFIG_PCI
+ if (t->flags & IS_CARDBUS) {
+ if (t->cs_irq || (pci_csc && t->cap.pci_irq))
+ cb_writel(sock, CB_SOCKET_MASK, CB_SM_CCD);
+ cb_writel(sock, CB_SOCKET_EVENT, -1);
+ }
+#endif
return 0;
} /* i365_set_socket */
case CB_SC_VCC_3V: state->Vcc = 33; break;
case CB_SC_VCC_5V: state->Vcc = 50; break;
}
- switch (reg & CB_SC_VCC_MASK) {
+ switch (reg & CB_SC_VPP_MASK) {
case CB_SC_VPP_3V: state->Vpp = 33; break;
case CB_SC_VPP_5V: state->Vpp = 50; break;
case CB_SC_VPP_12V: state->Vpp = 120; break;
(s->cap.pci_irq == state->io_irq));
s->bcr &= ~CB_BCR_CB_RESET;
s->bcr |= (state->flags & SS_RESET) ? CB_BCR_CB_RESET : 0;
- set_host_state(sock);
+ set_bridge_state(sock);
cb_set_power(sock, state);
/* Handle IO interrupt using ISA routing */
- reg = i365_get(sock, I365_INTCTL) & ~I365_IRQ_MASK;
+ reg = s->intr;
if (state->io_irq != s->cap.pci_irq) reg |= state->io_irq;
i365_set(sock, I365_INTCTL, reg);
if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
i365_set(sock, I365_CSCINT, reg);
i365_get(sock, I365_CSC);
+ if (s->cs_irq || (pci_csc && s->cap.pci_irq))
+ cb_writel(sock, CB_SOCKET_MASK, CB_SM_CCD);
+ cb_writel(sock, CB_SOCKET_EVENT, -1);
return 0;
} /* cb_set_socket */
#ifdef CONFIG_ISA
u_long flags = 0;
- if (!(socket[sock].flags & IS_CARDBUS))
- spin_lock_irqsave(&isa_lock, flags);
#endif
+ ISA_LOCK(sock, flags);
top = 0x40;
if (socket[sock].flags & IS_CARDBUS)
top = (socket[sock].flags & IS_CIRRUS) ? 0x140 : 0x50;
i365_get(sock,i+2), i365_get(sock,i+3),
((i % 16) == 12) ? "\n" : " ");
}
-#ifdef CONFIG_ISA
- if (!(socket[sock].flags & IS_CARDBUS))
- spin_unlock_irqrestore(&isa_lock, flags);
-#endif
+ ISA_UNLOCK(sock, flags);
return (p - buf);
}
int count, int *eof, void *data)
{
u_short sock = (socket_info_t *)data - socket;
- int len;
-
- len = sprintf(buf, "%08x %08x %08x %08x %08x %08x\n",
- cb_readl(sock,0), cb_readl(sock,4),
- cb_readl(sock,8), cb_readl(sock,12),
- cb_readl(sock,16), cb_readl(sock,32));
- return len;
+ char *p = buf;
+ int i, top;
+
+ top = (socket[sock].flags & IS_O2MICRO) ? 0x30 : 0x20;
+ for (i = 0; i < top; i += 0x10)
+ p += sprintf(p, "%08x %08x %08x %08x\n",
+ cb_readl(sock,i+0x00), cb_readl(sock,i+0x04),
+ cb_readl(sock,i+0x08), cb_readl(sock,i+0x0c));
+ return (p - buf);
}
#endif
static int pcic_service(u_int sock, u_int cmd, void *arg)
{
subfn_t fn;
-
+ int ret;
+#ifdef CONFIG_ISA
+ u_long flags = 0;
+#endif
+
DEBUG(2, "pcic_ioctl(%d, %d, 0x%p)\n", sock, cmd, arg);
if (cmd >= NFUNC)
}
#endif
+ ISA_LOCK(sock, flags);
+ ret = (fn == NULL) ? -EINVAL : fn(sock, arg);
+ ISA_UNLOCK(sock, flags);
+ return ret;
+} /* pcic_service */
+
+/*====================================================================*/
+
+static int __init init_i82365(void)
+{
+ servinfo_t serv;
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "i82365: Card Services release "
+ "does not match!\n");
+ return -1;
+ }
+ DEBUG(0, "%s\n", version);
+ printk(KERN_INFO "Intel PCIC probe: ");
+ sockets = 0;
+
+#ifdef CONFIG_PCI
+ if (do_pci_probe && pcibios_present()) {
+ pci_probe(PCI_CLASS_BRIDGE_CARDBUS, add_cb_bridge);
+ pci_probe(PCI_CLASS_BRIDGE_PCMCIA, add_pci_bridge);
+ }
+#endif
+
#ifdef CONFIG_ISA
- if (!(socket[sock].flags & IS_CARDBUS)) {
- int ret;
- u_long flags;
- spin_lock_irqsave(&isa_lock, flags);
- ret = (fn == NULL) ? -EINVAL : fn(sock, arg);
- spin_unlock_irqrestore(&isa_lock, flags);
- return ret;
+ isa_probe();
+#endif
+
+ if (sockets == 0) {
+ printk("not found.\n");
+ return -ENODEV;
}
+
+ /* Set up interrupt handler(s) */
+#ifdef CONFIG_ISA
+ if (grab_irq != 0)
+ request_irq(cs_irq, pcic_interrupt, 0, "i82365", NULL);
#endif
- return (fn == NULL) ? -EINVAL : fn(sock, arg);
-} /* pcic_service */
+#ifdef CONFIG_PCI
+ if (pci_csc) {
+ u_int i, irq, mask = 0;
+ for (i = 0; i < sockets; i++) {
+ irq = socket[i].cap.pci_irq;
+ if (irq && !(mask & (1<<irq)))
+ request_irq(irq, pcic_interrupt, SA_SHIRQ, "i82365", NULL);
+ mask |= (1<<irq);
+ }
+ }
+#endif
+
+ if (register_ss_entry(sockets, &pcic_service) != 0)
+ printk(KERN_NOTICE "i82365: register_ss_entry() failed\n");
-/*====================================================================*/
+ /* Finally, schedule a polling interrupt */
+ if (poll_interval != 0) {
+ poll_timer.function = pcic_interrupt_wrapper;
+ poll_timer.data = 0;
+ poll_timer.prev = poll_timer.next = NULL;
+ poll_timer.expires = jiffies + poll_interval;
+ add_timer(&poll_timer);
+ }
+
+ return 0;
+
+} /* init_i82365 */
+
+static void __exit exit_i82365(void)
+{
+ int i;
+#ifdef CONFIG_PROC_FS
+ for (i = 0; i < sockets; i++) pcic_proc_remove(i);
+#endif
+ unregister_ss_entry(&pcic_service);
+ if (poll_interval != 0)
+ del_timer(&poll_timer);
+#ifdef CONFIG_ISA
+ if (grab_irq != 0)
+ free_irq(cs_irq, NULL);
+#endif
+#ifdef CONFIG_PCI
+ if (pci_csc) {
+ u_int irq, mask = 0;
+ for (i = 0; i < sockets; i++) {
+ irq = socket[i].cap.pci_irq;
+ if (irq && !(mask & (1<<irq)))
+ free_irq(irq, NULL);
+ mask |= (1<<irq);
+ }
+ }
+#endif
+ for (i = 0; i < sockets; i++) {
+ /* Turn off all interrupt sources! */
+ i365_set(i, I365_CSCINT, 0);
+#ifdef CONFIG_PCI
+ if (socket[i].flags & IS_CARDBUS)
+ cb_writel(i, CB_SOCKET_MASK, 0);
+ if (socket[i].cb_virt) {
+ iounmap(socket[i].cb_virt);
+ release_mem_region(socket[i].cb_phys, 0x1000);
+ } else
+#endif
+ release_region(socket[i].ioaddr, 2);
+ }
+} /* exit_i82365 */
module_init(init_i82365);
module_exit(exit_i82365);
/*
- * o2micro.h 1.10 1999/09/03 16:43:35
+ * o2micro.h 1.12 1999/10/16 01:43:24
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
#ifndef PCI_DEVICE_ID_O2_6836
#define PCI_DEVICE_ID_O2_6836 0x6836
#endif
+#ifndef PCI_DEVICE_ID_O2_6812
+#define PCI_DEVICE_ID_O2_6812 0x6872
+#endif
+
+/* Additional PCI configuration registers */
+
+#define O2_MUX_CONTROL 0x90 /* 32 bit */
+#define O2_MUX_RING_OUT 0x0000000f
+#define O2_MUX_SKTB_ACTV 0x000000f0
+#define O2_MUX_SCTA_ACTV_ENA 0x00000100
+#define O2_MUX_SCTB_ACTV_ENA 0x00000200
+#define O2_MUX_SER_IRQ_ROUTE 0x0000e000
+#define O2_MUX_SER_PCI 0x00010000
+
+#define O2_MUX_SKTA_TURBO 0x000c0000 /* for 6833, 6860 */
+#define O2_MUX_SKTB_TURBO 0x00300000
+#define O2_MUX_AUX_VCC_3V 0x00400000
+#define O2_MUX_PCI_VCC_5V 0x00800000
+#define O2_MUX_PME_MUX 0x0f000000
+
+/* Additional ExCA registers */
#define O2_MODE_A 0x38
-#define O2_MODE_A_2 0x26 /* For 6833B, 6860C */
+#define O2_MODE_A_2 0x26 /* for 6833B, 6860C */
#define O2_MODE_A_CD_PULSE 0x04
#define O2_MODE_A_SUSP_EDGE 0x08
#define O2_MODE_A_HOST_SUSP 0x10
-#define O2_MODE_A_PWRCHIP 0x60
+#define O2_MODE_A_PWR_MASK 0x60
#define O2_MODE_A_QUIET 0x80
#define O2_MODE_B 0x39
-#define O2_MODE_B_2 0x2e /* For 6833B, 6860C */
+#define O2_MODE_B_2 0x2e /* for 6833B, 6860C */
#define O2_MODE_B_IDENT 0x03
#define O2_MODE_B_ID_BSTEP 0x00
#define O2_MODE_B_ID_CSTEP 0x01
#define O2_MODE_C_DREQ_WP 0x02
#define O2_MODE_C_DREQ_BVD2 0x03
#define O2_MODE_C_ZVIDEO 0x08
+#define O2_MODE_C_IREQ_SEL 0x30
+#define O2_MODE_C_MGMT_SEL 0xc0
#define O2_MODE_D 0x3b
#define O2_MODE_D_IRQ_MODE 0x03
+#define O2_MODE_D_PCI_CLKRUN 0x04
+#define O2_MODE_D_CB_CLKRUN 0x08
#define O2_MODE_D_SKT_ACTV 0x20
#define O2_MODE_D_PCI_FIFO 0x40 /* for OZ6729, OZ6730 */
-#define O2_MODE_D_W97_IRQ 0x40 /* for OZ6832 */
+#define O2_MODE_D_W97_IRQ 0x40
#define O2_MODE_D_ISA_IRQ 0x80
#define O2_MHPG_DMA 0x3c
Resource management routines
- rsrc_mgr.c 1.71 1999/09/15 15:32:19
+ rsrc_mgr.c 1.73 1999/10/19 00:54:04
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
#endif
-static spinlock_t rsrc_lock = SPIN_LOCK_UNLOCKED;
-
/*======================================================================
Linux resource management extensions
======================================================================*/
-typedef struct resource_entry_t {
- u_long base, num;
- char *name;
- struct resource_entry_t *next;
-} resource_entry_t;
-
-/* Ordered linked lists of allocated IO and memory blocks */
-static resource_entry_t io_list = { 0, 0, NULL, NULL };
-
-static resource_entry_t *find_gap(resource_entry_t *root,
- resource_entry_t *entry)
-{
- resource_entry_t *p;
-
- if (entry->base > entry->base+entry->num-1)
- return NULL;
- for (p = root; ; p = p->next) {
- if ((p != root) && (p->base+p->num-1 >= entry->base)) {
- p = NULL;
- break;
- }
- if ((p->next == NULL) ||
- (p->next->base > entry->base+entry->num-1))
- break;
- }
- return p;
-}
-
-static int register_my_resource(resource_entry_t *list,
- u_long base, u_long num, char *name)
-{
- u_long flags;
- resource_entry_t *p, *entry;
-
- entry = kmalloc(sizeof(resource_entry_t), GFP_ATOMIC);
- entry->base = base;
- entry->num = num;
- entry->name = name;
-
- spin_lock_irqsave(&rsrc_lock, flags);
- p = find_gap(list, entry);
- if (p == NULL) {
- spin_unlock_irqrestore(&rsrc_lock, flags);
- kfree(entry);
- return -EBUSY;
- }
- entry->next = p->next;
- p->next = entry;
- spin_unlock_irqrestore(&rsrc_lock, flags);
- return 0;
-}
-
-static void release_my_resource(resource_entry_t *list,
- u_long base, u_long num)
-{
- u_long flags;
- resource_entry_t *p, *q;
-
- spin_lock_irqsave(&rsrc_lock, flags);
- for (p = list; ; p = q) {
- q = p->next;
- if (q == NULL) break;
- if ((q->base == base) && (q->num == num)) {
- p->next = q->next;
- kfree(q);
- spin_unlock_irqrestore(&rsrc_lock, flags);
- return;
- }
- }
- spin_unlock_irqrestore(&rsrc_lock, flags);
- return;
-}
-
-static int check_my_resource(resource_entry_t *list,
- u_long base, u_long num)
-{
- if (register_my_resource(list, base, num, NULL) != 0)
- return -EBUSY;
- release_my_resource(list, base, num);
- return 0;
-}
+static spinlock_t rsrc_lock = SPIN_LOCK_UNLOCKED;
-int check_io_region(u_long base, u_long num)
-{
- return check_my_resource(&io_list, base, num);
-}
-void request_io_region(u_long base, u_long num, char *name)
-{
- register_my_resource(&io_list, base, num, name);
-}
-void release_io_region(u_long base, u_long num)
-{
- release_my_resource(&io_list, base, num);
-}
-#ifdef CONFIG_PROC_FS
-int proc_read_io(char *buf, char **start, off_t pos,
- int count, int *eof, void *data)
-{
- resource_entry_t *r;
- u_long flags;
- char *p = buf;
-
- spin_lock_irqsave(&rsrc_lock, flags);
- for (r = io_list.next; r; r = r->next)
- p += sprintf(p, "%04lx-%04lx : %s\n", r->base,
- r->base+r->num-1, r->name);
- spin_unlock_irqrestore(&rsrc_lock, flags);
- return (p - buf);
-}
-#endif
+#define check_io_region(b,n) (0)
/*======================================================================
void release_resource_db(void)
{
resource_map_t *p, *q;
- resource_entry_t *u, *v;
for (p = mem_db.next; p != &mem_db; p = q) {
q = p->next;
q = p->next;
kfree(p);
}
- for (u = io_list.next; u; u = v) {
- v = u->next;
- kfree(u);
- }
}
#ifndef _RSRC_MGR_H
#define _RSRC_MGR_H
-#ifdef __BEOS__
-int check_resource(int type, u_long base, u_long num);
-int register_resource(int type, u_long base, u_long num);
-int release_resource(int type, u_long base, u_long num);
-#endif
-
#endif /* _RSRC_MGR_H */
#define SNDCARD_WAVEFRONT 41
#define SNDCARD_OPL3SA2 42
#define SNDCARD_OPL3SA2_MPU 43
-#define SNDCARD_WAVEARTIST 44 /* Rebel Waveartist */
+#define SNDCARD_WAVEARTIST 44 /* Waveartist */
#define SNDCARD_OPL3SA2_MSS 45 /* Originally missed */
#define SNDCARD_AD1816 88
/*
- * drivers/sound/waveartist.c
+ * linux/drivers/sound/waveartist.c
*
* The low level driver for the RWA010 Rockwell Wave Artist
- * codec chip used in the Corel Computer NetWinder.
+ * codec chip used in the Rebel.com NetWinder.
*
* Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk)
+ * and Pat Beirne (patb@corel.ca)
*/
/*
- * Copyright (C) by Corel Computer 1998
+ * Copyright (C) by Rebel.com 1998-1999
*
* RWA010 specs received under NDA from Rockwell
*
#define debug_flg (0)
-#define DEB(x)
-#define DDB(x)
-#define DEB1(x)
-
#include <linux/module.h>
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/smp.h>
+#include <linux/spinlock.h>
#include <asm/dec21285.h>
#include <asm/hardware.h>
+#include <asm/system.h>
#include "soundmodule.h"
#include "sound_config.h"
#define _ISA_IRQ(x) (x)
#endif
-#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec
-
-#define MIXER_PRIVATE3_RESET 0x53570000
-#define MIXER_PRIVATE3_READ 0x53570001
-#define MIXER_PRIVATE3_WRITE 0x53570002
-
-#define VNC_INTERNAL_SPKR 0x01 //the sw mute on/off control bit
-#define VNC_INTERNAL_MIC 0x10 //the hw internal/handset mic bit
-
-/* Use RECSRC = speaker to mark the internal microphone
- *
- * Some cheating involved here: there is no way to relay
- * to the system, which microphone in in use
- * (left = handset, or right = internal)
- *
- * So while I do not flag SPEAKER in the Recording Devices
- * Mask, when on internal
- *
- * mike - I set the speaker bit hi. Some mixers can be
- * confused a bit...
- */
+#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_LINE |\
+ SOUND_MASK_MIC |\
+ SOUND_MASK_LINE1)
-#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_LINE |\
- SOUND_MASK_MIC |\
- SOUND_MASK_LINE1) //Line1 = analog phone
-
-#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\
- SOUND_MASK_PCM |\
- SOUND_MASK_LINE |\
- SOUND_MASK_MIC | \
- SOUND_MASK_LINE1 |\
- SOUND_MASK_RECLEV |\
- SOUND_MASK_VOLUME)
+#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\
+ SOUND_MASK_PCM |\
+ SOUND_MASK_LINE |\
+ SOUND_MASK_MIC |\
+ SOUND_MASK_LINE1 |\
+ SOUND_MASK_RECLEV |\
+ SOUND_MASK_VOLUME |\
+ SOUND_MASK_IMIX)
static unsigned short levels[SOUND_MIXER_NRDEVICES] = {
0x5555, /* Master Volume */
0x0000, /* Bass */
0x0000, /* Treble */
- 0x5555, /* Synth (FM) */
+ 0x2323, /* Synth (FM) */
0x4b4b, /* PCM */
0x0000, /* PC Speaker */
0x0000, /* Ext Line */
int dev_no;
/* Mixer parameters */
- unsigned short *levels;
- int handset_state;
- signed int slider_vol; /* hardware slider volume */
+ unsigned short *levels; /* cache of volume settings */
int recmask; /* currently enabled recording device! */
- int supported_devices; /* SUPPORTED_MIXER_DEVICES */
+ int supported_devices; /* SUPPORTED_MIXER_DEVICES */
int rec_devices; /* POSSIBLE_RECORDING_DEVICES */
- int handset_mute_sw :1;/* 1 - handset controlled in sw */
- int use_slider :1;/* use slider setting for o/p vol */
- int mute_state :1;
+
+#ifdef CONFIG_ARCH_NETWINDER
+ signed int slider_vol; /* hardware slider volume */
+ unsigned int handset_detect :1;
+ unsigned int telephone_detect:1;
+ unsigned int no_autoselect :1;/* handset/telephone autoselects a path */
+ unsigned int spkr_mute_state :1;/* set by ioctl or autoselect */
+ unsigned int line_mute_state :1;/* set by ioctl or autoselect */
+ unsigned int use_slider :1;/* use slider setting for o/p vol */
+#endif
} wavnc_info;
typedef struct wavnc_port_info {
int audio_format;
} wavnc_port_info;
-static int nr_waveartist_devs;
-static wavnc_info adev_info[MAX_AUDIO_DEV];
-
-static int waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level);
-
-/*
- * Corel Netwinder specifics...
- */
-static struct timer_list vnc_timer;
-extern spinlock_t gpio_lock;
-
-static void
-vnc_mute(wavnc_info *devc, int mute)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&gpio_lock, flags);
- cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE);
- spin_unlock_irqrestore(&gpio_lock, flags);
-
- devc->mute_state = mute;
-}
-
-static int
-vnc_volume_slider(wavnc_info *devc)
-{
- static signed int old_slider_volume;
- unsigned long flags;
- signed int volume = 255;
-
- *CSR_TIMER1_LOAD = 0x00ffffff;
-
- save_flags(flags);
- cli();
-
- outb(0xFF, 0x201);
- *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
-
- while (volume && (inb(0x201) & 0x01))
- volume--;
-
- *CSR_TIMER1_CNTL = 0;
-
- restore_flags(flags);
-
- volume = 0x00ffffff - *CSR_TIMER1_VALUE;
-
+static int nr_waveartist_devs;
+static wavnc_info adev_info[MAX_AUDIO_DEV];
+static spinlock_t waveartist_lock = SPIN_LOCK_UNLOCKED;
-#ifndef REVERSE
- volume = 150 - (volume >> 5);
-#else
- volume = (volume >> 6) - 25;
+#ifndef machine_is_netwinder
+#define machine_is_netwinder() 0
#endif
- if (volume < 0)
- volume = 0;
-
- if (volume > 100)
- volume = 100;
-
- /*
- * slider quite often reads +-8, so debounce this random noise
- */
- if ((volume - old_slider_volume) > 7 ||
- (old_slider_volume - volume) > 7) {
- old_slider_volume = volume;
-
- DEB(printk("Slider volume: %d.\n", old_slider_volume));
- }
-
- return old_slider_volume;
-}
-
-static int
-vnc_slider(wavnc_info *devc)
-{
- signed int slider_volume;
- unsigned int temp;
-
- /*
- * read the "buttons" state.
- * Bit 4 = handset present,
- * Bit 5 = offhook
- */
- // the state should be "querable" via a private IOCTL call
- temp = inb(0x201) & 0x30;
-
- if (!devc->handset_mute_sw &&
- (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) {
- devc->handset_state = temp;
- devc->handset_mute_sw = 0;
-
- vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0);
- }
-
- slider_volume = vnc_volume_slider(devc);
-
- /*
- * If we're using software controlled volume, and
- * the slider moves by more than 20%, then we
- * switch back to slider controlled volume.
- */
- if (devc->slider_vol > slider_volume) {
- if (devc->slider_vol - slider_volume > 20)
- devc->use_slider = 1;
- } else {
- if (slider_volume - devc->slider_vol > 20)
- devc->use_slider = 1;
- }
-
- /*
- * use only left channel
- */
- temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
-
- if (slider_volume != temp && devc->use_slider) {
- devc->slider_vol = slider_volume;
-
- waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
- slider_volume | slider_volume << 8);
-
- return 1;
- }
-
- return 0;
-}
-
-static void
-vnc_slider_tick(unsigned long data)
-{
- int next_timeout;
-
- if (vnc_slider(adev_info + data))
- next_timeout = 5; // mixer reported change
- else
- next_timeout = VNC_TIMER_PERIOD;
-
- mod_timer(&vnc_timer, jiffies + next_timeout);
-}
-
-static int
-vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg)
-{
- wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
- int val, temp;
-
- if (cmd == SOUND_MIXER_PRIVATE1) {
- /*
- * Use this call to override the automatic handset
- * behaviour - ignore handset.
- * bit 7 = total control over handset - do not
- * to plug/unplug
- * bit 4 = internal mic
- * bit 0 = mute internal speaker
- */
- if (get_user(val, (int *)arg))
- return -EFAULT;
-
- devc->handset_mute_sw = val;
-
- temp = val & VNC_INTERNAL_SPKR;
- if (devc->mute_state != temp)
- vnc_mute(devc, temp);
-
- devc->handset_state = val & VNC_INTERNAL_MIC;
- waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
- return 0;
- }
-#if 0
- if (cmd == SOUND_MIXER_PRIVATE2) {
-#define VNC_SOUND_PAUSE 0x53 //to pause the DSP
-#define VNC_SOUND_RESUME 0x57 //to unpause the DSP
- int val;
-
- val = *(int *) arg;
-
- printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val);
-
- if (val == VNC_SOUND_PAUSE) {
- wa_sendcmd(0x16); //PAUSE the ADC
- } else if (val == VNC_SOUND_RESUME) {
- wa_sendcmd(0x18); //RESUME the ADC
- } else {
- return -EINVAL; //invalid parameters...
- }
- return 0;
- }
-
- if (cmd == SOUND_MIXER_PRIVATE3) {
- long unsigned flags;
- int mixer_reg[15]; //reg 14 is actually a command: read,write,reset
-
- int val;
- int i;
-
- val = *(int *) arg;
-
- if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT))
- return (-EFAULT);
-
- memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg));
-
- if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { //reset command??
- wavnc_mixer_reset(devc);
- return (0);
- } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) { //write command??
-// printk("WaveArtist Mixer: Private write command.\n");
-
- wa_sendcmd(0x32); //Pair1 - word 1 and 5
- wa_sendcmd(mixer_reg[0]);
- wa_sendcmd(mixer_reg[4]);
-
- wa_sendcmd(0x32); //Pair2 - word 2 and 6
- wa_sendcmd(mixer_reg[1]);
- wa_sendcmd(mixer_reg[5]);
-
- wa_sendcmd(0x32); //Pair3 - word 3 and 7
- wa_sendcmd(mixer_reg[2]);
- wa_sendcmd(mixer_reg[6]);
-
- wa_sendcmd(0x32); //Pair4 - word 4 and 8
- wa_sendcmd(mixer_reg[3]);
- wa_sendcmd(mixer_reg[7]);
-
- wa_sendcmd(0x32); //Pair5 - word 9 and 10
- wa_sendcmd(mixer_reg[8]);
- wa_sendcmd(mixer_reg[9]);
-
- wa_sendcmd(0x0031); //set left and right PCM
- wa_sendcmd(mixer_reg[0x0A]);
- wa_sendcmd(mixer_reg[0x0B]);
-
- wa_sendcmd(0x0131); //set left and right FM
- wa_sendcmd(mixer_reg[0x0C]);
- wa_sendcmd(mixer_reg[0x0D]);
-
- return 0;
- } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) { //read command?
-// printk("WaveArtist Mixer: Private read command.\n");
-
- //first read all current values...
- save_flags(flags);
- cli();
-
- for (i = 0; i < 14; i++) {
- wa_sendcmd((i << 8) + 0x30); // get ready for command nn30H
-
- while (!(inb(STATR) & CMD_RF)) {
- }; //wait for response ready...
-
- mixer_reg[i] = inw(CMDR);
- }
- restore_flags(flags);
-
- if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT))
- return (-EFAULT);
-
- memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg));
- return 0;
- } else
- return -EINVAL;
- }
-#endif
- return -EINVAL;
-}
+static struct timer_list vnc_timer;
+static void vnc_configure_mixer(wavnc_info *devc);
+static int vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg);
+static void vnc_slider_tick(unsigned long data);
static inline void
waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set)
} while (timeout--);
if (timeout == 0) {
- printk("WaveArtist: reset timeout ");
+ printk(KERN_WARNING "WaveArtist: reset timeout ");
if (res != (unsigned int)-1)
printk("(res=%04X)", res);
printk("\n");
return 0;
}
+/* Helper function to send and receive words
+ * from WaveArtist. It handles all the handshaking
+ * and can send or receive multiple words.
+ */
static int
waveartist_cmd(wavnc_info *devc,
int nr_cmd, unsigned int *cmd,
return timed_out ? 1 : 0;
}
+/*
+ * Send one command word
+ */
+static inline int
+waveartist_cmd1(wavnc_info *devc, unsigned int cmd)
+{
+ return waveartist_cmd(devc, 1, &cmd, 0, NULL);
+}
+
+/*
+ * Send one command, receive one word
+ */
+static inline unsigned int
+waveartist_cmd1_r(wavnc_info *devc, unsigned int cmd)
+{
+ unsigned int ret;
+
+ waveartist_cmd(devc, 1, &cmd, 1, &ret);
+
+ return ret;
+}
+
+/*
+ * Send a double command, receive one
+ * word (and throw it away)
+ */
static inline int
waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg)
{
vals[0] = cmd;
vals[1] = arg;
- waveartist_cmd(devc, 2, vals, 1, vals);
-
- return 0;
+ return waveartist_cmd(devc, 2, vals, 1, vals);
}
+/*
+ * Send a triple command
+ */
static inline int
waveartist_cmd3(wavnc_info *devc, unsigned int cmd,
unsigned int arg1, unsigned int arg2)
}
static int
-waveartist_sendcmd(struct address_info *hw, unsigned int cmd)
-{
- int count;
-
- if (debug_flg & DEBUG_CMD)
- printk("waveartist_sendcmd: cmd=0x%04X...", cmd);
-
- udelay(10);
-
- if (inb(hw->io_base + STATR) & CMD_RF) {
- /*
- * flush the port
- */
- count = inw(hw->io_base + CMDR);
-
- udelay(10);
-
- if (debug_flg & DEBUG_CMD)
- printk(" flushed %04X...", count);
- }
-
- /*
- * preset timeout at 5000 loops
- */
- count = 5000;
-
- while (count --)
- if (inb(hw->io_base + STATR) & CMD_WE) {
- /* wait till CMD_WE is high
- * then output the command
- */
- outw(cmd, hw->io_base + CMDR);
- break;
- }
-
- /* ready BEFORE timeout?
- */
- if (debug_flg & DEBUG_CMD)
- printk(" %s\n", count ? "Done OK." : "Error!");
-
- udelay(10);
-
- return count ? 0 : 1;
-}
-
-static int
-waveartist_getrev(struct address_info *hw, char *rev)
+waveartist_getrev(wavnc_info *devc, char *rev)
{
- int temp;
+ unsigned int temp[2];
+ unsigned int cmd = WACMD_GETREV;
- waveartist_sendcmd(hw, 0);
- udelay(20);
- temp = inw(hw->io_base + CMDR);
- udelay(20);
- inw(hw->io_base + CMDR); // discard second word == 0
+ waveartist_cmd(devc, 1, &cmd, 2, temp);
- rev[0] = temp >> 8;
- rev[1] = temp & 255;
+ rev[0] = temp[0] >> 8;
+ rev[1] = temp[0] & 255;
rev[2] = '\0';
- return temp;
-}
-
-inline void
-waveartist_mute(wavnc_info *devc, int mute)
-{
+ return temp[0];
}
static void waveartist_halt_output(int dev);
devc = (wavnc_info *) audio_devs[dev]->devc;
portc = (wavnc_port_info *) audio_devs[dev]->portc;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&waveartist_lock, flags);
if (portc->open_mode || (devc->open_mode & mode)) {
- restore_flags(flags);
+ spin_unlock_irqrestore(&waveartist_lock, flags);
return -EBUSY;
}
devc->record_dev = dev;
if (mode & OPEN_WRITE)
devc->playback_dev = dev;
- restore_flags(flags);
-
- /*
- * Mute output until the playback really starts. This
- * decreases clicking (hope so).
- */
- waveartist_mute(devc, 1);
+ spin_unlock_irqrestore(&waveartist_lock, flags);
return 0;
}
wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
unsigned long flags;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&waveartist_lock, flags);
waveartist_halt(dev);
devc->open_mode &= ~portc->open_mode;
portc->open_mode = 0;
- waveartist_mute(devc, 1);
-
- restore_flags(flags);
+ spin_unlock_irqrestore(&waveartist_lock, flags);
}
static void
*/
}
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&waveartist_lock, flags);
/*
* set sample count
*/
- waveartist_cmd2(devc, 0x0024, count);
+ waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count);
devc->xfer_count = count;
devc->audio_mode |= PCM_ENABLE_OUTPUT;
- restore_flags(flags);
+ spin_unlock_irqrestore(&waveartist_lock, flags);
}
static void
*/
}
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&waveartist_lock, flags);
/*
* set sample count
*/
- waveartist_cmd2(devc, 0x0014, count);
- waveartist_mute(devc, 0);
+ waveartist_cmd2(devc, WACMD_INPUTSIZE, count);
devc->xfer_count = count;
devc->audio_mode |= PCM_ENABLE_INPUT;
- restore_flags(flags);
+ spin_unlock_irqrestore(&waveartist_lock, flags);
}
static int
speed = waveartist_get_speed(portc);
bits = waveartist_get_bits(portc);
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&waveartist_lock, flags);
if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
- printk("waveartist: error setting the record format to %d\n",
- portc->audio_format);
+ printk(KERN_WARNING "waveartist: error setting the "
+ "record format to %d\n", portc->audio_format);
if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels))
- printk("waveartist: error setting record to %d channels\n",
- portc->channels);
+ printk(KERN_WARNING "waveartist: error setting record "
+ "to %d channels\n", portc->channels);
/*
* write cmd SetSampleSpeedTimeConstant
*/
if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed))
- printk("waveartist: error setting the record speed "
- "to %dHz.\n", portc->speed);
+ printk(KERN_WARNING "waveartist: error setting the record "
+ "speed to %dHz.\n", portc->speed);
if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1))
- printk("waveartist: error setting the record data path "
- "to 0x%X\n", 1);
+ printk(KERN_WARNING "waveartist: error setting the record "
+ "data path to 0x%X\n", 1);
if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
- printk("waveartist: error setting the record format to %d\n",
- portc->audio_format);
+ printk(KERN_WARNING "waveartist: error setting the record "
+ "format to %d\n", portc->audio_format);
devc->xfer_count = 0;
- restore_flags(flags);
+ spin_unlock_irqrestore(&waveartist_lock, flags);
waveartist_halt_input(dev);
if (debug_flg & DEBUG_INTR) {
- printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR));
- printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR));
- printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT));
+ printk("WA CTLR reg: 0x%02X.\n",
+ inb(devc->hw.io_base + CTLR));
+ printk("WA STAT reg: 0x%02X.\n",
+ inb(devc->hw.io_base + STATR));
+ printk("WA IRQS reg: 0x%02X.\n",
+ inb(devc->hw.io_base + IRQSTAT));
}
return 0;
speed = waveartist_get_speed(portc);
bits = waveartist_get_bits(portc);
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&waveartist_lock, flags);
if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) &&
waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed))
- printk("waveartist: error setting the playback speed "
- "to %dHz.\n", portc->speed);
+ printk(KERN_WARNING "waveartist: error setting the playback "
+ "speed to %dHz.\n", portc->speed);
if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels))
- printk("waveartist: error setting the playback to"
- " %d channels\n", portc->channels);
+ printk(KERN_WARNING "waveartist: error setting the playback "
+ "to %d channels\n", portc->channels);
if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0))
- printk("waveartist: error setting the playback data path "
- "to 0x%X\n", 0);
+ printk(KERN_WARNING "waveartist: error setting the playback "
+ "data path to 0x%X\n", 0);
if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits))
- printk("waveartist: error setting the playback format to %d\n",
- portc->audio_format);
+ printk(KERN_WARNING "waveartist: error setting the playback "
+ "format to %d\n", portc->audio_format);
devc->xfer_count = 0;
- restore_flags(flags);
+ spin_unlock_irqrestore(&waveartist_lock, flags);
waveartist_halt_output(dev);
if (debug_flg & DEBUG_INTR) {
wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
wavnc_info *devc;
-
if (portc->open_mode & OPEN_WRITE)
waveartist_halt_output(dev);
wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
unsigned long flags;
- save_flags(flags);
- cli();
-
- waveartist_mute(devc, 1);
-
-//RMK disable_dma(audio_devs[dev]->dmap_in->dma);
+ spin_lock_irqsave(&waveartist_lock, flags);
/*
* Stop capture
*/
- waveartist_sendcmd(&devc->hw, 0x17);
+ waveartist_cmd1(devc, WACMD_INPUTSTOP);
-//RMK enable_dma(audio_devs[dev]->dmap_in->dma);
devc->audio_mode &= ~PCM_ENABLE_INPUT;
/*
// devc->audio_mode &= ~PCM_ENABLE_INPUT;
- restore_flags(flags);
+ spin_unlock_irqrestore(&waveartist_lock, flags);
}
static void
wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
unsigned long flags;
- save_flags(flags);
- cli();
-
- waveartist_mute(devc, 1);
-
-//RMK disable_dma(audio_devs[dev]->dmap_out->dma);
-
- waveartist_sendcmd(&devc->hw, 0x27);
+ spin_lock_irqsave(&waveartist_lock, flags);
-//RMK enable_dma(audio_devs[dev]->dmap_out->dma);
+ waveartist_cmd1(devc, WACMD_OUTPUTSTOP);
devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
// devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
- restore_flags(flags);
+ spin_unlock_irqrestore(&waveartist_lock, flags);
}
static void
printk("\n");
}
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&waveartist_lock, flags);
state &= devc->audio_mode;
/*
* enable ADC Data Transfer to PC
*/
- waveartist_sendcmd(&devc->hw, 0x15);
+ waveartist_cmd1(devc, WACMD_INPUTSTART);
if (portc->open_mode & OPEN_WRITE &&
state & PCM_ENABLE_OUTPUT)
/*
* enable DAC data transfer from PC
*/
- waveartist_sendcmd(&devc->hw, 0x25);
+ waveartist_cmd1(devc, WACMD_OUTPUTSTART);
- waveartist_mute(devc, 0);
-
- restore_flags(flags);
+ spin_unlock_irqrestore(&waveartist_lock, flags);
}
static int
if (status & IRQ_REQ) /* Clear interrupt */
waveartist_iack(devc);
else
- printk("waveartist: unexpected interrupt\n");
+ printk(KERN_WARNING "waveartist: unexpected interrupt\n");
#ifdef CONFIG_AUDIO
if (irqstatus & 0x01) {
temp = 0;
}
if (temp) //default:
- printk("WaveArtist: Unknown interrupt\n");
+ printk(KERN_WARNING "waveartist: Unknown interrupt\n");
}
#endif
if (irqstatus & 0x2)
// We do not use SB mode natively...
- printk("WaveArtist: Unexpected SB interrupt...\n");
+ printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n");
}
/* -------------------------------------------------------------------------
#define SCALE(lev,max) ((lev) * (max) / 100)
+ if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT)
+ whichDev = SOUND_MIXER_VOLUME;
+
switch(whichDev) {
case SOUND_MIXER_VOLUME:
mask = 0x000e;
break;
case SOUND_MIXER_LINE:
+ if ((devc->recmask & SOUND_MASK_LINE) == 0)
+ return;
mask = 0x07c0;
reg_l = 0x000;
reg_r = 0x400;
break;
case SOUND_MIXER_MIC:
+ if ((devc->recmask & SOUND_MASK_MIC) == 0)
+ return;
mask = 0x0030;
reg_l = 0x200;
reg_r = 0x600;
break;
case SOUND_MIXER_LINE1:
+ if ((devc->recmask & SOUND_MASK_LINE1) == 0)
+ return;
mask = 0x003e;
reg_l = 0x000;
reg_r = 0x400;
break;
case SOUND_MIXER_PCM:
- waveartist_cmd3(devc, 0x0031, SCALE(lev_left, 32767),
+ waveartist_cmd3(devc, WACMD_SET_LEVEL,
+ SCALE(lev_left, 32767),
SCALE(lev_right, 32767));
return;
case SOUND_MIXER_SYNTH:
- waveartist_cmd3(devc, 0x0131, SCALE(lev_left, 32767),
+ waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL,
+ SCALE(lev_left, 32767),
SCALE(lev_right, 32767));
return;
}
/* read left setting */
- vals[0] = reg_l + 0x30;
+ vals[0] = reg_l + WACMD_GET_LEVEL;
waveartist_cmd(devc, 1, vals, 1, vals + 1);
/* read right setting */
vals[2] = (vals[2] & ~mask) | (lev_right & mask);
/* write left,right back */
- vals[0] = 0x32;
+ vals[0] = WACMD_SET_MIXER;
waveartist_cmd(devc, 3, vals, 0, NULL);
}
waveartist_select_input(wavnc_info *devc, unsigned int input)
{
unsigned int vals[3];
-#if 1
- /* New mixer programming - switch recording source
- * using R/L_ADC_Mux_Select. We are playing with
- * left/right mux bit fields in reg 9.
- *
- * We can not switch Mux_Select while recording, so
- * for microphones, enable both left and right and
- * play with levels only!
- *
- * Unfortunately, we need to select the src of mono
- * recording (left or right) before starting the
- * recording - so can not dynamically switch between
- * handset amd internal microphones...
- */
/*
* Get reg 9
printk("RECSRC: old left: 0x%04X, old right: 0x%04X.\n",
vals[1] & 0x07, (vals[1] >> 3) & 0x07);
- vals[1] &= ~0x03F; //kill current left/right mux input select
+ /*
+ * kill current left/right mux input select
+ */
+ vals[1] &= ~0x03F;
switch (input) {
- /*
- * Handset or internal MIC
- */
case SOUND_MASK_MIC:
- /*
- * handset not plugged in?
- */
- if (devc->handset_state & VNC_INTERNAL_MIC) {
- /*
- * set mono recording from right mic
- */
- waveartist_sendcmd(&devc->hw, 0x0134);
-#if 0
- /*
- * right=mic, left=none
- */
- vals[1] |= 0x0028;
- /*
- * pretend int mic
- */
- devc->rec_devices |= SOUND_MASK_SPEAKER;
-#endif
- } else {
- /*
- * set mono rec from left mic
- */
- waveartist_sendcmd(&devc->hw, 0x0034);
-#if 0
- /*
- * right=none, left=mic
- */
- vals[1] |= 0x0005;
- /*
- * show no int mic
- */
- devc->rec_devices &= ~SOUND_MASK_SPEAKER;
-#endif
- }
/*
* right=mic, left=mic
*/
break;
case SOUND_MASK_LINE1:
- /*
- * set mono rec from left aux1
- */
- waveartist_sendcmd(&devc->hw, 0x0034);
/*
* right=none, left=Aux1;
*/
break;
case SOUND_MASK_LINE:
- /*
- * set mono rec from left (default)
- */
- waveartist_sendcmd(&devc->hw, 0x0034);
/*
* right=Line, left=Line;
*/
printk("RECSRC %d: left=0x%04X, right=0x%04X.\n", input,
vals[1] & 0x07, (vals[1] >> 3) & 0x07);
-#else
- /* This part is good, if input connected to
- * a mixer, so can be used for record-only modes...
- */
-
- /*
- * get reg 4
- */
- vals[0] = 0x0330;
- waveartist_cmd(devc, 1, vals, 1, vals + 1);
-
- /*
- * get reg 8
- */
- vals[0] = 0x0730;
- waveartist_cmd(devc, 1, vals, 1, vals + 2);
-
- if (debug_flg & DEBUG_MIXER)
- printk("RECSRC: old left: 0x%04X, old right: 0x%04X.\n",
- vals[1], vals[2]);
-
- /*
- * kill current left/right mux input select
- */
- vals[1] &= ~0x07F8;
- vals[2] &= ~0x07F8;
-
- switch (input) {
- /*
- * handset or internal mic
- */
- case SOUND_MASK_MIC:
- /*
- * handset not plugged in?
- */
- if (devc->handset_state & VNC_INTERNAL_MIC) {
- /*
- * set mono recording from right mic
- */
- waveartist_sendcmd(&devc->hw, 0x0134);
- /*
- * left = none, right = mic, RX filter gain
- */
- vals[1] |= 0x0C00;
- vals[2] |= 0x0C88;
- /*
- * pretend int mic
- */
- devc->rec_devices |= SOUND_MASK_SPEAKER;
- } else {
- /*
- * set mono rec from left mic
- */
- waveartist_sendcmd(&devc->hw, 0x0034);
- /*
- * left = mic, RX filter gain, right = none;
- */
- vals[1] |= 0x0C88;
- vals[2] |= 0x0C00;
- /*
- * show no int mic
- */
- devc->rec_devices &= ~SOUND_MASK_SPEAKER;
- }
- break;
-
- case SOUND_MASK_LINE1:
- /*
- * set mono rec from left aux1
- */
- waveartist_sendcmd(&devc->hw, 0x0034);
- /*
- * left = Aux1, right = none
- */
- vals[1] |= 0x0C40;
- vals[2] |= 0x0C00;
- break;
-
- case SOUND_MASK_LINE:
- /*
- * left = Line, right = Line
- */
- vals[1] |= 0x0C10;
- vals[2] |= 0x0C10;
- break;
- }
-
- if (debug_flg & DEBUG_MIXER)
- printk("RECSRC %d: left(4) 0x%04X, right(8) 0x%04X.\n",
- level, vals[1], vals[2]);
-#endif
/*
* and finally - write the reg pair back....
*/
- vals[0] = 0x32;
+ vals[0] = WACMD_SET_MIXER;
waveartist_cmd(devc, 3, vals, 0, NULL);
}
{
unsigned int lev_left = level & 0x007f;
unsigned int lev_right = (level & 0x7f00) >> 8;
-
- int left, right, devmask, changed, i;
+ int left, right, devmask;
left = level & 0x7f;
right = (level & 0x7f00) >> 8;
whichDev, level);
switch (whichDev) {
- /* Master volume (0-7)
- * We have 3 bits on the Left/Right Mixer Gain,
- * bits 3,2,1 on 3 and 7
- */
- case SOUND_MIXER_VOLUME:
+ case SOUND_MIXER_VOLUME: /* master volume (0-7) */
+ case SOUND_MIXER_LINE: /* external line (0-31) */
+ case SOUND_MIXER_MIC: /* mono mic (0-3) */
+ case SOUND_MIXER_RECLEV: /* recording level (0-7) */
+ case SOUND_MIXER_LINE1: /* mono external aux1 (0-31) */
+ case SOUND_MIXER_PCM: /* Waveartist PCM (0-32767) */
+ case SOUND_MIXER_SYNTH: /* internal synth (0-31) */
+ case SOUND_MIXER_IMIX: /* recording feedback */
devc->levels[whichDev] = lev_left | lev_right << 8;
waveartist_mixer_update(devc, whichDev);
break;
-
- /* External line (0-31)
- * use LOUT/ROUT bits 10...6, reg 1 and 5
- */
- case SOUND_MIXER_LINE:
- devc->levels[whichDev] = lev_left | lev_right << 8;
- waveartist_mixer_update(devc, whichDev);
- break;
-
- /* Mono microphone (0-3) mute,
- * 0db,10db,20db
- */
- case SOUND_MIXER_MIC:
-#if 1
- devc->levels[whichDev] = lev_left | lev_right << 8;
-#else
- /* we do not need to mute volume of
- * an unused mic - it is simply unused...
- */
- if (devc->handset_state & VNC_INTERNAL_MIC)
- devc->levels[whichDev] = lev_right << 8;
- else
- levels[whichDev] = lev_left;
-#endif
- waveartist_mixer_update(devc, whichDev);
- break;
-
- /* Recording level (0-7)
- */
- case SOUND_MIXER_RECLEV:
- devc->levels[whichDev] = lev_left | lev_right << 8;
- waveartist_mixer_update(devc, whichDev);
- break;
-
- /* Mono External Aux1 (0-31)
- * use LINE1 bits 5...1, reg 1 and 5
- */
- case SOUND_MIXER_LINE1:
- devc->levels[whichDev] = lev_left | lev_right << 8;
- waveartist_mixer_update(devc, whichDev);
- break;
-
- /* WaveArtist PCM (0-32767)
- */
- case SOUND_MIXER_PCM:
- devc->levels[whichDev] = lev_left | lev_right << 8;
- waveartist_mixer_update(devc, whichDev);
- break;
-
- /* Internal synthesizer (0-31)
- */
- case SOUND_MIXER_SYNTH:
- devc->levels[whichDev] = lev_left | lev_right << 8;
- waveartist_mixer_update(devc, whichDev);
- break;
-
-
/* Select recording input source
*/
case SOUND_MIXER_RECSRC:
- devmask = level & POSSIBLE_RECORDING_DEVICES;
+ devmask = level & devc->rec_devices;
- changed = devmask ^ devc->recmask;
- devc->recmask = devmask;
+#ifdef CONFIG_ARCH_NETWINDER
+ if (machine_is_netwinder())
+ vnc_configure_mixer(devc);
+ else
+#endif
+ {
+ waveartist_select_input(devc, level);
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if (changed & (1 << i))
- waveartist_mixer_update(devc, i);
+ /*
+ * if record monitoring is on, make sure the bit is set
+ */
+ if (devc->levels[SOUND_MIXER_IMIX])
+ waveartist_mixer_update(devc, SOUND_MIXER_IMIX);
+ }
- waveartist_select_input(devc, level);
/*
* do not save in "levels", return current setting
*/
/*
* reset mixer cmd
*/
- waveartist_sendcmd(&devc->hw, 0x33);
+ waveartist_cmd1(devc, WACMD_RST_MIXER);
/*
- * set input for ADC to come from
- * a mux (left and right) == reg 9,
- * initially none
+ * set input for ADC to come from 'quiet'
+ * turn on default modes
*/
- waveartist_cmd3(devc, 0x0032, 0x9800, 0xa836);
+ waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836);
/*
* set mixer input select to none, RX filter gains 0 db
*/
- waveartist_cmd3(devc, 0x0032, 0x4c00, 0x8c00);
+ waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00);
/*
* set bit 0 reg 2 to 1 - unmute MonoOut
*/
- waveartist_cmd3(devc, 0x0032, 0x2801, 0x6800);
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- waveartist_mixer_update(devc, i);
+ waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800);
/* set default input device = internal mic
* current recording device = none
- * no handset
*/
devc->recmask = 0;
- devc->handset_state = VNC_INTERNAL_MIC;
-// waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
- /*
- * start from enabling the hw setting
- */
- devc->handset_mute_sw = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ waveartist_mixer_update(devc, i);
+
devc->supported_devices = SUPPORTED_MIXER_DEVICES;
devc->rec_devices = POSSIBLE_RECORDING_DEVICES;
+
+ if (machine_is_netwinder()) {
+ devc->supported_devices |= SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT;
+ devc->rec_devices |= SOUND_MASK_PHONEIN;
+ }
}
static int
waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+ int ret;
- if (cmd == SOUND_MIXER_PRIVATE1 ||
- cmd == SOUND_MIXER_PRIVATE2 ||
- cmd == SOUND_MIXER_PRIVATE3)
- return vnc_private_ioctl(dev, cmd, arg);
+#ifdef CONFIG_ARCH_NETWINDER
+ if (machine_is_netwinder()) {
+ ret = vnc_private_ioctl(dev, cmd, arg);
+ if (ret != -ENOIOCTLCMD)
+ return ret;
+ }
+#endif
if (((cmd >> 8) & 0xff) == 'M') {
if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
if (get_user(val, (int *)arg))
return -EFAULT;
- /*
- * special case for master volume: if we
- * received this call - switch from hw
- * volume control to a software volume
- * control, till the hw volume is modified
- * to signal that user wants to be back in
- * hardware...
- */
- if ((cmd & 0xff) == SOUND_MIXER_VOLUME)
- devc->use_slider = 0;
-
return waveartist_mixer_set(devc, cmd & 0xff, val);
} else {
- int ret;
-
/*
* Return parameters
*/
switch (cmd & 0xff) {
case SOUND_MIXER_RECSRC:
ret = devc->recmask;
-
- if (devc->handset_state & VNC_INTERNAL_MIC)
- ret |= SOUND_MASK_SPEAKER;
break;
case SOUND_MIXER_DEVMASK:
return -EINVAL;
}
- return put_user(ret, (int *)arg) ? -EINVAL : 0;
+ return put_user(ret, (int *)arg) ? -EFAULT : 0;
}
}
sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name);
- if (waveartist_getrev(&devc->hw, rev)) {
+ if (waveartist_getrev(devc, rev)) {
strcat(dev_name, " rev. ");
strcat(dev_name, rev);
}
waveartist_iack(devc);
if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) {
- printk("%s: IRQ %d in use\n",
+ printk(KERN_ERR "%s: IRQ %d in use\n",
devc->hw.name, devc->hw.irq);
goto uninstall;
}
if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) {
- printk("%s: Can't allocate DMA%d\n",
+ printk(KERN_ERR "%s: Can't allocate DMA%d\n",
devc->hw.name, devc->hw.dma);
goto uninstall_irq;
}
if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA)
if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) {
- printk("%s: can't allocate DMA%d\n",
+ printk(KERN_ERR "%s: can't allocate DMA%d\n",
devc->hw.name, devc->hw.dma2);
goto uninstall_dma;
}
wavnc_info *devc = &adev_info[nr_waveartist_devs];
if (nr_waveartist_devs >= MAX_AUDIO_DEV) {
- printk("waveartist: too many audio devices\n");
+ printk(KERN_WARNING "waveartist: too many audio devices\n");
return 0;
}
if (check_region(hw_config->io_base, 15)) {
- printk("WaveArtist: I/O port conflict\n");
+ printk(KERN_WARNING "WaveArtist: I/O port conflict\n");
return 0;
}
if (hw_config->irq > _ISA_IRQ(15) || hw_config->irq < _ISA_IRQ(0)) {
- printk("WaveArtist: Bad IRQ %d\n", hw_config->irq);
+ printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n",
+ hw_config->irq);
return 0;
}
if (hw_config->dma != _ISA_DMA(3)) {
- printk("WaveArtist: Bad DMA %d\n", hw_config->dma);
+ printk(KERN_WARNING "WaveArtist: Bad DMA %d\n",
+ hw_config->dma);
return 0;
}
if (devc->dev_no < 0)
release_region(hw->io_base, 15);
else {
- init_timer(&vnc_timer);
- vnc_timer.function = vnc_slider_tick;
- vnc_timer.expires = jiffies;
- vnc_timer.data = nr_waveartist_devs;
- add_timer(&vnc_timer);
-
+#ifdef CONFIG_ARCH_NETWINDER
+ if (machine_is_netwinder()) {
+ init_timer(&vnc_timer);
+ vnc_timer.function = vnc_slider_tick;
+ vnc_timer.expires = jiffies;
+ vnc_timer.data = nr_waveartist_devs;
+ add_timer(&vnc_timer);
+
+ vnc_configure_mixer(devc);
+ }
+#endif
nr_waveartist_devs += 1;
-
- vnc_mute(devc, 0);
}
}
if (devc != NULL) {
int mixer;
+#ifdef CONFIG_ARCH_NETWINDER
+ if (machine_is_netwinder())
+ del_timer(&vnc_timer);
+#endif
+
release_region(devc->hw.io_base, 15);
waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0);
devc->hw.dma2 != NO_DMA)
sound_free_dma(devc->hw.dma2);
- del_timer(&vnc_timer);
-
mixer = audio_devs[devc->dev_no]->mixer_dev;
if (mixer >= 0)
for (; i < nr_waveartist_devs; i++)
adev_info[i] = adev_info[i + 1];
} else
- printk("waveartist: can't find device to unload\n");
+ printk(KERN_WARNING "waveartist: can't find device "
+ "to unload\n");
+}
+
+/*
+ * Rebel.com Netwinder specifics...
+ */
+
+#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec
+
+#define MIXER_PRIVATE3_RESET 0x53570000
+#define MIXER_PRIVATE3_READ 0x53570001
+#define MIXER_PRIVATE3_WRITE 0x53570002
+
+#define VNC_MUTE_INTERNAL_SPKR 0x01 //the sw mute on/off control bit
+#define VNC_MUTE_LINE_OUT 0x10
+#define VNC_PHONE_DETECT 0x20
+#define VNC_HANDSET_DETECT 0x40
+#define VNC_DISABLE_AUTOSWITCH 0x80
+
+extern spinlock_t gpio_lock;
+
+static inline void
+vnc_update_spkr_mute(wavnc_info *devc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+static void
+vnc_mute_lout(wavnc_info *devc, int mute)
+{
+}
+
+static int
+vnc_volume_slider(wavnc_info *devc)
+{
+ static signed int old_slider_volume;
+ unsigned long flags;
+ signed int volume = 255;
+
+ *CSR_TIMER1_LOAD = 0x00ffffff;
+
+ save_flags(flags);
+ cli();
+
+ outb(0xFF, 0x201);
+ *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
+
+ while (volume && (inb(0x201) & 0x01))
+ volume--;
+
+ *CSR_TIMER1_CNTL = 0;
+
+ restore_flags(flags);
+
+ volume = 0x00ffffff - *CSR_TIMER1_VALUE;
+
+
+#ifndef REVERSE
+ volume = 150 - (volume >> 5);
+#else
+ volume = (volume >> 6) - 25;
+#endif
+
+ if (volume < 0)
+ volume = 0;
+
+ if (volume > 100)
+ volume = 100;
+
+ /*
+ * slider quite often reads +-8, so debounce this random noise
+ */
+ if (abs(volume - old_slider_volume) > 7) {
+ old_slider_volume = volume;
+
+ if (debug_flg & DEBUG_MIXER)
+ printk(KERN_DEBUG "Slider volume: %d.\n", volume);
+ }
+
+ return old_slider_volume;
+}
+
+static void
+vnc_configure_mixer(wavnc_info *devc)
+{
+ u_int vals[3];
+
+ if (!devc->no_autoselect) {
+ if (devc->handset_detect) {
+ devc->recmask = SOUND_MASK_LINE1;
+ devc->spkr_mute_state = devc->line_mute_state = 1;
+ } else if (devc->telephone_detect) {
+ devc->recmask = SOUND_MASK_PHONEIN;
+ devc->spkr_mute_state = devc->line_mute_state = 1;
+ } else {
+ /* unless someone has asked for LINE-IN,
+ * we default to MIC
+ */
+ if ((devc->recmask & SOUND_MASK_LINE) == 0)
+ devc->recmask = SOUND_MASK_MIC;
+ devc->spkr_mute_state = devc->line_mute_state = 0;
+ }
+ vnc_update_spkr_mute(devc);
+ vnc_mute_lout(devc, devc->spkr_mute_state);
+ }
+
+ /* Ok. At this point, we have done the autoswitch logic, or we
+ * have had a command from an ioctl. We have a valid devc->recmask.
+ * Now we have to connect up the hardware to reflect the recmask.
+ */
+ vals[1] = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x800);
+ vals[2] = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x900);
+
+ vals[1] &= ~0x3f;
+
+ switch(devc->recmask) {
+ case SOUND_MASK_MIC: /* builtin mic */
+ waveartist_cmd1(devc, WACMD_SET_MONO | 0x100); /* right */
+ vals[1] |= 0x28;
+ break;
+
+ case SOUND_MASK_LINE1: /* out handset */
+ waveartist_cmd1(devc, WACMD_SET_MONO); /* left */
+ vals[1] |= 0x05;
+ break;
+
+ case SOUND_MASK_PHONEIN: /* our telephone mic */
+ waveartist_cmd1(devc, WACMD_SET_MONO); /* left */
+ vals[1] |= 0x04;
+ break;
+
+ case SOUND_MASK_LINE: /* stereo line in */
+ vals[1] |= 12;
+ break;
+
+ default:
+ return;
+ }
+
+ vals[0] = WACMD_SET_MIXER;
+ waveartist_cmd(devc, 3, vals, 0, NULL);
+
+ waveartist_mixer_update(devc, SOUND_MIXER_IMIX);
+}
+
+static int
+vnc_slider(wavnc_info *devc)
+{
+ signed int slider_volume;
+ unsigned int temp, old_hs, old_td;
+
+ /*
+ * read the "buttons" state.
+ * Bit 4 = handset present,
+ * Bit 5 = offhook
+ */
+ temp = inb(0x201) & 0x30;
+
+ old_hs = devc->handset_detect;
+ old_td = devc->telephone_detect;
+
+ devc->handset_detect = !(temp & 0x10);
+ devc->telephone_detect = !!(temp & 0x20);
+
+ if (!devc->no_autoselect &&
+ (old_hs != devc->handset_detect ||
+ old_td != devc->telephone_detect))
+ vnc_configure_mixer(devc);
+
+ slider_volume = vnc_volume_slider(devc);
+
+ /*
+ * If we're using software controlled volume, and
+ * the slider moves by more than 20%, then we
+ * switch back to slider controlled volume.
+ */
+ if (abs(devc->slider_vol - slider_volume) > 20)
+ devc->use_slider = 1;
+
+ /*
+ * use only left channel
+ */
+ temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
+
+ if (slider_volume != temp && devc->use_slider) {
+ devc->slider_vol = slider_volume;
+
+ waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
+ slider_volume | slider_volume << 8);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+vnc_slider_tick(unsigned long data)
+{
+ int next_timeout;
+
+ if (vnc_slider(adev_info + data))
+ next_timeout = 5; // mixer reported change
+ else
+ next_timeout = VNC_TIMER_PERIOD;
+
+ mod_timer(&vnc_timer, jiffies + next_timeout);
+}
+
+static int
+vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+ wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+ int val;
+
+ switch (cmd) {
+ case SOUND_MIXER_PRIVATE1:
+ {
+ u_int prev_spkr_mute, prev_line_mute, prev_auto_state;
+ int val;
+
+ get_user_ret(val, (int *)arg, -EFAULT);
+
+ /* check if parameter is logical */
+ if (val & ~(VNC_MUTE_INTERNAL_SPKR |
+ VNC_MUTE_LINE_OUT |
+ VNC_DISABLE_AUTOSWITCH))
+ return -EINVAL;
+
+ prev_auto_state = devc->no_autoselect;
+ prev_spkr_mute = devc->spkr_mute_state;
+ prev_line_mute = devc->line_mute_state;
+
+ devc->no_autoselect = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0;
+ devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0;
+ devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0;
+
+ if (prev_spkr_mute != devc->spkr_mute_state)
+ vnc_update_spkr_mute(devc);
+
+ if (prev_line_mute != devc->line_mute_state)
+ vnc_mute_lout(devc, devc->line_mute_state);
+
+ if (prev_auto_state != devc->no_autoselect)
+ vnc_configure_mixer(devc);
+ waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
+ return 0;
+ }
+
+ case SOUND_MIXER_PRIVATE2:
+ get_user_ret(val, (int *)arg, -EFAULT);
+
+ switch (val) {
+#define VNC_SOUND_PAUSE 0x53 //to pause the DSP
+#define VNC_SOUND_RESUME 0x57 //to unpause the DSP
+ case VNC_SOUND_PAUSE:
+ waveartist_cmd1(devc, 0x16);
+ break;
+
+ case VNC_SOUND_RESUME:
+ waveartist_cmd1(devc, 0x18);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+
+ /* private ioctl to allow bulk access to waveartist */
+ case SOUND_MIXER_PRIVATE3:
+ {
+ unsigned long flags;
+ int mixer_reg[15], i, val;
+
+ get_user_ret(val, (int *)arg, -EFAULT);
+ copy_from_user_ret(mixer_reg, (void *)val, sizeof(mixer_reg), -EFAULT);
+
+ switch (mixer_reg[14]) {
+ case MIXER_PRIVATE3_RESET:
+ waveartist_mixer_reset(devc);
+ break;
+
+ case MIXER_PRIVATE3_WRITE:
+ waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]);
+ waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]);
+ waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]);
+ waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]);
+ waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]);
+
+ waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]);
+ waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]);
+ break;
+
+ case MIXER_PRIVATE3_READ:
+ spin_lock_irqsave(&waveartist_lock, flags);
+
+ for (i = 0x30; i < 14 << 8; i += 1 << 8)
+ waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8));
+
+ spin_unlock_irqrestore(&waveartist_lock, flags);
+
+ copy_to_user_ret((void *)val, mixer_reg, sizeof(mixer_reg), -EFAULT);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ /* read back the state from PRIVATE1 */
+ case SOUND_MIXER_PRIVATE4:
+ val = (devc->spkr_mute_state ? VNC_MUTE_INTERNAL_SPKR : 0) |
+ (devc->line_mute_state ? VNC_MUTE_LINE_OUT : 0) |
+ (devc->handset_detect ? VNC_HANDSET_DETECT : 0) |
+ (devc->telephone_detect ? VNC_PHONE_DETECT : 0) |
+ (devc->no_autoselect ? VNC_DISABLE_AUTOSWITCH : 0);
+
+ return put_user(val, (int *)arg) ? -EFAULT : 0;
+ }
+
+ if (((cmd >> 8) & 0xff) == 'M') {
+ if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+ /*
+ * special case for master volume: if we
+ * received this call - switch from hw
+ * volume control to a software volume
+ * control, till the hw volume is modified
+ * to signal that user wants to be back in
+ * hardware...
+ */
+ if ((cmd & 0xff) == SOUND_MIXER_VOLUME)
+ devc->use_slider = 0;
+ } else if ((cmd & 0xff) == SOUND_MIXER_STEREODEVS) {
+ val = devc->supported_devices &
+ ~(SOUND_MASK_IMIX |
+ SOUND_MASK_MIC |
+ SOUND_MASK_LINE1 |
+ SOUND_MASK_PHONEIN |
+ SOUND_MASK_PHONEOUT);
+ return put_user(val, (int *)arg) ? -EFAULT : 0;
+ }
+ }
+
+ return -ENOIOCTLCMD;
}
#ifdef MODULE
-
-// def file for Rockwell RWA010 chip set, as installed in Corel NetWinder
+/*
+ * linux/drivers/sound/waveartist.h
+ *
+ * def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder
+ */
//registers
#define CMDR 0
//commands
-#define WACMD_SYSTEMID 0
+#define WACMD_SYSTEMID 0x00
+#define WACMD_GETREV 0x00
#define WACMD_INPUTFORMAT 0x10 //0-8S, 1-16S, 2-8U
#define WACMD_INPUTCHANNELS 0x11 //1-Mono, 2-Stereo
#define WACMD_INPUTSPEED 0x12 //sampling rate
#define WACMD_OUTPUTRESUME 0x28 //resume ADC
#define WACMD_OUTPUTPIO 0x29 //PIO ADC
+#define WACMD_GET_LEVEL 0x30
+#define WACMD_SET_LEVEL 0x31
+#define WACMD_SET_MIXER 0x32
+#define WACMD_RST_MIXER 0x33
+#define WACMD_SET_MONO 0x34
-
-
-int wa_sendcmd(unsigned int cmd);
-int wa_writecmd(unsigned int cmd, unsigned int arg);
+int wa_sendcmd(unsigned int cmd);
+int wa_writecmd(unsigned int cmd, unsigned int arg);
{
unsigned int status;
struct uhci_td *tmp;
- int count = 1000, bytesreceived = 0;
+ int count = 1000, actlength, explength;
if (rval)
*rval = 0;
do {
status = uhci_status_bits(tmp->status);
- if (status) {
- if (debug) {
- /* Must reset the toggle on first error */
- if (uhci_debug)
- printk(KERN_DEBUG "Set toggle from %x rval %ld\n",
- (unsigned int)tmp, rval ? *rval : 0);
-
- usb_settoggle(dev->usb, uhci_endpoint(tmp->info),
- uhci_packetout(tmp->info) ^ 1,
- uhci_toggle(tmp->info));
- break;
- }
- }
+ if (status)
+ break;
/* The length field is only valid if the TD was completed */
- if (!status)
if (!(tmp->status & TD_CTRL_ACTIVE) && uhci_packetin(tmp->info)) {
- bytesreceived += uhci_actual_length(tmp->status);
+ explength = uhci_expected_length(tmp->info);
+ actlength = uhci_actual_length(tmp->status);
if (rval)
- *rval += uhci_actual_length(tmp->status);
+ *rval += actlength;
+ if (explength != actlength) {
+ /* Reset the data toggle on error. */
+ if (debug || uhci_debug)
+ printk(KERN_DEBUG "Set toggle from %p rval %ld%c for status=%x to %d, exp=%d, act=%d\n",
+ tmp, rval ? *rval : 0,
+ rval ? '*' : '/', tmp->status,
+ uhci_toggle(tmp->info) ^ 1,
+ explength, actlength);
+ usb_settoggle(dev->usb, uhci_endpoint(tmp->info),
+ uhci_packetout(tmp->info),
+ uhci_toggle(tmp->info) ^ 1);
+ break; // Short packet
+ }
}
if ((tmp->link & UHCI_PTR_TERM) ||
tmp->pipetype == PIPE_CONTROL)
return USB_ST_NOERROR;
-#if 0
/* We got to an error, but the controller hasn't finished */
- /* with it, yet */
+ /* with it yet. */
if (tmp->status & TD_CTRL_ACTIVE)
return USB_ST_NOCHANGE;
-#endif
/* If this wasn't the last TD and SPD is set, ACTIVE */
/* is not and NAK isn't then we received a short */
__u32 nextlink;
unsigned long bytesrequested = len;
unsigned long bytesread = 0;
+#ifdef DUMP_RAW
+ unsigned char *orig_data = (unsigned char *) data;
+#endif
first = td = uhci_td_alloc(dev);
if (!td)
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
}
+#ifdef DUMP_RAW
+ if (!ret && usb_pipein(pipe)) { /* good Input control msg */
+ int i;
+
+ printk (KERN_CRIT "ctrl msg [%02x %02x %04x %04x %04x] on pipe %x returned:\n",
+ cmd->requesttype, cmd->request,
+ cmd->value, cmd->index, cmd->length, pipe);
+ for (i = 0; i < bytesrequested; ) {
+ printk(" %02x", orig_data[i]);
+ if (++i % 16 == 0)
+ printk("\n");
+ }
+ if (i % 16 != 0)
+ printk("\n");
+ }
+#endif
+
return ret;
}
list_add(&td->irq_list, &uhci->interrupt_list);
- usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), usb_pipeout(td->info) ^ 1);
+ usb_dotoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info));
td->info &= ~(1 << TD_TOKEN_TOGGLE); /* clear data toggle */
- td->info |= usb_gettoggle(usb_dev, usb_pipeendpoint(td->info),
+ td->info |= usb_gettoggle(usb_dev, uhci_endpoint(td->info),
uhci_packetout(td->info)) << TD_TOKEN_TOGGLE; /* toggle between data0 and data1 */
td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
/* The HC only removes it when it completed */
/* marked for removal */
td->flags &= ~UHCI_TD_REMOVE;
- usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), uhci_packetout(td->info));
+ usb_dotoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info));
uhci_remove_qh(td->qh->skel, td->qh);
uhci_qh_free(td->qh);
if (td->pipetype == PIPE_INTERRUPT)
/* TD's completed successfully */
status = uhci_td_result(td->dev, td, &rval, 0);
- if ((status == USB_ST_NOERROR) && (td->status & TD_CTRL_ACTIVE))
+ if (status == USB_ST_NOCHANGE)
continue;
/* remove from IRQ list */
}
/* Turn on all interrupts */
- outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
+ outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
+ io_addr + USBINTR);
/* Start at frame 0 */
outw(0, io_addr + USBFRNUM);
#define TD_TOKEN_TOGGLE 19
#define uhci_maxlen(token) ((token) >> 21)
-#define uhci_expected_length(info) ((info >> 21) + 1) /* 1-based */
+#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1)
#define uhci_endpoint(token) (((token) >> 15) & 0xf)
#define uhci_devaddr(token) (((token) >> 8) & 0x7f)
void usb_show_string(struct usb_device *dev, char *id, int index);
#ifdef USB_DEBUG
-#define PRINTD(format, args...) printk("usb: " format "\n" , ## args);
+#define PRINTD(format, args...) printk(KERN_DEBUG "usb: " format "\n" , ## args);
#else /* NOT DEBUGGING */
#define PRINTD(fmt, arg...) do {} while (0)
#endif /* USB_DEBUG */
extern int offb_setup(char*);
extern int atyfb_init(void);
extern int atyfb_setup(char*);
+extern int aty128fb_init(void);
+extern int aty128fb_setup(char*);
extern int igafb_init(void);
extern int igafb_setup(char*);
extern int imsttfb_init(void);
#ifdef CONFIG_FB_ATY
{ "atyfb", atyfb_init, atyfb_setup },
#endif
+#ifdef CONFIG_FB_ATY128
+ { "aty128fb", aty128fb_init, aty128fb_setup },
+#endif
#ifdef CONFIG_FB_IGA
{ "igafb", igafb_init, igafb_setup },
#endif
memset(kaddr+offset+len, 0, PAGE_SIZE-offset-len);
}
err = copy_from_user(kaddr + offset, str, bytes_to_copy);
- flush_page_to_ram(kaddr);
+ flush_page_to_ram(page);
kunmap((unsigned long)kaddr, KM_WRITE);
if (err)
adcs %0, %0, %4
adcs %0, %0, %5
adc %0, %0, #0"
- : "=r"(sum)
- : "r" (sum), "r" (daddr), "r" (saddr), "r" (ntohs(len) << 16), "Ir" (proto*256)
+ : "=&r"(sum)
+ : "r" (sum), "r" (daddr), "r" (saddr), "r" (ntohs(len) << 16), "Ir" (proto << 8)
: "cc");
return sum;
}
struct pci_bus;
struct machdep_calls {
- void (*setup_arch)(unsigned long * memory_start_p,
- unsigned long * memory_end_p);
+ void (*setup_arch)(void);
/* Optional, may be NULL. */
int (*setup_residual)(char *buffer);
/* Optional, may be NULL. */
/* to align the pointer to the (next) page boundary */
#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
-extern void clear_page(unsigned long page);
+extern void clear_page(void *page);
#define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE)
/* map phys->virtual and virtual->phys for RAM pages */
#ifndef _ASM_PCI_BRIDGE_H
#define _ASM_PCI_BRIDGE_H
-unsigned long pmac_find_bridges(unsigned long, unsigned long);
+void pmac_find_bridges(void);
/*
* pci_io_base returns the memory address at which you can access
#ifndef __ASSEMBLY__
#include <linux/mm.h>
+#include <linux/threads.h>
#include <asm/processor.h> /* For TASK_SIZE */
#include <asm/mmu.h>
#include <asm/page.h>
#define flush_cache_page(vma, p) do { } while (0)
extern void flush_icache_range(unsigned long, unsigned long);
-extern void flush_page_to_ram(unsigned long);
+extern void __flush_page_to_ram(unsigned long page_va);
+#define flush_page_to_ram(page) __flush_page_to_ram(page_address(page))
extern unsigned long va_to_phys(unsigned long address);
extern pte_t *va_to_pte(struct task_struct *tsk, unsigned long address);
#define PTRS_PER_PGD 1024
#define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE)
+#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT)
+#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS)
+
+#define pte_ERROR(e) \
+ printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e))
+#define pmd_ERROR(e) \
+ printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
+#define pgd_ERROR(e) \
+ printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
+
/*
* Just any arbitrary offset to the start of the vmalloc VM area: the
* current 64MB value just means that there will be a 64MB "hole" after the
* system. This really does become a problem for machines with good amounts
* of RAM. -- Cort
*/
-#define VMALLOC_OFFSET (0x4000000) /* 64M */
+#define VMALLOC_OFFSET (0x1000000) /* 16M */
#define VMALLOC_START ((((long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)))
#define VMALLOC_VMADDR(x) ((unsigned long)(x))
#define VMALLOC_END ioremap_bot
#define __S110 PAGE_SHARED
#define __S111 PAGE_SHARED
+#ifndef __ASSEMBLY__
+/*
+ * ZERO_PAGE is a global shared page that is always zero: used
+ * for zero-mapped memory areas etc..
+ */
+extern unsigned long empty_zero_page[1024];
+#define ZERO_PAGE(vaddr) (mem_map + MAP_NR(empty_zero_page))
+
/*
* BAD_PAGETABLE is used when we need a bogus page-table, while
* BAD_PAGE is used for a bogus page.
* ZERO_PAGE is a global shared page that is always zero: used
* for zero-mapped memory areas etc..
*/
-#ifndef __ASSEMBLY__
extern pte_t __bad_page(void);
extern pte_t * __bad_pagetable(void);
-extern unsigned long empty_zero_page[1024];
-#endif __ASSEMBLY__
#define BAD_PAGETABLE __bad_pagetable()
#define BAD_PAGE __bad_page()
-#define ZERO_PAGE(vaddr) ((unsigned long) empty_zero_page)
+#endif /* __ASSEMBLY__ */
/* number of bits that fit into a memory pointer */
#define BITS_PER_PTR (8*sizeof(unsigned long))
/* 64-bit machines, beware! SRB. */
#define SIZEOF_PTR_LOG2 2
-#ifndef __ASSEMBLY__
-extern inline int pte_none(pte_t pte) { return !pte_val(pte); }
-extern inline int pte_present(pte_t pte) { return pte_val(pte) & _PAGE_PRESENT; }
-extern inline void pte_clear(pte_t *ptep) { pte_val(*ptep) = 0; }
+#define pte_none(pte) (!pte_val(pte))
+#define pte_present(pte) (pte_val(pte) & _PAGE_PRESENT)
+#define pte_clear(ptep) do { pte_val(*(ptep)) = 0; } while (0)
+#define pte_pagenr(x) ((unsigned long)((pte_val(x) >> PAGE_SHIFT)))
-extern inline int pmd_none(pmd_t pmd) { return !pmd_val(pmd); }
-extern inline int pmd_bad(pmd_t pmd) { return (pmd_val(pmd) & ~PAGE_MASK) != 0; }
-extern inline int pmd_present(pmd_t pmd) { return (pmd_val(pmd) & PAGE_MASK) != 0; }
-extern inline void pmd_clear(pmd_t * pmdp) { pmd_val(*pmdp) = 0; }
+#define pmd_none(pmd) (!pmd_val(pmd))
+#define pmd_bad(pmd) ((pmd_val(pmd) & ~PAGE_MASK) != 0)
+#define pmd_present(pmd) ((pmd_val(pmd) & PAGE_MASK) != 0)
+#define pmd_clear(pmdp) do { pmd_val(*(pmdp)) = 0; } while (0)
+/*
+ * Permanent address of a page.
+ */
+#define page_address(page) (PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT))
+#define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT))
+#define pte_page(x) (mem_map+pte_pagenr(x))
+#ifndef __ASSEMBLY__
/*
* The "pgd_xxx()" functions here are trivial for a folded two-level
* setup: the pgd is never bad, and a pmd always exists (as it's folded
extern inline int pgd_none(pgd_t pgd) { return 0; }
extern inline int pgd_bad(pgd_t pgd) { return 0; }
extern inline int pgd_present(pgd_t pgd) { return 1; }
-extern inline void pgd_clear(pgd_t * pgdp) { }
+#define pgd_clear(xp) do { pgd_val(*(xp)) = 0; } while (0)
+
+#define pgd_page(pgd) \
+ ((unsigned long) __va(pgd_val(pgd) & PAGE_MASK))
/*
* The following only work if pte_present() is true.
* within a page table are directly modified. Thus, the following
* hook is made available.
*/
-#if 1
#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
-#else
-extern inline void set_pte(pte_t *pteptr, pte_t pteval)
-{
- unsigned long val = pte_val(pteval);
- extern void xmon(void *);
-
- if ((val & _PAGE_PRESENT) && ((val < 0x111000 || (val & 0x800)
- || ((val & _PAGE_HWWRITE) && (~val & (_PAGE_RW|_PAGE_DIRTY)))) {
- printk("bad pte val %lx ptr=%p\n", val, pteptr);
- xmon(0);
- }
- *pteptr = pteval;
-}
-#endif
/*
* Conversion functions: convert a page and protection to a page entry,
* and a page entry and page directory to the page they refer to.
*/
-static inline pte_t mk_pte_phys(unsigned long page, pgprot_t pgprot)
-{ pte_t pte; pte_val(pte) = (page) | pgprot_val(pgprot); return pte; }
+extern inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot)
+{
+ pte_t pte;
+ pte_val(pte) = physpage | pgprot_val(pgprot);
+ return pte;
+}
-extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot)
-{ pte_t pte; pte_val(pte) = __pa(page) | pgprot_val(pgprot); return pte; }
+extern inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
+{
+ pte_t pte;
+ pte_val(pte) = ((page - mem_map) << PAGE_SHIFT) | pgprot_val(pgprot);
+ return pte;
+}
extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
-{ pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; }
-
-extern inline unsigned long pte_page(pte_t pte)
-{ return (unsigned long) __va(pte_val(pte) & PAGE_MASK); }
+{
+ pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot);
+ return pte;
+}
-extern inline unsigned long pmd_page(pmd_t pmd)
-{ return pmd_val(pmd); }
+#define page_pte_prot(page,prot) mk_pte(page, prot)
+#define page_pte(page) page_pte_prot(page, __pgprot(0))
+#define pmd_page(pmd) (pmd_val(pmd))
/* to find an entry in a kernel page-table-directory */
#define pgd_offset_k(address) pgd_offset(&init_mm, address)
extern __inline__ pgd_t *get_pgd_slow(void)
{
- pgd_t *ret/* = (pgd_t *)__get_free_page(GFP_KERNEL)*/, *init;
+ pgd_t *ret, *init;
if ( (ret = (pgd_t *)get_zero_page_fast()) == NULL )
{
}
if (ret) {
init = pgd_offset(&init_mm, 0);
- /*memset (ret, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));*/
memcpy (ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
}
extern void flush_hash_page(unsigned context, unsigned long va);
-#define SWP_TYPE(entry) (((entry) >> 1) & 0x7f)
-#define SWP_OFFSET(entry) ((entry) >> 8)
-#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
+#define SWP_TYPE(entry) (((pte_val(entry)) >> 1) & 0x7f)
+#define SWP_OFFSET(entry) ((pte_val(entry)) >> 8)
+#define SWP_ENTRY(type,offset) __pte(((type) << 1) | ((offset) << 8))
#define module_map vmalloc
#define module_unmap vfree
#define ACPI_MINOR_DEV 167
/* RSDP location */
-#define ACPI_BIOS_ROM_BASE ((__u8*) 0xe0000)
-#define ACPI_BIOS_ROM_END ((__u8*) 0x100000)
+#define ACPI_BIOS_ROM_BASE (0x0e0000)
+#define ACPI_BIOS_ROM_END (0x100000)
/* Table signatures */
#define ACPI_RSDP1_SIG 0x20445352 /* 'RSD ' */
BUG();
kaddr = kmap(page, KM_WRITE);
memset((void *)(kaddr + offset), 0, size);
- flush_page_to_ram(kaddr);
+ flush_page_to_ram(page);
kunmap(kaddr, KM_WRITE);
}
/*
- * cs.h 1.67 1999/09/02 18:34:59
+ * cs.h 1.68 1999/10/20 18:59:32
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
#define WIN_SHARED 0x0040
#define WIN_FIRST_SHARED 0x0080
#define WIN_USE_WAIT 0x0100
+#define WIN_STRICT_ALIGN 0x0200
#define WIN_MAP_BELOW_1MB 0x0400
#define WIN_PREFETCH 0x0800
#define WIN_CACHEABLE 0x1000
extern int CardServices(int func, ...);
#endif
-#ifdef __BEOS__
-#define SS_MODULE_NAME(s) ("busses/pcmcia/" s "/v1")
-#define MTD_MODULE_NAME(s) ("busses/pcmcia/" s "/v1")
-#define CS_CLIENT_MODULE_NAME "bus_managers/pcmcia_cs/client/v1"
-typedef struct cs_client_module_info {
- bus_manager_info binfo;
- int (*_CardServices)(int, ...);
- int (*_MTDHelperEntry)(int, ...);
- void (*_add_timer)(struct timer_list *);
- void (*_del_timer)(struct timer_list *);
-} cs_client_module_info;
-#define CS_SOCKET_MODULE_NAME "bus_managers/pcmcia_cs/socket/v1"
-typedef struct cs_socket_module_info {
- bus_manager_info binfo;
- int (*_register_ss_entry)(int, ss_entry_t);
- void (*_unregister_ss_entry)(ss_entry_t);
- void (*_add_timer)(struct timer_list *);
- void (*_del_timer)(struct timer_list *);
- int (*register_resource)(int, u_long, u_long);
- int (*release_resource)(int, u_long, u_long);
- int (*check_resource)(int, u_long, u_long);
-} cs_socket_module_info;
-#endif
-
#endif /* __KERNEL__ */
#endif /* _LINUX_CS_H */
#ifndef _LINUX_CS_TYPES_H
#define _LINUX_CS_TYPES_H
-#ifdef __linux__
#include <linux/types.h>
-#endif
typedef u_short socket_t;
typedef u_short ioaddr_t;
int register_driver(struct driver_operations *ops);
void unregister_driver(struct driver_operations *ops);
-#ifdef __BEOS__
-#define CB_ENABLER_MODULE_NAME "bus_managers/cb_enabler/v1"
-typedef struct cb_enabler_module_info {
- bus_manager_info binfo;
- int (*register_driver)(struct driver_operations *ops);
- void (*unregister_driver)(struct driver_operations *ops);
-} cb_enabler_module_info;
-#endif /* __BEOS__ */
-
#endif /* __KERNEL__ */
#endif /* _LINUX_DRIVER_OPS_H */
#define register_pcmcia_driver register_pccard_driver
#define unregister_pcmcia_driver unregister_pccard_driver
-#ifdef __BEOS__
-#define DS_MODULE_NAME "bus_managers/pcmcia_ds/v1"
-typedef struct ds_module_info {
- bus_manager_info binfo;
- int (*_register_pccard_driver)(dev_info_t *,
- dev_link_t *(*)(void),
- void (*)(dev_link_t *));
- int (*_unregister_pccard_driver)(dev_info_t *);
- struct driver_info_t **root_driver;
- int *sockets;
- struct socket_info_t **socket_table;
- sem_id *list_sem;
-} ds_module_info;
-#endif /* __BEOS__ */
-
#endif /* __KERNEL__ */
#endif /* _LINUX_DS_H */
-/* version.h 1.72 1999/08/05 06:09:53 (David Hinds) */
+/* version.h 1.74 1999/09/29 20:41:44 (David Hinds) */
-#define CS_RELEASE "3.1.0"
-#define CS_RELEASE_CODE 0x3100
+#define CS_RELEASE "3.1.2"
+#define CS_RELEASE_CODE 0x3102
if (write) {
maddr = kmap(page, KM_WRITE);
memcpy((char *)maddr + (addr & ~PAGE_MASK), buf, len);
- flush_page_to_ram(maddr);
+ flush_page_to_ram(page);
kunmap(maddr, KM_WRITE);
} else {
maddr = kmap(page, KM_READ);
memcpy(buf, (char *)maddr + (addr & ~PAGE_MASK), len);
- flush_page_to_ram(maddr);
+ flush_page_to_ram(page);
kunmap(maddr, KM_READ);
}
return len;
__free_page(page);
return 0;
}
- flush_page_to_ram(pte_page(page));
+ flush_page_to_ram(page);
set_pte(pte, pte_mkwrite(page_pte_prot(page, PAGE_COPY)));
/* no need for flush_tlb */
return page;
NULL /* revalidate */
};
-struct inode swapper_inode = { i_op: &swapper_inode_operations };
+struct inode swapper_inode = {
+ i_op: &swapper_inode_operations,
+ i_pages: {&swapper_inode.i_pages,&swapper_inode.i_pages}
+};
#ifdef SWAP_CACHE_INFO
unsigned long swap_cache_add_total = 0;
if (pte_none(page))
continue;
if (pte_present(page)) {
- __free_page(mem_map+pte_pagenr(page));
+ unsigned long map_nr = pte_pagenr(page);
+ if (map_nr < max_mapnr)
+ __free_page(mem_map + map_nr);
continue;
}
printk("Whee.. Swapped out page in kernel page table\n");