/** * \file * \brief shelly - a shell process */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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"); } } }