162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * atari_nfeth.c - ARAnyM ethernet card driver for GNU/Linux 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2005 Milan Jurik, Petr Stehlik of ARAnyM dev team 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Based on ARAnyM driver for FreeMiNT written by Standa Opichal 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This software may be used and distributed according to the terms of 962306a36Sopenharmony_ci * the GNU General Public License (GPL), incorporated herein by reference. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define DRV_VERSION "0.3" 1362306a36Sopenharmony_ci#define DRV_RELDATE "10/12/2005" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/netdevice.h> 1862306a36Sopenharmony_ci#include <linux/etherdevice.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <asm/natfeat.h> 2262306a36Sopenharmony_ci#include <asm/virtconvert.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cienum { 2562306a36Sopenharmony_ci GET_VERSION = 0,/* no parameters, return NFAPI_VERSION in d0 */ 2662306a36Sopenharmony_ci XIF_INTLEVEL, /* no parameters, return Interrupt Level in d0 */ 2762306a36Sopenharmony_ci XIF_IRQ, /* acknowledge interrupt from host */ 2862306a36Sopenharmony_ci XIF_START, /* (ethX), called on 'ifup', start receiver thread */ 2962306a36Sopenharmony_ci XIF_STOP, /* (ethX), called on 'ifdown', stop the thread */ 3062306a36Sopenharmony_ci XIF_READLENGTH, /* (ethX), return size of network data block to read */ 3162306a36Sopenharmony_ci XIF_READBLOCK, /* (ethX, buffer, size), read block of network data */ 3262306a36Sopenharmony_ci XIF_WRITEBLOCK, /* (ethX, buffer, size), write block of network data */ 3362306a36Sopenharmony_ci XIF_GET_MAC, /* (ethX, buffer, size), return MAC HW addr in buffer */ 3462306a36Sopenharmony_ci XIF_GET_IPHOST, /* (ethX, buffer, size), return IP address of host */ 3562306a36Sopenharmony_ci XIF_GET_IPATARI,/* (ethX, buffer, size), return IP address of atari */ 3662306a36Sopenharmony_ci XIF_GET_NETMASK /* (ethX, buffer, size), return IP netmask */ 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define MAX_UNIT 8 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* These identify the driver base version and may not be removed. */ 4262306a36Sopenharmony_cistatic const char version[] = 4362306a36Sopenharmony_ci KERN_INFO KBUILD_MODNAME ".c:v" DRV_VERSION " " DRV_RELDATE 4462306a36Sopenharmony_ci " S.Opichal, M.Jurik, P.Stehlik\n" 4562306a36Sopenharmony_ci KERN_INFO " http://aranym.org/\n"; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciMODULE_AUTHOR("Milan Jurik"); 4862306a36Sopenharmony_ciMODULE_DESCRIPTION("Atari NFeth driver"); 4962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic long nfEtherID; 5362306a36Sopenharmony_cistatic int nfEtherIRQ; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct nfeth_private { 5662306a36Sopenharmony_ci int ethX; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic struct net_device *nfeth_dev[MAX_UNIT]; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int nfeth_open(struct net_device *dev) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct nfeth_private *priv = netdev_priv(dev); 6462306a36Sopenharmony_ci int res; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci res = nf_call(nfEtherID + XIF_START, priv->ethX); 6762306a36Sopenharmony_ci netdev_dbg(dev, "%s: %d\n", __func__, res); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Ready for data */ 7062306a36Sopenharmony_ci netif_start_queue(dev); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int nfeth_stop(struct net_device *dev) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct nfeth_private *priv = netdev_priv(dev); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* No more data */ 8062306a36Sopenharmony_ci netif_stop_queue(dev); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci nf_call(nfEtherID + XIF_STOP, priv->ethX); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci * Read a packet out of the adapter and pass it to the upper layers 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic inline void recv_packet(struct net_device *dev) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct nfeth_private *priv = netdev_priv(dev); 9362306a36Sopenharmony_ci unsigned short pktlen; 9462306a36Sopenharmony_ci struct sk_buff *skb; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* read packet length (excluding 32 bit crc) */ 9762306a36Sopenharmony_ci pktlen = nf_call(nfEtherID + XIF_READLENGTH, priv->ethX); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci netdev_dbg(dev, "%s: %u\n", __func__, pktlen); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!pktlen) { 10262306a36Sopenharmony_ci netdev_dbg(dev, "%s: pktlen == 0\n", __func__); 10362306a36Sopenharmony_ci dev->stats.rx_errors++; 10462306a36Sopenharmony_ci return; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci skb = dev_alloc_skb(pktlen + 2); 10862306a36Sopenharmony_ci if (!skb) { 10962306a36Sopenharmony_ci netdev_dbg(dev, "%s: out of mem (buf_alloc failed)\n", 11062306a36Sopenharmony_ci __func__); 11162306a36Sopenharmony_ci dev->stats.rx_dropped++; 11262306a36Sopenharmony_ci return; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci skb->dev = dev; 11662306a36Sopenharmony_ci skb_reserve(skb, 2); /* 16 Byte align */ 11762306a36Sopenharmony_ci skb_put(skb, pktlen); /* make room */ 11862306a36Sopenharmony_ci nf_call(nfEtherID + XIF_READBLOCK, priv->ethX, virt_to_phys(skb->data), 11962306a36Sopenharmony_ci pktlen); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 12262306a36Sopenharmony_ci netif_rx(skb); 12362306a36Sopenharmony_ci dev->stats.rx_packets++; 12462306a36Sopenharmony_ci dev->stats.rx_bytes += pktlen; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* and enqueue packet */ 12762306a36Sopenharmony_ci return; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic irqreturn_t nfeth_interrupt(int irq, void *dev_id) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci int i, m, mask; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci mask = nf_call(nfEtherID + XIF_IRQ, 0); 13562306a36Sopenharmony_ci for (i = 0, m = 1; i < MAX_UNIT; m <<= 1, i++) { 13662306a36Sopenharmony_ci if (mask & m && nfeth_dev[i]) { 13762306a36Sopenharmony_ci recv_packet(nfeth_dev[i]); 13862306a36Sopenharmony_ci nf_call(nfEtherID + XIF_IRQ, m); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci return IRQ_HANDLED; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int nfeth_xmit(struct sk_buff *skb, struct net_device *dev) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci unsigned int len; 14762306a36Sopenharmony_ci char *data, shortpkt[ETH_ZLEN]; 14862306a36Sopenharmony_ci struct nfeth_private *priv = netdev_priv(dev); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci data = skb->data; 15162306a36Sopenharmony_ci len = skb->len; 15262306a36Sopenharmony_ci if (len < ETH_ZLEN) { 15362306a36Sopenharmony_ci memset(shortpkt, 0, ETH_ZLEN); 15462306a36Sopenharmony_ci memcpy(shortpkt, data, len); 15562306a36Sopenharmony_ci data = shortpkt; 15662306a36Sopenharmony_ci len = ETH_ZLEN; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci netdev_dbg(dev, "%s: send %u bytes\n", __func__, len); 16062306a36Sopenharmony_ci nf_call(nfEtherID + XIF_WRITEBLOCK, priv->ethX, virt_to_phys(data), 16162306a36Sopenharmony_ci len); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci dev->stats.tx_packets++; 16462306a36Sopenharmony_ci dev->stats.tx_bytes += len; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci dev_kfree_skb(skb); 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void nfeth_tx_timeout(struct net_device *dev, unsigned int txqueue) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci dev->stats.tx_errors++; 17362306a36Sopenharmony_ci netif_wake_queue(dev); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic const struct net_device_ops nfeth_netdev_ops = { 17762306a36Sopenharmony_ci .ndo_open = nfeth_open, 17862306a36Sopenharmony_ci .ndo_stop = nfeth_stop, 17962306a36Sopenharmony_ci .ndo_start_xmit = nfeth_xmit, 18062306a36Sopenharmony_ci .ndo_tx_timeout = nfeth_tx_timeout, 18162306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 18262306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic struct net_device * __init nfeth_probe(int unit) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct net_device *dev; 18862306a36Sopenharmony_ci struct nfeth_private *priv; 18962306a36Sopenharmony_ci char mac[ETH_ALEN], host_ip[32], local_ip[32]; 19062306a36Sopenharmony_ci int err; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!nf_call(nfEtherID + XIF_GET_MAC, unit, virt_to_phys(mac), 19362306a36Sopenharmony_ci ETH_ALEN)) 19462306a36Sopenharmony_ci return NULL; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct nfeth_private)); 19762306a36Sopenharmony_ci if (!dev) 19862306a36Sopenharmony_ci return NULL; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci dev->irq = nfEtherIRQ; 20162306a36Sopenharmony_ci dev->netdev_ops = &nfeth_netdev_ops; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci eth_hw_addr_set(dev, mac); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci priv = netdev_priv(dev); 20662306a36Sopenharmony_ci priv->ethX = unit; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci err = register_netdev(dev); 20962306a36Sopenharmony_ci if (err) { 21062306a36Sopenharmony_ci free_netdev(dev); 21162306a36Sopenharmony_ci return NULL; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci nf_call(nfEtherID + XIF_GET_IPHOST, unit, 21562306a36Sopenharmony_ci virt_to_phys(host_ip), sizeof(host_ip)); 21662306a36Sopenharmony_ci nf_call(nfEtherID + XIF_GET_IPATARI, unit, 21762306a36Sopenharmony_ci virt_to_phys(local_ip), sizeof(local_ip)); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci netdev_info(dev, KBUILD_MODNAME " addr:%s (%s) HWaddr:%pM\n", host_ip, 22062306a36Sopenharmony_ci local_ip, mac); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return dev; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int __init nfeth_init(void) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci long ver; 22862306a36Sopenharmony_ci int error, i; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci nfEtherID = nf_get_id("ETHERNET"); 23162306a36Sopenharmony_ci if (!nfEtherID) 23262306a36Sopenharmony_ci return -ENODEV; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ver = nf_call(nfEtherID + GET_VERSION); 23562306a36Sopenharmony_ci pr_info("API %lu\n", ver); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci nfEtherIRQ = nf_call(nfEtherID + XIF_INTLEVEL); 23862306a36Sopenharmony_ci error = request_irq(nfEtherIRQ, nfeth_interrupt, IRQF_SHARED, 23962306a36Sopenharmony_ci "eth emu", nfeth_interrupt); 24062306a36Sopenharmony_ci if (error) { 24162306a36Sopenharmony_ci pr_err("request for irq %d failed %d", nfEtherIRQ, error); 24262306a36Sopenharmony_ci return error; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci for (i = 0; i < MAX_UNIT; i++) 24662306a36Sopenharmony_ci nfeth_dev[i] = nfeth_probe(i); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void __exit nfeth_cleanup(void) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int i; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci for (i = 0; i < MAX_UNIT; i++) { 25662306a36Sopenharmony_ci if (nfeth_dev[i]) { 25762306a36Sopenharmony_ci unregister_netdev(nfeth_dev[i]); 25862306a36Sopenharmony_ci free_netdev(nfeth_dev[i]); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci free_irq(nfEtherIRQ, nfeth_interrupt); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cimodule_init(nfeth_init); 26562306a36Sopenharmony_cimodule_exit(nfeth_cleanup); 266