网卡设备驱动

1.8139too网卡设备简介
一个PCI设备,总共有三个地址空间:内存,端口和设备。内存和端口事实上是同一个内容的不合拜望路径罢了。PCI设备的设备空间是标准化的,每个PCI设备的设备空间的前64个字节的含义差不多上一样的。但各个设备的内存和端口空间是完全不一样的。在硬件概念上,设备的I/O内存和I/O端口空间没有差别,事实上差不多上设备拥用的一些读写存放器。
    8139too网卡拥有256字节的读写存放器空间。它的全部构造如下:
        /* Symbolic offsets to registers. */
        enum RTL8139_registers {
            MAC0 = 0,      /* Ethernet hardware address. */
            MAR0 = 8,      /* Multicast filter. */
            TxStatus0 = 0x10,  /* Transmit status (Four 32bit registers). */
            TxAddr0 = 0x20,    /* Tx descriptors (also four 32bit). */
            RxBuf = 0x30,
小额信贷运作与管理            ChipCmd = 0x37,
            RxBufPtr = 0x38,
            RxBufAddr = 0x3A,
            IntrMask = 0x3C,
            IntrStatus = 0x3E,
            TxConfig = 0x40,
            RxConfig = 0x44,
            Timer = 0x48,      /* A general-purpose counter. */
            RxMissed = 0x4C,    /* 24 bits valid, write clears. */
            Cfg9346 = 0x50,
            Config0 = 0x51,
            Config1 = 0x52,
            FlashReg = 0x54,
            MediaStatus = 0x58,
            Config3 = 0x59,
            Config4 = 0x5A,    /* absent on RTL-8139A */
            HltClk = 0x5B,
            MultiIntr = 0x5C,
            TxSummary = 0x60,
            BasicModeCtrl = 0x62,
            BasicModeStatus = 0x64,
            NWayAdvert = 0x66,
            NWayLPAR = 0x68,
            NWayExpansion = 0x6A,
            /* Undocumented registers, but required for proper operation. */
            FIFOTMS = 0x70,    /* FIFO Control and test. */
            CSCR = 0x74,        /* Chip Status and Configuration Register. */
            PARA78 = 0x78,
            PARA7c = 0x7c,      /* Magic transceiver parameter register. */
            Config5 = 0xD8,    /* absent on RTL-8139A */
        };
    每个存放器都有它专门的含义和用处。举个例子(我们假设应用I/O内存的方法,ioaddr为设备内存映射在CPU内存地址空间的肇端地址,下述代码能读取板卡的版本号):
        #define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
