Network 版 (精华区)

发信人: qubo (qubo), 信区: Network
标  题: 转载一片老的经典
发信站: 哈工大紫丁香 (2003年08月14日21:29:15 星期四), 站内信件

不知道这里有没有,呵呵,愚以为挺经典。


◆ SNIFFER原理
袁哥 < yuange@163.net >
    
    大家经常讨论SNIFFER,觉得还是很多人没有真正理解SNIFFER,所以把我的一点理解
写出来大家共享。

    先讲讲HUB的工作原理吧。由于以太网等很多网络(常见共享HUB连接的内部网)是基
于总线方式,
物理上是广播的,就是一个机器发给另一个机器的数据,共享HUB先收到然后把它接收到的
数据再发给别
的(来的那个口不发了)每一个口,所以在共享HUB下面同一网段的所有机器的网卡都能接
收到数据。交换
式HUB的内部单片程序能记住每个口的MAC地址,以后就该哪个机器接收就发往哪个口,而
不是像共享HUB那
样发给所有的口,所以交换HUB下只有该接收数据的机器的网卡能接收到数据,当然广播包
还是发往所有口。
显然共享HUB的工作模式使得两个机器传输数据的时候别的口也占用了,所以共享HUB决定
了同一网段同一时
间只能有两个机器进行数据通信,而交换HUB两个机器传输数据的时候别的口没有占用,所
以别的口之间也可
以同时传输。这就是共享HUB与交换HUB不同的两个地方,共享HUB是同一时间只能一个机器
发数据并且所有机
器都可以接收,只要不是广播数据交换HUB同一时间可以有对机器进行数据传输并且数据是
私有的。

   再讲讲网卡的工作原理。网卡收到传输来的数据,网卡内的单片程序先接收数据头的目
的MAC地址,根据
计算机上的网卡驱动程序设置的接收模式判断该不该接收,认为该接收就接收后产生中断
信号通知CPU,认为
不该接收就丢掉不管,所以不该接收的数据网卡就截断了,计算机根本就不知道。CPU得到
中断信号产生中断,
操作系统就根据网卡的驱动程序设置的网卡中断程序地址调用驱动程序接收数据,驱动程
序接收数据后放入
信号堆栈让操作系统处理。

   有了这HUB、网卡的工作原理就可以讲SNIFFER了。首先,要知道要SNIFFER的东西必须
是要物理信号你能收
到的东西。显然只要通知网卡接收其收到的所有包(一般叫作乱模式),在共享HUB下就能
接收到这个网段的
所有包,但是交换HUB下就只能是自己的包加上广播包。知道了原理那就好办。要想在交换
HUB下接收别人的
包,那就要让其发往你的机器所在口。交换HUB记住一个口的MAC是通过接收到来至于那个
口的数据后记住其
源MAC,就像一个机器的IP与MAC对应的ARP列表,交换HUB维护一个物理口(就是HUB上的网
线插口,这而的所
有HUB口都是指这)与MAC的表,所以可以欺骗交换HUB的。那样你发一个包设置源MAC是你
想接收的机器的MAC,
那么交换HUB就把你机器的网线插的物理口与那个MAC对应起来了,以后发给那个MAC的包就
发往你的网线插口
了,也就是你的网卡可以SNIFFER到了。注意这物理口与MAC的表与机器的ARP表一样是动态
刷新的,那机器发包
后交换HUB就又记住他的口了,所以实际上是两个在争,这只能应用在只要收听少量包就可
以的场合,或者干脆
你弄死要冒牌的机器。还有内部网嘛基于IP的通信你可以用ARP欺骗别人机器让其发给你的
机器就可以了,如果
要想不影响原来两方的通信,可以欺骗两方,让其都发给你的机器你的机器再转发,就是
做中间人,这用ARP加
上编程很容易实现。还有现在很多设备支持远程管理,有很多交换HUB可以设置一个口监听
别的口,不过这就要
管理权限了。

    拨号用户嘛就是拨号服务器接收到包根据IP分析是哪个拨号用户的包,可能也是通过
