IP Stack Hardware Abstraction

The most important component of the TCP/IP stack is the hardware abstraction layer.  Without it, there is simply no way to communicate with the network.  As I’m still unsure about which hardware to use on the long run and being a good software architect, I created a hardware abstraction layer which is pretty simple to understand.  We need to initialize the hardware and to be able send/receive packets, and if somewhat possible keep track of some statistics.  All of these are grouped in the netif.h and netif.c files.  Like lwIP I created a structure (netif_t) that describes a network interface, via pointer functions the real implementation can be delegated to the hardware driver(s) to be provided for each ethernet chip/module.  Also I took over lwIP’s support for multiple network interfaces.  Adding all this together delivers me the following structure which describes pretty good a network interface:

/** Generic data structure used for all lwIP network interfaces.  The following fields should be filled in by the initialization
 *  function for the device driver: hwaddr_len, hwaddr[], mtu, flags */
typedef struct __attribute__((__packed__)) netif {
	struct netif *next; // Pointer to next network interface in linked list
	uint32_t ip_addr;
	uint32_t netmask;
	uint32_t gateway;

	netif_hw_input_fn hw_input; // Get a packet from the network adapter
	netif_input_fn input; // This function is called by the network device driver to pass a packet up the TCP/IP stack
	netif_output_fn output; // This function is called by the IP module when it wants to send a packet on the interface (incl. MAC resolving).
	netif_linkoutput_fn linkoutput; // This function is called by the ARP module when it wants to send a packet as-is on the interface.
	netif_status_callback_fn status_callback; // This function is called when the netif state is set to up or down (optional)
	netif_status_callback_fn link_callback; // This function is called when the netif link is set to up or down (optional)
	netif_status_callback_fn remove_callback; // This function is called when the netif has been removed (optional)

	void *state; // This field can be set by the device driver and could point  to state information for the device.
	struct dhcp *dhcp;  // the DHCP client state information for this netif
	uint8_t *hostname;
	uint16_t mtu;  // maximum transfer unit (in bytes)
	uint8_t hwaddr_len; // number of bytes used in hwaddr
	uint8_t hwaddr[NETIF_MAX_HWADDR_LEN]; // link level hardware address of this interface
	uint8_t flags; // flags (see NETIF_FLAG_ above)
	uint8_t name[2]; // descriptive abbreviation
	uint8_t num; // number of this interface
	uint8_t link_type;
	uint32_t link_speed;

	/* counters */
	uint32_t rx_bytes;
	uint32_t rx_packets;
	uint32_t rx_drop_packets;
	uint32_t tx_bytes;
	uint32_t tx_packets;
	uint32_t tx_drop_packets;
} netif_t;

These functions can be called externally to initialize/use the interface(s):

void netif_init(void);
void netif_set_default(netif_t*netif);
netif_t *netif_add(netif_t *netif, uint32_t ipaddr, uint32_t netmask, uint32_t gw, void *state, netif_init_fn init, netif_input_fn input);
void netif_set_addr(netif_t *netif, uint32_t ipaddr, uint32_t netmask, uint32_t gw);
void netif_set_up(netif_t *netif);
void netif_set_down(netif_t *netif);
void netif_poll(netif_t *netif);
void netif_poll_all(void);

For example to initialize the ENC28j60 (hardware implementation follows later) you would call the following:

netif_t *enc28j60;
...
	uint32_t ip_addr = 0;
	uint32_t netmask = 0;
	uint32_t gw = 0;
	enc28j60 = malloc(sizeof(netif_t));
	memset(enc28j60, 0, sizeof(netif_t));
	memcpy(enc28j60->hwaddr, MAC, 6);
	enc28j60 = netif_add(enc28j60, ip_addr, netmask, gw, NULL, drvEnc28j60_init, eth_input);
	netif_set_default(enc28j60);

The complete code for the netif can be found here:[wpfilebase tag=file id=13 tpl=simple /][wpfilebase tag=file id=14 tpl=simple /]

 

Tagged , , , . Bookmark the permalink.

2 Responses to IP Stack Hardware Abstraction

  1. Andrew Eliasz says:

    Not sure if there is an answer for this …
    I want to use lwip on an ARM Cortex spansion board with two ethernet interfaces. Is it possible to do this using lwIP and is there an example I can use to get me started please.

    • Patrick says:

      lwip is universal, you’ll need to develop a driver for the board you want to use or find one…

Leave a Reply

Your email address will not be published. Required fields are marked *