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 };
36
string_to_crc_type(const char * s)37 static int string_to_crc_type(const char *s)
38 {
39 int i;
40
41 for (i = 0; i < ARRAY_SIZE(crc_types); ++i) {
42 if (strcmp(s, crc_types[i]) == 0) {
43 return i;
44 }
45 }
46
47 return -1;
48 }
49
usage(const struct shell * sh)50 static void usage(const struct shell *sh)
51 {
52 size_t i;
53
54 shell_print(sh, "crc [options..] <address> <size>");
55 shell_print(sh, "options:");
56 shell_print(sh, "-f This is the first packet");
57 shell_print(sh, "-l This is the last packet");
58 shell_print(sh, "-p <poly> Use polynomial 'poly'");
59 shell_print(sh, "-r Reflect");
60 shell_print(sh, "-s <seed> Use 'seed' as the initial value");
61 shell_print(sh, "-t <type> Compute the CRC described by 'type'");
62 shell_print(sh, "Note: some options are only useful for certain CRCs");
63 shell_print(sh, "CRC Types:");
64 for (i = 0; i < ARRAY_SIZE(crc_types); ++i) {
65 shell_print(sh, "%s", crc_types[i]);
66 }
67 }
68
cmd_crc(const struct shell * sh,size_t argc,char ** argv)69 static int cmd_crc(const struct shell *sh, size_t argc, char **argv)
70 {
71 int rv;
72 size_t size = -1;
73 bool last = false;
74 uint32_t poly = 0;
75 bool first = false;
76 uint32_t seed = 0;
77 bool reflect = false;
78 void *addr = (void *)-1;
79 enum crc_type type = CRC32_IEEE;
80
81 optind = 1;
82
83 while ((rv = getopt(argc, argv, "fhlp:rs:t:")) != -1) {
84 switch (rv) {
85 case 'f':
86 first = true;
87 break;
88 case 'h':
89 usage(sh);
90 return 0;
91 case 'l':
92 last = true;
93 break;
94 case 'p':
95 poly = (size_t)strtoul(optarg, NULL, 16);
96 if (poly == 0 && errno == EINVAL) {
97 shell_error(sh, "invalid seed '%s'", optarg);
98 return -EINVAL;
99 }
100 break;
101 case 'r':
102 reflect = true;
103 break;
104 case 's':
105 seed = (size_t)strtoul(optarg, NULL, 16);
106 if (seed == 0 && errno == EINVAL) {
107 shell_error(sh, "invalid seed '%s'", optarg);
108 return -EINVAL;
109 }
110 break;
111 case 't':
112 type = string_to_crc_type(optarg);
113 if (type == -1) {
114 shell_error(sh, "invalid type '%s'", optarg);
115 return -EINVAL;
116 }
117 break;
118 case '?':
119 default:
120 usage(sh);
121 return -EINVAL;
122 }
123 }
124
125 if (optind + 2 > argc) {
126 shell_error(sh, "'address' and 'size' arguments are mandatory");
127 usage(sh);
128 return -EINVAL;
129 }
130
131 addr = (void *)strtoul(argv[optind], NULL, 16);
132 if (addr == 0 && errno == EINVAL) {
133 shell_error(sh, "invalid address '%s'", argv[optind]);
134 return -EINVAL;
135 }
136
137 size = (size_t)strtoul(argv[optind + 1], NULL, 0);
138 if (size == 0 && errno == EINVAL) {
139 shell_error(sh, "invalid size '%s'", argv[optind + 1]);
140 return -EINVAL;
141 }
142
143 shell_print(sh, "0x%x", crc_by_type(type, addr, size, seed, poly, reflect, first, last));
144
145 return 0;
146 }
147
148 SHELL_CMD_ARG_REGISTER(crc, NULL, NULL, cmd_crc, 0, 12);
149