Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / net / mambonet.c
1 /*
2  *  Bogus Network Driver for PowerPC Full System Simulator
3  *
4  *  (C) Copyright IBM Corporation 2003-2005
5  *
6  *  Bogus Network Driver
7  * 
8  *  Author: JimiX <jimix@watson.ibm.com>
9  *  Maintained By: Eric Van Hensbergen <ericvh@gmail.com>
10  *
11  *      inspired by drivers/net/ibmveth.c
12  *      written by Dave Larson 
13  *
14  *  Some code is from the IBM Full System Simulator Group in ARL
15  *  Author: Patrick Bohrer <IBM Austin Research Lab>
16  *
17  *  This program is free software; you can redistribute it and/or modify
18  *  it under the terms of the GNU General Public License as published by
19  *  the Free Software Foundation; either version 2, or (at your option)
20  *  any later version.
21  * 
22  *  This program is distributed in the hope that it will be useful,
23  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
24  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  *  GNU General Public License for more details.
26  *  
27  *  You should have received a copy of the GNU General Public License
28  *  along with this program; if not, write to:
29  *  Free Software Foundation
30  *  51 Franklin Street, Fifth Floor
31  *  Boston, MA  02111-1301  USA
32  *  
33  */
34
35 #include <linux/kernel.h>
36 #include <linux/sched.h>
37 #include <linux/interrupt.h>
38 #include <linux/fs.h>
39 #include <linux/types.h>
40 #include <linux/string.h>
41 #include <linux/socket.h>
42 #include <linux/errno.h>
43 #include <linux/fcntl.h>
44 #include <linux/in.h>
45 #include <linux/init.h>
46
47 #include <asm/system.h>
48 #include <asm/uaccess.h>
49 #include <asm/io.h>
50
51 #include <linux/inet.h>
52 #include <linux/netdevice.h>
53 #include <linux/etherdevice.h>
54 #include <linux/skbuff.h>
55 #include <net/sock.h>
56 #include <linux/if_ether.h>     /* For the statistics structure. */
57 #include <linux/if_arp.h>       /* For ARPHRD_ETHER */
58 #include <linux/workqueue.h>
59 #include <asm/prom.h>
60 #include <asm/systemsim.h>
61
62 #define MAMBO_BOGUS_NET_PROBE   119
63 #define MAMBO_BOGUS_NET_SEND    120
64 #define MAMBO_BOGUS_NET_RECV    121
65
66 static inline int MamboBogusNetProbe(int devno, void *buf)
67 {
68         return callthru2(MAMBO_BOGUS_NET_PROBE,
69                          (unsigned long)devno, (unsigned long)buf);
70 }
71
72 static inline int MamboBogusNetSend(int devno, void *buf, ulong size)
73 {
74         return callthru3(MAMBO_BOGUS_NET_SEND,
75                          (unsigned long)devno,
76                          (unsigned long)buf, (unsigned long)size);
77 }
78
79 static inline int MamboBogusNetRecv(int devno, void *buf, ulong size)
80 {
81         return callthru3(MAMBO_BOGUS_NET_RECV,
82                          (unsigned long)devno,
83                          (unsigned long)buf, (unsigned long)size);
84 }
85
86 static irqreturn_t
87 mambonet_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
88
89 #define INIT_BOTTOM_HALF(x,y,z) INIT_WORK(x, y, (void*)z)
90 #define SCHEDULE_BOTTOM_HALF(x) schedule_delayed_work(x, 1)
91 #define KILL_BOTTOM_HALF(x) cancel_delayed_work(x); flush_scheduled_work()
92
93 #define MAMBO_MTU 1500
94
95 struct netdev_private {
96         int devno;
97         int closing;
98         struct work_struct poll_task;
99         struct net_device_stats stats;
100 };
101
102 static int mambonet_probedev(int devno, void *buf)
103 {
104         struct device_node *mambo;
105         struct device_node *net;
106         unsigned int *reg;
107
108         mambo = find_path_device("/mambo");
109
110         if (mambo == NULL) {
111                 return -1;
112         }
113         net = find_path_device("/mambo/bogus-net@0");
114         if (net == NULL) {
115                 return -1;
116         }
117         reg = (unsigned int *)get_property(net, "reg", 0);
118
119         if (*reg != devno) {
120                 return -1;
121         }
122
123         return MamboBogusNetProbe(devno, buf);
124 }
125
126 static int mambonet_send(int devno, void *buf, ulong size)
127 {
128         return MamboBogusNetSend(devno, buf, size);
129 }
130
131 static int mambonet_recv(int devno, void *buf, ulong size)
132 {
133         return MamboBogusNetRecv(devno, buf, size);
134 }
135
136 static int mambonet_start_xmit(struct sk_buff *skb, struct net_device *dev)
137 {
138         struct netdev_private *priv = (struct netdev_private *)dev->priv;
139         int devno = priv->devno;
140
141         skb->dev = dev;
142
143         /* we might need to checksum or something */
144         mambonet_send(devno, skb->data, skb->len);
145
146         dev->last_rx = jiffies;
147         priv->stats.rx_bytes += skb->len;
148         priv->stats.tx_bytes += skb->len;
149         priv->stats.rx_packets++;
150         priv->stats.tx_packets++;
151
152         dev_kfree_skb(skb);
153
154         return (0);
155 }
156
157 static int mambonet_poll(struct net_device *dev, int *budget)
158 {
159         struct netdev_private *np = dev->priv;
160         int devno = np->devno;
161         char buffer[1600];
162         int ns;
163         struct sk_buff *skb;
164         int frames = 0;
165         int max_frames = min(*budget, dev->quota);
166         int ret = 0;
167
168         while ((ns = mambonet_recv(devno, buffer, 1600)) > 0) {
169                 if ((skb = dev_alloc_skb(ns + 2)) != NULL) {
170                         skb->dev = dev;
171                         skb_reserve(skb, 2);    /* 16 byte align the IP
172                                                  * header */
173 #ifdef HAS_IP_COPYSUM
174                         eth_copy_and_sum(skb, buffer, ns, 0);
175                         skb_put(skb, ns);
176 #else
177                         memcpy(skb_put(skb, ns), buffer, ns);
178 #endif
179                         skb->protocol = eth_type_trans(skb, dev);
180
181                         if (dev->irq)
182                                 netif_receive_skb(skb);
183                         else
184                                 netif_rx(skb);
185
186                         dev->last_rx = jiffies;
187                         np->stats.rx_packets++;
188                         np->stats.rx_bytes += ns;
189                 } else {
190                         printk("Failed to allocated skbuff, "
191                                "dropping packet\n");
192                         np->stats.rx_dropped++;
193                         /* wait for another cycle */
194                         return 1;
195                 }
196                 ++frames;
197                 if (frames > max_frames) {
198                         ret = 1;
199                         break;
200                 }
201         }
202         *budget -= frames;
203         dev->quota -= frames;
204
205         if ((!ret) && (dev->irq))
206                 netif_rx_complete(dev);
207
208         return ret;
209 }
210
211 static void mambonet_timer(struct net_device *dev)
212 {
213         int budget = 16;
214         struct netdev_private *priv = (struct netdev_private *)dev->priv;
215
216         mambonet_poll(dev, &budget);
217
218         if (!priv->closing) {
219                 SCHEDULE_BOTTOM_HALF(&priv->poll_task);
220         }
221 }
222
223 static struct net_device_stats *get_stats(struct net_device *dev)
224 {
225         struct netdev_private *priv = (struct netdev_private *)dev->priv;
226         return (struct net_device_stats *)&(priv->stats);
227 }
228
229 static irqreturn_t
230 mambonet_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
231 {
232         struct net_device *dev = dev_instance;
233         if (netif_rx_schedule_prep(dev)) {
234                 __netif_rx_schedule(dev);
235         }
236         return IRQ_HANDLED;
237 }
238
239 static int mambonet_open(struct net_device *dev)
240 {
241         struct netdev_private *priv;
242         int ret = 0;
243
244         priv = dev->priv;
245
246         /*
247          * we can't start polling in mambonet_init, because I don't think
248          * workqueues are usable that early. so start polling now.
249          */
250
251         if (dev->irq) {
252                 ret = request_irq(dev->irq, &mambonet_interrupt, 0,
253                                   dev->name, dev);
254
255                 if (ret == 0) {
256                         netif_start_queue(dev);
257                 } else {
258                         printk(KERN_ERR "mambonet: request irq failed\n");
259                 }
260
261                 MamboBogusNetProbe(priv->devno, NULL);  /* probe with NULL to activate interrupts */
262         } else {
263                 mambonet_timer(dev);
264         }
265
266         return ret;
267 }
268
269 static int mambonet_close(struct net_device *dev)
270 {
271         struct netdev_private *priv;
272
273         netif_stop_queue(dev);
274
275         if (dev->irq)
276                 free_irq(dev->irq, dev);
277
278         priv = dev->priv;
279         priv->closing = 1;
280         if (dev->irq == 0) {
281                 KILL_BOTTOM_HALF(&priv->poll_task);
282         }
283
284         kfree(priv);
285
286         return 0;
287 }
288
289 static struct net_device_stats mambonet_stats;
290
291 static struct net_device_stats *mambonet_get_stats(struct net_device *dev)
292 {
293         return &mambonet_stats;
294 }
295
296 static int mambonet_set_mac_address(struct net_device *dev, void *p)
297 {
298         return -EOPNOTSUPP;
299 }
300 static int mambonet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
301 {
302         return -EOPNOTSUPP;
303 }
304 static int nextdevno = 0;       /* running count of device numbers */
305
306 /* Initialize the rest of the device. */
307 int __init do_mambonet_probe(struct net_device *dev)
308 {
309         struct netdev_private *priv;
310         int devno = nextdevno++;
311         int irq;
312
313         printk("eth%d: bogus network driver initialization\n", devno);
314
315         irq = mambonet_probedev(devno, dev->dev_addr);
316
317         if (irq < 0) {
318                 printk("No IRQ retreived\n");
319                 return (-ENODEV);
320         }
321
322         printk("%s: %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", dev->name,
323                dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
324                dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
325
326         SET_MODULE_OWNER(dev);
327
328         dev->irq = irq;
329         dev->mtu = MAMBO_MTU;
330         dev->open = mambonet_open;
331         dev->poll = mambonet_poll;
332         dev->weight = 16;
333         dev->stop = mambonet_close;
334         dev->hard_start_xmit = mambonet_start_xmit;
335         dev->get_stats = mambonet_get_stats;
336         dev->set_mac_address = mambonet_set_mac_address;
337         dev->do_ioctl = mambonet_ioctl;
338
339         dev->priv = kmalloc(sizeof(struct netdev_private), GFP_KERNEL);
340         if (dev->priv == NULL)
341                 return -ENOMEM;
342         memset(dev->priv, 0, sizeof(struct netdev_private));
343
344         priv = dev->priv;
345         priv->devno = devno;
346         priv->closing = 0;
347         dev->get_stats = get_stats;
348
349         if (dev->irq == 0) {
350                 INIT_BOTTOM_HALF(&priv->poll_task, (void *)mambonet_timer,
351                                  (void *)dev);
352         }
353
354         return (0);
355 };
356
357 struct net_device *__init mambonet_probe(int unit)
358 {
359         struct net_device *dev = alloc_etherdev(0);
360         int err;
361
362         if (!dev)
363                 return ERR_PTR(-ENODEV);
364
365         sprintf(dev->name, "eth%d", unit);
366         netdev_boot_setup_check(dev);
367
368         err = do_mambonet_probe(dev);
369
370         if (err)
371                 goto out;
372
373         err = register_netdev(dev);
374         if (err)
375                 goto out;
376
377         return dev;
378
379       out:
380         free_netdev(dev);
381         return ERR_PTR(err);
382 }
383
384 int __init init_mambonet(void)
385 {
386         mambonet_probe(0);
387         return 0;
388 }
389
390 module_init(init_mambonet);
391 MODULE_LICENSE("GPL");