| /* |
| * Copyright (c) 2022 Meta |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <errno.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #ifdef CONFIG_ARCH_POSIX |
| #include <unistd.h> |
| #else |
| #include <zephyr/posix/unistd.h> |
| #endif |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/shell/shell.h> |
| #include <zephyr/sys/crc.h> |
| |
| static const char *const crc_types[] = { |
| [CRC4] = "4", |
| [CRC4_TI] = "4_ti", |
| [CRC7_BE] = "7_be", |
| [CRC8] = "8", |
| [CRC8_CCITT] = "8_ccitt", |
| [CRC16] = "16", |
| [CRC16_ANSI] = "16_ansi", |
| [CRC16_CCITT] = "16_ccitt", |
| [CRC16_ITU_T] = "16_itu_t", |
| [CRC32_C] = "32_c", |
| [CRC32_IEEE] = "32_ieee", |
| }; |
| |
| static int string_to_crc_type(const char *s) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(crc_types); ++i) { |
| if (strcmp(s, crc_types[i]) == 0) { |
| return i; |
| } |
| } |
| |
| return -1; |
| } |
| |
| static void usage(const struct shell *sh) |
| { |
| size_t i; |
| |
| shell_print(sh, "crc [options..] <address> <size>"); |
| shell_print(sh, "options:"); |
| shell_print(sh, "-f This is the first packet"); |
| shell_print(sh, "-l This is the last packet"); |
| shell_print(sh, "-p <poly> Use polynomial 'poly'"); |
| shell_print(sh, "-r Reflect"); |
| shell_print(sh, "-s <seed> Use 'seed' as the initial value"); |
| shell_print(sh, "-t <type> Compute the CRC described by 'type'"); |
| shell_print(sh, "Note: some options are only useful for certain CRCs"); |
| shell_print(sh, "CRC Types:"); |
| for (i = 0; i < ARRAY_SIZE(crc_types); ++i) { |
| shell_print(sh, "%s", crc_types[i]); |
| } |
| } |
| |
| static int cmd_crc(const struct shell *sh, size_t argc, char **argv) |
| { |
| int rv; |
| size_t size = -1; |
| bool last = false; |
| uint32_t poly = 0; |
| bool first = false; |
| uint32_t seed = 0; |
| bool reflect = false; |
| void *addr = (void *)-1; |
| enum crc_type type = CRC32_IEEE; |
| |
| optind = 1; |
| |
| while ((rv = getopt(argc, argv, "fhlp:rs:t:")) != -1) { |
| switch (rv) { |
| case 'f': |
| first = true; |
| break; |
| case 'h': |
| usage(sh); |
| return 0; |
| case 'l': |
| last = true; |
| break; |
| case 'p': |
| poly = (size_t)strtoul(optarg, NULL, 16); |
| if (poly == 0 && errno == EINVAL) { |
| shell_error(sh, "invalid seed '%s'", optarg); |
| return -EINVAL; |
| } |
| break; |
| case 'r': |
| reflect = true; |
| break; |
| case 's': |
| seed = (size_t)strtoul(optarg, NULL, 16); |
| if (seed == 0 && errno == EINVAL) { |
| shell_error(sh, "invalid seed '%s'", optarg); |
| return -EINVAL; |
| } |
| break; |
| case 't': |
| type = string_to_crc_type(optarg); |
| if (type == -1) { |
| shell_error(sh, "invalid type '%s'", optarg); |
| return -EINVAL; |
| } |
| break; |
| case '?': |
| default: |
| usage(sh); |
| return -EINVAL; |
| } |
| } |
| |
| if (optind + 2 > argc) { |
| shell_error(sh, "'address' and 'size' arguments are mandatory"); |
| usage(sh); |
| return -EINVAL; |
| } |
| |
| addr = (void *)strtoul(argv[optind], NULL, 16); |
| if (addr == 0 && errno == EINVAL) { |
| shell_error(sh, "invalid address '%s'", argv[optind]); |
| return -EINVAL; |
| } |
| |
| size = (size_t)strtoul(argv[optind + 1], NULL, 0); |
| if (size == 0 && errno == EINVAL) { |
| shell_error(sh, "invalid size '%s'", argv[optind + 1]); |
| return -EINVAL; |
| } |
| |
| shell_print(sh, "0x%x", crc_by_type(type, addr, size, seed, poly, reflect, first, last)); |
| |
| return 0; |
| } |
| |
| SHELL_CMD_ARG_REGISTER(crc, NULL, NULL, cmd_crc, 0, 12); |