您好,登錄后才能下訂單哦!
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_addr和 io_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ò)處理,使用 release和out標(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é)議棧工作流程。
免責(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)容。