查找分配拨号用户的IP
与拨号电话线的一个列表,然后就发往那个拨号用户的电话线,所以其相当于交换HUB,但
这儿是用的IP,交换
HUB是用的MAC,所以拨号用户能接收到自己的包。相应的要想接收别的包就得欺骗拨号服
务器,当然这不是交换
HUB那么好欺骗,因为显然拨号服务器的IP与电话线的列表不是交换HUB那么维护的,可以
看能不能破了拨号服务
器改其驱动让其所有包都发给你,显然不太可能了。:(

   下面是3COM网卡驱动程序,结合此程序讲讲网卡的原理,你可以对照程序理解。可能有
的网卡有些不一样,
但大致原理一样,所以理解很多东西不能完全照搬。

   一般网卡有个网卡地址MAC,这地址网卡厂家得申请,每个厂家得到一段地址,不同厂
家不同就像IP地址的分
配一样,然后用这段地址分配给其生产的每个网卡一个地址。一般说来网卡厂家保证每个
网卡地址不同,实际上
网卡地址一般不是放在网卡内部程序里,因为网卡内程序一般都是固化的,相同网卡所有
网卡的程序全部一样,
还有很多网卡可以配置支持很多方式,所以一般网卡都带一个EEPROM存储器,网卡地址MAC
就放这里面。其实网
卡接收包有几种方式,接收指定MAC地址的包、广播包、组播包、所有的包等,可以对其编
程。通常网卡驱动程
序设置的是接收指定MAC地址的包和广播包,如果设置了接收所有的包,就是我们常说的用
于SNIFFER的乱模式。
网卡内单片接收指定MAC地址的包的MAC也不是内部程序直接通过EEPROM的数据得到,而是
驱动程序通过读E2PROM
得到MAC,再把这MAC告诉网卡的MAC寄存器,网卡的内部程序通过MAC寄存器得到的,并且
以此为标准。所以只要
改驱动程序把你想要的MAC告诉MAC寄存器那么就得到你想要的MAC了。

一、关于MAC:
    1。   for (i = 0; i < 3; i++)
    phys_addr[i] = htons(read_eeprom(ioaddr, i));
读EEPROM中的MAC,有差不多的两个地方,可能是ISA、PCI总线网卡的差别,每次读出来的
是字(两个字节)。
       
       static ushort read_eeprom(short ioaddr, int index)
{
    outw(EEPROM_READ + index, ioaddr + 10);
    /* Pause for at least 162 us. for the read to take place. */
    udelay (500);
    return inw(ioaddr + 12);
}

这是读EEPROM的数据的函数调用,看这很可能有可以写EEPROM数据的可能,就是E2PROM里
面的MAC数据可以改写,
原来很多网卡有设置程序可以指定其MAC地址的,所以。。。

2。 memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr));
   EEPROM中读出来的MAC地址拷贝到DEV_ADDR中。
3。  for (i = 0; i < 6; i++)
    outb(dev->dev_addr[i], ioaddr + i);
   MAC写入MAC寄存器中,网卡是以写入MAC寄存器的内容为标准,实际上与网卡EEPROM数
据中的MAC无关。还有发
包的时候与源MAC无关,就和现在的IP包发包的时候的源IP可以随便填一样,只是接收包的
时候网卡通过MAC寄存
器内容和接收模式判断该不该接收。当然别人接收包后回复一般都是把收到的包的源MAC当
目的MAC,所以你发包的
源MAC是假的话可能也收不到回复包。
   还有要改MAC只要这儿的DEV_ADDR为你想要的MAC就可以,这儿可以从一个文件读入MAC
地址或者WINDOWS那样从
注册表里面读取MAC内容,这就要看驱动程序额外提供的接口了。
        
二、关于接收模式:
enum RxFilter {
RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
是定义的接收模式。

SetRxFilter = 16<<11,
是定义的设置接收模式的命令  

outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
是设置接收模式,加上RxMulticast还是RxProm应该就是乱模式了。

