日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

rtems网络移植-实现网卡驱动

發布時間:2023/12/31 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 rtems网络移植-实现网卡驱动 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

經過兩周的調試,終于初步實現網卡的發送功能。

在這里參考了uboot的beaglebone網卡驅動和《tcp/ip詳解卷二》

1、在前幾篇博文中,講解了網卡的mdio初始化過程,那么網卡lan8710a是如何與am335x處理器通信的呢?

首先看一張連接圖:



我們都知道osi七層協議,最底下的是物理層和數據鏈路層,也就是mac和phy。有一部分處理器是自帶mac層,也就是只需要pyh網卡一個外設就能實現網絡功能。還有一部分處理器沒有mac,也就是要外設一個mac和一個phy。beaglebone black的處理器是ti的am335x,是自帶mac層的,phy網卡采用的是smsc的lan8710A。

上圖很好的展現了網卡與處理器的通信:

首先處理器對于網卡寄存器的配置和控制方式采用的是MDIO通信

MDIO是由IEEE通過以太網標準IEEE 802.3的若干條款加以定義。MDIO是一種簡單的雙線串行接口,和spi類似,甚至不用時鐘同步。mdio控制的寄存器主要包括BMCR、BMSR,自適應等模式的配置。


然后是傳輸數據的通信方式采用MII接口:


MII共有14根線,包括發送和接收的八根線,還有時鐘線等。


再回到最上面的圖,能夠看到處理器控制mac然后以MII的通信方式進行數據傳輸,這里用到了DMA,好處是不用CPU的干預,傳輸速度很快。網卡直接把數據傳輸給
,然后DMA傳輸給內存DDR,而處理器對網卡的控制和配置則直接通過MDIO。下圖是LAN8710網卡官方文檔中的MII模式連接圖:





2、TI對于網卡設備的通用管理是CPSW方式,分為host和slave,如何配置?

這里給出的代碼分析是uboot中cpsw.c代碼

首先是cpsw設備的注冊:

int cpsw_register(struct cpsw_platform_data *data) {struct cpsw_priv *priv;struct cpsw_slave *slave;void *regs = (void *)data->cpsw_base;struct eth_device *dev;printf("cpsw_register \n");dev = calloc(sizeof(*dev), 1);if (!dev)return -ENOMEM;priv = calloc(sizeof(*priv), 1);if (!priv) {free(dev);return -ENOMEM;}priv->data = *data;priv->dev = dev;priv->slaves = malloc(sizeof(struct cpsw_slave) * data->slaves);if (!priv->slaves) {free(dev);free(priv);return -ENOMEM;}priv->host_port = data->host_port_num;priv->regs = regs;priv->host_port_regs = regs + data->host_port_reg_ofs;priv->dma_regs = regs + data->cpdma_reg_ofs;priv->ale_regs = regs + data->ale_reg_ofs;priv->descs = (void *)regs + data->bd_ram_ofs;int idx = 0;for_each_slave(slave, priv) {cpsw_slave_setup(slave, idx, priv);idx = idx + 1;}strcpy(dev->name, "cpsw");dev->iobase = 0;dev->init = cpsw_init;dev->halt = cpsw_halt;dev->send = cpsw_send;dev->recv = cpsw_recv;dev->priv = priv;eth_register(dev);cpsw_mdio_init(dev->name, data->mdio_base, data->mdio_div);priv->bus = miiphy_get_dev_by_name(dev->name);for_active_slave(slave, priv)cpsw_phy_init(dev, slave);return 1; }

首先是聲明幾個結構體變量,其中包括cpsw的主:cpsw_priv和從:cpsw_slave,然后是設置cpsw的基礎寄存器的地址cpsw_base,然后調用calloc函數為這些結構體分配空間。

分配好后對priv結構體中的成員進行初始化,host_port=0表示主機端口號是0,然后成員的寄存器的偏移地址進行初始化。

priv->host_port = data->host_port_num;priv->regs = regs;priv->host_port_regs = regs + data->host_port_reg_ofs;priv->dma_regs = regs + data->cpdma_reg_ofs;priv->ale_regs = regs + data->ale_reg_ofs;priv->descs = (void *)regs + data->bd_ram_ofs;

