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