   3COM的网卡驱动程序:
/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
/*
    Written 1993-1997 by Donald Becker.

    Copyright 1994-1997 by Donald Becker.
    Copyright 1993 United States Government as represented by the
    Director, National Security Agency.  This software may be used and
    distributed according to the terms of the GNU Public License,
    incorporated herein by reference.

    This driver is for the 3Com EtherLinkIII series.

    The author may be reached as becker@cesdis.gsfc.nasa.gov or
    C/O Center of Excellence in Space Data and Information Sciences
        Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771

    Known limitations:
    Because of the way 3c509 ISA detection works it's difficult to predict
    a priori which of several ISA-mode cards will be detected first.

    This driver does not use predictive interrupt mode, resulting in higher
    packet latency but lower overhead.  If interrupts are disabled for an
    unusually long time it could also result in missed packets, but in
    practice this rarely happens.


    FIXES:
        Alan Cox:       Removed the 'Unexpected interrupt' bug.
        Michael Meskes: Upgraded to Donald Becker's version 1.07.
        Alan Cox:   Increased the eeprom delay. Regardless of 
                what the docs say some people definitely
                get problems with lower (but in card spec)
                delays
        v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
                other cleanups.  -djb
        Andrea Arcangeli:   Upgraded to Donald Becker's version 1.12.
*/

static char *version = "3c509.c:1.12 6/4/97 becker@cesdis.gsfc.nasa.gov\n";
/* A few values that may be tweaked. */

/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT  (400*HZ/1000)
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
#define INTR_WORK      10

#include <linux/module.h>

#include <linux/config.h>   /* for CONFIG_MCA */
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/delay.h>    /* for udelay() */

#include <asm/bitops.h>
#include <asm/io.h>

#ifdef EL3_DEBUG
int el3_debug = EL3_DEBUG;
#else
int el3_debug = 2;
#endif

/* To minimize the size of the driver source I only define operating
   constants if they are used several times.  You'll need the manual
   anyway if you want to understand driver details. */
/* Offsets from base I/O address. */
#define EL3_DATA 0x00
#define EL3_CMD 0x0e
#define EL3_STATUS 0x0e
#define  EEPROM_READ 0x80

#define EL3_IO_EXTENT   16

#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)


/* The top five bits written to EL3_CMD are a command, the lower
   11 bits are the parameter, if applicable. */
enum c509cmd {
    TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
    RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
    TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
    FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
    SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
    SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
    StatsDisable = 22<<11, StopCoax = 23<<11,};

enum c509status {
    IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
    TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
    IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, };

/* The SetRxFilter command accepts the following classes: */
enum RxFilter {
    RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };

/* Register window 1 offsets, the window used in normal operation. */
#define TX_FIFO     0x00
#define RX_FIFO     0x00
#define RX_STATUS   0x08
#define TX_STATUS   0x0B
#define TX_FREE     0x0C        /* Remaining free bytes in Tx buffer. */

#define WN0_IRQ     0x08        /* Window 0: Set IRQ line in bits 12-15. */
#define WN4_MEDIA   0x0A        /* Window 4: Various transcvr/media bits. */
#define  MEDIA_TP   0x00C0      /* Enable link beat and jabber for 10baseT. */

/*
 * Must be a power of two (we use a binary and in the
 * circular queue)
 */
#define SKB_QUEUE_SIZE  64

struct el3_private {
    struct enet_statistics stats;
    struct device *next_dev;
    /* skb send-queue */
    int head, size;
    struct sk_buff *queue[SKB_QUEUE_SIZE];
};
static int id_port = 0x100;
static struct device *el3_root_dev = NULL;

static ushort id_read_eeprom(int index);
static ushort read_eeprom(short ioaddr, int index);
static int el3_open(struct device *dev);
static int el3_start_xmit(struct sk_buff *skb, struct device *dev);
static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void update_stats(struct device *dev);
static struct enet_statistics *el3_get_stats(struct device *dev);
static int el3_rx(struct device *dev);
static int el3_close(struct device *dev);
static void set_multicast_list(struct device *dev);



int el3_probe(struct device *dev)
{
    short lrs_state = 0xff, i;
    ushort ioaddr, irq, if_port;
    short phys_addr[3];
    static int current_tag = 0;

    /* First check all slots of the EISA bus.  The next slot address to
       probe is kept in 'eisa_addr' to support multiple probe() calls. */
    if (EISA_bus) {
        static int eisa_addr = 0x1000;
        while (eisa_addr < 0x9000) {
            ioaddr = eisa_addr;
            eisa_addr += 0x1000;

            /* Check the standard EISA ID register for an encoded '3Com'. */
            if (inw(ioaddr + 0xC80) != 0x6d50)
                continue;

            /* Change the register set to the configuration window 0. */
            outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);

            irq = inw(ioaddr + WN0_IRQ) >> 12;
            if_port = inw(ioaddr + 6)>>14;
            for (i = 0; i < 3; i++)
                phys_addr[i] = htons(read_eeprom(ioaddr, i));

            /* Restore the "Product ID" to the EEPROM read register. */
            read_eeprom(ioaddr, 3);

            /* Was the EISA code an add-on hack?  Nahhhhh... */
            goto found;
        }
    }

#ifdef CONFIG_MCA
    if (MCA_bus) {
        mca_adaptor_select_mode(1);
        for (i = 0; i < 8; i++)
            if ((mca_adaptor_id(i) | 1) == 0x627c) {
                ioaddr = mca_pos_base_addr(i);
                irq = inw(ioaddr + WN0_IRQ) >> 12;
                if_port = inw(ioaddr + 6)>>14;
                for (i = 0; i < 3; i++)
                    phys_addr[i] = htons(read_eeprom(ioaddr, i));

                mca_adaptor_select_mode(0);
                goto found;
            }
        mca_adaptor_select_mode(0);

    }
#endif

