1. 程式人生 > >Linux驅動修煉之道-DM9000A網絡卡驅動框架原始碼分析

Linux驅動修煉之道-DM9000A網絡卡驅動框架原始碼分析

網路裝置的初始化:

通過模組的載入函式看出DM9000A的驅動是以平臺驅動的形式註冊進核心的,下邊是模組的載入函式:
1.static int __init  
2.dm9000_init(void)  
2.{  
3.    printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);   4. 
5.    return platform_driver_register(&dm9000_driver);   6.} 

下邊是平臺驅動結構體:
1.static struct platform_driver dm9000_driver = {  
2.    .driver = {  
3.        .name    = "dm9000",  
2.        .owner   = THIS_MODULE,  
3.    },  
4.    .probe   = dm9000_probe,  
5.    .remove  = __devexit_p(dm9000_drv_remove),  
6.    .suspend = dm9000_drv_suspend,  
7.    .resume  = dm9000_drv_resume,  
8.}; 

下面來分析probe函式,用來執行分配的核心函式是alloc_netdev,函式原型是:
1.struct net_device *alloc_netdev(int sizeof_priv, const char *name, void (*setup)(struct net_device*)); 

這裡的sizeof_priv是驅動程式私有資料區的大小;這個區成員和net_device結構一同分配給網路裝置。實際上,他們都處於一大塊記憶體中,但是驅動程式不需要知道這些。name是介面的名字,其在使用者空間可見;這個名字可以使用類似printf中%d的格式,核心將用下一個可用的介面號替代%d,最後,setup是一個初始化函式,用來設定net_device結構剩餘的部分。網路子系統對alloc_netdev,為不同種類的介面封裝了許多函式。最常用的是alloc_etherdev,它定義在linux/etherdevice.h中:
1.struct net_device *alloc_etherdev(int sizeof_priv); 

該函式使用eth%d的形式指定分配給網路裝置的名字。它提供了自己的初始化函式(ether_setup),用正確的值為乙太網裝置設定net_device中的許多成員。那麼在DM9000A中這個私有資料成員是什麼呢,看下邊的結構:
1./* Structure/enum declaration ------------------------------- */ 
2.typedef struct board_info {  
2. 
3.    void __iomem    *io_addr;   /* Register I/O base address */  4.    void __iomem    *io_data;   /* Data I/O address */  5.    u16      irq;       /* IRQ */  6. 
7.    u16     tx_pkt_cnt;  
8.    u16     queue_pkt_len;  
9.    u16     queue_start_addr;  
10.    u16     dbug_cnt;  
11.    u8      io_mode;        /* 0:word, 2:byte */  12.    u8      phy_addr;  
13.    u8      imr_all;  
14. 
15.    unsigned int    flags;   16.    unsigned int    in_suspend :1;   17.    int     debug_level;   18. 
19.    enum dm9000_type type;   20. 
21.    void (*inblk)(void __iomem *port, void *data, int length);   22.    void (*outblk)(void __iomem *port, void *data, int length);   23.    void (*dumpblk)(void __iomem *port, int length);   24. 
25.    struct device   *dev;        /* parent device */  26. 
27.    struct resource *addr_res;   /* resources found */  28.    struct resource *data_res;   29.    struct resource *addr_req;   /* resources requested */  30.    struct resource *data_req;   31.    struct resource *irq_res;   32. 
33.    struct mutex     addr_lock; /* phy and eeprom access lock */  34. 
35.    struct delayed_work phy_poll;   36.    struct net_device  *ndev;   37. 
38.    spinlock_t  lock;   39. 
40.    struct mii_if_info mii;   41.    u32     msg_enable;  
42.} board_info_t; 

這個struct board_info就是那個私有資料,用來儲存晶片相關的一些私有資訊。