农业生产结构            (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
        #define HW_REVID_MASK  HW_REVID(1, 1, 1, 1, 1, 1, 1)
        /* identify chip attached to board */国家企业信用公示信息系统(全国)
        version = ioread32( ioaddr + TxConfig ) & HW_REVID_MASK;
    在我的电脑上,version读出来的值的0x74400000,经由比对,板卡名称为:RTL-8100B/8139D。
    下面是全部系例版本号的表格:
    enum chip_flags {
        HasHltClk = (1 << 0),
        HasLWake = (1 << 1),
    };
    /* directly indexed by chip_t, above */
    const static struct {
        const char *name;
        u32 version; /* from RTL8139C/RTL8139D docs */
        u32 flags;
    } rtl_chip_info[] = {
        { "RTL-8139",      HW_REVID(1, 0, 0, 0, 0, 0, 0),HasHltClk,},
        { "RTL-8139 rev K", HW_REVID(1, 1, 0, 0, 0, 0, 0),HasHltClk,},
        { "RTL-8139A",      HW_REVID(1, 1, 1, 0, 0, 0, 0),HasHltClk,},
        { "RTL-8139A rev G",HW_REVID(1, 1, 1, 0, 0, 1, 0),HasHltClk,},
        { "RTL-8139B",      HW_REVID(1, 1, 1, 1, 0, 0, 0),HasLWake, },
        { "RTL-8130",      HW_REVID(1, 1, 1, 1, 1, 0, 0),HasLWake, },
        { "RTL-8139C",      HW_REVID(1, 1, 1, 0, 1, 0, 0),HasLWake, },
        { "RTL-8100",      HW_REVID(1, 1, 1, 1, 0, 1, 0),HasLWake, },
        { "RTL-8100B/8139D",HW_REVID(1, 1, 1, 0, 1, 0, 1),HasLWake, },
        { "RTL-8101",      HW_REVID(1, 1, 1, 0, 1, 1, 1),HasLWake, },
    };
    在那个表格中,RTL_8139B以下(包含RTL_8139B)的板卡都属于较新的版本。新老版本之间有不合的唤醒方法。先看新版本的:
    #define LWAKE          0x10
    #define Cfg1_PM_Enable  0x01
    u8 new_tmp8, tmp8;

    enum Config4Bits {
        LWPTN = (1 << 2),  /* not on 8139, 8139A */江都市实验小学
    };
    enum Cfg9346Bits {
        Cfg9346_Lock = 0x00,
        Cfg9346_Unlock = 0xC0,
    };
    new_tmp8 = tmp8 = ioread8( ioaddr + Config1 );
    if( (rtl_chip_info[tp->chipset].flags & HasLWake) && (tmp8 & LWAKE))
        new_tmp8 &= ~LWAKE;
    new_tmp8 |= Cfg1_PM_Enable;
    if (new_tmp8 != tmp8) {
        iowrite8( Cfg9346_Unlock, ioaddr + Cfg9346 );万向联轴器
        iowrite8( tmp8, ioaddr + Config1 );
        iowrite8( Cfg9346_Lock, ioaddr + Cfg9346 );
    }
    if (rtl_chip_info[tp->chipset].flags & HasLWake) {
        tmp8 = ioread8( ioaddr + Config4 );
        if (tmp8 & LWPTN) {
            iowrite8( Cfg9346_Unlock, ioaddr + Cfg9346 );
            iowrite8( tmp8 & ~LWPTN, ioaddr + Config4 );
            iowrite8( Cfg9346_Lock, ioaddr + Cfg9346 );
        }
    }
    全然的一个流程是:假如板卡版本本身支撑了HasLWake,而Config1中读出的值带有LWAKE,把Config1的值写回,并把Config4 中的LWPTN去除。而我的网卡中从Config1, Config4读取的值分别为0x8d, 0x88,因此,无需做任何操作。
    下面是旧版本的唤醒方法:
        enum Config1Bits {
            Cfg1_PM_Enable = 0x01,
            Cfg1_VPD_Enable = 0x02,
            Cfg1_PIO = 0x04,
            Cfg1_MMIO = 0x08,
            LWAKE = 0x10,      /* not on 8139, 8139A */
            Cfg1_Driver_Load = 0x20,
            Cfg1_LED0 = 0x40,
            Cfg1_LED1 = 0x80,
锁流光            SLEEP = (1 << 1),  /* only on 8139, 8139A */
            PWRDN = (1 << 0),  /* only on 8139, 8139A */
        };
        tmp8 = ioread8( ioaddr + Config1 );
        tmp8 &= ~(SLEEP | PWRDN);
        iowrite8( tmp8, ioaddr + Config1 );

    下面是一个板卡的复位操作:
        enum ChipCmdBits {
            CmdReset = 0x10,
            CmdRxEnb = 0x08,
            CmdTxEnb = 0x04,
            RxBufEmpty = 0x01,
        };
        static void rtl8139_chip_reset (void __iomem *ioaddr)
        {
            int i;
            /* Soft reset the chip. */
            iowrite8( CmdReset, ioaddr + ChipCmd );
            /* Check that the chip has finished the reset. */
            for (i = 1000; i > 0; i--) {
                barrier();
                if ((ioread8( ioaddr + ChipCmd ) & CmdReset) == 0)
                    break;
                udelay (10);
            }
        }
    写一个CmdReset敕令到ChipCmd地位,等待该敕令消掉,即可。
    关于网卡的存放器操作,还有一些,再进行过程中碰到时再介绍。
2. 收集设备的初始化
3.一个简单的收集流量统计法度榜样
4. net_device构造体详解
    构造体net_device代表了一个收集设备接口,它是我们明白得收集设备驱动法度榜样的关键。那个地点,我们选择个中的一些重要成员,一一作具体分析,并结合以太网设备,看看Linux内核是若何为以太网设备供给构造体中某些成员的缺省值的。
    在Linux内核源代码中是如许为那个构造体作注释的:实际上,那个构造体是一个专门大年夜的缺点,它把I/O数据和更高层的数据混淆在一路,同时它几乎必须明白INET模块中的每个数据构造。
    毫无疑问,这是一个巨型构造体。但我们为编写收集设备驱动法度榜样,只须要明白得个中的一部分,下面选择个中的一些作分析,并给出以太网设备的缺省值。
   
unsigned short  flags;
void (*set_multicast_list)(struct net_device *dev);
    这是一个接口标记,包含了专门多值的位掩码。在以太网的缺省初始化函数中,该标记
被设置为:IFF_BROADCAST|IFF_MULTICAST,表示以太网卡是可广播的,同时是能够或许进行组播发送的。别的,该标记接口还有一些只读标记,如IFF_UP,当接口被激活并能够开端传输数据包时,内核设置该标记。而IFF_PROMISC被设置或清除时,会调用set_multicast_list函数通知板卡上的硬件过滤器。

unsigned short hard_header_len;
unsigned short type;
    hard_header_len是硬件头的长度,在以太网设备的初始化函数中,该成员被赋为ETH_HLEN,即以太网头的长度,该值为14,下面是以太网头的定义:
struct ethhdr {
    unsigned char  h_dest[ETH_ALEN];  /* destination eth addr */
    unsigned char  h_source[ETH_ALEN]; /* source ether addr    */
    unsigned short  h_proto;        /* packet type ID field */
} __attribute__((packed));
    ETH_ALEN被定义为6,即以太网MAC地址的长度。h_proto储存type的值。type是接口
的硬件类型,以太网设备的初始化函数中将其赋值为ARPHRD_ETHER,即10Mb以太网。

int (*hard_header) (struct sk_buff *skb, struct net_device *dev,
                unsigned short type, void *daddr,void *saddr,
                unsigned len);
    该函数在数据被传输之前被调用,它依照先前检索到的源和目标地址建立硬件头。以太网设备的默认函数是eth_header:
int eth_header(struct sk_buff *skb, struct net_device *dev,
                unsigned short type, void *daddr,
                void *saddr, unsigned len)
{
    struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);

    /*
    *  Set the protocol type. For a packet of
    *  type ETH_P_802_3 we put the length
    *  in here instead. It is up to the 802.2
    *  layer to carry protocol information.
    */
    if(type!=ETH_P_802_3)
        eth->h_proto = htons(type);
    else
        eth->h_proto = htons(len);

    //Set the source hardware address.
    if(!saddr)
        saddr = dev->dev_addr;
    memcpy(eth->h_source,saddr,dev->addr_len);
    //Anyway, the loopback-device should never
    //use
    if (dev->flags & (IFF_LOOPBACK|IFF_NOARP)){
        memset(eth->h_dest, 0, dev->addr_len);
        return ETH_HLEN;
    }

    if(daddr){
        memcpy(eth->h_dest,daddr,dev->addr_len);
        return ETH_HLEN;
    }
    return -ETH_HLEN;
}
    它依照传入的参数建立以太网头,假如传入的源地址为空,则应用设备的地址作为源地址。与之相干的还有一个int (*rebuild_header)(struct sk_buff *skb)函数,在以太网设备中,它是用于在ARP地址解析完成今后,从新建立以太网头,主假如更新目标MAC地址,
