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