對每個salve進行初始化,這里采用for循環的意義在于可能有多個網卡,am335支持雙網卡。

for_each_slave(slave, priv) {cpsw_slave_setup(slave, idx, priv);idx = idx + 1;}


以mdio方式對網卡配置進行初始化:主要是調用cpsw_phy_init函數進行初始化

eth_register(dev);cpsw_mdio_init(dev->name, data->mdio_base, data->mdio_div);priv->bus = miiphy_get_dev_by_name(dev->name);for_active_slave(slave, priv)cpsw_phy_init(dev, slave);



cpsw_phy_init函數定義:

static int cpsw_phy_init(struct eth_device *dev, struct cpsw_slave *slave) {struct cpsw_priv *priv = (struct cpsw_priv *)dev->priv;struct phy_device *phydev;u32 supported = PHY_GBIT_FEATURES;printf("cpsw_phy_init \n");printf("phy_addr:%d \n",slave->data->phy_addr);phydev = phy_connect(priv->bus,slave->data->phy_addr,dev,slave->data->phy_if);if (!phydev)return -1;phydev->supported &= supported;phydev->advertising = phydev->supported;priv->phydev = phydev;phy_config(phydev);return 1; }


該函數調用phy_connect函數連接網卡,返回的值如果合理就調用phy_config函數對該網卡進行配置,主要是配置網卡的速率和半雙工。


首先分析phy_connect函數:

struct phy_device *phy_connect(struct mii_dev *bus, int addr,struct eth_device *dev, phy_interface_t interface) #endif {struct phy_device *phydev;phydev = phy_find_by_mask(bus, 1 << addr, interface);if (phydev)phy_connect_dev(phydev, dev);elseprintf("Could not get PHY for %s: addr %d\n", bus->name, addr);return phydev; } 該函數首先調用phy_find_by_mask函數查詢網卡設備,如果存在則調用phy_connect_dev函數連接,否則就打印出錯信息


phy_connect_dev函數實現:

struct phy_device *phy_find_by_mask(struct mii_dev *bus, unsigned phy_mask,phy_interface_t interface) {/* Reset the bus */if (bus->reset) {bus->reset(bus);/* Wait 15ms to make sure the PHY has come out of hard reset */udelay(15000);}return get_phy_device_by_mask(bus, phy_mask, interface); }

該函數主要是調用get_phy_device_by_mask函數進行設備的查找,get_phy_device_by_mask函數的實現至關重要,包含了對于網卡的主要mdio通信


get_phy_device_by_mask函數實現:

static struct phy_device *get_phy_device_by_mask(struct mii_dev *bus,unsigned phy_mask, phy_interface_t interface) {int i;struct phy_device *phydev;phydev = search_for_existing_phy(bus, phy_mask, interface);if (phydev)return phydev;/* Try Standard (ie Clause 22) access *//* Otherwise we have to try Clause 45 */for (i = 0; i < 5; i++) {phydev = create_phy_by_mask(bus, phy_mask,i ? i : MDIO_DEVAD_NONE, interface);if (IS_ERR(phydev))return NULL;if (phydev)return phydev;}printf("Phy %d not found\n", ffs(phy_mask) - 1);return phy_device_create(bus, ffs(phy_mask) - 1, 0xffffffff, interface); }


該函數首先調用search_for_existing_phy函數查找當前存在的設備,如果存在則將該設備返回,不存在則調用create_phy_by_mask函數進行創建。


search_for_existing_phy函數實現:

static struct phy_device *search_for_existing_phy(struct mii_dev *bus,unsigned phy_mask, phy_interface_t interface) {/* If we have one, return the existing device, with new interface */while (phy_mask) {int addr = ffs(phy_mask) - 1;if (bus->phymap[addr]) {bus->phymap[addr]->interface = interface;return bus->phymap[addr];}phy_mask &= ~(1 << addr);}return NULL; }



create_phy_by_mask函數實現:
static struct phy_device *create_phy_by_mask(struct mii_dev *bus,unsigned phy_mask, int devad, phy_interface_t interface) {u32 phy_id = 0xffffffff;while (phy_mask) {int addr = ffs(phy_mask) - 1;int r = get_phy_id(bus, addr, devad, &phy_id);/* If the PHY ID is mostly f's, we didn't find anything */if (r == 0 && (phy_id & 0x1fffffff) != 0x1fffffff)return phy_device_create(bus, addr, phy_id, interface);phy_mask &= ~(1 << addr);}return NULL; }
該函數調用get_phy_id函數讓處理器通過mdio總線查看網卡寄存器存儲的ID,如果ID都是f,說明沒有ID,就返回空,否則返回phy_device_create函數進行創建一個網卡設備。


get_phy_id函數實現:

int __weak get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id) {int phy_reg;/* Grab the bits from PHYIR1, and put them* in the upper half */phy_reg = bus->read(bus, addr, devad, MII_PHYSID1);if (phy_reg < 0)return -EIO;*phy_id = (phy_reg & 0xffff) << 16;/* Grab the bits from PHYIR2, and put them in the lower half */phy_reg = bus->read(bus, addr, devad, MII_PHYSID2);if (phy_reg < 0)return -EIO;*phy_id |= (phy_reg & 0xffff);return 0; }


該函數就調用了bus->read總線讀函數,來讀取網卡寄存器的值,這里是讀取寄存器存儲的網卡ID,bus->read函數定義為cpsw_mdio_read


cpsw_mdio_read實現:

static int cpsw_mdio_read(struct mii_dev *bus, int phy_id,int dev_addr, int phy_reg) {int data;u32 reg;printf("cpsw_mdio_read \n");printf("phy_id %d\n",phy_id);if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)return -EINVAL;wait_for_user_access();reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) |(phy_id << 16));__raw_writel(reg, &mdio_regs->user[0].access);reg = wait_for_user_access();data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1;return data; }
該函數調用wait_for_user_access函數來等待能否讀取寄存器信號,當標志位表示能夠access時,將reg的值寫到&mdio_regs->user[0].access寄存器,reg包含了想要讀的寄存器的變量,然后繼續調用wait_for_user_access函數等待數據完成,將wait_for_user_access函數的返回值給reg,想要讀寄存器的data就等于(reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1; 然后返回data,就完成了mdio對于網卡寄存器的讀取。


看看wait_for_user_access函數的實現:

static inline u32 wait_for_user_access(void) {u32 reg = 0;int timeout = MDIO_TIMEOUT;while (timeout-- &&((reg = __raw_readl(&mdio_regs->user[0].access)) & USERACCESS_GO))udelay(10);if (timeout == -1) {printf("wait_for_user_access Timeout\n");return -ETIMEDOUT;}return reg; }

可以看出,這里用了一個while循環,等待100個機器周期來讀取網卡寄存器的值。


以上就是cpsw的注冊和網卡設備的mdio連接。

接下來分析最重要的cpsw_init函數,包含了ALE、DMA、cpsw_slave的初始化和配置。

cpsw.c?

cpsw_init函數實現:

static int cpsw_init(struct eth_device *dev, bd_t *bis) {struct cpsw_priv *priv = dev->priv;struct cpsw_slave *slave;int i, ret; printf("cpsw_init func\n");/* soft reset the controller and initialize priv */setbit_and_wait_for_clear32(&priv->regs->soft_reset);/* initialize and reset the address lookup engine */cpsw_ale_enable(priv, 1);cpsw_ale_clear(priv, 1);cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode *//* setup host port priority mapping */__raw_writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map);__raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map);/* disable priority elevation and enable statistics on all ports */__raw_writel(0, &priv->regs->ptype);/* enable statistics collection only on the host port */__raw_writel(BIT(priv->host_port), &priv->regs->stat_port_en);__raw_writel(0x7, &priv->regs->stat_port_en);printf("&priv->regs->stat_port_en:%x\n",&priv->regs->stat_port_en);printf("priv->regs->stat_port_en:%x\n",priv->regs->stat_port_en);cpsw_ale_port_state(priv, priv->host_port, ALE_PORT_STATE_FORWARD);//cpsw_ale_add_ucast(priv, priv->dev->enetaddr, priv->host_port, ALE_SECURE);//cpsw_ale_add_mcast(priv, net_bcast_ethaddr, 1 << priv->host_port);for_active_slave(slave, priv)cpsw_slave_init(slave, priv);cpsw_update_link(priv);/* init descriptor pool */for (i = 0; i < NUM_DESCS; i++) {desc_write(&priv->descs[i], hw_next,(i == (NUM_DESCS - 1)) ? 0 : &priv->descs[i+1]);}priv->desc_free = &priv->descs[0];printf("&priv->descs[0]:%x\n",&priv->descs[0]); printf("priv->dma_regs + CPDMA_RXHDP_VER2:%x\n",priv->dma_regs + CPDMA_RXHDP_VER2);/* initialize channels */if (priv->data.version == CPSW_CTRL_VERSION_2) {memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan));priv->rx_chan.hdp = priv->dma_regs + CPDMA_RXHDP_VER2;priv->rx_chan.cp = priv->dma_regs + CPDMA_RXCP_VER2;priv->rx_chan.rxfree = priv->dma_regs + CPDMA_RXFREE;memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan));priv->tx_chan.hdp = priv->dma_regs + CPDMA_TXHDP_VER2;priv->tx_chan.cp = priv->dma_regs + CPDMA_TXCP_VER2;} else {memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan));priv->rx_chan.hdp = priv->dma_regs + CPDMA_RXHDP_VER1;priv->rx_chan.cp = priv->dma_regs + CPDMA_RXCP_VER1;priv->rx_chan.rxfree = priv->dma_regs + CPDMA_RXFREE;memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan));priv->tx_chan.hdp = priv->dma_regs + CPDMA_TXHDP_VER1;priv->tx_chan.cp = priv->dma_regs + CPDMA_TXCP_VER1;}/* clear dma state */setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET);if (priv->data.version == CPSW_CTRL_VERSION_2) {for (i = 0; i < priv->data.channels; i++) {__raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER2 + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER2 + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER2 + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER2 + 4* i);}} else {for (i = 0; i < priv->data.channels; i++) {__raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER1 + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER1 + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER1 + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER1 + 4* i);}}__raw_writel(1, priv->dma_regs + CPDMA_TXCONTROL);__raw_writel(1, priv->dma_regs + CPDMA_RXCONTROL);/* submit rx descs */for (i = 0; i < PKTBUFSRX; i++) {ret = cpdma_submit(priv, &priv->rx_chan, net_rx_packets[i],PKTSIZE);if (ret < 0) {printf("error %d submitting rx desc\n", ret);break;}}return 0; }


首先是ALE的初始化:

ALE:address lookup engine 地址查詢引擎,是TI創造的一種對于雙網卡選擇的方式:

/* initialize and reset the address lookup engine */cpsw_ale_enable(priv, 1);cpsw_ale_clear(priv, 1);cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode */
這三個函數的實現都在cpsw.c文件中,都是向相應的ale寄存器中寫值,目的是為了使能ale引擎,并開啟vlan(虛擬局域網)

貼出代碼,但不具體解釋:

#define cpsw_ale_enable(priv, val) cpsw_ale_control(priv, 31, val) #define cpsw_ale_clear(priv, val) cpsw_ale_control(priv, 30, val) #define cpsw_ale_vlan_aware(priv, val) cpsw_ale_control(priv, 2, val) static inline void cpsw_ale_control(struct cpsw_priv *priv, int bit, int val) {u32 tmp, mask = BIT(bit);tmp = __raw_readl(priv->ale_regs + ALE_CONTROL);tmp &= ~mask;tmp |= val ? mask : 0;__raw_writel(tmp, priv->ale_regs + ALE_CONTROL); }


接下來設置端口的初始mapping,也就是設置內存映射,將硬件DMA通道的發送和接收寄存器的硬件地址映射到內存空間,這樣就可通過訪問和修改內存地址內容來修改相應硬件配置:

/* setup host port priority mapping */__raw_writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map);__raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map);/* disable priority elevation and enable statistics on all ports */__raw_writel(0, &priv->regs->ptype);/* enable statistics collection only on the host port */__raw_writel(BIT(priv->host_port), &priv->regs->stat_port_en);__raw_writel(0x7, &priv->regs->stat_port_en);


接著,初始化cpsw的slave,也就是網卡部分,具體實現在cpsw_slave_init函數,待會再分析。

for_active_slave(slave, priv)cpsw_slave_init(slave, priv);


接著是cpsw_update_link函數,該函數是刷新與網卡的連接,確保能夠通信

cpsw_update_link(priv);
該函數的實現主要是調用cpsw_slave_update_link函數對slave進行重新初始化連接:

函數實現:

static void cpsw_slave_update_link(struct cpsw_slave *slave,struct cpsw_priv *priv, int *link) {struct phy_device *phy;u32 mac_control = 0;phy = priv->phydev;if (!phy)return;phy_startup(phy);*link = phy->link;if (*link) { /* link up */mac_control = priv->data.mac_control;if (phy->speed == 1000)mac_control |= GIGABITEN;if (phy->duplex == DUPLEX_FULL)mac_control |= FULLDUPLEXEN;if (phy->speed == 100)mac_control |= MIIEN;}if (mac_control == slave->mac_control)return;if (mac_control) {printf("link up on port %d, speed %d, %s duplex\n",slave->slave_num, phy->speed,(phy->duplex == DUPLEX_FULL) ? "full" : "half");} else {printf("link down on port %d\n", slave->slave_num);}__raw_writel(mac_control, &slave->sliver->mac_control);slave->mac_control = mac_control; }
該函數調用phy_startup(phy)進行設備的開啟和連接,然后獲得數據進行判斷,當link為真時,進入if,判斷網卡的工作速率是在10M還是100M,工作模式是雙工還是單工。并且通過printf打印信息。

對于phy_startup函數主要是調用phy.c文件下的genphy_update_link函數和genphy_parse_link函數。


genphy_update_link實現如下:

int genphy_update_link(struct phy_device *phydev) {unsigned int mii_reg;/** Wait if the link is up, and autonegotiation is in progress* (ie - we're capable and it's not done)*/mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);/** If we already saw the link up, and it hasn't gone down, then* we don't need to wait for autoneg again*/if (phydev->link && mii_reg & BMSR_LSTATUS)return 0;if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) {int i = 0;printf("%s Waiting for PHY auto negotiation to complete",phydev->dev->name);while (!(mii_reg & BMSR_ANEGCOMPLETE)) {/** Timeout reached ?*/if (i > PHY_ANEG_TIMEOUT) {printf(" TIMEOUT !\n");phydev->link = 0;return 0;}if (ctrlc()) {puts("user interrupt!\n");phydev->link = 0;return -EINTR;}if ((i++ % 500) == 0)printf(".");udelay(1000); /* 1 ms */mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);}printf(" done\n");phydev->link = 1;} else {/* Read the link a second time to clear the latched state */mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);if (mii_reg & BMSR_LSTATUS)phydev->link = 1;elsephydev->link = 0;}return 0; }


該函數首先調用phy_read函數來獲取BMSR寄存器的值

什么是BMSR,這是網卡的狀態寄存器,BMCR是網卡的控制寄存器,一般而言,BMSR供我們讀取數據進行判斷網卡的狀態,而BMCR一般是供我們寫入數據進行控制。

下圖是該網卡的寄存器表:

如果想要查看各寄存器的定義和每一位的定義,可以到SMSC官網下載文檔。

回到函數,當讀取到網卡的狀態寄存器的值后,開始進行一系列判斷

if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) 這個判斷條件是判斷網卡是否完成自適應配置,如果完成,打印相應信息。不然的話,第二次讀取BMSR寄存器,重新判斷一次。


然后是genphy_config函數,該函數是對網卡的信息進行一個讀取,比如是否支持千兆網卡,是否支持10M/100M 單工/雙工。

函數實現:

int genphy_config(struct phy_device *phydev) {int val;u32 features;/* For now, I'll claim that the generic driver supports* all possible port types */features = (SUPPORTED_TP | SUPPORTED_MII| SUPPORTED_AUI | SUPPORTED_FIBRE |SUPPORTED_BNC);/* Do we support autonegotiation? */val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);if (val < 0)return val;if (val & BMSR_ANEGCAPABLE)features |= SUPPORTED_Autoneg;if (val & BMSR_100FULL)features |= SUPPORTED_100baseT_Full;if (val & BMSR_100HALF)features |= SUPPORTED_100baseT_Half;if (val & BMSR_10FULL)features |= SUPPORTED_10baseT_Full;if (val & BMSR_10HALF)features |= SUPPORTED_10baseT_Half;if (val & BMSR_ESTATEN) {val = phy_read(phydev, MDIO_DEVAD_NONE, MII_ESTATUS);if (val < 0)return val;if (val & ESTATUS_1000_TFULL)features |= SUPPORTED_1000baseT_Full;if (val & ESTATUS_1000_THALF)features |= SUPPORTED_1000baseT_Half;if (val & ESTATUS_1000_XFULL)features |= SUPPORTED_1000baseX_Full;if (val & ESTATUS_1000_XHALF)features |= SUPPORTED_1000baseX_Half;}phydev->supported = features;phydev->advertising = features;genphy_config_aneg(phydev);return 0; }
可以看出,該函數和genphy_update_link的實現風格很像,不再詳細說明


再回到cpsw_slave_update_link函數,這樣就完成了對于網卡的重新連接。


回到cpsw_init 函數,接著初始化DMA通道和DMA描述符

首先初始化描述符池

/* init descriptor pool */for (i = 0; i < NUM_DESCS; i++) {desc_write(&priv->descs[i], hw_next,(i == (NUM_DESCS - 1)) ? 0 : &priv->descs[i+1]);}priv->desc_free = &priv->descs[0];


然后初始化DMA通道,am335有8個channel

memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan));priv->rx_chan.hdp = priv->dma_regs + CPDMA_RXHDP_VER2;priv->rx_chan.cp = priv->dma_regs + CPDMA_RXCP_VER2;priv->rx_chan.rxfree = priv->dma_regs + CPDMA_RXFREE;memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan));priv->tx_chan.hdp = priv->dma_regs + CPDMA_TXHDP_VER2;priv->tx_chan.cp = priv->dma_regs + CPDMA_TXCP_VER2;


分配好channel的內存地址后,初始化這些通道,方法也很簡單,寫0即可:

__raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER2 + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER2 + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER2 + 4* i);__raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER2 + 4* i);}


以上就完成了對cpsw設備的初始化,網卡的配置也基本完成


3、實現網卡的發送

網卡的發送函數主要是調用cpsw_send函數,比如當輸入ping命令時,經過一系列的裝包,最后調用cpsw_send函數進行發送,在發送ICMP包之前,會先調用arp發送arp地址解析協議,然后根據收到的rarp的包得知主機的mac地址。然后再發送icmp包,因此在這里首先要看如何實現arp包的發送。


首先了解arp協議的格式:


然后分析cpsw_send函數:

static int cpsw_send(struct eth_device *dev, void *packet, int length) {struct cpsw_priv *priv = dev->priv;void *buffer;int len;int timeout = CPDMA_TIMEOUT;flush_dcache_range((unsigned long)packet,(unsigned long)packet + length);/* first reap completed packets */while (timeout-- &&(cpdma_process(priv, &priv->tx_chan, &buffer, &len) >= 0));if (timeout == -1) {printf("cpdma_process timeout\n");return -ETIMEDOUT;}return cpdma_submit(priv, &priv->tx_chan, packet, length); }

函數調用flush_dcache_range函數對數據緩存進行刷新,刷新的地址就是packet的地址,因為要保證緩存和內存的一致性,也就是一致性DMA。

然后調用cpdma_process和cpdma_submit函數將數據傳給DMA描述符,再傳送給DMA通道,DMA通過MII發送給網卡,網卡再將數據發送出去。




以上就是本人實現網卡驅動的大致過程,最后的描述可能有些不詳細,時間太晚了,有問題可以留言交流~。

轉載請說明出處。

總結

以上是生活随笔為你收集整理的rtems网络移植-实现网卡驱动的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。