1 /** @file
2  * @brief Modem command handler for modem context driver
3  *
4  * Text-based command handler implementation for modem context driver.
5  */
6 
7 /*
8  * Copyright (c) 2019-2020 Foundries.io
9  *
10  * SPDX-License-Identifier: Apache-2.0
11  */
12 
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(modem_cmd_handler, CONFIG_MODEM_LOG_LEVEL);
15 
16 #include <zephyr/kernel.h>
17 #include <stddef.h>
18 #include <zephyr/net/buf.h>
19 
20 #include "modem_context.h"
21 #include "modem_cmd_handler.h"
22 
23 
24 /*
25  * Parsing Functions
26  */
27 
is_crlf(uint8_t c)28 static bool is_crlf(uint8_t c)
29 {
30 	if (c == '\n' || c == '\r') {
31 		return true;
32 	} else {
33 		return false;
34 	}
35 }
36 
skipcrlf(struct modem_cmd_handler_data * data)37 static void skipcrlf(struct modem_cmd_handler_data *data)
38 {
39 	while (data->rx_buf && data->rx_buf->len &&
40 			is_crlf(*data->rx_buf->data)) {
41 		net_buf_pull_u8(data->rx_buf);
42 		if (!data->rx_buf->len) {
43 			data->rx_buf = net_buf_frag_del(NULL, data->rx_buf);
44 		}
45 	}
46 }
47 
findcrlf(struct modem_cmd_handler_data * data,struct net_buf ** frag,uint16_t * offset)48 static uint16_t findcrlf(struct modem_cmd_handler_data *data,
49 		      struct net_buf **frag, uint16_t *offset)
50 {
51 	struct net_buf *buf = data->rx_buf;
52 	uint16_t len = 0U, pos = 0U;
53 
54 	while (buf && buf->len && !is_crlf(*(buf->data + pos))) {
55 		if (pos + 1 >= buf->len) {
56 			len += buf->len;
57 			buf = buf->frags;
58 			pos = 0U;
59 		} else {
60 			pos++;
61 		}
62 	}
63 
64 	if (buf && buf->len && is_crlf(*(buf->data + pos))) {
65 		len += pos;
66 		*offset = pos;
67 		*frag = buf;
68 		return len;
69 	}
70 
71 	return 0;
72 }
73 
starts_with(struct net_buf * buf,const char * str)74 static bool starts_with(struct net_buf *buf, const char *str)
75 {
76 	int pos = 0;
77 
78 	while (buf && buf->len && *str) {
79 		if (*(buf->data + pos) == *str) {
80 			str++;
81 			pos++;
82 			if (pos >= buf->len) {
83 				buf = buf->frags;
84 				pos = 0;
85 			}
86 		} else {
87 			return false;
88 		}
89 	}
90 
91 	if (*str == 0) {
92 		return true;
93 	}
94 
95 	return false;
96 }
97 
98 /*
99  * Cmd Handler Functions
100  */
101 
read_rx_allocator(k_timeout_t timeout,void * user_data)102 static inline struct net_buf *read_rx_allocator(k_timeout_t timeout,
103 						void *user_data)
104 {
105 	return net_buf_alloc((struct net_buf_pool *)user_data, timeout);
106 }
107 
108 /* return scanned length for params */
parse_params(struct modem_cmd_handler_data * data,size_t match_len,const struct modem_cmd * cmd,uint8_t ** argv,size_t argv_len,uint16_t * argc)109 static int parse_params(struct modem_cmd_handler_data *data,  size_t match_len,
110 			const struct modem_cmd *cmd,
111 			uint8_t **argv, size_t argv_len, uint16_t *argc)
112 {
113 	int count = 0;
114 	size_t delim_len, begin, end, i;
115 	bool quoted = false;
116 
117 	if (!data || !data->match_buf || !match_len || !cmd || !argv || !argc) {
118 		return -EINVAL;
119 	}
120 
121 	begin = cmd->cmd_len;
122 	end = cmd->cmd_len;
123 	delim_len = strlen(cmd->delim);
124 	while (end < match_len) {
125 		/* Don't look for delimiters in the middle of a quoted parameter */
126 		if (data->match_buf[end] == '"') {
127 			quoted = !quoted;
128 		}
129 		if (quoted) {
130 			end++;
131 			continue;
132 		}
133 		/* Look for delimiter characters */
134 		for (i = 0; i < delim_len; i++) {
135 			if (data->match_buf[end] == cmd->delim[i]) {
136 				/* mark a parameter beginning */
137 				argv[*argc] = &data->match_buf[begin];
138 				/* end parameter with NUL char */
139 				data->match_buf[end] = '\0';
140 				/* bump begin */
141 				begin = end + 1;
142 				count += 1;
143 				(*argc)++;
144 				break;
145 			}
146 		}
147 
148 		if (count >= cmd->arg_count_max) {
149 			break;
150 		}
151 
152 		if (*argc == argv_len) {
153 			break;
154 		}
155 
156 		end++;
157 	}
158 
159 	/* consider the ending portion a param if end > begin */
160 	if (end > begin) {
161 		/* mark a parameter beginning */
162 		argv[*argc] = &data->match_buf[begin];
163 		/* end parameter with NUL char
164 		 * NOTE: if this is at the end of match_len will probably
165 		 * be overwriting a NUL that's already there
166 		 */
167 		data->match_buf[end] = '\0';
168 		(*argc)++;
169 	}
170 
171 	/* missing arguments */
172 	if (*argc < cmd->arg_count_min) {
173 		/* Do not return -EAGAIN here as there is no way new argument
174 		 * can be parsed later because match_len is computed to be
175 		 * the minimum of the distance to the first CRLF and the size
176 		 * of the buffer.
177 		 * Therefore, waiting more data on the interface won't change
178 		 * match_len value, which mean there is no point in waiting
179 		 * for more arguments, this will just end in a infinite loop
180 		 * parsing data and finding that some arguments are missing.
181 		 */
182 		return -EINVAL;
183 	}
184 
185 	/*
186 	 * return the beginning of the next unfinished param so we don't
187 	 * "skip" any data that could be parsed later.
188 	 */
189 	return begin - cmd->cmd_len;
190 }
191 
192 /* process a "matched" command */
process_cmd(const struct modem_cmd * cmd,size_t match_len,struct modem_cmd_handler_data * data)193 static int process_cmd(const struct modem_cmd *cmd, size_t match_len,
194 			struct modem_cmd_handler_data *data)
195 {
196 	int parsed_len = 0, ret = 0;
197 	uint8_t *argv[CONFIG_MODEM_CMD_HANDLER_MAX_PARAM_COUNT];
198 	uint16_t argc = 0U;
199 
200 	/* reset params */
201 	memset(argv, 0, sizeof(argv[0]) * ARRAY_SIZE(argv));
202 
203 	/* do we need to parse arguments? */
204 	if (cmd->arg_count_max > 0U) {
205 		/* returns < 0 on error and > 0 for parsed len */
206 		parsed_len = parse_params(data, match_len, cmd,
207 					  argv, ARRAY_SIZE(argv), &argc);
208 		if (parsed_len < 0) {
209 			return parsed_len;
210 		}
211 	}
212 
213 	/* skip cmd_len + parsed len */
214 	data->rx_buf = net_buf_skip(data->rx_buf, cmd->cmd_len + parsed_len);
215 
216 	/* call handler */
217 	if (cmd->func) {
218 		ret = cmd->func(data, match_len - cmd->cmd_len - parsed_len,
219 				argv, argc);
220 		if (ret == -EAGAIN) {
221 			/* wait for more data */
222 			net_buf_push(data->rx_buf, cmd->cmd_len + parsed_len);
223 		}
224 	}
225 
226 	return ret;
227 }
228 
229 /*
230  * check 3 arrays of commands for a match in match_buf:
231  * - response handlers[0]
232  * - unsolicited handlers[1]
233  * - current assigned handlers[2]
234  */
find_cmd_match(struct modem_cmd_handler_data * data)235 static const struct modem_cmd *find_cmd_match(
236 		struct modem_cmd_handler_data *data)
237 {
238 	int j;
239 	size_t i;
240 
241 	for (j = 0; j < ARRAY_SIZE(data->cmds); j++) {
242 		if (!data->cmds[j] || data->cmds_len[j] == 0U) {
243 			continue;
244 		}
245 
246 		for (i = 0; i < data->cmds_len[j]; i++) {
247 			/* match on "empty" cmd */
248 			if (strlen(data->cmds[j][i].cmd) == 0 ||
249 			    strncmp(data->match_buf, data->cmds[j][i].cmd,
250 				    data->cmds[j][i].cmd_len) == 0) {
251 				return &data->cmds[j][i];
252 			}
253 		}
254 	}
255 
256 	return NULL;
257 }
258 
find_cmd_direct_match(struct modem_cmd_handler_data * data)259 static const struct modem_cmd *find_cmd_direct_match(
260 		struct modem_cmd_handler_data *data)
261 {
262 	size_t j, i;
263 
264 	for (j = 0; j < ARRAY_SIZE(data->cmds); j++) {
265 		if (!data->cmds[j] || data->cmds_len[j] == 0U) {
266 			continue;
267 		}
268 
269 		for (i = 0; i < data->cmds_len[j]; i++) {
270 			/* match start of cmd */
271 			if (data->cmds[j][i].direct &&
272 			    (data->cmds[j][i].cmd[0] == '\0' ||
273 			     starts_with(data->rx_buf, data->cmds[j][i].cmd))) {
274 				return &data->cmds[j][i];
275 			}
276 		}
277 	}
278 
279 	return NULL;
280 }
281 
cmd_handler_process_iface_data(struct modem_cmd_handler_data * data,struct modem_iface * iface)282 static int cmd_handler_process_iface_data(struct modem_cmd_handler_data *data,
283 					  struct modem_iface *iface)
284 {
285 	struct net_buf *last;
286 	size_t bytes_read = 0;
287 	int ret;
288 
289 	if (!data->rx_buf) {
290 		data->rx_buf = net_buf_alloc(data->buf_pool,
291 					     data->alloc_timeout);
292 		if (!data->rx_buf) {
293 			/* there is potentially more data waiting */
294 			return -ENOMEM;
295 		}
296 	}
297 
298 	last = net_buf_frag_last(data->rx_buf);
299 
300 	/* read all of the data from modem iface */
301 	while (true) {
302 		struct net_buf *frag = last;
303 		size_t frag_room = net_buf_tailroom(frag);
304 
305 		if (!frag_room) {
306 			frag = net_buf_alloc(data->buf_pool,
307 					    data->alloc_timeout);
308 			if (!frag) {
309 				/* there is potentially more data waiting */
310 				return -ENOMEM;
311 			}
312 
313 			net_buf_frag_insert(last, frag);
314 			last = frag;
315 
316 			frag_room = net_buf_tailroom(frag);
317 		}
318 
319 		ret = iface->read(iface, net_buf_tail(frag), frag_room,
320 				  &bytes_read);
321 		if (ret < 0 || bytes_read == 0) {
322 			/* modem context buffer is empty */
323 			return 0;
324 		}
325 
326 		net_buf_add(frag, bytes_read);
327 	}
328 }
329 
cmd_handler_process_rx_buf(struct modem_cmd_handler_data * data)330 static void cmd_handler_process_rx_buf(struct modem_cmd_handler_data *data)
331 {
332 	const struct modem_cmd *cmd;
333 	struct net_buf *frag = NULL;
334 	size_t match_len;
335 	int ret;
336 	uint16_t offset, len;
337 
338 	/* process all of the data in the net_buf */
339 	while (data->rx_buf && data->rx_buf->len) {
340 		skipcrlf(data);
341 		if (!data->rx_buf || !data->rx_buf->len) {
342 			break;
343 		}
344 
345 		cmd = find_cmd_direct_match(data);
346 		if (cmd && cmd->func) {
347 			ret = cmd->func(data, cmd->cmd_len, NULL, 0);
348 			if (ret == -EAGAIN) {
349 				/* Wait for more data */
350 				break;
351 			} else if (ret > 0) {
352 				LOG_DBG("match direct cmd [%s] (ret:%d)",
353 					cmd->cmd, ret);
354 				data->rx_buf = net_buf_skip(data->rx_buf, ret);
355 			}
356 
357 			continue;
358 		}
359 
360 		frag = NULL;
361 		/* locate next CR/LF */
362 		len = findcrlf(data, &frag, &offset);
363 		if (!frag) {
364 			/*
365 			 * No CR/LF found.  Let's exit and leave any data
366 			 * for next time
367 			 */
368 			break;
369 		}
370 
371 		/* load match_buf with content up to the next CR/LF */
372 		/* NOTE: keep room in match_buf for ending NUL char */
373 		match_len = net_buf_linearize(data->match_buf,
374 					      data->match_buf_len - 1,
375 					      data->rx_buf, 0, len);
376 		if ((data->match_buf_len - 1) < match_len) {
377 			LOG_ERR("Match buffer size (%zu) is too small for "
378 				"incoming command size: %zu!  Truncating!",
379 				data->match_buf_len - 1, match_len);
380 		}
381 
382 #if defined(CONFIG_MODEM_CONTEXT_VERBOSE_DEBUG)
383 		LOG_HEXDUMP_DBG(data->match_buf, match_len, "RECV");
384 #endif
385 
386 		k_sem_take(&data->sem_parse_lock, K_FOREVER);
387 
388 		cmd = find_cmd_match(data);
389 		if (cmd) {
390 			LOG_DBG("match cmd [%s] (len:%zu)",
391 				cmd->cmd, match_len);
392 
393 			ret = process_cmd(cmd, match_len, data);
394 			if (ret == -EAGAIN) {
395 				k_sem_give(&data->sem_parse_lock);
396 				break;
397 			} else if (ret < 0) {
398 				LOG_ERR("process cmd [%s] (len:%zu, ret:%d)",
399 					cmd->cmd, match_len, ret);
400 			}
401 
402 			/*
403 			 * make sure we didn't run out of data during
404 			 * command processing
405 			 */
406 			if (!data->rx_buf) {
407 				/* we're out of data, exit early */
408 				k_sem_give(&data->sem_parse_lock);
409 				break;
410 			}
411 
412 			frag = NULL;
413 			/*
414 			 * We've handled the current line.
415 			 * Let's skip any "extra" data in that
416 			 * line, and look for the next CR/LF.
417 			 * This leaves us ready for the next
418 			 * handler search.
419 			 * Ignore the length returned.
420 			 */
421 			(void)findcrlf(data, &frag, &offset);
422 		}
423 
424 		k_sem_give(&data->sem_parse_lock);
425 
426 		if (frag && data->rx_buf) {
427 			/* clear out processed line (net_buf's) */
428 			while (frag && data->rx_buf != frag) {
429 				data->rx_buf = net_buf_frag_del(NULL,
430 								data->rx_buf);
431 			}
432 
433 			net_buf_pull(data->rx_buf, offset);
434 		}
435 	}
436 }
437 
cmd_handler_process(struct modem_cmd_handler * cmd_handler,struct modem_iface * iface)438 static void cmd_handler_process(struct modem_cmd_handler *cmd_handler,
439 				struct modem_iface *iface)
440 {
441 	struct modem_cmd_handler_data *data;
442 	int err;
443 
444 	if (!cmd_handler || !cmd_handler->cmd_handler_data ||
445 	    !iface || !iface->read) {
446 		return;
447 	}
448 
449 	data = (struct modem_cmd_handler_data *)(cmd_handler->cmd_handler_data);
450 
451 	do {
452 		err = cmd_handler_process_iface_data(data, iface);
453 		cmd_handler_process_rx_buf(data);
454 	} while (err);
455 }
456 
modem_cmd_handler_get_error(struct modem_cmd_handler_data * data)457 int modem_cmd_handler_get_error(struct modem_cmd_handler_data *data)
458 {
459 	if (!data) {
460 		return -EINVAL;
461 	}
462 
463 	return data->last_error;
464 }
465 
modem_cmd_handler_set_error(struct modem_cmd_handler_data * data,int error_code)466 int modem_cmd_handler_set_error(struct modem_cmd_handler_data *data,
467 				int error_code)
468 {
469 	if (!data) {
470 		return -EINVAL;
471 	}
472 
473 	data->last_error = error_code;
474 	return 0;
475 }
476 
modem_cmd_handler_update_cmds(struct modem_cmd_handler_data * data,const struct modem_cmd * handler_cmds,size_t handler_cmds_len,bool reset_error_flag)477 int modem_cmd_handler_update_cmds(struct modem_cmd_handler_data *data,
478 				  const struct modem_cmd *handler_cmds,
479 				  size_t handler_cmds_len,
480 				  bool reset_error_flag)
481 {
482 	if (!data) {
483 		return -EINVAL;
484 	}
485 
486 	data->cmds[CMD_HANDLER] = handler_cmds;
487 	data->cmds_len[CMD_HANDLER] = handler_cmds_len;
488 	if (reset_error_flag) {
489 		data->last_error = 0;
490 	}
491 
492 	return 0;
493 }
494 
modem_cmd_send_ext(struct modem_iface * iface,struct modem_cmd_handler * handler,const struct modem_cmd * handler_cmds,size_t handler_cmds_len,const uint8_t * buf,struct k_sem * sem,k_timeout_t timeout,int flags)495 int modem_cmd_send_ext(struct modem_iface *iface,
496 		       struct modem_cmd_handler *handler,
497 		       const struct modem_cmd *handler_cmds,
498 		       size_t handler_cmds_len, const uint8_t *buf,
499 		       struct k_sem *sem, k_timeout_t timeout, int flags)
500 {
501 	struct modem_cmd_handler_data *data;
502 	int ret = 0;
503 
504 	if (!iface || !handler || !handler->cmd_handler_data || !buf) {
505 		return -EINVAL;
506 	}
507 
508 	if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
509 		/* semaphore is not needed if there is no timeout */
510 		sem = NULL;
511 	} else if (!sem) {
512 		/* cannot respect timeout without semaphore */
513 		return -EINVAL;
514 	}
515 
516 	data = (struct modem_cmd_handler_data *)(handler->cmd_handler_data);
517 	if (!(flags & MODEM_NO_TX_LOCK)) {
518 		k_sem_take(&data->sem_tx_lock, K_FOREVER);
519 	}
520 
521 	if (!(flags & MODEM_NO_SET_CMDS)) {
522 		ret = modem_cmd_handler_update_cmds(data, handler_cmds,
523 						    handler_cmds_len, true);
524 		if (ret < 0) {
525 			goto unlock_tx_lock;
526 		}
527 	}
528 
529 #if defined(CONFIG_MODEM_CONTEXT_VERBOSE_DEBUG)
530 	LOG_HEXDUMP_DBG(buf, strlen(buf), "SENT DATA");
531 
532 	if (data->eol_len > 0) {
533 		if (data->eol[0] != '\r') {
534 			/* Print the EOL only if it is not \r, otherwise there
535 			 * is just too much printing.
536 			 */
537 			LOG_HEXDUMP_DBG(data->eol, data->eol_len, "SENT EOL");
538 		}
539 	} else {
540 		LOG_DBG("EOL not set!!!");
541 	}
542 #endif
543 	if (sem) {
544 		k_sem_reset(sem);
545 	}
546 
547 	iface->write(iface, buf, strlen(buf));
548 	iface->write(iface, data->eol, data->eol_len);
549 
550 	if (sem) {
551 		ret = k_sem_take(sem, timeout);
552 
553 		if (ret == 0) {
554 			ret = data->last_error;
555 		} else if (ret == -EAGAIN) {
556 			ret = -ETIMEDOUT;
557 		}
558 	}
559 
560 	if (!(flags & MODEM_NO_UNSET_CMDS)) {
561 		/* unset handlers and ignore any errors */
562 		(void)modem_cmd_handler_update_cmds(data, NULL, 0U, false);
563 	}
564 
565 unlock_tx_lock:
566 	if (!(flags & MODEM_NO_TX_LOCK)) {
567 		k_sem_give(&data->sem_tx_lock);
568 	}
569 
570 	return ret;
571 }
572 
573 /* run a set of AT commands */
modem_cmd_handler_setup_cmds(struct modem_iface * iface,struct modem_cmd_handler * handler,const struct setup_cmd * cmds,size_t cmds_len,struct k_sem * sem,k_timeout_t timeout)574 int modem_cmd_handler_setup_cmds(struct modem_iface *iface,
575 				 struct modem_cmd_handler *handler,
576 				 const struct setup_cmd *cmds, size_t cmds_len,
577 				 struct k_sem *sem, k_timeout_t timeout)
578 {
579 	int ret = 0;
580 	size_t i;
581 
582 	for (i = 0; i < cmds_len; i++) {
583 
584 		if (cmds[i].handle_cmd.cmd && cmds[i].handle_cmd.func) {
585 			ret = modem_cmd_send(iface, handler,
586 					     &cmds[i].handle_cmd, 1U,
587 					     cmds[i].send_cmd,
588 					     sem, timeout);
589 		} else {
590 			ret = modem_cmd_send(iface, handler,
591 					     NULL, 0, cmds[i].send_cmd,
592 					     sem, timeout);
593 		}
594 
595 		k_sleep(K_MSEC(50));
596 
597 		if (ret < 0) {
598 			LOG_ERR("command %s ret:%d",
599 				cmds[i].send_cmd, ret);
600 			break;
601 		}
602 	}
603 
604 	return ret;
605 }
606 
607 /* run a set of AT commands, without lock */
modem_cmd_handler_setup_cmds_nolock(struct modem_iface * iface,struct modem_cmd_handler * handler,const struct setup_cmd * cmds,size_t cmds_len,struct k_sem * sem,k_timeout_t timeout)608 int modem_cmd_handler_setup_cmds_nolock(struct modem_iface *iface,
609 					struct modem_cmd_handler *handler,
610 					const struct setup_cmd *cmds,
611 					size_t cmds_len, struct k_sem *sem,
612 					k_timeout_t timeout)
613 {
614 	int ret = 0;
615 	size_t i;
616 
617 	for (i = 0; i < cmds_len; i++) {
618 
619 		if (cmds[i].handle_cmd.cmd && cmds[i].handle_cmd.func) {
620 			ret = modem_cmd_send_nolock(iface, handler,
621 						    &cmds[i].handle_cmd, 1U,
622 						    cmds[i].send_cmd,
623 						    sem, timeout);
624 		} else {
625 			ret = modem_cmd_send_nolock(iface, handler,
626 						    NULL, 0, cmds[i].send_cmd,
627 						    sem, timeout);
628 		}
629 
630 		k_sleep(K_MSEC(50));
631 
632 		if (ret < 0) {
633 			LOG_ERR("command %s ret:%d",
634 				cmds[i].send_cmd, ret);
635 			break;
636 		}
637 	}
638 
639 	return ret;
640 }
641 
modem_cmd_handler_tx_lock(struct modem_cmd_handler * handler,k_timeout_t timeout)642 int modem_cmd_handler_tx_lock(struct modem_cmd_handler *handler,
643 			      k_timeout_t timeout)
644 {
645 	struct modem_cmd_handler_data *data;
646 	data = (struct modem_cmd_handler_data *)(handler->cmd_handler_data);
647 
648 	return k_sem_take(&data->sem_tx_lock, timeout);
649 }
650 
modem_cmd_handler_tx_unlock(struct modem_cmd_handler * handler)651 void modem_cmd_handler_tx_unlock(struct modem_cmd_handler *handler)
652 {
653 	struct modem_cmd_handler_data *data;
654 	data = (struct modem_cmd_handler_data *)(handler->cmd_handler_data);
655 
656 	k_sem_give(&data->sem_tx_lock);
657 }
658 
modem_cmd_handler_init(struct modem_cmd_handler * handler,struct modem_cmd_handler_data * data,const struct modem_cmd_handler_config * config)659 int modem_cmd_handler_init(struct modem_cmd_handler *handler,
660 			   struct modem_cmd_handler_data *data,
661 			   const struct modem_cmd_handler_config *config)
662 {
663 	/* Verify arguments */
664 	if (handler == NULL || data == NULL || config == NULL) {
665 		return -EINVAL;
666 	}
667 
668 	/* Verify config */
669 	if ((config->match_buf == NULL) ||
670 	    (config->match_buf_len == 0) ||
671 	    (config->buf_pool == NULL) ||
672 	    (NULL != config->response_cmds && 0 == config->response_cmds_len) ||
673 	    (NULL != config->unsol_cmds && 0 == config->unsol_cmds_len)) {
674 		return -EINVAL;
675 	}
676 
677 	/* Assign data to command handler */
678 	handler->cmd_handler_data = data;
679 
680 	/* Assign command process implementation to command handler */
681 	handler->process = cmd_handler_process;
682 
683 	/* Store arguments */
684 	data->match_buf = config->match_buf;
685 	data->match_buf_len = config->match_buf_len;
686 	data->buf_pool = config->buf_pool;
687 	data->alloc_timeout = config->alloc_timeout;
688 	data->eol = config->eol;
689 	data->cmds[CMD_RESP] = config->response_cmds;
690 	data->cmds_len[CMD_RESP] = config->response_cmds_len;
691 	data->cmds[CMD_UNSOL] = config->unsol_cmds;
692 	data->cmds_len[CMD_UNSOL] = config->unsol_cmds_len;
693 
694 	/* Process end of line */
695 	data->eol_len = data->eol == NULL ? 0 : strlen(data->eol);
696 
697 	/* Store optional user data */
698 	data->user_data = config->user_data;
699 
700 	/* Initialize command handler data members */
701 	k_sem_init(&data->sem_tx_lock, 1, 1);
702 	k_sem_init(&data->sem_parse_lock, 1, 1);
703 
704 	return 0;
705 }
706