概述
上篇博文介绍了在rtems下实现和网卡lan8710的通信,接下来就是实现网卡的标准化注册和初始化。
在这里本人参考了rtems m68k中gen68360的网络驱动文件和《tcp/ip详解卷二》:
首先是驱动的attach函数:
The driver attach function is responsible for configuring the driver and making the connection between the network stack and the driver.
这个驱动attach函数目的是为了配置驱动,并且连接协议栈和驱动函数,意义重大
以下是gen68360的attach函数实现:
int
rtems_scc1_driver_attach (struct rtems_bsdnet_ifconfig *config, int attaching)
{
struct scc_softc *sc;
struct ifnet *ifp;
int mtu;
int unitNumber;
char *unitName;
/*
* Make sure we're really being attached
*/
if (!attaching) {
printf ("SCC1 driver can not be detached.n");
return 0;
}
/*
* Parse driver name
*/
if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
return 0;
/*
* Is driver free?
*/
if ((unitNumber <= 0) || (unitNumber > NSCCDRIVER)) {
printf ("Bad SCC unit number.n");
return 0;
}
sc = &scc_softc[unitNumber - 1];
ifp = &sc->arpcom.ac_if;
if (ifp->if_softc != NULL) {
printf ("Driver already in use.n");
return 0;
}
/*
* Process options
*/
if (config->hardware_address) {
memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
}
else {
/*
* The first 4 bytes of the bootstrap prom
* contain the value loaded into the stack
* pointer as part of the CPU32's hardware
* reset exception handler. The following
* 4 bytes contain the value loaded into the
* program counter. The boards' Ethernet
* address is stored in the six bytes
* immediately preceding this initial
* program counter value.
*
* See start360/start360.s.
*/
const unsigned long *ExceptionVectors;
const unsigned char *entryPoint;
/*
* Sanity check -- assume entry point must be
* within 1 MByte of beginning of boot ROM.
*/
ExceptionVectors = (const unsigned long *)&_RomBase;
entryPoint = (const unsigned char *)ExceptionVectors[1];
if (((unsigned long)entryPoint - (unsigned long)ExceptionVectors)
>= (1 * 1024 * 1024)) {
printf ("Warning -- Ethernet address can not be found in bootstrap PROM.n");
sc->arpcom.ac_enaddr[0] = 0x08;
sc->arpcom.ac_enaddr[1] = 0xF3;
sc->arpcom.ac_enaddr[2] = 0x3E;
sc->arpcom.ac_enaddr[3] = 0xC2;
sc->arpcom.ac_enaddr[4] = 0x7E;
sc->arpcom.ac_enaddr[5] = 0x38;
}
else {
memcpy (sc->arpcom.ac_enaddr, entryPoint - ETHER_ADDR_LEN, ETHER_ADDR_LEN);
}
}
if (config->mtu)
mtu = config->mtu;
else
mtu = ETHERMTU;
if (config->rbuf_count)
sc->rxBdCount = config->rbuf_count;
else
sc->rxBdCount = RX_BUF_COUNT;
if (config->xbuf_count)
sc->txBdCount = config->xbuf_count;
else
sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF;
sc->acceptBroadcast = !config->ignore_broadcast;
/*
* Set up network interface values
*/
ifp->if_softc = sc;
ifp->if_unit = unitNumber;
ifp->if_name = unitName;
ifp->if_mtu = mtu;
ifp->if_init = scc_init;
ifp->if_ioctl = scc_ioctl;
ifp->if_start = scc_start;
ifp->if_output = ether_output;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
if (ifp->if_snd.ifq_maxlen == 0)
ifp->if_snd.ifq_maxlen = ifqmaxlen;
/*
* Attach the interface
*/
if_attach (ifp);
ether_ifattach (ifp);
return 1;
}
该函数首先注册两个结构体
ifnet结构包含了所有接口的通用信息,可以看到函数的最后部分就是对这个结构体中的成员进行初始化,if_name是一个短字符串,用于标识接口的类型,而if_unit 标识多个相同类型的实例,比如两个以太网卡,fe0和fe1,这样if_index在内核中唯一地标识这个接口。if_mtu表示最大的接收单元大小,通常是1500字节,if_init是一个初始化函数,所指向的scc_init函数是一个初始化网卡设置的函数,这个待会再说,if_ioctl是io控制函数,也就是io命令的接口,if_start是开始函数,指向的scc_start函数是实现以太网帧的发送,if_flag表明接口的操作状态和属性,IFF_BROADCAST表示接口用于广播网,IFF_SIMPLEX表示接口不能接受它自己发送的数据。如果支持多播的设备还要设置IFF_MULTICAST。
另一个结构体是scc_softc,是自己创建的结构体,每个结构的第一个成员是sc_ac,是一个arpcom结构。
声明完结构体后首先要转换驱动的名字,调用rtems_bsdnet_parse_driver_name函数,返回值是unitnumber,通常是大于0。
接着是设置硬件地址以及sc结构的一系列信息,这里不多说。
最后设置好ifp网络接口,然后调用if_attach把这个结构插入到接口链表中。
接着看下以上这个接口函数的参数rtems_bsdnet_ifconfig结构体的配置。
/*
** Configure the ethernet device structure
*/
if1_config.name = "fe1";//RTEMS_BSP_NETWORK_DRIVER_NAME;
if1_config.attach = RTEMS_BSP_NETWORK_DRIVER_ATTACH;
if1_config.ip_address= IPAddress;
if1_config.ip_netmask= NetMask;
if1_config.rbuf_count = 32;
if1_config.xbuf_count = 32;
if1_config.next = NULL;
在这里注册了rtems_bsdnet_ifconfig这个结构体if1_config,name选择fe1,在驱动函数中rtems_bsdnet_parse_driver_name解析这个name变成fe和1。attach这个成员非常重要,他告诉系统这个结构体的具体配置函数是RTEMS_BSP_NETWORK_DRIVER_ATTACH,也就是底层驱动实现的。
而整个驱动调用的步骤如下:首先启动系统,调用init函数,init中定义rtems_bsdnet_ifconfig这个结构体if1_config,然后初始化,在attach成员中选择底层驱动的attach的函数名,接着系统就会调用这个函数,也就是回到了最上面的驱动attach函数,对设备进行进一步的初始化。
对设备注册完后,要进行网卡的进一步的配置,比如工作方式是双工还是单工,速率是10/100/1000M,支不支持自协商等等。这个配置函数要在scc_init函数中调用
具体实现如下:
int genphy_config(void)
{
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 = cpsw_mdio_read(0,0,MII_BMSR); //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 = cpsw_mdio_read(0,0,MII_ESTATUS); //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(features);
return 0;
}
首先features指的是支持的通信方式,有MII、光纤等,这些用|表示支持一种即可
然后调用cpsw_mdio_read函数判断这个网卡是否支持自协商autonegotiation,如果不支持,则返回0并退出,如果结果包含BMSR_ANEGCAPABLE,说明支持自协商,就在features |= SUPPORTED_Autoneg;,然后是判断是否支持100M全双工,如果支持就在features加上该宏定义,以下都可以类推。
该函数到最后调用genphy_config_aneg函数将feature值写入到网卡寄存器中。
初始化后,调用genphy_update_link函数进行连接的更新:
具体实现如下:
int genphy_update_link(void)
{
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 = cpsw_mdio_read(0, 0, 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 (mii_reg & BMSR_LSTATUS)
{ phy_link=1;
return 0;
}
if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) {
int i = 0;
printf("Waiting for PHY auto negotiation to complete");
while (!(mii_reg & BMSR_ANEGCOMPLETE)) {
/*
* Timeout reached ?
*/
if (i > PHY_ANEG_TIMEOUT) {
printf(" TIMEOUT !n");
phy_link=0;
return 0;
}
if ((i++ % 500) == 0)
printf(".");
udelay(1000); /* 1 ms */
mii_reg = cpsw_mdio_read(0, 0, MII_BMSR);
}
printf(" donen");
phy_link=1;
} else {
/* Read the link a second time to clear the latched state */
mii_reg = cpsw_mdio_read(0, 0, MII_BMSR);
if (mii_reg & BMSR_LSTATUS)
phy_link=1;
else
phy_link=0;
}
return 0;
}
首先调用cpsw_mdio_read函数读状态寄存器,然后判断如果连接是成立的,就直接返回。否则就进入循环,每隔一段时间读取寄存器的值,然后判断。如果连接成立就返回函数,否则返回0,并打印timeout。
转载于:https://www.cnblogs.com/sichenzhao/p/9320304.html
最后
以上就是平常啤酒为你收集整理的rtems网络移植-网卡的注册和初始化的全部内容,希望文章能够帮你解决rtems网络移植-网卡的注册和初始化所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复