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