📄 bm8024drv.c
字号:
/* * */#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 "idma.h"#include "bm8024.h"#include "bm8024drv.h"#include <linux/in6.h>#include <asm/checksum.h>#include <asm/irq.h>#include <asm/semaphore.h>MODULE_AUTHOR("Zhaoling Qi");/* * This structure is private to each device. It is used to pass * packets in and out, so there is place for a packet */static int timeout = BM8024_TIMEOUT;struct bm8024_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;}; /** BM8024 defination*/#define SIU_IRQ1 2#define BM8024_IO_LEN 0x1FF#define BM8024_IRQ SIU_IRQ1char g_dev_mac[6];struct semaphore DmaRdySem, ExclusiveSem;/** MPC850 IDMA defination*/U32 txBdIndex=0;#define TXBD_INDEX_INCREASE(index) index = ((index + 1) % NO_IDMA_BD_TX)#define BDRX_OFFSET 0x2c00#define BDTX_OFFSET 0x2c30/** local function & variant*//* * The devices defination & function */struct net_device BM8024_dev;int bm8024_init(struct net_device *dev);void bm8024_tx_timeout (struct net_device *dev); /* * module function */int bm8024_init_module(void){ int result; strcpy(BM8024_dev.name, "eth%d"); BM8024_dev.init = bm8024_init; if ( (result = register_netdev(BM8024_dev)) ) { printk("bm8024: error %d occur in registering device\n",result); return -ENODEV; } return 0;}void bm8024_cleanup(void){ kfree(BM8024_dev); unregister_netdev(BM8024_dev); return;}/* * Open and close */int bm8024_open(struct net_device *dev){ MOD_INC_USE_COUNT; int ret; bm8024_chipset_init(); /* request_region(), request_irq(), .... (like fops->open) */ ret = request_8xxirq(dev->irq, bm8024_interrupt, 0, dev->name, dev); if (ret) { printk (" unable to get IRQ %d (errno=%d).\n", dev->irq, ret); return -EBUSY; } /* */ memcpy(dev->dev_addr, g_dev_mac, ETH_ALEN); netif_start_queue(dev); return 0;}int bm8024_release(struct net_device *dev){ /* release ports, irq and such -- like fops->close */ free_irq(dev->irq, dev); netif_stop_queue(dev); /* can't transmit any more */ 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 bm8024_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 "bm8024: Can't change I/O address\n"); return -EOPNOTSUPP; } /* Allow changing the IRQ */ if (map->irq != dev->irq) { printk(KERN_WARNING "bm8024: Can't change IRQ\n"); return -EOPNOTSUPP; /* request_irq() is delayed to open-time */ } /* ignore other fields */ return 0;}/* * Receive a packet: retrieve, encapsulate and pass over to upper levels */void bm8024_rx(struct net_device *dev, int len, unsigned char *buf){ struct sk_buff *skb; struct bm8024_priv *priv = (struct bm8024_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("bm8024 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 */void bm8024_interrupt(int irq, void *dev_id, struct pt_regs *regs){ int statusword; struct bm8024_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 == NULL) || (BM8024_IRQ != irq)) { printk("%s: bogus interrupt %d\n", dev?dev->name:"BM8024", irq); return; } /* Lock the device */ priv = (struct bm8024_priv *) dev->priv; spin_lock(&priv->lock); /* retrieve statusword: real netdevices use I/O instructions */ /*statusword = priv->status;*/ do { /* send it to bm8024_rx for handling */ bm8024_rx(dev, priv->rx_packetlen, priv->rx_packetdata); } while ( 0 ); /* Unlock the device and we are done */ spin_unlock(&priv->lock); return;}/* * Transmit a packet (low level interface) */void bm8024_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 bm8024 interface (if any). * In other words, this function implements the bm8024 behaviour, * while all other procedures are rather device-independent */ struct iphdr *ih; struct net_device *dest; struct bm8024_priv *priv; u32 *saddr, *daddr; /* I am paranoid. Ain't I? */ if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) { printk("bm8024: 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"); } /* * Ethhdr is 14 bytes, but the kernel arranges for iphdr * to be aligned (i.e., ethhdr is unaligned) */ ih = (struct iphdr *)(buf+sizeof(struct ethhdr)); saddr = &ih->saddr; daddr = &ih->daddr; ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */ ((u8 *)daddr)[2] ^= 1; ih->check = 0; /* and rebuild the checksum (ip needs it) */ ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl); if (dev == bm8024_devs) PDEBUGG("%08x:%05i --> %08x:%05i\n", ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source), ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest)); else PDEBUGG("%08x:%05i <-- %08x:%05i\n", ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest), ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source)); /* * Ok, now the packet is ready for transmission: first simulate a * receive interrupt on the twin device, then a * transmission-done on the transmitting device */ dest = bm8024_devs + (dev==bm8024_devs ? 1 : 0); priv = (struct bm8024_priv *) dest->priv; priv->status = bm8024_RX_INTR; priv->rx_packetlen = len; priv->rx_packetdata = buf; bm8024_interrupt(0, dest, NULL); priv = (struct bm8024_priv *) dev->priv; priv->status = bm8024_TX_INTR; priv->tx_packetlen = len; priv->tx_packetdata = buf; if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) { /* Simulate a dropped transmit interrupt */ netif_stop_queue(dev); PDEBUG("Simulate lockup at %ld, txp %ld\n", jiffies, (unsigned long) priv->stats.tx_packets); } else bm8024_interrupt(0, dev, NULL);}/* * Transmit a packet (called by the kernel) */int bm8024_tx(struct sk_buff *skb, struct net_device *dev){ int len; char *data; struct bm8024_priv *priv = (struct bm8024_priv *) dev->priv;#ifndef LINUX_24 if (dev->tbusy || skb == NULL) { PDEBUG("tint for %p, tbusy %ld, skb %p\n", dev, dev->tbusy, skb); bm8024_tx_timeout (dev); if (skb == NULL) return 0; }#endif len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; data = skb->data; dev->trans_start = jiffies; /* save the timestamp */ /* Remember the skb, so we can free it at interrupt time */ priv->skb = skb; /* actual deliver of data is device-specific, and not shown here */ bm8024_hw_tx(data, len, dev); return 0; /* Our simple device can not fail */}/* * Deal with a transmit timeout. */void bm8024_tx_timeout (struct net_device *dev){ struct bm8024_priv *priv = (struct bm8024_priv *) dev->priv; PDEBUG("Transmit timeout at %ld, latency %ld\n", jiffies, jiffies - dev->trans_start); priv->status = bm8024_TX_INTR; bm8024_interrupt(0, dev, NULL); priv->stats.tx_errors++; netif_wake_queue(dev); return;}/* * Ioctl commands */int bm8024_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ PDEBUG("ioctl\n"); return 0;}/* * Return statistics to the caller */struct net_device_stats *bm8024_stats(struct net_device *dev){ struct bm8024_priv *priv = (struct bm8024_priv *) dev->priv; return &priv->stats;}/* * This function is called to fill up an eth header, since arp is not * available on the interface*/int bm8024_rebuild_header(struct sk_buff *skb){ struct ethhdr *eth = (struct ethhdr *) skb->data; struct net_device *dev = skb->dev; memcpy(eth->h_source, dev->dev_addr, dev->addr_len); memcpy(eth->h_dest, dev->dev_addr, dev->addr_len); eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */ return 0;}int bm8024_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned int len){ struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN); eth->h_proto = htons(type); memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len); memcpy(eth->h_dest, daddr ? daddr : dev->dev_addr, dev->addr_len); eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */ return (dev->hard_header_len);}/* * The "change_mtu" method is usually not needed. * If you need it, it must be like this. */int bm8024_change_mtu(struct net_device *dev, int new_mtu){ unsigned long flags; spinlock_t *lock = &((struct bm8024_priv *) dev->priv)->lock; /* check ranges */ if ((new_mtu < 68) || (new_mtu > 1500)) return -EINVAL; /* * Do anything you need, and the accept the value */ spin_lock_irqsave(lock, flags); dev->mtu = new_mtu; spin_unlock_irqrestore(lock, flags); return 0; /* success */}/* * The init function (sometimes called probe). * It is invoked by register_netdev() */int bm8024_init(struct net_device *dev){#if 0 /* * Make the usual checks: check_region(), probe irq, ... -ENODEV * should be returned if no device found. No resource should be * grabbed: this is done on open(). */#endif /* * Then, assign other fields in dev, using ether_setup() and some * hand assignments */ g_dev_mac[0] = 0x00; g_dev_mac[1] = 0x01; g_dev_mac[2] = 0xee; g_dev_mac[3] = 0xa9; g_dev_mac[4] = 0xf1; g_dev_mac[5] = 0x09; ether_setup(dev); /* assign some of the fields */ dev->open = bm8024_open; dev->stop = bm8024_release; dev->set_config = bm8024_config; dev->hard_start_xmit = bm8024_tx; dev->do_ioctl = bm8024_ioctl; dev->get_stats = bm8024_stats; dev->change_mtu = bm8024_change_mtu; dev->rebuild_header = bm8024_rebuild_header; dev->hard_header = bm8024_header; dev->tx_timeout = bm8024_tx_timeout; dev->watchdog_timeo = timeout; dev->irq = BM8024_IRQ; dev->base_addr = 0x80000000; dev->mem_end = 0x80001000; dev->hard_header_cache = NULL; /* Disable caching */ SET_MODULE_OWNER(dev); /* * Then, allocate the priv field. This encloses the statistics * and a few private fields. */ dev->priv = kmalloc(sizeof(struct bm8024_priv), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct bm8024_priv)); spin_lock_init(& ((struct bm8024_priv *) dev->priv)->lock); sema_init return 0;}module_init(bm8024_init_module);module_exit(bm8024_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -