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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章