This commit was manufactured by cvs2svn to create branch 'vserver'.
[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 <linux/version.h>
61 #include <asm/systemsim.h>
62
63 #define MAMBO_BOGUS_NET_PROBE   119
64 #define MAMBO_BOGUS_NET_SEND    120
65 #define MAMBO_BOGUS_NET_RECV    121
66
67 static inline int MamboBogusNetProbe(int devno, void *buf)
68 {
69         return callthru2(MAMBO_BOGUS_NET_PROBE,
70                          (unsigned long)devno, (unsigned long)buf);
71 }
72
73 static inline int MamboBogusNetSend(int devno, void *buf, ulong size)
74 {
75         return callthru3(MAMBO_BOGUS_NET_SEND,
76                          (unsigned long)devno,
77                          (unsigned long)buf, (unsigned long)size);
78 }
79
80 static inline int MamboBogusNetRecv(int devno, void *buf, ulong size)
81 {
82         return callthru3(MAMBO_BOGUS_NET_RECV,
83                          (unsigned long)devno,
84                          (unsigned long)buf, (unsigned long)size);
85 }
86
87 static irqreturn_t
88 mambonet_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
89
90 #define INIT_BOTTOM_HALF(x,y,z) INIT_WORK(x, y, (void*)z)
91 #define SCHEDULE_BOTTOM_HALF(x) schedule_delayed_work(x, 1)
92 #define KILL_BOTTOM_HALF(x) cancel_delayed_work(x); flush_scheduled_work()
93
94 #define MAMBO_MTU 1500
95
96 struct netdev_private {
97         int devno;
98         int closing;
99         struct work_struct poll_task;
100         struct net_device_stats stats;
101 };
102
103 static int mambonet_probedev(int devno, void *buf)
104 {
105         struct device_node *mambo;
106         struct device_node *net;
107         unsigned int *reg;
108
109         mambo = find_path_device("/mambo");
110
111         if (mambo == NULL) {
112                 return -1;
113         }
114         net = find_path_device("/mambo/bogus-net@0");
115         if (net == NULL) {
116                 return -1;
117         }
118         reg = (unsigned int *)get_property(net, "reg", 0);
119
120         if (*reg != devno) {
121                 return -1;
122         }
123
124         return MamboBogusNetProbe(devno, buf);
125 }
126
127 static int mambonet_send(int devno, void *buf, ulong size)
128 {
129         return MamboBogusNetSend(devno, buf, size);
130 }
131
132 static int mambonet_recv(int devno, void *buf, ulong size)
133 {
134         return MamboBogusNetRecv(devno, buf, size);
135 }
136
137 static int mambonet_start_xmit(struct sk_buff *skb, struct net_device *dev)
138 {
139         struct netdev_private *priv = (struct netdev_private *)dev->priv;
140         int devno = priv->devno;
141
142         skb->dev = dev;
143
144         /* we might need to checksum or something */
145         mambonet_send(devno, skb->data, skb->len);
146
147         dev->last_rx = jiffies;
148         priv->stats.rx_bytes += skb->len;
149         priv->stats.tx_bytes += skb->len;
150         priv->stats.rx_packets++;
151         priv->stats.tx_packets++;
152
153         dev_kfree_skb(skb);
154
155         return (0);
156 }
157
158 static int mambonet_poll(struct net_device *dev, int *budget)
159 {
160         struct netdev_private *np = dev->priv;
161         int devno = np->devno;
162         char buffer[1600];
163         int ns;
164         struct sk_buff *skb;
165         int frames = 0;
166         int max_frames = min(*budget, dev->quota);
167         int ret = 0;
168
169         while ((ns = mambonet_recv(devno, buffer, 1600)) > 0) {
170                 if ((skb = dev_alloc_skb(ns + 2)) != NULL) {
171                         skb->dev = dev;
172                         skb_reserve(skb, 2);    /* 16 byte align the IP
173                                                  * header */
174 #ifdef HAS_IP_COPYSUM
175                         eth_copy_and_sum(skb, buffer, ns, 0);
176                         skb_put(skb, ns);
177 #else
178                         memcpy(skb_put(skb, ns), buffer, ns);
179 #endif
180                         skb->protocol = eth_type_trans(skb, dev);
181
182                         if (dev->irq)
183                                 netif_receive_skb(skb);
184                         else
185                                 netif_rx(skb);
186
187                         dev->last_rx = jiffies;
188                         np->stats.rx_packets++;
189                         np->stats.rx_bytes += ns;
190                 } else {
191                         printk("Failed to allocated skbuff, "
192                                "dropping packet\n");
193                         np->stats.rx_dropped++;
194                         /* wait for another cycle */
195                         return 1;
196                 }
197                 ++frames;
198                 if (frames > max_frames) {
199                         ret = 1;
200                         break;
201                 }
202         }
203         *budget -= frames;
204         dev->quota -= frames;
205
206         if ((!ret) && (dev->irq))
207                 netif_rx_complete(dev);
208
209         return ret;
210 }
211
212 static void mambonet_timer(struct net_device *dev)
213 {
214         int budget = 16;
215         struct netdev_private *priv = (struct netdev_private *)dev->priv;
216
217         mambonet_poll(dev, &budget);
218
219         if (!priv->closing) {
220                 SCHEDULE_BOTTOM_HALF(&priv->poll_task);
221         }
222 }
223
224 static struct net_device_stats *get_stats(struct net_device *dev)
225 {
226         struct netdev_private *priv = (struct netdev_private *)dev->priv;
227         return (struct net_device_stats *)&(priv->stats);
228 }
229
230 static irqreturn_t
231 mambonet_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
232 {
233         struct net_device *dev = dev_instance;
234         if (netif_rx_schedule_prep(dev)) {
235                 __netif_rx_schedule(dev);
236         }
237         return IRQ_HANDLED;
238 }
239
240 static int mambonet_open(struct net_device *dev)
241 {
242         struct netdev_private *priv;
243         int ret = 0;
244
245         priv = dev->priv;
246
247         /*
248          * we can't start polling in mambonet_init, because I don't think
249          * workqueues are usable that early. so start polling now.
250          */
251
252         if (dev->irq) {
253                 ret = request_irq(dev->irq, &mambonet_interrupt, 0,
254                                   dev->name, dev);
255
256                 if (ret == 0) {
257                         netif_start_queue(dev);
258                 } else {
259                         printk(KERN_ERR "mambonet: request irq failed\n");
260                 }
261
262                 MamboBogusNetProbe(priv->devno, NULL);  /* probe with NULL to activate interrupts */
263         } else {
264                 mambonet_timer(dev);
265         }
266
267         return ret;
268 }
269
270 static int mambonet_close(struct net_device *dev)
271 {
272         struct netdev_private *priv;
273
274         netif_stop_queue(dev);
275
276         if (dev->irq)
277                 free_irq(dev->irq, dev);
278
279         priv = dev->priv;
280         priv->closing = 1;
281         if (dev->irq == 0) {
282                 KILL_BOTTOM_HALF(&priv->poll_task);
283         }
284
285         kfree(priv);
286
287         return 0;
288 }
289
290 static struct net_device_stats mambonet_stats;
291
292 static struct net_device_stats *mambonet_get_stats(struct net_device *dev)
293 {
294         return &mambonet_stats;
295 }
296
297 static int mambonet_set_mac_address(struct net_device *dev, void *p)
298 {
299         return -EOPNOTSUPP;
300 }
301 static int mambonet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
302 {
303         return -EOPNOTSUPP;
304 }
305 static int nextdevno = 0;       /* running count of device numbers */
306
307 /* Initialize the rest of the device. */
308 int __init do_mambonet_probe(struct net_device *dev)
309 {
310         struct netdev_private *priv;
311         int devno = nextdevno++;
312         int irq;
313
314         printk("eth%d: bogus network driver initialization\n", devno);
315
316         irq = mambonet_probedev(devno, dev->dev_addr);
317
318         if (irq < 0) {
319                 printk("No IRQ retreived\n");
320                 return (-ENODEV);
321         }
322
323         printk("%s: %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", dev->name,
324                dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
325                dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
326
327         SET_MODULE_OWNER(dev);
328
329         dev->irq = irq;
330         dev->mtu = MAMBO_MTU;
331         dev->open = mambonet_open;
332         dev->poll = mambonet_poll;
333         dev->weight = 16;
334         dev->stop = mambonet_close;
335         dev->hard_start_xmit = mambonet_start_xmit;
336         dev->get_stats = mambonet_get_stats;
337         dev->set_mac_address = mambonet_set_mac_address;
338         dev->do_ioctl = mambonet_ioctl;
339
340         dev->priv = kmalloc(sizeof(struct netdev_private), GFP_KERNEL);
341         if (dev->priv == NULL)
342                 return -ENOMEM;
343         memset(dev->priv, 0, sizeof(struct netdev_private));
344
345         priv = dev->priv;
346         priv->devno = devno;
347         priv->closing = 0;
348         dev->get_stats = get_stats;
349
350         if (dev->irq == 0) {
351                 INIT_BOTTOM_HALF(&priv->poll_task, (void *)mambonet_timer,
352                                  (void *)dev);
353         }
354
355         return (0);
356 };
357
358 struct net_device *__init mambonet_probe(int unit)
359 {
360         struct net_device *dev = alloc_etherdev(0);
361         int err;
362
363         if (!dev)
364                 return ERR_PTR(-ENODEV);
365
366         sprintf(dev->name, "eth%d", unit);
367         netdev_boot_setup_check(dev);
368
369         err = do_mambonet_probe(dev);
370
371         if (err)
372                 goto out;
373
374         err = register_netdev(dev);
375         if (err)
376                 goto out;
377
378         return dev;
379
380       out:
381         free_netdev(dev);
382         return ERR_PTR(err);
383 }
384
385 int __init init_mambonet(void)
386 {
387         mambonet_probe(0);
388         return 0;
389 }
390
391 module_init(init_mambonet);
392 MODULE_LICENSE("GPL");