2、ethernetif.c 函数接口编写
ethernetif.c文件主要涉及5个接口函数的编写:
static void low_level_init(struct netif *netif);
static err_t low_level_output(struct netif *netif,
struct pbuf *p);
static struct pbuf * low_level_input(struct netif *netif);
err_t ethernetif_input(struct netif *netif);
err_t ethernetif_init(struct netif *netif);
(1) low_level_init(struct netif *netif)
从函数的声明可以看出,我们的主要工作是对struct netif定义的netif初始化,查阅LwIP源文件找到struct netif结构体的定义(删除条件编译后的基本定义):
struct netif {
/** pointer to next in linked list */
struct netif *next;
/** IP address configuration in network byte order */
ip_addr_t ip_addr;
ip_addr_t netmask;
ip_addr_t gw;
/** This function is called by the network device driver
* to pass a packet up the TCP/IP stack. */
netif_input_fn input;
/** This function is called by the IP module when it wants
* to send a packet on the interface. This function typically
* first resolves the hardware address, then sends the packet. */
netif_output_fn output;
/** This function is called by the ARP module when it wants
* to send a packet on the interface. This function outputs
* the pbuf as-is on the link medium. */
netif_linkoutput_fn linkoutput;
/** This field can be set by the device driver and could point
* to state information for the device. */
void *state;
/** maximum transfer unit (in bytes) */
u16_t mtu;
/** number of bytes used in hwaddr */
u8_t hwaddr_len;
/** link level hardware address of this interface */
u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
/** flags (see NETIF_FLAG_ above) */
u8_t flags;
/** descriptive abbreviation */
char name[2];
/** number of this interface */
u8_t num;
};
其中
typedef err_t (*netif_input_fn)(struct pbuf *p,
struct netif *inp);
typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr);
typedef err_t (*netif_linkoutput_fn)(struct netif *netif,
struct pbuf *p);
在这里我们将DM9000底层的MAC地址及初始化写入该函数内,其代码如下:
static void low_level_init(struct netif *netif)
{
/* set MAC hardware address length */
netif->hwaddr_len = ETHARP_HWADDR_LEN;
/* set MAC hardware address */
netif->hwaddr[0] = MAC_ADDR0;
netif->hwaddr[1] = MAC_ADDR1;
netif->hwaddr[2] = MAC_ADDR2;
netif->hwaddr[3] = MAC_ADDR3;
netif->hwaddr[4] = MAC_ADDR4;
netif->hwaddr[5] = MAC_ADDR5;
/* maximum transfer unit */
netif->mtu = 1500;
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
/* Do whatever else is needed to initialize interface. */
DM9000_Init();
}
(2) struct pbuf * low_level_input(struct netif netif)
该函数主要是用来实现将网卡中接收到的数据转换为LwIP定义的struct pbuf 类型的数据包,便于协议栈数据处理。我们先来看看结构体struct pbuf :
struct pbuf {
/** next pbuf in singly linked pbuf chain */
struct pbuf *next;
/** pointer to the actual data in the buffer */
void *payload;
/**
* total length of this buffer and all next buffers in chain
* belonging to the same packet.
*
* For non-queue packet chains this is the invariant:
* p->tot_len == p->len + (p->next? p->next->tot_len: 0)
*/
u16_t tot_len;
/** length of this buffer */
u16_t len;
/** pbuf_type as u8_t instead of enum to save space */
u8_t /*pbuf_type*/ type;
/** misc flags */
u8_t flags;
/**
* the reference count always equals the number of pointers
* that refer to this pbuf. This can be pointers from an application,
* the stack itself, or pbuf->next pointers from a chain.
*/
u16_t ref;
};
这里我们要做的工作就是将网卡中数据存入payload 中,并将网络数据的相关信息写入struct pbuf 链表中。先将low_level_input() 的实现代码列出:
static struct pbuf *
low_level_input(struct netif *netif)
{
// cm3 cpu is little endian mode,so the first byte copied(low address)is low byte in 16bit mode
// data copied from address 0x0c00 in dm9000 sram
// there are four bytes before the mac frame in the packet
// 1 byte:0x00 or 0x01 indicate there are or no packets coming;
// 2 byte:related RSR status;
// 3 byte:the mac frame length's low byte;
// 4 byte:the mac frame length's high byte;
// mac header: 14bytes
struct pbuf *p, *q;
uint16_t packet_length; // packet length
uint8_t frameheader[18]; // 14 mac header + 4 byte
uint8_t *payload;
uint16_t len;
uint16_t Rxstatus;
if(DM9000_CheckNetPacket()) // check packet is coming
{
DM9000->REG = DM9000_MRCMD;
DM9000_CopyIn(18, frameheader); //copy 18 byte header
Rxstatus = ((uint16_t)frameheader[1])<<8|frameheader[0];
packet_length = ((uint16_t)frameheader[3])<<8|frameheader[2];
// check frame is a IP packet(0x0800) or ARP packet(0x0806)
// check packet length is larger than 1536 bytes
if((frameheader[3]>0x06)||(frameheader[16] != 0x08)||((frameheader[17] != 0x00)&&(frameheader[17] != 0x06)))
{
DM9000_Discard(packet_length);
return NULL;
}
p = pbuf_alloc(PBUF_RAW, packet_length, PBUF_POOL); //allocate a pbuf
if(p != NULL)
{
memcpy(p->payload,frameheader+4,14);
// copy mac frame header
for(q=p;q!=NULL;q=q->next)
{
len = q->len;
payload= (uint8_t *)(q->payload);
if(q == p)
{
payload= (uint8_t *)(q->payload)+14;
len = q->len - 14;
}
DM9000_CopyIn(len, payload);
}
}
else // allocate a pbuf failed
{
printf("input packet allocate pbuf failed\n");
DM9000_Discard(packet_length-14);
}
}
else
{
p = NULL;
}
return p;
}
该函数工作流程首先是甄别网口是否接收到有效的数据包,然后利用pbuf_alloc()向内存池申请内存,并复制读取DM9000的SRAM接收到的数据。这里介绍一下以太网数据的帧格式:
D-MAC为目标机的MAC地址,S-MAC为源主机的MAC地址,类型决定数据是IP数据包(0x0800)还是ARP数据包(0x0806),CRC为循环冗余校验。根据DM9000的数据手册,我们看到DM9000接收到的数据一般在以太网数据帧头前额外添加4个字节,这4个字节的的含义已在程序中注释。程序利用函数DM9000_CheckNetPacket()检验DM9000接收的首字节是否为0x01,并复制收到的前18个字节数据(DM9000 add 4 bytes+MAC Frame 14 bytes)到frameheader[]数组中,以判别接收的数据包长度和类型是否合法。若合法,利用DM9000_CopyIn(len, payload)复制数据到新申请的pbuf中。DM9000_CopyIn()主要功能是复制DM9000 接收缓存SRAM中的数据到单片机内存中,其程序如下:
#define WORDMODE 1
void DM9000_CopyIn(uint16_t length, uint8_t* databuf)
// copy length of data from dm9000 sram to databuf
{
uint16_t count; // 16bit - packet words
uint16_t *dataread;
// WORDMODE decide by microcontroller 8-bit or 16-bit
count = WORDMODE?length>>1:length;
dataread = (uint16_t *)databuf;
#if WORDMODE
dataread = (uint16_t *)databuf;
while(count--)
*dataread++ = DM9000->DATA;
// length is odd
if(length&0x01)
*((uint8_t *)dataread) = (uint8_t)DM9000->DATA;
#else
while(count--)
*databuf++ = (uint8_t)DM9000->DATA;
#endif
}
需要注意到是DM9000->REG = DM9000_MRCMD; 对DM9000寄存器MRCMD的访问,其地址递增一个字节还是半字长是由DM9000的工作位长模式决定的。另外,在复制SRAM中的数据时,需要舍弃DM9000添加的前4个字节。程序中还有一函数个DM9000_Discard(packet_length); 该函数的作用是丢弃不合法 数据,实现MRCMD地址的移动,其源代码也附上如下:
void DM9000_Discard(uint16_t length)
{
uint16_t count;
uint16_t temp;
uint8_t btemp;
count = WORDMODE?length>>1:length;
#if WORDMODE
while(count--)
temp = DM9000->DATA ;
if(length&0x01)
btemp = DM9000->DATA;
#else
while(count--)
btemp = DM9000->DATA;
#endif
}
(3)low_level_output(struct netif *netif, struct pbuf *p)
该函数的主要功能是向某一网口发送数据,其难点在于如何将链表struct pbuf p中的数据组织成一串数据向网卡发送。由于DM9000的工作在16-bit模式,当链表pbuf中的某一个pbuf长度为偶数时还好说,当为奇数时,最后一个字节则需要填充字节,这对于整个pbuf链表来说是不允许的。*low_level_output()实现代码如下:
static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
struct pbuf *q;
uint8_t chainflag = 0;
// the chain flag between pbuf and next pbuf
uint8_t *dat; // transfer data pointer
uint16_t datalength; // transfer data length
uint16_t chaindat;
// link the last byte of pbuf and the next pbuf first byte
uint16_t packet_length; // packet data length
//pad length when send byte is lower than 64 byte
uint8_t isr; // setup IMR
packet_length = p->tot_len;
pad_length = 0;
//min packets (14 mac frame header + 46 data + 4 crc)
if(packet_length<60) //include 4 byte CRC
{
pad_length = 60- packet_length;
packet_length = 60;
}
isr = DM9000_ReadReg(DM9000_IMR);
isr &= ~IMR_PRI;
DM9000_WriteReg(isr,DM9000_IMR); // shut rx interrupt
//sending packet length setup
DM9000_WriteReg(packet_length&0xff, DM9000_TXPLL);
DM9000_WriteReg((packet_length&0xff00)>>8, DM9000_TXPLH);
DM9000->REG = DM9000_MWCMD; // to sram in dm9000
for(q=p;q!=NULL;q=q->next)
{
if(chainflag == 0) // pbuf first byte is not tranfered
{
if(((q->len)&0x01) && (q->next != NULL)) //pbuf length is odd
{
datalength = q->len - 1;
dat = (u8_t*)(q->payload);
// link this pbuf last byte and next pbuf first byte
chaindat = *((u8_t*)(q->payload)+q->len - 1);
chaindat += (u16_t)(*((u8_t*)(q->next->payload)))<<8;
chainflag = 1;
}
else
{
datalength = q->len;
dat = (u8_t *)(q->payload);
chainflag = 0;
}
}
else // fist byte has been transfered with last pbuf
{
if(((q->len)&0x01) && (q->next != NULL))
{
// one had been transferrd and the last will be transferred by chaindat
datalength = q->len-2;
dat = (u8_t*)(q->payload) + 1;
chaindat = *((u8_t*)(q->payload)+q->len - 1);
chaindat += (u16_t)(*((u8_t*)(q->next->payload)))<<8;
chainflag = 1;
}
else
{
datalength = q->len-1;
dat = (u8_t *)(q->payload)+1;
chainflag = 0;
}
}
// send data payload to dm9000's sram
DM9000_CopyOut(datalength, dat);
// send chain dat to dm9000's sram
if(chainflag == 1)
DM9000->DATA = chaindat;
}
// if packet length <60, pad 0 to send
if(pad_length>0)
DM9000_OutPad(pad_length);
DM9000_WriteReg(DM9000_ReadReg(DM9000_TCR)|0x01,DM9000_TCR); // start tx
/ // wait for sending completely
while(!(DM9000_ReadReg(DM9000_ISR)&0x02));
DM9000_WriteReg(0x02,DM9000_ISR);
isr |= IMR_PRI;
DM9000_WriteReg(isr, DM9000_IMR); // RX interrupt enable
return ERR_OK;
}
该函数在发送数据时,首先判断链表中即将发送的pbuf第一个数据是否已经被前一个pbuf 已经连接发送出去,然后根据本次发送pbuf长度的奇偶性复制数据到DM9000中的发送缓存SRAM中。文中注释已详细介绍每条语句的含义,需要注意的是在发送数据时一定要关闭接收中断。附上复制数据到DM9000的SRAM函数DM9000_CopyOut(datalength, dat) 和填充函数DM9000_OutPad(pad_length); 源程序,不做解释。
void DM9000_CopyOut(uint16_t length, uint8_t *databuf)
// copy databuf to dm9000's sram
{
uint16_t count;
uint16_t *datawrite;
count = WORDMODE?length>>1:length;
#if WORDMODE
//address is odd, dm9000 did not happen, for if
if((uint32_t)databuf & 0x01)
{
while(count--)
{
DM9000->DATA = (*databuf) | (*((uint16_t *)(databuf+1))<<8);
databuf += 2;
}
if(length&0x01)
DM9000->DATA = *databuf;
}
else // address is even
{
datawrite = (uint16_t *)databuf;
while(count--)
DM9000->DATA = *datawrite++;
if(length&0x01)
DM9000->DATA = *(uint8_t *)datawrite;
}
#else
DM9000->DATA = *databuf++; // 8-bit mode
#endif
}
(4)err_t ethernetif_init(struct netif *netif)
该函数可不用修改,源程序如下:
err_t ethernetif_init(struct netif *netif)
{
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
netif->output = etharp_output;
netif->linkoutput = low_level_output;
/* initialize the hardware */
low_level_init(netif);
return ERR_OK;
}
(5)err_t ethernetif_input(struct netif *netif)
源程序如下:
err_t ethernetif_input(struct netif *netif)
{
struct pbuf *p;
err_t err;
/* move received packet into a new pbuf */
p = low_level_input(netif);
/* no packet could be read, silently ignore this */
if (p == NULL) return ERR_MEM;
/* full packet send to tcpip_thread to process */
err = netif->input(p, netif); // use ethernet_input() in the etharp.c to tranfer pbuf to IP
if (err != ERR_OK)
{
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
pbuf_free(p);
}
return err;
}
|