    /* Reset the ISA PnP mechanism on 3c509b. */
    outb(0x02, 0x279);           /* Select PnP config control register. */
    outb(0x02, 0xA79);           /* Return to WaitForKey state. */
    /* Select an open I/O location at 0x1*0 to do contention select. */
    for (id_port = 0x100; id_port < 0x200; id_port += 0x10) {
        if (check_region(id_port, 1))
            continue;
        outb(0x00, id_port);
        outb(0xff, id_port);
        if (inb(id_port) & 0x01)
            break;
    }
    if (id_port >= 0x200) {             /* GCC optimizes this test out. */
        /* Rare -- do we really need a warning? */
        printk(" WARNING: No I/O port available for 3c509 activation.\n");
        return -ENODEV;
    }
    /* Next check for all ISA bus boards by sending the ID sequence to the
       ID_PORT.  We find cards past the first by setting the 'current_tag'
       on cards as they are found.  Cards with their tag set will not
       respond to subsequent ID sequences. */

    outb(0x00, id_port);
    outb(0x00, id_port);
    for(i = 0; i < 255; i++) {
        outb(lrs_state, id_port);
        lrs_state <<= 1;
        lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
    }

    /* For the first probe, clear all board's tag registers. */
    if (current_tag == 0)
        outb(0xd0, id_port);
    else                /* Otherwise kill off already-found boards. */
        outb(0xd8, id_port);

    if (id_read_eeprom(7) != 0x6d50) {
        return -ENODEV;
    }

    /* Read in EEPROM data, which does contention-select.
       Only the lowest address board will stay "on-line".
       3Com got the byte order backwards. */
    for (i = 0; i < 3; i++) {
        phys_addr[i] = htons(id_read_eeprom(i));
    }

    {
        unsigned short iobase = id_read_eeprom(8);
        if_port = iobase >> 14;
        ioaddr = 0x200 + ((iobase & 0x1f) << 4);
    }
    if (dev  &&  dev->irq > 1  &&  dev->irq < 16)
        irq = dev->irq;
    else
        irq = id_read_eeprom(9) >> 12;

    if (dev  &&  dev->base_addr != 0
        &&  dev->base_addr != (unsigned short)ioaddr) {
        return -ENODEV;
    }

    /* Set the adaptor tag so that the next card can be found. */
    outb(0xd0 + ++current_tag, id_port);

    /* Activate the adaptor at the EEPROM location. */
    outb((ioaddr >> 4) | 0xe0, id_port);

    EL3WINDOW(0);
    if (inw(ioaddr) != 0x6d50)
        return -ENODEV;

