溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

【驅(qū)動(dòng)】DM9000網(wǎng)卡驅(qū)動(dòng)分析

發(fā)布時(shí)間:2020-07-27 17:01:09 來(lái)源:網(wǎng)絡(luò) 閱讀:5982 作者:infohacker 欄目:移動(dòng)開發(fā)

Preface


   內(nèi)核源碼版本:linux-2.6.18

   網(wǎng)卡驅(qū)動(dòng)·linux內(nèi)核網(wǎng)絡(luò)分層結(jié)構(gòu):http://infohacker.blog.51cto.com/6751239/1221140



DM9000芯片


   DM9000是一款高度集成低功耗快速以太網(wǎng)處理器,該芯片集成了MAC和PHY。DM9000可以和CPU直接連接,支持8位、16位和32位數(shù)據(jù)總線寬度。該芯片支持10M和100M自適應(yīng)以太網(wǎng)接口,內(nèi)部有16K的FIFO以及4K雙字節(jié)SRAM,支持全雙工工作。

   DM9000內(nèi)部還集成了接收緩沖區(qū),可以在接收到數(shù)據(jù)的時(shí)候把數(shù)據(jù)存放到緩沖區(qū)中,鏈路層可以直接把數(shù)據(jù)從緩沖區(qū)取走。



網(wǎng)卡驅(qū)動(dòng)程序框架


   在一個(gè)網(wǎng)絡(luò)驅(qū)動(dòng)程序中,一般都提供了一個(gè)platform_driver結(jié)構(gòu)變量。

platform_driver結(jié)構(gòu)包括了網(wǎng)卡驅(qū)動(dòng)的相關(guān)操作函數(shù),通過platform_driver_register()函數(shù)注冊(cè)到內(nèi)核設(shè)備驅(qū)動(dòng)列表。

   內(nèi)核會(huì)根據(jù)驅(qū)動(dòng)程序中設(shè)備描述設(shè)置網(wǎng)卡的中斷和定時(shí)器,并且在網(wǎng)絡(luò)數(shù)據(jù)包到來(lái)的時(shí)候調(diào)用網(wǎng)卡對(duì)應(yīng)的處理函數(shù)。

   通常,網(wǎng)卡需要向內(nèi)核提供下面幾個(gè)接口函數(shù):

    • probe:加載網(wǎng)卡驅(qū)動(dòng)的時(shí)候執(zhí)行,主要用于初始化網(wǎng)卡硬件接口,設(shè)置網(wǎng)絡(luò)接口函數(shù);

    • remove:卸載網(wǎng)卡驅(qū)動(dòng)的時(shí)候執(zhí)行該函數(shù),用于從系統(tǒng)中注銷網(wǎng)絡(luò)接口函數(shù);

    • suspend:在掛起網(wǎng)絡(luò)設(shè)備的時(shí)候被調(diào)用;

    • resume:在恢復(fù)網(wǎng)絡(luò)設(shè)備的時(shí)候被調(diào)用。

   網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)主要是按照內(nèi)核網(wǎng)絡(luò)數(shù)據(jù)包處理流程中用到的數(shù)據(jù)結(jié)構(gòu),設(shè)置對(duì)應(yīng)的處理函數(shù)供內(nèi)核使用。



DM9000網(wǎng)卡驅(qū)動(dòng)主要數(shù)據(jù)結(jié)構(gòu)


   DM9000網(wǎng)卡驅(qū)動(dòng)位于driver/net/dm9000.c文件,有兩個(gè)主要的數(shù)據(jù)結(jié)構(gòu)dm9000_driver和board_info。其中,dm9000_driver是platform_driver結(jié)構(gòu)。

static struct platform_driver dm9000_driver = {
    .driver = {
        .name    = "dm9000",    //網(wǎng)卡名稱
        .owner   = THIS_MODULE,
    },
    .probe   = dm9000_probe,    //加載驅(qū)動(dòng)函數(shù)
    .remove  = dm9000_drv_remove,   //刪除驅(qū)動(dòng)函數(shù)
    .suspend = dm9000_drv_suspend,  //掛起驅(qū)動(dòng)函數(shù)
    .resume  = dm9000_drv_resume,   //恢復(fù)驅(qū)動(dòng)函數(shù)
};


  • dm9000_probe()函數(shù)在加載驅(qū)動(dòng)的時(shí)候被內(nèi)核調(diào)用,用于檢測(cè)mqh上設(shè)備并且分配資源,設(shè)置網(wǎng)絡(luò)接口控制器;

  • dm9000_drv_remove()函數(shù)在卸載驅(qū)動(dòng)的時(shí)候被調(diào)用,用于釋放網(wǎng)卡驅(qū)動(dòng)占用的資源;

  • dm9000_drv_suspend()函數(shù)在掛起網(wǎng)卡的時(shí)候被調(diào)用,該函數(shù)會(huì)暫時(shí)刪除網(wǎng)絡(luò)口;

  • dm9000_drv_resume()函數(shù)在恢復(fù)網(wǎng)卡接口時(shí)被調(diào)用,該函數(shù)重新加載網(wǎng)絡(luò)接口。


   DM9000網(wǎng)卡驅(qū)動(dòng)還設(shè)置了供DM9000網(wǎng)絡(luò)控制芯片使用的 board_info結(jié)構(gòu)

