1 /*
2 * Copyright (c) 2025 Linumiz GmbH
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "ocpp_i.h"
8
9 #include <time.h>
10
11 #include <zephyr/sys/clock.h>
12
13 LOG_MODULE_REGISTER(ocpp, CONFIG_OCPP_LOG_LEVEL);
14
15 #define OCPP_UPSTREAM_PRIORITY 7
16 #define OCPP_WS_TIMEOUT 5000
17 #define OCPP_INTERNAL_MSG_SIZE (sizeof(struct internal_msg))
18 #define FILL_PDU_TABLE(_pdu, _spdu)[_pdu] = {_pdu, _spdu, }
19
20 struct ocpp_msg_table {
21 enum ocpp_pdu_msg pdu;
22 char *spdu;
23 };
24
25 static K_THREAD_STACK_DEFINE(ocpp_int_handler_stack, CONFIG_OCPP_INT_THREAD_STACKSIZE);
26 static K_THREAD_STACK_DEFINE(ocpp_wsreader_stack, CONFIG_OCPP_WSREADER_THREAD_STACKSIZE);
27
28 K_MSGQ_DEFINE(ocpp_iq, OCPP_INTERNAL_MSG_SIZE, CONFIG_OCPP_INTERNAL_MSGQ_CNT, sizeof(uint32_t));
29
30 struct ocpp_info *gctx;
31
32 static struct ocpp_msg_table pdu_msg_table[] = {
33 FILL_PDU_TABLE(PDU_BOOTNOTIFICATION, "BootNotification"),
34 FILL_PDU_TABLE(PDU_AUTHORIZE, "Authorize"),
35 FILL_PDU_TABLE(PDU_START_TRANSACTION, "StartTransaction"),
36 FILL_PDU_TABLE(PDU_STOP_TRANSACTION, "StopTransaction"),
37 FILL_PDU_TABLE(PDU_HEARTBEAT, "Heartbeat"),
38 FILL_PDU_TABLE(PDU_METER_VALUES, "MeterValues"),
39 FILL_PDU_TABLE(PDU_CLEAR_CACHE, "ClearCache"),
40 FILL_PDU_TABLE(PDU_REMOTE_START_TRANSACTION, "RemoteStartTransaction"),
41 FILL_PDU_TABLE(PDU_REMOTE_STOP_TRANSACTION, "RemoteStopTransaction"),
42 FILL_PDU_TABLE(PDU_GET_CONFIGURATION, "GetConfiguration"),
43 FILL_PDU_TABLE(PDU_CHANGE_CONFIGURATION, "ChangeConfiguration"),
44 FILL_PDU_TABLE(PDU_CHANGE_AVAILABILITY, "ChangeAvailability"),
45 FILL_PDU_TABLE(PDU_UNLOCK_CONNECTOR, "UnlockConnector"),
46 FILL_PDU_TABLE(PDU_RESET, "Reset"),
47 };
48
ocpp_get_pdu_literal(enum ocpp_pdu_msg pdu)49 const char *ocpp_get_pdu_literal(enum ocpp_pdu_msg pdu)
50 {
51 if (pdu >= PDU_MSG_END) {
52 return "";
53 }
54
55 return pdu_msg_table[pdu].spdu;
56 }
57
ocpp_find_pdu_from_literal(const char * msg)58 int ocpp_find_pdu_from_literal(const char *msg)
59 {
60 struct ocpp_msg_table *mt = pdu_msg_table;
61 int i;
62
63 for (i = 0; i < PDU_MSG_END; i++) {
64 if (strncmp(mt[i].spdu, msg, strlen(mt[i].spdu)) == 0) {
65 break;
66 }
67 }
68
69 return i;
70 }
71
ocpp_get_utc_now(char utc[CISTR25])72 void ocpp_get_utc_now(char utc[CISTR25])
73 {
74 struct timespec ts;
75 struct tm htime = {0};
76
77 sys_clock_gettime(SYS_CLOCK_REALTIME, &ts);
78 gmtime_r(&ts.tv_sec, &htime);
79
80 snprintk(utc, CISTR25, "%04hu-%02hu-%02huT%02hu:%02hu:%02huZ",
81 htime.tm_year + 1900,
82 htime.tm_mon + 1,
83 htime.tm_mday,
84 htime.tm_hour,
85 htime.tm_min,
86 htime.tm_sec);
87 }
88
ocpp_session_is_valid(struct ocpp_session * sh)89 bool ocpp_session_is_valid(struct ocpp_session *sh)
90 {
91 sys_snode_t *node;
92 struct ocpp_session *lsh;
93 bool is_found = false;
94
95 if (sh == NULL) {
96 return is_found;
97 }
98
99 k_mutex_lock(&gctx->ilock, K_FOREVER);
100 SYS_SLIST_FOR_EACH_NODE(&gctx->slist, node) {
101 lsh = to_session(node);
102 if (lsh == sh) {
103 is_found = true;
104 break;
105 }
106 }
107 k_mutex_unlock(&gctx->ilock);
108
109 return is_found;
110 }
111
ocpp_ws_connect_cb(int ws_sock,struct http_request * req,void * user_data)112 static int ocpp_ws_connect_cb(int ws_sock, struct http_request *req,
113 void *user_data)
114 {
115 struct ocpp_info *ctx = user_data;
116
117 ctx->is_cs_offline = false;
118 return 0;
119 }
120
ocpp_connect_to_cs(struct ocpp_info * ctx)121 static int ocpp_connect_to_cs(struct ocpp_info *ctx)
122 {
123 int ret;
124 struct websocket_request config = {0};
125 struct ocpp_upstream_info *ui = &ctx->ui;
126 struct sockaddr addr_buf;
127 struct sockaddr *addr = &addr_buf;
128 int addr_size;
129
130 if (ui->csi.sa_family == AF_INET) {
131 #if defined(CONFIG_NET_IPV4)
132 addr_size = sizeof(struct sockaddr_in);
133 addr->sa_family = ui->csi.sa_family;
134 net_sin(addr)->sin_port = htons(ui->csi.port);
135 zsock_inet_pton(addr->sa_family, ui->csi.cs_ip,
136 &net_sin(addr)->sin_addr);
137 #else
138 return -EAFNOSUPPORT;
139 #endif
140 } else {
141 #if defined(CONFIG_NET_IPV6)
142 addr_size = sizeof(struct sockaddr_in6);
143 addr->sa_family = ui->csi.sa_family;
144 net_sin6(addr)->sin6_port = htons(ui->csi.port);
145 zsock_inet_pton(addr->sa_family, ui->csi.cs_ip,
146 &net_sin6(addr)->sin6_addr);
147 #else
148 return -EAFNOSUPPORT;
149 #endif
150 }
151
152 if (ui->tcpsock >= 0) {
153 zsock_close(ui->tcpsock);
154 }
155
156 ui->tcpsock = zsock_socket(ui->csi.sa_family, SOCK_STREAM,
157 IPPROTO_TCP);
158 if (ui->tcpsock < 0) {
159 return -errno;
160 }
161
162 ret = zsock_connect(ui->tcpsock, addr, addr_size);
163 if (ret < 0 && errno != EALREADY && errno != EISCONN) {
164 LOG_ERR("tcp socket connect fail %d %d", ret, errno);
165 return ret;
166 }
167
168 if (ui->wssock >= 0) {
169 websocket_disconnect(ui->wssock);
170 ui->wssock = -1;
171 }
172
173 if (ui->wssock < 0) {
174 char buf[128];
175 char const *optional_hdr[] = {
176 "Sec-WebSocket-Protocol: ocpp1.6\r\n",
177 NULL };
178
179 snprintk(buf, sizeof(buf), "%s:%u", ui->csi.cs_ip, ui->csi.port);
180 config.url = ui->csi.ws_url;
181 config.host = buf;
182 config.tmp_buf = ui->wsrecv_buf;
183 config.tmp_buf_len = sizeof(ui->wsrecv_buf);
184 config.cb = ocpp_ws_connect_cb;
185 config.optional_headers = optional_hdr;
186
187 ret = websocket_connect(ui->tcpsock, &config,
188 OCPP_WS_TIMEOUT, ctx);
189 if (ret < 0) {
190 LOG_ERR("Websocket connect fail %d", ret);
191 return ret;
192 }
193 ui->wssock = ret;
194 }
195
196 LOG_DBG("WS connect success %d", ui->wssock);
197 return 0;
198 }
199
bootnotification_free_resource(struct ocpp_cp_info * cpi)200 static inline void bootnotification_free_resource(struct ocpp_cp_info *cpi)
201 {
202 free(cpi->model);
203 free(cpi->vendor);
204 free(cpi->sl_no);
205 free(cpi->box_sl_no);
206 free(cpi->fw_ver);
207 free(cpi->iccid);
208 free(cpi->imsi);
209 free(cpi->meter_sl_no);
210 free(cpi->meter_type);
211 k_free(cpi);
212 }
213
ocpp_internal_handler(void * p1,void * p2,void * p3)214 static void ocpp_internal_handler(void *p1, void *p2, void *p3)
215 {
216 struct ocpp_info *ctx = p1;
217 struct ocpp_cp_info *cpi = p2;
218 ocpp_session_handle_t sh;
219 struct internal_msg msg = {0};
220 int ret;
221 int i;
222
223 /* open internal session */
224 ocpp_session_open(&sh);
225
226 while (!k_msgq_get(ctx->msgq, &msg, K_FOREVER)) {
227 switch (msg.msgtype) {
228 case PDU_BOOTNOTIFICATION:
229
230 if (!ctx->is_cs_offline && cpi) {
231 ret = ocpp_boot_notification(sh, cpi);
232 if (ret == 0) {
233 bootnotification_free_resource(cpi);
234 cpi = NULL;
235 ctx->state = CP_STATE_READY;
236 }
237 }
238
239 k_timer_start(&ctx->hb_timer,
240 K_SECONDS(ctx->hb_sec),
241 K_NO_WAIT);
242 break;
243
244 case PDU_METER_VALUES:
245 {
246 union ocpp_io_value io;
247 sys_snode_t *node;
248 struct ocpp_session *lsh;
249
250 /* list of all active session */
251 k_mutex_lock(&ctx->ilock, K_FOREVER);
252 SYS_SLIST_FOR_EACH_NODE(&ctx->slist, node) {
253 lsh = to_session(node);
254 if (lsh == sh ||
255 !lsh->is_active) {
256 continue;
257 }
258
259 k_mutex_lock(&lsh->slock, K_FOREVER);
260 k_mutex_unlock(&ctx->ilock);
261 io.meter_val.id_con = lsh->idcon;
262 for (i = 0; i < OCPP_OMM_END; i++) {
263 io.meter_val.mes = i;
264 ret = ctx->cb(OCPP_USR_GET_METER_VALUE,
265 &io, ctx->user_data);
266 if (ret < 0) {
267 continue;
268 }
269 ocpp_meter_values(lsh, i,
270 io.meter_val.val);
271 }
272 k_mutex_lock(&ctx->ilock, K_FOREVER);
273 k_mutex_unlock(&lsh->slock);
274 }
275 k_mutex_unlock(&ctx->ilock);
276 break;
277 }
278 case PDU_HEARTBEAT:
279 ocpp_heartbeat(sh);
280 /* adjust local time with cs time ! */
281 k_timer_start(&ctx->hb_timer,
282 K_SECONDS(ctx->hb_sec),
283 K_NO_WAIT);
284 break;
285
286 case PDU_CS_ONLINE:
287 /* check offline msg and do nothing on empty
288 * else read msg and send to server
289 */
290 break;
291
292 case PDU_REMOTE_START_TRANSACTION:
293 ctx->cb(OCPP_USR_START_CHARGING, &msg.usr, ctx->user_data);
294 break;
295
296 case PDU_REMOTE_STOP_TRANSACTION:
297 ctx->cb(OCPP_USR_STOP_CHARGING, &msg.usr, ctx->user_data);
298 break;
299
300 case PDU_UNLOCK_CONNECTOR:
301 ctx->cb(OCPP_USR_UNLOCK_CONNECTOR, &msg.usr, ctx->user_data);
302 break;
303
304 default:
305 break;
306 }
307 }
308 }
309
ocpp_process_server_msg(struct ocpp_info * ctx)310 static int ocpp_process_server_msg(struct ocpp_info *ctx)
311 {
312 ocpp_msg_fp_t fn;
313 struct ocpp_session *sh = NULL;
314 struct ocpp_upstream_info *ui = &ctx->ui;
315 char *buf, *tmp;
316 char uid[128];
317 int ret, idtxn, pdu, i;
318 bool is_rsp;
319 struct internal_msg msg;
320
321 ret = parse_rpc_msg(ui->recv_buf, sizeof(ui->recv_buf),
322 uid, sizeof(uid), &pdu, &is_rsp);
323 if (ret < 0) {
324 return ret;
325 }
326
327 if (is_rsp) {
328 buf = strtok_r(uid, "-", &tmp);
329 sh = (struct ocpp_session *)(uintptr_t)atoi(buf);
330
331 buf = strtok_r(NULL, "-", &tmp);
332 pdu = atoi(buf);
333
334 if (!ocpp_session_is_valid(sh)) {
335 sh = NULL;
336 }
337 }
338
339 if (pdu >= PDU_MSG_END) {
340 return -EINVAL;
341 }
342
343 fn = ctx->pfn[pdu];
344
345 switch (pdu) {
346 char skey[CISTR50];
347
348 case PDU_BOOTNOTIFICATION:
349 {
350 struct boot_notif binfo;
351
352 ret = fn(ui->recv_buf, &binfo);
353 if (ret == 0 && sh != NULL) {
354 sh->resp_status = binfo.status;
355 ctx->hb_sec = binfo.interval;
356 }
357 break;
358 }
359 case PDU_AUTHORIZE:
360 case PDU_STOP_TRANSACTION:
361 {
362 struct ocpp_idtag_info idinfo = {0};
363
364 if (sh == NULL) {
365 break;
366 }
367
368 ret = fn(ui->recv_buf, &idinfo);
369 if (ret == 0) {
370 sh->resp_status = idinfo.auth_status;
371 }
372 break;
373 }
374 case PDU_START_TRANSACTION:
375 {
376 struct ocpp_idtag_info idinfo = {0};
377 if (sh == NULL) {
378 break;
379 }
380
381 ret = fn(ui->recv_buf, &idtxn, &idinfo);
382 if (ret == 0) {
383 sh->idtxn = idtxn;
384 sh->resp_status = idinfo.auth_status;
385 }
386 break;
387 }
388 case PDU_GET_CONFIGURATION:
389 memset(skey, 0, sizeof(skey));
390
391 ret = fn(ui->recv_buf, skey);
392 if (ret != 0) {
393 break;
394 }
395
396 if (*skey != 0) {
397 enum ocpp_key key;
398
399 key = ocpp_key_to_cfg(skey);
400 ocpp_get_configuration(key, ctx, uid);
401 } else {
402 for (i = 0; i < OCPP_CFG_END; i++) {
403 ocpp_get_configuration(i, ctx, uid);
404 }
405 }
406
407 break;
408
409 case PDU_CHANGE_CONFIGURATION:
410 {
411 char sval[CISTR500] = {0};
412
413 memset(skey, 0, sizeof(skey));
414 ret = fn(ui->recv_buf, skey, sval);
415 if (ret < 0) {
416 break;
417 }
418
419 ocpp_change_configuration(skey, ctx, sval, uid);
420 break;
421 }
422 case PDU_HEARTBEAT:
423 /* todo : sync time */
424 break;
425
426 case PDU_REMOTE_START_TRANSACTION:
427 memset(&msg, 0, OCPP_INTERNAL_MSG_SIZE);
428 msg.msgtype = PDU_REMOTE_START_TRANSACTION;
429
430 ret = fn(ui->recv_buf, &msg.usr.start_charge.id_con,
431 msg.usr.start_charge.idtag);
432 if (ret < 0) {
433 break;
434 }
435
436 ocpp_remote_start_transaction(ctx, &msg, uid);
437 break;
438
439 case PDU_REMOTE_STOP_TRANSACTION:
440 memset(&msg, 0, OCPP_INTERNAL_MSG_SIZE);
441 msg.msgtype = PDU_REMOTE_STOP_TRANSACTION;
442
443 ret = fn(ui->recv_buf, &idtxn);
444 if (ret < 0) {
445 break;
446 }
447
448 ocpp_remote_stop_transaction(ctx, &msg, idtxn, uid);
449 break;
450
451 case PDU_UNLOCK_CONNECTOR:
452 memset(&msg, 0, OCPP_INTERNAL_MSG_SIZE);
453 msg.msgtype = PDU_UNLOCK_CONNECTOR;
454
455 ret = fn(ui->recv_buf, &msg.usr.unlock_con.id_con);
456 if (ret < 0) {
457 break;
458 }
459
460 ocpp_unlock_connector(ctx, &msg, uid);
461 break;
462
463 case PDU_METER_VALUES:
464 default:
465 break;
466 }
467
468 if (is_rsp) {
469 k_poll_signal_raise(&ui->ws_rspsig, 0);
470 }
471 return 0;
472 }
473
474 #define TCP_CONNECT_AFTER 20 /* fixme arbitrary */
ocpp_wsreader(void * p1,void * p2,void * p3)475 static void ocpp_wsreader(void *p1, void *p2, void *p3)
476 {
477 struct ocpp_info *ctx = p1;
478 struct ocpp_upstream_info *ui = &ctx->ui;
479 struct zsock_pollfd tcpfd;
480 int ret;
481 uint8_t retry_cnt = 0;
482
483 ctx->is_cs_offline = true;
484 tcpfd.events = ZSOCK_POLLIN;
485
486 while (1) {
487
488 if (ctx->is_cs_offline) {
489 if ((retry_cnt++ % TCP_CONNECT_AFTER) == 0) {
490 k_mutex_lock(&ctx->ilock, K_FOREVER);
491 ret = ocpp_connect_to_cs(ctx);
492 k_mutex_unlock(&ctx->ilock);
493
494 if (ret != 0) {
495 continue;
496 }
497 } else {
498 /* delay reconnection attempt */
499 k_msleep(200);
500 continue;
501 }
502 }
503
504 tcpfd.fd = ui->tcpsock;
505 ret = zsock_poll(&tcpfd, 1, 200);
506 if (ret <= 0) {
507 continue;
508 }
509
510 if ((tcpfd.revents & ZSOCK_POLLERR) ||
511 (tcpfd.revents & ZSOCK_POLLNVAL)) {
512 LOG_ERR("poll err %d", tcpfd.revents);
513 ctx->is_cs_offline = true;
514 continue;
515 }
516
517 if (tcpfd.revents & ZSOCK_POLLIN) {
518 struct ocpp_wamp_rpc_msg rcv = {0};
519 uint32_t msg_type;
520
521 rcv.ctx = ctx;
522 rcv.msg = ui->recv_buf;
523 rcv.sndlock = &ui->ws_sndlock;
524 rcv.msg_len = sizeof(ui->recv_buf);
525
526 memset(ui->recv_buf, 0, rcv.msg_len);
527 ret = ocpp_receive_from_server(&rcv, &msg_type, 200);
528
529 if (ret < 0) {
530 if (ret == -ENOTCONN) {
531 ctx->is_cs_offline = true;
532 }
533 continue;
534 }
535
536 if (msg_type & WEBSOCKET_FLAG_PING) {
537 websocket_send_msg(ctx->ui.wssock, NULL, 0,
538 WEBSOCKET_OPCODE_PONG, true,
539 true, 100);
540 } else if (msg_type & WEBSOCKET_FLAG_CLOSE) {
541 ctx->is_cs_offline = true;
542 } else {
543 ocpp_process_server_msg(ctx);
544 }
545
546 }
547
548 if (tcpfd.revents & ZSOCK_POLLHUP) {
549 LOG_ERR("poll err %d", tcpfd.revents);
550 ctx->is_cs_offline = true;
551 }
552 }
553 }
554
ocpp_upstream_init(struct ocpp_info * ctx,struct ocpp_cs_info * csi)555 int ocpp_upstream_init(struct ocpp_info *ctx, struct ocpp_cs_info *csi)
556 {
557 struct ocpp_upstream_info *ui = &ctx->ui;
558
559 LOG_INF("upstream init");
560
561 ui->csi.ws_url = strdup(csi->ws_url);
562 ui->csi.cs_ip = strdup(csi->cs_ip);
563 ui->csi.port = csi->port;
564 ui->csi.sa_family = csi->sa_family;
565 ui->tcpsock = -1;
566
567 k_mutex_init(&ui->ws_sndlock);
568 k_poll_signal_init(&ui->ws_rspsig);
569 ui->wssock = -1;
570
571 websocket_init();
572
573 k_thread_create(&ui->tinfo, ocpp_wsreader_stack,
574 CONFIG_OCPP_WSREADER_THREAD_STACKSIZE,
575 ocpp_wsreader, ctx, NULL, NULL,
576 OCPP_UPSTREAM_PRIORITY, 0, K_MSEC(100));
577
578 return 0;
579 }
580
timer_heartbeat_cb(struct k_timer * t)581 static void timer_heartbeat_cb(struct k_timer *t)
582 {
583 struct ocpp_info *ctx;
584 struct internal_msg msg = {PDU_HEARTBEAT};
585
586 ctx = k_timer_user_data_get(t);
587 if (ctx->state == CP_STATE_BOOTNOTIF) {
588 msg.msgtype = PDU_BOOTNOTIFICATION;
589 }
590
591 k_msgq_put(ctx->msgq, &msg, K_NO_WAIT);
592 }
593
timer_meter_cb(struct k_timer * t)594 static void timer_meter_cb(struct k_timer *t)
595 {
596 struct ocpp_info *ctx;
597 struct internal_msg msg = {PDU_METER_VALUES};
598
599 ctx = k_timer_user_data_get(t);
600
601 k_msgq_put(ctx->msgq, &msg, K_NO_WAIT);
602 }
603
bootnotification_fill_resource(struct ocpp_cp_info * cp,struct ocpp_cp_info * cpi,struct ocpp_info * ctx)604 static inline void bootnotification_fill_resource(struct ocpp_cp_info *cp,
605 struct ocpp_cp_info *cpi,
606 struct ocpp_info *ctx)
607 {
608 struct k_timer *hbt = &ctx->hb_timer;
609
610 cp->model = strdup(cpi->model);
611 cp->vendor = strdup(cpi->vendor);
612 if (cp->sl_no != NULL) {
613 cp->sl_no = strdup(cpi->sl_no);
614 }
615
616 if (cp->box_sl_no != NULL) {
617 cp->box_sl_no = strdup(cpi->box_sl_no);
618 }
619
620 if (cp->fw_ver != NULL) {
621 cp->fw_ver = strdup(cpi->fw_ver);
622 }
623
624 if (cp->iccid != NULL) {
625 cp->iccid = strdup(cpi->iccid);
626 }
627
628 if (cp->imsi != NULL) {
629 cp->imsi = strdup(cpi->imsi);
630 }
631
632 if (cp->meter_sl_no != NULL) {
633 cp->meter_sl_no = strdup(cpi->meter_sl_no);
634 }
635
636 if (cp->meter_type != NULL) {
637 cp->meter_type = strdup(cpi->meter_type);
638 }
639
640 ctx->state = CP_STATE_BOOTNOTIF;
641 ctx->hb_sec = 10;
642 k_timer_start(hbt, K_SECONDS(1), K_NO_WAIT);
643 }
644
ocpp_session_open(ocpp_session_handle_t * hndl)645 int ocpp_session_open(ocpp_session_handle_t *hndl)
646 {
647 struct ocpp_session *sh;
648
649 if (hndl == NULL) {
650 return -EINVAL;
651 }
652
653 sh = (struct ocpp_session *)k_calloc(1, sizeof(struct ocpp_session));
654 if (sh == NULL) {
655 return -ENOMEM;
656 }
657
658 sh->is_active = false;
659 sh->idcon = INVALID_CONN_ID;
660 sh->idtxn = INVALID_TXN_ID;
661 sh->ctx = gctx;
662 k_mutex_init(&sh->slock);
663
664 k_mutex_lock(&gctx->ilock, K_FOREVER);
665 sys_slist_append(&gctx->slist, &sh->node);
666 k_mutex_unlock(&gctx->ilock);
667
668 *hndl = (void *)sh;
669
670 return 0;
671 }
672
ocpp_session_close(ocpp_session_handle_t hndl)673 void ocpp_session_close(ocpp_session_handle_t hndl)
674 {
675 bool is_removed;
676 struct ocpp_session *sh = (struct ocpp_session *)hndl;
677
678 if (sh == NULL) {
679 return;
680 }
681
682 k_mutex_lock(&gctx->ilock, K_FOREVER);
683 is_removed = sys_slist_find_and_remove(&gctx->slist, &sh->node);
684 k_mutex_unlock(&gctx->ilock);
685
686 if (is_removed) {
687 k_mutex_lock(&sh->slock, K_FOREVER);
688 k_free(sh);
689 }
690 }
691
ocpp_init(struct ocpp_cp_info * cpi,struct ocpp_cs_info * csi,ocpp_user_notify_callback_t cb,void * user_data)692 int ocpp_init(struct ocpp_cp_info *cpi,
693 struct ocpp_cs_info *csi,
694 ocpp_user_notify_callback_t cb,
695 void *user_data)
696 {
697 struct ocpp_info *ctx;
698 struct ocpp_cp_info *cp;
699 union ocpp_keyval val;
700 int ret;
701
702 if (cpi == NULL ||
703 cpi->model == NULL ||
704 cpi->vendor == NULL ||
705 csi == NULL ||
706 csi->cs_ip == NULL ||
707 csi->ws_url == NULL ||
708 cb == NULL ||
709 cpi->num_of_con < 1) {
710 return -EINVAL;
711 }
712
713 ctx = (struct ocpp_info *)k_calloc(1, sizeof(struct ocpp_info));
714 if (ctx == NULL) {
715 return -ENOMEM;
716 }
717
718 gctx = ctx;
719 k_mutex_init(&ctx->ilock);
720 sys_slist_init(&ctx->slist);
721 ocpp_parser_init(&ctx->cfn, &ctx->pfn);
722
723 ctx->state = CP_STATE_INIT;
724 ctx->msgq = &ocpp_iq;
725
726 k_timer_init(&ctx->hb_timer, timer_heartbeat_cb, NULL);
727 k_timer_user_data_set(&ctx->hb_timer, ctx);
728 k_timer_init(&ctx->mtr_timer, timer_meter_cb, NULL);
729 k_timer_user_data_set(&ctx->mtr_timer, ctx);
730 atomic_set(&ctx->mtr_timer_ref_cnt, 0);
731
732 ctx->user_data = user_data;
733 ctx->cb = cb;
734 val.ival = cpi->num_of_con;
735 ocpp_set_cfg_val(CFG_NO_OF_CONNECTORS, &val);
736
737 ret = ocpp_upstream_init(ctx, csi);
738 if (ret < 0) {
739 LOG_ERR("ocpp upstream init fail %d", ret);
740 goto out;
741 }
742
743 /* free after success of bootnotification to CS */
744 cp = (struct ocpp_cp_info *)k_calloc(1, sizeof(struct ocpp_cp_info));
745 if (cp == NULL) {
746 ret = -ENOMEM;
747 goto out;
748 }
749
750 bootnotification_fill_resource(cp, cpi, ctx);
751
752 k_thread_create(&ctx->tinfo, ocpp_int_handler_stack,
753 CONFIG_OCPP_INT_THREAD_STACKSIZE,
754 ocpp_internal_handler, ctx,
755 cp, NULL, OCPP_UPSTREAM_PRIORITY,
756 0, K_NO_WAIT);
757
758 LOG_INF("ocpp init success");
759 return 0;
760
761 out:
762 k_free(ctx);
763 return ret;
764 }
765