carl9170 firmware: add radar pattern generator
authorChristian Lamparter <chunkeey@googlemail.com>
Fri, 4 May 2012 20:07:52 +0000 (22:07 +0200)
committerChristian Lamparter <chunkeey@googlemail.com>
Sat, 5 May 2012 00:12:04 +0000 (02:12 +0200)
This - rather large - patch includes parts of a
future radar pattern generator which hopefully
can be used for testing DFS without having to
resort to expensive hardware.

The basic concept has been successfully tested with
p54 [firmware detector - easy to fool] . But, that
does not mean it works with other devices just as
well (or at all!).

Note: This feature is WIP and it will not work
out-of-the-box.

Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
14 files changed:
autogen.sh
carlfw/Kconfig
carlfw/include/carl9170.h
carlfw/include/fwdsc.h
carlfw/include/radar.h [new file with mode: 0644]
carlfw/include/timer.h
carlfw/src/fw.c
carlfw/src/main.c
include/shared/fwdesc.h
include/shared/version.h
tools/.gitignore
tools/src/CMakeLists.txt
tools/src/fwinfo.c
tools/src/fwprepare.c [new file with mode: 0644]

index 729041e6e537656566a9b8035f5a3aee9a4bff51..5d79159c41b774b46c40beced6030cab58e412f7 100755 (executable)
@@ -33,6 +33,12 @@ case "$1" in
                        tools/src/miniboot a carlfw/carl9170.fw minifw/miniboot.fw
                fi
 
+
+               if [ "$CONFIG_CARL9170FW_BUILD_TOOLS" = "y" ]; then
+                       echo -n "Prepare firmware image..."
+                       tools/src/fwprepare carlfw/carl9170.fw
+               fi
+
                sudo install -m 644 carlfw/carl9170.fw \
                        /lib/firmware/carl9170-$CONFIG_CARL9170FW_RELEASE_VERSION.fw
                echo "done."
index f42268c417919c2cff9ba5e2173cf20b541a29d1..c2da51a0820ec4997a3fa57384747a53b869f5a8 100644 (file)
@@ -100,6 +100,16 @@ config CARL9170FW_EXPERIMENTAL
        def_bool y
        prompt "Experimental Features"
 
+config CARL9170FW_RADAR
+       def_bool n
+       prompt "Radar pattern generator"
+       depends on CARL9170FW_EXPERIMENTAL
+       ---help---
+        With this option enabled, the firmware can generate random
+        transmission pattern that might fool a reciever to believe
+        that there is an active radar on the channel.
+        Note: sadly, no SDR here.
+
 config CARL9170FW_WOL_OPTION
        def_bool n
        prompt "Wakeup on WLAN"
index d10a74e8064448eebbb60502a53b23c1bc7744ad..df41f8461bf4e1acbfb1fadf7c63a2234de74e58 100644 (file)
@@ -33,6 +33,7 @@
 #include "dma.h"
 #include "usb.h"
 #include "cmd.h"