/* Structure/enum declaration ------------------------------- */
typedef struct board_info {
void __iomem *io_addr;/* Register I/O base address *///控制寄存器地址
void __iomem *io_data;/* Data I/O address *///數(shù)據(jù)寄存器地址
u16 irq;/* IRQ *///中斷號(hào),在嵌入式系統(tǒng)常常無(wú)效
u16 tx_pkt_cnt;//已發(fā)送數(shù)據(jù)包個(gè)數(shù)
u16 queue_pkt_len;//數(shù)據(jù)包發(fā)送隊(duì)列中的數(shù)據(jù)包個(gè)數(shù)
u16 queue_start_addr;//數(shù)據(jù)包發(fā)送隊(duì)列的起始地址
u16 dbug_cnt;
u8 io_mode;/* 0:word, 2:byte */
u8 phy_addr;//網(wǎng)卡物理地址
void (*inblk)(void __iomem *port, void *data, int length);
void (*outblk)(void __iomem *port, void *data, int length);
void (*dumpblk)(void __iomem *port, int length);
struct resource*addr_res;   /* resources found */
struct resource *data_res;
struct resource*addr_req;   /* resources requested */
struct resource *data_req;
struct resource *irq_res;
struct timer_list timer;
struct net_device_stats stats;
unsigned char srom[128];//網(wǎng)絡(luò)控制器內(nèi)部EEPROM內(nèi)容
spinlock_t lock;
struct mii_if_info mii;
u32 msg_enable;
} board_info_t;


   board_info結(jié)構(gòu)存放在 net_device結(jié)構(gòu)的私有數(shù)據(jù)部分,DM9000驅(qū)動(dòng)的接口處理函數(shù)會(huì)使用該結(jié)構(gòu)訪問網(wǎng)絡(luò)控制芯片。

   在 board_info結(jié)構(gòu)中,

  • io_addrio_data成員變量存放了控制寄存器和數(shù)據(jù)寄存器地址;

  • tx_pkt_cnt記錄了發(fā)送數(shù)據(jù)包個(gè)數(shù);

  • queue_pkt_len記錄了發(fā)送隊(duì)列中數(shù)據(jù)包個(gè)數(shù);

  • queue_start_addr記錄了數(shù)據(jù)包發(fā)送隊(duì)列的起始地址;

  • phy_addr是網(wǎng)卡的物理地址;

  • srom是一個(gè)組,記錄了DM9000網(wǎng)絡(luò)控制芯片內(nèi)部EEPROM的內(nèi)容。



加載驅(qū)動(dòng)程序


   在dm9000.c文件中使用模塊加載宏和卸載宏設(shè)置了模塊的初始化函數(shù)dm9000_init()和卸載函數(shù)dm9000_cleanup()。

static int __init
dm9000_init(void)
{
    printk(KERN_INFO "%s Ethernet Driver\n", CARDNAME); //打印模塊啟動(dòng)信息
    return platform_driver_register(&dm9000_driver);    /* search board and register */ //調(diào)用驅(qū)動(dòng)注冊(cè)函數(shù)
}
static void __exit
dm9000_cleanup(void)
{
    platform_driver_unregister(&dm9000_driver); //調(diào)用驅(qū)動(dòng)卸載函數(shù)
}
module_init(dm9000_init);   //設(shè)置模塊啟動(dòng)函數(shù)
module_exit(dm9000_cleanup);    //設(shè)置模塊卸載函數(shù)


   設(shè)置好驅(qū)動(dòng)函數(shù)的初始化后,在啟動(dòng)的時(shí)候會(huì)注冊(cè) dm9000_driver結(jié)構(gòu)到內(nèi)核,內(nèi)核會(huì)調(diào)用 dm9000 driver結(jié)構(gòu)中的 probe函數(shù)成員,也就是調(diào)用 dm9000_probe()函數(shù)設(shè)置網(wǎng)卡驅(qū)動(dòng)。

   函數(shù)執(zhí)行過程如下。



步驟一


   函數(shù)首先是分配 board_info結(jié)構(gòu)占用的私有資源,在程序中使用 alloc_etherdev()函數(shù)分配網(wǎng)卡驅(qū)動(dòng)使用的私有資源。

   如果分配資源失敗,提示出錯(cuò)wyth 并且退出函數(shù),設(shè)置返回值為 -ENOMEM,表示沒有內(nèi)存。

