162306a36Sopenharmony_ci/*      cops.c: LocalTalk driver for Linux.
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci *	Authors:
462306a36Sopenharmony_ci *      - Jay Schulist <jschlst@samba.org>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *	With more than a little help from;
762306a36Sopenharmony_ci *	- Alan Cox <alan@lxorguk.ukuu.org.uk>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *      Derived from:
1062306a36Sopenharmony_ci *      - skeleton.c: A network driver outline for linux.
1162306a36Sopenharmony_ci *        Written 1993-94 by Donald Becker.
1262306a36Sopenharmony_ci *	- ltpc.c: A driver for the LocalTalk PC card.
1362306a36Sopenharmony_ci *	  Written by Bradford W. Johnson.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *      Copyright 1993 United States Government as represented by the
1662306a36Sopenharmony_ci *      Director, National Security Agency.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *      This software may be used and distributed according to the terms
1962306a36Sopenharmony_ci *      of the GNU General Public License, incorporated herein by reference.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci *	Changes:
2262306a36Sopenharmony_ci *	19970608	Alan Cox	Allowed dual card type support
2362306a36Sopenharmony_ci *					Can set board type in insmod
2462306a36Sopenharmony_ci *					Hooks for cops_setup routine
2562306a36Sopenharmony_ci *					(not yet implemented).
2662306a36Sopenharmony_ci *	19971101	Jay Schulist	Fixes for multiple lt* devices.
2762306a36Sopenharmony_ci *	19980607	Steven Hirsch	Fixed the badly broken support
2862306a36Sopenharmony_ci *					for Tangent type cards. Only
2962306a36Sopenharmony_ci *                                      tested on Daystar LT200. Some
3062306a36Sopenharmony_ci *                                      cleanup of formatting and program
3162306a36Sopenharmony_ci *                                      logic.  Added emacs 'local-vars'
3262306a36Sopenharmony_ci *                                      setup for Jay's brace style.
3362306a36Sopenharmony_ci *	20000211	Alan Cox	Cleaned up for softnet
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic const char *version =
3762306a36Sopenharmony_ci"cops.c:v0.04 6/7/98 Jay Schulist <jschlst@samba.org>\n";
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci *  Sources:
4062306a36Sopenharmony_ci *      COPS Localtalk SDK. This provides almost all of the information
4162306a36Sopenharmony_ci *      needed.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/*
4562306a36Sopenharmony_ci * insmod/modprobe configurable stuff.
4662306a36Sopenharmony_ci *	- IO Port, choose one your card supports or 0 if you dare.
4762306a36Sopenharmony_ci *	- IRQ, also choose one your card supports or nothing and let
4862306a36Sopenharmony_ci *	  the driver figure it out.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#include <linux/module.h>
5262306a36Sopenharmony_ci#include <linux/kernel.h>
5362306a36Sopenharmony_ci#include <linux/types.h>
5462306a36Sopenharmony_ci#include <linux/fcntl.h>
5562306a36Sopenharmony_ci#include <linux/interrupt.h>
5662306a36Sopenharmony_ci#include <linux/ptrace.h>
5762306a36Sopenharmony_ci#include <linux/ioport.h>
5862306a36Sopenharmony_ci#include <linux/in.h>
5962306a36Sopenharmony_ci#include <linux/string.h>
6062306a36Sopenharmony_ci#include <linux/errno.h>
6162306a36Sopenharmony_ci#include <linux/init.h>
6262306a36Sopenharmony_ci#include <linux/netdevice.h>
6362306a36Sopenharmony_ci#include <linux/etherdevice.h>
6462306a36Sopenharmony_ci#include <linux/skbuff.h>
6562306a36Sopenharmony_ci#include <linux/if_arp.h>
6662306a36Sopenharmony_ci#include <linux/if_ltalk.h>
6762306a36Sopenharmony_ci#include <linux/delay.h>	/* For udelay() */
6862306a36Sopenharmony_ci#include <linux/atalk.h>
6962306a36Sopenharmony_ci#include <linux/spinlock.h>
7062306a36Sopenharmony_ci#include <linux/bitops.h>
7162306a36Sopenharmony_ci#include <linux/jiffies.h>
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#include <net/Space.h>
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#include <asm/io.h>
7662306a36Sopenharmony_ci#include <asm/dma.h>
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#include "cops.h"		/* Our Stuff */
7962306a36Sopenharmony_ci#include "cops_ltdrv.h"		/* Firmware code for Tangent type cards. */
8062306a36Sopenharmony_ci#include "cops_ffdrv.h"		/* Firmware code for Dayna type cards. */
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/*
8362306a36Sopenharmony_ci *      The name of the card. Is used for messages and in the requests for
8462306a36Sopenharmony_ci *      io regions, irqs and dma channels
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic const char *cardname = "cops";
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#ifdef CONFIG_COPS_DAYNA
9062306a36Sopenharmony_cistatic int board_type = DAYNA;	/* Module exported */
9162306a36Sopenharmony_ci#else
9262306a36Sopenharmony_cistatic int board_type = TANGENT;
9362306a36Sopenharmony_ci#endif
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int io = 0x240;		/* Default IO for Dayna */
9662306a36Sopenharmony_cistatic int irq = 5;		/* Default IRQ */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/*
9962306a36Sopenharmony_ci *	COPS Autoprobe information.
10062306a36Sopenharmony_ci *	Right now if port address is right but IRQ is not 5 this will
10162306a36Sopenharmony_ci *      return a 5 no matter what since we will still get a status response.
10262306a36Sopenharmony_ci *      Need one more additional check to narrow down after we have gotten
10362306a36Sopenharmony_ci *      the ioaddr. But since only other possible IRQs is 3 and 4 so no real
10462306a36Sopenharmony_ci *	hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with
10562306a36Sopenharmony_ci *	this driver.
10662306a36Sopenharmony_ci *
10762306a36Sopenharmony_ci *	This driver has 2 modes and they are: Dayna mode and Tangent mode.
10862306a36Sopenharmony_ci *	Each mode corresponds with the type of card. It has been found
10962306a36Sopenharmony_ci *	that there are 2 main types of cards and all other cards are
11062306a36Sopenharmony_ci *	the same and just have different names or only have minor differences
11162306a36Sopenharmony_ci *	such as more IO ports. As this driver is tested it will
11262306a36Sopenharmony_ci *	become more clear on exactly what cards are supported. The driver
11362306a36Sopenharmony_ci *	defaults to using Dayna mode. To change the drivers mode, simply
11462306a36Sopenharmony_ci *	select Dayna or Tangent mode when configuring the kernel.
11562306a36Sopenharmony_ci *
11662306a36Sopenharmony_ci *      This driver should support:
11762306a36Sopenharmony_ci *      TANGENT driver mode:
11862306a36Sopenharmony_ci *              Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200,
11962306a36Sopenharmony_ci *		COPS LT-1
12062306a36Sopenharmony_ci *      DAYNA driver mode:
12162306a36Sopenharmony_ci *              Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95,
12262306a36Sopenharmony_ci *		Farallon PhoneNET PC III, Farallon PhoneNET PC II
12362306a36Sopenharmony_ci *	Other cards possibly supported mode unknown though:
12462306a36Sopenharmony_ci *		Dayna DL2000 (Full length), COPS LT/M (Micro-Channel)
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci *	Cards NOT supported by this driver but supported by the ltpc.c
12762306a36Sopenharmony_ci *	driver written by Bradford W. Johnson <johns393@maroon.tc.umn.edu>
12862306a36Sopenharmony_ci *		Farallon PhoneNET PC
12962306a36Sopenharmony_ci *		Original Apple LocalTalk PC card
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci *      N.B.
13262306a36Sopenharmony_ci *
13362306a36Sopenharmony_ci *      The Daystar Digital LT200 boards do not support interrupt-driven
13462306a36Sopenharmony_ci *      IO.  You must specify 'irq=0xff' as a module parameter to invoke
13562306a36Sopenharmony_ci *      polled mode.  I also believe that the port probing logic is quite
13662306a36Sopenharmony_ci *      dangerous at best and certainly hopeless for a polled card.  Best to
13762306a36Sopenharmony_ci *      specify both. - Steve H.
13862306a36Sopenharmony_ci *
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/*
14262306a36Sopenharmony_ci * Zero terminated list of IO ports to probe.
14362306a36Sopenharmony_ci */
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic unsigned int ports[] = {
14662306a36Sopenharmony_ci	0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260,
14762306a36Sopenharmony_ci	0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360,
14862306a36Sopenharmony_ci	0
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/*
15262306a36Sopenharmony_ci * Zero terminated list of IRQ ports to probe.
15362306a36Sopenharmony_ci */
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int cops_irqlist[] = {
15662306a36Sopenharmony_ci	5, 4, 3, 0
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic struct timer_list cops_timer;
16062306a36Sopenharmony_cistatic struct net_device *cops_timer_dev;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/* use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */
16362306a36Sopenharmony_ci#ifndef COPS_DEBUG
16462306a36Sopenharmony_ci#define COPS_DEBUG 1
16562306a36Sopenharmony_ci#endif
16662306a36Sopenharmony_cistatic unsigned int cops_debug = COPS_DEBUG;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci/* The number of low I/O ports used by the card. */
16962306a36Sopenharmony_ci#define COPS_IO_EXTENT       8
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/* Information that needs to be kept for each board. */
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistruct cops_local
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci        int board;			/* Holds what board type is. */
17662306a36Sopenharmony_ci	int nodeid;			/* Set to 1 once have nodeid. */
17762306a36Sopenharmony_ci        unsigned char node_acquire;	/* Node ID when acquired. */
17862306a36Sopenharmony_ci        struct atalk_addr node_addr;	/* Full node address */
17962306a36Sopenharmony_ci	spinlock_t lock;		/* RX/TX lock */
18062306a36Sopenharmony_ci};
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/* Index to functions, as function prototypes. */
18362306a36Sopenharmony_cistatic int  cops_probe1 (struct net_device *dev, int ioaddr);
18462306a36Sopenharmony_cistatic int  cops_irq (int ioaddr, int board);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic int  cops_open (struct net_device *dev);
18762306a36Sopenharmony_cistatic int  cops_jumpstart (struct net_device *dev);
18862306a36Sopenharmony_cistatic void cops_reset (struct net_device *dev, int sleep);
18962306a36Sopenharmony_cistatic void cops_load (struct net_device *dev);
19062306a36Sopenharmony_cistatic int  cops_nodeid (struct net_device *dev, int nodeid);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic irqreturn_t cops_interrupt (int irq, void *dev_id);
19362306a36Sopenharmony_cistatic void cops_poll(struct timer_list *t);
19462306a36Sopenharmony_cistatic void cops_timeout(struct net_device *dev, unsigned int txqueue);
19562306a36Sopenharmony_cistatic void cops_rx (struct net_device *dev);
19662306a36Sopenharmony_cistatic netdev_tx_t  cops_send_packet (struct sk_buff *skb,
19762306a36Sopenharmony_ci					    struct net_device *dev);
19862306a36Sopenharmony_cistatic void set_multicast_list (struct net_device *dev);
19962306a36Sopenharmony_cistatic int  cops_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
20062306a36Sopenharmony_cistatic int  cops_close (struct net_device *dev);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic void cleanup_card(struct net_device *dev)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	if (dev->irq)
20562306a36Sopenharmony_ci		free_irq(dev->irq, dev);
20662306a36Sopenharmony_ci	release_region(dev->base_addr, COPS_IO_EXTENT);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/*
21062306a36Sopenharmony_ci *      Check for a network adaptor of this type, and return '0' iff one exists.
21162306a36Sopenharmony_ci *      If dev->base_addr == 0, probe all likely locations.
21262306a36Sopenharmony_ci *      If dev->base_addr in [1..0x1ff], always return failure.
21362306a36Sopenharmony_ci *        otherwise go with what we pass in.
21462306a36Sopenharmony_ci */
21562306a36Sopenharmony_cistruct net_device * __init cops_probe(int unit)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct net_device *dev;
21862306a36Sopenharmony_ci	unsigned *port;
21962306a36Sopenharmony_ci	int base_addr;
22062306a36Sopenharmony_ci	int err = 0;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	dev = alloc_ltalkdev(sizeof(struct cops_local));
22362306a36Sopenharmony_ci	if (!dev)
22462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (unit >= 0) {
22762306a36Sopenharmony_ci		sprintf(dev->name, "lt%d", unit);
22862306a36Sopenharmony_ci		netdev_boot_setup_check(dev);
22962306a36Sopenharmony_ci		irq = dev->irq;
23062306a36Sopenharmony_ci		base_addr = dev->base_addr;
23162306a36Sopenharmony_ci	} else {
23262306a36Sopenharmony_ci		base_addr = dev->base_addr = io;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (base_addr > 0x1ff) {    /* Check a single specified location. */
23662306a36Sopenharmony_ci		err = cops_probe1(dev, base_addr);
23762306a36Sopenharmony_ci	} else if (base_addr != 0) { /* Don't probe at all. */
23862306a36Sopenharmony_ci		err = -ENXIO;
23962306a36Sopenharmony_ci	} else {
24062306a36Sopenharmony_ci		/* FIXME  Does this really work for cards which generate irq?
24162306a36Sopenharmony_ci		 * It's definitely N.G. for polled Tangent. sh
24262306a36Sopenharmony_ci		 * Dayna cards don't autoprobe well at all, but if your card is
24362306a36Sopenharmony_ci		 * at IRQ 5 & IO 0x240 we find it every time. ;) JS
24462306a36Sopenharmony_ci		 */
24562306a36Sopenharmony_ci		for (port = ports; *port && cops_probe1(dev, *port) < 0; port++)
24662306a36Sopenharmony_ci			;
24762306a36Sopenharmony_ci		if (!*port)
24862306a36Sopenharmony_ci			err = -ENODEV;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci	if (err)
25162306a36Sopenharmony_ci		goto out;
25262306a36Sopenharmony_ci	err = register_netdev(dev);
25362306a36Sopenharmony_ci	if (err)
25462306a36Sopenharmony_ci		goto out1;
25562306a36Sopenharmony_ci	return dev;
25662306a36Sopenharmony_ciout1:
25762306a36Sopenharmony_ci	cleanup_card(dev);
25862306a36Sopenharmony_ciout:
25962306a36Sopenharmony_ci	free_netdev(dev);
26062306a36Sopenharmony_ci	return ERR_PTR(err);
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic const struct net_device_ops cops_netdev_ops = {
26462306a36Sopenharmony_ci	.ndo_open               = cops_open,
26562306a36Sopenharmony_ci        .ndo_stop               = cops_close,
26662306a36Sopenharmony_ci	.ndo_start_xmit   	= cops_send_packet,
26762306a36Sopenharmony_ci	.ndo_tx_timeout		= cops_timeout,
26862306a36Sopenharmony_ci        .ndo_do_ioctl           = cops_ioctl,
26962306a36Sopenharmony_ci	.ndo_set_rx_mode	= set_multicast_list,
27062306a36Sopenharmony_ci};
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/*
27362306a36Sopenharmony_ci *      This is the real probe routine. Linux has a history of friendly device
27462306a36Sopenharmony_ci *      probes on the ISA bus. A good device probes avoids doing writes, and
27562306a36Sopenharmony_ci *      verifies that the correct device exists and functions.
27662306a36Sopenharmony_ci */
27762306a36Sopenharmony_cistatic int __init cops_probe1(struct net_device *dev, int ioaddr)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci        struct cops_local *lp;
28062306a36Sopenharmony_ci	static unsigned version_printed;
28162306a36Sopenharmony_ci	int board = board_type;
28262306a36Sopenharmony_ci	int retval;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci        if(cops_debug && version_printed++ == 0)
28562306a36Sopenharmony_ci		printk("%s", version);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* Grab the region so no one else tries to probe our ioports. */
28862306a36Sopenharmony_ci	if (!request_region(ioaddr, COPS_IO_EXTENT, dev->name))
28962306a36Sopenharmony_ci		return -EBUSY;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci        /*
29262306a36Sopenharmony_ci         * Since this board has jumpered interrupts, allocate the interrupt
29362306a36Sopenharmony_ci         * vector now. There is no point in waiting since no other device
29462306a36Sopenharmony_ci         * can use the interrupt, and this marks the irq as busy. Jumpered
29562306a36Sopenharmony_ci         * interrupts are typically not reported by the boards, and we must
29662306a36Sopenharmony_ci         * used AutoIRQ to find them.
29762306a36Sopenharmony_ci	 */
29862306a36Sopenharmony_ci	dev->irq = irq;
29962306a36Sopenharmony_ci	switch (dev->irq)
30062306a36Sopenharmony_ci	{
30162306a36Sopenharmony_ci		case 0:
30262306a36Sopenharmony_ci			/* COPS AutoIRQ routine */
30362306a36Sopenharmony_ci			dev->irq = cops_irq(ioaddr, board);
30462306a36Sopenharmony_ci			if (dev->irq)
30562306a36Sopenharmony_ci				break;
30662306a36Sopenharmony_ci			fallthrough;	/* Once no IRQ found on this port */
30762306a36Sopenharmony_ci		case 1:
30862306a36Sopenharmony_ci			retval = -EINVAL;
30962306a36Sopenharmony_ci			goto err_out;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		/* Fixup for users that don't know that IRQ 2 is really
31262306a36Sopenharmony_ci		 * IRQ 9, or don't know which one to set.
31362306a36Sopenharmony_ci		 */
31462306a36Sopenharmony_ci		case 2:
31562306a36Sopenharmony_ci			dev->irq = 9;
31662306a36Sopenharmony_ci			break;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		/* Polled operation requested. Although irq of zero passed as
31962306a36Sopenharmony_ci		 * a parameter tells the init routines to probe, we'll
32062306a36Sopenharmony_ci		 * overload it to denote polled operation at runtime.
32162306a36Sopenharmony_ci		 */
32262306a36Sopenharmony_ci		case 0xff:
32362306a36Sopenharmony_ci			dev->irq = 0;
32462306a36Sopenharmony_ci			break;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		default:
32762306a36Sopenharmony_ci			break;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	dev->base_addr = ioaddr;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* Reserve any actual interrupt. */
33362306a36Sopenharmony_ci	if (dev->irq) {
33462306a36Sopenharmony_ci		retval = request_irq(dev->irq, cops_interrupt, 0, dev->name, dev);
33562306a36Sopenharmony_ci		if (retval)
33662306a36Sopenharmony_ci			goto err_out;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci        lp = netdev_priv(dev);
34062306a36Sopenharmony_ci        spin_lock_init(&lp->lock);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Copy local board variable to lp struct. */
34362306a36Sopenharmony_ci	lp->board               = board;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	dev->netdev_ops 	= &cops_netdev_ops;
34662306a36Sopenharmony_ci	dev->watchdog_timeo	= HZ * 2;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* Tell the user where the card is and what mode we're in. */
35062306a36Sopenharmony_ci	if(board==DAYNA)
35162306a36Sopenharmony_ci		printk("%s: %s at %#3x, using IRQ %d, in Dayna mode.\n",
35262306a36Sopenharmony_ci			dev->name, cardname, ioaddr, dev->irq);
35362306a36Sopenharmony_ci	if(board==TANGENT) {
35462306a36Sopenharmony_ci		if(dev->irq)
35562306a36Sopenharmony_ci			printk("%s: %s at %#3x, IRQ %d, in Tangent mode\n",
35662306a36Sopenharmony_ci				dev->name, cardname, ioaddr, dev->irq);
35762306a36Sopenharmony_ci		else
35862306a36Sopenharmony_ci			printk("%s: %s at %#3x, using polled IO, in Tangent mode.\n",
35962306a36Sopenharmony_ci				dev->name, cardname, ioaddr);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci        return 0;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cierr_out:
36562306a36Sopenharmony_ci	release_region(ioaddr, COPS_IO_EXTENT);
36662306a36Sopenharmony_ci	return retval;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int __init cops_irq (int ioaddr, int board)
37062306a36Sopenharmony_ci{       /*
37162306a36Sopenharmony_ci         * This does not use the IRQ to determine where the IRQ is. We just
37262306a36Sopenharmony_ci         * assume that when we get a correct status response that it's the IRQ.
37362306a36Sopenharmony_ci         * This really just verifies the IO port but since we only have access
37462306a36Sopenharmony_ci         * to such a small number of IRQs (5, 4, 3) this is not bad.
37562306a36Sopenharmony_ci         * This will probably not work for more than one card.
37662306a36Sopenharmony_ci         */
37762306a36Sopenharmony_ci        int irqaddr=0;
37862306a36Sopenharmony_ci        int i, x, status;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci        if(board==DAYNA)
38162306a36Sopenharmony_ci        {
38262306a36Sopenharmony_ci                outb(0, ioaddr+DAYNA_RESET);
38362306a36Sopenharmony_ci                inb(ioaddr+DAYNA_RESET);
38462306a36Sopenharmony_ci                mdelay(333);
38562306a36Sopenharmony_ci        }
38662306a36Sopenharmony_ci        if(board==TANGENT)
38762306a36Sopenharmony_ci        {
38862306a36Sopenharmony_ci                inb(ioaddr);
38962306a36Sopenharmony_ci                outb(0, ioaddr);
39062306a36Sopenharmony_ci                outb(0, ioaddr+TANG_RESET);
39162306a36Sopenharmony_ci        }
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci        for(i=0; cops_irqlist[i] !=0; i++)
39462306a36Sopenharmony_ci        {
39562306a36Sopenharmony_ci                irqaddr = cops_irqlist[i];
39662306a36Sopenharmony_ci                for(x = 0xFFFF; x>0; x --)    /* wait for response */
39762306a36Sopenharmony_ci                {
39862306a36Sopenharmony_ci                        if(board==DAYNA)
39962306a36Sopenharmony_ci                        {
40062306a36Sopenharmony_ci                                status = (inb(ioaddr+DAYNA_CARD_STATUS)&3);
40162306a36Sopenharmony_ci                                if(status == 1)
40262306a36Sopenharmony_ci                                        return irqaddr;
40362306a36Sopenharmony_ci                        }
40462306a36Sopenharmony_ci                        if(board==TANGENT)
40562306a36Sopenharmony_ci                        {
40662306a36Sopenharmony_ci                                if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0)
40762306a36Sopenharmony_ci                                        return irqaddr;
40862306a36Sopenharmony_ci                        }
40962306a36Sopenharmony_ci                }
41062306a36Sopenharmony_ci        }
41162306a36Sopenharmony_ci        return 0;       /* no IRQ found */
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/*
41562306a36Sopenharmony_ci * Open/initialize the board. This is called (in the current kernel)
41662306a36Sopenharmony_ci * sometime after booting when the 'ifconfig' program is run.
41762306a36Sopenharmony_ci */
41862306a36Sopenharmony_cistatic int cops_open(struct net_device *dev)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci    struct cops_local *lp = netdev_priv(dev);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if(dev->irq==0)
42362306a36Sopenharmony_ci	{
42462306a36Sopenharmony_ci		/*
42562306a36Sopenharmony_ci		 * I don't know if the Dayna-style boards support polled
42662306a36Sopenharmony_ci		 * operation.  For now, only allow it for Tangent.
42762306a36Sopenharmony_ci		 */
42862306a36Sopenharmony_ci		if(lp->board==TANGENT)	/* Poll 20 times per second */
42962306a36Sopenharmony_ci		{
43062306a36Sopenharmony_ci		    cops_timer_dev = dev;
43162306a36Sopenharmony_ci		    timer_setup(&cops_timer, cops_poll, 0);
43262306a36Sopenharmony_ci		    cops_timer.expires 	= jiffies + HZ/20;
43362306a36Sopenharmony_ci		    add_timer(&cops_timer);
43462306a36Sopenharmony_ci		}
43562306a36Sopenharmony_ci		else
43662306a36Sopenharmony_ci		{
43762306a36Sopenharmony_ci			printk(KERN_WARNING "%s: No irq line set\n", dev->name);
43862306a36Sopenharmony_ci			return -EAGAIN;
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	cops_jumpstart(dev);	/* Start the card up. */
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	netif_start_queue(dev);
44562306a36Sopenharmony_ci        return 0;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/*
44962306a36Sopenharmony_ci *	This allows for a dynamic start/restart of the entire card.
45062306a36Sopenharmony_ci */
45162306a36Sopenharmony_cistatic int cops_jumpstart(struct net_device *dev)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct cops_local *lp = netdev_priv(dev);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/*
45662306a36Sopenharmony_ci         *      Once the card has the firmware loaded and has acquired
45762306a36Sopenharmony_ci         *      the nodeid, if it is reset it will lose it all.
45862306a36Sopenharmony_ci         */
45962306a36Sopenharmony_ci        cops_reset(dev,1);	/* Need to reset card before load firmware. */
46062306a36Sopenharmony_ci        cops_load(dev);		/* Load the firmware. */
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/*
46362306a36Sopenharmony_ci	 *	If atalkd already gave us a nodeid we will use that
46462306a36Sopenharmony_ci	 *	one again, else we wait for atalkd to give us a nodeid
46562306a36Sopenharmony_ci	 *	in cops_ioctl. This may cause a problem if someone steals
46662306a36Sopenharmony_ci	 *	our nodeid while we are resetting.
46762306a36Sopenharmony_ci	 */
46862306a36Sopenharmony_ci	if(lp->nodeid == 1)
46962306a36Sopenharmony_ci		cops_nodeid(dev,lp->node_acquire);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	return 0;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic void tangent_wait_reset(int ioaddr)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	int timeout=0;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	while(timeout++ < 5 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
47962306a36Sopenharmony_ci		mdelay(1);   /* Wait 1 second */
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci/*
48362306a36Sopenharmony_ci *      Reset the LocalTalk board.
48462306a36Sopenharmony_ci */
48562306a36Sopenharmony_cistatic void cops_reset(struct net_device *dev, int sleep)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci        struct cops_local *lp = netdev_priv(dev);
48862306a36Sopenharmony_ci        int ioaddr=dev->base_addr;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci        if(lp->board==TANGENT)
49162306a36Sopenharmony_ci        {
49262306a36Sopenharmony_ci                inb(ioaddr);		/* Clear request latch. */
49362306a36Sopenharmony_ci                outb(0,ioaddr);		/* Clear the TANG_TX_READY flop. */
49462306a36Sopenharmony_ci                outb(0, ioaddr+TANG_RESET);	/* Reset the adapter. */
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		tangent_wait_reset(ioaddr);
49762306a36Sopenharmony_ci                outb(0, ioaddr+TANG_CLEAR_INT);
49862306a36Sopenharmony_ci        }
49962306a36Sopenharmony_ci        if(lp->board==DAYNA)
50062306a36Sopenharmony_ci        {
50162306a36Sopenharmony_ci                outb(0, ioaddr+DAYNA_RESET);	/* Assert the reset port */
50262306a36Sopenharmony_ci                inb(ioaddr+DAYNA_RESET);	/* Clear the reset */
50362306a36Sopenharmony_ci		if (sleep)
50462306a36Sopenharmony_ci			msleep(333);
50562306a36Sopenharmony_ci		else
50662306a36Sopenharmony_ci			mdelay(333);
50762306a36Sopenharmony_ci        }
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	netif_wake_queue(dev);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic void cops_load (struct net_device *dev)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci        struct ifreq ifr;
51562306a36Sopenharmony_ci        struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_ifru;
51662306a36Sopenharmony_ci        struct cops_local *lp = netdev_priv(dev);
51762306a36Sopenharmony_ci        int ioaddr=dev->base_addr;
51862306a36Sopenharmony_ci	int length, i = 0;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci        strcpy(ifr.ifr_name,"lt0");
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci        /* Get card's firmware code and do some checks on it. */
52362306a36Sopenharmony_ci#ifdef CONFIG_COPS_DAYNA
52462306a36Sopenharmony_ci        if(lp->board==DAYNA)
52562306a36Sopenharmony_ci        {
52662306a36Sopenharmony_ci                ltf->length=sizeof(ffdrv_code);
52762306a36Sopenharmony_ci                ltf->data=ffdrv_code;
52862306a36Sopenharmony_ci        }
52962306a36Sopenharmony_ci        else
53062306a36Sopenharmony_ci#endif
53162306a36Sopenharmony_ci#ifdef CONFIG_COPS_TANGENT
53262306a36Sopenharmony_ci        if(lp->board==TANGENT)
53362306a36Sopenharmony_ci        {
53462306a36Sopenharmony_ci                ltf->length=sizeof(ltdrv_code);
53562306a36Sopenharmony_ci                ltf->data=ltdrv_code;
53662306a36Sopenharmony_ci        }
53762306a36Sopenharmony_ci        else
53862306a36Sopenharmony_ci#endif
53962306a36Sopenharmony_ci	{
54062306a36Sopenharmony_ci		printk(KERN_INFO "%s; unsupported board type.\n", dev->name);
54162306a36Sopenharmony_ci		return;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci        /* Check to make sure firmware is correct length. */
54562306a36Sopenharmony_ci        if(lp->board==DAYNA && ltf->length!=5983)
54662306a36Sopenharmony_ci        {
54762306a36Sopenharmony_ci                printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name);
54862306a36Sopenharmony_ci                return;
54962306a36Sopenharmony_ci        }
55062306a36Sopenharmony_ci        if(lp->board==TANGENT && ltf->length!=2501)
55162306a36Sopenharmony_ci        {
55262306a36Sopenharmony_ci                printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name);
55362306a36Sopenharmony_ci                return;
55462306a36Sopenharmony_ci        }
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci        if(lp->board==DAYNA)
55762306a36Sopenharmony_ci        {
55862306a36Sopenharmony_ci                /*
55962306a36Sopenharmony_ci                 *      We must wait for a status response
56062306a36Sopenharmony_ci                 *      with the DAYNA board.
56162306a36Sopenharmony_ci                 */
56262306a36Sopenharmony_ci                while(++i<65536)
56362306a36Sopenharmony_ci                {
56462306a36Sopenharmony_ci                       if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1)
56562306a36Sopenharmony_ci                                break;
56662306a36Sopenharmony_ci                }
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci                if(i==65536)
56962306a36Sopenharmony_ci                        return;
57062306a36Sopenharmony_ci        }
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci        /*
57362306a36Sopenharmony_ci         *      Upload the firmware and kick. Byte-by-byte works nicely here.
57462306a36Sopenharmony_ci         */
57562306a36Sopenharmony_ci	i=0;
57662306a36Sopenharmony_ci        length = ltf->length;
57762306a36Sopenharmony_ci        while(length--)
57862306a36Sopenharmony_ci        {
57962306a36Sopenharmony_ci                outb(ltf->data[i], ioaddr);
58062306a36Sopenharmony_ci                i++;
58162306a36Sopenharmony_ci        }
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	if(cops_debug > 1)
58462306a36Sopenharmony_ci		printk("%s: Uploaded firmware - %d bytes of %d bytes.\n",
58562306a36Sopenharmony_ci			dev->name, i, ltf->length);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci        if(lp->board==DAYNA) 	/* Tell Dayna to run the firmware code. */
58862306a36Sopenharmony_ci                outb(1, ioaddr+DAYNA_INT_CARD);
58962306a36Sopenharmony_ci	else			/* Tell Tang to run the firmware code. */
59062306a36Sopenharmony_ci		inb(ioaddr);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci        if(lp->board==TANGENT)
59362306a36Sopenharmony_ci        {
59462306a36Sopenharmony_ci                tangent_wait_reset(ioaddr);
59562306a36Sopenharmony_ci                inb(ioaddr);	/* Clear initial ready signal. */
59662306a36Sopenharmony_ci        }
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci/*
60062306a36Sopenharmony_ci * 	Get the LocalTalk Nodeid from the card. We can suggest
60162306a36Sopenharmony_ci *	any nodeid 1-254. The card will try and get that exact
60262306a36Sopenharmony_ci *	address else we can specify 0 as the nodeid and the card
60362306a36Sopenharmony_ci *	will autoprobe for a nodeid.
60462306a36Sopenharmony_ci */
60562306a36Sopenharmony_cistatic int cops_nodeid (struct net_device *dev, int nodeid)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct cops_local *lp = netdev_priv(dev);
60862306a36Sopenharmony_ci	int ioaddr = dev->base_addr;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if(lp->board == DAYNA)
61162306a36Sopenharmony_ci        {
61262306a36Sopenharmony_ci		/* Empty any pending adapter responses. */
61362306a36Sopenharmony_ci                while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0)
61462306a36Sopenharmony_ci                {
61562306a36Sopenharmony_ci			outb(0, ioaddr+COPS_CLEAR_INT);	/* Clear interrupts. */
61662306a36Sopenharmony_ci			if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST)
61762306a36Sopenharmony_ci				cops_rx(dev);	/* Kick any packets waiting. */
61862306a36Sopenharmony_ci			schedule();
61962306a36Sopenharmony_ci                }
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci                outb(2, ioaddr);       	/* Output command packet length as 2. */
62262306a36Sopenharmony_ci                outb(0, ioaddr);
62362306a36Sopenharmony_ci                outb(LAP_INIT, ioaddr);	/* Send LAP_INIT command byte. */
62462306a36Sopenharmony_ci                outb(nodeid, ioaddr);  	/* Suggest node address. */
62562306a36Sopenharmony_ci        }
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if(lp->board == TANGENT)
62862306a36Sopenharmony_ci        {
62962306a36Sopenharmony_ci                /* Empty any pending adapter responses. */
63062306a36Sopenharmony_ci                while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)
63162306a36Sopenharmony_ci                {
63262306a36Sopenharmony_ci			outb(0, ioaddr+COPS_CLEAR_INT);	/* Clear interrupt. */
63362306a36Sopenharmony_ci			cops_rx(dev);          	/* Kick out packets waiting. */
63462306a36Sopenharmony_ci			schedule();
63562306a36Sopenharmony_ci                }
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci		/* Not sure what Tangent does if nodeid picked is used. */
63862306a36Sopenharmony_ci                if(nodeid == 0)	         		/* Seed. */
63962306a36Sopenharmony_ci			nodeid = jiffies&0xFF;		/* Get a random try */
64062306a36Sopenharmony_ci                outb(2, ioaddr);        		/* Command length LSB */
64162306a36Sopenharmony_ci                outb(0, ioaddr);       			/* Command length MSB */
64262306a36Sopenharmony_ci                outb(LAP_INIT, ioaddr); 		/* Send LAP_INIT byte */
64362306a36Sopenharmony_ci                outb(nodeid, ioaddr); 		  	/* LAP address hint. */
64462306a36Sopenharmony_ci                outb(0xFF, ioaddr);     		/* Int. level to use */
64562306a36Sopenharmony_ci        }
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	lp->node_acquire=0;		/* Set nodeid holder to 0. */
64862306a36Sopenharmony_ci        while(lp->node_acquire==0)	/* Get *True* nodeid finally. */
64962306a36Sopenharmony_ci	{
65062306a36Sopenharmony_ci		outb(0, ioaddr+COPS_CLEAR_INT);	/* Clear any interrupt. */
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		if(lp->board == DAYNA)
65362306a36Sopenharmony_ci		{
65462306a36Sopenharmony_ci			if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST)
65562306a36Sopenharmony_ci				cops_rx(dev);	/* Grab the nodeid put in lp->node_acquire. */
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci		if(lp->board == TANGENT)
65862306a36Sopenharmony_ci		{
65962306a36Sopenharmony_ci			if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)
66062306a36Sopenharmony_ci				cops_rx(dev);   /* Grab the nodeid put in lp->node_acquire. */
66162306a36Sopenharmony_ci		}
66262306a36Sopenharmony_ci		schedule();
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if(cops_debug > 1)
66662306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n",
66762306a36Sopenharmony_ci			dev->name, lp->node_acquire);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	lp->nodeid=1;	/* Set got nodeid to 1. */
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci        return 0;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci/*
67562306a36Sopenharmony_ci *	Poll the Tangent type cards to see if we have work.
67662306a36Sopenharmony_ci */
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_cistatic void cops_poll(struct timer_list *unused)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	int ioaddr, status;
68162306a36Sopenharmony_ci	int boguscount = 0;
68262306a36Sopenharmony_ci	struct net_device *dev = cops_timer_dev;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	del_timer(&cops_timer);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if(dev == NULL)
68762306a36Sopenharmony_ci		return;	/* We've been downed */
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	ioaddr = dev->base_addr;
69062306a36Sopenharmony_ci	do {
69162306a36Sopenharmony_ci		status=inb(ioaddr+TANG_CARD_STATUS);
69262306a36Sopenharmony_ci		if(status & TANG_RX_READY)
69362306a36Sopenharmony_ci			cops_rx(dev);
69462306a36Sopenharmony_ci		if(status & TANG_TX_READY)
69562306a36Sopenharmony_ci			netif_wake_queue(dev);
69662306a36Sopenharmony_ci		status = inb(ioaddr+TANG_CARD_STATUS);
69762306a36Sopenharmony_ci	} while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY)));
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* poll 20 times per second */
70062306a36Sopenharmony_ci	cops_timer.expires = jiffies + HZ/20;
70162306a36Sopenharmony_ci	add_timer(&cops_timer);
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci/*
70562306a36Sopenharmony_ci *      The typical workload of the driver:
70662306a36Sopenharmony_ci *      Handle the network interface interrupts.
70762306a36Sopenharmony_ci */
70862306a36Sopenharmony_cistatic irqreturn_t cops_interrupt(int irq, void *dev_id)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci        struct net_device *dev = dev_id;
71162306a36Sopenharmony_ci        struct cops_local *lp;
71262306a36Sopenharmony_ci        int ioaddr, status;
71362306a36Sopenharmony_ci        int boguscount = 0;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci        ioaddr = dev->base_addr;
71662306a36Sopenharmony_ci        lp = netdev_priv(dev);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if(lp->board==DAYNA)
71962306a36Sopenharmony_ci	{
72062306a36Sopenharmony_ci		do {
72162306a36Sopenharmony_ci			outb(0, ioaddr + COPS_CLEAR_INT);
72262306a36Sopenharmony_ci			status=inb(ioaddr+DAYNA_CARD_STATUS);
72362306a36Sopenharmony_ci			if((status&0x03)==DAYNA_RX_REQUEST)
72462306a36Sopenharmony_ci				cops_rx(dev);
72562306a36Sopenharmony_ci			netif_wake_queue(dev);
72662306a36Sopenharmony_ci		} while(++boguscount < 20);
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci	else
72962306a36Sopenharmony_ci	{
73062306a36Sopenharmony_ci		do {
73162306a36Sopenharmony_ci			status=inb(ioaddr+TANG_CARD_STATUS);
73262306a36Sopenharmony_ci			if(status & TANG_RX_READY)
73362306a36Sopenharmony_ci				cops_rx(dev);
73462306a36Sopenharmony_ci			if(status & TANG_TX_READY)
73562306a36Sopenharmony_ci				netif_wake_queue(dev);
73662306a36Sopenharmony_ci			status=inb(ioaddr+TANG_CARD_STATUS);
73762306a36Sopenharmony_ci		} while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY)));
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci        return IRQ_HANDLED;
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci/*
74462306a36Sopenharmony_ci *      We have a good packet(s), get it/them out of the buffers.
74562306a36Sopenharmony_ci */
74662306a36Sopenharmony_cistatic void cops_rx(struct net_device *dev)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci        int pkt_len = 0;
74962306a36Sopenharmony_ci        int rsp_type = 0;
75062306a36Sopenharmony_ci        struct sk_buff *skb = NULL;
75162306a36Sopenharmony_ci        struct cops_local *lp = netdev_priv(dev);
75262306a36Sopenharmony_ci        int ioaddr = dev->base_addr;
75362306a36Sopenharmony_ci        int boguscount = 0;
75462306a36Sopenharmony_ci        unsigned long flags;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci        if(lp->board==DAYNA)
76062306a36Sopenharmony_ci        {
76162306a36Sopenharmony_ci                outb(0, ioaddr);                /* Send out Zero length. */
76262306a36Sopenharmony_ci                outb(0, ioaddr);
76362306a36Sopenharmony_ci                outb(DATA_READ, ioaddr);        /* Send read command out. */
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci                /* Wait for DMA to turn around. */
76662306a36Sopenharmony_ci                while(++boguscount<1000000)
76762306a36Sopenharmony_ci                {
76862306a36Sopenharmony_ci			barrier();
76962306a36Sopenharmony_ci                        if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY)
77062306a36Sopenharmony_ci                                break;
77162306a36Sopenharmony_ci                }
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci                if(boguscount==1000000)
77462306a36Sopenharmony_ci                {
77562306a36Sopenharmony_ci                        printk(KERN_WARNING "%s: DMA timed out.\n",dev->name);
77662306a36Sopenharmony_ci			spin_unlock_irqrestore(&lp->lock, flags);
77762306a36Sopenharmony_ci                        return;
77862306a36Sopenharmony_ci                }
77962306a36Sopenharmony_ci        }
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci        /* Get response length. */
78262306a36Sopenharmony_ci	pkt_len = inb(ioaddr);
78362306a36Sopenharmony_ci        pkt_len |= (inb(ioaddr) << 8);
78462306a36Sopenharmony_ci        /* Input IO code. */
78562306a36Sopenharmony_ci        rsp_type=inb(ioaddr);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci        /* Malloc up new buffer. */
78862306a36Sopenharmony_ci        skb = dev_alloc_skb(pkt_len);
78962306a36Sopenharmony_ci        if(skb == NULL)
79062306a36Sopenharmony_ci        {
79162306a36Sopenharmony_ci                printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n",
79262306a36Sopenharmony_ci			dev->name);
79362306a36Sopenharmony_ci                dev->stats.rx_dropped++;
79462306a36Sopenharmony_ci                while(pkt_len--)        /* Discard packet */
79562306a36Sopenharmony_ci                        inb(ioaddr);
79662306a36Sopenharmony_ci                spin_unlock_irqrestore(&lp->lock, flags);
79762306a36Sopenharmony_ci                return;
79862306a36Sopenharmony_ci        }
79962306a36Sopenharmony_ci        skb->dev = dev;
80062306a36Sopenharmony_ci        skb_put(skb, pkt_len);
80162306a36Sopenharmony_ci        skb->protocol = htons(ETH_P_LOCALTALK);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci        insb(ioaddr, skb->data, pkt_len);               /* Eat the Data */
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci        if(lp->board==DAYNA)
80662306a36Sopenharmony_ci                outb(1, ioaddr+DAYNA_INT_CARD);         /* Interrupt the card */
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci        spin_unlock_irqrestore(&lp->lock, flags);  /* Restore interrupts. */
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci        /* Check for bad response length */
81162306a36Sopenharmony_ci        if(pkt_len < 0 || pkt_len > MAX_LLAP_SIZE)
81262306a36Sopenharmony_ci        {
81362306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Bad packet length of %d bytes.\n",
81462306a36Sopenharmony_ci			dev->name, pkt_len);
81562306a36Sopenharmony_ci                dev->stats.tx_errors++;
81662306a36Sopenharmony_ci                dev_kfree_skb_any(skb);
81762306a36Sopenharmony_ci                return;
81862306a36Sopenharmony_ci        }
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci        /* Set nodeid and then get out. */
82162306a36Sopenharmony_ci        if(rsp_type == LAP_INIT_RSP)
82262306a36Sopenharmony_ci        {	/* Nodeid taken from received packet. */
82362306a36Sopenharmony_ci                lp->node_acquire = skb->data[0];
82462306a36Sopenharmony_ci                dev_kfree_skb_any(skb);
82562306a36Sopenharmony_ci                return;
82662306a36Sopenharmony_ci        }
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci        /* One last check to make sure we have a good packet. */
82962306a36Sopenharmony_ci        if(rsp_type != LAP_RESPONSE)
83062306a36Sopenharmony_ci        {
83162306a36Sopenharmony_ci                printk(KERN_WARNING "%s: Bad packet type %d.\n", dev->name, rsp_type);
83262306a36Sopenharmony_ci                dev->stats.tx_errors++;
83362306a36Sopenharmony_ci                dev_kfree_skb_any(skb);
83462306a36Sopenharmony_ci                return;
83562306a36Sopenharmony_ci        }
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci        skb_reset_mac_header(skb);    /* Point to entire packet. */
83862306a36Sopenharmony_ci        skb_pull(skb,3);
83962306a36Sopenharmony_ci        skb_reset_transport_header(skb);    /* Point to data (Skip header). */
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci        /* Update the counters. */
84262306a36Sopenharmony_ci        dev->stats.rx_packets++;
84362306a36Sopenharmony_ci        dev->stats.rx_bytes += skb->len;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci        /* Send packet to a higher place. */
84662306a36Sopenharmony_ci        netif_rx(skb);
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic void cops_timeout(struct net_device *dev, unsigned int txqueue)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci        struct cops_local *lp = netdev_priv(dev);
85262306a36Sopenharmony_ci        int ioaddr = dev->base_addr;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	dev->stats.tx_errors++;
85562306a36Sopenharmony_ci        if(lp->board==TANGENT)
85662306a36Sopenharmony_ci        {
85762306a36Sopenharmony_ci		if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
85862306a36Sopenharmony_ci			printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name);
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci	printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name);
86162306a36Sopenharmony_ci	cops_jumpstart(dev);	/* Restart the card. */
86262306a36Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
86362306a36Sopenharmony_ci	netif_wake_queue(dev);
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci/*
86862306a36Sopenharmony_ci *	Make the card transmit a LocalTalk packet.
86962306a36Sopenharmony_ci */
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic netdev_tx_t cops_send_packet(struct sk_buff *skb,
87262306a36Sopenharmony_ci					  struct net_device *dev)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci        struct cops_local *lp = netdev_priv(dev);
87562306a36Sopenharmony_ci        int ioaddr = dev->base_addr;
87662306a36Sopenharmony_ci        unsigned long flags;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci        /*
87962306a36Sopenharmony_ci         * Block a timer-based transmit from overlapping.
88062306a36Sopenharmony_ci	 */
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	netif_stop_queue(dev);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
88562306a36Sopenharmony_ci	if(lp->board == DAYNA)	 /* Wait for adapter transmit buffer. */
88662306a36Sopenharmony_ci		while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0)
88762306a36Sopenharmony_ci			cpu_relax();
88862306a36Sopenharmony_ci	if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */
88962306a36Sopenharmony_ci		while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
89062306a36Sopenharmony_ci			cpu_relax();
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	/* Output IO length. */
89362306a36Sopenharmony_ci	outb(skb->len, ioaddr);
89462306a36Sopenharmony_ci	outb(skb->len >> 8, ioaddr);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	/* Output IO code. */
89762306a36Sopenharmony_ci	outb(LAP_WRITE, ioaddr);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	if(lp->board == DAYNA)	/* Check the transmit buffer again. */
90062306a36Sopenharmony_ci		while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	outsb(ioaddr, skb->data, skb->len);	/* Send out the data. */
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	if(lp->board==DAYNA)	/* Dayna requires you kick the card */
90562306a36Sopenharmony_ci		outb(1, ioaddr+DAYNA_INT_CARD);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);	/* Restore interrupts. */
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* Done sending packet, update counters and cleanup. */
91062306a36Sopenharmony_ci	dev->stats.tx_packets++;
91162306a36Sopenharmony_ci	dev->stats.tx_bytes += skb->len;
91262306a36Sopenharmony_ci	dev_kfree_skb (skb);
91362306a36Sopenharmony_ci	return NETDEV_TX_OK;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci/*
91762306a36Sopenharmony_ci *	Dummy function to keep the Appletalk layer happy.
91862306a36Sopenharmony_ci */
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic void set_multicast_list(struct net_device *dev)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci        if(cops_debug >= 3)
92362306a36Sopenharmony_ci		printk("%s: set_multicast_list executed\n", dev->name);
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci/*
92762306a36Sopenharmony_ci *      System ioctls for the COPS LocalTalk card.
92862306a36Sopenharmony_ci */
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_cistatic int cops_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci        struct cops_local *lp = netdev_priv(dev);
93362306a36Sopenharmony_ci        struct sockaddr_at *sa = (struct sockaddr_at *)&ifr->ifr_addr;
93462306a36Sopenharmony_ci        struct atalk_addr *aa = &lp->node_addr;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci        switch(cmd)
93762306a36Sopenharmony_ci        {
93862306a36Sopenharmony_ci                case SIOCSIFADDR:
93962306a36Sopenharmony_ci			/* Get and set the nodeid and network # atalkd wants. */
94062306a36Sopenharmony_ci			cops_nodeid(dev, sa->sat_addr.s_node);
94162306a36Sopenharmony_ci			aa->s_net               = sa->sat_addr.s_net;
94262306a36Sopenharmony_ci                        aa->s_node              = lp->node_acquire;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci			/* Set broardcast address. */
94562306a36Sopenharmony_ci                        dev->broadcast[0]       = 0xFF;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci			/* Set hardware address. */
94862306a36Sopenharmony_ci                        dev->addr_len           = 1;
94962306a36Sopenharmony_ci			dev_addr_set(dev, &aa->s_node);
95062306a36Sopenharmony_ci                        return 0;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci                case SIOCGIFADDR:
95362306a36Sopenharmony_ci                        sa->sat_addr.s_net      = aa->s_net;
95462306a36Sopenharmony_ci                        sa->sat_addr.s_node     = aa->s_node;
95562306a36Sopenharmony_ci                        return 0;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci                default:
95862306a36Sopenharmony_ci                        return -EOPNOTSUPP;
95962306a36Sopenharmony_ci        }
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci/*
96362306a36Sopenharmony_ci *	The inverse routine to cops_open().
96462306a36Sopenharmony_ci */
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_cistatic int cops_close(struct net_device *dev)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	struct cops_local *lp = netdev_priv(dev);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	/* If we were running polled, yank the timer.
97162306a36Sopenharmony_ci	 */
97262306a36Sopenharmony_ci	if(lp->board==TANGENT && dev->irq==0)
97362306a36Sopenharmony_ci		del_timer(&cops_timer);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	netif_stop_queue(dev);
97662306a36Sopenharmony_ci        return 0;
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci#ifdef MODULE
98162306a36Sopenharmony_cistatic struct net_device *cops_dev;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
98462306a36Sopenharmony_cimodule_param_hw(io, int, ioport, 0);
98562306a36Sopenharmony_cimodule_param_hw(irq, int, irq, 0);
98662306a36Sopenharmony_cimodule_param_hw(board_type, int, other, 0);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic int __init cops_module_init(void)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	if (io == 0)
99162306a36Sopenharmony_ci		printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n",
99262306a36Sopenharmony_ci			cardname);
99362306a36Sopenharmony_ci	cops_dev = cops_probe(-1);
99462306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(cops_dev);
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_cistatic void __exit cops_module_exit(void)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	unregister_netdev(cops_dev);
100062306a36Sopenharmony_ci	cleanup_card(cops_dev);
100162306a36Sopenharmony_ci	free_netdev(cops_dev);
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_cimodule_init(cops_module_init);
100462306a36Sopenharmony_cimodule_exit(cops_module_exit);
100562306a36Sopenharmony_ci#endif /* MODULE */
1006