Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add QEMU Control handling #132

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -65,7 +65,7 @@ $(eval CFLAGS_$(1) = $(CFLAGS_$(1)) -I$(BUILD)/$(1)/res )
$(1): $(BUILD)/$(1)/$(1).pbz

$(1)_qemu: $(BUILD)/$(1)/fw.qemu_flash.bin $(BUILD)/$(1)/fw.qemu_spi.bin
$(QEMU) -rtc base=localtime -serial null -serial null -serial stdio -gdb tcp::63770,server $(QEMUFLAGS_$(1)) -pflash $(BUILD)/$(1)/fw.qemu_flash.bin -$(QEMUSPITYPE_$(1)) $(BUILD)/$(1)/fw.qemu_spi.bin $(QEMUFLAGS)
$(QEMU) -rtc base=localtime -serial null -serial tcp::63771,server -serial stdio -gdb tcp::63770,server $(QEMUFLAGS_$(1)) -pflash $(BUILD)/$(1)/fw.qemu_flash.bin -$(QEMUSPITYPE_$(1)) $(BUILD)/$(1)/fw.qemu_spi.bin $(QEMUFLAGS)

$(1)_gdb:
$(PFX)gdb -ex 'target remote localhost:63770' -ex "sym $(BUILD)/$(1)/tintin_fw.elf"
Expand Down
2 changes: 2 additions & 0 deletions config.mk
Expand Up @@ -109,6 +109,8 @@ SRCS_all += rcore/resource.c
SRCS_all += rcore/watchdog.c
SRCS_all += rcore/overlay_manager.c
SRCS_all += rcore/rebble_util.c
SRCS_all += rcore/qemu.c
SRCS_all += rcore/qemu_endpoints.c

SRCS_all += rcore/protocol/protocol_notification.c
SRCS_all += rcore/protocol/protocol_system.c
Expand Down
13 changes: 13 additions & 0 deletions hw/drivers/stm32_usart/stm32_usart.c
Expand Up @@ -248,3 +248,16 @@ size_t stm32_usart_read(stm32_usart_t *usart, uint8_t *buf, size_t len)

return i;
}

int stm32_usart_has_data(stm32_usart_t *usart)
{
stm32_power_request(usart->config->usart_periph_bus, usart->config->usart_clock);
stm32_power_request(STM32_POWER_AHB1, usart->config->gpio_clock);

int result = (usart->config->usart->SR & USART_FLAG_RXNE) != 0;

stm32_power_release(usart->config->usart_periph_bus, usart->config->usart_clock);
stm32_power_release(STM32_POWER_AHB1, usart->config->gpio_clock);

return result;
}
1 change: 1 addition & 0 deletions hw/drivers/stm32_usart/stm32_usart.h
Expand Up @@ -46,6 +46,7 @@ void stm32_usart_init_device(stm32_usart_t *usart);
void stm32_usart_set_baud(stm32_usart_t *usart, uint32_t baud);
size_t stm32_usart_write(stm32_usart_t *usart, const uint8_t *buf, size_t len);
size_t stm32_usart_read(stm32_usart_t *usart, uint8_t *buf, size_t len);
int stm32_usart_has_data(stm32_usart_t *usart);
void stm32_usart_send_dma(stm32_usart_t *usart, uint32_t *data, size_t len);
void stm32_usart_recv_dma(stm32_usart_t *usart, uint32_t *data, size_t len);

Expand Down
1 change: 1 addition & 0 deletions hw/platform/snowy/platform.h
Expand Up @@ -18,4 +18,5 @@
#include "snowy_ext_flash.h"
#include "btstack_rebble.h"
#include "snowy_bluetooth.h"
#include "snowy_qemu.h"
#include "debug.h"
1 change: 1 addition & 0 deletions hw/platform/snowy_family/config.mk
Expand Up @@ -25,6 +25,7 @@ SRCS_snowy_family += hw/platform/snowy_family/snowy_vibrate.c
SRCS_snowy_family += hw/platform/snowy_family/snowy_ambient.c
SRCS_snowy_family += hw/platform/snowy_family/snowy_ext_flash.c
SRCS_snowy_family += hw/platform/snowy_family/snowy_common.c
SRCS_snowy_family += hw/platform/snowy_family/snowy_qemu.c

