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>
Sun, 5 Jun 2016 21:53:46 +0000 (23:53 +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. Part of the patterns have been
tested by Ben Greear and shown to work. YMMV!

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

index a360065b8b4a467c9c051f75d1ce98b57a6e1677..c8e9f9c70b53078448a1b6a4fe07856b4cea66ae 100755 (executable)
@@ -29,6 +29,9 @@ case "$1" in
 
                echo -n "Installing firmware..."
                if [ "$CONFIG_CARL9170FW_BUILD_TOOLS" = "y" ]; then
+                       echo -n "Prepare firmware image..."
+                       tools/src/fwprepare carlfw/carl9170.fw
+
                        if [ "$CONFIG_CARL9170FW_BUILD_MINIBOOT" = "y" ]; then
                                echo -n "Apply miniboot..."
                                # also adds checksum
index 02c80bf138badd45cb9611e1eddb5b4f5a806869..1ca09610374bf7632bb04671d07e0976db0c68d1 100644 (file)
@@ -17,7 +17,7 @@ set(CARLFW_CFLAGS "${CARLFW_CFLAGS_DEF} ${CARLFW_CFLAGS_EXTRA} ${CARLFW_CFLAGS_A
 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)
 
index 46eb1bb87399f5ba4250d43d71a5d05daa9412fe..8583903665b5392a7d1d0f120a87d2867f6c5310 100644 (file)
@@ -85,6 +85,16 @@ config CARL9170FW_EXPERIMENTAL
        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"
index 6e8a3e16b722c972da05af1ff6e5109cc4ea7b52..d17b8963a4f01a24d7f474310146940e21ca28d3 100644 (file)
@@ -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 {
index de6d9d520b41a32253d888b42fec4b9191b05a71..ce9ae8fc9fe11472b9b97b4d5e27f12e32334b08 100644 (file)
@@ -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 --git a/carlfw/include/pattern_generator.h b/carlfw/include/pattern_generator.h
new file mode 100644 (file)
index 0000000..98e9c90
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Pattern Generator definitions
+ *
+ * Copyright 2012, 2013 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_PATTERN_GENERATOR_H
+#define __CARL9170FW_PATTERN_GENERATOR_H
+
+#include "generated/autoconf.h"
+#include "types.h"
+#include "compiler.h"
+#include "fwdesc.h"
+#include "pattern.h"
+
+#if defined(CONFIG_CARL9170FW_PATTERN_GENERATOR)
+void pattern_generator(void);
+
+#else
+static inline void pattern_generator(void)
+{
+}
+
+#endif /* CONFIG_CARL9170FW_PATTERN_GENERATOR */
+
+#endif /* __CARL9170FW_PATTERN_GENERATOR_H */
index 43d6f2ece5afbf8a79360caf28a9fd9a42c63ca6..f15e6fbdca33ef10ba596b96d8aad86383a82cf9 100644 (file)
@@ -49,6 +49,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 7ba152e7bea8004b911bf704c17d5c274da0d576..85a03fed99234b4275e7c661fc14148c97c80829 100644 (file)
@@ -20,6 +20,7 @@
  */
 #include "carl9170.h"
 #include "fwdsc.h"
+#include "pattern_generator.h"
 
 #define FILL(small, big, more...)                                      \
        .small = {                                                      \
@@ -64,6 +65,9 @@ const struct carl9170_firmware_descriptor __in_section(fwdsc) __visible carl9170
 #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),
@@ -89,7 +93,6 @@ const struct carl9170_firmware_descriptor __in_section(fwdsc) __visible carl9170
        ),
 #endif /* CONFIG_CARL9170FW_WOL */
 
-
        FILL(motd, MOTD,
             .fw_year_month_day = cpu_to_le32(
                        CARL9170FW_SET_DAY(CARL9170FW_VERSION_DAY) +
@@ -98,6 +101,14 @@ const struct carl9170_firmware_descriptor __in_section(fwdsc) __visible carl9170
             .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),
index 8c13bf8988c87bb7f748c5b6240bedf4d119716d..8cb5fc13986a9d2af967d107faba3912453b51c1 100644 (file)
@@ -30,6 +30,7 @@
 #include "wl.h"
 #include "rf.h"
 #include "usb.h"
+#include "pattern_generator.h"
 
 #define AR9170_WATCH_DOG_TIMER            0x100
 
@@ -124,6 +125,8 @@ static void __noreturn main_loop(void)
                handle_timer();
 
                tally_update();
+
+               pattern_generator();
        }
 }
 
diff --git a/carlfw/src/pattern_generator.c b/carlfw/src/pattern_generator.c
new file mode 100644 (file)
index 0000000..7cadb12
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * pattern generator
+ *
+ * Copyright 2013      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.
+ */
+
+#include "carl9170.h"
+#include "pattern_generator.h"
+#include "fwdsc.h"
+#include "timer.h"
+
+#if defined(CONFIG_CARL9170FW_PATTERN_GENERATOR)
+
+void pattern_generator(void)
+{
+       if (fw.phy.state == CARL9170_PHY_ON) {
+               if (likely(fw.wlan.soft_pattern == NO_PATTERN ||
+                   fw.wlan.soft_pattern >= __CARL9170FW_NUM_PATTERNS))
+                       return;
+
+               const struct pattern_info *pattern = &patterns[fw.wlan.soft_pattern];
+               if (pattern->pulses >= fw.wlan.pattern_index) {
+                       fw.wlan.pattern_index = 0;
+               }
+
+               if (pattern->pulses > fw.wlan.pattern_index) {
+                       const struct pattern_pulse_info *ppi = &pattern->pattern[fw.wlan.pattern_index];
+                       if (is_after_usecs(fw.wlan.pattern_last, ppi->pulse_interval)) {
+                               fw.wlan.pattern_last = get_clock_counter();
+                               set(0x1C3BC0, ppi->pulse_pattern);
+                               set(0x1C3BBC, ppi->pulse_mode);
+                               udelay(ppi->pulse_width);
+                               set(0x1C3BBC, 0);
+                               set(0x1C3BC0, 0);
+                               fw.wlan.pattern_index++;
+                       }
+               }
+       }
+}
+
+#endif /* CONFIG_CONFIG_CARL9170FW_RADAR */
diff --git a/include/pattern.h b/include/pattern.h
new file mode 100644 (file)
index 0000000..9f06923
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * carl9170 firmware - used by the ar9170 wireless device
+ *
+ * Pattern 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_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 = 0xaa55,
+               .pulse_mode    = 0x17f01,
+       },
+};
+
+static const struct pattern_pulse_info pattern_TEN_KHZ[] = {
+       {
+               .pulse_width = 1,
+               .pulse_interval = 100,
+               .pulse_pattern = 0xaa55,
+               .pulse_mode    = 0x17f01,
+       },
+};
+
+static const struct pattern_pulse_info pattern_ONE_TWO_KHZ[] = {
+       {
+               .pulse_width = 1,
+               .pulse_interval = 1000,
+               .pulse_pattern = 0xaa55,
+               .pulse_mode    = 0x17f01,
+       },
+
+       {
+               .pulse_width = 10,
+               .pulse_interval = 500,
+               .pulse_pattern = 0xaa55,
+               .pulse_mode    = 0x17f01,
+       },
+};
+
+/*
+ * Data taken from:
+ * <http://linuxwireless.org/en/developers/DFS>
+ */
+
+/* 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 = 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 = 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 = 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 */
index da05a4ecb73f2593050b4d9cd72036d0b5a2cb70..d542b01eb0409d8677c212e2f13838c9394ab4c9 100644 (file)
@@ -98,6 +98,7 @@ enum carl9170fw_feature_list {
 #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)
@@ -182,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 {
index caffdb05281c87585cf0c0ca941307209b3e99c3..efb38dd499f2110870e4c116c3aee633e85671fe 100644 (file)
@@ -2,5 +2,6 @@ src/checksum
 src/fwinfo
 src/fwprepare
 src/miniboot
+src/fwprepare
 src/wol
 carlu/carlu
index d141521e9652ea231984c12637f34d1fbf43acdf..19ec5082a2cb58e4d397b5cd25c64ee478a1c8c9 100644 (file)
@@ -4,7 +4,7 @@ project(tools)
 
 add_custom_target(wol ALL COMMAND gcc wol.c -o wol)
 
-set(tools fwinfo miniboot checksum)
+set(tools fwinfo miniboot checksum fwprepare)
 
 foreach(tool ${tools})
        add_executable( ${tool} ${tool}.c )
index 4270564a43da0370517be2846bd99e6db92e4de0..cd5466cb3e958ad518366fd55d7a3cfd6d577c35 100644 (file)
@@ -209,6 +209,25 @@ static void show_chk_desc(const struct carl9170fw_desc_head *head,
                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)
 
@@ -239,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 --git a/tools/src/fwprepare.c b/tools/src/fwprepare.c
new file mode 100644 (file)
index 0000000..67a00db
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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.h"
+
+#include "compiler.h"
+#include "pattern.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;
+}