Line data Source code
1 0 : /* 2 : * Copyright (c) 2024 Nordic Semiconductor ASA 3 : * 4 : * SPDX-License-Identifier: Apache-2.0 5 : */ 6 : 7 : #ifndef ZEPHYR_INCLUDE_SHELL_WEBSOCKET_H_ 8 : #define ZEPHYR_INCLUDE_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 0 : #define SHELL_WEBSOCKET_SERVICE_COUNT CONFIG_SHELL_WEBSOCKET_BACKEND_COUNT 20 : 21 : /** Line buffer structure. */ 22 1 : struct shell_websocket_line_buf { 23 : /** Line buffer. */ 24 1 : char buf[CONFIG_SHELL_WEBSOCKET_LINE_BUF_SIZE]; 25 : 26 : /** Current line length. */ 27 1 : uint16_t len; 28 : }; 29 : 30 : /** WEBSOCKET-based shell transport. */ 31 1 : struct shell_websocket { 32 : /** Handler function registered by shell. */ 33 1 : shell_transport_handler_t shell_handler; 34 : 35 : /** Context registered by shell. */ 36 1 : void *shell_context; 37 : 38 : /** Buffer for outgoing line. */ 39 1 : struct shell_websocket_line_buf line_out; 40 : 41 : /** Array for sockets used by the websocket service. */ 42 1 : struct zsock_pollfd fds[1]; 43 : 44 : /** Input buffer. */ 45 1 : uint8_t rx_buf[CONFIG_SHELL_CMD_BUFF_SIZE]; 46 : 47 : /** Number of data bytes within the input buffer. */ 48 1 : size_t rx_len; 49 : 50 : /** Mutex protecting the input buffer access. */ 51 1 : 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 1 : struct k_work_delayable send_work; 58 0 : struct k_work_sync work_sync; 59 : 60 : /** If set, no output is sent to the WEBSOCKET client. */ 61 1 : bool output_lock; 62 : }; 63 : 64 0 : extern const struct shell_transport_api shell_websocket_transport_api; 65 0 : extern int shell_websocket_setup(int ws_socket, void *user_data); 66 0 : extern int shell_websocket_enable(const struct shell *sh); 67 : 68 0 : #define GET_WS_NAME(_service) ws_ctx_##_service 69 0 : #define GET_WS_SHELL_NAME(_name) shell_websocket_##_name 70 0 : #define GET_WS_TRANSPORT_NAME(_service) transport_shell_ws_##_service 71 0 : #define GET_WS_DETAIL_NAME(_service) ws_res_detail_##_service 72 : 73 0 : #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 0 : #define SHELL_WS_PORT_NAME(_service) http_service_##_service 81 0 : #define SHELL_WS_BUF_NAME(_service) ws_recv_buffer_##_service 82 0 : #define SHELL_WS_TEMP_RECV_BUF_SIZE 256 83 : 84 0 : #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 0 : #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 0 : #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 0 : #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 /* ZEPHYR_INCLUDE_SHELL_WEBSOCKET_H_ */