这段时间在移植LWIP到ucos2上时,需要调试网卡驱动,平台是SOC3210i
就调试这一驱动的过程中尝试由下面的话来概括和感受下调试网卡驱动的过程:
首先我们得知道这个网卡系统实际上是由什么模块组成:
MAC控制器和PHY芯片
那么这两个模块是什么,起什么作用,摘自网络一段话:
网卡工作在osi的最后两层,物理层和数据链路层,物理层定义了数据传送与接收所需要的电与光信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口。物理层的芯片称之为PHY。数据链路层则提供寻址机构、数据帧的构建、数据差错检查、传送控制、向网络层提供标准的数据接口等功能。以太网卡中数据链路层的芯片称之为MAC控制器。很多网卡的这两个部分是做到一起的。
而SOC3210的MAC控制器和PHY是分开两部分的,分别对应实际硬件是:DC2114A和RTL8201B
其中,CPU是与MAC控制器打交道,而MAC控制器再与PHY打交道。那么CPU能不能直接与PHY打交道?那就需要MII接口,即媒体独立接口,通过它应用程序就可以监视和控制PHY。
一、网卡的初始化的流程:
1、复位MAC控制器
设置MAC总线模式寄存器的SWRESET位,并在查询该位,如果被清0了,那么复位成功。
2、设置MAC中断使能位
这里的中断使能位,是指控制器内部的中断配置,选择是否使能发送中断,或者接收中断,这里我们用查询发送,中断接收。所以只
置位了NINTE和RXIE。
3、开始初始化接收和发送描述符
网络数据的接收和发送,都是由MAC的DMA进行数据传输,而DMA就是根据描述符指定的信息来获取正确的数据,描述符是一个链表,其中将表头的指针保存到MAC0_RX_LIST_BASE_ADDR和MAC0_TX_LIST_BASE_ADDR就可以了。对于描述符链表,实际上就是有多个描述符组成,通常,它们的连接方式有两种:环模式和链模式。
这两种模式的区别是:
在“链”模式下,每个描述符中都存有一个指针,指向链表中下一个描述符的地址。在“环”模式下,链表中下一个描述符同当前描述符末尾的地址
间隔是固定的,存放在0号控制状态寄存器即总线模式寄存器中的DSL域中。
这里我们用链模式。也就是每个描述符只能保存一帧数据,同时指向下一个描述符。
在代码中用结构体描述:
58 /* Tx and Rx Descriptor */
59 typedef struct {
60 u32_t status;
61 u32_t ctrl;
62 u32_t addr;
63 u32_t next;
64 } eth_desc_t;
此阶段,只需要把各个描述符的地地址指针连接成一个链表就可以了。由于是用链的模式结构,所以ctrl域的第28位必须设置为1,也就是说发送描述符的ctrl与TD_TCH相或,接收描述符的ctrl与RD_RCH相或。这样,DMA传输数据的时候就会以链模式去获取地址和数据。在status字段里有个重要的域OWN,为1时,表示MAC占有该描述符,为0时,表示CPU占有该描述符。实际上,当属于这个描述符的资源,不进行数据传输时,为0,当进行数据传输时为1。这样,在初始化的时候,不发送,所以发送描述符的OWN为0,进行接收,所以接收描述符的OWN为1。
所以:
对于发送描述符
tx_desc.status = 0x80000000;
tx_desc.ctrl |= TD_TCH;
tx_desc.addr = 0;
tx_desc.next = next_tx_desc_addr;
对于接收描述符
rx_desc.status = 0;
rx_desc.ctrl |= RD_RCH;
rx_desc.addr = rev_buf1;
rx_desc.next = next_rx_desc_addr;
由于发送描述符的OWN为1,也就是说在进行接收,这样就需要指定接收的最后一个描述符,所以对于最后一个接收描述符,应该有这样的操作:rx_desc.ctrl |= RD_RER。也就是说,对于ctrl字段,设置第25位来指定该描述符为最后一个描述符。
接着,把第一个接收描述符和第一个发送描述符保存到MAC0_RX_LIST_BASE_ADDR和MAC0_TX_LIST_BASE_ADDR寄存器。
4、使能MAC控制器
这部分是通过设置操作模式寄存器MAC0_OPERATION_MODE,也即CSR6来进行操作。
CSR6 = 0x32000040 | OMR_ST | OMR_SR | OMR_PS
其中0x32000040为默认值,第6位为1,表示启动混杂模式,这样,所以的合法数据帧将被接收,如果只需要接收目标地址为MAC地址的数据包的话,那么需要进入正常模式,也就是把第6位清零。OMR_ST和OMR_SR为使能发送接收。OMR_PS为端口选择,设置该位后,就选择了MII接口。如果我们进入正常模式,那么:
CSR6 &= ~(1<<6);
5、发送建立数据帧
根据文档说明,建立数据帧并不是真正发送出去,而是进行地址滤,也就是说,需要接收什么目标地址的数据包,那么就将该MAC地址放到建立帧里。
那么该表内的Physical Adress都将被MAC接收,不在此表内的就丢弃。所以自己MAC地址必须包括在内,这样就可以对发送给自己的数据包接收了。
对该帧数据对应的发送描述符进行赋值:
tx_desc.status = (1<<OWN);
tx_desc.ctrl = TD_TCH | TD_SET| SETUP_FRAME_LEN;
tx_desc.addr = setup_frame_buf;
然后写入非零值到发送查询请求寄存器MAC_TX_POLL_REQ,即CSR3。
当OWN清零之后,数据发送完毕。这样建立帧完成了。
PS:关于建立帧帧格式和发送完成标志与DC2114A文档所说有所不同,在帧格式中的第一个地址里填入自身的MAC地址后,还需要在其后填上
0xffff,或者写入广播地址。建立帧完成后,status等于0,而不是文档所说的0x7fffffff。
6、使能CPU中断控制器中的MAC中断
设置中断触发类型
使能MAC中断
到此网卡的初始化基本完成。
二、网络驱动的读写操作:
读写操作都有两种方式,一种是中断方式,另一种是查询方式。中断方式是指由中断来判断操作是否完成了;查询方式是指查询OWN的值,直到它变为0,这时操作完成。在这里,我们用查询方式来进行发送,用中断方式接收数据。
1、读取接收到的数据
判断当前的接收描述符的OWN位是否为0,如果为0表示MAC接收数据完毕。否则发生错误,返回。
读取status字段的第15位,如果为1,发生了错误,返回。
读取status字段中的16~29位,获取数据帧长度len,其中最后4个字节为CRC32值。
从addr字段中获取地址,读取该地址的数据,这len-4个字节的数据就是接收到的数据。
清0网口状态寄存器的数据接收相关的中断状态位。
置该接收描述符的OWN位,以准备下次的接收。
2、发送一帧数据
设置发送描述符的地址addr为将要发送数据的缓冲区。
设置发送描述符的控制ctrl的29和30位,表示只发送一帧;设置发送数据的长度。
设置发送描述符的status的OWN。
往发送查询请求寄存器MAC_TX_POLL_REQ,即CSR1写入非零值,这样就开始发送了。
最后查询OWN为是否为0,如果为0,则发送操作完成,然后清零网口状态寄存器的与数据发送相关的中断状态位。
三、MII接口监控PHY:
这里的MII接口是通过SMI来进行操作,读写时序如下:
其中,MDC、MDO、MDEN、MDI信号变化,对其操作可通过寄存器的 MAC_SMI_EEPROM_CTL的 19 位至 16 位完成。
而PHY地址是根据PHY芯片的硬件上下拉电阻来决定的,具体对应PHY芯片实际硬件电路图和对应的手册。
Register地址在对应的PHY芯片手册里可以查到。
确定PHY地址还有一个办法,那就是查询PHY的内置ID号一决定PHY地址。查看RTL8201B知道第3个寄存器的值是一个固定值8201,那么设PHY地址从0到30,读取第3个寄存器,判断是否为0xffff,如果是,表示该地址没有PHY芯片,如果是8021,那么得到该PHY芯片的地址,如果是其它值,那么得到其它PHY芯片的地址。
这样,通过SMI接口,就可以读取PHY里面的状态,及修改PHY的设置,从而达到监控PHY的目的。具体各个寄存器的意义对应具体的PHY芯片手册。