static int
dm9000_probe(struct platform_device *pdev)
{
    struct dm9000_plat_data *pdata = pdev->dev.platform_data;
    struct board_info *db;  /* Point a board information structure */
    struct net_device *ndev;
    unsigned long base;
    int ret = 0;
    int iosize;
    int i;
    u32 id_val;
    /* Init network device */
    ndev = alloc_etherdev(sizeof (struct board_info));  //分配資源,在私有數(shù)據(jù)區(qū)保存 board_info內(nèi)容
    if (!ndev) {
        printk("%s: could not allocate device.\n", CARDNAME);
        return -ENOMEM;
    }
    SET_MODULE_OWNER(ndev); //該宏是空定義,被忽略,下面一行相同
    SET_NETDEV_DEV(ndev, &pdev->dev);
    PRINTK2("dm9000_probe()");



步驟二


   分配資源成功后,開始初始化 board_info結(jié)構(gòu),設(shè)置結(jié)構(gòu)的值為0,然后初始化 spin_lock,spin_lock稱做自旋鎖,是內(nèi)核中用于臨界資源的一種結(jié)構(gòu)。

   初始化自旋鎖以后,程序分配網(wǎng)絡(luò)適配器 I/O地址寄存器、數(shù)據(jù)寄存器用到的內(nèi)存,并且映射到內(nèi)核空間。

/* setup board info structure */
db = (struct board_info *) ndev->priv;
memset(db, 0, sizeof (*db));    //初始化 board_info結(jié)構(gòu)為0
spin_lock_init(&db->lock);   //初始化 spin_lock
if (pdev->num_resources < 2) {    //檢查是否安裝多個(gè)網(wǎng)絡(luò)適配器
    ret = -ENODEV;
    goto out;
} else if (pdev->num_resources == 2) {
    base = pdev->resource[0].start;
    if (!request_mem_region(base, 4, ndev->name)) {  //分配網(wǎng)絡(luò)適配器結(jié)構(gòu)占用的內(nèi)存
        ret = -EBUSY;
        goto out;
    }
    ndev->base_addr = base;  //設(shè)置網(wǎng)絡(luò)適配器 I/O基址
    ndev->irq = pdev->resource[1].start;  //設(shè)置網(wǎng)絡(luò)適配器中斷地址
    db->io_addr = (void __iomem *)base;  //設(shè)置網(wǎng)絡(luò)適配器地址寄存器基址
    db->io_data = (void __iomem *)(base + 4);    //設(shè)置網(wǎng)絡(luò)適配器數(shù)據(jù)寄存器基址
} else {
    db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);   //獲取 I/O地址
    db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);   //獲取 IRQ地址
    if (db->addr_res == NULL || db->data_res == NULL ||
        db->irq_res == NULL) {   //檢查網(wǎng)絡(luò)適配器用到的內(nèi)存地址是否有效
        printk(KERN_ERR PFX "insufficient resources\n");
        ret = -ENOENT;
        goto out;
    }
    i = res_size(db->addr_res);  //計(jì)算地址寄存器空間
    db->addr_req = request_mem_region(db->addr_res->start, i,
                      pdev->name);   //請(qǐng)求內(nèi)存地址
    if (db->addr_req == NULL) {  //檢查地址寄存器是否有效
        printk(KERN_ERR PFX "cannot claim address reg area\n");
        ret = -EIO;
        goto out;
    }
    db->io_addr = ioremap(db->addr_res->start, i); //映射網(wǎng)絡(luò)適配器 I/O地址
    if (db->io_addr == NULL) {   //檢查網(wǎng)絡(luò)適配器 I/O地址是否有效
        printk(KERN_ERR "failed to ioremap address reg\n");
        ret = -EINVAL;
        goto out;
    }
    iosize = res_size(db->data_res); //計(jì)算數(shù)據(jù)寄存器地址空間
    db->data_req = request_mem_region(db->data_res->start, iosize,
                      pdev->name);   //請(qǐng)求內(nèi)存地址
    if (db->data_req == NULL) {  //檢查數(shù)據(jù)寄存器地址是否有效
        printk(KERN_ERR PFX "cannot claim data reg area\n");
        ret = -EIO;
        goto out;
    }
    db->io_data = ioremap(db->data_res->start, iosize);    //映射網(wǎng)絡(luò)適配器數(shù)據(jù)寄存器地址
    if (db->io_data == NULL) {   //檢查 I/O地址是否有效
        printk(KERN_ERR "failed to ioremap data reg\n");
        ret = -EINVAL;
        goto out;
    }
    /* fill in parameters for net-dev structure */  //填充網(wǎng)絡(luò)設(shè)備數(shù)據(jù)結(jié)構(gòu)
    ndev->base_addr = (unsigned long)db->io_addr; //設(shè)置網(wǎng)絡(luò)設(shè)備 I/O地址
    ndev->irq    = db->irq_res->start; //設(shè)置IRQ
    /* ensure at least we have a default set of IO routines */
    dm9000_set_io(db, iosize);  //設(shè)置DM9000寄存器地址
}



步驟三


   檢查是否需要繼承系統(tǒng)提供的函數(shù)。

   初始化網(wǎng)絡(luò)適配器數(shù)據(jù)結(jié)構(gòu)后,需要設(shè)置網(wǎng)絡(luò)設(shè)備用到的回調(diào)函數(shù)。

/* check to see if anything is being over-ridden */ //檢查是否有繼承的數(shù)據(jù)
if (pdata != NULL) {
    /* check to see if the driver wants to over-ride the
     * default IO width */  //檢查是否繼承默認(rèn) I/O寬度
    if (pdata->flags & DM9000_PLATF_8BITONLY)    // 8位 I/O位寬
        dm9000_set_io(db, 1);
    if (pdata->flags & DM9000_PLATF_16BITONLY)   // 16位 I/O位寬
        dm9000_set_io(db, 2);
    if (pdata->flags & DM9000_PLATF_32BITONLY)   // 32位 I/O位寬
        dm9000_set_io(db, 4);
    /* check to see if there are any IO routine
     * over-rides */    //檢查是否有繼承的函數(shù)
    if (pdata->inblk != NULL)
        db->inblk = pdata->inblk; //入鏈路函數(shù)
    if (pdata->outblk != NULL)
        db->outblk = pdata->outblk;   //出鏈路
    if (pdata->dumpblk != NULL)
        db->dumpblk = pdata->dumpblk; // dump()函數(shù)
}



步驟四


   設(shè)置DM9000網(wǎng)絡(luò)適配器芯片ID。

dm9000_reset(db);   //復(fù)位DM9000網(wǎng)絡(luò)控制芯片
/* try two times, DM9000 sometimes gets the first read wrong */
for (i = 0; i < 2; i++) {    //讀取芯片 ID,需要讀兩次,這是芯片的一個(gè)Bug
    id_val  = ior(db, DM9000_VIDL);
    id_val |= (u32)ior(db, DM9000_VIDH) << 8;
    id_val |= (u32)ior(db, DM9000_PIDL) << 16;
    id_val |= (u32)ior(db, DM9000_PIDH) << 24;
    if (id_val == DM9000_ID)
        break;
    printk("%s: read wrong id 0x%08x\n", CARDNAME, id_val);
}
if (id_val != DM9000_ID) {  //檢查芯片 ID是否正確
    printk("%s: wrong id: 0x%08x\n", CARDNAME, id_val);
    goto release;
}

   這里的for循環(huán)從DM9000網(wǎng)絡(luò)控制器的寄存器兩次讀取芯片ID,這樣做的目的是因?yàn)樾酒腂ug,第一次讀取的芯片ID是錯(cuò)誤的,需要再讀取一次。

   讀取芯片ID結(jié)束后,程序驗(yàn)證芯片ID是否正確,如果不正確,跳轉(zhuǎn)到release標(biāo)簽,清空分配的內(nèi)存,退出函數(shù)。



步驟五


   設(shè)置網(wǎng)絡(luò)適配器用到的回調(diào)函數(shù)。

