linux驱动开发:了解网卡的相关函数和结构

linux四层概念模型:应用层,传输层,网际层,网络接口层
网卡设备驱动工作与网络接口层

what we care

net_device
{
    name
    base_addr//IO 基地址
    irq //中断号
    net_device_ops
    {
        netdev_tx_t     (*ndo_start_xmit) (struct sk_buff *skb,
                           struct net_device *dev);//发送数据


    }

}
申请net_dev   : alloc_etherdev(sizeof_priv)
注册      :register_netdev(struct net_device * dev)
注销      :unregister_netdev(struct net_device * dev)
//发送数据
(*ndo_start_xmit) (struct sk_buff *skb,struct net_device *dev);

接收:将网卡芯片收到的数据copy到DDRAM,形成一个struct sk_buff的结构,然后调用
netif_rx(...),驱动程序中的接收工作便完成了

sk_buff(驱动层发送,提交数据基本单元)

释放: dev_kfree_skb(...)
申请: dev_alloc_skb(...)

试着写一个虚拟网卡测试程序:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>

#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <asm/bitops.h>


static struct net_device *virnetdev = NULL;

/********   int         (*ndo_init)(struct net_device *dev)******/

static  int  vir_netdev_init(struct net_device *dev)
{
    printk("enter net dev init\n");
    // initial set
    ether_setup(dev);

    strcpy(dev->name,"vir_net");

    return 0;
}
/********   int         (*ndo_open)(struct net_device *dev)*******/

static int vir_netdev_open(struct net_device *dev)
{
    printk("enter net dev open\n");
    return 0;
}

/********   netdev_tx_t     (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev) ********/

static netdev_tx_t vir_netdev_xmit(struct sk_buff *skb, struct net_device *dev)
{
    printk("enter net dev xmit\n");

    //send data

    // free buff
    dev_kfree_skb(skb);

    return NETDEV_TX_OK;
}
static struct net_device_ops vir_netdev_ops =
{
    .ndo_init           = vir_netdev_init,
    .ndo_open           = vir_netdev_open,
    .ndo_start_xmit     = vir_netdev_xmit,
};



int __init virnet_init(void)
{
    // 1.alloc net device
    virnetdev =alloc_etherdev(0);

    // 2. full in the dev
    virnetdev->netdev_ops =&vir_netdev_ops;

    // 3 .register
    register_netdev(virnetdev);


    return 0;
}

void __exit virnet_exit(void)
{
    unregister_netdev(virnetdev);

    free_netdev(virnetdev);

}





module_init(virnet_init);
module_exit(virnet_exit);
MODULE_LICENSE("GPL");

运行效果:
这里写图片描述
初始时只有一块网卡设备eth0.
安装模块后:
这里写图片描述
vir_net网卡显示了出来
这里写图片描述
配置IP地址
这里写图片描述
ping命令测试.ping 命令并没有进入到该虚拟网卡的发送函数中,因为没有打印调试信息.


DM9000A网卡驱动移植要点:
其实当做上面的实验时,因为有用到NFS文件系统,本身内核中就已经将DM9000A的网卡驱动移植好了,但一直没有拿出来做笔记。

DM9000A
1) 10/100M 自适应
2) 8bit/16 bit 模式可配置
3) 内部有16k sram
4) 片选CS#(低电平有效.default:low, pin37)
5) CMD管脚(low:index port,high:data port pin 32)
6) INT 中断管脚 pin34
7) EECK (pin 20,high: int pin active low ,else int pin active high)
8) EECS (pin 21, EECS 如果被拉高,则bus工作于8bit mode,否则工作于16bit)
9) DM9000A每个寄存器的意义?
10)如何往DM9000A的寄存器里面写入/读取数据?REGTCR:TCR(0x02)写入0x55
index:0x02 data:0x55
1.保证片选: 基地址0x88000000 ,cs拉低
将0x80000000送到地址总线上去,此时bit2=0;
2.cmd 为低(连接到addr2上):传送0x02
volatile unsigned int *p =0x88000000
*p=0x02
cmd 为高:传送0x55
volatile unsigned int *p =0x88000004
*p=0x55
11)数据收发:chapter 9
a)将要发送的数据通过DMA方式写入TX SRAM
b)将要发送数据长度写入0xfc,0xfd
c)control reg bit 1

