linux/arch/m68k/emu/nfeth.c
<<
>>
Prefs
   1/*
   2 * atari_nfeth.c - ARAnyM ethernet card driver for GNU/Linux
   3 *
   4 * Copyright (c) 2005 Milan Jurik, Petr Stehlik of ARAnyM dev team
   5 *
   6 * Based on ARAnyM driver for FreeMiNT written by Standa Opichal
   7 *
   8 * This software may be used and distributed according to the terms of
   9 * the GNU General Public License (GPL), incorporated herein by reference.
  10 */
  11
  12#define DRV_VERSION     "0.3"
  13#define DRV_RELDATE     "10/12/2005"
  14
  15#define pr_fmt(fmt)     KBUILD_MODNAME ": " fmt
  16
  17#include <linux/netdevice.h>
  18#include <linux/etherdevice.h>
  19#include <linux/interrupt.h>
  20#include <linux/module.h>
  21#include <asm/natfeat.h>
  22#include <asm/virtconvert.h>
  23
  24enum {
  25        GET_VERSION = 0,/* no parameters, return NFAPI_VERSION in d0 */
  26        XIF_INTLEVEL,   /* no parameters, return Interrupt Level in d0 */
  27        XIF_IRQ,        /* acknowledge interrupt from host */
  28        XIF_START,      /* (ethX), called on 'ifup', start receiver thread */
  29        XIF_STOP,       /* (ethX), called on 'ifdown', stop the thread */
  30        XIF_READLENGTH, /* (ethX), return size of network data block to read */
  31        XIF_READBLOCK,  /* (ethX, buffer, size), read block of network data */
  32        XIF_WRITEBLOCK, /* (ethX, buffer, size), write block of network data */
  33        XIF_GET_MAC,    /* (ethX, buffer, size), return MAC HW addr in buffer */
  34        XIF_GET_IPHOST, /* (ethX, buffer, size), return IP address of host */
  35        XIF_GET_IPATARI,/* (ethX, buffer, size), return IP address of atari */
  36        XIF_GET_NETMASK /* (ethX, buffer, size), return IP netmask */
  37};
  38
  39#define MAX_UNIT        8
  40
  41/* These identify the driver base version and may not be removed. */
  42static const char version[] =
  43        KERN_INFO KBUILD_MODNAME ".c:v" DRV_VERSION " " DRV_RELDATE
  44        " S.Opichal, M.Jurik, P.Stehlik\n"
  45        KERN_INFO " http://aranym.org/\n";
  46
  47MODULE_AUTHOR("Milan Jurik");
  48MODULE_DESCRIPTION("Atari NFeth driver");
  49MODULE_LICENSE("GPL");
  50/*
  51MODULE_PARM(nfeth_debug, "i");
  52MODULE_PARM_DESC(nfeth_debug, "nfeth_debug level (1-2)");
  53*/
  54
  55
  56static long nfEtherID;
  57static int nfEtherIRQ;
  58
  59struct nfeth_private {
  60        int ethX;
  61};
  62
  63static struct net_device *nfeth_dev[MAX_UNIT];
  64
  65static int nfeth_open(struct net_device *dev)
  66{
  67        struct nfeth_private *priv = netdev_priv(dev);
  68        int res;
  69
  70        res = nf_call(nfEtherID + XIF_START, priv->ethX);
  71        netdev_dbg(dev, "%s: %d\n", __func__, res);
  72
  73        /* Ready for data */
  74        netif_start_queue(dev);
  75
  76        return 0;
  77}
  78
  79static int nfeth_stop(struct net_device *dev)
  80{
  81        struct nfeth_private *priv = netdev_priv(dev);
  82
  83        /* No more data */
  84        netif_stop_queue(dev);
  85
  86        nf_call(nfEtherID + XIF_STOP, priv->ethX);
  87
  88        return 0;
  89}
  90
  91/*
  92 * Read a packet out of the adapter and pass it to the upper layers
  93 */
  94static inline void recv_packet(struct net_device *dev)
  95{
  96        struct nfeth_private *priv = netdev_priv(dev);
  97        unsigned short pktlen;
  98        struct sk_buff *skb;
  99
 100        /* read packet length (excluding 32 bit crc) */
 101        pktlen = nf_call(nfEtherID + XIF_READLENGTH, priv->ethX);
 102
 103        netdev_dbg(dev, "%s: %u\n", __func__, pktlen);
 104
 105        if (!pktlen) {
 106                netdev_dbg(dev, "%s: pktlen == 0\n", __func__);
 107                dev->stats.rx_errors++;
 108                return;
 109        }
 110
 111        skb = dev_alloc_skb(pktlen + 2);
 112        if (!skb) {
 113                netdev_dbg(dev, "%s: out of mem (buf_alloc failed)\n",
 114                           __func__);
 115                dev->stats.rx_dropped++;
 116                return;
 117        }
 118
 119        skb->dev = dev;
 120        skb_reserve(skb, 2);            /* 16 Byte align  */
 121        skb_put(skb, pktlen);           /* make room */
 122        nf_call(nfEtherID + XIF_READBLOCK, priv->ethX, virt_to_phys(skb->data),
 123                pktlen);
 124
 125        skb->protocol = eth_type_trans(skb, dev);
 126        netif_rx(skb);
 127        dev->last_rx = jiffies;
 128        dev->stats.rx_packets++;
 129        dev->stats.rx_bytes += pktlen;
 130
 131        /* and enqueue packet */
 132        return;
 133}
 134
 135static irqreturn_t nfeth_interrupt(int irq, void *dev_id)
 136{
 137        int i, m, mask;
 138
 139        mask = nf_call(nfEtherID + XIF_IRQ, 0);
 140        for (i = 0, m = 1; i < MAX_UNIT; m <<= 1, i++) {
 141                if (mask & m && nfeth_dev[i]) {
 142                        recv_packet(nfeth_dev[i]);
 143                        nf_call(nfEtherID + XIF_IRQ, m);
 144                }
 145        }
 146        return IRQ_HANDLED;
 147}
 148
 149static int nfeth_xmit(struct sk_buff *skb, struct net_device *dev)
 150{
 151        unsigned int len;
 152        char *data, shortpkt[ETH_ZLEN];
 153        struct nfeth_private *priv = netdev_priv(dev);
 154
 155        data = skb->data;
 156        len = skb->len;
 157        if (len < ETH_ZLEN) {
 158                memset(shortpkt, 0, ETH_ZLEN);
 159                memcpy(shortpkt, data, len);
 160                data = shortpkt;
 161                len = ETH_ZLEN;
 162        }
 163
 164        netdev_dbg(dev, "%s: send %u bytes\n", __func__, len);
 165        nf_call(nfEtherID + XIF_WRITEBLOCK, priv->ethX, virt_to_phys(data),
 166                len);
 167
 168        dev->stats.tx_packets++;
 169        dev->stats.tx_bytes += len;
 170
 171        dev_kfree_skb(skb);
 172        return 0;
 173}
 174
 175static void nfeth_tx_timeout(struct net_device *dev)
 176{
 177        dev->stats.tx_errors++;
 178        netif_wake_queue(dev);
 179}
 180
 181static const struct net_device_ops nfeth_netdev_ops = {
 182        .ndo_open               = nfeth_open,
 183        .ndo_stop               = nfeth_stop,
 184        .ndo_start_xmit         = nfeth_xmit,
 185        .ndo_tx_timeout         = nfeth_tx_timeout,
 186        .ndo_validate_addr      = eth_validate_addr,
 187        .ndo_change_mtu         = eth_change_mtu,
 188        .ndo_set_mac_address    = eth_mac_addr,
 189};
 190
 191static struct net_device * __init nfeth_probe(int unit)
 192{
 193        struct net_device *dev;
 194        struct nfeth_private *priv;
 195        char mac[ETH_ALEN], host_ip[32], local_ip[32];
 196        int err;
 197
 198        if (!nf_call(nfEtherID + XIF_GET_MAC, unit, virt_to_phys(mac),
 199                     ETH_ALEN))
 200                return NULL;
 201
 202        dev = alloc_etherdev(sizeof(struct nfeth_private));
 203        if (!dev)
 204                return NULL;
 205
 206        dev->irq = nfEtherIRQ;
 207        dev->netdev_ops = &nfeth_netdev_ops;
 208
 209        memcpy(dev->dev_addr, mac, ETH_ALEN);
 210
 211        priv = netdev_priv(dev);
 212        priv->ethX = unit;
 213
 214        err = register_netdev(dev);
 215        if (err) {
 216                free_netdev(dev);
 217                return NULL;
 218        }
 219
 220        nf_call(nfEtherID + XIF_GET_IPHOST, unit,
 221                virt_to_phys(host_ip), sizeof(host_ip));
 222        nf_call(nfEtherID + XIF_GET_IPATARI, unit,
 223                virt_to_phys(local_ip), sizeof(local_ip));
 224
 225        netdev_info(dev, KBUILD_MODNAME " addr:%s (%s) HWaddr:%pM\n", host_ip,
 226                    local_ip, mac);
 227
 228        return dev;
 229}
 230
 231static int __init nfeth_init(void)
 232{
 233        long ver;
 234        int error, i;
 235
 236        nfEtherID = nf_get_id("ETHERNET");
 237        if (!nfEtherID)
 238                return -ENODEV;
 239
 240        ver = nf_call(nfEtherID + GET_VERSION);
 241        pr_info("API %lu\n", ver);
 242
 243        nfEtherIRQ = nf_call(nfEtherID + XIF_INTLEVEL);
 244        error = request_irq(nfEtherIRQ, nfeth_interrupt, IRQF_SHARED,
 245                            "eth emu", nfeth_interrupt);
 246        if (error) {
 247                pr_err("request for irq %d failed %d", nfEtherIRQ, error);
 248                return error;
 249        }
 250
 251        for (i = 0; i < MAX_UNIT; i++)
 252                nfeth_dev[i] = nfeth_probe(i);
 253
 254        return 0;
 255}
 256
 257static void __exit nfeth_cleanup(void)
 258{
 259        int i;
 260
 261        for (i = 0; i < MAX_UNIT; i++) {
 262                if (nfeth_dev[i]) {
 263                        unregister_netdev(nfeth_dev[0]);
 264                        free_netdev(nfeth_dev[0]);
 265                }
 266        }
 267        free_irq(nfEtherIRQ, nfeth_interrupt);
 268}
 269
 270module_init(nfeth_init);
 271module_exit(nfeth_cleanup);
 272