/* from this point we assume that we have found a DM9000 */
/* driver system function */
ether_setup(ndev);//初始化網(wǎng)絡(luò)控制芯片內(nèi)部結(jié)構(gòu)
ndev->open = &dm9000_open;//設(shè)置打開網(wǎng)卡驅(qū)動(dòng)函數(shù)
ndev->hard_start_xmit    = &dm9000_start_xmit;//設(shè)置發(fā)送數(shù)據(jù)包函數(shù)
ndev->tx_timeout         = &dm9000_timeout;//設(shè)置超時(shí)處理函數(shù)
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);//設(shè)置超時(shí)時(shí)間
ndev->stop = &dm9000_stop;//設(shè)置停止網(wǎng)卡回調(diào)函數(shù)
ndev->get_stats = &dm9000_get_stats;//設(shè)置獲取網(wǎng)卡信息函數(shù)
ndev->set_multicast_list = &dm9000_hash_table;
#ifdef CONFIG_NET_POLL_CONTROLLER
ndev->poll_controller = &dm9000_poll_controller;
#endif

   程序中使用 ether_setup()函數(shù)設(shè)置了網(wǎng)卡的驅(qū)動(dòng)描述結(jié)構(gòu),然后設(shè)置網(wǎng)卡打開、接收數(shù)據(jù)包、發(fā)送數(shù)據(jù)包超時(shí)處理函數(shù)。



步驟六


   設(shè)置回調(diào)函數(shù)后,設(shè)置MII接口,MII接口不是所有的處理器都支持,設(shè)置的目的是供支持MII接口的處理器使用。

#ifdef DM9000_PROGRAM_EEPROM
    program_eeprom(db); //更新網(wǎng)絡(luò)控制器內(nèi)部EEPROM
#endif
    db->msg_enable       = NETIF_MSG_LINK;   //設(shè)置MII接口
    db->mii.phy_id_mask  = 0x1f;
    db->mii.reg_num_mask = 0x1f;
    db->mii.force_media  = 0;
    db->mii.full_duplex  = 0;
    db->mii.dev       = ndev;
    db->mii.mdio_read    = dm9000_phy_read;  // MII方式讀函數(shù)
    db->mii.mdio_write   = dm9000_phy_write; // MII方式寫函數(shù)
    /* Read SROM content */
    for (i = 0; i < 64; i++) //讀取 EEPROM內(nèi)容,每次讀 16b
        ((u16 *) db->srom)[i] = read_srom_word(db, i);
    /* Set Node Address */
    for (i = 0; i < 6; i++)  // EEPROM的前6個(gè)字節(jié)是MAC地址,存放到 board_info結(jié)構(gòu)內(nèi)
        ndev->dev_addr[i] = db->srom[i];
    if (!is_valid_ether_addr(ndev->dev_addr)) {  //驗(yàn)證MAC地址是否合法
        /* try reading from mac */
        for (i = 0; i < 6; i++)  //如果是不合法地址,則從DM9000內(nèi)部寄存器中重新讀取 MAC地址
            ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
    }
    if (!is_valid_ether_addr(ndev->dev_addr))    //兩次驗(yàn)證MAC地址
        printk("%s: Invalid ethernet MAC address.  Please "
               "set using ifconfig\n", ndev->name);
    platform_set_drvdata(pdev, ndev);
    ret = register_netdev(ndev);    //注冊(cè)網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)
    if (ret == 0) { //打印網(wǎng)卡信息
        printk("%s: dm9000 at %p,%p IRQ %d MAC: ",
               ndev->name,  db->io_addr, db->io_data, ndev->irq);
        for (i = 0; i < 5; i++)
            printk("%02x:", ndev->dev_addr[i]);
        printk("%02x\n", ndev->dev_addr[5]);
    }
    return 0;


   程序讀取 EEPROM的內(nèi)容到 board_info結(jié)構(gòu),每次讀取16b數(shù)據(jù)。讀取結(jié)束后,再讀取 EEPROM的前6個(gè)字節(jié),該處存放了網(wǎng)卡的 MAC地址。

   讀取 MAC地址后,使用 is_valid_ether_addr()函數(shù)驗(yàn)證 MAC地址是否正確,如果不正確,則從DM9000網(wǎng)絡(luò)控制器的寄存器中重新讀取 MAC地址,然后兩次比較,如果 MAC地址還是錯(cuò)誤,提示用戶使用 ifconfig命令設(shè)置 MAC地址。

   驗(yàn)證 MAC地址結(jié)束后,程序使用 register_netdev()函數(shù)注冊(cè)網(wǎng)絡(luò)驅(qū)動(dòng)到內(nèi)核。如果注冊(cè)成功,則打印網(wǎng)卡的基本信息。



步驟七


   出錯(cuò)處理。

   函數(shù)的最后是出錯(cuò)處理,使用 releaseout標(biāo)號(hào)標(biāo)記,對(duì)應(yīng)不同的處理流程。函數(shù)在執(zhí)行過程中,可以使用 goto語(yǔ)言直接轉(zhuǎn)到出錯(cuò)處理代碼。

   出錯(cuò)處理的主要功能是釋放網(wǎng)絡(luò)適配器用到的數(shù)據(jù)結(jié)構(gòu),然后返回出錯(cuò)值。

release:
 out:
    printk("%s: not found (%d).\n", CARDNAME, ret);
    dm9000_release_board(pdev, db);
    kfree(ndev);
    return ret;
}



停止網(wǎng)卡


   停止網(wǎng)卡是用戶使用 ifdown命令設(shè)置網(wǎng)卡暫時(shí)停止,用戶的命令通過系統(tǒng)調(diào)用最終會(huì)調(diào)用網(wǎng)卡驅(qū)動(dòng)的停止函數(shù),對(duì)于 DM9000網(wǎng)卡驅(qū)動(dòng)來(lái)說(shuō) dm9000_stop()函數(shù)

static void
dm9000_shutdown(struct net_device *dev)
{
    board_info_t *db = (board_info_t *) dev->priv;
    /* RESET device */
    dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */ //重啟 PHY
    iow(db, DM9000_GPR, 0x01);  /* Power-Down PHY */    //關(guān)閉 PHY
    iow(db, DM9000_IMR, IMR_PAR);   /* Disable all interrupt */ //屏蔽所有中斷
    iow(db, DM9000_RCR, 0x00);  /* Disable RX */    //停止接收數(shù)據(jù)包
}
/*
 * Stop the interface.
 * The interface is stopped when it is brought.
 */
static int
dm9000_stop(struct net_device *ndev)
{
    board_info_t *db = (board_info_t *) ndev->priv;
    PRINTK1("entering %s\n",__FUNCTION__);
    /* deleted timer */
    del_timer(&db->timer);   //刪除定時(shí)器
    netif_stop_queue(ndev); //停止數(shù)據(jù)包發(fā)送隊(duì)列
    netif_carrier_off(ndev);
    /* free interrupt */
    free_irq(ndev->irq, ndev);   //釋放所有中斷請(qǐng)求
    dm9000_shutdown(ndev);
    return 0;
}


   函數(shù)首先刪除網(wǎng)絡(luò)驅(qū)動(dòng)的定時(shí)器,然后停止數(shù)據(jù)包發(fā)送隊(duì)列工作、釋放中斷請(qǐng)求,最后調(diào)用 dm9000_shutdown()函數(shù)。

   dm9000_shutdown()函數(shù)的作用是重啟設(shè)備,然后通過設(shè)置網(wǎng)絡(luò)控制器的控制寄存器關(guān)閉芯片的部分電源和中斷,并且停止接收數(shù)據(jù)包。



啟動(dòng)網(wǎng)卡


   與關(guān)閉網(wǎng)卡相反,用戶使用 ifup命令可以啟動(dòng)一個(gè)網(wǎng)卡,內(nèi)核會(huì)調(diào)用一個(gè)網(wǎng)卡的啟動(dòng)函數(shù)。

   DM9000網(wǎng)卡的 dm9000_open()函數(shù)供內(nèi)核在啟動(dòng)網(wǎng)卡時(shí)調(diào)用。

/*
 *  Open the interface.
 *  The interface is opened whenever "ifconfig" actives it.
 */
static int
dm9000_open(struct net_device *dev)
{
    board_info_t *db = (board_info_t *) dev->priv;
    PRINTK2("entering dm9000_open\n");
    if (request_irq(dev->irq, &dm9000_interrupt, IRQF_SHARED, dev->name, dev))
        return -EAGAIN;
    /* Initialize DM9000 board */
    dm9000_reset(db);   //重啟網(wǎng)絡(luò)控制芯片
    dm9000_init_dm9000(dev);    //初始化網(wǎng)絡(luò)控制芯片
    /* Init driver variable */
    db->dbug_cnt = 0;
    /* set and active a timer process */
    init_timer(&db->timer);  //初始化定時(shí)器
    db->timer.expires  = DM9000_TIMER_WUT;   //設(shè)置超時(shí)值
    db->timer.data     = (unsigned long) dev;
    db->timer.function = &dm9000_timer;  //設(shè)置超時(shí)處理函數(shù)
    add_timer(&db->timer);   //添加定時(shí)器
    mii_check_media(&db->mii, netif_msg_link(db), 1);    //檢查 MII接口
    netif_start_queue(dev); //啟動(dòng)包發(fā)送隊(duì)列
    return 0;
}


   程序中使用 request_irq()函數(shù)申請(qǐng)中斷請(qǐng)求,然后重啟網(wǎng)絡(luò)控制芯片,然后調(diào)用 dm9000_init_dm9000()函數(shù)初始化網(wǎng)絡(luò)控制芯片。

   網(wǎng)絡(luò)控制芯片設(shè)置完畢后,程序初始化定時(shí)器,然后設(shè)置定時(shí)器超時(shí)值,添加定時(shí)器超時(shí)處理函數(shù)。

   程序最后檢查MII接口,然后調(diào)用 netif_start_queue()函數(shù)啟動(dòng)包發(fā)送隊(duì)列。



發(fā)送數(shù)據(jù)包


   網(wǎng)卡驅(qū)動(dòng)程序需要向內(nèi)核提供兩個(gè)發(fā)送數(shù)據(jù)包的回調(diào)函數(shù),一個(gè)用于發(fā)送數(shù)據(jù)包,一個(gè)用于數(shù)據(jù)包發(fā)送完畢后的處理。

   DM9000向內(nèi)核提供 dm9000_start_xmit()函數(shù)用于發(fā)送數(shù)據(jù)包

/*
 *  Hardware start transmission.
 *  Send a packet to media from the upper layer.
 */
static int
dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    board_info_t *db = (board_info_t *) dev->priv;
    PRINTK3("dm9000_start_xmit\n");
    if (db->tx_pkt_cnt > 1)
        return 1;
    netif_stop_queue(dev);  //停止接收隊(duì)列
    /* Disable all interrupts */
    iow(db, DM9000_IMR, IMR_PAR);   //關(guān)閉所有中斷
    /* Move data to DM9000 TX RAM */
    writeb(DM9000_MWCMD, db->io_addr);   //設(shè)置網(wǎng)卡控制器的控制寄存器
    (db->outblk)(db->io_data, skb->data, skb->len); //復(fù)制 sk_buff的數(shù)據(jù)到網(wǎng)卡控制器的 SRAM
    db->stats.tx_bytes += skb->len;   //發(fā)送字節(jié)數(shù)統(tǒng)計(jì)加上當(dāng)前數(shù)據(jù)包長(zhǎng)度
    /* TX control: First packet immediately send, second packet queue */
    if (db->tx_pkt_cnt == 0) {   //判斷是否第一次發(fā)送數(shù)據(jù)包
        /* First Packet */
        db->tx_pkt_cnt++;    //發(fā)送數(shù)據(jù)包總結(jié)加1
        /* Set TX length to DM9000 */
        iow(db, DM9000_TXPLL, skb->len & 0xff);  //設(shè)置 DM9000的發(fā)送數(shù)據(jù)長(zhǎng)度寄存器
        iow(db, DM9000_TXPLH, (skb->len >> 8) & 0xff);
        /* Issue TX polling command */
        iow(db, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */ //設(shè)定發(fā)送請(qǐng)求
        dev->trans_start = jiffies;  /* save the time stamp */   //寫入發(fā)送數(shù)據(jù)包的時(shí)間戳
    } else {
        /* Second packet */
        db->tx_pkt_cnt++;
        db->queue_pkt_len = skb->len;
    }
    /* free this SKB */
    dev_kfree_skb(skb); //釋放數(shù)據(jù)包緩存
    /* Re-enable resource check */
    if (db->tx_pkt_cnt == 1) //檢查第一個(gè)包是否發(fā)送完畢
        netif_wake_queue(dev);  //如果發(fā)送完畢可以重啟接收隊(duì)列
    /* Re-enable interrupt */
    iow(db, DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM);   //打開 DM9000中斷,如果此時(shí)數(shù)據(jù)包成功發(fā)送,
                                                        //會(huì)收到 DM9000發(fā)送的中斷,轉(zhuǎn)到驅(qū)動(dòng)的中斷處理函數(shù)進(jìn)行處理
    return 0;
}


   發(fā)送數(shù)據(jù)包的流程需要考慮到內(nèi)核數(shù)據(jù)包隊(duì)列和中斷控制器。

   程序首先使用 netif_stop_queue()函數(shù)停止接收隊(duì)列,該隊(duì)列是內(nèi)核與網(wǎng)卡驅(qū)動(dòng)之間的數(shù)據(jù)包隊(duì)列,內(nèi)核把發(fā)送的數(shù)據(jù)包放到隊(duì)列中,網(wǎng)卡驅(qū)動(dòng)從隊(duì)列中取出數(shù)據(jù)包進(jìn)行發(fā)送。

   關(guān)閉隊(duì)列后,程序操作 DM9000的控制寄存器,關(guān)閉中斷請(qǐng)求,目的是防止在發(fā)送數(shù)據(jù)包的過程中被打斷,因?yàn)閮?nèi)核的代碼都是可重入的,這點(diǎn)需要注意。

   程序第16行設(shè)置 DM9000的控制寄存器通知 DM9000開始內(nèi)存復(fù)制,然后把發(fā)適的數(shù)據(jù)包 sk_buff中的內(nèi)容復(fù)制到 DM9000內(nèi)部的SRAM,然后更新網(wǎng)卡發(fā)送字節(jié)統(tǒng)計(jì)。

   程序進(jìn)行發(fā)送流程時(shí),首先通過 tx_pkt_cnt變量判斷是否發(fā)送第一個(gè)數(shù)據(jù)包,DM9000的驅(qū)動(dòng)設(shè)計(jì)第一個(gè)數(shù)據(jù)包可以被發(fā)送,第二個(gè)數(shù)據(jù)包通過 dm9000_tx_done()函數(shù)發(fā)送。如果發(fā)送的是第一數(shù)據(jù)包,則程序把發(fā)送數(shù)據(jù)包個(gè)數(shù)加1,通過設(shè)置 DM9000控制寄存器,通知發(fā)送數(shù)據(jù)包長(zhǎng)度,然后向 DM9000寫入發(fā)送命令。

   設(shè)置發(fā)送數(shù)據(jù)包后,可以認(rèn)為數(shù)據(jù)包已經(jīng)發(fā)送出去,而發(fā)送的狀態(tài)需要通過中斷得到。接下來(lái),程序釋放已經(jīng)發(fā)送數(shù)據(jù)包的 sk_buff,然后檢查 tx_pkt_cnt,判斷數(shù)據(jù)包是否已經(jīng)發(fā)送。如果數(shù)據(jù)包已經(jīng)發(fā)送,則通過 netif_wake_queue()函數(shù)重新開啟接收隊(duì)列。

   最后,在程序中寫入 DM9000的命令打開中斷響應(yīng),如果數(shù)據(jù)包已經(jīng)發(fā)送,驅(qū)動(dòng)程序會(huì)收到 DM9000控制器發(fā)送的中斷。

   數(shù)據(jù)包發(fā)送完畢后,內(nèi)核會(huì)調(diào)用后續(xù)的處理函數(shù),DM9000驅(qū)動(dòng)程序提供了 dm9000_tx_done()函數(shù)

/*
 * DM9000 interrupt handler
 * receive the packet to upper layer, free the transmitted packet
 */
static void
dm9000_tx_done(struct net_device *dev, board_info_t * db)
{
    int tx_status = ior(db, DM9000_NSR);    /* Got TX status */
    if (tx_status & (NSR_TX2END | NSR_TX1END)) {    //判斷是否已經(jīng)有一個(gè)數(shù)據(jù)包發(fā)送完畢
        /* One packet sent complete */
        db->tx_pkt_cnt--;
        db->stats.tx_packets++;
        /* Queue packet check & send */
        if (db->tx_pkt_cnt > 0) { //判斷緩沖區(qū)是否有未發(fā)送的數(shù)據(jù)包
            iow(db, DM9000_TXPLL, db->queue_pkt_len & 0xff); //設(shè)置發(fā)送數(shù)據(jù)包長(zhǎng)度
            iow(db, DM9000_TXPLH, (db->queue_pkt_len >> 8) & 0xff);
            iow(db, DM9000_TCR, TCR_TXREQ); //啟動(dòng)數(shù)據(jù)包發(fā)送
            dev->trans_start = jiffies;
        }
        netif_wake_queue(dev);  //通知內(nèi)核開啟接收隊(duì)列
    }
}


   程序首先判斷是否已經(jīng)有一個(gè)數(shù)據(jù)包被成功發(fā)送,如果已經(jīng)有數(shù)據(jù)包功能發(fā)送,則進(jìn)入第二個(gè)數(shù)據(jù)包處理。程序通過判斷緩沖區(qū)是否有未發(fā)送的數(shù)據(jù)包,如果有,則通知 DM9000控制器數(shù)據(jù)包的長(zhǎng)度,然后寫入命令發(fā)送數(shù)據(jù)包。

   數(shù)據(jù)包發(fā)送完畢后,程序開啟內(nèi)核接收數(shù)據(jù)包隊(duì)列。



