diff options
| -rw-r--r-- | src/framebuffer.c | 84 | ||||
| -rw-r--r-- | src/framebuffer.h | 17 | ||||
| -rw-r--r-- | src/kernel.c | 23 | ||||
| -rw-r--r-- | src/mailbox.c | 19 | ||||
| -rw-r--r-- | src/mailbox.h | 2 | ||||
| -rw-r--r-- | src/string.c | 59 | ||||
| -rw-r--r-- | src/string.h | 9 | ||||
| -rw-r--r-- | src/uart.c | 1 |
8 files changed, 171 insertions, 43 deletions
diff --git a/src/framebuffer.c b/src/framebuffer.c index c3845b1..6505ace 100644 --- a/src/framebuffer.c +++ b/src/framebuffer.c | |||
| @@ -1,58 +1,74 @@ | |||
| 1 | #include <framebuffer.h> | 1 | #include <framebuffer.h> |
| 2 | 2 | ||
| 3 | #include <mailbox.h> | 3 | #include <mailbox.h> |
| 4 | #include <string.h> | ||
| 4 | 5 | ||
| 5 | #include <stddef.h> | 6 | #include <stddef.h> |
| 6 | #include <stdint.h> | 7 | #include <stdint.h> |
| 7 | 8 | ||
| 8 | #define WIDTH 640 | 9 | #define WIDTH 640 |
| 9 | #define HEIGHT 480 | 10 | #define HEIGHT 480 |
| 10 | #define DEPTH 32 | 11 | #define DEPTH 24 |
| 11 | #define ALIGNMENT 16 // Framebuffer byte alignment. | 12 | #define ALIGNMENT 16 // Framebuffer byte alignment. |
| 12 | 13 | ||
| 13 | typedef struct Framebuffer { | ||
| 14 | volatile void* pixels; | ||
| 15 | size_t size; | ||
| 16 | } Framebuffer; | ||
| 17 | |||
| 18 | static Framebuffer framebuffer = {}; | 14 | static Framebuffer framebuffer = {}; |
| 19 | 15 | ||
| 20 | bool framebuffer_init(uint32_t* error) { | 16 | bool framebuffer_init(uint32_t* error) { |
| 21 | MAIL_T uint32_t ConfigureScreen[20] = { | 17 | // TODO: We can combine both messages, this one and the one below, into a |
| 22 | 80, // Size in bytes, aligned to MAIL_ALIGN. | 18 | // single array and make a single call to mbox_write(). |
| 23 | MAILBOX_REQUEST, | 19 | MAIL_T uint32_t InitFramebuffer[24] = { |
| 24 | TAG_FRAMEBUFFER_SET_PHYSICAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, | 20 | /*00*/96, // Size in bytes, aligned to MAIL_ALIGN. |
| 25 | TAG_FRAMEBUFFER_SET_VIRTUAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, | 21 | /*01*/MAILBOX_REQUEST, |
| 26 | TAG_FRAMEBUFFER_SET_DEPTH, 4, MAILBOX_REQUEST, DEPTH, | 22 | /*02*/TAG_FRAMEBUFFER_SET_PHYSICAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, |
| 27 | TAG_END, | 23 | /*07*/TAG_FRAMEBUFFER_SET_VIRTUAL_SCREEN_SIZE, 8, MAILBOX_REQUEST, WIDTH, HEIGHT, |
| 28 | 0, 0, 0 // Padding. | 24 | /*12*/TAG_FRAMEBUFFER_SET_DEPTH, 4, MAILBOX_REQUEST, DEPTH, |
| 29 | }; | 25 | /*16*/TAG_FRAMEBUFFER_ALLOCATE, 8, MAILBOX_REQUEST, ALIGNMENT, 0, |
| 30 | mbox_write(PROPERTY_CHANNEL, ConfigureScreen); | 26 | /*21*/TAG_END, |
| 31 | const Mail* response = mbox_read(PROPERTY_CHANNEL); | 27 | /*22*/0, 0 // Padding. |
| 32 | if (response->code != MAILBOX_SUCCESS) { | ||
| 33 | goto end; | ||
| 34 | } | ||
| 35 | |||
| 36 | MAIL_T uint32_t InitFramebuffer[8] = { | ||
| 37 | 32, // Size in bytes, aligned to MAIL_ALIGN. | ||
| 38 | MAILBOX_REQUEST, | ||
| 39 | TAG_FRAMEBUFFER_ALLOCATE, 8, MAILBOX_REQUEST, ALIGNMENT, 0, | ||
| 40 | TAG_END | ||
| 41 | }; | 28 | }; |
| 42 | mbox_write(PROPERTY_CHANNEL, InitFramebuffer); | 29 | const uint32_t result = mbox_write(PROPERTY_CHANNEL, InitFramebuffer); |
| 43 | response = mbox_read(PROPERTY_CHANNEL); | 30 | if (result != MAILBOX_SUCCESS) { |
| 44 | if (response->code != MAILBOX_SUCCESS) { | ||
| 45 | goto end; | 31 | goto end; |
| 46 | } | 32 | } |
| 47 | 33 | ||
| 48 | // The input mail is overwritten with the response. | 34 | // The input mail is overwritten with the response. |
| 35 | // | ||
| 36 | // TAG_FRAMEBUFFER_ALLOCATE response: | ||
| 49 | // u32 fb base address | 37 | // u32 fb base address |
| 50 | // u32 fb size | 38 | // u32 fb size |
| 51 | framebuffer.pixels = (void*)(uintptr_t)InitFramebuffer[5]; | 39 | // TODO: Do we need & 0x3FFFFFFF? Something about converting GPU address space |
| 52 | framebuffer.size = InitFramebuffer[6]; | 40 | // to ARM address space. |
| 53 | 41 | // TODO: Should we read back the pitch, or does pitch match width? | |
| 42 | framebuffer = (Framebuffer) { | ||
| 43 | .pixels = (Pixel*)((uintptr_t)InitFramebuffer[19] & 0x3FFFFFFF), | ||
| 44 | .size = InitFramebuffer[20], | ||
| 45 | .width = InitFramebuffer[5], | ||
| 46 | .height = InitFramebuffer[6], | ||
| 47 | .depth = InitFramebuffer[15] | ||
| 48 | }; | ||
| 49 | |||
| 54 | end: | 50 | end: |
| 55 | *error = response->code; | 51 | *error = result; |
| 56 | return response->code == MAILBOX_SUCCESS; | 52 | return result == MAILBOX_SUCCESS; |
| 53 | } | ||
| 54 | |||
| 55 | const Framebuffer* framebuffer_get() { | ||
| 56 | return &framebuffer; | ||
| 57 | } | ||
| 58 | |||
| 59 | void framebuffer_present(const Pixel* pixels) { | ||
| 60 | memcpy(framebuffer.pixels, pixels, framebuffer.size); | ||
| 61 | } | ||
| 62 | |||
| 63 | void framebuffer_clear(Pixel colour) { | ||
| 64 | const uint32_t num_channels = framebuffer.depth / 8; | ||
| 65 | const uint32_t num_pixels = framebuffer.size / num_channels; | ||
| 66 | volatile Pixel* fb = framebuffer.pixels; | ||
| 67 | for (size_t i = 0; i < num_pixels; i++) { | ||
| 68 | fb->r = colour.r; | ||
| 69 | fb->g = colour.g; | ||
| 70 | fb->b = colour.b; | ||
| 71 | fb++; | ||
| 72 | } | ||
| 57 | } | 73 | } |
| 58 | 74 | ||
diff --git a/src/framebuffer.h b/src/framebuffer.h index d2d57cd..b44fa44 100644 --- a/src/framebuffer.h +++ b/src/framebuffer.h | |||
| @@ -3,5 +3,22 @@ | |||
| 3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
| 4 | #include <stdint.h> | 4 | #include <stdint.h> |
| 5 | 5 | ||
| 6 | typedef uint8_t Channel; | ||
| 7 | |||
| 8 | typedef struct Pixel { | ||
| 9 | Channel r, g, b; | ||
| 10 | } Pixel; | ||
| 11 | |||
| 12 | typedef struct Framebuffer { | ||
| 13 | Pixel* pixels; | ||
| 14 | uint32_t size; | ||
| 15 | uint32_t width; | ||
| 16 | uint32_t height; | ||
| 17 | uint32_t depth; | ||
| 18 | } Framebuffer; | ||
| 19 | |||
| 6 | bool framebuffer_init(uint32_t* error); | 20 | bool framebuffer_init(uint32_t* error); |
| 21 | const Framebuffer* framebuffer_get(); | ||
| 22 | void framebuffer_present(const Pixel* pixels); | ||
| 23 | void framebuffer_clear(Pixel colour); | ||
| 7 | 24 | ||
diff --git a/src/kernel.c b/src/kernel.c index 151028d..2a4005a 100644 --- a/src/kernel.c +++ b/src/kernel.c | |||
| @@ -2,8 +2,11 @@ | |||
| 2 | #include <mailbox.h> | 2 | #include <mailbox.h> |
| 3 | #include <mmio.h> | 3 | #include <mmio.h> |
| 4 | #include <raspi.h> | 4 | #include <raspi.h> |
| 5 | #include <string.h> | ||
| 5 | #include <uart.h> | 6 | #include <uart.h> |
| 6 | 7 | ||
| 8 | #include <stdint.h> | ||
| 9 | |||
| 7 | static void halt() { | 10 | static void halt() { |
| 8 | while (1) { | 11 | while (1) { |
| 9 | asm volatile("wfi"); // Wait for interrupt. Core enters low-power state. | 12 | asm volatile("wfi"); // Wait for interrupt. Core enters low-power state. |
| @@ -13,6 +16,7 @@ static void halt() { | |||
| 13 | void main() { | 16 | void main() { |
| 14 | bool success = true; | 17 | bool success = true; |
| 15 | uint32_t error = -1; | 18 | uint32_t error = -1; |
| 19 | char buf[32]; | ||
| 16 | 20 | ||
| 17 | const int raspi = raspi_init(); | 21 | const int raspi = raspi_init(); |
| 18 | mmio_init(raspi); // Must be initialized before other peripherals. | 22 | mmio_init(raspi); // Must be initialized before other peripherals. |
| @@ -31,7 +35,24 @@ void main() { | |||
| 31 | goto end; | 35 | goto end; |
| 32 | } | 36 | } |
| 33 | 37 | ||
| 34 | uart_print("Hello world!\n"); | 38 | const Framebuffer* fb = framebuffer_get(); |
| 39 | uart_print("Framebuffer:"); | ||
| 40 | uart_print("\n width: "); | ||
| 41 | uart_print(utoa(fb->width, buf, sizeof(buf))); | ||
| 42 | uart_print("\n height: "); | ||
| 43 | uart_print(utoa(fb->height, buf, sizeof(buf))); | ||
| 44 | uart_print("\n depth: "); | ||
| 45 | uart_print(utoa(fb->depth, buf, sizeof(buf))); | ||
| 46 | uart_print("\n addr: "); | ||
| 47 | uart_print(ptoa(fb->pixels, buf, sizeof(buf))); | ||
| 48 | uart_print("\n size: "); | ||
| 49 | uart_print(utoa(fb->size, buf, sizeof(buf))); | ||
| 50 | uart_print("\n"); | ||
| 51 | |||
| 52 | uart_print("Clearing framebuffer\n"); | ||
| 53 | framebuffer_clear((Pixel){255, 0, 255}); | ||
| 54 | |||
| 55 | uart_print("All done\n"); | ||
| 35 | 56 | ||
| 36 | end: | 57 | end: |
| 37 | halt(); | 58 | halt(); |
diff --git a/src/mailbox.c b/src/mailbox.c index 310b2b1..fcad179 100644 --- a/src/mailbox.c +++ b/src/mailbox.c | |||
| @@ -7,7 +7,9 @@ | |||
| 7 | 7 | ||
| 8 | enum | 8 | enum |
| 9 | { | 9 | { |
| 10 | MAILBOX = 0xB880 // Mailbox address relative to MMIO base address. | 10 | MAILBOX = 0xB880, // Mailbox address relative to MMIO base address. |
| 11 | STATUS_EMPTY = 0x40000000, // Empty bit. | ||
| 12 | STATUS_FULL = 0x80000000, // Full bit. | ||
| 11 | }; | 13 | }; |
| 12 | 14 | ||
| 13 | typedef uint32_t Message; | 15 | typedef uint32_t Message; |
| @@ -21,7 +23,7 @@ static inline Message msg_make(uint8_t channel, volatile const void* data) { | |||
| 21 | } | 23 | } |
| 22 | 24 | ||
| 23 | typedef struct Mailbox { | 25 | typedef struct Mailbox { |
| 24 | Message inbox; // 0x00 | 26 | Message inbox; // 0x00 |
| 25 | uint32_t unused_1; // 0x04 | 27 | uint32_t unused_1; // 0x04 |
| 26 | uint32_t unused_2; // 0x08 | 28 | uint32_t unused_2; // 0x08 |
| 27 | uint32_t unused_3; // 0x0C | 29 | uint32_t unused_3; // 0x0C |
| @@ -29,15 +31,15 @@ typedef struct Mailbox { | |||
| 29 | uint32_t unused_5; // 0x14 | 31 | uint32_t unused_5; // 0x14 |
| 30 | uint32_t status; // 0x18 | 32 | uint32_t status; // 0x18 |
| 31 | uint32_t unused_6; // 0x1C | 33 | uint32_t unused_6; // 0x1C |
| 32 | Message outbox; // 0x20 | 34 | Message outbox; // 0x20 |
| 33 | } Mailbox; | 35 | } Mailbox; |
| 34 | 36 | ||
| 35 | static inline bool inbox_empty(const volatile Mailbox* pMailbox) { | 37 | static inline bool inbox_empty(const volatile Mailbox* pMailbox) { |
| 36 | return (pMailbox->status & 0x40000000) != 0; | 38 | return (pMailbox->status & STATUS_EMPTY) != 0; |
| 37 | } | 39 | } |
| 38 | 40 | ||
| 39 | static inline bool outbox_full(const volatile Mailbox* pMailbox) { | 41 | static inline bool outbox_full(const volatile Mailbox* pMailbox) { |
| 40 | return (pMailbox->status & 0x80000000) != 0; | 42 | return (pMailbox->status & STATUS_FULL) != 0; |
| 41 | } | 43 | } |
| 42 | 44 | ||
| 43 | static volatile Mailbox* pMailbox; | 45 | static volatile Mailbox* pMailbox; |
| @@ -59,10 +61,15 @@ const Mail* mbox_read(uint8_t channel) { | |||
| 59 | return (const Mail*)((uintptr_t)msg & ~0xf); | 61 | return (const Mail*)((uintptr_t)msg & ~0xf); |
| 60 | } | 62 | } |
| 61 | 63 | ||
| 62 | void mbox_write(uint8_t channel, volatile const void* mail) { | 64 | uint32_t mbox_write(uint8_t channel, volatile const void* mail) { |
| 63 | // Wait until the outbox is clear. | 65 | // Wait until the outbox is clear. |
| 64 | while (outbox_full(pMailbox)); | 66 | while (outbox_full(pMailbox)); |
| 65 | // Send the mail. | 67 | // Send the mail. |
| 66 | pMailbox->outbox = msg_make(channel, mail); | 68 | pMailbox->outbox = msg_make(channel, mail); |
| 69 | // Wait for the response. | ||
| 70 | // Mailbox messages cannot be streamed anyway, so wait here so that the API | ||
| 71 | // does not allow the caller to write two messages in a row without waiting. | ||
| 72 | const Mail* response = mbox_read(channel); | ||
| 73 | return response->code; | ||
| 67 | } | 74 | } |
| 68 | 75 | ||
diff --git a/src/mailbox.h b/src/mailbox.h index 2104265..dbd7be0 100644 --- a/src/mailbox.h +++ b/src/mailbox.h | |||
| @@ -66,5 +66,5 @@ typedef struct __attribute__((aligned(MAIL_ALIGN))) Mail { | |||
| 66 | 66 | ||
| 67 | void mbox_init(); | 67 | void mbox_init(); |
| 68 | const Mail* mbox_read(uint8_t channel); | 68 | const Mail* mbox_read(uint8_t channel); |
| 69 | void mbox_write(uint8_t channel, volatile const void* mail); | 69 | uint32_t mbox_write(uint8_t channel, volatile const void* mail); |
| 70 | 70 | ||
diff --git a/src/string.c b/src/string.c new file mode 100644 index 0000000..4801154 --- /dev/null +++ b/src/string.c | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | #include <string.h> | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | |||
| 5 | void* memcpy(void* dst, const void* src, size_t count) { | ||
| 6 | // TODO: Can probably use better implementations based on hw instruction set. | ||
| 7 | uint32_t* dst32 = (uint32_t*)dst; | ||
| 8 | const uint32_t* src32 = (uint32_t*)src; | ||
| 9 | for(; count >= 4; count -= 4) { | ||
| 10 | *dst32++ = *src32++; | ||
| 11 | } | ||
| 12 | uint8_t* dst8 = (uint8_t*)dst32; | ||
| 13 | const uint8_t* src8 = (uint8_t*)src32; | ||
| 14 | for(; count != 0; count--) { | ||
| 15 | *dst8++ = *src8++; | ||
| 16 | } | ||
| 17 | return dst8; | ||
| 18 | } | ||
| 19 | |||
| 20 | static int count_digits_u(unsigned int x) { | ||
| 21 | int count = 0; | ||
| 22 | for(; x != 0; x /= 10, count++); | ||
| 23 | return count; | ||
| 24 | } | ||
| 25 | |||
| 26 | char* utoa(unsigned int x, char* buffer, size_t size) { | ||
| 27 | if (size > 0) { | ||
| 28 | const int num_digits = count_digits_u(x); | ||
| 29 | size_t i = 0; | ||
| 30 | while ((x != 0) && ((i+1) < size)) { | ||
| 31 | const unsigned int digit = x % 10; | ||
| 32 | x /= 10; | ||
| 33 | buffer[num_digits-1 - i++] = '0' + digit; | ||
| 34 | } | ||
| 35 | buffer[i] = 0; | ||
| 36 | } | ||
| 37 | return buffer; | ||
| 38 | } | ||
| 39 | |||
| 40 | char* ptoa(const void* ptr, char* buffer, size_t size) { | ||
| 41 | if (size > 2) { | ||
| 42 | size_t i = 0; | ||
| 43 | buffer[i++] = '0'; | ||
| 44 | buffer[i++] = 'x'; | ||
| 45 | uintptr_t x = (uintptr_t)ptr; | ||
| 46 | const int num_digits = count_digits_u(x); | ||
| 47 | while ((x != 0) && (i < size)) { | ||
| 48 | const unsigned int digit = x % 16; | ||
| 49 | x /= 16; | ||
| 50 | const char hex = (digit < 10) ? ('0' + digit) : ('a' + (digit-10)); | ||
| 51 | buffer[num_digits-1+2 - i++] = hex; | ||
| 52 | } | ||
| 53 | buffer[i] = 0; | ||
| 54 | } else if (size > 0) { | ||
| 55 | buffer[0] = 0; | ||
| 56 | } | ||
| 57 | return buffer; | ||
| 58 | } | ||
| 59 | |||
diff --git a/src/string.h b/src/string.h new file mode 100644 index 0000000..3f0e200 --- /dev/null +++ b/src/string.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <stddef.h> | ||
| 4 | |||
| 5 | void* memcpy(void* dest, const void* src, size_t count); | ||
| 6 | |||
| 7 | char* utoa(unsigned int x, char* buffer, size_t size); | ||
| 8 | char* ptoa(const void* ptr, char* buffer, size_t size); | ||
| 9 | |||
| @@ -75,7 +75,6 @@ void uart_init(int raspi) { | |||
| 75 | if (raspi >= 3) { | 75 | if (raspi >= 3) { |
| 76 | // Send message over property channel to configure UART clock. | 76 | // Send message over property channel to configure UART clock. |
| 77 | mbox_write(PROPERTY_CHANNEL, UART_SET_CLK); | 77 | mbox_write(PROPERTY_CHANNEL, UART_SET_CLK); |
| 78 | mbox_read(PROPERTY_CHANNEL); | ||
| 79 | } | 78 | } |
| 80 | 79 | ||
| 81 | // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. | 80 | // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. |
