mirror of
https://github.com/isometimes/rpi4-osdev
synced 2024-11-09 20:00:40 +00:00
Upgraded part15-tcpip to include code for serving web pages
This commit is contained in:
parent
f5b10bb1de
commit
76be43e53a
35 changed files with 332 additions and 636 deletions
BIN
part15-tcpip-webserver/.DS_Store
vendored
Normal file
BIN
part15-tcpip-webserver/.DS_Store
vendored
Normal file
Binary file not shown.
94
part15-tcpip-webserver/README.md
Normal file
94
part15-tcpip-webserver/README.md
Normal file
|
@ -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<h1>ERROR</h1>");
|
||||||
|
} 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<h1>Hello world!</h1>");
|
||||||
|
} 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<h1>Goodbye cruel world.</h1>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
BIN
part15-tcpip-webserver/images/15-tcpip-webserver-browser.png
Normal file
BIN
part15-tcpip-webserver/images/15-tcpip-webserver-browser.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 519 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
@ -17,3 +17,5 @@ void gpio_setPinOutputBool(unsigned int pin_number, unsigned int onOrOff);
|
||||||
void gpio_initOutputPinWithPullNone(unsigned int pin_number);
|
void gpio_initOutputPinWithPullNone(unsigned int pin_number);
|
||||||
void uart_hex(unsigned int d);
|
void uart_hex(unsigned int d);
|
||||||
void uart_byte(unsigned char b);
|
void uart_byte(unsigned char b);
|
||||||
|
unsigned long HAL_GetTick(void);
|
||||||
|
void HAL_Delay(unsigned int ms);
|
|
@ -1,17 +1,8 @@
|
||||||
#include "../net/enc28j60.h"
|
|
||||||
#include "../include/fb.h"
|
#include "../include/fb.h"
|
||||||
|
#include "../include/spi.h"
|
||||||
|
#include "../net/enc28j60.h"
|
||||||
#include "../tcpip/ip_arp_udp_tcp.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
|
// HELPER FUNCTIONS
|
||||||
|
|
||||||
void *memset(void *dest, unsigned char val, unsigned short len)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void enc28j60PacketSend(unsigned short buflen, void *buffer) {
|
int strncmp(const char *s1, const char *s2, unsigned short n)
|
||||||
if (ENC_RestoreTXBuffer(&handle, buflen) == 0) {
|
|
||||||
ENC_WriteBuffer((unsigned char *) buffer, buflen);
|
|
||||||
handle.transmitLength = buflen;
|
|
||||||
ENC_Transmit(&handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MAIN FUNCTIONS
|
|
||||||
|
|
||||||
void net_test(void)
|
|
||||||
{
|
{
|
||||||
while (1) {
|
unsigned char u1, u2;
|
||||||
while (!ENC_GetReceivedFrame(&handle));
|
|
||||||
|
|
||||||
uint16_t len = handle.RxFrameInfos.length;
|
while (n-- > 0)
|
||||||
uint8_t *buffer = (uint8_t *)handle.RxFrameInfos.buffer;
|
{
|
||||||
packetloop_arp_icmp_tcp(buffer, len);
|
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)
|
void init_network(void)
|
||||||
{
|
{
|
||||||
handle.Init.DuplexMode = ETH_MODE_HALFDUPLEX;
|
handle.Init.DuplexMode = ETH_MODE_HALFDUPLEX;
|
||||||
|
@ -100,3 +95,61 @@ void init_network(void)
|
||||||
debugstr("done.");
|
debugstr("done.");
|
||||||
debugcrlf();
|
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<h1>ERROR</h1>");
|
||||||
|
} 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<h1>Hello world!</h1>");
|
||||||
|
} 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<h1>Goodbye cruel world.</h1>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
2
part15-tcpip-webserver/kernel/kernel.h
Normal file
2
part15-tcpip-webserver/kernel/kernel.h
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
void enc28j60PacketSend(unsigned short buflen, void *buffer);
|
||||||
|
void *memcpy(void *dest, const void *src, unsigned short len);
|
|
@ -197,3 +197,33 @@ void uart_byte(unsigned char b) {
|
||||||
}
|
}
|
||||||
uart_writeByteBlockingActual(' ');
|
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));
|
||||||
|
}
|
|
@ -81,6 +81,7 @@ Errata 18 is implemented in lwip stack
|
||||||
/* Includes ------------------------------------------------------------------*/
|
/* Includes ------------------------------------------------------------------*/
|
||||||
#include "enc28j60.h"
|
#include "enc28j60.h"
|
||||||
#include "../include/fb.h"
|
#include "../include/fb.h"
|
||||||
|
#include "../include/io.h"
|
||||||
|
|
||||||
/** @addtogroup BSP
|
/** @addtogroup BSP
|
||||||
* @{
|
* @{
|
||||||
|
@ -1171,6 +1172,8 @@ void ENC_Transmit(ENC_HandleTypeDef *handle)
|
||||||
enc_waitwhilegreg(ENC_EIR, EIR_TXIF | EIR_TXERIF, 0);
|
enc_waitwhilegreg(ENC_EIR, EIR_TXIF | EIR_TXERIF, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
HAL_Delay(20); // Added by AGB - fixes weird timing bug
|
||||||
|
|
||||||
/* Stop transmission */
|
/* Stop transmission */
|
||||||
enc_bfcgreg(ENC_ECON1, ECON1_TXRTS);
|
enc_bfcgreg(ENC_ECON1, ECON1_TXRTS);
|
||||||
|
|
|
@ -61,11 +61,6 @@
|
||||||
#define MIN_FRAMELEN 64
|
#define MIN_FRAMELEN 64
|
||||||
#define MAX_FRAMELEN 1518
|
#define MAX_FRAMELEN 1518
|
||||||
|
|
||||||
|
|
||||||
/* External functions --------------------------------------------------------*/
|
|
||||||
void HAL_Delay(volatile uint32_t Delay);
|
|
||||||
uint32_t HAL_GetTick(void);
|
|
||||||
|
|
||||||
/* Callback functions *********************************************************/
|
/* Callback functions *********************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -16,19 +16,21 @@
|
||||||
* large pages.
|
* large pages.
|
||||||
*
|
*
|
||||||
*********************************************/
|
*********************************************/
|
||||||
|
#include "net.h"
|
||||||
|
#include "../net/enc28j60.h"
|
||||||
|
#include "../kernel/kernel.h"
|
||||||
|
#include "../include/fb.h"
|
||||||
#include "ip_config.h"
|
#include "ip_config.h"
|
||||||
#include "ip_arp_udp_tcp.h"
|
|
||||||
|
|
||||||
// I use them to debug stuff:
|
// I use them to debug stuff:
|
||||||
#define LEDOFF PORTB|=(1<<PORTB1)
|
#define LEDOFF PORTB|=(1<<PORTB1)
|
||||||
#define LEDON PORTB&=~(1<<PORTB1)
|
#define LEDON PORTB&=~(1<<PORTB1)
|
||||||
|
#define LEDISOFF PORTB&(1<<PORTB1)
|
||||||
//
|
//
|
||||||
static uint8_t macaddr[6];
|
static uint8_t macaddr[6];
|
||||||
static uint8_t ipaddr[4]={0,0,0,0};
|
static uint8_t ipaddr[4]={0,0,0,0};
|
||||||
//static uint8_t seqnum=0xa; // my initial tcp sequence number
|
static uint8_t seqnum=0xa; // my initial tcp sequence number
|
||||||
static void (*icmp_callback)(uint8_t *ip);
|
static void (*icmp_callback)(uint8_t *ip);
|
||||||
|
|
||||||
//
|
//
|
||||||
#if defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client)
|
#if defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client)
|
||||||
#define ARP_MAC_resolver_client 1
|
#define ARP_MAC_resolver_client 1
|
||||||
|
@ -59,10 +61,10 @@ static uint16_t (*client_tcp_datafill_callback)(uint8_t);
|
||||||
static uint8_t www_fd=0;
|
static uint8_t www_fd=0;
|
||||||
static uint8_t browsertype=0; // 0 = get, 1 = post
|
static uint8_t browsertype=0; // 0 = get, 1 = post
|
||||||
static void (*client_browser_callback)(uint16_t,uint16_t,uint16_t); // the fields are: uint16_t webstatuscode,uint16_t datapos,uint16_t len; datapos is start of http data and len the the length of that data
|
static void (*client_browser_callback)(uint16_t,uint16_t,uint16_t); // the fields are: uint16_t webstatuscode,uint16_t datapos,uint16_t len; datapos is start of http data and len the the length of that data
|
||||||
static const prog_char *client_additionalheaderline;
|
static const char *client_additionalheaderline_p; // null pointer or pointer to a string in progmem
|
||||||
static char *client_postval;
|
static char *client_postval;
|
||||||
static const prog_char *client_urlbuf;
|
static const char *client_urlbuf_p; // null pointer or pointer to a string in progmem
|
||||||
static const char *client_urlbuf_var;
|
static char *client_urlbuf_var;
|
||||||
static const char *client_hoststr;
|
static const char *client_hoststr;
|
||||||
static uint8_t *bufptr=0; // ugly workaround for backward compatibility
|
static uint8_t *bufptr=0; // ugly workaround for backward compatibility
|
||||||
#endif
|
#endif
|
||||||
|
@ -89,6 +91,8 @@ static uint16_t info_data_len=0;
|
||||||
|
|
||||||
#if defined (ALL_clients)
|
#if defined (ALL_clients)
|
||||||
static uint8_t ipnetmask[4]={255,255,255,255};
|
static uint8_t ipnetmask[4]={255,255,255,255};
|
||||||
|
#endif
|
||||||
|
#if defined (ALL_clients) || defined (WOL_client)
|
||||||
static uint8_t ipid=0x2; // IP-identification, it works as well if you do not change it but it is better to fill the field, we count this number up and wrap.
|
static uint8_t ipid=0x2; // IP-identification, it works as well if you do not change it but it is better to fill the field, we count this number up and wrap.
|
||||||
const char iphdr[] ={0x45,0,0,0x82,0,0,0x40,0,0x20}; // 0x82 is the total len on ip, 0x20 is ttl (time to live), the second 0,0 is IP-identification and may be changed.
|
const char iphdr[] ={0x45,0,0,0x82,0,0,0x40,0,0x20}; // 0x82 is the total len on ip, 0x20 is ttl (time to live), the second 0,0 is IP-identification and may be changed.
|
||||||
#endif
|
#endif
|
||||||
|
@ -412,20 +416,6 @@ uint16_t fill_tcp_data_p(uint8_t *buf,uint16_t pos, const uint8_t *progmem_s)
|
||||||
return(pos);
|
return(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t fill_tcp_data_string(uint8_t *buf,uint16_t pos, char *reply)
|
|
||||||
{
|
|
||||||
// char c;
|
|
||||||
// fill in tcp data at position pos
|
|
||||||
//
|
|
||||||
// with no options the data starts after the checksum + 2 more bytes (urgent ptr)
|
|
||||||
while ( !(*reply == '\n') ) {
|
|
||||||
buf[TCP_CHECKSUM_L_P+3+pos] = *reply;
|
|
||||||
reply++;
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
return(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill a binary string of len data into the tcp packet
|
// fill a binary string of len data into the tcp packet
|
||||||
uint16_t fill_tcp_data_len(uint8_t *buf,uint16_t pos, const uint8_t *s, uint8_t len)
|
uint16_t fill_tcp_data_len(uint8_t *buf,uint16_t pos, const uint8_t *s, uint8_t len)
|
||||||
{
|
{
|
||||||
|
@ -644,22 +634,21 @@ void www_server_reply(uint8_t *buf,uint16_t dlen)
|
||||||
// This code requires that we send only one data packet
|
// This code requires that we send only one data packet
|
||||||
// because we keep no state information. We must therefore set
|
// because we keep no state information. We must therefore set
|
||||||
// the fin here:
|
// 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
|
make_tcp_ack_with_data_noflags(buf,dlen); // send data
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WWW_server
|
#endif // WWW_server
|
||||||
|
|
||||||
#if defined (ALL_clients)
|
#if defined (ALL_clients) || defined (GRATARP) || defined (WOL_client)
|
||||||
// fill buffer with a prog-mem string
|
// 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){
|
while (len){
|
||||||
*buf= progmem_s[i];
|
*buf= pgm_read_byte(progmem_str_p);
|
||||||
buf++;
|
buf++;
|
||||||
len--; i++;
|
progmem_str_p++;
|
||||||
|
len--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -725,6 +714,7 @@ void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport,uint8_t *dst
|
||||||
{
|
{
|
||||||
uint8_t i=0;
|
uint8_t i=0;
|
||||||
uint16_t ck;
|
uint16_t ck;
|
||||||
|
if (!enc28j60linkup())return;
|
||||||
//
|
//
|
||||||
while(i<6){
|
while(i<6){
|
||||||
buf[ETH_DST_MAC +i]=dstmac[i]; // gw mac in local lan or host mac
|
buf[ETH_DST_MAC +i]=dstmac[i]; // gw mac in local lan or host mac
|
||||||
|
@ -1179,27 +1169,27 @@ uint16_t www_client_internal_datafill_callback(uint8_t fd){
|
||||||
if (browsertype==0){
|
if (browsertype==0){
|
||||||
// GET
|
// GET
|
||||||
len=fill_tcp_data_p(bufptr,0,PSTR("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);
|
len=fill_tcp_data(bufptr,len,client_urlbuf_var);
|
||||||
// I would prefer http/1.0 but there is a funny
|
// I would prefer http/1.0 but there is a funny
|
||||||
// bug in some apache webservers which causes
|
// bug in some apache webservers which causes
|
||||||
// them to send two packets (fragmented PDU)
|
// them to send two packets (fragmented PDU)
|
||||||
// if we don't use HTTP/1.1 + Connection: close
|
// 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_p(bufptr,len,PSTR(" HTTP/1.1\r\nHost: "));
|
||||||
len=fill_tcp_data(bufptr,len,client_hoststr);
|
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\nConnection: close\r\n\r\n"));
|
len=fill_tcp_data_p(bufptr,len,PSTR("\r\nUser-Agent: tgr/1.1\r\nAccept: text/html\r\n\r\n"));
|
||||||
}else{
|
}else{
|
||||||
// POST
|
// POST
|
||||||
len=fill_tcp_data_p(bufptr,0,PSTR("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(bufptr,len,client_urlbuf_var);
|
||||||
len=fill_tcp_data_p(bufptr,len,PSTR(" HTTP/1.1\r\nHost: "));
|
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,client_hoststr);
|
||||||
if (client_additionalheaderline){
|
if (client_additionalheaderline_p){
|
||||||
len=fill_tcp_data_p(bufptr,len,PSTR("\r\n"));
|
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: "));
|
len=fill_tcp_data_p(bufptr,len,PSTR("Content-Length: "));
|
||||||
itoa(strlen(client_postval),strbuf,10);
|
itoa(strlen(client_postval),strbuf,10);
|
||||||
len=fill_tcp_data(bufptr,len,strbuf);
|
len=fill_tcp_data(bufptr,len,strbuf);
|
||||||
|
@ -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
|
// The string buffers to which urlbuf_varpart and hoststr are pointing
|
||||||
// must not be changed until the callback is executed.
|
// 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;
|
if (!enc28j60linkup())return;
|
||||||
client_urlbuf=urlbuf;
|
client_urlbuf_p=urlbuf_p;
|
||||||
client_urlbuf_var=urlbuf_varpart;
|
client_urlbuf_var=urlbuf_varpart;
|
||||||
client_hoststr=hoststr;
|
client_hoststr=hoststr;
|
||||||
browsertype=0;
|
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:
|
// 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
|
// The string buffers to which urlbuf_varpart and hoststr are pointing
|
||||||
// must not be changed until the callback is executed.
|
// 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).
|
// when the post operation was really done (e.g when callback was executed).
|
||||||
// postval must be urlencoded.
|
// 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;
|
if (!enc28j60linkup())return;
|
||||||
client_urlbuf=urlbuf;
|
client_urlbuf_p=urlbuf_p;
|
||||||
client_hoststr=hoststr;
|
client_hoststr=hoststr;
|
||||||
client_urlbuf_var=urlbuf_varpart;
|
client_urlbuf_var=urlbuf_varpart;
|
||||||
client_additionalheaderline=additionalheaderline;
|
client_additionalheaderline_p=additionalheaderline_p;
|
||||||
client_postval=postval;
|
client_postval=postval;
|
||||||
browsertype=1;
|
browsertype=1;
|
||||||
client_browser_callback=callback;
|
client_browser_callback=callback;
|
||||||
|
@ -1318,7 +1308,7 @@ uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost)
|
||||||
// of the tcp data if there is tcp data part
|
// of the tcp data if there is tcp data part
|
||||||
uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen)
|
uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen)
|
||||||
{
|
{
|
||||||
// uint16_t len;
|
uint16_t len;
|
||||||
#if defined (TCP_client)
|
#if defined (TCP_client)
|
||||||
uint8_t send_fin=0;
|
uint8_t send_fin=0;
|
||||||
uint16_t tcpstart;
|
uint16_t tcpstart;
|
||||||
|
@ -1379,13 +1369,13 @@ uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen)
|
||||||
(*icmp_callback)(&(buf[IP_SRC_P]));
|
(*icmp_callback)(&(buf[IP_SRC_P]));
|
||||||
}
|
}
|
||||||
// a ping packet, let's send pong
|
// a ping packet, let's send pong
|
||||||
debugstr("Sending ping reply...");
|
debugstr("Replying to ping..."); debugcrlf();
|
||||||
debugcrlf();
|
|
||||||
make_echo_reply_from_request(buf,plen);
|
make_echo_reply_from_request(buf,plen);
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
if (plen<54 && buf[IP_PROTO_P]!=IP_PROTO_TCP_V ){
|
// this is an important check to avoid working on the wrong packets:
|
||||||
// smaller than the smallest TCP packet and not tcp port
|
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);
|
return(0);
|
||||||
}
|
}
|
||||||
#if defined (TCP_client)
|
#if defined (TCP_client)
|
||||||
|
@ -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
|
// 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);
|
(*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);
|
return(0);
|
||||||
}
|
}
|
||||||
len=get_tcp_data_len(buf);
|
len=get_tcp_data_len(buf);
|
||||||
|
@ -1470,11 +1460,25 @@ uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(tcp_client_state==5){
|
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);
|
return(0);
|
||||||
}
|
}
|
||||||
if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V){
|
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
|
tcp_client_state=5; // connection terminated
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
@ -1490,7 +1494,7 @@ uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen)
|
||||||
//
|
//
|
||||||
#ifdef WWW_server
|
#ifdef WWW_server
|
||||||
// tcp port web server start
|
// 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){
|
if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V){
|
||||||
make_tcp_synack_from_syn(buf);
|
make_tcp_synack_from_syn(buf);
|
||||||
// make_tcp_synack_from_syn does already send the syn,ack
|
// make_tcp_synack_from_syn does already send the syn,ack
|
|
@ -12,10 +12,6 @@
|
||||||
#ifndef IP_ARP_UDP_TCP_H
|
#ifndef IP_ARP_UDP_TCP_H
|
||||||
#define IP_ARP_UDP_TCP_H 1
|
#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"
|
#include "ip_config.h"
|
||||||
|
|
||||||
// set my own mac address:
|
// set my own mac address:
|
|
@ -22,7 +22,7 @@
|
||||||
// a server answering to UDP messages
|
// a server answering to UDP messages
|
||||||
#define UDP_server
|
#define UDP_server
|
||||||
// a web server
|
// a web server
|
||||||
#undef WWW_server
|
#define WWW_server
|
||||||
|
|
||||||
// to send out a ping:
|
// to send out a ping:
|
||||||
#undef PING_client
|
#undef PING_client
|
|
@ -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)
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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);
|
|
|
@ -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
|
|
Loading…
Reference in a new issue