diff --git a/part15-tcpip-webserver/.DS_Store b/part15-tcpip-webserver/.DS_Store new file mode 100644 index 0000000..22c5e21 Binary files /dev/null and b/part15-tcpip-webserver/.DS_Store differ diff --git a/part15-tcpip/Makefile b/part15-tcpip-webserver/Makefile similarity index 100% rename from part15-tcpip/Makefile rename to part15-tcpip-webserver/Makefile diff --git a/part15-tcpip/Makefile.gcc b/part15-tcpip-webserver/Makefile.gcc similarity index 100% rename from part15-tcpip/Makefile.gcc rename to part15-tcpip-webserver/Makefile.gcc diff --git a/part15-tcpip-webserver/README.md b/part15-tcpip-webserver/README.md new file mode 100644 index 0000000..88135e6 --- /dev/null +++ b/part15-tcpip-webserver/README.md @@ -0,0 +1,94 @@ +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

ERROR

"); + } else { + if (strncmp("/ ", (char *)&(buf[dat_p+4]), 2) == 0) { + // just one web page in the "root directory" of the web server + debugstr("GET root"); + dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n

Hello world!

"); + } else { + // just one web page not in the "root directory" of the web server + debugstr("GET not root"); + dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n

Goodbye cruel world.

"); + } + } + + www_server_reply(buf, dat_p); // send web page data + debugcrlf(); + } + } +} +``` + +This is an infinite loop which waits for an incoming packet and then firstly passes it to Guido's `packetloop_arp_icmp_tcp()` function. This function implements some useful things, like responding to pings. I modified the routine to print a message to the screen when it sends a "pong" (look from line 1371 of _tcpip/ip_arp_udp_tcp.c_), so we can see when it's in action! + +Examining the return value of `packetloop_arp_icmp_tcp()` then allows us to check whether there is an incoming web request, since we've configured the TCP/IP library to be a web server in _tcpip/ip_config.h_ with `#define WWW_server`. + +We then serve responses based on three possible cases: + + * The incoming request is not a GET request (eg. maybe it's a HEAD request) - you can simulate this using the `curl` tool: `curl -I 192.168.0.66` + * The incoming request is a GET request for the root web page `/` - `curl 192.168.0.66/` + * The incoming request is a GET request for any non-root web page - eg. `curl 192.168.0.66/isometimes/monkey` + +I recommend reading [this page](http://tuxgraphics.org/electronics/200905/embedded-tcp-ip-stack.shtml) for a full explanation. The code I have ported is very similar to what you see there. + +_Imagine my excitement when I built, ran and could ping my RPi4 at 192.168.0.66 and get a web response to my browser on both my laptop and my iPhone!_ + +![Pinging from my iPhone](images/15-tcpip-webserver-pinging.jpg) +![Browsing from my laptop](images/15-tcpip-webserver-browser.png) diff --git a/part15-tcpip/boot/boot.S b/part15-tcpip-webserver/boot/boot.S similarity index 100% rename from part15-tcpip/boot/boot.S rename to part15-tcpip-webserver/boot/boot.S diff --git a/part15-tcpip/boot/link.ld b/part15-tcpip-webserver/boot/link.ld similarity index 100% rename from part15-tcpip/boot/link.ld rename to part15-tcpip-webserver/boot/link.ld diff --git a/part15-tcpip/boot/sysregs.h b/part15-tcpip-webserver/boot/sysregs.h similarity index 100% rename from part15-tcpip/boot/sysregs.h rename to part15-tcpip-webserver/boot/sysregs.h diff --git a/part15-tcpip-webserver/images/15-tcpip-webserver-browser.png b/part15-tcpip-webserver/images/15-tcpip-webserver-browser.png new file mode 100644 index 0000000..14cfdc0 Binary files /dev/null and b/part15-tcpip-webserver/images/15-tcpip-webserver-browser.png differ diff --git a/part15-tcpip/images/15-tcpip-pinging.jpg b/part15-tcpip-webserver/images/15-tcpip-webserver-pinging.jpg similarity index 100% rename from part15-tcpip/images/15-tcpip-pinging.jpg rename to part15-tcpip-webserver/images/15-tcpip-webserver-pinging.jpg diff --git a/part15-tcpip/include/fb.h b/part15-tcpip-webserver/include/fb.h similarity index 100% rename from part15-tcpip/include/fb.h rename to part15-tcpip-webserver/include/fb.h diff --git a/part15-tcpip/include/io.h b/part15-tcpip-webserver/include/io.h similarity index 91% rename from part15-tcpip/include/io.h rename to part15-tcpip-webserver/include/io.h index 0c79113..707ae63 100644 --- a/part15-tcpip/include/io.h +++ b/part15-tcpip-webserver/include/io.h @@ -17,3 +17,5 @@ void gpio_setPinOutputBool(unsigned int pin_number, unsigned int onOrOff); void gpio_initOutputPinWithPullNone(unsigned int pin_number); void uart_hex(unsigned int d); void uart_byte(unsigned char b); +unsigned long HAL_GetTick(void); +void HAL_Delay(unsigned int ms); diff --git a/part15-tcpip/include/mb.h b/part15-tcpip-webserver/include/mb.h similarity index 100% rename from part15-tcpip/include/mb.h rename to part15-tcpip-webserver/include/mb.h diff --git a/part15-tcpip/include/multicore.h b/part15-tcpip-webserver/include/multicore.h similarity index 100% rename from part15-tcpip/include/multicore.h rename to part15-tcpip-webserver/include/multicore.h diff --git a/part15-tcpip/include/spi.h b/part15-tcpip-webserver/include/spi.h similarity index 100% rename from part15-tcpip/include/spi.h rename to part15-tcpip-webserver/include/spi.h diff --git a/part15-tcpip/include/terminal.h b/part15-tcpip-webserver/include/terminal.h similarity index 100% rename from part15-tcpip/include/terminal.h rename to part15-tcpip-webserver/include/terminal.h diff --git a/part15-tcpip/kernel/arp.c b/part15-tcpip-webserver/kernel/kernel.c similarity index 56% rename from part15-tcpip/kernel/arp.c rename to part15-tcpip-webserver/kernel/kernel.c index 98c084f..420afa1 100644 --- a/part15-tcpip/kernel/arp.c +++ b/part15-tcpip-webserver/kernel/kernel.c @@ -1,17 +1,8 @@ -#include "../net/enc28j60.h" #include "../include/fb.h" +#include "../include/spi.h" +#include "../net/enc28j60.h" #include "../tcpip/ip_arp_udp_tcp.h" -ENC_HandleTypeDef handle; - -// MAC address to be assigned to the ENC28J60 - -unsigned char myMAC[6] = { 0xc0, 0xff, 0xee, 0xc0, 0xff, 0xee }; - -// IP address to be assigned to the ENC28J60 - -unsigned char deviceIP[4] = { 192, 168, 0, 66 }; - // HELPER FUNCTIONS void *memset(void *dest, unsigned char val, unsigned short len) @@ -45,27 +36,31 @@ uint8_t memcmp(void *str1, void *str2, unsigned count) return 0; } -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); - } -} - -// MAIN FUNCTIONS - -void net_test(void) +int strncmp(const char *s1, const char *s2, unsigned short n) { - while (1) { - while (!ENC_GetReceivedFrame(&handle)); - - uint16_t len = handle.RxFrameInfos.length; - uint8_t *buffer = (uint8_t *)handle.RxFrameInfos.buffer; - packetloop_arp_icmp_tcp(buffer, len); - } + unsigned char u1, u2; + + while (n-- > 0) + { + u1 = (unsigned char) *s1++; + u2 = (unsigned char) *s2++; + if (u1 != u2) return u1 - u2; + if (u1 == '\0') return 0; + } + + return 0; } +// NETWORKING GLOBALS AND FUNCTIONS + +ENC_HandleTypeDef handle; + +// MAC address to be assigned to the ENC28J60 +unsigned char myMAC[6] = { 0xc0, 0xff, 0xee, 0xc0, 0xff, 0xee }; + +// IP address to be assigned to the ENC28J60 +unsigned char deviceIP[4] = { 192, 168, 0, 66 }; + void init_network(void) { handle.Init.DuplexMode = ETH_MODE_HALFDUPLEX; @@ -100,3 +95,61 @@ void init_network(void) debugstr("done."); debugcrlf(); } + +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); + } +} + +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

