rtems网络移植-网卡的注册和初始化

上篇博文介绍了在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(" done\n");
		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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值