18c2ecf20Sopenharmony_ci/* cops.c: LocalTalk driver for Linux. 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * Authors: 48c2ecf20Sopenharmony_ci * - Jay Schulist <jschlst@samba.org> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * With more than a little help from; 78c2ecf20Sopenharmony_ci * - Alan Cox <alan@lxorguk.ukuu.org.uk> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Derived from: 108c2ecf20Sopenharmony_ci * - skeleton.c: A network driver outline for linux. 118c2ecf20Sopenharmony_ci * Written 1993-94 by Donald Becker. 128c2ecf20Sopenharmony_ci * - ltpc.c: A driver for the LocalTalk PC card. 138c2ecf20Sopenharmony_ci * Written by Bradford W. Johnson. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Copyright 1993 United States Government as represented by the 168c2ecf20Sopenharmony_ci * Director, National Security Agency. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * This software may be used and distributed according to the terms 198c2ecf20Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * Changes: 228c2ecf20Sopenharmony_ci * 19970608 Alan Cox Allowed dual card type support 238c2ecf20Sopenharmony_ci * Can set board type in insmod 248c2ecf20Sopenharmony_ci * Hooks for cops_setup routine 258c2ecf20Sopenharmony_ci * (not yet implemented). 268c2ecf20Sopenharmony_ci * 19971101 Jay Schulist Fixes for multiple lt* devices. 278c2ecf20Sopenharmony_ci * 19980607 Steven Hirsch Fixed the badly broken support 288c2ecf20Sopenharmony_ci * for Tangent type cards. Only 298c2ecf20Sopenharmony_ci * tested on Daystar LT200. Some 308c2ecf20Sopenharmony_ci * cleanup of formatting and program 318c2ecf20Sopenharmony_ci * logic. Added emacs 'local-vars' 328c2ecf20Sopenharmony_ci * setup for Jay's brace style. 338c2ecf20Sopenharmony_ci * 20000211 Alan Cox Cleaned up for softnet 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic const char *version = 378c2ecf20Sopenharmony_ci"cops.c:v0.04 6/7/98 Jay Schulist <jschlst@samba.org>\n"; 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * Sources: 408c2ecf20Sopenharmony_ci * COPS Localtalk SDK. This provides almost all of the information 418c2ecf20Sopenharmony_ci * needed. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * insmod/modprobe configurable stuff. 468c2ecf20Sopenharmony_ci * - IO Port, choose one your card supports or 0 if you dare. 478c2ecf20Sopenharmony_ci * - IRQ, also choose one your card supports or nothing and let 488c2ecf20Sopenharmony_ci * the driver figure it out. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <linux/module.h> 528c2ecf20Sopenharmony_ci#include <linux/kernel.h> 538c2ecf20Sopenharmony_ci#include <linux/types.h> 548c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 558c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 568c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 578c2ecf20Sopenharmony_ci#include <linux/ioport.h> 588c2ecf20Sopenharmony_ci#include <linux/in.h> 598c2ecf20Sopenharmony_ci#include <linux/string.h> 608c2ecf20Sopenharmony_ci#include <linux/errno.h> 618c2ecf20Sopenharmony_ci#include <linux/init.h> 628c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 638c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 648c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 658c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 668c2ecf20Sopenharmony_ci#include <linux/if_ltalk.h> 678c2ecf20Sopenharmony_ci#include <linux/delay.h> /* For udelay() */ 688c2ecf20Sopenharmony_ci#include <linux/atalk.h> 698c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 708c2ecf20Sopenharmony_ci#include <linux/bitops.h> 718c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#include <net/Space.h> 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#include <asm/io.h> 768c2ecf20Sopenharmony_ci#include <asm/dma.h> 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#include "cops.h" /* Our Stuff */ 798c2ecf20Sopenharmony_ci#include "cops_ltdrv.h" /* Firmware code for Tangent type cards. */ 808c2ecf20Sopenharmony_ci#include "cops_ffdrv.h" /* Firmware code for Dayna type cards. */ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* 838c2ecf20Sopenharmony_ci * The name of the card. Is used for messages and in the requests for 848c2ecf20Sopenharmony_ci * io regions, irqs and dma channels 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic const char *cardname = "cops"; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#ifdef CONFIG_COPS_DAYNA 908c2ecf20Sopenharmony_cistatic int board_type = DAYNA; /* Module exported */ 918c2ecf20Sopenharmony_ci#else 928c2ecf20Sopenharmony_cistatic int board_type = TANGENT; 938c2ecf20Sopenharmony_ci#endif 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int io = 0x240; /* Default IO for Dayna */ 968c2ecf20Sopenharmony_cistatic int irq = 5; /* Default IRQ */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * COPS Autoprobe information. 1008c2ecf20Sopenharmony_ci * Right now if port address is right but IRQ is not 5 this will 1018c2ecf20Sopenharmony_ci * return a 5 no matter what since we will still get a status response. 1028c2ecf20Sopenharmony_ci * Need one more additional check to narrow down after we have gotten 1038c2ecf20Sopenharmony_ci * the ioaddr. But since only other possible IRQs is 3 and 4 so no real 1048c2ecf20Sopenharmony_ci * hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with 1058c2ecf20Sopenharmony_ci * this driver. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * This driver has 2 modes and they are: Dayna mode and Tangent mode. 1088c2ecf20Sopenharmony_ci * Each mode corresponds with the type of card. It has been found 1098c2ecf20Sopenharmony_ci * that there are 2 main types of cards and all other cards are 1108c2ecf20Sopenharmony_ci * the same and just have different names or only have minor differences 1118c2ecf20Sopenharmony_ci * such as more IO ports. As this driver is tested it will 1128c2ecf20Sopenharmony_ci * become more clear on exactly what cards are supported. The driver 1138c2ecf20Sopenharmony_ci * defaults to using Dayna mode. To change the drivers mode, simply 1148c2ecf20Sopenharmony_ci * select Dayna or Tangent mode when configuring the kernel. 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * This driver should support: 1178c2ecf20Sopenharmony_ci * TANGENT driver mode: 1188c2ecf20Sopenharmony_ci * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200, 1198c2ecf20Sopenharmony_ci * COPS LT-1 1208c2ecf20Sopenharmony_ci * DAYNA driver mode: 1218c2ecf20Sopenharmony_ci * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, 1228c2ecf20Sopenharmony_ci * Farallon PhoneNET PC III, Farallon PhoneNET PC II 1238c2ecf20Sopenharmony_ci * Other cards possibly supported mode unknown though: 1248c2ecf20Sopenharmony_ci * Dayna DL2000 (Full length), COPS LT/M (Micro-Channel) 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * Cards NOT supported by this driver but supported by the ltpc.c 1278c2ecf20Sopenharmony_ci * driver written by Bradford W. Johnson <johns393@maroon.tc.umn.edu> 1288c2ecf20Sopenharmony_ci * Farallon PhoneNET PC 1298c2ecf20Sopenharmony_ci * Original Apple LocalTalk PC card 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * N.B. 1328c2ecf20Sopenharmony_ci * 1338c2ecf20Sopenharmony_ci * The Daystar Digital LT200 boards do not support interrupt-driven 1348c2ecf20Sopenharmony_ci * IO. You must specify 'irq=0xff' as a module parameter to invoke 1358c2ecf20Sopenharmony_ci * polled mode. I also believe that the port probing logic is quite 1368c2ecf20Sopenharmony_ci * dangerous at best and certainly hopeless for a polled card. Best to 1378c2ecf20Sopenharmony_ci * specify both. - Steve H. 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * Zero terminated list of IO ports to probe. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic unsigned int ports[] = { 1468c2ecf20Sopenharmony_ci 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260, 1478c2ecf20Sopenharmony_ci 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360, 1488c2ecf20Sopenharmony_ci 0 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* 1528c2ecf20Sopenharmony_ci * Zero terminated list of IRQ ports to probe. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int cops_irqlist[] = { 1568c2ecf20Sopenharmony_ci 5, 4, 3, 0 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic struct timer_list cops_timer; 1608c2ecf20Sopenharmony_cistatic struct net_device *cops_timer_dev; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */ 1638c2ecf20Sopenharmony_ci#ifndef COPS_DEBUG 1648c2ecf20Sopenharmony_ci#define COPS_DEBUG 1 1658c2ecf20Sopenharmony_ci#endif 1668c2ecf20Sopenharmony_cistatic unsigned int cops_debug = COPS_DEBUG; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* The number of low I/O ports used by the card. */ 1698c2ecf20Sopenharmony_ci#define COPS_IO_EXTENT 8 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* Information that needs to be kept for each board. */ 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistruct cops_local 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int board; /* Holds what board type is. */ 1768c2ecf20Sopenharmony_ci int nodeid; /* Set to 1 once have nodeid. */ 1778c2ecf20Sopenharmony_ci unsigned char node_acquire; /* Node ID when acquired. */ 1788c2ecf20Sopenharmony_ci struct atalk_addr node_addr; /* Full node address */ 1798c2ecf20Sopenharmony_ci spinlock_t lock; /* RX/TX lock */ 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* Index to functions, as function prototypes. */ 1838c2ecf20Sopenharmony_cistatic int cops_probe1 (struct net_device *dev, int ioaddr); 1848c2ecf20Sopenharmony_cistatic int cops_irq (int ioaddr, int board); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int cops_open (struct net_device *dev); 1878c2ecf20Sopenharmony_cistatic int cops_jumpstart (struct net_device *dev); 1888c2ecf20Sopenharmony_cistatic void cops_reset (struct net_device *dev, int sleep); 1898c2ecf20Sopenharmony_cistatic void cops_load (struct net_device *dev); 1908c2ecf20Sopenharmony_cistatic int cops_nodeid (struct net_device *dev, int nodeid); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic irqreturn_t cops_interrupt (int irq, void *dev_id); 1938c2ecf20Sopenharmony_cistatic void cops_poll(struct timer_list *t); 1948c2ecf20Sopenharmony_cistatic void cops_timeout(struct net_device *dev, unsigned int txqueue); 1958c2ecf20Sopenharmony_cistatic void cops_rx (struct net_device *dev); 1968c2ecf20Sopenharmony_cistatic netdev_tx_t cops_send_packet (struct sk_buff *skb, 1978c2ecf20Sopenharmony_ci struct net_device *dev); 1988c2ecf20Sopenharmony_cistatic void set_multicast_list (struct net_device *dev); 1998c2ecf20Sopenharmony_cistatic int cops_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); 2008c2ecf20Sopenharmony_cistatic int cops_close (struct net_device *dev); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic void cleanup_card(struct net_device *dev) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci if (dev->irq) 2058c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 2068c2ecf20Sopenharmony_ci release_region(dev->base_addr, COPS_IO_EXTENT); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* 2108c2ecf20Sopenharmony_ci * Check for a network adaptor of this type, and return '0' iff one exists. 2118c2ecf20Sopenharmony_ci * If dev->base_addr == 0, probe all likely locations. 2128c2ecf20Sopenharmony_ci * If dev->base_addr in [1..0x1ff], always return failure. 2138c2ecf20Sopenharmony_ci * otherwise go with what we pass in. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_cistruct net_device * __init cops_probe(int unit) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct net_device *dev; 2188c2ecf20Sopenharmony_ci unsigned *port; 2198c2ecf20Sopenharmony_ci int base_addr; 2208c2ecf20Sopenharmony_ci int err = 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci dev = alloc_ltalkdev(sizeof(struct cops_local)); 2238c2ecf20Sopenharmony_ci if (!dev) 2248c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (unit >= 0) { 2278c2ecf20Sopenharmony_ci sprintf(dev->name, "lt%d", unit); 2288c2ecf20Sopenharmony_ci netdev_boot_setup_check(dev); 2298c2ecf20Sopenharmony_ci irq = dev->irq; 2308c2ecf20Sopenharmony_ci base_addr = dev->base_addr; 2318c2ecf20Sopenharmony_ci } else { 2328c2ecf20Sopenharmony_ci base_addr = dev->base_addr = io; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (base_addr > 0x1ff) { /* Check a single specified location. */ 2368c2ecf20Sopenharmony_ci err = cops_probe1(dev, base_addr); 2378c2ecf20Sopenharmony_ci } else if (base_addr != 0) { /* Don't probe at all. */ 2388c2ecf20Sopenharmony_ci err = -ENXIO; 2398c2ecf20Sopenharmony_ci } else { 2408c2ecf20Sopenharmony_ci /* FIXME Does this really work for cards which generate irq? 2418c2ecf20Sopenharmony_ci * It's definitely N.G. for polled Tangent. sh 2428c2ecf20Sopenharmony_ci * Dayna cards don't autoprobe well at all, but if your card is 2438c2ecf20Sopenharmony_ci * at IRQ 5 & IO 0x240 we find it every time. ;) JS 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci for (port = ports; *port && cops_probe1(dev, *port) < 0; port++) 2468c2ecf20Sopenharmony_ci ; 2478c2ecf20Sopenharmony_ci if (!*port) 2488c2ecf20Sopenharmony_ci err = -ENODEV; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci if (err) 2518c2ecf20Sopenharmony_ci goto out; 2528c2ecf20Sopenharmony_ci err = register_netdev(dev); 2538c2ecf20Sopenharmony_ci if (err) 2548c2ecf20Sopenharmony_ci goto out1; 2558c2ecf20Sopenharmony_ci return dev; 2568c2ecf20Sopenharmony_ciout1: 2578c2ecf20Sopenharmony_ci cleanup_card(dev); 2588c2ecf20Sopenharmony_ciout: 2598c2ecf20Sopenharmony_ci free_netdev(dev); 2608c2ecf20Sopenharmony_ci return ERR_PTR(err); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic const struct net_device_ops cops_netdev_ops = { 2648c2ecf20Sopenharmony_ci .ndo_open = cops_open, 2658c2ecf20Sopenharmony_ci .ndo_stop = cops_close, 2668c2ecf20Sopenharmony_ci .ndo_start_xmit = cops_send_packet, 2678c2ecf20Sopenharmony_ci .ndo_tx_timeout = cops_timeout, 2688c2ecf20Sopenharmony_ci .ndo_do_ioctl = cops_ioctl, 2698c2ecf20Sopenharmony_ci .ndo_set_rx_mode = set_multicast_list, 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* 2738c2ecf20Sopenharmony_ci * This is the real probe routine. Linux has a history of friendly device 2748c2ecf20Sopenharmony_ci * probes on the ISA bus. A good device probes avoids doing writes, and 2758c2ecf20Sopenharmony_ci * verifies that the correct device exists and functions. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_cistatic int __init cops_probe1(struct net_device *dev, int ioaddr) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct cops_local *lp; 2808c2ecf20Sopenharmony_ci static unsigned version_printed; 2818c2ecf20Sopenharmony_ci int board = board_type; 2828c2ecf20Sopenharmony_ci int retval; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if(cops_debug && version_printed++ == 0) 2858c2ecf20Sopenharmony_ci printk("%s", version); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* Grab the region so no one else tries to probe our ioports. */ 2888c2ecf20Sopenharmony_ci if (!request_region(ioaddr, COPS_IO_EXTENT, dev->name)) 2898c2ecf20Sopenharmony_ci return -EBUSY; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* 2928c2ecf20Sopenharmony_ci * Since this board has jumpered interrupts, allocate the interrupt 2938c2ecf20Sopenharmony_ci * vector now. There is no point in waiting since no other device 2948c2ecf20Sopenharmony_ci * can use the interrupt, and this marks the irq as busy. Jumpered 2958c2ecf20Sopenharmony_ci * interrupts are typically not reported by the boards, and we must 2968c2ecf20Sopenharmony_ci * used AutoIRQ to find them. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci dev->irq = irq; 2998c2ecf20Sopenharmony_ci switch (dev->irq) 3008c2ecf20Sopenharmony_ci { 3018c2ecf20Sopenharmony_ci case 0: 3028c2ecf20Sopenharmony_ci /* COPS AutoIRQ routine */ 3038c2ecf20Sopenharmony_ci dev->irq = cops_irq(ioaddr, board); 3048c2ecf20Sopenharmony_ci if (dev->irq) 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci fallthrough; /* Once no IRQ found on this port */ 3078c2ecf20Sopenharmony_ci case 1: 3088c2ecf20Sopenharmony_ci retval = -EINVAL; 3098c2ecf20Sopenharmony_ci goto err_out; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* Fixup for users that don't know that IRQ 2 is really 3128c2ecf20Sopenharmony_ci * IRQ 9, or don't know which one to set. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci case 2: 3158c2ecf20Sopenharmony_ci dev->irq = 9; 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Polled operation requested. Although irq of zero passed as 3198c2ecf20Sopenharmony_ci * a parameter tells the init routines to probe, we'll 3208c2ecf20Sopenharmony_ci * overload it to denote polled operation at runtime. 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_ci case 0xff: 3238c2ecf20Sopenharmony_ci dev->irq = 0; 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci default: 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci dev->base_addr = ioaddr; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Reserve any actual interrupt. */ 3338c2ecf20Sopenharmony_ci if (dev->irq) { 3348c2ecf20Sopenharmony_ci retval = request_irq(dev->irq, cops_interrupt, 0, dev->name, dev); 3358c2ecf20Sopenharmony_ci if (retval) 3368c2ecf20Sopenharmony_ci goto err_out; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci lp = netdev_priv(dev); 3408c2ecf20Sopenharmony_ci spin_lock_init(&lp->lock); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Copy local board variable to lp struct. */ 3438c2ecf20Sopenharmony_ci lp->board = board; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci dev->netdev_ops = &cops_netdev_ops; 3468c2ecf20Sopenharmony_ci dev->watchdog_timeo = HZ * 2; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Tell the user where the card is and what mode we're in. */ 3508c2ecf20Sopenharmony_ci if(board==DAYNA) 3518c2ecf20Sopenharmony_ci printk("%s: %s at %#3x, using IRQ %d, in Dayna mode.\n", 3528c2ecf20Sopenharmony_ci dev->name, cardname, ioaddr, dev->irq); 3538c2ecf20Sopenharmony_ci if(board==TANGENT) { 3548c2ecf20Sopenharmony_ci if(dev->irq) 3558c2ecf20Sopenharmony_ci printk("%s: %s at %#3x, IRQ %d, in Tangent mode\n", 3568c2ecf20Sopenharmony_ci dev->name, cardname, ioaddr, dev->irq); 3578c2ecf20Sopenharmony_ci else 3588c2ecf20Sopenharmony_ci printk("%s: %s at %#3x, using polled IO, in Tangent mode.\n", 3598c2ecf20Sopenharmony_ci dev->name, cardname, ioaddr); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cierr_out: 3658c2ecf20Sopenharmony_ci release_region(ioaddr, COPS_IO_EXTENT); 3668c2ecf20Sopenharmony_ci return retval; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int __init cops_irq (int ioaddr, int board) 3708c2ecf20Sopenharmony_ci{ /* 3718c2ecf20Sopenharmony_ci * This does not use the IRQ to determine where the IRQ is. We just 3728c2ecf20Sopenharmony_ci * assume that when we get a correct status response that it's the IRQ. 3738c2ecf20Sopenharmony_ci * This really just verifies the IO port but since we only have access 3748c2ecf20Sopenharmony_ci * to such a small number of IRQs (5, 4, 3) this is not bad. 3758c2ecf20Sopenharmony_ci * This will probably not work for more than one card. 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci int irqaddr=0; 3788c2ecf20Sopenharmony_ci int i, x, status; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if(board==DAYNA) 3818c2ecf20Sopenharmony_ci { 3828c2ecf20Sopenharmony_ci outb(0, ioaddr+DAYNA_RESET); 3838c2ecf20Sopenharmony_ci inb(ioaddr+DAYNA_RESET); 3848c2ecf20Sopenharmony_ci mdelay(333); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci if(board==TANGENT) 3878c2ecf20Sopenharmony_ci { 3888c2ecf20Sopenharmony_ci inb(ioaddr); 3898c2ecf20Sopenharmony_ci outb(0, ioaddr); 3908c2ecf20Sopenharmony_ci outb(0, ioaddr+TANG_RESET); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci for(i=0; cops_irqlist[i] !=0; i++) 3948c2ecf20Sopenharmony_ci { 3958c2ecf20Sopenharmony_ci irqaddr = cops_irqlist[i]; 3968c2ecf20Sopenharmony_ci for(x = 0xFFFF; x>0; x --) /* wait for response */ 3978c2ecf20Sopenharmony_ci { 3988c2ecf20Sopenharmony_ci if(board==DAYNA) 3998c2ecf20Sopenharmony_ci { 4008c2ecf20Sopenharmony_ci status = (inb(ioaddr+DAYNA_CARD_STATUS)&3); 4018c2ecf20Sopenharmony_ci if(status == 1) 4028c2ecf20Sopenharmony_ci return irqaddr; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci if(board==TANGENT) 4058c2ecf20Sopenharmony_ci { 4068c2ecf20Sopenharmony_ci if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0) 4078c2ecf20Sopenharmony_ci return irqaddr; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci return 0; /* no IRQ found */ 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/* 4158c2ecf20Sopenharmony_ci * Open/initialize the board. This is called (in the current kernel) 4168c2ecf20Sopenharmony_ci * sometime after booting when the 'ifconfig' program is run. 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_cistatic int cops_open(struct net_device *dev) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct cops_local *lp = netdev_priv(dev); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if(dev->irq==0) 4238c2ecf20Sopenharmony_ci { 4248c2ecf20Sopenharmony_ci /* 4258c2ecf20Sopenharmony_ci * I don't know if the Dayna-style boards support polled 4268c2ecf20Sopenharmony_ci * operation. For now, only allow it for Tangent. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_ci if(lp->board==TANGENT) /* Poll 20 times per second */ 4298c2ecf20Sopenharmony_ci { 4308c2ecf20Sopenharmony_ci cops_timer_dev = dev; 4318c2ecf20Sopenharmony_ci timer_setup(&cops_timer, cops_poll, 0); 4328c2ecf20Sopenharmony_ci cops_timer.expires = jiffies + HZ/20; 4338c2ecf20Sopenharmony_ci add_timer(&cops_timer); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci else 4368c2ecf20Sopenharmony_ci { 4378c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: No irq line set\n", dev->name); 4388c2ecf20Sopenharmony_ci return -EAGAIN; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci cops_jumpstart(dev); /* Start the card up. */ 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci netif_start_queue(dev); 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci/* 4498c2ecf20Sopenharmony_ci * This allows for a dynamic start/restart of the entire card. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_cistatic int cops_jumpstart(struct net_device *dev) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct cops_local *lp = netdev_priv(dev); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* 4568c2ecf20Sopenharmony_ci * Once the card has the firmware loaded and has acquired 4578c2ecf20Sopenharmony_ci * the nodeid, if it is reset it will lose it all. 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_ci cops_reset(dev,1); /* Need to reset card before load firmware. */ 4608c2ecf20Sopenharmony_ci cops_load(dev); /* Load the firmware. */ 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* 4638c2ecf20Sopenharmony_ci * If atalkd already gave us a nodeid we will use that 4648c2ecf20Sopenharmony_ci * one again, else we wait for atalkd to give us a nodeid 4658c2ecf20Sopenharmony_ci * in cops_ioctl. This may cause a problem if someone steals 4668c2ecf20Sopenharmony_ci * our nodeid while we are resetting. 4678c2ecf20Sopenharmony_ci */ 4688c2ecf20Sopenharmony_ci if(lp->nodeid == 1) 4698c2ecf20Sopenharmony_ci cops_nodeid(dev,lp->node_acquire); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic void tangent_wait_reset(int ioaddr) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci int timeout=0; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci while(timeout++ < 5 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) 4798c2ecf20Sopenharmony_ci mdelay(1); /* Wait 1 second */ 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci/* 4838c2ecf20Sopenharmony_ci * Reset the LocalTalk board. 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_cistatic void cops_reset(struct net_device *dev, int sleep) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct cops_local *lp = netdev_priv(dev); 4888c2ecf20Sopenharmony_ci int ioaddr=dev->base_addr; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if(lp->board==TANGENT) 4918c2ecf20Sopenharmony_ci { 4928c2ecf20Sopenharmony_ci inb(ioaddr); /* Clear request latch. */ 4938c2ecf20Sopenharmony_ci outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */ 4948c2ecf20Sopenharmony_ci outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */ 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci tangent_wait_reset(ioaddr); 4978c2ecf20Sopenharmony_ci outb(0, ioaddr+TANG_CLEAR_INT); 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci if(lp->board==DAYNA) 5008c2ecf20Sopenharmony_ci { 5018c2ecf20Sopenharmony_ci outb(0, ioaddr+DAYNA_RESET); /* Assert the reset port */ 5028c2ecf20Sopenharmony_ci inb(ioaddr+DAYNA_RESET); /* Clear the reset */ 5038c2ecf20Sopenharmony_ci if (sleep) 5048c2ecf20Sopenharmony_ci msleep(333); 5058c2ecf20Sopenharmony_ci else 5068c2ecf20Sopenharmony_ci mdelay(333); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci netif_wake_queue(dev); 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic void cops_load (struct net_device *dev) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct ifreq ifr; 5158c2ecf20Sopenharmony_ci struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_ifru; 5168c2ecf20Sopenharmony_ci struct cops_local *lp = netdev_priv(dev); 5178c2ecf20Sopenharmony_ci int ioaddr=dev->base_addr; 5188c2ecf20Sopenharmony_ci int length, i = 0; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci strcpy(ifr.ifr_name,"lt0"); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* Get card's firmware code and do some checks on it. */ 5238c2ecf20Sopenharmony_ci#ifdef CONFIG_COPS_DAYNA 5248c2ecf20Sopenharmony_ci if(lp->board==DAYNA) 5258c2ecf20Sopenharmony_ci { 5268c2ecf20Sopenharmony_ci ltf->length=sizeof(ffdrv_code); 5278c2ecf20Sopenharmony_ci ltf->data=ffdrv_code; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci else 5308c2ecf20Sopenharmony_ci#endif 5318c2ecf20Sopenharmony_ci#ifdef CONFIG_COPS_TANGENT 5328c2ecf20Sopenharmony_ci if(lp->board==TANGENT) 5338c2ecf20Sopenharmony_ci { 5348c2ecf20Sopenharmony_ci ltf->length=sizeof(ltdrv_code); 5358c2ecf20Sopenharmony_ci ltf->data=ltdrv_code; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci else 5388c2ecf20Sopenharmony_ci#endif 5398c2ecf20Sopenharmony_ci { 5408c2ecf20Sopenharmony_ci printk(KERN_INFO "%s; unsupported board type.\n", dev->name); 5418c2ecf20Sopenharmony_ci return; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* Check to make sure firmware is correct length. */ 5458c2ecf20Sopenharmony_ci if(lp->board==DAYNA && ltf->length!=5983) 5468c2ecf20Sopenharmony_ci { 5478c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name); 5488c2ecf20Sopenharmony_ci return; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci if(lp->board==TANGENT && ltf->length!=2501) 5518c2ecf20Sopenharmony_ci { 5528c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name); 5538c2ecf20Sopenharmony_ci return; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if(lp->board==DAYNA) 5578c2ecf20Sopenharmony_ci { 5588c2ecf20Sopenharmony_ci /* 5598c2ecf20Sopenharmony_ci * We must wait for a status response 5608c2ecf20Sopenharmony_ci * with the DAYNA board. 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ci while(++i<65536) 5638c2ecf20Sopenharmony_ci { 5648c2ecf20Sopenharmony_ci if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1) 5658c2ecf20Sopenharmony_ci break; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if(i==65536) 5698c2ecf20Sopenharmony_ci return; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* 5738c2ecf20Sopenharmony_ci * Upload the firmware and kick. Byte-by-byte works nicely here. 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_ci i=0; 5768c2ecf20Sopenharmony_ci length = ltf->length; 5778c2ecf20Sopenharmony_ci while(length--) 5788c2ecf20Sopenharmony_ci { 5798c2ecf20Sopenharmony_ci outb(ltf->data[i], ioaddr); 5808c2ecf20Sopenharmony_ci i++; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if(cops_debug > 1) 5848c2ecf20Sopenharmony_ci printk("%s: Uploaded firmware - %d bytes of %d bytes.\n", 5858c2ecf20Sopenharmony_ci dev->name, i, ltf->length); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if(lp->board==DAYNA) /* Tell Dayna to run the firmware code. */ 5888c2ecf20Sopenharmony_ci outb(1, ioaddr+DAYNA_INT_CARD); 5898c2ecf20Sopenharmony_ci else /* Tell Tang to run the firmware code. */ 5908c2ecf20Sopenharmony_ci inb(ioaddr); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if(lp->board==TANGENT) 5938c2ecf20Sopenharmony_ci { 5948c2ecf20Sopenharmony_ci tangent_wait_reset(ioaddr); 5958c2ecf20Sopenharmony_ci inb(ioaddr); /* Clear initial ready signal. */ 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci/* 6008c2ecf20Sopenharmony_ci * Get the LocalTalk Nodeid from the card. We can suggest 6018c2ecf20Sopenharmony_ci * any nodeid 1-254. The card will try and get that exact 6028c2ecf20Sopenharmony_ci * address else we can specify 0 as the nodeid and the card 6038c2ecf20Sopenharmony_ci * will autoprobe for a nodeid. 6048c2ecf20Sopenharmony_ci */ 6058c2ecf20Sopenharmony_cistatic int cops_nodeid (struct net_device *dev, int nodeid) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct cops_local *lp = netdev_priv(dev); 6088c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if(lp->board == DAYNA) 6118c2ecf20Sopenharmony_ci { 6128c2ecf20Sopenharmony_ci /* Empty any pending adapter responses. */ 6138c2ecf20Sopenharmony_ci while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) 6148c2ecf20Sopenharmony_ci { 6158c2ecf20Sopenharmony_ci outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupts. */ 6168c2ecf20Sopenharmony_ci if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) 6178c2ecf20Sopenharmony_ci cops_rx(dev); /* Kick any packets waiting. */ 6188c2ecf20Sopenharmony_ci schedule(); 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci outb(2, ioaddr); /* Output command packet length as 2. */ 6228c2ecf20Sopenharmony_ci outb(0, ioaddr); 6238c2ecf20Sopenharmony_ci outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ 6248c2ecf20Sopenharmony_ci outb(nodeid, ioaddr); /* Suggest node address. */ 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if(lp->board == TANGENT) 6288c2ecf20Sopenharmony_ci { 6298c2ecf20Sopenharmony_ci /* Empty any pending adapter responses. */ 6308c2ecf20Sopenharmony_ci while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) 6318c2ecf20Sopenharmony_ci { 6328c2ecf20Sopenharmony_ci outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupt. */ 6338c2ecf20Sopenharmony_ci cops_rx(dev); /* Kick out packets waiting. */ 6348c2ecf20Sopenharmony_ci schedule(); 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* Not sure what Tangent does if nodeid picked is used. */ 6388c2ecf20Sopenharmony_ci if(nodeid == 0) /* Seed. */ 6398c2ecf20Sopenharmony_ci nodeid = jiffies&0xFF; /* Get a random try */ 6408c2ecf20Sopenharmony_ci outb(2, ioaddr); /* Command length LSB */ 6418c2ecf20Sopenharmony_ci outb(0, ioaddr); /* Command length MSB */ 6428c2ecf20Sopenharmony_ci outb(LAP_INIT, ioaddr); /* Send LAP_INIT byte */ 6438c2ecf20Sopenharmony_ci outb(nodeid, ioaddr); /* LAP address hint. */ 6448c2ecf20Sopenharmony_ci outb(0xFF, ioaddr); /* Int. level to use */ 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci lp->node_acquire=0; /* Set nodeid holder to 0. */ 6488c2ecf20Sopenharmony_ci while(lp->node_acquire==0) /* Get *True* nodeid finally. */ 6498c2ecf20Sopenharmony_ci { 6508c2ecf20Sopenharmony_ci outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci if(lp->board == DAYNA) 6538c2ecf20Sopenharmony_ci { 6548c2ecf20Sopenharmony_ci if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) 6558c2ecf20Sopenharmony_ci cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci if(lp->board == TANGENT) 6588c2ecf20Sopenharmony_ci { 6598c2ecf20Sopenharmony_ci if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) 6608c2ecf20Sopenharmony_ci cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci schedule(); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if(cops_debug > 1) 6668c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n", 6678c2ecf20Sopenharmony_ci dev->name, lp->node_acquire); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci lp->nodeid=1; /* Set got nodeid to 1. */ 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci return 0; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci/* 6758c2ecf20Sopenharmony_ci * Poll the Tangent type cards to see if we have work. 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic void cops_poll(struct timer_list *unused) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci int ioaddr, status; 6818c2ecf20Sopenharmony_ci int boguscount = 0; 6828c2ecf20Sopenharmony_ci struct net_device *dev = cops_timer_dev; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci del_timer(&cops_timer); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if(dev == NULL) 6878c2ecf20Sopenharmony_ci return; /* We've been downed */ 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci ioaddr = dev->base_addr; 6908c2ecf20Sopenharmony_ci do { 6918c2ecf20Sopenharmony_ci status=inb(ioaddr+TANG_CARD_STATUS); 6928c2ecf20Sopenharmony_ci if(status & TANG_RX_READY) 6938c2ecf20Sopenharmony_ci cops_rx(dev); 6948c2ecf20Sopenharmony_ci if(status & TANG_TX_READY) 6958c2ecf20Sopenharmony_ci netif_wake_queue(dev); 6968c2ecf20Sopenharmony_ci status = inb(ioaddr+TANG_CARD_STATUS); 6978c2ecf20Sopenharmony_ci } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci /* poll 20 times per second */ 7008c2ecf20Sopenharmony_ci cops_timer.expires = jiffies + HZ/20; 7018c2ecf20Sopenharmony_ci add_timer(&cops_timer); 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci/* 7058c2ecf20Sopenharmony_ci * The typical workload of the driver: 7068c2ecf20Sopenharmony_ci * Handle the network interface interrupts. 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_cistatic irqreturn_t cops_interrupt(int irq, void *dev_id) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct net_device *dev = dev_id; 7118c2ecf20Sopenharmony_ci struct cops_local *lp; 7128c2ecf20Sopenharmony_ci int ioaddr, status; 7138c2ecf20Sopenharmony_ci int boguscount = 0; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci ioaddr = dev->base_addr; 7168c2ecf20Sopenharmony_ci lp = netdev_priv(dev); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci if(lp->board==DAYNA) 7198c2ecf20Sopenharmony_ci { 7208c2ecf20Sopenharmony_ci do { 7218c2ecf20Sopenharmony_ci outb(0, ioaddr + COPS_CLEAR_INT); 7228c2ecf20Sopenharmony_ci status=inb(ioaddr+DAYNA_CARD_STATUS); 7238c2ecf20Sopenharmony_ci if((status&0x03)==DAYNA_RX_REQUEST) 7248c2ecf20Sopenharmony_ci cops_rx(dev); 7258c2ecf20Sopenharmony_ci netif_wake_queue(dev); 7268c2ecf20Sopenharmony_ci } while(++boguscount < 20); 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci else 7298c2ecf20Sopenharmony_ci { 7308c2ecf20Sopenharmony_ci do { 7318c2ecf20Sopenharmony_ci status=inb(ioaddr+TANG_CARD_STATUS); 7328c2ecf20Sopenharmony_ci if(status & TANG_RX_READY) 7338c2ecf20Sopenharmony_ci cops_rx(dev); 7348c2ecf20Sopenharmony_ci if(status & TANG_TX_READY) 7358c2ecf20Sopenharmony_ci netif_wake_queue(dev); 7368c2ecf20Sopenharmony_ci status=inb(ioaddr+TANG_CARD_STATUS); 7378c2ecf20Sopenharmony_ci } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci/* 7448c2ecf20Sopenharmony_ci * We have a good packet(s), get it/them out of the buffers. 7458c2ecf20Sopenharmony_ci */ 7468c2ecf20Sopenharmony_cistatic void cops_rx(struct net_device *dev) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci int pkt_len = 0; 7498c2ecf20Sopenharmony_ci int rsp_type = 0; 7508c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 7518c2ecf20Sopenharmony_ci struct cops_local *lp = netdev_priv(dev); 7528c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 7538c2ecf20Sopenharmony_ci int boguscount = 0; 7548c2ecf20Sopenharmony_ci unsigned long flags; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if(lp->board==DAYNA) 7608c2ecf20Sopenharmony_ci { 7618c2ecf20Sopenharmony_ci outb(0, ioaddr); /* Send out Zero length. */ 7628c2ecf20Sopenharmony_ci outb(0, ioaddr); 7638c2ecf20Sopenharmony_ci outb(DATA_READ, ioaddr); /* Send read command out. */ 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* Wait for DMA to turn around. */ 7668c2ecf20Sopenharmony_ci while(++boguscount<1000000) 7678c2ecf20Sopenharmony_ci { 7688c2ecf20Sopenharmony_ci barrier(); 7698c2ecf20Sopenharmony_ci if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY) 7708c2ecf20Sopenharmony_ci break; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if(boguscount==1000000) 7748c2ecf20Sopenharmony_ci { 7758c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: DMA timed out.\n",dev->name); 7768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 7778c2ecf20Sopenharmony_ci return; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* Get response length. */ 7828c2ecf20Sopenharmony_ci pkt_len = inb(ioaddr); 7838c2ecf20Sopenharmony_ci pkt_len |= (inb(ioaddr) << 8); 7848c2ecf20Sopenharmony_ci /* Input IO code. */ 7858c2ecf20Sopenharmony_ci rsp_type=inb(ioaddr); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* Malloc up new buffer. */ 7888c2ecf20Sopenharmony_ci skb = dev_alloc_skb(pkt_len); 7898c2ecf20Sopenharmony_ci if(skb == NULL) 7908c2ecf20Sopenharmony_ci { 7918c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", 7928c2ecf20Sopenharmony_ci dev->name); 7938c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 7948c2ecf20Sopenharmony_ci while(pkt_len--) /* Discard packet */ 7958c2ecf20Sopenharmony_ci inb(ioaddr); 7968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 7978c2ecf20Sopenharmony_ci return; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci skb->dev = dev; 8008c2ecf20Sopenharmony_ci skb_put(skb, pkt_len); 8018c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_LOCALTALK); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci insb(ioaddr, skb->data, pkt_len); /* Eat the Data */ 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if(lp->board==DAYNA) 8068c2ecf20Sopenharmony_ci outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card */ 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); /* Restore interrupts. */ 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* Check for bad response length */ 8118c2ecf20Sopenharmony_ci if(pkt_len < 0 || pkt_len > MAX_LLAP_SIZE) 8128c2ecf20Sopenharmony_ci { 8138c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Bad packet length of %d bytes.\n", 8148c2ecf20Sopenharmony_ci dev->name, pkt_len); 8158c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 8168c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 8178c2ecf20Sopenharmony_ci return; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci /* Set nodeid and then get out. */ 8218c2ecf20Sopenharmony_ci if(rsp_type == LAP_INIT_RSP) 8228c2ecf20Sopenharmony_ci { /* Nodeid taken from received packet. */ 8238c2ecf20Sopenharmony_ci lp->node_acquire = skb->data[0]; 8248c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 8258c2ecf20Sopenharmony_ci return; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* One last check to make sure we have a good packet. */ 8298c2ecf20Sopenharmony_ci if(rsp_type != LAP_RESPONSE) 8308c2ecf20Sopenharmony_ci { 8318c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Bad packet type %d.\n", dev->name, rsp_type); 8328c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 8338c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 8348c2ecf20Sopenharmony_ci return; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); /* Point to entire packet. */ 8388c2ecf20Sopenharmony_ci skb_pull(skb,3); 8398c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); /* Point to data (Skip header). */ 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* Update the counters. */ 8428c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 8438c2ecf20Sopenharmony_ci dev->stats.rx_bytes += skb->len; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* Send packet to a higher place. */ 8468c2ecf20Sopenharmony_ci netif_rx(skb); 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic void cops_timeout(struct net_device *dev, unsigned int txqueue) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci struct cops_local *lp = netdev_priv(dev); 8528c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 8558c2ecf20Sopenharmony_ci if(lp->board==TANGENT) 8568c2ecf20Sopenharmony_ci { 8578c2ecf20Sopenharmony_ci if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) 8588c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name); 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name); 8618c2ecf20Sopenharmony_ci cops_jumpstart(dev); /* Restart the card. */ 8628c2ecf20Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 8638c2ecf20Sopenharmony_ci netif_wake_queue(dev); 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci/* 8688c2ecf20Sopenharmony_ci * Make the card transmit a LocalTalk packet. 8698c2ecf20Sopenharmony_ci */ 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic netdev_tx_t cops_send_packet(struct sk_buff *skb, 8728c2ecf20Sopenharmony_ci struct net_device *dev) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci struct cops_local *lp = netdev_priv(dev); 8758c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 8768c2ecf20Sopenharmony_ci unsigned long flags; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* 8798c2ecf20Sopenharmony_ci * Block a timer-based transmit from overlapping. 8808c2ecf20Sopenharmony_ci */ 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci netif_stop_queue(dev); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 8858c2ecf20Sopenharmony_ci if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */ 8868c2ecf20Sopenharmony_ci while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) 8878c2ecf20Sopenharmony_ci cpu_relax(); 8888c2ecf20Sopenharmony_ci if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */ 8898c2ecf20Sopenharmony_ci while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) 8908c2ecf20Sopenharmony_ci cpu_relax(); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci /* Output IO length. */ 8938c2ecf20Sopenharmony_ci outb(skb->len, ioaddr); 8948c2ecf20Sopenharmony_ci outb(skb->len >> 8, ioaddr); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci /* Output IO code. */ 8978c2ecf20Sopenharmony_ci outb(LAP_WRITE, ioaddr); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if(lp->board == DAYNA) /* Check the transmit buffer again. */ 9008c2ecf20Sopenharmony_ci while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci outsb(ioaddr, skb->data, skb->len); /* Send out the data. */ 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if(lp->board==DAYNA) /* Dayna requires you kick the card */ 9058c2ecf20Sopenharmony_ci outb(1, ioaddr+DAYNA_INT_CARD); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); /* Restore interrupts. */ 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci /* Done sending packet, update counters and cleanup. */ 9108c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 9118c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 9128c2ecf20Sopenharmony_ci dev_kfree_skb (skb); 9138c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci/* 9178c2ecf20Sopenharmony_ci * Dummy function to keep the Appletalk layer happy. 9188c2ecf20Sopenharmony_ci */ 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic void set_multicast_list(struct net_device *dev) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci if(cops_debug >= 3) 9238c2ecf20Sopenharmony_ci printk("%s: set_multicast_list executed\n", dev->name); 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci/* 9278c2ecf20Sopenharmony_ci * System ioctls for the COPS LocalTalk card. 9288c2ecf20Sopenharmony_ci */ 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic int cops_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci struct cops_local *lp = netdev_priv(dev); 9338c2ecf20Sopenharmony_ci struct sockaddr_at *sa = (struct sockaddr_at *)&ifr->ifr_addr; 9348c2ecf20Sopenharmony_ci struct atalk_addr *aa = &lp->node_addr; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci switch(cmd) 9378c2ecf20Sopenharmony_ci { 9388c2ecf20Sopenharmony_ci case SIOCSIFADDR: 9398c2ecf20Sopenharmony_ci /* Get and set the nodeid and network # atalkd wants. */ 9408c2ecf20Sopenharmony_ci cops_nodeid(dev, sa->sat_addr.s_node); 9418c2ecf20Sopenharmony_ci aa->s_net = sa->sat_addr.s_net; 9428c2ecf20Sopenharmony_ci aa->s_node = lp->node_acquire; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* Set broardcast address. */ 9458c2ecf20Sopenharmony_ci dev->broadcast[0] = 0xFF; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* Set hardware address. */ 9488c2ecf20Sopenharmony_ci dev->dev_addr[0] = aa->s_node; 9498c2ecf20Sopenharmony_ci dev->addr_len = 1; 9508c2ecf20Sopenharmony_ci return 0; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci case SIOCGIFADDR: 9538c2ecf20Sopenharmony_ci sa->sat_addr.s_net = aa->s_net; 9548c2ecf20Sopenharmony_ci sa->sat_addr.s_node = aa->s_node; 9558c2ecf20Sopenharmony_ci return 0; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci default: 9588c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci/* 9638c2ecf20Sopenharmony_ci * The inverse routine to cops_open(). 9648c2ecf20Sopenharmony_ci */ 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic int cops_close(struct net_device *dev) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci struct cops_local *lp = netdev_priv(dev); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci /* If we were running polled, yank the timer. 9718c2ecf20Sopenharmony_ci */ 9728c2ecf20Sopenharmony_ci if(lp->board==TANGENT && dev->irq==0) 9738c2ecf20Sopenharmony_ci del_timer(&cops_timer); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci netif_stop_queue(dev); 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci#ifdef MODULE 9818c2ecf20Sopenharmony_cistatic struct net_device *cops_dev; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 9848c2ecf20Sopenharmony_cimodule_param_hw(io, int, ioport, 0); 9858c2ecf20Sopenharmony_cimodule_param_hw(irq, int, irq, 0); 9868c2ecf20Sopenharmony_cimodule_param_hw(board_type, int, other, 0); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic int __init cops_module_init(void) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci if (io == 0) 9918c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n", 9928c2ecf20Sopenharmony_ci cardname); 9938c2ecf20Sopenharmony_ci cops_dev = cops_probe(-1); 9948c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(cops_dev); 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic void __exit cops_module_exit(void) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci unregister_netdev(cops_dev); 10008c2ecf20Sopenharmony_ci cleanup_card(cops_dev); 10018c2ecf20Sopenharmony_ci free_netdev(cops_dev); 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_cimodule_init(cops_module_init); 10048c2ecf20Sopenharmony_cimodule_exit(cops_module_exit); 10058c2ecf20Sopenharmony_ci#endif /* MODULE */ 1006