ERROR

"); + } else { + if (strncmp("/ ", (char *)&(buf[dat_p+4]), 2) == 0) { + // just one web page in the "root directory" of the web server + debugstr("GET root"); + dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n

Hello world!

"); + } else { + // just one web page not in the "root directory" of the web server + debugstr("GET not root"); + dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n

Goodbye cruel world.

"); + } + } + + www_server_reply(buf, dat_p); // send web page data + debugcrlf(); + } + } +} + +// MAIN FUNCTION + +void main(void) +{ + fb_init(); + + // Init network and serve web pages + + spi_init(); + init_network(); + serve(); + + // Catch us if we fall + + while(1); +} diff --git a/part15-tcpip-webserver/kernel/kernel.h b/part15-tcpip-webserver/kernel/kernel.h new file mode 100644 index 0000000..38ebe29 --- /dev/null +++ b/part15-tcpip-webserver/kernel/kernel.h @@ -0,0 +1,2 @@ +void enc28j60PacketSend(unsigned short buflen, void *buffer); +void *memcpy(void *dest, const void *src, unsigned short len); diff --git a/part15-tcpip/lib/fb.c b/part15-tcpip-webserver/lib/fb.c similarity index 100% rename from part15-tcpip/lib/fb.c rename to part15-tcpip-webserver/lib/fb.c diff --git a/part15-tcpip/lib/io.c b/part15-tcpip-webserver/lib/io.c similarity index 88% rename from part15-tcpip/lib/io.c rename to part15-tcpip-webserver/lib/io.c index 34a02aa..6a7b221 100644 --- a/part15-tcpip/lib/io.c +++ b/part15-tcpip-webserver/lib/io.c @@ -197,3 +197,33 @@ void uart_byte(unsigned char b) { } uart_writeByteBlockingActual(' '); } + +// TIMER + +struct timer_regs { + volatile unsigned int control_status; + volatile unsigned int counter_lo; + volatile unsigned int counter_hi; + volatile unsigned int compare[4]; +}; + +#define REGS_TIMER ((struct timer_regs *)(PERIPHERAL_BASE + 0x00003000)) + +unsigned long HAL_GetTick(void) { + unsigned int hi = REGS_TIMER->counter_hi; + unsigned int lo = REGS_TIMER->counter_lo; + + //double check hi value didn't change after setting it... + if (hi != REGS_TIMER->counter_hi) { + hi = REGS_TIMER->counter_hi; + lo = REGS_TIMER->counter_lo; + } + + return ((unsigned long)hi << 32) | lo; +} + +void HAL_Delay(unsigned int ms) { + unsigned long start = HAL_GetTick(); + + while(HAL_GetTick() < start + (ms * 1000)); +} diff --git a/part15-tcpip/lib/mb.c b/part15-tcpip-webserver/lib/mb.c similarity index 100% rename from part15-tcpip/lib/mb.c rename to part15-tcpip-webserver/lib/mb.c diff --git a/part15-tcpip/lib/multicore.c b/part15-tcpip-webserver/lib/multicore.c similarity index 100% rename from part15-tcpip/lib/multicore.c rename to part15-tcpip-webserver/lib/multicore.c diff --git a/part15-tcpip/lib/spi.c b/part15-tcpip-webserver/lib/spi.c similarity index 100% rename from part15-tcpip/lib/spi.c rename to part15-tcpip-webserver/lib/spi.c diff --git a/part15-tcpip/net/enc28j60.c b/part15-tcpip-webserver/net/enc28j60.c similarity index 96% rename from part15-tcpip/net/enc28j60.c rename to part15-tcpip-webserver/net/enc28j60.c index 9547184..eab8a76 100644 --- a/part15-tcpip/net/enc28j60.c +++ b/part15-tcpip-webserver/net/enc28j60.c @@ -81,6 +81,7 @@ Errata 18 is implemented in lwip stack /* Includes ------------------------------------------------------------------*/ #include "enc28j60.h" #include "../include/fb.h" +#include "../include/io.h" /** @addtogroup BSP * @{ @@ -1171,6 +1172,8 @@ void ENC_Transmit(ENC_HandleTypeDef *handle) enc_waitwhilegreg(ENC_EIR, EIR_TXIF | EIR_TXERIF, 0); #endif + HAL_Delay(20); // Added by AGB - fixes weird timing bug + /* Stop transmission */ enc_bfcgreg(ENC_ECON1, ECON1_TXRTS); diff --git a/part15-tcpip/net/enc28j60.h b/part15-tcpip-webserver/net/enc28j60.h similarity index 97% rename from part15-tcpip/net/enc28j60.h rename to part15-tcpip-webserver/net/enc28j60.h index dc77865..e2c3504 100644 --- a/part15-tcpip/net/enc28j60.h +++ b/part15-tcpip-webserver/net/enc28j60.h @@ -61,11 +61,6 @@ #define MIN_FRAMELEN 64 #define MAX_FRAMELEN 1518 - -/* External functions --------------------------------------------------------*/ -void HAL_Delay(volatile uint32_t Delay); -uint32_t HAL_GetTick(void); - /* Callback functions *********************************************************/ /** diff --git a/part15-tcpip/net/encspi.c b/part15-tcpip-webserver/net/encspi.c similarity index 100% rename from part15-tcpip/net/encspi.c rename to part15-tcpip-webserver/net/encspi.c diff --git a/part15-tcpip/tcpip/ip_arp_udp_tcp.c b/part15-tcpip-webserver/tcpip/ip_arp_udp_tcp.c similarity index 91% rename from part15-tcpip/tcpip/ip_arp_udp_tcp.c rename to part15-tcpip-webserver/tcpip/ip_arp_udp_tcp.c index bce8f00..b0af8be 100644 --- a/part15-tcpip/tcpip/ip_arp_udp_tcp.c +++ b/part15-tcpip-webserver/tcpip/ip_arp_udp_tcp.c @@ -2,7 +2,7 @@ * vim:sw=8:ts=8:si:et * To use the above modeline in vim you must have "set modeline" in your .vimrc * - * Author: Guido Socher + * Author: Guido Socher * Copyright:LGPL V2 * See http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html * @@ -16,27 +16,29 @@ * large pages. * *********************************************/ - +#include "net.h" +#include "../net/enc28j60.h" +#include "../kernel/kernel.h" +#include "../include/fb.h" #include "ip_config.h" -#include "ip_arp_udp_tcp.h" // I use them to debug stuff: #define LEDOFF PORTB|=(1<>8; buf[IP_TOTLEN_L_P]=j& 0xff; @@ -597,10 +587,10 @@ void make_tcp_synack_from_syn(uint8_t *buf) // put an inital seq number buf[TCP_SEQ_H_P+0]= 0; buf[TCP_SEQ_H_P+1]= 0; - // we step only the second byte, this allows us to send packts + // we step only the second byte, this allows us to send packts // with 255 bytes, 512 or 765 (step by 3) without generating // overlapping numbers. - buf[TCP_SEQ_H_P+2]= seqnum; + buf[TCP_SEQ_H_P+2]= seqnum; buf[TCP_SEQ_H_P+3]= 0; // step the inititial seq num by something we will not use // during this tcp session: @@ -629,7 +619,7 @@ void make_tcp_synack_from_syn(uint8_t *buf) // you must have initialized info_data_len at some time before calling this function // -// This info_data_len initialisation is done automatically if you call +// This info_data_len initialisation is done automatically if you call // packetloop_icmp_tcp(buf,enc28j60PacketReceive(BUFFER_SIZE, buf)); // and test the return value for non zero. // @@ -644,28 +634,27 @@ void www_server_reply(uint8_t *buf,uint16_t dlen) // This code requires that we send only one data packet // because we keep no state information. We must therefore set // the fin here: - buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V;//|TCP_FLAGS_FIN_V; + buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V; make_tcp_ack_with_data_noflags(buf,dlen); // send data } #endif // WWW_server -#if defined (ALL_clients) +#if defined (ALL_clients) || defined (GRATARP) || defined (WOL_client) // fill buffer with a prog-mem string -void fill_buf_p(uint8_t *buf,uint16_t len, const char *progmem_s) +void fill_buf_p(uint8_t *buf,uint16_t len, const char *progmem_str_p) { - uint8_t i=0; - while (len){ - *buf= progmem_s[i]; + *buf= pgm_read_byte(progmem_str_p); buf++; - len--; i++; + progmem_str_p++; + len--; } } -#endif +#endif #ifdef PING_client -// icmp echo, matchpat is a pattern that has to be sent back by the +// icmp echo, matchpat is a pattern that has to be sent back by the // host answering the ping. // The ping is sent to destip and mac dstmac void client_icmp_request(uint8_t *buf,uint8_t *destip,uint8_t *dstmac) @@ -697,14 +686,14 @@ void client_icmp_request(uint8_t *buf,uint8_t *destip,uint8_t *dstmac) buf[ICMP_CHECKSUM_H_P]=0; buf[ICMP_CHECKSUM_L_P]=0; // a possibly unique id of this host: - buf[ICMP_IDENT_H_P]=5; // some number + buf[ICMP_IDENT_H_P]=5; // some number buf[ICMP_IDENT_L_P]=ipaddr[3]; // last byte of my IP // buf[ICMP_IDENT_L_P+1]=0; // seq number, high byte buf[ICMP_IDENT_L_P+2]=1; // seq number, low byte, we send only 1 ping at a time // copy the data: i=0; - while(i<56){ + while(i<56){ buf[ICMP_DATA_P+i]=PINGPATTERN; i++; } @@ -725,6 +714,7 @@ void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport,uint8_t *dst { uint8_t i=0; uint16_t ck; + if (!enc28j60linkup())return; // while(i<6){ buf[ETH_DST_MAC +i]=dstmac[i]; // gw mac in local lan or host mac @@ -756,7 +746,7 @@ void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport,uint8_t *dst // copy the data: i=0; // most fields are zero, here we zero everything and fill later - while(i<48){ + while(i<48){ buf[UDP_DATA_P+i]=0; i++; } @@ -772,7 +762,7 @@ void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport,uint8_t *dst // return 1 on sucessful processing of answer uint8_t client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l){ if (dstport_l){ - if (buf[UDP_DST_PORT_L_P]!=dstport_l){ + if (buf[UDP_DST_PORT_L_P]!=dstport_l){ return(0); } } @@ -787,9 +777,9 @@ uint8_t client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l) #endif #ifdef UDP_client -// -------------------- send a spontanious UDP packet to a server +// -------------------- send a spontanious UDP packet to a server // There are two ways of using this: -// 1) you call send_udp_prepare, you fill the data yourself into buf starting at buf[UDP_DATA_P], +// 1) you call send_udp_prepare, you fill the data yourself into buf starting at buf[UDP_DATA_P], // you send the packet by calling send_udp_transmit // // 2) You just allocate a large enough buffer for you data and you call send_udp and nothing else @@ -820,9 +810,9 @@ void send_udp_prepare(uint8_t *buf,uint16_t sport, const uint8_t *dip, uint16_t } // done in transmit: fill_ip_hdr_checksum(buf); buf[UDP_DST_PORT_H_P]=(dport>>8); - buf[UDP_DST_PORT_L_P]=0xff&dport; + buf[UDP_DST_PORT_L_P]=0xff&dport; buf[UDP_SRC_PORT_H_P]=(sport>>8); - buf[UDP_SRC_PORT_L_P]=sport&0xff; + buf[UDP_SRC_PORT_L_P]=sport&0xff; buf[UDP_LEN_H_P]=0; // done in transmit: buf[UDP_LEN_L_P]=UDP_HEADER_LEN+datalen; // zero the checksum @@ -912,7 +902,7 @@ void send_wol(uint8_t *buf,uint8_t *wolmac) buf[UDP_CHECKSUM_L_P]=0; // copy the data (102 bytes): i=0; - while(i<6){ + while(i<6){ buf[UDP_DATA_P+i]=0xff; i++; } @@ -920,7 +910,7 @@ void send_wol(uint8_t *buf,uint8_t *wolmac) pos=UDP_DATA_P+6; while (m<16){ i=0; - while(i<6){ + while(i<6){ buf[pos]=wolmac[i]; i++; pos++; @@ -960,7 +950,7 @@ uint8_t gratutious_arp(uint8_t *buf) buf[ETH_TYPE_L_P] = ETHTYPE_ARP_L_V; // arp request and reply are the same execept for // the opcode: - fill_buf_p(&buf[ETH_ARP_P],8,arpreqhdr); + fill_buf_p(&buf[ETH_ARP_P],8,arpreqhdr); //buf[ETH_ARP_OPCODE_L_P]=ETH_ARP_OPCODE_REPLY_L_V; // reply i=0; while(i<6){ @@ -982,8 +972,8 @@ uint8_t gratutious_arp(uint8_t *buf) #if ARP_MAC_resolver_client // make a arp request -// Note: you must have initialized the stack with -// init_udp_or_www_server or client_ifconfig +// Note: you must have initialized the stack with +// init_udp_or_www_server or client_ifconfig // before you can use this function void client_arp_whohas(uint8_t *buf,uint8_t *ip_we_search) { @@ -1025,8 +1015,8 @@ uint8_t get_mac_with_arp_wait(void) // reference_number is something that is just returned in the callback // to make matching and waiting for a given ip/mac address pair easier -// Note: you must have initialized the stack with -// init_udp_or_www_server or client_ifconfig +// Note: you must have initialized the stack with +// init_udp_or_www_server or client_ifconfig // before you can use this function void get_mac_with_arp(uint8_t *ip, uint8_t reference_number,void (*arp_result_callback)(uint8_t *ip,uint8_t reference_number,uint8_t *mac)) { @@ -1039,7 +1029,7 @@ void get_mac_with_arp(uint8_t *ip, uint8_t reference_number,void (*arp_result_ca i++; } } -#endif +#endif #if defined (TCP_client) // Make a tcp syn packet @@ -1076,12 +1066,12 @@ void tcp_client_syn(uint8_t *buf,uint8_t srcport,uint16_t dstport) buf[TCP_SEQ_H_P+i]=0; i++; } - // -- header ready + // -- header ready // put inital seq number - // we step only the second byte, this allows us to send packts + // we step only the second byte, this allows us to send packts // with 255 bytes 512 (if we step the initial seqnum by 2) // or 765 (step by 3) - buf[TCP_SEQ_H_P+2]= seqnum; + buf[TCP_SEQ_H_P+2]= seqnum; // step the inititial seq num by something we will not use // during this tcp session: seqnum+=3; @@ -1110,7 +1100,7 @@ void tcp_client_syn(uint8_t *buf,uint8_t srcport,uint16_t dstport) } #endif // TCP_client -#if defined (TCP_client) +#if defined (TCP_client) // This is how to use the tcp client: // // Declare a callback function to get the result (tcp data from the server): @@ -1126,16 +1116,16 @@ void tcp_client_syn(uint8_t *buf,uint8_t srcport,uint16_t dstport) // output such that this will be the only packet. // // close_tcp_session=1 means close the session now. close_tcp_session=0 -// read all data and leave it to the other side to close it. +// read all data and leave it to the other side to close it. // If you connect to a web server then you want close_tcp_session=0. // If you connect to a modbus/tcp equipment then you want close_tcp_session=1 // -// Declare a callback function to be called in order to fill in the +// Declare a callback function to be called in order to fill in the // // request (tcp data sent to the server): // uint16_t your_client_tcp_datafill_callback(uint8_t fd){...your code;return(len_of_data_filled_in);} // -// Now call: +// Now call: // fd=client_tcp_req(&your_client_tcp_result_callback,&your_client_tcp_datafill_callback,portnumber); // // fd is a file descriptor like number that you get back in the fill and result @@ -1171,7 +1161,7 @@ uint8_t client_tcp_req(uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode, } #endif // TCP_client -#if defined (WWW_client) +#if defined (WWW_client) uint16_t www_client_internal_datafill_callback(uint8_t fd){ char strbuf[5]; uint16_t len=0; @@ -1179,27 +1169,27 @@ uint16_t www_client_internal_datafill_callback(uint8_t fd){ if (browsertype==0){ // GET len=fill_tcp_data_p(bufptr,0,PSTR("GET ")); - len=fill_tcp_data_p(bufptr,len,client_urlbuf); + len=fill_tcp_data_p(bufptr,len,client_urlbuf_p); len=fill_tcp_data(bufptr,len,client_urlbuf_var); // I would prefer http/1.0 but there is a funny // bug in some apache webservers which causes // them to send two packets (fragmented PDU) // if we don't use HTTP/1.1 + Connection: close len=fill_tcp_data_p(bufptr,len,PSTR(" HTTP/1.1\r\nHost: ")); - len=fill_tcp_data(bufptr,len,client_hoststr); - len=fill_tcp_data_p(bufptr,len,PSTR("\r\nUser-Agent: tgr/1.1\r\nAccept: text/html\r\nConnection: close\r\n\r\n")); + len=fill_tcp_data_p(bufptr,len,client_hoststr); + len=fill_tcp_data_p(bufptr,len,PSTR("\r\nUser-Agent: tgr/1.1\r\nAccept: text/html\r\n\r\n")); }else{ // POST len=fill_tcp_data_p(bufptr,0,PSTR("POST ")); - len=fill_tcp_data_p(bufptr,len,client_urlbuf); + len=fill_tcp_data_p(bufptr,len,client_urlbuf_p); len=fill_tcp_data(bufptr,len,client_urlbuf_var); len=fill_tcp_data_p(bufptr,len,PSTR(" HTTP/1.1\r\nHost: ")); - len=fill_tcp_data(bufptr,len,client_hoststr); - if (client_additionalheaderline){ + len=fill_tcp_data_p(bufptr,len,client_hoststr); + if (client_additionalheaderline_p){ len=fill_tcp_data_p(bufptr,len,PSTR("\r\n")); - len=fill_tcp_data_p(bufptr,len,client_additionalheaderline); + len=fill_tcp_data_p(bufptr,len,client_additionalheaderline_p); } - len=fill_tcp_data_p(bufptr,len,PSTR("\r\nUser-Agent: tgr/1.1\r\nAccept: */*\r\nConnection: close\r\n")); + len=fill_tcp_data_p(bufptr,len,PSTR("\r\nUser-Agent: tgr/1.1\r\nAccept: */*\r\n")); len=fill_tcp_data_p(bufptr,len,PSTR("Content-Length: ")); itoa(strlen(client_postval),strbuf,10); len=fill_tcp_data(bufptr,len,strbuf); @@ -1213,7 +1203,7 @@ uint16_t www_client_internal_datafill_callback(uint8_t fd){ uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data){ uint16_t web_statuscode=0; // tcp status is OK but we need to check http layer too - uint8_t i=0; + uint8_t i=0; if (fd!=www_fd){ (*client_browser_callback)(500,0,0); return(0); @@ -1222,7 +1212,7 @@ uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint // we might have a http status code // http status codes are 3digit numbers as ascii text. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html // The buffer would look like this: HTTP/1.1 200 OK\r\n - // web_statuscode=0 means we got a corrupted answer + // web_statuscode=0 means we got a corrupted answer if (client_browser_callback){ if (isblank(bufptr[datapos+8]) && isdigit(bufptr[datapos+9])&& isdigit(bufptr[datapos+11])){ // e.g 200 OK, a status code has 3 digits from datapos+9 to datapos+11, copy over the web/http status code to web_statuscode: while(i<2){ @@ -1260,10 +1250,10 @@ uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint // The string buffers to which urlbuf_varpart and hoststr are pointing // must not be changed until the callback is executed. // -void client_browse_url(const prog_char *urlbuf,const char *urlbuf_varpart,const char *hoststr,void (*callback)(uint16_t,uint16_t,uint16_t),uint8_t *dstip,uint8_t *dstmac) +void client_browse_url(const char *urlbuf_p,char *urlbuf_varpart,const char *hoststr,void (*callback)(uint16_t,uint16_t,uint16_t),uint8_t *dstip,uint8_t *dstmac) { if (!enc28j60linkup())return; - client_urlbuf=urlbuf; + client_urlbuf_p=urlbuf_p; client_urlbuf_var=urlbuf_varpart; client_hoststr=hoststr; browsertype=0; @@ -1272,19 +1262,19 @@ void client_browse_url(const prog_char *urlbuf,const char *urlbuf_varpart,const } // client web browser using http POST operation: -// additionalheaderline must be set to NULL if not used. +// additionalheaderline_p must be set to NULL if not used. // The string buffers to which urlbuf_varpart and hoststr are pointing // must not be changed until the callback is executed. -// postval is a string buffer which can only be de-allocated by the caller +// postval is a string buffer which can only be de-allocated by the caller // when the post operation was really done (e.g when callback was executed). // postval must be urlencoded. -void client_http_post(const prog_char *urlbuf, const char *urlbuf_varpart,const char *hoststr, const prog_char *additionalheaderline,char *postval,void (*callback)(uint16_t,uint16_t,uint16_t),uint8_t *dstip,uint8_t *dstmac) +void client_http_post(const char *urlbuf_p, char *urlbuf_varpart,const char *hoststr, const char *additionalheaderline_p,char *postval,void (*callback)(uint16_t,uint16_t,uint16_t),uint8_t *dstip,uint8_t *dstmac) { if (!enc28j60linkup())return; - client_urlbuf=urlbuf; + client_urlbuf_p=urlbuf_p; client_hoststr=hoststr; client_urlbuf_var=urlbuf_varpart; - client_additionalheaderline=additionalheaderline; + client_additionalheaderline_p=additionalheaderline_p; client_postval=postval; browsertype=1; client_browser_callback=callback; @@ -1314,18 +1304,18 @@ uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost) #endif // PING_client -// return 0 to just continue in the packet loop and return the position +// return 0 to just continue in the packet loop and return the position // of the tcp data if there is tcp data part uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen) { -// uint16_t len; + uint16_t len; #if defined (TCP_client) uint8_t send_fin=0; uint16_t tcpstart; uint16_t save_len; #endif #ifdef ARP_MAC_resolver_client - //plen will be unequal to zero if there is a valid + //plen will be unequal to zero if there is a valid // packet (without crc error): if(plen==0){ if (arpip_state == (WGW_ACCEPT_ARP_REPLY|WGW_INITIAL_ARP) && arp_delaycnt==0 ){ @@ -1352,17 +1342,17 @@ uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen) } #endif // ARP_MAC_resolver_client // arp is broadcast if unknown but a host may also - // verify the mac address by sending it to + // verify the mac address by sending it to // a unicast address. if(eth_type_is_arp_and_my_ip(buf,plen)){ if (buf[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REQ_L_V){ - // is it an arp request + // is it an arp request make_arp_answer_from_request(buf); } #ifdef ARP_MAC_resolver_client if ((arpip_state & WGW_ACCEPT_ARP_REPLY) && (buf[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REPLY_L_V)){ - // is it an arp reply - if (memcmp(&buf[ETH_ARP_SRC_IP_P],arpip,4)!=0) return(0); // not an arp reply for the IP we were searching + // is it an arp reply + if (memcmp(&buf[ETH_ARP_SRC_IP_P],arpip,4)!=0) return(0); // not an arp reply for the IP we were searching (*client_arp_result_callback)(arpip,arp_reference_number,buf+ETH_ARP_SRC_MAC_P); arpip_state=WGW_HAVE_MAC; } @@ -1379,13 +1369,13 @@ uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen) (*icmp_callback)(&(buf[IP_SRC_P])); } // a ping packet, let's send pong - debugstr("Sending ping reply..."); - debugcrlf(); + debugstr("Replying to ping..."); debugcrlf(); make_echo_reply_from_request(buf,plen); return(0); } - if (plen<54 && buf[IP_PROTO_P]!=IP_PROTO_TCP_V ){ - // smaller than the smallest TCP packet and not tcp port + // this is an important check to avoid working on the wrong packets: + if (plen<54 || buf[IP_PROTO_P]!=IP_PROTO_TCP_V ){ + // smaller than the smallest TCP packet (TCP packet with no options section) or not tcp port return(0); } #if defined (TCP_client) @@ -1393,7 +1383,7 @@ uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen) if ( buf[TCP_DST_PORT_H_P]==TCPCLIENT_SRC_PORT_H){ #if defined (WWW_client) // workaround to pass pointer to www_client_internal.. - bufptr=buf; + bufptr=buf; #endif // WWW_client if (check_ip_message_is_from(buf,tcp_otherside_ip)==0){ return(0); @@ -1404,7 +1394,7 @@ uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen) // parameters in client_tcp_result_callback: fd, status, buf_start, len (*client_tcp_result_callback)((buf[TCP_DST_PORT_L_P]>>5)&0x7,3,0,0); } - tcp_client_state=5; + tcp_client_state=6; return(0); } len=get_tcp_data_len(buf); @@ -1418,7 +1408,7 @@ uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen) // still have a valid tcp-ack in the buffer. In other words // you have just called make_tcp_ack_from_any(buf,0). if (client_tcp_datafill_callback){ - // in this case it is src port because the above + // in this case it is src port because the above // make_tcp_ack_from_any swaps the dst and src port: len=(*client_tcp_datafill_callback)((buf[TCP_SRC_PORT_L_P]>>5)&0x7); }else{ @@ -1447,7 +1437,7 @@ uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen) } // in tcp_client_state==3 we will normally first get an empty // ack-packet and then a ack-packet with data. - if (tcp_client_state==3 && len>0){ + if (tcp_client_state==3 && len>0){ // our first real data packet tcp_client_state=4; // return the data we received @@ -1470,11 +1460,25 @@ uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen) } } if(tcp_client_state==5){ - // no more ack + // we get one more final ack to our fin-ack: + if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V){ + tcp_client_state=6; // in state 6 communication should be finished + } + return(0); + } + if(tcp_client_state==6){ + // something wrong, can't deal with this, reset the connection + len++; + if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V) len=0; // if packet was an ack then do not step the ack number + make_tcp_ack_from_any(buf,len,TCP_FLAGS_RST_V); + // just a single reset, do not repeat if more messages: + tcp_client_state=7; return(0); } if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V){ - make_tcp_ack_from_any(buf,len+1,TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V); + // this normally a fin ack message but it could be + // any message with fin we answer with fin-ack: + make_tcp_ack_from_any(buf,len+1,TCP_FLAGS_FIN_V); tcp_client_state=5; // connection terminated return(0); } @@ -1490,7 +1494,7 @@ uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen) // #ifdef WWW_server // tcp port web server start - if (buf[IP_PROTO_P]==IP_PROTO_TCP_V && buf[TCP_DST_PORT_H_P]==wwwport_h && buf[TCP_DST_PORT_L_P]==wwwport_l){ + if (buf[TCP_DST_PORT_H_P]==wwwport_h && buf[TCP_DST_PORT_L_P]==wwwport_l){ if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V){ make_tcp_synack_from_syn(buf); // make_tcp_synack_from_syn does already send the syn,ack diff --git a/part15-tcpip/tcpip/ip_arp_udp_tcp.h b/part15-tcpip-webserver/tcpip/ip_arp_udp_tcp.h similarity index 99% rename from part15-tcpip/tcpip/ip_arp_udp_tcp.h rename to part15-tcpip-webserver/tcpip/ip_arp_udp_tcp.h index 7c20b4f..e4b2e57 100644 --- a/part15-tcpip/tcpip/ip_arp_udp_tcp.h +++ b/part15-tcpip-webserver/tcpip/ip_arp_udp_tcp.h @@ -12,10 +12,6 @@ #ifndef IP_ARP_UDP_TCP_H #define IP_ARP_UDP_TCP_H 1 -#include "net.h" -#include "../net/enc28j60.h" -#include "../kernel/kernel.h" -#include "../include/fb.h" #include "ip_config.h" // set my own mac address: diff --git a/part15-tcpip/tcpip/ip_config.h b/part15-tcpip-webserver/tcpip/ip_config.h similarity index 98% rename from part15-tcpip/tcpip/ip_config.h rename to part15-tcpip-webserver/tcpip/ip_config.h index 22c108b..b043ee9 100644 --- a/part15-tcpip/tcpip/ip_config.h +++ b/part15-tcpip-webserver/tcpip/ip_config.h @@ -22,7 +22,7 @@ // a server answering to UDP messages #define UDP_server // a web server -#undef WWW_server +#define WWW_server // to send out a ping: #undef PING_client diff --git a/part15-tcpip/tcpip/net.h b/part15-tcpip-webserver/tcpip/net.h similarity index 100% rename from part15-tcpip/tcpip/net.h rename to part15-tcpip-webserver/tcpip/net.h diff --git a/part15-tcpip/README.md b/part15-tcpip/README.md deleted file mode 100644 index f6065ff..0000000 --- a/part15-tcpip/README.md +++ /dev/null @@ -1,63 +0,0 @@ -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 Github repository](https://github.com/ussserrr/maglev-ti-rtos) 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_. Finally, _kernel/kernel.c_ now calls a function called `net_test()` instead of our original `arp_test()`. - -The changes to _arp.c_ ----------------------- -We 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, the major change is the new `net_test()` function: - -```c -void net_test(void) -{ - while (1) { - while (!ENC_GetReceivedFrame(&handle)); - - uint16_t len = handle.RxFrameInfos.length; - uint8_t *buffer = (uint8_t *)handle.RxFrameInfos.buffer; - packetloop_arp_icmp_tcp(buffer, len); - } -} -``` - -This is an infinite loop which waits for an incoming packet and then simply passes it to Guido's `packetloop_arp_icmp_tcp()` function. This function implements some useful things, like responding to pings. I modified the routine to print a message to the screen when it sends a "pong" (look from line 1381 of _tcpip/ip_arp_udp_tcp.c_), so we can see when it's in action! - -_Imagine my excitement when I built, ran and could ping my RPi4 at 192.168.0.66 and get a response to both my laptop and my iPhone!_ - -I recommend reading [this page](http://tuxgraphics.org/electronics/200905/embedded-tcp-ip-stack.shtml) to give you some ideas about what else you might achieve with Guido's library... - -![Pinging from my iPhone](images/15-tcpip-pinging.jpg) diff --git a/part15-tcpip/kernel/irq.c b/part15-tcpip/kernel/irq.c deleted file mode 100644 index 3ab82d1..0000000 --- a/part15-tcpip/kernel/irq.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "kernel.h" - -void enable_interrupt_controller() { - REGS_IRQ->irq0_enable_0 = SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3; -} - -void disable_interrupt_controller() { - REGS_IRQ->irq0_enable_0 = 0; -} - -void handle_irq() { - unsigned int irq = REGS_IRQ->irq0_pending_0; - - while(irq) { - if (irq & SYS_TIMER_IRQ_1) { - irq &= ~SYS_TIMER_IRQ_1; - - handle_timer_1(); - } - - if (irq & SYS_TIMER_IRQ_3) { - irq &= ~SYS_TIMER_IRQ_3; - - handle_timer_3(); - } - } -} diff --git a/part15-tcpip/kernel/irqentry.S b/part15-tcpip/kernel/irqentry.S deleted file mode 100644 index d24ae00..0000000 --- a/part15-tcpip/kernel/irqentry.S +++ /dev/null @@ -1,160 +0,0 @@ -#define SYNC_INVALID_EL1t 0 -#define IRQ_INVALID_EL1t 1 -#define FIQ_INVALID_EL1t 2 -#define ERROR_INVALID_EL1t 3 - -#define SYNC_INVALID_EL1h 4 -#define IRQ_INVALID_EL1h 5 -#define FIQ_INVALID_EL1h 6 -#define ERROR_INVALID_EL1h 7 - -#define SYNC_INVALID_EL0_64 8 -#define IRQ_INVALID_EL0_64 9 -#define FIQ_INVALID_EL0_64 10 -#define ERROR_INVALID_EL0_64 11 - -#define SYNC_INVALID_EL0_32 12 -#define IRQ_INVALID_EL0_32 13 -#define FIQ_INVALID_EL0_32 14 -#define ERROR_INVALID_EL0_32 15 - -//stack frame size -#define S_FRAME_SIZE 256 - -.macro kernel_entry - sub sp, sp, #S_FRAME_SIZE - stp x0, x1, [sp, #16 * 0] - stp x2, x3, [sp, #16 * 1] - stp x4, x5, [sp, #16 * 2] - stp x6, x7, [sp, #16 * 3] - stp x8, x9, [sp, #16 * 4] - stp x10, x11, [sp, #16 * 5] - stp x12, x13, [sp, #16 * 6] - stp x14, x15, [sp, #16 * 7] - stp x16, x17, [sp, #16 * 8] - stp x18, x19, [sp, #16 * 9] - stp x20, x21, [sp, #16 * 10] - stp x22, x23, [sp, #16 * 11] - stp x24, x25, [sp, #16 * 12] - stp x26, x27, [sp, #16 * 13] - stp x28, x29, [sp, #16 * 14] - str x30, [sp, #16 * 15] -.endm - -.macro kernel_exit - ldp x0, x1, [sp, #16 * 0] - ldp x2, x3, [sp, #16 * 1] - ldp x4, x5, [sp, #16 * 2] - ldp x6, x7, [sp, #16 * 3] - ldp x8, x9, [sp, #16 * 4] - ldp x10, x11, [sp, #16 * 5] - ldp x12, x13, [sp, #16 * 6] - ldp x14, x15, [sp, #16 * 7] - ldp x16, x17, [sp, #16 * 8] - ldp x18, x19, [sp, #16 * 9] - ldp x20, x21, [sp, #16 * 10] - ldp x22, x23, [sp, #16 * 11] - ldp x24, x25, [sp, #16 * 12] - ldp x26, x27, [sp, #16 * 13] - ldp x28, x29, [sp, #16 * 14] - ldr x30, [sp, #16 * 15] - add sp, sp, #S_FRAME_SIZE - eret -.endm - -.macro handle_invalid_entry type - kernel_entry - mov x0, #\type - mrs x1, esr_el1 - mrs x2, elr_el1 - - // We could pass this to a function to print an error here - // e.g. bl show_invalid_entry_message - // - // For now we'll just hang - - b err_hang -.endm - -.macro ventry label -.align 7 - b \label -.endm - -//Exception vectors table -.align 11 -.globl vectors -vectors: - ventry sync_invalid_el1t // Synchronous EL1t - ventry irq_invalid_el1t // IRQ EL1t - ventry fiq_invalid_el1t // FIQ EL1t - ventry error_invalid_el1t // Error EL1t - - ventry sync_invalid_el1h // Synchronous EL1h - ventry handle_el1_irq // IRQ EL1h - ventry fiq_invalid_el1h // FIQ EL1h - ventry error_invalid_el1h // Error EL1h - - ventry sync_invalid_el0_64 // Synchronous 64-bit EL0 - ventry irq_invalid_el0_64 // IRQ 64-bit EL0 - ventry fiq_invalid_el0_64 // FIQ 64-bit EL0 - ventry error_invalid_el0_64 // Error 64-bit EL0 - - ventry sync_invalid_el0_32 // Synchronous 32-bit EL0 - ventry irq_invalid_el0_32 // IRQ 32-bit EL0 - ventry fiq_invalid_el0_32 // FIQ 32-bit EL0 - ventry error_invalid_el0_32 // Error 32-bit EL0 - - -sync_invalid_el1t: - handle_invalid_entry SYNC_INVALID_EL1t - -irq_invalid_el1t: - handle_invalid_entry IRQ_INVALID_EL1t - -fiq_invalid_el1t: - handle_invalid_entry FIQ_INVALID_EL1t - -error_invalid_el1t: - handle_invalid_entry ERROR_INVALID_EL1t - -sync_invalid_el1h: - handle_invalid_entry SYNC_INVALID_EL1h - -fiq_invalid_el1h: - handle_invalid_entry FIQ_INVALID_EL1h - -error_invalid_el1h: - handle_invalid_entry ERROR_INVALID_EL1h - -sync_invalid_el0_64: - handle_invalid_entry SYNC_INVALID_EL0_64 - -irq_invalid_el0_64: - handle_invalid_entry IRQ_INVALID_EL0_64 - -fiq_invalid_el0_64: - handle_invalid_entry FIQ_INVALID_EL0_64 - -error_invalid_el0_64: - handle_invalid_entry ERROR_INVALID_EL0_64 - -sync_invalid_el0_32: - handle_invalid_entry SYNC_INVALID_EL0_32 - -irq_invalid_el0_32: - handle_invalid_entry IRQ_INVALID_EL0_32 - -fiq_invalid_el0_32: - handle_invalid_entry FIQ_INVALID_EL0_32 - -error_invalid_el0_32: - handle_invalid_entry ERROR_INVALID_EL0_32 - -handle_el1_irq: - kernel_entry - bl handle_irq - kernel_exit - -.globl err_hang -err_hang: b err_hang diff --git a/part15-tcpip/kernel/kernel.c b/part15-tcpip/kernel/kernel.c deleted file mode 100644 index 24e4ce9..0000000 --- a/part15-tcpip/kernel/kernel.c +++ /dev/null @@ -1,167 +0,0 @@ -#include "../include/fb.h" -#include "../include/io.h" -#include "../include/spi.h" -#include "../include/multicore.h" -#include "kernel.h" - -void initProgress(void) -{ - drawRect(0, 0, 301, 50, 0x0f, 0); - drawString(309, 21, "Core 0", 0x0f, 1); - - drawRect(0, 60, 301, 110, 0x0f, 0); - drawString(309, 81, "Core 1", 0x0f, 1); - - drawRect(0, 120, 301, 170, 0x0f, 0); - drawString(309, 141, "Timer 1", 0x0f, 1); - - drawRect(0, 180, 301, 230, 0x0f, 0); - drawString(309, 201, "Timer 3", 0x0f, 1); -} - -void drawProgress(unsigned int core, unsigned int val) { - unsigned char col = (core + 1) + ((core + 1) << 4); - - // val should be 0-100 - if (val == 0) drawRect(1, (60 * core) + 1, 300, (60 * core) + 49, 0x00, 1); - if (val > 0) drawRect(1, (60 * core) + 1, (val * 3), (60 * core) + 49, col, 1); -} - -void core3_main(void) { - clear_core3(); // Only run once - - // Test the network card - - spi_init(); - init_network(); - net_test(); - - while(1); -} - -void core0_main(void) -{ - unsigned int core0_val = 0; - - while (core0_val <= 100) { - wait_msec(0x100000); - drawProgress(0, core0_val); - core0_val++; - } - - debugstr("Core 0 done."); - debugcrlf(); -} - -void core1_main(void) -{ - unsigned int core1_val = 0; - - clear_core1(); // Only run once - - while (core1_val <= 100) { - wait_msec(0x3FFFF); - drawProgress(1, core1_val); - core1_val++; - } - - debugstr("Core 1 done."); - debugcrlf(); - - while(1); -} - -// TIMER FUNCTIONS - -const unsigned int timer1_int = CLOCKHZ; -const unsigned int timer3_int = CLOCKHZ / 4; -unsigned int timer1_val = 0; -unsigned int timer3_val = 0; - -void timer_init() { - timer1_val = REGS_TIMER->counter_lo; - timer1_val += timer1_int; - REGS_TIMER->compare[1] = timer1_val; - - timer3_val = REGS_TIMER->counter_lo; - timer3_val += timer3_int; - REGS_TIMER->compare[3] = timer3_val; -} - -void handle_timer_1() { - timer1_val += timer1_int; - REGS_TIMER->compare[1] = timer1_val; - REGS_TIMER->control_status |= SYS_TIMER_IRQ_1; - - unsigned int progval = timer1_val / timer1_int; - if (progval <= 100) { - drawProgress(2, progval); - } else { - debugstr("Timer 1 done."); - debugcrlf(); - } -} - -void handle_timer_3() { - timer3_val += timer3_int; - REGS_TIMER->compare[3] = timer3_val; - REGS_TIMER->control_status |= SYS_TIMER_IRQ_3; - - unsigned int progval = timer3_val / timer3_int; - if (progval <= 100) drawProgress(3, progval); -} - -unsigned long HAL_GetTick(void) { - unsigned int hi = REGS_TIMER->counter_hi; - unsigned int lo = REGS_TIMER->counter_lo; - - //double check hi value didn't change after setting it... - if (hi != REGS_TIMER->counter_hi) { - hi = REGS_TIMER->counter_hi; - lo = REGS_TIMER->counter_lo; - } - - return ((unsigned long)hi << 32) | lo; -} - -void HAL_Delay(unsigned int ms) { - unsigned long start = HAL_GetTick(); - - while(HAL_GetTick() < start + (ms * 1000)); -} - -void main(void) -{ - fb_init(); - - unsigned int i=0; - while (i++<30) debugcrlf(); - - initProgress(); - - // Kick it off on core 1 - - start_core1(core1_main); - - // Kick off the timers - - irq_init_vectors(); - enable_interrupt_controller(); - irq_enable(); - timer_init(); - - // Kick it off on core 3 - - start_core3(core3_main); - - // Kick it off on core 0 - - core0_main(); - - // Disable IRQs and loop endlessly - - irq_disable(); - disable_interrupt_controller(); - - while(1); -} diff --git a/part15-tcpip/kernel/kernel.h b/part15-tcpip/kernel/kernel.h deleted file mode 100644 index b2dcb44..0000000 --- a/part15-tcpip/kernel/kernel.h +++ /dev/null @@ -1,51 +0,0 @@ -#define PERIPHERAL_BASE 0xFE000000 -#define CLOCKHZ 1000000 - -struct timer_regs { - volatile unsigned int control_status; - volatile unsigned int counter_lo; - volatile unsigned int counter_hi; - volatile unsigned int compare[4]; -}; - -#define REGS_TIMER ((struct timer_regs *)(PERIPHERAL_BASE + 0x00003000)) - -struct arm_irq_regs_2711 { - volatile unsigned int irq0_pending_0; - volatile unsigned int irq0_pending_1; - volatile unsigned int irq0_pending_2; - volatile unsigned int res0; - volatile unsigned int irq0_enable_0; - volatile unsigned int irq0_enable_1; - volatile unsigned int irq0_enable_2; - volatile unsigned int res1; - volatile unsigned int irq0_disable_0; - volatile unsigned int irq0_disable_1; - volatile unsigned int irq0_disable_2; -}; - -typedef struct arm_irq_regs_2711 arm_irq_regs; - -#define REGS_IRQ ((arm_irq_regs *)(PERIPHERAL_BASE + 0x0000B200)) - -enum vc_irqs { - SYS_TIMER_IRQ_0 = 1, - SYS_TIMER_IRQ_1 = 2, - SYS_TIMER_IRQ_2 = 4, - SYS_TIMER_IRQ_3 = 8, - AUX_IRQ = (1 << 29) -}; - -void irq_init_vectors(); -void irq_enable(); -void irq_disable(); -void enable_interrupt_controller(); -void disable_interrupt_controller(); - -void handle_timer_1(); -void handle_timer_3(); - -void init_network(void); -void net_test(void); -void enc28j60PacketSend(unsigned short buflen, void *buffer); -void *memcpy(void *dest, const void *src, unsigned short len); diff --git a/part15-tcpip/kernel/utils.S b/part15-tcpip/kernel/utils.S deleted file mode 100644 index 6027c0d..0000000 --- a/part15-tcpip/kernel/utils.S +++ /dev/null @@ -1,15 +0,0 @@ -.globl irq_init_vectors -irq_init_vectors: - adr x0, vectors - msr vbar_el1, x0 - ret - -.globl irq_enable -irq_enable: - msr daifclr, #2 - ret - -.globl irq_disable -irq_disable: - msr daifset, #2 - ret