    /* Free the interrupt so that some other card can use it. */
    outw(0x0f00, ioaddr + WN0_IRQ);
 found:
    if (dev == NULL) {
        dev = init_etherdev(dev, sizeof(struct el3_private));
    }
    memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr));
    dev->base_addr = ioaddr;
    dev->irq = irq;
    dev->if_port = (dev->mem_start & 0x1f) ? dev->mem_start & 3 : if_port;

    request_region(dev->base_addr, EL3_IO_EXTENT, "3c509");

    {
        const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
        printk("%s: 3c509 at %#3.3lx tag %d, %s port, address ",
               dev->name, dev->base_addr, current_tag, if_names[dev->if_port]);
    }

    /* Read in the station address. */
    for (i = 0; i < 6; i++)
        printk(" %2.2x", dev->dev_addr[i]);
    printk(", IRQ %d.\n", dev->irq);

    /* Make up a EL3-specific-data structure. */
    if (dev->priv == NULL)
        dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
    if (dev->priv == NULL)
        return -ENOMEM;
    memset(dev->priv, 0, sizeof(struct el3_private));

    ((struct el3_private *)dev->priv)->next_dev = el3_root_dev;
    el3_root_dev = dev;

    if (el3_debug > 0)
        printk(version);

    /* The EL3-specific entries in the device structure. */
    dev->open = &el3_open;
    dev->hard_start_xmit = &el3_start_xmit;
    dev->stop = &el3_close;
    dev->get_stats = &el3_get_stats;
    dev->set_multicast_list = &set_multicast_list;

    /* Fill in the generic fields of the device structure. */
    ether_setup(dev);
    return 0;
}

/* Read a word from the EEPROM using the regular EEPROM access register.
   Assume that we are in register window zero.
 */
static ushort read_eeprom(short ioaddr, int index)
{
    outw(EEPROM_READ + index, ioaddr + 10);
    /* Pause for at least 162 us. for the read to take place. */
    udelay (500);
    return inw(ioaddr + 12);
}

/* Read a word from the EEPROM when in the ISA ID probe state. */
static ushort id_read_eeprom(int index)
{
    int bit, word = 0;

    /* Issue read command, and pause for at least 162 us. for it to complete.
       Assume extra-fast 16Mhz bus. */
    outb(EEPROM_READ + index, id_port);

    /* Pause for at least 162 us. for the read to take place. */
    udelay (500);
    
    for (bit = 15; bit >= 0; bit--)
        word = (word << 1) + (inb(id_port) & 0x01);

    if (el3_debug > 3)
        printk("  3c509 EEPROM word %d %#4.4x.\n", index, word);

    return word;
}



static int
el3_open(struct device *dev)
{
    int ioaddr = dev->base_addr;
    int i;

    outw(TxReset, ioaddr + EL3_CMD);
    outw(RxReset, ioaddr + EL3_CMD);
    outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);

    if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev)) {
        return -EAGAIN;
    }

    EL3WINDOW(0);
    if (el3_debug > 3)
        printk("%s: Opening, IRQ %d  status@%x %4.4x.\n", dev->name,
               dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));

    /* Activate board: this is probably unnecessary. */
    outw(0x0001, ioaddr + 4);

    /* Set the IRQ line. */
    outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);

    /* Set the station address in window 2 each time opened. */
    EL3WINDOW(2);

    for (i = 0; i < 6; i++)
        outb(dev->dev_addr[i], ioaddr + i);

    if (dev->if_port == 3)
        /* Start the thinnet transceiver. We should really wait 50ms...*/
        outw(StartCoax, ioaddr + EL3_CMD);
    else if (dev->if_port == 0) {
        /* 10baseT interface, enabled link beat and jabber check. */
        EL3WINDOW(4);
        outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
    }

    /* Switch to the stats window, and clear all stats by reading. */
    outw(StatsDisable, ioaddr + EL3_CMD);
    EL3WINDOW(6);
    for (i = 0; i < 9; i++)
        inb(ioaddr + i);
    inw(ioaddr + 10);
    inw(ioaddr + 12);

    /* Switch to register set 1 for normal use. */
    EL3WINDOW(1);

    /* Accept b-case and phys addr only. */
    outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
    outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */

    dev->interrupt = 0;
    dev->tbusy = 0;
    dev->start = 1;

    outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
    outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
    /* Allow status bits to be seen. */
    outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
    /* Ack all pending events, and set active indicator mask. */
    outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
         ioaddr + EL3_CMD);
    outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull,
         ioaddr + EL3_CMD);

    if (el3_debug > 3)
        printk("%s: Opened 3c509  IRQ %d  status %4.4x.\n",
               dev->name, dev->irq, inw(ioaddr + EL3_STATUS));

    MOD_INC_USE_COUNT;
    return 0;                   /* Always succeed */
}

