314 lines
10 KiB
C
314 lines
10 KiB
C
/**
|
|
* \file
|
|
* \brief shelly - a shell process
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <drivers/lpuart.h>
|
|
#include <drivers/gic_dist.h>
|
|
#include <maps/imx8x_map.h>
|
|
#include <maps/qemu_map.h>
|
|
#include <aos/aos.h>
|
|
#include <aos/inthandler.h>
|
|
#include <aos/ump_binding.h>
|
|
#include <aos/ump_chan.h>
|
|
#include <aos/shelly_client.h>
|
|
#include <aos/aos_rpc.h>
|
|
#include <fs/fs.h>
|
|
#include <grading.h>
|
|
|
|
#include "shelly.h"
|
|
|
|
//state that is shared for all clients
|
|
static struct shelly_st_global * global_state;
|
|
|
|
#include "shelly_cmd.c"
|
|
|
|
static void init_fs_on_demand(void) {
|
|
errval_t err;
|
|
if (global_state->did_init_filesystem == false) {
|
|
err = filesystem_init();
|
|
if (err_is_fail(err)) shelly_write_str("Failed to init filesystem\n\r");
|
|
global_state->did_init_filesystem = true;
|
|
SHELLY_DEBUG("initialized filesystem\n");
|
|
}
|
|
}
|
|
|
|
|
|
bool is_newline(char c) {
|
|
return c == ASCII_NL || c == ASCII_EOF || c == ASCII_CR;
|
|
}
|
|
|
|
//prints a null terminated string
|
|
errval_t shelly_write_str(char * str) {
|
|
errval_t err;
|
|
for (size_t i = 0; str[i] != '\0'; ++i) {
|
|
err = lpuart_putchar(global_state->uart_s, str[i]);
|
|
if (err_is_fail(err)) return err_push(err, SHELLY_ERR_WRITE);
|
|
}
|
|
err = lpuart_putchar(global_state->uart_s, '\0');
|
|
if (err_is_fail(err)) return err_push(err, SHELLY_ERR_WRITE);
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
void shelly_reset_buffer(void) {
|
|
global_state->buffer_i = 0;
|
|
memset(global_state->buf, 0, SHELLY_BUF_SIZE);
|
|
}
|
|
|
|
errval_t shelly_buffer_append(char c) {
|
|
global_state->buf[global_state->buffer_i] = c;
|
|
global_state->buffer_i += 1;
|
|
|
|
if (global_state->buffer_i == SHELLY_BUF_SIZE) {
|
|
shelly_reset_buffer();
|
|
return SHELLY_ERR_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
__attribute__((__used__))
|
|
static void handle_lpuart_interrupt(void * arg) {
|
|
errval_t err;
|
|
assert(global_state);
|
|
|
|
char input_char;
|
|
while (true) {
|
|
err = lpuart_getchar(global_state->uart_s, &input_char);
|
|
if (err == LPUART_ERR_NO_DATA) {
|
|
break;
|
|
} else if (err_is_fail(err)) {
|
|
USER_PANIC_ERR(err, "while trying to get character");
|
|
}
|
|
|
|
err = lpuart_putchar(global_state->uart_s, input_char);
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "while trying to put character");
|
|
|
|
if (is_newline(input_char)) {
|
|
err = lpuart_putchar(global_state->uart_s, ASCII_NL);
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "while trying to put character");
|
|
}
|
|
|
|
if (is_newline(input_char)) {
|
|
global_state->buf[global_state->buffer_i] = '\0';
|
|
global_state->buffer_i += 1;
|
|
|
|
err = shelly_write_str("\n\r");
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "while trying to write string");
|
|
|
|
if (global_state->current_getchar_request != NULL) {
|
|
//send to client
|
|
for (int i = 0; i < global_state->buffer_i; ++i) {
|
|
send_response(global_state->current_getchar_request, SYS_ERR_OK, global_state->buf[i], 0, NULL);
|
|
}
|
|
global_state->current_getchar_request = NULL;
|
|
shelly_reset_buffer();
|
|
} else {
|
|
//treat as command
|
|
err = handle_shelly_cmd(global_state->buf, global_state->buffer_i);
|
|
if (err_is_fail(err)) shelly_write_str("failed to run command :(\n\r");
|
|
|
|
shelly_reset_buffer();
|
|
|
|
err = shelly_write_str("\n\ryou@windows me > ");
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "while trying to write string");
|
|
}
|
|
} else {
|
|
err = shelly_buffer_append(input_char);
|
|
if (err_is_fail(err)) {
|
|
err = shelly_write_str("shelly buffer full! resetting...\n\r");
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "while trying to write string");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handle_send_completed(void *arg, struct ump_send_queue_entry *entry) {
|
|
free((void *)entry->header);
|
|
free((void *)entry->payload);
|
|
free(entry);
|
|
}
|
|
|
|
static void send_response(struct shelly_st_local * local_state, errval_t err, char c, size_t payload_size, void *payload) {
|
|
assert(local_state);
|
|
|
|
// SHELLY_DEBUG("[send_response]\n");
|
|
struct shelly_header * header = malloc(sizeof(struct shelly_header));
|
|
assert(header);
|
|
header->type = SHELLY_MSG_RESPONSE;
|
|
header->err = err;
|
|
header->character = c;
|
|
|
|
struct ump_send_queue_entry *entry = malloc(sizeof(struct ump_send_queue_entry));
|
|
if (entry == NULL) USER_PANIC("Ran out of memory");
|
|
|
|
ump_send(
|
|
local_state->send_chan,
|
|
entry,
|
|
sizeof(struct shelly_header),
|
|
header,
|
|
payload_size,
|
|
payload,
|
|
handle_send_completed,
|
|
NULL
|
|
);
|
|
// SHELLY_DEBUG("[send_response] done\n");
|
|
}
|
|
|
|
static void handle_payload(void *arg, size_t payload_size, void *payload) {
|
|
struct shelly_st_local * local_state = arg;
|
|
assert(local_state);
|
|
assert(global_state);
|
|
|
|
// listen for the next request on this channel
|
|
ump_recv_header(local_state->recv_chan, handle_client_request, local_state);
|
|
}
|
|
|
|
static void handle_client_request(void *arg, size_t header_size, void *header_buf, size_t payload_size) {
|
|
errval_t err;
|
|
struct shelly_st_local * local_state = arg;
|
|
assert(local_state);
|
|
assert(global_state);
|
|
|
|
// SHELLY_DEBUG("[handle_client_request] client_id=%d\n", local_state->client_id);
|
|
|
|
struct shelly_header * header = (struct shelly_header *) header_buf;
|
|
|
|
if (header->type == SHELLY_MSG_GETCHAR) {
|
|
grading_rpc_handler_serial_getchar();
|
|
shelly_reset_buffer();
|
|
global_state->current_getchar_request = local_state;
|
|
} else if (header->type == SHELLY_MSG_PUTCHAR) {
|
|
grading_rpc_handler_serial_putchar(header->character);
|
|
err = lpuart_putchar(global_state->uart_s, header->character);
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "while trying to put character");
|
|
if (is_newline(header->character)) {
|
|
err = lpuart_putchar(global_state->uart_s, ASCII_CR);
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "while trying to put character");
|
|
}
|
|
}
|
|
|
|
// skip payload
|
|
ump_recv_payload(local_state->recv_chan, NULL, handle_payload, local_state);
|
|
}
|
|
|
|
__attribute__((__used__))
|
|
static errval_t connection_callback(void *arg, struct capref cap) {
|
|
errval_t err;
|
|
|
|
SHELLY_DEBUG("incoming connection\n");
|
|
assert(global_state);
|
|
|
|
//create a new local state
|
|
struct shelly_st_local * local_state = calloc(sizeof(struct shelly_st_local), 1);
|
|
assert(local_state);
|
|
|
|
local_state->client_id = global_state->num_clients;
|
|
global_state->num_clients += 1;
|
|
|
|
// we can run the server on the default waitset since we are dispatching on
|
|
// it for listening to connections anyways
|
|
err = ump_chan_init(UMP_ROLE_SERVER, &local_state->send_chan, &local_state->recv_chan, sizeof(struct shelly_header), cap, global_state->ws);
|
|
if (err_is_fail(err)) return err;
|
|
// SHELLY_DEBUG("[connection_callback] local_state=%p\n", local_state);
|
|
|
|
ump_recv_header(local_state->recv_chan, handle_client_request, local_state);
|
|
|
|
SHELLY_DEBUG("connection ready to receive requests\n");
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
errval_t err;
|
|
|
|
global_state = calloc(sizeof(struct shelly_st_global), 1);
|
|
assert(global_state);
|
|
|
|
global_state->num_clients = 0;
|
|
global_state->buffer_i = 0;
|
|
global_state->ws = get_default_waitset();
|
|
global_state->current_getchar_request = NULL;
|
|
global_state->did_init_filesystem = false;
|
|
|
|
//afeer: map lpuart registers
|
|
struct capref cap_arg0 = {
|
|
.cnode = cnode_arg,
|
|
.slot = 0
|
|
};
|
|
err = paging_map_frame_attr(get_current_paging_state(),
|
|
(void **) &global_state->lpuart_base, IMX8X_UART_SIZE,
|
|
cap_arg0, VREGION_FLAGS_READ_WRITE_NOCACHE);
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "while mapping lpuart registers");
|
|
|
|
SHELLY_DEBUG("mapped lpuart\n");
|
|
|
|
//afeer: map gic registers
|
|
struct capref cap_arg1 = {
|
|
.cnode = cnode_arg,
|
|
.slot = 1
|
|
};
|
|
err = paging_map_frame_attr(get_current_paging_state(),
|
|
(void**) &global_state->gic_base, IMX8X_GIC_DIST_SIZE,
|
|
cap_arg1, VREGION_FLAGS_READ_WRITE_NOCACHE);
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "while mapping gic registers");
|
|
|
|
SHELLY_DEBUG("mapped gic\n");
|
|
|
|
//afeer: init gic driver
|
|
err = gic_dist_init(&global_state->gic_s, global_state->gic_base);
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "couln't init gic_dist interrupts");
|
|
|
|
SHELLY_DEBUG("initialized gic driver\n");
|
|
|
|
//afeer: init lpuart driver
|
|
err = lpuart_init(&global_state->uart_s, global_state->lpuart_base);
|
|
if (err_is_fail(err)) {
|
|
USER_PANIC_ERR(err, "couldn't init lpuart");
|
|
}
|
|
|
|
SHELLY_DEBUG("initialized lpuart driver\n");
|
|
|
|
struct capref inter_cap;
|
|
err = inthandler_alloc_dest_irq_cap(IMX8X_UART3_INT, &inter_cap);
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "couln't alloc dest cap");
|
|
|
|
SHELLY_DEBUG("created interrupt cap\n");
|
|
|
|
err = inthandler_setup(inter_cap, global_state->ws, MKCLOSURE(handle_lpuart_interrupt, NULL));
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "couln't setup interrupt handler");
|
|
|
|
SHELLY_DEBUG("setup interrupt handler\n");
|
|
|
|
err = gic_dist_enable_interrupt(global_state->gic_s, IMX8X_UART3_INT, 0b11111111, 0);
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "couln't enable gic interrupts");
|
|
|
|
SHELLY_DEBUG("enabled gic interrupt\n");
|
|
|
|
err = lpuart_enable_interrupt(global_state->uart_s);
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "couln't enable lpuart interrupts");
|
|
|
|
SHELLY_DEBUG("enabled lpuart interrupt\n");
|
|
|
|
struct ump_binding_server server;
|
|
err = ump_binding_register(&server, UMP_SERVER_SHELLY, connection_callback, NULL);
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to register UMP server");
|
|
|
|
SHELLY_DEBUG("registered as shelly server\n");
|
|
|
|
err = shelly_write_str("Welcome to Shelly(TM)(TM)!\n\r");
|
|
if (err_is_fail(err)) USER_PANIC_ERR(err, "while trying to write welcome message");
|
|
|
|
while (true) {
|
|
err = event_dispatch(global_state->ws);
|
|
if (err_is_fail(err)) {
|
|
USER_PANIC_ERR(err, "in event_dispatch");
|
|
}
|
|
}
|
|
}
|