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