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 : int shell_websocket_setup(int ws_socket, struct http_request_ctx *request_ctx, void *user_data);
66 0 : 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 : NULL, \
125 : NULL, \
126 : _sec_tag_list, \
127 : _sec_tag_list_size); \
128 : DEFINE_WEBSOCKET_SERVICE(_service); \
129 :
130 :
131 : #else /* CONFIG_NET_SOCKETS_SOCKOPT_TLS */
132 : /* TLS not possible so define only normal HTTP service */
133 0 : #define WEBSOCKET_CONSOLE_DEFINE(_service, _sec_tag_list, _sec_tag_list_size) \
134 : static uint16_t SHELL_WS_PORT_NAME(_service) = \
135 : CONFIG_SHELL_WEBSOCKET_PORT; \
136 : HTTP_SERVICE_DEFINE(_service, \
137 : CONFIG_SHELL_WEBSOCKET_IP_ADDR, \
138 : &SHELL_WS_PORT_NAME(_service), \
139 : SHELL_WEBSOCKET_SERVICE_COUNT, \
140 : SHELL_WEBSOCKET_SERVICE_COUNT, \
141 : NULL, NULL, NULL); \
142 : DEFINE_WEBSOCKET_SERVICE(_service)
143 :
144 : #endif /* CONFIG_NET_SOCKETS_SOCKOPT_TLS */
145 :
146 0 : #define WEBSOCKET_CONSOLE_ENABLE(_service) \
147 : (void)shell_websocket_enable(&GET_WS_SHELL_NAME(_service))
148 :
149 : #ifdef __cplusplus
150 : }
151 : #endif
152 :
153 : #endif /* ZEPHYR_INCLUDE_SHELL_WEBSOCKET_H_ */
|