2008-11-18 21:02:18 +01:00
/*
* 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>
2011-06-11 14:58:17 -07:00
# include <linux/interrupt.h>
2008-11-18 21:02:18 +01:00
# 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. */
2012-12-21 14:06:37 -08:00
static const char version [ ] =
2008-11-18 21:02:18 +01:00
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 ;
2013-08-14 10:45:00 +02:00
if ( ! nf_call ( nfEtherID + XIF_GET_MAC , unit , virt_to_phys ( mac ) ,
ETH_ALEN ) )
2008-11-18 21:02:18 +01:00
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 ,
2013-08-14 10:45:00 +02:00
virt_to_phys ( host_ip ) , sizeof ( host_ip ) ) ;
2008-11-18 21:02:18 +01:00
nf_call ( nfEtherID + XIF_GET_IPATARI , unit ,
2013-08-14 10:45:00 +02:00
virt_to_phys ( local_ip ) , sizeof ( local_ip ) ) ;
2008-11-18 21:02:18 +01:00
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 ) ;