下面是probe函式的實現:
1./* 
2. * Search DM9000 board, allocate space and register it 
3. */ 
4.static int __devinit  
2.dm9000_probe(struct platform_device *pdev)   3.{  
4.    /*獲得平臺數據,這個應該在platform_device那邊指定了*/  5.    struct dm9000_plat_data *pdata = pdev->dev.platform_data;   6.    struct board_info *db;  /* Point a board information structure */  7.    struct net_device *ndev;   8.    const unsigned char *mac_src;   9.    int ret = 0;   10.    int iosize;   11.    int i;   12.    u32 id_val;  
13. 
14.    /*分配乙太網的網路裝置*/  15.    ndev = alloc_etherdev(sizeof(struct board_info));   16.    if (!ndev) {   17.        dev_err(&pdev->dev, "could not allocate device.\n");   18.        return -ENOMEM;   19.    }  
20.    /*#define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))*/  21.    SET_NETDEV_DEV(ndev, &pdev->dev);  
22. 
23.    dev_dbg(&pdev->dev, "dm9000_probe()\n");   24. 
25.    /*設定struct board_info為ndev的私有資料*/  26.    db = netdev_priv(ndev);  
27.    memset(db, 0, sizeof(*db));   28.      
29.    db->dev = &pdev->dev;  
30.    db->ndev = ndev;  
31. 
32.    spin_lock_init(&db->lock);   33.    mutex_init(&db->addr_lock);  
34.    /*提交一個任務給一個工作佇列,你需要填充一個work_struct結構db->phy_poll*/  35.    INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);  
36.    /*獲取IO記憶體和中斷資源*/  37.    db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
38.    db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);  
39.    db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
40.      
41.    if (db->addr_res == NULL || db->data_res == NULL ||   42.        db->irq_res == NULL) {  
43.        dev_err(db->dev, "insufficient resources\n");   44.        ret = -ENOENT;  
45.        goto out;   46.    }  
47.    /*對映到核心,並獲得IO記憶體的虛擬地址,ioremap完成頁表的建立,不同於vmalloc,但是,它實際上不分配記憶體*/      48.    iosize = res_size(db->addr_res);  
49.    db->addr_req = request_mem_region(db->addr_res->start, iosize,  
50.                      pdev->name);  
51. 
52.    if (db->addr_req == NULL) {   53.        dev_err(db->dev, "cannot claim address reg area\n");   54.        ret = -EIO;  
55.        goto out;   56.    }  
57. 
58.    db->io_addr = ioremap(db->addr_res->start, iosize);  
59. 
60.    if (db->io_addr == NULL) {   61.        dev_err(db->dev, "failed to ioremap address reg\n");   62.        ret = -EINVAL;  
63.        goto out;   64.    }  
65. 
66.    iosize = res_size(db->data_res);  
67.    db->data_req = request_mem_region(db->data_res->start, iosize,  
68.                      pdev->name);  
69. 
70.    if (db->data_req == NULL) {   71.        dev_err(db->dev, "cannot claim data reg area\n");   72.        ret = -EIO;  
73.        goto out;   74.    }  
75. 
76.    db->io_data = ioremap(db->data_res->start, iosize);  
77. 
78.    if (db->io_data == NULL) {   79.        dev_err(db->dev, "failed to ioremap data reg\n");   80.        ret = -EINVAL;  
81.        goto out;   82.    }  
83. 
84.    /*獲得網路裝置的基地址*/  85.    ndev->base_addr = (unsigned long)db->io_addr;   86.    /*獲得網路裝置的中斷號*/  87.    ndev->irq = db->irq_res->start;  
88. 
89.    /*設定預設的IO函式*/  90.    dm9000_set_io(db, iosize);  
91. 
92.    /*如果平臺數據不為空*/  93.    if (pdata != NULL) {   94.        /* check to see if the driver wants to over-ride the  95.         * default IO width */ 
96.          
97.        if (pdata->flags & DM9000_PLATF_8BITONLY)   98.            dm9000_set_io(db, 1);  
99. 
100.        if (pdata->flags & DM9000_PLATF_16BITONLY)   101.            dm9000_set_io(db, 2);  
102. 
103.        if (pdata->flags & DM9000_PLATF_32BITONLY)   104.            dm9000_set_io(db, 4);  
105. 
106.        /* check to see if there are any IO routine  107.         * over-rides */ 
108. 
109.        if (pdata->inblk != NULL)   110.            db->inblk = pdata->inblk;  
111. 
112.        if (pdata->outblk != NULL)   113.            db->outblk = pdata->outblk;  
114. 
115.        if (pdata->dumpblk != NULL)   116.            db->dumpblk = pdata->dumpblk;  
117. 
118.        db->flags = pdata->flags;  
119.    }  120.
121.#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL  
122.    db->flags |= DM9000_PLATF_SIMPLE_PHY;  123.#endif  
124.    /*dm9000復位*/  125.    dm9000_reset(db);  
126.    /*讀取Vendor ID Register,Product ID Register中的值,與0x90000A46比較,如果相等,則說明是DM9000*/  127.    /* try multiple times, DM9000 sometimes gets the read wrong */  128.    for (i = 0; i < 8; i++) {   129.        id_val  = ior(db, DM9000_VIDL);  
130.        id_val |= (u32)ior(db, DM9000_VIDH) << 8;  
131.        id_val |= (u32)ior(db, DM9000_PIDL) << 16;  
132.        id_val |= (u32)ior(db, DM9000_PIDH) << 24;  
133. 
134.        if (id_val == DM9000_ID)   135.            break;   136.        dev_err(db->dev, "read wrong id 0x%08x\n", id_val);   137.    }  
138. 
139.    if (id_val != DM9000_ID) {   140.        dev_err(db->dev, "wrong id: 0x%08x\n", id_val);   141.        ret = -ENODEV;  
142.        goto out;   143.    }  
144. 
145.    /* Identify what type of DM9000 we are working on */  146.    /*讀取Chip Revision Register中的值*/  147.    id_val = ior(db, DM9000_CHIPR);  
148.    dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);   149. 
150.    switch (id_val) {   151.    case CHIPR_DM9000A:   152.        db->type = TYPE_DM9000A;  
153.        break;   154.    case CHIPR_DM9000B:   155.        db->type = TYPE_DM9000B;  
156.        break;   157.    default:   158.        dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);   159.        db->type = TYPE_DM9000E;  
160.    }  
161. 
162.    /* from this point we assume that we have found a DM9000 */  163. 
164.    /* driver system function */  165.    /*設定部分net_device欄位*/  166.    ether_setup(ndev);  
167. 
168.    ndev->open        = &dm9000_open;  
169.    ndev->hard_start_xmit    = &dm9000_start_xmit;  
170.    ndev->tx_timeout         = &dm9000_timeout;  
171.    ndev->watchdog_timeo = msecs_to_jiffies(watchdog);  
172.    ndev->stop        = &dm9000_stop;  
173.    ndev->set_multicast_list = &dm9000_hash_table;  
174.    /*對ethtool支援的相關宣告可在<linux/ethtool.h>中找到。它的核心是一個ethtool_ops型別的結構,裡邊包含一個全部的24個不同的方法來支援ethtool*/  175.    ndev->ethtool_ops     = &dm9000_ethtool_ops;  
176.    ndev->do_ioctl        = &dm9000_ioctl;  177.
178.#ifdef CONFIG_NET_POLL_CONTROLLER  
179.    ndev->poll_controller     = &dm9000_poll_controller;  180.#endif  
181. 
182.    db->msg_enable       = NETIF_MSG_LINK;  
183.    db->mii.phy_id_mask  = 0x1f;  
184.    db->mii.reg_num_mask = 0x1f;  
185.    db->mii.force_media  = 0;  
186.    db->mii.full_duplex  = 0;  
187.    db->mii.dev       = ndev;  
188.    db->mii.mdio_read    = dm9000_phy_read;  
189.    db->mii.mdio_write   = dm9000_phy_write;  
190.    /*MAC地址的源是eeprom*/  191.    mac_src = "eeprom";   192. 
193.    /* try reading the node address from the attached EEPROM */  194.    for (i = 0; i < 6; i += 2)   195.        dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);  
196.    /*如果從eeprom中讀取的地址無效,並且私有資料不為空,從platform_device的私有資料中獲取dev_addr*/  197.    if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {   198.        mac_src = "platform data";   199.        memcpy(ndev->dev_addr, pdata->dev_addr, 6);  
200.    }  
201.    /*如果地址依然無效,從PAR:實體地址(MAC)暫存器(Physical Address Register)中讀取*/  202.    if (!is_valid_ether_addr(ndev->dev_addr)) {   203.        /* try reading from mac */  204.          
205.        mac_src = "chip";   206.        for (i = 0; i < 6; i++)   207.            ndev->dev_addr[i] = ior(db, i+DM9000_PAR);  
208.    }  
209.    /*檢視乙太網網絡卡裝置地址是否有效*/  210.    if (!is_valid_ether_addr(ndev->dev_addr))   211.        dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "  212.             "set using ifconfig\n", ndev->name);   213.    /*將ndev儲存到pdev->dev->driver_data中*/  214.    platform_set_drvdata(pdev, ndev);  
215.    /*一切都初始化好後,註冊網路裝置*/  216.    ret = register_netdev(ndev);  
217. 
218.    if (ret == 0)   219.        printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",   220.               ndev->name, dm9000_type_to_char(db->type),  
221.               db->io_addr, db->io_data, ndev->irq,  
222.               ndev->dev_addr, mac_src);  
223.    return 0;   224. 
225.out:   226.    dev_err(db->dev, "not found (%d).\n", ret);   227. 
228.    dm9000_release_board(pdev, db);  
229.    free_netdev(ndev);  
230. 
231.    return ret;   232.} 