LDFLAGS_snowy_family = $(LDFLAGS_stm32f4xx)
LIBS_snowy_family = $(LIBS_stm32f4xx)
Expand Down
60 changes: 60 additions & 0 deletions hw/platform/snowy_family/snowy_qemu.c
@@ -0,0 +1,60 @@
#include "snowy_qemu.h"
#include "stm32_usart.h"
#include "stm32_power.h"
#include "semphr.h"

static const stm32_usart_config_t _usart2_config = {
.usart = USART2,
.flow_control_enabled = FLOW_CONTROL_DISABLED,
.usart_periph_bus = STM32_POWER_APB1,
.gpio_pin_tx_num = 2,
.gpio_pin_rx_num = 3, // this is even more ignored by QEMU than tx
.gpio_pin_rts_num = 0,
.gpio_pin_cts_num = 0,
.gpio_ptr = GPIOA,
.gpio_clock = RCC_AHB1Periph_GPIOA,
.usart_clock = RCC_APB1Periph_USART2,
.af = GPIO_AF_USART2,
};

static stm32_usart_t _usart2 = {
&_usart2_config,
NULL, /* no dma */
230400
};

static StaticSemaphore_t _usart2_mutex_mem;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is very WIP, but as a general rule any RTOS specific code such as a mutex/semaphore thread etc live in /rcore

This allows for a little clearer separation and when it comes to getting tintin ready it will make this a bit easier.

I'm sure you know already, but just a gentle tap

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll clarify in that locking usually happens in rcore. in this case rcore/qemu.c looks likely

static SemaphoreHandle_t _usart2_mutex;

void hw_qemu_init(void)
{
stm32_usart_init_device(&_usart2);
_usart2_mutex = xSemaphoreCreateMutexStatic(&_usart2_mutex_mem);
}

int hw_qemu_has_data(void)
{
if (xSemaphoreTake(_usart2_mutex, 0) == pdPASS)
{
int result = stm32_usart_has_data(&_usart2);
xSemaphoreGive(_usart2_mutex);
return result;
}
return 0;
}

size_t hw_qemu_read(void *buffer, size_t max_len)
{
xSemaphoreTake(_usart2_mutex, portMAX_DELAY);
size_t bytes_read = stm32_usart_read(&_usart2, (uint8_t*)buffer, max_len);
xSemaphoreGive(_usart2_mutex);
return bytes_read;
}

size_t hw_qemu_write(const void *buffer, size_t len)
{
xSemaphoreTake(_usart2_mutex, portMAX_DELAY);
size_t bytes_written = stm32_usart_write(&_usart2, (const uint8_t*)buffer, len);
xSemaphoreGive(_usart2_mutex);
return bytes_written;
}
7 changes: 7 additions & 0 deletions hw/platform/snowy_family/snowy_qemu.h
@@ -0,0 +1,7 @@
#pragma once
#include "platform.h"

void hw_qemu_init(void);
int hw_qemu_has_data(void);
size_t hw_qemu_read(void *buffer, size_t max_len);
size_t hw_qemu_write(const void *buffer, size_t len);
2 changes: 2 additions & 0 deletions lib/minilib/inc/minilib.h
Expand Up @@ -32,6 +32,8 @@ extern void tohex(char *s, unsigned long l);
extern void btohex(char *s, unsigned char c);
extern unsigned short htons(unsigned short in);
extern unsigned int htonl(unsigned int in);
extern unsigned short ntohs(unsigned short in);
extern unsigned int ntohl(unsigned int in);