static int
el3_start_xmit(struct sk_buff *skb, struct device *dev)
{
    struct el3_private *lp = (struct el3_private *)dev->priv;
    int ioaddr = dev->base_addr;

    /* Transmitter timeout, serious problems. */
    if (dev->tbusy) {
        int tickssofar = jiffies - dev->trans_start;
        if (tickssofar < TX_TIMEOUT)
            return 1;
        printk("%s: transmit timed out, Tx_status %2.2x status %4.4x "
               "Tx FIFO room %d.\n",
               dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
               inw(ioaddr + TX_FREE));
        lp->stats.tx_errors++;
        dev->trans_start = jiffies;
        /* Issue TX_RESET and TX_START commands. */
        outw(TxReset, ioaddr + EL3_CMD);
        outw(TxEnable, ioaddr + EL3_CMD);
        dev->tbusy = 0;
    }

    if (el3_debug > 4) {
        printk("%s: el3_start_xmit(length = %u) called, status %4.4x.\n",
               dev->name, skb->len, inw(ioaddr + EL3_STATUS));
    }
#if 0
#ifndef final_version
    {   /* Error-checking code, delete someday. */
        ushort status = inw(ioaddr + EL3_STATUS);
        if (status & 0x0001         /* IRQ line active, missed one. */
            && inw(ioaddr + EL3_STATUS) & 1) {          
            /* Make sure. */
            printk("%s: Missed interrupt, status then %04x now %04x"
                   "  Tx %2.2x Rx %4.4x.\n", dev->name, status,
                   inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
                   inw(ioaddr + RX_STATUS));
            /* Fake interrupt trigger by masking, acknowledge interrupts. */
            outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
            outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
                 ioaddr + EL3_CMD);
            outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
        }
    }
#endif
#endif
    /* Avoid timer-based retransmission conflicts. */
    if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
        printk("%s: Transmitter access conflict.\n", dev->name);
    else {
        /* Put out the doubleword header... */
        outw(skb->len, ioaddr + TX_FIFO);
        outw(0x00, ioaddr + TX_FIFO);
        /* ... and the packet rounded to a doubleword. */
#ifdef  __powerpc__
        outsl_unswapped(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
#else
        outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
#endif

        dev->trans_start = jiffies;
        if (inw(ioaddr + TX_FREE) > 1536) {
            dev->tbusy = 0;
        } else
            /* Interrupt us when the FIFO has room for max-sized packet. */
            outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
    }

    dev_kfree_skb (skb);

    /* Clear the Tx status stack. */
    {
        short tx_status;
        int i = 4;

        while (--i > 0  &&  (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
            if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
            if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
            if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
            outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
        }
    }
    return 0;
}

/* The EL3 interrupt handler. */
static void
el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    struct device *dev = (struct device *)dev_id;
    int ioaddr, status;
    int i = INTR_WORK;

    if (dev == NULL) {
        printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
        return;
    }

    if (dev->interrupt)
        printk("%s: Re-entering the interrupt handler.\n", dev->name);
    dev->interrupt = 1;

    ioaddr = dev->base_addr;

    if (el3_debug > 4) {
        status = inw(ioaddr + EL3_STATUS);
        printk("%s: interrupt, status %4.4x.\n", dev->name, status);
    }

    while ((status = inw(ioaddr + EL3_STATUS)) &
           (IntLatch | RxComplete | StatsFull)) {

        if (status & RxComplete)
            el3_rx(dev);

        if (status & TxAvailable) {
            if (el3_debug > 5)
                printk("    TX room bit was handled.\n");
            /* There's room in the FIFO for a full-sized packet. */
            outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
            dev->tbusy = 0;
            mark_bh(NET_BH);
        }
        if (status & (AdapterFailure | RxEarly | StatsFull)) {
            /* Handle all uncommon interrupts. */
            if (status & StatsFull)             
            /* Empty statistics. */
                update_stats(dev);
            if (status & RxEarly) {             
            /* Rx early is unused. */
                el3_rx(dev);
                outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
            }
            if (status & AdapterFailure) {
                /* Adapter failure requires Rx reset and reinit. */
                outw(RxReset, ioaddr + EL3_CMD);
                /* Set the Rx filter to the current state. */
                outw(SetRxFilter | RxStation | RxBroadcast
                     | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
                     | (dev->flags & IFF_PROMISC ? RxProm : 0),
                     ioaddr + EL3_CMD);
                outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
                outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
            }
        }

        if (--i < 0) {
            printk("%s: Infinite loop in interrupt, status %4.4x.\n",
                   dev->name, status);
            /* Clear all interrupts. */
            outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
            break;
        }
        /* Acknowledge the IRQ. */
        outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */
    }

    if (el3_debug > 4) {
        printk("%s: exiting interrupt, status %4.4x.\n", dev->name,
               inw(ioaddr + EL3_STATUS));
    }

    dev->interrupt = 0;
    return;
}