因为在前一次建立时,因为没有经由 ARP协定,有可能目标MAC地址是缺点的(未完,待续)。
5. net_device构造体详解(续)
int (*set_mac_address)(struct net_device *dev, void *addr);
    改变网卡的mac地址。实际上,专门多硬件设备接口全然不支撑这种功能,就算支撑,也须要硬件厂商供给的对象修改EPROM中的硬件地址。一样我们在某一操作体系下所谓的修改MAC地址,只是修改了操作体系在安装网卡时从网卡EPROM中读出来的值,假如体系被重装,则MAC地址又复原为本来的值。以太网设备的默认函数是eth_mac_addr,它只是简单地修改了dev->dev_addr的值。

int (*hard_header_cache)(struct neighbour *neigh,struct hh_cache *hh);
void (*header_cache_update)(struct hh_cache *hh,struct net_device *dev,
                            unsigned char *  haddr);
int (*hard_header_parse)(struct sk_buff *skb,unsigned char *haddr);
    这三个函数在以太网设备接口中都有默认的值,hard_header_cache把硬件地址缓存到struct hh_cache中;header_cache_update在地址产生变更中,更新hh_cache构造中的目标地址;而 hard_header_parse则从skb中的数据包中获得源地址,并将其复制到位于haddr的缓冲区。

int (*change_mtu)(struct net_device *dev, int new_mtu);
    下面是以太网设备接口的change_mtu的实现:
    static int eth_change_mtu(struct net_device *dev, int new_mtu)
    {
        if ((new_mtu < 68) || (new_mtu > 1500))
            return -EINVAL;
        dev->mtu = new_mtu;
        return 0;
    }

    在net_device中,还有专门多收集驱动法度榜样必须涉及的重要成员,跟着我们的8139too网卡驱动法度榜样的进一步深刻,在涉及时再作具体分析

本文发布于:2024-09-21 12:29:09,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/167085.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:设备   地址   硬件   网卡   函数   板卡   法度
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议