3)硬件原理图
1)DM9000A 的SD0-SD15连接到CPU的数据总线上
2)CMD管脚连接到 cpu的地址总线的ADDR bus的bit2
3)CS连接到CPU的xm0_csn1上 bank1:(0x8800_0000 -0x8FFF_FFFF)DM9000挂在cpu的bank1上,当你的cpu的地址总线上出现的值在 rang(0x8800_0000 -0x8FFF_FFFF)
都会导致 xm0_csn1变为低电平,也就是选中DM9000芯片
4) INT —EINT7
5) EECK low : 中断高电平有效
6) EECS low :16bit mode
这里写图片描述

网卡的驱动是基于linux平台总线设备模型的,一般情况下,这类驱动不需要修改对应的driver本身,只需要修改对应的resource结构和platform_data即可。当然要弄清楚这部分怎么修改,本身也是比较耗时的事情,尤其是弄懂其中的意义。当然需要阅读芯片手册,开发板的原理图,s5pv210的芯片手册,内核中关于dm9000a的驱动代码来分析。

//S5PV210_PA_SROM_BANK1 Bank1 base addr
static struct resource smdkv210_dm9000_resources[] = {
    [0] = DEFINE_RES_MEM(S5PV210_PA_SROM_BANK1, 1),//write index addr
    [1] = DEFINE_RES_MEM(S5PV210_PA_SROM_BANK1 + 4, 4),//write data 
    [2] = DEFINE_RES_NAMED(IRQ_EINT(7), 1, NULL, IORESOURCE_IRQ \
                | IORESOURCE_IRQ_HIGHLEVEL),
};

static struct dm9000_plat_data smdkv210_dm9000_platdata = {
    .flags      = DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM,
    /* try reading the node address from the attached EEPROM ,the address for dm9000 read its mac or others use,we dont use it*/
    .dev_addr   = { 0x00, 0x09, 0xc0, 0xff, 0xec, 0x48 },
};
static void __init smdkv210_dm9000_init(void)
{
    unsigned int tmp;

    gpio_request(S5PV210_MP01(1), "nCS1");
    s3c_gpio_cfgpin(S5PV210_MP01(1), S3C_GPIO_SFN(2));
    gpio_free(S5PV210_MP01(1));

    tmp = (5 << S5P_SROM_BCX__TACC__SHIFT);


    //S5P_SROM_BC1:Specifies the SROM Bank1 control register
    //set the BC1 Access cycle: tacc 5 clock
    __raw_writel(tmp, S5P_SROM_BC1);

    //S5P_SROM_BW:Specifies the SROM Bus width & wait control
    tmp = __raw_readl(S5P_SROM_BW);

    //just get the bit : bit7-bit4,other bit clear to 0 
    tmp &= (S5P_SROM_BW__CS_MASK << S5P_SROM_BW__NCS1__SHIFT);
    //set bank1 16 bit mode
    tmp |= (1 << S5P_SROM_BW__NCS1__SHIFT);

    __raw_writel(tmp, S5P_SROM_BW);
}

这边用到两个reg的设定,具体参考:
这里写图片描述
这里写图片描述

DM9000a内部有一个eeprom用于存放一些固定的信息,比如说mac地址。这边我们并未用这个.我们在配置它的platform_data时,也强制配置成no eeprom的模式,所以下一行的 dev ddr也就不用care,以为这是大致用来操作它本身的
eeprom的,没用到,自然可以不关心。所以写到这里,我们已经可以正常使用网卡收发数据了。当然能进入nfs文件系统本身就说明网卡的驱动是OK的.

/* try reading the node address from the attached EEPROM */
    for (i = 0; i < 6; i += 2)
        dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);

    if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
        mac_src = "platform data";
        memcpy(ndev->dev_addr, pdata->dev_addr, 6);
    }

    if (!is_valid_ether_addr(ndev->dev_addr)) {
        /* try reading from mac */

        mac_src = "chip";
        for (i = 0; i < 6; i++)
            ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
    }

    if (!is_valid_ether_addr(ndev->dev_addr)) {
        dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "
             "set using ifconfig\n", ndev->name);

        eth_hw_addr_random(ndev);
        mac_src = "random";
    }

实际上翻阅dm9000a的代码,它调用dev_adr是用来操作dm9000的eeprom的。

关于驱动,因为是基于平台总线的,我们移植时需要关系的只是对应的 资源列表和platform_data平台数据。这些都是与硬件连接相关的最底层配置。反而网卡driver那边一般是不需要配置的。这就是平台驱动,设备,总线模型开发驱动的好处.

发布了37 篇原创文章 · 获赞 5 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章