static struct enet_statistics *
el3_get_stats(struct device *dev)
{
    struct el3_private *lp = (struct el3_private *)dev->priv;
    unsigned long flags;

    save_flags(flags);
    cli();
    update_stats(dev);
    restore_flags(flags);
    return &lp->stats;
}

/*  Update statistics.  We change to register window 6, so this should be run
    single-threaded if the device is active. This is expected to be a rare
    operation, and it's simpler for the rest of the driver to assume that
    window 1 is always valid rather than use a special window-state variable.
    */
static void update_stats(struct device *dev)
{
    struct el3_private *lp = (struct el3_private *)dev->priv;
    int ioaddr = dev->base_addr;

    if (el3_debug > 5)
        printk("   Updating the statistics.\n");
    /* Turn off statistics updates while reading. */
    outw(StatsDisable, ioaddr + EL3_CMD);
    /* Switch to the stats window, and read everything. */
    EL3WINDOW(6);
    lp->stats.tx_carrier_errors     += inb(ioaddr + 0);
    lp->stats.tx_heartbeat_errors   += inb(ioaddr + 1);
    /* Multiple collisions. */      inb(ioaddr + 2);
    lp->stats.collisions            += inb(ioaddr + 3);
    lp->stats.tx_window_errors      += inb(ioaddr + 4);
    lp->stats.rx_fifo_errors        += inb(ioaddr + 5);
    lp->stats.tx_packets            += inb(ioaddr + 6);
    /* Rx packets   */              inb(ioaddr + 7);
    /* Tx deferrals */              inb(ioaddr + 8);
    inw(ioaddr + 10);   /* Total Rx and Tx octets. */
    inw(ioaddr + 12);

    /* Back to window 1, and turn statistics back on. */
    EL3WINDOW(1);
    outw(StatsEnable, ioaddr + EL3_CMD);
    return;
}

static int
el3_rx(struct device *dev)
{
    struct el3_private *lp = (struct el3_private *)dev->priv;
    int ioaddr = dev->base_addr;
    short rx_status;

    if (el3_debug > 5)
        printk("   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
               inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
    while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
        if (rx_status & 0x4000) { /* Error, update stats. */
            short error = rx_status & 0x3800;
            lp->stats.rx_errors++;
            switch (error) {
            case 0x0000:        lp->stats.rx_over_errors++; break;
            case 0x0800:        lp->stats.rx_length_errors++; break;
            case 0x1000:        lp->stats.rx_frame_errors++; break;
            case 0x1800:        lp->stats.rx_length_errors++; break;
            case 0x2000:        lp->stats.rx_frame_errors++; break;
            case 0x2800:        lp->stats.rx_crc_errors++; break;
            }
        } else {
            short pkt_len = rx_status & 0x7ff;
            struct sk_buff *skb;

            skb = dev_alloc_skb(pkt_len+5);
            if (el3_debug > 4)
                printk("Receiving packet size %d status %4.4x.\n",
                       pkt_len, rx_status);
            if (skb != NULL) {
                skb->dev = dev;
                skb_reserve(skb, 2);     /* Align IP on 16 byte */

                /* 'skb->data' points to the start of sk_buff data area. */
#ifdef  __powerpc__
                insl_unswapped(ioaddr+RX_FIFO, skb_put(skb,pkt_len),
                               (pkt_len + 3) >> 2);
#else
                insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
                     (pkt_len + 3) >> 2);
#endif

                skb->protocol = eth_type_trans(skb,dev);
                netif_rx(skb);
                outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
                lp->stats.rx_packets++;
                continue;
            } else if (el3_debug)
                printk("%s: Couldn't allocate a sk_buff of size %d.\n",
                       dev->name, pkt_len);
        }
        lp->stats.rx_dropped++;
        outw(RxDiscard, ioaddr + EL3_CMD);
        while (inw(ioaddr + EL3_STATUS) & 0x1000)
            printk("    Waiting for 3c509 to discard packet, status %x.\n",
                   inw(ioaddr + EL3_STATUS) );
    }

    return 0;
}