接收數(shù)據(jù)包


   DM9000向內(nèi)核提供了dm9000_rx()函數(shù),在內(nèi)核收到DM9000網(wǎng)絡(luò)控制器的接收數(shù)據(jù)包中斷后被內(nèi)核調(diào)用。dm9000_rx()函數(shù)使用了一個(gè)自定義的dm9000_rxhdr結(jié)構(gòu),該結(jié)構(gòu)與DM9000網(wǎng)絡(luò)控制器提供的數(shù)據(jù)包接收信息對(duì)應(yīng)。

struct dm9000_rxhdr {
    u16 RxStatus;
    u16 RxLen;
} __attribute__((__packed__));
/*
 *  Received a packet and pass to upper layer
 */
static void
dm9000_rx(struct net_device *dev)
{
    board_info_t *db = (board_info_t *) dev->priv;
    struct dm9000_rxhdr rxhdr;
    struct sk_buff *skb;
    u8 rxbyte, *rdptr;
    int GoodPacket;
    int RxLen;
    /* Check packet ready or not */
    do {
        ior(db, DM9000_MRCMDX); /* Dummy read */
        /* Get most updated data */
        rxbyte = readb(db->io_data); //讀取網(wǎng)絡(luò)控制器狀態(tài)
        /* Status check: this byte must be 0 or 1 */
        if (rxbyte > DM9000_PKT_RDY) {   //判斷狀態(tài)是否正確
            printk("status check failed: %d\n", rxbyte);
            iow(db, DM9000_RCR, 0x00);  /* Stop Device */   //停止網(wǎng)絡(luò)控制器
            iow(db, DM9000_ISR, IMR_PAR);   /* Stop INT request */  //停止中斷請(qǐng)求
            return;
        }
        if (rxbyte != DM9000_PKT_RDY)
            return;
        /* A packet ready now  & Get status/length */
        GoodPacket = TRUE;
        writeb(DM9000_MRCMD, db->io_addr);   //向控制器發(fā)起讀命令
        (db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));  //讀取包頭
        RxLen = rxhdr.RxLen;    //讀取包長(zhǎng)度
        /* Packet Status check */
        if (RxLen < 0x40) {  //判斷數(shù)據(jù)包是否小于64字節(jié)
            GoodPacket = FALSE;
            PRINTK1("Bad Packet received (runt)\n");
        }
        if (RxLen > DM9000_PKT_MAX) {    //判斷數(shù)據(jù)包是否超過 1536字節(jié)
            PRINTK1("RST: RX Len:%x\n", RxLen);
        }
        if (rxhdr.RxStatus & 0xbf00) {  //檢查接收狀態(tài)是否出錯(cuò)
            GoodPacket = FALSE;
            if (rxhdr.RxStatus & 0x100) {   // FIFO 錯(cuò)誤
                PRINTK1("fifo error\n");
                db->stats.rx_fifo_errors++;
            }
            if (rxhdr.RxStatus & 0x200) {   // CRC 錯(cuò)誤
                PRINTK1("crc error\n");
                db->stats.rx_crc_errors++;
            }
            if (rxhdr.RxStatus & 0x8000) {  // 包長(zhǎng)度錯(cuò)誤
                PRINTK1("length error\n");
                db->stats.rx_length_errors++;
            }
        }
        /* Move data from DM9000 */
        if (GoodPacket
            && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {    //分配 sk_buff
            skb->dev = dev;
            skb_reserve(skb, 2);
            rdptr = (u8 *) skb_put(skb, RxLen - 4);
            /* Read received packet from RX SRAM */
            (db->inblk)(db->io_data, rdptr, RxLen);   //所數(shù)據(jù)包從 DM9000控制器復(fù)制到 sk_buff
            db->stats.rx_bytes += RxLen; //更新包計(jì)數(shù)器
            /* Pass to upper layer */
            skb->protocol = eth_type_trans(skb, dev);
            netif_rx(skb);
            db->stats.rx_packets++;
        } else {
            /* need to dump the packet's data */
            (db->dumpblk)(db->io_data, RxLen);
        }
    } while (rxbyte == DM9000_PKT_RDY); //判斷網(wǎng)絡(luò)控制器處理準(zhǔn)備好狀態(tài)
}


   程序中 dm9000_rxhdr結(jié)構(gòu)的 RxStatus成員變量存放接收數(shù)據(jù)包的狀態(tài),RxLen存放接收到的數(shù)據(jù)包長(zhǎng)度。dm9000_rx()函數(shù)內(nèi)部是一個(gè)大的 do……while{}循環(huán)。

   從18行開始,首先獲取網(wǎng)絡(luò)控制器狀態(tài),然后判斷網(wǎng)絡(luò)控制器狀態(tài)是否正確。如果網(wǎng)絡(luò)控制器狀態(tài)不正確,則停止網(wǎng)絡(luò)控制器,并且屏蔽中斷請(qǐng)求。