下邊看看掛起和喚醒函式:

掛起函式完成了設定掛起標誌,並沒有真正把裝置移除而只是設定了移除標誌,復位PHY,停止PHY,禁止所有中斷,禁止接受引腳。
1.static int 
2.dm9000_drv_suspend(struct platform_device *dev, pm_message_t state)  
2.{  
3.    struct net_device *ndev = platform_get_drvdata(dev);   4.    board_info_t *db;  
5. 
6.    if (ndev) {   7.        db = netdev_priv(ndev);  
8.        db->in_suspend = 1;  
9. 
10.        if (netif_running(ndev)) {   11.            netif_device_detach(ndev);  
12.            dm9000_shutdown(ndev);  
13.        }  
14.    }  
15.    return 0;   16.} 

喚醒函式完成了復位dm9000,初始化dm9000,標記裝置為attached,清除掛起標誌。
1.static int 
2.dm9000_drv_resume(struct platform_device *dev)  
2.{  
3.    struct net_device *ndev = platform_get_drvdata(dev);   4.    board_info_t *db = netdev_priv(ndev);  
5. 
6.    if (ndev) {   7. 
8.        if (netif_running(ndev)) {   9.            dm9000_reset(db);  
10.            dm9000_init_dm9000(ndev);  
11.            netif_device_attach(ndev);  
12.        }  
13.        db->in_suspend = 0;  
14.    }  
15.    return 0;   16.} 

