123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- /*
- * atari_nfeth.c - ARAnyM ethernet card driver for GNU/Linux
- *
- * Copyright (c) 2005 Milan Jurik, Petr Stehlik of ARAnyM dev team
- *
- * Based on ARAnyM driver for FreeMiNT written by Standa Opichal
- *
- * This software may be used and distributed according to the terms of
- * the GNU General Public License (GPL), incorporated herein by reference.
- */
- #define DRV_VERSION "0.3"
- #define DRV_RELDATE "10/12/2005"
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/interrupt.h>
- #include <linux/module.h>
- #include <asm/natfeat.h>
- #include <asm/virtconvert.h>
- enum {
- GET_VERSION = 0,/* no parameters, return NFAPI_VERSION in d0 */
- XIF_INTLEVEL, /* no parameters, return Interrupt Level in d0 */
- XIF_IRQ, /* acknowledge interrupt from host */
- XIF_START, /* (ethX), called on 'ifup', start receiver thread */
- XIF_STOP, /* (ethX), called on 'ifdown', stop the thread */
- XIF_READLENGTH, /* (ethX), return size of network data block to read */
- XIF_READBLOCK, /* (ethX, buffer, size), read block of network data */
- XIF_WRITEBLOCK, /* (ethX, buffer, size), write block of network data */
- XIF_GET_MAC, /* (ethX, buffer, size), return MAC HW addr in buffer */
- XIF_GET_IPHOST, /* (ethX, buffer, size), return IP address of host */
- XIF_GET_IPATARI,/* (ethX, buffer, size), return IP address of atari */
- XIF_GET_NETMASK /* (ethX, buffer, size), return IP netmask */
- };
- #define MAX_UNIT 8
- /* These identify the driver base version and may not be removed. */
- static const char version[] =
- KERN_INFO KBUILD_MODNAME ".c:v" DRV_VERSION " " DRV_RELDATE
- " S.Opichal, M.Jurik, P.Stehlik\n"
- KERN_INFO " http://aranym.org/\n";
- MODULE_AUTHOR("Milan Jurik");
- MODULE_DESCRIPTION("Atari NFeth driver");
- MODULE_LICENSE("GPL");
- /*
- MODULE_PARM(nfeth_debug, "i");
- MODULE_PARM_DESC(nfeth_debug, "nfeth_debug level (1-2)");
- */
- static long nfEtherID;
- static int nfEtherIRQ;
- struct nfeth_private {
- int ethX;
- };
- static struct net_device *nfeth_dev[MAX_UNIT];
- static int nfeth_open(struct net_device *dev)
- {
- struct nfeth_private *priv = netdev_priv(dev);
- int res;
- res = nf_call(nfEtherID + XIF_START, priv->ethX);
- netdev_dbg(dev, "%s: %d\n", __func__, res);
- /* Ready for data */
- netif_start_queue(dev);
- return 0;
- }
- static int nfeth_stop(struct net_device *dev)
- {
- struct nfeth_private *priv = netdev_priv(dev);
- /* No more data */
- netif_stop_queue(dev);
- nf_call(nfEtherID + XIF_STOP, priv->ethX);
- return 0;
- }
- /*
- * Read a packet out of the adapter and pass it to the upper layers
- */
- static inline void recv_packet(struct net_device *dev)
- {
- struct nfeth_private *priv = netdev_priv(dev);
- unsigned short pktlen;
- struct sk_buff *skb;
- /* read packet length (excluding 32 bit crc) */
- pktlen = nf_call(nfEtherID + XIF_READLENGTH, priv->ethX);
- netdev_dbg(dev, "%s: %u\n", __func__, pktlen);
- if (!pktlen) {
- netdev_dbg(dev, "%s: pktlen == 0\n", __func__);
- dev->stats.rx_errors++;
- return;
- }
- skb = dev_alloc_skb(pktlen + 2);
- if (!skb) {
- netdev_dbg(dev, "%s: out of mem (buf_alloc failed)\n",
- __func__);
- dev->stats.rx_dropped++;
- return;
- }
- skb->dev = dev;
- skb_reserve(skb, 2); /* 16 Byte align */
- skb_put(skb, pktlen); /* make room */
- nf_call(nfEtherID + XIF_READBLOCK, priv->ethX, virt_to_phys(skb->data),
- pktlen);
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->last_rx = jiffies;
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pktlen;
- /* and enqueue packet */
- return;
- }
- static irqreturn_t nfeth_interrupt(int irq, void *dev_id)
- {
- int i, m, mask;
- mask = nf_call(nfEtherID + XIF_IRQ, 0);
- for (i = 0, m = 1; i < MAX_UNIT; m <<= 1, i++) {
- if (mask & m && nfeth_dev[i]) {
- recv_packet(nfeth_dev[i]);
- nf_call(nfEtherID + XIF_IRQ, m);
- }
- }
- return IRQ_HANDLED;
- }
- static int nfeth_xmit(struct sk_buff *skb, struct net_device *dev)
- {
- unsigned int len;
- char *data, shortpkt[ETH_ZLEN];
- struct nfeth_private *priv = netdev_priv(dev);
- data = skb->data;
- len = skb->len;
- if (len < ETH_ZLEN) {
- memset(shortpkt, 0, ETH_ZLEN);
- memcpy(shortpkt, data, len);
- data = shortpkt;
- len = ETH_ZLEN;
- }
- netdev_dbg(dev, "%s: send %u bytes\n", __func__, len);
- nf_call(nfEtherID + XIF_WRITEBLOCK, priv->ethX, virt_to_phys(data),
- len);
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += len;
- dev_kfree_skb(skb);
- return 0;
- }
- static void nfeth_tx_timeout(struct net_device *dev)
- {
- dev->stats.tx_errors++;
- netif_wake_queue(dev);
- }
- static const struct net_device_ops nfeth_netdev_ops = {
- .ndo_open = nfeth_open,
- .ndo_stop = nfeth_stop,
- .ndo_start_xmit = nfeth_xmit,
- .ndo_tx_timeout = nfeth_tx_timeout,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_change_mtu = eth_change_mtu,
- .ndo_set_mac_address = eth_mac_addr,
- };
- static struct net_device * __init nfeth_probe(int unit)
- {
- struct net_device *dev;
- struct nfeth_private *priv;
- char mac[ETH_ALEN], host_ip[32], local_ip[32];
- int err;
- if (!nf_call(nfEtherID + XIF_GET_MAC, unit, virt_to_phys(mac),
- ETH_ALEN))
- return NULL;
- dev = alloc_etherdev(sizeof(struct nfeth_private));
- if (!dev)
- return NULL;
- dev->irq = nfEtherIRQ;
- dev->netdev_ops = &nfeth_netdev_ops;
- memcpy(dev->dev_addr, mac, ETH_ALEN);
- priv = netdev_priv(dev);
- priv->ethX = unit;
- err = register_netdev(dev);
- if (err) {
- free_netdev(dev);
- return NULL;
- }
- nf_call(nfEtherID + XIF_GET_IPHOST, unit,
- virt_to_phys(host_ip), sizeof(host_ip));
- nf_call(nfEtherID + XIF_GET_IPATARI, unit,
- virt_to_phys(local_ip), sizeof(local_ip));
- netdev_info(dev, KBUILD_MODNAME " addr:%s (%s) HWaddr:%pM\n", host_ip,
- local_ip, mac);
- return dev;
- }
- static int __init nfeth_init(void)
- {
- long ver;
- int error, i;
- nfEtherID = nf_get_id("ETHERNET");
- if (!nfEtherID)
- return -ENODEV;
- ver = nf_call(nfEtherID + GET_VERSION);
- pr_info("API %lu\n", ver);
- nfEtherIRQ = nf_call(nfEtherID + XIF_INTLEVEL);
- error = request_irq(nfEtherIRQ, nfeth_interrupt, IRQF_SHARED,
- "eth emu", nfeth_interrupt);
- if (error) {
- pr_err("request for irq %d failed %d", nfEtherIRQ, error);
- return error;
- }
- for (i = 0; i < MAX_UNIT; i++)
- nfeth_dev[i] = nfeth_probe(i);
- return 0;
- }
- static void __exit nfeth_cleanup(void)
- {
- int i;
- for (i = 0; i < MAX_UNIT; i++) {
- if (nfeth_dev[i]) {
- unregister_netdev(nfeth_dev[0]);
- free_netdev(nfeth_dev[0]);
- }
- }
- free_irq(nfEtherIRQ, nfeth_interrupt);
- }
- module_init(nfeth_init);
- module_exit(nfeth_cleanup);
|