Writing a "bare metal" operating system for Raspberry Pi 4 (Part 15) ==================================================================== Adding a TCP/IP stack --------------------- Having achieved "proof of life" from our Ethernet module in _part14-spi-ethernet_, you're doubtless wondering how to go from there to serving web pages, posting tweets on Twitter or perhaps even just simply responding to a ping! This is where you'll need a fully-fledged TCP/IP stack that goes way beyond handcrafted ARPs, implementing many more protocols to achieve efficient bi-directional communication. In this part we make use of some code from Guido Socher of [tuxgraphics.org](http://tuxgraphics.org/), designed to be a lightweight TCP/IP stack for embedded devices. I chose this because it was super simple to get working (or "port"), but you might want to look at [LwIP](https://en.wikipedia.org/wiki/LwIP) if you need something more advanced. The code -------- Most of the new code is in the _tcpip/_ subdirectory. I actually came across it in [this tarball](http://tuxgraphics.org/common/src2/article09051/eth_tcp_client_server-dhcp-5.10.tar.gz) and, again, made only a very few cosmetic changes (`diff` is your friend!). It did require me to expose the `strlen()` function we implemented in _lib/fb.c_, so that's added to _include/fb.h_. Similarly, we expose the `memcpy()` function we implemented in _kernel/kernel.c_, so that's added to _kernel/kernel.h_. I also needed a single function that tells the ENC to send a packet. Nothing new here, just different packaging: ```c void enc28j60PacketSend(unsigned short buflen, void *buffer) { if (ENC_RestoreTXBuffer(&handle, buflen) == 0) { ENC_WriteBuffer((unsigned char *) buffer, buflen); handle.transmitLength = buflen; ENC_Transmit(&handle); } } ``` This was also added to _kernel/kernel.h_. What happened to _arp.c_? ------------------------- You'll notice that I've merged _arp.c_ and _kernel.c_. We still initialise the network card in exactly the same way but, when we're done, we call this function in Guido's code: ```c init_udp_or_www_server(myMAC, deviceIP); ``` This tells the TCP/IP library who we are, so we're all on the same page! Finally, and aside from a little cleanup (eg. moving the HAL timer functions to _io.c_ with the commensurate changes to _io.h_), the major change is the new `serve()` function: ```c void serve(void) { while (1) { while (!ENC_GetReceivedFrame(&handle)); uint8_t *buf = (uint8_t *)handle.RxFrameInfos.buffer; uint16_t len = handle.RxFrameInfos.length; uint16_t dat_p = packetloop_arp_icmp_tcp(buf, len); if (dat_p != 0) { debugstr("Incoming web request... "); if (strncmp("GET ", (char *)&(buf[dat_p]), 4) != 0) { debugstr("not GET"); dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 401 Unauthorized\r\nContent-Type: text/html\r\n\r\n