網路裝置的開啟與釋放:

首先來看這個open函式:
1.static int 
2.dm9000_open(struct net_device *dev)  
2.{  
3.    board_info_t *db = netdev_priv(dev);  
4.    unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;  
2. 
3.    if (netif_msg_ifup(db))   4.        dev_dbg(db->dev, "enabling %s\n", dev->name);   5. 
6.    /* If there is no IRQ type specified, default to something that  7.     * may work, and tell the user that this is a problem */ 
8. 
9.    if (irqflags == IRQF_TRIGGER_NONE)   10.        dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");   11. 
12.    irqflags |= IRQF_SHARED;  
13.    /*註冊中斷處理函式*/  14.    if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))   15.        return -EAGAIN;   16. 
17.    /* Initialize DM9000 board */  18.    /*復位DM9000*/  19.    dm9000_reset(db);  
20.    /*初始化DM9000的暫存器*/  21.    dm9000_init_dm9000(dev);  
22. 
23.    /* Init driver variable */  24.    db->dbug_cnt = 0;  
25.    /*檢查鏈路載波狀況*/  26.    mii_check_media(&db->mii, netif_msg_link(db), 1);  
27.    /*啟動傳送佇列*/  28.    netif_start_queue(dev);  
29.    /*之前在probe函式中呼叫 INIT_DELAYED_WORK初始化了工作佇列,並關聯了一個操作函式dm9000_poll_work(),此時執行dm9000_schedule_poll來呼叫這個函式*/  30.    dm9000_schedule_poll(db);  
31. 
32.    return 0;   33.} 

