From bc2ecc934d51335e2a556984603015d98a7ad475 Mon Sep 17 00:00:00 2001 From: Adam Greenwood-Byrne Date: Sat, 6 Nov 2021 17:16:46 +0000 Subject: [PATCH] Added part15-tcpip - same as part14-ethernet, but replies to pings now --- part15-tcpip/Makefile | 20 + part15-tcpip/Makefile.gcc | 20 + part15-tcpip/boot/boot.S | 114 ++ part15-tcpip/boot/link.ld | 35 + part15-tcpip/boot/sysregs.h | 44 + part15-tcpip/include/fb.h | 14 + part15-tcpip/include/io.h | 19 + part15-tcpip/include/mb.h | 34 + part15-tcpip/include/multicore.h | 11 + part15-tcpip/include/spi.h | 6 + part15-tcpip/include/terminal.h | 253 +++++ part15-tcpip/kernel/arp.c | 199 ++++ part15-tcpip/kernel/irq.c | 58 + part15-tcpip/kernel/irqentry.S | 156 +++ part15-tcpip/kernel/kernel.c | 176 ++++ part15-tcpip/kernel/kernel.h | 56 + part15-tcpip/kernel/utils.S | 15 + part15-tcpip/lib/fb.c | 260 +++++ part15-tcpip/lib/io.c | 199 ++++ part15-tcpip/lib/mb.c | 39 + part15-tcpip/lib/multicore.c | 44 + part15-tcpip/lib/spi.c | 100 ++ part15-tcpip/net/enc28j60.c | 1450 +++++++++++++++++++++++++ part15-tcpip/net/enc28j60.h | 816 ++++++++++++++ part15-tcpip/net/encspi.c | 21 + part15-tcpip/tcpip/ip_arp_udp_tcp.c | 1525 +++++++++++++++++++++++++++ part15-tcpip/tcpip/ip_arp_udp_tcp.h | 212 ++++ part15-tcpip/tcpip/ip_config.h | 53 + part15-tcpip/tcpip/net.h | 132 +++ 29 files changed, 6081 insertions(+) create mode 100644 part15-tcpip/Makefile create mode 100644 part15-tcpip/Makefile.gcc create mode 100644 part15-tcpip/boot/boot.S create mode 100644 part15-tcpip/boot/link.ld create mode 100644 part15-tcpip/boot/sysregs.h create mode 100644 part15-tcpip/include/fb.h create mode 100644 part15-tcpip/include/io.h create mode 100644 part15-tcpip/include/mb.h create mode 100644 part15-tcpip/include/multicore.h create mode 100644 part15-tcpip/include/spi.h create mode 100644 part15-tcpip/include/terminal.h create mode 100644 part15-tcpip/kernel/arp.c create mode 100644 part15-tcpip/kernel/irq.c create mode 100644 part15-tcpip/kernel/irqentry.S create mode 100644 part15-tcpip/kernel/kernel.c create mode 100644 part15-tcpip/kernel/kernel.h create mode 100644 part15-tcpip/kernel/utils.S create mode 100644 part15-tcpip/lib/fb.c create mode 100644 part15-tcpip/lib/io.c create mode 100644 part15-tcpip/lib/mb.c create mode 100644 part15-tcpip/lib/multicore.c create mode 100644 part15-tcpip/lib/spi.c create mode 100644 part15-tcpip/net/enc28j60.c create mode 100644 part15-tcpip/net/enc28j60.h create mode 100644 part15-tcpip/net/encspi.c create mode 100644 part15-tcpip/tcpip/ip_arp_udp_tcp.c create mode 100644 part15-tcpip/tcpip/ip_arp_udp_tcp.h create mode 100644 part15-tcpip/tcpip/ip_config.h create mode 100644 part15-tcpip/tcpip/net.h diff --git a/part15-tcpip/Makefile b/part15-tcpip/Makefile new file mode 100644 index 0000000..7c9e887 --- /dev/null +++ b/part15-tcpip/Makefile @@ -0,0 +1,20 @@ +CFILES = $(wildcard *.c lib/*.c kernel/*.c net/*.c tcpip/*.c) +SFILES = $(wildcard boot/*.S lib/*.S kernel/*.S) +OFILES = $(CFILES:.c=.o) $(SFILES:.S=.o) +LLVMPATH = /opt/homebrew/opt/llvm/bin +CLANGFLAGS = -Wall -O0 -ffreestanding -nostdlib -mcpu=cortex-a72+nosimd + +all: clean kernel8.img + +%.o: %.c + $(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@ + +%.o: %.S + $(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@ + +kernel8.img: $(OFILES) + $(LLVMPATH)/ld.lld -m aarch64elf -nostdlib $(OFILES) -T boot/link.ld -o kernel8.elf + $(LLVMPATH)/llvm-objcopy -O binary kernel8.elf kernel8.img + +clean: + /bin/rm kernel8.elf */*.o *.img > /dev/null 2> /dev/null || true diff --git a/part15-tcpip/Makefile.gcc b/part15-tcpip/Makefile.gcc new file mode 100644 index 0000000..cb4b843 --- /dev/null +++ b/part15-tcpip/Makefile.gcc @@ -0,0 +1,20 @@ +CFILES = $(wildcard *.c lib/*.c kernel/*.c net/*.c tcpip/*.c) +SFILES = $(wildcard boot/*.S lib/*.S kernel/*.S) +OFILES = $(CFILES:.c=.o) $(SFILES:.S=.o) +GCCFLAGS = -Wall -O0 -ffreestanding -nostdlib -nostartfiles +GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin + +all: clean kernel8.img + +%.o: %.c + $(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@ + +%.o: %.S + $(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@ + +kernel8.img: $(OFILES) + $(GCCPATH)/aarch64-none-elf-ld -nostdlib $(OFILES) -T boot/link.ld -o kernel8.elf + $(GCCPATH)/aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img + +clean: + /bin/rm kernel8.elf */*.o *.img > /dev/null 2> /dev/null || true diff --git a/part15-tcpip/boot/boot.S b/part15-tcpip/boot/boot.S new file mode 100644 index 0000000..f644aee --- /dev/null +++ b/part15-tcpip/boot/boot.S @@ -0,0 +1,114 @@ +#include "sysregs.h" + +#define LOCAL_CONTROL 0xff800000 +#define LOCAL_PRESCALER 0xff800008 +#define OSC_FREQ 54000000 +#define MAIN_STACK 0x400000 + +.section ".text.boot" // Make sure the linker puts this at the start of the kernel image + +.global _start // Execution starts here + +_start: + ldr x0, =LOCAL_CONTROL // Sort out the timer + str wzr, [x0] + mov w1, 0x80000000 + str w1, [x0, #(LOCAL_PRESCALER - LOCAL_CONTROL)] + + ldr x0, =OSC_FREQ + msr cntfrq_el0, x0 + msr cntvoff_el2, xzr + + // Check processor ID is zero (executing on main core), else hang + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, 2f + + // We're not on the main core, so hang in an infinite wait loop + adr x5, spin_cpu0 +1: wfe + ldr x4, [x5, x1, lsl #3] + cbz x4, 1b + + ldr x2, =__stack_start // Get ourselves a fresh stack - location depends on CPU core asking + lsl x1, x1, #9 // Multiply core_number by 512 + add x3, x2, x1 // Add to the address + mov sp, x3 + + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + br x4 + b 1b +2: // We're on the main core! + // First enable the FPU + + mov x0, #0x33ff + msr cptr_el3, x0 // Disable coprocessor traps to EL3 + mov x0, #3 << 20 + msr cpacr_el1, x0 // Enable FP/SIMD at EL1 + + // Now get ready to switch from EL3 down to EL1 + + ldr x0, =SCTLR_VALUE_MMU_DISABLED + msr sctlr_el1, x0 + + ldr x0, =HCR_VALUE + msr hcr_el2, x0 + + ldr x0, =SCR_VALUE + msr scr_el3, x0 + + ldr x0, =SPSR_VALUE + msr spsr_el3, x0 + + adr x0, el1_entry + msr elr_el3, x0 + + eret +el1_entry: + // We're in EL1 + // Clean the BSS section + ldr x1, =__bss_start // Start address + ldr w2, =__bss_size // Size of the section +3: cbz w2, 4f // Quit loop if zero + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, 3b // Loop if non-zero + + // Set stack to start somewhere safe + mov sp, #MAIN_STACK + + // Jump to our main() routine in C (make sure it doesn't return) +4: bl main + // In case it does return, halt the master core too + b 1b + +.ltorg + +.org 0x110 +.globl spin_cpu0 +spin_cpu0: + .quad 0 + +.org 0x118 +.globl spin_cpu1 +spin_cpu1: + .quad 0 + +.org 0x120 +.globl spin_cpu2 +spin_cpu2: + .quad 0 + +.org 0x128 +.globl spin_cpu3 +spin_cpu3: + .quad 0 + +.globl get_el +get_el: + mrs x0, CurrentEL + lsr x0, x0, #2 + ret diff --git a/part15-tcpip/boot/link.ld b/part15-tcpip/boot/link.ld new file mode 100644 index 0000000..4e84390 --- /dev/null +++ b/part15-tcpip/boot/link.ld @@ -0,0 +1,35 @@ +SECTIONS +{ + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + __bss_size = (__bss_end - __bss_start)>>3; + } + .cpu1Stack : + { + . = ALIGN(16); + __stack_start = .; + . = . + 512; + __cpu1_stack = .; + } + .cpu2Stack : + { + . = . + 512; + __cpu2_stack = .; + } + .cpu3Stack : + { + . = . + 512; + __cpu3_stack = .; + } + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} diff --git a/part15-tcpip/boot/sysregs.h b/part15-tcpip/boot/sysregs.h new file mode 100644 index 0000000..385cd79 --- /dev/null +++ b/part15-tcpip/boot/sysregs.h @@ -0,0 +1,44 @@ +#ifndef _SYSREGS_H +#define _SYSREGS_H + +// *************************************** +// SCTLR_EL1, System Control Register (EL1), Page 2654 of AArch64-Reference-Manual. +// *************************************** + +#define SCTLR_RESERVED (3 << 28) | (3 << 22) | (1 << 20) | (1 << 11) +#define SCTLR_EE_LITTLE_ENDIAN (0 << 25) +#define SCTLR_EOE_LITTLE_ENDIAN (0 << 24) +#define SCTLR_I_CACHE_DISABLED (0 << 12) +#define SCTLR_D_CACHE_DISABLED (0 << 2) +#define SCTLR_I_CACHE_ENABLED (1 << 12) +#define SCTLR_D_CACHE_ENABLED (1 << 2) +#define SCTLR_MMU_DISABLED (0 << 0) +#define SCTLR_MMU_ENABLED (1 << 0) + +#define SCTLR_VALUE_MMU_DISABLED (SCTLR_RESERVED | SCTLR_EE_LITTLE_ENDIAN | SCTLR_I_CACHE_ENABLED | SCTLR_D_CACHE_ENABLED | SCTLR_MMU_DISABLED) + +// *************************************** +// HCR_EL2, Hypervisor Configuration Register (EL2), Page 2487 of AArch64-Reference-Manual. +// *************************************** + +#define HCR_RW (1 << 31) +#define HCR_VALUE HCR_RW + +// *************************************** +// SCR_EL3, Secure Configuration Register (EL3), Page 2648 of AArch64-Reference-Manual. +// *************************************** + +#define SCR_RESERVED (3 << 4) +#define SCR_RW (1 << 10) +#define SCR_NS (1 << 0) +#define SCR_VALUE (SCR_RESERVED | SCR_RW | SCR_NS) + +// *************************************** +// SPSR_EL3, Saved Program Status Register (EL3) Page 389 of AArch64-Reference-Manual. +// *************************************** + +#define SPSR_MASK_ALL (7 << 6) +#define SPSR_EL1h (5 << 0) +#define SPSR_VALUE (SPSR_MASK_ALL | SPSR_EL1h) + +#endif diff --git a/part15-tcpip/include/fb.h b/part15-tcpip/include/fb.h new file mode 100644 index 0000000..88e0ee4 --- /dev/null +++ b/part15-tcpip/include/fb.h @@ -0,0 +1,14 @@ +void fb_init(); +void drawPixel(int x, int y, unsigned char attr); +void drawChar(unsigned char ch, int x, int y, unsigned char attr, int zoom); +void drawString(int x, int y, char *s, unsigned char attr, int zoom); +void drawRect(int x1, int y1, int x2, int y2, unsigned char attr, int fill); +void drawCircle(int x0, int y0, int radius, unsigned char attr, int fill); +void drawLine(int x1, int y1, int x2, int y2, unsigned char attr); +void moveRect(int oldx, int oldy, int width, int height, int shiftx, int shifty, unsigned char attr); +void wait_msec(unsigned int n); +void debugstr(char *str); +void debugcrlf(void); +void debugch(unsigned char b); +void debughex(unsigned int d); +int strlen(const char *str); diff --git a/part15-tcpip/include/io.h b/part15-tcpip/include/io.h new file mode 100644 index 0000000..0c79113 --- /dev/null +++ b/part15-tcpip/include/io.h @@ -0,0 +1,19 @@ +#define PERIPHERAL_BASE 0xFE000000 +#define LEGACY_BASE 0x7E000000 +#define SAFE_ADDRESS 0x00400000 // Somewhere safe to store a lot of data + +void uart_init(); +void uart_writeText(char *buffer); +void uart_loadOutputFifo(); +unsigned char uart_readByte(); +unsigned int uart_isReadByteReady(); +void uart_writeByteBlockingActual(unsigned char ch); +void uart_update(); +void mmio_write(long reg, unsigned int val); +unsigned int mmio_read(long reg); +void gpio_useAsAlt0(unsigned int pin_number); +void gpio_useAsAlt3(unsigned int pin_number); +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); diff --git a/part15-tcpip/include/mb.h b/part15-tcpip/include/mb.h new file mode 100644 index 0000000..0e1861b --- /dev/null +++ b/part15-tcpip/include/mb.h @@ -0,0 +1,34 @@ +extern volatile unsigned int mbox[36]; + +enum { + MBOX_REQUEST = 0 +}; + +enum { + MBOX_CH_POWER = 0, + MBOX_CH_FB = 1, + MBOX_CH_VUART = 2, + MBOX_CH_VCHIQ = 3, + MBOX_CH_LEDS = 4, + MBOX_CH_BTNS = 5, + MBOX_CH_TOUCH = 6, + MBOX_CH_COUNT = 7, + MBOX_CH_PROP = 8 // Request from ARM for response by VideoCore +}; + +enum { + MBOX_TAG_SETPOWER = 0x28001, + MBOX_TAG_SETCLKRATE = 0x38002, + + MBOX_TAG_SETPHYWH = 0x48003, + MBOX_TAG_SETVIRTWH = 0x48004, + MBOX_TAG_SETVIRTOFF = 0x48009, + MBOX_TAG_SETDEPTH = 0x48005, + MBOX_TAG_SETPXLORDR = 0x48006, + MBOX_TAG_GETFB = 0x40001, + MBOX_TAG_GETPITCH = 0x40008, + + MBOX_TAG_LAST = 0 +}; + +unsigned int mbox_call(unsigned char ch); diff --git a/part15-tcpip/include/multicore.h b/part15-tcpip/include/multicore.h new file mode 100644 index 0000000..4c3dc0f --- /dev/null +++ b/part15-tcpip/include/multicore.h @@ -0,0 +1,11 @@ +extern unsigned int spin_cpu0; +extern unsigned int spin_cpu1; +extern unsigned int spin_cpu2; +extern unsigned int spin_cpu3; + +void start_core1(void (*func)(void)); +void start_core2(void (*func)(void)); +void start_core3(void (*func)(void)); +void clear_core1(void); +void clear_core2(void); +void clear_core3(void); diff --git a/part15-tcpip/include/spi.h b/part15-tcpip/include/spi.h new file mode 100644 index 0000000..c5fca8b --- /dev/null +++ b/part15-tcpip/include/spi.h @@ -0,0 +1,6 @@ +void spi_init(); +void spi_send_recv(unsigned char *sbuffer, unsigned char *rbuffer, unsigned int size); +void spi_send(unsigned char *data, unsigned int size); +void spi_recv(unsigned char *data, unsigned int size); +void spi_send_no_selection(unsigned char command); +void spi_chip_select(unsigned char chip_select); diff --git a/part15-tcpip/include/terminal.h b/part15-tcpip/include/terminal.h new file mode 100644 index 0000000..ef05b85 --- /dev/null +++ b/part15-tcpip/include/terminal.h @@ -0,0 +1,253 @@ +unsigned int vgapal[] = { + 0x000000, + 0x0000AA, + 0x00AA00, + 0x00AAAA, + 0xAA0000, + 0xAA00AA, + 0xAA5500, + 0xAAAAAA, + 0x555555, + 0x5555FF, + 0x55FF55, + 0x55FFFF, + 0xFF5555, + 0xFF55FF, + 0xFFFF55, + 0xFFFFFF +}; + +enum { + FONT_WIDTH = 8, + FONT_HEIGHT = 8, + FONT_BPG = 8, // Bytes per glyph + FONT_BPL = 1, // Bytes per line + FONT_NUMGLYPHS = 224 +}; + +unsigned char font[FONT_NUMGLYPHS][FONT_BPG] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space) + { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!) + { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (") + { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#) + { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($) + { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%) + { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&) + { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (') + { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (() + { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ()) + { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*) + { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,) + { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) + { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) + { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) + { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) + { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) + { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) + { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) + { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) + { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) + { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) + { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) + { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;) + { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<) + { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=) + { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>) + { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?) + { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@) + { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) + { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) + { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) + { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) + { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) + { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) + { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) + { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) + { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) + { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) + { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) + { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) + { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) + { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) + { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) + { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) + { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) + { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) + { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) + { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) + { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) + { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) + { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([) + { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\) + { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (]) + { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_) + { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`) + { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) + { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) + { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) + { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) + { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) + { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) + { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) + { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) + { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) + { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) + { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) + { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) + { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) + { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) + { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) + { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) + { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) + { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) + { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) + { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) + { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) + { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({) + { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|) + { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (}) + { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007F + { 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x18, 0x30, 0x1E}, // U+00C7 (C cedille) + { 0x00, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FC (u umlaut) + { 0x38, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E9 (e aigu) + { 0x7E, 0xC3, 0x3C, 0x60, 0x7C, 0x66, 0xFC, 0x00}, // U+00E2 (a circumflex) + { 0x33, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E4 (a umlaut) + { 0x07, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E0 (a grave) + { 0x0C, 0x0C, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E5 (a ring) + { 0x00, 0x00, 0x1E, 0x03, 0x03, 0x1E, 0x30, 0x1C}, // U+00E7 (c cedille) + { 0x7E, 0xC3, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00}, // U+00EA (e circumflex) + { 0x33, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00EB (e umlaut) + { 0x07, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E8 (e grave) + { 0x33, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EF (i umlaut) + { 0x3E, 0x63, 0x1C, 0x18, 0x18, 0x18, 0x3C, 0x00}, // U+00EE (i circumflex) + { 0x07, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EC (i grave) + { 0x63, 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x63, 0x00}, // U+00C4 (A umlaut) + { 0x0C, 0x0C, 0x00, 0x1E, 0x33, 0x3F, 0x33, 0x00}, // U+00C5 (A ring) + { 0x07, 0x00, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00}, // U+00C8 (E grave) + { 0x00, 0x00, 0xFE, 0x30, 0xFE, 0x33, 0xFE, 0x00}, // U+00E6 (ae) + { 0x7C, 0x36, 0x33, 0x7F, 0x33, 0x33, 0x73, 0x00}, // U+00C6 (AE) + { 0x1E, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F4 (o circumflex) + { 0x00, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F6 (o umlaut) + { 0x00, 0x07, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F2 (o grave) + { 0x1E, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FB (u circumflex) + { 0x00, 0x07, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00F9 (u grave) + { 0x00, 0x33, 0x00, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+00FF (y umlaut) + { 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00}, // U+00D6 (O umlaut) + { 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+00DC (U umlaut) + { 0x18, 0x18, 0x7E, 0x03, 0x03, 0x7E, 0x18, 0x18}, // U+00A2 (dollarcents) + { 0x1C, 0x36, 0x26, 0x0F, 0x06, 0x67, 0x3F, 0x00}, // U+00A3 (pound sterling) + { 0x33, 0x33, 0x1E, 0x3F, 0x0C, 0x3F, 0x0C, 0x0C}, // U+00A5 (yen) + { 0x7C, 0xC6, 0x1C, 0x36, 0x36, 0x1C, 0x33, 0x1E}, // U+00A7 (paragraph) + { 0x70, 0xD8, 0x18, 0x3C, 0x18, 0x18, 0x1B, 0x0E}, // U+0192 (dutch florijn) + { 0x38, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E1 (a aigu) + { 0x1C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00ED (i augu) + { 0x00, 0x38, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F3 (o aigu) + { 0x00, 0x38, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FA (u aigu) + { 0x00, 0x1F, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x00}, // U+00F1 (n ~) + { 0x3F, 0x00, 0x33, 0x37, 0x3F, 0x3B, 0x33, 0x00}, // U+00D1 (N ~) + { 0x3C, 0x36, 0x36, 0x7C, 0x00, 0x00, 0x00, 0x00}, // U+00AA (superscript a) + { 0x1C, 0x36, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00}, // U+00BA (superscript 0) + { 0x0C, 0x00, 0x0C, 0x06, 0x03, 0x33, 0x1E, 0x00}, // U+00BF (inverted ?) + { 0x00, 0x00, 0x00, 0x3F, 0x03, 0x03, 0x00, 0x00}, // U+2310 (gun pointing right) + { 0x00, 0x00, 0x00, 0x3F, 0x30, 0x30, 0x00, 0x00}, // U+00AC (gun pointing left) + { 0xC3, 0x63, 0x33, 0x7B, 0xCC, 0x66, 0x33, 0xF0}, // U+00BD (1/2) + { 0xC3, 0x63, 0x33, 0xBD, 0xEC, 0xF6, 0xF3, 0x03}, // U+00BC (1/4) + { 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00}, // U+00A1 (inverted !) + { 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00}, // U+00AB (<<) + { 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00}, // U+00BB (>>) + { 0x55, 0x00, 0xAA, 0x00, 0x55, 0x00, 0xAA, 0x00}, // U+2591 (25% solid) + { 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA}, // U+2592 (50% solid) + { 0xFF, 0xAA, 0xFF, 0x55, 0xFF, 0xAA, 0xFF, 0x55}, // U+2593 (75% solid) + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, // U+2502 (thin vertical) + { 0x08, 0x08, 0x08, 0x08, 0x0f, 0x08, 0x08, 0x08}, // U+2524 (down L, left L, up L) + { 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2561 (up L, down L, left D) + { 0x14, 0x14, 0x14, 0x14, 0x17, 0x14, 0x14, 0x14}, // U+2562 (up D, down D, left L) + { 0x00, 0x00, 0x00, 0x00, 0x1F, 0x14, 0x14, 0x14}, // U+2556 (down D, left L) + { 0x00, 0x00, 0x00, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2555 (down L, left D) + { 0x14, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14}, // U+2563 (up D, down D, left D) + { 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14}, // U+2551 (double vertical) + { 0x00, 0x00, 0x00, 0x1F, 0x10, 0x17, 0x14, 0x14}, // U+2557 (down D, left D) + { 0x14, 0x14, 0x14, 0x17, 0x10, 0x1F, 0x00, 0x00}, // U+255D (up D, left D) + { 0x14, 0x14, 0x14, 0x14, 0x1F, 0x00, 0x00, 0x00}, // U+255C (up D, left L) + { 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x00, 0x00}, // U+255B (up L, left D) + { 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08}, // U+2510 (down L, left L) + { 0x08, 0x08, 0x08, 0x08, 0xf8, 0x00, 0x00, 0x00}, // U+2514 (up L, right L) + { 0x08, 0x08, 0x08, 0x08, 0xff, 0x00, 0x00, 0x00}, // U+2534 (up L, right L, left L) + { 0x00, 0x00, 0x00, 0x00, 0xff, 0x08, 0x08, 0x08}, // U+252C (down L, right L, left L) + { 0x08, 0x08, 0x08, 0x08, 0xf8, 0x08, 0x08, 0x08}, // U+251C (down L, right L, up L) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00}, // U+2500 (thin horizontal) + { 0x08, 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08}, // U+253C (up L, right L, left L, down L) + { 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+255E (up L, down L, right D) + { 0x14, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x14, 0x14}, // U+255F (up D, down D, right L) + { 0x14, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x00, 0x00}, // U+255A (up D, right D) + { 0x00, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14}, // U+2554 (down D, right D) + { 0x14, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, 0x00}, // U+2569 (left D, right D, up D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14}, // U+2566 (left D, right D, down D) + { 0x14, 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x14, 0x14}, // U+2560 (up D, down D, right D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2550 (double horizontal) + { 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14}, // U+256C (left D, right D, down D, up D) + { 0x08, 0x08, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2567 (left D, right D, up L) + { 0x14, 0x14, 0x14, 0x14, 0xFF, 0x00, 0x00, 0x00}, // U+2568 (left L, right L, up D) + { 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x08, 0x08}, // U+2564 (left D, right D, down L) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x14, 0x14, 0x14}, // U+2565 (left L, right L, down D) + { 0x14, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x00, 0x00}, // U+2559 (up D, right L) + { 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x00, 0x00}, // U+2558 (up L, right D) + { 0x00, 0x00, 0x00, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+2552 (down L, right D) + { 0x00, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x14, 0x14}, // U+2553 (down D, right L) + { 0x14, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x14, 0x14}, // U+256B (left L, right L, down D, up D) + { 0x08, 0x08, 0x08, 0xFF, 0x08, 0xFF, 0x08, 0x08}, // U+256A (left D, right D, down L, up L) + { 0x08, 0x08, 0x08, 0x08, 0x0f, 0x00, 0x00, 0x00}, // U+2518 (up L, left L) + { 0x00, 0x00, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08}, // U+250C (down L, right L) + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2588 (solid) + { 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2584 (bottom half) + { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, // U+258C (left half) + { 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0}, // U+2590 (right half) + { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, // U+2580 (top half) +}; diff --git a/part15-tcpip/kernel/arp.c b/part15-tcpip/kernel/arp.c new file mode 100644 index 0000000..c6b1c5c --- /dev/null +++ b/part15-tcpip/kernel/arp.c @@ -0,0 +1,199 @@ +#include "../net/enc28j60.h" +#include "../include/fb.h" +#include "../tcpip/ip_arp_udp_tcp.h" + +ENC_HandleTypeDef handle; + +// Structure for Ethernet header + +typedef struct { + uint8_t DestAddrs[6]; + uint8_t SrcAddrs[6]; + uint16_t type; +} EtherNetII; + +// Ethernet packet types + +#define ARPPACKET 0x0608 +#define IPPACKET 0x0008 + +// Structure for an ARP Packet + +typedef struct { + EtherNetII eth; + uint16_t hardware; + uint16_t protocol; + uint8_t hardwareSize; + uint8_t protocolSize; + uint16_t opCode; + uint8_t senderMAC[6]; + uint8_t senderIP[4]; + uint8_t targetMAC[6]; + uint8_t targetIP[4]; +} ARP; + +// ARP OpCodes + +#define ARPREPLY 0x0200 +#define ARPREQUEST 0x0100 + +// ARP hardware types + +#define ETHERNET 0x0100 + +// MAC address to be assigned to the ENC28J60 + +uint8_t myMAC[6] = { 0xc0, 0xff, 0xee, 0xc0, 0xff, 0xee }; + +// Router MAC is not known to start with, and requires an ARP reply to find out + +uint8_t routerMAC[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +// IP address to be assigned to the ENC28J60 + +uint8_t deviceIP[4] = { 192, 168, 0, 66 }; + +// IP Address of the router, whose hardware address we will find using the ARP request + +uint8_t routerIP[4] = { 192, 168, 0, 1 }; + +// HELPER FUNCTIONS + +void *memset(void *dest, unsigned char val, unsigned short len) +{ + uint8_t *ptr = dest; + while (len-- > 0) + *ptr++ = val; + return dest; +} + +void *memcpy(void *dest, const void *src, unsigned short len) +{ + uint8_t *d = dest; + const uint8_t *s = src; + while (len--) + *d++ = *s++; + return dest; +} + +uint8_t memcmp(void *str1, void *str2, unsigned count) +{ + uint8_t *s1 = str1; + uint8_t *s2 = str2; + + while (count-- > 0) + { + if (*s1++ != *s2++) + return s1[-1] < s2[-1] ? -1 : 1; + } + + return 0; +} + +// MAIN FUNCTIONS + +void SendArpPacket(uint8_t *targetIP, uint8_t *deviceMAC) +{ + /* Parameters: + * targetIP - The target IP Address for the ARP request (the one whose hardware + * address we want) + * deviceMAC - The MAC address of the ENC28J60, i.e. the source MAC for the ARP + * request + */ + + ARP arpPacket; + + // The source of the packet will be the ENC28J60 MAC address + memcpy(arpPacket.eth.SrcAddrs, deviceMAC, 6); + + // The destination is broadcast - a MAC address of FF:FF:FF:FF:FF:FF */ + memset(arpPacket.eth.DestAddrs, 0xFF, 6); + + arpPacket.eth.type = ARPPACKET; + arpPacket.hardware = ETHERNET; + + // We want an IP address resolved + + arpPacket.protocol = IPPACKET; + arpPacket.hardwareSize = 0x06; // sizeof(deviceMAC); + arpPacket.protocolSize = 0x04; // sizeof(deviceIP); + arpPacket.opCode = ARPREQUEST; + + // Target MAC is set to 0 as it is unknown + memset(arpPacket.targetMAC, 0, 6); + + // Sender MAC is the ENC28J60's MAC address + memcpy(arpPacket.senderMAC, deviceMAC, 6); + + // The target IP is the IP address we want resolved + memcpy(arpPacket.targetIP, targetIP, 4); + + // Check if the last reply has come from an IP address that we want i.e. someone else is already using it + if (!memcmp(targetIP, deviceIP, 4)) { + // Yes, someone is using our IP so set the sender IP to 0.0.0.0 + memset(arpPacket.senderIP, 0, 4); + } else { + // No, nobody is using our IP so we can use it confidently + memcpy(arpPacket.senderIP, deviceIP, 4); + } + + // Send the packet + + debugstr("Sending ARP request."); + debugcrlf(); + enc28j60PacketSend(sizeof(ARP), &arpPacket); +} + +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); + } +} + +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 init_network(void) +{ + handle.Init.DuplexMode = ETH_MODE_HALFDUPLEX; + handle.Init.MACAddr = myMAC; + handle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; + handle.Init.InterruptEnableBits = EIE_LINKIE | EIE_PKTIE; + + debugstr("Starting network up."); + debugcrlf(); + if (!ENC_Start(&handle)) { + debugstr("Could not initialise network card."); + } else { + debugstr("Setting MAC address to C0:FF:EE:C0:FF:EE."); + debugcrlf(); + + ENC_SetMacAddr(&handle); + + debugstr("Network card successfully initialised."); + } + debugcrlf(); + + debugstr("Waiting for ifup... "); + while (!(handle.LinkStatus & PHSTAT2_LSTAT)) ENC_IRQHandler(&handle); + debugstr("done."); + debugcrlf(); + + debugstr("Initialising the TCP stack... "); + init_udp_or_www_server(myMAC, deviceIP); + debugstr("done."); + debugcrlf(); + + // Re-enable global interrupts + ENC_EnableInterrupts(EIE_INTIE); +} diff --git a/part15-tcpip/kernel/irq.c b/part15-tcpip/kernel/irq.c new file mode 100644 index 0000000..bc2276a --- /dev/null +++ b/part15-tcpip/kernel/irq.c @@ -0,0 +1,58 @@ +#include "kernel.h" +#include "../include/fb.h" + +char *entry_error_messages[] = { + "SYNC_INVALID_EL1t", + "IRQ_INVALID_EL1t", + "FIQ_INVALID_EL1t", + "ERROR_INVALID_EL1T", + + "SYNC_INVALID_EL1h", + "IRQ_INVALID_EL1h", + "FIQ_INVALID_EL1h", + "ERROR_INVALID_EL1h", + + "SYNC_INVALID_EL0_64", + "IRQ_INVALID_EL0_64", + "FIQ_INVALID_EL0_64", + "ERROR_INVALID_EL0_64", + + "SYNC_INVALID_EL0_32", + "IRQ_INVALID_EL0_32", + "FIQ_INVALID_EL0_32", + "ERROR_INVALID_EL0_32" +}; + +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 show_invalid_entry_message(int type, unsigned long esr, unsigned long address) { + debugstr(entry_error_messages[type]); + debugstr(" ESR: "); + debughex(esr); + debugstr("Addr: "); + debughex(address); +} + +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 new file mode 100644 index 0000000..28d0b94 --- /dev/null +++ b/part15-tcpip/kernel/irqentry.S @@ -0,0 +1,156 @@ +#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 + bl show_invalid_entry_message + + 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 new file mode 100644 index 0000000..6801d19 --- /dev/null +++ b/part15-tcpip/kernel/kernel.c @@ -0,0 +1,176 @@ +#include "../include/fb.h" +#include "../include/io.h" +#include "../include/spi.h" +#include "../include/multicore.h" +#include "../net/enc28j60.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 timer_get_ticks() { + 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 timer_sleep(unsigned int ms) { + unsigned long start = timer_get_ticks(); + + while(timer_get_ticks() < start + (ms * 1000)); +} + +void HAL_Delay(volatile unsigned int Delay) { + timer_sleep(Delay); +} + +unsigned int HAL_GetTick(void) { + return timer_get_ticks(); +} + +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 new file mode 100644 index 0000000..df91316 --- /dev/null +++ b/part15-tcpip/kernel/kernel.h @@ -0,0 +1,56 @@ +#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(); + +unsigned long timer_get_ticks(); +void timer_sleep(unsigned int ms); +void HAL_Delay(volatile unsigned int Delay); +unsigned int HAL_GetTick(void); + +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 new file mode 100644 index 0000000..6027c0d --- /dev/null +++ b/part15-tcpip/kernel/utils.S @@ -0,0 +1,15 @@ +.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 diff --git a/part15-tcpip/lib/fb.c b/part15-tcpip/lib/fb.c new file mode 100644 index 0000000..2cc89f7 --- /dev/null +++ b/part15-tcpip/lib/fb.c @@ -0,0 +1,260 @@ +#include "../include/io.h" +#include "../include/mb.h" +#include "../include/terminal.h" + +unsigned int width, height, pitch, isrgb; +unsigned char *fb; + +int curx = 0; +int cury = 0; + +void fb_init() +{ + mbox[0] = 35*4; // Length of message in bytes + mbox[1] = MBOX_REQUEST; + + mbox[2] = MBOX_TAG_SETPHYWH; // Tag identifier + mbox[3] = 8; // Value size in bytes + mbox[4] = 0; + mbox[5] = 1920; // Value(width) + mbox[6] = 1080; // Value(height) + + mbox[7] = MBOX_TAG_SETVIRTWH; + mbox[8] = 8; + mbox[9] = 8; + mbox[10] = 1920; + mbox[11] = 1080; + + mbox[12] = MBOX_TAG_SETVIRTOFF; + mbox[13] = 8; + mbox[14] = 8; + mbox[15] = 0; // Value(x) + mbox[16] = 0; // Value(y) + + mbox[17] = MBOX_TAG_SETDEPTH; + mbox[18] = 4; + mbox[19] = 4; + mbox[20] = 32; // Bits per pixel + + mbox[21] = MBOX_TAG_SETPXLORDR; + mbox[22] = 4; + mbox[23] = 4; + mbox[24] = 1; // RGB + + mbox[25] = MBOX_TAG_GETFB; + mbox[26] = 8; + mbox[27] = 8; + mbox[28] = 4096; // FrameBufferInfo.pointer + mbox[29] = 0; // FrameBufferInfo.size + + mbox[30] = MBOX_TAG_GETPITCH; + mbox[31] = 4; + mbox[32] = 4; + mbox[33] = 0; // Bytes per line + + mbox[34] = MBOX_TAG_LAST; + + // Check call is successful and we have a pointer with depth 32 + if (mbox_call(MBOX_CH_PROP) && mbox[20] == 32 && mbox[28] != 0) { + mbox[28] &= 0x3FFFFFFF; // Convert GPU address to ARM address + width = mbox[10]; // Actual physical width + height = mbox[11]; // Actual physical height + pitch = mbox[33]; // Number of bytes per line + isrgb = mbox[24]; // Pixel order + fb = (unsigned char *)((long)mbox[28]); + } +} + +void drawPixel(int x, int y, unsigned char attr) +{ + int offs = (y * pitch) + (x * 4); + *((unsigned int*)(fb + offs)) = vgapal[attr & 0x0f]; +} + +void drawRect(int x1, int y1, int x2, int y2, unsigned char attr, int fill) +{ + int y=y1; + + while (y <= y2) { + int x=x1; + while (x <= x2) { + if ((x == x1 || x == x2) || (y == y1 || y == y2)) drawPixel(x, y, attr); + else if (fill) drawPixel(x, y, (attr & 0xf0) >> 4); + x++; + } + y++; + } +} + +void drawLine(int x1, int y1, int x2, int y2, unsigned char attr) +{ + int dx, dy, p, x, y; + + dx = x2-x1; + dy = y2-y1; + x = x1; + y = y1; + p = 2*dy-dx; + + while (x= 0) { + drawPixel(x,y,attr); + y++; + p = p+2*dy-2*dx; + } else { + drawPixel(x,y,attr); + p = p+2*dy; + } + x++; + } +} + +void drawCircle(int x0, int y0, int radius, unsigned char attr, int fill) +{ + int x = radius; + int y = 0; + int err = 0; + + while (x >= y) { + if (fill) { + drawLine(x0 - y, y0 + x, x0 + y, y0 + x, (attr & 0xf0) >> 4); + drawLine(x0 - x, y0 + y, x0 + x, y0 + y, (attr & 0xf0) >> 4); + drawLine(x0 - x, y0 - y, x0 + x, y0 - y, (attr & 0xf0) >> 4); + drawLine(x0 - y, y0 - x, x0 + y, y0 - x, (attr & 0xf0) >> 4); + } + drawPixel(x0 - y, y0 + x, attr); + drawPixel(x0 + y, y0 + x, attr); + drawPixel(x0 - x, y0 + y, attr); + drawPixel(x0 + x, y0 + y, attr); + drawPixel(x0 - x, y0 - y, attr); + drawPixel(x0 + x, y0 - y, attr); + drawPixel(x0 - y, y0 - x, attr); + drawPixel(x0 + y, y0 - x, attr); + + if (err <= 0) { + y += 1; + err += 2*y + 1; + } + + if (err > 0) { + x -= 1; + err -= 2*x + 1; + } + } +} + +void drawChar(unsigned char ch, int x, int y, unsigned char attr, int zoom) +{ + unsigned char *glyph = (unsigned char *)&font + (ch < FONT_NUMGLYPHS ? ch : 0) * FONT_BPG; + + for (int i=1;i<=(FONT_HEIGHT*zoom);i++) { + for (int j=0;j<(FONT_WIDTH*zoom);j++) { + unsigned char mask = 1 << (j/zoom); + unsigned char col = (*glyph & mask) ? attr & 0x0f : (attr & 0xf0) >> 4; + + drawPixel(x+j, y+i, col); + } + glyph += (i%zoom) ? 0 : FONT_BPL; + } +} + +void drawString(int x, int y, char *s, unsigned char attr, int zoom) +{ + while (*s) { + if (*s == '\r') { + x = 0; + } else if(*s == '\n') { + x = 0; y += (FONT_HEIGHT*zoom); + } else { + drawChar(*s, x, y, attr, zoom); + x += (FONT_WIDTH*zoom); + } + s++; + } +} + +void moveRect(int oldx, int oldy, int width, int height, int shiftx, int shifty, unsigned char attr) +{ + unsigned int newx = oldx + shiftx, newy = oldy + shifty; + unsigned int xcount = 0, ycount = 0; + unsigned int bitmap[width][height]; // This is very unsafe if it's too big for the stack... + unsigned int offs; + + // Save the bitmap + while (xcount < width) { + while (ycount < height) { + offs = ((oldy + ycount) * pitch) + ((oldx + xcount) * 4); + + bitmap[xcount][ycount] = *((unsigned int*)(fb + offs)); + ycount++; + } + ycount=0; + xcount++; + } + // Wipe it out with background colour + drawRect(oldx, oldy, oldx + width, oldy + width, attr, 1); + // Draw it again + for (int i=newx;i= 1920) { + curx = 0; cury += 8; + } + if (cury + 8 >= 1080) { + cury = 0; + } + drawString(curx, cury, str, 0x0f, 1); + curx += (strlen(str) * 8); +} + +void debugcrlf(void) { + curx = 0; cury += 8; +} + +void debugch(unsigned char b) { + unsigned int n; + int c; + for(c=4;c>=0;c-=4) { + n=(b>>c)&0xF; + n+=n>9?0x37:0x30; + debugstr((char *)&n); + } + debugstr(" "); +} + +void debughex(unsigned int d) { + unsigned int n; + int c; + for(c=28;c>=0;c-=4) { + n=(d>>c)&0xF; + n+=n>9?0x37:0x30; + debugstr((char *)&n); + } + debugstr(" "); +} diff --git a/part15-tcpip/lib/io.c b/part15-tcpip/lib/io.c new file mode 100644 index 0000000..34a02aa --- /dev/null +++ b/part15-tcpip/lib/io.c @@ -0,0 +1,199 @@ +#include "../include/io.h" + +// GPIO + +enum { + GPFSEL0 = PERIPHERAL_BASE + 0x200000, + GPSET0 = PERIPHERAL_BASE + 0x20001C, + GPCLR0 = PERIPHERAL_BASE + 0x200028, + GPPUPPDN0 = PERIPHERAL_BASE + 0x2000E4 +}; + +enum { + GPIO_MAX_PIN = 53, + GPIO_FUNCTION_OUT = 1, + GPIO_FUNCTION_ALT5 = 2, + GPIO_FUNCTION_ALT3 = 7, + GPIO_FUNCTION_ALT0 = 4 +}; + +enum { + Pull_None = 0, + Pull_Down = 1, // Are down and up the right way around? + Pull_Up = 2 +}; + +void mmio_write(long reg, unsigned int val) { *(volatile unsigned int *)reg = val; } +unsigned int mmio_read(long reg) { return *(volatile unsigned int *)reg; } + +unsigned int gpio_call(unsigned int pin_number, unsigned int value, unsigned int base, unsigned int field_size, unsigned int field_max) { + unsigned int field_mask = (1 << field_size) - 1; + + if (pin_number > field_max) return 0; + if (value > field_mask) return 0; + + unsigned int num_fields = 32 / field_size; + unsigned int reg = base + ((pin_number / num_fields) * 4); + unsigned int shift = (pin_number % num_fields) * field_size; + + unsigned int curval = mmio_read(reg); + curval &= ~(field_mask << shift); + curval |= value << shift; + mmio_write(reg, curval); + + return 1; +} + +unsigned int gpio_set (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPSET0, 1, GPIO_MAX_PIN); } +unsigned int gpio_clear (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPCLR0, 1, GPIO_MAX_PIN); } +unsigned int gpio_pull (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPPUPPDN0, 2, GPIO_MAX_PIN); } +unsigned int gpio_function(unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPFSEL0, 3, GPIO_MAX_PIN); } + +void gpio_useAsAlt0(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_ALT0); +} + +void gpio_useAsAlt3(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_ALT3); +} + +void gpio_useAsAlt5(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_ALT5); +} + +void gpio_initOutputPinWithPullNone(unsigned int pin_number) { + gpio_pull(pin_number, Pull_None); + gpio_function(pin_number, GPIO_FUNCTION_OUT); +} + +void gpio_setPinOutputBool(unsigned int pin_number, unsigned int onOrOff) { + if (onOrOff) { + gpio_set(pin_number, 1); + } else { + gpio_clear(pin_number, 1); + } +} + +// UART + +enum { + AUX_BASE = PERIPHERAL_BASE + 0x215000, + AUX_IRQ = AUX_BASE, + AUX_ENABLES = AUX_BASE + 4, + AUX_MU_IO_REG = AUX_BASE + 64, + AUX_MU_IER_REG = AUX_BASE + 68, + AUX_MU_IIR_REG = AUX_BASE + 72, + AUX_MU_LCR_REG = AUX_BASE + 76, + AUX_MU_MCR_REG = AUX_BASE + 80, + AUX_MU_LSR_REG = AUX_BASE + 84, + AUX_MU_MSR_REG = AUX_BASE + 88, + AUX_MU_SCRATCH = AUX_BASE + 92, + AUX_MU_CNTL_REG = AUX_BASE + 96, + AUX_MU_STAT_REG = AUX_BASE + 100, + AUX_MU_BAUD_REG = AUX_BASE + 104, + AUX_UART_CLOCK = 500000000, + UART_MAX_QUEUE = 16 * 1024 +}; + +#define AUX_MU_BAUD(baud) ((AUX_UART_CLOCK/(baud*8))-1) + +unsigned char uart_output_queue[UART_MAX_QUEUE]; +unsigned int uart_output_queue_write = 0; +unsigned int uart_output_queue_read = 0; + +void uart_init() { + mmio_write(AUX_ENABLES, 1); //enable UART1 + mmio_write(AUX_MU_IER_REG, 0); + mmio_write(AUX_MU_CNTL_REG, 0); + mmio_write(AUX_MU_LCR_REG, 3); //8 bits + mmio_write(AUX_MU_MCR_REG, 0); + mmio_write(AUX_MU_IER_REG, 0); + mmio_write(AUX_MU_IIR_REG, 0xC6); //disable interrupts + mmio_write(AUX_MU_BAUD_REG, AUX_MU_BAUD(115200)); + gpio_useAsAlt5(14); + gpio_useAsAlt5(15); + mmio_write(AUX_MU_CNTL_REG, 3); //enable RX/TX +} + +unsigned int uart_isOutputQueueEmpty() { + return uart_output_queue_read == uart_output_queue_write; +} + +unsigned int uart_isReadByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x01; } +unsigned int uart_isWriteByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x20; } + +unsigned char uart_readByte() { + while (!uart_isReadByteReady()); + return (unsigned char)mmio_read(AUX_MU_IO_REG); +} + +void uart_writeByteBlockingActual(unsigned char ch) { + while (!uart_isWriteByteReady()); + mmio_write(AUX_MU_IO_REG, (unsigned int)ch); +} + +void uart_loadOutputFifo() { + while (!uart_isOutputQueueEmpty() && uart_isWriteByteReady()) { + uart_writeByteBlockingActual(uart_output_queue[uart_output_queue_read]); + uart_output_queue_read = (uart_output_queue_read + 1) & (UART_MAX_QUEUE - 1); // Don't overrun + } +} + +void uart_writeByteBlocking(unsigned char ch) { + unsigned int next = (uart_output_queue_write + 1) & (UART_MAX_QUEUE - 1); // Don't overrun + + while (next == uart_output_queue_read) uart_loadOutputFifo(); + + uart_output_queue[uart_output_queue_write] = ch; + uart_output_queue_write = next; +} + +void uart_writeText(char *buffer) { + while (*buffer) { + if (*buffer == '\n') uart_writeByteBlockingActual('\r'); + uart_writeByteBlockingActual(*buffer++); + } +} + +void uart_drainOutputQueue() { + while (!uart_isOutputQueueEmpty()) uart_loadOutputFifo(); +} + +void uart_update() { + uart_loadOutputFifo(); + + if (uart_isReadByteReady()) { + unsigned char ch = uart_readByte(); + if (ch == '\r') uart_writeText("\n"); else uart_writeByteBlocking(ch); + } +} + +void uart_hex(unsigned int d) { + unsigned int n; + int c; + for(c=28;c>=0;c-=4) { + // get highest tetrad + n=(d>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + + uart_writeByteBlockingActual(n); + } +} + +void uart_byte(unsigned char b) { + unsigned int n; + int c; + for(c=4;c>=0;c-=4) { + // get highest tetrad + n=(b>>c)&0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n+=n>9?0x37:0x30; + + uart_writeByteBlockingActual(n); + } + uart_writeByteBlockingActual(' '); +} diff --git a/part15-tcpip/lib/mb.c b/part15-tcpip/lib/mb.c new file mode 100644 index 0000000..65dbbb8 --- /dev/null +++ b/part15-tcpip/lib/mb.c @@ -0,0 +1,39 @@ +#include "../include/io.h" + +// The buffer must be 16-byte aligned as only the upper 28 bits of the address can be passed via the mailbox +volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +enum { + VIDEOCORE_MBOX = (PERIPHERAL_BASE + 0x0000B880), + MBOX_READ = (VIDEOCORE_MBOX + 0x0), + MBOX_POLL = (VIDEOCORE_MBOX + 0x10), + MBOX_SENDER = (VIDEOCORE_MBOX + 0x14), + MBOX_STATUS = (VIDEOCORE_MBOX + 0x18), + MBOX_CONFIG = (VIDEOCORE_MBOX + 0x1C), + MBOX_WRITE = (VIDEOCORE_MBOX + 0x20), + MBOX_RESPONSE = 0x80000000, + MBOX_FULL = 0x80000000, + MBOX_EMPTY = 0x40000000 +}; + +unsigned int mbox_call(unsigned char ch) +{ + // 28-bit address (MSB) and 4-bit value (LSB) + unsigned int r = ((unsigned int)((long) &mbox) &~ 0xF) | (ch & 0xF); + + // Wait until we can write + while (mmio_read(MBOX_STATUS) & MBOX_FULL); + + // Write the address of our buffer to the mailbox with the channel appended + mmio_write(MBOX_WRITE, r); + + while (1) { + // Is there a reply? + while (mmio_read(MBOX_STATUS) & MBOX_EMPTY); + + // Is it a reply to our message? + if (r == mmio_read(MBOX_READ)) return mbox[1]==MBOX_RESPONSE; // Is it successful? + + } + return 0; +} diff --git a/part15-tcpip/lib/multicore.c b/part15-tcpip/lib/multicore.c new file mode 100644 index 0000000..0000bcd --- /dev/null +++ b/part15-tcpip/lib/multicore.c @@ -0,0 +1,44 @@ +#include "../include/multicore.h" + +void store32(unsigned long address, unsigned long value) +{ + *(unsigned long *) address = value; +} + +unsigned long load32(unsigned long address) +{ + return *(unsigned long *) address; +} + +void start_core1(void (*func)(void)) +{ + store32((unsigned long)&spin_cpu1, (unsigned long)func); + asm volatile ("sev"); +} + +void start_core2(void (*func)(void)) +{ + store32((unsigned long)&spin_cpu2, (unsigned long)func); + asm volatile ("sev"); +} + +void start_core3(void (*func)(void)) +{ + store32((unsigned long)&spin_cpu3, (unsigned long)func); + asm volatile ("sev"); +} + +void clear_core1(void) +{ + store32((unsigned long)&spin_cpu1, 0); +} + +void clear_core2(void) +{ + store32((unsigned long)&spin_cpu2, 0); +} + +void clear_core3(void) +{ + store32((unsigned long)&spin_cpu3, 0); +} diff --git a/part15-tcpip/lib/spi.c b/part15-tcpip/lib/spi.c new file mode 100644 index 0000000..a03d564 --- /dev/null +++ b/part15-tcpip/lib/spi.c @@ -0,0 +1,100 @@ +#include "../include/io.h" +#include "../include/fb.h" + +// SPI + +struct Spi0Regs { + volatile unsigned int cs; + volatile unsigned int fifo; + volatile unsigned int clock; + volatile unsigned int data_length; + volatile unsigned int ltoh; + volatile unsigned int dc; +}; + +#define REGS_SPI0 ((struct Spi0Regs *)(PERIPHERAL_BASE + 0x00204000)) + +// CS Register +#define CS_LEN_LONG (1 << 25) +#define CS_DMA_LEN (1 << 24) +#define CS_CSPOL2 (1 << 23) +#define CS_CSPOL1 (1 << 22) +#define CS_CSPOL0 (1 << 21) +#define CS_RXF (1 << 20) +#define CS_RXR (1 << 19) +#define CS_TXD (1 << 18) +#define CS_RXD (1 << 17) +#define CS_DONE (1 << 16) +#define CS_LEN (1 << 13) +#define CS_REN (1 << 12) +#define CS_ADCS (1 << 11) +#define CS_INTR (1 << 10) +#define CS_INTD (1 << 9) +#define CS_DMAEN (1 << 8) +#define CS_TA (1 << 7) +#define CS_CSPOL (1 << 6) +#define CS_CLEAR_RX (1 << 5) +#define CS_CLEAR_TX (1 << 4) +#define CS_CPOL__SHIFT 3 +#define CS_CPHA__SHIFT 2 +#define CS_CS (1 << 0) +#define CS_CS__SHIFT 0 + +void spi_init() { + gpio_useAsAlt0(7); //CS1 + gpio_initOutputPinWithPullNone(8); //CS0 + gpio_useAsAlt0(9); //MISO + gpio_useAsAlt0(10); //MOSI + gpio_useAsAlt0(11); //SCLK +} + +void spi_chip_select(unsigned char chip_select) { + gpio_setPinOutputBool(8, chip_select); +} + +void spi_send_recv(unsigned char *sbuffer, unsigned char *rbuffer, unsigned int size) { + REGS_SPI0->data_length = size; + REGS_SPI0->cs = REGS_SPI0->cs | CS_CLEAR_RX | CS_CLEAR_TX | CS_TA; + + unsigned int read_count = 0; + unsigned int write_count = 0; + + while(read_count < size || write_count < size) { + while(write_count < size && REGS_SPI0->cs & CS_TXD) { + if (sbuffer) { + REGS_SPI0->fifo = *sbuffer++; + } else { + REGS_SPI0->fifo = 0; + } + + write_count++; + } + + while(read_count < size && REGS_SPI0->cs & CS_RXD) { + unsigned int data = REGS_SPI0->fifo; + + if (rbuffer) { + *rbuffer++ = data; + } + + read_count++; + } + } + + while(!(REGS_SPI0->cs & CS_DONE)) { + while(REGS_SPI0->cs & CS_RXD) { + unsigned int r = REGS_SPI0->fifo; + debughex(r); + } + } + + REGS_SPI0->cs = (REGS_SPI0->cs & ~CS_TA); +} + +void spi_send(unsigned char *data, unsigned int size) { + spi_send_recv(data, 0, size); +} + +void spi_recv(unsigned char *data, unsigned int size) { + spi_send_recv(0, data, size); +} diff --git a/part15-tcpip/net/enc28j60.c b/part15-tcpip/net/enc28j60.c new file mode 100644 index 0000000..9547184 --- /dev/null +++ b/part15-tcpip/net/enc28j60.c @@ -0,0 +1,1450 @@ +/** + ****************************************************************************** + * @file enc28j60.c + * @author Christian Schoffit, portions from Gregory Nutt: + * Copyright (C) 2010-2012, 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * @version V1.0.0 + * @date 02-June-2015 + * @brief This file provides a set of functions needed to manage the ENC28J60 + * Stand-Alone Ethernet Controller with SPI Interface. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 Christian Schoffit

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Christian Schoffit nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/* +Module Feature Issue Issue Summary Affected Revisions + B1 B4 B5 B7 +MAC Interface - 1. MAC registers unreliable with slow asynchronous SPI clock X X +Reset - 2. CLKRDY set early X X X X +Core Operating 3. Industrial (-40°C to +85°C) temperature range unsupported X X + Specifications +Oscillator CLKOUT pin 4. CLKOUT unavailable in Power Save mode X X X X +Memory Ethernet 5. Receive buffer must start at 0000h X X X X + Buffer +Interrupts - 6. Receive Packet Pending Interrupt Flag (PKTIF) unreliable X X X X +PHY - 7. TPIN+/- automatic polarity detection and correction + unreliable X X X X +PHY - 8. RBIAS resistor value differs between silicon revisions X X +PHY - 9. Internal loopback in half-duplex unreliable X X X X +PHY - 10. Internal loopback in full-duplex unreliable X X X X +PHY LEDs - 11. Combined Collision and Duplex Status mode unavailable X X X X +Transmit - 12. Transmit abort may stall transmit logic X X X X +Logic +PHY - 13. Received link pulses potentially cause collisions X X +Memory Ethernet 14. Even values in ERXRDPT may corrupt receive buffer X X X X + Buffer +Transmit - 15. LATECOL Status bit unreliable X X X X +Logic +PHY LEDs - 16. LED auto-polarity detection unreliable X X X X +DMA - 17. DMA checksum calculations will abort receive packets X X X X +Receive - 18. Pattern match filter allows reception of extra packets X X X X +Filter +SPI - 19. Reset command unavailable in Power Save mode X X X X +Interface + +Only workaround relative to issues affecting B7 silicon revision are implemented. Therefore, issues +specific to Ethernet conformance are not addressed, since they only affect B1 and B3 silicon revisions. + +Erratas 7, 8, 16... have workaround implemented by hardware + +Errata 18 is implemented in lwip stack +*/ + +/* Includes ------------------------------------------------------------------*/ +#include "enc28j60.h" +#include "../include/fb.h" + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup Components + * @{ + */ + +/** @defgroup ENC28J60 + * @{ + */ + +/** @defgroup ENC28J60_Private_Types_Definitions + * @{ + */ + +/** @defgroup ENC28J60_Private_Defines + * @{ + */ + +/* Poll timeout */ + +#define ENC_POLLTIMEOUT 50 + +/** + * @} + */ + +/** @defgroup ENC28J60_Private_Macros + * @{ + */ + +/* Packet Memory ************************************************************/ + +/* Packet memory layout */ + +#define ALIGNED_BUFSIZE ((CONFIG_NET_ETH_MTU + 255) & ~255) + +/* Work around Errata #5 (spurious reset of ERXWRPT to 0) by placing the RX + * FIFO at the beginning of packet memory. + */ + +# define PKTMEM_RX_START 0x0000 /* RX buffer must be at addr 0 for errata 5 */ +# define PKTMEM_RX_END (PKTMEM_END-ALIGNED_BUFSIZE) /* RX buffer length is total SRAM minus TX buffer */ +# define PKTMEM_TX_START (PKTMEM_RX_END+1) /* Start TX buffer after */ +# define PKTMEM_TX_ENDP1 (PKTMEM_TX_START+ALIGNED_BUFSIZE) /* Allow TX buffer for two frames */ + +/* Misc. Helper Macros ******************************************************/ + +#define enc_rdgreg(ctrlreg) \ + enc_rdgreg2(ENC_RCR | GETADDR(ctrlreg)) +#define enc_wrgreg(ctrlreg, wrdata) \ + enc_wrgreg2(ENC_WCR | GETADDR(ctrlreg), wrdata) +#define enc_bfcgreg(ctrlreg,clrbits) \ + enc_wrgreg2(ENC_BFC | GETADDR(ctrlreg), clrbits) +#define enc_bfsgreg(ctrlreg,setbits) \ + enc_wrgreg2(ENC_BFS | GETADDR(ctrlreg), setbits) + +/** + * @} + */ + +/** @defgroup ENC28J60_Private_Variables + * @{ + */ + + /* Stores how many iterations the microcontroller can do in 1 µs */ +static uint32_t iter_per_us=0; + +/** + * @} + */ + +/** @defgroup ENC28J60_Private_Function_Prototypes + * @{ + */ +/** + * @} + */ + +/** @defgroup ENC28J60_Private_Functions + * @{ + */ + +/** + Calibrate the constant time + **/ + +static void calibrate(void) +{ + uint32_t time; + volatile uint32_t i; + + iter_per_us = 1000000; + + time = HAL_GetTick(); + /* Wait for next tick */ + while (HAL_GetTick() == time) { + /* wait */ + } + for (i=0; ibank = 0; /* Initialize the trace on the current selected bank */ + //up_mdelay(2); + HAL_Delay(2); /* >1000 µs, conforms to errata #2 */ +} + +/**************************************************************************** + * Function: enc_setbank + * + * Description: + * Set the bank for these next control register access. + * + * Assumption: + * The caller has exclusive access to the SPI bus + * + * Parameters: + * handle - Reference to the driver state structure + * bank - The bank to select (0-3) + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void enc_setbank(ENC_HandleTypeDef *handle, uint8_t bank) { + + if (bank != handle->bank) { + /* Select bank 0 (just so that all of the bits are cleared) */ + + enc_bfcgreg(ENC_ECON1, ECON1_BSEL_MASK); + + /* Then OR in bits to get the correct bank */ + + if (bank != 0) + { + enc_bfsgreg(ENC_ECON1, (bank << ECON1_BSEL_SHIFT)); + } + + /* Then remember the bank setting */ + + handle->bank = bank; + } +} + +/**************************************************************************** + * Function: enc_rdbreg + * + * Description: + * Read from a banked control register using the RCR command. + * + * Parameters: + * handle - Reference to the driver state structure + * ctrlreg - Bit encoded address of banked register to read + * + * Returned Value: + * The byte read from the banked register + * + * Assumptions: + * + ****************************************************************************/ + +static uint8_t enc_rdbreg(ENC_HandleTypeDef *handle, uint8_t ctrlreg) +{ + uint8_t data[3]; + + /* Set the bank */ + + enc_setbank(handle, GETBANK(ctrlreg)); + + /* Send the RCR command and collect the data. How we collect the data + * depends on if this is a PHY/CAN or not. The normal sequence requires + * 16-clocks: 8 to clock out the cmd and 8 to clock in the data. + */ + + data[0] = ENC_RCR | GETADDR(ctrlreg); + + /* The PHY/MAC sequence requires 24-clocks: 8 to clock out the cmd, + * 8 dummy bits, and 8 to clock in the PHY/MAC data. + */ + + ENC_SPI_SendBuf(data, data, (ISPHYMAC(ctrlreg))?3:2); + return (ISPHYMAC(ctrlreg))?data[2]:data[1]; +} + +/**************************************************************************** + * Function: enc_wrbreg + * + * Description: + * Write to a banked control register using the WCR command. Unlike + * reading, this same SPI sequence works for normal, MAC, and PHY + * registers. + * + * Parameters: + * handle - Reference to the driver state structure + * ctrlreg - Bit encoded address of banked register to write + * wrdata - The data to send + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void enc_wrbreg(ENC_HandleTypeDef *handle, uint8_t ctrlreg, + uint8_t wrdata) +{ + uint8_t data[2]; + + /* Set the bank */ + + enc_setbank(handle, GETBANK(ctrlreg)); + + /* Send the WCR command and data. The sequence requires 16-clocks: + * 8 to clock out the cmd + 8 to clock out the data. + */ + + data[0] = ENC_WCR | GETADDR(ctrlreg); + data[1] = wrdata; + + ENC_SPI_SendBuf(data, NULL, 2); +} + +/**************************************************************************** + * Function: enc_waitbreg + * + * Description: + * Wait until banked register bit(s) take a specific value (or a timeout + * occurs). + * + * Parameters: + * handle - Reference to the driver state structure + * ctrlreg - Bit encoded address of banked register to check + * bits - The bits to check (a mask) + * value - The value of the bits to return (value under mask) + * + * Returned Value: + * OK on success, negated errno on failure + * + * Assumptions: + * + ****************************************************************************/ + +static bool enc_waitbreg(ENC_HandleTypeDef *handle, uint8_t ctrlreg, + uint8_t bits, uint8_t value) +{ + uint32_t start = HAL_GetTick(); + uint32_t elapsed; + uint8_t rddata; + + /* Loop until the exit condition is met */ + + do + { + /* Read the byte from the requested banked register */ + + rddata = enc_rdbreg(handle, ctrlreg); + elapsed = HAL_GetTick() - start; + } + while ((rddata & bits) != value && elapsed < ENC_POLLTIMEOUT); + + return (rddata & bits) == value; +} + + +/**************************************************************************** + * Function: enc_rdphy + * + * Description: + * Read 16-bits of PHY data. + * + * Parameters: + * priv - Reference to the driver state structure + * phyaddr - The PHY register address + * + * Returned Value: + * 16-bit value read from the PHY + * + * Assumptions: + * + ****************************************************************************/ + +static uint16_t enc_rdphy(ENC_HandleTypeDef *handle, uint8_t phyaddr) +{ + uint16_t data = 0; + + /* "To read from a PHY register: + * + * 1. Write the address of the PHY register to read from into the MIREGADR + * register. + */ + + enc_wrbreg(handle, ENC_MIREGADR, phyaddr); + + /* 2. Set the MICMD.MIIRD bit. The read operation begins and the + * MISTAT.BUSY bit is set. + */ + + enc_wrbreg(handle, ENC_MICMD, MICMD_MIIRD); + + /* 3. Wait 10.24 µs. Poll the MISTAT.BUSY bit to be certain that the + * operation is complete. While busy, the host controller should not + * start any MIISCAN operations or write to the MIWRH register. + * + * When the MAC has obtained the register contents, the BUSY bit will + * clear itself. + */ + +// volatile int i; +// for (i=0; i<12*17; i++) { +// } + + up_udelay(12); + + if (enc_waitbreg(handle, ENC_MISTAT, MISTAT_BUSY, 0x00)) + { + /* 4. Clear the MICMD.MIIRD bit. */ + + enc_wrbreg(handle, ENC_MICMD, 0x00); + + /* 5. Read the desired data from the MIRDL and MIRDH registers. The + * order that these bytes are accessed is unimportant." + */ + + data = (uint16_t)enc_rdbreg(handle, ENC_MIRDL); + data |= (uint16_t)enc_rdbreg(handle, ENC_MIRDH) << 8; + } + + return data; +} + +/**************************************************************************** + * Function: enc_wrphy + * + * Description: + * write 16-bits of PHY data. + * + * Parameters: + * handle - Reference to the driver state structure + * phyaddr - The PHY register address + * phydata - 16-bit data to write to the PHY + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void enc_wrphy(ENC_HandleTypeDef *handle, uint8_t phyaddr, + uint16_t phydata) +{ + /* "To write to a PHY register: + * + * 1. Write the address of the PHY register to write to into the + * MIREGADR register. + */ + + enc_wrbreg(handle, ENC_MIREGADR, phyaddr); + + /* 2. Write the lower 8 bits of data to write into the MIWRL register. */ + + enc_wrbreg(handle, ENC_MIWRL, phydata); + + /* 3. Write the upper 8 bits of data to write into the MIWRH register. + * Writing to this register automatically begins the MIIM transaction, + * so it must be written to after MIWRL. The MISTAT.BUSY bit becomes + * set. + */ + + enc_wrbreg(handle, ENC_MIWRH, phydata >> 8); + + /* The PHY register will be written after the MIIM operation completes, + * which takes 10.24 µs. When the write operation has completed, the BUSY + * bit will clear itself. + * + * The host controller should not start any MIISCAN or MIIRD operations + * while busy." + */ + + /* wait for approx 12 µs */ +// volatile int i; +// for (i=0; i<12*17; i++) { +// } + + up_udelay(12); + enc_waitbreg(handle, ENC_MISTAT, MISTAT_BUSY, 0x00); +} + + +/**************************************************************************** + * Function: enc_pwrfull + * + * Description: + * When normal operation is desired, the host controller must perform + * a slightly modified procedure: + * + * 1. Wake-up by clearing ECON2.PWRSV. + * 2. Wait at least 300 µs for the PHY to stabilize. To accomplish the + * delay, the host controller may poll ESTAT.CLKRDY and wait for it + * to become set. + * 3. Restore receive capability by setting ECON1.RXEN. + * + * After leaving Sleep mode, there is a delay of many milliseconds + * before a new link is established (assuming an appropriate link + * partner is present). The host controller may wish to wait until + * the link is established before attempting to transmit any packets. + * The link status can be determined by polling the PHSTAT2.LSTAT bit. + * Alternatively, the link change interrupt may be used if it is + * enabled. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +/* Power save mode not used (fix errata 4 and 19) */ +#if 0 +static void enc_pwrfull(ENC_HandleTypeDef *handle) +{ + /* 1. Wake-up by clearing ECON2.PWRSV. */ + + enc_bfcgreg(ENC_ECON2, ECON2_PWRSV); + + /* 2. Wait at least 300 µs for the PHY to stabilize. To accomplish the + * delay, the host controller may poll ESTAT.CLKRDY and wait for it to + * become set. + */ + + /* wait for approx 350 µs */ +// volatile int i; +// for (i=0; i<350*17; i++) { +// } + + up_udelay(350); + + enc_waitbreg(handle, ENC_ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY); + + /* 3. Restore receive capability by setting ECON1.RXEN. + * + * The caller will do this when it is ready to receive packets + */ +} +#endif + +/** + * @brief Initialize the enc28j60 and configure the needed hardware resources + * @param handle: Handle on data configuration. + * @retval None + */ +bool ENC_Start(ENC_HandleTypeDef *handle) +{ + /* register value */ + uint8_t regval; + + /* Calibrate time constant */ + calibrate(); + + /* System reset */ + enc_reset(handle); + + /* Use bank 0 */ + enc_setbank(handle, 0); + + /* Check if we are actually communicating with the ENC28J60. If its + * 0x00 or 0xff, then we are probably not communicating correctly + * via SPI. + */ + + regval = enc_rdbreg(handle, ENC_EREVID); + if (regval == 0x00 || regval == 0xff) { + return false; + } + + /* Initialize ECON2: Enable address auto increment. + */ + + enc_wrgreg(ENC_ECON2, ECON2_AUTOINC /* | ECON2_VRPS*/); + + /* Initialize receive buffer. + * First, set the receive buffer start address. + */ + + handle->nextpkt = PKTMEM_RX_START; + enc_wrbreg(handle, ENC_ERXSTL, PKTMEM_RX_START & 0xff); + enc_wrbreg(handle, ENC_ERXSTH, PKTMEM_RX_START >> 8); + + /* Set the receive data pointer */ + + /* Errata 14 */ + enc_wrbreg(handle, ENC_ERXRDPTL, PKTMEM_RX_END & 0xff); + enc_wrbreg(handle, ENC_ERXRDPTH, PKTMEM_RX_END >> 8); +/* + enc_wrbreg(handle, ENC_ERXRDPTL, PKTMEM_RX_START & 0xff); + enc_wrbreg(handle, ENC_ERXRDPTH, PKTMEM_RX_START >> 8); +*/ + + /* Set the receive buffer end. */ + + enc_wrbreg(handle, ENC_ERXNDL, PKTMEM_RX_END & 0xff); + enc_wrbreg(handle, ENC_ERXNDH, PKTMEM_RX_END >> 8); + + /* Set transmit buffer start. */ + + handle->transmitLength = 0; + enc_wrbreg(handle, ENC_ETXSTL, PKTMEM_TX_START & 0xff); + enc_wrbreg(handle, ENC_ETXSTH, PKTMEM_TX_START >> 8); + + /* Set filter mode: unicast OR broadcast AND crc valid */ + + enc_wrbreg(handle, ENC_ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_BCEN); + + do { + HAL_Delay(10); /* Wait for 10 ms to let the clock be ready */ + regval = enc_rdbreg(handle, ENC_ESTAT); + } while ((regval & ESTAT_CLKRDY) == 0); + + /* Enable MAC receive */ + + enc_wrbreg(handle, ENC_MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS); + + /* Enable automatic padding and CRC operations */ + + if (handle->Init.DuplexMode == ETH_MODE_HALFDUPLEX) { + enc_wrbreg(handle, ENC_MACON3, + ((handle->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE)?MACON3_PADCFG0 | MACON3_TXCRCEN:0) | + MACON3_FRMLNEN); + enc_wrbreg(handle, ENC_MACON4, MACON4_DEFER); /* Defer transmission enable */ + + /* Set Non-Back-to-Back Inter-Packet Gap */ + + enc_wrbreg(handle, ENC_MAIPGL, 0x12); + enc_wrbreg(handle, ENC_MAIPGH, 0x0c); + + /* Set Back-to-Back Inter-Packet Gap */ + + enc_wrbreg(handle, ENC_MABBIPG, 0x12); + } else { + /* Set filter mode: unicast OR broadcast AND crc valid AND Full Duplex */ + + enc_wrbreg(handle, ENC_MACON3, + ((handle->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE)?MACON3_PADCFG0 | MACON3_TXCRCEN:0) | + MACON3_FRMLNEN | MACON3_FULDPX); + + /* Set Non-Back-to-Back Inter-Packet Gap */ + + enc_wrbreg(handle, ENC_MAIPGL, 0x12); + + /* Set Back-to-Back Inter-Packet Gap */ + + enc_wrbreg(handle, ENC_MABBIPG, 0x15); + } + + /* Set the maximum packet size which the controller will accept */ + + enc_wrbreg(handle, ENC_MAMXFLL, (CONFIG_NET_ETH_MTU+18) & 0xff); + enc_wrbreg(handle, ENC_MAMXFLH, (CONFIG_NET_ETH_MTU+18) >> 8); + + /* Configure LEDs (No, just use the defaults for now) */ + /* enc_wrphy(priv, ENC_PHLCON, ??); */ + + /* Setup up PHCON1 & 2 */ + + if (handle->Init.DuplexMode == ETH_MODE_HALFDUPLEX) { + enc_wrphy(handle, ENC_PHCON1, 0x00); + enc_wrphy(handle, ENC_PHCON2, PHCON2_HDLDIS); /* errata 9 workaround */ + } else { + enc_wrphy(handle, ENC_PHCON1, PHCON1_PDPXMD); /* errata 10 workaround */ + enc_wrphy(handle, ENC_PHCON2, 0x00); + } + + /* Not used Restore normal operation mode + enc_pwrfull(handle); */ + + /* Process interrupt settings */ + if (handle->Init.InterruptEnableBits & EIE_LINKIE) { + /* Enable link change interrupt in PHY module */ + enc_wrphy(handle, ENC_PHIE, PHIE_PGEIE | PHIE_PLNKIE); + } + + /* Since we not modify PHLCON register, we don't fall in errata 11 case */ + + /* Reset all interrupt flags */ + enc_bfcgreg(ENC_EIR, EIR_ALLINTS); + + regval = handle->Init.InterruptEnableBits; + if (regval) { + /* Ensure INTIE is set when at least an interruption is selected */ + regval |= EIE_INTIE; + } + /* Enable selected interrupts in ethernet controller module */ + enc_bfsgreg(ENC_EIE, regval); + + /* Enable the receiver */ + enc_bfsgreg(ENC_ECON1, ECON1_RXEN); + + return true; +} + +/** + * @} + */ + +/**************************************************************************** + * Function: ENC_SetMacAddr + * + * Description: + * Set the MAC address to the configured value. This is done after ifup + * or after a TX timeout. Note that this means that the interface must + * be down before configuring the MAC addr. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_SetMacAddr(ENC_HandleTypeDef *handle) +{ + /* Program the hardware with it's MAC address (for filtering). + * MAADR1 MAC Address Byte 1 (MAADR<47:40>), OUI Byte 1 + * MAADR2 MAC Address Byte 2 (MAADR<39:32>), OUI Byte 2 + * MAADR3 MAC Address Byte 3 (MAADR<31:24>), OUI Byte 3 + * MAADR4 MAC Address Byte 4 (MAADR<23:16>) + * MAADR5 MAC Address Byte 5 (MAADR<15:8>) + * MAADR6 MAC Address Byte 6 (MAADR<7:0>) + */ + + enc_wrbreg(handle, ENC_MAADR1, handle->Init.MACAddr[0]); + enc_wrbreg(handle, ENC_MAADR2, handle->Init.MACAddr[1]); + enc_wrbreg(handle, ENC_MAADR3, handle->Init.MACAddr[2]); + enc_wrbreg(handle, ENC_MAADR4, handle->Init.MACAddr[3]); + enc_wrbreg(handle, ENC_MAADR5, handle->Init.MACAddr[4]); + enc_wrbreg(handle, ENC_MAADR6, handle->Init.MACAddr[5]); +} + + +/**************************************************************************** + * Function: ENC_WriteBuffer + * + * Description: + * Write a buffer of data. + * + * Parameters: + * buffer - A pointer to the buffer to write from + * buflen - The number of bytes to write + * + * Returned Value: + * None + * + * Assumptions: + * Read pointer is set to the correct address + * + ****************************************************************************/ + +void ENC_WriteBuffer(void *buffer, uint16_t buflen) +{ + /* Send the WBM command and copy the packet itself into the transmit + * buffer at the position of the EWRPT register. + */ + + /* Select ENC28J60 chip + * + * "The WBM command is started by lowering the CS pin. ..." + * We explicitly select the ENC28J60 chip because we have to transmits several pieces of + * information while keeping CS low + * + */ + + ENC_SPI_Select(true); + + /* Send the write buffer memory command (ignoring the response) + * + * "...The [3-bit]WBM opcode should then be sent to the ENC28J60, + * followed by the 5-bit constant, 1Ah." + */ + + + ENC_SPI_SendWithoutSelection(ENC_WBM); + + /* Send the buffer + * + * "... After the WBM command and constant are sent, the data to + * be stored in the memory pointed to by EWRPT should be shifted + * out MSb first to the ENC28J60. After 8 data bits are received, + * the Write Pointer will automatically increment if AUTOINC is + * set. The host controller can continue to provide clocks on the + * SCK pin and send data on the SI pin, without raising CS, to + * keep writing to the memory. In this manner, with AUTOINC + * enabled, it is possible to continuously write sequential bytes + * to the buffer memory without any extra SPI command + * overhead. + */ + + ENC_SPI_SendBuf(buffer, NULL, buflen); + + /* De-select ENC28J60 chip + * + * "The WBM command is terminated by bringing up the CS pin. ..." + * done in ENC_SPI_SendBuf callback + */ + +} + +/**************************************************************************** + * Function: enc_rdbuffer + * + * Description: + * Read a buffer of data. + * + * Parameters: + * buffer - A pointer to the buffer to read into + * buflen - The number of bytes to read + * + * Returned Value: + * None + * + * Assumptions: + * Read pointer is set to the correct address + * + ****************************************************************************/ + +static void enc_rdbuffer(void *buffer, int16_t buflen) +{ + /* Select ENC28J60 chip */ + + ENC_SPI_Select(true); + + /* Send the read buffer memory command (ignoring the response) */ + + ENC_SPI_SendWithoutSelection(ENC_RBM); + + /* Then read the buffer data */ + + ENC_SPI_SendBuf(NULL, buffer, buflen); + + /* De-select ENC28J60 chip: done in ENC_SPI_SendBuf callback */ +} + +/**************************************************************************** + * Function: ENC_RestoreTXBuffer + * + * Description: + * Prepare TX buffer + * + * Parameters: + * handle - Reference to the driver state structure + * len - length of buffer + * + * Returned Value: + * ERR_OK 0 No error, everything OK. + * ERR_MEM -1 Out of memory error. + * ERR_TIMEOUT -3 Timeout. + * + * Assumptions: + * + ****************************************************************************/ + +int8_t ENC_RestoreTXBuffer(ENC_HandleTypeDef *handle, uint16_t len) +{ + uint16_t txend; + uint8_t control_write[2]; + + /* Wait while TX is busy */ + if (!enc_waitgreg(ENC_ECON1, ECON1_TXRTS, 0)) { + return ERR_TIMEOUT; + } + + /* Verify that the hardware is ready to send another packet. The driver + * starts a transmission process by setting ECON1.TXRTS. When the packet is + * finished transmitting or is aborted due to an error/cancellation, the + * ECON1.TXRTS bit will be cleared. + * + * NOTE: If we got here, then we have committed to sending a packet. + * higher level logic must have assured that TX-related interrupts are disabled. + */ + + /* Send the packet: address=priv->dev.d_buf, length=priv->dev.d_len */ + + /* Set transmit buffer start (is this necessary?). */ + + enc_wrbreg(handle, ENC_ETXSTL, PKTMEM_TX_START & 0xff); + enc_wrbreg(handle, ENC_ETXSTH, PKTMEM_TX_START >> 8); + + /* Reset the write pointer to start of transmit buffer */ + + enc_wrbreg(handle, ENC_EWRPTL, PKTMEM_TX_START & 0xff); + enc_wrbreg(handle, ENC_EWRPTH, PKTMEM_TX_START >> 8); + + /* Set the TX End pointer based on the size of the packet to send. Note + * that the offset accounts for the control byte at the beginning the + * buffer plus the size of the packet data. + */ + + txend = PKTMEM_TX_START + len; + + if (txend+8>PKTMEM_TX_ENDP1) { + return ERR_MEM; + } + + enc_wrbreg(handle, ENC_ETXNDL, txend & 0xff); + enc_wrbreg(handle, ENC_ETXNDH, txend >> 8); + + /* Send the write buffer memory command (ignoring the response) + * + * "...The [3-bit]WBM opcode should then be sent to the ENC28J60, + * followed by the 5-bit constant, 1Ah." + * + * "...the ENC28J60 requires a single per packet control byte to + * precede the packet for transmission." + * + * POVERRIDE: Per Packet Override bit (Not set): + * 1 = The values of PCRCEN, PPADEN and PHUGEEN will override the + * configuration defined by MACON3. + * 0 = The values in MACON3 will be used to determine how the packet + * will be transmitted + * PCRCEN: Per Packet CRC Enable bit (Set, but won't be used because + * POVERRIDE is zero). + * PPADEN: Per Packet Padding Enable bit (Set, but won't be used because + * POVERRIDE is zero). + * PHUGEEN: Per Packet Huge Frame Enable bit (Set, but won't be used + * because POVERRIDE is zero). + */ + + control_write[0] = ENC_WBM; + control_write[1] = PKTCTRL_PCRCEN | PKTCTRL_PPADEN | PKTCTRL_PHUGEEN; + ENC_SPI_SendBuf(control_write, control_write, 2); + + return ERR_OK; +} + +/**************************************************************************** + * Function: ENC_Transmit + * + * Description: + * Start hardware transmission. Called either from: + * + * - pkif interrupt when an application responds to the receipt of data + * by trying to send something, or + * - From watchdog based polling. + * + * Parameters: + * handle - Reference to the driver state structure + * len - length of buffer + * + * Returned Value: + * none + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef USE_PROTOTHREADS +PT_THREAD(ENC_Transmit(struct pt *pt, ENC_HandleTypeDef *handle)) +#else +void ENC_Transmit(ENC_HandleTypeDef *handle) +#endif +{ + PT_BEGIN(pt); + + if (handle->transmitLength != 0) { + /* A frame is ready for transmission */ + /* Set TXRTS to send the packet in the transmit buffer */ + + //enc_bfsgreg(ENC_ECON1, ECON1_TXRTS); + /* Implement erratas 12, 13 and 15 */ + /* Reset transmit logic */ + handle->retries = 16; + do { + enc_bfsgreg(ENC_ECON1, ECON1_TXRST); + enc_bfcgreg(ENC_ECON1, ECON1_TXRST); + enc_bfcgreg(ENC_EIR, EIR_TXERIF | EIR_TXIF); + + /* Start transmission */ + enc_bfsgreg(ENC_ECON1, ECON1_TXRTS); + +#ifdef USE_PROTOTHREADS + handle->startTime = HAL_GetTick(); + handle->duration = 20; /* Timeout after 20 ms */ + PT_WAIT_UNTIL(pt, (((enc_rdgreg(ENC_EIR) & (EIR_TXIF | EIR_TXERIF)) != 0) || + (HAL_GetTick() - handle->startTime > handle->duration))); +#else + /* Wait for end of transmission */ + enc_waitwhilegreg(ENC_EIR, EIR_TXIF | EIR_TXERIF, 0); +#endif + + /* Stop transmission */ + enc_bfcgreg(ENC_ECON1, ECON1_TXRTS); + + { + uint16_t addtTsv4; + uint8_t tsv4, regval; + + /* read tsv */ + addtTsv4 = PKTMEM_TX_START + handle->transmitLength + 4; + + enc_wrbreg(handle, ENC_ERDPTL, addtTsv4 & 0xff); + enc_wrbreg(handle, ENC_ERDPTH, addtTsv4 >> 8); + + enc_rdbuffer(&tsv4, 1); + + regval = enc_rdgreg(ENC_EIR); + if (!(regval & EIR_TXERIF) || !(tsv4 & TSV_LATECOL)) { + break; + } + } + handle->retries--; + } while (handle->retries > 0); + /* Transmission finished (but can be unsuccessful) */ + handle->transmitLength = 0; + } + PT_END(pt); +} + +/**************************************************************************** + * Function: ENC_GetReceivedFrame + * + * Description: + * Check if we have received packet, and if so, retrieve them. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * true if new packet is available; false otherwise + * + * Assumptions: + * + ****************************************************************************/ + +bool ENC_GetReceivedFrame(ENC_HandleTypeDef *handle) +{ + uint8_t rsv[6]; + uint16_t pktlen; + uint16_t rxstat; + + uint8_t pktcnt; + + bool result = true; + + pktcnt = enc_rdbreg(handle, ENC_EPKTCNT); + if (pktcnt == 0) { + return false; + }; + + /* Set the read pointer to the start of the received packet (ERDPT) */ + + enc_wrbreg(handle, ENC_ERDPTL, (handle->nextpkt) & 0xff); + enc_wrbreg(handle, ENC_ERDPTH, (handle->nextpkt) >> 8); + + /* Read the next packet pointer and the 4 byte read status vector (RSV) + * at the beginning of the received packet. (ERDPT should auto-increment + * and wrap to the beginning of the read buffer as necessary) + */ + + enc_rdbuffer(rsv, 6); + + /* Decode the new next packet pointer, and the RSV. The + * RSV is encoded as: + * + * Bits 0-15: Indicates length of the received frame. This includes the + * destination address, source address, type/length, data, + * padding and CRC fields. This field is stored in little- + * endian format. + * Bits 16-31: Bit encoded RX status. + */ + + handle->nextpkt = (uint16_t)rsv[1] << 8 | (uint16_t)rsv[0]; + pktlen = (uint16_t)rsv[3] << 8 | (uint16_t)rsv[2]; + rxstat = (uint16_t)rsv[5] << 8 | (uint16_t)rsv[4]; + + /* Check if the packet was received OK */ + + if ((rxstat & RXSTAT_OK) == 0) { +#ifdef CONFIG_ENC28J60_STATS + priv->stats.rxnotok++; +#endif + result = false; + } else { /* Check for a usable packet length (4 added for the CRC) */ + if (pktlen > (CONFIG_NET_ETH_MTU + 4) || pktlen <= (ETH_HDRLEN + 4)) { + #ifdef CONFIG_ENC28J60_STATS + priv->stats.rxpktlen++; + #endif + result = false; + } else { /* Otherwise, read and process the packet */ + /* Save the packet length (without the 4 byte CRC) in handle->RxFrameInfos.length*/ + + handle->RxFrameInfos.length = pktlen - 4; + + /* Copy the data data from the receive buffer to priv->dev.d_buf. + * ERDPT should be correctly positioned from the last call to to + * end_rdbuffer (above). + */ + + enc_rdbuffer(handle->RxFrameInfos.buffer, handle->RxFrameInfos.length); + + } + } + + /* Move the RX read pointer to the start of the next received packet. + * This frees the memory we just read. + */ + + /* Errata 14 (on se sert de rxstat comme variable temporaire */ + rxstat = handle->nextpkt; + if (rxstat == PKTMEM_RX_START) { + rxstat = PKTMEM_RX_END; + } else { + rxstat--; + } + enc_wrbreg(handle, ENC_ERXRDPTL, rxstat & 0xff); + enc_wrbreg(handle, ENC_ERXRDPTH, rxstat >> 8); +/* + enc_wrbreg(handle, ENC_ERXRDPTL, (handle->nextpkt)); + enc_wrbreg(handle, ENC_ERXRDPTH, (handle->nextpkt) >> 8); +*/ + + /* Decrement the packet counter indicate we are done with this packet */ + + enc_bfsgreg(ENC_ECON2, ECON2_PKTDEC); + + return result; +} + +/**************************************************************************** + * Function: enc_linkstatus + * + * Description: + * The current link status can be obtained from the PHSTAT1.LLSTAT or + * PHSTAT2.LSTAT. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void enc_linkstatus(ENC_HandleTypeDef *handle) +{ + handle->LinkStatus = enc_rdphy(handle, ENC_PHSTAT2); +} + +/**************************************************************************** + * Function: ENC_EnableInterrupts + * + * Description: + * Enable individual ENC28J60 interrupts + * + * Parameters: + * bits - The individual bits to enable + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_EnableInterrupts(uint8_t bits) +{ + enc_bfsgreg(ENC_EIE, bits); +} + + +/**************************************************************************** + * Function: ENC_IRQHandler + * + * Description: + * Perform interrupt handling logic outside of the interrupt handler (on + * the work queue thread). + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_IRQHandler(ENC_HandleTypeDef *handle) +{ + uint8_t eir; + + /* Disable further interrupts by clearing the global interrupt enable bit. + * "After an interrupt occurs, the host controller should clear the global + * enable bit for the interrupt pin before servicing the interrupt. Clearing + * the enable bit will cause the interrupt pin to return to the non-asserted + * state (high). Doing so will prevent the host controller from missing a + * falling edge should another interrupt occur while the immediate interrupt + * is being serviced." + */ + + enc_bfcgreg(ENC_EIE, EIE_INTIE); + + /* Read EIR for interrupt flags + */ + + eir = enc_rdgreg(ENC_EIR) & EIR_ALLINTS; + + /* PKTIF is not reliable, check PKCNT instead */ + if (enc_rdbreg(handle, ENC_EPKTCNT) != 0) { + /* Manage EIR_PKTIF by software */ + eir |= EIR_PKTIF; + } + + /* Store interrupt flags in handle */ + handle->interruptFlags = eir; + + /* If link status has changed, read it */ + if ((eir & EIR_LINKIF) != 0) /* Link change interrupt */ + { + enc_linkstatus(handle); /* Get current link status */ + enc_rdphy(handle, ENC_PHIR); /* Clear the LINKIF interrupt */ + } + + /* Reset ENC28J60 interrupt flags, except PKTIF form which interruption is deasserted when PKTCNT reaches 0 */ + enc_bfcgreg(ENC_EIR, EIR_ALLINTS); + + /* Enable Ethernet interrupts */ + /* done after effective process on interrupts enc_bfsgreg(ENC_EIE, EIE_INTIE); */ +} + +/**************************************************************************** + * Function: ENC_GetPkcnt + * + * Description: + * Get the number of pending receive packets + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * the number of receive packet not processed yet + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_GetPkcnt(ENC_HandleTypeDef *handle) +{ + handle->pktCnt = enc_rdbreg(handle, ENC_EPKTCNT); +} + + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/part15-tcpip/net/enc28j60.h b/part15-tcpip/net/enc28j60.h new file mode 100644 index 0000000..dc77865 --- /dev/null +++ b/part15-tcpip/net/enc28j60.h @@ -0,0 +1,816 @@ +/** + ****************************************************************************** + * @file enc28j60.h + * @author Christian Schoffit, portions from Gregory Nutt: + * Copyright (C) 2010-2012, 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * @version V1.0.0 + * @date 02-June-2015 + * @brief This file provides a set of functions needed to manage the ENC28J60 + * Stand-Alone Ethernet Controller with SPI Interface. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 Christian Schoffit

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Christian Schoffit nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +#ifndef ENC28J60_H_INCLUDED +#define ENC28J60_H_INCLUDED + +#include +#include + +#ifdef USE_PROTOTHREADS +#include "pt.h" +#else +#define PT_BEGIN(x) +#define PT_END(x) +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +/* Ethernet frames are between 64 and 1518 bytes long */ + +#define MIN_FRAMELEN 64 +#define MAX_FRAMELEN 1518 + + +/* External functions --------------------------------------------------------*/ +void HAL_Delay(volatile uint32_t Delay); +uint32_t HAL_GetTick(void); + +/* Callback functions *********************************************************/ + +/** + * Implement SPI Slave selection and deselection. Must be provided by user code + * param select: true if the ENC28J60 slave SPI if selected, false otherwise + * retval none + */ + +void ENC_SPI_Select(bool select); + +/** + * Implement SPI single byte send and receive. + * The ENC28J60 slave SPI must already be selected and wont be deselected after transmission + * Must be provided by user code + * param command: command or data to be sent to ENC28J60 + * retval answer from ENC28J60 + */ + +void ENC_SPI_SendWithoutSelection(uint8_t command); + +/** + * Implement SPI single byte send and receive. Must be provided by user code + * param command: command or data to be sent to ENC28J60 + * retval answer from ENC28J60 + */ + +void ENC_SPI_Send(uint8_t command); + +/** + * Implement SPI buffer send and receive. Must be provided by user code + * param master2slave: data to be sent from host to ENC28J60, can be NULL if we only want to receive data from slave + * param slave2master: answer from ENC28J60 to host, can be NULL if we only want to send data to slave + * retval none + */ + +void ENC_SPI_SendBuf(uint8_t *master2slave, uint8_t *slave2master, uint16_t bufferSize); + +/* Exported types ------------------------------------------------------------*/ +/** @defgroup ETH_Exported_Types ETH Exported Types + * @{ + */ + +/** + * @brief ETH Init Structure definition + */ + +typedef struct +{ + uint32_t DuplexMode; /*!< Selects the MAC duplex mode: Half-Duplex or Full-Duplex mode + This parameter can be a value of @ref ETH_Duplex_Mode */ + + uint8_t *MACAddr; /*!< MAC Address of used Hardware: must be pointer on an array of 6 bytes */ + + uint32_t ChecksumMode; /*!< Selects if the checksum is check by hardware or by software. + This parameter can be a value of @ref ETH_Checksum_Mode */ + + uint8_t InterruptEnableBits; /*!< Selects the enabled interrupts */ +} ENC_InitTypeDef; + + +/** + * @brief Received Frame Informations structure definition + */ +typedef struct +{ + uint32_t length; /*!< Frame length */ + + uint8_t buffer[MAX_FRAMELEN+20]; /*!< Frame buffer */ + +} ENC_RxFrameInfos; + + +/** + * @brief ENC28J60 Handle Structure definition + */ + +typedef struct +{ + ENC_InitTypeDef Init; /*!< Ethernet Init Configuration */ + + uint8_t bank; /*!< Currently selected bank */ + uint8_t interruptFlags;/*!< The last value of interrupts flags */ + uint8_t pktCnt; /*!< The number of pending receive packets */ + uint16_t nextpkt; /*!< Next packet address */ + uint16_t LinkStatus; /*!< Ethernet link status */ + uint16_t transmitLength;/*!< The length of ip frame to transmit */ + uint32_t startTime; /*!< The start time of the current timer */ + uint32_t duration; /*!< The duration of the current timer in ms */ + uint16_t retries; /*!< The number of transmission retries left to do */ + + ENC_RxFrameInfos RxFrameInfos; /*!< last Rx frame infos */ +} ENC_HandleTypeDef; + + /** + * @} + */ + +/* Exported constants --------------------------------------------------------*/ +/** @defgroup ETH_Exported_Constants ETH Exported Constants + * @{ + */ +/* Size of the Ethernet header */ +#define ETH_HDRLEN 14 /* Minimum size: 2*6 + 2 */ + + +#define ERR_OK 0 /* No error, everything OK. */ +#define ERR_MEM -1 /* Out of memory error. */ +#define ERR_BUF -2 /* Buffer error. */ +#define ERR_TIMEOUT -3 /* Timeout. */ + +/* ENC28J60 Commands ********************************************************/ +/* A total of seven instructions are implemented on the ENC28J60. Where: + * + * aaaaaa is the 5-bit address of a control register, and + * dddddddd is one or more bytes of data that may accompany the command. + */ + +#define ENC_RCR (0x00) /* Read Control Register + * 000 | aaaaa | (Register value returned)) */ +#define ENC_RBM (0x3a) /* Read Buffer Memory + * 001 | 11010 | (Read buffer data follows) */ +#define ENC_WCR (0x40) /* Write Control Register + * 010 | aaaaa | dddddddd */ +#define ENC_WBM (0x7a) /* Write Buffer Memory + * 011 | 11010 | (Write buffer data follows) */ +#define ENC_BFS (0x80) /* Bit Field Set + * 100 | aaaaa | dddddddd */ +#define ENC_BFC (0xa0) /* Bit Field Clear + * 101 | aaaaa | dddddddd */ +#define ENC_SRC (0xff) /* System Reset + * 111 | 11111 | (No data) */ + +/* Global Control Registers *************************************************/ +/* Control registers are accessed with the RCR, RBM, WCR, BFS, and BFC + * commands. The following identifies all ENC28J60 control registers. The + * control register memory is partitioned into four banks, selectable by the + * bank select bits, BSEL1:BSEL0, in the ECON1 register. + * + * The last five locations (0x1b to 0x1f) of all banks point to a common set + * of registers: EIE, EIR, ESTAT, ECON2 and ECON1. These are key registers + * used in controlling and monitoring the operation of the device. Their + * common mapping allows easy access without switching the bank. + * + * Control registers for the ENC28J60 are generically grouped as ETH, MAC and + * MII registers. Register names starting with E belong to the ETH group. + * Similarly, registers names starting with MA belong to the MAC group and + * registers prefixed with MI belong to the MII group. + */ + +#define ENC_EIE (0x1b) /* Ethernet Interrupt Enable Register */ +#define ENC_EIR (0x1c) /* Ethernet Interupt Request Register */ +#define ENC_ESTAT (0x1d) /* Ethernet Status Register */ +#define ENC_ECON2 (0x1e) /* Ethernet Control 2 Register */ +#define ENC_ECON1 (0x1f) /* Ethernet Control 1 Register */ + +/* Ethernet Interrupt Enable Register Bit Definitions */ + +#define EIE_RXERIE (1 << 0) /* Bit 0: Receive Error Interrupt Enable */ +#define EIE_TXERIE (1 << 1) /* Bit 1: Transmit Error Interrupt Enable */ + /* Bit 2: Reserved */ +#define EIE_TXIE (1 << 3) /* Bit 3: Transmit Enable */ +#define EIE_LINKIE (1 << 4) /* Bit 4: Link Status Change Interrupt Enable */ +#define EIE_DMAIE (1 << 5) /* Bit 5: DMA Interrupt Enable */ +#define EIE_PKTIE (1 << 6) /* Bit 6: Receive Packet Pending Interrupt Enable */ +#define EIE_INTIE (1 << 7) /* Bit 7: Global INT Interrupt Enable */ + +/* Ethernet Interrupt Request Register Bit Definitions */ + +#define EIR_RXERIF (1 << 0) /* Bit 0: Receive Error Interrupt */ +#define EIR_TXERIF (1 << 1) /* Bit 1: Transmit Error Interrupt */ + /* Bit 2: Reserved */ +#define EIR_TXIF (1 << 3) /* Bit 3: Transmit Interrupt */ +#define EIR_LINKIF (1 << 4) /* Bit 4: Link Change Interrupt */ +#define EIR_DMAIF (1 << 5) /* Bit 5: DMA Interrupt */ +#define EIR_PKTIF (1 << 6) /* Bit 6: Receive Packet Pending Interrupt */ + /* Bit 7: Reserved */ +#define EIR_ALLINTS (EIR_RXERIF | EIR_TXERIF | EIR_TXIF | EIR_LINKIF | EIR_DMAIF) /* All interrupts bar EIR_PKTIF*/ + +/* Ethernet Status Register Bit Definitions */ + +#define ESTAT_CLKRDY (1 << 0) /* Bit 0: Clock Ready */ +#define ESTAT_TXABRT (1 << 1) /* Bit 1: Transmit Abort Error */ +#define ESTAT_RXBUSY (1 << 2) /* Bit 2: Receive Busy */ + /* Bit 3: Reserved */ +#define ESTAT_LATECOL (1 << 4) /* Bit 4: Late Collision Error */ + /* Bit 5: Reserved */ +#define ESTAT_BUFER (1 << 6) /* Bit 6: Ethernet Buffer Error Status */ +#define ESTAT_INT (1 << 7) /* Bit 7: INT Interrupt */ + +/* Ethernet Control 1 Register Bit Definitions */ + +#define ECON1_BSEL_SHIFT (0) /* Bits 0-1: Bank select */ +#define ECON1_BSEL_MASK (3 << ECON1_BSEL_SHIFT) +# define ECON1_BSEL_BANK0 (0 << ECON1_BSEL_SHIFT) /* Bank 0 */ +# define ECON1_BSEL_BANK1 (1 << ECON1_BSEL_SHIFT) /* Bank 1 */ +# define ECON1_BSEL_BANK2 (2 << ECON1_BSEL_SHIFT) /* Bank 2 */ +# define ECON1_BSEL_BANK3 (3 << ECON1_BSEL_SHIFT) /* Bank 3 */ +#define ECON1_RXEN (1 << 2) /* Bit 2: Receive Enable */ +#define ECON1_TXRTS (1 << 3) /* Bit 3: Transmit Request to Send */ +#define ECON1_CSUMEN (1 << 4) /* Bit 4: DMA Checksum Enable */ +#define ECON1_DMAST (1 << 5) /* Bit 5: DMA Start and Busy Status */ +#define ECON1_RXRST (1 << 6) /* Bit 6: Receive Logic Reset */ +#define ECON1_TXRST (1 << 7) /* Bit 7: Transmit Logic Reset */ + +/* Ethernet Control 2 Register */ + /* Bits 0-2: Reserved */ +#define ECON2_VRPS (1 << 3) /* Bit 3: Voltage Regulator Power Save Enable */ + /* Bit 4: Reserved */ +#define ECON2_PWRSV (1 << 5) /* Bit 5: Power Save Enable */ +#define ECON2_PKTDEC (1 << 6) /* Bit 6: Packet Decrement */ +#define ECON2_AUTOINC (1 << 7) /* Bit 7: Automatic Buffer Pointer Increment Enable */ + +/* Banked Control Registers *************************************************/ +/* The remaining control registers are identified with a a 5 bit address and + * a bank selection. We pack the bank number and an indication if this is + * a MAC/PHY register access together with the control register address + * together to keep the design simpler. + */ + +#define ENC_ADDR_SHIFT (0) /* Bits 0-4: Register address */ +#define ENC_ADDR_MASK (0x1f << ENC_ADDR_SHIFT) +#define ENC_BANK_SHIFT (5) /* Bits 5-6: Bank number */ +#define ENC_BANK_MASK (3 << ENC_BSEL_SHIFT) +# define ENC_BANK0 (0 << ENC_BSEL_SHIFT) +# define ENC_BANK1 (1 << ENC_BSEL_SHIFT) +# define ENC_BANK2 (2 << ENC_BSEL_SHIFT) +# define ENC_BANK3 (3 << ENC_BSEL_SHIFT) +#define ENC_PHYMAC_SHIFT (7) /* Bit 7: This is a PHY/MAC command */ +#define ENC_PHYMAC (1 << ENC_PHYMAC_SHIFT) + +#define REGADDR(a,b,m) ((m) << ENC_PHYMAC_SHIFT | (b) << ENC_BANK_SHIFT | (a)) +#define GETADDR(a) ((a) & ENC_ADDR_MASK) +#define GETBANK(a) (((a) >> ENC_BANK_SHIFT) & 3) +#define ISPHYMAC(a) (((a) & ENC_PHYMAC) != 0) + +/* Bank 0 Control Register Addresses */ + +#define ENC_ERDPTL REGADDR(0x00, 0, 0) /* Read Pointer Low Byte (ERDPT<7:0> */ +#define ENC_ERDPTH REGADDR(0x01, 0, 0) /* Read Pointer High Byte (ERDPT<12:8>) */ +#define ENC_EWRPTL REGADDR(0x02, 0, 0) /* Write Pointer Low Byte (EWRPT<7:0>) */ +#define ENC_EWRPTH REGADDR(0x03, 0, 0) /* Write Pointer High Byte (EWRPT<12:8>) */ +#define ENC_ETXSTL REGADDR(0x04, 0, 0) /* TX Start Low Byte (ETXST<7:0>) */ +#define ENC_ETXSTH REGADDR(0x05, 0, 0) /* TX Start High Byte (ETXST<12:8>) */ +#define ENC_ETXNDL REGADDR(0x06, 0, 0) /* TX End Low Byte (ETXND<7:0>) */ +#define ENC_ETXNDH REGADDR(0x07, 0, 0) /* TX End High Byte (ETXND<12:8>) */ +#define ENC_ERXSTL REGADDR(0x08, 0, 0) /* RX Start Low Byte (ERXST<7:0>) */ +#define ENC_ERXSTH REGADDR(0x09, 0, 0) /* RX Start High Byte (ERXST<12:8>) */ +#define ENC_ERXNDL REGADDR(0x0a, 0, 0) /* RX End Low Byte (ERXND<7:0>) */ +#define ENC_ERXNDH REGADDR(0x0b, 0, 0) /* RX End High Byte (ERXND<12:8>) */ +#define ENC_ERXRDPTL REGADDR(0x0c, 0, 0) /* RX RD Pointer Low Byte (ERXRDPT<7:0>) */ +#define ENC_ERXRDPTH REGADDR(0x0d, 0, 0) /* RX RD Pointer High Byte (ERXRDPT<12:8>) */ +#define ENC_ERXWRPTL REGADDR(0x0e, 0, 0) /* RX WR Pointer Low Byte (ERXWRPT<7:0>) */ +#define ENC_ERXWRPTH REGADDR(0x0f, 0, 0) /* RX WR Pointer High Byte (ERXWRPT<12:8>) */ +#define ENC_EDMASTL REGADDR(0x10, 0, 0) /* DMA Start Low Byte (EDMAST<7:0>) */ +#define ENC_EDMASTH REGADDR(0x11, 0, 0) /* DMA Start High Byte (EDMAST<12:8>) */ +#define ENC_EDMANDL REGADDR(0x12, 0, 0) /* DMA End Low Byte (EDMAND<7:0>) */ +#define ENC_EDMANDH REGADDR(0x13, 0, 0) /* DMA End High Byte (EDMAND<12:8>) */ +#define ENC_EDMADSTL REGADDR(0x14, 0, 0) /* DMA Destination Low Byte (EDMADST<7:0>) */ +#define ENC_EDMADSTH REGADDR(0x15, 0, 0) /* DMA Destination High Byte (EDMADST<12:8>) */ +#define ENC_EDMACSL REGADDR(0x16, 0, 0) /* DMA Checksum Low Byte (EDMACS<7:0>) */ +#define ENC_EDMACSH REGADDR(0x17, 0, 0) /* DMA Checksum High Byte (EDMACS<15:8>) */ + /* 0x18-0x1a: Reserved */ + /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */ +/* Bank 1 Control Register Addresses */ + +#define ENC_EHT0 REGADDR(0x00, 1, 0) /* Hash Table Byte 0 (EHT<7:0>) */ +#define ENC_EHT1 REGADDR(0x01, 1, 0) /* Hash Table Byte 1 (EHT<15:8>) */ +#define ENC_EHT2 REGADDR(0x02, 1, 0) /* Hash Table Byte 2 (EHT<23:16>) */ +#define ENC_EHT3 REGADDR(0x03, 1, 0) /* Hash Table Byte 3 (EHT<31:24>) */ +#define ENC_EHT4 REGADDR(0x04, 1, 0) /* Hash Table Byte 4 (EHT<39:32>) */ +#define ENC_EHT5 REGADDR(0x05, 1, 0) /* Hash Table Byte 5 (EHT<47:40>) */ +#define ENC_EHT6 REGADDR(0x06, 1, 0) /* Hash Table Byte 6 (EHT<55:48>) */ +#define ENC_EHT7 REGADDR(0x07, 1, 0) /* Hash Table Byte 7 (EHT<63:56>) */ +#define ENC_EPMM0 REGADDR(0x08, 1, 0) /* Pattern Match Mask Byte 0 (EPMM<7:0>) */ +#define ENC_EPMM1 REGADDR(0x09, 1, 0) /* Pattern Match Mask Byte 1 (EPMM<15:8>) */ +#define ENC_EPMM2 REGADDR(0x0a, 1, 0) /* Pattern Match Mask Byte 2 (EPMM<23:16>) */ +#define ENC_EPMM3 REGADDR(0x0b, 1, 0) /* Pattern Match Mask Byte 3 (EPMM<31:24>) */ +#define ENC_EPMM4 REGADDR(0x0c, 1, 0) /* Pattern Match Mask Byte 4 (EPMM<39:32>) */ +#define ENC_EPMM5 REGADDR(0x0d, 1, 0) /* Pattern Match Mask Byte 5 (EPMM<47:40>) */ +#define ENC_EPMM6 REGADDR(0x0e, 1, 0) /* Pattern Match Mask Byte 6 (EPMM<55:48>) */ +#define ENC_EPMM7 REGADDR(0x0f, 1, 0) /* Pattern Match Mask Byte 7 (EPMM<63:56>) */ +#define ENC_EPMCSL REGADDR(0x10, 1, 0) /* Pattern Match Checksum Low Byte (EPMCS<7:0>) */ +#define ENC_EPMCSH REGADDR(0x11, 1, 0) /* Pattern Match Checksum High Byte (EPMCS<15:0>) */ + /* 0x12-0x13: Reserved */ +#define ENC_EPMOL REGADDR(0x14, 1, 0) /* Pattern Match Offset Low Byte (EPMO<7:0>) */ +#define ENC_EPMOH REGADDR(0x15, 1, 0) /* Pattern Match Offset High Byte (EPMO<12:8>) */ + /* 0x16-0x17: Reserved */ +#define ENC_ERXFCON REGADDR(0x18, 1, 0) /* Receive Filter Configuration */ +#define ENC_EPKTCNT REGADDR(0x19, 1, 0) /* Ethernet Packet Count */ + /* 0x1a: Reserved */ + /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */ + +/* Receive Filter Configuration Bit Definitions */ + +#define ERXFCON_BCEN (1 << 0) /* Bit 0: Broadcast Filter Enable */ +#define ERXFCON_MCEN (1 << 1) /* Bit 1: Multicast Filter Enable */ +#define ERXFCON_HTEN (1 << 2) /* Bit 2: Hash Table Filter Enable */ +#define ERXFCON_MPEN (1 << 3) /* Bit 3: Magic Packet Filter Enable */ +#define ERXFCON_PMEN (1 << 4) /* Bit 4: Pattern Match Filter Enable */ +#define ERXFCON_CRCEN (1 << 5) /* Bit 5: Post-Filter CRC Check Enable */ +#define ERXFCON_ANDOR (1 << 6) /* Bit 6: AND/OR Filter Select */ +#define ERXFCON_UCEN (1 << 7) /* Bit 7: Unicast Filter Enable */ + +/* Bank 2 Control Register Addresses */ + +#define ENC_MACON1 REGADDR(0x00, 2, 1) /* MAC Control 1 */ + /* 0x01: Reserved */ +#define ENC_MACON3 REGADDR(0x02, 2, 1) /* MAC Control 3 */ +#define ENC_MACON4 REGADDR(0x03, 2, 1) /* MAC Control 4 */ +#define ENC_MABBIPG REGADDR(0x04, 2, 1) /* Back-to-Back Inter-Packet Gap (BBIPG<6:0>) */ + /* 0x05: Reserved */ +#define ENC_MAIPGL REGADDR(0x06, 2, 1) /* Non-Back-to-Back Inter-Packet Gap Low Byte (MAIPGL<6:0>) */ +#define ENC_MAIPGH REGADDR(0x07, 2, 1) /* Non-Back-to-Back Inter-Packet Gap High Byte (MAIPGH<6:0>) */ +#define ENC_MACLCON1 REGADDR(0x08, 2, 1) /* MAC Collision Control 1 */ +#define ENC_MACLCON2 REGADDR(0x09, 2, 1) /* MAC Collision Control 2 */ +#define ENC_MAMXFLL REGADDR(0x0a, 2, 1) /* Maximum Frame Length Low Byte (MAMXFL<7:0>) */ +#define ENC_MAMXFLH REGADDR(0x0b, 2, 1) /* Maximum Frame Length High Byte (MAMXFL<15:8>) */ + /* 0x0c-0x11: Reserved */ +#define ENC_MICMD REGADDR(0x12, 2, 1) /* MII Command Register */ + /* 0x13: Reserved */ +#define ENC_MIREGADR REGADDR(0x14, 2, 1) /* MII Register Address */ + /* 0x15: Reserved */ +#define ENC_MIWRL REGADDR(0x16, 2, 1) /* MII Write Data Low Byte (MIWR<7:0>) */ +#define ENC_MIWRH REGADDR(0x17, 2, 1) /* MII Write Data High Byte (MIWR<15:8>) */ +#define ENC_MIRDL REGADDR(0x18, 2, 1) /* MII Read Data Low Byte (MIRD<7:0>) */ +#define ENC_MIRDH REGADDR(0x19, 2, 1) /* MII Read Data High Byte(MIRD<15:8>) */ + /* 0x1a: Reserved */ + /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */ + +/* MAC Control 1 Register Bit Definitions */ + +#define MACON1_MARXEN (1 << 0) /* Bit 0: MAC Receive Enable */ +#define MACON1_PASSALL (1 << 1) /* Bit 1: Pass All Received Frames Enable */ +#define MACON1_RXPAUS (1 << 2) /* Bit 2: Pause Control Frame Reception Enable */ +#define MACON1_TXPAUS (1 << 3) /* Bit 3: Pause Control Frame Transmission Enable */ + /* Bits 4-7: Unimplemented or reserved */ + +/* MAC Control 1 Register Bit Definitions */ + +#define MACON3_FULDPX (1 << 0) /* Bit 0: MAC Full-Duplex Enable */ +#define MACON3_FRMLNEN (1 << 1) /* Bit 1: Frame Length Checking Enable */ +#define MACON3_HFRMLEN (1 << 2) /* Bit 2: Huge Frame Enable */ +#define MACON3_PHDRLEN (1 << 3) /* Bit 3: Proprietary Header Enable */ +#define MACON3_TXCRCEN (1 << 4) /* Bit 4: Transmit CRC Enable */ +#define MACON3_PADCFG0 (1 << 5) /* Bit 5: Automatic Pad and CRC Configuration */ +#define MACON3_PADCFG1 (1 << 6) /* Bit 6: " " " " " " " " " " */ +#define MACON3_PADCFG2 (1 << 7) /* Bit 7: " " " " " " " " " " */ + +/* MAC Control 1 Register Bit Definitions */ + +#define MACON4_NOBKOFF (1 << 4) /* Bit 4: No Backoff Enable */ +#define MACON4_BPEN (1 << 5) /* Bit 5: No Backoff During Backpressure Enable */ +#define MACON4_DEFER (1 << 6) /* Bit 6: Defer Transmission Enable bit */ + +/* MII Command Register Bit Definitions */ + +#define MICMD_MIIRD (1 << 0) /* Bit 0: MII Read Enable */ +#define MICMD_MIISCAN (1 << 1) /* Bit 1: MII Scan Enable */ + +/* Bank 3 Control Register Addresses */ + +#define ENC_MAADR5 REGADDR(0x00, 3, 1) /* MAC Address Byte 5 (MAADR<15:8>) */ +#define ENC_MAADR6 REGADDR(0x01, 3, 1) /* MAC Address Byte 6 (MAADR<7:0>) */ +#define ENC_MAADR3 REGADDR(0x02, 3, 1) /* MAC Address Byte 3 (MAADR<31:24>), OUI Byte 3 */ +#define ENC_MAADR4 REGADDR(0x03, 3, 1) /* MAC Address Byte 4 (MAADR<23:16>) */ +#define ENC_MAADR1 REGADDR(0x04, 3, 1) /* MAC Address Byte 1 (MAADR<47:40>), OUI Byte 1 */ +#define ENC_MAADR2 REGADDR(0x05, 3, 1) /* MAC Address Byte 2 (MAADR<39:32>), OUI Byte 2 */ +#define ENC_EBSTSD REGADDR(0x06, 3, 0) /* Built-in Self-Test Fill Seed (EBSTSD<7:0>) */ +#define ENC_EBSTCON REGADDR(0x07, 3, 0) /* Built-in Self-Test Control */ +#define ENC_EBSTCSL REGADDR(0x08, 3, 0) /* Built-in Self-Test Checksum Low Byte (EBSTCS<7:0>) */ +#define ENC_EBSTCSH REGADDR(0x09, 3, 0) /* Built-in Self-Test Checksum High Byte (EBSTCS<15:8>) */ +#define ENC_MISTAT REGADDR(0x0a, 3, 1) /* MII Status Register */ + /* 0x0b-0x11: Reserved */ +#define ENC_EREVID REGADDR(0x12, 3, 0) /* Ethernet Revision ID */ + /* 0x13-0x14: Reserved */ +#define ENC_ECOCON REGADDR(0x15, 3, 0) /* Clock Output Control */ + /* 0x16: Reserved */ +#define ENC_EFLOCON REGADDR(0x17, 3, 0) /* Ethernet Flow Control */ +#define ENC_EPAUSL REGADDR(0x18, 3, 0) /* Pause Timer Value Low Byte (EPAUS<7:0>) */ +#define ENC_EPAUSH REGADDR(0x19, 3, 0) /* Pause Timer Value High Byte (EPAUS<15:8>) */ + /* 0x1a: Reserved */ + /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */ + +/* Built-in Self-Test Control Register Bit Definitions */ + +#define EBSTCON_BISTST (1 << 0) /* Bit 0: Built-in Self-Test Start/Busy */ +#define EBSTCON_TME (1 << 1) /* Bit 1: Test Mode Enable */ +#define EBSTCON_TMSEL0 (1 << 2) /* Bit 2: Test Mode Select */ +#define EBSTCON_TMSEL1 (1 << 3) /* Bit 3: " " " " " " */ +#define EBSTCON_PSEL (1 << 4) /* Bit 4: Port Select */ +#define EBSTCON_PSV0 (1 << 5) /* Bit 5: Pattern Shift Value */ +#define EBSTCON_PSV1 (1 << 6) /* Bit 6: " " " " " */ +#define EBSTCON_PSV2 (1 << 7) /* Bit 7: " " " " " */ + +/* MII Status Register Register Bit Definitions */ + +#define MISTAT_BUSY (1 << 0) /* Bit 0: MII Management Busy */ +#define MISTAT_SCAN (1 << 1) /* Bit 1: MII Management Scan Operation */ +#define MISTAT_NVALID (1 << 2) /* Bit 2: MII Management Read Data Not Valid */ + /* Bits 3-7: Reserved or unimplemented */ + +/* Ethernet Flow Control Register Bit Definitions */ + +#define EFLOCON_FCEN0 (1 << 0) /* Bit 0: Flow Control Enable */ +#define EFLOCON_FCEN1 (1 << 1) /* Bit 1: " " " " " " */ +#define EFLOCON_FULDPXS (1 << 2) /* Bit 2: Read-Only MAC Full-Duplex Shadow */ + /* Bits 3-7: Reserved or unimplemented */ + +/* PHY Registers ************************************************************/ + +#define ENC_PHCON1 (0x00) /* PHY Control Register 1 */ +#define ENC_PHSTAT1 (0x01) /* PHY Status 1 */ +#define ENC_PHID1 (0x02) /* PHY ID Register 1 */ +#define ENC_PHID2 (0x03) /* PHY ID Register 2 */ +#define ENC_PHCON2 (0x10) /* PHY Control Register 2 */ +#define ENC_PHSTAT2 (0x11) /* PHY Status 2 */ +#define ENC_PHIE (0x12) /* PHY Interrupt Enable Register */ +#define ENC_PHIR (0x13) /* PHY Interrupt Request Register */ +#define ENC_PHLCON (0x14) + +/* PHY Control Register 1 Register Bit Definitions */ + +#define PHCON1_PDPXMD (1 << 8) /* Bit 8: PHY Duplex Mode */ +#define PHCON1_PPWRSV (1 << 11) /* Bit 11: PHY Power-Down */ +#define PHCON1_PLOOPBK (1 << 14) /* Bit 14: PHY Loopback */ +#define PHCON1_PRST (1 << 15) /* Bit 15: PHY Software Reset */ + +/* PHY Status 1 Register Bit Definitions */ + +#define PHSTAT1_JBSTAT (1 << 1) /* Bit 1: PHY Latching Jabber Status */ +#define PHSTAT1_LLSTAT (1 << 2) /* Bit 2: PHY Latching Link Status */ +#define PHSTAT1_PHDPX (1 << 11) /* Bit 11: PHY Half-Duplex Capable */ +#define PHSTAT1_PFDPX (1 << 12) /* Bit 12: PHY Full-Duplex Capable */ + +/* PHY Control Register 2 Register Bit Definitions */ + +#define PHCON2_HDLDIS (1 << 8) /* Bit 8: PHY Half-Duplex Loopback Disable */ +#define PHCON2_JABBER (1 << 10) /* Bit 10: Jabber Correction Disable */ +#define PHCON2_TXDIS (1 << 13) /* Bit 13: Twisted-Pair Transmitter Disable */ +#define PHCON2_FRCLINK (1 << 14) /* Bit 14: PHY Force Linkup */ + +/* PHY Status 2 Register Bit Definitions */ + +#define PHSTAT2_PLRITY (1 << 5) /* Bit 5: Polarity Status */ +#define PHSTAT2_DPXSTAT (1 << 9) /* Bit 9: PHY Duplex Status */ +#define PHSTAT2_LSTAT (1 << 10) /* Bit 10: PHY Link Status */ +#define PHSTAT2_COLSTAT (1 << 11) /* Bit 11: PHY Collision Status */ +#define PHSTAT2_RXSTAT (1 << 12) /* Bit 12: PHY Receive Status */ +#define PHSTAT2_TXSTAT (1 << 13) /* Bit 13: PHY Transmit Status */ + +/* PHY Interrupt Enable Register Bit Definitions */ + +#define PHIE_PGEIE (1 << 1) /* Bit 1: PHY Global Interrupt Enable */ +#define PHIE_PLNKIE (1 << 4) /* Bit 4: PHY Link Change Interrupt Enable */ + +/* PHIR Regiser Bit Definitions */ + +#define PHIR_PGIF (1 << 2) /* Bit 2: PHY Global Interrupt */ +#define PHIR_PLNKIF (1 << 4) /* Bit 4: PHY Link Change Interrupt */ + +/* PHLCON Regiser Bit Definitions */ + /* Bit 0: Reserved */ +#define PHLCON_STRCH (1 << 1) /* Bit 1: LED Pulse Stretching Enable */ +#define PHLCON_LFRQ0 (1 << 2) /* Bit 2: LED Pulse Stretch Time Configuration */ +#define PHLCON_LFRQ1 (1 << 3) /* Bit 3: " " " " " " " " " */ +#define PHLCON_LBCFG0 (1 << 4) /* Bit 4: LEDB Configuration */ +#define PHLCON_LBCFG1 (1 << 5) /* Bit 5: " " " " */ +#define PHLCON_LBCFG2 (1 << 6) /* Bit 6: " " " " */ +#define PHLCON_LBCFG3 (1 << 7) /* Bit 7: " " " " */ +#define PHLCON_LACFG0 (1 << 8) /* Bit 8: LEDA Configuration */ +#define PHLCON_LACFG1 (1 << 9) /* Bit 9: " " " " */ +#define PHLCON_LACFG2 (1 << 10) /* Bit 10: " " " " */ +#define PHLCON_LACFG3 (1 << 11) /* Bit 11: " " " " */ + +/* Packet Memory ************************************************************/ + +/* 8-Kbyte Transmit/Receive Packet Dual Port SRAM */ + +#define PKTMEM_START 0x0000 +#define PKTMEM_END 0x1fff + +/* maximum transfer unit */ +#define CONFIG_NET_ETH_MTU 1500 + +/* Packet Control Bits Definitions ******************************************/ + +#define PKTCTRL_POVERRIDE (1 << 0) /* Bit 0: Per Packet Override */ +#define PKTCTRL_PCRCEN (1 << 1) /* Bit 1: Per Packet CRC Enable */ +#define PKTCTRL_PPADEN (1 << 2) /* Bit 2: Per Packet Padding Enable */ +#define PKTCTRL_PHUGEEN (1 << 3) /* Bit 3: Per Packet Huge Frame Enable */ + +/* RX Status Bit Definitions ************************************************/ + +#define RXSTAT_LDEVENT (1 << 0) /* Bit 0: Long event or pack dropped */ + /* Bit 1: Reserved */ +#define RXSTAT_CEPS (1 << 2) /* Bit 2: Carrier event previously seen */ + /* Bit 3: Reserved */ +#define RXSTAT_CRCERROR (1 << 4) /* Bit 4: Frame CRC field bad */ +#define RXSTAT_LENERROR (1 << 5) /* Bit 5: Packet length != data length */ +#define RXSTAT_LENRANGE (1 << 6) /* Bit 6: Type/length field > 1500 bytes */ +#define RXSTAT_OK (1 << 7) /* Bit 7: Packet with valid CRC and no symbol errors */ +#define RXSTAT_MCAST (1 << 8) /* Bit 8: Packet with multicast address */ +#define RXSTAT_BCAST (1 << 9) /* Bit 9: Packet with broadcast address */ +#define RXSTAT_DRIBBLE (1 << 10) /* Bit 10: Additional bits received after packet */ +#define RXSTAT_CTRLFRAME (1 << 11) /* Bit 11: Control frame with valid type/length */ +#define RXSTAT_PAUSE (1 << 12) /* Bit 12: Control frame with pause frame opcde */ +#define RXSTAT_UNKOPCODE (1 << 13) /* Bit 13: Control frame with unknown opcode */ +#define RXSTAT_VLANTYPE (1 << 14) /* Bit 14: Current frame is a VLAN tagged frame */ + /* Bit 15: Zero */ +/* TSV bit definitions */ +#define TSV_LATECOL (1 << 5) /* Bit 5: Late Collision Error, RSV byte 3 */ + + + +/** @defgroup ETH_Duplex_Mode ETH Duplex Mode + * @{ + */ +#define ETH_MODE_FULLDUPLEX ((uint32_t)0x00000800) +#define ETH_MODE_HALFDUPLEX ((uint32_t)0x00000000) +/** + * @} + */ + +/** @defgroup ETH_Rx_Mode ETH Rx Mode + * @{ + */ +#define ETH_RXPOLLING_MODE ((uint32_t)0x00000000) +#define ETH_RXINTERRUPT_MODE ((uint32_t)0x00000001) +/** + * @} + */ + +/** @defgroup ETH_Checksum_Mode ETH Checksum Mode + * @{ + */ +#define ETH_CHECKSUM_BY_HARDWARE ((uint32_t)0x00000000) +#define ETH_CHECKSUM_BY_SOFTWARE ((uint32_t)0x00000001) +/** + * @} + */ + +/* Exported functions --------------------------------------------------------*/ +/** @addtogroup SPI_Exported_Functions + * @{ + */ + +/** @addtogroup SPI_Exported_Functions_Group1 + * @{ + */ +/* Initialization/de-initialization functions **********************************/ + + +/** + * Initialize the enc28j60 and configure the needed hardware resources + * param handle: Handle on data configuration. + * retval None + */ + +bool ENC_Start(ENC_HandleTypeDef *handle); + +/**************************************************************************** + * Function: ENC_SetMacAddr + * + * Description: + * Set the MAC address to the configured value. This is done after ifup + * or after a TX timeout. Note that this means that the interface must + * be down before configuring the MAC addr. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_SetMacAddr(ENC_HandleTypeDef *handle); + +/**************************************************************************** + * Function: ENC_RestoreTXBuffer + * + * Description: + * Prepare TX buffer + * + * Parameters: + * handle - Reference to the driver state structure + * len - length of buffer + * + * Returned Value: + * ERR_OK 0 No error, everything OK. + * ERR_MEM -1 Out of memory error. + * ERR_TIMEOUT -3 Timeout. + * + * Assumptions: + * + ****************************************************************************/ + +int8_t ENC_RestoreTXBuffer(ENC_HandleTypeDef *handle, uint16_t len); + +/**************************************************************************** + * Function: ENC_WriteBuffer + * + * Description: + * Write a buffer of data. + * + * Parameters: + * buffer - A pointer to the buffer to write from + * buflen - The number of bytes to write + * + * Returned Value: + * None + * + * Assumptions: + * Read pointer is set to the correct address + * + ****************************************************************************/ + +void ENC_WriteBuffer(void *buffer, uint16_t buflen); + +/**************************************************************************** + * Function: ENC_Transmit + * + * Description: + * Start hardware transmission. Called either from: + * + * - pkif interrupt when an application responds to the receipt of data + * by trying to send something, or + * - From watchdog based polling. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * none + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef USE_PROTOTHREADS +PT_THREAD(ENC_Transmit(struct pt *pt, ENC_HandleTypeDef *handle)); +#else +void ENC_Transmit(ENC_HandleTypeDef *handle); +#endif + + /** + * @} + */ + + /** + * @} + */ + +/**************************************************************************** + * Function: ENC_GetReceivedFrame + * + * Description: + * Check if we have received packet, and if so, retrive them. + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * true if new packet is available; false otherwise + * + * Assumptions: + * + ****************************************************************************/ + +bool ENC_GetReceivedFrame(ENC_HandleTypeDef *handle); + +/**************************************************************************** + * Function: ENC_IRQHandler + * + * Description: + * Perform interrupt handling logic outside of the interrupt handler (on + * the work queue thread). + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_IRQHandler(ENC_HandleTypeDef *handle); + +/**************************************************************************** + * Function: ENC_EnableInterrupts + * + * Description: + * Enable individual ENC28J60 interrupts + * + * Parameters: + * bits - The individual bits to enable + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_EnableInterrupts(uint8_t bits); + +/**************************************************************************** + * Function: ENC_GetPkcnt + * + * Description: + * Get the number of pending receive packets + * + * Parameters: + * handle - Reference to the driver state structure + * + * Returned Value: + * the number of receive packet not processed yet + * + * Assumptions: + * + ****************************************************************************/ + +void ENC_GetPkcnt(ENC_HandleTypeDef *handle); + + +/**************************************************************************** + * Function: up_udelay + * + * Description: + * wait us µs + * + * Parameters: + * us - The amount of time to wait in µs + * + * Returned Value: + * none + * + * Assumptions: + * + ****************************************************************************/ + +void up_udelay(uint32_t us); + +#endif /* ENC28J60_H_INCLUDED */ diff --git a/part15-tcpip/net/encspi.c b/part15-tcpip/net/encspi.c new file mode 100644 index 0000000..524f73f --- /dev/null +++ b/part15-tcpip/net/encspi.c @@ -0,0 +1,21 @@ +#include "../include/spi.h" + +void ENC_SPI_Select(unsigned char truefalse) { + spi_chip_select(!truefalse); // If it's true, select 0 (the ENC), if false, select 1 (i.e. deselect the ENC) +} + +void ENC_SPI_SendBuf(unsigned char *master2slave, unsigned char *slave2master, unsigned short bufferSize) { + spi_chip_select(0); + spi_send_recv(master2slave, slave2master, bufferSize); + spi_chip_select(1); // De-select the ENC +} + +void ENC_SPI_Send(unsigned char command) { + spi_chip_select(0); + spi_send(&command, 1); + spi_chip_select(1); // De-select the ENC +} + +void ENC_SPI_SendWithoutSelection(unsigned char command) { + spi_send(&command, 1); +} diff --git a/part15-tcpip/tcpip/ip_arp_udp_tcp.c b/part15-tcpip/tcpip/ip_arp_udp_tcp.c new file mode 100644 index 0000000..eb2f183 --- /dev/null +++ b/part15-tcpip/tcpip/ip_arp_udp_tcp.c @@ -0,0 +1,1525 @@ +/********************************************* + * 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 + * Copyright:LGPL V2 + * See http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html + * + * IP, Arp, UDP and TCP functions. + * + * The TCP implementation uses some size optimisations which are valid + * only if all data can be sent in one single packet. This is however + * not a big limitation for a microcontroller as you will anyhow use + * small web-pages. The web server must send the entire web page in one + * packet. The client "web browser" as implemented here can also receive + * large pages. + * + * Chip type : ATMEGA88/168/328/644 with ENC28J60 + *********************************************/ + +#include "ip_config.h" +#include "ip_arp_udp_tcp.h" + +// I use them to debug stuff: +#define LEDOFF PORTB|=(1<>4)*4) +const char arpreqhdr[] ={0,1,8,0,6,4,0,1}; +#ifdef NTP_client +const char ntpreqhdr[] ={0xe3,0,4,0xfa,0,1,0,0,0,1}; +#endif + +// The Ip checksum is calculated over the ip header only starting +// with the header length field and a total length of 20 bytes +// unitl ip.dst +// You must set the IP checksum field to zero before you start +// the calculation. +// len for ip is 20. +// +// For UDP/TCP we do not make up the required pseudo header. Instead we +// use the ip.src and ip.dst fields of the real packet: +// The udp checksum calculation starts with the ip.src field +// Ip.src=4bytes,Ip.dst=4 bytes,Udp header=8bytes + data length=16+len +// In other words the len here is 8 + length over which you actually +// want to calculate the checksum. +// You must set the checksum field to zero before you start +// the calculation. +// The same algorithm is also used for udp and tcp checksums. +// len for udp is: 8 + 8 + data length +// len for tcp is: 4+4 + 20 + option len + data length +// +// For more information on how this algorithm works see: +// http://www.netfor2.com/checksum.html +// http://www.msc.uky.edu/ken/cs471/notes/chap3.htm +// The RFC has also a C code example: http://www.faqs.org/rfcs/rfc1071.html +uint16_t checksum(uint8_t *buf, uint16_t len,uint8_t type){ + // type 0=ip , icmp + // 1=udp + // 2=tcp + uint32_t sum = 0; + + //if(type==0){ + // // do not add anything, standard IP checksum as described above + // // Usable for ICMP and IP header + //} + if(type==1){ + sum+=IP_PROTO_UDP_V; // protocol udp + // the length here is the length of udp (data+header len) + // =length given to this function - (IP.scr+IP.dst length) + sum+=len-8; // = real udp len + } + if(type==2){ + sum+=IP_PROTO_TCP_V; + // the length here is the length of tcp (data+header len) + // =length given to this function - (IP.scr+IP.dst length) + sum+=len-8; // = real tcp len + } + // build the sum of 16bit words + while(len >1){ + sum += 0xFFFF & (((uint32_t)*buf<<8)|*(buf+1)); + buf+=2; + len-=2; + } + // if there is a byte left then add it (padded with zero) + if (len){ + sum += ((uint32_t)(0xFF & *buf))<<8; + } + // now calculate the sum over the bytes in the sum + // until the result is only 16bit long + while (sum>>16){ + sum = (sum & 0xFFFF)+(sum >> 16); + } + // build 1's complement: + return( (uint16_t) sum ^ 0xFFFF); +} + +void init_mac(uint8_t *mymac){ + if (mymac){ + memcpy(macaddr,mymac,6); + } +} + +#if defined (ALL_clients) +void client_ifconfig(uint8_t *ip,uint8_t *netmask) +{ + uint8_t i; + if (ip){ + i=0;while(i<4){ipaddr[i]=ip[i];i++;} + } + if (netmask){ + i=0;while(i<4){ipnetmask[i]=netmask[i];i++;} + } +} + +// returns 1 if destip must be routed via the GW. Returns 0 if destip is on the local LAN +uint8_t route_via_gw(uint8_t *destip) +{ + uint8_t i=0; + while(i<4){ + if ((destip[i] & ipnetmask[i]) != (ipaddr[i] & ipnetmask[i])){ + return(1); + } + i++; + } + return(0); +} +#endif + + +uint8_t check_ip_message_is_from(uint8_t *buf,uint8_t *ip) +{ + uint8_t i=0; + while(i<4){ + if(buf[IP_SRC_P+i]!=ip[i]){ + return(0); + } + i++; + } + return(1); +} + +uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf,uint16_t len){ + uint8_t i=0; + // + if (len<41){ + return(0); + } + if(buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || + buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V){ + return(0); + } + while(i<4){ + if(buf[ETH_ARP_DST_IP_P+i] != ipaddr[i]){ + return(0); + } + i++; + } + return(1); +} + +uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len){ + uint8_t i=0; + //eth+ip+udp header is 42 + if (len<42){ + return(0); + } + if(buf[ETH_TYPE_H_P]!=ETHTYPE_IP_H_V || + buf[ETH_TYPE_L_P]!=ETHTYPE_IP_L_V){ + return(0); + } + if (buf[IP_HEADER_LEN_VER_P]!=0x45){ + // must be IP V4 and 20 byte header + return(0); + } + while(i<4){ + if(buf[IP_DST_P+i]!=ipaddr[i]){ + return(0); + } + i++; + } + return(1); +} + +// make a return eth header from a received eth packet +void make_eth(uint8_t *buf) +{ + uint8_t i=0; + // + //copy the destination mac from the source and fill my mac into src + while(i<6){ + buf[ETH_DST_MAC +i]=buf[ETH_SRC_MAC +i]; + buf[ETH_SRC_MAC +i]=macaddr[i]; + i++; + } +} +void fill_ip_hdr_checksum(uint8_t *buf) +{ + uint16_t ck; + // clear the 2 byte checksum + buf[IP_CHECKSUM_P]=0; + buf[IP_CHECKSUM_P+1]=0; + buf[IP_FLAGS_P]=0x40; // don't fragment + buf[IP_FLAGS_P+1]=0; // fragement offset + buf[IP_TTL_P]=64; // ttl + // calculate the checksum: + ck=checksum(&buf[IP_P], IP_HEADER_LEN,0); + buf[IP_CHECKSUM_P]=ck>>8; + buf[IP_CHECKSUM_P+1]=ck& 0xff; +} + +// make a return ip header from a received ip packet +void make_ip(uint8_t *buf) +{ + uint8_t i=0; + while(i<4){ + buf[IP_DST_P+i]=buf[IP_SRC_P+i]; + buf[IP_SRC_P+i]=ipaddr[i]; + i++; + } + fill_ip_hdr_checksum(buf); +} + +// swap seq and ack number and count ack number up +void step_seq(uint8_t *buf,uint16_t rel_ack_num,uint8_t cp_seq) +{ + uint8_t i; + uint8_t tseq; + i=4; + // sequence numbers: + // add the rel ack num to SEQACK + while(i>0){ + rel_ack_num=buf[TCP_SEQ_H_P+i-1]+rel_ack_num; + tseq=buf[TCP_SEQACK_H_P+i-1]; + buf[TCP_SEQACK_H_P+i-1]=0xff&rel_ack_num; + if (cp_seq){ + // copy the acknum sent to us into the sequence number + buf[TCP_SEQ_H_P+i-1]=tseq; + }else{ + buf[TCP_SEQ_H_P+i-1]= 0; // some preset value + } + rel_ack_num=rel_ack_num>>8; + i--; + } +} + +// make a return tcp header from a received tcp packet +// rel_ack_num is how much we must step the seq number received from the +// other side. We do not send more than 765 bytes of text (=data) in the tcp packet. +// No mss is included here. +// +// After calling this function you can fill in the first data byte at TCP_OPTIONS_P+4 +// If cp_seq=0 then an initial sequence number is used (should be use in synack) +// otherwise it is copied from the packet we received +void make_tcphead(uint8_t *buf,uint16_t rel_ack_num,uint8_t cp_seq) +{ + uint8_t i; + // copy ports: + i=buf[TCP_DST_PORT_H_P]; + buf[TCP_DST_PORT_H_P]=buf[TCP_SRC_PORT_H_P]; + buf[TCP_SRC_PORT_H_P]=i; + // + i=buf[TCP_DST_PORT_L_P]; + buf[TCP_DST_PORT_L_P]=buf[TCP_SRC_PORT_L_P]; + buf[TCP_SRC_PORT_L_P]=i; + step_seq(buf,rel_ack_num,cp_seq); + // zero the checksum + buf[TCP_CHECKSUM_H_P]=0; + buf[TCP_CHECKSUM_L_P]=0; + // no options: + // 20 bytes: + // The tcp header length is only a 4 bit field (the upper 4 bits). + // It is calculated in units of 4 bytes. + // E.g 20 bytes: 20/4=6 => 0x50=header len field + buf[TCP_HEADER_LEN_P]=0x50; +} + +void make_arp_answer_from_request(uint8_t *buf) +{ + uint8_t i=0; + // + make_eth(buf); + buf[ETH_ARP_OPCODE_H_P]=ETH_ARP_OPCODE_REPLY_H_V; + buf[ETH_ARP_OPCODE_L_P]=ETH_ARP_OPCODE_REPLY_L_V; + // fill the mac addresses: + while(i<6){ + buf[ETH_ARP_DST_MAC_P+i]=buf[ETH_ARP_SRC_MAC_P+i]; + buf[ETH_ARP_SRC_MAC_P+i]=macaddr[i]; + i++; + } + i=0; + while(i<4){ + buf[ETH_ARP_DST_IP_P+i]=buf[ETH_ARP_SRC_IP_P+i]; + buf[ETH_ARP_SRC_IP_P+i]=ipaddr[i]; + i++; + } + // eth+arp is 42 bytes: + enc28j60PacketSend(42,buf); +} + +void make_echo_reply_from_request(uint8_t *buf,uint16_t len) +{ + make_eth(buf); + make_ip(buf); + buf[ICMP_TYPE_P]=ICMP_TYPE_ECHOREPLY_V; + // we changed only the icmp.type field from request(=8) to reply(=0). + // we can therefore easily correct the checksum: + if (buf[ICMP_CHECKSUM_P] > (0xff-0x08)){ + buf[ICMP_CHECKSUM_P+1]++; + } + buf[ICMP_CHECKSUM_P]+=0x08; + // + enc28j60PacketSend(len,buf); +} + +// do some basic length calculations +uint16_t get_tcp_data_len(uint8_t *buf) +{ + int16_t i; + i=(((int16_t)buf[IP_TOTLEN_H_P])<<8)|(buf[IP_TOTLEN_L_P]&0xff); + i-=IP_HEADER_LEN; + i-=(buf[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes; + if (i<=0){ + i=0; + } + return((uint16_t)i); +} + + +// fill in tcp data at position pos. pos=0 means start of +// tcp data. Returns the position at which the string after +// this string could be filled. +uint16_t fill_tcp_data_p(uint8_t *buf,uint16_t pos, const uint8_t *progmem_s) +{ + char c; + // fill in tcp data at position pos + // + // with no options the data starts after the checksum + 2 more bytes (urgent ptr) + while ((c = *(progmem_s++))) { + buf[TCP_CHECKSUM_L_P+3+pos]=c; + 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 +uint16_t fill_tcp_data_len(uint8_t *buf,uint16_t pos, const uint8_t *s, uint8_t len) +{ + // fill in tcp data at position pos + // + // with no options the data starts after the checksum + 2 more bytes (urgent ptr) + while (len) { + buf[TCP_CHECKSUM_L_P+3+pos]=*s; + pos++; + s++; + len--; + } + return(pos); +} + +// fill in tcp data at position pos. pos=0 means start of +// tcp data. Returns the position at which the string after +// this string could be filled. +uint16_t fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s) +{ + return(fill_tcp_data_len(buf,pos,(uint8_t*)s,strlen(s))); +} + +// Make just an ack packet with no tcp data inside +// This will modify the eth/ip/tcp header +void make_tcp_ack_from_any(uint8_t *buf,int16_t datlentoack,uint8_t addflags) +{ + uint16_t j; + make_eth(buf); + // fill the header: + buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|addflags; + if (addflags==TCP_FLAGS_RST_V){ + make_tcphead(buf,datlentoack,1); + }else{ + if (datlentoack==0){ + // if there is no data then we must still acknoledge one packet + datlentoack=1; + } + // normal case, ack the data: + make_tcphead(buf,datlentoack,1); // no options + } + // total length field in the IP header must be set: + // 20 bytes IP + 20 bytes tcp (when no options) + j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN; + buf[IP_TOTLEN_H_P]=j>>8; + buf[IP_TOTLEN_L_P]=j& 0xff; + make_ip(buf); + // use a low window size otherwise we have to have + // timers and can not just react on every packet. + buf[TCP_WIN_SIZE]=0x4; // 1024=0x400, 1280=0x500 2048=0x800 768=0x300 + buf[TCP_WIN_SIZE+1]=0; + // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len + j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN,2); + buf[TCP_CHECKSUM_H_P]=j>>8; + buf[TCP_CHECKSUM_L_P]=j& 0xff; + enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN,buf); +} + + +// dlen is the amount of tcp data (http data) we send in this packet +// You can use this function only immediately after make_tcp_ack_from_any +// This is because this function will NOT modify the eth/ip/tcp header except for +// length and checksum +// You must set TCP_FLAGS before calling this +void make_tcp_ack_with_data_noflags(uint8_t *buf,uint16_t dlen) +{ + uint16_t j; + // total length field in the IP header must be set: + // 20 bytes IP + 20 bytes tcp (when no options) + len of data + j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen; + buf[IP_TOTLEN_H_P]=j>>8; + buf[IP_TOTLEN_L_P]=j& 0xff; + fill_ip_hdr_checksum(buf); + // zero the checksum + buf[TCP_CHECKSUM_H_P]=0; + buf[TCP_CHECKSUM_L_P]=0; + // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len + j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+dlen,2); + buf[TCP_CHECKSUM_H_P]=j>>8; + buf[TCP_CHECKSUM_L_P]=j& 0xff; + enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN,buf); +} + +#if defined (UDP_server) +// a udp server +void make_udp_reply_from_request_udpdat_ready(uint8_t *buf,uint16_t datalen,uint16_t port) +{ + uint16_t j; + make_eth(buf); + if (datalen>220){ + datalen=220; + } + // total length field in the IP header must be set: + j=IP_HEADER_LEN+UDP_HEADER_LEN+datalen; + buf[IP_TOTLEN_H_P]=j>>8; + buf[IP_TOTLEN_L_P]=j& 0xff; + make_ip(buf); + // send to port: + //buf[UDP_DST_PORT_H_P]=port>>8; + //buf[UDP_DST_PORT_L_P]=port & 0xff; + // sent to port of sender and use "port" as own source: + buf[UDP_DST_PORT_H_P]=buf[UDP_SRC_PORT_H_P]; + buf[UDP_DST_PORT_L_P]= buf[UDP_SRC_PORT_L_P]; + buf[UDP_SRC_PORT_H_P]=port>>8; + buf[UDP_SRC_PORT_L_P]=port & 0xff; + // calculte the udp length: + j=UDP_HEADER_LEN+datalen; + buf[UDP_LEN_H_P]=j>>8; + buf[UDP_LEN_L_P]=j& 0xff; + // zero the checksum + buf[UDP_CHECKSUM_H_P]=0; + buf[UDP_CHECKSUM_L_P]=0; + j=checksum(&buf[IP_SRC_P], 16 + datalen,1); + buf[UDP_CHECKSUM_H_P]=j>>8; + buf[UDP_CHECKSUM_L_P]=j& 0xff; + enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen,buf); +} + +// you can send a max of 220 bytes of data because we use only one +// byte for the data but udp messages are normally small. +void make_udp_reply_from_request(uint8_t *buf,char *data,uint8_t datalen,uint16_t port) +{ + uint8_t i=0; + // copy the data: + while(i>8)&0xff; + wwwport_l=(port&0xff); +} + +// this is for the server not the client: +void make_tcp_synack_from_syn(uint8_t *buf) +{ + uint16_t ck; + make_eth(buf); + // total length field in the IP header must be set: + // 20 bytes IP + 24 bytes (20tcp+4tcp options) + buf[IP_TOTLEN_H_P]=0; + buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4; + make_ip(buf); + buf[TCP_FLAGS_P]=TCP_FLAGS_SYNACK_V; + make_tcphead(buf,1,0); + // 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 + // 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+3]= 0; + // step the inititial seq num by something we will not use + // during this tcp session: + seqnum+=3; + // add an mss options field with MSS to 1280: + // 1280 in hex is 0x500 + buf[TCP_OPTIONS_P]=2; + buf[TCP_OPTIONS_P+1]=4; + buf[TCP_OPTIONS_P+2]=0x05; + buf[TCP_OPTIONS_P+3]=0x0; + // The tcp header length is only a 4 bit field (the upper 4 bits). + // It is calculated in units of 4 bytes. + // E.g 24 bytes: 24/4=6 => 0x60=header len field + buf[TCP_HEADER_LEN_P]=0x60; + // here we must just be sure that the web browser contacting us + // will send only one get packet + buf[TCP_WIN_SIZE]=0x0a; // was 1400=0x578, 2560=0xa00 suggested by Andras Tucsni to be able to receive bigger packets + buf[TCP_WIN_SIZE+1]=0; // + // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + 4 (one option: mss) + ck=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+4,2); + buf[TCP_CHECKSUM_H_P]=ck>>8; + buf[TCP_CHECKSUM_L_P]=ck& 0xff; + // add 4 for option mss: + enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4+ETH_HEADER_LEN,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 +// packetloop_icmp_tcp(buf,enc28j60PacketReceive(BUFFER_SIZE, buf)); +// and test the return value for non zero. +// +// dlen is the amount of tcp data (http data) we send in this packet +// You can use this function only immediately after make_tcp_ack_from_any +// This is because this function will NOT modify the eth/ip/tcp header except for +// length and checksum +void www_server_reply(uint8_t *buf,uint16_t dlen) +{ + make_tcp_ack_from_any(buf,info_data_len,0); // send ack for http get + // fill the header: + // 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; + make_tcp_ack_with_data_noflags(buf,dlen); // send data +} + +#endif // WWW_server + +#if defined (ALL_clients) +// fill buffer with a prog-mem string +void fill_buf_p(uint8_t *buf,uint16_t len, const char *progmem_s) +{ + uint8_t i=0; + + while (len){ + *buf= progmem_s[i]; + buf++; + len--; i++; + } +} +#endif + +#ifdef PING_client +// 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) +{ + uint8_t i=0; + uint16_t ck; + // + while(i<6){ + buf[ETH_DST_MAC +i]=dstmac[i]; // gw mac in local lan or host mac + buf[ETH_SRC_MAC +i]=macaddr[i]; + i++; + } + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + fill_buf_p(&buf[IP_P],9,iphdr); + buf[IP_ID_L_P]=ipid; ipid++; + buf[IP_TOTLEN_L_P]=0x54; + buf[IP_PROTO_P]=IP_PROTO_ICMP_V; + i=0; + while(i<4){ + buf[IP_DST_P+i]=destip[i]; + buf[IP_SRC_P+i]=ipaddr[i]; + i++; + } + fill_ip_hdr_checksum(buf); + buf[ICMP_TYPE_P]=ICMP_TYPE_ECHOREQUEST_V; + buf[ICMP_TYPE_P+1]=0; // code + // zero the checksum + 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_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){ + buf[ICMP_DATA_P+i]=PINGPATTERN; + i++; + } + // + ck=checksum(&buf[ICMP_TYPE_P], 56+8,0); + buf[ICMP_CHECKSUM_H_P]=ck>>8; + buf[ICMP_CHECKSUM_L_P]=ck& 0xff; + enc28j60PacketSend(98,buf); +} +#endif // PING_client + + +#ifdef NTP_client +// ntp udp packet +// See http://tools.ietf.org/html/rfc958 for details +// +void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport,uint8_t *dstmac) +{ + uint8_t i=0; + uint16_t ck; + // + while(i<6){ + buf[ETH_DST_MAC +i]=dstmac[i]; // gw mac in local lan or host mac + buf[ETH_SRC_MAC +i]=macaddr[i]; + i++; + } + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + fill_buf_p(&buf[IP_P],9,iphdr); + buf[IP_ID_L_P]=ipid; ipid++; + buf[IP_TOTLEN_L_P]=0x4c; + buf[IP_PROTO_P]=IP_PROTO_UDP_V; + i=0; + while(i<4){ + buf[IP_DST_P+i]=ntpip[i]; + buf[IP_SRC_P+i]=ipaddr[i]; + i++; + } + fill_ip_hdr_checksum(buf); + buf[UDP_DST_PORT_H_P]=0; + buf[UDP_DST_PORT_L_P]=0x7b; // ntp=123 + buf[UDP_SRC_PORT_H_P]=10; + buf[UDP_SRC_PORT_L_P]=srcport; // lower 8 bit of src port + buf[UDP_LEN_H_P]=0; + buf[UDP_LEN_L_P]=56; // fixed len + // zero the checksum + buf[UDP_CHECKSUM_H_P]=0; + buf[UDP_CHECKSUM_L_P]=0; + // copy the data: + i=0; + // most fields are zero, here we zero everything and fill later + while(i<48){ + buf[UDP_DATA_P+i]=0; + i++; + } + fill_buf_p(&buf[UDP_DATA_P],10,ntpreqhdr); + // + ck=checksum(&buf[IP_SRC_P], 16 + 48,1); + buf[UDP_CHECKSUM_H_P]=ck>>8; + buf[UDP_CHECKSUM_L_P]=ck& 0xff; + enc28j60PacketSend(90,buf); +} +// process the answer from the ntp server: +// if dstport==0 then accept any port otherwise only answers going to dstport +// 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){ + return(0); + } + } + if (buf[UDP_LEN_H_P]!=0 || buf[UDP_LEN_L_P]!=56 || buf[UDP_SRC_PORT_L_P]!=0x7b){ + // not ntp + return(0); + } + // copy time from the transmit time stamp field: + *time=((uint32_t)buf[0x52]<<24)|((uint32_t)buf[0x53]<<16)|((uint32_t)buf[0x54]<<8)|((uint32_t)buf[0x55]); + return(1); +} +#endif + +#ifdef UDP_client +// -------------------- 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], +// 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 +// needs to be done. +// +void send_udp_prepare(uint8_t *buf,uint16_t sport, const uint8_t *dip, uint16_t dport,const uint8_t *dstmac) +{ + uint8_t i=0; + // + while(i<6){ + buf[ETH_DST_MAC +i]=dstmac[i]; // gw mac in local lan or host mac + buf[ETH_SRC_MAC +i]=macaddr[i]; + i++; + } + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + fill_buf_p(&buf[IP_P],9,iphdr); + buf[IP_ID_L_P]=ipid; ipid++; + // total length field in the IP header must be set: + buf[IP_TOTLEN_H_P]=0; + // done in transmit: buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+UDP_HEADER_LEN+datalen; + buf[IP_PROTO_P]=IP_PROTO_UDP_V; + i=0; + while(i<4){ + buf[IP_DST_P+i]=dip[i]; + buf[IP_SRC_P+i]=ipaddr[i]; + i++; + } + // 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_SRC_PORT_H_P]=(sport>>8); + 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 + buf[UDP_CHECKSUM_H_P]=0; + buf[UDP_CHECKSUM_L_P]=0; + // copy the data: + // now starting with the first byte at buf[UDP_DATA_P] + // +} + +void send_udp_transmit(uint8_t *buf,uint16_t datalen) +{ + uint16_t tmp16; + tmp16=IP_HEADER_LEN+UDP_HEADER_LEN+datalen; + buf[IP_TOTLEN_L_P]=tmp16& 0xff; + buf[IP_TOTLEN_H_P]=tmp16>>8; + fill_ip_hdr_checksum(buf); + tmp16=UDP_HEADER_LEN+datalen; + buf[UDP_LEN_L_P]=tmp16& 0xff; + buf[UDP_LEN_H_P]=tmp16>>8; + // + tmp16=checksum(&buf[IP_SRC_P], 16 + datalen,1); + buf[UDP_CHECKSUM_L_P]=tmp16& 0xff; + buf[UDP_CHECKSUM_H_P]=tmp16>>8; + enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen,buf); +} + +void send_udp(uint8_t *buf,char *data,uint8_t datalen,uint16_t sport, const uint8_t *dip, uint16_t dport,const uint8_t *dstmac) +{ + send_udp_prepare(buf,sport, dip, dport,dstmac); + uint8_t i=0; + // limit the length: + if (datalen>220){ + datalen=220; + } + // copy the data: + i=0; + while(i>8; + buf[UDP_CHECKSUM_L_P]=ck& 0xff; + enc28j60PacketSend(pos,buf); +} +#endif // WOL_client + +#if defined GRATARP +// Send a Gratuitous arp, this is to refresh the arp +// cash of routers and switches. It can improve the response +// time in wifi networks as some wifi equipment expects the initial +// communication to not start from the network side. That is wrong +// but some consumer devices are made like this. +// +// A Gratuitous ARP can be a request or a reply. +// A request frame is as well called Unsolicited ARP +uint8_t gratutious_arp(uint8_t *buf) +{ + uint8_t i=0; + if (!enc28j60linkup()){ + return(0); + } + // + while(i<6){ + buf[ETH_DST_MAC +i]=0xff; + buf[ETH_SRC_MAC +i]=macaddr[i]; + i++; + } + buf[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V; + 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); + //buf[ETH_ARP_OPCODE_L_P]=ETH_ARP_OPCODE_REPLY_L_V; // reply + i=0; + while(i<6){ + buf[ETH_ARP_SRC_MAC_P +i]=macaddr[i]; + buf[ETH_ARP_DST_MAC_P+i]=0xff; + i++; + } + i=0; + while(i<4){ + buf[ETH_ARP_DST_IP_P+i]=ipaddr[i]; + buf[ETH_ARP_SRC_IP_P+i]=ipaddr[i]; + i++; + } + // 0x2a=42=len of packet + enc28j60PacketSend(0x2a,buf); + return(1); +} +#endif // GRATARP + +#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 +// before you can use this function +void client_arp_whohas(uint8_t *buf,uint8_t *ip_we_search) +{ + uint8_t i=0; + if (ipaddr[0]==0) return; // error ipaddr not set + // + while(i<6){ + buf[ETH_DST_MAC +i]=0xff; + buf[ETH_SRC_MAC +i]=macaddr[i]; + i++; + } + buf[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_ARP_L_V; + fill_buf_p(&buf[ETH_ARP_P],8,arpreqhdr); + i=0; + while(i<6){ + buf[ETH_ARP_SRC_MAC_P +i]=macaddr[i]; + buf[ETH_ARP_DST_MAC_P+i]=0; + i++; + } + i=0; + while(i<4){ + buf[ETH_ARP_DST_IP_P+i]=*(ip_we_search +i); + buf[ETH_ARP_SRC_IP_P+i]=ipaddr[i]; + i++; + } + // 0x2a=42=len of packet + enc28j60PacketSend(0x2a,buf); +} + +// return zero when current transaction is finished +uint8_t get_mac_with_arp_wait(void) +{ + if (arpip_state == WGW_HAVE_MAC){ + return(0); + } + return(1); +} + +// 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 +// 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)) +{ + uint8_t i=0; + client_arp_result_callback=arp_result_callback; + arpip_state=WGW_INITIAL_ARP; // causes an arp request in the packet loop + arp_reference_number=reference_number; + while(i<4){ + arpip[i]=ip[i]; + i++; + } +} +#endif + +#if defined (TCP_client) +// Make a tcp syn packet +void tcp_client_syn(uint8_t *buf,uint8_t srcport,uint16_t dstport) +{ + uint16_t ck; + uint8_t i=0; + // -- make the main part of the eth/IP/tcp header: + while(i<6){ + buf[ETH_DST_MAC +i]=tcp_dst_mac[i]; // gw mac in local lan or host mac + buf[ETH_SRC_MAC +i]=macaddr[i]; + i++; + } + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + fill_buf_p(&buf[IP_P],9,iphdr); + buf[IP_TOTLEN_L_P]=44; // good for syn + buf[IP_ID_L_P]=ipid; ipid++; + buf[IP_PROTO_P]=IP_PROTO_TCP_V; + i=0; + while(i<4){ + buf[IP_DST_P+i]=tcp_otherside_ip[i]; + buf[IP_SRC_P+i]=ipaddr[i]; + i++; + } + fill_ip_hdr_checksum(buf); + buf[TCP_DST_PORT_H_P]=(dstport>>8)&0xff; + buf[TCP_DST_PORT_L_P]=(dstport&0xff); + buf[TCP_SRC_PORT_H_P]=TCPCLIENT_SRC_PORT_H; + buf[TCP_SRC_PORT_L_P]=srcport; // lower 8 bit of src port + i=0; + // zero out sequence number and acknowledgement number + while(i<8){ + buf[TCP_SEQ_H_P+i]=0; + i++; + } + // -- header ready + // put inital seq number + // 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; + // step the inititial seq num by something we will not use + // during this tcp session: + seqnum+=3; + buf[TCP_HEADER_LEN_P]=0x60; // 0x60=24 len: (0x60>>4) * 4 + buf[TCP_FLAGS_P]=TCP_FLAGS_SYN_V; + // use a low window size otherwise we have to have + // timers and can not just react on every packet. + buf[TCP_WIN_SIZE]=0x3; // 1024=0x400 768=0x300, initial window + buf[TCP_WIN_SIZE+1]=0x0; + // zero the checksum + buf[TCP_CHECKSUM_H_P]=0; + buf[TCP_CHECKSUM_L_P]=0; + // urgent pointer + buf[TCP_CHECKSUM_L_P+1]=0; + buf[TCP_CHECKSUM_L_P+2]=0; + // MSS= max IP len that we want to have: + buf[TCP_OPTIONS_P]=2; + buf[TCP_OPTIONS_P+1]=4; + buf[TCP_OPTIONS_P+2]=(CLIENTMSS>>8); + buf[TCP_OPTIONS_P+3]=CLIENTMSS & 0xff; + ck=checksum(&buf[IP_SRC_P], 8 +TCP_HEADER_LEN_PLAIN+4,2); + buf[TCP_CHECKSUM_H_P]=ck>>8; + buf[TCP_CHECKSUM_L_P]=ck& 0xff; + // 4 is the tcp mss option: + enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN+4,buf); +} +#endif // 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): +// +// uint8_t your_client_tcp_result_callback(uint8_t fd, uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data){...your code;return(close_tcp_session);} +// +// statuscode=0 means the buffer has valid data, otherwise len and pos_in_buf +// are invalid. That is: do to use data_start_pos_in_buf and len_of_data +// if statuscode!=0. +// +// This callback gives you access to the TCP data of the first +// packet returned from the server. You should aim to minimize the server +// 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. +// 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 +// +// 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: +// 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 +// function so you know to which call of client_tcp_req this callback belongs. +// +// You can not start different clients (e.g modbus and web) at the +// same time but you can start them one after each other. That is +// when the request has timed out or when the result_callback was +// executed then you can start a new one. The fd makes it still possible to +// distinguish in the callback code the different types you started. +// +// Note that you might never get called back if the other side does +// not answer. A timer would be needed to recongnize such a condition. +// +// We use callback functions because that saves memory and a uC is very +// limited in memory +// +uint8_t client_tcp_req(uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data),uint16_t (*datafill_callback)(uint8_t fd),uint16_t port,uint8_t *dstip,uint8_t *dstmac) +{ + uint8_t i=0; + client_tcp_result_callback=result_callback; + client_tcp_datafill_callback=datafill_callback; + while(i<4){tcp_otherside_ip[i]=dstip[i];i++;} + i=0; + while(i<6){tcp_dst_mac[i]=dstmac[i];i++;} + tcp_client_port=port; + tcp_client_state=1; // send a syn + tcp_fd++; + if (tcp_fd>7){ + tcp_fd=0; + } + return(tcp_fd); +} +#endif // TCP_client + +#if defined (WWW_client) +uint16_t www_client_internal_datafill_callback(uint8_t fd){ + char strbuf[5]; + uint16_t len=0; + if (fd==www_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(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")); + }else{ + // POST + len=fill_tcp_data_p(bufptr,0,PSTR("POST ")); + len=fill_tcp_data_p(bufptr,len,client_urlbuf); + 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,PSTR("\r\n")); + len=fill_tcp_data_p(bufptr,len,client_additionalheaderline); + } + 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("Content-Length: ")); + itoa(strlen(client_postval),strbuf,10); + len=fill_tcp_data(bufptr,len,strbuf); + len=fill_tcp_data_p(bufptr,len,PSTR("\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n")); + len=fill_tcp_data(bufptr,len,client_postval); + } + return(len); + } + return(0); +} + +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; + if (fd!=www_fd){ + (*client_browser_callback)(500,0,0); + return(0); + } + if (statuscode==0 && len_of_data>12){ + // 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 + 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){ + web_statuscode+=bufptr[datapos+9+i]-'0'; + web_statuscode*=10; + i++; + } + web_statuscode+=bufptr[datapos+11]-'0'; + } + //(*client_browser_callback)(web_statuscode,((uint16_t)TCP_SRC_PORT_H_P+(bufptr[TCP_HEADER_LEN_P]>>4)*4),len_of_data); + (*client_browser_callback)(web_statuscode,datapos,len_of_data); + } + } + return(0); +} + +// call this function externally like this: +// +// Declare a callback function: void browserresult(uint8_t webstatuscode,uint16_t datapos,uint16_t len){...your code} +// The variable datapos is the index in the packet buffer. +// Now call client_browser_url: +// client_browser_url(PSTR("/cgi-bin/checkip"),NULL,"tuxgraphics.org",&browserresult,other_side_ip,gwmac); +// urlbuf_varpart is a pointer to a string buffer that contains the second +// non constant part of the url. You must keep this buffer allocated until the +// callback function is executed or until you can be sure that the server side +// has timed out. +// hoststr is the name of the host. This is needed because many sites host several +// sites on the same physical machine with only one IP address. The web server needs +// to know to which site you want to go. +// webstatuscode is zero if there was no proper reply from the server (garbage message total communication failure, this is rare). +// webstatuscode is the http status code (e.g webstatuscode=200 for 200 OK); +// webstatuscode is zero if there was a garbage answer received from the server. +// For possible status codes look at http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +// Basically 2xx is success and any 5xx, 4xx is a failure. +// 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) +{ + if (!enc28j60linkup())return; + client_urlbuf=urlbuf; + client_urlbuf_var=urlbuf_varpart; + client_hoststr=hoststr; + browsertype=0; + client_browser_callback=callback; + www_fd=client_tcp_req(&www_client_internal_result_callback,&www_client_internal_datafill_callback,80,dstip,dstmac); +} + +// client web browser using http POST operation: +// additionalheaderline 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 +// 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) +{ + if (!enc28j60linkup())return; + client_urlbuf=urlbuf; + client_hoststr=hoststr; + client_urlbuf_var=urlbuf_varpart; + client_additionalheaderline=additionalheaderline; + client_postval=postval; + browsertype=1; + client_browser_callback=callback; + www_fd=client_tcp_req(&www_client_internal_result_callback,&www_client_internal_datafill_callback,80,dstip,dstmac); +} +#endif // WWW_client + +void register_ping_rec_callback(void (*callback)(uint8_t *srcip)) +{ + icmp_callback=callback; +} + +#ifdef PING_client +// loop over this to check if we get a ping reply: +uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost) +{ + if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREPLY_V){ + if (buf[ICMP_DATA_P]== PINGPATTERN){ + if (check_ip_message_is_from(buf,ip_monitoredhost)){ + return(1); + // ping reply is from monitored host and ping was from us + } + } + } + return(0); +} +#endif // PING_client + + +// 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; +#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 + // packet (without crc error): + if(plen==0){ + if (arpip_state == (WGW_ACCEPT_ARP_REPLY|WGW_INITIAL_ARP) && arp_delaycnt==0 ){ + // arp_delaycnt has wrapped no arp reply yet + if (enc28j60linkup()) client_arp_whohas(buf,arpip); + } + if (arpip_state == WGW_INITIAL_ARP && enc28j60linkup()){ + client_arp_whohas(buf,arpip); + arpip_state|=WGW_ACCEPT_ARP_REPLY; // WGW_INITIAL_ARP and WGW_ACCEPT_ARP_REPLY set + arp_delaycnt=0; // this is like a timer, not so precise but good enough, it wraps in about 2 sec + } + arp_delaycnt++; +#if defined (TCP_client) + if (tcp_client_state==1 && enc28j60linkup()){ // send a syn + tcp_client_state=2; + tcpclient_src_port_l++; // allocate a new port + // we encode our 3 bit fd into the src port this + // way we get it back in every message that comes + // from the server: + tcp_client_syn(buf,((tcp_fd<<5) | (0x1f & tcpclient_src_port_l)),tcp_client_port); + } +#endif + return(0); + } +#endif // ARP_MAC_resolver_client + // arp is broadcast if unknown but a host may also + // 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 + 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 + (*client_arp_result_callback)(arpip,arp_reference_number,buf+ETH_ARP_SRC_MAC_P); + arpip_state=WGW_HAVE_MAC; + } +#endif // ARP_MAC_resolver_client + return(0); + + } + // check if ip packets are for us: + if(eth_type_is_ip_and_my_ip(buf,plen)==0){ + return(0); + } + if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V){ + if (icmp_callback){ + (*icmp_callback)(&(buf[IP_SRC_P])); + } + // a ping packet, let's send pong + debugstr("Sending ping reply..."); + 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 + return(0); + } +#if defined (TCP_client) + // a message for the tcp client, tcp_client_state is zero if client was never used + 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; +#endif // WWW_client + if (check_ip_message_is_from(buf,tcp_otherside_ip)==0){ + return(0); + } + // if we get a reset: + if (buf[TCP_FLAGS_P] & TCP_FLAGS_RST_V){ + if (client_tcp_result_callback){ + // 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; + return(0); + } + len=get_tcp_data_len(buf); + if (tcp_client_state==2){ + if ((buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) && (buf[TCP_FLAGS_P] &TCP_FLAGS_ACK_V)){ + // synack, answer with ack + make_tcp_ack_from_any(buf,0,0); + buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V; + + // Make a tcp message with data. When calling this function we must + // 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 + // 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{ + // this is just to prevent a crash + len=0; + } + tcp_client_state=3; + make_tcp_ack_with_data_noflags(buf,len); + return(0); + }else{ + // reset only if we have sent a syn and don't get syn-ack back. + // If we connect to a non listen port then we get a RST + // which will be handeled above. In other words there is + // normally no danger for an endless loop. + tcp_client_state=1; // retry + // do not inform application layer as we retry. + len++; + if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V){ + // if packet was an ack then do not step the ack number + len=0; + } + // refuse and reset the connection + make_tcp_ack_from_any(buf,len,TCP_FLAGS_RST_V); + return(0); + } + } + // 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){ + // our first real data packet + tcp_client_state=4; + // return the data we received + if (client_tcp_result_callback){ + tcpstart=TCP_DATA_START; // TCP_DATA_START is a formula + // out of buffer bounds check, needed in case of fragmented IP packets + if (tcpstart>plen-8){ + tcpstart=plen-8; // dummy but save + } + save_len=len; + if (tcpstart+len>plen){ + save_len=plen-tcpstart; + } + send_fin=(*client_tcp_result_callback)((buf[TCP_DST_PORT_L_P]>>5)&0x7,0,tcpstart,save_len); + } + if (send_fin){ + make_tcp_ack_from_any(buf,len,TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V); + tcp_client_state=5; + return(0); + } + } + if(tcp_client_state==5){ + // no more ack + 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); + tcp_client_state=5; // connection terminated + return(0); + } + // ack all data (the web page may be long): + // if we just get a fragment then len will be zero + // and we ack only once we have the full packet + if (len>0){ + make_tcp_ack_from_any(buf,len,0); + } + return(0); + } +#endif // TCP_client + // +#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_FLAGS_P] & TCP_FLAGS_SYN_V){ + make_tcp_synack_from_syn(buf); + // make_tcp_synack_from_syn does already send the syn,ack + return(0); + } + if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V){ + info_data_len=get_tcp_data_len(buf); + // we can possibly have no data, just ack: + // Here we misuse plen for something else to save a variable. + // plen is now the position of start of the tcp user data. + if (info_data_len==0){ + if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V){ + // finack, answer with ack + make_tcp_ack_from_any(buf,0,0); + } + // just an ack with no data, wait for next packet + return(0); + } + // Here we misuse len for something else to save a variable + len=TCP_DATA_START; // TCP_DATA_START is a formula + // check for data corruption + if (len>plen-8){ + return(0); + } + return(len); + } + } +#endif // WWW_server + return(0); +} +/* end of ip_arp_udp.c */ diff --git a/part15-tcpip/tcpip/ip_arp_udp_tcp.h b/part15-tcpip/tcpip/ip_arp_udp_tcp.h new file mode 100644 index 0000000..7b4fa8d --- /dev/null +++ b/part15-tcpip/tcpip/ip_arp_udp_tcp.h @@ -0,0 +1,212 @@ +/********************************************* + * 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 + * Copyright:LGPL V2 + * See http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html + * + * IP/ARP/UDP/TCP functions + * + * Chip type : ATMEGA88/168/328/644 with ENC28J60 + *********************************************/ +//@{ +#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: +extern void init_mac(uint8_t *mymac); // not needed if you call init_udp_or_www_server +// -- web server functions -- +#if defined (WWW_server) || defined (UDP_server) +// you must call this function once before you use any of the other server functions: +// mymac may be set to NULL in this function if init_mac was used before +// init_ip_arp_udp_tcp is now replaced by init_udp_or_www_server and the www_server_port function. +extern void init_udp_or_www_server(uint8_t *mymac,uint8_t *myip); +#endif + +#if defined (WWW_server) +extern void www_server_port(uint16_t port); // not needed if you want port 80 +// send data from the web server to the client: +extern void www_server_reply(uint8_t *buf,uint16_t dlen); +#endif + +// for a UDP server: +#if defined (UDP_server) +extern uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len); +extern void make_udp_reply_from_request_udpdat_ready(uint8_t *buf,uint16_t datalen,uint16_t port); +extern void make_udp_reply_from_request(uint8_t *buf,char *data,uint8_t datalen,uint16_t port); +#endif +// return 0 to just continue in the packet loop and return the position +// of the tcp data if there is tcp data part: +extern uint16_t packetloop_arp_icmp_tcp(uint8_t *buf,uint16_t plen); +// functions to fill the web pages with data: +extern uint16_t fill_tcp_data_p(uint8_t *buf,uint16_t pos, const uint8_t *progmem_s); +extern uint16_t fill_tcp_data_string(uint8_t *buf,uint16_t pos, char *reply); + +extern uint16_t fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s); +// fill a binary string of len data into the tcp packet: +extern uint16_t fill_tcp_data_len(uint8_t *buf,uint16_t pos, const uint8_t *s, uint8_t len); + +// -- client only functions -- +#if defined (WWW_client) || defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) +extern void client_ifconfig(uint8_t *ip,uint8_t *netmask); +// route_via_gw can be used decide if a packed needs to be routed via GW or can be found on the LAN: +extern uint8_t route_via_gw(uint8_t *destip); // returns 1 if destip must be routed via the GW. Returns 0 if destip is on the local LAN +// +// The get_mac_with_arp function can be used to find the MAC address of +// a host that is directly connected to the same LAN. It translates the IP address into +// a MAC address. +// You need to provide a callback function. That function will be executed once the +// MAC address is found. We do this to not block execution in the main loop. +// NOTE: you can only do one MAC address resolution at a time. The reference_number is just +// a number given back to you to make it easier to know what this relates to. +// +// You declare the callback function: +// +//#define TRANS_NUM_GWMAC 12 +//void arpresolver_result_callback(uint8_t *ip __attribute__((unused)),uint8_t reference_number,uint8_t *mac){ // the __attribute__((unused)) is a gcc compiler directive to avoid warnings about unsed variables. +// uint8_t i=0; +// if (reference_number==TRANS_NUM_GWMAC){ +// // copy mac address over: +// while(i<6){gwmac[i]=mac[i];i++;} +// } +//} +// +// and then you can just call get_mac_with_arp like this: +// get_mac_with_arp(gwip,TRANS_NUM_GWMAC,&arpresolver_result_callback); +// Note: you must have initialized the stack with init_udp_or_www_server or client_ifconfig +// before you can use get_mac_with_arp(). The arp request will automatically be repeated if +// there is no answer. +extern 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)); +uint8_t get_mac_with_arp_wait(void); // checks current ongoing transaction, retuns 0 when the transaction is over +#endif + +#ifdef TCP_client +// To use the tcp client you need to: +// +// Declare a callback function to get the result (tcp data from the server): +// +// uint8_t your_client_tcp_result_callback(uint8_t fd, uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data){ +// ...your code; +// return(close_tcp_session); +// } +// +// statuscode=0 means the buffer has valid data, otherwise len and pos_in_buf +// are invalid. That is: do to use data_start_pos_in_buf and len_of_data +// if statuscode!=0. +// +// This callback gives you access to the TCP data of the first +// packet returned from the server. You should aim to minimize the server +// 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. +// 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 +// +// 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: +// 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 +// function so you know to which call of client_tcp_req this callback belongs. +// +// You can not start different clients (e.g modbus and web) at the +// same time but you can start them one after each other. That is +// when the request has timed out or when the result_callback was +// executed then you can start a new one. The fd makes it still possible to +// distinguish in the callback code the different types you started. +// +// Note that you might never get called back if the other side does +// not answer. A timer would be needed to recongnize such a condition. +// +// We use callback functions because that is the best implementation +// given the fact that we have very little RAM memory. +// +extern uint8_t client_tcp_req(uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data),uint16_t (*datafill_callback)(uint8_t fd),uint16_t port,uint8_t *dstip,uint8_t *dstmac); +#endif + +#ifdef WWW_client +// ----- http get +// The string buffers to which urlbuf_varpart and hoststr are pointing +// must not be changed until the callback is executed. +extern void client_browse_url(const prog_char *urlbuf, char *urlbuf_varpart, const char *hoststr,void (*callback)(uint16_t,uint16_t,uint16_t),uint8_t *dstip,uint8_t *dstmac); +// The callback is a reference to a function which must look like this: +// void browserresult_callback(uint16_t webstatuscode,uint16_t datapos,uint16_t len) +// webstatuscode is zero if there was no proper reply from the server (garbage message total communication failure, this is rare). +// webstatuscode is otherwise the http status code (e.g webstatuscode=200 for 200 OK); +// For possible status codes look at http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +// Basically 2xx is success and any 5xx, 4xx is a failure. +// ----- http post +// client web browser using http POST operation: +// additionalheaderline 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 +// when the post operation was really done (e.g when callback was executed). +// postval must be urlencoded. +extern void client_http_post(const prog_char *urlbuf, 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); +// The callback is a reference to a function which must look like this: +// void browserresult_callback(uint16_t webstatuscode,uint16_t datapos,uint16_t len) +// webstatuscode is zero if there was no proper reply from the server (garbage message total communication failure, this is rare). +// webstatuscode is otherwise the http status code (e.g webstatuscode=200 for 200 OK); +#endif + +#ifdef NTP_client +// be careful to not mix client_ntp_request with situations where you are filling +// a web-page. Normally you will be using the same packet buffer and +// client_ntp_request writes immediately to buf. You might need to +// set a marker and call client_ntp_request when your main loop is idle. +extern void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport,uint8_t *dstmac); +extern uint8_t client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l); +#endif + +#ifdef UDP_client +// There are two ways of using this UDP client: +// +// 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 +// needs to be done. +// +extern void send_udp_prepare(uint8_t *buf,uint16_t sport, const uint8_t *dip, uint16_t dport,const uint8_t *dstmac); +extern void send_udp_transmit(uint8_t *buf,uint16_t datalen); + +// send_udp sends via gwip, you must call client_set_gwip at startup, datalen must be less than 220 bytes +extern void send_udp(uint8_t *buf,char *data,uint8_t datalen,uint16_t sport, const uint8_t *dip, uint16_t dport,const uint8_t *dstmac); +#endif + +// you can find out who ping-ed you if you want: +extern void register_ping_rec_callback(void (*callback)(uint8_t *srcip)); + +#ifdef PING_client +extern void client_icmp_request(uint8_t *buf,uint8_t *destip,uint8_t *dstmac); +// you must loop over this function to check if there was a ping reply: +extern uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost); +#endif // PING_client + +#ifdef WOL_client +extern void send_wol(uint8_t *buf,uint8_t *wolmac); +#endif // WOL_client + +#if defined GRATARP +// send a Gratuitous arp, this is to refresh the arp +// cash of routers and switches. It can improve the response +// time in wifi networks as some wifi equipment expects the initial +// communication to not start from the network side. That is wrong +// but some consumer devices are made like this. +extern uint8_t gratutious_arp(uint8_t *buf); +#endif // GRATARP + +#endif /* IP_ARP_UDP_TCP_H */ +//@} diff --git a/part15-tcpip/tcpip/ip_config.h b/part15-tcpip/tcpip/ip_config.h new file mode 100644 index 0000000..22c108b --- /dev/null +++ b/part15-tcpip/tcpip/ip_config.h @@ -0,0 +1,53 @@ +/********************************************* + * 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 + * Copyright:LGPL V2 + * See http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html + * + * This file can be used to decide which functionallity of the + * TCP/IP stack shall be available. By picking the right functions + * you can significantly reduce the size of the resulting code. + * + *********************************************/ +//@{ +#ifndef IP_CONFIG_H +#define IP_CONFIG_H + +//------------- functions in ip_arp_udp_tcp.c -------------- +// an NTP client (ntp clock): +#undef NTP_client +// a spontanious sending UDP client (needed as well for DNS and DHCP) +#undef UDP_client +// a server answering to UDP messages +#define UDP_server +// a web server +#undef WWW_server + +// to send out a ping: +#undef PING_client +#define PINGPATTERN 0x42 + +// a UDP wake on lan sender: +#undef WOL_client + +// function to send a gratuitous arp +#undef GRATARP + +// a "web browser". This can be use to upload data +// to a web server on the internet by encoding the data +// into the url (like a Form action of type GET): +#undef WWW_client +// if you do not need a browser and just a server: +//#undef WWW_client +// +//------------- functions in websrv_help_functions.c -------------- +// +// functions to decode cgi-form data: +#undef FROMDECODE_websrv_help + +// function to encode a URL (mostly needed for a web client) +#undef URLENCODE_websrv_help + +#endif /* IP_CONFIG_H */ +//@} diff --git a/part15-tcpip/tcpip/net.h b/part15-tcpip/tcpip/net.h new file mode 100644 index 0000000..b862746 --- /dev/null +++ b/part15-tcpip/tcpip/net.h @@ -0,0 +1,132 @@ +/********************************************* + * 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 + * Copyright:LGPL V2 + * See http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html + * + * Based on the net.h file from the AVRlib library by Pascal Stang. + * For AVRlib See http://www.procyonengineering.com/ + * Used with explicit permission of Pascal Stang. + * + *********************************************/ + +// notation: _P = position of a field +// _V = value of a field + +//@{ + +#ifndef NET_H +#define NET_H + +// ******* ETH ******* +#define ETH_HEADER_LEN 14 +// values of certain bytes: +#define ETHTYPE_ARP_H_V 0x08 +#define ETHTYPE_ARP_L_V 0x06 +#define ETHTYPE_IP_H_V 0x08 +#define ETHTYPE_IP_L_V 0x00 +// byte positions in the ethernet frame: +// +// Ethernet type field (2bytes): +#define ETH_TYPE_H_P 12 +#define ETH_TYPE_L_P 13 +// +#define ETH_DST_MAC 0 +#define ETH_SRC_MAC 6 + + +// ******* ARP ******* +#define ETH_ARP_OPCODE_REPLY_H_V 0x0 +#define ETH_ARP_OPCODE_REPLY_L_V 0x02 +#define ETH_ARP_OPCODE_REQ_H_V 0x0 +#define ETH_ARP_OPCODE_REQ_L_V 0x01 +// start of arp header: +#define ETH_ARP_P 0xe +// +#define ETHTYPE_ARP_L_V 0x06 +// arp.dst.ip +#define ETH_ARP_DST_IP_P 0x26 +// arp.opcode +#define ETH_ARP_OPCODE_H_P 0x14 +#define ETH_ARP_OPCODE_L_P 0x15 +// arp.src.mac +#define ETH_ARP_SRC_MAC_P 0x16 +#define ETH_ARP_SRC_IP_P 0x1c +#define ETH_ARP_DST_MAC_P 0x20 +#define ETH_ARP_DST_IP_P 0x26 + +// ******* IP ******* +#define IP_HEADER_LEN 20 +// ip.src +#define IP_SRC_P 0x1a +#define IP_DST_P 0x1e +#define IP_HEADER_LEN_VER_P 0xe +#define IP_CHECKSUM_P 0x18 +#define IP_TTL_P 0x16 +#define IP_FLAGS_P 0x14 +#define IP_P 0xe +#define IP_TOTLEN_H_P 0x10 +#define IP_TOTLEN_L_P 0x11 +#define IP_ID_H_P 0x12 +#define IP_ID_L_P 0x13 + +#define IP_PROTO_P 0x17 + +#define IP_PROTO_ICMP_V 1 +#define IP_PROTO_TCP_V 6 +// 17=0x11 +#define IP_PROTO_UDP_V 17 +// ******* ICMP ******* +#define ICMP_TYPE_ECHOREPLY_V 0 +#define ICMP_TYPE_ECHOREQUEST_V 8 +// +#define ICMP_TYPE_P 0x22 +#define ICMP_CHECKSUM_P 0x24 +#define ICMP_CHECKSUM_H_P 0x24 +#define ICMP_CHECKSUM_L_P 0x25 +#define ICMP_IDENT_H_P 0x26 +#define ICMP_IDENT_L_P 0x27 +#define ICMP_DATA_P 0x2a + +// ******* UDP ******* +#define UDP_HEADER_LEN 8 +// +#define UDP_SRC_PORT_H_P 0x22 +#define UDP_SRC_PORT_L_P 0x23 +#define UDP_DST_PORT_H_P 0x24 +#define UDP_DST_PORT_L_P 0x25 +// +#define UDP_LEN_H_P 0x26 +#define UDP_LEN_L_P 0x27 +#define UDP_CHECKSUM_H_P 0x28 +#define UDP_CHECKSUM_L_P 0x29 +#define UDP_DATA_P 0x2a + +// ******* TCP ******* +#define TCP_SRC_PORT_H_P 0x22 +#define TCP_SRC_PORT_L_P 0x23 +#define TCP_DST_PORT_H_P 0x24 +#define TCP_DST_PORT_L_P 0x25 +// the tcp seq number is 4 bytes 0x26-0x29 +#define TCP_SEQ_H_P 0x26 +#define TCP_SEQACK_H_P 0x2a +// flags: SYN=2 +#define TCP_FLAGS_P 0x2f +#define TCP_FLAGS_SYN_V 2 +#define TCP_FLAGS_FIN_V 1 +#define TCP_FLAGS_RST_V 4 +#define TCP_FLAGS_PUSH_V 8 +#define TCP_FLAGS_SYNACK_V 0x12 +#define TCP_FLAGS_ACK_V 0x10 +#define TCP_FLAGS_PSHACK_V 0x18 +// plain len without the options: +#define TCP_HEADER_LEN_PLAIN 20 +#define TCP_HEADER_LEN_P 0x2e +#define TCP_WIN_SIZE 0x30 +#define TCP_CHECKSUM_H_P 0x32 +#define TCP_CHECKSUM_L_P 0x33 +#define TCP_OPTIONS_P 0x36 +// +#endif +//@}