1 /*
2  * Copyright (c) 2022 Meta
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 #include <stdbool.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #ifdef CONFIG_ARCH_POSIX
12 #include <unistd.h>
13 #else
14 #include <zephyr/posix/unistd.h>
15 #endif
16 
17 #include <zephyr/kernel.h>
18 #include <zephyr/shell/shell.h>
19 #include <zephyr/sys/crc.h>
20 
21 static const char *const crc_types[] = {
22 	[CRC4] = "4",
23 	[CRC4_TI] = "4_ti",
24 	[CRC7_BE] = "7_be",
25 	[CRC8] = "8",
26 	[CRC8_CCITT] = "8_ccitt",
27 	[CRC8_ROHC] = "8_rohc",
28 	[CRC16] = "16",
29 	[CRC16_ANSI] = "16_ansi",
30 	[CRC16_CCITT] = "16_ccitt",
31 	[CRC16_ITU_T] = "16_itu_t",
32 	[CRC24_PGP] = "24_pgp",
33 	[CRC32_C] = "32_c",
34 	[CRC32_IEEE] = "32_ieee",
35 	[CRC32_K_4_2] = "32_k_4_2",
36 };
37 
string_to_crc_type(const char * s)38 static int string_to_crc_type(const char *s)
39 {
40 	int i;
41 
42 	for (i = 0; i < ARRAY_SIZE(crc_types); ++i) {
43 		if (strcmp(s, crc_types[i]) == 0) {
44 			return i;
45 		}
46 	}
47 
48 	return -1;
49 }
50 
usage(const struct shell * sh)51 static void usage(const struct shell *sh)
52 {
53 	size_t i;
54 
55 	shell_print(sh, "crc [options..] <address> <size>");
56 	shell_print(sh, "options:");
57 	shell_print(sh, "-f         This is the first packet");
58 	shell_print(sh, "-l         This is the last packet");
59 	shell_print(sh, "-p <poly>  Use polynomial 'poly'");
60 	shell_print(sh, "-r         Reflect");
61 	shell_print(sh, "-s <seed>  Use 'seed' as the initial value");
62 	shell_print(sh, "-t <type>  Compute the CRC described by 'type'");
63 	shell_print(sh, "Note: some options are only useful for certain CRCs");
64 	shell_print(sh, "CRC Types:");
65 	for (i = 0; i < ARRAY_SIZE(crc_types); ++i) {
66 		shell_print(sh, "%s", crc_types[i]);
67 	}
68 }
69 
cmd_crc(const struct shell * sh,size_t argc,char ** argv)70 static int cmd_crc(const struct shell *sh, size_t argc, char **argv)
71 {
72 	int rv;
73 	size_t size = -1;
74 	bool last = false;
75 	uint32_t poly = 0;
76 	bool first = false;
77 	uint32_t seed = 0;
78 	bool reflect = false;
79 	void *addr = (void *)-1;
80 	enum crc_type type = CRC32_IEEE;
81 
82 	optind = 1;
83 
84 	while ((rv = getopt(argc, argv, "fhlp:rs:t:")) != -1) {
85 		switch (rv) {
86 		case 'f':
87 			first = true;
88 			break;
89 		case 'h':
90 			usage(sh);
91 			return 0;
92 		case 'l':
93 			last = true;
94 			break;
95 		case 'p':
96 			poly = (size_t)strtoul(optarg, NULL, 16);
97 			if (poly == 0 && errno == EINVAL) {
98 				shell_error(sh, "invalid seed '%s'", optarg);
99 				return -EINVAL;
100 			}
101 			break;
102 		case 'r':
103 			reflect = true;
104 			break;
105 		case 's':
106 			seed = (size_t)strtoul(optarg, NULL, 16);
107 			if (seed == 0 && errno == EINVAL) {
108 				shell_error(sh, "invalid seed '%s'", optarg);
109 				return -EINVAL;
110 			}
111 			break;
112 		case 't':
113 			type = string_to_crc_type(optarg);
114 			if (type == -1) {
115 				shell_error(sh, "invalid type '%s'", optarg);
116 				return -EINVAL;
117 			}
118 			break;
119 		case '?':
120 		default:
121 			usage(sh);
122 			return -EINVAL;
123 		}
124 	}
125 
126 	if (optind + 2 > argc) {
127 		shell_error(sh, "'address' and 'size' arguments are mandatory");
128 		usage(sh);
129 		return -EINVAL;
130 	}
131 
132 	addr = (void *)strtoul(argv[optind], NULL, 16);
133 	if (addr == 0 && errno == EINVAL) {
134 		shell_error(sh, "invalid address '%s'", argv[optind]);
135 		return -EINVAL;
136 	}
137 
138 	size = (size_t)strtoul(argv[optind + 1], NULL, 0);
139 	if (size == 0 && errno == EINVAL) {
140 		shell_error(sh, "invalid size '%s'", argv[optind + 1]);
141 		return -EINVAL;
142 	}
143 
144 	shell_print(sh, "0x%x", crc_by_type(type, addr, size, seed, poly, reflect, first, last));
145 
146 	return 0;
147 }
148 
149 SHELL_CMD_ARG_REGISTER(crc, NULL, NULL, cmd_crc, 0, 12);
150