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