以太网驱动封装

xnet_tinny.h
cpp
typedef enum _xnet_err_t
{
XNET_ERR_OK = 0,
XNET_ERR_IO = -1,
}xnet_err_t;
xnet_err_t xnet_driver_open (uint8_t * mac_addr);
xnet_err_t xnet_driver_send (xnet_packet_t * packet);
xnet_err_t xnet_driver_read (xnet_packet_t ** packet);
port_pcap.c
cpp
#include<string.h>
#include<stdlib.h>
#include "pcap_device.h"
#include"../xnet_tiny/xnet_tiny.h"
static pcap_t* pcap;
// pcap所用网卡
const char* ip_str = "192.168.254.1";
const char my_mac_addr[] = { 0x11,0x22,0x33,0x44,0x55,0x66 };
xnet_err_t xnet_driver_open(uint8_t* mac_addr)
{
memcpy(mac_addr, my_mac_addr, sizeof(my_mac_addr));
pcap = pcap_device_open(ip_str, mac_addr, 1);
if (pcap == (pcap_t*)0)
exit(-1);
return XNET_ERR_OK;
}
xnet_err_t xnet_driver_send(xnet_packet_t* packet)
{
return pcap_device_send(pcap, packet->data, packet->size);
}
xnet_err_t xnet_driver_read(xnet_packet_t** packet)
{
uint16_t size;
xnet_packet_t* r_packet = xnet_alloc_for_read(XNET_CFG_PACKET_MAX_SIZE);
size = pcap_device_read(pcap, r_packet->data, XNET_CFG_PACKET_MAX_SIZE);
if (size)
{
r_packet->size = size;
*packet = r_packet;
return XNET_ERR_OK;
}
return XNET_ERR_IO;
}
以太网输入输出处理
不包含前导码(最前面)和CRC(在payload的最后),这部分内容由驱动自动填充,使用硬件电路可以自动校验CRC


xnet_tinny.h
定义以太网数据帧格式
为了预防编译器自动内存对齐的优化,我们使用#pragma pack(1)和#pragma pack()把该格式夹住
cpp
#pragma pack(1)
#define XNET_MAC_ADDR_SIZE 6
//以太网数据帧格式
typedef struct _xether_hdr_t
{
uint8_t dst[XNET_MAC_ADDR_SIZE];
uint8_t src[XNET_MAC_ADDR_SIZE];
uint16_t protocol;
}xether_hdr_t;
#pragma pack()
上层协议字段:
cpp
typedef enum _xnet_protocol_t
{
XNET_PROTOCOL_ARP = 0x0806,
XNET_PROTOCOL_IP = 0x0800,
}xnet_protocol_t;
回顾一下程序主流程:
cpp
#include <stdio.h>
#include "xnet_tiny.h"
int main (void)
{
xnet_init();
printf("xnet running\n");
while (1)
{
xnet_poll();
}
return 0;
}
以太网协议栈的初始化部分
xnet_tinny.c
cpp
void xnet_init(void)
{
ethernet_init();
}
ethernet_init 函数实现:
cpp
static uint8_t netif_mac[XNET_MAC_ADDR_SIZE];
static xnet_err_t ethernet_init(void)
{
xnet_err_t err = xnet_driver_open(netif_mac);
if (err < 0)
return err;
return XNET_ERR_OK;
}
以太网数据包轮询部分
cpp
void xnet_poll(void)
{
ethernet_poll();
}
以太网轮询函数ethernet_poll实现
cpp
static void ethernet_poll(void)
{
xnet_packet_t* packet;
if (xnet_driver_read(&packet) == XNET_ERR_OK)
{
ethernet_in(packet);
}
}
接收数据部分处理:
cpp
#define swap_order16(v) ((((v) & 0xFF) << 8) | (((v) >> 8) & 0xFF))
static void ethernet_in(xnet_packet_t* packet)
{
if (packet->size <= sizeof(xether_hdr_t))
return;
xether_hdr_t* hdr = (xether_hdr_t*)packet->data;
switch (swap_order16(hdr->protocol))
{
case XNET_PROTOCOL_ARP:
break;
case XNET_PROTOCOL_IP:
break;
}
}
发送数据部分:
cpp
static xnet_err_t ethernet_out_to(xnet_protocol_t protocol, const uint8_t* mac_addr, xnet_packet_t* packet)
{
xether_hdr_t* ether_hdr;
add_header(packet, sizeof(xether_hdr_t));
ether_hdr = (xether_hdr_t*)packet->data;
memcpy(ether_hdr->dst, mac_addr, XNET_MAC_ADDR_SIZE);
memcpy(ether_hdr->src, netif_mac, XNET_MAC_ADDR_SIZE);
ether_hdr->protocol = swap_order16(protocol);
return xnet_driver_send(packet);
}
注意接收/发送网络数据需要交换大/小端

在以太网输入处理部分下断,能正常断下
并且hdr->protocol转换前是0x0608,转换后是0x0806就没问题了

补充:为何只对hdr->protocol字段进行大小端转换?
对于逐字节拷贝的内容,数据分布在连续内存上,无需转换大小端
byte b[6]={0x11,0x22,0x33,0x44,0x55,0x66};就是按照顺序进入内存
而short x=0x1122;则是按照0x22 0x11进入内存,所以大小端完全不同