/*
 *     Set or clear the multicast filter for this adaptor.
 */
static void
set_multicast_list(struct device *dev)
{
    short ioaddr = dev->base_addr;
    if (el3_debug > 1) {
        static int old = 0;
        if (old != dev->mc_count) {
            old = dev->mc_count;
            printk("%s: Setting Rx mode to %d addresses.\n", dev->name, 
dev->mc_count);
        }
    }
    if (dev->flags&IFF_PROMISC) {
        outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
             ioaddr + EL3_CMD);
    }
    else if (dev->mc_count || (dev->flags&IFF_ALLMULTI)) {
        outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + 
EL3_CMD);
    }
    else
                outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
}

static int
el3_close(struct device *dev)
{
    int ioaddr = dev->base_addr;

    if (el3_debug > 2)
        printk("%s: Shutting down ethercard.\n", dev->name);

    dev->tbusy = 1;
    dev->start = 0;

    /* Turn off statistics ASAP.  We update lp->stats below. */
    outw(StatsDisable, ioaddr + EL3_CMD);

    /* Disable the receiver and transmitter. */
    outw(RxDisable, ioaddr + EL3_CMD);
    outw(TxDisable, ioaddr + EL3_CMD);

    if (dev->if_port == 3)
        /* Turn off thinnet power.  Green! */
        outw(StopCoax, ioaddr + EL3_CMD);
    else if (dev->if_port == 0) {
        /* Disable link beat and jabber, if_port may change ere next open(). 
*/
        EL3WINDOW(4);
        outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
    }

    free_irq(dev->irq, dev);
    /* Switching back to window 0 disables the IRQ. */
    EL3WINDOW(0);
    /* But we explicitly zero the IRQ line select anyway. */
    outw(0x0f00, ioaddr + WN0_IRQ);

    update_stats(dev);
    MOD_DEC_USE_COUNT;
    return 0;
}

#ifdef MODULE
/* Parameter that may be passed into the module. */
static int debug = -1;
static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1};

MODULE_PARM(debug,"i");
MODULE_PARM(irq,"1-8i");
MODULE_PARM(xcvr,"1-8i");

int
init_module(void)
{
    int el3_cards = 0;

    if (debug >= 0)
        el3_debug = debug;

    el3_root_dev = NULL;
    while (el3_probe(0) == 0) {
        if (irq[el3_cards] > 1)
            el3_root_dev->irq = irq[el3_cards];
        if (xcvr[el3_cards] >= 0)
            el3_root_dev->if_port = xcvr[el3_cards];
        el3_cards++;
    }

    return el3_cards ? 0 : -ENODEV;
}

void
cleanup_module(void)
{
    struct device *next_dev;

    /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
    while (el3_root_dev) {
        next_dev = ((struct el3_private *)el3_root_dev->priv)->next_dev;
        unregister_netdev(el3_root_dev);
        release_region(el3_root_dev->base_addr, EL3_IO_EXTENT);
        kfree(el3_root_dev);
        el3_root_dev = next_dev;
    }
}
#endif /* MODULE */

/*
 * Local variables:
 *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 
-c 3c509.c"
 *  version-control: t
 *  kept-new-versions: 5
 *  tab-width: 4
 * End:
 */


 

 
 

--
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.228.73]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:403.392毫秒