-
Notifications
You must be signed in to change notification settings - Fork 55
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this was annoying There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes :) |
||
//SYS_LOG("PWR", APP_LOG_LEVEL_INFO, "VBAT %ldmV %d%%", _bat_voltage, _bat_pct); | ||
|
||
/* battery low */ | ||
if (_bat_pct > 20) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
} | ||
} | ||
|
||
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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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