blob: 9ca9d2ef0ad1c87152167dc53cc67db97418e30a [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30#include <bcmos_system.h>
31#include <bcmtr_interface.h>
32#include <bcmtr_header.h>
33#include <bcmolt_api.h>
34#include <bcm_api_cli_helpers.h>
35#include <bcmtr_debug.h>
36#include <bcmcli_session.h>
37#include "bcmolt_remote_cli.h"
38#ifdef ENABLE_LOG
39#include "bcm_dev_log.h"
40#endif
41
42#define REMOTE_CLI_BUFFER_MAX 65635 /* Max reassembled packet plus header */
43#define REMOTE_CLI_MESSAGE_QUEUE_DEPTH BCMOS_MSG_POOL_DEFAULT_SIZE
44
45typedef enum
46{
47 REMOTE_CLI_OPCODE_CLI_COMMAND,
48 REMOTE_CLI_OPCODE_INDICATION,
49 REMOTE_CLI_OPCODE_PROXY_RX
50} remote_cli_opcode;
51
52/* Statistics */
53typedef struct
54{
55 unsigned long rx_requests;
56 unsigned long tx_responses;
57 unsigned long indications;
58 unsigned long proxy_rxs;
59 unsigned long unpack_errors;
60 unsigned long pack_errors;
61 unsigned long correlation_errors;
62 unsigned long tx_socket_errors;
63 unsigned long rx_socket_errors;
64 unsigned long message_errors;
65} remote_cli_stats;
66
67typedef struct
68{
69 bcmos_task output_task;
70 bcmos_task socket_task;
71 struct sockaddr_in client;
72 uint16_t port;
73 int client_socket;
74 bcmos_bool is_running;
75 dev_log_id log_id;
76 bcmos_bool output_pending;
77 uint8_t input_buffer[REMOTE_CLI_BUFFER_MAX];
78 char *input_str;
79 uint8_t output_buffer[REMOTE_CLI_BUFFER_MAX];
80 uint16_t output_current_char;
81 bcmos_mutex output_lock;
82 int current_corr_tag;
83 bcmcli_session *session;
84 remote_cli_stats stats;
85} remote_cli_control_block;
86
87static remote_cli_control_block remote_cli_data[BCMTR_MAX_OLTS];
88
89static bcmos_errno remote_cli_stats_cmd(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
90{
91 int device = (int)parm[0].value.number;
92
93 if (device >= BCMTR_MAX_OLTS)
94 {
95 return BCM_ERR_RANGE;
96 }
97
98 bcmcli_print(session, "%-16s: %lu\n", "rx_requests", remote_cli_data[device].stats.rx_requests);
99 bcmcli_print(session, "%-16s: %lu\n", "tx_responses", remote_cli_data[device].stats.tx_responses);
100 bcmcli_print(session, "%-16s: %lu\n", "indications", remote_cli_data[device].stats.indications);
101 bcmcli_print(session, "%-16s: %lu\n", "proxy_rxs", remote_cli_data[device].stats.proxy_rxs);
102 bcmcli_print(session, "%-16s: %lu\n", "unpack_errors", remote_cli_data[device].stats.unpack_errors);
103 bcmcli_print(session, "%-16s: %lu\n", "pack_errors", remote_cli_data[device].stats.pack_errors);
104 bcmcli_print(session, "%-16s: %lu\n", "tx_socket_errors", remote_cli_data[device].stats.tx_socket_errors);
105 bcmcli_print(session, "%-16s: %lu\n", "rx_socket_errors", remote_cli_data[device].stats.rx_socket_errors);
106 bcmcli_print(session, "%-16s: %lu\n", "message_errors", remote_cli_data[device].stats.message_errors);
107 memset(&remote_cli_data[device].stats, 0, sizeof(remote_cli_stats));
108
109 return BCM_ERR_OK;
110}
111
112static void send_output_packets(bcmolt_devid device, remote_cli_opcode opcode, uint16_t corr_tag)
113{
114 bcmolt_buf buf = {};
115 static uint8_t scratch_buffer[REMOTE_CLI_BUFFER_MAX];
116 ssize_t len;
117 bcmos_bool ok = BCMOS_TRUE;
118
119 if (remote_cli_data[device].output_current_char == 0)
120 {
121 return;
122 }
123
124 bcmolt_buf_init(&buf, REMOTE_CLI_BUFFER_MAX, scratch_buffer, BCMOLT_BUF_ENDIAN_FIXED);
125 ok = ok && bcmolt_buf_write_u8(&buf, (uint8_t)opcode);
126 ok = ok && bcmolt_buf_write_u16_be(&buf, corr_tag);
127 ok = ok && bcmolt_buf_write(
128 &buf,
129 remote_cli_data[device].output_buffer,
130 remote_cli_data[device].output_current_char);
131
132 if (!ok)
133 {
134 ++remote_cli_data[device].stats.pack_errors;
135 return;
136 }
137
138 /* Send to client */
139 len = sendto(
140 remote_cli_data[device].client_socket,
141 buf.start,
142 bcmolt_buf_get_used(&buf),
143 0,
144 (struct sockaddr *)&remote_cli_data[device].client,
145 sizeof(remote_cli_data[device].client));
146
147 if (len <= 0)
148 {
149 ++remote_cli_data[device].stats.tx_socket_errors;
150 }
151}
152
153static void process_cli_input(bcmolt_devid device)
154{
155 bcmos_mutex_lock(&remote_cli_data[device].output_lock);
156 remote_cli_data[device].output_current_char = 0;
157 memset(remote_cli_data[device].output_buffer, 0, sizeof(remote_cli_data[device].output_buffer));
158 bcmcli_parse(remote_cli_data[device].session, remote_cli_data[device].input_str);
159 bcmos_mutex_unlock(&remote_cli_data[device].output_lock);
160}
161
162static int remote_cli_session_write_cb(bcmcli_session *session, const char *buf, uint32_t size)
163{
164 int i;
165 bcmolt_devid device = *((bcmolt_devid *)bcmcli_session_user_priv(session));
166
167 bcmos_mutex_lock(&remote_cli_data[device].output_lock);
168 for (i = 0; i < size; ++i)
169 {
170 remote_cli_data[device].output_buffer[remote_cli_data[device].output_current_char] = buf[i];
171 ++remote_cli_data[device].output_current_char;
172 }
173 bcmos_mutex_unlock(&remote_cli_data[device].output_lock);
174
175 return size;
176}
177
178static bcmos_errno remote_cli_open_session(bcmolt_devid device)
179{
180 bcmos_errno rc;
181 bcmcli_session_parm sess_parm = {};
182 sess_parm.access_right = BCMCLI_ACCESS_DEBUG;
183 sess_parm.write = remote_cli_session_write_cb;
184 sess_parm.user_priv = bcmos_calloc(sizeof(device));
185 sess_parm.line_edit_mode = BCMCLI_LINE_EDIT_DISABLE;
186 BUG_ON(sess_parm.user_priv == NULL);
187 *((bcmolt_devid *)sess_parm.user_priv) = device;
188 rc = bcmcli_session_open(&sess_parm, &remote_cli_data[device].session);
189
190 if (rc != BCM_ERR_OK)
191 {
192 perror("Can't open session");
193#ifdef ENABLE_LOG
194 BCM_LOG(
195 ERROR,
196 remote_cli_data[device].log_id,
197 "Can't open session\n");
198#endif
199 shutdown(remote_cli_data[device].client_socket, SHUT_RDWR);
200 }
201 return rc;
202}
203
204static bcmos_errno remote_cli_open_socket(bcmolt_devid device)
205{
206 struct sockaddr_in sa = {};
207 /* Start listening on port */
208 remote_cli_data[device].client_socket = socket(AF_INET, SOCK_DGRAM, 0);
209 if (remote_cli_data[device].client_socket < 0)
210 {
211 perror("Can't create socket");
212#ifdef ENABLE_LOG
213 BCM_LOG(
214 ERROR,
215 remote_cli_data[device].log_id,
216 "Can't create socket\n");
217#endif
218 return BCM_ERR_INTERNAL;
219 }
220
221 /* Bind local */
222 sa.sin_family = AF_INET;
223 sa.sin_port = (in_port_t)htons(remote_cli_data[device].port);
224 sa.sin_addr.s_addr = INADDR_ANY;
225 if (bind(remote_cli_data[device].client_socket, (struct sockaddr*)&sa, sizeof(sa)) == -1)
226 {
227 perror("Can't bind to socket");
228#ifdef ENABLE_LOG
229 BCM_LOG(
230 ERROR,
231 remote_cli_data[device].log_id,
232 "Can't bind socket to port %u\n",
233 remote_cli_data[device].port);
234#endif
235 shutdown(remote_cli_data[device].client_socket, SHUT_RDWR);
236 return BCM_ERR_INTERNAL;
237 }
238 return BCM_ERR_OK;
239}
240
241static void remote_cli_indication_cb(bcmos_module_id module_id, bcmos_msg *msg)
242{
243 bcmolt_devid device = (bcmolt_devid)(module_id - BCMOS_MODULE_ID_REMOTE_CLI_DEV0);
244 bcmolt_msg *ind = msg->data;
245
246 bcmos_mutex_lock(&remote_cli_data[device].output_lock);
247 remote_cli_data[device].output_current_char = 0;
248 memset(remote_cli_data[device].output_buffer, 0, sizeof(remote_cli_data[device].output_buffer));
249
250 apicli_msg_dump(remote_cli_data[device].session, ind);
251 if (ind->group == BCMOLT_MGT_GROUP_AUTO)
252 {
253 send_output_packets(device, REMOTE_CLI_OPCODE_INDICATION, ind->corr_tag);
254 ++remote_cli_data[device].stats.indications;
255 }
256 else if (ind->group == BCMOLT_MGT_GROUP_PROXY_RX)
257 {
258 send_output_packets(device, REMOTE_CLI_OPCODE_PROXY_RX, ind->corr_tag);
259 ++remote_cli_data[device].stats.proxy_rxs;
260 }
261 else
262 {
263#ifdef ENABLE_LOG
264 BCM_LOG(
265 ERROR,
266 remote_cli_data[device].log_id,
267 "Unknown group type %u\n",
268 ind->group);
269#endif
270 }
271 bcmos_mutex_unlock(&remote_cli_data[device].output_lock);
272
273 bcmolt_msg_free(ind);
274}
275
276static void remote_cli_output_cb(bcmos_module_id module_id, bcmos_msg *msg)
277{
278 bcmolt_devid device = (bcmolt_devid)(module_id - BCMOS_MODULE_ID_REMOTE_CLI_DEV0);
279 process_cli_input(device);
280 send_output_packets(device, REMOTE_CLI_OPCODE_CLI_COMMAND, remote_cli_data[device].current_corr_tag);
281 remote_cli_data[device].output_pending = BCMOS_FALSE;
282 ++remote_cli_data[device].stats.tx_responses;
283}
284
285static int remote_cli_socket_task(long data)
286{
287 bcmolt_devid device = (bcmolt_devid)data;
288 struct sockaddr_in sender;
289 socklen_t sendsize = sizeof(sender);
290 bcmolt_buf buf;
291 ssize_t len;
292 uint8_t opcode = 0;
293 uint16_t corr_tag = 0;
294 bcmos_bool ok = BCMOS_TRUE;
295 bcmos_msg output_msg = { .handler = remote_cli_output_cb };
296 bcmos_errno rc;
297
298 rc = remote_cli_open_socket(device);
299 BCMOS_CHECK_RETURN(rc != BCM_ERR_OK, rc, -1);
300 rc = remote_cli_open_session(device);
301 BCMOS_CHECK_RETURN(rc != BCM_ERR_OK, rc, -1);
302
303 while (remote_cli_data[device].is_running)
304 {
305 if (remote_cli_data[device].output_pending)
306 {
307 bcmos_usleep(1000);
308 continue;
309 }
310
311 remote_cli_data[device].current_corr_tag = -1;
312 memset(remote_cli_data[device].input_buffer, 0, sizeof(remote_cli_data[device].input_buffer));
313
314 memset(&sender, 0, sizeof(sender));
315 len = recvfrom(
316 remote_cli_data[device].client_socket,
317 remote_cli_data[device].input_buffer,
318 sizeof(remote_cli_data[device].input_buffer),
319 0,
320 (struct sockaddr *)&sender,
321 &sendsize);
322 if (len < 0)
323 {
324 bcmos_usleep(1000000);
325 continue;
326 }
327 ++remote_cli_data[device].stats.rx_requests;
328
329 if ((remote_cli_data[device].client.sin_addr.s_addr != sender.sin_addr.s_addr) ||
330 (remote_cli_data[device].client.sin_port != sender.sin_port))
331 {
332 remote_cli_data[device].client = sender;
333 }
334
335 /* Unpack received message */
336 bcmolt_buf_init(&buf, (uint32_t)len, remote_cli_data[device].input_buffer, BCMOLT_BUF_ENDIAN_FIXED);
337 ok = ok && bcmolt_buf_read_u8(&buf, &opcode);
338 ok = ok && bcmolt_buf_read_u16_be(&buf, &corr_tag);
339
340 if (!ok)
341 {
342 ++remote_cli_data[device].stats.unpack_errors;
343 continue;
344 }
345
346 if ((remote_cli_opcode)opcode != REMOTE_CLI_OPCODE_CLI_COMMAND)
347 {
348 ++remote_cli_data[device].stats.unpack_errors;
349 continue;
350 }
351
352 if ((remote_cli_data[device].current_corr_tag != corr_tag) && (remote_cli_data[device].current_corr_tag >= 0))
353 {
354 ++remote_cli_data[device].stats.correlation_errors;
355 continue;
356 }
357
358 remote_cli_data[device].input_str = (char *)bcmolt_buf_snap_get(&buf);
359 remote_cli_data[device].input_str[bcmolt_buf_get_remaining_size(&buf)] = '\0';
360 remote_cli_data[device].current_corr_tag = corr_tag;
361 remote_cli_data[device].output_pending = BCMOS_TRUE;
362 rc = bcmos_msg_send_to_module(
363 bcmos_module_id_for_device(BCMOS_MODULE_ID_REMOTE_CLI_DEV0, device),
364 &output_msg,
365 BCMOS_MSG_SEND_AUTO_FREE);
366 if (rc != BCM_ERR_OK)
367 {
368 ++remote_cli_data[device].stats.message_errors;
369 }
370 }
371
372 bcmos_free(bcmcli_session_user_priv(remote_cli_data[device].session));
373 bcmcli_session_close(remote_cli_data[device].session);
374 shutdown(remote_cli_data[device].client_socket, SHUT_RDWR);
375 remote_cli_data[device].socket_task.destroyed = BCMOS_TRUE;
376 return 0;
377}
378
379static bcmos_errno remote_cli_start(bcmolt_devid device, uint32_t remote_cli_port)
380{
381 bcmos_errno rc;
382 bcmos_task_parm output_task_params =
383 {
384 .name = "remote_cli_indication",
385 .priority = TASK_PRIORITY_TRANSPORT_REMOTE_CLI
386 };
387
388 bcmos_task_parm socket_task_params =
389 {
390 .name = "remote_cli_main",
391 .handler = remote_cli_socket_task,
392 .priority = TASK_PRIORITY_TRANSPORT_REMOTE_CLI,
393 .data = (long)device
394 };
395
396 bcmos_module_parm module_params =
397 {
398 .qparm = { .name = "remote_cli", .size = REMOTE_CLI_MESSAGE_QUEUE_DEPTH }
399 };
400
401 remote_cli_data[device].port = remote_cli_port;
402 remote_cli_data[device].current_corr_tag = -1;
403 remote_cli_data[device].is_running = BCMOS_TRUE;
404
405 /* Create thread listening for incoming APIs */
406 rc = bcmos_task_create(&remote_cli_data[device].socket_task, &socket_task_params);
407 BUG_ON(BCM_ERR_OK != rc);
408 rc = bcmos_task_create(&remote_cli_data[device].output_task, &output_task_params);
409 BUG_ON(BCM_ERR_OK != rc);
410 bcmos_mutex_create(&remote_cli_data[device].output_lock, 0, "remote_cli");
411
412 rc = bcmos_module_create(
413 bcmos_module_id_for_device(BCMOS_MODULE_ID_REMOTE_CLI_DEV0, device),
414 &remote_cli_data[device].output_task,
415 &module_params);
416 BUG_ON(BCM_ERR_OK != rc);
417
418#ifdef ENABLE_LOG
419 BCM_LOG(
420 INFO,
421 remote_cli_data[device].log_id,
422 "BCM68620 remote cli for device %d is listening on port %u\n",
423 (int)device,
424 remote_cli_data[device].port);
425#endif
426
427 return BCM_ERR_OK;
428}
429
430/* Auto / proxy message handler */
431void bcmolt_remote_cli_auto_rx_cb(bcmolt_devid device, bcmolt_msg *msg)
432{
433 bcmos_errno err;
434 bcmolt_msg *ind_clone = NULL;
435
436 if (device >= BCMTR_MAX_OLTS)
437 {
438 return;
439 }
440
441 if (remote_cli_data[device].is_running)
442 {
443 err = bcmolt_msg_clone(&ind_clone, msg);
444 if (err != BCM_ERR_OK)
445 {
446#ifdef ENABLE_LOG
447 BCM_LOG(ERROR, remote_cli_data[device].log_id, "Indication clone failed: %s\n", bcmos_strerror(err));
448#endif
449 return;
450 }
451
452 ind_clone->os_msg.handler = remote_cli_indication_cb;
453 ind_clone->os_msg.data = ind_clone;
454 err = bcmos_msg_send_to_module(
455 bcmos_module_id_for_device(BCMOS_MODULE_ID_REMOTE_CLI_DEV0, device),
456 &ind_clone->os_msg,
457 0);
458 if (err != BCM_ERR_OK)
459 {
460 ++remote_cli_data[device].stats.message_errors;
461 }
462 }
463}
464
465bcmos_errno bcmolt_remote_cli_init(bcmcli_entry *root, bcmolt_devid device, uint32_t remote_cli_port)
466{
467 bcmcli_entry *dir;
468 if (device >= BCMTR_MAX_OLTS)
469 {
470 return BCM_ERR_PARM;
471 }
472
473 if (remote_cli_data[device].is_running)
474 {
475 return BCM_ERR_ALREADY;
476 }
477
478 BCM_MEMZERO_STRUCT(&remote_cli_data[device]);
479
480#ifdef ENABLE_LOG
481 {
482 char log_id[32];
483 snprintf(log_id, sizeof(log_id) - 1, "remote_cli_%d", (int)device);
484 remote_cli_data[device].log_id =
485 bcm_dev_log_id_register(log_id, DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
486 }
487#else
488 remote_cli_data[device].log_id = DEV_LOG_INVALID_ID;
489#endif
490
491 dir = bcmcli_dir_add(root, "remote_cli", "Remote CLI", BCMCLI_ACCESS_GUEST, NULL);
492 if (!dir)
493 {
494#ifdef ENABLE_LOG
495 BCM_LOG(ERROR, remote_cli_data[device].log_id, "Can't create remote CLI directory\n");
496#endif
497 BUG();
498 }
499
500 BCMCLI_MAKE_CMD(dir, "stat", "Remote CLI statistics", remote_cli_stats_cmd,
501 BCMCLI_MAKE_PARM_RANGE(
502 "device",
503 "Device index",
504 BCMCLI_PARM_NUMBER,
505 BCMCLI_PARM_FLAG_OPTIONAL,
506 0,
507 BCMTR_MAX_OLTS - 1));
508
509 return remote_cli_start(device, remote_cli_port);
510}
511
512void bcmolt_remote_cli_stop(void)
513{
514 int i;
515 bcmos_bool was_running[BCMTR_MAX_OLTS] = {};
516
517 for (i = 0; i < BCMTR_MAX_OLTS; i++)
518 {
519 was_running[i] = remote_cli_data[i].is_running;
520 if (was_running[i])
521 {
522 shutdown(remote_cli_data[i].client_socket, SHUT_RDWR);
523 remote_cli_data[i].is_running = BCMOS_FALSE;
524 }
525 }
526
527 for (i = 0; i < BCMTR_MAX_OLTS; i++)
528 {
529 if (!was_running[i])
530 {
531 continue;
532 }
533 while (!remote_cli_data[i].socket_task.destroyed)
534 {
535 bcmos_usleep(10000);
536 }
537 bcmos_task_destroy(&remote_cli_data[i].socket_task);
538 bcmos_module_destroy(bcmos_module_id_for_device(BCMOS_MODULE_ID_REMOTE_CLI_DEV0, (bcmolt_devid)i));
539 bcmos_task_destroy(&remote_cli_data[i].output_task);
540 bcmos_mutex_destroy(&remote_cli_data[i].output_lock);
541 }
542}
543