+#include "radar.h"
 
 struct carl9170_bar_ctx {
        uint8_t ta[6];
@@ -142,6 +143,10 @@ struct firmware_context_struct {
                unsigned int ba_tail_idx,
                             ba_head_idx,
                             queued_ba;
+
+               unsigned int soft_radar,
+                            radar_last,
+                            pattern_index;
        } wlan;
 
        struct {
index 936bfed03c8b50b162a3b673a7c6c1f3301eacb1..6ae6563c6bd099851ab48971c51eeb6a63bb1ec8 100644 (file)
@@ -35,6 +35,9 @@ struct carl9170_firmware_descriptor {
        struct carl9170fw_wol_desc  wol;
 #endif /* CONFIG_CARL9170FW_WOL */
        struct carl9170fw_motd_desc motd;
+#ifdef CONFIG_CARL9170FW_RADAR
+       struct carl9170fw_radar_desc radar;
+#endif /* CONFIG_CARL9170FW_RADAR */
        struct carl9170fw_dbg_desc  dbg;
        struct carl9170fw_last_desc last;
 } __packed;
diff --git a/carlfw/include/radar.h b/carlfw/include/radar.h
new file mode 100644 (file)
index 0000000..864882a
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Radar pulse definitions
+ *
+ * Copyright 2012 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __CARL9170FW_RADAR_H
+#define __CARL9170FW_RADAR_H
+
+#include "generated/autoconf.h"
+#include "types.h"
+#include "compiler.h"
+#include "fwdesc.h"
+
+enum RADAR_TYPE {
+       NO_RADAR = 0,
+       ONE_KHZ,
+       TEN_KHZ,
+
+       ONE_TWO_KHZ,
+
+       FCC1,
+       FCC4,
+
+       ETSIFIXED,
+
+       /* keep last */
+       __CARL9170FW_NUM_RADARS
+};
+
+struct radar_info {
+       unsigned int pulses;
+       const struct radar_info_pattern *pattern;
+};
+
+struct radar_info_pattern {
+       unsigned int pulse_width;
+       unsigned int pulse_interval;
+       uint32_t     pulse_pattern;
+       uint32_t     pulse_mode;
+};
+
+static const struct radar_info_pattern radar_NO_RADAR[0] = {  };
+static const struct radar_info_pattern radar_ONE_KHZ[] = {
+       {
+               .pulse_width = 1,
+               .pulse_interval = 1000,
+               .pulse_pattern = 0xa7438080,
+               .pulse_mode    = 0x5f01,
+       },
+};
+
+static const struct radar_info_pattern radar_TEN_KHZ[] = {
+       {
+               .pulse_width = 1,
+               .pulse_interval = 100,
+               .pulse_pattern = 0x436f0001,
+               .pulse_mode    = 0x5f01,
+       },
+};
+
+static const struct radar_info_pattern radar_ONE_TWO_KHZ[] = {
+       {
+               .pulse_width = 1,
+               .pulse_interval = 1000,
+               .pulse_pattern = 0xa7438080,
+               .pulse_mode    = 0x5f01,
+       },
+
+       {
+               .pulse_width = 10,
+               .pulse_interval = 500,
+               .pulse_pattern = 0xa7431001,
+               .pulse_mode    = 0x5f01,
+       },
+};
+
+/*
+ * Data taken from:
+ * <http://linuxwireless.org/en/developers/DFS>
+ */
+
+/* FCC Test Signal 1 - 1us pulse, 1428 us interval */
+static const struct radar_info_pattern radar_FCC1[] = {
+       {
+               .pulse_width = 1,
+               .pulse_interval = 1428,
+               .pulse_pattern = 0xa7438080,
+               .pulse_mode    = 0x5f01,
+       },
+};
+
+/* FCC Test Signal 4 - 11-20us pulse, 200-500 us interval */
+static const struct radar_info_pattern radar_FCC4[] = {
+       {
+               .pulse_width = 11,
+               .pulse_interval = 200,
+               .pulse_pattern = 0xf3128008,
+               .pulse_mode    = 0x5f01,
+       },
+};
+
+/* ETSI Test Signal 1 (Fixed) - 1us Pulse, 750 us interval */
+static const struct radar_info_pattern radar_ETSIFIXED[] = {
+       {
+               .pulse_width = 1,
+               .pulse_interval = 750,
+               .pulse_pattern = 0x8a5f8080,
+               .pulse_mode    = 0x5f01,
+       },
+};
+
+
+#define ADD_RADAR(name) [name] = { .pulses = ARRAY_SIZE(radar_## name), .pattern = radar_## name }
+
+static const struct radar_info radars[__CARL9170FW_NUM_RADARS] = {
+       ADD_RADAR(NO_RADAR),
+       ADD_RADAR(ONE_KHZ),
+       ADD_RADAR(TEN_KHZ),
+       ADD_RADAR(ONE_TWO_KHZ),
+       ADD_RADAR(FCC1),
+       ADD_RADAR(FCC4),
+       ADD_RADAR(ETSIFIXED),
+};
+
+#define MAP_ENTRY(idx) [idx] = { .index = idx, .name = # idx , }
+#define NAMED_MAP_ENTRY(idx, named) [idx] = {.index = idx, .name = named, }
+
+static const struct carl9170fw_radar_map_entry radar_names[__CARL9170FW_NUM_RADARS] = {
+       MAP_ENTRY(NO_RADAR),
+       MAP_ENTRY(ONE_KHZ),
+       MAP_ENTRY(TEN_KHZ),
+       MAP_ENTRY(ONE_TWO_KHZ),
+
+       MAP_ENTRY(FCC1),
+       MAP_ENTRY(FCC4),
+
+       MAP_ENTRY(ETSIFIXED),
+};
+
+#endif /* __CARL9170FW_RADAR_H */
index 1c1c6cd4fa6f0f30c9f539c0a110c8f78baed5ee..0464914091353f30eb7029e4be0453250b283713 100644 (file)
@@ -50,6 +50,11 @@ static inline __inline bool is_after_msecs(const uint32_t t0, const uint32_t mse
        return ((get_clock_counter() - t0) / 1000) > (msecs * fw.ticks_per_usec);
 }
 
+static inline __inline bool is_after_usecs(const uint32_t t0, const uint32_t usecs)
+{
+       return ((get_clock_counter() - t0)) > (usecs * fw.ticks_per_usec);
+}
+
 /*
  * Note: Be careful with [u]delay. They won't service the
  * hardware watchdog timer. It might trigger if you
index fde4a5d4a3263f33a9408080f43c29d5324ee245..c91914113e7f290ef345dd39091c4d4524ceb002 100644 (file)
@@ -65,6 +65,9 @@ const struct carl9170_firmware_descriptor __section(fwdsc) carl9170fw_desc = {
 #ifdef CONFIG_CARL9170FW_WOL
                                        BIT(CARL9170FW_WOL) |
 #endif /* CONFIG_CARL9170FW_WOL */
+#ifdef CONFIG_CARL9170FW_RADAR
+                                       BIT(CARL9170FW_RADAR_PATTERN_GENERATOR) |
+#endif /* CONFIG_CARL9170FW_RADAR */
                                        (0)),
 
             .miniboot_size = cpu_to_le16(0),
@@ -90,7 +93,6 @@ const struct carl9170_firmware_descriptor __section(fwdsc) carl9170fw_desc = {
        ),
 #endif /* CONFIG_CARL9170FW_WOL */
 
-
        FILL(motd, MOTD,
             .fw_year_month_day = cpu_to_le32(
                        CARL9170FW_SET_DAY(CARL9170FW_VERSION_DAY) +
@@ -99,6 +101,15 @@ const struct carl9170_firmware_descriptor __section(fwdsc) carl9170fw_desc = {
             .desc = "Community AR9170 Linux",
             .release = CARL9170FW_VERSION_GIT),
 
+
+#ifdef CONFIG_CARL9170FW_RADAR
+       FILL(radar, RADAR,
+            .soft_radar = cpu_to_le32(&fw.wlan.soft_radar),
+            .num_radars = __CARL9170FW_NUM_RADARS,
+            .radars = { /* filled by the fwprepare tool */ },
+       ),
+#endif /* CONFIG_CARL9170FW_RADAR */
+
        FILL(dbg, DBG,
             .bogoclock_addr = cpu_to_le32(0),
             .counter_addr = cpu_to_le32(&fw.counter),
index 17cbaf922d06500a76234b4e78988edfc19cc76f..c35f5ad104a9cf4feaf6716c2419783f83e7d9af 100644 (file)
@@ -31,6 +31,7 @@
 #include "wl.h"
 #include "rf.h"
 #include "usb.h"
+#include "radar.h"
 
 #define AR9170_WATCH_DOG_TIMER            0x100
 
@@ -185,6 +186,32 @@ static void tally_update(void)
        fw.counter++;
 }
 
+static void radar_pattern_generator(void)
+{
+       if (fw.phy.state == CARL9170_PHY_ON) {
+               if (fw.wlan.soft_radar == NO_RADAR ||
+                   fw.wlan.soft_radar >= __CARL9170FW_NUM_RADARS)
+                       return;
+
+               const struct radar_info *radar = &radars[fw.wlan.soft_radar];
+               if (radar->pulses >= fw.wlan.pattern_index) {
+                       fw.wlan.pattern_index = 0;
+               }
+
+               if (radar->pulses > fw.wlan.pattern_index) {
+                       const struct radar_info_pattern *pattern = &radar->pattern[fw.wlan.pattern_index];
+                       if (is_after_usecs(fw.wlan.radar_last, pattern->pulse_interval)) {
+                               fw.wlan.radar_last = get_clock_counter();
+                               //set(PATTERN, pattern->pulse_pattern);
+                               //set(MODE, pattern->pulse_mode);
+                               udelay(pattern->pulse_width);
+                               //set(MODE, ~pattern->pulse_mode);
+                               fw.wlan.pattern_index++;
+                       }
+               }
+       }
+}
+
 static void __noreturn main_loop(void)
 {
        /* main loop */
@@ -204,6 +231,8 @@ static void __noreturn main_loop(void)
                handle_timer();
 
                tally_update();
+
+               radar_pattern_generator();
        }
 }
 
index 6d9c0891ce7f9edd1c0cf91af5b15e203dbd7c72..903a346bebd54061785f2909eb24fb254abdf584 100644 (file)
@@ -78,6 +78,9 @@ enum carl9170fw_feature_list {
        /* HW (ANI, CCA, MIB) tally counters */
        CARL9170FW_HW_COUNTERS,
 
+       /* Radar pattern generator */
+       CARL9170FW_RADAR_PATTERN_GENERATOR,
+
        /* KEEP LAST */
        __CARL9170FW_FEATURE_NUM
 };
@@ -89,6 +92,7 @@ enum carl9170fw_feature_list {
 #define CHK_MAGIC      "CHK\0"
 #define TXSQ_MAGIC     "TXSQ"
 #define WOL_MAGIC      "WOL\0"
+#define RADAR_MAGIC    "RDR\0"
 #define LAST_MAGIC     "LAST"
 
 #define CARL9170FW_SET_DAY(d) (((d) - 1) % 31)
@@ -173,6 +177,25 @@ struct carl9170fw_dbg_desc {
 #define CARL9170FW_DBG_DESC_SIZE                       \
        (sizeof(struct carl9170fw_dbg_desc))
 
+#define CARL9170FW_RADAR_MAP_NAME_LEN          15
+struct carl9170fw_radar_map_entry {
+       u8 index;
+       char name[CARL9170FW_RADAR_MAP_NAME_LEN];
+} __packed;
+
+#define CARL9170FW_RADAR_DESC_MIN_VER                  1
+#define CARL9170FW_RADAR_DESC_CUR_VER                  1
+struct carl9170fw_radar_desc {
+       struct carl9170fw_desc_head head;
+
+       __le32 soft_radar;
+       __le32 num_radars;
+       struct carl9170fw_radar_map_entry radars[0];
+       /* Put your debugging definitions here */
+} __packed;
+#define CARL9170FW_RADAR_DESC_SIZE                     \
+       (sizeof(struct carl9170fw_radar_desc))
+
 #define CARL9170FW_CHK_DESC_MIN_VER                    1
 #define CARL9170FW_CHK_DESC_CUR_VER                    2
 struct carl9170fw_chk_desc {
index 9fa241e2a673e5cea63745f2f6226d1d32f66ef5..b6174e1b60b930e300822e38e3088b063b20da7c 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __CARL9170_SHARED_VERSION_H
 #define __CARL9170_SHARED_VERSION_H
 #define CARL9170FW_VERSION_YEAR 12
-#define CARL9170FW_VERSION_MONTH 3
-#define CARL9170FW_VERSION_DAY 14
-#define CARL9170FW_VERSION_GIT "1.9.5"
+#define CARL9170FW_VERSION_MONTH 4
+#define CARL9170FW_VERSION_DAY 22
+#define CARL9170FW_VERSION_GIT "1.9.5-2-g2bd700b"
 #endif /* __CARL9170_SHARED_VERSION_H */
index 6b998910e87399642ad5adf85086b1e48dc55da7..8fec132bedc33dfda6f2a6e9e6919564951b2879 100644 (file)
@@ -1,6 +1,7 @@
 src/checksum
 src/fwinfo
 src/miniboot
+src/fwprepare
 src/eeprom_fix
 src/wol
 carlu/carlu
index 3cd31fdca2639c714b62683ba4d258262363da6c..4605aa4b8ba93f92e567fb4365d7f9708488faed 100644 (file)
@@ -4,7 +4,7 @@ project(tools)
 
 add_custom_target(wol ALL COMMAND gcc wol.c -o wol)
 
-set(tools fwinfo miniboot checksum eeprom_fix)
+set(tools fwinfo miniboot checksum fwprepare eeprom_fix)
 
 foreach(tool ${tools})
        add_executable( ${tool} ${tool}.c )
index 995e3ed11becdd2d274b33b2d0323cce71201680..0f674f72075c5ed7c2a953163a6dd3d6fa09c257 100644 (file)
@@ -68,6 +68,7 @@ static const struct feature_list known_otus_features_v1[] = {
        CHECK_FOR_FEATURE(CARL9170FW_WOL),
        CHECK_FOR_FEATURE(CARL9170FW_FIXED_5GHZ_PSM),
        CHECK_FOR_FEATURE(CARL9170FW_HW_COUNTERS),
+       CHECK_FOR_FEATURE(CARL9170FW_RADAR_PATTERN_GENERATOR),
 };
 
 static void check_feature_list(const struct carl9170fw_desc_head *head,
@@ -206,6 +207,25 @@ static void show_chk_desc(const struct carl9170fw_desc_head *head,
                le32_to_cpu(chk->fw_crc32));
 }
 
+static void show_radar_desc(const struct carl9170fw_desc_head *head,
+                         struct carlfw *fw __unused)
+{
+       const struct carl9170fw_radar_desc *radar = (const void *) head;
+       const struct carl9170fw_radar_map_entry *map = radar->radars;
+       int map_entries = (head->length - sizeof(*radar)) / sizeof(*map);
+       int i;
+
+       fprintf(stdout, "\tRadar index register: %08x\n",
+               le32_to_cpu(radar->soft_radar));
+       fprintf(stdout, "\tNumber of supported radar patterns: %08x\n",
+               le32_to_cpu(radar->num_radars));
+
+       for (i = 0; i < map_entries; i++) {
+               fprintf(stdout, "\t\tindex:0x%x, description:%s\n",
+                       map[i].index, map[i].name);
+       }
+}
+
 static void show_last_desc(const struct carl9170fw_desc_head *head,
                           struct carlfw *fw __unused)
 
@@ -236,6 +256,7 @@ static const struct {
        ADD_HANDLER(FIX, show_fix_desc),
        ADD_HANDLER(CHK, show_chk_desc),
        ADD_HANDLER(WOL, show_wol_desc),
+       ADD_HANDLER(RADAR, show_radar_desc),
        ADD_HANDLER(LAST, show_last_desc),
 };
 
diff --git a/tools/src/fwprepare.c b/tools/src/fwprepare.c
new file mode 100644 (file)
index 0000000..891dad2
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2012 Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <error.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "../../carlfw/include/radar.h"
+#include "carlfw.h"
+
+#include "compiler.h"
+
+static void checksum_help(void)
+{
+       fprintf(stderr, "Usage:\n");
+       fprintf(stderr, "\tfwprepare FW-FILE\n");
+
+       fprintf(stderr, "\nDescription:\n");
+       fprintf(stderr, "\tThis simple utility prepares the firmware "
+                       "for release.\n");
+
+       fprintf(stderr, "\nParameteres:\n");
+       fprintf(stderr, "\t 'FW-FILE'   = firmware name\n");
+       fprintf(stderr, "\n");
+}
+
+static int add_radars(struct carlfw *fw) {
+       const struct carl9170fw_otus_desc *otus_desc = NULL;
+       struct carl9170fw_radar_desc *radar_desc = NULL;
+       int radars_to_add;
+
+       otus_desc = carlfw_find_desc(fw, (uint8_t *) OTUS_MAGIC,
+                                    sizeof(*otus_desc),
+                                    CARL9170FW_OTUS_DESC_CUR_VER);
+       if (!otus_desc) {
+               fprintf(stderr, "No OTUS descriptor found\n");
+               return -1;
+       }
+
+       if (!carl9170fw_supports(otus_desc->feature_set, CARL9170FW_RADAR_PATTERN_GENERATOR)) {
+               return 0;
+       }
+
+       radar_desc = carlfw_find_desc(fw, (uint8_t *) RADAR_MAGIC,
+                                     sizeof(*radar_desc),
+                                     CARL9170FW_RADAR_DESC_CUR_VER);
+
+       if (!radar_desc) {
+               fprintf(stderr, "Firmware has radar pattern feature set, but "
+                       "can't find a valid radar descriptor\n");
+       }
+
+       radars_to_add = radar_desc->num_radars -
+                ((radar_desc->head.length - sizeof(*radar_desc)) /
+                sizeof(struct carl9170fw_radar_map_entry));
+       if (radars_to_add == 0) {
+               /* been there, done that */
+               return 0;
+       }
+
+       if (radars_to_add == __CARL9170FW_NUM_RADARS) {
+               struct carl9170fw_radar_desc *tmp;
+               unsigned int len, map_len;
+
+               map_len = sizeof(struct carl9170fw_radar_map_entry) * radars_to_add;
+               len = sizeof(*tmp) + map_len;
+               tmp = malloc(len);
+               if (!tmp)
+                       return -ENOMEM;
+
+               radar_desc = carlfw_desc_mod_len(fw, &radar_desc->head, map_len);
+               if (IS_ERR_OR_NULL(radar_desc))
+                       return (int) PTR_ERR(radar_desc);
+
+               memcpy(&radar_desc->radars, radar_names, map_len);
+               return 0;
+       } else {
+               fprintf(stderr, "don't know what you did, but congrats you broke it!");
+               return -EINVAL;
+       }
+}
+
+static int add_checksums(struct carlfw __unused *fw)
+{
+       /*
+        * No magic here, The checksum descriptor is added/update
+        * automatically in a subroutine of carlfw_store().
+        */
+       return 0;
+}
+
+int main(int argc, char *args[])
+{
+       struct carlfw *fw = NULL;
+       int err = 0;
+
+       if (argc != 2) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       fw = carlfw_load(args[1]);
+       if (IS_ERR_OR_NULL(fw)) {
+               err = PTR_ERR(fw);
+               fprintf(stderr, "Failed to open file \"%s\" (%d).\n",
+                       args[1], err);
+               goto out;
+       }
+
+       err = add_radars(fw);
+       if (err)
+               goto out;
+
+       err = add_checksums(fw);
+       if (err)
+               goto out;
+
+       err = carlfw_store(fw);
+       if (err) {
+               fprintf(stderr, "Failed to apply checksum (%d).\n", err);
+               goto out;
+       }
+
+out:
+       switch (err) {
+       case 0:
+               fprintf(stdout, "firmware was prepared successfully.\n");
+               break;
+       case -EINVAL:
+               checksum_help();
+               break;
+       default:
+               break;
+       }
+
+       carlfw_release(fw);
+       return err ? EXIT_FAILURE : EXIT_SUCCESS;
+}