/* crc32.c */
extern void crc32_init();
Expand Down
32 changes: 21 additions & 11 deletions lib/minilib/minilib.c
Expand Up @@ -17,23 +17,23 @@ void _memcpy_fast(void *dest, const void *src, int bytes)
{
/* I hate everyone */
/* Since we otherwise compile with -O0, we might as well manually speed this up a bit. */

char *cdest = dest;
const char *csrc = src;
int *idest;
const int *isrc;
int nwords;

/* Align to src (picked arbitrarily; might as well align to something) */
while (bytes && ((unsigned int)csrc & 3))
{
*(cdest++) = *(csrc++);
bytes--;
}

idest = (int *)cdest;
isrc = (const int *)csrc;

nwords = bytes / 4;
bytes -= bytes & ~3;
if (nwords != 0)
Expand All @@ -48,7 +48,7 @@ void _memcpy_fast(void *dest, const void *src, int bytes)
case 2: nwords--; *(idest++) = *(isrc++);
case 1: nwords--; *(idest++) = *(isrc++);
} while (nwords);

cdest = (char *)idest;
csrc = (const char *)isrc;
while (bytes) /* Clean up the remainder */
Expand All @@ -62,7 +62,7 @@ void _memcpy_slow(void *dest, const void *src, int bytes)
{
unsigned char *cdest = dest;
const unsigned char *csrc = src;

while (bytes--)
*(cdest++) = *(csrc++);
}
Expand Down Expand Up @@ -108,7 +108,7 @@ void *memmove(void *dest, const void *src, int bytes)
*(--cdest) = *(--csrc);
} else
memcpy(dest, src, bytes);

return dest;
}

Expand Down Expand Up @@ -151,24 +151,24 @@ int strlen(const char *c)
void *strcpy(char *a2, const char *a1)
{
char *origa2 = a2;

do {
*(a2++) = *a1;
} while (*(a1++));

return origa2;
}

void *strcat(char *dest, const char *src)
{
char *origdest = dest;

while (*dest)
dest++;
while (*src)
*(dest++) = *(src++);
*(dest++) = *(src++);

return origdest;
}
static const char hexarr[] = "0123456789ABCDEF";
Expand Down Expand Up @@ -201,6 +201,16 @@ unsigned int htonl(unsigned int in)
((in & 0xff000000UL) >> 24);
}

unsigned short ntohs(unsigned short in)
{
return htons(in); // for pebble this is equivalent
}

unsigned int ntohl(unsigned int in)
{
return htonl(in);
}

int atoi(const char *c)
{
if (!c) return 0;
Expand Down
2 changes: 1 addition & 1 deletion rcore/power.c
Expand Up @@ -57,7 +57,7 @@ void power_update_battery(void)
{
_bat_voltage = hw_power_get_bat_mv();
_bat_pct = map_range(_bat_voltage, 2600, 3500, 0, 100);
SYS_LOG("PWR", APP_LOG_LEVEL_INFO, "VBAT %ldmV %d%%", _bat_voltage, _bat_pct);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this was annoying

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes :)
I also left my debug spews in for now

//SYS_LOG("PWR", APP_LOG_LEVEL_INFO, "VBAT %ldmV %d%%", _bat_voltage, _bat_pct);

/* battery low */
if (_bat_pct > 20)
Expand Down
96 changes: 96 additions & 0 deletions rcore/qemu.c
@@ -0,0 +1,96 @@
#include "qemu.h"
#include "task.h"
#include "log.h"
#include "rebbleos.h"

extern const QemuEndpoint qemu_endpoints[];

#define STACK_SZ_QEMU configMINIMAL_STACK_SIZE + 600

static TaskHandle_t _qemu_task;
static StackType_t _qemu_task_stack[STACK_SZ_QEMU];
static StaticTask_t _qemu_task_buf;
static uint8_t _qemu_packet_buffer[QEMU_MAX_DATA_LEN];

static void _qemu_thread(void *pvParameters);
static void _qemu_handle_packet();

uint8_t qemu_init(void)
{
hw_qemu_init();
_qemu_task = xTaskCreateStatic(_qemu_thread,
"QEMU", STACK_SZ_QEMU, NULL,
tskIDLE_PRIORITY + 3UL,
_qemu_task_stack, &_qemu_task_buf);
return INIT_RESP_OK;
}

static QemuEndpointHandler _qemu_find_endpoint_handler(uint16_t protocol)
{
const QemuEndpoint *endpoint = qemu_endpoints;
while (endpoint->handler != NULL)
{
if (endpoint->protocol == protocol)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is not true do we end up looping forever? What circumstance could cause this?

return endpoint->handler;
}
return NULL;
}

static void _qemu_thread(void *pvParameters)
{
for (;;)
{
if (hw_qemu_has_data())
_qemu_handle_packet();
vTaskDelay(pdMS_TO_TICKS(16));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the qemu CTS pin connected? Would be nice to use an interrupt for this.
maybe a define for the 16ms? Does this affect performance?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this test I just wanted to get something working, an interrupt I would strongly prefer as well.
I guess this brute force method will affect performance much more than we want

}
}

static void _qemu_read_header(QemuCommChannelHeader *header)
{
hw_qemu_read(_qemu_packet_buffer, sizeof(QemuCommChannelHeader));
const QemuCommChannelHeader *raw_header = (const QemuCommChannelHeader*)_qemu_packet_buffer;
header->signature = ntohs(raw_header->signature);
header->protocol = ntohs(raw_header->protocol);
header->len = ntohs(raw_header->len);
}

static void _qemu_read_footer(QemuCommChannelFooter *footer)
{
hw_qemu_read(_qemu_packet_buffer, sizeof(QemuCommChannelFooter));
const QemuCommChannelFooter *raw_footer = (const QemuCommChannelFooter*)_qemu_packet_buffer;
footer->signature = ntohs(raw_footer->signature);
}

static void _qemu_handle_packet(void)
{
QemuCommChannelHeader header;
_qemu_read_header(&header);
if (header.signature != QEMU_HEADER_SIGNATURE)
{
KERN_LOG("QEMU", APP_LOG_LEVEL_ERROR, "Invalid header signature: %x", header.signature);
return;
}
if (header.len > QEMU_MAX_DATA_LEN)
{
KERN_LOG("QEMU", APP_LOG_LEVEL_ERROR, "Invalid packet size: %d", header.len);
return;
}
QemuEndpointHandler handler = _qemu_find_endpoint_handler(header.protocol);
if (handler == NULL)
{
KERN_LOG("QEMU", APP_LOG_LEVEL_ERROR, "Unknown protocol: %d", header.protocol);
}

size_t len = header.len;
hw_qemu_read(_qemu_packet_buffer, len);
handler(_qemu_packet_buffer, len);

const QemuCommChannelFooter footer;
_qemu_read_footer(&footer);
if (footer.signature != QEMU_FOOTER_SIGNATURE)
{
KERN_LOG("QEMU", APP_LOG_LEVEL_ERROR, "Invalid footer signature: %x", footer.signature);
return;
}
}
44 changes: 44 additions & 0 deletions rcore/qemu.h
@@ -0,0 +1,44 @@
#pragma once
#include "platform.h"

#define QEMU_HEADER_SIGNATURE 0xFEED
#define QEMU_FOOTER_SIGNATURE 0xBEEF
#define QEMU_MAX_DATA_LEN 2048

typedef struct
{
uint16_t signature; // QEMU_HEADER_SIGNATURE
uint16_t protocol; // one of QemuProtocol
uint16_t len; // number of bytes that follow (not including this header or footer)
} QemuCommChannelHeader;

typedef struct
{
uint16_t signature; // QEMU_FOOTER_SIGNATURE
} QemuCommChannelFooter;

enum
{
// Pebble standard
QemuProtocol_SPP = 1,
QemuProtocol_Tap = 2,
QemuProtocol_BluetoothConnection = 3,
QemuProtocol_Compass = 4,
QemuProtocol_Battery = 5,
QemuProtocol_Accel = 6,
QemuProtocol_Vibration = 7,
QemuProtocol_Button = 8,

// Rebble custom
QemuProtocol_Tests = 100
};

typedef void (*QemuEndpointHandler)(const void *data, size_t len);
typedef struct
{
uint16_t protocol;
QemuEndpointHandler handler;
} QemuEndpoint;

uint8_t qemu_init(void);
void qemu_send_packet(const void *buffer, size_t len);