From: Christian Lamparter Date: Tue, 25 Aug 2015 21:21:21 +0000 (+0200) Subject: Merge branch 'master' into radar X-Git-Url: https://jxself.org/git/?a=commitdiff_plain;ds=sidebyside;h=ed51f317816e1fe0dc13ef356d9a9a07056d06b0;hp=-c;p=carl9170fw.git Merge branch 'master' into radar --- ed51f317816e1fe0dc13ef356d9a9a07056d06b0 diff --combined carlfw/CMakeLists.txt index cacccd9,02c80bf..1ca0961 --- a/carlfw/CMakeLists.txt +++ b/carlfw/CMakeLists.txt @@@ -17,7 -17,7 +17,7 @@@ set(CARLFW_CFLAGS "${CARLFW_CFLAGS_DEF include_directories (../include/linux ../include/shared ../include include) set(carl9170_main_src src/main.c src/wlan.c src/wlanrx.c src/wlantx.c - src/fw.c src/gpio.c src/timer.c + src/fw.c src/gpio.c src/timer.c src/pattern_generator.c src/uart.c src/dma.c src/hostif.c src/reboot.S src/printf.c src/rf.c src/cam.c src/wol.c) @@@ -42,21 -42,21 +42,21 @@@ set_target_properties(carl9170.elf PROP add_custom_target(firmware ALL) add_custom_command( - SOURCE carl9170.elf + DEPENDS carl9170.elf COMMAND ${OBJCOPY} ARGS --strip-unneeded -O binary -R .sram -R .eeprom -R .fwdsc carl9170.elf carl9170.bin TARGET firmware OUTPUTS carl9170.bin) add_custom_command( - SOURCE carl9170.elf + DEPENDS carl9170.elf COMMAND ${OBJCOPY} ARGS --strip-unneeded -O binary -j .fwdsc carl9170.elf carl9170.dsc TARGET firmware OUTPUTS carl9170.dsc) add_custom_command( - SOURCE firmware + DEPENDS firmware TARGET firmware COMMAND cat ARGS "carl9170.bin" "carl9170.dsc" > "carl9170.fw" diff --combined carlfw/Kconfig index 1612973,46eb1bb..8583903 --- a/carlfw/Kconfig +++ b/carlfw/Kconfig @@@ -85,16 -85,6 +85,16 @@@ config CARL9170FW_EXPERIMENTA def_bool y prompt "Experimental Features" +config CARL9170FW_PATTERN_GENERATOR + def_bool n + prompt "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" @@@ -148,9 -138,19 +148,19 @@@ config CARL9170FW_NOISY_MAC_RESE prompt "Notify MAC RESET events" depends on CARL9170FW_FW_MAC_RESET + config CARL9170FW_80MHZ_CLOCK + def_bool n + prompt "Allow 80/88MHz clock for HT40" + depends on CARL9170FW_EXPERIMENTAL + ---help--- + The SoC can run up to 80/88MHz in HT40 mode. This improves + throughput and timing accuracy over the 40/44MHz clock. + However some devices don't have heat shields and they with + this option enabled, they become unstable under load. + config CARL9170FW_BROKEN_FEATURES def_bool n - prompt "Broken Featurs" + prompt "Broken Features" config CARL9170FW_DEBUG def_bool n diff --combined carlfw/include/carl9170.h index d12bb98,6e8a3e1..d17b896 --- a/carlfw/include/carl9170.h +++ b/carlfw/include/carl9170.h @@@ -16,8 -16,7 +16,7 @@@ * 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. + * with this program; If not, see . */ #ifndef __CARL9170FW_CARL9170_H @@@ -140,12 -139,6 +139,12 @@@ struct firmware_context_struct queued_ba; unsigned int queued_bar; + +#if defined(CONFIG_CARL9170FW_PATTERN_GENERATOR) + unsigned int soft_pattern, + pattern_last, + pattern_index; +#endif /* CONFIG_CARL9170FW_PATTERN_GENERATOR */ } wlan; struct { diff --combined carlfw/include/fwdsc.h index 2e1d1dd,de6d9d5..ce9ae8f --- a/carlfw/include/fwdsc.h +++ b/carlfw/include/fwdsc.h @@@ -16,8 -16,7 +16,7 @@@ * 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. + * with this program; If not, see . */ #ifndef __CARL9170FW_FWDSC_H @@@ -35,9 -34,6 +34,9 @@@ struct carl9170_firmware_descriptor struct carl9170fw_wol_desc wol; #endif /* CONFIG_CARL9170FW_WOL */ struct carl9170fw_motd_desc motd; +#if defined(CONFIG_CARL9170FW_PATTERN_GENERATOR) + struct carl9170fw_pattern_desc pattern; +#endif /* CONFIG_CARL9170FW_PATTERN_GENERATOR */ struct carl9170fw_dbg_desc dbg; struct carl9170fw_last_desc last; } __packed; diff --combined carlfw/include/timer.h index d9eee7a,43d6f2e..f15e6fb --- a/carlfw/include/timer.h +++ b/carlfw/include/timer.h @@@ -19,8 -19,7 +19,7 @@@ * 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. + * with this program; If not, see . */ #ifndef __CARL9170FW_TIMER_H @@@ -50,11 -49,6 +49,11 @@@ static inline __inline bool is_after_ms 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 diff --combined carlfw/src/fw.c index a98b735,7ba152e..85a03fe --- a/carlfw/src/fw.c +++ b/carlfw/src/fw.c @@@ -16,12 -16,10 +16,11 @@@ * 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. + * with this program; If not, see . */ #include "carl9170.h" #include "fwdsc.h" +#include "pattern_generator.h" #define FILL(small, big, more...) \ .small = { \ @@@ -32,7 -30,7 +31,7 @@@ more \ } - const struct carl9170_firmware_descriptor __section(fwdsc) __visible carl9170fw_desc = { + const struct carl9170_firmware_descriptor __in_section(fwdsc) __visible carl9170fw_desc = { FILL(otus, OTUS, .feature_set = cpu_to_le32(BIT(CARL9170FW_DUMMY_FEATURE) | BIT(CARL9170FW_USB_RESP_EP2) | @@@ -41,6 -39,7 +40,7 @@@ BIT(CARL9170FW_HW_COUNTERS) | BIT(CARL9170FW_RX_BA_FILTER) | BIT(CARL9170FW_USB_INIT_FIRMWARE) | + BIT(CARL9170FW_HAS_WREGB_CMD) | #ifdef CONFIG_CARL9170FW_USB_UP_STREAM BIT(CARL9170FW_USB_UP_STREAM) | #endif /* CONFIG_CARL9170FW_USB_UP_STREAM */ @@@ -65,9 -64,6 +65,9 @@@ #ifdef CONFIG_CARL9170FW_WOL BIT(CARL9170FW_WOL) | #endif /* CONFIG_CARL9170FW_WOL */ +#if defined(CONFIG_CARL9170FW_PATTERN_GENERATOR) + BIT(CARL9170FW_PATTERN_GENERATOR) | +#endif /* CONFIG_CARL9170FW_PATTERN_GENERATOR */ (0)), .miniboot_size = cpu_to_le16(0), @@@ -93,6 -89,7 +93,6 @@@ ), #endif /* CONFIG_CARL9170FW_WOL */ - FILL(motd, MOTD, .fw_year_month_day = cpu_to_le32( CARL9170FW_SET_DAY(CARL9170FW_VERSION_DAY) + @@@ -101,14 -98,6 +101,14 @@@ .desc = "Community AR9170 Linux", .release = CARL9170FW_VERSION_GIT), +#if defined(CONFIG_CARL9170FW_PATTERN_GENERATOR) + FILL(pattern, PATTERN, + .soft_pattern = cpu_to_le32(&fw.wlan.soft_pattern), + .num_patterns = __CARL9170FW_NUM_PATTERNS, + .patterns = { /* 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), diff --combined carlfw/src/main.c index b24d905,8c13bf8..8cb5fc1 --- a/carlfw/src/main.c +++ b/carlfw/src/main.c @@@ -19,8 -19,7 +19,7 @@@ * 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. + * with this program; If not, see . */ #include "carl9170.h" @@@ -31,7 -30,6 +30,7 @@@ #include "wl.h" #include "rf.h" #include "usb.h" +#include "pattern_generator.h" #define AR9170_WATCH_DOG_TIMER 0x100 @@@ -126,8 -124,6 +125,8 @@@ static void __noreturn main_loop(void handle_timer(); tally_update(); + + pattern_generator(); } } @@@ -141,7 -137,7 +140,7 @@@ * we put _start() there with the linker script carl9170.lds. */ - void __section(boot) __noreturn __visible start(void) + void __in_section(boot) __noreturn __visible start(void) { clock_set(AHB_40MHZ_OSC, true); diff --combined include/pattern.h index ba9e88f,0000000..9f06923 mode 100644,000000..100644 --- a/include/pattern.h +++ b/include/pattern.h @@@ -1,156 -1,0 +1,156 @@@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Pattern pulse definitions + * + * Copyright 2012 Christian Lamparter + * + * 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_PATTERN_H +#define __CARL9170FW_PATTERN_H + +#include "types.h" +#include "compiler.h" +#include "fwdesc.h" + +enum PATTERN_TYPE { + NO_PATTERN = 0, + ONE_KHZ, + TEN_KHZ, + + ONE_TWO_KHZ, + + FCC1, + FCC4, + + ETSIFIXED, + + /* keep last */ + __CARL9170FW_NUM_PATTERNS +}; + +struct pattern_pulse_info { + unsigned int pulse_width; + unsigned int pulse_interval; + uint32_t pulse_pattern; + uint32_t pulse_mode; +}; + +struct pattern_info { + unsigned int pulses; + const struct pattern_pulse_info *pattern; +}; + +static const struct pattern_pulse_info pattern_NO_PATTERN[0] = { }; +static const struct pattern_pulse_info pattern_ONE_KHZ[] = { + { + .pulse_width = 1, + .pulse_interval = 1000, - .pulse_pattern = 0xa7438080, - .pulse_mode = 0x5f01, ++ .pulse_pattern = 0xaa55, ++ .pulse_mode = 0x17f01, + }, +}; + +static const struct pattern_pulse_info pattern_TEN_KHZ[] = { + { + .pulse_width = 1, + .pulse_interval = 100, - .pulse_pattern = 0x436f0001, - .pulse_mode = 0x5f01, ++ .pulse_pattern = 0xaa55, ++ .pulse_mode = 0x17f01, + }, +}; + +static const struct pattern_pulse_info pattern_ONE_TWO_KHZ[] = { + { + .pulse_width = 1, + .pulse_interval = 1000, - .pulse_pattern = 0xa7438080, - .pulse_mode = 0x5f01, ++ .pulse_pattern = 0xaa55, ++ .pulse_mode = 0x17f01, + }, + + { + .pulse_width = 10, + .pulse_interval = 500, - .pulse_pattern = 0xa7431001, - .pulse_mode = 0x5f01, ++ .pulse_pattern = 0xaa55, ++ .pulse_mode = 0x17f01, + }, +}; + +/* + * Data taken from: + * + */ + +/* FCC Test Signal 1 - 1us pulse, 1428 us interval */ +static const struct pattern_pulse_info pattern_FCC1[] = { + { + .pulse_width = 1, + .pulse_interval = 1428, - .pulse_pattern = 0xa7438080, - .pulse_mode = 0x5f01, ++ .pulse_pattern = 0xaa55, ++ .pulse_mode = 0x17f01, + }, +}; + +/* FCC Test Signal 4 - 11-20us pulse, 200-500 us interval */ +static const struct pattern_pulse_info pattern_FCC4[] = { + { + .pulse_width = 11, + .pulse_interval = 200, - .pulse_pattern = 0xf3128008, - .pulse_mode = 0x5f01, ++ .pulse_pattern = 0xaa55, ++ .pulse_mode = 0x7f01, + }, +}; + +/* ETSI Test Signal 1 (Fixed) - 1us Pulse, 750 us interval */ +static const struct pattern_pulse_info pattern_ETSIFIXED[] = { + { + .pulse_width = 1, + .pulse_interval = 750, - .pulse_pattern = 0x8a5f8080, - .pulse_mode = 0x5f01, ++ .pulse_pattern = 0xaa55, ++ .pulse_mode = 0x7f01, + }, +}; + + +#define ADD_RADAR(name) [name] = { .pulses = ARRAY_SIZE(pattern_## name), .pattern = pattern_## name } + +static const struct pattern_info patterns[__CARL9170FW_NUM_PATTERNS] = { + ADD_RADAR(NO_PATTERN), + 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_pattern_map_entry pattern_names[__CARL9170FW_NUM_PATTERNS] = { + MAP_ENTRY(NO_PATTERN), + MAP_ENTRY(ONE_KHZ), + MAP_ENTRY(TEN_KHZ), + MAP_ENTRY(ONE_TWO_KHZ), + + MAP_ENTRY(FCC1), + MAP_ENTRY(FCC4), + + MAP_ENTRY(ETSIFIXED), +}; + +#endif /* __CARL9170FW_PATTERN_H */ diff --combined include/shared/fwdesc.h index 424a4e7,a88d07b..10327a7 --- a/include/shared/fwdesc.h +++ b/include/shared/fwdesc.h @@@ -78,12 -78,12 +78,15 @@@ enum carl9170fw_feature_list /* HW (ANI, CCA, MIB) tally counters */ CARL9170FW_HW_COUNTERS, + /* Pattern generator */ + CARL9170FW_PATTERN_GENERATOR, + /* Firmware will pass BA when BARs are queued */ CARL9170FW_RX_BA_FILTER, + /* Firmware has support to write a byte at a time */ + CARL9170FW_HAS_WREGB_CMD, + /* KEEP LAST */ __CARL9170FW_FEATURE_NUM }; @@@ -95,7 -95,6 +98,7 @@@ #define CHK_MAGIC "CHK\0" #define TXSQ_MAGIC "TXSQ" #define WOL_MAGIC "WOL\0" +#define PATTERN_MAGIC "RDR\0" #define LAST_MAGIC "LAST" #define CARL9170FW_SET_DAY(d) (((d) - 1) % 31) @@@ -180,24 -179,6 +183,24 @@@ struct carl9170fw_dbg_desc #define CARL9170FW_DBG_DESC_SIZE \ (sizeof(struct carl9170fw_dbg_desc)) +#define CARL9170FW_PATTERN_MAP_NAME_LEN 15 +struct carl9170fw_pattern_map_entry { + u8 index; + char name[CARL9170FW_PATTERN_MAP_NAME_LEN]; +} __packed; + +#define CARL9170FW_PATTERN_DESC_MIN_VER 1 +#define CARL9170FW_PATTERN_DESC_CUR_VER 1 +struct carl9170fw_pattern_desc { + struct carl9170fw_desc_head head; + + __le32 soft_pattern; + __le32 num_patterns; + struct carl9170fw_pattern_map_entry patterns[0]; +} __packed; +#define CARL9170FW_PATTERN_DESC_SIZE \ + (sizeof(struct carl9170fw_pattern_desc)) + #define CARL9170FW_CHK_DESC_MIN_VER 1 #define CARL9170FW_CHK_DESC_CUR_VER 2 struct carl9170fw_chk_desc { diff --combined tools/src/fwinfo.c index 3547041,229f0e5..cd5466c --- a/tools/src/fwinfo.c +++ b/tools/src/fwinfo.c @@@ -69,7 -69,7 +69,8 @@@ static const struct feature_list known_ CHECK_FOR_FEATURE(CARL9170FW_FIXED_5GHZ_PSM), CHECK_FOR_FEATURE(CARL9170FW_HW_COUNTERS), CHECK_FOR_FEATURE(CARL9170FW_RX_BA_FILTER), + CHECK_FOR_FEATURE(CARL9170FW_HAS_WREGB_CMD), + CHECK_FOR_FEATURE(CARL9170FW_PATTERN_GENERATOR), }; static void check_feature_list(const struct carl9170fw_desc_head *head, @@@ -208,25 -208,6 +209,25 @@@ static void show_chk_desc(const struct le32_to_cpu(chk->fw_crc32)); } +static void show_pattern_desc(const struct carl9170fw_desc_head *head, + struct carlfw *fw __unused) +{ + const struct carl9170fw_pattern_desc *pattern = (const void *) head; + const struct carl9170fw_pattern_map_entry *map = pattern->patterns; + int map_entries = (head->length - sizeof(*pattern)) / sizeof(*map); + int i; + + fprintf(stdout, "\tPattern index register: %08x\n", + le32_to_cpu(pattern->soft_pattern)); + fprintf(stdout, "\tNumber of supported patterns: %08x\n", + le32_to_cpu(pattern->num_patterns)); + + 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) @@@ -257,7 -238,6 +258,7 @@@ static const struct ADD_HANDLER(FIX, show_fix_desc), ADD_HANDLER(CHK, show_chk_desc), ADD_HANDLER(WOL, show_wol_desc), + ADD_HANDLER(PATTERN, show_pattern_desc), ADD_HANDLER(LAST, show_last_desc), }; diff --combined tools/src/fwprepare.c index 7bf3542,0000000..f022874 mode 100644,000000..100644 --- a/tools/src/fwprepare.c +++ b/tools/src/fwprepare.c @@@ -1,158 -1,0 +1,158 @@@ +/* + * Copyright 2012 Christian Lamparter + * + * 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 +#include +#include +#include +#include +#include +#include +#include ++#include + - #include "pattern.h" +#include "carlfw.h" ++#include "pattern.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_patterns(struct carlfw *fw) { + const struct carl9170fw_otus_desc *otus_desc = NULL; + struct carl9170fw_pattern_desc *pattern_desc = NULL; + int 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_PATTERN_GENERATOR)) { + return 0; + } + + pattern_desc = carlfw_find_desc(fw, (uint8_t *) PATTERN_MAGIC, + sizeof(*pattern_desc), + CARL9170FW_PATTERN_DESC_CUR_VER); + + if (!pattern_desc) { + fprintf(stderr, "Firmware has the pattern generator feature set, but " + "can't find a valid pattern descriptor\n"); + return 0; + } + + to_add = pattern_desc->num_patterns - + ((pattern_desc->head.length - sizeof(*pattern_desc)) / + sizeof(struct carl9170fw_pattern_map_entry)); + if (to_add == 0) { + /* been there, done that */ + return 0; + } + + if (to_add == __CARL9170FW_NUM_PATTERNS) { + struct carl9170fw_pattern_desc *tmp; + unsigned int len, map_len; + + map_len = sizeof(struct carl9170fw_pattern_map_entry) * to_add; + len = sizeof(*tmp) + map_len; + tmp = malloc(len); + if (!tmp) + return -ENOMEM; + + pattern_desc = carlfw_desc_mod_len(fw, &pattern_desc->head, map_len); + if (IS_ERR_OR_NULL(pattern_desc)) + return (int) PTR_ERR(pattern_desc); + + memcpy(&pattern_desc->patterns, pattern_names, map_len); + return 0; + } else { + fprintf(stderr, "No idea, what you just 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_patterns(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; +}