Writing a "bare metal" operating system for Raspberry Pi 4 (Part 15) ==================================================================== [< Go back to part14-spi-ethernet](../part14-spi-ethernet) 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_. I have also gone away from doing anything wih multicore or IRQ timers to keep this kernel simple. 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/system timer functions to _lib/io.c_ with the commensurate changes to _include/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