然後是stop函式:
1.static int 
2.dm9000_stop(struct net_device *ndev)  
2.{  
3.    board_info_t *db = netdev_priv(ndev);  
4. 
5.    if (netif_msg_ifdown(db))  
2.        dev_dbg(db->dev, "shutting down %s\n", ndev->name);  
2.    /*殺死延時工作佇列phy_poll*/ 
2.    cancel_delayed_work_sync(&db->phy_poll);  
3.    /*停止傳送佇列*/  4.    netif_stop_queue(ndev);  
5.    /*通知核心鏈路失去連線*/  6.    netif_carrier_off(ndev);  
7.    /* free interrupt */  8.    free_irq(ndev->irq, ndev);  
9.    /*關閉DM9000*/  10.    dm9000_shutdown(ndev);  
11.    return 0;   12.} 

復位PHY,停止PHY,禁止所有中斷,禁止接收引腳。
1.static void 
2.dm9000_shutdown(struct net_device *dev)  
2.{  
3.    board_info_t *db = netdev_priv(dev);  
4. 
5.    /* RESET device */ 
2.    dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */ 
2.    iow(db, DM9000_GPR, 0x01);  /* Power-Down PHY */ 
2.    iow(db, DM9000_IMR, IMR_PAR);   /* Disable all interrupt */  3.    iow(db, DM9000_RCR, 0x00);  /* Disable RX */  4.} 

相關推薦

Linux驅動修煉-DM9000A驅動框架原始碼分析

