19cd7b148SMilan Jurik /* 29cd7b148SMilan Jurik * atari_nfeth.c - ARAnyM ethernet card driver for GNU/Linux 39cd7b148SMilan Jurik * 49cd7b148SMilan Jurik * Copyright (c) 2005 Milan Jurik, Petr Stehlik of ARAnyM dev team 59cd7b148SMilan Jurik * 69cd7b148SMilan Jurik * Based on ARAnyM driver for FreeMiNT written by Standa Opichal 79cd7b148SMilan Jurik * 89cd7b148SMilan Jurik * This software may be used and distributed according to the terms of 99cd7b148SMilan Jurik * the GNU General Public License (GPL), incorporated herein by reference. 109cd7b148SMilan Jurik */ 119cd7b148SMilan Jurik 129cd7b148SMilan Jurik #define DRV_VERSION "0.3" 139cd7b148SMilan Jurik #define DRV_RELDATE "10/12/2005" 149cd7b148SMilan Jurik 159cd7b148SMilan Jurik #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 169cd7b148SMilan Jurik 179cd7b148SMilan Jurik #include <linux/netdevice.h> 189cd7b148SMilan Jurik #include <linux/etherdevice.h> 19ac752277SGeert Uytterhoeven #include <linux/interrupt.h> 209cd7b148SMilan Jurik #include <linux/module.h> 219cd7b148SMilan Jurik #include <asm/natfeat.h> 229cd7b148SMilan Jurik #include <asm/virtconvert.h> 239cd7b148SMilan Jurik 249cd7b148SMilan Jurik enum { 259cd7b148SMilan Jurik GET_VERSION = 0,/* no parameters, return NFAPI_VERSION in d0 */ 269cd7b148SMilan Jurik XIF_INTLEVEL, /* no parameters, return Interrupt Level in d0 */ 279cd7b148SMilan Jurik XIF_IRQ, /* acknowledge interrupt from host */ 289cd7b148SMilan Jurik XIF_START, /* (ethX), called on 'ifup', start receiver thread */ 299cd7b148SMilan Jurik XIF_STOP, /* (ethX), called on 'ifdown', stop the thread */ 309cd7b148SMilan Jurik XIF_READLENGTH, /* (ethX), return size of network data block to read */ 319cd7b148SMilan Jurik XIF_READBLOCK, /* (ethX, buffer, size), read block of network data */ 329cd7b148SMilan Jurik XIF_WRITEBLOCK, /* (ethX, buffer, size), write block of network data */ 339cd7b148SMilan Jurik XIF_GET_MAC, /* (ethX, buffer, size), return MAC HW addr in buffer */ 349cd7b148SMilan Jurik XIF_GET_IPHOST, /* (ethX, buffer, size), return IP address of host */ 359cd7b148SMilan Jurik XIF_GET_IPATARI,/* (ethX, buffer, size), return IP address of atari */ 369cd7b148SMilan Jurik XIF_GET_NETMASK /* (ethX, buffer, size), return IP netmask */ 379cd7b148SMilan Jurik }; 389cd7b148SMilan Jurik 399cd7b148SMilan Jurik #define MAX_UNIT 8 409cd7b148SMilan Jurik 419cd7b148SMilan Jurik /* These identify the driver base version and may not be removed. */ 42b881bc46SGreg Kroah-Hartman static const char version[] = 439cd7b148SMilan Jurik KERN_INFO KBUILD_MODNAME ".c:v" DRV_VERSION " " DRV_RELDATE 449cd7b148SMilan Jurik " S.Opichal, M.Jurik, P.Stehlik\n" 459cd7b148SMilan Jurik KERN_INFO " http://aranym.org/\n"; 469cd7b148SMilan Jurik 479cd7b148SMilan Jurik MODULE_AUTHOR("Milan Jurik"); 489cd7b148SMilan Jurik MODULE_DESCRIPTION("Atari NFeth driver"); 499cd7b148SMilan Jurik MODULE_LICENSE("GPL"); 509cd7b148SMilan Jurik 519cd7b148SMilan Jurik 529cd7b148SMilan Jurik static long nfEtherID; 539cd7b148SMilan Jurik static int nfEtherIRQ; 549cd7b148SMilan Jurik 559cd7b148SMilan Jurik struct nfeth_private { 569cd7b148SMilan Jurik int ethX; 579cd7b148SMilan Jurik }; 589cd7b148SMilan Jurik 599cd7b148SMilan Jurik static struct net_device *nfeth_dev[MAX_UNIT]; 609cd7b148SMilan Jurik 619cd7b148SMilan Jurik static int nfeth_open(struct net_device *dev) 629cd7b148SMilan Jurik { 639cd7b148SMilan Jurik struct nfeth_private *priv = netdev_priv(dev); 649cd7b148SMilan Jurik int res; 659cd7b148SMilan Jurik 669cd7b148SMilan Jurik res = nf_call(nfEtherID + XIF_START, priv->ethX); 679cd7b148SMilan Jurik netdev_dbg(dev, "%s: %d\n", __func__, res); 689cd7b148SMilan Jurik 699cd7b148SMilan Jurik /* Ready for data */ 709cd7b148SMilan Jurik netif_start_queue(dev); 719cd7b148SMilan Jurik 729cd7b148SMilan Jurik return 0; 739cd7b148SMilan Jurik } 749cd7b148SMilan Jurik 759cd7b148SMilan Jurik static int nfeth_stop(struct net_device *dev) 769cd7b148SMilan Jurik { 779cd7b148SMilan Jurik struct nfeth_private *priv = netdev_priv(dev); 789cd7b148SMilan Jurik 799cd7b148SMilan Jurik /* No more data */ 809cd7b148SMilan Jurik netif_stop_queue(dev); 819cd7b148SMilan Jurik 829cd7b148SMilan Jurik nf_call(nfEtherID + XIF_STOP, priv->ethX); 839cd7b148SMilan Jurik 849cd7b148SMilan Jurik return 0; 859cd7b148SMilan Jurik } 869cd7b148SMilan Jurik 879cd7b148SMilan Jurik /* 889cd7b148SMilan Jurik * Read a packet out of the adapter and pass it to the upper layers 899cd7b148SMilan Jurik */ 909cd7b148SMilan Jurik static inline void recv_packet(struct net_device *dev) 919cd7b148SMilan Jurik { 929cd7b148SMilan Jurik struct nfeth_private *priv = netdev_priv(dev); 939cd7b148SMilan Jurik unsigned short pktlen; 949cd7b148SMilan Jurik struct sk_buff *skb; 959cd7b148SMilan Jurik 969cd7b148SMilan Jurik /* read packet length (excluding 32 bit crc) */ 979cd7b148SMilan Jurik pktlen = nf_call(nfEtherID + XIF_READLENGTH, priv->ethX); 989cd7b148SMilan Jurik 999cd7b148SMilan Jurik netdev_dbg(dev, "%s: %u\n", __func__, pktlen); 1009cd7b148SMilan Jurik 1019cd7b148SMilan Jurik if (!pktlen) { 1029cd7b148SMilan Jurik netdev_dbg(dev, "%s: pktlen == 0\n", __func__); 1039cd7b148SMilan Jurik dev->stats.rx_errors++; 1049cd7b148SMilan Jurik return; 1059cd7b148SMilan Jurik } 1069cd7b148SMilan Jurik 1079cd7b148SMilan Jurik skb = dev_alloc_skb(pktlen + 2); 1089cd7b148SMilan Jurik if (!skb) { 1099cd7b148SMilan Jurik netdev_dbg(dev, "%s: out of mem (buf_alloc failed)\n", 1109cd7b148SMilan Jurik __func__); 1119cd7b148SMilan Jurik dev->stats.rx_dropped++; 1129cd7b148SMilan Jurik return; 1139cd7b148SMilan Jurik } 1149cd7b148SMilan Jurik 1159cd7b148SMilan Jurik skb->dev = dev; 1169cd7b148SMilan Jurik skb_reserve(skb, 2); /* 16 Byte align */ 1179cd7b148SMilan Jurik skb_put(skb, pktlen); /* make room */ 1189cd7b148SMilan Jurik nf_call(nfEtherID + XIF_READBLOCK, priv->ethX, virt_to_phys(skb->data), 1199cd7b148SMilan Jurik pktlen); 1209cd7b148SMilan Jurik 1219cd7b148SMilan Jurik skb->protocol = eth_type_trans(skb, dev); 1229cd7b148SMilan Jurik netif_rx(skb); 1239cd7b148SMilan Jurik dev->stats.rx_packets++; 1249cd7b148SMilan Jurik dev->stats.rx_bytes += pktlen; 1259cd7b148SMilan Jurik 1269cd7b148SMilan Jurik /* and enqueue packet */ 1279cd7b148SMilan Jurik return; 1289cd7b148SMilan Jurik } 1299cd7b148SMilan Jurik 1309cd7b148SMilan Jurik static irqreturn_t nfeth_interrupt(int irq, void *dev_id) 1319cd7b148SMilan Jurik { 1329cd7b148SMilan Jurik int i, m, mask; 1339cd7b148SMilan Jurik 1349cd7b148SMilan Jurik mask = nf_call(nfEtherID + XIF_IRQ, 0); 1359cd7b148SMilan Jurik for (i = 0, m = 1; i < MAX_UNIT; m <<= 1, i++) { 1369cd7b148SMilan Jurik if (mask & m && nfeth_dev[i]) { 1379cd7b148SMilan Jurik recv_packet(nfeth_dev[i]); 1389cd7b148SMilan Jurik nf_call(nfEtherID + XIF_IRQ, m); 1399cd7b148SMilan Jurik } 1409cd7b148SMilan Jurik } 1419cd7b148SMilan Jurik return IRQ_HANDLED; 1429cd7b148SMilan Jurik } 1439cd7b148SMilan Jurik 1449cd7b148SMilan Jurik static int nfeth_xmit(struct sk_buff *skb, struct net_device *dev) 1459cd7b148SMilan Jurik { 1469cd7b148SMilan Jurik unsigned int len; 1479cd7b148SMilan Jurik char *data, shortpkt[ETH_ZLEN]; 1489cd7b148SMilan Jurik struct nfeth_private *priv = netdev_priv(dev); 1499cd7b148SMilan Jurik 1509cd7b148SMilan Jurik data = skb->data; 1519cd7b148SMilan Jurik len = skb->len; 1529cd7b148SMilan Jurik if (len < ETH_ZLEN) { 1539cd7b148SMilan Jurik memset(shortpkt, 0, ETH_ZLEN); 1549cd7b148SMilan Jurik memcpy(shortpkt, data, len); 1559cd7b148SMilan Jurik data = shortpkt; 1569cd7b148SMilan Jurik len = ETH_ZLEN; 1579cd7b148SMilan Jurik } 1589cd7b148SMilan Jurik 1599cd7b148SMilan Jurik netdev_dbg(dev, "%s: send %u bytes\n", __func__, len); 1609cd7b148SMilan Jurik nf_call(nfEtherID + XIF_WRITEBLOCK, priv->ethX, virt_to_phys(data), 1619cd7b148SMilan Jurik len); 1629cd7b148SMilan Jurik 1639cd7b148SMilan Jurik dev->stats.tx_packets++; 1649cd7b148SMilan Jurik dev->stats.tx_bytes += len; 1659cd7b148SMilan Jurik 1669cd7b148SMilan Jurik dev_kfree_skb(skb); 1679cd7b148SMilan Jurik return 0; 1689cd7b148SMilan Jurik } 1699cd7b148SMilan Jurik 1700290bd29SMichael S. Tsirkin static void nfeth_tx_timeout(struct net_device *dev, unsigned int txqueue) 1719cd7b148SMilan Jurik { 1729cd7b148SMilan Jurik dev->stats.tx_errors++; 1739cd7b148SMilan Jurik netif_wake_queue(dev); 1749cd7b148SMilan Jurik } 1759cd7b148SMilan Jurik 1769cd7b148SMilan Jurik static const struct net_device_ops nfeth_netdev_ops = { 1779cd7b148SMilan Jurik .ndo_open = nfeth_open, 1789cd7b148SMilan Jurik .ndo_stop = nfeth_stop, 1799cd7b148SMilan Jurik .ndo_start_xmit = nfeth_xmit, 1809cd7b148SMilan Jurik .ndo_tx_timeout = nfeth_tx_timeout, 1819cd7b148SMilan Jurik .ndo_validate_addr = eth_validate_addr, 1829cd7b148SMilan Jurik .ndo_set_mac_address = eth_mac_addr, 1839cd7b148SMilan Jurik }; 1849cd7b148SMilan Jurik 1859cd7b148SMilan Jurik static struct net_device * __init nfeth_probe(int unit) 1869cd7b148SMilan Jurik { 1879cd7b148SMilan Jurik struct net_device *dev; 1889cd7b148SMilan Jurik struct nfeth_private *priv; 1899cd7b148SMilan Jurik char mac[ETH_ALEN], host_ip[32], local_ip[32]; 1909cd7b148SMilan Jurik int err; 1919cd7b148SMilan Jurik 19255490050SGeert Uytterhoeven if (!nf_call(nfEtherID + XIF_GET_MAC, unit, virt_to_phys(mac), 19355490050SGeert Uytterhoeven ETH_ALEN)) 1949cd7b148SMilan Jurik return NULL; 1959cd7b148SMilan Jurik 1969cd7b148SMilan Jurik dev = alloc_etherdev(sizeof(struct nfeth_private)); 1979cd7b148SMilan Jurik if (!dev) 1989cd7b148SMilan Jurik return NULL; 1999cd7b148SMilan Jurik 2009cd7b148SMilan Jurik dev->irq = nfEtherIRQ; 2019cd7b148SMilan Jurik dev->netdev_ops = &nfeth_netdev_ops; 2029cd7b148SMilan Jurik 2039cd7b148SMilan Jurik memcpy(dev->dev_addr, mac, ETH_ALEN); 2049cd7b148SMilan Jurik 2059cd7b148SMilan Jurik priv = netdev_priv(dev); 2069cd7b148SMilan Jurik priv->ethX = unit; 2079cd7b148SMilan Jurik 2089cd7b148SMilan Jurik err = register_netdev(dev); 2099cd7b148SMilan Jurik if (err) { 2109cd7b148SMilan Jurik free_netdev(dev); 2119cd7b148SMilan Jurik return NULL; 2129cd7b148SMilan Jurik } 2139cd7b148SMilan Jurik 2149cd7b148SMilan Jurik nf_call(nfEtherID + XIF_GET_IPHOST, unit, 21555490050SGeert Uytterhoeven virt_to_phys(host_ip), sizeof(host_ip)); 2169cd7b148SMilan Jurik nf_call(nfEtherID + XIF_GET_IPATARI, unit, 21755490050SGeert Uytterhoeven virt_to_phys(local_ip), sizeof(local_ip)); 2189cd7b148SMilan Jurik 2199cd7b148SMilan Jurik netdev_info(dev, KBUILD_MODNAME " addr:%s (%s) HWaddr:%pM\n", host_ip, 2209cd7b148SMilan Jurik local_ip, mac); 2219cd7b148SMilan Jurik 2229cd7b148SMilan Jurik return dev; 2239cd7b148SMilan Jurik } 2249cd7b148SMilan Jurik 2259cd7b148SMilan Jurik static int __init nfeth_init(void) 2269cd7b148SMilan Jurik { 2279cd7b148SMilan Jurik long ver; 2289cd7b148SMilan Jurik int error, i; 2299cd7b148SMilan Jurik 2309cd7b148SMilan Jurik nfEtherID = nf_get_id("ETHERNET"); 2319cd7b148SMilan Jurik if (!nfEtherID) 2329cd7b148SMilan Jurik return -ENODEV; 2339cd7b148SMilan Jurik 2349cd7b148SMilan Jurik ver = nf_call(nfEtherID + GET_VERSION); 2359cd7b148SMilan Jurik pr_info("API %lu\n", ver); 2369cd7b148SMilan Jurik 2379cd7b148SMilan Jurik nfEtherIRQ = nf_call(nfEtherID + XIF_INTLEVEL); 2389cd7b148SMilan Jurik error = request_irq(nfEtherIRQ, nfeth_interrupt, IRQF_SHARED, 2399cd7b148SMilan Jurik "eth emu", nfeth_interrupt); 2409cd7b148SMilan Jurik if (error) { 2419cd7b148SMilan Jurik pr_err("request for irq %d failed %d", nfEtherIRQ, error); 2429cd7b148SMilan Jurik return error; 2439cd7b148SMilan Jurik } 2449cd7b148SMilan Jurik 2459cd7b148SMilan Jurik for (i = 0; i < MAX_UNIT; i++) 2469cd7b148SMilan Jurik nfeth_dev[i] = nfeth_probe(i); 2479cd7b148SMilan Jurik 2489cd7b148SMilan Jurik return 0; 2499cd7b148SMilan Jurik } 2509cd7b148SMilan Jurik 2519cd7b148SMilan Jurik static void __exit nfeth_cleanup(void) 2529cd7b148SMilan Jurik { 2539cd7b148SMilan Jurik int i; 2549cd7b148SMilan Jurik 2559cd7b148SMilan Jurik for (i = 0; i < MAX_UNIT; i++) { 2569cd7b148SMilan Jurik if (nfeth_dev[i]) { 2579cd7b148SMilan Jurik unregister_netdev(nfeth_dev[0]); 2589cd7b148SMilan Jurik free_netdev(nfeth_dev[0]); 2599cd7b148SMilan Jurik } 2609cd7b148SMilan Jurik } 2619cd7b148SMilan Jurik free_irq(nfEtherIRQ, nfeth_interrupt); 2629cd7b148SMilan Jurik } 2639cd7b148SMilan Jurik 2649cd7b148SMilan Jurik module_init(nfeth_init); 2659cd7b148SMilan Jurik module_exit(nfeth_cleanup); 266