mirror of
https://github.com/isometimes/rpi4-osdev
synced 2024-11-23 18:50:40 +00:00
Compare commits
3 commits
c1edecf21f
...
0bd322d7b7
Author | SHA1 | Date | |
---|---|---|---|
|
0bd322d7b7 | ||
|
c71a1cc18a | ||
|
d7ad289e54 |
96 changed files with 11831 additions and 179 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*.o
|
||||
*.elf
|
||||
*.img
|
||||
|
53
config.txt
Normal file
53
config.txt
Normal file
|
@ -0,0 +1,53 @@
|
|||
# For more options and information see
|
||||
# http://rptl.io/configtxt
|
||||
# Some settings may impact device functionality. See link above for details
|
||||
|
||||
# Uncomment some or all of these to enable the optional hardware interfaces
|
||||
#dtparam=i2c_arm=on
|
||||
#dtparam=i2s=on
|
||||
#dtparam=spi=on
|
||||
|
||||
# Enable audio (loads snd_bcm2835)
|
||||
dtparam=audio=on
|
||||
|
||||
# Additional overlays and parameters are documented
|
||||
# /boot/firmware/overlays/README
|
||||
|
||||
# Automatically load overlays for detected cameras
|
||||
camera_auto_detect=1
|
||||
|
||||
# Automatically load overlays for detected DSI displays
|
||||
display_auto_detect=1
|
||||
|
||||
# Automatically load initramfs files, if found
|
||||
auto_initramfs=1
|
||||
|
||||
# Enable DRM VC4 V3D driver
|
||||
dtoverlay=vc4-kms-v3d
|
||||
max_framebuffers=2
|
||||
|
||||
# Don't have the firmware create an initial video= setting in cmdline.txt.
|
||||
# Use the kernel's default instead.
|
||||
disable_fw_kms_setup=1
|
||||
|
||||
# Run in 64-bit mode
|
||||
arm_64bit=1
|
||||
|
||||
# Disable compensation for displays with overscan
|
||||
disable_overscan=1
|
||||
|
||||
# Run as fast as firmware / board allows
|
||||
arm_boost=1
|
||||
|
||||
[cm4]
|
||||
# Enable host mode on the 2711 built-in XHCI USB controller.
|
||||
# This line should be removed if the legacy DWC2 controller is required
|
||||
# (e.g. for USB device mode) or if USB support is not required.
|
||||
otg_mode=1
|
||||
|
||||
[all]
|
||||
kernel_old=1
|
||||
disable_commandline_tags=1
|
||||
core_freq_min=500
|
||||
hdmi_group=1
|
||||
hdmi_mode=16
|
|
@ -1,22 +1,22 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
LLVMPATH = /opt/homebrew/opt/llvm/bin
|
||||
CLANGFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -mcpu=cortex-a72+nosimd
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c boot.S -o boot.o
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
audio.o : audio.bin
|
||||
$(LLVMPATH)/llvm-objcopy -I binary -O elf64-littleaarch64 -B aarch64 $< $@
|
||||
$(GCCPATH)/aarch64-none-elf-objcopy -I binary -O elf64-littleaarch64 -B aarch64 $< $@
|
||||
|
||||
%.o: %.c
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES) audio.o
|
||||
$(LLVMPATH)/ld.lld -m aarch64elf -nostdlib boot.o $(OFILES) audio.o -T link.ld -o kernel8.elf
|
||||
$(LLVMPATH)/llvm-objcopy -O binary kernel8.elf kernel8.img
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) audio.o -T 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
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
audio.o : audio.bin
|
||||
$(GCCPATH)/aarch64-none-elf-objcopy -I binary -O elf64-littleaarch64 -B aarch64 $< $@
|
||||
|
||||
%.o: %.c
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES) audio.o
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) audio.o -T 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
|
|
@ -1,25 +1,25 @@
|
|||
CFILES = $(wildcard *.c lib/*.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
LLVMPATH = /opt/homebrew/opt/llvm/bin
|
||||
CLANGFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -mcpu=cortex-a72+nosimd
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot/boot.o: boot/boot.S
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot/boot.S -o boot/boot.o
|
||||
|
||||
bin/BCM4345C0.o : bin/BCM4345C0.hcd
|
||||
$(LLVMPATH)/llvm-objcopy -I binary -O elf64-littleaarch64 -B aarch64 $< $@
|
||||
$(GCCPATH)/aarch64-none-elf-objcopy -I binary -O elf64-littleaarch64 -B aarch64 $< $@
|
||||
|
||||
bin/audio.o : bin/audio.bin
|
||||
$(LLVMPATH)/llvm-objcopy -I binary -O elf64-littleaarch64 -B aarch64 $< $@
|
||||
$(GCCPATH)/aarch64-none-elf-objcopy -I binary -O elf64-littleaarch64 -B aarch64 $< $@
|
||||
|
||||
%.o: %.c
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot/boot.o $(OFILES) bin/BCM4345C0.o bin/audio.o
|
||||
$(LLVMPATH)/ld.lld -m aarch64elf -nostdlib boot/boot.o $(OFILES) bin/BCM4345C0.o bin/audio.o -T boot/link.ld -o kernel8.elf
|
||||
$(LLVMPATH)/llvm-objcopy -O binary kernel8.elf kernel8.img
|
||||
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot/boot.o $(OFILES) bin/BCM4345C0.o bin/audio.o -T boot/link.ld -o kernel8.elf
|
||||
$(GCCPATH)/aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img
|
||||
clean:
|
||||
/bin/rm kernel8.elf *.o bin/*.o boot/*.o lib/*.o *.img > /dev/null 2> /dev/null || true
|
||||
/bin/rm kernel8.elf *.o bin/*.o boot/*.o *.img > /dev/null 2> /dev/null || true
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
CFILES = $(wildcard *.c lib/*.c kernel/*.c)
|
||||
SFILES = $(wildcard boot/*.S lib/*.S kernel/*.S)
|
||||
OFILES = $(CFILES:.c=.o) $(SFILES:.S=.o)
|
||||
LLVMPATH = /opt/homebrew/opt/llvm/bin
|
||||
CLANGFLAGS = -Wall -O2 -ffreestanding -nostdlib -mcpu=cortex-a72+nosimd
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
%.o: %.c
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
%.o: %.S
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -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
|
||||
$(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
|
||||
|
|
164
part13a-gpio-interrupts/README.md
Normal file
164
part13a-gpio-interrupts/README.md
Normal file
|
@ -0,0 +1,164 @@
|
|||
Writing a "bare metal" operating system for Raspberry Pi 4 (Part 13)
|
||||
====================================================================
|
||||
|
||||
[< Go back to part12-wgt](../part12-wgt)
|
||||
|
||||
What are interrupts?
|
||||
--------------------
|
||||
If you've spent any time looking at the Bluetooth code in these tutorials, you'll notice we're always "polling" for updates. In fact, in _part11-breakout-smp_ we tie up an entire core just waiting around for something to happen. This clearly isn't the best use of CPU time. Fortunately, the world solved that problem for us years ago with _interrupts_.
|
||||
|
||||
Ideally, we want to tell a piece of hardware to do something and have it simply notify us when the work is complete so we can move on with our lives in the meantime. These notifications are known as _interrupts_ because they disrupt normal program execution and force the CPU to immediately run an _interrupt handler_.
|
||||
|
||||
The simplest device that interrupts
|
||||
-----------------------------------
|
||||
One useful piece of built-in hardware is a system timer, which can be programmed to interrupt at regular intervals e.g. every second. You'll need this if you want to schedule multiple processes to run on a single core e.g. using the principle of time slicing.
|
||||
|
||||
For now, however, we're simply going to learn how to program the timer and respond to its interrupts.
|
||||
|
||||
The codebase
|
||||
------------
|
||||
Let me quickly explain what you're looking at in the _part13-interrupts_ code:
|
||||
|
||||
* _boot/_ : the same _boot_ code directory from _part12-wgt_
|
||||
* _include/_ : some useful headers copied directly from _part11-multicore_
|
||||
* _lib/_ : some useful libraries copied directly from _part11-multicore_
|
||||
* _kernel/_ : the only new code we need to concern ourselves with in this tutorial
|
||||
|
||||
Please note: I have also done some work to tidy up the _Makefile_ and respect this directory structure, but nothing to write home about!
|
||||
|
||||
The new code
|
||||
------------
|
||||
You'll recognise a lot of _kernel.c_ from _part10-multicore_, except instead of showing four cores at work and playing sound, we're now only using core 0 & 1 and, in addition, making use of two timer interrupts to show four progress bars. So, the `main()` routine kicks off core 1, sets up the timers, and then finally kicks off core 0's workload.
|
||||
|
||||
The timers are set up using these calls:
|
||||
|
||||
```c
|
||||
irq_init_vectors();
|
||||
enable_interrupt_controller();
|
||||
irq_barrier();
|
||||
irq_enable();
|
||||
timer_init();
|
||||
```
|
||||
|
||||
Initialising the exception vector table
|
||||
---------------------------------------
|
||||
In fact, interrupts are a more specific kind of _exception_ - something that, when "raised", needs the immediate attention of the processor. A perfect example of when an exception might occur is when bad code tries to do something "impossible" e.g. divide by zero. The CPU needs to know how to respond when/if this happens i.e. jump to an address of some code to run which handles this exception gracefully e.g. by printing an error to the screen. These addresses are stored in an _exception vector table_.
|
||||
|
||||
_irqentry.S_ sets up a list called `vectors` which contains individual _vector entries_. These vector entries are simply jump instructions to handler code.
|
||||
|
||||
The CPU is told where this exception vector table is stored during the `irq_init_vectors()` call from `main()` in _kernel.c_. You'll find this code in _utils.S_:
|
||||
|
||||
```c
|
||||
irq_init_vectors:
|
||||
adr x0, vectors
|
||||
msr vbar_el1, x0
|
||||
ret
|
||||
```
|
||||
|
||||
It simply sets the Vector Base Address Register to the address of the `vectors` list.
|
||||
|
||||
Interrupt handling
|
||||
------------------
|
||||
The only vector entry we really care about for the purposes of this tutorial is `handle_el1_irq`. This is a generic handler for any interrupt request (IRQ) that comes in at EL1 (kernel execution level).
|
||||
|
||||
If you do want a deeper understanding, I highly recommend reading s-matyukevich's work [here](https://github.com/s-matyukevich/raspberry-pi-os/blob/master/docs/lesson03/rpi-os.md).
|
||||
|
||||
```c
|
||||
handle_el1_irq:
|
||||
kernel_entry
|
||||
bl handle_irq
|
||||
kernel_exit
|
||||
```
|
||||
|
||||
Put simply, `kernel_entry` saves the register state before the interrupt handler runs, and `kernel_exit` restores this register state before we return. As we're _interrupting_ normal program execution, we want to be sure that we put things back to how they were so that nothing unpredictable happens as our kernel code resumes.
|
||||
|
||||
In the middle we simply call a function called `handle_irq()` which is written in the C language in _irq.c_. Its purpose is to look more closely at the interrupt request, figure out what device was responsible for generating an interrupt, and run the right sub-handler:
|
||||
|
||||
```c
|
||||
void handle_irq() {
|
||||
unsigned int irq = REGS_IRQ->irq0_pending_0;
|
||||
|
||||
while(irq & (SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3)) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, we're handling two different timer interrupts in this code. In fact, `handle_timer_1()` and `handle_timer_3()` are implemented in _kernel.c_ and serve to demonstrate that the timer has fired by incrementing a progress counter and updating a graphical representation of its value. Timer 3 is configured to progress at 4 times the speed of Timer 1.
|
||||
|
||||
The interrupt controller
|
||||
------------------------
|
||||
The interrupt controller is the hardware responsible for telling the CPU about interrupts as they occur. We can use the interrupt controller to act as a gatekeeper and allow/block (or enable/disable) interrupts. We can also use it to figure out which device generated the interrupt, as we did in `handle_irq()`.
|
||||
|
||||
In `enable_interrupt_controller()`, called from `main()` in _kernel.c_, we allow the Timer 1 and Timer 3 interrupts through and in `disable_interrupt_controller()` we block all interrupts:
|
||||
|
||||
```c
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
Masking/unmasking interrupts
|
||||
----------------------------
|
||||
To begin receiving interrupts, we need to take one more step: unmasking all types of interrupts.
|
||||
|
||||
Masking is a technique used by the CPU to prevent a particular piece of code from being stopped in its tracks by an interrupt. It's used to protect important code that *must* complete. Imagine what would happen if our `kernel_entry` code (that saves register state) was interrupted halfway through! In this case, the register state would be overwritten and lost. This is why the CPU automatically masks all interrupts when an exception handler is executed.
|
||||
|
||||
The `irq_enable` and `irq_disable` functions in _utils.S_ are responsible for masking and unmasking interrupts.
|
||||
|
||||
They are helped by the `irq_barrier` function which ensures that the `enable_interrupt_controller()` call properly finishes before the `irq_enable()` call is made:
|
||||
|
||||
```c
|
||||
.globl irq_enable
|
||||
irq_enable:
|
||||
msr daifclr, #2
|
||||
ret
|
||||
|
||||
.globl irq_disable
|
||||
irq_disable:
|
||||
msr daifset, #2
|
||||
ret
|
||||
|
||||
.globl irq_barrier
|
||||
irq_barrier:
|
||||
dsb sy
|
||||
ret
|
||||
```
|
||||
|
||||
As soon as `irq_enable()` is called from `main()` in _kernel.c_, the timer handler is run when the timer interrupt fires. Well, sort of...!
|
||||
|
||||
Initialising the system timer
|
||||
-----------------------------
|
||||
We still need to initialise the timer.
|
||||
|
||||
The RPi4's system timer couldn't be simpler. It has a counter which increases by 1 with each clock tick. It then has 4 interrupt lines (0 & 2 reserved for the GPU, 1 & 3 used by us in this tutorial!) with 4 corresponding compare registers. When the value of the counter becomes equal to a value in one of the compare registers, the corresponding interrupt is fired.
|
||||
|
||||
So before we receive any timer interrupts, we must also set the right compare registers to have a non-zero value. The `timer_init()` function (called from `main()` in _kernel.c_) gets the current timer value, adds the timer interval and sets the compare register to that total, so when the right number of clock ticks pass, the interrupt fires. It does this for both Timer 1 and Timer 3, setting Timer 3 to run 4 times as fast.
|
||||
|
||||
Handling the timer interrupts
|
||||
-----------------------------
|
||||
This is the simplest bit.
|
||||
|
||||
We update the compare register so the next interrupt will be generated after the same interval again. Importantly we then acknowledge the interrupt by setting the right bit of the Control Status register.
|
||||
|
||||
Then we update the screen to show our progress!
|
||||
|
||||
_And... hey presto! You're handling two system timer interrupts like a pro!_
|
||||
|
||||
![Timers firing on all cylinders on the Raspberry Pi 4](images/13-interrupts-running.jpg)
|
||||
|
||||
[Go to part14-spi-ethernet >](../part14-spi-ethernet)
|
114
part13a-gpio-interrupts/boot/boot.S
Normal file
114
part13a-gpio-interrupts/boot/boot.S
Normal file
|
@ -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
|
35
part13a-gpio-interrupts/boot/link.ld
Normal file
35
part13a-gpio-interrupts/boot/link.ld
Normal file
|
@ -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*) }
|
||||
}
|
44
part13a-gpio-interrupts/boot/sysregs.h
Normal file
44
part13a-gpio-interrupts/boot/sysregs.h
Normal file
|
@ -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
|
BIN
part13a-gpio-interrupts/images/13-interrupts-running.jpg
Executable file
BIN
part13a-gpio-interrupts/images/13-interrupts-running.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
13
part13a-gpio-interrupts/include/fb.h
Normal file
13
part13a-gpio-interrupts/include/fb.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
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);
|
17
part13a-gpio-interrupts/include/io.h
Normal file
17
part13a-gpio-interrupts/include/io.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#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 uart_hex(unsigned int d);
|
||||
void uart_byte(unsigned char b);
|
34
part13a-gpio-interrupts/include/mb.h
Normal file
34
part13a-gpio-interrupts/include/mb.h
Normal file
|
@ -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);
|
11
part13a-gpio-interrupts/include/multicore.h
Normal file
11
part13a-gpio-interrupts/include/multicore.h
Normal file
|
@ -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);
|
253
part13a-gpio-interrupts/include/terminal.h
Normal file
253
part13a-gpio-interrupts/include/terminal.h
Normal file
|
@ -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)
|
||||
};
|
60
part13a-gpio-interrupts/kernel/irq.c
Normal file
60
part13a-gpio-interrupts/kernel/irq.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
#include "kernel.h"
|
||||
|
||||
// textdefinitionmap of invalid error-types
|
||||
const char entry_error_messages[16][32] = {
|
||||
"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 show_invalid_entry_message(unsigned int type, unsigned long esr, unsigned long address) {
|
||||
debugstr("ERROR CAUGHT: %s - %d, ESR: %X, Address: %X\n",
|
||||
entry_error_messages[type], type, esr, address);
|
||||
}
|
||||
|
||||
void enable_interrupt_controller() {
|
||||
REGS_IRQ->irq0_enable_0 = SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3 | SPI_INT;
|
||||
}
|
||||
|
||||
void disable_interrupt_controller() {
|
||||
REGS_IRQ->irq0_enable_0 = 0;
|
||||
}
|
||||
|
||||
void handle_irq() {
|
||||
unsigned int irq = REGS_IRQ->irq0_pending_0;
|
||||
|
||||
while(irq & (SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3 | SPI_INT)) {
|
||||
switch(irq) {
|
||||
case (SYS_TIMER_IRQ_1):
|
||||
irq &= ~SYS_TIMER_IRQ_1;
|
||||
handle_timer_1();
|
||||
break;
|
||||
case (SYS_TIMER_IRQ_3):
|
||||
irq &= ~SYS_TIMER_IRQ_3;
|
||||
handle_timer_3();
|
||||
break;
|
||||
case(SPI_INT):
|
||||
irq &= ~SPI_INT;
|
||||
handle_spi();
|
||||
break;
|
||||
default:
|
||||
debugstr("Unknown pending irq: %x /r/n",irq);
|
||||
}
|
||||
}
|
||||
}
|
156
part13a-gpio-interrupts/kernel/irqentry.S
Normal file
156
part13a-gpio-interrupts/kernel/irqentry.S
Normal file
|
@ -0,0 +1,156 @@
|
|||
//D1.10.2 of ARMv8 reference manual
|
||||
#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 // We could pass this to a function to print an error here
|
||||
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
|
152
part13a-gpio-interrupts/kernel/kernel.c
Normal file
152
part13a-gpio-interrupts/kernel/kernel.c
Normal file
|
@ -0,0 +1,152 @@
|
|||
#include "../include/fb.h"
|
||||
#include "../include/io.h"
|
||||
#include "../include/multicore.h"
|
||||
#include "kernel.h"
|
||||
|
||||
void initProgress(void)
|
||||
{
|
||||
drawRect(0, 0, 301, 50, 0x0f, 0);
|
||||
drawString(309, 21, "Core 0", 0x0f, 1);
|
||||
|
||||
drawRect(0, 60, 301, 110, 0x0f, 0);
|
||||
drawString(309, 81, "Core 1", 0x0f, 1);
|
||||
|
||||
drawRect(0, 120, 301, 170, 0x0f, 0);
|
||||
drawString(309, 141, "Timer 1", 0x0f, 1);
|
||||
|
||||
drawRect(0, 180, 301, 230, 0x0f, 0);
|
||||
drawString(309, 201, "Timer 3", 0x0f, 1);
|
||||
}
|
||||
|
||||
void drawProgress(unsigned int core, unsigned int val) {
|
||||
unsigned char col = (core + 1) + ((core + 1) << 4);
|
||||
|
||||
// val should be 0-100
|
||||
if (val == 0) drawRect(1, (60 * core) + 1, 300, (60 * core) + 49, 0x00, 1);
|
||||
if (val > 0) drawRect(1, (60 * core) + 1, (val * 3), (60 * core) + 49, col, 1);
|
||||
}
|
||||
|
||||
void 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();
|
||||
|
||||
irq_disable();
|
||||
disable_interrupt_controller();
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
// ISR for incoming packet
|
||||
|
||||
debugstr("we are into isr now");
|
||||
// Re-enable Interrupts
|
||||
ENC_IRQHandler(&handle);
|
||||
|
||||
|
||||
|
||||
*/
|
||||
// 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_spi() {
|
||||
//while((REGS_AUX->mu_iir & 4) == 4) {
|
||||
debugstr("UART Recv: ");
|
||||
//uart_send(uart_recv());
|
||||
debugstr("\n");
|
||||
//}
|
||||
debugstr("button pressed");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
fb_init();
|
||||
|
||||
// Init GPIO interupt
|
||||
//init_GPIOinterrupt();
|
||||
|
||||
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_barrier();
|
||||
irq_enable();
|
||||
timer_init();
|
||||
|
||||
// Loop endlessly
|
||||
|
||||
core0_main();
|
||||
}
|
54
part13a-gpio-interrupts/kernel/kernel.h
Normal file
54
part13a-gpio-interrupts/kernel/kernel.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
#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),
|
||||
GPIO_INT_0 = (1 << 49),
|
||||
GPIO_INT_1 = (1 << 50),
|
||||
GPIO_INT_2 = (1 << 51),
|
||||
GPIO_INT_3 = (1 << 52),
|
||||
SPI_INT = (1 << 54)
|
||||
};
|
||||
|
||||
// from util.S
|
||||
void irq_init_vectors();
|
||||
void irq_enable();
|
||||
void irq_barrier();
|
||||
void irq_disable();
|
||||
void enable_interrupt_controller();
|
||||
void disable_interrupt_controller();
|
||||
|
||||
void handle_timer_1();
|
||||
void handle_timer_3();
|
||||
void handle_spi();
|
21
part13a-gpio-interrupts/kernel/utils.S
Normal file
21
part13a-gpio-interrupts/kernel/utils.S
Normal file
|
@ -0,0 +1,21 @@
|
|||
//D13.2.137 of ARMv8 reference manual
|
||||
.globl irq_init_vectors
|
||||
irq_init_vectors:
|
||||
adr x0, vectors
|
||||
msr vbar_el1, x0
|
||||
ret
|
||||
//C5.2.2 of ARMv8 reference manual
|
||||
.globl irq_enable
|
||||
irq_enable:
|
||||
msr daifclr, #2
|
||||
ret
|
||||
|
||||
.globl irq_disable
|
||||
irq_disable:
|
||||
msr daifset, #2
|
||||
ret
|
||||
|
||||
.globl irq_barrier
|
||||
irq_barrier:
|
||||
dsb sy
|
||||
ret
|
260
part13a-gpio-interrupts/lib/fb.c
Normal file
260
part13a-gpio-interrupts/lib/fb.c
Normal file
|
@ -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<x2) {
|
||||
if (p >= 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<newx + width;i++) {
|
||||
for (int j=newy;j<newy + height;j++) {
|
||||
offs = (j * pitch) + (i * 4);
|
||||
*((unsigned int*)(fb + offs)) = bitmap[i-newx][j-newy];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wait_msec(unsigned int n)
|
||||
{
|
||||
register unsigned long f, t, r;
|
||||
|
||||
// Get the current counter frequency
|
||||
asm volatile ("mrs %0, cntfrq_el0" : "=r"(f));
|
||||
// Read the current counter
|
||||
asm volatile ("mrs %0, cntpct_el0" : "=r"(t));
|
||||
// Calculate expire value for counter
|
||||
t+=((f/1000)*n)/1000;
|
||||
do{asm volatile ("mrs %0, cntpct_el0" : "=r"(r));}while(r<t);
|
||||
}
|
||||
|
||||
int strlen(const char *str) {
|
||||
const char *s;
|
||||
|
||||
for (s = str; *s; ++s);
|
||||
return (s - str);
|
||||
}
|
||||
|
||||
void debugstr(char *str) {
|
||||
if (curx + (strlen(str) * 8) >= 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(" ");
|
||||
}
|
199
part13a-gpio-interrupts/lib/io.c
Normal file
199
part13a-gpio-interrupts/lib/io.c
Normal file
|
@ -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(' ');
|
||||
}
|
39
part13a-gpio-interrupts/lib/mb.c
Normal file
39
part13a-gpio-interrupts/lib/mb.c
Normal file
|
@ -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;
|
||||
}
|
44
part13a-gpio-interrupts/lib/multicore.c
Normal file
44
part13a-gpio-interrupts/lib/multicore.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
CFILES = $(wildcard *.c lib/*.c kernel/*.c net/*.c tcpip/*.c)
|
||||
CFILES = $(wildcard *.c lib/*.c kernel/*.c net/*.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
|
||||
BOOTMNT ?= /media/cvdeveloper/bootfs
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
|
@ -16,5 +17,9 @@ 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
|
||||
|
||||
cp kernel8.img $(BOOTMNT)/kernel8.img
|
||||
cp config.txt $(BOOTMNT)/
|
||||
sync
|
||||
|
||||
clean:
|
||||
/bin/rm kernel8.elf */*.o *.img > /dev/null 2> /dev/null || true
|
211
part14-spi-ethernet-interrupt/README.md
Normal file
211
part14-spi-ethernet-interrupt/README.md
Normal file
|
@ -0,0 +1,211 @@
|
|||
Writing a "bare metal" operating system for Raspberry Pi 4 (Part 14)
|
||||
====================================================================
|
||||
|
||||
[< Go back to part13-interrupts](../part13-interrupts)
|
||||
|
||||
Bare metal Ethernet for under £10
|
||||
---------------------------------
|
||||
It's exciting to build your own OS, but until you give it the ability to communicate with the outside world, your possibilities are limited. Indeed, our simple Bluetooth comms got us up and running - but if we're to do anything meaningful then we need proper networking.
|
||||
|
||||
In this tutorial, we're going to connect to an external Ethernet controller (a network card, if you like) using the RPi4's Serial Peripheral Interface (SPI).
|
||||
|
||||
Things you'll need:
|
||||
|
||||
* An [ENC28J60 Ethernet module](https://www.amazon.co.uk/dp/B00DB76ZSK) - it cost me less than £6 and was worth every penny (n.b. code only tested on this exact model)
|
||||
* Some [female-to-female jumper cables](https://www.amazon.co.uk/dp/B072LN3HLG) - cost me less than £2.50
|
||||
* An Ethernet cable to connect to your Internet router
|
||||
|
||||
Connecting up the ENC28J60 Ethernet module
|
||||
------------------------------------------
|
||||
I followed the very helpful instructions [here](https://www.instructables.com/Super-Cheap-Ethernet-for-the-Raspberry-Pi/) to hook up the ENC28J60 to the RPi4's SPI0 interface.
|
||||
|
||||
We won't be connecting the interrupt line for now, so there are just six jumper leads (I've suggested colours) that need connecting:
|
||||
|
||||
| Pi pin | Pi GPIO | Jumper colour | ENC28J60 pin |
|
||||
| ------ | ----------- | ------------- | ------------ |
|
||||
| Pin 17 | +3V3 power | Red | VCC |
|
||||
| Pin 19 | GPIO10/MOSI | Green | SI |
|
||||
| Pin 20 | GND | Black | GND |
|
||||
| Pin 21 | GPIO09/MISO | Yellow | SO |
|
||||
| Pin 23 | GPIO11/SCLK | Blue | SCK |
|
||||
| Pin 24 | GPIO08/CE0 | Green | CS |
|
||||
|
||||
![GPIO location](../part3-helloworld/images/3-helloworld-pinloc.png)
|
||||
|
||||
Here's a (not very useful) photo of my RPi4 connected correctly:
|
||||
|
||||
![ENC28J60 connections](images/14-spi-ethernet-photo.jpg)
|
||||
|
||||
The SPI library
|
||||
---------------
|
||||
Let's start by looking at how we implement SPI communication.
|
||||
|
||||
I'm not going to write a long paper on how SPI works and why we need it, because it's [very well documented elsewhere](https://learn.sparkfun.com/tutorials/serial-peripheral-interface-spi/). It's recommended background reading, but not essential if all you want to do is get something working.
|
||||
|
||||
Look at _lib/spi.c_. It uses some of existing functions in _lib/io.c_ that you'll remember from earlier tutorials. In fact, I've added two functions to the _include/io.h_ header file so we can call them from our SPI library:
|
||||
|
||||
```c
|
||||
void gpio_setPinOutputBool(unsigned int pin_number, unsigned int onOrOff);
|
||||
void gpio_initOutputPinWithPullNone(unsigned int pin_number);
|
||||
```
|
||||
|
||||
Specifically, `spi_init()` sets GPIO 7, 9, 10, and 11 to use the ALT0 function. Cross-referencing with the [BCM2711 ARM Peripherals document](https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf), page 77, you'll see that this maps SPI0 to the GPIO header. GPIO 8 is mapped as an output pin, since we'll use this to signal to the ENC28J60 that we want to talk. In fact, the `spi_chip_select()` function takes a true/false (boolean) parameter which either sets or clears this pin.
|
||||
|
||||
Looking at the SPI0 register map on page 136, we see this reflected in our `REGS_SPI0` structure. This gives us handy access to the SPI0 peripheral's memory-mapped registers.
|
||||
|
||||
Our `spi_send_recv()` function then sets us up for some communcation:
|
||||
|
||||
* Sets the DLEN Register to the number of bytes to transfer (a length we passed into the function)
|
||||
* Clears the RX & TX FIFOs
|
||||
* Sets the Transfer Active (TA) flag
|
||||
|
||||
Then, whilst there's either data to write or data to read (and we haven't written/read more bytes than we asked for), we write to/read from the FIFO using the buffers we passed in. Once we think we're done, we wait until the SPI interface agrees i.e. the DONE flag in the CS Register is set. If there are extraneous bytes to read, we just throw them away (well, dump them to the screen for now because this shouldn't happen).
|
||||
|
||||
Finally, to be absolutely sure, we clear the TA flag.
|
||||
|
||||
I've then set up two convenient functions - `spi_send()` and `spi_recv()` - which exercise `spi_send_recv()`, mainly to make future code more readable.
|
||||
|
||||
The ENC28J60 drivers
|
||||
--------------------
|
||||
Let's now look into the _net/_ subdirectory.
|
||||
|
||||
Both _enc28j60.c_ and _enc28j60.h_ make up the driver code for the ENC28J60 Ethernet module. Whilst we could have laboured for months writing our own driver based on [the module's datasheet](http://ww1.microchip.com/downloads/en/devicedoc/39662c.pdf), I chose to leverage somebody else's hard work instead. It felt like a win that I could effortlessly bring somebody else's good code into my own OS! I did, however, make sure I understood what the code was doing at every turn (optional!).
|
||||
|
||||
Thanks to [this Github repository](https://github.com/wolfgangr/enc28j60) for saving me months of work. I made a very few changes to the code, but nothing worth documenting here. If you're keen to see how little I needed to change, clone the repo and make good use of the `diff` command.
|
||||
|
||||
What I did need to do is write some bridging code between the driver and the RPi4 hardware. Essentially, I'm talking about hooking up our SPI library to the driver - the whole reason for _encspi.c_.
|
||||
|
||||
It defines four functions that the driver requires (well documented in the _enc28j60.h_ file):
|
||||
|
||||
```c
|
||||
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);
|
||||
}
|
||||
```
|
||||
|
||||
Perhaps the most confusing aspect is the chip selection. Through a bit of trial & error I discovered that when GPIO08 is clear, the device is selected, and when it's set, the device is deselected. It turns out this is because the Chip Select pin on the ENC28J60 is active LOW and there's no invertor on the board (presumably to save costs), so GPIO08 must be LOW to enable the IC.
|
||||
|
||||
Some more timer functions
|
||||
-------------------------
|
||||
The only other thing our ENC28J60 driver requires is access to a couple of well-defined timer functions:
|
||||
|
||||
* `HAL_GetTick()` - returns the current number of timer ticks since start
|
||||
* `HAL_Delay()` - delays by a specified number of milliseconds
|
||||
|
||||
These are quickly implemented in _kernel/kernel.c_ and weren't too much of a stretch after _part13-interrupts_:
|
||||
|
||||
```c
|
||||
unsigned long HAL_GetTick(void) {
|
||||
unsigned int hi = REGS_TIMER->counter_hi;
|
||||
unsigned int lo = REGS_TIMER->counter_lo;
|
||||
|
||||
//double check hi value didn't change after setting it...
|
||||
if (hi != REGS_TIMER->counter_hi) {
|
||||
hi = REGS_TIMER->counter_hi;
|
||||
lo = REGS_TIMER->counter_lo;
|
||||
}
|
||||
|
||||
return ((unsigned long)hi << 32) | lo;
|
||||
}
|
||||
|
||||
void HAL_Delay(unsigned int ms) {
|
||||
unsigned long start = HAL_GetTick();
|
||||
|
||||
while(HAL_GetTick() < start + (ms * 1000));
|
||||
}
|
||||
```
|
||||
|
||||
Let's connect!
|
||||
--------------
|
||||
So we have a working driver that's interfacing with our hardware via _net/encspi.c_ and a few timer functions in _kernel/kernel.c_. Now what?
|
||||
|
||||
The design goals of our kernel's networking demo will be to:
|
||||
|
||||
1. Prove we can talk to the hardware
|
||||
2. Bring the network up successfully
|
||||
3. Prove we can connect to something else on the network and get a response
|
||||
|
||||
My proposals for how we fulfil these goals are:
|
||||
|
||||
1. Prove we can detect whether a network link has been established at a physical level (CAT5 cable plugged in and connected to a working switch)
|
||||
2. Rely on the ENC28J60 driver to tell us that we've started up successfully
|
||||
3. Handcraft and send an [ARP](https://en.wikipedia.org/wiki/Address_Resolution_Protocol) request and await an ARP response from my Internet router (the traditional way devices "find each other" on a network from a point of zero knowledge)
|
||||
|
||||
Look at _kernel/arp.c_. First we create a handle to reference our driver instance `ENC_HandleTypeDef handle`. We then initialise this structure in `init_network()`:
|
||||
|
||||
```c
|
||||
handle.Init.DuplexMode = ETH_MODE_HALFDUPLEX;
|
||||
handle.Init.MACAddr = myMAC;
|
||||
handle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
|
||||
handle.Init.InterruptEnableBits = EIE_LINKIE | EIE_PKTIE;
|
||||
```
|
||||
|
||||
This starts the module in half duplex mode (can't transmit & receive simultaneously), sets its MAC address (my favourite: `C0:FF:EE:C0:FF:EE`), tells the hardware to add its own packet checksums (we don't want to have to create them in software), and enables interrupt messages for "link up/down" and "packet received".
|
||||
|
||||
We then call the driver routine `ENC_Start(&handle)` and check it returns true (this fulfils design requirement 2 - the driver tells us we've started correctly). We go on to set the MAC address using `ENC_SetMacAddr(&handle)`.
|
||||
|
||||
This line waits until a physical network link has been established (fulfilling design requirement 1):
|
||||
|
||||
```c
|
||||
while (!(handle.LinkStatus & PHSTAT2_LSTAT)) ENC_IRQHandler(&handle);
|
||||
```
|
||||
|
||||
The driver's `ENC_IRQHandler(&handle)` routine would ordinarily be called when an interrupt was raised to refresh the driver status flags. Because we didn't hook up the interrupt line and to keep things simple, we're just polling in the software for now. When we see the `handle.LinkStatus` flag has the `PHSTAT2_LSTAT` bit set, we know the link is up (documented on page 24 of the module's datasheet).
|
||||
|
||||
Before we're done, we have to re-enable Ethernet interrupts (`ENC_IRQHandler()` disables them, but doesn't re-enable them - something I discovered by reading the code).
|
||||
|
||||
Sending/receiving an ARP
|
||||
------------------------
|
||||
To transmit on an Ethernet network, we need to format our packets correctly. The ENC28J60 deals with the physical layer (including the checksum, as we asked it to), so we only need concern ourselves with the data link layer - made up of a header, and a payload.
|
||||
|
||||
The header (our `EtherNetII` struct) is simply a destination and source MAC address, as well as a [16-bit packet type](https://en.wikipedia.org/wiki/EtherType). ARP packets, for example, have type `0x0806`. You'll note in our `#define ARPPACKET` that we've swapped the two bytes. This is because big-endianness is the dominant ordering in network protocols, and the RPi4 is a little-endian architecture (some reading may be required here!). We've had to do this across the board.
|
||||
|
||||
The payload is the full [ARP packet](https://en.wikipedia.org/wiki/Address_Resolution_Protocol) defined in the `ARP` struct. The `SendArpPacket()` function sets up the data we need in the structure (documented in code comments) and then uses driver calls to transmit the packet:
|
||||
|
||||
```c
|
||||
// Send the packet
|
||||
|
||||
if (ENC_RestoreTXBuffer(&handle, sizeof(ARP)) == 0) {
|
||||
debugstr("Sending ARP request.");
|
||||
debugcrlf();
|
||||
|
||||
ENC_WriteBuffer((unsigned char *)&arpPacket, sizeof(ARP));
|
||||
handle.transmitLength = sizeof(ARP);
|
||||
|
||||
ENC_Transmit(&handle);
|
||||
}
|
||||
```
|
||||
|
||||
`ENC_RestoreTXBuffer()` simply prepares the transmit buffer and returns 0 if successful. `ENC_WriteBuffer()` sends the packet to the ENC28J60 over the SPI. We then set the transmit buffer length in the driver status flags and call `ENC_Transmit()` to tell the ENC to send the packet across the network.
|
||||
|
||||
You'll see that the `arp_test()` function sends our first ARP this way. We tell it the IP of our router (`192.168.0.1` in my case), but we don't know its MAC address - that's what we want to find out. Once the ARP is sent, `arp_test()` then waits for received Ethernet packets, checks whether they're for us and, if they come from the router's IP address (therefore likely to be the ARP response to our request), we print out the router's MAC address.
|
||||
|
||||
This fulfils design requirement 3, and therefore we're done! All we need to do is ensure that _kernel/kernel.c_ calls our networking routines in the right order. I've chosen to do this on core 3 with a few easy-to-follow changes from where we left off in _part13-interrupts_. Essentially these are all the calls we need:
|
||||
|
||||
```c
|
||||
spi_init();
|
||||
init_network();
|
||||
arp_test();
|
||||
```
|
||||
|
||||
_Imagine how happy I was to see my router's (correct!) MAC address appear on-screen - a sign of life, and proof that my OS is actually networking!_
|
||||
|
||||
![ARP response](images/14-spi-ethernet-arp.jpg)
|
||||
|
||||
[Go to part15-tcpip-webserver >](../part15-tcpip-webserver)
|
114
part14-spi-ethernet-interrupt/boot/boot.S
Normal file
114
part14-spi-ethernet-interrupt/boot/boot.S
Normal file
|
@ -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
|
35
part14-spi-ethernet-interrupt/boot/link.ld
Normal file
35
part14-spi-ethernet-interrupt/boot/link.ld
Normal file
|
@ -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*) }
|
||||
}
|
44
part14-spi-ethernet-interrupt/boot/sysregs.h
Normal file
44
part14-spi-ethernet-interrupt/boot/sysregs.h
Normal file
|
@ -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
|
48
part14-spi-ethernet-interrupt/config.txt
Normal file
48
part14-spi-ethernet-interrupt/config.txt
Normal file
|
@ -0,0 +1,48 @@
|
|||
# For more options and information see
|
||||
# http://rptl.io/configtxt
|
||||
# Some settings may impact device functionality. See link above for details
|
||||
|
||||
# Uncomment some or all of these to enable the optional hardware interfaces
|
||||
#dtparam=i2c_arm=on
|
||||
#dtparam=i2s=on
|
||||
dtparam=spi=on
|
||||
|
||||
# Enable audio (loads snd_bcm2835)
|
||||
dtparam=audio=on
|
||||
|
||||
# Additional overlays and parameters are documented
|
||||
# /boot/firmware/overlays/README
|
||||
|
||||
# Automatically load overlays for detected cameras
|
||||
camera_auto_detect=1
|
||||
|
||||
# Automatically load overlays for detected DSI displays
|
||||
display_auto_detect=1
|
||||
|
||||
# Automatically load initramfs files, if found
|
||||
auto_initramfs=1
|
||||
|
||||
# Enable DRM VC4 V3D driver
|
||||
dtoverlay=vc4-kms-v3d
|
||||
max_framebuffers=2
|
||||
|
||||
# Don't have the firmware create an initial video= setting in cmdline.txt.
|
||||
# Use the kernel's default instead.
|
||||
disable_fw_kms_setup=1
|
||||
|
||||
# Run in 64-bit mode
|
||||
arm_64bit=1
|
||||
|
||||
# Disable compensation for displays with overscan
|
||||
disable_overscan=1
|
||||
|
||||
# Run as fast as firmware / board allows
|
||||
arm_boost=1
|
||||
|
||||
[all]
|
||||
kernel_old=1
|
||||
disable_commandline_tags=1
|
||||
core_freq_min=500
|
||||
hdmi_group=1
|
||||
hdmi_mode=16
|
||||
|
BIN
part14-spi-ethernet-interrupt/images/14-spi-ethernet-arp.jpg
Normal file
BIN
part14-spi-ethernet-interrupt/images/14-spi-ethernet-arp.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
BIN
part14-spi-ethernet-interrupt/images/14-spi-ethernet-photo.jpg
Normal file
BIN
part14-spi-ethernet-interrupt/images/14-spi-ethernet-photo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
19
part14-spi-ethernet-interrupt/include/io.h
Normal file
19
part14-spi-ethernet-interrupt/include/io.h
Normal file
|
@ -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);
|
11
part14-spi-ethernet-interrupt/include/multicore.h
Normal file
11
part14-spi-ethernet-interrupt/include/multicore.h
Normal file
|
@ -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);
|
5
part14-spi-ethernet-interrupt/include/spi.h
Normal file
5
part14-spi-ethernet-interrupt/include/spi.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
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_chip_select(unsigned char chip_select);
|
229
part14-spi-ethernet-interrupt/kernel/arp.c
Normal file
229
part14-spi-ethernet-interrupt/kernel/arp.c
Normal file
|
@ -0,0 +1,229 @@
|
|||
#include "../net/enc28j60.h"
|
||||
#include "../include/io.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] = { 10, 7, 3, 20 };
|
||||
|
||||
// IP Address of the router, whose hardware address we will find using the ARP request
|
||||
|
||||
uint8_t routerIP[4] = { 10, 7, 3, 18 };
|
||||
|
||||
// HELPER FUNCTIONS
|
||||
|
||||
void *memset(void *dest, uint8_t val, uint16_t len)
|
||||
{
|
||||
uint8_t *ptr = dest;
|
||||
while (len-- > 0)
|
||||
*ptr++ = val;
|
||||
return dest;
|
||||
}
|
||||
|
||||
void *memcpy(void *dest, const void *src, uint16_t 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
|
||||
|
||||
if (ENC_RestoreTXBuffer(&handle, sizeof(ARP)) == 0) {
|
||||
uart_writeText("Sending ARP request.\n");
|
||||
|
||||
/*uart_writeText("My MAC is ");
|
||||
uart_hex(myMAC[0]);
|
||||
uart_writeText(":");
|
||||
uart_hex(myMAC[1]);
|
||||
uart_writeText(":");
|
||||
uart_hex(myMAC[2]);
|
||||
uart_writeText(":");
|
||||
uart_hex(myMAC[3]);
|
||||
uart_writeText(":");
|
||||
uart_hex(myMAC[4]);
|
||||
uart_writeText(":");
|
||||
uart_hex(myMAC[5]);
|
||||
uart_writeText("\n");*/
|
||||
|
||||
ENC_WriteBuffer((unsigned char *)&arpPacket, sizeof(ARP));
|
||||
handle.transmitLength = sizeof(ARP);
|
||||
|
||||
ENC_Transmit(&handle);
|
||||
}
|
||||
}
|
||||
|
||||
void arp_test(void)
|
||||
{
|
||||
ARP *checkPacket;
|
||||
|
||||
SendArpPacket(routerIP, myMAC);
|
||||
|
||||
uart_writeText("Waiting for ARP response.\n");
|
||||
|
||||
while (1) {
|
||||
while (!ENC_GetReceivedFrame(&handle));
|
||||
|
||||
uint16_t len = handle.RxFrameInfos.length;
|
||||
uint8_t *buffer = (uint8_t *)handle.RxFrameInfos.buffer;
|
||||
checkPacket = (ARP *)buffer;
|
||||
|
||||
if (len > 0) {
|
||||
if (!memcmp(checkPacket->senderIP, routerIP, 4)) {
|
||||
// Success! We have found our router's MAC address
|
||||
|
||||
memcpy(routerMAC, checkPacket->senderMAC, 6);
|
||||
uart_writeText("Router MAC is ");
|
||||
uart_hex(routerMAC[0]);
|
||||
uart_writeText(":");
|
||||
uart_hex(routerMAC[1]);
|
||||
uart_writeText(":");
|
||||
uart_hex(routerMAC[2]);
|
||||
uart_writeText(":");
|
||||
uart_hex(routerMAC[3]);
|
||||
uart_writeText(":");
|
||||
uart_hex(routerMAC[4]);
|
||||
uart_writeText(":");
|
||||
uart_hex(routerMAC[5]);
|
||||
uart_writeText("\n");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
uart_writeText("Starting network up.\n");
|
||||
if (!ENC_Start(&handle)) {
|
||||
uart_writeText("could not initialise network card.\n");
|
||||
} else {
|
||||
uart_writeText("Setting MAC address to C0:FF:FE:C0:FF:FE.\n");
|
||||
|
||||
ENC_SetMacAddr(&handle);
|
||||
|
||||
uart_writeText("Network card successfully initialised.\n");
|
||||
}
|
||||
|
||||
uart_writeText("Waiting for ifup... \n");
|
||||
while (!(handle.LinkStatus & PHSTAT2_LSTAT)) ENC_IRQHandler(&handle);
|
||||
uart_writeText("done.\n");
|
||||
|
||||
// Re-enable global interrupts
|
||||
ENC_EnableInterrupts(EIE_INTIE);
|
||||
}
|
27
part14-spi-ethernet-interrupt/kernel/irq.c
Normal file
27
part14-spi-ethernet-interrupt/kernel/irq.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "kernel.h"
|
||||
|
||||
void enable_interrupt_controller() {
|
||||
REGS_IRQ->irq0_enable_0 = SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3;
|
||||
}
|
||||
|
||||
void disable_interrupt_controller() {
|
||||
REGS_IRQ->irq0_enable_0 = 0;
|
||||
}
|
||||
|
||||
void handle_irq() {
|
||||
unsigned int irq = REGS_IRQ->irq0_pending_0;
|
||||
|
||||
while(irq & (SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3)) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
160
part14-spi-ethernet-interrupt/kernel/irqentry.S
Normal file
160
part14-spi-ethernet-interrupt/kernel/irqentry.S
Normal file
|
@ -0,0 +1,160 @@
|
|||
#define SYNC_INVALID_EL1t 0
|
||||
#define IRQ_INVALID_EL1t 1
|
||||
#define FIQ_INVALID_EL1t 2
|
||||
#define ERROR_INVALID_EL1t 3
|
||||
|
||||
#define SYNC_INVALID_EL1h 4
|
||||
#define IRQ_INVALID_EL1h 5
|
||||
#define FIQ_INVALID_EL1h 6
|
||||
#define ERROR_INVALID_EL1h 7
|
||||
|
||||
#define SYNC_INVALID_EL0_64 8
|
||||
#define IRQ_INVALID_EL0_64 9
|
||||
#define FIQ_INVALID_EL0_64 10
|
||||
#define ERROR_INVALID_EL0_64 11
|
||||
|
||||
#define SYNC_INVALID_EL0_32 12
|
||||
#define IRQ_INVALID_EL0_32 13
|
||||
#define FIQ_INVALID_EL0_32 14
|
||||
#define ERROR_INVALID_EL0_32 15
|
||||
|
||||
//stack frame size
|
||||
#define S_FRAME_SIZE 256
|
||||
|
||||
.macro kernel_entry
|
||||
sub sp, sp, #S_FRAME_SIZE
|
||||
stp x0, x1, [sp, #16 * 0]
|
||||
stp x2, x3, [sp, #16 * 1]
|
||||
stp x4, x5, [sp, #16 * 2]
|
||||
stp x6, x7, [sp, #16 * 3]
|
||||
stp x8, x9, [sp, #16 * 4]
|
||||
stp x10, x11, [sp, #16 * 5]
|
||||
stp x12, x13, [sp, #16 * 6]
|
||||
stp x14, x15, [sp, #16 * 7]
|
||||
stp x16, x17, [sp, #16 * 8]
|
||||
stp x18, x19, [sp, #16 * 9]
|
||||
stp x20, x21, [sp, #16 * 10]
|
||||
stp x22, x23, [sp, #16 * 11]
|
||||
stp x24, x25, [sp, #16 * 12]
|
||||
stp x26, x27, [sp, #16 * 13]
|
||||
stp x28, x29, [sp, #16 * 14]
|
||||
str x30, [sp, #16 * 15]
|
||||
.endm
|
||||
|
||||
.macro kernel_exit
|
||||
ldp x0, x1, [sp, #16 * 0]
|
||||
ldp x2, x3, [sp, #16 * 1]
|
||||
ldp x4, x5, [sp, #16 * 2]
|
||||
ldp x6, x7, [sp, #16 * 3]
|
||||
ldp x8, x9, [sp, #16 * 4]
|
||||
ldp x10, x11, [sp, #16 * 5]
|
||||
ldp x12, x13, [sp, #16 * 6]
|
||||
ldp x14, x15, [sp, #16 * 7]
|
||||
ldp x16, x17, [sp, #16 * 8]
|
||||
ldp x18, x19, [sp, #16 * 9]
|
||||
ldp x20, x21, [sp, #16 * 10]
|
||||
ldp x22, x23, [sp, #16 * 11]
|
||||
ldp x24, x25, [sp, #16 * 12]
|
||||
ldp x26, x27, [sp, #16 * 13]
|
||||
ldp x28, x29, [sp, #16 * 14]
|
||||
ldr x30, [sp, #16 * 15]
|
||||
add sp, sp, #S_FRAME_SIZE
|
||||
eret
|
||||
.endm
|
||||
|
||||
.macro handle_invalid_entry type
|
||||
kernel_entry
|
||||
mov x0, #\type
|
||||
mrs x1, esr_el1
|
||||
mrs x2, elr_el1
|
||||
|
||||
// We could pass this to a function to print an error here
|
||||
// e.g. bl show_invalid_entry_message
|
||||
//
|
||||
// For now we'll just hang
|
||||
|
||||
b err_hang
|
||||
.endm
|
||||
|
||||
.macro ventry label
|
||||
.align 7
|
||||
b \label
|
||||
.endm
|
||||
|
||||
//Exception vectors table
|
||||
.align 11
|
||||
.globl vectors
|
||||
vectors:
|
||||
ventry sync_invalid_el1t // Synchronous EL1t
|
||||
ventry irq_invalid_el1t // IRQ EL1t
|
||||
ventry fiq_invalid_el1t // FIQ EL1t
|
||||
ventry error_invalid_el1t // Error EL1t
|
||||
|
||||
ventry sync_invalid_el1h // Synchronous EL1h
|
||||
ventry handle_el1_irq // IRQ EL1h
|
||||
ventry fiq_invalid_el1h // FIQ EL1h
|
||||
ventry error_invalid_el1h // Error EL1h
|
||||
|
||||
ventry sync_invalid_el0_64 // Synchronous 64-bit EL0
|
||||
ventry irq_invalid_el0_64 // IRQ 64-bit EL0
|
||||
ventry fiq_invalid_el0_64 // FIQ 64-bit EL0
|
||||
ventry error_invalid_el0_64 // Error 64-bit EL0
|
||||
|
||||
ventry sync_invalid_el0_32 // Synchronous 32-bit EL0
|
||||
ventry irq_invalid_el0_32 // IRQ 32-bit EL0
|
||||
ventry fiq_invalid_el0_32 // FIQ 32-bit EL0
|
||||
ventry error_invalid_el0_32 // Error 32-bit EL0
|
||||
|
||||
|
||||
sync_invalid_el1t:
|
||||
handle_invalid_entry SYNC_INVALID_EL1t
|
||||
|
||||
irq_invalid_el1t:
|
||||
handle_invalid_entry IRQ_INVALID_EL1t
|
||||
|
||||
fiq_invalid_el1t:
|
||||
handle_invalid_entry FIQ_INVALID_EL1t
|
||||
|
||||
error_invalid_el1t:
|
||||
handle_invalid_entry ERROR_INVALID_EL1t
|
||||
|
||||
sync_invalid_el1h:
|
||||
handle_invalid_entry SYNC_INVALID_EL1h
|
||||
|
||||
fiq_invalid_el1h:
|
||||
handle_invalid_entry FIQ_INVALID_EL1h
|
||||
|
||||
error_invalid_el1h:
|
||||
handle_invalid_entry ERROR_INVALID_EL1h
|
||||
|
||||
sync_invalid_el0_64:
|
||||
handle_invalid_entry SYNC_INVALID_EL0_64
|
||||
|
||||
irq_invalid_el0_64:
|
||||
handle_invalid_entry IRQ_INVALID_EL0_64
|
||||
|
||||
fiq_invalid_el0_64:
|
||||
handle_invalid_entry FIQ_INVALID_EL0_64
|
||||
|
||||
error_invalid_el0_64:
|
||||
handle_invalid_entry ERROR_INVALID_EL0_64
|
||||
|
||||
sync_invalid_el0_32:
|
||||
handle_invalid_entry SYNC_INVALID_EL0_32
|
||||
|
||||
irq_invalid_el0_32:
|
||||
handle_invalid_entry IRQ_INVALID_EL0_32
|
||||
|
||||
fiq_invalid_el0_32:
|
||||
handle_invalid_entry FIQ_INVALID_EL0_32
|
||||
|
||||
error_invalid_el0_32:
|
||||
handle_invalid_entry ERROR_INVALID_EL0_32
|
||||
|
||||
handle_el1_irq:
|
||||
kernel_entry
|
||||
bl handle_irq
|
||||
kernel_exit
|
||||
|
||||
.globl err_hang
|
||||
err_hang: b err_hang
|
150
part14-spi-ethernet-interrupt/kernel/kernel.c
Normal file
150
part14-spi-ethernet-interrupt/kernel/kernel.c
Normal file
|
@ -0,0 +1,150 @@
|
|||
#include "../include/io.h"
|
||||
#include "../include/spi.h"
|
||||
#include "../include/multicore.h"
|
||||
#include "kernel.h"
|
||||
|
||||
void wait_msec(unsigned int n)
|
||||
{
|
||||
register unsigned long f, t, r;
|
||||
|
||||
// Get the current counter frequency
|
||||
asm volatile ("mrs %0, cntfrq_el0" : "=r"(f));
|
||||
// Read the current counter
|
||||
asm volatile ("mrs %0, cntpct_el0" : "=r"(t));
|
||||
// Calculate expire value for counter
|
||||
t+=((f/1000)*n)/1000;
|
||||
do{asm volatile ("mrs %0, cntpct_el0" : "=r"(r));}while(r<t);
|
||||
}
|
||||
void core3_main(void) {
|
||||
clear_core3(); // Only run once
|
||||
|
||||
uart_writeText("testing the network card\n");
|
||||
|
||||
spi_init();
|
||||
init_network();
|
||||
arp_test();
|
||||
|
||||
while(1) {
|
||||
arp_test();
|
||||
wait_msec(0x100000);
|
||||
}
|
||||
}
|
||||
|
||||
void core0_main(void)
|
||||
{
|
||||
unsigned int core0_val = 0;
|
||||
|
||||
while (core0_val <= 1000) {
|
||||
wait_msec(0x100000);
|
||||
uart_writeText("another second on core 0\n");
|
||||
//drawProgress(0, core0_val/10);
|
||||
core0_val++;
|
||||
}
|
||||
|
||||
uart_writeText("Core 0 done.\n");
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
uart_writeText("Core 1 done.\n");
|
||||
|
||||
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 {
|
||||
uart_writeText("Timer 1 done.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void handle_timer_3() {
|
||||
timer3_val += timer3_int;
|
||||
REGS_TIMER->compare[3] = timer3_val;
|
||||
REGS_TIMER->control_status |= SYS_TIMER_IRQ_3;
|
||||
|
||||
unsigned int progval = timer3_val / timer3_int;
|
||||
if (progval <= 100); // drawProgress(3, progval);
|
||||
}
|
||||
|
||||
unsigned long HAL_GetTick(void) {
|
||||
unsigned int hi = REGS_TIMER->counter_hi;
|
||||
unsigned int lo = REGS_TIMER->counter_lo;
|
||||
|
||||
//double check hi value didn't change after setting it...
|
||||
if (hi != REGS_TIMER->counter_hi) {
|
||||
hi = REGS_TIMER->counter_hi;
|
||||
lo = REGS_TIMER->counter_lo;
|
||||
}
|
||||
|
||||
return ((unsigned long)hi << 32) | lo;
|
||||
}
|
||||
|
||||
void HAL_Delay(unsigned int ms) {
|
||||
unsigned long start = HAL_GetTick();
|
||||
|
||||
while(HAL_GetTick() < start + (ms * 1000));
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
uart_init();
|
||||
uart_writeText("uart initialised.\n");
|
||||
|
||||
//uart_writeText("Kick it off on core 1\n");
|
||||
//start_core1(core1_main);
|
||||
|
||||
// Kick off the timers
|
||||
|
||||
irq_init_vectors();
|
||||
enable_interrupt_controller();
|
||||
irq_barrier();
|
||||
irq_enable();
|
||||
timer_init();
|
||||
|
||||
uart_writeText("Kick it off on core 3\n");
|
||||
|
||||
start_core3(core3_main);
|
||||
|
||||
//uart_writeText("Kick it off on core 0\n");
|
||||
//core0_main();
|
||||
|
||||
// Disable IRQs and loop endlessly
|
||||
|
||||
irq_disable();
|
||||
disable_interrupt_controller();
|
||||
|
||||
while(1);
|
||||
}
|
51
part14-spi-ethernet-interrupt/kernel/kernel.h
Normal file
51
part14-spi-ethernet-interrupt/kernel/kernel.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
#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_barrier();
|
||||
void irq_disable();
|
||||
void enable_interrupt_controller();
|
||||
void disable_interrupt_controller();
|
||||
|
||||
void handle_timer_1();
|
||||
void handle_timer_3();
|
||||
|
||||
void init_network(void);
|
||||
void arp_test(void);
|
||||
void wait_msec(unsigned int n);
|
20
part14-spi-ethernet-interrupt/kernel/utils.S
Normal file
20
part14-spi-ethernet-interrupt/kernel/utils.S
Normal file
|
@ -0,0 +1,20 @@
|
|||
.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
|
||||
|
||||
.globl irq_barrier
|
||||
irq_barrier:
|
||||
dsb sy
|
||||
ret
|
199
part14-spi-ethernet-interrupt/lib/io.c
Normal file
199
part14-spi-ethernet-interrupt/lib/io.c
Normal file
|
@ -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(' ');
|
||||
}
|
39
part14-spi-ethernet-interrupt/lib/mb.c
Normal file
39
part14-spi-ethernet-interrupt/lib/mb.c
Normal file
|
@ -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;
|
||||
}
|
44
part14-spi-ethernet-interrupt/lib/multicore.c
Normal file
44
part14-spi-ethernet-interrupt/lib/multicore.c
Normal file
|
@ -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);
|
||||
}
|
99
part14-spi-ethernet-interrupt/lib/spi.c
Normal file
99
part14-spi-ethernet-interrupt/lib/spi.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
#include "../include/io.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;
|
||||
uart_hex(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);
|
||||
}
|
1450
part14-spi-ethernet-interrupt/net/enc28j60.c
Normal file
1450
part14-spi-ethernet-interrupt/net/enc28j60.c
Normal file
File diff suppressed because it is too large
Load diff
816
part14-spi-ethernet-interrupt/net/enc28j60.h
Normal file
816
part14-spi-ethernet-interrupt/net/enc28j60.h
Normal file
|
@ -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 <gnutt@nuttx.org>
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* <h2><center>© COPYRIGHT(c) 2015 Christian Schoffit</center></h2>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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 <EFBFBD>s
|
||||
*
|
||||
* Parameters:
|
||||
* us - The amount of time to wait in <EFBFBD>s
|
||||
*
|
||||
* Returned Value:
|
||||
* none
|
||||
*
|
||||
* Assumptions:
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void up_udelay(uint32_t us);
|
||||
|
||||
#endif /* ENC28J60_H_INCLUDED */
|
21
part14-spi-ethernet-interrupt/net/encspi.c
Normal file
21
part14-spi-ethernet-interrupt/net/encspi.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -1,20 +1,25 @@
|
|||
BOOTMNT ?= /media/cvdeveloper/bootfs
|
||||
|
||||
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
|
||||
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
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
%.o: %.S
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -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
|
||||
$(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
|
||||
cp kernel8.img $(BOOTMNT)/kernel8.img
|
||||
cp config.txt $(BOOTMNT)/
|
||||
sync
|
||||
|
||||
clean:
|
||||
/bin/rm kernel8.elf */*.o *.img > /dev/null 2> /dev/null || true
|
||||
|
|
53
part15-tcpip-webserver/config.txt
Normal file
53
part15-tcpip-webserver/config.txt
Normal file
|
@ -0,0 +1,53 @@
|
|||
# For more options and information see
|
||||
# http://rptl.io/configtxt
|
||||
# Some settings may impact device functionality. See link above for details
|
||||
|
||||
# Uncomment some or all of these to enable the optional hardware interfaces
|
||||
#dtparam=i2c_arm=on
|
||||
#dtparam=i2s=on
|
||||
#dtparam=spi=on
|
||||
|
||||
# Enable audio (loads snd_bcm2835)
|
||||
dtparam=audio=on
|
||||
|
||||
# Additional overlays and parameters are documented
|
||||
# /boot/firmware/overlays/README
|
||||
|
||||
# Automatically load overlays for detected cameras
|
||||
camera_auto_detect=1
|
||||
|
||||
# Automatically load overlays for detected DSI displays
|
||||
display_auto_detect=1
|
||||
|
||||
# Automatically load initramfs files, if found
|
||||
auto_initramfs=1
|
||||
|
||||
# Enable DRM VC4 V3D driver
|
||||
dtoverlay=vc4-kms-v3d
|
||||
max_framebuffers=2
|
||||
|
||||
# Don't have the firmware create an initial video= setting in cmdline.txt.
|
||||
# Use the kernel's default instead.
|
||||
disable_fw_kms_setup=1
|
||||
|
||||
# Run in 64-bit mode
|
||||
arm_64bit=1
|
||||
|
||||
# Disable compensation for displays with overscan
|
||||
disable_overscan=1
|
||||
|
||||
# Run as fast as firmware / board allows
|
||||
arm_boost=1
|
||||
|
||||
[cm4]
|
||||
# Enable host mode on the 2711 built-in XHCI USB controller.
|
||||
# This line should be removed if the legacy DWC2 controller is required
|
||||
# (e.g. for USB device mode) or if USB support is not required.
|
||||
otg_mode=1
|
||||
|
||||
[all]
|
||||
kernel_old=1
|
||||
disable_commandline_tags=1
|
||||
core_freq_min=500
|
||||
hdmi_group=1
|
||||
hdmi_mode=16
|
|
@ -59,7 +59,7 @@ ENC_HandleTypeDef handle;
|
|||
unsigned char myMAC[6] = { 0xc0, 0xff, 0xee, 0xc0, 0xff, 0xee };
|
||||
|
||||
// IP address to be assigned to the ENC28J60
|
||||
unsigned char deviceIP[4] = { 192, 168, 0, 66 };
|
||||
unsigned char deviceIP[4] = { 10, 7, 3, 20 };
|
||||
|
||||
void init_network(void)
|
||||
{
|
||||
|
|
26
part15a-tcpip-webserver-gpio-interrupt/Makefile
Normal file
26
part15a-tcpip-webserver-gpio-interrupt/Makefile
Normal file
|
@ -0,0 +1,26 @@
|
|||
|
||||
BOOTMNT ?= /media/cvdeveloper/bootfs
|
||||
|
||||
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 -O2 -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
|
||||
cp kernel8.img $(BOOTMNT)/kernel8.img
|
||||
cp config.txt $(BOOTMNT)/
|
||||
sync
|
||||
|
||||
clean:
|
||||
/bin/rm kernel8.elf */*.o *.img > /dev/null 2> /dev/null || true
|
96
part15a-tcpip-webserver-gpio-interrupt/README.md
Normal file
96
part15a-tcpip-webserver-gpio-interrupt/README.md
Normal file
|
@ -0,0 +1,96 @@
|
|||
Writing a "bare metal" operating system for Raspberry Pi 4 (Part 15)
|
||||
====================================================================
|
||||
|
||||
[< Go back to part14-spi-ethernet](../part14-spi-ethernet)
|
||||
|
||||
Adding a TCP/IP stack
|
||||
---------------------
|
||||
Having achieved "proof of life" from our Ethernet module in _part14-spi-ethernet_, you're doubtless wondering how to go from there to serving web pages, posting tweets on Twitter or perhaps even just simply responding to a ping!
|
||||
|
||||
This is where you'll need a fully-fledged TCP/IP stack that goes way beyond handcrafted ARPs, implementing many more protocols to achieve efficient bi-directional communication.
|
||||
|
||||
In this part we make use of some code from Guido Socher of [tuxgraphics.org](http://tuxgraphics.org/), designed to be a lightweight TCP/IP stack for embedded devices. I chose this because it was super simple to get working (or "port"), but you might want to look at [LwIP](https://en.wikipedia.org/wiki/LwIP) if you need something more advanced.
|
||||
|
||||
The code
|
||||
--------
|
||||
Most of the new code is in the _tcpip/_ subdirectory. I actually came across it in [this tarball](http://tuxgraphics.org/common/src2/article09051/eth_tcp_client_server-dhcp-5.10.tar.gz) and, again, made only a very few cosmetic changes (`diff` is your friend!).
|
||||
|
||||
It did require me to expose the `strlen()` function we implemented in _lib/fb.c_, so that's added to _include/fb.h_. Similarly, we expose the `memcpy()` function we implemented in _kernel/kernel.c_, so that's added to _kernel/kernel.h_.
|
||||
|
||||
I also needed a single function that tells the ENC to send a packet. Nothing new here, just different packaging:
|
||||
|
||||
```c
|
||||
void enc28j60PacketSend(unsigned short buflen, void *buffer) {
|
||||
if (ENC_RestoreTXBuffer(&handle, buflen) == 0) {
|
||||
ENC_WriteBuffer((unsigned char *) buffer, buflen);
|
||||
handle.transmitLength = buflen;
|
||||
ENC_Transmit(&handle);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This was also added to _kernel/kernel.h_.
|
||||
|
||||
What happened to _arp.c_?
|
||||
-------------------------
|
||||
You'll notice that I've merged _arp.c_ and _kernel.c_. I have also gone away from doing anything wih multicore or IRQ timers to keep this kernel simple. We still initialise the network card in exactly the same way but, when we're done, we call this function in Guido's code:
|
||||
|
||||
```c
|
||||
init_udp_or_www_server(myMAC, deviceIP);
|
||||
```
|
||||
|
||||
This tells the TCP/IP library who we are, so we're all on the same page!
|
||||
|
||||
Finally, and aside from a little cleanup (eg. moving the HAL/system timer functions to _lib/io.c_ with the commensurate changes to _include/io.h_), the major change is the new `serve()` function:
|
||||
|
||||
```c
|
||||
void serve(void)
|
||||
{
|
||||
while (1) {
|
||||
while (!ENC_GetReceivedFrame(&handle));
|
||||
|
||||
uint8_t *buf = (uint8_t *)handle.RxFrameInfos.buffer;
|
||||
uint16_t len = handle.RxFrameInfos.length;
|
||||
uint16_t dat_p = packetloop_arp_icmp_tcp(buf, len);
|
||||
|
||||
if (dat_p != 0) {
|
||||
debugstr("Incoming web request... ");
|
||||
|
||||
if (strncmp("GET ", (char *)&(buf[dat_p]), 4) != 0) {
|
||||
debugstr("not GET");
|
||||
dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 401 Unauthorized\r\nContent-Type: text/html\r\n\r\n<h1>ERROR</h1>");
|
||||
} else {
|
||||
if (strncmp("/ ", (char *)&(buf[dat_p+4]), 2) == 0) {
|
||||
// just one web page in the "root directory" of the web server
|
||||
debugstr("GET root");
|
||||
dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Hello world!</h1>");
|
||||
} else {
|
||||
// just one web page not in the "root directory" of the web server
|
||||
debugstr("GET not root");
|
||||
dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Goodbye cruel world.</h1>");
|
||||
}
|
||||
}
|
||||
|
||||
www_server_reply(buf, dat_p); // send web page data
|
||||
debugcrlf();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is an infinite loop which waits for an incoming packet and then firstly passes it to Guido's `packetloop_arp_icmp_tcp()` function. This function implements some useful things, like responding to pings. I modified the routine to print a message to the screen when it sends a "pong" (look from line 1371 of _tcpip/ip_arp_udp_tcp.c_), so we can see when it's in action!
|
||||
|
||||
Examining the return value of `packetloop_arp_icmp_tcp()` then allows us to check whether there is an incoming web request, since we've configured the TCP/IP library to be a web server in _tcpip/ip_config.h_ with `#define WWW_server`.
|
||||
|
||||
We then serve responses based on three possible cases:
|
||||
|
||||
* The incoming request is not a GET request (eg. maybe it's a HEAD request) - you can simulate this using the `curl` tool: `curl -I 192.168.0.66`
|
||||
* The incoming request is a GET request for the root web page `/` - `curl 192.168.0.66/`
|
||||
* The incoming request is a GET request for any non-root web page - eg. `curl 192.168.0.66/babbleberry/monkey`
|
||||
|
||||
I recommend reading [this page](http://tuxgraphics.org/electronics/200905/embedded-tcp-ip-stack.shtml) for a full explanation. The code I have ported is very similar to what you see there.
|
||||
|
||||
_Imagine my excitement when I built, ran and could ping my RPi4 at 192.168.0.66 and get a web response to my browser on both my laptop and my iPhone!_
|
||||
|
||||
![Pinging from my iPhone](images/15-tcpip-webserver-pinging.jpg)
|
||||
![Browsing from my laptop](images/15-tcpip-webserver-browser.png)
|
114
part15a-tcpip-webserver-gpio-interrupt/boot/boot.S
Normal file
114
part15a-tcpip-webserver-gpio-interrupt/boot/boot.S
Normal file
|
@ -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
|
35
part15a-tcpip-webserver-gpio-interrupt/boot/link.ld
Normal file
35
part15a-tcpip-webserver-gpio-interrupt/boot/link.ld
Normal file
|
@ -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*) }
|
||||
}
|
44
part15a-tcpip-webserver-gpio-interrupt/boot/sysregs.h
Normal file
44
part15a-tcpip-webserver-gpio-interrupt/boot/sysregs.h
Normal file
|
@ -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
|
53
part15a-tcpip-webserver-gpio-interrupt/config.txt
Normal file
53
part15a-tcpip-webserver-gpio-interrupt/config.txt
Normal file
|
@ -0,0 +1,53 @@
|
|||
# For more options and information see
|
||||
# http://rptl.io/configtxt
|
||||
# Some settings may impact device functionality. See link above for details
|
||||
|
||||
# Uncomment some or all of these to enable the optional hardware interfaces
|
||||
#dtparam=i2c_arm=on
|
||||
#dtparam=i2s=on
|
||||
#dtparam=spi=on
|
||||
|
||||
# Enable audio (loads snd_bcm2835)
|
||||
dtparam=audio=on
|
||||
|
||||
# Additional overlays and parameters are documented
|
||||
# /boot/firmware/overlays/README
|
||||
|
||||
# Automatically load overlays for detected cameras
|
||||
camera_auto_detect=1
|
||||
|
||||
# Automatically load overlays for detected DSI displays
|
||||
display_auto_detect=1
|
||||
|
||||
# Automatically load initramfs files, if found
|
||||
auto_initramfs=1
|
||||
|
||||
# Enable DRM VC4 V3D driver
|
||||
dtoverlay=vc4-kms-v3d
|
||||
max_framebuffers=2
|
||||
|
||||
# Don't have the firmware create an initial video= setting in cmdline.txt.
|
||||
# Use the kernel's default instead.
|
||||
disable_fw_kms_setup=1
|
||||
|
||||
# Run in 64-bit mode
|
||||
arm_64bit=1
|
||||
|
||||
# Disable compensation for displays with overscan
|
||||
disable_overscan=1
|
||||
|
||||
# Run as fast as firmware / board allows
|
||||
arm_boost=1
|
||||
|
||||
[cm4]
|
||||
# Enable host mode on the 2711 built-in XHCI USB controller.
|
||||
# This line should be removed if the legacy DWC2 controller is required
|
||||
# (e.g. for USB device mode) or if USB support is not required.
|
||||
otg_mode=1
|
||||
|
||||
[all]
|
||||
kernel_old=1
|
||||
disable_commandline_tags=1
|
||||
core_freq_min=500
|
||||
hdmi_group=1
|
||||
hdmi_mode=16
|
Binary file not shown.
After Width: | Height: | Size: 519 KiB |
BIN
part15a-tcpip-webserver-gpio-interrupt/images/15-tcpip-webserver-pinging.jpg
Executable file
BIN
part15a-tcpip-webserver-gpio-interrupt/images/15-tcpip-webserver-pinging.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
14
part15a-tcpip-webserver-gpio-interrupt/include/fb.h
Normal file
14
part15a-tcpip-webserver-gpio-interrupt/include/fb.h
Normal file
|
@ -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);
|
21
part15a-tcpip-webserver-gpio-interrupt/include/io.h
Normal file
21
part15a-tcpip-webserver-gpio-interrupt/include/io.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#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);
|
||||
unsigned long HAL_GetTick(void);
|
||||
void HAL_Delay(unsigned int ms);
|
34
part15a-tcpip-webserver-gpio-interrupt/include/mb.h
Normal file
34
part15a-tcpip-webserver-gpio-interrupt/include/mb.h
Normal file
|
@ -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);
|
11
part15a-tcpip-webserver-gpio-interrupt/include/multicore.h
Normal file
11
part15a-tcpip-webserver-gpio-interrupt/include/multicore.h
Normal file
|
@ -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);
|
5
part15a-tcpip-webserver-gpio-interrupt/include/spi.h
Normal file
5
part15a-tcpip-webserver-gpio-interrupt/include/spi.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
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_chip_select(unsigned char chip_select);
|
253
part15a-tcpip-webserver-gpio-interrupt/include/terminal.h
Normal file
253
part15a-tcpip-webserver-gpio-interrupt/include/terminal.h
Normal file
|
@ -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)
|
||||
};
|
27
part15a-tcpip-webserver-gpio-interrupt/kernel/irq.c
Normal file
27
part15a-tcpip-webserver-gpio-interrupt/kernel/irq.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "kernel.h"
|
||||
|
||||
void enable_interrupt_controller() {
|
||||
REGS_IRQ->irq0_enable_0 = SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3;
|
||||
}
|
||||
|
||||
void disable_interrupt_controller() {
|
||||
REGS_IRQ->irq0_enable_0 = 0;
|
||||
}
|
||||
|
||||
void handle_irq() {
|
||||
unsigned int irq = REGS_IRQ->irq0_pending_0;
|
||||
|
||||
while(irq & (SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3)) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
160
part15a-tcpip-webserver-gpio-interrupt/kernel/irqentry.S
Normal file
160
part15a-tcpip-webserver-gpio-interrupt/kernel/irqentry.S
Normal file
|
@ -0,0 +1,160 @@
|
|||
#define SYNC_INVALID_EL1t 0
|
||||
#define IRQ_INVALID_EL1t 1
|
||||
#define FIQ_INVALID_EL1t 2
|
||||
#define ERROR_INVALID_EL1t 3
|
||||
|
||||
#define SYNC_INVALID_EL1h 4
|
||||
#define IRQ_INVALID_EL1h 5
|
||||
#define FIQ_INVALID_EL1h 6
|
||||
#define ERROR_INVALID_EL1h 7
|
||||
|
||||
#define SYNC_INVALID_EL0_64 8
|
||||
#define IRQ_INVALID_EL0_64 9
|
||||
#define FIQ_INVALID_EL0_64 10
|
||||
#define ERROR_INVALID_EL0_64 11
|
||||
|
||||
#define SYNC_INVALID_EL0_32 12
|
||||
#define IRQ_INVALID_EL0_32 13
|
||||
#define FIQ_INVALID_EL0_32 14
|
||||
#define ERROR_INVALID_EL0_32 15
|
||||
|
||||
//stack frame size
|
||||
#define S_FRAME_SIZE 256
|
||||
|
||||
.macro kernel_entry
|
||||
sub sp, sp, #S_FRAME_SIZE
|
||||
stp x0, x1, [sp, #16 * 0]
|
||||
stp x2, x3, [sp, #16 * 1]
|
||||
stp x4, x5, [sp, #16 * 2]
|
||||
stp x6, x7, [sp, #16 * 3]
|
||||
stp x8, x9, [sp, #16 * 4]
|
||||
stp x10, x11, [sp, #16 * 5]
|
||||
stp x12, x13, [sp, #16 * 6]
|
||||
stp x14, x15, [sp, #16 * 7]
|
||||
stp x16, x17, [sp, #16 * 8]
|
||||
stp x18, x19, [sp, #16 * 9]
|
||||
stp x20, x21, [sp, #16 * 10]
|
||||
stp x22, x23, [sp, #16 * 11]
|
||||
stp x24, x25, [sp, #16 * 12]
|
||||
stp x26, x27, [sp, #16 * 13]
|
||||
stp x28, x29, [sp, #16 * 14]
|
||||
str x30, [sp, #16 * 15]
|
||||
.endm
|
||||
|
||||
.macro kernel_exit
|
||||
ldp x0, x1, [sp, #16 * 0]
|
||||
ldp x2, x3, [sp, #16 * 1]
|
||||
ldp x4, x5, [sp, #16 * 2]
|
||||
ldp x6, x7, [sp, #16 * 3]
|
||||
ldp x8, x9, [sp, #16 * 4]
|
||||
ldp x10, x11, [sp, #16 * 5]
|
||||
ldp x12, x13, [sp, #16 * 6]
|
||||
ldp x14, x15, [sp, #16 * 7]
|
||||
ldp x16, x17, [sp, #16 * 8]
|
||||
ldp x18, x19, [sp, #16 * 9]
|
||||
ldp x20, x21, [sp, #16 * 10]
|
||||
ldp x22, x23, [sp, #16 * 11]
|
||||
ldp x24, x25, [sp, #16 * 12]
|
||||
ldp x26, x27, [sp, #16 * 13]
|
||||
ldp x28, x29, [sp, #16 * 14]
|
||||
ldr x30, [sp, #16 * 15]
|
||||
add sp, sp, #S_FRAME_SIZE
|
||||
eret
|
||||
.endm
|
||||
|
||||
.macro handle_invalid_entry type
|
||||
kernel_entry
|
||||
mov x0, #\type
|
||||
mrs x1, esr_el1
|
||||
mrs x2, elr_el1
|
||||
|
||||
// We could pass this to a function to print an error here
|
||||
// e.g. bl show_invalid_entry_message
|
||||
//
|
||||
// For now we'll just hang
|
||||
|
||||
b err_hang
|
||||
.endm
|
||||
|
||||
.macro ventry label
|
||||
.align 7
|
||||
b \label
|
||||
.endm
|
||||
|
||||
//Exception vectors table
|
||||
.align 11
|
||||
.globl vectors
|
||||
vectors:
|
||||
ventry sync_invalid_el1t // Synchronous EL1t
|
||||
ventry irq_invalid_el1t // IRQ EL1t
|
||||
ventry fiq_invalid_el1t // FIQ EL1t
|
||||
ventry error_invalid_el1t // Error EL1t
|
||||
|
||||
ventry sync_invalid_el1h // Synchronous EL1h
|
||||
ventry handle_el1_irq // IRQ EL1h
|
||||
ventry fiq_invalid_el1h // FIQ EL1h
|
||||
ventry error_invalid_el1h // Error EL1h
|
||||
|
||||
ventry sync_invalid_el0_64 // Synchronous 64-bit EL0
|
||||
ventry irq_invalid_el0_64 // IRQ 64-bit EL0
|
||||
ventry fiq_invalid_el0_64 // FIQ 64-bit EL0
|
||||
ventry error_invalid_el0_64 // Error 64-bit EL0
|
||||
|
||||
ventry sync_invalid_el0_32 // Synchronous 32-bit EL0
|
||||
ventry irq_invalid_el0_32 // IRQ 32-bit EL0
|
||||
ventry fiq_invalid_el0_32 // FIQ 32-bit EL0
|
||||
ventry error_invalid_el0_32 // Error 32-bit EL0
|
||||
|
||||
|
||||
sync_invalid_el1t:
|
||||
handle_invalid_entry SYNC_INVALID_EL1t
|
||||
|
||||
irq_invalid_el1t:
|
||||
handle_invalid_entry IRQ_INVALID_EL1t
|
||||
|
||||
fiq_invalid_el1t:
|
||||
handle_invalid_entry FIQ_INVALID_EL1t
|
||||
|
||||
error_invalid_el1t:
|
||||
handle_invalid_entry ERROR_INVALID_EL1t
|
||||
|
||||
sync_invalid_el1h:
|
||||
handle_invalid_entry SYNC_INVALID_EL1h
|
||||
|
||||
fiq_invalid_el1h:
|
||||
handle_invalid_entry FIQ_INVALID_EL1h
|
||||
|
||||
error_invalid_el1h:
|
||||
handle_invalid_entry ERROR_INVALID_EL1h
|
||||
|
||||
sync_invalid_el0_64:
|
||||
handle_invalid_entry SYNC_INVALID_EL0_64
|
||||
|
||||
irq_invalid_el0_64:
|
||||
handle_invalid_entry IRQ_INVALID_EL0_64
|
||||
|
||||
fiq_invalid_el0_64:
|
||||
handle_invalid_entry FIQ_INVALID_EL0_64
|
||||
|
||||
error_invalid_el0_64:
|
||||
handle_invalid_entry ERROR_INVALID_EL0_64
|
||||
|
||||
sync_invalid_el0_32:
|
||||
handle_invalid_entry SYNC_INVALID_EL0_32
|
||||
|
||||
irq_invalid_el0_32:
|
||||
handle_invalid_entry IRQ_INVALID_EL0_32
|
||||
|
||||
fiq_invalid_el0_32:
|
||||
handle_invalid_entry FIQ_INVALID_EL0_32
|
||||
|
||||
error_invalid_el0_32:
|
||||
handle_invalid_entry ERROR_INVALID_EL0_32
|
||||
|
||||
handle_el1_irq:
|
||||
kernel_entry
|
||||
bl handle_irq
|
||||
kernel_exit
|
||||
|
||||
.globl err_hang
|
||||
err_hang: b err_hang
|
244
part15a-tcpip-webserver-gpio-interrupt/kernel/kernel.c
Normal file
244
part15a-tcpip-webserver-gpio-interrupt/kernel/kernel.c
Normal file
|
@ -0,0 +1,244 @@
|
|||
#include "../include/fb.h"
|
||||
#include "../include/spi.h"
|
||||
#include "../net/enc28j60.h"
|
||||
#include "../tcpip/ip_arp_udp_tcp.h"
|
||||
#include "kernel.h"
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
int strncmp(const char *s1, const char *s2, unsigned short n)
|
||||
{
|
||||
unsigned char u1, u2;
|
||||
|
||||
while (n-- > 0)
|
||||
{
|
||||
u1 = (unsigned char) *s1++;
|
||||
u2 = (unsigned char) *s2++;
|
||||
if (u1 != u2) return u1 - u2;
|
||||
if (u1 == '\0') return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NETWORKING GLOBALS AND FUNCTIONS
|
||||
|
||||
ENC_HandleTypeDef handle;
|
||||
|
||||
// MAC address to be assigned to the ENC28J60
|
||||
unsigned char myMAC[6] = { 0xb0, 0x07, 0x10, 0xad, 0xca, 0x11 };
|
||||
|
||||
// IP address to be assigned to the ENC28J60
|
||||
unsigned char deviceIP[4] = { 10, 7, 3, 20 };
|
||||
|
||||
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 b0:07:10:ad:ca:11.");
|
||||
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();
|
||||
|
||||
// Re-enable global interrupts
|
||||
ENC_EnableInterrupts(EIE_INTIE);
|
||||
|
||||
debugstr("Initialising the TCP stack... ");
|
||||
init_udp_or_www_server(myMAC, deviceIP);
|
||||
debugstr("done.");
|
||||
debugcrlf();
|
||||
}
|
||||
|
||||
void enc28j60PacketSend(unsigned short buflen, void *buffer) {
|
||||
if (ENC_RestoreTXBuffer(&handle, buflen) == 0) {
|
||||
ENC_WriteBuffer((unsigned char *) buffer, buflen);
|
||||
handle.transmitLength = buflen;
|
||||
ENC_Transmit(&handle);
|
||||
}
|
||||
}
|
||||
|
||||
void serve(void)
|
||||
{
|
||||
while (1) {
|
||||
while (!ENC_GetReceivedFrame(&handle));
|
||||
|
||||
uint8_t *buf = (uint8_t *)handle.RxFrameInfos.buffer;
|
||||
uint16_t len = handle.RxFrameInfos.length;
|
||||
uint16_t dat_p = packetloop_arp_icmp_tcp(buf, len);
|
||||
|
||||
if (dat_p != 0) {
|
||||
debugstr("Incoming web request... ");
|
||||
|
||||
if (strncmp("GET ", (char *)&(buf[dat_p]), 4) != 0) {
|
||||
debugstr("not GET");
|
||||
dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 401 Unauthorized\r\nContent-Type: text/html\r\n\r\n<h1>ERROR</h1>");
|
||||
} else {
|
||||
if (strncmp("/ ", (char *)&(buf[dat_p+4]), 2) == 0) {
|
||||
// just one web page in the "root directory" of the web server
|
||||
debugstr("GET root");
|
||||
dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Hello world!</h1>");
|
||||
} else {
|
||||
// just one web page not in the "root directory" of the web server
|
||||
debugstr("GET not root");
|
||||
dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Goodbye cruel world.</h1>");
|
||||
}
|
||||
}
|
||||
|
||||
www_server_reply(buf, dat_p); // send web page data
|
||||
debugcrlf();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
// ISR for incoming packet
|
||||
|
||||
debugstr("we are into isr now");
|
||||
// Re-enable Interrupts
|
||||
ENC_IRQHandler(&handle);
|
||||
|
||||
while (!ENC_GetReceivedFrame(&handle));
|
||||
|
||||
uint8_t *buf = (uint8_t *)handle.RxFrameInfos.buffer;
|
||||
uint16_t len = handle.RxFrameInfos.length;
|
||||
uint16_t dat_p = packetloop_arp_icmp_tcp(buf, len);
|
||||
|
||||
if (dat_p != 0) {
|
||||
debugstr("Incoming web request... ");
|
||||
|
||||
if (strncmp("GET ", (char *)&(buf[dat_p]), 4) != 0) {
|
||||
debugstr("not GET");
|
||||
dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 401 Unauthorized\r\nContent-Type: text/html\r\n\r\n<h1>ERROR</h1>");
|
||||
} else {
|
||||
if (strncmp("/ ", (char *)&(buf[dat_p+4]), 2) == 0) {
|
||||
// just one web page in the "root directory" of the web server
|
||||
debugstr("GET root");
|
||||
dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Hello world!</h1>");
|
||||
} else {
|
||||
// just one web page not in the "root directory" of the web server
|
||||
debugstr("GET not root");
|
||||
dat_p = fill_tcp_data(buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Goodbye cruel world.</h1>");
|
||||
}
|
||||
}
|
||||
|
||||
www_server_reply(buf, dat_p); // send web page data
|
||||
debugcrlf();
|
||||
}
|
||||
|
||||
*/
|
||||
// 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);
|
||||
}
|
||||
|
||||
// MAIN FUNCTION
|
||||
|
||||
void main(void)
|
||||
{
|
||||
fb_init();
|
||||
// Init GPIO interupt
|
||||
//init_GPIOinterrupt();
|
||||
|
||||
// Kick off the timers
|
||||
|
||||
irq_init_vectors();
|
||||
enable_interrupt_controller();
|
||||
irq_barrier();
|
||||
irq_enable();
|
||||
timer_init();
|
||||
|
||||
|
||||
// Init network and serve web pages
|
||||
|
||||
spi_init();
|
||||
init_network();
|
||||
|
||||
serve();
|
||||
|
||||
// Catch us if we fall
|
||||
|
||||
while(1);
|
||||
|
||||
}
|
50
part15a-tcpip-webserver-gpio-interrupt/kernel/kernel.h
Normal file
50
part15a-tcpip-webserver-gpio-interrupt/kernel/kernel.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
#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_barrier();
|
||||
void irq_disable();
|
||||
void enable_interrupt_controller();
|
||||
void disable_interrupt_controller();
|
||||
|
||||
void handle_timer_1();
|
||||
void handle_timer_3();
|
||||
|
||||
void enc28j60PacketSend(unsigned short buflen, void *buffer);
|
||||
void *memcpy(void *dest, const void *src, unsigned short len);
|
20
part15a-tcpip-webserver-gpio-interrupt/kernel/utils.S
Normal file
20
part15a-tcpip-webserver-gpio-interrupt/kernel/utils.S
Normal file
|
@ -0,0 +1,20 @@
|
|||
.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
|
||||
|
||||
.globl irq_barrier
|
||||
irq_barrier:
|
||||
dsb sy
|
||||
ret
|
260
part15a-tcpip-webserver-gpio-interrupt/lib/fb.c
Normal file
260
part15a-tcpip-webserver-gpio-interrupt/lib/fb.c
Normal file
|
@ -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<x2) {
|
||||
if (p >= 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<newx + width;i++) {
|
||||
for (int j=newy;j<newy + height;j++) {
|
||||
offs = (j * pitch) + (i * 4);
|
||||
*((unsigned int*)(fb + offs)) = bitmap[i-newx][j-newy];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wait_msec(unsigned int n)
|
||||
{
|
||||
register unsigned long f, t, r;
|
||||
|
||||
// Get the current counter frequency
|
||||
asm volatile ("mrs %0, cntfrq_el0" : "=r"(f));
|
||||
// Read the current counter
|
||||
asm volatile ("mrs %0, cntpct_el0" : "=r"(t));
|
||||
// Calculate expire value for counter
|
||||
t+=((f/1000)*n)/1000;
|
||||
do{asm volatile ("mrs %0, cntpct_el0" : "=r"(r));}while(r<t);
|
||||
}
|
||||
|
||||
int strlen(const char *str) {
|
||||
const char *s;
|
||||
|
||||
for (s = str; *s; ++s);
|
||||
return (s - str);
|
||||
}
|
||||
|
||||
void debugstr(char *str) {
|
||||
if (curx + (strlen(str) * 8) >= 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(" ");
|
||||
}
|
229
part15a-tcpip-webserver-gpio-interrupt/lib/io.c
Normal file
229
part15a-tcpip-webserver-gpio-interrupt/lib/io.c
Normal file
|
@ -0,0 +1,229 @@
|
|||
#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(' ');
|
||||
}
|
||||
|
||||
// TIMER
|
||||
|
||||
struct timer_regs {
|
||||
volatile unsigned int control_status;
|
||||
volatile unsigned int counter_lo;
|
||||
volatile unsigned int counter_hi;
|
||||
volatile unsigned int compare[4];
|
||||
};
|
||||
|
||||
#define REGS_TIMER ((struct timer_regs *)(PERIPHERAL_BASE + 0x00003000))
|
||||
|
||||
unsigned long HAL_GetTick(void) {
|
||||
unsigned int hi = REGS_TIMER->counter_hi;
|
||||
unsigned int lo = REGS_TIMER->counter_lo;
|
||||
|
||||
//double check hi value didn't change after setting it...
|
||||
if (hi != REGS_TIMER->counter_hi) {
|
||||
hi = REGS_TIMER->counter_hi;
|
||||
lo = REGS_TIMER->counter_lo;
|
||||
}
|
||||
|
||||
return ((unsigned long)hi << 32) | lo;
|
||||
}
|
||||
|
||||
void HAL_Delay(unsigned int ms) {
|
||||
unsigned long start = HAL_GetTick();
|
||||
|
||||
while(HAL_GetTick() < start + (ms * 1000));
|
||||
}
|
39
part15a-tcpip-webserver-gpio-interrupt/lib/mb.c
Normal file
39
part15a-tcpip-webserver-gpio-interrupt/lib/mb.c
Normal file
|
@ -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;
|
||||
}
|
44
part15a-tcpip-webserver-gpio-interrupt/lib/multicore.c
Normal file
44
part15a-tcpip-webserver-gpio-interrupt/lib/multicore.c
Normal file
|
@ -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);
|
||||
}
|
100
part15a-tcpip-webserver-gpio-interrupt/lib/spi.c
Normal file
100
part15a-tcpip-webserver-gpio-interrupt/lib/spi.c
Normal file
|
@ -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);
|
||||
}
|
1453
part15a-tcpip-webserver-gpio-interrupt/net/enc28j60.c
Normal file
1453
part15a-tcpip-webserver-gpio-interrupt/net/enc28j60.c
Normal file
File diff suppressed because it is too large
Load diff
811
part15a-tcpip-webserver-gpio-interrupt/net/enc28j60.h
Normal file
811
part15a-tcpip-webserver-gpio-interrupt/net/enc28j60.h
Normal file
|
@ -0,0 +1,811 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* @file enc28j60.h
|
||||
* @author Christian Schoffit, portions from Gregory Nutt:
|
||||
* Copyright (C) 2010-2012, 2014 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* <h2><center>© COPYRIGHT(c) 2015 Christian Schoffit</center></h2>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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
|
||||
|
||||
/* 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 */
|
21
part15a-tcpip-webserver-gpio-interrupt/net/encspi.c
Normal file
21
part15a-tcpip-webserver-gpio-interrupt/net/encspi.c
Normal file
|
@ -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);
|
||||
}
|
1528
part15a-tcpip-webserver-gpio-interrupt/tcpip/ip_arp_udp_tcp.c
Normal file
1528
part15a-tcpip-webserver-gpio-interrupt/tcpip/ip_arp_udp_tcp.c
Normal file
File diff suppressed because it is too large
Load diff
207
part15a-tcpip-webserver-gpio-interrupt/tcpip/ip_arp_udp_tcp.h
Normal file
207
part15a-tcpip-webserver-gpio-interrupt/tcpip/ip_arp_udp_tcp.h
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*********************************************
|
||||
* 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
|
||||
*
|
||||
*********************************************/
|
||||
//@{
|
||||
#ifndef IP_ARP_UDP_TCP_H
|
||||
#define IP_ARP_UDP_TCP_H 1
|
||||
|
||||
#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 */
|
||||
//@}
|
53
part15a-tcpip-webserver-gpio-interrupt/tcpip/ip_config.h
Normal file
53
part15a-tcpip-webserver-gpio-interrupt/tcpip/ip_config.h
Normal file
|
@ -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
|
||||
#define 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 */
|
||||
//@}
|
133
part15a-tcpip-webserver-gpio-interrupt/tcpip/net.h
Normal file
133
part15a-tcpip-webserver-gpio-interrupt/tcpip/net.h
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*********************************************
|
||||
* 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
|
||||
//@}
|
||||
|
|
@ -1,19 +1,19 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
LLVMPATH = /opt/homebrew/opt/llvm/bin
|
||||
CLANGFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -mcpu=cortex-a72+nosimd
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c boot.S -o boot.o
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
%.o: %.c
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES)
|
||||
$(LLVMPATH)/ld.lld -m aarch64elf -nostdlib boot.o $(OFILES) -T link.ld -o kernel8.elf
|
||||
$(LLVMPATH)/llvm-objcopy -O binary kernel8.elf kernel8.img
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) -T 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
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
%.o: %.c
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES)
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) -T 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
|
|
@ -1,19 +1,24 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
LLVMPATH = /opt/homebrew/opt/llvm/bin
|
||||
CLANGFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -mcpu=cortex-a72+nosimd
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
BOOTMNT ?= /media/cvdeveloper/bootfs
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c boot.S -o boot.o
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
%.o: %.c
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES)
|
||||
$(LLVMPATH)/ld.lld -m aarch64elf -nostdlib boot.o $(OFILES) -T link.ld -o kernel8.elf
|
||||
$(LLVMPATH)/llvm-objcopy -O binary kernel8.elf kernel8.img
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) -T link.ld -o kernel8.elf
|
||||
$(GCCPATH)/aarch64-none-elf-objcopy -O binary kernel8.elf kernel8-rpi4.img
|
||||
|
||||
cp kernel8-rpi4.img $(BOOTMNT)/kernel8-rpi4.img
|
||||
cp config.txt $(BOOTMNT)/
|
||||
sync
|
||||
|
||||
clean:
|
||||
/bin/rm kernel8.elf *.o *.img > /dev/null 2> /dev/null || true
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
%.o: %.c
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES)
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) -T 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
|
46
part3-helloworld/config.txt
Normal file
46
part3-helloworld/config.txt
Normal file
|
@ -0,0 +1,46 @@
|
|||
# For more options and information see
|
||||
# http://rptl.io/configtxt
|
||||
# Some settings may impact device functionality. See link above for details
|
||||
|
||||
# Uncomment some or all of these to enable the optional hardware interfaces
|
||||
#dtparam=i2c_arm=on
|
||||
#dtparam=i2s=on
|
||||
#dtparam=spi=on
|
||||
|
||||
# Enable audio (loads snd_bcm2835)
|
||||
dtparam=audio=on
|
||||
|
||||
# Additional overlays and parameters are documented
|
||||
# /boot/firmware/overlays/README
|
||||
|
||||
# Automatically load overlays for detected cameras
|
||||
camera_auto_detect=1
|
||||
|
||||
# Automatically load overlays for detected DSI displays
|
||||
display_auto_detect=1
|
||||
|
||||
# Automatically load initramfs files, if found
|
||||
auto_initramfs=1
|
||||
|
||||
# Enable DRM VC4 V3D driver
|
||||
dtoverlay=vc4-kms-v3d
|
||||
max_framebuffers=2
|
||||
|
||||
# Don't have the firmware create an initial video= setting in cmdline.txt.
|
||||
# Use the kernel's default instead.
|
||||
disable_fw_kms_setup=1
|
||||
|
||||
# Run in 64-bit mode
|
||||
arm_64bit=1
|
||||
|
||||
# Disable compensation for displays with overscan
|
||||
disable_overscan=1
|
||||
|
||||
# Run as fast as firmware / board allows
|
||||
arm_boost=1
|
||||
|
||||
[all]
|
||||
kernel_old=1
|
||||
disable_commandline_tags=1
|
||||
arm_64bit=1
|
||||
core_freq_min=500
|
|
@ -1,19 +1,24 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
LLVMPATH = /opt/homebrew/opt/llvm/bin
|
||||
CLANGFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -mcpu=cortex-a72+nosimd
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
BOOTMNT ?= /media/cvdeveloper/bootfs
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c boot.S -o boot.o
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
%.o: %.c
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES)
|
||||
$(LLVMPATH)/ld.lld -m aarch64elf -nostdlib boot.o $(OFILES) -T link.ld -o kernel8.elf
|
||||
$(LLVMPATH)/llvm-objcopy -O binary kernel8.elf kernel8.img
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) -T link.ld -o kernel8.elf
|
||||
$(GCCPATH)/aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img
|
||||
|
||||
cp kernel8.img $(BOOTMNT)/kernel8.img
|
||||
cp config.txt $(BOOTMNT)/
|
||||
sync
|
||||
|
||||
clean:
|
||||
/bin/rm kernel8.elf *.o *.img > /dev/null 2> /dev/null || true
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
%.o: %.c
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES)
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) -T 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
|
46
part4-miniuart/config.txt
Normal file
46
part4-miniuart/config.txt
Normal file
|
@ -0,0 +1,46 @@
|
|||
# For more options and information see
|
||||
# http://rptl.io/configtxt
|
||||
# Some settings may impact device functionality. See link above for details
|
||||
|
||||
# Uncomment some or all of these to enable the optional hardware interfaces
|
||||
#dtparam=i2c_arm=on
|
||||
#dtparam=i2s=on
|
||||
#dtparam=spi=on
|
||||
|
||||
# Enable audio (loads snd_bcm2835)
|
||||
dtparam=audio=on
|
||||
|
||||
# Additional overlays and parameters are documented
|
||||
# /boot/firmware/overlays/README
|
||||
|
||||
# Automatically load overlays for detected cameras
|
||||
camera_auto_detect=1
|
||||
|
||||
# Automatically load overlays for detected DSI displays
|
||||
display_auto_detect=1
|
||||
|
||||
# Automatically load initramfs files, if found
|
||||
auto_initramfs=1
|
||||
|
||||
# Enable DRM VC4 V3D driver
|
||||
dtoverlay=vc4-kms-v3d
|
||||
max_framebuffers=2
|
||||
|
||||
# Don't have the firmware create an initial video= setting in cmdline.txt.
|
||||
# Use the kernel's default instead.
|
||||
disable_fw_kms_setup=1
|
||||
|
||||
# Run in 64-bit mode
|
||||
arm_64bit=1
|
||||
|
||||
# Disable compensation for displays with overscan
|
||||
disable_overscan=1
|
||||
|
||||
# Run as fast as firmware / board allows
|
||||
arm_boost=1
|
||||
|
||||
[all]
|
||||
kernel_old=1
|
||||
disable_commandline_tags=1
|
||||
arm_64bit=1
|
||||
core_freq_min=500
|
|
@ -1,19 +1,19 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
LLVMPATH = /opt/homebrew/opt/llvm/bin
|
||||
CLANGFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -mcpu=cortex-a72+nosimd
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c boot.S -o boot.o
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
%.o: %.c
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES)
|
||||
$(LLVMPATH)/ld.lld -m aarch64elf -nostdlib boot.o $(OFILES) -T link.ld -o kernel8.elf
|
||||
$(LLVMPATH)/llvm-objcopy -O binary kernel8.elf kernel8.img
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) -T 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
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
%.o: %.c
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES)
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) -T 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
|
|
@ -1,19 +1,19 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
LLVMPATH = /opt/homebrew/opt/llvm/bin
|
||||
CLANGFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -mcpu=cortex-a72+nosimd
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c boot.S -o boot.o
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
%.o: %.c
|
||||
$(LLVMPATH)/clang --target=aarch64-elf $(CLANGFLAGS) -c $< -o $@
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES)
|
||||
$(LLVMPATH)/ld.lld -m aarch64elf -nostdlib boot.o $(OFILES) -T link.ld -o kernel8.elf
|
||||
$(LLVMPATH)/llvm-objcopy -O binary kernel8.elf kernel8.img
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) -T 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
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
CFILES = $(wildcard *.c)
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||
GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin
|
||||
|
||||
all: clean kernel8.img
|
||||
|
||||
boot.o: boot.S
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||
|
||||
%.o: %.c
|
||||
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||
|
||||
kernel8.img: boot.o $(OFILES)
|
||||
$(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) -T 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
|
Loading…
Reference in a new issue