xref: /titanic_51/usr/src/cmd/picl/plugins/sun4u/lw2plus/fcal_leds/fc_led_parse.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <libintl.h>
36 #include <syslog.h>
37 #include "fcal_leds.h"
38 
39 /*
40  * function templates for static functions
41  */
42 static token_t get_token(char **pptr, int lineNo, actfun_t *fun);
43 static int get_cstr(str *p_str, cstr *p_cstr_res);
44 static int get_assert(str *p_str, int *assert);
45 static int get_pnz(str *p_str, int *pnz);
46 static int get_mask(str *p_str, int n_disks, int *p_intarray);
47 
48 /*
49  * Templates for functions which may be returned by get_token().
50  * These functions are all called with a pointer to the position just
51  * beyond the token being actioned.
52  */
53 static int act_version(str *p_str, led_dtls_t *dtls);
54 static int act_leds_board(str *p_str, led_dtls_t *dtls);
55 static int act_status_board(str *p_str, led_dtls_t *dtls);
56 static int act_disk_driver(str *p_str, led_dtls_t *dtls);
57 static int act_n_disks(str *p_str, led_dtls_t *dtls);
58 static int act_asrt_pres(str *p_str, led_dtls_t *dtls);
59 static int act_asrt_fault(str *p_str, led_dtls_t *dtls);
60 static int act_led_on(str *p_str, led_dtls_t *dtls);
61 static int act_disk_present(str *p_str, led_dtls_t *dtls);
62 static int act_disk_fault(str *p_str, led_dtls_t *dtls);
63 static int act_led_id(str *p_str, led_dtls_t *dtls);
64 static int act_slow_poll(str *p_str, led_dtls_t *dtls);
65 static int act_fast_poll(str *p_str, led_dtls_t *dtls);
66 static int act_relax_interval(str *p_str, led_dtls_t *dtls);
67 static int act_test_interval(str *p_str, led_dtls_t *dtls);
68 static int act_disk_parent(str *p_str, led_dtls_t *dtls);
69 static int act_unit_parent(str *p_str, led_dtls_t *dtls);
70 static int act_led_nodes(str *p_str, led_dtls_t *dtls);
71 
72 /*
73  * The table below is used to lookup .conf file keywords to yield either
74  * a corresponding enum or a function to process the keyword.
75  */
76 static lookup_t table[] = {
77 	{ FCAL_VERSION,		"VERSION", 		act_version	},
78 	{ FCAL_REMOK_LED,	"REMOK",		NULL		},
79 	{ FCAL_FAULT_LED,	"FAULT",		NULL		},
80 	{ FCAL_READY_LED,	"READY",		NULL		},
81 	{ FCAL_LEDS_BOARD,	"FCAL-LEDS",		act_leds_board	},
82 	{ FCAL_STATUS_BOARD,	"FCAL-STATUS",		act_status_board },
83 	{ FCAL_DISK_DRIVER,	"FCAL-DISK-DRIVER",	act_disk_driver },
84 	{ FCAL_N_DISKS,		"N-DISKS",		act_n_disks	},
85 	{ FCAL_ASSERT_PRESENT,	"ASSERT-PRESENT",	act_asrt_pres	},
86 	{ FCAL_ASSERT_FAULT,	"ASSERT-FAULT",		act_asrt_fault	},
87 	{ FCAL_LED_ON,		"LED-ON",		act_led_on	},
88 	{ FCAL_DISK_PRESENT,	"DISK-PRESENT",		act_disk_present },
89 	{ FCAL_DISK_FAULT,	"DISK-FAULT",		act_disk_fault	},
90 	{ FCAL_LED_ID,		"LED",			act_led_id	},
91 	{ FCAL_SLOW_POLL,	"SLOW-POLL",		act_slow_poll	},
92 	{ FCAL_FAST_POLL,	"FAST-POLL",		act_fast_poll	},
93 	{ FCAL_RELAX_INTERVAL,	"RELAX-INTERVAL",	act_relax_interval },
94 	{ FCAL_TEST_INTERVAL,	"LED-TEST-INTERVAL",	act_test_interval },
95 	{ FCAL_DISK_PARENT,	"FCAL-DISK-PARENT",	act_disk_parent	},
96 	{ FCAL_UNIT_PARENT,	"DISK-UNIT-PARENT",	act_unit_parent	},
97 	{ FCAL_LED_NODES,	"DISK-LED-NODES",	act_led_nodes	}
98 };
99 
100 /*
101  * length of longest string in table (with space for null terminator)
102  */
103 #define	MAX_FCAL_TOKEN_LEN	18
104 
105 static const int tab_len = (sizeof (table))/sizeof (table[0]);
106 
107 /*
108  * get_token
109  * Parses the current line of data and returns the next token.
110  * If there are no significant characters in the line, NO_TOKEN is returned.
111  * If a syntax error is encountered, TOKEN_ERROR is returned.
112  * Pointer to position in current line is updated to point to the terminator
113  * of the token, unless TOKEN_ERROR is returned.
114  */
115 static token_t
116 get_token(
117 	char **pptr,	/* pointer to pointer to position in current line */
118 			/* *ptr is updated by the function */
119 	int lineNo,	/* current line number, used for syslog. If set to */
120 			/* zero, syslogging is supressed */
121 	actfun_t *fun)	/* pointer to function variable to receive action */
122 			/* pointer for the token found. NULL may be returned */
123 {
124 	char		*ptr;
125 	char		*token_start;
126 	int		toklen;
127 	int		i;
128 	int		ch;
129 
130 	*fun = NULL;
131 	ptr = *pptr;
132 
133 	/* strip leading white space */
134 	do {
135 		ch = (unsigned)(*ptr++);
136 
137 	} while (isspace(ch));
138 
139 	if ((ch == '\0') || (ch == '#')) {
140 		*pptr = ptr;
141 		return (NO_TOKEN);	/* empty line or comment */
142 	}
143 
144 	if (!isalpha(ch)) {
145 		if (lineNo != 0)
146 			SYSLOG(LOG_ERR, EM_NONALF_TOK, lineNo);
147 		return (TOKEN_ERROR);
148 	}
149 	token_start = ptr - 1;
150 	toklen = strcspn(token_start, ",: \t");
151 	*pptr = token_start + toklen;
152 	/*
153 	 * got token, now look it up
154 	 */
155 	for (i = 0; i < tab_len; i++) {
156 		if ((strncasecmp(token_start, table[i].tok_str,
157 		    toklen) == 0) && (table[i].tok_str[toklen] == '\0')) {
158 			*fun = table[i].action;
159 			return (table[i].tok);
160 		}
161 	}
162 	if (lineNo != 0)
163 		SYSLOG(LOG_ERR, EM_UNKN_TOK, lineNo);
164 	return (TOKEN_ERROR);
165 }
166 
167 static int
168 act_version(str *p_str, led_dtls_t *dtls)
169 {
170 	dtls->ver_maj = strtoul(*p_str, p_str, 0);
171 	if (*(*p_str)++ != '.') {
172 		SYSLOG(LOG_ERR, EM_VER_FRMT);
173 		return (-1);
174 	}
175 	dtls->ver_min = strtoul(*p_str, p_str, 0);
176 	if ((**p_str != '\0') && !isspace(**p_str)) {
177 		SYSLOG(LOG_ERR, EM_VER_FRMT);
178 		return (-1);
179 	}
180 	if ((dtls->ver_maj != 1) || (dtls->ver_min != 0)) {
181 		SYSLOG(LOG_ERR, EM_WRNGVER, dtls->ver_maj, dtls->ver_min);
182 		return (-1);
183 	}
184 	return (0);
185 }
186 
187 /*
188  * get space to hold white-space terminated string at *p_str
189  * advance *p_str to point to terminator
190  * return copy of string, null terminated
191  */
192 static int
193 get_cstr(str *p_str, cstr *p_cstr_res)
194 {
195 	int ch;
196 	int len;
197 	char *ptr;
198 
199 	while (isspace(**p_str))
200 		(*p_str)++;
201 	ptr = *p_str;
202 
203 	do {
204 		ch = *++ptr;
205 	} while ((ch != '\0') && (!isspace(ch)));
206 
207 	len = ptr - *p_str;
208 	if (*p_cstr_res != NULL)
209 		free((void *)(*p_cstr_res));
210 	ptr = malloc(len + 1);
211 	*p_cstr_res = ptr;
212 	if (ptr == NULL) {
213 		return (ENOMEM);
214 	}
215 	(void) memcpy(ptr, *p_str, len);
216 	ptr[len] = '\0';
217 	(*p_str) += len;
218 	return (0);
219 }
220 
221 static int
222 act_leds_board(str *p_str, led_dtls_t *dtls)
223 {
224 	int res = get_cstr(p_str, &dtls->fcal_leds);
225 	if (res == 0) {
226 		if (dtls->fcal_leds[0] != '/') {
227 			free((void *)dtls->fcal_leds);
228 			dtls->fcal_leds = NULL;
229 			SYSLOG(LOG_ERR, EM_REL_PATH);
230 			return (-1);
231 		}
232 	}
233 	return (res);
234 }
235 
236 static int
237 act_status_board(str *p_str, led_dtls_t *dtls)
238 {
239 	int res = get_cstr(p_str, &dtls->fcal_status);
240 	if (res == 0) {
241 		if (dtls->fcal_status[0] != '/') {
242 			free((void *)dtls->fcal_status);
243 			dtls->fcal_status = NULL;
244 			SYSLOG(LOG_ERR, EM_REL_PATH);
245 			return (-1);
246 		}
247 	}
248 	return (res);
249 }
250 
251 static int
252 act_disk_driver(str *p_str, led_dtls_t *dtls)
253 {
254 	return (get_cstr(p_str, &dtls->fcal_driver));
255 }
256 
257 static int
258 act_disk_parent(str *p_str, led_dtls_t *dtls)
259 {
260 	return (get_cstr(p_str, &dtls->fcal_disk_parent));
261 }
262 
263 static int
264 act_unit_parent(str *p_str, led_dtls_t *dtls)
265 {
266 	return (get_cstr(p_str, &dtls->disk_unit_parent));
267 }
268 
269 static int
270 act_led_nodes(str *p_str, led_dtls_t *dtls)
271 {
272 	return (get_cstr(p_str, &dtls->disk_led_nodes));
273 }
274 
275 /*
276  * A number of fields in the led_dtls_t structure have per-disk copies.
277  * This action routine creates the space for all such fields.
278  * Following any failure, an error is returned and the calling routine
279  * must handle the fact that only a subset of these fields are populated.
280  * In practice, this function is only called by get_token() on behalf of
281  * fc_led_parse(). fc_led_parse calls free_led_dtls() after any error.
282  */
283 static int
284 act_n_disks(str *p_str, led_dtls_t *dtls)
285 {
286 	int i;
287 
288 	if (dtls->n_disks != 0) {
289 		SYSLOG(LOG_ERR, EM_NDISKS_DBL);
290 		return (-1);
291 	}
292 	dtls->n_disks = strtoul(*p_str, p_str, 0);
293 	if ((**p_str != '\0') && !isspace(**p_str)) {
294 		SYSLOG(LOG_ERR, EM_NUM_TERM);
295 		return (-1);
296 	}
297 	if (dtls->n_disks < 1) {
298 		SYSLOG(LOG_ERR, EM_NO_DISKS);
299 		return (-1);
300 	}
301 	dtls->presence = calloc(dtls->n_disks, sizeof (int));
302 	if (dtls->presence == NULL)
303 		return (ENOMEM);
304 	dtls->faults = calloc(dtls->n_disks, sizeof (int));
305 	if (dtls->faults == NULL)
306 		return (ENOMEM);
307 	dtls->disk_detected = calloc(dtls->n_disks, sizeof (int));
308 	if (dtls->disk_detected == NULL)
309 		return (ENOMEM);
310 	dtls->disk_ready = calloc(dtls->n_disks, sizeof (int));
311 	if (dtls->disk_ready == NULL)
312 		return (ENOMEM);
313 	dtls->disk_prev = calloc(dtls->n_disks, sizeof (int));
314 	if (dtls->disk_prev == NULL)
315 		return (ENOMEM);
316 	dtls->led_test_end = calloc(dtls->n_disks, sizeof (int));
317 	if (dtls->led_test_end == NULL)
318 		return (ENOMEM);
319 	dtls->picl_retry = calloc(dtls->n_disks, sizeof (boolean_t));
320 	if (dtls->picl_retry == NULL)
321 		return (ENOMEM);
322 	dtls->disk_port = calloc(dtls->n_disks, sizeof (char *));
323 	if (dtls->disk_port == NULL) {
324 		return (ENOMEM);
325 	}
326 	for (i = 0; i < FCAL_LED_CNT; i++) {
327 		dtls->led_addr[i] = calloc(dtls->n_disks, sizeof (int));
328 		if (dtls->led_addr[i] == NULL)
329 			return (ENOMEM);
330 		dtls->led_state[i] = calloc(dtls->n_disks,
331 		    sizeof (led_state_t));
332 		if (dtls->led_state[i] == NULL)
333 			return (ENOMEM);
334 	}
335 	return (0);
336 }
337 
338 static int
339 get_assert(str *p_str, int *assert)
340 {
341 	int i = strtoul(*p_str, p_str, 0);
342 	if ((**p_str != '\0') && !isspace(**p_str)) {
343 		SYSLOG(LOG_ERR, EM_NUM_TERM);
344 		return (-1);
345 	}
346 	if ((i != 0) && (i != 1)) {
347 		SYSLOG(LOG_ERR, EM_LOGIC_LVL);
348 		return (-1);
349 	}
350 	*assert = i;
351 	return (0);
352 }
353 
354 static int
355 get_pnz(str *p_str, int *pnz)
356 {
357 	int i = strtoul(*p_str, p_str, 0);
358 	if ((**p_str != '\0') && !isspace(**p_str)) {
359 		SYSLOG(LOG_ERR, EM_NUM_TERM);
360 		return (-1);
361 	}
362 	if (i < 1) {
363 		SYSLOG(LOG_ERR, EM_NOTPOS);
364 		return (-1);
365 	}
366 	*pnz = i;
367 	return (0);
368 }
369 
370 static int
371 act_asrt_pres(str *p_str, led_dtls_t *dtls)
372 {
373 	return (get_assert(p_str, &dtls->assert_presence));
374 }
375 
376 static int
377 act_asrt_fault(str *p_str, led_dtls_t *dtls)
378 {
379 	return (get_assert(p_str, &dtls->assert_fault));
380 }
381 
382 static int
383 act_led_on(str *p_str, led_dtls_t *dtls)
384 {
385 	return (get_assert(p_str, &dtls->assert_led_on));
386 }
387 
388 static int
389 get_mask(str *p_str, int n_disks, int *p_intarray)
390 {
391 	int i;
392 	int j = strtoul(*p_str, p_str, 0);
393 	if (*(*p_str)++ != ',') {
394 		SYSLOG(LOG_ERR, EM_NUM_TERM);
395 		return (-1);
396 	}
397 	if ((j < 0) || (j > n_disks)) {
398 		SYSLOG(LOG_ERR, EM_DISK_RANGE);
399 		return (-1);
400 	}
401 	i = strtoul(*p_str, p_str, 0);
402 	if ((**p_str != '\0') && !isspace(**p_str)) {
403 		SYSLOG(LOG_ERR, EM_NUM_TERM);
404 		return (-1);
405 	}
406 	p_intarray[j] = i;
407 	return (0);
408 }
409 
410 static int
411 act_disk_present(str *p_str, led_dtls_t *dtls)
412 {
413 	return (get_mask(p_str, dtls->n_disks, dtls->presence));
414 }
415 
416 static int
417 act_disk_fault(str *p_str, led_dtls_t *dtls)
418 {
419 	return (get_mask(p_str, dtls->n_disks, dtls->faults));
420 }
421 
422 static int
423 act_led_id(str *p_str, led_dtls_t *dtls)
424 {
425 	token_t		tok;
426 	actfun_t	action;
427 	int		i;
428 	int		j = strtoul(*p_str, p_str, 0);
429 
430 	if (*(*p_str)++ != ',') {
431 		SYSLOG(LOG_ERR, EM_NUM_TERM);
432 		return (-1);
433 	}
434 	if ((j < 0) || (j >= dtls->n_disks)) {
435 		SYSLOG(LOG_ERR, EM_DISK_RANGE);
436 		return (-1);
437 	}
438 	tok = get_token(p_str, 0, &action);
439 	if ((tok <= LED_PROPS_START) || (tok >= LED_PROPS_END)) {
440 		SYSLOG(LOG_ERR, EM_NO_LED_PROP);
441 		return (-1);
442 	}
443 	if (*(*p_str)++ != ',') {
444 		SYSLOG(LOG_ERR, EM_PROP_TERM);
445 		return (-1);
446 	}
447 	i = strtoul(*p_str, p_str, 0);
448 	if ((**p_str != '\0') && !isspace(**p_str)) {
449 		SYSLOG(LOG_ERR, EM_NUM_TERM);
450 		return (-1);
451 	}
452 	dtls->led_addr[tok - FCAL_REMOK_LED][j] = i;
453 	return (0);
454 }
455 
456 static int
457 act_slow_poll(str *p_str, led_dtls_t *dtls)
458 {
459 	return (get_pnz(p_str, &dtls->slow_poll_ticks));
460 }
461 
462 static int
463 act_fast_poll(str *p_str, led_dtls_t *dtls)
464 {
465 	return (get_pnz(p_str, &dtls->fast_poll));
466 }
467 
468 static int
469 act_relax_interval(str *p_str, led_dtls_t *dtls)
470 {
471 	return (get_pnz(p_str, &dtls->relax_time_ticks));
472 }
473 
474 static int
475 act_test_interval(str *p_str, led_dtls_t *dtls)
476 {
477 	return (get_pnz(p_str, &dtls->led_test_time));
478 }
479 
480 /*
481  * Create a led_dtls_t structure
482  * Parse configuration file and populate the led_dtls_t
483  * In the event of an error, free the structure and return an error
484  */
485 int
486 fc_led_parse(FILE *fp, led_dtls_t **p_dtls)
487 {
488 	int		lineNo = 0;
489 	int		err = 0;
490 	char		linebuf[160];
491 	char		*ptr;
492 	led_dtls_t	*dtls = calloc(1, sizeof (led_dtls_t));
493 	actfun_t	action;
494 	token_t		tok;
495 
496 	*p_dtls = dtls;
497 	if (dtls == NULL) {
498 		return (ENOMEM);
499 	}
500 	dtls->ver_min = -1;	/* mark as version unknown */
501 
502 	while ((ptr = fgets(linebuf, sizeof (linebuf), fp)) != NULL) {
503 		lineNo++;
504 		tok = get_token(&ptr, lineNo, &action);
505 		if (tok == NO_TOKEN)
506 			continue;
507 		if (tok == TOKEN_ERROR) {
508 			err = -1;
509 			break;
510 		}
511 		if (tok == FCAL_VERSION) {
512 			if ((err = (*action)(&ptr, dtls)) != 0)
513 				break;
514 			else
515 				continue;
516 		}
517 		if (dtls->ver_min < 0) {
518 			SYSLOG(LOG_ERR, EM_NOVERS);
519 			err = -1;
520 			break;
521 		}
522 		if (tok <= LINE_DEFS) {
523 			SYSLOG(LOG_ERR, EM_INVAL_TOK, lineNo);
524 			err = -1;
525 			break;
526 		}
527 		if (*ptr++ != ':') {
528 			SYSLOG(LOG_ERR, EM_NOCOLON, lineNo);
529 			err = -1;
530 			break;
531 		}
532 		if ((err = (*action)(&ptr, dtls)) != 0) {
533 			SYSLOG(LOG_ERR, EM_ERRLINE, lineNo);
534 			break;
535 		}
536 		else
537 			continue;
538 	}
539 
540 	if (err == 0) {
541 		err = -1;	/* just in case */
542 		if (dtls->ver_min < 0) {
543 			SYSLOG(LOG_ERR, EM_NOVERS);
544 		} else if (dtls->n_disks == 0) {
545 			SYSLOG(LOG_ERR, EM_NO_DISKS);
546 		} else if (dtls->fcal_leds == NULL) {
547 			SYSLOG(LOG_ERR, EM_STR_NOT_SET, "fcal-leds");
548 		} else if (dtls->fcal_status == NULL) {
549 			SYSLOG(LOG_ERR, EM_STR_NOT_SET, "fcal-status");
550 		} else if (dtls->fcal_driver == NULL) {
551 			SYSLOG(LOG_ERR, EM_STR_NOT_SET, "fcal-driver");
552 		} else
553 			err = 0;
554 	}
555 
556 	if (err != 0) {
557 		/*
558 		 * clean up after error detected
559 		 */
560 		free_led_dtls(dtls);
561 		*p_dtls = NULL;
562 		return (err);
563 	}
564 
565 	/*
566 	 * set any unset timers to default time
567 	 */
568 	if (dtls->slow_poll_ticks == 0)
569 		dtls->slow_poll_ticks = DFLT_SLOW_POLL;
570 	if (dtls->fast_poll == 0)
571 		dtls->fast_poll = DFLT_FAST_POLL;
572 	if (dtls->relax_time_ticks == 0)
573 		dtls->relax_time_ticks = DFLT_RELAX_TIME;
574 	if (dtls->led_test_time == 0)
575 		dtls->led_test_time = DFLT_TEST_TIME;
576 
577 	/*
578 	 * set polling flag to avoid a start-up glitch
579 	 * it will be cleared again if the poll thread fails
580 	 */
581 	dtls->polling = B_TRUE;
582 
583 	/*
584 	 * convert derived timers to multiples of fast poll time
585 	 */
586 	dtls->slow_poll_ticks += dtls->fast_poll - 1;	/* for round up */
587 	dtls->slow_poll_ticks /= dtls->fast_poll;
588 	dtls->relax_time_ticks += dtls->fast_poll - 1;
589 	dtls->relax_time_ticks /= dtls->fast_poll;
590 	dtls->led_test_time += dtls->fast_poll - 1;
591 	dtls->led_test_time /= dtls->fast_poll;
592 	return (0);
593 }
594 
595 void
596 free_led_dtls(led_dtls_t *dtls)
597 {
598 	int	i;
599 
600 	if (dtls == NULL)
601 		return;
602 	if (dtls->fcal_leds != NULL)
603 		free((void *)dtls->fcal_leds);
604 	if (dtls->fcal_status != NULL)
605 		free((void *)dtls->fcal_status);
606 	if (dtls->fcal_driver != NULL)
607 		free((void *)dtls->fcal_driver);
608 	if (dtls->presence != NULL)
609 		free((void *)dtls->presence);
610 	if (dtls->faults != NULL)
611 		free((void *)dtls->faults);
612 	if (dtls->disk_detected != NULL)
613 		free((void *)dtls->disk_detected);
614 	if (dtls->disk_ready != NULL)
615 		free((void *)dtls->disk_ready);
616 	if (dtls->disk_prev != NULL)
617 		free((void *)dtls->disk_prev);
618 	if (dtls->led_test_end != NULL)
619 		free((void *)dtls->led_test_end);
620 	if (dtls->picl_retry != NULL)
621 		free((void *)dtls->picl_retry);
622 	if (dtls->disk_port != NULL) {
623 		for (i = 0; i < dtls->n_disks; i++) {
624 			if (dtls->disk_port[i] != NULL)
625 				free(dtls->disk_port[i]);
626 		}
627 		free(dtls->disk_port);
628 	}
629 	for (i = 0; i < FCAL_LED_CNT; i++) {
630 		if (dtls->led_addr[i] != NULL)
631 			free((void *)dtls->led_addr[i]);
632 		if (dtls->led_state[i] != NULL)
633 			free((void *)dtls->led_state[i]);
634 	}
635 
636 	free(dtls);
637 }
638