diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/um/drivers/net_kern.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/um/drivers/net_kern.c')
-rw-r--r-- | arch/um/drivers/net_kern.c | 896 |
1 files changed, 896 insertions, 0 deletions
diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c new file mode 100644 index 000000000000..4eeaf88c1e97 --- /dev/null +++ b/arch/um/drivers/net_kern.c | |||
@@ -0,0 +1,896 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and | ||
3 | * James Leu (jleu@mindspring.net). | ||
4 | * Copyright (C) 2001 by various other people who didn't put their name here. | ||
5 | * Licensed under the GPL. | ||
6 | */ | ||
7 | |||
8 | #include "linux/config.h" | ||
9 | #include "linux/kernel.h" | ||
10 | #include "linux/netdevice.h" | ||
11 | #include "linux/rtnetlink.h" | ||
12 | #include "linux/skbuff.h" | ||
13 | #include "linux/socket.h" | ||
14 | #include "linux/spinlock.h" | ||
15 | #include "linux/module.h" | ||
16 | #include "linux/init.h" | ||
17 | #include "linux/etherdevice.h" | ||
18 | #include "linux/list.h" | ||
19 | #include "linux/inetdevice.h" | ||
20 | #include "linux/ctype.h" | ||
21 | #include "linux/bootmem.h" | ||
22 | #include "linux/ethtool.h" | ||
23 | #include "asm/uaccess.h" | ||
24 | #include "user_util.h" | ||
25 | #include "kern_util.h" | ||
26 | #include "net_kern.h" | ||
27 | #include "net_user.h" | ||
28 | #include "mconsole_kern.h" | ||
29 | #include "init.h" | ||
30 | #include "irq_user.h" | ||
31 | #include "irq_kern.h" | ||
32 | |||
33 | #define DRIVER_NAME "uml-netdev" | ||
34 | |||
35 | static DEFINE_SPINLOCK(opened_lock); | ||
36 | LIST_HEAD(opened); | ||
37 | |||
38 | static int uml_net_rx(struct net_device *dev) | ||
39 | { | ||
40 | struct uml_net_private *lp = dev->priv; | ||
41 | int pkt_len; | ||
42 | struct sk_buff *skb; | ||
43 | |||
44 | /* If we can't allocate memory, try again next round. */ | ||
45 | skb = dev_alloc_skb(dev->mtu); | ||
46 | if (skb == NULL) { | ||
47 | lp->stats.rx_dropped++; | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | skb->dev = dev; | ||
52 | skb_put(skb, dev->mtu); | ||
53 | skb->mac.raw = skb->data; | ||
54 | pkt_len = (*lp->read)(lp->fd, &skb, lp); | ||
55 | |||
56 | if (pkt_len > 0) { | ||
57 | skb_trim(skb, pkt_len); | ||
58 | skb->protocol = (*lp->protocol)(skb); | ||
59 | netif_rx(skb); | ||
60 | |||
61 | lp->stats.rx_bytes += skb->len; | ||
62 | lp->stats.rx_packets++; | ||
63 | return pkt_len; | ||
64 | } | ||
65 | |||
66 | kfree_skb(skb); | ||
67 | return pkt_len; | ||
68 | } | ||
69 | |||
70 | irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
71 | { | ||
72 | struct net_device *dev = dev_id; | ||
73 | struct uml_net_private *lp = dev->priv; | ||
74 | int err; | ||
75 | |||
76 | if(!netif_running(dev)) | ||
77 | return(IRQ_NONE); | ||
78 | |||
79 | spin_lock(&lp->lock); | ||
80 | while((err = uml_net_rx(dev)) > 0) ; | ||
81 | if(err < 0) { | ||
82 | printk(KERN_ERR | ||
83 | "Device '%s' read returned %d, shutting it down\n", | ||
84 | dev->name, err); | ||
85 | dev_close(dev); | ||
86 | goto out; | ||
87 | } | ||
88 | reactivate_fd(lp->fd, UM_ETH_IRQ); | ||
89 | |||
90 | out: | ||
91 | spin_unlock(&lp->lock); | ||
92 | return(IRQ_HANDLED); | ||
93 | } | ||
94 | |||
95 | static int uml_net_open(struct net_device *dev) | ||
96 | { | ||
97 | struct uml_net_private *lp = dev->priv; | ||
98 | char addr[sizeof("255.255.255.255\0")]; | ||
99 | int err; | ||
100 | |||
101 | spin_lock(&lp->lock); | ||
102 | |||
103 | if(lp->fd >= 0){ | ||
104 | err = -ENXIO; | ||
105 | goto out; | ||
106 | } | ||
107 | |||
108 | if(!lp->have_mac){ | ||
109 | dev_ip_addr(dev, addr, &lp->mac[2]); | ||
110 | set_ether_mac(dev, lp->mac); | ||
111 | } | ||
112 | |||
113 | lp->fd = (*lp->open)(&lp->user); | ||
114 | if(lp->fd < 0){ | ||
115 | err = lp->fd; | ||
116 | goto out; | ||
117 | } | ||
118 | |||
119 | err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt, | ||
120 | SA_INTERRUPT | SA_SHIRQ, dev->name, dev); | ||
121 | if(err != 0){ | ||
122 | printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err); | ||
123 | if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user); | ||
124 | lp->fd = -1; | ||
125 | err = -ENETUNREACH; | ||
126 | } | ||
127 | |||
128 | lp->tl.data = (unsigned long) &lp->user; | ||
129 | netif_start_queue(dev); | ||
130 | |||
131 | /* clear buffer - it can happen that the host side of the interface | ||
132 | * is full when we get here. In this case, new data is never queued, | ||
133 | * SIGIOs never arrive, and the net never works. | ||
134 | */ | ||
135 | while((err = uml_net_rx(dev)) > 0) ; | ||
136 | |||
137 | out: | ||
138 | spin_unlock(&lp->lock); | ||
139 | return(err); | ||
140 | } | ||
141 | |||
142 | static int uml_net_close(struct net_device *dev) | ||
143 | { | ||
144 | struct uml_net_private *lp = dev->priv; | ||
145 | |||
146 | netif_stop_queue(dev); | ||
147 | spin_lock(&lp->lock); | ||
148 | |||
149 | free_irq_by_irq_and_dev(dev->irq, dev); | ||
150 | free_irq(dev->irq, dev); | ||
151 | if(lp->close != NULL) | ||
152 | (*lp->close)(lp->fd, &lp->user); | ||
153 | lp->fd = -1; | ||
154 | |||
155 | spin_unlock(&lp->lock); | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||
160 | { | ||
161 | struct uml_net_private *lp = dev->priv; | ||
162 | unsigned long flags; | ||
163 | int len; | ||
164 | |||
165 | netif_stop_queue(dev); | ||
166 | |||
167 | spin_lock_irqsave(&lp->lock, flags); | ||
168 | |||
169 | len = (*lp->write)(lp->fd, &skb, lp); | ||
170 | |||
171 | if(len == skb->len) { | ||
172 | lp->stats.tx_packets++; | ||
173 | lp->stats.tx_bytes += skb->len; | ||
174 | dev->trans_start = jiffies; | ||
175 | netif_start_queue(dev); | ||
176 | |||
177 | /* this is normally done in the interrupt when tx finishes */ | ||
178 | netif_wake_queue(dev); | ||
179 | } | ||
180 | else if(len == 0){ | ||
181 | netif_start_queue(dev); | ||
182 | lp->stats.tx_dropped++; | ||
183 | } | ||
184 | else { | ||
185 | netif_start_queue(dev); | ||
186 | printk(KERN_ERR "uml_net_start_xmit: failed(%d)\n", len); | ||
187 | } | ||
188 | |||
189 | spin_unlock_irqrestore(&lp->lock, flags); | ||
190 | |||
191 | dev_kfree_skb(skb); | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static struct net_device_stats *uml_net_get_stats(struct net_device *dev) | ||
197 | { | ||
198 | struct uml_net_private *lp = dev->priv; | ||
199 | return &lp->stats; | ||
200 | } | ||
201 | |||
202 | static void uml_net_set_multicast_list(struct net_device *dev) | ||
203 | { | ||
204 | if (dev->flags & IFF_PROMISC) return; | ||
205 | else if (dev->mc_count) dev->flags |= IFF_ALLMULTI; | ||
206 | else dev->flags &= ~IFF_ALLMULTI; | ||
207 | } | ||
208 | |||
209 | static void uml_net_tx_timeout(struct net_device *dev) | ||
210 | { | ||
211 | dev->trans_start = jiffies; | ||
212 | netif_wake_queue(dev); | ||
213 | } | ||
214 | |||
215 | static int uml_net_set_mac(struct net_device *dev, void *addr) | ||
216 | { | ||
217 | struct uml_net_private *lp = dev->priv; | ||
218 | struct sockaddr *hwaddr = addr; | ||
219 | |||
220 | spin_lock(&lp->lock); | ||
221 | memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); | ||
222 | spin_unlock(&lp->lock); | ||
223 | |||
224 | return(0); | ||
225 | } | ||
226 | |||
227 | static int uml_net_change_mtu(struct net_device *dev, int new_mtu) | ||
228 | { | ||
229 | struct uml_net_private *lp = dev->priv; | ||
230 | int err = 0; | ||
231 | |||
232 | spin_lock(&lp->lock); | ||
233 | |||
234 | new_mtu = (*lp->set_mtu)(new_mtu, &lp->user); | ||
235 | if(new_mtu < 0){ | ||
236 | err = new_mtu; | ||
237 | goto out; | ||
238 | } | ||
239 | |||
240 | dev->mtu = new_mtu; | ||
241 | |||
242 | out: | ||
243 | spin_unlock(&lp->lock); | ||
244 | return err; | ||
245 | } | ||
246 | |||
247 | static int uml_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | ||
248 | { | ||
249 | static const struct ethtool_drvinfo info = { | ||
250 | .cmd = ETHTOOL_GDRVINFO, | ||
251 | .driver = DRIVER_NAME, | ||
252 | .version = "42", | ||
253 | }; | ||
254 | void *useraddr; | ||
255 | u32 ethcmd; | ||
256 | |||
257 | switch (cmd) { | ||
258 | case SIOCETHTOOL: | ||
259 | useraddr = ifr->ifr_data; | ||
260 | if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) | ||
261 | return -EFAULT; | ||
262 | switch (ethcmd) { | ||
263 | case ETHTOOL_GDRVINFO: | ||
264 | if (copy_to_user(useraddr, &info, sizeof(info))) | ||
265 | return -EFAULT; | ||
266 | return 0; | ||
267 | default: | ||
268 | return -EOPNOTSUPP; | ||
269 | } | ||
270 | default: | ||
271 | return -EINVAL; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | void uml_net_user_timer_expire(unsigned long _conn) | ||
276 | { | ||
277 | #ifdef undef | ||
278 | struct connection *conn = (struct connection *)_conn; | ||
279 | |||
280 | dprintk(KERN_INFO "uml_net_user_timer_expire [%p]\n", conn); | ||
281 | do_connect(conn); | ||
282 | #endif | ||
283 | } | ||
284 | |||
285 | static DEFINE_SPINLOCK(devices_lock); | ||
286 | static struct list_head devices = LIST_HEAD_INIT(devices); | ||
287 | |||
288 | static struct device_driver uml_net_driver = { | ||
289 | .name = DRIVER_NAME, | ||
290 | .bus = &platform_bus_type, | ||
291 | }; | ||
292 | static int driver_registered; | ||
293 | |||
294 | static int eth_configure(int n, void *init, char *mac, | ||
295 | struct transport *transport) | ||
296 | { | ||
297 | struct uml_net *device; | ||
298 | struct net_device *dev; | ||
299 | struct uml_net_private *lp; | ||
300 | int save, err, size; | ||
301 | |||
302 | size = transport->private_size + sizeof(struct uml_net_private) + | ||
303 | sizeof(((struct uml_net_private *) 0)->user); | ||
304 | |||
305 | device = kmalloc(sizeof(*device), GFP_KERNEL); | ||
306 | if (device == NULL) { | ||
307 | printk(KERN_ERR "eth_configure failed to allocate uml_net\n"); | ||
308 | return(1); | ||
309 | } | ||
310 | |||
311 | memset(device, 0, sizeof(*device)); | ||
312 | INIT_LIST_HEAD(&device->list); | ||
313 | device->index = n; | ||
314 | |||
315 | spin_lock(&devices_lock); | ||
316 | list_add(&device->list, &devices); | ||
317 | spin_unlock(&devices_lock); | ||
318 | |||
319 | if (setup_etheraddr(mac, device->mac)) | ||
320 | device->have_mac = 1; | ||
321 | |||
322 | printk(KERN_INFO "Netdevice %d ", n); | ||
323 | if (device->have_mac) | ||
324 | printk("(%02x:%02x:%02x:%02x:%02x:%02x) ", | ||
325 | device->mac[0], device->mac[1], | ||
326 | device->mac[2], device->mac[3], | ||
327 | device->mac[4], device->mac[5]); | ||
328 | printk(": "); | ||
329 | dev = alloc_etherdev(size); | ||
330 | if (dev == NULL) { | ||
331 | printk(KERN_ERR "eth_configure: failed to allocate device\n"); | ||
332 | return 1; | ||
333 | } | ||
334 | |||
335 | /* sysfs register */ | ||
336 | if (!driver_registered) { | ||
337 | driver_register(¨_net_driver); | ||
338 | driver_registered = 1; | ||
339 | } | ||
340 | device->pdev.id = n; | ||
341 | device->pdev.name = DRIVER_NAME; | ||
342 | platform_device_register(&device->pdev); | ||
343 | SET_NETDEV_DEV(dev,&device->pdev.dev); | ||
344 | |||
345 | /* If this name ends up conflicting with an existing registered | ||
346 | * netdevice, that is OK, register_netdev{,ice}() will notice this | ||
347 | * and fail. | ||
348 | */ | ||
349 | snprintf(dev->name, sizeof(dev->name), "eth%d", n); | ||
350 | device->dev = dev; | ||
351 | |||
352 | (*transport->kern->init)(dev, init); | ||
353 | |||
354 | dev->mtu = transport->user->max_packet; | ||
355 | dev->open = uml_net_open; | ||
356 | dev->hard_start_xmit = uml_net_start_xmit; | ||
357 | dev->stop = uml_net_close; | ||
358 | dev->get_stats = uml_net_get_stats; | ||
359 | dev->set_multicast_list = uml_net_set_multicast_list; | ||
360 | dev->tx_timeout = uml_net_tx_timeout; | ||
361 | dev->set_mac_address = uml_net_set_mac; | ||
362 | dev->change_mtu = uml_net_change_mtu; | ||
363 | dev->do_ioctl = uml_net_ioctl; | ||
364 | dev->watchdog_timeo = (HZ >> 1); | ||
365 | dev->irq = UM_ETH_IRQ; | ||
366 | |||
367 | rtnl_lock(); | ||
368 | err = register_netdevice(dev); | ||
369 | rtnl_unlock(); | ||
370 | if (err) { | ||
371 | device->dev = NULL; | ||
372 | /* XXX: should we call ->remove() here? */ | ||
373 | free_netdev(dev); | ||
374 | return 1; | ||
375 | } | ||
376 | lp = dev->priv; | ||
377 | |||
378 | /* lp.user is the first four bytes of the transport data, which | ||
379 | * has already been initialized. This structure assignment will | ||
380 | * overwrite that, so we make sure that .user gets overwritten with | ||
381 | * what it already has. | ||
382 | */ | ||
383 | save = lp->user[0]; | ||
384 | *lp = ((struct uml_net_private) | ||
385 | { .list = LIST_HEAD_INIT(lp->list), | ||
386 | .dev = dev, | ||
387 | .fd = -1, | ||
388 | .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0}, | ||
389 | .have_mac = device->have_mac, | ||
390 | .protocol = transport->kern->protocol, | ||
391 | .open = transport->user->open, | ||
392 | .close = transport->user->close, | ||
393 | .remove = transport->user->remove, | ||
394 | .read = transport->kern->read, | ||
395 | .write = transport->kern->write, | ||
396 | .add_address = transport->user->add_address, | ||
397 | .delete_address = transport->user->delete_address, | ||
398 | .set_mtu = transport->user->set_mtu, | ||
399 | .user = { save } }); | ||
400 | |||
401 | init_timer(&lp->tl); | ||
402 | spin_lock_init(&lp->lock); | ||
403 | lp->tl.function = uml_net_user_timer_expire; | ||
404 | if (lp->have_mac) | ||
405 | memcpy(lp->mac, device->mac, sizeof(lp->mac)); | ||
406 | |||
407 | if (transport->user->init) | ||
408 | (*transport->user->init)(&lp->user, dev); | ||
409 | |||
410 | if (device->have_mac) | ||
411 | set_ether_mac(dev, device->mac); | ||
412 | |||
413 | spin_lock(&opened_lock); | ||
414 | list_add(&lp->list, &opened); | ||
415 | spin_unlock(&opened_lock); | ||
416 | |||
417 | return(0); | ||
418 | } | ||
419 | |||
420 | static struct uml_net *find_device(int n) | ||
421 | { | ||
422 | struct uml_net *device; | ||
423 | struct list_head *ele; | ||
424 | |||
425 | spin_lock(&devices_lock); | ||
426 | list_for_each(ele, &devices){ | ||
427 | device = list_entry(ele, struct uml_net, list); | ||
428 | if(device->index == n) | ||
429 | goto out; | ||
430 | } | ||
431 | device = NULL; | ||
432 | out: | ||
433 | spin_unlock(&devices_lock); | ||
434 | return(device); | ||
435 | } | ||
436 | |||
437 | static int eth_parse(char *str, int *index_out, char **str_out) | ||
438 | { | ||
439 | char *end; | ||
440 | int n; | ||
441 | |||
442 | n = simple_strtoul(str, &end, 0); | ||
443 | if(end == str){ | ||
444 | printk(KERN_ERR "eth_setup: Failed to parse '%s'\n", str); | ||
445 | return(1); | ||
446 | } | ||
447 | if(n < 0){ | ||
448 | printk(KERN_ERR "eth_setup: device %d is negative\n", n); | ||
449 | return(1); | ||
450 | } | ||
451 | str = end; | ||
452 | if(*str != '='){ | ||
453 | printk(KERN_ERR | ||
454 | "eth_setup: expected '=' after device number\n"); | ||
455 | return(1); | ||
456 | } | ||
457 | str++; | ||
458 | if(find_device(n)){ | ||
459 | printk(KERN_ERR "eth_setup: Device %d already configured\n", | ||
460 | n); | ||
461 | return(1); | ||
462 | } | ||
463 | if(index_out) *index_out = n; | ||
464 | *str_out = str; | ||
465 | return(0); | ||
466 | } | ||
467 | |||
468 | struct eth_init { | ||
469 | struct list_head list; | ||
470 | char *init; | ||
471 | int index; | ||
472 | }; | ||
473 | |||
474 | /* Filled in at boot time. Will need locking if the transports become | ||
475 | * modular. | ||
476 | */ | ||
477 | struct list_head transports = LIST_HEAD_INIT(transports); | ||
478 | |||
479 | /* Filled in during early boot */ | ||
480 | struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line); | ||
481 | |||
482 | static int check_transport(struct transport *transport, char *eth, int n, | ||
483 | void **init_out, char **mac_out) | ||
484 | { | ||
485 | int len; | ||
486 | |||
487 | len = strlen(transport->name); | ||
488 | if(strncmp(eth, transport->name, len)) | ||
489 | return(0); | ||
490 | |||
491 | eth += len; | ||
492 | if(*eth == ',') | ||
493 | eth++; | ||
494 | else if(*eth != '\0') | ||
495 | return(0); | ||
496 | |||
497 | *init_out = kmalloc(transport->setup_size, GFP_KERNEL); | ||
498 | if(*init_out == NULL) | ||
499 | return(1); | ||
500 | |||
501 | if(!transport->setup(eth, mac_out, *init_out)){ | ||
502 | kfree(*init_out); | ||
503 | *init_out = NULL; | ||
504 | } | ||
505 | return(1); | ||
506 | } | ||
507 | |||
508 | void register_transport(struct transport *new) | ||
509 | { | ||
510 | struct list_head *ele, *next; | ||
511 | struct eth_init *eth; | ||
512 | void *init; | ||
513 | char *mac = NULL; | ||
514 | int match; | ||
515 | |||
516 | list_add(&new->list, &transports); | ||
517 | |||
518 | list_for_each_safe(ele, next, ð_cmd_line){ | ||
519 | eth = list_entry(ele, struct eth_init, list); | ||
520 | match = check_transport(new, eth->init, eth->index, &init, | ||
521 | &mac); | ||
522 | if(!match) | ||
523 | continue; | ||
524 | else if(init != NULL){ | ||
525 | eth_configure(eth->index, init, mac, new); | ||
526 | kfree(init); | ||
527 | } | ||
528 | list_del(ð->list); | ||
529 | } | ||
530 | } | ||
531 | |||
532 | static int eth_setup_common(char *str, int index) | ||
533 | { | ||
534 | struct list_head *ele; | ||
535 | struct transport *transport; | ||
536 | void *init; | ||
537 | char *mac = NULL; | ||
538 | |||
539 | list_for_each(ele, &transports){ | ||
540 | transport = list_entry(ele, struct transport, list); | ||
541 | if(!check_transport(transport, str, index, &init, &mac)) | ||
542 | continue; | ||
543 | if(init != NULL){ | ||
544 | eth_configure(index, init, mac, transport); | ||
545 | kfree(init); | ||
546 | } | ||
547 | return(1); | ||
548 | } | ||
549 | return(0); | ||
550 | } | ||
551 | |||
552 | static int eth_setup(char *str) | ||
553 | { | ||
554 | struct eth_init *new; | ||
555 | int n, err; | ||
556 | |||
557 | err = eth_parse(str, &n, &str); | ||
558 | if(err) return(1); | ||
559 | |||
560 | new = alloc_bootmem(sizeof(new)); | ||
561 | if (new == NULL){ | ||
562 | printk("eth_init : alloc_bootmem failed\n"); | ||
563 | return(1); | ||
564 | } | ||
565 | |||
566 | INIT_LIST_HEAD(&new->list); | ||
567 | new->index = n; | ||
568 | new->init = str; | ||
569 | |||
570 | list_add_tail(&new->list, ð_cmd_line); | ||
571 | return(1); | ||
572 | } | ||
573 | |||
574 | __setup("eth", eth_setup); | ||
575 | __uml_help(eth_setup, | ||
576 | "eth[0-9]+=<transport>,<options>\n" | ||
577 | " Configure a network device.\n\n" | ||
578 | ); | ||
579 | |||
580 | #if 0 | ||
581 | static int eth_init(void) | ||
582 | { | ||
583 | struct list_head *ele, *next; | ||
584 | struct eth_init *eth; | ||
585 | |||
586 | list_for_each_safe(ele, next, ð_cmd_line){ | ||
587 | eth = list_entry(ele, struct eth_init, list); | ||
588 | |||
589 | if(eth_setup_common(eth->init, eth->index)) | ||
590 | list_del(ð->list); | ||
591 | } | ||
592 | |||
593 | return(1); | ||
594 | } | ||
595 | __initcall(eth_init); | ||
596 | #endif | ||
597 | |||
598 | static int net_config(char *str) | ||
599 | { | ||
600 | int n, err; | ||
601 | |||
602 | err = eth_parse(str, &n, &str); | ||
603 | if(err) return(err); | ||
604 | |||
605 | str = uml_strdup(str); | ||
606 | if(str == NULL){ | ||
607 | printk(KERN_ERR "net_config failed to strdup string\n"); | ||
608 | return(-1); | ||
609 | } | ||
610 | err = !eth_setup_common(str, n); | ||
611 | if(err) | ||
612 | kfree(str); | ||
613 | return(err); | ||
614 | } | ||
615 | |||
616 | static int net_remove(char *str) | ||
617 | { | ||
618 | struct uml_net *device; | ||
619 | struct net_device *dev; | ||
620 | struct uml_net_private *lp; | ||
621 | char *end; | ||
622 | int n; | ||
623 | |||
624 | n = simple_strtoul(str, &end, 0); | ||
625 | if((*end != '\0') || (end == str)) | ||
626 | return(-1); | ||
627 | |||
628 | device = find_device(n); | ||
629 | if(device == NULL) | ||
630 | return(0); | ||
631 | |||
632 | dev = device->dev; | ||
633 | lp = dev->priv; | ||
634 | if(lp->fd > 0) return(-1); | ||
635 | if(lp->remove != NULL) (*lp->remove)(&lp->user); | ||
636 | unregister_netdev(dev); | ||
637 | platform_device_unregister(&device->pdev); | ||
638 | |||
639 | list_del(&device->list); | ||
640 | kfree(device); | ||
641 | free_netdev(dev); | ||
642 | return(0); | ||
643 | } | ||
644 | |||
645 | static struct mc_device net_mc = { | ||
646 | .name = "eth", | ||
647 | .config = net_config, | ||
648 | .get_config = NULL, | ||
649 | .remove = net_remove, | ||
650 | }; | ||
651 | |||
652 | static int uml_inetaddr_event(struct notifier_block *this, unsigned long event, | ||
653 | void *ptr) | ||
654 | { | ||
655 | struct in_ifaddr *ifa = ptr; | ||
656 | u32 addr = ifa->ifa_address; | ||
657 | u32 netmask = ifa->ifa_mask; | ||
658 | struct net_device *dev = ifa->ifa_dev->dev; | ||
659 | struct uml_net_private *lp; | ||
660 | void (*proc)(unsigned char *, unsigned char *, void *); | ||
661 | unsigned char addr_buf[4], netmask_buf[4]; | ||
662 | |||
663 | if(dev->open != uml_net_open) return(NOTIFY_DONE); | ||
664 | |||
665 | lp = dev->priv; | ||
666 | |||
667 | proc = NULL; | ||
668 | switch (event){ | ||
669 | case NETDEV_UP: | ||
670 | proc = lp->add_address; | ||
671 | break; | ||
672 | case NETDEV_DOWN: | ||
673 | proc = lp->delete_address; | ||
674 | break; | ||
675 | } | ||
676 | if(proc != NULL){ | ||
677 | addr_buf[0] = addr & 0xff; | ||
678 | addr_buf[1] = (addr >> 8) & 0xff; | ||
679 | addr_buf[2] = (addr >> 16) & 0xff; | ||
680 | addr_buf[3] = addr >> 24; | ||
681 | netmask_buf[0] = netmask & 0xff; | ||
682 | netmask_buf[1] = (netmask >> 8) & 0xff; | ||
683 | netmask_buf[2] = (netmask >> 16) & 0xff; | ||
684 | netmask_buf[3] = netmask >> 24; | ||
685 | (*proc)(addr_buf, netmask_buf, &lp->user); | ||
686 | } | ||
687 | return(NOTIFY_DONE); | ||
688 | } | ||
689 | |||
690 | struct notifier_block uml_inetaddr_notifier = { | ||
691 | .notifier_call = uml_inetaddr_event, | ||
692 | }; | ||
693 | |||
694 | static int uml_net_init(void) | ||
695 | { | ||
696 | struct list_head *ele; | ||
697 | struct uml_net_private *lp; | ||
698 | struct in_device *ip; | ||
699 | struct in_ifaddr *in; | ||
700 | |||
701 | mconsole_register_dev(&net_mc); | ||
702 | register_inetaddr_notifier(¨_inetaddr_notifier); | ||
703 | |||
704 | /* Devices may have been opened already, so the uml_inetaddr_notifier | ||
705 | * didn't get a chance to run for them. This fakes it so that | ||
706 | * addresses which have already been set up get handled properly. | ||
707 | */ | ||
708 | list_for_each(ele, &opened){ | ||
709 | lp = list_entry(ele, struct uml_net_private, list); | ||
710 | ip = lp->dev->ip_ptr; | ||
711 | if(ip == NULL) continue; | ||
712 | in = ip->ifa_list; | ||
713 | while(in != NULL){ | ||
714 | uml_inetaddr_event(NULL, NETDEV_UP, in); | ||
715 | in = in->ifa_next; | ||
716 | } | ||
717 | } | ||
718 | |||
719 | return(0); | ||
720 | } | ||
721 | |||
722 | __initcall(uml_net_init); | ||
723 | |||
724 | static void close_devices(void) | ||
725 | { | ||
726 | struct list_head *ele; | ||
727 | struct uml_net_private *lp; | ||
728 | |||
729 | list_for_each(ele, &opened){ | ||
730 | lp = list_entry(ele, struct uml_net_private, list); | ||
731 | if((lp->close != NULL) && (lp->fd >= 0)) | ||
732 | (*lp->close)(lp->fd, &lp->user); | ||
733 | if(lp->remove != NULL) (*lp->remove)(&lp->user); | ||
734 | } | ||
735 | } | ||
736 | |||
737 | __uml_exitcall(close_devices); | ||
738 | |||
739 | int setup_etheraddr(char *str, unsigned char *addr) | ||
740 | { | ||
741 | char *end; | ||
742 | int i; | ||
743 | |||
744 | if(str == NULL) | ||
745 | return(0); | ||
746 | for(i=0;i<6;i++){ | ||
747 | addr[i] = simple_strtoul(str, &end, 16); | ||
748 | if((end == str) || | ||
749 | ((*end != ':') && (*end != ',') && (*end != '\0'))){ | ||
750 | printk(KERN_ERR | ||
751 | "setup_etheraddr: failed to parse '%s' " | ||
752 | "as an ethernet address\n", str); | ||
753 | return(0); | ||
754 | } | ||
755 | str = end + 1; | ||
756 | } | ||
757 | if(addr[0] & 1){ | ||
758 | printk(KERN_ERR | ||
759 | "Attempt to assign a broadcast ethernet address to a " | ||
760 | "device disallowed\n"); | ||
761 | return(0); | ||
762 | } | ||
763 | return(1); | ||
764 | } | ||
765 | |||
766 | void dev_ip_addr(void *d, char *buf, char *bin_buf) | ||
767 | { | ||
768 | struct net_device *dev = d; | ||
769 | struct in_device *ip = dev->ip_ptr; | ||
770 | struct in_ifaddr *in; | ||
771 | u32 addr; | ||
772 | |||
773 | if((ip == NULL) || ((in = ip->ifa_list) == NULL)){ | ||
774 | printk(KERN_WARNING "dev_ip_addr - device not assigned an " | ||
775 | "IP address\n"); | ||
776 | return; | ||
777 | } | ||
778 | addr = in->ifa_address; | ||
779 | sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, | ||
780 | (addr >> 16) & 0xff, addr >> 24); | ||
781 | if(bin_buf){ | ||
782 | bin_buf[0] = addr & 0xff; | ||
783 | bin_buf[1] = (addr >> 8) & 0xff; | ||
784 | bin_buf[2] = (addr >> 16) & 0xff; | ||
785 | bin_buf[3] = addr >> 24; | ||
786 | } | ||
787 | } | ||
788 | |||
789 | void set_ether_mac(void *d, unsigned char *addr) | ||
790 | { | ||
791 | struct net_device *dev = d; | ||
792 | |||
793 | memcpy(dev->dev_addr, addr, ETH_ALEN); | ||
794 | } | ||
795 | |||
796 | struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra) | ||
797 | { | ||
798 | if((skb != NULL) && (skb_tailroom(skb) < extra)){ | ||
799 | struct sk_buff *skb2; | ||
800 | |||
801 | skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC); | ||
802 | dev_kfree_skb(skb); | ||
803 | skb = skb2; | ||
804 | } | ||
805 | if(skb != NULL) skb_put(skb, extra); | ||
806 | return(skb); | ||
807 | } | ||
808 | |||
809 | void iter_addresses(void *d, void (*cb)(unsigned char *, unsigned char *, | ||
810 | void *), | ||
811 | void *arg) | ||
812 | { | ||
813 | struct net_device *dev = d; | ||
814 | struct in_device *ip = dev->ip_ptr; | ||
815 | struct in_ifaddr *in; | ||
816 | unsigned char address[4], netmask[4]; | ||
817 | |||
818 | if(ip == NULL) return; | ||
819 | in = ip->ifa_list; | ||
820 | while(in != NULL){ | ||
821 | address[0] = in->ifa_address & 0xff; | ||
822 | address[1] = (in->ifa_address >> 8) & 0xff; | ||
823 | address[2] = (in->ifa_address >> 16) & 0xff; | ||
824 | address[3] = in->ifa_address >> 24; | ||
825 | netmask[0] = in->ifa_mask & 0xff; | ||
826 | netmask[1] = (in->ifa_mask >> 8) & 0xff; | ||
827 | netmask[2] = (in->ifa_mask >> 16) & 0xff; | ||
828 | netmask[3] = in->ifa_mask >> 24; | ||
829 | (*cb)(address, netmask, arg); | ||
830 | in = in->ifa_next; | ||
831 | } | ||
832 | } | ||
833 | |||
834 | int dev_netmask(void *d, void *m) | ||
835 | { | ||
836 | struct net_device *dev = d; | ||
837 | struct in_device *ip = dev->ip_ptr; | ||
838 | struct in_ifaddr *in; | ||
839 | __u32 *mask_out = m; | ||
840 | |||
841 | if(ip == NULL) | ||
842 | return(1); | ||
843 | |||
844 | in = ip->ifa_list; | ||
845 | if(in == NULL) | ||
846 | return(1); | ||
847 | |||
848 | *mask_out = in->ifa_mask; | ||
849 | return(0); | ||
850 | } | ||
851 | |||
852 | void *get_output_buffer(int *len_out) | ||
853 | { | ||
854 | void *ret; | ||
855 | |||
856 | ret = (void *) __get_free_pages(GFP_KERNEL, 0); | ||
857 | if(ret) *len_out = PAGE_SIZE; | ||
858 | else *len_out = 0; | ||
859 | return(ret); | ||
860 | } | ||
861 | |||
862 | void free_output_buffer(void *buffer) | ||
863 | { | ||
864 | free_pages((unsigned long) buffer, 0); | ||
865 | } | ||
866 | |||
867 | int tap_setup_common(char *str, char *type, char **dev_name, char **mac_out, | ||
868 | char **gate_addr) | ||
869 | { | ||
870 | char *remain; | ||
871 | |||
872 | remain = split_if_spec(str, dev_name, mac_out, gate_addr, NULL); | ||
873 | if(remain != NULL){ | ||
874 | printk("tap_setup_common - Extra garbage on specification : " | ||
875 | "'%s'\n", remain); | ||
876 | return(1); | ||
877 | } | ||
878 | |||
879 | return(0); | ||
880 | } | ||
881 | |||
882 | unsigned short eth_protocol(struct sk_buff *skb) | ||
883 | { | ||
884 | return(eth_type_trans(skb, skb->dev)); | ||
885 | } | ||
886 | |||
887 | /* | ||
888 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
889 | * Emacs will notice this stuff at the end of the file and automatically | ||
890 | * adjust the settings for this buffer only. This must remain at the end | ||
891 | * of the file. | ||
892 | * --------------------------------------------------------------------------- | ||
893 | * Local variables: | ||
894 | * c-file-style: "linux" | ||
895 | * End: | ||
896 | */ | ||