網路裝置的初始化: 通過模組的載入函式看出DM9000A的驅動是以平臺驅動的形式註冊進核心的,下邊是模組的載入函式: 1.static int __init   2.dm9000_init(void)   2.{   3.    printk(KERN_INFO "%

tiny4412學習(三)移植linux-4.x驅動(1)支援驅動

一、思路 上一節我們通過DNW將核心、檔案系統、裝置樹檔案燒入到記憶體中,並使用bootm啟動核心:bootm0x40600000  0x41000000  0x42000000。因為此時核心並沒有S

嵌入式Linux——驅動(1):驅動框架介紹

宣告:文字是看完韋東山老師的視訊和看了一些文章後,所寫的總結。我會盡力將自己所瞭解的知識寫出來,但由於自己感覺並沒有學的很好,所以文中可能有錯的地方敬請指出,謝謝。         在介紹本文之前,我想先對前面的知識做一下總結,我們知道Linux系統的裝置分為字元裝置(ch

基於S3C2440的Linux-3.6.6移植——DM9000驅動移植

Linux-3.6.6很好的支援了DM9000,因此對於S3C2440晶片來說無需進行任何修改,甚至連menuconfig都已經預設配置了網絡卡驅動。但我們還需要設定網絡卡的MAC和IP等資訊。有許多方法可以實現網絡卡的設定,在這裡我們選擇一種比較簡單的方法——修改根檔案系

Linux 上安裝Realtek瑞昱驅動

新買了個筆記本,安裝了Ubuntu 16.04,發現筆記本的Realtek瑞昱無線網絡卡不能自動被linux識別。上網查詢原因為驅動問題,也有很多人抱怨過Realtek瑞昱網絡卡在linux上驅動難弄。 這裡記錄下解決過程,給其他人一點方便。 其實安裝方法還算

Linux 下小米WIFI 的無線驅動

在小米的罈子裡看到了大神發的 小米WIFI 驅動 for Linux。於是就下載下來為自己的linux(Fedora 21 ,  kernel:3.17.8-300 )安裝小米WIFI 驅動。 過程記錄如下(其實也適用於 小度WIFI 和 360 WIFI ,只要用的是晶

初識Linux 驅動移植 dm9621驅動移植

概述 將kernel移植到開發板並能正常載入和啟動核心後,發現網絡卡並沒有工作,因此將網絡卡作為第一個移植的實踐。這篇文章用於記錄移植dm9621網絡卡過程中遇到的問題以及如何定位問題並嘗試解決。 配置核心 在找到dm9621網絡卡驅動的原始碼後,需要將其新增

Linux驅動修煉-SPI驅動框架原始碼分析(中-續)

然後看這裡是怎樣註冊spi主機控制器驅動的: int spi_register_master(struct spi_master *master)   {       。。。。。。。。。。。。。。。。       /*將spi新增到核心,這

Linux驅動修煉-SPI驅動框架原始碼分析(中)

來自:http://blog.csdn.net/woshixingaaa/article/details/6574220 這篇來分析spi子系統的建立過程。 嵌入式微處理器訪問SPI裝置有兩種方式:使用GPIO模擬SPI介面的工作時序或者使用SPI控制

linux驅動驅動程式框架

我們這裡說的是網絡卡驅動程式,不是網路驅動程式,網路有七層,我們寫的只是最底層的東西,網路這麼多層,但是最終你還是要操作硬體啊 所以上面肯定有個硬體相關層,我們要寫的就是硬體相關的驅動程式這一小塊。 網絡卡你不需要開啟什麼裝置,你只需要socket程式設計就行了 怎

Linux驅動修煉-SPI驅動框架原始碼分析(上)

SPI驅動架構,以前用過,不過沒這個詳細,跟各位一起分享: 來自:http://blog.csdn.net/woshixingaaa/article/details/6574215 SPI協議是一種同步的序列資料連線標準,由摩托羅拉公司命名,可工作於全雙工模式。相

linux驅動驅動-虛擬驅動編寫

我們來實現這麼一個目的 我ping 3.3.3.4的時候,按理說如果是真實網絡卡的話,我們3.3.3.3的機器,和3.3.3.4的機器。ping 3.3.3.4的時候3.3.3.3的機器會把包發給3.3.3.4,3.3.3.4收到包之後又會把包發給3.3.3.3 在驅

Linux驅動修煉-SPI驅動框架原始碼分析(下-續)

spi_async在spi.h中定義的: <span style="font-size:18px;">staticinlineint spi_async(struct spi_device *spi, struct spi_mess

Linux驅動修煉-DMA框架原始碼分析(下)

static irqreturn_t s3c2410_dma_irq(int irq, void *devpw) { struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw; struct s3c2410_dma_buf *buf

Linux驅動修煉

一些學習Linux驅動的筆記整理在這裡與大家分享,如果那裡有錯誤也請高手指出。若干年後能進入INTEL開源中心或IBM搞linux kernel是我目前的目標。君子藏器於身,待時而動。文章連載,不斷更新中。

linux驅動修煉-混雜裝置

Linux驅動中把無法歸類的五花八門的裝置定義為混雜裝置(用miscdevice結構體表述)。miscdevice共享一個主裝置號MISC_MAJOR(即10),但次裝置號不同。 所有的miscdevice裝置形成了一個連結串列,對裝置訪問時核心根據次裝置號查詢對應的mi

linux驅動分析probe函式

       從上面結果可以看出,該裝置使用了6個BAR中的2個BAR,即BAR0和BAR1,該裝置申請了兩塊IO記憶體,BAR0的範圍為:fea00000-fea1ffff,大小為128KB,用來對映裝置暫存器,BAR1的範圍為fea20000-fea23fff,大小為32KB,用來對映flash。裝置需要

Linux驅動修煉-DMA框架原始碼分析

DMA使用3個狀態的有限狀態機: 1.初始狀態,DMA等待DMA請求,一旦請求到達DMA進入狀態2,DMA ACK與INT REQ為0。 2.在這個狀態,DMA ACK置為1並且計數器CURR_TC的值被從DCON[19:0]載入,注意DMA ACK保持為1直到它被清除。 3.在這個狀態,處理DMA原子

l(轉)Linux DM9000驅動程式完全分析

[置頂] Linux DM9000網絡卡驅動程式完全分析 分類: Linux裝置驅動程式第三版學習筆記 2011-02-26 16:11 3513人閱讀 評論(34) 收藏 舉報 說明1:本文分析基於核心原始碼版本為linux-2

LINUX核心升級 - 更新驅動

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!