⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 snull.c

📁 Linux 设备驱动 Edition 3,书中的例题
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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.21 2004/11/05 02:36:03 rubini Exp $ */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/moduleparam.h>#include <linux/sched.h>#include <linux/kernel.h> /* printk() */#include <linux/slab.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"#include <linux/in6.h>#include <asm/checksum.h>MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");MODULE_LICENSE("Dual BSD/GPL");/* * Transmitter lockup simulation, normally disabled. */static int lockup = 0;module_param(lockup, int, 0);static int timeout = SNULL_TIMEOUT;module_param(timeout, int, 0);/* * Do we run in NAPI mode? */static int use_napi = 0;module_param(use_napi, int, 0);/* * A structure representing an in-flight packet. */struct snull_packet {	struct snull_packet *next;	struct net_device *dev;	int	datalen;	u8 data[ETH_DATA_LEN];};int pool_size = 8;module_param(pool_size, int, 0);/* * 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;	struct snull_packet *ppool;	struct snull_packet *rx_queue;  /* List of incoming packets */	int rx_int_enabled;	int tx_packetlen;	u8 *tx_packetdata;	struct sk_buff *skb;	spinlock_t lock;};static void snull_tx_timeout(struct net_device *dev);static void (*snull_interrupt)(int, void *, struct pt_regs *);/* * Set up a device's packet pool. */void snull_setup_pool(struct net_device *dev){	struct snull_priv *priv = netdev_priv(dev);	int i;	struct snull_packet *pkt;	priv->ppool = NULL;	for (i = 0; i < pool_size; i++) {		pkt = kmalloc (sizeof (struct snull_packet), GFP_KERNEL);		if (pkt == NULL) {			printk (KERN_NOTICE "Ran out of memory allocating packet pool\n");			return;		}		pkt->dev = dev;		pkt->next = priv->ppool;		priv->ppool = pkt;	}}void snull_teardown_pool(struct net_device *dev){	struct snull_priv *priv = netdev_priv(dev);	struct snull_packet *pkt;    	while ((pkt = priv->ppool)) {		priv->ppool = pkt->next;		kfree (pkt);		/* FIXME - in-flight packets ? */	}}    /* * Buffer/pool management. */struct snull_packet *snull_get_tx_buffer(struct net_device *dev){	struct snull_priv *priv = netdev_priv(dev);	unsigned long flags;	struct snull_packet *pkt;    	spin_lock_irqsave(&priv->lock, flags);	pkt = priv->ppool;	priv->ppool = pkt->next;	if (priv->ppool == NULL) {		printk (KERN_INFO "Pool empty\n");		netif_stop_queue(dev);	}	spin_unlock_irqrestore(&priv->lock, flags);	return pkt;}void snull_release_buffer(struct snull_packet *pkt){	unsigned long flags;	struct snull_priv *priv = netdev_priv(pkt->dev);		spin_lock_irqsave(&priv->lock, flags);	pkt->next = priv->ppool;	priv->ppool = pkt;	spin_unlock_irqrestore(&priv->lock, flags);	if (netif_queue_stopped(pkt->dev) && pkt->next == NULL)		netif_wake_queue(pkt->dev);}void snull_enqueue_buf(struct net_device *dev, struct snull_packet *pkt){	unsigned long flags;	struct snull_priv *priv = netdev_priv(dev);	spin_lock_irqsave(&priv->lock, flags);	pkt->next = priv->rx_queue;  /* FIXME - misorders packets */	priv->rx_queue = pkt;	spin_unlock_irqrestore(&priv->lock, flags);}struct snull_packet *snull_dequeue_buf(struct net_device *dev){	struct snull_priv *priv = netdev_priv(dev);	struct snull_packet *pkt;	unsigned long flags;	spin_lock_irqsave(&priv->lock, flags);	pkt = priv->rx_queue;	if (pkt != NULL)		priv->rx_queue = pkt->next;	spin_unlock_irqrestore(&priv->lock, flags);	return pkt;}/* * Enable and disable receive interrupts. */static void snull_rx_ints(struct net_device *dev, int enable){	struct snull_priv *priv = netdev_priv(dev);	priv->rx_int_enabled = enable;}    /* * Open and close */int snull_open(struct net_device *dev){	/* request_region(), request_irq(), ....  (like fops->open) */	/* 	 * 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);	if (dev == snull_devs[1])		dev->dev_addr[ETH_ALEN-1]++; /* \0SNUL1 */	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 */	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, struct snull_packet *pkt){	struct sk_buff *skb;	struct snull_priv *priv = netdev_priv(dev);	/*	 * The packet has been retrieved from the transmission	 * medium. Build an skb around it, so upper layers can handle it	 */	skb = dev_alloc_skb(pkt->datalen + 2);	if (!skb) {		if (printk_ratelimit())			printk(KERN_NOTICE "snull rx: low on mem - packet dropped\n");		priv->stats.rx_dropped++;		goto out;	}	skb_reserve(skb, 2); /* align IP on 16B boundary */  	memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);	/* 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++;	priv->stats.rx_bytes += pkt->datalen;	netif_rx(skb);  out:	return;}    /* * The poll implementation. */static int snull_poll(struct net_device *dev, int *budget){	int npackets = 0, quota = min(dev->quota, *budget);	struct sk_buff *skb;	struct snull_priv *priv = netdev_priv(dev);	struct snull_packet *pkt;    	while (npackets < quota && priv->rx_queue) {		pkt = snull_dequeue_buf(dev);		skb = dev_alloc_skb(pkt->datalen + 2);		if (! skb) {			if (printk_ratelimit())				printk(KERN_NOTICE "snull: packet dropped\n");			priv->stats.rx_dropped++;			snull_release_buffer(pkt);			continue;		}		skb_reserve(skb, 2); /* align IP on 16B boundary */  		memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);		skb->dev = dev;		skb->protocol = eth_type_trans(skb, dev);		skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */		netif_receive_skb(skb);		        	/* Maintain stats */		npackets++;		priv->stats.rx_packets++;		priv->stats.rx_bytes += pkt->datalen;		snull_release_buffer(pkt);	}	/* If we processed all packets, we're done; tell the kernel and reenable ints */	*budget -= npackets;	dev->quota -= npackets;	if (! priv->rx_queue) {		netif_rx_complete(dev);		snull_rx_ints(dev, 1);		return 0;	}	/* We couldn't process everything. */	return 1;}	            /* * The typical interrupt entry point */static void snull_regular_interrupt(int irq, void *dev_id, struct pt_regs *regs){	int statusword;	struct snull_priv *priv;	struct snull_packet *pkt = NULL;	/*	 * As usual, check the "device" pointer to be sure it is	 * really interrupting.	 * Then assign "struct device *dev"	 */	struct net_device *dev = (struct net_device *)dev_id;	/* ... and check with hw if it's really ours */	/* paranoid */	if (!dev)		return;	/* Lock the device */	priv = netdev_priv(dev);	spin_lock(&priv->lock);	/* retrieve statusword: real netdevices use I/O instructions */	statusword = priv->status;	priv->status = 0;	if (statusword & SNULL_RX_INTR) {		/* send it to snull_rx for handling */		pkt = priv->rx_queue;		if (pkt) {			priv->rx_queue = pkt->next;			snull_rx(dev, pkt);		}	}	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);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -