|
| 1 | +/* |
| 2 | + * Copyright (c) 2024 Nordic Semiconductor ASA |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#ifndef SHELL_WEBSOCKET_H__ |
| 8 | +#define SHELL_WEBSOCKET_H__ |
| 9 | + |
| 10 | +#include <zephyr/net/socket.h> |
| 11 | +#include <zephyr/net/http/server.h> |
| 12 | +#include <zephyr/net/http/service.h> |
| 13 | +#include <zephyr/shell/shell.h> |
| 14 | + |
| 15 | +#ifdef __cplusplus |
| 16 | +extern "C" { |
| 17 | +#endif |
| 18 | + |
| 19 | +#define SHELL_WEBSOCKET_SERVICE_COUNT CONFIG_SHELL_WEBSOCKET_BACKEND_COUNT |
| 20 | + |
| 21 | +/** Line buffer structure. */ |
| 22 | +struct shell_websocket_line_buf { |
| 23 | + /** Line buffer. */ |
| 24 | + char buf[CONFIG_SHELL_WEBSOCKET_LINE_BUF_SIZE]; |
| 25 | + |
| 26 | + /** Current line length. */ |
| 27 | + uint16_t len; |
| 28 | +}; |
| 29 | + |
| 30 | +/** WEBSOCKET-based shell transport. */ |
| 31 | +struct shell_websocket { |
| 32 | + /** Handler function registered by shell. */ |
| 33 | + shell_transport_handler_t shell_handler; |
| 34 | + |
| 35 | + /** Context registered by shell. */ |
| 36 | + void *shell_context; |
| 37 | + |
| 38 | + /** Buffer for outgoing line. */ |
| 39 | + struct shell_websocket_line_buf line_out; |
| 40 | + |
| 41 | + /** Array for sockets used by the websocket service. */ |
| 42 | + struct zsock_pollfd fds[1]; |
| 43 | + |
| 44 | + /** Input buffer. */ |
| 45 | + uint8_t rx_buf[CONFIG_SHELL_CMD_BUFF_SIZE]; |
| 46 | + |
| 47 | + /** Number of data bytes within the input buffer. */ |
| 48 | + size_t rx_len; |
| 49 | + |
| 50 | + /** Mutex protecting the input buffer access. */ |
| 51 | + struct k_mutex rx_lock; |
| 52 | + |
| 53 | + /** The delayed work is used to send non-lf terminated output that has |
| 54 | + * been around for "too long". This will prove to be useful |
| 55 | + * to send the shell prompt for instance. |
| 56 | + */ |
| 57 | + struct k_work_delayable send_work; |
| 58 | + struct k_work_sync work_sync; |
| 59 | + |
| 60 | + /** If set, no output is sent to the WEBSOCKET client. */ |
| 61 | + bool output_lock; |
| 62 | +}; |
| 63 | + |
| 64 | +extern const struct shell_transport_api shell_websocket_transport_api; |
| 65 | +extern int shell_websocket_setup(int ws_socket, void *user_data); |
| 66 | +extern int shell_websocket_enable(const struct shell *sh); |
| 67 | + |
| 68 | +#define GET_WS_NAME(_service) ws_ctx_##_service |
| 69 | +#define GET_WS_SHELL_NAME(_name) shell_websocket_##_name |
| 70 | +#define GET_WS_TRANSPORT_NAME(_service) transport_shell_ws_##_service |
| 71 | +#define GET_WS_DETAIL_NAME(_service) ws_res_detail_##_service |
| 72 | + |
| 73 | +#define SHELL_WEBSOCKET_DEFINE(_service) \ |
| 74 | + static struct shell_websocket GET_WS_NAME(_service); \ |
| 75 | + static struct shell_transport GET_WS_TRANSPORT_NAME(_service) = { \ |
| 76 | + .api = &shell_websocket_transport_api, \ |
| 77 | + .ctx = &GET_WS_NAME(_service), \ |
| 78 | + } |
| 79 | + |
| 80 | +#define SHELL_WS_PORT_NAME(_service) http_service_##_service |
| 81 | +#define SHELL_WS_BUF_NAME(_service) ws_recv_buffer_##_service |
| 82 | +#define SHELL_WS_TEMP_RECV_BUF_SIZE 256 |
| 83 | + |
| 84 | +#define DEFINE_WEBSOCKET_HTTP_SERVICE(_service) \ |
| 85 | + uint8_t SHELL_WS_BUF_NAME(_service)[SHELL_WS_TEMP_RECV_BUF_SIZE]; \ |
| 86 | + struct http_resource_detail_websocket \ |
| 87 | + GET_WS_DETAIL_NAME(_service) = { \ |
| 88 | + .common = { \ |
| 89 | + .type = HTTP_RESOURCE_TYPE_WEBSOCKET, \ |
| 90 | + \ |
| 91 | + /* We need HTTP/1.1 GET method for upgrading */ \ |
| 92 | + .bitmask_of_supported_http_methods = BIT(HTTP_GET), \ |
| 93 | + }, \ |
| 94 | + .cb = shell_websocket_setup, \ |
| 95 | + .data_buffer = SHELL_WS_BUF_NAME(_service), \ |
| 96 | + .data_buffer_len = sizeof(SHELL_WS_BUF_NAME(_service)), \ |
| 97 | + .user_data = &GET_WS_NAME(_service), \ |
| 98 | + }; \ |
| 99 | + HTTP_RESOURCE_DEFINE(ws_resource_##_service, _service, \ |
| 100 | + "/" CONFIG_SHELL_WEBSOCKET_ENDPOINT_URL, \ |
| 101 | + &GET_WS_DETAIL_NAME(_service)) |
| 102 | + |
| 103 | +#define DEFINE_WEBSOCKET_SERVICE(_service) \ |
| 104 | + SHELL_WEBSOCKET_DEFINE(_service); \ |
| 105 | + SHELL_DEFINE(shell_websocket_##_service, \ |
| 106 | + CONFIG_SHELL_WEBSOCKET_PROMPT, \ |
| 107 | + &GET_WS_TRANSPORT_NAME(_service), \ |
| 108 | + CONFIG_SHELL_WEBSOCKET_LOG_MESSAGE_QUEUE_SIZE, \ |
| 109 | + CONFIG_SHELL_WEBSOCKET_LOG_MESSAGE_QUEUE_TIMEOUT, \ |
| 110 | + SHELL_FLAG_OLF_CRLF); \ |
| 111 | + DEFINE_WEBSOCKET_HTTP_SERVICE(_service) |
| 112 | + |
| 113 | +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) |
| 114 | +/* Use a secure connection only for Websocket. */ |
| 115 | +#define WEBSOCKET_CONSOLE_DEFINE(_service, _sec_tag_list, _sec_tag_list_size) \ |
| 116 | + static uint16_t SHELL_WS_PORT_NAME(_service) = \ |
| 117 | + CONFIG_SHELL_WEBSOCKET_PORT; \ |
| 118 | + HTTPS_SERVICE_DEFINE(_service, \ |
| 119 | + CONFIG_SHELL_WEBSOCKET_IP_ADDR, \ |
| 120 | + &SHELL_WS_PORT_NAME(_service), \ |
| 121 | + SHELL_WEBSOCKET_SERVICE_COUNT, \ |
| 122 | + SHELL_WEBSOCKET_SERVICE_COUNT, \ |
| 123 | + NULL, \ |
| 124 | + _sec_tag_list, \ |
| 125 | + _sec_tag_list_size); \ |
| 126 | + DEFINE_WEBSOCKET_SERVICE(_service); \ |
| 127 | + |
| 128 | + |
| 129 | +#else /* CONFIG_NET_SOCKETS_SOCKOPT_TLS */ |
| 130 | +/* TLS not possible so define only normal HTTP service */ |
| 131 | +#define WEBSOCKET_CONSOLE_DEFINE(_service, _sec_tag_list, _sec_tag_list_size) \ |
| 132 | + static uint16_t SHELL_WS_PORT_NAME(_service) = \ |
| 133 | + CONFIG_SHELL_WEBSOCKET_PORT; \ |
| 134 | + HTTP_SERVICE_DEFINE(_service, \ |
| 135 | + CONFIG_SHELL_WEBSOCKET_IP_ADDR, \ |
| 136 | + &SHELL_WS_PORT_NAME(_service), \ |
| 137 | + SHELL_WEBSOCKET_SERVICE_COUNT, \ |
| 138 | + SHELL_WEBSOCKET_SERVICE_COUNT, \ |
| 139 | + NULL); \ |
| 140 | + DEFINE_WEBSOCKET_SERVICE(_service) |
| 141 | + |
| 142 | +#endif /* CONFIG_NET_SOCKETS_SOCKOPT_TLS */ |
| 143 | + |
| 144 | +#define WEBSOCKET_CONSOLE_ENABLE(_service) \ |
| 145 | + (void)shell_websocket_enable(&GET_WS_SHELL_NAME(_service)) |
| 146 | + |
| 147 | +#ifdef __cplusplus |
| 148 | +} |
| 149 | +#endif |
| 150 | + |
| 151 | +#endif /* SHELL_WEBSOCKET_H__ */ |
0 commit comments