📄 snull.c
字号:
/* * snull.c -- the Simple Network Utility * * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet * Copyright (C) 2001 O'Reilly & Associates * * The source code in this file can be freely used, adapted, * and redistributed in source or binary form, so long as an * acknowledgment appears in derived source files. The citation * should list that the code comes from the book "Linux Device * Drivers" by Alessandro Rubini and Jonathan Corbet, published * by O'Reilly & Associates. No warranty is attached; * we cannot take responsibility for errors or fitness for use. * * $Id: snull.c,v 1.17 2001/07/18 22:28:18 rubini Exp $ */#ifndef __KERNEL__# define __KERNEL__#endif#ifndef MODULE# define MODULE#endif#include <linux/config.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/kernel.h> /* printk() */#include <linux/malloc.h> /* kmalloc() */#include <linux/errno.h> /* error codes */#include <linux/types.h> /* size_t */#include <linux/interrupt.h> /* mark_bh */#include <linux/in.h>#include <linux/netdevice.h> /* struct device, and other headers */#include <linux/etherdevice.h> /* eth_type_trans */#include <linux/ip.h> /* struct iphdr */#include <linux/tcp.h> /* struct tcphdr */#include <linux/skbuff.h>#include "snull.h"#ifdef LINUX_20# include <linux/if_ether.h># define net_device_stats enet_statistics#else# include <linux/in6.h>#endif#include <asm/checksum.h>MODULE_AUTHOR("Alessandro Rubini");/* This is a load-time options */static int eth = 0; /* Call yourself "ethX". Default is "sn0"/"sn1" */MODULE_PARM(eth, "i");/* * Transmitter lockup simulation, normally disabled. */static int lockup = 0;MODULE_PARM(lockup, "i");#ifdef HAVE_TX_TIMEOUTstatic int timeout = SNULL_TIMEOUT;MODULE_PARM(timeout, "i");#endifint snull_eth;/* * This structure is private to each device. It is used to pass * packets in and out, so there is place for a packet */struct snull_priv { struct net_device_stats stats; int status; int rx_packetlen; u8 *rx_packetdata; int tx_packetlen; u8 *tx_packetdata; struct sk_buff *skb; spinlock_t lock;};extern struct net_device snull_devs[];void snull_tx_timeout (struct net_device *dev); /* * Open and close */int snull_open(struct net_device *dev){ MOD_INC_USE_COUNT; /* request_region(), request_irq(), .... (like fops->open) */#if 0 && defined(LINUX_20) /* * We have no irq line, otherwise this assignment can be used to * grab a non-shared interrupt. To share interrupt lines use * the dev_id argument of request_irq. See snull_interrupt below. */ irq2dev_map[dev->irq] = dev;#endif /* * Assign the hardware address of the board: use "\0SNULx", where * x is 0 or 1. The first byte is '\0' to avoid being a multicast * address (the first byte of multicast addrs is odd). */ memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN); dev->dev_addr[ETH_ALEN-1] += (dev - snull_devs); /* the number */#ifndef LINUX_24 dev->start = 1; #endif netif_start_queue(dev); return 0;}int snull_release(struct net_device *dev){ /* release ports, irq and such -- like fops->close */ netif_stop_queue(dev); /* can't transmit any more */#ifndef LINUX_24 dev->start = 0; #endif MOD_DEC_USE_COUNT; /* if irq2dev_map was used (2.0 kernel), zero the entry here */ return 0;}/* * Configuration changes (passed on by ifconfig) */int snull_config(struct net_device *dev, struct ifmap *map){ if (dev->flags & IFF_UP) /* can't act on a running interface */ return -EBUSY; /* Don't allow changing the I/O address */ if (map->base_addr != dev->base_addr) { printk(KERN_WARNING "snull: Can't change I/O address\n"); return -EOPNOTSUPP; } /* Allow changing the IRQ */ if (map->irq != dev->irq) { dev->irq = map->irq; /* request_irq() is delayed to open-time */ } /* ignore other fields */ return 0;}/* * Receive a packet: retrieve, encapsulate and pass over to upper levels */void snull_rx(struct net_device *dev, int len, unsigned char *buf){ struct sk_buff *skb; struct snull_priv *priv = (struct snull_priv *) dev->priv; /* * The packet has been retrieved from the transmission * medium. Build an skb around it, so upper layers can handle it */ skb = dev_alloc_skb(len+2); if (!skb) { printk("snull rx: low on mem - packet dropped\n"); priv->stats.rx_dropped++; return; } skb_reserve(skb, 2); /* align IP on 16B boundary */ memcpy(skb_put(skb, len), buf, len); /* Write metadata, and then pass to the receive level */ skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ priv->stats.rx_packets++;#ifndef LINUX_20 priv->stats.rx_bytes += len;#endif netif_rx(skb); return;} /* * The typical interrupt entry point */#ifdef LINUX_24void snull_interrupt(int irq, void *dev_id, struct pt_regs *regs){ int statusword; struct snull_priv *priv; /* * As usual, check the "device" pointer for shared handlers. * Then assign "struct device *dev" */ struct net_device *dev = (struct net_device *)dev_id; /* ... and check with hw if it's really ours */ if (!dev /*paranoid*/ ) return; /* Lock the device */ priv = (struct snull_priv *) dev->priv; spin_lock(&priv->lock); /* retrieve statusword: real netdevices use I/O instructions */ statusword = priv->status; if (statusword & SNULL_RX_INTR) { /* send it to snull_rx for handling */ snull_rx(dev, priv->rx_packetlen, priv->rx_packetdata); } if (statusword & SNULL_TX_INTR) { /* a transmission is over: free the skb */ priv->stats.tx_packets++; priv->stats.tx_bytes += priv->tx_packetlen; dev_kfree_skb(priv->skb); } /* Unlock the device and we are done */ spin_unlock(&priv->lock); return;}#else /* LINUX_22 or earlier */void snull_interrupt(int irq, void *dev_id, struct pt_regs *regs){ int statusword; struct snull_priv *priv; /* * As usual, check the "device" pointer for shared handlers. * Then assign "struct device *dev" */#if 0 && defined(LINUX_20) /* This is the way to do things for non-shared handlers */ struct device *dev = (struct device *)(irq2dev_map[irq]);#else /* Otherwise use this SA_SHIRQ-safe approach */ struct net_device *dev = (struct net_device *)dev_id; /* ... and check with hw if it's really ours */#endif if (!dev /*paranoid*/ ) return; /* Lock the device */ priv = (struct snull_priv *) dev->priv; spin_lock(&priv->lock); dev->interrupt = 1; /* retrieve statusword: real netdevices use inb() or inw() */ statusword = priv->status; if (statusword & SNULL_RX_INTR) { /* send it to snull_rx for handling */ snull_rx(dev, priv->rx_packetlen, priv->rx_packetdata); } if (statusword & SNULL_TX_INTR) { /* a transmission is over: tell we are no more busy */ priv->stats.tx_packets++;#ifndef LINUX_20 priv->stats.tx_bytes += priv->tx_packetlen;#endif dev_kfree_skb(priv->skb); } spin_unlock(&priv->lock); dev->interrupt = 0; return;}#endif /* LINUX_22 or earlier *//* * Transmit a packet (low level interface) */void snull_hw_tx(char *buf, int len, struct net_device *dev){ /* * This function deals with hw details. This interface loops * back the packet to the other snull interface (if any). * In other words, this function implements the snull behaviour, * while all other procedures are rather device-independent */ struct iphdr *ih; struct net_device *dest; struct snull_priv *priv; u32 *saddr, *daddr; /* I am paranoid. Ain't I? */ if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) { printk("snull: Hmm... packet too short (%i octets)\n", len); return; } if (0) { /* enable this conditional to look at the data */ int i; PDEBUG("len is %i\n" KERN_DEBUG "data:",len); for (i=14 ; i<len; i++) printk(" %02x",buf[i]&0xff); printk("\n"); } /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -