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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

經(jīng)過兩周的調(diào)試,終于初步實現(xiàn)網(wǎng)卡的發(fā)送功能。

在這里參考了uboot的beaglebone網(wǎng)卡驅(qū)動和《tcp/ip詳解卷二》

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

首先看一張連接圖:



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

上圖很好的展現(xiàn)了網(wǎng)卡與處理器的通信:

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

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


然后是傳輸數(shù)據(jù)的通信方式采用MII接口:


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


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





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

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

首先是cpsw設(shè)備的注冊:

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; }

首先是聲明幾個結(jié)構(gòu)體變量,其中包括cpsw的主:cpsw_priv和從:cpsw_slave,然后是設(shè)置cpsw的基礎(chǔ)寄存器的地址cpsw_base,然后調(diào)用calloc函數(shù)為這些結(jié)構(gòu)體分配空間。

分配好后對priv結(jié)構(gòu)體中的成員進(jìn)行初始化,host_port=0表示主機(jī)端口號是0,然后成員的寄存器的偏移地址進(jìn)行初始化。

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進(jìn)行初始化,這里采用for循環(huán)的意義在于可能有多個網(wǎng)卡,am335支持雙網(wǎng)卡。

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


以mdio方式對網(wǎng)卡配置進(jìn)行初始化:主要是調(diào)用cpsw_phy_init函數(shù)進(jìn)行初始化

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函數(shù)定義:

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; }


該函數(shù)調(diào)用phy_connect函數(shù)連接網(wǎng)卡,返回的值如果合理就調(diào)用phy_config函數(shù)對該網(wǎng)卡進(jìn)行配置,主要是配置網(wǎng)卡的速率和半雙工。


首先分析phy_connect函數(shù):

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; } 該函數(shù)首先調(diào)用phy_find_by_mask函數(shù)查詢網(wǎng)卡設(shè)備,如果存在則調(diào)用phy_connect_dev函數(shù)連接,否則就打印出錯信息


phy_connect_dev函數(shù)實現(xiàn):

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); }

該函數(shù)主要是調(diào)用get_phy_device_by_mask函數(shù)進(jìn)行設(shè)備的查找,get_phy_device_by_mask函數(shù)的實現(xiàn)至關(guān)重要,包含了對于網(wǎng)卡的主要mdio通信


get_phy_device_by_mask函數(shù)實現(xiàn):

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); }


該函數(shù)首先調(diào)用search_for_existing_phy函數(shù)查找當(dāng)前存在的設(shè)備,如果存在則將該設(shè)備返回,不存在則調(diào)用create_phy_by_mask函數(shù)進(jìn)行創(chuàng)建。


search_for_existing_phy函數(shù)實現(xiàn):

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函數(shù)實現(xiàn):
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; }
該函數(shù)調(diào)用get_phy_id函數(shù)讓處理器通過mdio總線查看網(wǎng)卡寄存器存儲的ID,如果ID都是f,說明沒有ID,就返回空,否則返回phy_device_create函數(shù)進(jìn)行創(chuàng)建一個網(wǎng)卡設(shè)備。


get_phy_id函數(shù)實現(xiàn):

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; }


該函數(shù)就調(diào)用了bus->read總線讀函數(shù),來讀取網(wǎng)卡寄存器的值,這里是讀取寄存器存儲的網(wǎng)卡ID,bus->read函數(shù)定義為cpsw_mdio_read


cpsw_mdio_read實現(xiàn):

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; }
該函數(shù)調(diào)用wait_for_user_access函數(shù)來等待能否讀取寄存器信號,當(dāng)標(biāo)志位表示能夠access時,將reg的值寫到&mdio_regs->user[0].access寄存器,reg包含了想要讀的寄存器的變量,然后繼續(xù)調(diào)用wait_for_user_access函數(shù)等待數(shù)據(jù)完成,將wait_for_user_access函數(shù)的返回值給reg,想要讀寄存器的data就等于(reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1; 然后返回data,就完成了mdio對于網(wǎng)卡寄存器的讀取。


看看wait_for_user_access函數(shù)的實現(xiàn):

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循環(huán),等待100個機(jī)器周期來讀取網(wǎng)卡寄存器的值。


以上就是cpsw的注冊和網(wǎng)卡設(shè)備的mdio連接。

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

cpsw.c?

cpsw_init函數(shù)實現(xiàn):

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創(chuàng)造的一種對于雙網(wǎng)卡選擇的方式:

/* 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 */
這三個函數(shù)的實現(xiàn)都在cpsw.c文件中,都是向相應(yīng)的ale寄存器中寫值,目的是為了使能ale引擎,并開啟vlan(虛擬局域網(wǎng))

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

#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); }


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

/* 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,也就是網(wǎng)卡部分,具體實現(xiàn)在cpsw_slave_init函數(shù),待會再分析。

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


接著是cpsw_update_link函數(shù),該函數(shù)是刷新與網(wǎng)卡的連接,確保能夠通信

cpsw_update_link(priv);
該函數(shù)的實現(xiàn)主要是調(diào)用cpsw_slave_update_link函數(shù)對slave進(jìn)行重新初始化連接:

函數(shù)實現(xiàn):

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; }
該函數(shù)調(diào)用phy_startup(phy)進(jìn)行設(shè)備的開啟和連接,然后獲得數(shù)據(jù)進(jìn)行判斷,當(dāng)link為真時,進(jìn)入if,判斷網(wǎng)卡的工作速率是在10M還是100M,工作模式是雙工還是單工。并且通過printf打印信息。

對于phy_startup函數(shù)主要是調(diào)用phy.c文件下的genphy_update_link函數(shù)和genphy_parse_link函數(shù)。


genphy_update_link實現(xiàn)如下:

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; }


該函數(shù)首先調(diào)用phy_read函數(shù)來獲取BMSR寄存器的值

什么是BMSR,這是網(wǎng)卡的狀態(tài)寄存器,BMCR是網(wǎng)卡的控制寄存器,一般而言,BMSR供我們讀取數(shù)據(jù)進(jìn)行判斷網(wǎng)卡的狀態(tài),而BMCR一般是供我們寫入數(shù)據(jù)進(jìn)行控制。

下圖是該網(wǎng)卡的寄存器表:

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

回到函數(shù),當(dāng)讀取到網(wǎng)卡的狀態(tài)寄存器的值后,開始進(jìn)行一系列判斷

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


然后是genphy_config函數(shù),該函數(shù)是對網(wǎng)卡的信息進(jìn)行一個讀取,比如是否支持千兆網(wǎng)卡,是否支持10M/100M 單工/雙工。

函數(shù)實現(xiàn):

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; }
可以看出,該函數(shù)和genphy_update_link的實現(xiàn)風(fēng)格很像,不再詳細(xì)說明


再回到cpsw_slave_update_link函數(shù),這樣就完成了對于網(wǎng)卡的重新連接。


回到cpsw_init 函數(shù),接著初始化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的內(nèi)存地址后,初始化這些通道,方法也很簡單,寫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設(shè)備的初始化,網(wǎng)卡的配置也基本完成


3、實現(xiàn)網(wǎng)卡的發(fā)送

網(wǎng)卡的發(fā)送函數(shù)主要是調(diào)用cpsw_send函數(shù),比如當(dāng)輸入ping命令時,經(jīng)過一系列的裝包,最后調(diào)用cpsw_send函數(shù)進(jìn)行發(fā)送,在發(fā)送ICMP包之前,會先調(diào)用arp發(fā)送arp地址解析協(xié)議,然后根據(jù)收到的rarp的包得知主機(jī)的mac地址。然后再發(fā)送icmp包,因此在這里首先要看如何實現(xiàn)arp包的發(fā)送。


首先了解arp協(xié)議的格式:


然后分析cpsw_send函數(shù):

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); }

函數(shù)調(diào)用flush_dcache_range函數(shù)對數(shù)據(jù)緩存進(jìn)行刷新,刷新的地址就是packet的地址,因為要保證緩存和內(nèi)存的一致性,也就是一致性DMA。

然后調(diào)用cpdma_process和cpdma_submit函數(shù)將數(shù)據(jù)傳給DMA描述符,再傳送給DMA通道,DMA通過MII發(fā)送給網(wǎng)卡,網(wǎng)卡再將數(shù)據(jù)發(fā)送出去。




以上就是本人實現(xiàn)網(wǎng)卡驅(qū)動的大致過程,最后的描述可能有些不詳細(xì),時間太晚了,有問題可以留言交流~。

轉(zhuǎn)載請說明出處。

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。