如果網(wǎng)絡(luò)控制器處理"準(zhǔn)備好"的狀態(tài),則向網(wǎng)絡(luò)控制器發(fā)起讀數(shù)據(jù)包命令。

   程序從34行讀取數(shù)據(jù)包頭,然后取出包長(zhǎng)。再判斷數(shù)據(jù)包長(zhǎng)度是否小于64字節(jié),因?yàn)?span >以太網(wǎng)協(xié)議規(guī)定,小于64字節(jié)的數(shù)據(jù)包是錯(cuò)誤的。

   在從網(wǎng)絡(luò)控制器接收數(shù)據(jù)包內(nèi)容之前,程序首先在使用 dev_alloc_skb()函數(shù)分配了一個(gè) sk_buff緩沖區(qū),用于存放數(shù)據(jù)包,然后把數(shù)據(jù)包從網(wǎng)絡(luò)控制器的 SRAM復(fù)制到 sk_buff,再更新字節(jié)計(jì)數(shù)器。

   新的數(shù)據(jù)包收到后,就可以通知上層協(xié)議棧處理了,程序使用 eth_type_trans()函數(shù)把數(shù)據(jù)包丟給協(xié)議棧,然后更新包計(jì)數(shù)器。



中斷和定時(shí)器處理


   網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)需要提供中斷處理函數(shù)和定時(shí)處理函數(shù)供內(nèi)核使用。

  • 中斷處理函數(shù)當(dāng)網(wǎng)絡(luò)控制器向CPU發(fā)出中斷后,由內(nèi)核中斷處理函數(shù)調(diào)用。

  • 定時(shí)器處理函數(shù)是由內(nèi)核的一個(gè)定時(shí)器周期的調(diào)用。

   DM9000網(wǎng)卡驅(qū)動(dòng)設(shè)計(jì)了 dm9000_interrupt()函數(shù)響應(yīng)網(wǎng)絡(luò)控制發(fā)送的中斷請(qǐng)求。

static irqreturn_t
dm9000_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    struct net_device *dev = dev_id;
    board_info_t *db;
    int int_status;
    u8 reg_save;
    PRINTK3("entering %s\n",__FUNCTION__);
    if (!dev) { //檢查網(wǎng)絡(luò)設(shè)備是否存在
        PRINTK1("dm9000_interrupt() without DEVICE arg\n");
        return IRQ_HANDLED;
    }
    /* A real interrupt coming */
    db = (board_info_t *) dev->priv;
    spin_lock(&db->lock);    //對(duì)臨界資源加鎖
    /* Save previous register address */
    reg_save = readb(db->io_addr);   //保存當(dāng)前中斷寄存器的值
    /* Disable all interrupts */
    iow(db, DM9000_IMR, IMR_PAR);   //關(guān)閉所有中斷請(qǐng)求
    /* Got DM9000 interrupt status */
    int_status = ior(db, DM9000_ISR);   /* Got ISR */   //獲取 ISR
    iow(db, DM9000_ISR, int_status);    /* Clear ISR status */  //清除 ISR狀態(tài)
    /* Received the coming packet */
    if (int_status & ISR_PRS)   //判斷是否收到數(shù)據(jù)包中斷
        dm9000_rx(dev); //調(diào)用接收數(shù)據(jù)包函數(shù)處理
    /* Trnasmit Interrupt check */
    if (int_status & ISR_PTS)   //判斷是否發(fā)送數(shù)據(jù)包中斷
        dm9000_tx_done(dev, db);    //調(diào)用發(fā)送數(shù)據(jù)包函數(shù)處理
    /* Re-enable interrupt mask */
    iow(db, DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM);   //打開中斷請(qǐng)求
    /* Restore previous register address */
    writeb(reg_save, db->io_addr);   //恢復(fù)中斷處理前中斷寄存器的值
    spin_unlock(&db->lock);  //對(duì)臨界資源解鎖
    return IRQ_HANDLED;
}


   DM9000的中斷處理函數(shù)只處理網(wǎng)絡(luò)控制器發(fā)送的接收數(shù)據(jù)包發(fā)送數(shù)據(jù)包請(qǐng)求。

   進(jìn)入函數(shù)后,程序街道檢查內(nèi)核傳入的網(wǎng)絡(luò)設(shè)備句柄是否合法,不合法則直接退出函數(shù)。如果是合法的網(wǎng)絡(luò)設(shè)備句柄,則對(duì)網(wǎng)絡(luò)設(shè)備加鎖,防止其它例程處理。

   然后取出當(dāng)前中斷寄存器的值保存,關(guān)閉中斷請(qǐng)求,并處理 DM9000的ISR。

   前面的工作都是建立中斷處理的環(huán)境,接下在程序判斷是否是接收到數(shù)據(jù)包中斷,如果是則調(diào)用 dm9000_rx()函數(shù)接收數(shù)據(jù)包,再判斷是否發(fā)送數(shù)據(jù)包中斷,如果是則調(diào)用 dm9000_tx_done()函數(shù)進(jìn)行處理。

   處理完所有的中斷以后,程序重新打開中斷請(qǐng)求,然后恢復(fù)中斷處理之前中斷寄存器的值。最后對(duì)臨界資源解鎖,整個(gè)中斷處理流程結(jié)束。



小結(jié)


   在網(wǎng)絡(luò)通信中,計(jì)算機(jī)通過網(wǎng)卡(包括網(wǎng)絡(luò)控制器和網(wǎng)絡(luò)接口)與其它網(wǎng)絡(luò)節(jié)點(diǎn)通信。由于不同的網(wǎng)絡(luò)有不同協(xié)議,網(wǎng)卡的設(shè)計(jì)不僅需要兼顧到網(wǎng)絡(luò)上數(shù)據(jù)包的處理,還涉及主機(jī)網(wǎng)絡(luò)協(xié)議棧的接口。

   網(wǎng)卡驅(qū)動(dòng)在linux內(nèi)核是一類復(fù)雜的設(shè)備驅(qū)動(dòng),學(xué)習(xí)的時(shí)候要立足從網(wǎng)絡(luò)協(xié)議入手,需要了解網(wǎng)絡(luò)協(xié)議和內(nèi)核協(xié)議棧工作流程。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI