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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33961829/article/details/93943668

智能推荐

Java问题点积累_java的问题点-程序员宅基地

文章浏览阅读155次。备忘录:为查询和总结记录。记录:NO.235记录:还能写几行代码?。!本例环境: IDEA版本:IntelliJ IDEA 2019.3.3 x64 操作系统:CentOS 7 JDK版本:jdk-8u251-linux-x64 Tomcat8:apache-tomcat-8.5.571.两个字符串比较 比较两个不相等,成立。 !StringUtils.equals(args[i].toString(),..._java的问题点

网络空间安全导论实践报告_西电网络空间安全导论实验-程序员宅基地

文章浏览阅读1.9k次。现代密码技术已经延伸到了信息安全诸多领域,例如身份认证,数据完整性检测等,是信息安全的基础与核心。随着密码学在网络信息系统的广泛应用,密码技术的标准化和管理的规范化也初具雏形,为信息安全保障提供了坚实的后盾。本实践通过介绍对称加密算法和非对称加密算法,并利用CrypTool软件模拟算法,来提高我们生活中的加密意识,提升自我防护水平。_西电网络空间安全导论实验

安装GIT服务器 gitolite,及权限管理_安装git服务器 用户管理-程序员宅基地

文章浏览阅读562次。安装参考:http://zengrong.net/post/1720.htmhttp://blog.chinaunix.net/uid-15174104-id-3843570.htmlgit使用参考: Git的思想和基本工作原理 http://www.nowamagic.net/academy/detail/48160210 Git 教程_安装git服务器 用户管理

基于阻塞队列实现消费者和生产者_阻塞队列实现下单-程序员宅基地

文章浏览阅读313次。不知道为什么原来写的文章内容被清空,可能是没有上传成功吧,那我就再写一遍好了。。。 关于阻塞队列的说明和实现在另一篇文章中已经做了比较消息的介绍了,这里就不再赘述。 生产者-消费者 算得上是计算机领域中经典的问题之一了,生产者生产数据,消费者消费数据,类似于小学数学中的“一个池子进水,一个池子出水”问题。废话不多说了,直接上代码吧: PS:本来想粘代码图片上来,但是考虑到reader对象很可能_阻塞队列实现下单

The Applications of RT-Thread RTOS-程序员宅基地

文章浏览阅读93次。The Applications of RT-Thread RTOSIntroductionThe user application is the application layer of RT-Thread RTOS. The developer can develop his/her application out of RT-Thread RTOS firmware environm..._rtt工程中applications

【SpringCloud微服务技术栈(中)-异步通信】_同步通信和异步通信-程序员宅基地

文章浏览阅读455次。MQ(MessageQueue),中文是消息队列,字面来看就是存放消息的队列.也就是事件驱动架构中的Broker[安装]:方式一:在线拉取方式二:从本地下载将已有的安装包上传(拖到)到虚拟机后,使用命令加载镜像即可:安装MQ,执行下面的命令来运行MQ容器:界面:[案例]:HelloWorld案例[实现]:导入项目工程mq-demo代码说明:PublicerTest代码:可以看到Consumer发送完消息后就把连接和通道关闭了,这充分说明解耦合。_同步通信和异步通信

随便推点

7-56 统计不及格人数_7-56 不及格人数-程序员宅基地

文章浏览阅读599次。7-56 不及格人数分数 20全屏浏览题目切换布局作者 usx程序设计类课程组单位 绍兴文理学院n个同学参加了期末考试,以作出的题目数量代表成绩,成绩为0或者1表示不及格,求出不及格的同学数量。_7-56 不及格人数

初学verilog必看_verilog 通配符-程序员宅基地

文章浏览阅读3.4w次,点赞44次,收藏364次。原文链接:http://www.cnblogs.com/capark/p/4121369.html先记下来: 1、不使用初始化语句; 2、不使用延时语句; 3、不使用循环次数不确定的语句,如:forever,while等; 4、尽量采用同步方式设计电路; 5、尽量采用行为语句完成设计; 6、always过程块描述组合逻辑,应在敏感信号表中列出所有的输入信号; 7、所有的内部寄存器都应该_verilog 通配符

一种用于永磁同步电机PMSM的自适应状态反馈速度控制器(Simulink仿真实现)-程序员宅基地

文章浏览阅读790次,点赞22次,收藏21次。电机的效率、紧凑的结构和可靠性是许多应用中的重要特性,例如工业机器人、计算机数控(CNC)机床、通风和空调系统、电动和混合动力车辆[1, 2]。众所周知,如果使用具有恒定系数的控制器,非恒定的设备参数可能导致系统的不良行为。自适应控制器应用于许多领域,例如PMSM驱动的速度控制[9]、感应电机驱动的控制[10]、双质量感应电机驱动[11]、拖车移动机器人[12]、混沌系统的控制[13]、航天器的控制[14]、磁微机器人控制[15]、机器人踝关节外骨骼控制器[16]和MEMS三轴陀螺仪控制器[17]。

关于反激电源中RCD电路的一些随笔_交错反激电路rcd-程序员宅基地

文章浏览阅读476次,点赞4次,收藏10次。硬件知识随笔_交错反激电路rcd

Tars学习(三) —— Docker方式之Tars安装与扩容_docker怎么部署tars微服务-程序员宅基地

文章浏览阅读3.3k次。本篇文章主要介绍如何使用docker快速安装Tars框架!1. 下载镜像 docker pull mysql:5.6 docker pull tarscloud/tars:dev docker pull tarscloud/tars-node:dev2. 搭建Tars框架2.1 创建mysql容器 docker run --name my..._docker怎么部署tars微服务

java 内部类的四种“写法”_java内部类怎么写-程序员宅基地

文章浏览阅读2.2k次,点赞10次,收藏15次。java进阶——内部类(成员内部类、静态内部类、局部内部类,匿名内部类)_java内部类怎么写