Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 /*
  2  * snull.c --  the Simple Network Utility
  3  *
  4  * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
  5  * Copyright (C) 2001 O'Reilly & Associates
  6  *
  7  * The source code in this file can be freely used, adapted,
  8  * and redistributed in source or binary form, so long as an
  9  * acknowledgment appears in derived source files.  The citation
 10  * should list that the code comes from the book "Linux Device
 11  * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 12  * by O'Reilly & Associates.   No warranty is attached;
 13  * we cannot take responsibility for errors or fitness for use.
 14  *
 15  * $Id: snull.c,v 1.21 2004/11/05 02:36:03 rubini Exp $
 16  */
 17 
 18 /* I have made an attempt to bring this code up to kernel 2.6.25,
 19  * but am not certain it works, and am less convinced it is still
 20  * a good example of how to write a network device driver.
 21  * It seems that circa kernel 2.6.24 the NAPI interfaces were
 22  * changed.  Responsibility for managing the quote was move
 23  * from individual drivers' poll functions to shared code.
 24  * There also seems to be some new NAPI registration stuff.
 25  * See http://lwn.net/Articles/244640/ for more info.
 26  */
 27 
 28 #include <linux/module.h>
 29 #include <linux/init.h>
 30 #include <linux/moduleparam.h>
 31 
 32 #include <linux/sched.h>
 33 #include <linux/kernel.h> /* printk() */
 34 #include <linux/slab.h> /* kmalloc() */
 35 #include <linux/errno.h>  /* error codes */
 36 #include <linux/types.h>  /* size_t */
 37 #include <linux/interrupt.h> /* mark_bh */
 38 
 39 #include <linux/in.h>
 40 #include <linux/netdevice.h>   /* struct device, and other headers */
 41 #include <linux/etherdevice.h> /* eth_type_trans */
 42 #include <linux/ip.h>          /* struct iphdr */
 43 #include <linux/tcp.h>         /* struct tcphdr */
 44 #include <linux/skbuff.h>
 45 
 46 #include "snull.h"
 47 
 48 #include <linux/in6.h>
 49 #include <asm/checksum.h>
 50 
 51 MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
 52 MODULE_LICENSE("Dual BSD/GPL");
 53 
 54 
 55 /*
 56  * Transmitter lockup simulation, normally disabled.
 57  */
 58 static int lockup = 0;
 59 module_param(lockup, int, 0);
 60 
 61 static int timeout = SNULL_TIMEOUT;
 62 module_param(timeout, int, 0);
 63 
 64 /*
 65  * Do we run in NAPI mode?
 66  */
 67 static int use_napi = 0;
 68 module_param(use_napi, int, 0);
 69 
 70 
 71 /*
 72  * A structure representing an in-flight packet.
 73  */
 74 struct snull_packet {
 75         struct snull_packet *next;
 76         struct net_device *dev;
 77         int     datalen;
 78         u8 data[ETH_DATA_LEN];
 79 };
 80 
 81 int pool_size = 8;
 82 module_param(pool_size, int, 0);
 83 
 84 /*
 85  * This structure is private to each device. It is used to pass
 86  * packets in and out, so there is place for a packet
 87  */
 88 
 89 struct snull_priv {
 90         struct net_device_stats stats;
 91         int status;
 92         struct snull_packet *ppool;
 93         struct snull_packet *rx_queue;  /* List of incoming packets */
 94         int rx_int_enabled;
 95         int tx_packetlen;
 96         u8 *tx_packetdata;
 97         struct sk_buff *skb;
 98         spinlock_t lock;
 99         struct net_device *dev;
100         struct napi_struct napi;
101   /* Consider creating new struct for snull device, and putting
102    *  the struct net_dev in here.
103    */
104 };
105 
106 static void snull_tx_timeout(struct net_device *dev);
107 static void (*snull_interrupt)(int, void *, struct pt_regs *);
108 
109 /*
110  * Set up a device's packet pool.
111  */
112 void snull_setup_pool(struct net_device *dev)
113 {
114         struct snull_priv *priv = netdev_priv(dev);
115         int i;
116         struct snull_packet *pkt;
117 
118         priv->ppool = NULL;
119         for (i = 0; i < pool_size; i++) {
120                 pkt = kmalloc (sizeof (struct snull_packet), GFP_KERNEL);
121                 if (pkt == NULL) {
122                         printk (KERN_NOTICE "Ran out of memory allocating packet pool\n");
123                         return;
124                 }
125                 pkt->dev = dev;
126                 pkt->next = priv->ppool;
127                 priv->ppool = pkt;
128         }
129 }
130 
131 void snull_teardown_pool(struct net_device *dev)
132 {
133         struct snull_priv *priv = netdev_priv(dev);
134         struct snull_packet *pkt;
135     
136         while ((pkt = priv->ppool)) {
137                 priv->ppool = pkt->next;
138                 kfree (pkt);
139                 /* FIXME - in-flight packets ? */
140         }
141 }    
142 
143 /*
144  * Buffer/pool management.
145  */
146 struct snull_packet *snull_get_tx_buffer(struct net_device *dev)
147 {
148         struct snull_priv *priv = netdev_priv(dev);
149         unsigned long flags;
150         struct snull_packet *pkt;
151     
152         spin_lock_irqsave(&priv->lock, flags);
153         pkt = priv->ppool;
154         priv->ppool = pkt->next;
155         if (priv->ppool == NULL) {
156                 printk (KERN_INFO "Pool empty\n");
157                 netif_stop_queue(dev);
158         }
159         spin_unlock_irqrestore(&priv->lock, flags);
160         return pkt;
161 }
162 
163 
164 void snull_release_buffer(struct snull_packet *pkt)
165 {
166         unsigned long flags;
167         struct snull_priv *priv = netdev_priv(pkt->dev);
168         
169         spin_lock_irqsave(&priv->lock, flags);
170         pkt->next = priv->ppool;
171         priv->ppool = pkt;
172         spin_unlock_irqrestore(&priv->lock, flags);
173         if (netif_queue_stopped(pkt->dev) && pkt->next == NULL)
174                 netif_wake_queue(pkt->dev);
175 }
176 
177 void snull_enqueue_buf(struct net_device *dev, struct snull_packet *pkt)
178 {
179         unsigned long flags;
180         struct snull_priv *priv = netdev_priv(dev);
181 
182         spin_lock_irqsave(&priv->lock, flags);
183         pkt->next = priv->rx_queue;  /* FIXME - misorders packets */
184         priv->rx_queue = pkt;
185         spin_unlock_irqrestore(&priv->lock, flags);
186 }
187 
188 struct snull_packet *snull_dequeue_buf(struct net_device *dev)
189 {
190         struct snull_priv *priv = netdev_priv(dev);
191         struct snull_packet *pkt;
192         unsigned long flags;
193 
194         spin_lock_irqsave(&priv->lock, flags);
195         pkt = priv->rx_queue;
196         if (pkt != NULL)
197                 priv->rx_queue = pkt->next;
198         spin_unlock_irqrestore(&priv->lock, flags);
199         return pkt;
200 }
201 
202 /*
203  * Enable and disable receive interrupts.
204  */
205 static void snull_rx_ints(struct net_device *dev, int enable)
206 {
207         struct snull_priv *priv = netdev_priv(dev);
208         priv->rx_int_enabled = enable;
209 }
210 
211     
212 /*
213  * Open and close
214  */
215 
216 int snull_open(struct net_device *dev)
217 {
218         /* request_region(), request_irq(), ....  (like fops->open) */
219 
220         /* 
221          * Assign the hardware address of the board: use "\0SNULx", where
222          * x is 0 or 1. The first byte is '\0' to avoid being a multicast
223          * address (the first byte of multicast addrs is odd).
224          */
225         memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN);
226         if (dev == snull_devs[1])
227                 dev->dev_addr[ETH_ALEN-1]++; /* \0SNUL1 */
228         netif_start_queue(dev);
229         return 0;
230 }
231 
232 int snull_release(struct net_device *dev)
233 {
234     /* release ports, irq and such -- like fops->close */
235 
236         netif_stop_queue(dev); /* can't transmit any more */
237         return 0;
238 }
239 
240 /*
241  * Configuration changes (passed on by ifconfig)
242  */
243 int snull_config(struct net_device *dev, struct ifmap *map)
244 {
245         if (dev->flags & IFF_UP) /* can't act on a running interface */
246                 return -EBUSY;
247 
248         /* Don't allow changing the I/O address */
249         if (map->base_addr != dev->base_addr) {
250                 printk(KERN_WARNING "snull: Can't change I/O address\n");
251                 return -EOPNOTSUPP;
252         }
253 
254         /* Allow changing the IRQ */
255         if (map->irq != dev->irq) {
256                 dev->irq = map->irq;
257                 /* request_irq() is delayed to open-time */
258         }
259 
260         /* ignore other fields */
261         return 0;
262 }
263 
264 /*
265  * Receive a packet: retrieve, encapsulate and pass over to upper levels
266  */
267 void snull_rx(struct net_device *dev, struct snull_packet *pkt)
268 {
269         struct sk_buff *skb;
270         struct snull_priv *priv = netdev_priv(dev);
271 
272         /*
273          * The packet has been retrieved from the transmission
274          * medium. Build an skb around it, so upper layers can handle it
275          */
276         skb = dev_alloc_skb(pkt->datalen + 2);
277         if (!skb) {
278                 if (printk_ratelimit())
279                         printk(KERN_NOTICE "snull rx: low on mem - packet dropped\n");
280                 priv->stats.rx_dropped++;
281                 goto out;
282         }
283         skb_reserve(skb, 2); /* align IP on 16B boundary */  
284         memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
285 
286         /* Write metadata, and then pass to the receive level */
287         skb->dev = dev;
288         skb->protocol = eth_type_trans(skb, dev);
289         skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
290         priv->stats.rx_packets++;
291         priv->stats.rx_bytes += pkt->datalen;
292         netif_rx(skb);
293   out:
294         return;
295 }
296     
297 
298 /*
299  * The poll implementation.
300  */
301 static int snull_poll(struct napi_struct *napi, int budget)
302 {
303         int npackets = 0;
304         struct sk_buff *skb;
305         struct snull_priv  *priv = container_of(napi, struct snull_priv, napi);
306         struct net_device  *dev =  priv->dev;
307         struct snull_packet *pkt;
308     
309         while (npackets < budget && priv->rx_queue) {
310                 pkt = snull_dequeue_buf(dev);
311                 skb = dev_alloc_skb(pkt->datalen + 2);
312                 if (! skb) {
313                         if (printk_ratelimit())
314                                 printk(KERN_NOTICE "snull: packet dropped\n");
315                         priv->stats.rx_dropped++;
316                         snull_release_buffer(pkt);
317                         continue;
318                 }
319                 skb_reserve(skb, 2); /* align IP on 16B boundary */  
320                 memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
321                 skb->dev = dev;
322                 skb->protocol = eth_type_trans(skb, dev);
323                 skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
324                 netif_receive_skb(skb);
325                 /* Maintain stats */
326                 npackets++;
327                 priv->stats.rx_packets++;
328                 priv->stats.rx_bytes += pkt->datalen;
329                 snull_release_buffer(pkt);
330         }
331         /* If we processed all packets, we're done; tell the kernel and reenable ints */
332         if (npackets < budget) {
333                 netif_rx_complete(dev, napi);
334                 snull_rx_ints(dev, 1);
335         }
336         return npackets;
337 }
338             
339         
340 /*
341  * The typical interrupt entry point
342  */
343 static void snull_regular_interrupt(int irq, void *dev_id, struct pt_regs *regs)
344 {
345         int statusword;
346         struct snull_priv *priv;
347         struct snull_packet *pkt = NULL;
348         /*
349          * As usual, check the "device" pointer to be sure it is
350          * really interrupting.
351          * Then assign "struct device *dev"
352          */
353         struct net_device *dev = (struct net_device *)dev_id;
354         /* ... and check with hw if it's really ours */
355 
356         /* paranoid */
357         if (!dev)
358                 return;
359 
360         /* Lock the device */
361         priv = netdev_priv(dev);
362         spin_lock(&priv->lock);
363 
364         /* retrieve statusword: real netdevices use I/O instructions */
365         statusword = priv->status;
366         priv->status = 0;
367         if (statusword & SNULL_RX_INTR) {
368                 /* send it to snull_rx for handling */
369                 pkt = priv->rx_queue;
370                 if (pkt) {
371                         priv->rx_queue = pkt->next;
372                         snull_rx(dev, pkt);
373                 }
374         }
375         if (statusword & SNULL_TX_INTR) {
376                 /* a transmission is over: free the skb */
377                 priv->stats.tx_packets++;
378                 priv->stats.tx_bytes += priv->tx_packetlen;
379                 dev_kfree_skb(priv->skb);
380         }
381 
382         /* Unlock the device and we are done */
383         spin_unlock(&priv->lock);
384         if (pkt) snull_release_buffer(pkt); /* Do this outside the lock! */
385         return;
386 }
387 
388 /*
389  * A NAPI interrupt handler.
390  */
391 static void snull_napi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
392 {
393         int statusword;
394         struct snull_priv *priv;
395 
396         /*
397          * As usual, check the "device" pointer for shared handlers.
398          * Then assign "struct device *dev"
399          */
400         struct net_device *dev = (struct net_device *)dev_id;
401         /* ... and check with hw if it's really ours */
402 
403         /* paranoid */
404         if (!dev)
405                 return;
406 
407         /* Lock the device */
408         priv = netdev_priv(dev);
409         spin_lock(&priv->lock);
410 
411         /* retrieve statusword: real netdevices use I/O instructions */
412         statusword = priv->status;
413         priv->status = 0;
414         if (statusword & SNULL_RX_INTR) {
415                 snull_rx_ints(dev, 0);  /* Disable further interrupts */
416                 netif_rx_schedule(dev, &(priv->napi));
417         }
418         if (statusword & SNULL_TX_INTR) {
419                 /* a transmission is over: free the skb */
420                 priv->stats.tx_packets++;
421                 priv->stats.tx_bytes += priv->tx_packetlen;
422                 dev_kfree_skb(priv->skb);
423         }
424 
425         /* Unlock the device and we are done */
426         spin_unlock(&priv->lock);
427         return;
428 }
429 
430 
431 
432 /*
433  * Transmit a packet (low level interface)
434  */
435 static void snull_hw_tx(char *buf, int len, struct net_device *dev)
436 {
437         /*
438          * This function deals with hw details. This interface loops
439          * back the packet to the other snull interface (if any).
440          * In other words, this function implements the snull behaviour,
441          * while all other procedures are rather device-independent
442          */
443         struct iphdr *ih;
444         struct net_device *dest;
445         struct snull_priv *priv;
446         u32 *saddr, *daddr;
447         struct snull_packet *tx_buffer;
448     
449         /* I am paranoid. Ain't I? */
450         if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {
451                 printk("snull: Hmm... packet too short (%i octets)\n",
452                                 len);
453                 return;
454         }
455 
456         if (0) { /* enable this conditional to look at the data */
457                 int i;
458                 PDEBUG("len is %i\n" KERN_DEBUG "data:",len);
459                 for (i=14 ; i<len; i++)
460                         printk(" %02x",buf[i]&0xff);
461                 printk("\n");
462         }
463         /*
464          * Ethhdr is 14 bytes, but the kernel arranges for iphdr
465          * to be aligned (i.e., ethhdr is unaligned)
466          */
467         ih = (struct iphdr *)(buf+sizeof(struct ethhdr));
468         saddr = &ih->saddr;
469         daddr = &ih->daddr;
470 
471         ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
472         ((u8 *)daddr)[2] ^= 1;
473 
474         ih->check = 0;         /* and rebuild the checksum (ip needs it) */
475         ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
476 
477         if (dev == snull_devs[0])
478                 PDEBUGG("%08x:%05i --> %08x:%05i\n",
479                                 ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source),
480                                 ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest));
481         else
482                 PDEBUGG("%08x:%05i <-- %08x:%05i\n",
483                                 ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest),
484                                 ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source));
485 
486         /*
487          * Ok, now the packet is ready for transmission: first simulate a
488          * receive interrupt on the twin device, then  a
489          * transmission-done on the transmitting device
490          */
491         dest = snull_devs[dev == snull_devs[0] ? 1 : 0];
492         priv = netdev_priv(dest);
493         tx_buffer = snull_get_tx_buffer(dev);
494         tx_buffer->datalen = len;
495         memcpy(tx_buffer->data, buf, len);
496         snull_enqueue_buf(dest, tx_buffer);
497         if (priv->rx_int_enabled) {
498                 priv->status |= SNULL_RX_INTR;
499                 snull_interrupt(0, dest, NULL);
500         }
501 
502         priv = netdev_priv(dev);
503         priv->tx_packetlen = len;
504         priv->tx_packetdata = buf;
505         priv->status |= SNULL_TX_INTR;
506         if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) {
507                 /* Simulate a dropped transmit interrupt */
508                 netif_stop_queue(dev);
509                 PDEBUG("Simulate lockup at %ld, txp %ld\n", jiffies,
510                                 (unsigned long) priv->stats.tx_packets);
511         }
512         else
513                 snull_interrupt(0, dev, NULL);
514 }
515 
516 /*
517  * Transmit a packet (called by the kernel)
518  */
519 int snull_tx(struct sk_buff *skb, struct net_device *dev)
520 {
521         int len;
522         char *data, shortpkt[ETH_ZLEN];
523         struct snull_priv *priv = netdev_priv(dev);
524         
525         data = skb->data;
526         len = skb->len;
527         if (len < ETH_ZLEN) {
528                 memset(shortpkt, 0, ETH_ZLEN);
529                 memcpy(shortpkt, skb->data, skb->len);
530                 len = ETH_ZLEN;
531                 data = shortpkt;
532         }
533         dev->trans_start = jiffies; /* save the timestamp */
534 
535         /* Remember the skb, so we can free it at interrupt time */
536         priv->skb = skb;
537 
538         /* actual deliver of data is device-specific, and not shown here */
539         snull_hw_tx(data, len, dev);
540 
541         return 0; /* Our simple device can not fail */
542 }
543 
544 /*
545  * Deal with a transmit timeout.
546  */
547 void snull_tx_timeout (struct net_device *dev)
548 {
549         struct snull_priv *priv = netdev_priv(dev);
550 
551         PDEBUG("Transmit timeout at %ld, latency %ld\n", jiffies,
552                         jiffies - dev->trans_start);
553         /* Simulate a transmission interrupt to get things moving */
554         priv->status = SNULL_TX_INTR;
555         snull_interrupt(0, dev, NULL);
556         priv->stats.tx_errors++;
557         netif_wake_queue(dev);
558         return;
559 }
560 
561 
562 
563 /*
564  * Ioctl commands 
565  */
566 int snull_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
567 {
568         PDEBUG("ioctl\n");
569         return 0;
570 }
571 
572 /*
573  * Return statistics to the caller
574  */
575 struct net_device_stats *snull_stats(struct net_device *dev)
576 {
577         struct snull_priv *priv = netdev_priv(dev);
578         return &priv->stats;
579 }
580 
581 /*
582  * This function is called to fill up an eth header, since arp is not
583  * available on the interface
584  */
585 int snull_rebuild_header(struct sk_buff *skb)
586 {
587         struct ethhdr *eth = (struct ethhdr *) skb->data;
588         struct net_device *dev = skb->dev;
589     
590         memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
591         memcpy(eth->h_dest, dev->dev_addr, dev->addr_len);
592         eth->h_dest[ETH_ALEN-1]   ^= 0x01;   /* dest is us xor 1 */
593         return 0;
594 }
595 
596 
597 int snull_header(struct sk_buff *skb, struct net_device *dev,
598                  unsigned short type, const void *daddr, const void *saddr,
599                 unsigned int len)
600 {
601         struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
602 
603         eth->h_proto = htons(type);
604         memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
605         memcpy(eth->h_dest,   daddr ? daddr : dev->dev_addr, dev->addr_len);
606         eth->h_dest[ETH_ALEN-1]   ^= 0x01;   /* dest is us xor 1 */
607         return (dev->hard_header_len);
608 }
609 
610 
611 
612 
613 
614 /*
615  * The "change_mtu" method is usually not needed.
616  * If you need it, it must be like this.
617  */
618 int snull_change_mtu(struct net_device *dev, int new_mtu)
619 {
620         unsigned long flags;
621         struct snull_priv *priv = netdev_priv(dev);
622         spinlock_t *lock = &priv->lock;
623     
624         /* check ranges */
625         if ((new_mtu < 68) || (new_mtu > 1500))
626                 return -EINVAL;
627         /*
628          * Do anything you need, and the accept the value
629          */
630         spin_lock_irqsave(lock, flags);
631         dev->mtu = new_mtu;
632         spin_unlock_irqrestore(lock, flags);
633         return 0; /* success */
634 }
635 
636 static const struct header_ops snull_header_ops = {
637         .create  = snull_header,
638         .rebuild = snull_rebuild_header,
639         .cache   = NULL,  /* disable caching */
640 };
641 
642 /*
643  * The init function (sometimes called probe).
644  * It is invoked by register_netdev()
645  */
646 void snull_init(struct net_device *dev)
647 {
648         struct snull_priv *priv;
649 #if 0
650         /*
651          * Make the usual checks: check_region(), probe irq, ...  -ENODEV
652          * should be returned if no device found.  No resource should be
653          * grabbed: this is done on open(). 
654          */
655 #endif
656 
657         /* 
658          * Then, assign other fields in dev, using ether_setup() and some
659          * hand assignments
660          */
661         ether_setup(dev); /* assign some of the fields */
662 
663         dev->open            = snull_open;
664         dev->stop            = snull_release;
665         dev->set_config      = snull_config;
666         dev->hard_start_xmit = snull_tx;
667         dev->do_ioctl        = snull_ioctl;
668         dev->get_stats       = snull_stats;
669         dev->change_mtu      = snull_change_mtu;  
670         dev->header_ops      = &snull_header_ops;
671         dev->tx_timeout      = snull_tx_timeout;
672         dev->watchdog_timeo = timeout;
673         /* keep the default flags, just add NOARP */
674         dev->flags           |= IFF_NOARP;
675         dev->features        |= NETIF_F_NO_CSUM;
676         /*
677          * Then, initialize the priv field. This encloses the statistics
678          * and a few private fields.
679          */
680         priv = netdev_priv(dev);
681         memset(priv, 0, sizeof(struct snull_priv));
682         priv->dev = dev;
683         netif_napi_add(dev, &priv->napi, snull_poll, 2);
684         /* The last parameter above is the NAPI "weight". */
685         spin_lock_init(&priv->lock);
686         snull_rx_ints(dev, 1);          /* enable receive interrupts */
687         snull_setup_pool(dev);
688 }
689 
690 /*
691  * The devices
692  */
693 
694 struct net_device *snull_devs[2];
695 
696 
697 
698 /*
699  * Finally, the module stuff
700  */
701 
702 void snull_cleanup(void)
703 {
704         int i;
705     
706         for (i = 0; i < 2;  i++) {
707                 if (snull_devs[i]) {
708                         unregister_netdev(snull_devs[i]);
709                         snull_teardown_pool(snull_devs[i]);
710                         free_netdev(snull_devs[i]);
711                 }
712         }
713         return;
714 }
715 
716 
717 
718 
719 int snull_init_module(void)
720 {
721         int result, i, ret = -ENOMEM;
722 
723         snull_interrupt = use_napi ? snull_napi_interrupt : snull_regular_interrupt;
724 
725         /* Allocate the devices */
726         snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
727                         snull_init);
728         snull_devs[1] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
729                         snull_init);
730         if (snull_devs[0] == NULL || snull_devs[1] == NULL)
731                 goto out;
732 
733         ret = -ENODEV;
734         for (i = 0; i < 2;  i++)
735                 if ((result = register_netdev(snull_devs[i])))
736                         printk("snull: error %i registering device \"%s\"\n",
737                                         result, snull_devs[i]->name);
738                 else
739                         ret = 0;
740    out:
741         if (ret) 
742                 snull_cleanup();
743         return ret;
744 }
745 
746 
747 module_init(snull_init_module);
748 module_exit(snull_cleanup);
749 
  This page was automatically generated by the LXR engine.