]>
Commit | Line | Data |
---|---|---|
7282bee7 CZ |
1 | /* |
2 | * | |
3 | * arch/xtensa/platform-iss/network.c | |
4 | * | |
5 | * Platform specific initialization. | |
6 | * | |
7 | * Authors: Chris Zankel <chris@zankel.net> | |
8 | * Based on work form the UML team. | |
9 | * | |
10 | * Copyright 2005 Tensilica Inc. | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify it | |
13 | * under the terms of the GNU General Public License as published by the | |
14 | * Free Software Foundation; either version 2 of the License, or (at your | |
15 | * option) any later version. | |
16 | * | |
17 | */ | |
18 | ||
7282bee7 CZ |
19 | #include <linux/list.h> |
20 | #include <linux/irq.h> | |
21 | #include <linux/spinlock.h> | |
22 | #include <linux/slab.h> | |
23 | #include <linux/timer.h> | |
24 | #include <linux/if_ether.h> | |
25 | #include <linux/inetdevice.h> | |
26 | #include <linux/init.h> | |
27 | #include <linux/if_tun.h> | |
28 | #include <linux/etherdevice.h> | |
29 | #include <linux/interrupt.h> | |
30 | #include <linux/ioctl.h> | |
31 | #include <linux/bootmem.h> | |
32 | #include <linux/ethtool.h> | |
33 | #include <linux/rtnetlink.h> | |
34 | #include <linux/timer.h> | |
d052d1be | 35 | #include <linux/platform_device.h> |
7282bee7 | 36 | |
173d6681 | 37 | #include <asm/platform/simcall.h> |
7282bee7 CZ |
38 | |
39 | #define DRIVER_NAME "iss-netdev" | |
40 | #define ETH_MAX_PACKET 1500 | |
41 | #define ETH_HEADER_OTHER 14 | |
42 | #define ISS_NET_TIMER_VALUE (2 * HZ) | |
43 | ||
44 | ||
45 | static DEFINE_SPINLOCK(opened_lock); | |
46 | static LIST_HEAD(opened); | |
47 | ||
48 | static DEFINE_SPINLOCK(devices_lock); | |
49 | static LIST_HEAD(devices); | |
50 | ||
51 | /* ------------------------------------------------------------------------- */ | |
52 | ||
53 | /* We currently only support the TUNTAP transport protocol. */ | |
54 | ||
55 | #define TRANSPORT_TUNTAP_NAME "tuntap" | |
56 | #define TRANSPORT_TUNTAP_MTU ETH_MAX_PACKET | |
57 | ||
58 | struct tuntap_info { | |
59 | char dev_name[IFNAMSIZ]; | |
60 | int fixed_config; | |
61 | unsigned char gw[ETH_ALEN]; | |
62 | int fd; | |
63 | }; | |
64 | ||
65 | /* ------------------------------------------------------------------------- */ | |
66 | ||
67 | ||
68 | /* This structure contains out private information for the driver. */ | |
69 | ||
70 | struct iss_net_private { | |
71 | ||
72 | struct list_head device_list; | |
73 | struct list_head opened_list; | |
74 | ||
75 | spinlock_t lock; | |
76 | struct net_device *dev; | |
77 | struct platform_device pdev; | |
78 | struct timer_list tl; | |
79 | struct net_device_stats stats; | |
80 | ||
81 | struct timer_list timer; | |
82 | unsigned int timer_val; | |
83 | ||
84 | int index; | |
85 | int mtu; | |
86 | ||
87 | unsigned char mac[ETH_ALEN]; | |
88 | int have_mac; | |
89 | ||
90 | struct { | |
91 | union { | |
92 | struct tuntap_info tuntap; | |
93 | } info; | |
94 | ||
95 | int (*open)(struct iss_net_private *lp); | |
96 | void (*close)(struct iss_net_private *lp); | |
97 | int (*read)(struct iss_net_private *lp, struct sk_buff **skb); | |
98 | int (*write)(struct iss_net_private *lp, struct sk_buff **skb); | |
99 | unsigned short (*protocol)(struct sk_buff *skb); | |
100 | int (*poll)(struct iss_net_private *lp); | |
101 | } tp; | |
102 | ||
103 | }; | |
104 | ||
105 | /* ======================= ISS SIMCALL INTERFACE =========================== */ | |
106 | ||
107 | /* Note: __simc must _not_ be declared inline! */ | |
108 | ||
109 | static int errno; | |
110 | ||
111 | static int __simc (int a, int b, int c, int d, int e, int f) | |
112 | { | |
113 | int ret; | |
114 | __asm__ __volatile__ ("simcall\n" | |
115 | "mov %0, a2\n" | |
116 | "mov %1, a3\n" : "=a" (ret), "=a" (errno) | |
117 | : : "a2", "a3"); | |
118 | return ret; | |
119 | } | |
120 | ||
121 | static int inline simc_open(char *file, int flags, int mode) | |
122 | { | |
123 | return __simc(SYS_open, (int) file, flags, mode, 0, 0); | |
124 | } | |
125 | ||
126 | static int inline simc_close(int fd) | |
127 | { | |
128 | return __simc(SYS_close, fd, 0, 0, 0, 0); | |
129 | } | |
130 | ||
131 | static int inline simc_ioctl(int fd, int request, void *arg) | |
132 | { | |
133 | return __simc(SYS_ioctl, fd, request, (int) arg, 0, 0); | |
134 | } | |
135 | ||
136 | static int inline simc_read(int fd, void *buf, size_t count) | |
137 | { | |
138 | return __simc(SYS_read, fd, (int) buf, count, 0, 0); | |
139 | } | |
140 | ||
141 | static int inline simc_write(int fd, void *buf, size_t count) | |
142 | { | |
143 | return __simc(SYS_write, fd, (int) buf, count, 0, 0); | |
144 | } | |
145 | ||
146 | static int inline simc_poll(int fd) | |
147 | { | |
148 | struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; | |
149 | ||
150 | return __simc(SYS_select_one, fd, XTISS_SELECT_ONE_READ, (int)&tv,0,0); | |
151 | } | |
152 | ||
153 | /* ================================ HELPERS ================================ */ | |
154 | ||
155 | ||
156 | static char *split_if_spec(char *str, ...) | |
157 | { | |
158 | char **arg, *end; | |
159 | va_list ap; | |
160 | ||
161 | va_start(ap, str); | |
162 | while ((arg = va_arg(ap, char**)) != NULL) { | |
163 | if (*str == '\0') | |
164 | return NULL; | |
165 | end = strchr(str, ','); | |
166 | if (end != str) | |
167 | *arg = str; | |
168 | if (end == NULL) | |
169 | return NULL; | |
170 | *end ++ = '\0'; | |
171 | str = end; | |
172 | } | |
173 | va_end(ap); | |
174 | return str; | |
175 | } | |
176 | ||
177 | ||
178 | #if 0 | |
179 | /* Adjust SKB. */ | |
180 | ||
181 | struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra) | |
182 | { | |
183 | if ((skb != NULL) && (skb_tailroom(skb) < extra)) { | |
184 | struct sk_buff *skb2; | |
185 | ||
186 | skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC); | |
187 | dev_kfree_skb(skb); | |
188 | skb = skb2; | |
189 | } | |
190 | if (skb != NULL) | |
191 | skb_put(skb, extra); | |
192 | ||
193 | return skb; | |
194 | } | |
195 | #endif | |
196 | ||
197 | /* Return the IP address as a string for a given device. */ | |
198 | ||
199 | static void dev_ip_addr(void *d, char *buf, char *bin_buf) | |
200 | { | |
201 | struct net_device *dev = d; | |
202 | struct in_device *ip = dev->ip_ptr; | |
203 | struct in_ifaddr *in; | |
a144ea4b | 204 | __be32 addr; |
7282bee7 CZ |
205 | |
206 | if ((ip == NULL) || ((in = ip->ifa_list) == NULL)) { | |
207 | printk(KERN_WARNING "Device not assigned an IP address!\n"); | |
208 | return; | |
209 | } | |
210 | ||
211 | addr = in->ifa_address; | |
212 | sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, | |
213 | (addr >> 16) & 0xff, addr >> 24); | |
214 | ||
215 | if (bin_buf) { | |
216 | bin_buf[0] = addr & 0xff; | |
217 | bin_buf[1] = (addr >> 8) & 0xff; | |
218 | bin_buf[2] = (addr >> 16) & 0xff; | |
219 | bin_buf[3] = addr >> 24; | |
220 | } | |
221 | } | |
222 | ||
223 | /* Set Ethernet address of the specified device. */ | |
224 | ||
225 | static void inline set_ether_mac(void *d, unsigned char *addr) | |
226 | { | |
227 | struct net_device *dev = d; | |
228 | memcpy(dev->dev_addr, addr, ETH_ALEN); | |
229 | } | |
230 | ||
231 | ||
232 | /* ======================= TUNTAP TRANSPORT INTERFACE ====================== */ | |
233 | ||
234 | static int tuntap_open(struct iss_net_private *lp) | |
235 | { | |
236 | struct ifreq ifr; | |
237 | char *dev_name = lp->tp.info.tuntap.dev_name; | |
238 | int err = -EINVAL; | |
239 | int fd; | |
240 | ||
241 | /* We currently only support a fixed configuration. */ | |
242 | ||
243 | if (!lp->tp.info.tuntap.fixed_config) | |
244 | return -EINVAL; | |
245 | ||
246 | if ((fd = simc_open("/dev/net/tun", 02, 0)) < 0) { /* O_RDWR */ | |
247 | printk("Failed to open /dev/net/tun, returned %d " | |
248 | "(errno = %d)\n", fd, errno); | |
249 | return fd; | |
250 | } | |
251 | ||
252 | memset(&ifr, 0, sizeof ifr); | |
253 | ifr.ifr_flags = IFF_TAP | IFF_NO_PI; | |
0ddb16cf | 254 | strlcpy(ifr.ifr_name, dev_name, sizeof ifr.ifr_name); |
7282bee7 CZ |
255 | |
256 | if ((err = simc_ioctl(fd, TUNSETIFF, (void*) &ifr)) < 0) { | |
257 | printk("Failed to set interface, returned %d " | |
258 | "(errno = %d)\n", err, errno); | |
259 | simc_close(fd); | |
260 | return err; | |
261 | } | |
262 | ||
263 | lp->tp.info.tuntap.fd = fd; | |
264 | return err; | |
265 | } | |
266 | ||
267 | static void tuntap_close(struct iss_net_private *lp) | |
268 | { | |
269 | #if 0 | |
270 | if (lp->tp.info.tuntap.fixed_config) | |
271 | iter_addresses(lp->tp.info.tuntap.dev, close_addr, lp->host.dev_name); | |
272 | #endif | |
273 | simc_close(lp->tp.info.tuntap.fd); | |
274 | lp->tp.info.tuntap.fd = -1; | |
275 | } | |
276 | ||
277 | static int tuntap_read (struct iss_net_private *lp, struct sk_buff **skb) | |
278 | { | |
279 | #if 0 | |
280 | *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); | |
281 | if (*skb == NULL) | |
282 | return -ENOMEM; | |
283 | #endif | |
284 | ||
285 | return simc_read(lp->tp.info.tuntap.fd, | |
286 | (*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER); | |
287 | } | |
288 | ||
289 | static int tuntap_write (struct iss_net_private *lp, struct sk_buff **skb) | |
290 | { | |
291 | return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len); | |
292 | } | |
293 | ||
294 | unsigned short tuntap_protocol(struct sk_buff *skb) | |
295 | { | |
296 | return eth_type_trans(skb, skb->dev); | |
297 | } | |
298 | ||
299 | static int tuntap_poll(struct iss_net_private *lp) | |
300 | { | |
301 | return simc_poll(lp->tp.info.tuntap.fd); | |
302 | } | |
303 | ||
304 | /* | |
305 | * Currently only a device name is supported. | |
306 | * ethX=tuntap[,[mac address][,[device name]]] | |
307 | */ | |
308 | ||
309 | static int tuntap_probe(struct iss_net_private *lp, int index, char *init) | |
310 | { | |
311 | const int len = strlen(TRANSPORT_TUNTAP_NAME); | |
312 | char *dev_name = NULL, *mac_str = NULL, *rem = NULL; | |
313 | ||
314 | /* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */ | |
315 | ||
316 | if (strncmp(init, TRANSPORT_TUNTAP_NAME, len)) | |
317 | return 0; | |
318 | ||
319 | if (*(init += strlen(TRANSPORT_TUNTAP_NAME)) == ',') { | |
320 | if ((rem=split_if_spec(init+1, &mac_str, &dev_name)) != NULL) { | |
321 | printk("Extra garbage on specification : '%s'\n", rem); | |
322 | return 0; | |
323 | } | |
324 | } else if (*init != '\0') { | |
325 | printk("Invalid argument: %s. Skipping device!\n", init); | |
326 | return 0; | |
327 | } | |
328 | ||
329 | if (dev_name) { | |
330 | strncpy(lp->tp.info.tuntap.dev_name, dev_name, | |
331 | sizeof lp->tp.info.tuntap.dev_name); | |
332 | lp->tp.info.tuntap.fixed_config = 1; | |
333 | } else | |
334 | strcpy(lp->tp.info.tuntap.dev_name, TRANSPORT_TUNTAP_NAME); | |
335 | ||
336 | ||
337 | #if 0 | |
338 | if (setup_etheraddr(mac_str, lp->mac)) | |
339 | lp->have_mac = 1; | |
340 | #endif | |
341 | lp->mtu = TRANSPORT_TUNTAP_MTU; | |
342 | ||
343 | //lp->info.tuntap.gate_addr = gate_addr; | |
344 | ||
345 | lp->tp.info.tuntap.fd = -1; | |
346 | ||
347 | lp->tp.open = tuntap_open; | |
348 | lp->tp.close = tuntap_close; | |
349 | lp->tp.read = tuntap_read; | |
350 | lp->tp.write = tuntap_write; | |
351 | lp->tp.protocol = tuntap_protocol; | |
352 | lp->tp.poll = tuntap_poll; | |
353 | ||
354 | printk("TUN/TAP backend - "); | |
355 | #if 0 | |
356 | if (lp->host.gate_addr != NULL) | |
357 | printk("IP = %s", lp->host.gate_addr); | |
358 | #endif | |
359 | printk("\n"); | |
360 | ||
361 | return 1; | |
362 | } | |
363 | ||
364 | /* ================================ ISS NET ================================ */ | |
365 | ||
366 | static int iss_net_rx(struct net_device *dev) | |
367 | { | |
368 | struct iss_net_private *lp = dev->priv; | |
369 | int pkt_len; | |
370 | struct sk_buff *skb; | |
371 | ||
372 | /* Check if there is any new data. */ | |
373 | ||
374 | if (lp->tp.poll(lp) == 0) | |
375 | return 0; | |
376 | ||
377 | /* Try to allocate memory, if it fails, try again next round. */ | |
378 | ||
379 | if ((skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER)) == NULL) { | |
380 | lp->stats.rx_dropped++; | |
381 | return 0; | |
382 | } | |
383 | ||
384 | skb_reserve(skb, 2); | |
385 | ||
386 | /* Setup skb */ | |
387 | ||
388 | skb->dev = dev; | |
459a98ed | 389 | skb_reset_mac_header(skb); |
7282bee7 CZ |
390 | pkt_len = lp->tp.read(lp, &skb); |
391 | skb_put(skb, pkt_len); | |
392 | ||
393 | if (pkt_len > 0) { | |
394 | skb_trim(skb, pkt_len); | |
395 | skb->protocol = lp->tp.protocol(skb); | |
396 | // netif_rx(skb); | |
397 | netif_rx_ni(skb); | |
398 | ||
399 | lp->stats.rx_bytes += skb->len; | |
400 | lp->stats.rx_packets++; | |
401 | return pkt_len; | |
402 | } | |
403 | kfree_skb(skb); | |
404 | return pkt_len; | |
405 | } | |
406 | ||
407 | static int iss_net_poll(void) | |
408 | { | |
409 | struct list_head *ele; | |
410 | int err, ret = 0; | |
411 | ||
412 | spin_lock(&opened_lock); | |
413 | ||
414 | list_for_each(ele, &opened) { | |
415 | struct iss_net_private *lp; | |
416 | ||
417 | lp = list_entry(ele, struct iss_net_private, opened_list); | |
418 | ||
419 | if (!netif_running(lp->dev)) | |
420 | break; | |
421 | ||
422 | spin_lock(&lp->lock); | |
423 | ||
424 | while ((err = iss_net_rx(lp->dev)) > 0) | |
425 | ret++; | |
426 | ||
427 | spin_unlock(&lp->lock); | |
428 | ||
429 | if (err < 0) { | |
430 | printk(KERN_ERR "Device '%s' read returned %d, " | |
431 | "shutting it down\n", lp->dev->name, err); | |
432 | dev_close(lp->dev); | |
433 | } else { | |
434 | // FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ); | |
435 | } | |
436 | } | |
437 | ||
438 | spin_unlock(&opened_lock); | |
439 | return ret; | |
440 | } | |
441 | ||
442 | ||
443 | static void iss_net_timer(unsigned long priv) | |
444 | { | |
445 | struct iss_net_private* lp = (struct iss_net_private*) priv; | |
446 | ||
447 | spin_lock(&lp->lock); | |
448 | ||
449 | iss_net_poll(); | |
450 | ||
451 | mod_timer(&lp->timer, jiffies + lp->timer_val); | |
452 | ||
453 | spin_unlock(&lp->lock); | |
454 | } | |
455 | ||
456 | ||
457 | static int iss_net_open(struct net_device *dev) | |
458 | { | |
459 | struct iss_net_private *lp = dev->priv; | |
460 | char addr[sizeof "255.255.255.255\0"]; | |
461 | int err; | |
462 | ||
463 | spin_lock(&lp->lock); | |
464 | ||
465 | if ((err = lp->tp.open(lp)) < 0) | |
466 | goto out; | |
467 | ||
468 | if (!lp->have_mac) { | |
469 | dev_ip_addr(dev, addr, &lp->mac[2]); | |
470 | set_ether_mac(dev, lp->mac); | |
471 | } | |
472 | ||
473 | netif_start_queue(dev); | |
474 | ||
475 | /* clear buffer - it can happen that the host side of the interface | |
4af410a8 | 476 | * is full when we get here. In this case, new data is never queued, |
7282bee7 CZ |
477 | * SIGIOs never arrive, and the net never works. |
478 | */ | |
479 | while ((err = iss_net_rx(dev)) > 0) | |
480 | ; | |
481 | ||
482 | spin_lock(&opened_lock); | |
483 | list_add(&lp->opened_list, &opened); | |
484 | spin_unlock(&opened_lock); | |
485 | ||
486 | init_timer(&lp->timer); | |
487 | lp->timer_val = ISS_NET_TIMER_VALUE; | |
488 | lp->timer.data = (unsigned long) lp; | |
489 | lp->timer.function = iss_net_timer; | |
490 | mod_timer(&lp->timer, jiffies + lp->timer_val); | |
491 | ||
492 | out: | |
493 | spin_unlock(&lp->lock); | |
494 | return err; | |
495 | } | |
496 | ||
497 | static int iss_net_close(struct net_device *dev) | |
498 | { | |
499 | struct iss_net_private *lp = dev->priv; | |
500 | printk("iss_net_close!\n"); | |
501 | netif_stop_queue(dev); | |
502 | spin_lock(&lp->lock); | |
503 | ||
504 | spin_lock(&opened_lock); | |
505 | list_del(&opened); | |
506 | spin_unlock(&opened_lock); | |
507 | ||
508 | del_timer_sync(&lp->timer); | |
509 | ||
510 | lp->tp.close(lp); | |
511 | ||
512 | spin_unlock(&lp->lock); | |
513 | return 0; | |
514 | } | |
515 | ||
516 | static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev) | |
517 | { | |
518 | struct iss_net_private *lp = dev->priv; | |
519 | unsigned long flags; | |
520 | int len; | |
521 | ||
522 | netif_stop_queue(dev); | |
523 | spin_lock_irqsave(&lp->lock, flags); | |
524 | ||
525 | len = lp->tp.write(lp, &skb); | |
526 | ||
527 | if (len == skb->len) { | |
528 | lp->stats.tx_packets++; | |
529 | lp->stats.tx_bytes += skb->len; | |
530 | dev->trans_start = jiffies; | |
531 | netif_start_queue(dev); | |
532 | ||
533 | /* this is normally done in the interrupt when tx finishes */ | |
534 | netif_wake_queue(dev); | |
535 | ||
536 | } else if (len == 0) { | |
537 | netif_start_queue(dev); | |
538 | lp->stats.tx_dropped++; | |
539 | ||
540 | } else { | |
541 | netif_start_queue(dev); | |
542 | printk(KERN_ERR "iss_net_start_xmit: failed(%d)\n", len); | |
543 | } | |
544 | ||
545 | spin_unlock_irqrestore(&lp->lock, flags); | |
546 | ||
547 | dev_kfree_skb(skb); | |
548 | return 0; | |
549 | } | |
550 | ||
551 | ||
552 | static struct net_device_stats *iss_net_get_stats(struct net_device *dev) | |
553 | { | |
554 | struct iss_net_private *lp = dev->priv; | |
555 | return &lp->stats; | |
556 | } | |
557 | ||
558 | static void iss_net_set_multicast_list(struct net_device *dev) | |
559 | { | |
560 | #if 0 | |
561 | if (dev->flags & IFF_PROMISC) | |
562 | return; | |
563 | else if (dev->mc_count) | |
564 | dev->flags |= IFF_ALLMULTI; | |
565 | else | |
566 | dev->flags &= ~IFF_ALLMULTI; | |
567 | #endif | |
568 | } | |
569 | ||
570 | static void iss_net_tx_timeout(struct net_device *dev) | |
571 | { | |
572 | #if 0 | |
573 | dev->trans_start = jiffies; | |
574 | netif_wake_queue(dev); | |
575 | #endif | |
576 | } | |
577 | ||
578 | static int iss_net_set_mac(struct net_device *dev, void *addr) | |
579 | { | |
580 | #if 0 | |
581 | struct iss_net_private *lp = dev->priv; | |
582 | struct sockaddr *hwaddr = addr; | |
583 | ||
584 | spin_lock(&lp->lock); | |
585 | memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); | |
586 | spin_unlock(&lp->lock); | |
587 | #endif | |
588 | ||
589 | return 0; | |
590 | } | |
591 | ||
592 | static int iss_net_change_mtu(struct net_device *dev, int new_mtu) | |
593 | { | |
594 | #if 0 | |
595 | struct iss_net_private *lp = dev->priv; | |
596 | int err = 0; | |
597 | ||
598 | spin_lock(&lp->lock); | |
599 | ||
600 | // FIXME not needed new_mtu = transport_set_mtu(new_mtu, &lp->user); | |
601 | ||
602 | if (new_mtu < 0) | |
603 | err = new_mtu; | |
604 | else | |
605 | dev->mtu = new_mtu; | |
606 | ||
607 | spin_unlock(&lp->lock); | |
608 | return err; | |
609 | #endif | |
610 | return -EINVAL; | |
611 | } | |
612 | ||
7282bee7 CZ |
613 | void iss_net_user_timer_expire(unsigned long _conn) |
614 | { | |
615 | } | |
616 | ||
617 | ||
3ae5eaec RK |
618 | static struct platform_driver iss_net_driver = { |
619 | .driver = { | |
620 | .name = DRIVER_NAME, | |
621 | }, | |
7282bee7 CZ |
622 | }; |
623 | ||
624 | static int driver_registered; | |
625 | ||
626 | static int iss_net_configure(int index, char *init) | |
627 | { | |
628 | struct net_device *dev; | |
629 | struct iss_net_private *lp; | |
630 | int err; | |
631 | ||
632 | if ((dev = alloc_etherdev(sizeof *lp)) == NULL) { | |
633 | printk(KERN_ERR "eth_configure: failed to allocate device\n"); | |
634 | return 1; | |
635 | } | |
636 | ||
637 | /* Initialize private element. */ | |
638 | ||
639 | lp = dev->priv; | |
640 | *lp = ((struct iss_net_private) { | |
641 | .device_list = LIST_HEAD_INIT(lp->device_list), | |
642 | .opened_list = LIST_HEAD_INIT(lp->opened_list), | |
643 | .lock = SPIN_LOCK_UNLOCKED, | |
644 | .dev = dev, | |
645 | .index = index, | |
646 | //.fd = -1, | |
647 | .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 }, | |
648 | .have_mac = 0, | |
649 | }); | |
650 | ||
651 | /* | |
652 | * Try all transport protocols. | |
653 | * Note: more protocols can be added by adding '&& !X_init(lp, eth)'. | |
654 | */ | |
655 | ||
656 | if (!tuntap_probe(lp, index, init)) { | |
657 | printk("Invalid arguments. Skipping device!\n"); | |
658 | goto errout; | |
659 | } | |
660 | ||
661 | printk(KERN_INFO "Netdevice %d ", index); | |
662 | if (lp->have_mac) | |
663 | printk("(%02x:%02x:%02x:%02x:%02x:%02x) ", | |
664 | lp->mac[0], lp->mac[1], | |
665 | lp->mac[2], lp->mac[3], | |
666 | lp->mac[4], lp->mac[5]); | |
667 | printk(": "); | |
668 | ||
669 | /* sysfs register */ | |
670 | ||
671 | if (!driver_registered) { | |
3ae5eaec | 672 | platform_driver_register(&iss_net_driver); |
7282bee7 CZ |
673 | driver_registered = 1; |
674 | } | |
675 | ||
676 | spin_lock(&devices_lock); | |
677 | list_add(&lp->device_list, &devices); | |
678 | spin_unlock(&devices_lock); | |
679 | ||
680 | lp->pdev.id = index; | |
681 | lp->pdev.name = DRIVER_NAME; | |
682 | platform_device_register(&lp->pdev); | |
683 | SET_NETDEV_DEV(dev,&lp->pdev.dev); | |
684 | ||
685 | /* | |
686 | * If this name ends up conflicting with an existing registered | |
687 | * netdevice, that is OK, register_netdev{,ice}() will notice this | |
688 | * and fail. | |
689 | */ | |
690 | snprintf(dev->name, sizeof dev->name, "eth%d", index); | |
691 | ||
692 | dev->mtu = lp->mtu; | |
693 | dev->open = iss_net_open; | |
694 | dev->hard_start_xmit = iss_net_start_xmit; | |
695 | dev->stop = iss_net_close; | |
696 | dev->get_stats = iss_net_get_stats; | |
697 | dev->set_multicast_list = iss_net_set_multicast_list; | |
698 | dev->tx_timeout = iss_net_tx_timeout; | |
699 | dev->set_mac_address = iss_net_set_mac; | |
700 | dev->change_mtu = iss_net_change_mtu; | |
7282bee7 CZ |
701 | dev->watchdog_timeo = (HZ >> 1); |
702 | dev->irq = -1; | |
703 | ||
704 | rtnl_lock(); | |
705 | err = register_netdevice(dev); | |
706 | rtnl_unlock(); | |
707 | ||
708 | if (err) { | |
709 | printk("Error registering net device!\n"); | |
710 | /* XXX: should we call ->remove() here? */ | |
711 | free_netdev(dev); | |
712 | return 1; | |
713 | } | |
714 | ||
715 | init_timer(&lp->tl); | |
716 | lp->tl.function = iss_net_user_timer_expire; | |
717 | ||
718 | #if 0 | |
719 | if (lp->have_mac) | |
720 | set_ether_mac(dev, lp->mac); | |
721 | #endif | |
722 | return 0; | |
723 | ||
724 | errout: | |
725 | // FIXME: unregister; free, etc.. | |
726 | return -EIO; | |
727 | ||
728 | } | |
729 | ||
730 | /* ------------------------------------------------------------------------- */ | |
731 | ||
732 | /* Filled in during early boot */ | |
733 | ||
734 | struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line); | |
735 | ||
736 | struct iss_net_init { | |
737 | struct list_head list; | |
738 | char *init; /* init string */ | |
739 | int index; | |
740 | }; | |
741 | ||
742 | /* | |
743 | * Parse the command line and look for 'ethX=...' fields, and register all | |
744 | * those fields. They will be later initialized in iss_net_init. | |
745 | */ | |
746 | ||
747 | #define ERR KERN_ERR "iss_net_setup: " | |
748 | ||
749 | static int iss_net_setup(char *str) | |
750 | { | |
751 | struct iss_net_private *device = NULL; | |
752 | struct iss_net_init *new; | |
753 | struct list_head *ele; | |
754 | char *end; | |
755 | int n; | |
756 | ||
757 | n = simple_strtoul(str, &end, 0); | |
758 | if (end == str) { | |
759 | printk(ERR "Failed to parse '%s'\n", str); | |
760 | return 1; | |
761 | } | |
762 | if (n < 0) { | |
763 | printk(ERR "Device %d is negative\n", n); | |
764 | return 1; | |
765 | } | |
766 | if (*(str = end) != '=') { | |
767 | printk(ERR "Expected '=' after device number\n"); | |
768 | return 1; | |
769 | } | |
770 | ||
771 | spin_lock(&devices_lock); | |
772 | ||
773 | list_for_each(ele, &devices) { | |
774 | device = list_entry(ele, struct iss_net_private, device_list); | |
775 | if (device->index == n) | |
776 | break; | |
777 | } | |
778 | ||
779 | spin_unlock(&devices_lock); | |
780 | ||
781 | if (device && device->index == n) { | |
782 | printk(ERR "Device %d already configured\n", n); | |
783 | return 1; | |
784 | } | |
785 | ||
786 | if ((new = alloc_bootmem(sizeof new)) == NULL) { | |
787 | printk("Alloc_bootmem failed\n"); | |
788 | return 1; | |
789 | } | |
790 | ||
791 | INIT_LIST_HEAD(&new->list); | |
792 | new->index = n; | |
793 | new->init = str + 1; | |
794 | ||
795 | list_add_tail(&new->list, ð_cmd_line); | |
796 | return 1; | |
797 | } | |
798 | ||
799 | #undef ERR | |
800 | ||
0a8141e2 | 801 | __setup("eth=", iss_net_setup); |
7282bee7 CZ |
802 | |
803 | /* | |
804 | * Initialize all ISS Ethernet devices previously registered in iss_net_setup. | |
805 | */ | |
806 | ||
807 | static int iss_net_init(void) | |
808 | { | |
809 | struct list_head *ele, *next; | |
810 | ||
811 | /* Walk through all Ethernet devices specified in the command line. */ | |
812 | ||
813 | list_for_each_safe(ele, next, ð_cmd_line) { | |
814 | struct iss_net_init *eth; | |
815 | eth = list_entry(ele, struct iss_net_init, list); | |
816 | iss_net_configure(eth->index, eth->init); | |
817 | } | |
818 | ||
819 | return 1; | |
820 | } | |
821 | ||
822 | module_init(iss_net_init); | |
823 |