summaryrefslogtreecommitdiff
path: root/libre/linux-libre-firmware
diff options
context:
space:
mode:
Diffstat (limited to 'libre/linux-libre-firmware')
-rw-r--r--libre/linux-libre-firmware/0001-Add-offline-files-and-a-toolchain-option-in-Makefile.patch236
-rw-r--r--libre/linux-libre-firmware/0001-Update-carl9170-to-latest-upstream.patch4982
-rw-r--r--libre/linux-libre-firmware/0002-Add-firmware-for-the-ATUSB-IEEE-802.15.4-USB-Adapter.patch4893
-rw-r--r--libre/linux-libre-firmware/0003-Update-INSTALL-document.patch44
-rw-r--r--libre/linux-libre-firmware/0004-atusb-Build-updates.patch73
-rw-r--r--libre/linux-libre-firmware/0006-Makefile-Change-spaces-for-atusb-to-tab.patch25
-rw-r--r--libre/linux-libre-firmware/0007-Makefile-Add-atusb-to-all.patch25
-rw-r--r--libre/linux-libre-firmware/0008-Makefile-Set-shell-to-bin-bash.patch25
-rw-r--r--libre/linux-libre-firmware/PKGBUILD73
9 files changed, 10119 insertions, 257 deletions
diff --git a/libre/linux-libre-firmware/0001-Add-offline-files-and-a-toolchain-option-in-Makefile.patch b/libre/linux-libre-firmware/0001-Add-offline-files-and-a-toolchain-option-in-Makefile.patch
deleted file mode 100644
index a5dae8afa..000000000
--- a/libre/linux-libre-firmware/0001-Add-offline-files-and-a-toolchain-option-in-Makefile.patch
+++ /dev/null
@@ -1,236 +0,0 @@
-From 5ceda644200c8c84b9c1ea66f2ee677d374c34cb Mon Sep 17 00:00:00 2001
-From: David P <megver83@parabola.nu>
-Date: Fri, 7 Sep 2018 21:28:40 -0300
-Subject: [PATCH] Add offline files, and a toolchain option in Makefile
-
-The *.offline files are so if you want to build offline, you just have to
-pass toolchains= unset, or toolchains=1 for downloading them (which is the
-default). Read the INSTALL file for more.
-
-Signed-off-by: David P <megver83@parabola.nu>
----
- INSTALL | 20 +++++++
- Makefile | 33 +++++++++--
- ath9k_htc/Makefile.offline | 8 +++
- ath9k_htc/target_firmware/configure.offline | 64 +++++++++++++++++++++
- carl9170fw/extra/sh-elf-linux.cmake.offline | 22 +++++++
- 5 files changed, 143 insertions(+), 4 deletions(-)
- create mode 100644 ath9k_htc/Makefile.offline
- create mode 100755 ath9k_htc/target_firmware/configure.offline
- create mode 100644 carl9170fw/extra/sh-elf-linux.cmake.offline
-
-diff --git a/INSTALL b/INSTALL
-index 74c5cfd..733aab8 100644
---- a/INSTALL
-+++ b/INSTALL
-@@ -6,6 +6,14 @@ also specify targets to make. For example:
-
- Please see the included Makefile for a list of all available targets.
-
-+Some firmwares download toolchains. If you want this, run "make"
-+passing the "toolchains=1" option (this is the default). If, however,
-+you want to build these firmwares using your system's cross-compilers,
-+then run "make" with the "toolchains=" option unset (anything different
-+from "1" works), like this:
-+
-+ make toolchains=
-+
- Once the desired things are built, "make install" will put them into
- the appropriate place, which is /lib/firmware by default although you
- can override this with something like:
-@@ -35,6 +43,18 @@ On GNU/Linux distros that use apt you can install these with:
- apt install binutils-arm-linux-gnueabi binutils-arm-none-eabi bison \
- cmake flex g++ gcc gcc-arm-linux-gnueabi gcc-arm-none-eabi gperf make wget
-
-+And if you pretend to build everything offline, you will also need:
-+
-+ * GNU C cross-compiler for Xtensa:
-+ - xtensa-elf-gcc
-+ - xtensa-elf-ld
-+ - xtensa-elf-objcopy
-+ * GNU C cross-compiler for SuperH:
-+ - sh-elf-gcc (with newlib support)
-+ - sh-elf-ld
-+ - sh-elf-objcopy
-+ * Newlib for SuperH (sh-elf)
-+
- CARL9170 Firmware Configuration
- When building the carl9170 firmware you will be prompted with
- configuration questions.
-diff --git a/Makefile b/Makefile
-index 21d16fb..3805eaf 100644
---- a/Makefile
-+++ b/Makefile
-@@ -16,6 +16,7 @@
- shell=/bin/sh
- prefix=/lib/firmware
- install_program=install
-+toolchains=1
-
- .PHONY: all test clean install a56 as31 aica ath9k_htc_toolchain ath9k_htc av7110 b43-tools carl9170fw-toolchain carl9170fw cis-tools cis dsp56k ihex2fw isci keyspan_pda openfwwf usbdux
-
-@@ -31,10 +32,22 @@ aica:
- cd aica/arm && $(MAKE)
-
- ath9k_htc_toolchain:
-- cd ath9k_htc && $(MAKE) toolchain
-+ if [[ "$(toolchains)" = 1 ]]; then \
-+ cd ath9k_htc && $(MAKE) toolchain; \
-+ else \
-+ for f in $(shell find ath9k_htc -type f -name "*.offline") ; do \
-+ if [ "$(shell find ath9k_htc -type f -name "*.bak")" = "" ]; then \
-+ cp -v $${f%.offline} $${f%.offline}.bak ; \
-+ cp -v $$f $${f%.offline} ; \
-+ fi; \
-+ done; \
-+ fi
-
- ath9k_htc: ath9k_htc_toolchain
-- cd ath9k_htc && $(MAKE) -C target_firmware
-+ cd ath9k_htc && $(MAKE) -C target_firmware && \
-+ for f in $(shell find -type f -name "*.bak") ; do \
-+ mv -v ../$$f ../$${f%.bak}; \
-+ done
-
- av7110:
- cd av7110 && $(MAKE)
-@@ -43,10 +56,22 @@ b43-tools:
- cd b43-tools/assembler && $(MAKE)
-
- carl9170fw-toolchain:
-- cd carl9170fw && $(MAKE) -C toolchain
-+ if [[ "$(toolchains)" = 1 ]]; then \
-+ cd carl9170fw && $(MAKE) -C toolchain; \
-+ else \
-+ for f in $(shell find carl9170fw -type f -name "*.offline") ; do \
-+ if [ "$(shell find carl9170fw -type f -name "*.bak")" = "" ]; then \
-+ cp -v $${f%.offline} $${f%.offline}.bak ; \
-+ cp -v $$f $${f%.offline} ; \
-+ fi; \
-+ done; \
-+ fi
-
- carl9170fw: carl9170fw-toolchain
-- cd carl9170fw && ./autogen.sh
-+ cd carl9170fw && ./autogen.sh && \
-+ for f in $(shell find -type f -name "*.bak") ; do \
-+ mv -v ../$$f ../$${f%.bak}; \
-+ done
-
- cis: cis-tools
- cd cis && $(MAKE)
-diff --git a/ath9k_htc/Makefile.offline b/ath9k_htc/Makefile.offline
-new file mode 100644
-index 0000000..958eaaa
---- /dev/null
-+++ b/ath9k_htc/Makefile.offline
-@@ -0,0 +1,8 @@
-+all: firmware
-+clean:
-+ $(MAKE) -C target_firmware clean
-+
-+firmware:
-+ +$(MAKE) -C target_firmware
-+
-+.PHONY: all clean firmware
-diff --git a/ath9k_htc/target_firmware/configure.offline b/ath9k_htc/target_firmware/configure.offline
-new file mode 100755
-index 0000000..6e49093
---- /dev/null
-+++ b/ath9k_htc/target_firmware/configure.offline
-@@ -0,0 +1,64 @@
-+#!/bin/sh
-+##
-+ # Copyright (c) 2013 Qualcomm Atheros, Inc.
-+ #
-+ # All rights reserved.
-+ #
-+ # Redistribution and use in source and binary forms, with or without
-+ # modification, are permitted (subject to the limitations in the
-+ # disclaimer below) provided that the following conditions are met:
-+ #
-+ # * Redistributions of source code must retain the above copyright
-+ # notice, this list of conditions and the following disclaimer.
-+ #
-+ # * Redistributions in binary form must reproduce the above copyright
-+ # notice, this list of conditions and the following disclaimer in the
-+ # documentation and/or other materials provided with the
-+ # distribution.
-+ #
-+ # * Neither the name of Qualcomm Atheros nor the names of its
-+ # contributors may be used to endorse or promote products derived
-+ # from this software without specific prior written permission.
-+ #
-+ # NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
-+ # GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
-+ # HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
-+ # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-+ # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-+ # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-+ # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-+ # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-+ # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ ##
-+
-+TARGET=xtensa-elf
-+
-+[ -z "$CROSS_COMPILE" ] &&
-+ CROSS_COMPILE="$TARGET-"
-+
-+TOOLCHAIN_FILE="$PWD/build/toolchain.cmake"
-+
-+set -e
-+rm -rf build
-+mkdir -p build
-+
-+cat > "$TOOLCHAIN_FILE" <<EOF
-+SET(CMAKE_SYSTEM_NAME Generic)
-+SET(CMAKE_C_COMPILER "${CROSS_COMPILE}gcc")
-+EOF
-+
-+do_cmake() {
-+ cmake -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" "$@"
-+}
-+
-+mkdir -p build/k2 build/magpie
-+cd build/k2
-+do_cmake -DTARGET_K2=ON ../..
-+cd -
-+cd build/magpie
-+do_cmake -DTARGET_MAGPIE=ON ../..
-+cd -
-diff --git a/carl9170fw/extra/sh-elf-linux.cmake.offline b/carl9170fw/extra/sh-elf-linux.cmake.offline
-new file mode 100644
-index 0000000..c0b1e84
---- /dev/null
-+++ b/carl9170fw/extra/sh-elf-linux.cmake.offline
-@@ -0,0 +1,22 @@
-+set(CMAKE_SYSTEM_NAME "Generic")
-+set(CMAKE_SYSTEM_PROCESSOR "sh2")
-+
-+set_property(DIRECTORY PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
-+
-+set(CMAKE_FIND_ROOT_PATH /)
-+
-+set(CMAKE_C_FLAGS "-m2 -ml -Os -ffreestanding -nostartfiles")
-+set(CMAKE_C_LINK_FLAGS "-Wl,-static,-EL,-x,--gc-sections")
-+set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
-+
-+set(OBJCOPY sh-elf-objcopy)
-+set(CMAKE_C_COMPILER "sh-elf-gcc")
-+set(CMAKE_AR sh-elf-ar)
-+set(CMAKE_ASM_COMPILER sh-elf-as)
-+set(CMAKE_ASM-ATT_COMPILER sh-elf-as)
-+set(CMAKE_LINKER sh-elf-ld)
-+set(CMAKE_C_LINK_EXECUTABLE "sh-elf-gcc <OBJECTS> ${CMAKE_C_FLAGS} <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> -o <TARGET>")
-+
-+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
---
-2.18.0
-
diff --git a/libre/linux-libre-firmware/0001-Update-carl9170-to-latest-upstream.patch b/libre/linux-libre-firmware/0001-Update-carl9170-to-latest-upstream.patch
new file mode 100644
index 000000000..e56707e4c
--- /dev/null
+++ b/libre/linux-libre-firmware/0001-Update-carl9170-to-latest-upstream.patch
@@ -0,0 +1,4982 @@
+From c164bf7f87f9081fee7e1a186dd7a87a9a020b9e Mon Sep 17 00:00:00 2001
+From: Jason Self <j@jxself.org>
+Date: Thu, 4 Jul 2019 15:12:33 -0700
+Subject: [PATCH 1/8] Update carl9170 to latest upstream
+
+Based on commit 001384147050b9cd9daadb4d3115cc0f13f5b319
+Dated May 5 2019.
+---
+ WHENCE | 4 +-
+ carl9170fw/carlfw/include/wl.h | 2 +-
+ carl9170fw/carlfw/src/hostif.c | 8 +-
+ carl9170fw/carlfw/src/printf.c | 0
+ carl9170fw/carlfw/src/wlantx.c | 5 +-
+ carl9170fw/config/CMakeLists.txt | 6 +-
+ carl9170fw/config/conf.c | 210 ++++++----
+ carl9170fw/config/confdata.c | 329 +++++++++------
+ carl9170fw/config/expr.c | 206 +++++++---
+ carl9170fw/config/expr.h | 105 ++++-
+ carl9170fw/config/kconf_id.c | 54 ---
+ carl9170fw/config/lkc.h | 50 +--
+ carl9170fw/config/lkc_proto.h | 19 +-
+ carl9170fw/config/menu.c | 259 +++++++++---
+ carl9170fw/config/preprocess.c | 573 +++++++++++++++++++++++++++
+ carl9170fw/config/symbol.c | 255 ++++--------
+ carl9170fw/config/util.c | 86 ++--
+ carl9170fw/config/zconf.l | 336 ++++++++++------
+ carl9170fw/config/zconf.y | 403 +++++++++----------
+ carl9170fw/include/linux/ieee80211.h | 24 +-
+ carl9170fw/include/shared/wlan.h | 2 +-
+ carl9170fw/toolchain/Makefile | 8 +-
+ carl9170fw/toolchain/SHA256SUMS | 18 +-
+ 23 files changed, 1942 insertions(+), 1020 deletions(-)
+ mode change 100755 => 100644 carl9170fw/carlfw/src/printf.c
+ delete mode 100644 carl9170fw/config/kconf_id.c
+ create mode 100644 carl9170fw/config/preprocess.c
+
+diff --git a/WHENCE b/WHENCE
+index dd8ec20..2932155 100644
+--- a/WHENCE
++++ b/WHENCE
+@@ -142,8 +142,8 @@ From https://git.kernel.org/pub/scm/utils/cis-tools/cis-tools.git
+
+ Driver: carl9170 -- Atheros AR9170 802.11 draft-n USB driver
+
+-Version: Based on commit 370b7919114a02149088c482c8709cafb9bf7478
+-dated May 2 2018.
++Version: Based on commit 001384147050b9cd9daadb4d3115cc0f13f5b319
++dated May 5 2019.
+
+ Licence: GPLv2 or later.
+
+diff --git a/carl9170fw/carlfw/include/wl.h b/carl9170fw/carlfw/include/wl.h
+index 8499ca2..5566be4 100644
+--- a/carl9170fw/carlfw/include/wl.h
++++ b/carl9170fw/carlfw/include/wl.h
+@@ -237,7 +237,7 @@ static inline __inline void unhide_super(struct dma_desc *desc)
+ desc->totalLen += sizeof(struct carl9170_tx_superdesc);
+ }
+
+-static inline __inline __hot void read_tsf(uint32_t *tsf)
++static inline __inline __hot void read_tsf(uint32_t tsf[static 2])
+ {
+ /*
+ * "According to the [hardware] documentation:
+diff --git a/carl9170fw/carlfw/src/hostif.c b/carl9170fw/carlfw/src/hostif.c
+index 73e89c7..06726db 100644
+--- a/carl9170fw/carlfw/src/hostif.c
++++ b/carl9170fw/carlfw/src/hostif.c
+@@ -213,10 +213,14 @@ void handle_cmd(struct carl9170_rsp *resp)
+ fw.reboot = 1;
+ break;
+
+- case CARL9170_CMD_READ_TSF:
++ case CARL9170_CMD_READ_TSF: {
++ uint32_t tmptsf[2];
++
++ read_tsf(tmptsf);
+ resp->hdr.len = 8;
+- read_tsf((uint32_t *)resp->tsf.tsf);
++ memcpy(resp->tsf.tsf, tmptsf, sizeof(tmptsf));
+ break;
++ }
+
+ case CARL9170_CMD_RX_FILTER:
+ resp->hdr.len = 0;
+diff --git a/carl9170fw/carlfw/src/printf.c b/carl9170fw/carlfw/src/printf.c
+old mode 100755
+new mode 100644
+diff --git a/carl9170fw/carlfw/src/wlantx.c b/carl9170fw/carlfw/src/wlantx.c
+index 474c040..a8d0952 100644
+--- a/carl9170fw/carlfw/src/wlantx.c
++++ b/carl9170fw/carlfw/src/wlantx.c
+@@ -260,7 +260,7 @@ static void __wlan_tx(struct dma_desc *desc)
+
+ if (unlikely(super->s.fill_in_tsf)) {
+ struct ieee80211_mgmt *mgmt = (void *) &super->f.data.i3e;
+- uint32_t *tsf = (uint32_t *) &mgmt->u.probe_resp.timestamp;
++ uint32_t tmptsf[2];
+
+ /*
+ * Truth be told: this is a hack.
+@@ -272,7 +272,8 @@ static void __wlan_tx(struct dma_desc *desc)
+ * (even, if it's got an accurate atomic clock source).
+ */
+
+- read_tsf(tsf);
++ read_tsf(tmptsf);
++ memcpy(&mgmt->u.probe_resp.timestamp, tmptsf, sizeof(tmptsf));
+ }
+
+ wlan_tx_ampdu(super);
+diff --git a/carl9170fw/config/CMakeLists.txt b/carl9170fw/config/CMakeLists.txt
+index 0a96a82..23e7218 100644
+--- a/carl9170fw/config/CMakeLists.txt
++++ b/carl9170fw/config/CMakeLists.txt
+@@ -11,13 +11,13 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+ file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../include/generated")
+
+-BISON_TARGET(zconf zconf.y zconf.tab.c COMPILE_FLAGS "-l -b zconf -p zconf -t")
+-FLEX_TARGET(zconfscan zconf.l zconf.lex.c COMPILE_FLAGS "-Pzconf -L")
++BISON_TARGET(zconf zconf.y zconf.tab.c COMPILE_FLAGS "-l -b zconf -p yy -t")
++FLEX_TARGET(zconfscan zconf.l zconf.lex.c COMPILE_FLAGS "-Pyy -L")
+
+ SET(zconf_deps ${FLEX_zconfscan_OUTPUTS})
+ SET_SOURCE_FILES_PROPERTIES(${BISON_zconf_OUTPUTS}
+ PROPERTIES OBJECT_DEPENDS "${zconf_deps}")
+
+-set(conf_src conf.c ${BISON_zconf_OUTPUTS})
++set(conf_src conf.c symbol.c confdata.c expr.c preprocess.c ${BISON_zconf_OUTPUTS} ${FLEX_zconfscan_OUTPUTS})
+
+ add_executable(conf ${conf_src})
+diff --git a/carl9170fw/config/conf.c b/carl9170fw/config/conf.c
+index 6be6143..2949b7d 100644
+--- a/carl9170fw/config/conf.c
++++ b/carl9170fw/config/conf.c
+@@ -1,9 +1,8 @@
++// SPDX-License-Identifier: GPL-2.0
+ /*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+- * Released under the terms of the GNU GPL v2.0.
+ */
+
+-#include <locale.h>
+ #include <ctype.h>
+ #include <limits.h>
+ #include <stdio.h>
+@@ -20,10 +19,10 @@
+
+ static void conf(struct menu *menu);
+ static void check_conf(struct menu *menu);
+-static void xfgets(char *str, int size, FILE *in);
+
+ enum input_mode {
+ oldaskconfig,
++ syncconfig,
+ oldconfig,
+ allnoconfig,
+ allyesconfig,
+@@ -33,12 +32,13 @@ enum input_mode {
+ defconfig,
+ savedefconfig,
+ listnewconfig,
+- oldnoconfig,
+-} input_mode = oldaskconfig;
++ olddefconfig,
++};
++static enum input_mode input_mode = oldaskconfig;
+
+ static int indent = 1;
+ static int tty_stdio;
+-static int valid_stdin = 1;
++static int sync_kconfig;
+ static int conf_cnt;
+ static char line[PATH_MAX];
+ static struct menu *rootEntry;
+@@ -70,14 +70,14 @@ static void strip(char *str)
+ *p-- = 0;
+ }
+
+-static void check_stdin(void)
++/* Helper function to facilitate fgets() by Jean Sacren. */
++static void xfgets(char *str, int size, FILE *in)
+ {
+- if (!valid_stdin) {
+- printf(_("aborted!\n\n"));
+- printf(_("Console input/output is redirected. "));
+- printf(_("Run 'make config' to update configuration.\n\n"));
+- exit(1);
+- }
++ if (!fgets(str, size, in))
++ fprintf(stderr, "\nError in reading or end of file.\n");
++
++ if (!tty_stdio)
++ printf("%s", str);
+ }
+
+ static int conf_askvalue(struct symbol *sym, const char *def)
+@@ -85,7 +85,7 @@ static int conf_askvalue(struct symbol *sym, const char *def)
+ enum symbol_type type = sym_get_type(sym);
+
+ if (!sym_has_value(sym))
+- printf(_("(NEW) "));
++ printf("(NEW) ");
+
+ line[0] = '\n';
+ line[1] = 0;
+@@ -99,17 +99,15 @@ static int conf_askvalue(struct symbol *sym, const char *def)
+
+ switch (input_mode) {
+ case oldconfig:
++ case syncconfig:
+ if (sym_has_value(sym)) {
+ printf("%s\n", def);
+ return 0;
+ }
+- check_stdin();
+ /* fall through */
+ case oldaskconfig:
+ fflush(stdout);
+ xfgets(line, sizeof(line), stdin);
+- if (!tty_stdio)
+- printf("\n");
+ return 1;
+ default:
+ break;
+@@ -134,7 +132,7 @@ static int conf_string(struct menu *menu)
+ const char *def;
+
+ while (1) {
+- printf("%*s%s ", indent - 1, "", _(menu->prompt->text));
++ printf("%*s%s ", indent - 1, "", menu->prompt->text);
+ printf("(%s) ", sym->name);
+ def = sym_get_string_value(sym);
+ if (sym_get_string_value(sym))
+@@ -167,7 +165,7 @@ static int conf_sym(struct menu *menu)
+ tristate oldval, newval;
+
+ while (1) {
+- printf("%*s%s ", indent - 1, "", _(menu->prompt->text));
++ printf("%*s%s ", indent - 1, "", menu->prompt->text);
+ if (sym->name)
+ printf("(%s) ", sym->name);
+ putchar('[');
+@@ -189,9 +187,7 @@ static int conf_sym(struct menu *menu)
+ printf("/m");
+ if (oldval != yes && sym_tristate_within_range(sym, yes))
+ printf("/y");
+- if (menu_has_help(menu))
+- printf("/?");
+- printf("] ");
++ printf("/?] ");
+ if (!conf_askvalue(sym, sym_get_string_value(sym)))
+ return 0;
+ strip(line);
+@@ -254,7 +250,7 @@ static int conf_choice(struct menu *menu)
+ case no:
+ return 1;
+ case mod:
+- printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu)));
++ printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
+ return 0;
+ case yes:
+ break;
+@@ -264,7 +260,7 @@ static int conf_choice(struct menu *menu)
+ while (1) {
+ int cnt, def;
+
+- printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu)));
++ printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
+ def_sym = sym_get_choice_value(sym);
+ cnt = def = 0;
+ line[0] = 0;
+@@ -272,7 +268,7 @@ static int conf_choice(struct menu *menu)
+ if (!menu_is_visible(child))
+ continue;
+ if (!child->sym) {
+- printf("%*c %s\n", indent, '*', _(menu_get_prompt(child)));
++ printf("%*c %s\n", indent, '*', menu_get_prompt(child));
+ continue;
+ }
+ cnt++;
+@@ -281,30 +277,27 @@ static int conf_choice(struct menu *menu)
+ printf("%*c", indent, '>');
+ } else
+ printf("%*c", indent, ' ');
+- printf(" %d. %s", cnt, _(menu_get_prompt(child)));
++ printf(" %d. %s", cnt, menu_get_prompt(child));
+ if (child->sym->name)
+ printf(" (%s)", child->sym->name);
+ if (!sym_has_value(child->sym))
+- printf(_(" (NEW)"));
++ printf(" (NEW)");
+ printf("\n");
+ }
+- printf(_("%*schoice"), indent - 1, "");
++ printf("%*schoice", indent - 1, "");
+ if (cnt == 1) {
+ printf("[1]: 1\n");
+ goto conf_childs;
+ }
+- printf("[1-%d", cnt);
+- if (menu_has_help(menu))
+- printf("?");
+- printf("]: ");
++ printf("[1-%d?]: ", cnt);
+ switch (input_mode) {
+ case oldconfig:
++ case syncconfig:
+ if (!is_new) {
+ cnt = def;
+ printf("%d\n", cnt);
+ break;
+ }
+- check_stdin();
+ /* fall through */
+ case oldaskconfig:
+ fflush(stdout);
+@@ -364,9 +357,11 @@ static void conf(struct menu *menu)
+
+ switch (prop->type) {
+ case P_MENU:
+- if ((input_mode == listnewconfig ||
+- input_mode == oldnoconfig) &&
+- rootEntry != menu) {
++ /*
++ * Except in oldaskconfig mode, we show only menus that
++ * contain new symbols.
++ */
++ if (input_mode != oldaskconfig && rootEntry != menu) {
+ check_conf(menu);
+ return;
+ }
+@@ -376,7 +371,7 @@ static void conf(struct menu *menu)
+ if (prompt)
+ printf("%*c\n%*c %s\n%*c\n",
+ indent, '*',
+- indent, '*', _(prompt),
++ indent, '*', prompt,
+ indent, '*');
+ default:
+ ;
+@@ -426,12 +421,22 @@ static void check_conf(struct menu *menu)
+ if (sym_is_changable(sym) ||
+ (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) {
+ if (input_mode == listnewconfig) {
+- if (sym->name && !sym_is_choice_value(sym)) {
+- printf("%s%s\n", CONFIG_, sym->name);
++ if (sym->name) {
++ const char *str;
++
++ if (sym->type == S_STRING) {
++ str = sym_get_string_value(sym);
++ str = sym_escape_string_value(str);
++ printf("%s%s=%s\n", CONFIG_, sym->name, str);
++ free((void *)str);
++ } else {
++ str = sym_get_string_value(sym);
++ printf("%s%s=%s\n", CONFIG_, sym->name, str);
++ }
+ }
+- } else if (input_mode != oldnoconfig) {
++ } else {
+ if (!conf_cnt++)
+- printf(_("*\n* Restart config...\n*\n"));
++ printf("*\n* Restart config...\n*\n");
+ rootEntry = menu_get_parent_menu(menu);
+ conf(rootEntry);
+ }
+@@ -443,8 +448,9 @@ static void check_conf(struct menu *menu)
+ }
+
+ static struct option long_opts[] = {
+- {"askconfig", no_argument, NULL, oldaskconfig},
+- {"config", no_argument, NULL, oldconfig},
++ {"oldaskconfig", no_argument, NULL, oldaskconfig},
++ {"oldconfig", no_argument, NULL, oldconfig},
++ {"syncconfig", no_argument, NULL, syncconfig},
+ {"defconfig", optional_argument, NULL, defconfig},
+ {"savedefconfig", required_argument, NULL, savedefconfig},
+ {"allnoconfig", no_argument, NULL, allnoconfig},
+@@ -453,7 +459,7 @@ static struct option long_opts[] = {
+ {"alldefconfig", no_argument, NULL, alldefconfig},
+ {"randconfig", no_argument, NULL, randconfig},
+ {"listnewconfig", no_argument, NULL, listnewconfig},
+- {"noconfig", no_argument, NULL, oldnoconfig},
++ {"olddefconfig", no_argument, NULL, olddefconfig},
+ {NULL, 0, NULL, 0}
+ };
+
+@@ -463,10 +469,11 @@ static void conf_usage(const char *progname)
+ printf("Usage: %s [-s] [option] <kconfig-file>\n", progname);
+ printf("[option] is _one_ of the following:\n");
+ printf(" --listnewconfig List new options\n");
+- printf(" --askconfig Start a new configuration using a line-oriented program\n");
+- printf(" --config Update a configuration using a provided .config as base\n");
+- printf(" --silentconfig Same as config, but quietly, additionally update deps\n");
+- printf(" --noconfig Same as silentconfig but set new symbols to no\n");
++ printf(" --oldaskconfig Start a new configuration using a line-oriented program\n");
++ printf(" --oldconfig Update a configuration using a provided .config as base\n");
++ printf(" --syncconfig Similar to oldconfig but generates configuration in\n"
++ " include/{generated/,config/}\n");
++ printf(" --olddefconfig Same as oldconfig but sets new symbols to their default value\n");
+ printf(" --defconfig <file> New config with default defined in <file>\n");
+ printf(" --savedefconfig <file> Save the minimal current configuration to <file>\n");
+ printf(" --allnoconfig New config where all options are answered with no\n");
+@@ -482,12 +489,9 @@ int main(int ac, char **av)
+ int opt;
+ const char *name, *defconfig_file = NULL /* gcc uninit */;
+ struct stat tmpstat;
++ int no_conf_write = 0;
+
+- setlocale(LC_ALL, "");
+- bindtextdomain(PACKAGE, LOCALEDIR);
+- textdomain(PACKAGE);
+-
+- tty_stdio = isatty(0) && isatty(1) && isatty(2);
++ tty_stdio = isatty(0) && isatty(1);
+
+ while ((opt = getopt_long(ac, av, "s", long_opts, NULL)) != -1) {
+ if (opt == 's') {
+@@ -496,6 +500,14 @@ int main(int ac, char **av)
+ }
+ input_mode = (enum input_mode)opt;
+ switch (opt) {
++ case syncconfig:
++ /*
++ * syncconfig is invoked during the build stage.
++ * Suppress distracting "configuration written to ..."
++ */
++ conf_set_message_callback(NULL);
++ sync_kconfig = 1;
++ break;
+ case defconfig:
+ case savedefconfig:
+ defconfig_file = optarg;
+@@ -532,7 +544,7 @@ int main(int ac, char **av)
+ case allmodconfig:
+ case alldefconfig:
+ case listnewconfig:
+- case oldnoconfig:
++ case olddefconfig:
+ break;
+ case '?':
+ conf_usage(progname);
+@@ -541,30 +553,45 @@ int main(int ac, char **av)
+ }
+ }
+ if (ac == optind) {
+- printf(_("%s: Kconfig file missing\n"), av[0]);
++ fprintf(stderr, "%s: Kconfig file missing\n", av[0]);
+ conf_usage(progname);
+ exit(1);
+ }
+ name = av[optind];
+ conf_parse(name);
+ //zconfdump(stdout);
++ if (sync_kconfig) {
++ name = conf_get_configname();
++ if (stat(name, &tmpstat)) {
++ fprintf(stderr, "***\n"
++ "*** Configuration file \"%s\" not found!\n"
++ "***\n"
++ "*** Please run some configurator (e.g. \"make oldconfig\" or\n"
++ "*** \"make menuconfig\" or \"make xconfig\").\n"
++ "***\n", name);
++ exit(1);
++ }
++ }
+
+ switch (input_mode) {
+ case defconfig:
+ if (!defconfig_file)
+ defconfig_file = conf_get_default_confname();
+ if (conf_read(defconfig_file)) {
+- printf(_("***\n"
+- "*** Can't find default configuration \"%s\"!\n"
+- "***\n"), defconfig_file);
++ fprintf(stderr,
++ "***\n"
++ "*** Can't find default configuration \"%s\"!\n"
++ "***\n",
++ defconfig_file);
+ exit(1);
+ }
+ break;
+ case savedefconfig:
++ case syncconfig:
+ case oldaskconfig:
+ case oldconfig:
+ case listnewconfig:
+- case oldnoconfig:
++ case olddefconfig:
+ conf_read(NULL);
+ break;
+ case allnoconfig:
+@@ -578,7 +605,7 @@ int main(int ac, char **av)
+ if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) {
+ if (conf_read_simple(name, S_DEF_USER)) {
+ fprintf(stderr,
+- _("*** Can't read seed configuration \"%s\"!\n"),
++ "*** Can't read seed configuration \"%s\"!\n",
+ name);
+ exit(1);
+ }
+@@ -595,7 +622,7 @@ int main(int ac, char **av)
+ if (conf_read_simple(name, S_DEF_USER) &&
+ conf_read_simple("all.config", S_DEF_USER)) {
+ fprintf(stderr,
+- _("*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n"),
++ "*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n",
+ name);
+ exit(1);
+ }
+@@ -604,7 +631,17 @@ int main(int ac, char **av)
+ break;
+ }
+
+- valid_stdin = tty_stdio;
++ if (sync_kconfig) {
++ name = getenv("KCONFIG_NOSILENTUPDATE");
++ if (name && *name) {
++ if (conf_get_changed()) {
++ fprintf(stderr,
++ "\n*** The configuration requires explicit update.\n\n");
++ return 1;
++ }
++ no_conf_write = 1;
++ }
++ }
+
+ switch (input_mode) {
+ case allnoconfig:
+@@ -635,44 +672,51 @@ int main(int ac, char **av)
+ /* fall through */
+ case oldconfig:
+ case listnewconfig:
+- case oldnoconfig:
++ case syncconfig:
+ /* Update until a loop caused no more changes */
+ do {
+ conf_cnt = 0;
+ check_conf(&rootmenu);
+- } while (conf_cnt &&
+- (input_mode != listnewconfig &&
+- input_mode != oldnoconfig));
++ } while (conf_cnt);
++ break;
++ case olddefconfig:
++ default:
+ break;
+ }
+
+ if (input_mode == savedefconfig) {
+ if (conf_write_defconfig(defconfig_file)) {
+- fprintf(stderr, _("n*** Error while saving defconfig to: %s\n\n"),
++ fprintf(stderr, "n*** Error while saving defconfig to: %s\n\n",
+ defconfig_file);
+ return 1;
+ }
+ } else if (input_mode != listnewconfig) {
+- /*
+- * build so we shall update autoconf.
+- */
+- if (conf_write(NULL)) {
+- fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n"));
++ if (!no_conf_write && conf_write(NULL)) {
++ fprintf(stderr, "\n*** Error during writing of the configuration.\n\n");
+ exit(1);
+ }
+- if (conf_write_autoconf()) {
+- fprintf(stderr, _("\n*** Error during update of the configuration.\n\n"));
++
++ /*
++ * Create auto.conf if it does not exist.
++ * This prevents GNU Make 4.1 or older from emitting
++ * "include/generated/auto.conf: No such file or directory"
++ * in the top-level Makefile
++ *
++ * syncconfig always creates or updates auto.conf because it is
++ * used during the build.
++ */
++
++ /*
++ * In our cmake case, we always want to update the autogenerated
++ * files.
++ */
++ sync_kconfig = 1;
++
++ if (conf_write_autoconf(sync_kconfig) && sync_kconfig) {
++ fprintf(stderr,
++ "\n*** Error during sync of the configuration.\n\n");
+ return 1;
+ }
+ }
+ return 0;
+ }
+-
+-/*
+- * Helper function to facilitate fgets() by Jean Sacren.
+- */
+-void xfgets(char *str, int size, FILE *in)
+-{
+- if (fgets(str, size, in) == NULL)
+- fprintf(stderr, "\nError in reading or end of file.\n");
+-}
+diff --git a/carl9170fw/config/confdata.c b/carl9170fw/config/confdata.c
+index e606f06..d67695d 100644
+--- a/carl9170fw/config/confdata.c
++++ b/carl9170fw/config/confdata.c
+@@ -1,12 +1,13 @@
++// SPDX-License-Identifier: GPL-2.0
+ /*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+- * Released under the terms of the GNU GPL v2.0.
+ */
+
+ #include <sys/stat.h>
+ #include <ctype.h>
+ #include <errno.h>
+ #include <fcntl.h>
++#include <limits.h>
+ #include <stdarg.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -16,6 +17,105 @@
+
+ #include "lkc.h"
+
++/* return true if 'path' exists, false otherwise */
++static bool is_present(const char *path)
++{
++ struct stat st;
++
++ return !stat(path, &st);
++}
++
++/* return true if 'path' exists and it is a directory, false otherwise */
++static bool is_dir(const char *path)
++{
++ struct stat st;
++
++ if (stat(path, &st))
++ return 0;
++
++ return S_ISDIR(st.st_mode);
++}
++
++/*
++ * Create the parent directory of the given path.
++ *
++ * For example, if 'include/config/auto.conf' is given, create 'include/config'.
++ */
++static int make_parent_dir(const char *path)
++{
++ char tmp[PATH_MAX + 1];
++ char *p;
++
++ strncpy(tmp, path, sizeof(tmp));
++ tmp[sizeof(tmp) - 1] = 0;
++
++ /* Remove the base name. Just return if nothing is left */
++ p = strrchr(tmp, '/');
++ if (!p)
++ return 0;
++ *(p + 1) = 0;
++
++ /* Just in case it is an absolute path */
++ p = tmp;
++ while (*p == '/')
++ p++;
++
++ while ((p = strchr(p, '/'))) {
++ *p = 0;
++
++ /* skip if the directory exists */
++ if (!is_dir(tmp) && mkdir(tmp, 0755))
++ return -1;
++
++ *p = '/';
++ while (*p == '/')
++ p++;
++ }
++
++ return 0;
++}
++
++static char depfile_path[PATH_MAX];
++static size_t depfile_prefix_len;
++
++/* touch depfile for symbol 'name' */
++static int conf_touch_dep(const char *name)
++{
++ int fd, ret;
++ const char *s;
++ char *d, c;
++
++ /* check overflow: prefix + name + ".h" + '\0' must fit in buffer. */
++ if (depfile_prefix_len + strlen(name) + 3 > sizeof(depfile_path))
++ return -1;
++
++ d = depfile_path + depfile_prefix_len;
++ s = name;
++
++ while ((c = *s++))
++ *d++ = (c == '_') ? '/' : tolower(c);
++ strcpy(d, ".h");
++
++ /* Assume directory path already exists. */
++ fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
++ if (fd == -1) {
++ if (errno != ENOENT)
++ return -1;
++
++ ret = make_parent_dir(depfile_path);
++ if (ret)
++ return ret;
++
++ /* Try it again. */
++ fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
++ if (fd == -1)
++ return -1;
++ }
++ close(fd);
++
++ return 0;
++}
++
+ struct conf_printer {
+ void (*print_symbol)(FILE *, struct symbol *, const char *, void *);
+ void (*print_comment)(FILE *, const char *, void *);
+@@ -28,7 +128,7 @@ static void conf_message(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+ static const char *conf_filename;
+-static int conf_lineno, conf_warnings, conf_unsaved;
++static int conf_lineno, conf_warnings;
+
+ const char conf_defname[] = "include/generated/defconfig";
+
+@@ -43,16 +143,16 @@ static void conf_warning(const char *fmt, ...)
+ conf_warnings++;
+ }
+
+-static void conf_default_message_callback(const char *fmt, va_list ap)
++static void conf_default_message_callback(const char *s)
+ {
+ printf("#\n# ");
+- vprintf(fmt, ap);
++ printf("%s", s);
+ printf("\n#\n");
+ }
+
+-static void (*conf_message_callback) (const char *fmt, va_list ap) =
++static void (*conf_message_callback)(const char *s) =
+ conf_default_message_callback;
+-void conf_set_message_callback(void (*fn) (const char *fmt, va_list ap))
++void conf_set_message_callback(void (*fn)(const char *s))
+ {
+ conf_message_callback = fn;
+ }
+@@ -60,10 +160,15 @@ void conf_set_message_callback(void (*fn) (const char *fmt, va_list ap))
+ static void conf_message(const char *fmt, ...)
+ {
+ va_list ap;
++ char buf[4096];
++
++ if (!conf_message_callback)
++ return;
+
+ va_start(ap, fmt);
+- if (conf_message_callback)
+- conf_message_callback(fmt, ap);
++
++ vsnprintf(buf, sizeof(buf), fmt, ap);
++ conf_message_callback(buf);
+ va_end(ap);
+ }
+
+@@ -81,43 +186,16 @@ const char *conf_get_autoconfig_name(void)
+ return name ? name : "include/generated/auto.conf";
+ }
+
+-static char *conf_expand_value(const char *in)
+-{
+- struct symbol *sym;
+- const char *src;
+- static char res_value[SYMBOL_MAXLENGTH];
+- char *dst, name[SYMBOL_MAXLENGTH];
+-
+- res_value[0] = 0;
+- dst = name;
+- while ((src = strchr(in, '$'))) {
+- strncat(res_value, in, src - in);
+- src++;
+- dst = name;
+- while (isalnum(*src) || *src == '_')
+- *dst++ = *src++;
+- *dst = 0;
+- sym = sym_lookup(name, 0);
+- sym_calc_value(sym);
+- strcat(res_value, sym_get_string_value(sym));
+- in = src;
+- }
+- strcat(res_value, in);
+-
+- return res_value;
+-}
+-
+ char *conf_get_default_confname(void)
+ {
+- struct stat buf;
+ static char fullname[PATH_MAX+1];
+ char *env, *name;
+
+- name = conf_expand_value(conf_defname);
++ name = expand_string(conf_defname);
+ env = getenv(SRCTREE);
+ if (env) {
+ sprintf(fullname, "%s/%s", env, name);
+- if (!stat(fullname, &buf))
++ if (is_present(fullname))
+ return fullname;
+ }
+ return name;
+@@ -150,14 +228,6 @@ static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p)
+ conf_warning("symbol value '%s' invalid for %s",
+ p, sym->name);
+ return 1;
+- case S_OTHER:
+- if (*p != '"') {
+- for (p2 = p; *p2 && !isspace(*p2); p2++)
+- ;
+- sym->type = S_STRING;
+- goto done;
+- }
+- /* fall through */
+ case S_STRING:
+ if (*p++ != '"')
+ break;
+@@ -176,9 +246,8 @@ static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p)
+ /* fall through */
+ case S_INT:
+ case S_HEX:
+- done:
+ if (sym_string_valid(sym, p)) {
+- sym->def[def].val = strdup(p);
++ sym->def[def].val = xstrdup(p);
+ sym->flags |= def_flags;
+ } else {
+ if (def != S_DEF_AUTO)
+@@ -201,7 +270,7 @@ static int add_byte(int c, char **lineptr, size_t slen, size_t *n)
+ if (new_size > *n) {
+ new_size += LINE_GROWTH - 1;
+ new_size *= 2;
+- nline = realloc(*lineptr, new_size);
++ nline = xrealloc(*lineptr, new_size);
+ if (!nline)
+ return -1;
+
+@@ -274,10 +343,11 @@ int conf_read_simple(const char *name, int def)
+ if (expr_calc_value(prop->visible.expr) == no ||
+ prop->expr->type != E_SYMBOL)
+ continue;
+- name = conf_expand_value(prop->expr->left.sym->name);
++ sym_calc_value(prop->expr->left.sym);
++ name = sym_get_string_value(prop->expr->left.sym);
+ in = zconf_fopen(name);
+ if (in) {
+- conf_message(_("using defaults found in %s"),
++ conf_message("using defaults found in %s",
+ name);
+ goto load;
+ }
+@@ -290,7 +360,6 @@ load:
+ conf_filename = name;
+ conf_lineno = 0;
+ conf_warnings = 0;
+- conf_unsaved = 0;
+
+ def_flags = SYMBOL_DEF << def;
+ for_all_symbols(i, sym) {
+@@ -327,7 +396,7 @@ load:
+ sym = sym_find(line + 2 + strlen(CONFIG_));
+ if (!sym) {
+ sym_add_change_count(1);
+- goto setsym;
++ continue;
+ }
+ } else {
+ sym = sym_lookup(line + 2 + strlen(CONFIG_), 0);
+@@ -357,17 +426,22 @@ load:
+ if (*p2 == '\r')
+ *p2 = 0;
+ }
+- if (def == S_DEF_USER) {
+- sym = sym_find(line + strlen(CONFIG_));
+- if (!sym) {
++
++ sym = sym_find(line + strlen(CONFIG_));
++ if (!sym) {
++ if (def == S_DEF_AUTO)
++ /*
++ * Reading from include/config/auto.conf
++ * If CONFIG_FOO previously existed in
++ * auto.conf but it is missing now,
++ * include/config/foo.h must be touched.
++ */
++ conf_touch_dep(line + strlen(CONFIG_));
++ else
+ sym_add_change_count(1);
+- goto setsym;
+- }
+- } else {
+- sym = sym_lookup(line + strlen(CONFIG_), 0);
+- if (sym->type == S_UNKNOWN)
+- sym->type = S_OTHER;
++ continue;
+ }
++
+ if (sym->flags & def_flags) {
+ conf_warning("override: reassigning to symbol %s", sym->name);
+ }
+@@ -380,7 +454,7 @@ load:
+
+ continue;
+ }
+-setsym:
++
+ if (sym && sym_is_choice_value(sym)) {
+ struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+ switch (sym->def[def].tri) {
+@@ -409,6 +483,7 @@ setsym:
+ int conf_read(const char *name)
+ {
+ struct symbol *sym;
++ int conf_unsaved = 0;
+ int i;
+
+ sym_set_change_count(0);
+@@ -422,7 +497,7 @@ int conf_read(const char *name)
+
+ for_all_symbols(i, sym) {
+ sym_calc_value(sym);
+- if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO))
++ if (sym_is_choice(sym) || (sym->flags & SYMBOL_NO_WRITE))
+ continue;
+ if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) {
+ /* check that calculated value agrees with saved value */
+@@ -678,7 +753,6 @@ static void conf_write_symbol(FILE *fp, struct symbol *sym,
+ const char *str;
+
+ switch (sym->type) {
+- case S_OTHER:
+ case S_UNKNOWN:
+ break;
+ case S_STRING:
+@@ -791,15 +865,14 @@ int conf_write(const char *name)
+ struct menu *menu;
+ const char *basename;
+ const char *str;
+- char dirname[PATH_MAX+1], tmpname[PATH_MAX+1], newname[PATH_MAX+1];
++ char dirname[PATH_MAX+1], tmpname[PATH_MAX+22], newname[PATH_MAX+8];
+ char *env;
+
+ dirname[0] = 0;
+ if (name && name[0]) {
+- struct stat st;
+ char *slash;
+
+- if (!stat(name, &st) && S_ISDIR(st.st_mode)) {
++ if (is_dir(name)) {
+ strcpy(dirname, name);
+ strcat(dirname, "/");
+ basename = conf_get_configname();
+@@ -877,33 +950,61 @@ next:
+ return 1;
+ }
+
+- conf_message(_("configuration written to %s"), newname);
++ conf_message("configuration written to %s", newname);
+
+ sym_set_change_count(0);
+
+ return 0;
+ }
+
+-static int conf_split_config(void)
++/* write a dependency file as used by kbuild to track dependencies */
++static int conf_write_dep(const char *name)
++{
++ struct file *file;
++ FILE *out;
++
++ if (!name)
++ name = ".kconfig.d";
++ out = fopen("..config.tmp", "w");
++ if (!out)
++ return 1;
++ fprintf(out, "deps_config := \\\n");
++ for (file = file_list; file; file = file->next) {
++ if (file->next)
++ fprintf(out, "\t%s \\\n", file->name);
++ else
++ fprintf(out, "\t%s\n", file->name);
++ }
++ fprintf(out, "\n%s: \\\n"
++ "\t$(deps_config)\n\n", conf_get_autoconfig_name());
++
++ env_write_dep(out, conf_get_autoconfig_name());
++
++ fprintf(out, "\n$(deps_config): ;\n");
++ fclose(out);
++
++ if (make_parent_dir(name))
++ return 1;
++ rename("..config.tmp", name);
++ return 0;
++}
++
++static int conf_touch_deps(void)
+ {
+ const char *name;
+- char path[PATH_MAX+1];
+- char *s, *d, c;
+ struct symbol *sym;
+- struct stat sb;
+- int res, i, fd;
++ int res, i;
++
++ strcpy(depfile_path, "include/generated/");
++ depfile_prefix_len = strlen(depfile_path);
+
+ name = conf_get_autoconfig_name();
+ conf_read_simple(name, S_DEF_AUTO);
+ sym_calc_value(modules_sym);
+
+- if (chdir("include/generated"))
+- return 1;
+-
+- res = 0;
+ for_all_symbols(i, sym) {
+ sym_calc_value(sym);
+- if ((sym->flags & SYMBOL_AUTO) || !sym->name)
++ if ((sym->flags & SYMBOL_NO_WRITE) || !sym->name)
+ continue;
+ if (sym->flags & SYMBOL_WRITE) {
+ if (sym->flags & SYMBOL_DEF_AUTO) {
+@@ -952,63 +1053,30 @@ static int conf_split_config(void)
+ * different from 'no').
+ */
+
+- /* Replace all '_' and append ".h" */
+- s = sym->name;
+- d = path;
+- while ((c = *s++)) {
+- c = tolower(c);
+- *d++ = (c == '_') ? '/' : c;
+- }
+- strcpy(d, ".h");
+-
+- /* Assume directory path already exists. */
+- fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+- if (fd == -1) {
+- if (errno != ENOENT) {
+- res = 1;
+- break;
+- }
+- /*
+- * Create directory components,
+- * unless they exist already.
+- */
+- d = path;
+- while ((d = strchr(d, '/'))) {
+- *d = 0;
+- if (stat(path, &sb) && mkdir(path, 0755)) {
+- res = 1;
+- goto out;
+- }
+- *d++ = '/';
+- }
+- /* Try it again. */
+- fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+- if (fd == -1) {
+- res = 1;
+- break;
+- }
+- }
+- close(fd);
++ res = conf_touch_dep(sym->name);
++ if (res)
++ return res;
+ }
+-out:
+- if (chdir("../.."))
+- return 1;
+
+- return res;
++ return 0;
+ }
+
+-int conf_write_autoconf(void)
++int conf_write_autoconf(int overwrite)
+ {
+ struct symbol *sym;
+ const char *name;
++ const char *autoconf_name = conf_get_autoconfig_name();
+ FILE *out, *tristate, *out_h, *out_c;
+ int i;
+
++ if (!overwrite && is_present(autoconf_name))
++ return 0;
++
+ sym_clear_all_valid();
+
+- file_write_dep("include/generated/auto.conf.cmd");
++ conf_write_dep("include/generated/auto.conf.cmd");
+
+- if (conf_split_config())
++ if (conf_touch_deps())
+ return 1;
+
+ out = fopen(".tmpconfig", "w");
+@@ -1065,24 +1133,35 @@ int conf_write_autoconf(void)
+ name = getenv("KCONFIG_AUTOHEADER");
+ if (!name)
+ name = "include/generated/autoconf.h";
++ if (make_parent_dir(name))
++ return 1;
+ if (rename(".tmpconfig.h", name))
+ return 1;
++
+ name = getenv("KCONFIG_TRISTATE");
+ if (!name)
+ name = "include/generated/tristate.conf";
++ if (make_parent_dir(name))
++ return 1;
+ if (rename(".tmpconfig_tristate", name))
+ return 1;
++
++ if (make_parent_dir(autoconf_name))
++ return 1;
++
+ name = getenv("KCONFIG_CMAKE");
+ if (!name)
+ name = "config.cmake";
++ if (make_parent_dir(name))
++ return 1;
+ if (rename(".tmpconfig.cmake", name))
+ return 1;
+- name = conf_get_autoconfig_name();
++
+ /*
+ * This must be the last step, kbuild has a dependency on auto.conf
+ * and this marks the successful completion of the previous steps.
+ */
+- if (rename(".tmpconfig", name))
++ if (rename(".tmpconfig", autoconf_name))
+ return 1;
+
+ return 0;
+@@ -1186,7 +1265,7 @@ void set_all_choice_values(struct symbol *csym)
+ bool conf_set_all_new_symbols(enum conf_def_mode mode)
+ {
+ struct symbol *sym, *csym;
+- int i, cnt, pby, pty, ptm; /* pby: probability of boolean = y
++ int i, cnt, pby, pty, ptm; /* pby: probability of bool = y
+ * pty: probability of tristate = y
+ * ptm: probability of tristate = m
+ */
+diff --git a/carl9170fw/config/expr.c b/carl9170fw/config/expr.c
+index 8cee597..77ffff3 100644
+--- a/carl9170fw/config/expr.c
++++ b/carl9170fw/config/expr.c
+@@ -1,8 +1,10 @@
++// SPDX-License-Identifier: GPL-2.0
+ /*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+- * Released under the terms of the GNU GPL v2.0.
+ */
+
++#include <ctype.h>
++#include <errno.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -94,7 +96,7 @@ struct expr *expr_copy(const struct expr *org)
+ e->right.expr = expr_copy(org->right.expr);
+ break;
+ default:
+- printf("can't copy type %d\n", e->type);
++ fprintf(stderr, "can't copy type %d\n", e->type);
+ free(e);
+ e = NULL;
+ break;
+@@ -113,7 +115,7 @@ void expr_free(struct expr *e)
+ break;
+ case E_NOT:
+ expr_free(e->left.expr);
+- return;
++ break;
+ case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+@@ -127,7 +129,7 @@ void expr_free(struct expr *e)
+ expr_free(e->right.expr);
+ break;
+ default:
+- printf("how to free type %d?\n", e->type);
++ fprintf(stderr, "how to free type %d?\n", e->type);
+ break;
+ }
+ free(e);
+@@ -138,8 +140,18 @@ static int trans_count;
+ #define e1 (*ep1)
+ #define e2 (*ep2)
+
++/*
++ * expr_eliminate_eq() helper.
++ *
++ * Walks the two expression trees given in 'ep1' and 'ep2'. Any node that does
++ * not have type 'type' (E_OR/E_AND) is considered a leaf, and is compared
++ * against all other leaves. Two equal leaves are both replaced with either 'y'
++ * or 'n' as appropriate for 'type', to be eliminated later.
++ */
+ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
+ {
++ /* Recurse down to leaves */
++
+ if (e1->type == type) {
+ __expr_eliminate_eq(type, &e1->left.expr, &e2);
+ __expr_eliminate_eq(type, &e1->right.expr, &e2);
+@@ -150,12 +162,18 @@ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct e
+ __expr_eliminate_eq(type, &e1, &e2->right.expr);
+ return;
+ }
++
++ /* e1 and e2 are leaves. Compare them. */
++
+ if (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+ e1->left.sym == e2->left.sym &&
+ (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no))
+ return;
+ if (!expr_eq(e1, e2))
+ return;
++
++ /* e1 and e2 are equal leaves. Prepare them for elimination. */
++
+ trans_count++;
+ expr_free(e1); expr_free(e2);
+ switch (type) {
+@@ -172,6 +190,35 @@ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct e
+ }
+ }
+
++/*
++ * Rewrites the expressions 'ep1' and 'ep2' to remove operands common to both.
++ * Example reductions:
++ *
++ * ep1: A && B -> ep1: y
++ * ep2: A && B && C -> ep2: C
++ *
++ * ep1: A || B -> ep1: n
++ * ep2: A || B || C -> ep2: C
++ *
++ * ep1: A && (B && FOO) -> ep1: FOO
++ * ep2: (BAR && B) && A -> ep2: BAR
++ *
++ * ep1: A && (B || C) -> ep1: y
++ * ep2: (C || B) && A -> ep2: y
++ *
++ * Comparisons are done between all operands at the same "level" of && or ||.
++ * For example, in the expression 'e1 && (e2 || e3) && (e4 || e5)', the
++ * following operands will be compared:
++ *
++ * - 'e1', 'e2 || e3', and 'e4 || e5', against each other
++ * - e2 against e3
++ * - e4 against e5
++ *
++ * Parentheses are irrelevant within a single level. 'e1 && (e2 && e3)' and
++ * '(e1 && e2) && e3' are both a single level.
++ *
++ * See __expr_eliminate_eq() as well.
++ */
+ void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
+ {
+ if (!e1 || !e2)
+@@ -197,6 +244,12 @@ void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
+ #undef e1
+ #undef e2
+
++/*
++ * Returns true if 'e1' and 'e2' are equal, after minor simplification. Two
++ * &&/|| expressions are considered equal if every operand in one expression
++ * equals some operand in the other (operands do not need to appear in the same
++ * order), recursively.
++ */
+ static int expr_eq(struct expr *e1, struct expr *e2)
+ {
+ int res, old_count;
+@@ -243,6 +296,17 @@ static int expr_eq(struct expr *e1, struct expr *e2)
+ return 0;
+ }
+
++/*
++ * Recursively performs the following simplifications in-place (as well as the
++ * corresponding simplifications with swapped operands):
++ *
++ * expr && n -> n
++ * expr && y -> expr
++ * expr || n -> expr
++ * expr || y -> y
++ *
++ * Returns the optimized expression.
++ */
+ static struct expr *expr_eliminate_yn(struct expr *e)
+ {
+ struct expr *tmp;
+@@ -516,12 +580,21 @@ static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
+ return NULL;
+ }
+
++/*
++ * expr_eliminate_dups() helper.
++ *
++ * Walks the two expression trees given in 'ep1' and 'ep2'. Any node that does
++ * not have type 'type' (E_OR/E_AND) is considered a leaf, and is compared
++ * against all other leaves to look for simplifications.
++ */
+ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
+ {
+ #define e1 (*ep1)
+ #define e2 (*ep2)
+ struct expr *tmp;
+
++ /* Recurse down to leaves */
++
+ if (e1->type == type) {
+ expr_eliminate_dups1(type, &e1->left.expr, &e2);
+ expr_eliminate_dups1(type, &e1->right.expr, &e2);
+@@ -532,6 +605,9 @@ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct
+ expr_eliminate_dups1(type, &e1, &e2->right.expr);
+ return;
+ }
++
++ /* e1 and e2 are leaves. Compare and process them. */
++
+ if (e1 == e2)
+ return;
+
+@@ -568,6 +644,17 @@ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct
+ #undef e2
+ }
+
++/*
++ * Rewrites 'e' in-place to remove ("join") duplicate and other redundant
++ * operands.
++ *
++ * Example simplifications:
++ *
++ * A || B || A -> A || B
++ * A && B && A=y -> A=y && B
++ *
++ * Returns the deduplicated expression.
++ */
+ struct expr *expr_eliminate_dups(struct expr *e)
+ {
+ int oldcount;
+@@ -584,6 +671,7 @@ struct expr *expr_eliminate_dups(struct expr *e)
+ ;
+ }
+ if (!trans_count)
++ /* No simplifications done in this pass. We're done */
+ break;
+ e = expr_eliminate_yn(e);
+ }
+@@ -591,6 +679,12 @@ struct expr *expr_eliminate_dups(struct expr *e)
+ return e;
+ }
+
++/*
++ * Performs various simplifications involving logical operators and
++ * comparisons.
++ *
++ * Allocates and returns a new expression.
++ */
+ struct expr *expr_transform(struct expr *e)
+ {
+ struct expr *tmp;
+@@ -805,6 +899,20 @@ bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
+ return false;
+ }
+
++/*
++ * Inserts explicit comparisons of type 'type' to symbol 'sym' into the
++ * expression 'e'.
++ *
++ * Examples transformations for type == E_UNEQUAL, sym == &symbol_no:
++ *
++ * A -> A!=n
++ * !A -> A=n
++ * A && B -> !(A=n || B=n)
++ * A || B -> !(A=n && B=n)
++ * A && (B || C) -> !(A=n || (B=n && C=n))
++ *
++ * Allocates and returns a new expression.
++ */
+ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym)
+ {
+ struct expr *e1, *e2;
+@@ -874,7 +982,6 @@ enum string_value_kind {
+ k_string,
+ k_signed,
+ k_unsigned,
+- k_invalid
+ };
+
+ union string_value {
+@@ -905,13 +1012,10 @@ static enum string_value_kind expr_parse_string(const char *str,
+ val->u = strtoull(str, &tail, 16);
+ kind = k_unsigned;
+ break;
+- case S_STRING:
+- case S_UNKNOWN:
++ default:
+ val->s = strtoll(str, &tail, 0);
+ kind = k_signed;
+ break;
+- default:
+- return k_invalid;
+ }
+ return !errno && !*tail && tail > str && isxdigit(tail[-1])
+ ? kind : k_string;
+@@ -967,13 +1071,7 @@ tristate expr_calc_value(struct expr *e)
+
+ if (k1 == k_string || k2 == k_string)
+ res = strcmp(str1, str2);
+- else if (k1 == k_invalid || k2 == k_invalid) {
+- if (e->type != E_EQUAL && e->type != E_UNEQUAL) {
+- printf("Cannot compare \"%s\" and \"%s\"\n", str1, str2);
+- return no;
+- }
+- res = strcmp(str1, str2);
+- } else if (k1 == k_unsigned || k2 == k_unsigned)
++ else if (k1 == k_unsigned || k2 == k_unsigned)
+ res = (lval.u > rval.u) - (lval.u < rval.u);
+ else /* if (k1 == k_signed && k2 == k_signed) */
+ res = (lval.s > rval.s) - (lval.s < rval.s);
+@@ -1031,49 +1129,9 @@ static int expr_compare_type(enum expr_type t1, enum expr_type t2)
+ return 0;
+ }
+
+-static inline struct expr *
+-expr_get_leftmost_symbol(const struct expr *e)
+-{
+-
+- if (e == NULL)
+- return NULL;
+-
+- while (e->type != E_SYMBOL)
+- e = e->left.expr;
+-
+- return expr_copy(e);
+-}
+-
+-/*
+- * Given expression `e1' and `e2', returns the leaf of the longest
+- * sub-expression of `e1' not containing 'e2.
+- */
+-struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2)
+-{
+- struct expr *ret;
+-
+- switch (e1->type) {
+- case E_OR:
+- return expr_alloc_and(
+- expr_simplify_unmet_dep(e1->left.expr, e2),
+- expr_simplify_unmet_dep(e1->right.expr, e2));
+- case E_AND: {
+- struct expr *e;
+- e = expr_alloc_and(expr_copy(e1), expr_copy(e2));
+- e = expr_eliminate_dups(e);
+- ret = (!expr_eq(e, e1)) ? e1 : NULL;
+- expr_free(e);
+- break;
+- }
+- default:
+- ret = e1;
+- break;
+- }
+-
+- return expr_get_leftmost_symbol(ret);
+-}
+-
+-void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken)
++void expr_print(struct expr *e,
++ void (*fn)(void *, struct symbol *, const char *),
++ void *data, int prevtoken)
+ {
+ if (!e) {
+ fn(data, NULL, "y");
+@@ -1207,3 +1265,33 @@ void expr_gstr_print(struct expr *e, struct gstr *gs)
+ {
+ expr_print(e, expr_print_gstr_helper, gs, E_NONE);
+ }
++
++/*
++ * Transform the top level "||" tokens into newlines and prepend each
++ * line with a minus. This makes expressions much easier to read.
++ * Suitable for reverse dependency expressions.
++ */
++static void expr_print_revdep(struct expr *e,
++ void (*fn)(void *, struct symbol *, const char *),
++ void *data, tristate pr_type, const char **title)
++{
++ if (e->type == E_OR) {
++ expr_print_revdep(e->left.expr, fn, data, pr_type, title);
++ expr_print_revdep(e->right.expr, fn, data, pr_type, title);
++ } else if (expr_calc_value(e) == pr_type) {
++ if (*title) {
++ fn(data, NULL, *title);
++ *title = NULL;
++ }
++
++ fn(data, NULL, " - ");
++ expr_print(e, fn, data, E_NONE);
++ fn(data, NULL, "\n");
++ }
++}
++
++void expr_gstr_print_revdep(struct expr *e, struct gstr *gs,
++ tristate pr_type, const char *title)
++{
++ expr_print_revdep(e, expr_print_gstr_helper, gs, pr_type, &title);
++}
+diff --git a/carl9170fw/config/expr.h b/carl9170fw/config/expr.h
+index a73f762..999edb6 100644
+--- a/carl9170fw/config/expr.h
++++ b/carl9170fw/config/expr.h
+@@ -1,6 +1,6 @@
++/* SPDX-License-Identifier: GPL-2.0 */
+ /*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+- * Released under the terms of the GNU GPL v2.0.
+ */
+
+ #ifndef EXPR_H
+@@ -62,7 +62,7 @@ struct symbol_value {
+ };
+
+ enum symbol_type {
+- S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER
++ S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING
+ };
+
+ /* enum values are used as index to symbol.def[] */
+@@ -74,21 +74,64 @@ enum {
+ S_DEF_COUNT
+ };
+
++/*
++ * Represents a configuration symbol.
++ *
++ * Choices are represented as a special kind of symbol and have the
++ * SYMBOL_CHOICE bit set in 'flags'.
++ */
+ struct symbol {
++ /* The next symbol in the same bucket in the symbol hash table */
+ struct symbol *next;
++
++ /* The name of the symbol, e.g. "FOO" for 'config FOO' */
+ char *name;
++
++ /* S_BOOLEAN, S_TRISTATE, ... */
+ enum symbol_type type;
++
++ /*
++ * The calculated value of the symbol. The SYMBOL_VALID bit is set in
++ * 'flags' when this is up to date. Note that this value might differ
++ * from the user value set in e.g. a .config file, due to visibility.
++ */
+ struct symbol_value curr;
++
++ /*
++ * Values for the symbol provided from outside. def[S_DEF_USER] holds
++ * the .config value.
++ */
+ struct symbol_value def[S_DEF_COUNT];
++
++ /*
++ * An upper bound on the tristate value the user can set for the symbol
++ * if it is a boolean or tristate. Calculated from prompt dependencies,
++ * which also inherit dependencies from enclosing menus, choices, and
++ * ifs. If 'n', the user value will be ignored.
++ *
++ * Symbols lacking prompts always have visibility 'n'.
++ */
+ tristate visible;
++
++ /* SYMBOL_* flags */
+ int flags;
++
++ /* List of properties. See prop_type. */
+ struct property *prop;
++
++ /* Dependencies from enclosing menus, choices, and ifs */
+ struct expr_value dir_dep;
++
++ /* Reverse dependencies through being selected by other symbols */
+ struct expr_value rev_dep;
++
++ /*
++ * "Weak" reverse dependencies through being implied by other symbols
++ */
+ struct expr_value implied;
+ };
+
+-#define for_all_symbols(i, sym) for (i = 0; i < SYMBOL_HASHSIZE; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER)
++#define for_all_symbols(i, sym) for (i = 0; i < SYMBOL_HASHSIZE; i++) for (sym = symbol_hash[i]; sym; sym = sym->next)
+
+ #define SYMBOL_CONST 0x0001 /* symbol is const */
+ #define SYMBOL_CHECK 0x0008 /* used during dependency checking */
+@@ -98,7 +141,7 @@ struct symbol {
+ #define SYMBOL_OPTIONAL 0x0100 /* choice is optional - values can be 'n' */
+ #define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */
+ #define SYMBOL_CHANGED 0x0400 /* ? */
+-#define SYMBOL_AUTO 0x1000 /* value from environment variable */
++#define SYMBOL_NO_WRITE 0x1000 /* Symbol for internal use only; it will not be written */
+ #define SYMBOL_CHECKED 0x2000 /* used during dependency checking */
+ #define SYMBOL_WARNED 0x8000 /* warning has been issued */
+
+@@ -128,18 +171,20 @@ struct symbol {
+ * config BAZ
+ * int "BAZ Value"
+ * range 1..255
++ *
++ * Please, also check zconf.y:print_symbol() when modifying the
++ * list of property types!
+ */
+ enum prop_type {
+ P_UNKNOWN,
+ P_PROMPT, /* prompt "foo prompt" or "BAZ Value" */
+ P_COMMENT, /* text associated with a comment */
+- P_MENU, /* prompt associated with a menuconfig option */
++ P_MENU, /* prompt associated with a menu or menuconfig symbol */
+ P_DEFAULT, /* default y */
+ P_CHOICE, /* choice value */
+ P_SELECT, /* select BAR */
+ P_IMPLY, /* imply BAR */
+ P_RANGE, /* range 7..100 (for a symbol) */
+- P_ENV, /* value from environment variable */
+ P_SYMBOL, /* where a symbol is defined */
+ };
+
+@@ -166,22 +211,67 @@ struct property {
+ for (st = sym->prop; st; st = st->next) \
+ if (st->text)
+
++/*
++ * Represents a node in the menu tree, as seen in e.g. menuconfig (though used
++ * for all front ends). Each symbol, menu, etc. defined in the Kconfig files
++ * gets a node. A symbol defined in multiple locations gets one node at each
++ * location.
++ */
+ struct menu {
++ /* The next menu node at the same level */
+ struct menu *next;
++
++ /* The parent menu node, corresponding to e.g. a menu or choice */
+ struct menu *parent;
++
++ /* The first child menu node, for e.g. menus and choices */
+ struct menu *list;
++
++ /*
++ * The symbol associated with the menu node. Choices are implemented as
++ * a special kind of symbol. NULL for menus, comments, and ifs.
++ */
+ struct symbol *sym;
++
++ /*
++ * The prompt associated with the node. This holds the prompt for a
++ * symbol as well as the text for a menu or comment, along with the
++ * type (P_PROMPT, P_MENU, etc.)
++ */
+ struct property *prompt;
++
++ /*
++ * 'visible if' dependencies. If more than one is given, they will be
++ * ANDed together.
++ */
+ struct expr *visibility;
++
++ /*
++ * Ordinary dependencies from e.g. 'depends on' and 'if', ANDed
++ * together
++ */
+ struct expr *dep;
++
++ /* MENU_* flags */
+ unsigned int flags;
++
++ /* Any help text associated with the node */
+ char *help;
++
++ /* The location where the menu node appears in the Kconfig files */
+ struct file *file;
+ int lineno;
++
++ /* For use by front ends that need to store auxiliary data */
+ void *data;
+ };
+
++/*
++ * Set on a menu node when the corresponding symbol changes state in some way.
++ * Can be checked by front ends.
++ */
+ #define MENU_CHANGED 0x0001
++
+ #define MENU_ROOT 0x0002
+
+ struct jump_key {
+@@ -217,11 +307,12 @@ struct expr *expr_transform(struct expr *e);
+ int expr_contains_symbol(struct expr *dep, struct symbol *sym);
+ bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
+ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
+-struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2);
+
+ void expr_fprint(struct expr *e, FILE *out);
+ struct gstr; /* forward */
+ void expr_gstr_print(struct expr *e, struct gstr *gs);
++void expr_gstr_print_revdep(struct expr *e, struct gstr *gs,
++ tristate pr_type, const char *title);
+
+ static inline int expr_is_yes(struct expr *e)
+ {
+diff --git a/carl9170fw/config/kconf_id.c b/carl9170fw/config/kconf_id.c
+deleted file mode 100644
+index 5abbc72..0000000
+--- a/carl9170fw/config/kconf_id.c
++++ /dev/null
+@@ -1,54 +0,0 @@
+-
+-static struct kconf_id kconf_id_array[] = {
+- { "mainmenu", T_MAINMENU, TF_COMMAND },
+- { "menu", T_MENU, TF_COMMAND },
+- { "endmenu", T_ENDMENU, TF_COMMAND },
+- { "source", T_SOURCE, TF_COMMAND },
+- { "choice", T_CHOICE, TF_COMMAND },
+- { "endchoice", T_ENDCHOICE, TF_COMMAND },
+- { "comment", T_COMMENT, TF_COMMAND },
+- { "config", T_CONFIG, TF_COMMAND },
+- { "menuconfig", T_MENUCONFIG, TF_COMMAND },
+- { "help", T_HELP, TF_COMMAND },
+- { "---help---", T_HELP, TF_COMMAND },
+- { "if", T_IF, TF_COMMAND|TF_PARAM },
+- { "endif", T_ENDIF, TF_COMMAND },
+- { "depends", T_DEPENDS, TF_COMMAND },
+- { "optional", T_OPTIONAL, TF_COMMAND },
+- { "default", T_DEFAULT, TF_COMMAND, S_UNKNOWN },
+- { "prompt", T_PROMPT, TF_COMMAND },
+- { "tristate", T_TYPE, TF_COMMAND, S_TRISTATE },
+- { "def_tristate", T_DEFAULT, TF_COMMAND, S_TRISTATE },
+- { "bool", T_TYPE, TF_COMMAND, S_BOOLEAN },
+- { "boolean", T_TYPE, TF_COMMAND, S_BOOLEAN },
+- { "def_bool", T_DEFAULT, TF_COMMAND, S_BOOLEAN },
+- { "int", T_TYPE, TF_COMMAND, S_INT },
+- { "hex", T_TYPE, TF_COMMAND, S_HEX },
+- { "string", T_TYPE, TF_COMMAND, S_STRING },
+- { "select", T_SELECT, TF_COMMAND },
+- { "imply", T_IMPLY, TF_COMMAND },
+- { "range", T_RANGE, TF_COMMAND },
+- { "visible", T_VISIBLE, TF_COMMAND },
+- { "option", T_OPTION, TF_COMMAND },
+- { "on", T_ON, TF_PARAM },
+- { "modules", T_OPT_MODULES, TF_OPTION },
+- { "defconfig_list", T_OPT_DEFCONFIG_LIST, TF_OPTION },
+- { "env", T_OPT_ENV, TF_OPTION },
+- { "allnoconfig_y", T_OPT_ALLNOCONFIG_Y, TF_OPTION },
+-};
+-
+-#define KCONF_ID_ARRAY_SIZE (sizeof(kconf_id_array)/sizeof(struct kconf_id))
+-
+-static const struct kconf_id *kconf_id_lookup(register const char *str, register unsigned int len)
+-{
+- int i;
+-
+- for (i = 0; i < KCONF_ID_ARRAY_SIZE; i++) {
+- struct kconf_id *id = kconf_id_array+i;
+- int l = strlen(id->name);
+-
+- if (len == l && !memcmp(str, id->name, len))
+- return id;
+- }
+- return NULL;
+-}
+diff --git a/carl9170fw/config/lkc.h b/carl9170fw/config/lkc.h
+index cdcbe43..531ff7c 100644
+--- a/carl9170fw/config/lkc.h
++++ b/carl9170fw/config/lkc.h
+@@ -1,6 +1,6 @@
++/* SPDX-License-Identifier: GPL-2.0 */
+ /*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+- * Released under the terms of the GNU GPL v2.0.
+ */
+
+ #ifndef LKC_H
+@@ -8,15 +8,6 @@
+
+ #include "expr.h"
+
+-#ifndef KBUILD_NO_NLS
+-# include <libintl.h>
+-#else
+-static inline const char *gettext(const char *txt) { return txt; }
+-static inline void textdomain(const char *domainname) {}
+-static inline void bindtextdomain(const char *name, const char *dir) {}
+-static inline char *bind_textdomain_codeset(const char *dn, char *c) { return c; }
+-#endif
+-
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+@@ -29,11 +20,6 @@ extern "C" {
+ #define PACKAGE "linux"
+ #endif
+
+-#define LOCALEDIR "/usr/share/locale"
+-
+-#define _(text) gettext(text)
+-#define N_(text) (text)
+-
+ #ifndef CONFIG_
+ #define CONFIG_ "CONFIG_"
+ #endif
+@@ -44,10 +30,6 @@ static inline const char *CONFIG_prefix(void)
+ #undef CONFIG_
+ #define CONFIG_ CONFIG_prefix()
+
+-#define TF_COMMAND 0x0001
+-#define TF_PARAM 0x0002
+-#define TF_OPTION 0x0004
+-
+ enum conf_def_mode {
+ def_default,
+ def_yes,
+@@ -56,18 +38,7 @@ enum conf_def_mode {
+ def_random
+ };
+
+-#define T_OPT_MODULES 1
+-#define T_OPT_DEFCONFIG_LIST 2
+-#define T_OPT_ENV 3
+-#define T_OPT_ALLNOCONFIG_Y 4
+-
+-struct kconf_id {
+- const char *name;
+- int token;
+- unsigned int flags;
+- enum symbol_type stype;
+-};
+-
++extern int yylineno;
+ void zconfdump(FILE *out);
+ void zconf_starthelp(void);
+ FILE *zconf_fopen(const char *name);
+@@ -100,21 +71,27 @@ void menu_warn(struct menu *menu, const char *fmt, ...);
+ struct menu *menu_add_menu(void);
+ void menu_end_menu(void);
+ void menu_add_entry(struct symbol *sym);
+-void menu_end_entry(void);
+ void menu_add_dep(struct expr *dep);
+ void menu_add_visibility(struct expr *dep);
+ struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep);
+ void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep);
+ void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
+-void menu_add_option(int token, char *arg);
++void menu_add_option_modules(void);
++void menu_add_option_defconfig_list(void);
++void menu_add_option_allnoconfig_y(void);
+ void menu_finalize(struct menu *parent);
+ void menu_set_type(int type);
+
+ /* util.c */
+ struct file *file_lookup(const char *name);
+-int file_write_dep(const char *name);
+ void *xmalloc(size_t size);
+ void *xcalloc(size_t nmemb, size_t size);
++void *xrealloc(void *p, size_t size);
++char *xstrdup(const char *s);
++char *xstrndup(const char *s, size_t n);
++
++/* zconf.l */
++int yylex(void);
+
+ struct gstr {
+ size_t len;
+@@ -132,16 +109,13 @@ void str_printf(struct gstr *gs, const char *fmt, ...);
+ const char *str_get(struct gstr *gs);
+
+ /* symbol.c */
+-extern struct expr *sym_env_list;
+-
+-void sym_init(void);
+ void sym_clear_all_valid(void);
+ struct symbol *sym_choice_default(struct symbol *sym);
++struct property *sym_get_range_prop(struct symbol *sym);
+ const char *sym_get_string_default(struct symbol *sym);
+ struct symbol *sym_check_deps(struct symbol *sym);
+ struct property *prop_alloc(enum prop_type type, struct symbol *sym);
+ struct symbol *prop_get_symbol(struct property *prop);
+-struct property *sym_get_env_prop(struct symbol *sym);
+
+ static inline tristate sym_get_tristate_value(struct symbol *sym)
+ {
+diff --git a/carl9170fw/config/lkc_proto.h b/carl9170fw/config/lkc_proto.h
+index 5d86e2d..86c2675 100644
+--- a/carl9170fw/config/lkc_proto.h
++++ b/carl9170fw/config/lkc_proto.h
+@@ -7,10 +7,10 @@ int conf_read(const char *name);
+ int conf_read_simple(const char *name, int);
+ int conf_write_defconfig(const char *name);
+ int conf_write(const char *name);
+-int conf_write_autoconf(void);
++int conf_write_autoconf(int overwrite);
+ bool conf_get_changed(void);
+ void conf_set_changed_callback(void (*fn)(void));
+-void conf_set_message_callback(void (*fn)(const char *fmt, va_list ap));
++void conf_set_message_callback(void (*fn)(const char *s));
+
+ /* menu.c */
+ extern struct menu rootmenu;
+@@ -31,7 +31,6 @@ extern struct symbol * symbol_hash[SYMBOL_HASHSIZE];
+
+ struct symbol * sym_lookup(const char *name, int flags);
+ struct symbol * sym_find(const char *name);
+-const char * sym_expand_string_value(const char *in);
+ const char * sym_escape_string_value(const char *in);
+ struct symbol ** sym_re_search(const char *pattern);
+ const char * sym_type_name(enum symbol_type type);
+@@ -49,5 +48,19 @@ const char * sym_get_string_value(struct symbol *sym);
+
+ const char * prop_get_type_name(enum prop_type type);
+
++/* preprocess.c */
++enum variable_flavor {
++ VAR_SIMPLE,
++ VAR_RECURSIVE,
++ VAR_APPEND,
++};
++void env_write_dep(FILE *f, const char *auto_conf_name);
++void variable_add(const char *name, const char *value,
++ enum variable_flavor flavor);
++void variable_all_del(void);
++char *expand_string(const char *in);
++char *expand_dollar(const char **str);
++char *expand_one_token(const char **str);
++
+ /* expr.c */
+ void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken);
+diff --git a/carl9170fw/config/menu.c b/carl9170fw/config/menu.c
+index e935793..d9d1646 100644
+--- a/carl9170fw/config/menu.c
++++ b/carl9170fw/config/menu.c
+@@ -1,6 +1,6 @@
++// SPDX-License-Identifier: GPL-2.0
+ /*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+- * Released under the terms of the GNU GPL v2.0.
+ */
+
+ #include <ctype.h>
+@@ -62,13 +62,8 @@ void menu_add_entry(struct symbol *sym)
+ menu_add_symbol(P_SYMBOL, sym, NULL);
+ }
+
+-void menu_end_entry(void)
+-{
+-}
+-
+ struct menu *menu_add_menu(void)
+ {
+- menu_end_entry();
+ last_entry_ptr = &current_entry->list;
+ return current_menu = current_entry;
+ }
+@@ -79,19 +74,23 @@ void menu_end_menu(void)
+ current_menu = current_menu->parent;
+ }
+
+-static struct expr *menu_check_dep(struct expr *e)
++/*
++ * Rewrites 'm' to 'm' && MODULES, so that it evaluates to 'n' when running
++ * without modules
++ */
++static struct expr *rewrite_m(struct expr *e)
+ {
+ if (!e)
+ return e;
+
+ switch (e->type) {
+ case E_NOT:
+- e->left.expr = menu_check_dep(e->left.expr);
++ e->left.expr = rewrite_m(e->left.expr);
+ break;
+ case E_OR:
+ case E_AND:
+- e->left.expr = menu_check_dep(e->left.expr);
+- e->right.expr = menu_check_dep(e->right.expr);
++ e->left.expr = rewrite_m(e->left.expr);
++ e->right.expr = rewrite_m(e->right.expr);
+ break;
+ case E_SYMBOL:
+ /* change 'm' into 'm' && MODULES */
+@@ -106,7 +105,7 @@ static struct expr *menu_check_dep(struct expr *e)
+
+ void menu_add_dep(struct expr *dep)
+ {
+- current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep));
++ current_entry->dep = expr_alloc_and(current_entry->dep, dep);
+ }
+
+ void menu_set_type(int type)
+@@ -131,7 +130,7 @@ static struct property *menu_add_prop(enum prop_type type, char *prompt, struct
+
+ prop->menu = current_entry;
+ prop->expr = expr;
+- prop->visible.expr = menu_check_dep(dep);
++ prop->visible.expr = dep;
+
+ if (prompt) {
+ if (isspace(*prompt)) {
+@@ -196,31 +195,26 @@ void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
+ menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep);
+ }
+
+-void menu_add_option(int token, char *arg)
++void menu_add_option_modules(void)
+ {
+- switch (token) {
+- case T_OPT_MODULES:
+- if (modules_sym)
+- zconf_error("symbol '%s' redefines option 'modules'"
+- " already defined by symbol '%s'",
+- current_entry->sym->name,
+- modules_sym->name
+- );
+- modules_sym = current_entry->sym;
+- break;
+- case T_OPT_DEFCONFIG_LIST:
+- if (!sym_defconfig_list)
+- sym_defconfig_list = current_entry->sym;
+- else if (sym_defconfig_list != current_entry->sym)
+- zconf_error("trying to redefine defconfig symbol");
+- break;
+- case T_OPT_ENV:
+- prop_add_env(arg);
+- break;
+- case T_OPT_ALLNOCONFIG_Y:
+- current_entry->sym->flags |= SYMBOL_ALLNOCONFIG_Y;
+- break;
+- }
++ if (modules_sym)
++ zconf_error("symbol '%s' redefines option 'modules' already defined by symbol '%s'",
++ current_entry->sym->name, modules_sym->name);
++ modules_sym = current_entry->sym;
++}
++
++void menu_add_option_defconfig_list(void)
++{
++ if (!sym_defconfig_list)
++ sym_defconfig_list = current_entry->sym;
++ else if (sym_defconfig_list != current_entry->sym)
++ zconf_error("trying to redefine defconfig symbol");
++ sym_defconfig_list->flags |= SYMBOL_NO_WRITE;
++}
++
++void menu_add_option_allnoconfig_y(void)
++{
++ current_entry->sym->flags |= SYMBOL_ALLNOCONFIG_Y;
+ }
+
+ static int menu_validate_number(struct symbol *sym, struct symbol *sym2)
+@@ -252,6 +246,16 @@ static void sym_check_prop(struct symbol *sym)
+ "'%s': number is invalid",
+ sym->name);
+ }
++ if (sym_is_choice(sym)) {
++ struct property *choice_prop =
++ sym_get_choice_prop(sym2);
++
++ if (!choice_prop ||
++ prop_get_symbol(choice_prop) != sym)
++ prop_warn(prop,
++ "choice default symbol '%s' is not contained in the choice",
++ sym2->name);
++ }
+ break;
+ case P_SELECT:
+ case P_IMPLY:
+@@ -260,13 +264,13 @@ static void sym_check_prop(struct symbol *sym)
+ if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)
+ prop_warn(prop,
+ "config symbol '%s' uses %s, but is "
+- "not boolean or tristate", sym->name, use);
++ "not bool or tristate", sym->name, use);
+ else if (sym2->type != S_UNKNOWN &&
+ sym2->type != S_BOOLEAN &&
+ sym2->type != S_TRISTATE)
+ prop_warn(prop,
+ "'%s' has wrong type. '%s' only "
+- "accept arguments of boolean and "
++ "accept arguments of bool and "
+ "tristate type", sym2->name, use);
+ break;
+ case P_RANGE:
+@@ -292,6 +296,11 @@ void menu_finalize(struct menu *parent)
+
+ sym = parent->sym;
+ if (parent->list) {
++ /*
++ * This menu node has children. We (recursively) process them
++ * and propagate parent dependencies before moving on.
++ */
++
+ if (sym && sym_is_choice(sym)) {
+ if (sym->type == S_UNKNOWN) {
+ /* find the first choice value to find out choice type */
+@@ -309,30 +318,83 @@ void menu_finalize(struct menu *parent)
+ if (menu->sym && menu->sym->type == S_UNKNOWN)
+ menu_set_type(sym->type);
+ }
++
++ /*
++ * Use the choice itself as the parent dependency of
++ * the contained items. This turns the mode of the
++ * choice into an upper bound on the visibility of the
++ * choice value symbols.
++ */
+ parentdep = expr_alloc_symbol(sym);
+ } else if (parent->prompt)
++ /* Menu node for 'menu' */
+ parentdep = parent->prompt->visible.expr;
+ else
++ /* Menu node for 'if' */
+ parentdep = parent->dep;
+
++ /* For each child menu node... */
+ for (menu = parent->list; menu; menu = menu->next) {
+- basedep = expr_transform(menu->dep);
++ /*
++ * Propagate parent dependencies to the child menu
++ * node, also rewriting and simplifying expressions
++ */
++ basedep = rewrite_m(menu->dep);
++ basedep = expr_transform(basedep);
+ basedep = expr_alloc_and(expr_copy(parentdep), basedep);
+ basedep = expr_eliminate_dups(basedep);
+ menu->dep = basedep;
++
+ if (menu->sym)
++ /*
++ * Note: For symbols, all prompts are included
++ * too in the symbol's own property list
++ */
+ prop = menu->sym->prop;
+ else
++ /*
++ * For non-symbol menu nodes, we just need to
++ * handle the prompt
++ */
+ prop = menu->prompt;
++
++ /* For each property... */
+ for (; prop; prop = prop->next) {
+ if (prop->menu != menu)
++ /*
++ * Two possibilities:
++ *
++ * 1. The property lacks dependencies
++ * and so isn't location-specific,
++ * e.g. an 'option'
++ *
++ * 2. The property belongs to a symbol
++ * defined in multiple locations and
++ * is from some other location. It
++ * will be handled there in that
++ * case.
++ *
++ * Skip the property.
++ */
+ continue;
+- dep = expr_transform(prop->visible.expr);
++
++ /*
++ * Propagate parent dependencies to the
++ * property's condition, rewriting and
++ * simplifying expressions at the same time
++ */
++ dep = rewrite_m(prop->visible.expr);
++ dep = expr_transform(dep);
+ dep = expr_alloc_and(expr_copy(basedep), dep);
+ dep = expr_eliminate_dups(dep);
+ if (menu->sym && menu->sym->type != S_TRISTATE)
+ dep = expr_trans_bool(dep);
+ prop->visible.expr = dep;
++
++ /*
++ * Handle selects and implies, which modify the
++ * dependencies of the selected/implied symbol
++ */
+ if (prop->type == P_SELECT) {
+ struct symbol *es = prop_get_symbol(prop);
+ es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
+@@ -344,34 +406,81 @@ void menu_finalize(struct menu *parent)
+ }
+ }
+ }
++
++ if (sym && sym_is_choice(sym))
++ expr_free(parentdep);
++
++ /*
++ * Recursively process children in the same fashion before
++ * moving on
++ */
+ for (menu = parent->list; menu; menu = menu->next)
+ menu_finalize(menu);
+ } else if (sym) {
++ /*
++ * Automatic submenu creation. If sym is a symbol and A, B, C,
++ * ... are consecutive items (symbols, menus, ifs, etc.) that
++ * all depend on sym, then the following menu structure is
++ * created:
++ *
++ * sym
++ * +-A
++ * +-B
++ * +-C
++ * ...
++ *
++ * This also works recursively, giving the following structure
++ * if A is a symbol and B depends on A:
++ *
++ * sym
++ * +-A
++ * | +-B
++ * +-C
++ * ...
++ */
++
+ basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
+ basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
+ basedep = expr_eliminate_dups(expr_transform(basedep));
++
++ /* Examine consecutive elements after sym */
+ last_menu = NULL;
+ for (menu = parent->next; menu; menu = menu->next) {
+ dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
+ if (!expr_contains_symbol(dep, sym))
++ /* No dependency, quit */
+ break;
+ if (expr_depends_symbol(dep, sym))
++ /* Absolute dependency, put in submenu */
+ goto next;
++
++ /*
++ * Also consider it a dependency on sym if our
++ * dependencies contain sym and are a "superset" of
++ * sym's dependencies, e.g. '(sym || Q) && R' when sym
++ * depends on R.
++ *
++ * Note that 'R' might be from an enclosing menu or if,
++ * making this a more common case than it might seem.
++ */
+ dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
+ dep = expr_eliminate_dups(expr_transform(dep));
+ dep2 = expr_copy(basedep);
+ expr_eliminate_eq(&dep, &dep2);
+ expr_free(dep);
+ if (!expr_is_yes(dep2)) {
++ /* Not superset, quit */
+ expr_free(dep2);
+ break;
+ }
++ /* Superset, put in submenu */
+ expr_free(dep2);
+ next:
+ menu_finalize(menu);
+ menu->parent = parent;
+ last_menu = menu;
+ }
++ expr_free(basedep);
+ if (last_menu) {
+ parent->list = parent->next;
+ parent->next = last_menu->next;
+@@ -420,6 +529,35 @@ void menu_finalize(struct menu *parent)
+ *ep = expr_alloc_one(E_LIST, NULL);
+ (*ep)->right.sym = menu->sym;
+ }
++
++ /*
++ * This code serves two purposes:
++ *
++ * (1) Flattening 'if' blocks, which do not specify a submenu
++ * and only add dependencies.
++ *
++ * (Automatic submenu creation might still create a submenu
++ * from an 'if' before this code runs.)
++ *
++ * (2) "Undoing" any automatic submenus created earlier below
++ * promptless symbols.
++ *
++ * Before:
++ *
++ * A
++ * if ... (or promptless symbol)
++ * +-B
++ * +-C
++ * D
++ *
++ * After:
++ *
++ * A
++ * if ... (or promptless symbol)
++ * B
++ * C
++ * D
++ */
+ if (menu->list && (!menu->prompt || !menu->prompt->text)) {
+ for (last_menu = menu->list; ; last_menu = last_menu->next) {
+ last_menu->parent = parent;
+@@ -444,6 +582,15 @@ void menu_finalize(struct menu *parent)
+ sym->flags |= SYMBOL_WARNED;
+ }
+
++ /*
++ * For non-optional choices, add a reverse dependency (corresponding to
++ * a select) of '<visibility> && m'. This prevents the user from
++ * setting the choice mode to 'n' when the choice is visible.
++ *
++ * This would also work for non-choice symbols, but only non-optional
++ * choices clear SYMBOL_OPTIONAL as of writing. Choices are implemented
++ * as a type of symbol.
++ */
+ if (sym && !sym_is_optional(sym) && parent->prompt) {
+ sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
+ expr_alloc_and(parent->prompt->visible.expr,
+@@ -558,7 +705,7 @@ static void get_prompt_str(struct gstr *r, struct property *prop,
+ struct menu *submenu[8], *menu, *location = NULL;
+ struct jump_key *jump = NULL;
+
+- str_printf(r, _("Prompt: %s\n"), _(prop->text));
++ str_printf(r, "Prompt: %s\n", prop->text);
+ menu = prop->menu->parent;
+ for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) {
+ bool accessible = menu_is_visible(menu);
+@@ -591,16 +738,16 @@ static void get_prompt_str(struct gstr *r, struct property *prop,
+ }
+
+ if (i > 0) {
+- str_printf(r, _(" Location:\n"));
++ str_printf(r, " Location:\n");
+ for (j = 4; --i >= 0; j += 2) {
+ menu = submenu[i];
+ if (jump && menu == location)
+ jump->offset = strlen(r->s);
+ str_printf(r, "%*c-> %s", j, ' ',
+- _(menu_get_prompt(menu)));
++ menu_get_prompt(menu));
+ if (menu->sym) {
+ str_printf(r, " (%s [=%s])", menu->sym->name ?
+- menu->sym->name : _("<choice>"),
++ menu->sym->name : "<choice>",
+ sym_get_string_value(menu->sym));
+ }
+ str_append(r, "\n");
+@@ -664,27 +811,27 @@ static void get_symbol_str(struct gstr *r, struct symbol *sym,
+
+ prop = get_symbol_prop(sym);
+ if (prop) {
+- str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name,
++ str_printf(r, " Defined at %s:%d\n", prop->menu->file->name,
+ prop->menu->lineno);
+ if (!expr_is_yes(prop->visible.expr)) {
+- str_append(r, _(" Depends on: "));
++ str_append(r, " Depends on: ");
+ expr_gstr_print(prop->visible.expr, r);
+ str_append(r, "\n");
+ }
+ }
+
+- get_symbol_props_str(r, sym, P_SELECT, _(" Selects: "));
++ get_symbol_props_str(r, sym, P_SELECT, " Selects: ");
+ if (sym->rev_dep.expr) {
+- str_append(r, _(" Selected by: "));
+- expr_gstr_print(sym->rev_dep.expr, r);
+- str_append(r, "\n");
++ expr_gstr_print_revdep(sym->rev_dep.expr, r, yes, " Selected by [y]:\n");
++ expr_gstr_print_revdep(sym->rev_dep.expr, r, mod, " Selected by [m]:\n");
++ expr_gstr_print_revdep(sym->rev_dep.expr, r, no, " Selected by [n]:\n");
+ }
+
+- get_symbol_props_str(r, sym, P_IMPLY, _(" Implies: "));
++ get_symbol_props_str(r, sym, P_IMPLY, " Implies: ");
+ if (sym->implied.expr) {
+- str_append(r, _(" Implied by: "));
+- expr_gstr_print(sym->implied.expr, r);
+- str_append(r, "\n");
++ expr_gstr_print_revdep(sym->implied.expr, r, yes, " Implied by [y]:\n");
++ expr_gstr_print_revdep(sym->implied.expr, r, mod, " Implied by [m]:\n");
++ expr_gstr_print_revdep(sym->implied.expr, r, no, " Implied by [n]:\n");
+ }
+
+ str_append(r, "\n\n");
+@@ -699,7 +846,7 @@ struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head)
+ for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
+ get_symbol_str(&res, sym, head);
+ if (!i)
+- str_append(&res, _("No matches found.\n"));
++ str_append(&res, "No matches found.\n");
+ return res;
+ }
+
+@@ -714,7 +861,7 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help)
+ str_printf(help, "%s%s:\n\n", CONFIG_, sym->name);
+ help_text = menu_get_help(menu);
+ }
+- str_printf(help, "%s\n", _(help_text));
++ str_printf(help, "%s\n", help_text);
+ if (sym)
+ get_symbol_str(help, sym, NULL);
+ }
+diff --git a/carl9170fw/config/preprocess.c b/carl9170fw/config/preprocess.c
+new file mode 100644
+index 0000000..592dfbf
+--- /dev/null
++++ b/carl9170fw/config/preprocess.c
+@@ -0,0 +1,573 @@
++// SPDX-License-Identifier: GPL-2.0
++//
++// Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com>
++
++#include <ctype.h>
++#include <stdarg.h>
++#include <stdbool.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include "list.h"
++#include "lkc.h"
++
++#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
++
++static char *expand_string_with_args(const char *in, int argc, char *argv[]);
++
++static void __attribute__((noreturn)) pperror(const char *format, ...)
++{
++ va_list ap;
++
++ fprintf(stderr, "%s:%d: ", current_file->name, yylineno);
++ va_start(ap, format);
++ vfprintf(stderr, format, ap);
++ va_end(ap);
++ fprintf(stderr, "\n");
++
++ exit(1);
++}
++
++/*
++ * Environment variables
++ */
++static LIST_HEAD(env_list);
++
++struct env {
++ char *name;
++ char *value;
++ struct list_head node;
++};
++
++static void env_add(const char *name, const char *value)
++{
++ struct env *e;
++
++ e = xmalloc(sizeof(*e));
++ e->name = xstrdup(name);
++ e->value = xstrdup(value);
++
++ list_add_tail(&e->node, &env_list);
++}
++
++static void env_del(struct env *e)
++{
++ list_del(&e->node);
++ free(e->name);
++ free(e->value);
++ free(e);
++}
++
++/* The returned pointer must be freed when done */
++static char *env_expand(const char *name)
++{
++ struct env *e;
++ const char *value;
++
++ if (!*name)
++ return NULL;
++
++ list_for_each_entry(e, &env_list, node) {
++ if (!strcmp(name, e->name))
++ return xstrdup(e->value);
++ }
++
++ value = getenv(name);
++ if (!value)
++ return NULL;
++
++ /*
++ * We need to remember all referenced environment variables.
++ * They will be written out to include/config/auto.conf.cmd
++ */
++ env_add(name, value);
++
++ return xstrdup(value);
++}
++
++void env_write_dep(FILE *f, const char *autoconfig_name)
++{
++ struct env *e, *tmp;
++
++ list_for_each_entry_safe(e, tmp, &env_list, node) {
++ fprintf(f, "ifneq \"$(%s)\" \"%s\"\n", e->name, e->value);
++ fprintf(f, "%s: FORCE\n", autoconfig_name);
++ fprintf(f, "endif\n");
++ env_del(e);
++ }
++}
++
++/*
++ * Built-in functions
++ */
++struct function {
++ const char *name;
++ unsigned int min_args;
++ unsigned int max_args;
++ char *(*func)(int argc, char *argv[]);
++};
++
++static char *do_error_if(int argc, char *argv[])
++{
++ if (!strcmp(argv[0], "y"))
++ pperror("%s", argv[1]);
++
++ return NULL;
++}
++
++static char *do_filename(int argc, char *argv[])
++{
++ return xstrdup(current_file->name);
++}
++
++static char *do_info(int argc, char *argv[])
++{
++ printf("%s\n", argv[0]);
++
++ return xstrdup("");
++}
++
++static char *do_lineno(int argc, char *argv[])
++{
++ char buf[16];
++
++ sprintf(buf, "%d", yylineno);
++
++ return xstrdup(buf);
++}
++
++static char *do_shell(int argc, char *argv[])
++{
++ FILE *p;
++ char buf[256];
++ char *cmd;
++ size_t nread;
++ int i;
++
++ cmd = argv[0];
++
++ p = popen(cmd, "r");
++ if (!p) {
++ perror(cmd);
++ exit(1);
++ }
++
++ nread = fread(buf, 1, sizeof(buf), p);
++ if (nread == sizeof(buf))
++ nread--;
++
++ /* remove trailing new lines */
++ while (nread > 0 && buf[nread - 1] == '\n')
++ nread--;
++
++ buf[nread] = 0;
++
++ /* replace a new line with a space */
++ for (i = 0; i < nread; i++) {
++ if (buf[i] == '\n')
++ buf[i] = ' ';
++ }
++
++ if (pclose(p) == -1) {
++ perror(cmd);
++ exit(1);
++ }
++
++ return xstrdup(buf);
++}
++
++static char *do_warning_if(int argc, char *argv[])
++{
++ if (!strcmp(argv[0], "y"))
++ fprintf(stderr, "%s:%d: %s\n",
++ current_file->name, yylineno, argv[1]);
++
++ return xstrdup("");
++}
++
++static const struct function function_table[] = {
++ /* Name MIN MAX Function */
++ { "error-if", 2, 2, do_error_if },
++ { "filename", 0, 0, do_filename },
++ { "info", 1, 1, do_info },
++ { "lineno", 0, 0, do_lineno },
++ { "shell", 1, 1, do_shell },
++ { "warning-if", 2, 2, do_warning_if },
++};
++
++#define FUNCTION_MAX_ARGS 16
++
++static char *function_expand(const char *name, int argc, char *argv[])
++{
++ const struct function *f;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(function_table); i++) {
++ f = &function_table[i];
++ if (strcmp(f->name, name))
++ continue;
++
++ if (argc < f->min_args)
++ pperror("too few function arguments passed to '%s'",
++ name);
++
++ if (argc > f->max_args)
++ pperror("too many function arguments passed to '%s'",
++ name);
++
++ return f->func(argc, argv);
++ }
++
++ return NULL;
++}
++
++/*
++ * Variables (and user-defined functions)
++ */
++static LIST_HEAD(variable_list);
++
++struct variable {
++ char *name;
++ char *value;
++ enum variable_flavor flavor;
++ int exp_count;
++ struct list_head node;
++};
++
++static struct variable *variable_lookup(const char *name)
++{
++ struct variable *v;
++
++ list_for_each_entry(v, &variable_list, node) {
++ if (!strcmp(name, v->name))
++ return v;
++ }
++
++ return NULL;
++}
++
++static char *variable_expand(const char *name, int argc, char *argv[])
++{
++ struct variable *v;
++ char *res;
++
++ v = variable_lookup(name);
++ if (!v)
++ return NULL;
++
++ if (argc == 0 && v->exp_count)
++ pperror("Recursive variable '%s' references itself (eventually)",
++ name);
++
++ if (v->exp_count > 1000)
++ pperror("Too deep recursive expansion");
++
++ v->exp_count++;
++
++ if (v->flavor == VAR_RECURSIVE)
++ res = expand_string_with_args(v->value, argc, argv);
++ else
++ res = xstrdup(v->value);
++
++ v->exp_count--;
++
++ return res;
++}
++
++void variable_add(const char *name, const char *value,
++ enum variable_flavor flavor)
++{
++ struct variable *v;
++ char *new_value;
++ bool append = false;
++
++ v = variable_lookup(name);
++ if (v) {
++ /* For defined variables, += inherits the existing flavor */
++ if (flavor == VAR_APPEND) {
++ flavor = v->flavor;
++ append = true;
++ } else {
++ free(v->value);
++ }
++ } else {
++ /* For undefined variables, += assumes the recursive flavor */
++ if (flavor == VAR_APPEND)
++ flavor = VAR_RECURSIVE;
++
++ v = xmalloc(sizeof(*v));
++ v->name = xstrdup(name);
++ v->exp_count = 0;
++ list_add_tail(&v->node, &variable_list);
++ }
++
++ v->flavor = flavor;
++
++ if (flavor == VAR_SIMPLE)
++ new_value = expand_string(value);
++ else
++ new_value = xstrdup(value);
++
++ if (append) {
++ v->value = xrealloc(v->value,
++ strlen(v->value) + strlen(new_value) + 2);
++ strcat(v->value, " ");
++ strcat(v->value, new_value);
++ free(new_value);
++ } else {
++ v->value = new_value;
++ }
++}
++
++static void variable_del(struct variable *v)
++{
++ list_del(&v->node);
++ free(v->name);
++ free(v->value);
++ free(v);
++}
++
++void variable_all_del(void)
++{
++ struct variable *v, *tmp;
++
++ list_for_each_entry_safe(v, tmp, &variable_list, node)
++ variable_del(v);
++}
++
++/*
++ * Evaluate a clause with arguments. argc/argv are arguments from the upper
++ * function call.
++ *
++ * Returned string must be freed when done
++ */
++static char *eval_clause(const char *str, size_t len, int argc, char *argv[])
++{
++ char *tmp, *name, *res, *endptr, *prev, *p;
++ int new_argc = 0;
++ char *new_argv[FUNCTION_MAX_ARGS];
++ int nest = 0;
++ int i;
++ unsigned long n;
++
++ tmp = xstrndup(str, len);
++
++ /*
++ * If variable name is '1', '2', etc. It is generally an argument
++ * from a user-function call (i.e. local-scope variable). If not
++ * available, then look-up global-scope variables.
++ */
++ n = strtoul(tmp, &endptr, 10);
++ if (!*endptr && n > 0 && n <= argc) {
++ res = xstrdup(argv[n - 1]);
++ goto free_tmp;
++ }
++
++ prev = p = tmp;
++
++ /*
++ * Split into tokens
++ * The function name and arguments are separated by a comma.
++ * For example, if the function call is like this:
++ * $(foo,$(x),$(y))
++ *
++ * The input string for this helper should be:
++ * foo,$(x),$(y)
++ *
++ * and split into:
++ * new_argv[0] = 'foo'
++ * new_argv[1] = '$(x)'
++ * new_argv[2] = '$(y)'
++ */
++ while (*p) {
++ if (nest == 0 && *p == ',') {
++ *p = 0;
++ if (new_argc >= FUNCTION_MAX_ARGS)
++ pperror("too many function arguments");
++ new_argv[new_argc++] = prev;
++ prev = p + 1;
++ } else if (*p == '(') {
++ nest++;
++ } else if (*p == ')') {
++ nest--;
++ }
++
++ p++;
++ }
++ new_argv[new_argc++] = prev;
++
++ /*
++ * Shift arguments
++ * new_argv[0] represents a function name or a variable name. Put it
++ * into 'name', then shift the rest of the arguments. This simplifies
++ * 'const' handling.
++ */
++ name = expand_string_with_args(new_argv[0], argc, argv);
++ new_argc--;
++ for (i = 0; i < new_argc; i++)
++ new_argv[i] = expand_string_with_args(new_argv[i + 1],
++ argc, argv);
++
++ /* Search for variables */
++ res = variable_expand(name, new_argc, new_argv);
++ if (res)
++ goto free;
++
++ /* Look for built-in functions */
++ res = function_expand(name, new_argc, new_argv);
++ if (res)
++ goto free;
++
++ /* Last, try environment variable */
++ if (new_argc == 0) {
++ res = env_expand(name);
++ if (res)
++ goto free;
++ }
++
++ res = xstrdup("");
++free:
++ for (i = 0; i < new_argc; i++)
++ free(new_argv[i]);
++ free(name);
++free_tmp:
++ free(tmp);
++
++ return res;
++}
++
++/*
++ * Expand a string that follows '$'
++ *
++ * For example, if the input string is
++ * ($(FOO)$($(BAR)))$(BAZ)
++ * this helper evaluates
++ * $($(FOO)$($(BAR)))
++ * and returns a new string containing the expansion (note that the string is
++ * recursively expanded), also advancing 'str' to point to the next character
++ * after the corresponding closing parenthesis, in this case, *str will be
++ * $(BAR)
++ */
++static char *expand_dollar_with_args(const char **str, int argc, char *argv[])
++{
++ const char *p = *str;
++ const char *q;
++ int nest = 0;
++
++ /*
++ * In Kconfig, variable/function references always start with "$(".
++ * Neither single-letter variables as in $A nor curly braces as in ${CC}
++ * are supported. '$' not followed by '(' loses its special meaning.
++ */
++ if (*p != '(') {
++ *str = p;
++ return xstrdup("$");
++ }
++
++ p++;
++ q = p;
++ while (*q) {
++ if (*q == '(') {
++ nest++;
++ } else if (*q == ')') {
++ if (nest-- == 0)
++ break;
++ }
++ q++;
++ }
++
++ if (!*q)
++ pperror("unterminated reference to '%s': missing ')'", p);
++
++ /* Advance 'str' to after the expanded initial portion of the string */
++ *str = q + 1;
++
++ return eval_clause(p, q - p, argc, argv);
++}
++
++char *expand_dollar(const char **str)
++{
++ return expand_dollar_with_args(str, 0, NULL);
++}
++
++static char *__expand_string(const char **str, bool (*is_end)(char c),
++ int argc, char *argv[])
++{
++ const char *in, *p;
++ char *expansion, *out;
++ size_t in_len, out_len;
++
++ out = xmalloc(1);
++ *out = 0;
++ out_len = 1;
++
++ p = in = *str;
++
++ while (1) {
++ if (*p == '$') {
++ in_len = p - in;
++ p++;
++ expansion = expand_dollar_with_args(&p, argc, argv);
++ out_len += in_len + strlen(expansion);
++ out = xrealloc(out, out_len);
++ strncat(out, in, in_len);
++ strcat(out, expansion);
++ free(expansion);
++ in = p;
++ continue;
++ }
++
++ if (is_end(*p))
++ break;
++
++ p++;
++ }
++
++ in_len = p - in;
++ out_len += in_len;
++ out = xrealloc(out, out_len);
++ strncat(out, in, in_len);
++
++ /* Advance 'str' to the end character */
++ *str = p;
++
++ return out;
++}
++
++static bool is_end_of_str(char c)
++{
++ return !c;
++}
++
++/*
++ * Expand variables and functions in the given string. Undefined variables
++ * expand to an empty string.
++ * The returned string must be freed when done.
++ */
++static char *expand_string_with_args(const char *in, int argc, char *argv[])
++{
++ return __expand_string(&in, is_end_of_str, argc, argv);
++}
++
++char *expand_string(const char *in)
++{
++ return expand_string_with_args(in, 0, NULL);
++}
++
++static bool is_end_of_token(char c)
++{
++ return !(isalnum(c) || c == '_' || c == '-');
++}
++
++/*
++ * Expand variables in a token. The parsing stops when a token separater
++ * (in most cases, it is a whitespace) is encountered. 'str' is updated to
++ * point to the next character.
++ *
++ * The returned string must be freed when done.
++ */
++char *expand_one_token(const char **str)
++{
++ return __expand_string(str, is_end_of_token, 0, NULL);
++}
+diff --git a/carl9170fw/config/symbol.c b/carl9170fw/config/symbol.c
+index 3c8bd9b..1f9266d 100644
+--- a/carl9170fw/config/symbol.c
++++ b/carl9170fw/config/symbol.c
+@@ -1,6 +1,6 @@
++// SPDX-License-Identifier: GPL-2.0
+ /*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+- * Released under the terms of the GNU GPL v2.0.
+ */
+
+ #include <ctype.h>
+@@ -33,33 +33,6 @@ struct symbol *sym_defconfig_list;
+ struct symbol *modules_sym;
+ tristate modules_val;
+
+-struct expr *sym_env_list;
+-
+-static void sym_add_default(struct symbol *sym, const char *def)
+-{
+- struct property *prop = prop_alloc(P_DEFAULT, sym);
+-
+- prop->expr = expr_alloc_symbol(sym_lookup(def, SYMBOL_CONST));
+-}
+-
+-void sym_init(void)
+-{
+- struct symbol *sym;
+- struct utsname uts;
+- static bool inited = false;
+-
+- if (inited)
+- return;
+- inited = true;
+-
+- uname(&uts);
+-
+- sym = sym_lookup("UNAME_RELEASE", 0);
+- sym->type = S_STRING;
+- sym->flags |= SYMBOL_AUTO;
+- sym_add_default(sym, uts.release);
+-}
+-
+ enum symbol_type sym_get_type(struct symbol *sym)
+ {
+ enum symbol_type type = sym->type;
+@@ -77,7 +50,7 @@ const char *sym_type_name(enum symbol_type type)
+ {
+ switch (type) {
+ case S_BOOLEAN:
+- return "boolean";
++ return "bool";
+ case S_TRISTATE:
+ return "tristate";
+ case S_INT:
+@@ -88,8 +61,6 @@ const char *sym_type_name(enum symbol_type type)
+ return "string";
+ case S_UNKNOWN:
+ return "unknown";
+- case S_OTHER:
+- break;
+ }
+ return "???";
+ }
+@@ -103,15 +74,6 @@ struct property *sym_get_choice_prop(struct symbol *sym)
+ return NULL;
+ }
+
+-struct property *sym_get_env_prop(struct symbol *sym)
+-{
+- struct property *prop;
+-
+- for_all_properties(sym, prop, P_ENV)
+- return prop;
+- return NULL;
+-}
+-
+ static struct property *sym_get_default_prop(struct symbol *sym)
+ {
+ struct property *prop;
+@@ -124,7 +86,7 @@ static struct property *sym_get_default_prop(struct symbol *sym)
+ return NULL;
+ }
+
+-static struct property *sym_get_range_prop(struct symbol *sym)
++struct property *sym_get_range_prop(struct symbol *sym)
+ {
+ struct property *prop;
+
+@@ -183,7 +145,7 @@ static void sym_validate_range(struct symbol *sym)
+ sprintf(str, "%lld", val2);
+ else
+ sprintf(str, "0x%llx", val2);
+- sym->curr.val = strdup(str);
++ sym->curr.val = xstrdup(str);
+ }
+
+ static void sym_set_changed(struct symbol *sym)
+@@ -243,7 +205,7 @@ static void sym_calc_visibility(struct symbol *sym)
+ tri = yes;
+ if (sym->dir_dep.expr)
+ tri = expr_calc_value(sym->dir_dep.expr);
+- if (tri == mod)
++ if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
+ tri = yes;
+ if (sym->dir_dep.tri != tri) {
+ sym->dir_dep.tri = tri;
+@@ -333,6 +295,27 @@ static struct symbol *sym_calc_choice(struct symbol *sym)
+ return def_sym;
+ }
+
++static void sym_warn_unmet_dep(struct symbol *sym)
++{
++ struct gstr gs = str_new();
++
++ str_printf(&gs,
++ "\nWARNING: unmet direct dependencies detected for %s\n",
++ sym->name);
++ str_printf(&gs,
++ " Depends on [%c]: ",
++ sym->dir_dep.tri == mod ? 'm' : 'n');
++ expr_gstr_print(sym->dir_dep.expr, &gs);
++ str_printf(&gs, "\n");
++
++ expr_gstr_print_revdep(sym->rev_dep.expr, &gs, yes,
++ " Selected by [y]:\n");
++ expr_gstr_print_revdep(sym->rev_dep.expr, &gs, mod,
++ " Selected by [m]:\n");
++
++ fputs(str_get(&gs), stderr);
++}
++
+ void sym_calc_value(struct symbol *sym)
+ {
+ struct symbol_value newval, oldval;
+@@ -371,11 +354,13 @@ void sym_calc_value(struct symbol *sym)
+ sym->curr.tri = no;
+ return;
+ }
+- if (!sym_is_choice_value(sym))
+- sym->flags &= ~SYMBOL_WRITE;
++ sym->flags &= ~SYMBOL_WRITE;
+
+ sym_calc_visibility(sym);
+
++ if (sym->visible != no)
++ sym->flags |= SYMBOL_WRITE;
++
+ /* set default if recursively called */
+ sym->curr = newval;
+
+@@ -390,7 +375,6 @@ void sym_calc_value(struct symbol *sym)
+ /* if the symbol is visible use the user value
+ * if available, otherwise try the default value
+ */
+- sym->flags |= SYMBOL_WRITE;
+ if (sym_has_value(sym)) {
+ newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri,
+ sym->visible);
+@@ -402,9 +386,10 @@ void sym_calc_value(struct symbol *sym)
+ if (!sym_is_choice(sym)) {
+ prop = sym_get_default_prop(sym);
+ if (prop) {
+- sym->flags |= SYMBOL_WRITE;
+ newval.tri = EXPR_AND(expr_calc_value(prop->expr),
+ prop->visible.tri);
++ if (newval.tri != no)
++ sym->flags |= SYMBOL_WRITE;
+ }
+ if (sym->implied.tri != no) {
+ sym->flags |= SYMBOL_WRITE;
+@@ -412,18 +397,8 @@ void sym_calc_value(struct symbol *sym)
+ }
+ }
+ calc_newval:
+- if (sym->dir_dep.tri == no && sym->rev_dep.tri != no) {
+- struct expr *e;
+- e = expr_simplify_unmet_dep(sym->rev_dep.expr,
+- sym->dir_dep.expr);
+- fprintf(stderr, "warning: (");
+- expr_fprint(e, stderr);
+- fprintf(stderr, ") selects %s which has unmet direct dependencies (",
+- sym->name);
+- expr_fprint(sym->dir_dep.expr, stderr);
+- fprintf(stderr, ")\n");
+- expr_free(e);
+- }
++ if (sym->dir_dep.tri < sym->rev_dep.tri)
++ sym_warn_unmet_dep(sym);
+ newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri);
+ }
+ if (newval.tri == mod &&
+@@ -433,12 +408,9 @@ void sym_calc_value(struct symbol *sym)
+ case S_STRING:
+ case S_HEX:
+ case S_INT:
+- if (sym->visible != no) {
+- sym->flags |= SYMBOL_WRITE;
+- if (sym_has_value(sym)) {
+- newval.val = sym->def[S_DEF_USER].val;
+- break;
+- }
++ if (sym->visible != no && sym_has_value(sym)) {
++ newval.val = sym->def[S_DEF_USER].val;
++ break;
+ }
+ prop = sym_get_default_prop(sym);
+ if (prop) {
+@@ -480,7 +452,7 @@ void sym_calc_value(struct symbol *sym)
+ }
+ }
+
+- if (sym->flags & SYMBOL_AUTO)
++ if (sym->flags & SYMBOL_NO_WRITE)
+ sym->flags &= ~SYMBOL_WRITE;
+
+ if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES)
+@@ -783,7 +755,6 @@ const char *sym_get_string_default(struct symbol *sym)
+ return str;
+ case S_STRING:
+ return str;
+- case S_OTHER:
+ case S_UNKNOWN:
+ break;
+ }
+@@ -851,7 +822,7 @@ struct symbol *sym_lookup(const char *name, int flags)
+ : !(symbol->flags & (SYMBOL_CONST|SYMBOL_CHOICE))))
+ return symbol;
+ }
+- new_name = strdup(name);
++ new_name = xstrdup(name);
+ } else {
+ new_name = NULL;
+ hash = 0;
+@@ -896,55 +867,6 @@ struct symbol *sym_find(const char *name)
+ return symbol;
+ }
+
+-/*
+- * Expand symbol's names embedded in the string given in argument. Symbols'
+- * name to be expanded shall be prefixed by a '$'. Unknown symbol expands to
+- * the empty string.
+- */
+-const char *sym_expand_string_value(const char *in)
+-{
+- const char *src;
+- char *res;
+- size_t reslen;
+-
+- reslen = strlen(in) + 1;
+- res = xmalloc(reslen);
+- res[0] = '\0';
+-
+- while ((src = strchr(in, '$'))) {
+- char *p, name[SYMBOL_MAXLENGTH];
+- const char *symval = "";
+- struct symbol *sym;
+- size_t newlen;
+-
+- strncat(res, in, src - in);
+- src++;
+-
+- p = name;
+- while (isalnum(*src) || *src == '_')
+- *p++ = *src++;
+- *p = '\0';
+-
+- sym = sym_find(name);
+- if (sym != NULL) {
+- sym_calc_value(sym);
+- symval = sym_get_string_value(sym);
+- }
+-
+- newlen = strlen(res) + strlen(symval) + strlen(src) + 1;
+- if (newlen > reslen) {
+- reslen = newlen;
+- res = realloc(res, reslen);
+- }
+-
+- strcat(res, symval);
+- in = src;
+- }
+- strcat(res, in);
+-
+- return res;
+-}
+-
+ const char *sym_escape_string_value(const char *in)
+ {
+ const char *p;
+@@ -1086,7 +1008,7 @@ static struct dep_stack {
+ struct dep_stack *prev, *next;
+ struct symbol *sym;
+ struct property *prop;
+- struct expr *expr;
++ struct expr **expr;
+ } *check_top;
+
+ static void dep_stack_insert(struct dep_stack *stack, struct symbol *sym)
+@@ -1150,37 +1072,52 @@ static void sym_check_print_recursive(struct symbol *last_sym)
+ if (stack->sym == last_sym)
+ fprintf(stderr, "%s:%d:error: recursive dependency detected!\n",
+ prop->file->name, prop->lineno);
+- fprintf(stderr, "For a resolution refer to Documentation/kbuild/kconfig-language.txt\n");
+- fprintf(stderr, "subsection \"Kconfig recursive dependency limitations\"\n");
+- if (stack->expr) {
+- fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n",
+- prop->file->name, prop->lineno,
++
++ if (sym_is_choice(sym)) {
++ fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n",
++ menu->file->name, menu->lineno,
+ sym->name ? sym->name : "<choice>",
+- prop_get_type_name(prop->type),
+ next_sym->name ? next_sym->name : "<choice>");
+- } else if (stack->prop) {
++ } else if (sym_is_choice_value(sym)) {
++ fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n",
++ menu->file->name, menu->lineno,
++ sym->name ? sym->name : "<choice>",
++ next_sym->name ? next_sym->name : "<choice>");
++ } else if (stack->expr == &sym->dir_dep.expr) {
+ fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n",
+ prop->file->name, prop->lineno,
+ sym->name ? sym->name : "<choice>",
+ next_sym->name ? next_sym->name : "<choice>");
+- } else if (sym_is_choice(sym)) {
+- fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n",
+- menu->file->name, menu->lineno,
++ } else if (stack->expr == &sym->rev_dep.expr) {
++ fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n",
++ prop->file->name, prop->lineno,
+ sym->name ? sym->name : "<choice>",
+ next_sym->name ? next_sym->name : "<choice>");
+- } else if (sym_is_choice_value(sym)) {
+- fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n",
+- menu->file->name, menu->lineno,
++ } else if (stack->expr == &sym->implied.expr) {
++ fprintf(stderr, "%s:%d:\tsymbol %s is implied by %s\n",
++ prop->file->name, prop->lineno,
++ sym->name ? sym->name : "<choice>",
++ next_sym->name ? next_sym->name : "<choice>");
++ } else if (stack->expr) {
++ fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n",
++ prop->file->name, prop->lineno,
+ sym->name ? sym->name : "<choice>",
++ prop_get_type_name(prop->type),
+ next_sym->name ? next_sym->name : "<choice>");
+ } else {
+- fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n",
++ fprintf(stderr, "%s:%d:\tsymbol %s %s is visible depending on %s\n",
+ prop->file->name, prop->lineno,
+ sym->name ? sym->name : "<choice>",
++ prop_get_type_name(prop->type),
+ next_sym->name ? next_sym->name : "<choice>");
+ }
+ }
+
++ fprintf(stderr,
++ "For a resolution refer to Documentation/kbuild/kconfig-language.txt\n"
++ "subsection \"Kconfig recursive dependency limitations\"\n"
++ "\n");
++
+ if (check_top == &cv_stack)
+ dep_stack_remove();
+ }
+@@ -1215,7 +1152,7 @@ static struct symbol *sym_check_expr_deps(struct expr *e)
+ default:
+ break;
+ }
+- printf("Oops! How to check %d?\n", e->type);
++ fprintf(stderr, "Oops! How to check %d?\n", e->type);
+ return NULL;
+ }
+
+@@ -1228,12 +1165,26 @@ static struct symbol *sym_check_sym_deps(struct symbol *sym)
+
+ dep_stack_insert(&stack, sym);
+
++ stack.expr = &sym->dir_dep.expr;
++ sym2 = sym_check_expr_deps(sym->dir_dep.expr);
++ if (sym2)
++ goto out;
++
++ stack.expr = &sym->rev_dep.expr;
+ sym2 = sym_check_expr_deps(sym->rev_dep.expr);
+ if (sym2)
+ goto out;
+
++ stack.expr = &sym->implied.expr;
++ sym2 = sym_check_expr_deps(sym->implied.expr);
++ if (sym2)
++ goto out;
++
++ stack.expr = NULL;
++
+ for (prop = sym->prop; prop; prop = prop->next) {
+- if (prop->type == P_CHOICE || prop->type == P_SELECT)
++ if (prop->type == P_CHOICE || prop->type == P_SELECT ||
++ prop->type == P_IMPLY)
+ continue;
+ stack.prop = prop;
+ sym2 = sym_check_expr_deps(prop->visible.expr);
+@@ -1241,7 +1192,7 @@ static struct symbol *sym_check_sym_deps(struct symbol *sym)
+ break;
+ if (prop->type != P_DEFAULT || sym_is_choice(sym))
+ continue;
+- stack.expr = prop->expr;
++ stack.expr = &prop->expr;
+ sym2 = sym_check_expr_deps(prop->expr);
+ if (sym2)
+ break;
+@@ -1319,9 +1270,6 @@ struct symbol *sym_check_deps(struct symbol *sym)
+ sym->flags &= ~SYMBOL_CHECK;
+ }
+
+- if (sym2 && sym2 == sym)
+- sym2 = NULL;
+-
+ return sym2;
+ }
+
+@@ -1360,8 +1308,6 @@ const char *prop_get_type_name(enum prop_type type)
+ switch (type) {
+ case P_PROMPT:
+ return "prompt";
+- case P_ENV:
+- return "env";
+ case P_COMMENT:
+ return "comment";
+ case P_MENU:
+@@ -1383,32 +1329,3 @@ const char *prop_get_type_name(enum prop_type type)
+ }
+ return "unknown";
+ }
+-
+-static void prop_add_env(const char *env)
+-{
+- struct symbol *sym, *sym2;
+- struct property *prop;
+- char *p;
+-
+- sym = current_entry->sym;
+- sym->flags |= SYMBOL_AUTO;
+- for_all_properties(sym, prop, P_ENV) {
+- sym2 = prop_get_symbol(prop);
+- if (strcmp(sym2->name, env))
+- menu_warn(current_entry, "redefining environment symbol from %s",
+- sym2->name);
+- return;
+- }
+-
+- prop = prop_alloc(P_ENV, sym);
+- prop->expr = expr_alloc_symbol(sym_lookup(env, SYMBOL_CONST));
+-
+- sym_env_list = expr_alloc_one(E_LIST, sym_env_list);
+- sym_env_list->right.sym = sym;
+-
+- p = getenv(env);
+- if (p)
+- sym_add_default(sym, p);
+- else
+- menu_warn(current_entry, "environment variable %s undefined", env);
+-}
+diff --git a/carl9170fw/config/util.c b/carl9170fw/config/util.c
+index 0e76042..2958539 100644
+--- a/carl9170fw/config/util.c
++++ b/carl9170fw/config/util.c
+@@ -1,8 +1,7 @@
++// SPDX-License-Identifier: GPL-2.0
+ /*
+ * Copyright (C) 2002-2005 Roman Zippel <zippel@linux-m68k.org>
+ * Copyright (C) 2002-2005 Sam Ravnborg <sam@ravnborg.org>
+- *
+- * Released under the terms of the GNU GPL v2.0.
+ */
+
+ #include <stdarg.h>
+@@ -14,69 +13,21 @@
+ struct file *file_lookup(const char *name)
+ {
+ struct file *file;
+- const char *file_name = sym_expand_string_value(name);
+
+ for (file = file_list; file; file = file->next) {
+ if (!strcmp(name, file->name)) {
+- free((void *)file_name);
+ return file;
+ }
+ }
+
+ file = xmalloc(sizeof(*file));
+ memset(file, 0, sizeof(*file));
+- file->name = file_name;
++ file->name = xstrdup(name);
+ file->next = file_list;
+ file_list = file;
+ return file;
+ }
+
+-/* write a dependency file as used by kbuild to track dependencies */
+-int file_write_dep(const char *name)
+-{
+- struct symbol *sym, *env_sym;
+- struct expr *e;
+- struct file *file;
+- FILE *out;
+-
+- if (!name)
+- name = ".kconfig.d";
+- out = fopen("..config.tmp", "w");
+- if (!out)
+- return 1;
+- fprintf(out, "deps_config := \\\n");
+- for (file = file_list; file; file = file->next) {
+- if (file->next)
+- fprintf(out, "\t%s \\\n", file->name);
+- else
+- fprintf(out, "\t%s\n", file->name);
+- }
+- fprintf(out, "\n%s: \\\n"
+- "\t$(deps_config)\n\n", conf_get_autoconfig_name());
+-
+- expr_list_for_each_sym(sym_env_list, e, sym) {
+- struct property *prop;
+- const char *value;
+-
+- prop = sym_get_env_prop(sym);
+- env_sym = prop_get_symbol(prop);
+- if (!env_sym)
+- continue;
+- value = getenv(env_sym->name);
+- if (!value)
+- value = "";
+- fprintf(out, "ifneq \"$(%s)\" \"%s\"\n", env_sym->name, value);
+- fprintf(out, "%s: FORCE\n", conf_get_autoconfig_name());
+- fprintf(out, "endif\n");
+- }
+-
+- fprintf(out, "\n$(deps_config): ;\n");
+- fclose(out);
+- rename("..config.tmp", name);
+- return 0;
+-}
+-
+-
+ /* Allocate initial growable string */
+ struct gstr str_new(void)
+ {
+@@ -104,7 +55,7 @@ void str_append(struct gstr *gs, const char *s)
+ if (s) {
+ l = strlen(gs->s) + strlen(s) + 1;
+ if (l > gs->len) {
+- gs->s = realloc(gs->s, l);
++ gs->s = xrealloc(gs->s, l);
+ gs->len = l;
+ }
+ strcat(gs->s, s);
+@@ -145,3 +96,34 @@ void *xcalloc(size_t nmemb, size_t size)
+ fprintf(stderr, "Out of memory.\n");
+ exit(1);
+ }
++
++void *xrealloc(void *p, size_t size)
++{
++ p = realloc(p, size);
++ if (p)
++ return p;
++ fprintf(stderr, "Out of memory.\n");
++ exit(1);
++}
++
++char *xstrdup(const char *s)
++{
++ char *p;
++
++ p = strdup(s);
++ if (p)
++ return p;
++ fprintf(stderr, "Out of memory.\n");
++ exit(1);
++}
++
++char *xstrndup(const char *s, size_t n)
++{
++ char *p;
++
++ p = strndup(s, n);
++ if (p)
++ return p;
++ fprintf(stderr, "Out of memory.\n");
++ exit(1);
++}
+diff --git a/carl9170fw/config/zconf.l b/carl9170fw/config/zconf.l
+index 9720530..c52cce8 100644
+--- a/carl9170fw/config/zconf.l
++++ b/carl9170fw/config/zconf.l
+@@ -1,13 +1,13 @@
+-%option nostdinit noyywrap never-interactive full ecs
+-%option 8bit nodefault perf-report perf-report
+-%option noinput
+-%x COMMAND HELP STRING PARAM
+-%{
++/* SPDX-License-Identifier: GPL-2.0 */
+ /*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+- * Released under the terms of the GNU GPL v2.0.
+ */
++%option nostdinit noyywrap never-interactive full ecs
++%option 8bit nodefault yylineno
++%x ASSIGN_VAL HELP STRING
++%{
+
++#include <assert.h>
+ #include <limits.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -15,6 +15,9 @@
+ #include <unistd.h>
+
+ #include "lkc.h"
++#include "zconf.tab.h"
++
++#define YY_DECL static int yylex1(void)
+
+ #define START_STRSIZE 16
+
+@@ -23,6 +26,8 @@ static struct {
+ int lineno;
+ } current_pos;
+
++static int prev_prev_token = T_EOL;
++static int prev_token = T_EOL;
+ static char *text;
+ static int text_size, text_asize;
+
+@@ -35,6 +40,8 @@ struct buffer *current_buf;
+
+ static int last_ts, first_ts;
+
++static char *expand_token(const char *in, size_t n);
++static void append_expanded_string(const char *in);
+ static void zconf_endhelp(void);
+ static void zconf_endfile(void);
+
+@@ -52,7 +59,7 @@ static void append_string(const char *str, int size)
+ if (new_size > text_asize) {
+ new_size += START_STRSIZE - 1;
+ new_size &= -START_STRSIZE;
+- text = realloc(text, new_size);
++ text = xrealloc(text, new_size);
+ text_asize = new_size;
+ }
+ memcpy(text + text_size, str, size);
+@@ -71,7 +78,7 @@ static void warn_ignored_character(char chr)
+ {
+ fprintf(stderr,
+ "%s:%d:warning: ignoring unsupported character '%c'\n",
+- zconf_curname(), zconf_lineno(), chr);
++ current_file->name, yylineno, chr);
+ }
+ %}
+
+@@ -81,116 +88,113 @@ n [A-Za-z0-9_-]
+ int str = 0;
+ int ts, i;
+
+-[ \t]*#.*\n |
+-[ \t]*\n {
+- current_file->lineno++;
+- return T_EOL;
+-}
+-[ \t]*#.*
+-
+-
+-[ \t]+ {
+- BEGIN(COMMAND);
+-}
+-
+-. {
+- unput(yytext[0]);
+- BEGIN(COMMAND);
+-}
+-
+-
+-<COMMAND>{
+- {n}+ {
+- const struct kconf_id *id = kconf_id_lookup(yytext, yyleng);
+- BEGIN(PARAM);
+- current_pos.file = current_file;
+- current_pos.lineno = current_file->lineno;
+- if (id && id->flags & TF_COMMAND) {
+- zconflval.id = id;
+- return id->token;
+- }
+- alloc_string(yytext, yyleng);
+- zconflval.string = text;
+- return T_WORD;
+- }
+- . warn_ignored_character(*yytext);
+- \n {
+- BEGIN(INITIAL);
+- current_file->lineno++;
+- return T_EOL;
+- }
+-}
++#.* /* ignore comment */
++[ \t]* /* whitespaces */
++\\\n /* escaped new line */
++\n return T_EOL;
++"allnoconfig_y" return T_ALLNOCONFIG_Y;
++"bool" return T_BOOL;
++"choice" return T_CHOICE;
++"comment" return T_COMMENT;
++"config" return T_CONFIG;
++"def_bool" return T_DEF_BOOL;
++"def_tristate" return T_DEF_TRISTATE;
++"default" return T_DEFAULT;
++"defconfig_list" return T_DEFCONFIG_LIST;
++"depends" return T_DEPENDS;
++"endchoice" return T_ENDCHOICE;
++"endif" return T_ENDIF;
++"endmenu" return T_ENDMENU;
++"help"|"---help---" return T_HELP;
++"hex" return T_HEX;
++"if" return T_IF;
++"imply" return T_IMPLY;
++"int" return T_INT;
++"mainmenu" return T_MAINMENU;
++"menu" return T_MENU;
++"menuconfig" return T_MENUCONFIG;
++"modules" return T_MODULES;
++"on" return T_ON;
++"option" return T_OPTION;
++"optional" return T_OPTIONAL;
++"prompt" return T_PROMPT;
++"range" return T_RANGE;
++"select" return T_SELECT;
++"source" return T_SOURCE;
++"string" return T_STRING;
++"tristate" return T_TRISTATE;
++"visible" return T_VISIBLE;
++"||" return T_OR;
++"&&" return T_AND;
++"=" return T_EQUAL;
++"!=" return T_UNEQUAL;
++"<" return T_LESS;
++"<=" return T_LESS_EQUAL;
++">" return T_GREATER;
++">=" return T_GREATER_EQUAL;
++"!" return T_NOT;
++"(" return T_OPEN_PAREN;
++")" return T_CLOSE_PAREN;
++":=" return T_COLON_EQUAL;
++"+=" return T_PLUS_EQUAL;
++\"|\' {
++ str = yytext[0];
++ new_string();
++ BEGIN(STRING);
++ }
++{n}+ {
++ alloc_string(yytext, yyleng);
++ yylval.string = text;
++ return T_WORD;
++ }
++({n}|$)+ {
++ /* this token includes at least one '$' */
++ yylval.string = expand_token(yytext, yyleng);
++ if (strlen(yylval.string))
++ return T_WORD;
++ free(yylval.string);
++ }
++. warn_ignored_character(*yytext);
+
+-<PARAM>{
+- "&&" return T_AND;
+- "||" return T_OR;
+- "(" return T_OPEN_PAREN;
+- ")" return T_CLOSE_PAREN;
+- "!" return T_NOT;
+- "=" return T_EQUAL;
+- "!=" return T_UNEQUAL;
+- "<=" return T_LESS_EQUAL;
+- ">=" return T_GREATER_EQUAL;
+- "<" return T_LESS;
+- ">" return T_GREATER;
+- \"|\' {
+- str = yytext[0];
+- new_string();
+- BEGIN(STRING);
+- }
+- \n BEGIN(INITIAL); current_file->lineno++; return T_EOL;
+- ({n}|[/.])+ {
+- const struct kconf_id *id = kconf_id_lookup(yytext, yyleng);
+- if (id && id->flags & TF_PARAM) {
+- zconflval.id = id;
+- return id->token;
+- }
++<ASSIGN_VAL>{
++ [^[:blank:]\n]+.* {
+ alloc_string(yytext, yyleng);
+- zconflval.string = text;
+- return T_WORD;
+- }
+- #.* /* comment */
+- \\\n current_file->lineno++;
+- [[:blank:]]+
+- . warn_ignored_character(*yytext);
+- <<EOF>> {
+- BEGIN(INITIAL);
++ yylval.string = text;
++ return T_ASSIGN_VAL;
+ }
++ \n { BEGIN(INITIAL); return T_EOL; }
++ .
+ }
+
+ <STRING>{
+- [^'"\\\n]+/\n {
+- append_string(yytext, yyleng);
+- zconflval.string = text;
+- return T_WORD_QUOTE;
+- }
+- [^'"\\\n]+ {
++ "$".* append_expanded_string(yytext);
++ [^$'"\\\n]+ {
+ append_string(yytext, yyleng);
+ }
+- \\.?/\n {
+- append_string(yytext + 1, yyleng - 1);
+- zconflval.string = text;
+- return T_WORD_QUOTE;
+- }
+ \\.? {
+ append_string(yytext + 1, yyleng - 1);
+ }
+ \'|\" {
+ if (str == yytext[0]) {
+- BEGIN(PARAM);
+- zconflval.string = text;
++ BEGIN(INITIAL);
++ yylval.string = text;
+ return T_WORD_QUOTE;
+ } else
+ append_string(yytext, 1);
+ }
+ \n {
+- printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
+- current_file->lineno++;
++ fprintf(stderr,
++ "%s:%d:warning: multi-line strings not supported\n",
++ zconf_curname(), zconf_lineno());
++ unput('\n');
+ BEGIN(INITIAL);
+- return T_EOL;
++ yylval.string = text;
++ return T_WORD_QUOTE;
+ }
+ <<EOF>> {
+ BEGIN(INITIAL);
++ yylval.string = text;
++ return T_WORD_QUOTE;
+ }
+ }
+
+@@ -218,12 +222,10 @@ n [A-Za-z0-9_-]
+ }
+ }
+ [ \t]*\n/[^ \t\n] {
+- current_file->lineno++;
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ [ \t]*\n {
+- current_file->lineno++;
+ append_string("\n", 1);
+ }
+ [^ \t\n].* {
+@@ -243,6 +245,12 @@ n [A-Za-z0-9_-]
+ }
+
+ <<EOF>> {
++ BEGIN(INITIAL);
++
++ if (prev_token != T_EOL && prev_token != T_HELPTEXT)
++ fprintf(stderr, "%s:%d:warning: no new line at end of file\n",
++ current_file->name, yylineno);
++
+ if (current_file) {
+ zconf_endfile();
+ return T_EOL;
+@@ -252,6 +260,93 @@ n [A-Za-z0-9_-]
+ }
+
+ %%
++
++/* second stage lexer */
++int yylex(void)
++{
++ int token;
++
++repeat:
++ token = yylex1();
++
++ if (prev_token == T_EOL || prev_token == T_HELPTEXT) {
++ if (token == T_EOL) {
++ /* Do not pass unneeded T_EOL to the parser. */
++ goto repeat;
++ } else {
++ /*
++ * For the parser, update file/lineno at the first token
++ * of each statement. Generally, \n is a statement
++ * terminator in Kconfig, but it is not always true
++ * because \n could be escaped by a backslash.
++ */
++ current_pos.file = current_file;
++ current_pos.lineno = yylineno;
++ }
++ }
++
++ if (prev_prev_token == T_EOL && prev_token == T_WORD &&
++ (token == T_EQUAL || token == T_COLON_EQUAL || token == T_PLUS_EQUAL))
++ BEGIN(ASSIGN_VAL);
++
++ prev_prev_token = prev_token;
++ prev_token = token;
++
++ return token;
++}
++
++static char *expand_token(const char *in, size_t n)
++{
++ char *out;
++ int c;
++ char c2;
++ const char *rest, *end;
++
++ new_string();
++ append_string(in, n);
++
++ /* get the whole line because we do not know the end of token. */
++ while ((c = input()) != EOF) {
++ if (c == '\n') {
++ unput(c);
++ break;
++ }
++ c2 = c;
++ append_string(&c2, 1);
++ }
++
++ rest = text;
++ out = expand_one_token(&rest);
++
++ /* push back unused characters to the input stream */
++ end = rest + strlen(rest);
++ while (end > rest)
++ unput(*--end);
++
++ free(text);
++
++ return out;
++}
++
++static void append_expanded_string(const char *str)
++{
++ const char *end;
++ char *res;
++
++ str++;
++
++ res = expand_dollar(&str);
++
++ /* push back unused characters to the input stream */
++ end = str + strlen(str);
++ while (end > str)
++ unput(*--end);
++
++ append_string(res, strlen(res));
++
++ free(res);
++}
++
+ void zconf_starthelp(void)
+ {
+ new_string();
+@@ -261,7 +356,7 @@ void zconf_starthelp(void)
+
+ static void zconf_endhelp(void)
+ {
+- zconflval.string = text;
++ yylval.string = text;
+ BEGIN(INITIAL);
+ }
+
+@@ -294,7 +389,7 @@ void zconf_initscan(const char *name)
+ {
+ yyin = zconf_fopen(name);
+ if (!yyin) {
+- printf("can't find file %s\n", name);
++ fprintf(stderr, "can't find file %s\n", name);
+ exit(1);
+ }
+
+@@ -302,7 +397,7 @@ void zconf_initscan(const char *name)
+ memset(current_buf, 0, sizeof(*current_buf));
+
+ current_file = file_lookup(name);
+- current_file->lineno = 1;
++ yylineno = 1;
+ }
+
+ void zconf_nextfile(const char *name)
+@@ -315,35 +410,34 @@ void zconf_nextfile(const char *name)
+ current_buf->state = YY_CURRENT_BUFFER;
+ yyin = zconf_fopen(file->name);
+ if (!yyin) {
+- printf("%s:%d: can't open file \"%s\"\n",
+- zconf_curname(), zconf_lineno(), file->name);
++ fprintf(stderr, "%s:%d: can't open file \"%s\"\n",
++ zconf_curname(), zconf_lineno(), file->name);
+ exit(1);
+ }
+ yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+ buf->parent = current_buf;
+ current_buf = buf;
+
+- for (iter = current_file->parent; iter; iter = iter->parent ) {
+- if (!strcmp(current_file->name,iter->name) ) {
+- printf("%s:%d: recursive inclusion detected. "
+- "Inclusion path:\n current file : '%s'\n",
+- zconf_curname(), zconf_lineno(),
+- zconf_curname());
+- iter = current_file->parent;
+- while (iter && \
+- strcmp(iter->name,current_file->name)) {
+- printf(" included from: '%s:%d'\n",
+- iter->name, iter->lineno-1);
++ current_file->lineno = yylineno;
++ file->parent = current_file;
++
++ for (iter = current_file; iter; iter = iter->parent) {
++ if (!strcmp(iter->name, file->name)) {
++ fprintf(stderr,
++ "Recursive inclusion detected.\n"
++ "Inclusion path:\n"
++ " current file : %s\n", file->name);
++ iter = file;
++ do {
+ iter = iter->parent;
+- }
+- if (iter)
+- printf(" included from: '%s:%d'\n",
+- iter->name, iter->lineno+1);
++ fprintf(stderr, " included from: %s:%d\n",
++ iter->name, iter->lineno - 1);
++ } while (strcmp(iter->name, file->name));
+ exit(1);
+ }
+ }
+- file->lineno = 1;
+- file->parent = current_file;
++
++ yylineno = 1;
+ current_file = file;
+ }
+
+@@ -352,6 +446,8 @@ static void zconf_endfile(void)
+ struct buffer *parent;
+
+ current_file = current_file->parent;
++ if (current_file)
++ yylineno = current_file->lineno;
+
+ parent = current_buf->parent;
+ if (parent) {
+diff --git a/carl9170fw/config/zconf.y b/carl9170fw/config/zconf.y
+index 79c4f04..60936c7 100644
+--- a/carl9170fw/config/zconf.y
++++ b/carl9170fw/config/zconf.y
+@@ -1,8 +1,8 @@
+-%{
++/* SPDX-License-Identifier: GPL-2.0 */
+ /*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+- * Released under the terms of the GNU GPL v2.0.
+ */
++%{
+
+ #include <ctype.h>
+ #include <stdarg.h>
+@@ -20,63 +20,69 @@
+
+ int cdebug = PRINTD;
+
+-extern int zconflex(void);
++static void yyerror(const char *err);
+ static void zconfprint(const char *err, ...);
+ static void zconf_error(const char *err, ...);
+-static void zconferror(const char *err);
+-static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken);
++static bool zconf_endtoken(const char *tokenname,
++ const char *expected_tokenname);
+
+ struct symbol *symbol_hash[SYMBOL_HASHSIZE];
+
+ static struct menu *current_menu, *current_entry;
+
+ %}
+-%expect 32
+
+ %union
+ {
+ char *string;
+- struct file *file;
+ struct symbol *symbol;
+ struct expr *expr;
+ struct menu *menu;
+- const struct kconf_id *id;
++ enum symbol_type type;
++ enum variable_flavor flavor;
+ }
+
+-%token <id>T_MAINMENU
+-%token <id>T_MENU
+-%token <id>T_ENDMENU
+-%token <id>T_SOURCE
+-%token <id>T_CHOICE
+-%token <id>T_ENDCHOICE
+-%token <id>T_COMMENT
+-%token <id>T_CONFIG
+-%token <id>T_MENUCONFIG
+-%token <id>T_HELP
+ %token <string> T_HELPTEXT
+-%token <id>T_IF
+-%token <id>T_ENDIF
+-%token <id>T_DEPENDS
+-%token <id>T_OPTIONAL
+-%token <id>T_PROMPT
+-%token <id>T_TYPE
+-%token <id>T_DEFAULT
+-%token <id>T_SELECT
+-%token <id>T_IMPLY
+-%token <id>T_RANGE
+-%token <id>T_VISIBLE
+-%token <id>T_OPTION
+-%token <id>T_ON
+ %token <string> T_WORD
+ %token <string> T_WORD_QUOTE
+-%token T_UNEQUAL
+-%token T_LESS
+-%token T_LESS_EQUAL
+-%token T_GREATER
+-%token T_GREATER_EQUAL
++%token T_ALLNOCONFIG_Y
++%token T_BOOL
++%token T_CHOICE
+ %token T_CLOSE_PAREN
++%token T_COLON_EQUAL
++%token T_COMMENT
++%token T_CONFIG
++%token T_DEFAULT
++%token T_DEFCONFIG_LIST
++%token T_DEF_BOOL
++%token T_DEF_TRISTATE
++%token T_DEPENDS
++%token T_ENDCHOICE
++%token T_ENDIF
++%token T_ENDMENU
++%token T_HELP
++%token T_HEX
++%token T_IF
++%token T_IMPLY
++%token T_INT
++%token T_MAINMENU
++%token T_MENU
++%token T_MENUCONFIG
++%token T_MODULES
++%token T_ON
+ %token T_OPEN_PAREN
++%token T_OPTION
++%token T_OPTIONAL
++%token T_PLUS_EQUAL
++%token T_PROMPT
++%token T_RANGE
++%token T_SELECT
++%token T_SOURCE
++%token T_STRING
++%token T_TRISTATE
++%token T_VISIBLE
+ %token T_EOL
++%token <string> T_ASSIGN_VAL
+
+ %left T_OR
+ %left T_AND
+@@ -85,13 +91,15 @@ static struct menu *current_menu, *current_entry;
+ %nonassoc T_NOT
+
+ %type <string> prompt
++%type <symbol> nonconst_symbol
+ %type <symbol> symbol
++%type <type> type logic_type default
+ %type <expr> expr
+ %type <expr> if_expr
+-%type <id> end
+-%type <id> option_name
++%type <string> end
+ %type <menu> if_entry menu_entry choice_entry
+-%type <string> symbol_option_arg word_opt
++%type <string> word_opt assign_val
++%type <flavor> assign_op
+
+ %destructor {
+ fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+@@ -100,71 +108,53 @@ static struct menu *current_menu, *current_entry;
+ menu_end_menu();
+ } if_entry menu_entry choice_entry
+
+-%{
+-/* Include zconf_id.c here so it can see the token constants. */
+-#include "kconf_id.c"
+-%}
+-
+ %%
+-input: nl start | start;
++input: mainmenu_stmt stmt_list | stmt_list;
+
+-start: mainmenu_stmt stmt_list | stmt_list;
++/* mainmenu entry */
++
++mainmenu_stmt: T_MAINMENU prompt T_EOL
++{
++ menu_add_prompt(P_MENU, $2, NULL);
++};
+
+ stmt_list:
+ /* empty */
+ | stmt_list common_stmt
+ | stmt_list choice_stmt
+ | stmt_list menu_stmt
+- | stmt_list end { zconf_error("unexpected end statement"); }
+ | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); }
+- | stmt_list option_name error T_EOL
+-{
+- zconf_error("unexpected option \"%s\"", $2->name);
+-}
+ | stmt_list error T_EOL { zconf_error("invalid statement"); }
+ ;
+
+-option_name:
+- T_DEPENDS | T_PROMPT | T_TYPE | T_SELECT | T_IMPLY | T_OPTIONAL | T_RANGE | T_DEFAULT | T_VISIBLE
+-;
+-
+ common_stmt:
+- T_EOL
+- | if_stmt
++ if_stmt
+ | comment_stmt
+ | config_stmt
+ | menuconfig_stmt
+ | source_stmt
++ | assignment_stmt
+ ;
+
+-option_error:
+- T_WORD error T_EOL { zconf_error("unknown option \"%s\"", $1); }
+- | error T_EOL { zconf_error("invalid option"); }
+-;
+-
+-
+ /* config/menuconfig entry */
+
+-config_entry_start: T_CONFIG T_WORD T_EOL
++config_entry_start: T_CONFIG nonconst_symbol T_EOL
+ {
+- struct symbol *sym = sym_lookup($2, 0);
+- sym->flags |= SYMBOL_OPTIONAL;
+- menu_add_entry(sym);
+- printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2);
++ $2->flags |= SYMBOL_OPTIONAL;
++ menu_add_entry($2);
++ printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2->name);
+ };
+
+ config_stmt: config_entry_start config_option_list
+ {
+- menu_end_entry();
+ printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+ };
+
+-menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL
++menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL
+ {
+- struct symbol *sym = sym_lookup($2, 0);
+- sym->flags |= SYMBOL_OPTIONAL;
+- menu_add_entry(sym);
+- printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2);
++ $2->flags |= SYMBOL_OPTIONAL;
++ menu_add_entry($2);
++ printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2->name);
+ };
+
+ menuconfig_stmt: menuconfig_entry_start config_option_list
+@@ -173,26 +163,22 @@ menuconfig_stmt: menuconfig_entry_start config_option_list
+ current_entry->prompt->type = P_MENU;
+ else
+ zconfprint("warning: menuconfig statement without prompt");
+- menu_end_entry();
+ printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+ };
+
+ config_option_list:
+ /* empty */
+ | config_option_list config_option
+- | config_option_list symbol_option
+ | config_option_list depends
+ | config_option_list help
+- | config_option_list option_error
+- | config_option_list T_EOL
+ ;
+
+-config_option: T_TYPE prompt_stmt_opt T_EOL
++config_option: type prompt_stmt_opt T_EOL
+ {
+- menu_set_type($1->stype);
++ menu_set_type($1);
+ printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+ zconf_curname(), zconf_lineno(),
+- $1->stype);
++ $1);
+ };
+
+ config_option: T_PROMPT prompt if_expr T_EOL
+@@ -201,25 +187,25 @@ config_option: T_PROMPT prompt if_expr T_EOL
+ printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+ };
+
+-config_option: T_DEFAULT expr if_expr T_EOL
++config_option: default expr if_expr T_EOL
+ {
+ menu_add_expr(P_DEFAULT, $2, $3);
+- if ($1->stype != S_UNKNOWN)
+- menu_set_type($1->stype);
++ if ($1 != S_UNKNOWN)
++ menu_set_type($1);
+ printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
+ zconf_curname(), zconf_lineno(),
+- $1->stype);
++ $1);
+ };
+
+-config_option: T_SELECT T_WORD if_expr T_EOL
++config_option: T_SELECT nonconst_symbol if_expr T_EOL
+ {
+- menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3);
++ menu_add_symbol(P_SELECT, $2, $3);
+ printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
+ };
+
+-config_option: T_IMPLY T_WORD if_expr T_EOL
++config_option: T_IMPLY nonconst_symbol if_expr T_EOL
+ {
+- menu_add_symbol(P_IMPLY, sym_lookup($2, 0), $3);
++ menu_add_symbol(P_IMPLY, $2, $3);
+ printd(DEBUG_PARSE, "%s:%d:imply\n", zconf_curname(), zconf_lineno());
+ };
+
+@@ -229,34 +215,30 @@ config_option: T_RANGE symbol symbol if_expr T_EOL
+ printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
+ };
+
+-symbol_option: T_OPTION symbol_option_list T_EOL
+-;
++config_option: T_OPTION T_MODULES T_EOL
++{
++ menu_add_option_modules();
++};
+
+-symbol_option_list:
+- /* empty */
+- | symbol_option_list T_WORD symbol_option_arg
++config_option: T_OPTION T_DEFCONFIG_LIST T_EOL
+ {
+- const struct kconf_id *id = kconf_id_lookup($2, strlen($2));
+- if (id && id->flags & TF_OPTION)
+- menu_add_option(id->token, $3);
+- else
+- zconfprint("warning: ignoring unknown option %s", $2);
+- free($2);
++ menu_add_option_defconfig_list();
+ };
+
+-symbol_option_arg:
+- /* empty */ { $$ = NULL; }
+- | T_EQUAL prompt { $$ = $2; }
+-;
++config_option: T_OPTION T_ALLNOCONFIG_Y T_EOL
++{
++ menu_add_option_allnoconfig_y();
++};
+
+ /* choice entry */
+
+ choice: T_CHOICE word_opt T_EOL
+ {
+ struct symbol *sym = sym_lookup($2, SYMBOL_CHOICE);
+- sym->flags |= SYMBOL_AUTO;
++ sym->flags |= SYMBOL_NO_WRITE;
+ menu_add_entry(sym);
+ menu_add_expr(P_CHOICE, NULL, NULL);
++ free($2);
+ printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
+ };
+
+@@ -267,7 +249,7 @@ choice_entry: choice choice_option_list
+
+ choice_end: end
+ {
+- if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) {
++ if (zconf_endtoken($1, "choice")) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
+ }
+@@ -281,8 +263,6 @@ choice_option_list:
+ | choice_option_list choice_option
+ | choice_option_list depends
+ | choice_option_list help
+- | choice_option_list T_EOL
+- | choice_option_list option_error
+ ;
+
+ choice_option: T_PROMPT prompt if_expr T_EOL
+@@ -291,15 +271,11 @@ choice_option: T_PROMPT prompt if_expr T_EOL
+ printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+ };
+
+-choice_option: T_TYPE prompt_stmt_opt T_EOL
++choice_option: logic_type prompt_stmt_opt T_EOL
+ {
+- if ($1->stype == S_BOOLEAN || $1->stype == S_TRISTATE) {
+- menu_set_type($1->stype);
+- printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+- zconf_curname(), zconf_lineno(),
+- $1->stype);
+- } else
+- YYERROR;
++ menu_set_type($1);
++ printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
++ zconf_curname(), zconf_lineno(), $1);
+ };
+
+ choice_option: T_OPTIONAL T_EOL
+@@ -308,16 +284,28 @@ choice_option: T_OPTIONAL T_EOL
+ printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+ };
+
+-choice_option: T_DEFAULT T_WORD if_expr T_EOL
++choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL
+ {
+- if ($1->stype == S_UNKNOWN) {
+- menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3);
+- printd(DEBUG_PARSE, "%s:%d:default\n",
+- zconf_curname(), zconf_lineno());
+- } else
+- YYERROR;
++ menu_add_symbol(P_DEFAULT, $2, $3);
++ printd(DEBUG_PARSE, "%s:%d:default\n",
++ zconf_curname(), zconf_lineno());
+ };
+
++type:
++ logic_type
++ | T_INT { $$ = S_INT; }
++ | T_HEX { $$ = S_HEX; }
++ | T_STRING { $$ = S_STRING; }
++
++logic_type:
++ T_BOOL { $$ = S_BOOLEAN; }
++ | T_TRISTATE { $$ = S_TRISTATE; }
++
++default:
++ T_DEFAULT { $$ = S_UNKNOWN; }
++ | T_DEF_BOOL { $$ = S_BOOLEAN; }
++ | T_DEF_TRISTATE { $$ = S_TRISTATE; }
++
+ choice_block:
+ /* empty */
+ | choice_block common_stmt
+@@ -325,7 +313,7 @@ choice_block:
+
+ /* if entry */
+
+-if_entry: T_IF expr nl
++if_entry: T_IF expr T_EOL
+ {
+ printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
+ menu_add_entry(NULL);
+@@ -335,29 +323,15 @@ if_entry: T_IF expr nl
+
+ if_end: end
+ {
+- if (zconf_endtoken($1, T_IF, T_ENDIF)) {
++ if (zconf_endtoken($1, "if")) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
+ }
+ };
+
+-if_stmt: if_entry if_block if_end
++if_stmt: if_entry stmt_list if_end
+ ;
+
+-if_block:
+- /* empty */
+- | if_block common_stmt
+- | if_block menu_stmt
+- | if_block choice_stmt
+-;
+-
+-/* mainmenu entry */
+-
+-mainmenu_stmt: T_MAINMENU prompt nl
+-{
+- menu_add_prompt(P_MENU, $2, NULL);
+-};
+-
+ /* menu entry */
+
+ menu: T_MENU prompt T_EOL
+@@ -367,33 +341,33 @@ menu: T_MENU prompt T_EOL
+ printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
+ };
+
+-menu_entry: menu visibility_list depends_list
++menu_entry: menu menu_option_list
+ {
+ $$ = menu_add_menu();
+ };
+
+ menu_end: end
+ {
+- if (zconf_endtoken($1, T_MENU, T_ENDMENU)) {
++ if (zconf_endtoken($1, "menu")) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
+ }
+ };
+
+-menu_stmt: menu_entry menu_block menu_end
++menu_stmt: menu_entry stmt_list menu_end
+ ;
+
+-menu_block:
++menu_option_list:
+ /* empty */
+- | menu_block common_stmt
+- | menu_block menu_stmt
+- | menu_block choice_stmt
++ | menu_option_list visible
++ | menu_option_list depends
+ ;
+
+ source_stmt: T_SOURCE prompt T_EOL
+ {
+ printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2);
+ zconf_nextfile($2);
++ free($2);
+ };
+
+ /* comment entry */
+@@ -405,10 +379,13 @@ comment: T_COMMENT prompt T_EOL
+ printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
+ };
+
+-comment_stmt: comment depends_list
+-{
+- menu_end_entry();
+-};
++comment_stmt: comment comment_option_list
++;
++
++comment_option_list:
++ /* empty */
++ | comment_option_list depends
++;
+
+ /* help option */
+
+@@ -420,18 +397,22 @@ help_start: T_HELP T_EOL
+
+ help: help_start T_HELPTEXT
+ {
++ if (current_entry->help) {
++ free(current_entry->help);
++ zconfprint("warning: '%s' defined with more than one help text -- only the last one will be used",
++ current_entry->sym->name ?: "<choice>");
++ }
++
++ /* Is the help text empty or all whitespace? */
++ if ($2[strspn($2, " \f\n\r\t\v")] == '\0')
++ zconfprint("warning: '%s' defined with blank help text",
++ current_entry->sym->name ?: "<choice>");
++
+ current_entry->help = $2;
+ };
+
+ /* depends option */
+
+-depends_list:
+- /* empty */
+- | depends_list depends
+- | depends_list T_EOL
+- | depends_list option_error
+-;
+-
+ depends: T_DEPENDS T_ON expr T_EOL
+ {
+ menu_add_dep($3);
+@@ -439,14 +420,7 @@ depends: T_DEPENDS T_ON expr T_EOL
+ };
+
+ /* visibility option */
+-
+-visibility_list:
+- /* empty */
+- | visibility_list visible
+- | visibility_list T_EOL
+-;
+-
+-visible: T_VISIBLE if_expr
++visible: T_VISIBLE if_expr T_EOL
+ {
+ menu_add_visibility($2);
+ };
+@@ -464,14 +438,9 @@ prompt: T_WORD
+ | T_WORD_QUOTE
+ ;
+
+-end: T_ENDMENU T_EOL { $$ = $1; }
+- | T_ENDCHOICE T_EOL { $$ = $1; }
+- | T_ENDIF T_EOL { $$ = $1; }
+-;
+-
+-nl:
+- T_EOL
+- | nl T_EOL
++end: T_ENDMENU T_EOL { $$ = "menu"; }
++ | T_ENDCHOICE T_EOL { $$ = "choice"; }
++ | T_ENDIF T_EOL { $$ = "if"; }
+ ;
+
+ if_expr: /* empty */ { $$ = NULL; }
+@@ -491,13 +460,31 @@ expr: symbol { $$ = expr_alloc_symbol($1); }
+ | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); }
+ ;
+
+-symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); }
++/* For symbol definitions, selects, etc., where quotes are not accepted */
++nonconst_symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); };
++
++symbol: nonconst_symbol
+ | T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); }
+ ;
+
+ word_opt: /* empty */ { $$ = NULL; }
+ | T_WORD
+
++/* assignment statement */
++
++assignment_stmt: T_WORD assign_op assign_val T_EOL { variable_add($1, $3, $2); free($1); free($3); }
++
++assign_op:
++ T_EQUAL { $$ = VAR_RECURSIVE; }
++ | T_COLON_EQUAL { $$ = VAR_SIMPLE; }
++ | T_PLUS_EQUAL { $$ = VAR_APPEND; }
++;
++
++assign_val:
++ /* empty */ { $$ = xstrdup(""); };
++ | T_ASSIGN_VAL
++;
++
+ %%
+
+ void conf_parse(const char *name)
+@@ -507,61 +494,51 @@ void conf_parse(const char *name)
+
+ zconf_initscan(name);
+
+- sym_init();
+ _menu_init();
+- rootmenu.prompt = menu_add_prompt(P_MENU, "CARL9170 Firmware Configuration", NULL);
+
+ if (getenv("ZCONF_DEBUG"))
+- zconfdebug = 1;
+- zconfparse();
+- if (zconfnerrs)
++ yydebug = 1;
++ yyparse();
++
++ /* Variables are expanded in the parse phase. We can free them here. */
++ variable_all_del();
++
++ if (yynerrs)
+ exit(1);
+ if (!modules_sym)
+ modules_sym = sym_find( "n" );
+
+- rootmenu.prompt->text = _(rootmenu.prompt->text);
+- rootmenu.prompt->text = sym_expand_string_value(rootmenu.prompt->text);
++ if (!menu_has_prompt(&rootmenu)) {
++ current_entry = &rootmenu;
++ menu_add_prompt(P_MENU, "Main menu", NULL);
++ }
+
+ menu_finalize(&rootmenu);
+ for_all_symbols(i, sym) {
+ if (sym_check_deps(sym))
+- zconfnerrs++;
++ yynerrs++;
+ }
+- if (zconfnerrs)
++ if (yynerrs)
+ exit(1);
+ sym_set_change_count(1);
+ }
+
+-static const char *zconf_tokenname(int token)
+-{
+- switch (token) {
+- case T_MENU: return "menu";
+- case T_ENDMENU: return "endmenu";
+- case T_CHOICE: return "choice";
+- case T_ENDCHOICE: return "endchoice";
+- case T_IF: return "if";
+- case T_ENDIF: return "endif";
+- case T_DEPENDS: return "depends";
+- case T_VISIBLE: return "visible";
+- }
+- return "<token>";
+-}
+-
+-static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken)
++static bool zconf_endtoken(const char *tokenname,
++ const char *expected_tokenname)
+ {
+- if (id->token != endtoken) {
++ if (strcmp(tokenname, expected_tokenname)) {
+ zconf_error("unexpected '%s' within %s block",
+- id->name, zconf_tokenname(starttoken));
+- zconfnerrs++;
++ tokenname, expected_tokenname);
++ yynerrs++;
+ return false;
+ }
+ if (current_menu->file != current_file) {
+ zconf_error("'%s' in different file than '%s'",
+- id->name, zconf_tokenname(starttoken));
++ tokenname, expected_tokenname);
+ fprintf(stderr, "%s:%d: location of the '%s'\n",
+ current_menu->file->name, current_menu->lineno,
+- zconf_tokenname(starttoken));
+- zconfnerrs++;
++ expected_tokenname);
++ yynerrs++;
+ return false;
+ }
+ return true;
+@@ -582,7 +559,7 @@ static void zconf_error(const char *err, ...)
+ {
+ va_list ap;
+
+- zconfnerrs++;
++ yynerrs++;
+ fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+ va_start(ap, err);
+ vfprintf(stderr, err, ap);
+@@ -590,7 +567,7 @@ static void zconf_error(const char *err, ...)
+ fprintf(stderr, "\n");
+ }
+
+-static void zconferror(const char *err)
++static void yyerror(const char *err)
+ {
+ fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
+ }
+@@ -623,7 +600,7 @@ static void print_symbol(FILE *out, struct menu *menu)
+ fprintf(out, "\nconfig %s\n", sym->name);
+ switch (sym->type) {
+ case S_BOOLEAN:
+- fputs(" boolean\n", out);
++ fputs(" bool\n", out);
+ break;
+ case S_TRISTATE:
+ fputs(" tristate\n", out);
+@@ -686,6 +663,10 @@ static void print_symbol(FILE *out, struct menu *menu)
+ print_quoted_string(out, prop->text);
+ fputc('\n', out);
+ break;
++ case P_SYMBOL:
++ fputs( " symbol ", out);
++ fprintf(out, "%s\n", prop->sym->name);
++ break;
+ default:
+ fprintf(out, " unknown prop %d!\n", prop->type);
+ break;
+@@ -746,9 +727,5 @@ void zconfdump(FILE *out)
+ }
+ }
+
+-#include "zconf.lex.c"
+ #include "util.c"
+-#include "confdata.c"
+-#include "expr.c"
+-#include "symbol.c"
+ #include "menu.c"
+diff --git a/carl9170fw/include/linux/ieee80211.h b/carl9170fw/include/linux/ieee80211.h
+index 31c59ea..46ce6cf 100644
+--- a/carl9170fw/include/linux/ieee80211.h
++++ b/carl9170fw/include/linux/ieee80211.h
+@@ -897,33 +897,33 @@ struct ieee80211_mgmt {
+ __le16 status_code;
+ /* possibly followed by Challenge text */
+ u8 variable[0];
+- } __packed auth;
++ } __packed __aligned(4) auth;
+ struct {
+ __le16 reason_code;
+- } __packed deauth;
++ } __packed __aligned(4) deauth;
+ struct {
+ __le16 capab_info;
+ __le16 listen_interval;
+ /* followed by SSID and Supported rates */
+ u8 variable[0];
+- } __packed assoc_req;
++ } __packed __aligned(4) assoc_req;
+ struct {
+ __le16 capab_info;
+ __le16 status_code;
+ __le16 aid;
+ /* followed by Supported rates */
+ u8 variable[0];
+- } __packed assoc_resp, reassoc_resp;
++ } __packed __aligned(4) assoc_resp, reassoc_resp;
+ struct {
+ __le16 capab_info;
+ __le16 listen_interval;
+ u8 current_ap[6];
+ /* followed by SSID and Supported rates */
+ u8 variable[0];
+- } __packed reassoc_req;
++ } __packed __aligned(4) reassoc_req;
+ struct {
+ __le16 reason_code;
+- } __packed disassoc;
++ } __packed __aligned(4) disassoc;
+ struct {
+ __le64 timestamp;
+ __le16 beacon_int;
+@@ -931,11 +931,11 @@ struct ieee80211_mgmt {
+ /* followed by some of SSID, Supported rates,
+ * FH Params, DS Params, CF Params, IBSS Params, TIM */
+ u8 variable[0];
+- } __packed beacon;
++ } __packed __aligned(4) beacon;
+ struct {
+ /* only variable items: SSID, Supported rates */
+ u8 variable[0];
+- } __packed probe_req;
++ } __packed __aligned(4) probe_req;
+ struct {
+ __le64 timestamp;
+ __le16 beacon_int;
+@@ -943,7 +943,7 @@ struct ieee80211_mgmt {
+ /* followed by some of SSID, Supported rates,
+ * FH Params, DS Params, CF Params, IBSS Params */
+ u8 variable[0];
+- } __packed probe_resp;
++ } __packed __aligned(4) probe_resp;
+ struct {
+ u8 category;
+ union {
+@@ -1041,8 +1041,8 @@ struct ieee80211_mgmt {
+ u8 variable[0];
+ } __packed ftm;
+ } u;
+- } __packed action;
+- } u;
++ } __packed __aligned(4) action;
++ } u __aligned(2);
+ } __packed __aligned(2);
+
+ /* Supported rates membership selectors */
+@@ -1245,7 +1245,7 @@ struct ieee80211_bar {
+ __u8 ta[6];
+ __le16 control;
+ __le16 start_seq_num;
+-} __packed __aligned(4);
++} __packed __aligned(2);
+
+ /* 802.11 BA(R) control masks */
+ #define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000
+diff --git a/carl9170fw/include/shared/wlan.h b/carl9170fw/include/shared/wlan.h
+index 9c6b7ff..117f005 100644
+--- a/carl9170fw/include/shared/wlan.h
++++ b/carl9170fw/include/shared/wlan.h
+@@ -273,7 +273,7 @@ struct ar9170_tx_frame {
+ struct ieee80211_hdr i3e;
+ u8 payload[0];
+ } data;
+-} __packed;
++} __packed __aligned(4);
+
+ struct carl9170_tx_superframe {
+ struct carl9170_tx_superdesc s;
+diff --git a/carl9170fw/toolchain/Makefile b/carl9170fw/toolchain/Makefile
+index 11db906..43e546d 100644
+--- a/carl9170fw/toolchain/Makefile
++++ b/carl9170fw/toolchain/Makefile
+@@ -1,16 +1,16 @@
+-BINUTILS_VER=2.31.1
++BINUTILS_VER=2.32
+ BINUTILS_TAR=binutils-$(BINUTILS_VER).tar.xz
+ BINUTILS_URL="https://ftp.gnu.org/gnu/binutils/$(BINUTILS_TAR)"
+
+-NEWLIB_VER=3.0.0
++NEWLIB_VER=3.1.0
+ NEWLIB_TAR=newlib-$(NEWLIB_VER).tar.gz
+ NEWLIB_URL="ftp://sourceware.org/pub/newlib/$(NEWLIB_TAR)"
+
+-GCC_VER=8.2.0
++GCC_VER=9.1.0
+ GCC_TAR=gcc-$(GCC_VER).tar.xz
+ GCC_URL="https://ftp.gnu.org/gnu/gcc/gcc-$(GCC_VER)/$(GCC_TAR)"
+
+-MPFR_VER=4.0.1
++MPFR_VER=4.0.2
+ MPFR_TAR=mpfr-$(MPFR_VER).tar.xz
+ MPFR_URL="https://ftp.gnu.org/gnu/mpfr/$(MPFR_TAR)"
+
+diff --git a/carl9170fw/toolchain/SHA256SUMS b/carl9170fw/toolchain/SHA256SUMS
+index 1b65040..3a53959 100644
+--- a/carl9170fw/toolchain/SHA256SUMS
++++ b/carl9170fw/toolchain/SHA256SUMS
+@@ -1,16 +1,6 @@
+-1cf7adf8ff4b5aa49041c8734bbcf1ad18cc4c94d0029aae0f4e48841088479a src/gcc-7.2.0.tar.xz
+-5b76a9b97c9464209772ed25ce55181a7bb144a66e5669aaec945aa64da3189b src/newlib-2.5.0.tar.gz
+-0b871e271c4c620444f8264f72143b4d224aa305306d85dd77ab8dce785b1e85 src/binutils-2.29.tar.xz
+ 87b565e89a9a684fe4ebeeddb8399dce2599f9c9049854ca8c0dfbdea0e21912 src/gmp-6.1.2.tar.xz
+-617decc6ea09889fb08ede330917a00b16809b8db88c29c31bfbb49cbf88ecc3 src/mpc-1.0.3.tar.gz
+-7a62ac1a04408614fccdc506e4844b10cf0ad2c2b1677097f8f35d3a1344a950 src/mpfr-3.1.6.tar.xz
+ 6985c538143c1208dcb1ac42cedad6ff52e267b47e5f970183a3e75125b43c2e src/mpc-1.1.0.tar.gz
+-fbe2cd1418b321f5c899ce4f0f0f4e73f5ecc7d02145b0e1fd096f5c3afb8a1d src/mpfr-4.0.0.tar.xz
+-c8566335ee74e5fcaeb8595b4ebd0400c4b043d6acb3263ecb1314f8f5501332 src/newlib-3.0.0.tar.gz
+-832ca6ae04636adbb430e865a1451adf6979ab44ca1c8374f61fba65645ce15c src/gcc-7.3.0.tar.xz
+-e7010a46969f9d3e53b650a518663f98a5dde3c3ae21b7d71e5e6803bc36b577 src/binutils-2.29.1.tar.xz
+-67874a60826303ee2fb6affc6dc0ddd3e749e9bfcb4c8655e3953d0458a6e16e src/mpfr-4.0.1.tar.xz
+-6e46b8aeae2f727a36f0bd9505e405768a72218f1796f0d09757d45209871ae6 src/binutils-2.30.tar.xz
+-1d1866f992626e61349a1ccd0b8d5253816222cdc13390dcfaa74b093aa2b153 src/gcc-8.1.0.tar.xz
+-5d20086ecf5752cc7d9134246e9588fa201740d540f7eb84d795b1f7a93bca86 src/binutils-2.31.1.tar.xz
+-196c3c04ba2613f893283977e6011b2345d1cd1af9abeac58e916b1aab3e0080 src/gcc-8.2.0.tar.xz
+\ No newline at end of file
++fb4fa1cc21e9060719208300a61420e4089d6de6ef59cf533b57fe74801d102a src/newlib-3.1.0.tar.gz
++1d3be708604eae0e42d578ba93b390c2a145f17743a744d8f3f8c2ad5855a38a src/mpfr-4.0.2.tar.xz
++0ab6c55dd86a92ed561972ba15b9b70a8b9f75557f896446c82e8b36e473ee04 src/binutils-2.32.tar.xz
++79a66834e96a6050d8fe78db2c3b32fb285b230b855d0a66288235bc04b327a0 src/gcc-9.1.0.tar.xz
+--
+2.26.0
+
diff --git a/libre/linux-libre-firmware/0002-Add-firmware-for-the-ATUSB-IEEE-802.15.4-USB-Adapter.patch b/libre/linux-libre-firmware/0002-Add-firmware-for-the-ATUSB-IEEE-802.15.4-USB-Adapter.patch
new file mode 100644
index 000000000..aa4cb07dc
--- /dev/null
+++ b/libre/linux-libre-firmware/0002-Add-firmware-for-the-ATUSB-IEEE-802.15.4-USB-Adapter.patch
@@ -0,0 +1,4893 @@
+From dd4bc9ff49b9a7075e579fdd62fd930d27a9a7df Mon Sep 17 00:00:00 2001
+From: Jason Self <j@jxself.org>
+Date: Thu, 4 Jul 2019 15:55:48 -0700
+Subject: [PATCH 2/8] Add firmware for the ATUSB IEEE 802.15.4 USB Adapter
+
+http://shop.sysmocom.de/products/atusb/
+---
+ INSTALL | 20 +-
+ Makefile | 6 +-
+ WHENCE | 12 ++
+ atusb/Makefile | 236 +++++++++++++++++++++
+ atusb/README | 94 +++++++++
+ atusb/an/README | 25 +++
+ atusb/an/dec.py | 127 ++++++++++++
+ atusb/an/get.py | 31 +++
+ atusb/an/plot | 12 ++
+ atusb/atusb.c | 63 ++++++
+ atusb/board.c | 120 +++++++++++
+ atusb/board.h | 95 +++++++++
+ atusb/board_app.c | 173 ++++++++++++++++
+ atusb/board_atusb.c | 162 +++++++++++++++
+ atusb/board_atusb.h | 48 +++++
+ atusb/board_hulusb.c | 179 ++++++++++++++++
+ atusb/board_hulusb.h | 66 ++++++
+ atusb/board_rzusb.c | 169 +++++++++++++++
+ atusb/board_rzusb.h | 48 +++++
+ atusb/boot.c | 77 +++++++
+ atusb/descr.c | 104 ++++++++++
+ atusb/ep0.c | 338 ++++++++++++++++++++++++++++++
+ atusb/flash.c | 97 +++++++++
+ atusb/include/at86rf230.h | 402 ++++++++++++++++++++++++++++++++++++
+ atusb/include/atusb/atusb.h | 97 +++++++++
+ atusb/include/atusb/ep0.h | 64 ++++++
+ atusb/mac.c | 250 ++++++++++++++++++++++
+ atusb/mac.h | 26 +++
+ atusb/sernum.c | 47 +++++
+ atusb/sernum.h | 37 ++++
+ atusb/spi.c | 51 +++++
+ atusb/spi.h | 30 +++
+ atusb/uart.c | 64 ++++++
+ atusb/uart.h | 25 +++
+ atusb/usb/atu2.c | 247 ++++++++++++++++++++++
+ atusb/usb/dfu.c | 260 +++++++++++++++++++++++
+ atusb/usb/dfu.h | 119 +++++++++++
+ atusb/usb/dfu_common.c | 101 +++++++++
+ atusb/usb/usb.c | 181 ++++++++++++++++
+ atusb/usb/usb.h | 189 +++++++++++++++++
+ atusb/version.h | 23 +++
+ 42 files changed, 4512 insertions(+), 3 deletions(-)
+ create mode 100644 atusb/Makefile
+ create mode 100644 atusb/README
+ create mode 100644 atusb/an/README
+ create mode 100755 atusb/an/dec.py
+ create mode 100755 atusb/an/get.py
+ create mode 100755 atusb/an/plot
+ create mode 100644 atusb/atusb.c
+ create mode 100644 atusb/board.c
+ create mode 100644 atusb/board.h
+ create mode 100644 atusb/board_app.c
+ create mode 100644 atusb/board_atusb.c
+ create mode 100644 atusb/board_atusb.h
+ create mode 100644 atusb/board_hulusb.c
+ create mode 100644 atusb/board_hulusb.h
+ create mode 100644 atusb/board_rzusb.c
+ create mode 100644 atusb/board_rzusb.h
+ create mode 100644 atusb/boot.c
+ create mode 100644 atusb/descr.c
+ create mode 100644 atusb/ep0.c
+ create mode 100644 atusb/flash.c
+ create mode 100644 atusb/include/at86rf230.h
+ create mode 100644 atusb/include/atusb/atusb.h
+ create mode 100644 atusb/include/atusb/ep0.h
+ create mode 100644 atusb/mac.c
+ create mode 100644 atusb/mac.h
+ create mode 100644 atusb/sernum.c
+ create mode 100644 atusb/sernum.h
+ create mode 100644 atusb/spi.c
+ create mode 100644 atusb/spi.h
+ create mode 100644 atusb/uart.c
+ create mode 100644 atusb/uart.h
+ create mode 100644 atusb/usb/atu2.c
+ create mode 100644 atusb/usb/dfu.c
+ create mode 100644 atusb/usb/dfu.h
+ create mode 100644 atusb/usb/dfu_common.c
+ create mode 100644 atusb/usb/usb.c
+ create mode 100644 atusb/usb/usb.h
+ create mode 100644 atusb/version.h
+
+diff --git a/INSTALL b/INSTALL
+index 74c5cfd..7fb1116 100644
+--- a/INSTALL
++++ b/INSTALL
+@@ -16,6 +16,8 @@ In order to build everything you will need the following on the host
+ system:
+
+ * A C/C++ compiler, like GCC
++ * AVR-GCC
++ * Standard C library for AVR-GCC
+ * Cmake
+ * GNU Bison/YACC
+ * GNU Flex
+@@ -32,13 +34,27 @@ system:
+
+ On GNU/Linux distros that use apt you can install these with:
+
+- apt install binutils-arm-linux-gnueabi binutils-arm-none-eabi bison \
+- cmake flex g++ gcc gcc-arm-linux-gnueabi gcc-arm-none-eabi gperf make wget
++ apt install avr-gcc avr-libc binutils-arm-linux-gnueabi \
++ binutils-arm-none-eabi bison cmake flex g++ gcc \
++ gcc-arm-linux-gnueabi gcc-arm-none-eabi gperf make wget
+
+ CARL9170 Firmware Configuration
++-------------------------------
+ When building the carl9170 firmware you will be prompted with
+ configuration questions.
+
++atusb: Firmware for the ATUSB IEEE 802.15.4 USB Adapter
++-------------------------------------------------------
++
++To flash the firmware you need dfu-util on the host. Issue
++
++ make dfu
++
++right after plugging the device into the USB port while the red led is
++still on.
++
++Refer to the included README file for more information.
++
+ Licensing
+ ---------
+
+diff --git a/Makefile b/Makefile
+index 21d16fb..8474b30 100644
+--- a/Makefile
++++ b/Makefile
+@@ -17,7 +17,7 @@ shell=/bin/sh
+ prefix=/lib/firmware
+ install_program=install
+
+-.PHONY: all test clean install a56 as31 aica ath9k_htc_toolchain ath9k_htc av7110 b43-tools carl9170fw-toolchain carl9170fw cis-tools cis dsp56k ihex2fw isci keyspan_pda openfwwf usbdux
++.PHONY: all test clean install a56 as31 aica ath9k_htc_toolchain ath9k_htc atusb av7110 b43-tools carl9170fw-toolchain carl9170fw cis-tools cis dsp56k ihex2fw isci keyspan_pda openfwwf usbdux
+
+ all: aica ath9k_htc av7110 carl9170fw cis dsp56k isci keyspan_pda openfwwf usbdux
+
+@@ -36,6 +36,9 @@ ath9k_htc_toolchain:
+ ath9k_htc: ath9k_htc_toolchain
+ cd ath9k_htc && $(MAKE) -C target_firmware
+
++atusb:
++ cd atusb && $(MAKE)
++
+ av7110:
+ cd av7110 && $(MAKE)
+
+@@ -81,6 +84,7 @@ clean:
+ if [ -a as31/Makefile ]; then cd as31 && $(MAKE) clean; fi;
+ cd ath9k_htc && $(MAKE) toolchain-clean
+ cd ath9k_htc && $(MAKE) -C target_firmware clean
++ cd atusb && $(MAKE) clean
+ cd av7110 && $(MAKE) clean
+ cd carl9170fw/toolchain && $(MAKE) clean
+ if [ -a carl9170fw/Makefile ]; then cd carl9170fw && $(MAKE) clean; fi;
+diff --git a/WHENCE b/WHENCE
+index 2932155..756de43 100644
+--- a/WHENCE
++++ b/WHENCE
+@@ -112,6 +112,18 @@ From https://github.com/qca/open-ath9k-htc-firmware
+
+ --------------------------------------------------------------------------
+
++atusb: Firmware for the ATUSB IEEE 802.15.4 USB Adapter
++http://shop.sysmocom.de/products/atusb/
++
++From http://projects.qi-hardware.com/index.php/p/ben-wpan/source/tree/master/atusb/fw
++
++License: GPL-2.0-or-later
++
++Version: Based on commit 805db6ebf5d80692158acadf88e239da9d3e67af
++dated September 13 2017
++
++--------------------------------------------------------------------------
++
+ Driver: b43 - OpenFWWF -- Free firmware for some Broadcom 43xx series WLAN chips
+
+ License: GPLv2
+diff --git a/atusb/Makefile b/atusb/Makefile
+new file mode 100644
+index 0000000..c79cb26
+--- /dev/null
++++ b/atusb/Makefile
+@@ -0,0 +1,236 @@
++#
++# Makefile - Makefile of the ATUSB firmware
++#
++# Written 2010-2011, 2013 by Werner Almesberger
++# Copyright 2010-2011, 2013 by Werner Almesberger
++#
++# 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.
++#
++
++SHELL = /bin/bash
++
++NAME = atusb
++DEBUG = false
++
++CFLAGS = -g -mmcu=$(CHIP) -DBOOT_ADDR=$(BOOT_ADDR) \
++ -Wall -Wextra -Wshadow -Werror -Wno-unused-parameter \
++ -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes
++
++ifeq ($(DEBUG),true)
++CFLAGS += -DDEBUG
++endif
++
++ifeq ($(NAME),rzusb)
++CHIP=at90usb1287
++CFLAGS += -DRZUSB -DAT86RF230
++else ifeq ($(NAME),hulusb)
++CHIP=at90usb1287
++CFLAGS += -DHULUSB -DAT86RF212
++else
++CHIP=atmega32u2
++CFLAGS += -DATUSB -DAT86RF231
++endif
++HOST=jlime
++BOOT_ADDR=0x7000
++
++AVR_PREFIX = $(BIN_PATH) avr-
++CC = $(AVR_PREFIX)gcc
++OBJCOPY = $(AVR_PREFIX)objcopy
++#OBJDUMP = $(AVR_PREFIX)objdump
++SIZE = $(AVR_PREFIX)size
++
++# BCD notion is 0xJJMM with JJ being major and MM being minor. Thus 0x0020 is
++# version 0.2 */
++USB_BCD_VERSION = 0030
++USB_VENDOR_ID = 20b7
++USB_PRODUCT_ID = 1540
++USB_ID = $(USB_VENDOR_ID):$(USB_PRODUCT_ID)
++
++OBJS = atusb.o board.o board_app.o sernum.o spi.o descr.o ep0.o \
++ dfu_common.o usb.o app-atu2.o mac.o
++BOOT_OBJS = boot.o board.o sernum.o spi.o flash.o dfu.o \
++ dfu_common.o usb.o boot-atu2.o
++
++ifeq ($(DEBUG),true)
++OBJS += uart.o
++endif
++
++ifeq ($(NAME),rzusb)
++OBJS += board_rzusb.o
++BOOT_OBJS += board_rzusb.o
++else ifeq ($(NAME),hulusb)
++OBJS += board_hulusb.o
++BOOT_OBJS += board_hulusb.o
++else
++OBJS += board_atusb.o
++BOOT_OBJS += board_atusb.o
++endif
++
++
++vpath %.c usb/
++
++CFLAGS += -Iinclude -Iusb -I.
++
++# ----- Verbosity control -----------------------------------------------------
++
++CC_normal := $(CC)
++BUILD_normal :=
++DEPEND_normal := $(CPP) $(CFLAGS) -MM -MG
++
++CC_quiet = @echo " CC " $@ && $(CC_normal)
++BUILD_quiet = @echo " BUILD " $@ && $(BUILD_normal)
++DEPEND_quiet = @$(DEPEND_normal)
++
++ifeq ($(V),1)
++ CC = $(CC_normal)
++ BUILD = $(BUILD_normal)
++ DEPEND = $(DEPEND_normal)
++else
++ CC = $(CC_quiet)
++ BUILD = $(BUILD_quiet)
++ DEPEND = $(DEPEND_quiet)
++endif
++
++# ----- Rules -----------------------------------------------------------------
++
++.PHONY: all clean upload prog dfu update version.c bindist
++.PHONY: prog-app prog-read on off reset
++
++all: $(NAME).bin boot.hex
++
++$(NAME).elf: $(OBJS)
++ $(MAKE) version.o
++ $(CC) $(CFLAGS) -o $@ $(OBJS) version.o
++ $(SIZE) $@
++
++boot.elf: $(BOOT_OBJS)
++ $(CC) $(CFLAGS) -o $@ $(BOOT_OBJS) \
++ -Wl,--section-start=.text=$(BOOT_ADDR)
++ $(SIZE) $@
++
++%.bin: %.elf
++ $(BUILD) $(OBJCOPY) -j .text -j .data -O binary $< $@
++ @echo "build #`cat .version`, `ls -l $@`"
++
++%.dfu: %.bin
++ cp $(NAME).bin $(NAME).dfu
++ dfu-suffix -a $(NAME).dfu -d 0x$(USB_BCD_VERSION) \
++ -p 0x$(USB_PRODUCT_ID) -v 0x$(USB_VENDOR_ID)
++
++%.hex: %.elf
++ $(BUILD) $(OBJCOPY) -j .text -j .data -O ihex $< $@
++ @echo "Size: `$(SIZE) -A boot.hex | sed '/Total */s///p;d'` B"
++
++# ----- Cleanup ---------------------------------------------------------------
++
++clean:
++ rm -f $(NAME).bin $(NAME).elf $(NAME).dfu
++ rm -f $(OBJS) $(OBJS:.o=.d)
++ rm -f boot.hex boot.elf
++ rm -f $(BOOT_OBJS) $(BOOT_OBJS:.o=.d)
++ rm -f version.c version.d version.o
++
++# ----- Build version ---------------------------------------------------------
++
++version.c:
++ @if [ -f .version ]; then \
++ v=`cat .version`; \
++ expr $$v + 1 >.version; \
++ else \
++ echo 0 >.version; \
++ fi
++ @[ -s .version ] || echo 0 >.version
++ @echo '/* MACHINE-GENERATED. DO NOT EDIT ! */' >version.c
++ @echo '#include "version.h"' >>version.c
++ @echo "const char *build_date = \"`date`\";" >>version.c
++ @echo "const uint16_t build_number = `cat .version`;" \
++ >>version.c
++
++# ----- Dependencies ----------------------------------------------------------
++
++MKDEP = \
++ $(DEPEND) $< | \
++ sed \
++ -e 's|^$(basename $(notdir $<)).o:|$@:|' \
++ -e '/^\(.*:\)\? */{p;s///;s/ *\\\?$$/ /;s/ */:\n/g;H;}' \
++ -e '$${g;p;}' \
++ -e d >$(basename $@).d; \
++ [ "$${PIPESTATUS[*]}" = "0 0" ] || \
++ { rm -f $(basename $@).d; exit 1; }
++
++%.o: %.c
++ $(CC) $(CFLAGS) -Os -c $<
++ $(MKDEP)
++
++-include $(OBJS:.o=.d)
++
++# ----- Object file variants --------------------------------------------------
++
++app-%.o: usb/%.c
++ $(CC) $(CFLAGS) -Os -o $@ -c $<
++ $(MKDEP)
++
++boot-%.o: usb/%.c
++ $(CC) $(CFLAGS) -DBOOT_LOADER -Os -o $@ -c $<
++ $(MKDEP)
++
++# ----- Distribution ----------------------------------------------------------
++
++BINDIST_BASE=http://downloads.qi-hardware.com/people/werner/wpan/bindist
++ATUSB_BIN_NAME=atusb-`git rev-parse HEAD | cut -c 1-7`.bin
++
++bindist:
++ qippl atusb.bin wpan/bindist/$(ATUSB_BIN_NAME)
++ @echo $(BINDIST_BASE)/$(ATUSB_BIN_NAME)
++ @echo md5sum: `md5sum atusb.bin | sed 's/ .*//'`
++ @echo atrf-id: \
++ `sed '/.*number = \(.*\);/s//#\1/p;d' version.c` \
++ `sed '/.*date = "\(.*\)";/s//\1/p;d' version.c`
++
++# ----- Programming and device control ----------------------------------------
++
++upload: $(NAME).bin boot.hex
++ scp $(NAME).bin boot.hex $(HOST):
++
++# lfuse: external clock, slow start-up
++# hfuse: 4 kB boot loader, reset into boot loader
++# lock: allow everything but SPM to the boot loader
++# Note: when trying to program 0xef, we get back 0x2f, failing
++# verification. So we just program 0x2f.
++
++prog-app:
++ ssh $(HOST) avrdude -F -p $(CHIP) -c nanonote_atusb -e \
++ -U flash:w:atusb.bin:r \
++ -U lfuse:w:0x60:m
++
++prog:
++ ssh $(HOST) avrdude -F -p $(CHIP) -c nanonote_atusb -e \
++ -U flash:w:boot.hex:i \
++ -U lfuse:w:0x60:m \
++ -U hfuse:w:0xd8:m \
++ -U lock:w:0x2f:m
++
++prog-read:
++ ssh $(HOST) avrdude -F -p $(CHIP) -c nanonote_atusb \
++ -U flash:r:mcu.bin:r
++
++dfu: $(NAME).dfu
++ dfu-util -d $(USB_ID) -D $(NAME).dfu
++
++update: $(NAME).bin
++ -atrf-reset -a
++ usbwait -r -i 0.01 -t 5 $(USB_ID)
++ $(MAKE) dfu
++
++on:
++ ssh $(HOST) poke 0x10010318 4
++
++off:
++ ssh $(HOST) poke 0x10010314 4
++
++reset:
++ ssh $(HOST) poke 0x10010318 2048
++ ssh $(HOST) poke 0x10010314 2048
+diff --git a/atusb/README b/atusb/README
+new file mode 100644
+index 0000000..99ceb22
+--- /dev/null
++++ b/atusb/README
+@@ -0,0 +1,94 @@
++Requires a very recent toolchain, because ATmega32U2 is relatively new.
++
++- Building:
++
++ make
++
++- Uploading the firmware to a Ben (for flashing with the atusb-pgm cable):
++
++ make HOST=<hostname> upload
++
++ Example:
++
++ make HOST=ben upload
++
++ HOST defaults to "jlime".
++
++- Flashing the boot loader:
++
++ Prerequisite: avrdude on the Ben.
++
++ Disconnect the atusb board from USB. Insert the atusb-pgm connector into
++ the Ben. Place the atusb-pgm adapter on the exposed contact pads of the
++ atusb board and push it down. Then run
++
++ make prog
++
++ This takes about 30 seconds. If the programming fails with an error
++ message like "Yikes! Invalid device signature.", verify that the
++ atusb-pgm board is properly connected and placed, then try again.
++
++- Uploading the application:
++
++ Prerequisite: dfu-util installed on the PC.
++
++ Insert atusb into the PC, then run
++
++ make dfu
++
++ Note: since the boot loader resets the USB bus after timing out,
++ this operation can fail with a message like "No DFU capable USB device
++ found". Just retry, and it will eventually get through.
++
++
++HULUSB notes:
++-------------
++To prepare and flash the firmware on a HULUSB device some additional steps are
++needed;
++
++avr-objcopy -O ihex -R .signature -R .fuse -R .eeprom hulusb.elf hulusb.hex
++dfu-programmer at90usb1287 flash hulusb.hex
++dfu-programmer at90usb1287 reset
++
++--------------------------
++
++Making the toolchain:
++
++# patches according to
++# http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=789527
++
++# some gcc prerequisites
++
++apt-get remove avr-libc gcc-avr binutils-avr
++apt-get install libmpfr-dev libmpc-dev
++
++# binutils
++
++wget http://ftp.gnu.org/gnu/binutils/binutils-2.21.tar.bz2
++tar xfj binutils-2.21.tar.bz2
++cd binutils-2.21
++./configure --target=avr --disable-nls
++make
++make install
++
++# gcc
++
++wget http://ftpmirror.gnu.org/gcc/gcc-4.5.2/gcc-4.5.2.tar.bz2
++wget -O gcc_452_avr.patch http://gcc.gnu.org/bugzilla/attachment.cgi?id=23050
++tar xfj gcc-4.5.2.tar.bz2
++cd gcc-4.5.2
++patch -p1 -s <../gcc_452_avr.patch
++mkdir obj-avr
++cd obj-avr
++../configure --target=avr --enable-languages=c \
++ --disable-nls --disable-libssp --with-dwarf2
++make
++make install
++
++wget http://download.savannah.gnu.org/releases/avr-libc/avr-libc-1.7.1.tar.bz2
++tar xfj avr-libc-1.7.1.tar.bz2
++cd avr-libc-1.7.1
++./bootstrap # the automake at the end takes a while
++./configure --build=`./config.guess` --host=avr
++make
++make install
+diff --git a/atusb/an/README b/atusb/an/README
+new file mode 100644
+index 0000000..8e0d2fc
+--- /dev/null
++++ b/atusb/an/README
+@@ -0,0 +1,25 @@
++workflow:
++
++- connect zprobe (note: it currently inverts because it didn't have any
++ other chips around. this may change later.)
++
++- capture the USB signals at an interesting moment with a sample rate of
++ 50 MSa/s
++
++- zoom into the frame(s) of interest
++
++- download the data with
++ ./get.py
++
++- decode with
++ ./dec.py
++
++ For manual decoding, set the coders to D+ and D- (we need D- for SE0
++ and SE1 detection), then click on a rising clock edge left of the
++ packet and move the cursor to the right.
++
++- if there are problems with the clock, the analog signal and digital
++ signals derived from it can be examined after running dec.py with
++ ./plot
++
++ (Note that the digital zprobe hides any analog anomalies.)
+diff --git a/atusb/an/dec.py b/atusb/an/dec.py
+new file mode 100755
+index 0000000..8534857
+--- /dev/null
++++ b/atusb/an/dec.py
+@@ -0,0 +1,127 @@
++#!/usr/bin/python
++
++from tmc.wave import *
++from tmc.dxplore import dxplore
++from tmc.decode import d_usb_stream
++
++
++#
++# Clock recovery: we assume that each change in the wave is triggered by a
++# clock edge. We know the clock's nominal period and resynchronize on each
++# edge. Additionally, we can obtain a list of times when a timing violation
++# has occurred.
++#
++# Note that the timing violations logic doesn't make much sense in its present
++# form, since it mainly measures noise (particularly if we're digitizing slow
++# edges) and not clock drift.
++#
++# A more useful metric would be accumulated error from some point of reference
++# or at least the timing of same edges, to eliminate (generally harmless) time
++# offsets introduced by digitizing.
++#
++# So it would probably make more sense for "recover" not to check for timing
++# violations at all, and leave this to more specialized functions.
++#
++def recover(self, period, min = None, max = None, t0 = None):
++ if t0 is None:
++ t0 = self.data[0]
++ v = not self.initial
++ res = []
++ violations = []
++ for t in self.data:
++ v = not v
++ if t <= t0:
++ continue
++ n = 0
++ while t0 < t-period/2:
++ res.append(t0)
++ t0 += period
++ n += 1
++ if min is not None:
++ if t0-t > n*min:
++ violations.append(t)
++ if max is not None:
++ if t-t0 > n*max:
++ violations.append(t)
++ t0 = t
++ return res, violations
++
++
++#
++# Load the analog waves saved by get.py
++#
++wv = waves()
++wv.load("_wv")
++
++#
++# Digitize the waves and save the result.
++#
++dp = wv[0].digitize(1.5, 1.8)
++dm = wv[1].digitize(1.5, 1.8)
++wv = waves(dp, dm, dp-dm)
++wv.save("_dig")
++
++#
++# Also record the differential signal.
++#
++wd = wv[1]-wv[0]
++dd = wd.digitize(-0.5, 0.5)
++wd.save("_diff")
++
++#
++# Run clock recovery on D+/D-. We only need one, but check both to be sure.
++#
++#p = 1/1.5e6
++p = 1/12e6
++dp_t, viol = recover(dp, p, p*0.9, p*1.1)
++print viol
++dm_t, viol = recover(dm, p, p*.9, p*1.1, t0 = dp.data[0])
++print viol
++
++#
++# Shift the clock by half a period, add a few periods to get steady state and
++# SE0s (if any), and then sample the data lines.
++#
++clk = map(lambda t: t+p/2, dp_t)
++clk.extend((clk[-1]+p, clk[-1]+2*p, clk[-1]+3*p))
++dp_bv = dp.get(clk)
++dm_bv = dm.get(clk)
++
++#
++# Save a wave with the recovered clock to make it easier to find the bits in
++# analog graphs.
++#
++dd.data = dp_t;
++dd.save("_clk")
++
++#
++# For decoding, we need a fake bit clock. We generate it by doubling each data
++# bit and generating a L->H transition during this bit.
++#
++dpd = []
++dmd = []
++dck = []
++
++# err, silly, seems that we've mixed up D+ and D- all over the place :-)
++print d_usb_stream(dm_bv[:], dp_bv[:])
++
++for v in dp_bv:
++ dpd.append(v)
++ dpd.append(v)
++ dck.append(0)
++ dck.append(1)
++
++for v in dm_bv:
++ dmd.append(v)
++ dmd.append(v)
++
++#
++# Display the reconstructed digital signal. Note that the absolute time is only
++# correct at the beginning and that relative time is only accurate over
++# intervals in which no significant clock resynchronization has occurred.
++#
++# In fact, dxplore should probably have an option to either turn off time
++# entirely or to display a user-provided time axis. The latter may be a bit
++# tricky to implement.
++#
++dxplore((dmd, dpd, dck), 0, p/2, labels = ("D+", "D-", "CLK"))
+diff --git a/atusb/an/get.py b/atusb/an/get.py
+new file mode 100755
+index 0000000..685e00f
+--- /dev/null
++++ b/atusb/an/get.py
+@@ -0,0 +1,31 @@
++#!/usr/bin/python
++
++from tmc.scope import rigol_ds1000c
++
++#-800, +1600
++s = rigol_ds1000c()
++#s.debug = False
++
++pos = s.hor.pos
++scale = s.hor.scale
++t0 = pos-scale*s.div_hor/2
++t1 = pos+scale*s.div_hor/2
++print t0, t1
++
++#zoom = 10
++#step = scale/s.samples_per_div/zoom
++#print step
++step = 4e-9
++step = 2e-9
++
++w = s.wave((s.ch[0], s.ch[1]), start = t0, end = t1, step = step)
++w[0] = 3.3-w[0]
++w[1] = 3.3-w[1]
++
++s.hor.pos = pos
++s.hor.scale = scale
++
++w[0].label = "D+";
++w[1].label = "D-";
++
++w.save("_wv")
+diff --git a/atusb/an/plot b/atusb/an/plot
+new file mode 100755
+index 0000000..1dea789
+--- /dev/null
++++ b/atusb/an/plot
+@@ -0,0 +1,12 @@
++#!/bin/sh
++#
++# Plot output of "dec"
++#
++gnuplot -persist <<EOF
++set style data lines
++plot "_wv" using 1:(\$2-4), \
++ "_dig" using 1:(\$2*3.3-4) lw 2, \
++ "_wv" using 1:3, \
++ "_dig" using 1:(\$3*3.3) lw 2, \
++ "_clk" using 1:(\$2+1) lt 7
++EOF
+diff --git a/atusb/atusb.c b/atusb/atusb.c
+new file mode 100644
+index 0000000..28faf40
+--- /dev/null
++++ b/atusb/atusb.c
+@@ -0,0 +1,63 @@
++/*
++ * fw/atusb.c - ATUSB initialization and main loop
++ *
++ * Written 2008-2011 by Werner Almesberger
++ * Copyright 2008-2011 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#include <stdint.h>
++
++#include <avr/io.h>
++#include <avr/sleep.h>
++#include <avr/interrupt.h>
++
++#include "usb.h"
++
++#include "board.h"
++#include "sernum.h"
++#include "spi.h"
++#include "atusb/ep0.h"
++
++#ifdef DEBUG
++#include "uart.h"
++#endif
++
++
++int main(void)
++{
++ board_init();
++ board_app_init();
++ reset_rf();
++
++ user_get_descriptor = sernum_get_descr;
++
++ /* now we should be at 8 MHz */
++
++#ifdef DEBUG
++ uart_init();
++ static FILE atben_stdout = FDEV_SETUP_STREAM(uart_write_char, NULL,
++ _FDEV_SETUP_WRITE);
++ stdout = &atben_stdout;
++#endif
++
++ usb_init();
++ ep0_init();
++#ifdef ATUSB
++ timer_init();
++
++ /* move interrupt vectors to 0 */
++ MCUCR = 1 << IVCE;
++ MCUCR = 0;
++#endif
++
++ sei();
++
++ while (1)
++ sleep_mode();
++}
+diff --git a/atusb/board.c b/atusb/board.c
+new file mode 100644
+index 0000000..c3b8d26
+--- /dev/null
++++ b/atusb/board.c
+@@ -0,0 +1,120 @@
++/*
++ * fw/board.c - Board-specific functions (for boot loader and application)
++ *
++ * Written 2011, 2013 by Werner Almesberger
++ * Copyright 2011, 2013 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include <avr/io.h>
++#include <avr/interrupt.h>
++#include <avr/boot.h>
++
++#define F_CPU 8000000UL
++#include <util/delay.h>
++
++#include "usb.h"
++#include "at86rf230.h"
++#include "board.h"
++#include "spi.h"
++
++
++uint8_t board_sernum[42] = { 42, USB_DT_STRING };
++
++/* ----- Register access --------------------------------------------------- */
++
++void change_state(uint8_t new)
++{
++ while ((reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK) ==
++ TRX_STATUS_TRANSITION);
++ reg_write(REG_TRX_STATE, new);
++}
++
++
++uint8_t reg_read(uint8_t reg)
++{
++ uint8_t value;
++
++ spi_begin();
++ spi_send(AT86RF230_REG_READ | reg);
++ value = spi_recv();
++ spi_end();
++
++ return value;
++}
++
++
++uint8_t subreg_read(uint8_t address, uint8_t mask, uint8_t position)
++{
++ /* Read current register value and mask out subregister. */
++ uint8_t register_value = reg_read(address);
++ register_value &= mask;
++ register_value >>= position; /* Align subregister value. */
++
++ return register_value;
++}
++
++
++void reg_write(uint8_t reg, uint8_t value)
++{
++ spi_begin();
++ spi_send(AT86RF230_REG_WRITE | reg);
++ spi_send(value);
++ spi_end();
++}
++
++
++void subreg_write(uint8_t address, uint8_t mask, uint8_t position, uint8_t value)
++{
++ /* Read current register value and mask area outside the subregister. */
++ uint8_t register_value = reg_read(address);
++ register_value &= ~mask;
++
++ /* Start preparing the new subregister value. shift in place and mask. */
++ value <<= position;
++ value &= mask;
++
++ value |= register_value; /* Set the new subregister value. */
++
++ /* Write the modified register value. */
++ reg_write(address, value);
++}
++
++
++void panic(void)
++{
++ cli();
++ while (1) {
++ SET(LED);
++ _delay_ms(100);
++ CLR(LED);
++ _delay_ms(100);
++ }
++}
++
++
++static char hex(uint8_t nibble)
++{
++ return nibble < 10 ? '0'+nibble : 'a'+nibble-10;
++}
++
++
++void get_sernum(void)
++{
++ uint8_t sig;
++ uint8_t i;
++
++ for (i = 0; i != 10; i++) {
++ sig = boot_signature_byte_get(i+0xe);
++ board_sernum[(i << 2)+2] = hex(sig >> 4);
++ board_sernum[(i << 2)+4] = hex(sig & 0xf);
++ }
++}
+diff --git a/atusb/board.h b/atusb/board.h
+new file mode 100644
+index 0000000..dbcd410
+--- /dev/null
++++ b/atusb/board.h
+@@ -0,0 +1,95 @@
++/*
++ * fw/board.h - Board-specific functions and definitions
++ *
++ * Written 2008-2011, 2013, 2013 by Werner Almesberger
++ * Copyright 2008-2011, 2013, 2013 Werner Almesberger
++ *
++ * 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.
++ */
++
++#ifndef BOARD_H
++#define BOARD_H
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include <atusb/atusb.h>
++
++#ifdef ATUSB
++#include "board_atusb.h"
++#endif
++#ifdef RZUSB
++#include "board_rzusb.h"
++#endif
++#ifdef HULUSB
++#include "board_hulusb.h"
++#endif
++
++#define SET_2(p, b) PORT##p |= 1 << (b)
++#define CLR_2(p, b) PORT##p &= ~(1 << (b))
++#define IN_2(p, b) DDR##p &= ~(1 << (b))
++#define OUT_2(p, b) DDR##p |= 1 << (b)
++#define PIN_2(p, b) ((PIN##p >> (b)) & 1)
++
++#define SET_1(p, b) SET_2(p, b)
++#define CLR_1(p, b) CLR_2(p, b)
++#define IN_1(p, b) IN_2(p, b)
++#define OUT_1(p, b) OUT_2(p, b)
++#define PIN_1(p, b) PIN_2(p, b)
++
++#define SET(n) SET_1(n##_PORT, n##_BIT)
++#define CLR(n) CLR_1(n##_PORT, n##_BIT)
++#define IN(n) IN_1(n##_PORT, n##_BIT)
++#define OUT(n) OUT_1(n##_PORT, n##_BIT)
++#define PIN(n) PIN_1(n##_PORT, n##_BIT)
++
++
++#define USB_VENDOR ATUSB_VENDOR_ID
++#define USB_PRODUCT ATUSB_PRODUCT_ID
++
++#define DFU_USB_VENDOR USB_VENDOR
++#define DFU_USB_PRODUCT USB_PRODUCT
++
++
++#define BOARD_MAX_mA 40
++
++#ifdef BOOT_LOADER
++#define NUM_EPS 1
++#else
++#define NUM_EPS 2
++#endif
++
++#define HAS_BOARD_SERNUM
++
++extern uint8_t board_sernum[42];
++extern uint8_t irq_serial;
++
++
++void reset_rf(void);
++void reset_cpu(void);
++uint8_t read_irq(void);
++void slp_tr(void);
++
++void led(bool on);
++void panic(void);
++
++uint64_t timer_read(void);
++void timer_init(void);
++
++bool gpio(uint8_t port, uint8_t data, uint8_t dir, uint8_t mask, uint8_t *res);
++void gpio_cleanup(void);
++
++void get_sernum(void);
++
++void board_app_init(void);
++
++uint8_t reg_read(uint8_t reg);
++uint8_t subreg_read(uint8_t address, uint8_t mask, uint8_t position);
++void reg_write(uint8_t reg, uint8_t value);
++void subreg_write(uint8_t address, uint8_t mask, uint8_t position, uint8_t value);
++void change_state(uint8_t new);
++
++#endif /* !BOARD_H */
+diff --git a/atusb/board_app.c b/atusb/board_app.c
+new file mode 100644
+index 0000000..1fa9bf4
+--- /dev/null
++++ b/atusb/board_app.c
+@@ -0,0 +1,173 @@
++/*
++ * fw/board_app.c - Board-specific functions (for the application)
++ *
++ * Written 2011, 2013 by Werner Almesberger
++ * Copyright 2011, 2013 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#include <stddef.h>
++#include <stdbool.h>
++#include <stdint.h>
++
++#include <avr/io.h>
++#include <avr/interrupt.h>
++
++#define F_CPU 8000000UL
++#include <util/delay.h>
++
++#include "usb.h"
++#include "at86rf230.h"
++#include "spi.h"
++#include "mac.h"
++#include "board.h"
++
++
++static volatile uint32_t timer_h = 0; /* 2^(16+32) / 8 MHz = ~1.1 years */
++
++
++void reset_cpu(void)
++{
++ WDTCSR = 1 << WDE;
++}
++
++
++uint8_t read_irq(void)
++{
++ return PIN(IRQ_RF);
++}
++
++
++void slp_tr(void)
++{
++ SET(SLP_TR);
++ CLR(SLP_TR);
++}
++
++
++ISR(TIMER1_OVF_vect)
++{
++ timer_h++;
++}
++
++
++uint64_t timer_read(void)
++{
++ uint32_t high;
++ uint8_t low, mid;
++
++ do {
++ if (TIFR1 & (1 << TOV1)) {
++ TIFR1 = 1 << TOV1;
++ timer_h++;
++ }
++ high = timer_h;
++ low = TCNT1L;
++ mid = TCNT1H;
++ }
++ while (TIFR1 & (1 << TOV1));
++
++ /*
++ * We need all these casts because the intermediate results are handled
++ * as if they were signed and thus get sign-expanded. Sounds wrong-ish.
++ */
++ return (uint64_t) high << 16 | (uint64_t) mid << 8 | (uint64_t) low;
++}
++
++
++void timer_init(void)
++{
++ /* configure timer 1 as a free-running CLK counter */
++
++ TCCR1A = 0;
++ TCCR1B = 1 << CS10;
++
++ /* enable timer overflow interrupt */
++
++ TIMSK1 = 1 << TOIE1;
++}
++
++
++bool gpio(uint8_t port, uint8_t data, uint8_t dir, uint8_t mask, uint8_t *res)
++{
++ EIMSK = 0; /* recover INT_RF to ATUSB_GPIO_CLEANUP or an MCU reset */
++
++ switch (port) {
++ case 1:
++ DDRB = (DDRB & ~mask) | dir;
++ PORTB = (PORTB & ~mask) | data;
++ break;
++ case 2:
++ DDRC = (DDRC & ~mask) | dir;
++ PORTC = (PORTC & ~mask) | data;
++ break;
++ case 3:
++ DDRD = (DDRD & ~mask) | dir;
++ PORTD = (PORTD & ~mask) | data;
++ break;
++ default:
++ return 0;
++ }
++
++ /* disable the UART so that we can meddle with these pins as well. */
++ spi_off();
++ _delay_ms(1);
++
++ switch (port) {
++ case 1:
++ res[0] = PINB;
++ res[1] = PORTB;
++ res[2] = DDRB;
++ break;
++ case 2:
++ res[0] = PINC;
++ res[1] = PORTC;
++ res[2] = DDRC;
++ break;
++ case 3:
++ res[0] = PIND;
++ res[1] = PORTD;
++ res[2] = DDRD;
++ break;
++ }
++
++ return 1;
++}
++
++
++void gpio_cleanup(void)
++{
++ EIMSK = 1 << 0;
++}
++
++
++static void done(void *user)
++{
++ led(0);
++}
++
++
++uint8_t irq_serial;
++
++#if defined(ATUSB) || defined(HULUSB)
++ISR(INT0_vect)
++#endif
++#ifdef RZUSB
++ISR(TIMER1_CAPT_vect)
++#endif
++{
++ if (mac_irq) {
++ if (mac_irq())
++ return;
++ }
++ if (eps[1].state == EP_IDLE) {
++ led(1);
++ irq_serial = (irq_serial+1) | 0x80;
++ usb_send(&eps[1], &irq_serial, 1, done, NULL);
++ }
++}
+diff --git a/atusb/board_atusb.c b/atusb/board_atusb.c
+new file mode 100644
+index 0000000..a02fb7f
+--- /dev/null
++++ b/atusb/board_atusb.c
+@@ -0,0 +1,162 @@
++/*
++ * fw/board_atusb.c - ATUSB Board-specific functions (for boot loader and application)
++ *
++ * Written 2016 by Stefan Schmidt
++ * Copyright 2016 Stefan Schmidt
++ *
++ * 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.
++ */
++
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include <avr/io.h>
++#include <avr/interrupt.h>
++#include <avr/boot.h>
++
++#define F_CPU 8000000UL
++#include <util/delay.h>
++
++#include "usb.h"
++#include "at86rf230.h"
++#include "board.h"
++#include "spi.h"
++#include "usb/usb.h"
++
++static bool spi_initialized = 0;
++
++void reset_rf(void)
++{
++ /* set up all the outputs; default port value is 0 */
++
++ DDRB = 0;
++ DDRC = 0;
++ DDRD = 0;
++ PORTB = 0;
++ PORTC = 0;
++ PORTD = 0;
++
++ OUT(LED);
++ OUT(nRST_RF); /* this also resets the transceiver */
++ OUT(SLP_TR);
++
++ spi_init();
++
++ /* AT86RF231 data sheet, 12.4.13, reset pulse width: 625 ns (min) */
++
++ CLR(nRST_RF);
++ _delay_us(2);
++ SET(nRST_RF);
++
++ /* 12.4.14: SPI access latency after reset: 625 ns (min) */
++
++ _delay_us(2);
++
++ /* we must restore TRX_CTRL_0 after each reset (9.6.4) */
++
++ set_clkm();
++}
++
++void led(bool on)
++{
++ if (on)
++ SET(LED);
++ else
++ CLR(LED);
++}
++
++void set_clkm(void)
++{
++ /* switch CLKM to 8 MHz */
++
++ /*
++ * @@@ Note: Atmel advise against changing the external clock in
++ * mid-flight. We should therefore switch to the RC clock first, then
++ * crank up the external clock, and finally switch back to the external
++ * clock. The clock switching procedure is described in the ATmega32U2
++ * data sheet in secton 8.2.2.
++ */
++ spi_begin();
++ spi_send(AT86RF230_REG_WRITE | REG_TRX_CTRL_0);
++ spi_send(CLKM_CTRL_8MHz);
++ spi_end();
++}
++
++void board_init(void)
++{
++ /* Disable the watchdog timer */
++
++ MCUSR = 0; /* Remove override */
++ WDTCSR |= 1 << WDCE; /* Enable change */
++ WDTCSR = 1 << WDCE; /* Disable watchdog while still enabling
++ change */
++
++ CLKPR = 1 << CLKPCE;
++ /* We start with a 1 MHz/8 clock. Disable the prescaler. */
++ CLKPR = 0;
++
++ get_sernum();
++}
++
++void spi_begin(void)
++{
++ if (!spi_initialized)
++ spi_init();
++ CLR(nSS);
++}
++
++void spi_off(void)
++{
++ spi_initialized = 0;
++ UCSR1B = 0;
++}
++
++void spi_init(void)
++{
++ SET(nSS);
++ OUT(SCLK);
++ OUT(MOSI);
++ OUT(nSS);
++ IN(MISO);
++
++ UBRR1 = 0; /* set bit rate to zero to begin */
++ UCSR1C = 1 << UMSEL11 | 1 << UMSEL10;
++ /* set MSPI, MSB first, SPI data mode 0 */
++ UCSR1B = 1 << RXEN1 | 1 << TXEN1;
++ /* enable receiver and transmitter */
++ UBRR1 = 0; /* reconfirm the bit rate */
++
++ spi_initialized = 1;
++}
++
++void usb_init(void)
++{
++ USBCON |= 1 << FRZCLK; /* freeze the clock */
++
++ /* enable the PLL and wait for it to lock */
++ PLLCSR &= ~(1 << PLLP2 | 1 << PLLP1 | 1 << PLLP0);
++ PLLCSR |= 1 << PLLE;
++ while (!(PLLCSR & (1 << PLOCK)));
++
++ USBCON &= ~(1 << USBE); /* reset the controller */
++ USBCON |= 1 << USBE;
++
++ USBCON &= ~(1 << FRZCLK); /* thaw the clock */
++
++ UDCON &= ~(1 << DETACH); /* attach the pull-up */
++ UDIEN = 1 << EORSTE; /* enable device interrupts */
++// UDCON |= 1 << RSTCPU; /* reset CPU on bus reset */
++
++ ep_init();
++}
++
++void board_app_init(void)
++{
++ /* enable INT0, trigger on rising edge */
++ EICRA = 1 << ISC01 | 1 << ISC00;
++ EIMSK = 1 << 0;
++}
+diff --git a/atusb/board_atusb.h b/atusb/board_atusb.h
+new file mode 100644
+index 0000000..e5974c7
+--- /dev/null
++++ b/atusb/board_atusb.h
+@@ -0,0 +1,48 @@
++/*
++ * fw/board_atusb.h - ATUSB Board-specific functions and definitions
++ *
++ * Written 2016 by Stefan Schmidt
++ * Copyright 2016 Stefan Schmidt
++ *
++ * 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.
++ */
++
++#ifndef BOARD_ATUSB_H
++#define BOARD_ATUSB_H
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#define LED_PORT B
++#define LED_BIT 6
++#define nRST_RF_PORT C
++#define nRST_RF_BIT 7
++#define SLP_TR_PORT B
++#define SLP_TR_BIT 4
++
++#define SCLK_PORT D
++#define SCLK_BIT 5
++#define MOSI_PORT D
++#define MOSI_BIT 3
++
++#define MISO_PORT D
++#define MISO_BIT 2
++#define nSS_PORT D
++#define nSS_BIT 1
++#define IRQ_RF_PORT D
++#define IRQ_RF_BIT 0
++
++#define SPI_WAIT_DONE() while (!(UCSR1A & 1 << RXC1))
++#define SPI_DATA UDR1
++
++void set_clkm(void);
++void board_init(void);
++
++void spi_begin(void);
++void spi_off(void);
++void spi_init(void);
++
++#endif /* !BOARD_H */
+diff --git a/atusb/board_hulusb.c b/atusb/board_hulusb.c
+new file mode 100644
+index 0000000..084714e
+--- /dev/null
++++ b/atusb/board_hulusb.c
+@@ -0,0 +1,179 @@
++/*
++ * fw/board_hulusb.c - Busware HUL Board-specific functions (for boot loader and application)
++ *
++ * Written 2017 by Filzmaier Josef
++ * Based on fw/board_rzusb written and Copyright 2016 Stefan Schmidt
++ *
++ * 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.
++ */
++
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include <avr/io.h>
++#include <avr/interrupt.h>
++#include <avr/boot.h>
++
++#define F_CPU 8000000UL
++#include <util/delay.h>
++
++#include "usb.h"
++#include "at86rf230.h"
++#include "board.h"
++#include "spi.h"
++#include "usb/usb.h"
++
++static bool spi_initialized = 0;
++
++void reset_rf(void)
++{
++ /* set up all the outputs; default port value is 0 */
++
++ DDRB = 0;
++ DDRC = 0;
++ DDRD = 0;
++ PORTB = 0;
++ PORTC = 0;
++ PORTD = 0;
++
++ OUT(LED_RED);
++ OUT(LED_GREEN);
++ SET(LED_RED); /* Leds are active low on HULUSB board */
++ CLR(LED_GREEN); /* Green Led indicates the dongle is running */
++ OUT(nRST_RF); /* this also resets the transceiver */
++ OUT(SLP_TR);
++
++ spi_init();
++
++ /* AT86RF212 data sheet, Appendix B, p166 Power-On Reset procedure */
++ /*-----------------------------------------------------------------*/
++ CLR(SLP_TR);
++ SET(nRST_RF);
++ SET(nSS);
++ _delay_us(400);
++
++ CLR(nRST_RF);
++ _delay_us(2);
++ SET(nRST_RF);
++
++ /* 5.1.4.5: Wait t10: 625 ns (min) */
++
++ _delay_us(2);
++
++ reg_write(REG_TRX_CTRL_0, 0x19);
++
++ change_state(TRX_CMD_FORCE_TRX_OFF);
++ /*-----------------------------------------------------------------*/
++
++ /* we must restore TRX_CTRL_0 after each reset (7.7.4) */
++
++ set_clkm();
++}
++
++void led_red(bool on) {
++ if (on)
++ CLR(LED_RED);
++ else
++ SET(LED_RED);
++}
++
++void led_green(bool on) {
++ if (on)
++ CLR(LED_GREEN);
++ else
++ SET(LED_GREEN);
++}
++
++void led(bool on)
++{
++ led_red(on);
++}
++
++void set_clkm(void)
++{
++ /* CLKM is not connected on BUSWARE HUL and therefore it is running in
++ * async mode. */
++ reg_write(REG_TRX_CTRL_0, 0x00);
++
++ /* TX_AUTO_CRC_ON, default disabled */
++ subreg_write(SR_TX_AUTO_CRC_ON, 1);
++}
++
++void board_init(void)
++{
++ /* Disable the watchdog timer */
++
++ MCUSR = 0; /* Remove override */
++ WDTCSR |= 1 << WDCE; /* Enable change */
++ WDTCSR = 1 << WDCE; /* Disable watchdog while still enabling
++ change */
++
++ CLKPR = 1 << CLKPCE;
++ /* We start with a 16 MHz/8 clock. Put the prescaler to 2. */
++ CLKPR = 1 << CLKPS0;
++
++ get_sernum();
++}
++
++void spi_begin(void)
++{
++ if (!spi_initialized)
++ spi_init();
++ CLR(nSS);
++}
++
++void spi_off(void)
++{
++ spi_initialized = 0;
++ SPCR &= ~(1 << SPE);
++}
++
++void spi_init(void)
++{
++ SET(nSS);
++ OUT(SCLK);
++ OUT(MOSI);
++ OUT(nSS);
++ IN(MISO);
++
++ SPCR = (1 << SPE) | (1 << MSTR);
++ SPSR = (1 << SPI2X);
++
++ spi_initialized = 1;
++}
++
++void usb_init(void)
++{
++ USBCON |= 1 << FRZCLK; /* freeze the clock */
++
++ /* enable the PLL and wait for it to lock */
++ /* TODO sheet page 50 For Atmel AT90USB128x only. Do not use with Atmel AT90USB64x. */
++ /* FOR 8 XTAL Mhz only!!! */
++ PLLCSR = ((1 << PLLP1) | (1 << PLLP0));
++ PLLCSR |= 1 << PLLE;
++ while (!(PLLCSR & (1 << PLOCK)));
++
++ UHWCON |= (1 << UVREGE);
++
++ USBCON &= ~((1 << USBE) | (1 << OTGPADE)); /* reset the controller */
++ USBCON |= ((1 << USBE) | (1 << OTGPADE));
++
++ USBCON &= ~(1 << FRZCLK); /* thaw the clock */
++
++ UDCON &= ~(1 << DETACH); /* attach the pull-up */
++ UDIEN = 1 << EORSTE; /* enable device interrupts */
++ // UDCON |= 1 << RSTCPU; /* reset CPU on bus reset */
++
++ ep_init();
++}
++
++void board_app_init(void)
++{
++ /* enable INT0, trigger on rising edge */
++ EICRA = 1 << ISC01 | 1 << ISC00;
++ EIMSK = 1 << INT0;
++}
+diff --git a/atusb/board_hulusb.h b/atusb/board_hulusb.h
+new file mode 100644
+index 0000000..a1dadf0
+--- /dev/null
++++ b/atusb/board_hulusb.h
+@@ -0,0 +1,66 @@
++/*
++ * fw/board_hulusb.h - Busware HUL Board-specific functions (for boot loader and application)
++ *
++ * Written 2017 by Filzmaier Josef
++ * Based on fw/board_rzusb written and Copyright 2016 Stefan Schmidt
++ *
++ * 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.
++ */
++
++#ifndef BOARD_HULUSB_H
++#define BOARD_HULUSB_H
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#define LED_RED_PORT A
++#define LED_GREEN_PORT A
++#define LED_RED_BIT 3
++#define LED_GREEN_BIT 4
++#define LED_PORT LED_RED_PORT
++#define LED_BIT LED_RED_BIT
++
++#define nRST_RF_PORT B
++#define nRST_RF_BIT 5
++#define SLP_TR_PORT B
++#define SLP_TR_BIT 4
++
++#define SCLK_PORT B
++#define SCLK_BIT 1
++#define MOSI_PORT B
++#define MOSI_BIT 2
++
++#define MISO_PORT B
++#define MISO_BIT 3
++#define nSS_PORT B
++#define nSS_BIT 0
++#define IRQ_RF_PORT D
++#define IRQ_RF_BIT 4
++
++#define SR_TX_AUTO_CRC_ON 0x04, 0x20, 5
++#define SR_CHANNEL 0x08, 0x1f, 0
++
++#define RG_CC_CTRL_1 (0x14)
++
++#define SPI_WAIT_DONE() while ((SPSR & (1 << SPIF)) == 0)
++#define SPI_DATA SPDR
++
++void set_clkm(void);
++void board_init(void);
++
++void led_red(bool on);
++void led_green(bool on);
++
++void spi_begin(void);
++void spi_off(void);
++void spi_init(void);
++
++#ifdef DEBUG
++void printStatus(void);
++#define PRINT_STATUS() printStatus()
++#endif
++
++#endif /* !BOARD_HULUSB_H */
+diff --git a/atusb/board_rzusb.c b/atusb/board_rzusb.c
+new file mode 100644
+index 0000000..e83d6fa
+--- /dev/null
++++ b/atusb/board_rzusb.c
+@@ -0,0 +1,169 @@
++/*
++ * fw/board_rzusb.c - RZUSB Board-specific functions (for boot loader and application)
++ *
++ * Written 2016 by Stefan Schmidt
++ * Copyright 2016 Stefan Schmidt
++ *
++ * 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.
++ */
++
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include <avr/io.h>
++#include <avr/interrupt.h>
++#include <avr/boot.h>
++
++#define F_CPU 8000000UL
++#include <util/delay.h>
++
++#include "usb.h"
++#include "at86rf230.h"
++#include "board.h"
++#include "spi.h"
++#include "usb/usb.h"
++
++static bool spi_initialized = 0;
++
++void reset_rf(void)
++{
++ /* set up all the outputs; default port value is 0 */
++
++ DDRB = 0;
++ DDRC = 0;
++ DDRD = 0;
++ PORTB = 0;
++ PORTC = 0;
++ PORTD = 0;
++
++ OUT(LED);
++ OUT(nRST_RF); /* this also resets the transceiver */
++ OUT(SLP_TR);
++
++ spi_init();
++
++ /* AT86RF231 data sheet, 12.4.13, reset pulse width: 625 ns (min) */
++
++ CLR(nRST_RF);
++ _delay_us(2);
++ SET(nRST_RF);
++
++ /* 12.4.14: SPI access latency after reset: 625 ns (min) */
++
++ _delay_us(2);
++
++ /* we must restore TRX_CTRL_0 after each reset (9.6.4) */
++
++ set_clkm();
++}
++
++void led(bool on)
++{
++ if (on)
++ SET(LED);
++ else
++ CLR(LED);
++}
++
++void set_clkm(void)
++{
++ /* switch CLKM to 8 MHz */
++
++ /*
++ * @@@ Note: Atmel advise against changing the external clock in
++ * mid-flight. We should therefore switch to the RC clock first, then
++ * crank up the external clock, and finally switch back to the external
++ * clock. The clock switching procedure is described in the ATmega32U2
++ * data sheet in secton 8.2.2.
++ */
++ spi_begin();
++ spi_send(AT86RF230_REG_WRITE | REG_TRX_CTRL_0);
++ spi_send(0x10);
++ spi_end();
++
++ /* TX_AUTO_CRC_ON, default disabled */
++ spi_begin();
++ spi_send(AT86RF230_REG_WRITE | 0x05);
++ spi_send(0x80);
++ spi_end();
++}
++
++void board_init(void)
++{
++ /* Disable the watchdog timer */
++
++ MCUSR = 0; /* Remove override */
++ WDTCSR |= 1 << WDCE; /* Enable change */
++ WDTCSR = 1 << WDCE; /* Disable watchdog while still enabling
++ change */
++
++ CLKPR = 1 << CLKPCE;
++ /* We start with a 16 MHz/8 clock. Put the prescaler to 2. */
++ CLKPR = 1 << CLKPS0;
++
++ get_sernum();
++}
++
++void spi_begin(void)
++{
++ if (!spi_initialized)
++ spi_init();
++ CLR(nSS);
++}
++
++void spi_off(void)
++{
++ spi_initialized = 0;
++ SPCR &= ~(1 << SPE);
++}
++
++void spi_init(void)
++{
++ SET(nSS);
++ OUT(SCLK);
++ OUT(MOSI);
++ OUT(nSS);
++ IN(MISO);
++
++ SPCR = (1 << SPE) | (1 << MSTR);
++ SPSR = (1 << SPI2X);
++
++ spi_initialized = 1;
++}
++
++void usb_init(void)
++{
++ USBCON |= 1 << FRZCLK; /* freeze the clock */
++
++ /* enable the PLL and wait for it to lock */
++ /* TODO sheet page 50 For Atmel AT90USB128x only. Do not use with Atmel AT90USB64x. */
++ /* FOR 8 XTAL Mhz only!!! */
++ PLLCSR = ((1 << PLLP1) | (1 << PLLP0));
++ PLLCSR |= 1 << PLLE;
++ while (!(PLLCSR & (1 << PLOCK)));
++
++ UHWCON |= (1 << UVREGE);
++
++ USBCON &= ~((1 << USBE) | (1 << OTGPADE)); /* reset the controller */
++ USBCON |= ((1 << USBE) | (1 << OTGPADE));
++
++ USBCON &= ~(1 << FRZCLK); /* thaw the clock */
++
++ UDCON &= ~(1 << DETACH); /* attach the pull-up */
++ UDIEN = 1 << EORSTE; /* enable device interrupts */
++// UDCON |= 1 << RSTCPU; /* reset CPU on bus reset */
++
++ ep_init();
++}
++
++void board_app_init(void)
++{
++ /* enable timer input capture 1, trigger on rising edge */
++ TCCR1B = (1 << ICES1);
++ TIFR1 = (1 << ICF1);
++ TIMSK1 = (1 << ICIE1);
++}
+diff --git a/atusb/board_rzusb.h b/atusb/board_rzusb.h
+new file mode 100644
+index 0000000..c2e518f
+--- /dev/null
++++ b/atusb/board_rzusb.h
+@@ -0,0 +1,48 @@
++/*
++ * fw/board_rzusb.h - RZUSB Board-specific functions and definitions
++ *
++ * Written 2016 by Stefan Schmidt
++ * Copyright 2016 Stefan Schmidt
++ *
++ * 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.
++ */
++
++#ifndef BOARD_RZUSB_H
++#define BOARD_RZUSB_H
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#define LED_PORT D
++#define LED_BIT 7
++#define nRST_RF_PORT B
++#define nRST_RF_BIT 5
++#define SLP_TR_PORT B
++#define SLP_TR_BIT 4
++
++#define SCLK_PORT B
++#define SCLK_BIT 1
++#define MOSI_PORT B
++#define MOSI_BIT 2
++
++#define MISO_PORT B
++#define MISO_BIT 3
++#define nSS_PORT B
++#define nSS_BIT 0
++#define IRQ_RF_PORT D
++#define IRQ_RF_BIT 4
++
++#define SPI_WAIT_DONE() while ((SPSR & (1 << SPIF)) == 0)
++#define SPI_DATA SPDR
++
++void set_clkm(void);
++void board_init(void);
++
++void spi_begin(void);
++void spi_off(void);
++void spi_init(void);
++
++#endif /* !BOARD_H */
+diff --git a/atusb/boot.c b/atusb/boot.c
+new file mode 100644
+index 0000000..6826ac6
+--- /dev/null
++++ b/atusb/boot.c
+@@ -0,0 +1,77 @@
++/*
++ * fw/boot.c - DFU boot loader for ATUSB
++ *
++ * Written 2008-2011 by Werner Almesberger
++ * Copyright 2008-2011 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#include <stdint.h>
++
++#include <avr/io.h>
++#include <avr/interrupt.h>
++#include <avr/pgmspace.h>
++
++#define F_CPU 8000000UL
++#include <util/delay.h>
++
++#include "usb.h"
++#include "dfu.h"
++
++#include "board.h"
++#include "spi.h"
++#include "atusb/ep0.h"
++
++
++#define MS_TO_LOOPS(ms) ((uint32_t) (ms)*335)
++
++
++static void (*run_payload)(void) = 0;
++
++
++int main(void)
++{
++ /*
++ * pgm_read_byte gets cached and there doesn't seem to be any other
++ * way to dissuade gcc from doing this.
++ */
++ volatile int zero = 0;
++ uint32_t loop = 0;
++
++ board_init();
++ reset_rf();
++
++ /* now we should be at 8 MHz */
++
++ usb_init();
++ dfu_init();
++
++ /* move interrupt vectors to the boot loader */
++ MCUCR = 1 << IVCE;
++ MCUCR = 1 << IVSEL;
++
++ sei();
++
++ led(1);
++
++ while (loop != MS_TO_LOOPS(2500)) {
++ if (dfu.state == dfuIDLE && pgm_read_byte(zero) != 0xff)
++ loop++;
++ else
++ loop = 0;
++ }
++
++ led(0);
++
++ cli();
++
++ usb_reset();
++ run_payload();
++
++ while (1); /* not reached */
++}
+diff --git a/atusb/descr.c b/atusb/descr.c
+new file mode 100644
+index 0000000..f96b0ee
+--- /dev/null
++++ b/atusb/descr.c
+@@ -0,0 +1,104 @@
++/*
++ * fw/descr.c - USB descriptors
++ *
++ * Written 2008-2011, 2014 by Werner Almesberger
++ * Copyright 2008-2011, 2014 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#include "usb.h"
++#include "dfu.h"
++#include "board.h"
++
++
++#define LE(x) ((uint16_t) (x) & 0xff), ((uint16_t) (x) >> 8)
++
++/*
++ * Device descriptor
++ */
++
++const uint8_t device_descriptor[18] = {
++ 18, /* bLength */
++ USB_DT_DEVICE, /* bDescriptorType */
++ LE(0x200), /* bcdUSB */
++ USB_CLASS_VENDOR_SPEC, /* bDeviceClass */
++ 0x00, /* bDeviceSubClass */
++ 0x00, /* bDeviceProtocol */
++ EP0_SIZE, /* bMaxPacketSize */
++ LE(USB_VENDOR), /* idVendor */
++ LE(USB_PRODUCT), /* idProduct */
++ LE(0x0001), /* bcdDevice */
++ 0, /* iManufacturer */
++ 0, /* iProduct */
++#ifdef HAS_BOARD_SERNUM
++ 1, /* iSerialNumber */
++#else
++ 0, /* iSerialNumber */
++#endif
++ 1 /* bNumConfigurations */
++};
++
++
++/*
++ * Our configuration
++ *
++ * We're always bus-powered.
++ */
++
++const uint8_t config_descriptor[] = {
++ 9, /* bLength */
++ USB_DT_CONFIG, /* bDescriptorType */
++#if 0
++ LE(9+9+7+7), /* wTotalLength */
++#else
++ LE(9+9+7+9), /* wTotalLength */
++#endif
++ 2, /* bNumInterfaces */
++ 1, /* bConfigurationValue (> 0 !) */
++ 0, /* iConfiguration */
++ USB_ATTR_BUS_POWERED, /* bmAttributes */
++ ((BOARD_MAX_mA)+1)/2, /* bMaxPower */
++
++ /* Interface #0 */
++
++ 9, /* bLength */
++ USB_DT_INTERFACE, /* bDescriptorType */
++ 0, /* bInterfaceNumber */
++ 0, /* bAlternateSetting */
++ 1, /* bNumEndpoints */
++ USB_CLASS_VENDOR_SPEC, /* bInterfaceClass */
++ 0, /* bInterfaceSubClass */
++ 0, /* bInterfaceProtocol */
++ 0, /* iInterface */
++
++#if 0
++ /* EP OUT */
++
++ 7, /* bLength */
++ USB_DT_ENDPOINT, /* bDescriptorType */
++ 0x01, /* bEndPointAddress */
++ 0x02, /* bmAttributes (bulk) */
++ LE(EP1_SIZE), /* wMaxPacketSize */
++ 0, /* bInterval */
++#endif
++
++#if 1
++ /* EP IN */
++
++ 7, /* bLength */
++ USB_DT_ENDPOINT, /* bDescriptorType */
++ 0x81, /* bEndPointAddress */
++ 0x02, /* bmAttributes (bulk) */
++ LE(EP1_SIZE), /* wMaxPacketSize */
++ 0, /* bInterval */
++#endif
++
++ /* Interface #1 */
++
++ DFU_ITF_DESCR(1, 0, dfu_proto_runtime, 0)
++};
+diff --git a/atusb/ep0.c b/atusb/ep0.c
+new file mode 100644
+index 0000000..fa43f3b
+--- /dev/null
++++ b/atusb/ep0.c
+@@ -0,0 +1,338 @@
++/*
++ * fw/ep0.c - EP0 extension protocol
++ *
++ * Written 2008-2011, 2013 by Werner Almesberger
++ * Copyright 2008-2011, 2013 Werner Almesberger
++ * Copyright 2015-2016 Stefan Schmidt
++ *
++ * 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.
++ */
++
++
++#include <stdbool.h>
++#include <stdint.h>
++#include <string.h>
++
++#include <avr/io.h>
++#include <avr/eeprom.h>
++
++#define F_CPU 8000000UL
++#include <util/delay.h>
++
++#ifndef NULL
++#define NULL 0
++#endif
++
++#include "usb.h"
++#include "dfu.h"
++
++#include "at86rf230.h"
++#include "atusb/ep0.h"
++#include "version.h"
++#include "board.h"
++#include "sernum.h"
++#include "spi.h"
++#include "mac.h"
++
++#ifdef ATUSB
++#define HW_TYPE ATUSB_HW_TYPE_110131
++#endif
++
++#ifdef RZUSB
++#define HW_TYPE ATUSB_HW_TYPE_RZUSB
++#endif
++
++#ifdef HULUSB
++#define HW_TYPE ATUSB_HW_TYPE_HULUSB
++#endif
++
++#ifdef DEBUG
++#include "uart.h"
++#include <stdio.h>
++#define debug(FORMAT,args...) printf(FORMAT,##args)
++#define error(FORMAT,args...) printf(FORMAT,##args)
++#else
++#define debug(...)
++#define error(...)
++#endif
++
++
++static const uint8_t id[] = { EP0ATUSB_MAJOR, EP0ATUSB_MINOR, HW_TYPE };
++static uint8_t buf[MAX_PSDU+3]; /* command, PHDR, and LQI */
++static uint8_t size;
++
++
++static void do_eeprom_write(void *user)
++{
++ int i;
++
++ for (i = 0; i < size; i++)
++ eeprom_update_byte((uint8_t*)i, buf[i]);
++}
++
++static void do_buf_write(void *user)
++{
++ uint8_t i;
++
++ spi_begin();
++ for (i = 0; i != size; i++)
++ spi_send(buf[i]);
++ spi_end();
++}
++
++
++#define BUILD_OFFSET 7 /* '#' plus "65535" plus ' ' */
++
++
++static bool my_setup(const struct setup_request *setup)
++{
++ uint16_t req = setup->bmRequestType | setup->bRequest << 8;
++ unsigned tmp;
++ uint8_t i;
++ uint64_t tmp64;
++
++ switch (req) {
++ case ATUSB_FROM_DEV(ATUSB_ID):
++ debug("ATUSB_ID\n");
++ if (setup->wLength > 3)
++ return 0;
++ usb_send(&eps[0], id, setup->wLength, NULL, NULL);
++ return 1;
++ case ATUSB_FROM_DEV(ATUSB_BUILD):
++ debug("ATUSB_BUILD\n");
++ tmp = build_number;
++ for (i = BUILD_OFFSET-2; tmp; i--) {
++ buf[i] = (tmp % 10)+'0';
++ tmp /= 10;
++ }
++ buf[i] = '#';
++ buf[BUILD_OFFSET-1] = ' ';
++ for (size = 0; build_date[size]; size++)
++ buf[BUILD_OFFSET+size] = build_date[size];
++ size += BUILD_OFFSET-i;
++ if (size > setup->wLength)
++ return 0;
++ usb_send(&eps[0], buf+i, size, NULL, NULL);
++ return 1;
++
++ case ATUSB_TO_DEV(ATUSB_RESET):
++ debug("ATUSB_RESET\n");
++ reset_cpu();
++ while (1);
++
++ case ATUSB_TO_DEV(ATUSB_RF_RESET):
++ debug("ATUSB_RF_RESET\n");
++ reset_rf();
++ mac_reset();
++ //ep_send_zlp(EP_CTRL);
++ return 1;
++
++ case ATUSB_FROM_DEV(ATUSB_POLL_INT):
++ debug("ATUSB_POLL_INT\n");
++ if (setup->wLength < 1)
++ return 0;
++ *buf = read_irq();
++ usb_send(&eps[0], buf, 1, NULL, NULL);
++ return 1;
++
++ case ATUSB_FROM_DEV(ATUSB_TIMER):
++ debug("ATUSB_TIMER\n");
++ size = setup->wLength;
++ if (size > sizeof(tmp64))
++ size = sizeof(tmp64);
++ tmp64 = timer_read();
++ memcpy(buf, &tmp64, sizeof(tmp64));
++ usb_send(&eps[0], buf, size, NULL, NULL);
++ return 1;
++
++ case ATUSB_FROM_DEV(ATUSB_GPIO):
++ debug("ATUSB_GPIO\n");
++ if (setup->wLength < 3)
++ return 0;
++ if (!gpio(setup->wIndex, setup->wValue, setup->wValue >> 8,
++ setup->wIndex >> 8, buf))
++ return 0;
++ usb_send(&eps[0], buf, 3, NULL, NULL);
++ return 1;
++ case ATUSB_TO_DEV(ATUSB_GPIO_CLEANUP):
++ gpio_cleanup();
++ return 1;
++
++ case ATUSB_TO_DEV(ATUSB_SLP_TR):
++ debug("ATUSB_SLP_TR\n");
++ slp_tr();
++ return 1;
++
++ case ATUSB_TO_DEV(ATUSB_REG_WRITE):
++ debug("ATUSB_REG_WRITE\n");
++ spi_begin();
++ spi_send(AT86RF230_REG_WRITE | setup->wIndex);
++ spi_send(setup->wValue);
++ spi_end();
++ //ep_send_zlp(EP_CTRL);
++ return 1;
++ case ATUSB_FROM_DEV(ATUSB_REG_READ):
++ debug("ATUSB_REG_READ\n");
++ spi_begin();
++ spi_send(AT86RF230_REG_READ | setup->wIndex);
++ *buf = spi_recv();
++ spi_end();
++ usb_send(&eps[0], buf, 1, NULL, NULL);
++ return 1;
++
++ case ATUSB_TO_DEV(ATUSB_BUF_WRITE):
++ debug("ATUSB_BUF_WRITE\n");
++ if (setup->wLength < 1)
++ return 0;
++ if (setup->wLength > MAX_PSDU)
++ return 0;
++ buf[0] = AT86RF230_BUF_WRITE;
++ buf[1] = setup->wLength;
++ size = setup->wLength+2;
++ usb_recv(&eps[0], buf+2, setup->wLength, do_buf_write, NULL);
++ return 1;
++ case ATUSB_FROM_DEV(ATUSB_BUF_READ):
++ debug("ATUSB_BUF_READ\n");
++ if (setup->wLength < 2) /* PHR+LQI */
++ return 0;
++ if (setup->wLength > MAX_PSDU+2) /* PHR+PSDU+LQI */
++ return 0;
++ spi_begin();
++ spi_send(AT86RF230_BUF_READ);
++ size = spi_recv();
++ if (size >= setup->wLength)
++ size = setup->wLength-1;
++ for (i = 0; i != size+1; i++)
++ buf[i] = spi_recv();
++ spi_end();
++ usb_send(&eps[0], buf, size+1, NULL, NULL);
++ return 1;
++
++ case ATUSB_TO_DEV(ATUSB_SRAM_WRITE):
++ debug("ATUSB_SRAM_WRITE\n");
++ if (setup->wIndex > SRAM_SIZE)
++ return 0;
++ if (setup->wIndex+setup->wLength > SRAM_SIZE)
++ return 0;
++ buf[0] = AT86RF230_SRAM_WRITE;
++ buf[1] = setup->wIndex;
++ size = setup->wLength+2;
++ usb_recv(&eps[0], buf+2, setup->wLength, do_buf_write, NULL);
++ return 1;
++ case ATUSB_FROM_DEV(ATUSB_SRAM_READ):
++ debug("ATUSB_SRAM_READ\n");
++ if (setup->wIndex > SRAM_SIZE)
++ return 0;
++ if (setup->wIndex+setup->wLength > SRAM_SIZE)
++ return 0;
++ spi_begin();
++ spi_send(AT86RF230_SRAM_READ);
++ spi_send(setup->wIndex);
++ for (i = 0; i != setup->wLength; i++)
++ buf[i] = spi_recv();
++ spi_end();
++ usb_send(&eps[0], buf, setup->wLength, NULL, NULL);
++ return 1;
++
++ case ATUSB_TO_DEV(ATUSB_SPI_WRITE):
++ size = setup->wLength+2;
++ if (size > sizeof(buf))
++ return 0;
++ buf[0] = setup->wValue;
++ buf[1] = setup->wIndex;
++ if (setup->wLength)
++ usb_recv(&eps[0], buf+2, setup->wLength,
++ do_buf_write, NULL);
++ else
++ do_buf_write(NULL);
++ return 1;
++ case ATUSB_FROM_DEV(ATUSB_SPI_WRITE2_SYNC):
++ spi_begin();
++ spi_send(setup->wValue);
++ spi_send(setup->wIndex);
++ spi_end();
++ buf[0] = irq_serial;
++ if (setup->wLength)
++ usb_send(&eps[0], buf, 1, NULL, NULL);
++ return 1;
++
++ case ATUSB_FROM_DEV(ATUSB_SPI_READ1):
++ case ATUSB_FROM_DEV(ATUSB_SPI_READ2):
++ spi_begin();
++ spi_send(setup->wValue);
++ if (req == ATUSB_FROM_DEV(ATUSB_SPI_READ2))
++ spi_send(setup->wIndex);
++ for (i = 0; i != setup->wLength; i++)
++ buf[i] = spi_recv();
++ spi_end();
++ usb_send(&eps[0], buf, setup->wLength, NULL, NULL);
++ return 1;
++
++ case ATUSB_TO_DEV(ATUSB_RX_MODE):
++ return mac_rx(setup->wValue);
++ case ATUSB_TO_DEV(ATUSB_TX):
++ return mac_tx(setup->wValue, setup->wIndex, setup->wLength);
++ case ATUSB_TO_DEV(ATUSB_EUI64_WRITE):
++ debug("ATUSB_EUI64_WRITE\n");
++ usb_recv(&eps[0], buf, setup->wLength, do_eeprom_write, NULL);
++ _delay_ms(100);
++ reset_cpu();
++ return 1;
++
++ case ATUSB_FROM_DEV(ATUSB_EUI64_READ):
++ debug("ATUSB_EUI64_READ\n");
++ eeprom_read_block(buf, (const void*)0, 8);
++ usb_send(&eps[0], buf, 8, NULL, NULL);
++ return 1;
++
++ default:
++ error("Unrecognized SETUP: 0x%02x 0x%02x ...\n",
++ setup->bmRequestType, setup->bRequest);
++ return 0;
++ }
++}
++
++
++static bool my_dfu_setup(const struct setup_request *setup)
++{
++ switch (setup->bmRequestType | setup->bRequest << 8) {
++ case DFU_TO_DEV(DFU_DETACH):
++ /* @@@ should use wTimeout */
++ dfu.state = appDETACH;
++ return 1;
++ default:
++ return dfu_setup_common(setup);
++ }
++}
++
++
++static void my_set_interface(int nth)
++{
++ if (nth) {
++ user_setup = my_dfu_setup;
++ user_get_descriptor = dfu_my_descr;
++ dfu.state = appIDLE;
++ } else {
++ user_setup = my_setup;
++ user_get_descriptor = sernum_get_descr;
++ }
++}
++
++
++static void my_reset(void)
++{
++ if (dfu.state == appDETACH)
++ reset_cpu();
++}
++
++
++void ep0_init(void)
++{
++ user_setup = my_setup;
++ user_set_interface = my_set_interface;
++ my_set_interface(0);
++ user_reset = my_reset;
++}
+diff --git a/atusb/flash.c b/atusb/flash.c
+new file mode 100644
+index 0000000..1f8e59d
+--- /dev/null
++++ b/atusb/flash.c
+@@ -0,0 +1,97 @@
++/*
++ * fw/flash.c - Board-specific flash functions
++ *
++ * Written 2011, 2013-2015 by Werner Almesberger
++ * Copyright 2011, 2013-2015 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include <avr/boot.h>
++#include <avr/pgmspace.h>
++
++#include "dfu.h"
++#include "board.h"
++
++
++static uint32_t payload;
++
++
++static void flash_start(void)
++{
++ payload = 0;
++}
++
++
++static bool flash_can_write(uint16_t size)
++{
++ return payload+size <= BOOT_ADDR;
++}
++
++
++static void flash_write(const uint8_t *buf, uint16_t size)
++{
++ static uint8_t last;
++ const uint8_t *p;
++
++ for (p = buf; p != buf+size; p++) {
++ if (!(payload & (SPM_PAGESIZE-1))) {
++ boot_page_erase(payload);
++ boot_spm_busy_wait();
++ }
++
++ if (payload & 1)
++ boot_page_fill(payload, last | (*p << 8));
++ else
++ last = *p;
++ payload++;
++
++ if (!(payload & (SPM_PAGESIZE-1))) {
++ boot_page_write(payload-SPM_PAGESIZE);
++ boot_spm_busy_wait();
++ }
++ }
++}
++
++
++static void flash_end_write(void)
++{
++ if (payload & (SPM_PAGESIZE-1)) {
++ boot_page_write(payload & ~(SPM_PAGESIZE-1));
++ boot_spm_busy_wait();
++ }
++ boot_rww_enable();
++}
++
++
++static uint16_t flash_read(uint8_t *buf, uint16_t size)
++{
++ uint16_t got = 0;
++
++ while (size && payload != (uint32_t) FLASHEND+1) {
++ *buf++ = pgm_read_byte(payload);
++ payload++;
++ size--;
++ got++;
++ }
++ return got;
++}
++
++
++static const struct dfu_flash_ops flash_ops = {
++ .start = flash_start,
++ .can_write = flash_can_write,
++ .write = flash_write,
++ .end_write = flash_end_write,
++ .read = flash_read,
++};
++
++
++const struct dfu_flash_ops *dfu_flash_ops = &flash_ops;
+diff --git a/atusb/include/at86rf230.h b/atusb/include/at86rf230.h
+new file mode 100644
+index 0000000..4c3ae22
+--- /dev/null
++++ b/atusb/include/at86rf230.h
+@@ -0,0 +1,402 @@
++/*
++ * include/at86rf230.h - AT86RF230/AT86RF231 protocol and register definitions
++ *
++ * Written 2008-2011 by Werner Almesberger
++ * Copyright 2008-2011 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#ifndef AT86RF230_H
++#define AT86RF230_H
++
++enum {
++ AT86RF230_REG_WRITE = 0xc0, /* 11... */
++ AT86RF230_REG_READ = 0x80, /* 10... */
++ AT86RF230_BUF_WRITE = 0x60, /* 011... */
++ AT86RF230_BUF_READ = 0x20, /* 001... */
++ AT86RF230_SRAM_WRITE = 0x40, /* 010... */
++ AT86RF230_SRAM_READ = 0x00 /* 000... */
++};
++
++#define MAX_PSDU 127 /* octets, see AT86RF230 manual section 8.1 */
++#define SRAM_SIZE 128
++
++
++/* --- Registers ----------------------------------------------------------- */
++
++enum {
++ REG_TRX_STATUS = 0x01,
++ REG_TRX_STATE = 0x02,
++ REG_TRX_CTRL_0 = 0x03,
++
++ REG_TRX_CTRL_1 = 0x04, /* 231 only */
++
++ REG_PHY_TX_PWR = 0x05,
++ REG_PHY_RSSI = 0x06,
++ REG_PHY_ED_LEVEL = 0x07,
++ REG_PHY_CC_CCA = 0x08,
++ REG_CCA_THRES = 0x09,
++
++ REG_RX_CTRL = 0x0a, /* 231 only */
++ REG_SFD_VALUE = 0x0b, /* 231 only */
++ REG_TRX_CTRL_2 = 0x0c, /* 231 only */
++ REG_ANT_DIV = 0x0d, /* 231 only */
++
++ REG_IRQ_MASK = 0x0e,
++ REG_IRQ_STATUS = 0x0f,
++ REG_VREG_CTRL = 0x10,
++ REG_BATMON = 0x11,
++ REG_XOSC_CTRL = 0x12,
++
++ REG_RX_SYN = 0x15, /* 231 only */
++ REG_XAH_CTRL_1 = 0x17, /* 231 only */
++ REG_FTN_CTRL = 0x18, /* 231 only */
++
++ REG_PLL_CF = 0x1a,
++ REL_PLL_DCU = 0x1b,
++ REG_PART_NUM = 0x1c,
++ REG_VERSION_NUM = 0x1d,
++ REG_MAN_ID_0 = 0x1e,
++ REG_MAN_ID_1 = 0x1f,
++ REG_SHORT_ADDR_0 = 0x20,
++ REG_SHORT_ADDR_1 = 0x21,
++ REG_PAN_ID_0 = 0x22,
++ REG_PAN_ID_1 = 0x23,
++ REG_IEEE_ADDR_0 = 0x24,
++ REG_IEEE_ADDR_1 = 0x25,
++ REG_IEEE_ADDR_2 = 0x26,
++ REG_IEEE_ADDR_3 = 0x27,
++ REG_IEEE_ADDR_4 = 0x28,
++ REG_IEEE_ADDR_5 = 0x29,
++ REG_IEEE_ADDR_6 = 0x2a,
++ REG_IEEE_ADDR_7 = 0x2b,
++
++ REG_XAH_CTRL_0 = 0x2c, /* XAH_CTRL in 230 */
++ REG_CSMA_SEED_0 = 0x2d,
++ REG_CSMA_SEED_1 = 0x2e,
++ REG_CSMA_BE = 0x2f, /* 231 only */
++
++ REG_CONT_TX_0 = 0x36,
++ REG_CONT_TX_1 = 0x3d, /* 230 only */
++};
++
++/* --- TRX_STATUS --- ------------------------------------------------------ */
++
++#define CCA_DONE (1 << 7)
++#define CCA_STATUS (1 << 6)
++
++#define TRX_STATUS_SHIFT 0
++#define TRX_STATUS_MASK 0x1f
++
++enum {
++ TRX_STATUS_P_ON = 0x00, /* reset default */
++ TRX_STATUS_BUSY_RX = 0x01,
++ TRX_STATUS_BUSY_TX = 0x02,
++ TRX_STATUS_RX_ON = 0x06,
++ TRX_STATUS_TRX_OFF = 0x08,
++ TRX_STATUS_PLL_ON = 0x09,
++ TRX_STATUS_SLEEP = 0x0f,
++ TRX_STATUS_BUSY_RX_AACK = 0x11,
++ TRX_STATUS_BUSY_TX_ARET = 0x12,
++ TRX_STATUS_RX_AACK_ON = 0x16,
++ TRX_STATUS_TX_ARET_ON = 0x19,
++ TRX_STATUS_RX_ON_NOCLK = 0x1c,
++ TRX_STATUS_RX_AACK_ON_NOCLK = 0x1d,
++ TRX_STATUS_BUSY_RX_AACK_NOCLK = 0x1e,
++ TRX_STATUS_TRANSITION = 0x1f /* ..._IN_PROGRESS */
++};
++
++/* --- TRX_STATE ----------------------------------------------------------- */
++
++#define TRAC_STATUS_SHIFT 5
++#define TRAC_STATUS_MASK 7
++
++enum {
++ TRAC_STATUS_SUCCESS = 0, /* reset default */
++ TRAC_STATUS_SUCCESS_DATA_PENDING = 1,
++ TRAC_STATUS_SUCCESS_WAIT_FOR_ACK = 2, /* 231 only */
++ TRAC_STATUS_CHANNEL_ACCESS_FAILURE = 3,
++ TRAC_STATUS_NO_ACK = 5,
++ TRAC_STATUS_INVALID = 7
++};
++
++#define TRX_CMD_SHIFT 0
++#define TRX_CMD_MASK 0x1f
++
++enum {
++ TRX_CMD_NOP = 0x00, /* reset default */
++ TRX_CMD_TX_START = 0x02,
++ TRX_CMD_FORCE_TRX_OFF = 0x03,
++ TRX_CMD_FORCE_PLL_ON = 0x04, /* 231 only */
++ TRX_CMD_RX_ON = 0x06,
++ TRX_CMD_TRX_OFF = 0x08,
++ TRX_CMD_PLL_ON = 0x09,
++ TRX_CMD_RX_AACK_ON = 0x16,
++ TRX_CMD_TX_ARET_ON = 0x19,
++};
++
++/* --- TRX_CTRL_0 ---------------------------------------------------------- */
++
++#define PAD_IO_SHIFT 6
++#define PAD_IO_MASK 3
++
++enum {
++ PAD_IO_2mA, /* reset default */
++ PAD_IO_4mA,
++ PAD_IO_6mA,
++ PAD_IO_8mA
++};
++
++#define PAD_IO_CLKM_SHIFT 4
++#define PAD_IO_CLKM_MASK 3
++
++enum {
++ PAD_IO_CLKM_2mA,
++ PAD_IO_CLKM_4mA, /* reset default */
++ PAD_IO_CLKM_5mA,
++ PAD_IO_CLKM_8mA,
++};
++
++#define CLKM_SHA_SEL (1 << 3)
++
++#define CLKM_CTRL_SHIFT 0
++#define CLKM_CTRL_MASK 7
++
++enum {
++ CLKM_CTRL_OFF = 0,
++ CLKM_CTRL_1MHz = 1, /* reset default */
++ CLKM_CTRL_2MHz = 2,
++ CLKM_CTRL_4MHz = 3,
++ CLKM_CTRL_8MHz = 4,
++ CLKM_CTRL_16MHz = 5
++};
++
++/* --- TRX_CTRL_1 (231 only) ----------------------------------------------- */
++
++#define PA_EXT_EN (1 << 7)
++#define IRQ_2_EXT_EN (1 << 6)
++#define TX_AUTO_CRC_ON (1 << 5) /* 231 location */
++#define RX_BL_CTRL (1 << 4)
++
++#define SPI_CMD_MODE_SHIFT 2
++#define SPI_CMD_MODE_MASK 3
++
++enum {
++ SPI_CMD_MODE_EMPTY = 0, /* reset default */
++ SPI_CMD_MODE_TRX_STATUS = 1,
++ SPI_CMD_MODE_PHY_RSSI = 2,
++ SPI_CMD_MODE_IRQ_STATUS = 3,
++};
++
++#define IRQ_MASK_MODE (1 << 1)
++#define IRQ_POLARITY (1 << 0)
++
++/* --- PHY_TX_PWR ---------------------------------------------------------- */
++
++#define TX_AUTO_CRC_ON_230 (1 << 7) /* 230 location */
++
++#define PA_BUF_LT_SHIFT 6
++#define PA_BUF_LT_MASK 3
++
++#define PA_LT_SHIFT 4
++#define PA_LT_MASK 3
++
++#define TX_PWR_SHIFT 0
++#define TX_PWR_MASK 0x0f
++
++/* --- PHY_RSSI ------------------------------------------------------------ */
++
++#define RX_CRC_VALID (1 << 7)
++
++#define RND_VALUE_SHIFT 5 /* 231 only */
++#define RND_VALUE_MASK 3
++
++#define RSSI_SHIFT 0
++#define RSSI_MASK 0x1f
++
++/* --- PHY_CC_CCA ---------------------------------------------------------- */
++
++#define CCA_REQUEST (1 << 7)
++
++#define CCA_MODE_SHIFT 5
++#define CCA_MODE_MASK 3
++
++enum {
++ CCA_MODE_CARRIER_OR_ENERGY = 0, /* 231 only */
++ CCA_MODE_ENERGY = 1, /* reset default */
++ CCA_MODE_CARRIER = 2,
++ CCA_MODE_CARRIER_AND_ENERGY = 3
++};
++
++#define CHANNEL_SHIFT 0
++#define CHANNEL_MASK 0x1f
++
++/* --- CCA_THRES ----------------------------------------------------------- */
++
++#define CCA_ED_THRES_SHIFT 0
++#define CCA_ED_THRES_MASK 0x0f
++
++/* --- RX_CTRL (231 only) -------------------------------------------------- */
++
++#define PDT_THRES_SHIFT 0
++#define PDT_THRES_MASK 0x0f
++
++enum {
++ PDT_THRES_DEFAULT = 0x07, /* reset default */
++ PDT_THRES_DIVERSITY = 0x03,
++};
++
++/* --- TRX_CTRL_2 (231 only) ----------------------------------------------- */
++
++#define RX_SAFE_MODE (1 << 7)
++
++#define OQPSK_DATA_RATE_SHIFT 0
++#define OQPSK_DATA_RATE_MASK 3
++
++enum {
++ OQPSK_DATA_RATE_250 = 0, /* reset default */
++ OQPSK_DATA_RATE_500 = 1,
++ OQPSK_DATA_RATE_1000 = 2,
++ OQPSK_DATA_RATE_2000 = 3
++};
++
++/* --- ANT_DIV (231 only) -------------------------------------------------- */
++
++#define ANT_SEL (1 << 7)
++#define ANT_DIV_EN (1 << 3)
++#define ANT_EXT_SW_EN (1 << 2)
++
++#define ANT_CTRL_SHIFT 0
++#define ANT_CTRL_MASK 3
++
++enum {
++ ANT_CTRL_ANT_0 = 1,
++ ANT_CTRL_ANT_1 = 2,
++ ANT_CTRL_NODIV = 3, /* reset default */
++};
++
++/* --- IRQ_MASK/IRQ_STATUS ------------------------------------------------- */
++
++enum {
++ IRQ_PLL_LOCK = 1 << 0,
++ IRQ_PLL_UNLOCK = 1 << 1,
++ IRQ_RX_START = 1 << 2,
++ IRQ_TRX_END = 1 << 3,
++ IRQ_CCA_ED_DONE = 1 << 4, /* 231 only */
++ IRQ_AMI = 1 << 5, /* 231 only */
++ IRQ_TRX_UR = 1 << 6,
++ IRQ_BAT_LOW = 1 << 7
++};
++
++/* --- VREG_CTRL ----------------------------------------------------------- */
++
++#define AVREG_EXT (1 << 7)
++#define AVDD_OK (1 << 6)
++#define DVREG_EXT (1 << 3)
++#define DVDD_OK (1 << 2)
++
++/* --- BATMON -------------------------------------------------------------- */
++
++#define BATMON_OK (1 << 5)
++#define BATMON_HR (1 << 4)
++
++#define BATMON_VTH_SHIFT 0
++#define BATMON_VTH_MASK 0x0f
++
++/* --- XOSC_CTRL ----------------------------------------------------------- */
++
++#define XTAL_MODE_SHIFT 4
++#define XTAL_MODE_MASK 0x0f
++
++enum {
++ XTAL_MODE_OFF = 0x0, /* 230 only */
++ XTAL_MODE_EXT = 0x4,
++ XTAL_MODE_INT = 0xf /* reset default */
++};
++
++#define XTAL_TRIM_SHIFT 4
++#define XTAL_TRIM_MASK 0x0f
++
++/* --- RX_SYN (231 only) --------------------------------------------------- */
++
++#define RX_PDT_DIS (1 << 7)
++
++#define RX_PDT_LEVEL_SHIFT 0
++#define RX_PDT_LEVEL_MASK 0xf
++
++/* --- XAH_CTRL_1 (231 only) ----------------------------------------------- */
++
++#define AACK_FLTR_RES_FT (1 << 5)
++#define AACK_UPLD_RES_FT (1 << 4)
++#define AACK_ACK_TIME (1 << 2)
++#define AACK_PROM_MODE (1 << 1)
++
++/* --- FTN_CTRL (231 only) ------------------------------------------------- */
++
++#define FTN_START (1 << 7)
++
++/* --- PLL_CF -------------------------------------------------------------- */
++
++#define PLL_CF_START (1 << 7)
++
++/* --- PLL_DCU ------------------------------------------------------------- */
++
++#define PLL_DCU_START (1 << 7)
++
++/* --- XAH_CTRL_0 (XAH_CTRL in 230) ---------------------------------------- */
++
++#define MAX_FRAME_RETRIES_SHIFT 4
++#define MAX_FRAME_RETRIES_MASK 0x0f
++
++#define MAX_CSMA_RETRIES_SHIFT 1
++#define MAX_CSMA_RETRIES_MASK 0x07
++
++#define SLOTTED_OPERATION (1 << 0) /* 231 only */
++
++/* --- CSMA_SEED_1 --------------------------------------------------------- */
++
++#define MIN_BE_SHIFT_230 6 /* 230 location */
++#define MIN_BE_MASK_230 3
++
++#define AACK_FVN_MODE_SHIFT 6 /* 231 only */
++#define AACK_FVN_MODE_MASK 3
++
++enum {
++ AACK_FVN_MODE_0 = 0,
++ AACK_FVN_MODE_01 = 1, /* reset default */
++ AACK_FVN_MODE_012 = 2,
++ AACK_FVN_MODE_ANY = 3
++};
++
++#define AACK_SET_PD (1 << 5)
++#define AACK_DIS_ACK (1 << 4) /* 231 only */
++#define I_AM_COORD (1 << 3)
++
++#define CSMA_SEED_1_SHIFT 0
++#define CSMA_SEED_1_MASK 7
++
++/* --- CSMA_BE ------------------------------------------------------------- */
++
++#define MAX_BE_SHIFT 4
++#define MAX_BE_MASK 0x0f
++
++#define MIN_BE_SHIFT 0 /* 231 location */
++#define MIN_BE_MASK 0x0f
++
++/* --- REG_CONT_TX_0 ------------------------------------------------------- */
++
++#define CONT_TX_MAGIC 0x0f
++
++/* --- REG_CONT_TX_1 (230 only) -------------------------------------------- */
++
++#define CONT_TX_MOD 0x00 /* modulated */
++#define CONT_TX_M2M 0x10 /* f_CH-2 MHz */
++#define CONT_TX_M500K 0x80 /* f_CH-0.5 MHz */
++#define CONT_TX_P500K 0xc0 /* f_CH+0.5 MHz */
++
++#endif /* !AT86RF230_H */
+diff --git a/atusb/include/atusb/atusb.h b/atusb/include/atusb/atusb.h
+new file mode 100644
+index 0000000..555d14b
+--- /dev/null
++++ b/atusb/include/atusb/atusb.h
+@@ -0,0 +1,97 @@
++/*
++ * atusb.h - Definitions shared between kernel and ATUSB firmware
++ *
++ * Written 2013 by Werner Almesberger <werner@almesberger.net>
++ *
++ * 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, or
++ * (at your option) any later version.
++ *
++ * This file should be identical for kernel and firmware.
++ * Kernel: drivers/net/ieee802154/atusb.h
++ * Firmware: ben-wpan/atusb/fw/include/atusb/atusb.h
++ */
++
++#ifndef _ATUSB_H
++#define _ATUSB_H
++
++#define ATUSB_VENDOR_ID 0x20b7 /* Qi Hardware*/
++#define ATUSB_PRODUCT_ID 0x1540 /* 802.15.4, device 0 */
++ /* -- - - */
++
++#define ATUSB_BUILD_SIZE 256 /* maximum build version/date message length */
++
++/* Commands to our device. Make sure this is synced with the firmware */
++enum atusb_requests {
++ ATUSB_ID = 0x00, /* system status/control grp */
++ ATUSB_BUILD,
++ ATUSB_RESET,
++ ATUSB_RF_RESET = 0x10, /* debug/test group */
++ ATUSB_POLL_INT,
++ ATUSB_TEST, /* atusb-sil only */
++ ATUSB_TIMER,
++ ATUSB_GPIO,
++ ATUSB_SLP_TR,
++ ATUSB_GPIO_CLEANUP,
++ ATUSB_REG_WRITE = 0x20, /* transceiver group */
++ ATUSB_REG_READ,
++ ATUSB_BUF_WRITE,
++ ATUSB_BUF_READ,
++ ATUSB_SRAM_WRITE,
++ ATUSB_SRAM_READ,
++ ATUSB_SPI_WRITE = 0x30, /* SPI group */
++ ATUSB_SPI_READ1,
++ ATUSB_SPI_READ2,
++ ATUSB_SPI_WRITE2_SYNC,
++ ATUSB_RX_MODE = 0x40, /* HardMAC group */
++ ATUSB_TX,
++ ATUSB_EUI64_WRITE = 0x50, /* Parameter in EEPROM grp */
++ ATUSB_EUI64_READ,
++};
++
++enum {
++ ATUSB_HW_TYPE_100813, /* 2010-08-13 */
++ ATUSB_HW_TYPE_101216, /* 2010-12-16 */
++ ATUSB_HW_TYPE_110131, /* 2011-01-31, ATmega32U2-based */
++ ATUSB_HW_TYPE_RZUSB, /* Atmel Raven USB dongle with at86rf230 */
++ ATUSB_HW_TYPE_HULUSB, /* Busware HUL USB dongle with at86rf212 */
++};
++
++/*
++ * Direction bRequest wValue wIndex wLength
++ *
++ * ->host ATUSB_ID - - 3
++ * ->host ATUSB_BUILD - - #bytes
++ * host-> ATUSB_RESET - - 0
++ *
++ * host-> ATUSB_RF_RESET - - 0
++ * ->host ATUSB_POLL_INT - - 1
++ * host-> ATUSB_TEST - - 0
++ * ->host ATUSB_TIMER - - #bytes (6)
++ * ->host ATUSB_GPIO dir+data mask+p# 3
++ * host-> ATUSB_SLP_TR - - 0
++ * host-> ATUSB_GPIO_CLEANUP - - 0
++ *
++ * host-> ATUSB_REG_WRITE value addr 0
++ * ->host ATUSB_REG_READ - addr 1
++ * host-> ATUSB_BUF_WRITE - - #bytes
++ * ->host ATUSB_BUF_READ - - #bytes
++ * host-> ATUSB_SRAM_WRITE - addr #bytes
++ * ->host ATUSB_SRAM_READ - addr #bytes
++ *
++ * host-> ATUSB_SPI_WRITE byte0 byte1 #bytes
++ * ->host ATUSB_SPI_READ1 byte0 - #bytes
++ * ->host ATUSB_SPI_READ2 byte0 byte1 #bytes
++ * ->host ATUSB_SPI_WRITE2_SYNC byte0 byte1 0/1
++ *
++ * host-> ATUSB_RX_MODE on - 0
++ * host-> ATUSB_TX flags ack_seq #bytes
++ * host-> ATUSB_EUI64_WRITE - - #bytes (8)
++ * ->host ATUSB_EUI64_READ - - #bytes (8)
++ */
++
++#define ATUSB_REQ_FROM_DEV (USB_TYPE_VENDOR | USB_DIR_IN)
++#define ATUSB_REQ_TO_DEV (USB_TYPE_VENDOR | USB_DIR_OUT)
++
++#endif /* !_ATUSB_H */
+diff --git a/atusb/include/atusb/ep0.h b/atusb/include/atusb/ep0.h
+new file mode 100644
+index 0000000..7777345
+--- /dev/null
++++ b/atusb/include/atusb/ep0.h
+@@ -0,0 +1,64 @@
++/*
++ * include/atusb/ep0.h - EP0 extension protocol
++ *
++ * Written 2008-2011, 2013 by Werner Almesberger
++ * Copyright 2008-2011, 2013 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#ifndef EP0_H
++#define EP0_H
++
++#include <atusb/atusb.h>
++
++
++/*
++ * EP0 protocol:
++ *
++ * 0.0 initial release
++ * 0.1 addition of ATUSB_TEST
++ * 0.2 First public release
++ * 0.3 ATUSB_EUI64_READ/WRITE for permanent EUI64 handling
++ * Support to run the firmware on Atmel Raven USB dongles
++ * Remove FCS frame check from firmware and leave it to the driver
++ * Use extended operation mode for TX for automatic ACK handling
++ */
++
++#define EP0ATUSB_MAJOR 0 /* EP0 protocol, major revision */
++#define EP0ATUSB_MINOR 3 /* EP0 protocol, minor revision */
++
++
++/*
++ * bmRequestType:
++ *
++ * D7 D6..5 D4...0
++ * | | |
++ * direction (0 = host->dev)
++ * type (2 = vendor)
++ * recipient (0 = device)
++ */
++
++#ifndef USB_TYPE_VENDOR
++#define USB_TYPE_VENDOR 0x40
++#endif
++
++#ifndef USB_DIR_IN
++#define USB_DIR_IN 0x80
++#endif
++
++#ifndef USB_DIR_OUT
++#define USB_DIR_OUT 0x00
++#endif
++
++#define ATUSB_FROM_DEV(req) (ATUSB_REQ_FROM_DEV | (req) << 8)
++#define ATUSB_TO_DEV(req) (ATUSB_REQ_TO_DEV | (req) << 8)
++
++
++void ep0_init(void);
++
++#endif /* !EP0_H */
+diff --git a/atusb/mac.c b/atusb/mac.c
+new file mode 100644
+index 0000000..835002c
+--- /dev/null
++++ b/atusb/mac.c
+@@ -0,0 +1,250 @@
++/*
++ * fw/mac.c - HardMAC functions
++ *
++ * Written 2011, 2013 by Werner Almesberger
++ * Copyright 2011, 2013 Werner Almesberger
++ *
++ * 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.
++ */
++
++#include <stddef.h>
++#include <stdbool.h>
++#include <stdint.h>
++
++#include "usb.h"
++
++#include "at86rf230.h"
++#include "spi.h"
++#include "board.h"
++#include "mac.h"
++
++#define RX_BUFS 3
++
++
++bool (*mac_irq)(void) = NULL;
++
++
++static uint8_t rx_buf[RX_BUFS][MAX_PSDU+2]; /* PHDR+payload+LQ */
++static uint8_t tx_buf[MAX_PSDU];
++static uint8_t tx_size = 0;
++static bool txing = 0;
++static bool queued_tx_ack = 0;
++static uint8_t next_seq, this_seq, queued_seq;
++
++
++/* ----- Receive buffer management ----------------------------------------- */
++
++
++static uint8_t rx_in = 0, rx_out = 0;
++
++
++static inline void next_buf(uint8_t *index)
++{
++ *index = (*index+1) % RX_BUFS;
++}
++
++
++/* ----- Interrupt handling ------------------------------------------------ */
++
++
++static void rx_done(void *user);
++static void tx_ack_done(void *user);
++
++
++static void usb_next(void)
++{
++ const uint8_t *buf;
++
++ if (rx_in != rx_out) {
++ buf = rx_buf[rx_out];
++ led(1);
++ usb_send(&eps[1], buf, buf[0]+2, rx_done, NULL);
++ }
++
++ if (queued_tx_ack) {
++ usb_send(&eps[1], &queued_seq, 1, tx_ack_done, NULL);
++ queued_tx_ack = 0;
++ }
++}
++
++
++static void tx_ack_done(void *user)
++{
++ usb_next();
++}
++
++static void rx_done(void *user)
++{
++ led(0);
++ next_buf(&rx_out);
++ usb_next();
++#ifdef AT86RF230
++ /* slap at86rf230 - reduce fragmentation issue */
++ change_state(TRX_STATUS_RX_AACK_ON);
++#endif
++}
++
++
++static void receive_frame(void)
++{
++ uint8_t size;
++ uint8_t *buf;
++
++ spi_begin();
++ spi_io(AT86RF230_BUF_READ);
++
++ size = spi_recv();
++ if (!size || (size & 0x80)) {
++ spi_end();
++ return;
++ }
++
++ buf = rx_buf[rx_in];
++ spi_recv_block(buf+1, size+1);
++ spi_end();
++
++ buf[0] = size;
++ next_buf(&rx_in);
++
++ if (eps[1].state == EP_IDLE)
++ usb_next();
++}
++
++
++static bool handle_irq(void)
++{
++ uint8_t irq;
++
++ irq = reg_read(REG_IRQ_STATUS);
++ if (!(irq & IRQ_TRX_END))
++ return 1;
++
++ if (txing) {
++ if (eps[1].state == EP_IDLE) {
++ usb_send(&eps[1], &this_seq, 1, tx_ack_done, NULL);
++ } else {
++ queued_tx_ack = 1;
++ queued_seq = this_seq;
++ }
++ txing = 0;
++ return 1;
++ }
++
++ /* likely */
++ if (eps[1].state == EP_IDLE || rx_in != rx_out)
++ receive_frame();
++
++ return 1;
++}
++
++
++/* ----- TX/RX ------------------------------------------------------------- */
++
++
++bool mac_rx(int on)
++{
++ if (on) {
++ mac_irq = handle_irq;
++ reg_read(REG_IRQ_STATUS);
++ change_state(TRX_CMD_RX_AACK_ON);
++ } else {
++ mac_irq = NULL;
++ change_state(TRX_CMD_FORCE_TRX_OFF);
++ txing = 0;
++ }
++ return 1;
++}
++
++
++static void do_tx(void *user)
++{
++ uint16_t timeout = 0xffff;
++ uint8_t status;
++ uint8_t i;
++
++ /*
++ * If we time out here, the host driver will time out waiting for the
++ * TRX_END acknowledgement.
++ */
++ do {
++ if (!--timeout)
++ return;
++ status = reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK;
++ }
++ while (status != TRX_STATUS_RX_ON && status != TRX_STATUS_RX_AACK_ON);
++
++#ifdef AT86RF231
++ /*
++ * We use TRX_CMD_FORCE_PLL_ON instead of TRX_CMD_PLL_ON because a new
++ * reception may have begun while we were still working on the previous
++ * one.
++ */
++ reg_write(REG_TRX_STATE, TRX_CMD_FORCE_PLL_ON);
++#endif
++#ifdef AT86RF230
++ /*
++ * at86rf230 doesn't support force change, nevetherless this works
++ * somehow
++ */
++ reg_write(REG_TRX_STATE, TRX_CMD_PLL_ON);
++#endif
++#ifdef AT86RF212
++ /*
++ * We use TRX_CMD_FORCE_PLL_ON instead of TRX_CMD_PLL_ON because a new
++ * reception may have begun while we were still working on the previous
++ * one.
++ */
++ reg_write(REG_TRX_STATE, TRX_CMD_FORCE_PLL_ON);
++#endif
++
++ handle_irq();
++
++ spi_begin();
++ spi_send(AT86RF230_BUF_WRITE);
++ spi_send(tx_size+2); /* CRC */
++ for (i = 0; i != tx_size; i++)
++ spi_send(tx_buf[i]);
++ spi_end();
++
++ change_state(TRX_STATUS_TX_ARET_ON);
++
++ slp_tr();
++
++ txing = 1;
++ this_seq = next_seq;
++
++ /*
++ * Wait until we reach BUSY_TX_ARET, so that we command the transition to
++ * RX_AACK_ON which will be executed upon TX completion.
++ */
++ change_state(TRX_CMD_PLL_ON);
++ change_state(TRX_CMD_RX_AACK_ON);
++}
++
++
++bool mac_tx(uint16_t flags, uint8_t seq, uint16_t len)
++{
++ if (len > MAX_PSDU)
++ return 0;
++ tx_size = len;
++ next_seq = seq;
++ usb_recv(&eps[0], tx_buf, len, do_tx, NULL);
++ return 1;
++}
++
++
++void mac_reset(void)
++{
++ mac_irq = NULL;
++ txing = 0;
++ queued_tx_ack = 0;
++ rx_in = rx_out = 0;
++ next_seq = this_seq = queued_seq = 0;
++
++ /* enable CRC and PHY_RSSI (with RX_CRC_VALID) in SPI status return */
++ reg_write(REG_TRX_CTRL_1,
++ TX_AUTO_CRC_ON | SPI_CMD_MODE_PHY_RSSI << SPI_CMD_MODE_SHIFT);
++}
+diff --git a/atusb/mac.h b/atusb/mac.h
+new file mode 100644
+index 0000000..f3c92fb
+--- /dev/null
++++ b/atusb/mac.h
+@@ -0,0 +1,26 @@
++/*
++ * fw/mac.h - HardMAC functions
++ *
++ * Written 2011, 2013 by Werner Almesberger
++ * Copyright 2011, 2013 Werner Almesberger
++ *
++ * 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.
++ */
++
++#ifndef MAC_H
++#define MAC_H
++
++#include <stdbool.h>
++#include <stdint.h>
++
++
++extern bool (*mac_irq)(void);
++
++bool mac_rx(int on);
++bool mac_tx(uint16_t flags, uint8_t seq, uint16_t len);
++void mac_reset(void);
++
++#endif /* !MAC_H */
+diff --git a/atusb/sernum.c b/atusb/sernum.c
+new file mode 100644
+index 0000000..41e434c
+--- /dev/null
++++ b/atusb/sernum.c
+@@ -0,0 +1,47 @@
++/*
++ * fw/sernum.c - ATUSB serial number
++ *
++ * Written 2008-2011, 2013 by Werner Almesberger
++ * Copyright 2008-2011, 2013 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include "usb.h"
++
++#include "board.h"
++#include "sernum.h"
++
++
++static const uint8_t string_descriptor_0[] = {
++ 4, /* blength */
++ USB_DT_STRING, /* bDescriptorType */
++ LE(USB_LANGID_ENGLISH_US) /* wLANGID[0] */
++};
++
++
++bool sernum_get_descr(uint8_t type, uint8_t index, const uint8_t **reply,
++ uint8_t *size)
++{
++ if (type != USB_DT_STRING)
++ return 0;
++ switch (index) {
++ case 0:
++ *reply = string_descriptor_0;
++ *size = sizeof(string_descriptor_0);
++ return 1;
++ case 1:
++ *reply = board_sernum;
++ *size = sizeof(board_sernum);
++ return 1;
++ default:
++ return 0;
++ }
++}
+diff --git a/atusb/sernum.h b/atusb/sernum.h
+new file mode 100644
+index 0000000..31a8e27
+--- /dev/null
++++ b/atusb/sernum.h
+@@ -0,0 +1,37 @@
++/*
++ * fw/sernum.h - ATUSB serial number
++ *
++ * Written 2011, 2013 by Werner Almesberger
++ * Copyright 2011, 2013 Werner Almesberger
++ *
++ * 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.
++ */
++
++#ifndef SERNUM_H
++#define SERNUM_H
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include "board.h"
++
++
++#ifdef HAS_BOARD_SERNUM
++
++bool sernum_get_descr(uint8_t type, uint8_t index, const uint8_t **reply,
++ uint8_t *size);
++
++#else /* HAS_BOARD_SERNUM */
++
++static inline bool sernum_get_descr(uint8_t type, uint8_t index,
++ const uint8_t **reply, uint8_t *size)
++{
++ return 0;
++}
++
++#endif /* !HAS_BOARD_SERNUM */
++
++#endif /* !SERNUM_H */
+diff --git a/atusb/spi.c b/atusb/spi.c
+new file mode 100644
+index 0000000..3fa5715
+--- /dev/null
++++ b/atusb/spi.c
+@@ -0,0 +1,51 @@
++/*
++ * fw/spi.c - ATmega8 family SPI I/O
++ *
++ * Written 2011, 2013 by Werner Almesberger
++ * Copyright 2011, 2013 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include <avr/io.h>
++
++#include "board.h"
++#include "spi.h"
++
++
++uint8_t spi_io(uint8_t v)
++{
++// while (!(UCSR1A & 1 << UDRE1));
++ SPI_DATA = v;
++ SPI_WAIT_DONE();
++ return SPI_DATA;
++}
++
++
++void spi_end(void)
++{
++// while (!(UCSR1A & 1 << TXC1));
++ SET(nSS);
++}
++
++
++void spi_recv_block(uint8_t *buf, uint8_t n)
++{
++ if (!n)
++ return;
++ SPI_DATA = 0;
++ while (--n) {
++ SPI_WAIT_DONE();
++ *buf++ = SPI_DATA;
++ SPI_DATA = 0;
++ }
++ SPI_WAIT_DONE();
++ *buf++ = SPI_DATA;
++}
+diff --git a/atusb/spi.h b/atusb/spi.h
+new file mode 100644
+index 0000000..6e04f4e
+--- /dev/null
++++ b/atusb/spi.h
+@@ -0,0 +1,30 @@
++/*
++ * fw/spi.h - ATmega8 family SPI I/O
++ *
++ * Written 2011, 2013 by Werner Almesberger
++ * Copyright 2011, 2013 Werner Almesberger
++ *
++ * 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.
++ */
++
++#ifndef SPI_H
++#define SPI_H
++
++#include <stdint.h>
++
++
++void spi_begin(void);
++uint8_t spi_io(uint8_t v);
++void spi_end(void);
++void spi_off(void);
++void spi_init(void);
++
++#define spi_send(v) (void) spi_io(v)
++#define spi_recv(v) spi_io(0)
++
++void spi_recv_block(uint8_t *buf, uint8_t n);
++
++#endif /* !SPI_H */
+diff --git a/atusb/uart.c b/atusb/uart.c
+new file mode 100644
+index 0000000..44bec27
+--- /dev/null
++++ b/atusb/uart.c
+@@ -0,0 +1,64 @@
++/*
++ * fw/uart.h - Functions needed for debugging over uart
++ *
++ * Code adapted from http://www.roboternetz.de/wissen/index.php/UART_mit_avr-gcc
++ * and http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
++ *
++ * Published under the Creative Commons Share-Alike licence
++ * https://creativecommons.org/licenses/by-sa/2.0/de/
++ *
++ * S. Salewski 2007
++ *
++ * Adapted by
++ * Josef Filzmaier 2017
++ */
++
++#include <avr/io.h>
++#include "uart.h"
++
++#define USART_BAUD 38400UL
++#define F_CPU 8000000UL
++
++#define Wait_USART_Ready() while (!(UCSR1A & (1<<UDRE1)))
++#define UART_UBRR (F_CPU/(16L*USART_BAUD)-1)
++
++// initialize USART, 8N1 mode
++void
++uart_init(void)
++{
++/* TODO: Find a working configuration for uart for the atmega32u2 */
++#if CHIP == at90usb1287
++ CLKPR = (1 << CLKPCE);
++ CLKPR = 0; // clock prescaler == 0, so we have 16 MHz mpu frequency
++ UBRR1 = UART_UBRR;
++ UCSR1C = (1 << UCSZ10) | (1 << UCSZ11);
++ UCSR1B = (1 << TXEN1);
++ do
++ {
++ UDR1;
++ }
++ while (UCSR1A & (1 << RXC1));
++#endif
++
++}
++
++int uart_write_char(char c, FILE* stream)
++{
++ if (c == '\n'){
++ uart_new_line();
++ }
++ else {
++ Wait_USART_Ready();
++ UDR1 = c;
++ }
++ return 0;
++}
++
++void
++uart_new_line(void)
++{
++ Wait_USART_Ready();
++ UDR1 = '\r';
++ Wait_USART_Ready();
++ UDR1 = '\n';
++}
+diff --git a/atusb/uart.h b/atusb/uart.h
+new file mode 100644
+index 0000000..4810f9c
+--- /dev/null
++++ b/atusb/uart.h
+@@ -0,0 +1,25 @@
++/*
++ * fw/uart.h - Functions needed for debugging over uart
++ *
++ * Code adapted from http://www.roboternetz.de/wissen/index.php/UART_mit_avr-gcc
++ * and http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
++ *
++ * Published under the Creative Commons Share-Alike licence
++ * https://creativecommons.org/licenses/by-sa/2.0/de/
++ *
++ * S. Salewski 2007
++ *
++ * Adapted by
++ * Josef Filzmaier 2017
++ */
++
++#ifndef UART_H_
++#define UART_H_
++
++#include <stdio.h>
++
++void uart_init(void);
++int uart_write_char(char c, FILE* stream);
++void uart_new_line(void);
++
++#endif /* UART_H_ */
+diff --git a/atusb/usb/atu2.c b/atusb/usb/atu2.c
+new file mode 100644
+index 0000000..98158bf
+--- /dev/null
++++ b/atusb/usb/atu2.c
+@@ -0,0 +1,247 @@
++/*
++ * fw/usb/atu2.c - Chip-specific driver for Atmel ATxxxU2 USB chips
++ *
++ * Written 2008-2011, 2013-2014 by Werner Almesberger
++ * Copyright 2008-2011, 2013-2014 Werner Almesberger
++ *
++ * 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.
++ */
++
++/*
++ * Known issues:
++ * - no suspend/resume
++ * - we don't call back after failed transmissions,
++ * - we don't reset the EP buffer after failed receptions
++ * - enumeration often encounters an error -71 (from which it recovers)
++ */
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#define F_CPU 8000000UL
++#include <util/delay.h>
++
++#include <avr/io.h>
++#include <avr/interrupt.h>
++#include "usb.h"
++#include "board.h"
++
++
++#ifndef NULL
++#define NULL 0
++#endif
++
++#if 1
++#define BUG_ON(cond) do { if (cond) panic(); } while (0)
++#else
++#define BUG_ON(cond)
++#endif
++
++
++struct ep_descr eps[NUM_EPS];
++
++
++static uint16_t usb_read_word(void)
++{
++ uint8_t low;
++
++ low = UEDATX;
++ return low | UEDATX << 8;
++}
++
++
++static void enable_addr(void *user)
++{
++ while (!(UEINTX & (1 << TXINI)));
++ UDADDR |= 1 << ADDEN;
++}
++
++
++void set_addr(uint8_t addr)
++{
++ UDADDR = addr;
++ usb_send(&eps[0], NULL, 0, enable_addr, NULL);
++}
++
++
++void usb_ep_change(struct ep_descr *ep)
++{
++ if (ep->state == EP_TX) {
++ UENUM = ep-eps;
++ UEIENX |= 1 << TXINE;
++ }
++}
++
++
++static bool ep_setup(void)
++{
++ struct setup_request setup;
++
++ BUG_ON(UEBCLX < 8);
++
++ setup.bmRequestType = UEDATX;
++ setup.bRequest = UEDATX;
++ setup.wValue = usb_read_word();
++ setup.wIndex = usb_read_word();
++ setup.wLength = usb_read_word();
++
++ if (!handle_setup(&setup))
++ return 0;
++ if (!(setup.bmRequestType & 0x80) && eps[0].state == EP_IDLE)
++ usb_send(&eps[0], NULL, 0, NULL, NULL);
++ return 1;
++}
++
++
++static bool ep_rx(struct ep_descr *ep)
++{
++ uint8_t size;
++
++ size = UEBCLX;
++ if (size > ep->end-ep->buf)
++ return 0;
++ while (size--)
++ *ep->buf++ = UEDATX;
++ if (ep->buf == ep->end) {
++ ep->state = EP_IDLE;
++ if (ep->callback)
++ ep->callback(ep->user);
++// if (ep == &eps[0])
++ usb_send(ep, NULL, 0, NULL, NULL);
++ }
++ return 1;
++}
++
++
++static void ep_tx(struct ep_descr *ep)
++{
++ uint8_t size = ep->end-ep->buf;
++ uint8_t left;
++
++ if (size > ep->size)
++ size = ep->size;
++ for (left = size; left; left--)
++ UEDATX = *ep->buf++;
++ if (size == ep->size)
++ return;
++ ep->state = EP_IDLE;
++}
++
++
++static void handle_ep(int n)
++{
++ struct ep_descr *ep = eps+n;
++ uint8_t mask;
++
++ UENUM = n;
++ if (UEINTX & (1 << RXSTPI)) {
++ /* @@@ EP_RX. EP_TX: cancel */
++ ep->state = EP_IDLE;
++ if (!ep_setup())
++ goto stall;
++ UEINTX = ~(1 << RXSTPI);
++ }
++ if (UEINTX & (1 << RXOUTI)) {
++ /* @@ EP_TX: cancel */
++ if (ep->state != EP_RX)
++ goto stall;
++ if (!ep_rx(ep))
++ goto stall;
++ /* @@@ gcc 4.5.2 wants this cast */
++ UEINTX = (uint8_t) ~(1 << RXOUTI | 1 << FIFOCON);
++ }
++ if (UEINTX & (1 << STALLEDI)) {
++ ep->state = EP_IDLE;
++ UEINTX = ~(1 << STALLEDI);
++ }
++ if (UEINTX & (1 << TXINI)) {
++ /* @@ EP_RX: cancel (?) */
++ if (ep->state == EP_TX) {
++ ep_tx(ep);
++ mask = 1 << TXINI;
++ if (n)
++ mask |= 1 << FIFOCON;
++ UEINTX = ~mask;
++ if (ep->state == EP_IDLE && ep->callback)
++ ep->callback(ep->user);
++ } else {
++ UEIENX &= ~(1 << TXINE);
++ }
++ }
++ return;
++
++stall:
++ UEINTX = ~(1 << RXSTPI | 1 << RXOUTI | 1 << STALLEDI);
++ ep->state = EP_IDLE;
++ UECONX |= 1 << STALLRQ;
++}
++
++
++void ep_init(void)
++{
++ UENUM = 0;
++ UECONX = (1 << RSTDT) | (1 << EPEN); /* enable */
++ UECFG0X = 0; /* control, direction is ignored */
++ UECFG1X = 3 << EPSIZE0; /* 64 bytes */
++ UECFG1X |= 1 << ALLOC;
++
++ while (!(UESTA0X & (1 << CFGOK)));
++
++ UEIENX =
++ (1 << RXSTPE) | (1 << RXOUTE) | (1 << STALLEDE) | (1 << TXINE);
++
++ eps[0].state = EP_IDLE;
++ eps[0].size = 64;
++
++#ifndef BOOT_LOADER
++
++ UENUM = 1;
++ UECONX = (1 << RSTDT) | (1 << EPEN); /* enable */
++ UECFG0X = (1 << EPTYPE1) | (1 << EPDIR); /* bulk IN */
++ UECFG1X = 3 << EPSIZE0; /* 64 bytes */
++ UECFG1X |= 1 << ALLOC;
++
++ while (!(UESTA0X & (1 << CFGOK)));
++
++ UEIENX = (1 << STALLEDE) | (1 << TXINE);
++
++ eps[1].state = EP_IDLE;
++ eps[1].size = 64;
++
++#endif
++}
++
++
++ISR(USB_GEN_vect)
++{
++ uint8_t flags;
++
++ flags = UDINT;
++ if (flags & (1 << EORSTI)) {
++ if (user_reset)
++ user_reset();
++ ep_init();
++ UDINT = ~(1 << EORSTI);
++ }
++}
++
++
++ISR(USB_COM_vect)
++{
++ uint8_t flags, i;
++
++ flags = UEINT;
++ for (i = 0; i != NUM_EPS; i++)
++ if (flags & (1 << i))
++ handle_ep(i);
++}
++
++
++void usb_reset(void)
++{
++ UDCON |= 1 << DETACH; /* detach the pull-up */
++ _delay_ms(1);
++}
+diff --git a/atusb/usb/dfu.c b/atusb/usb/dfu.c
+new file mode 100644
+index 0000000..c84a28d
+--- /dev/null
++++ b/atusb/usb/dfu.c
+@@ -0,0 +1,260 @@
++/*
++ * boot/dfu.c - DFU protocol engine
++ *
++ * Written 2008-2011, 2013-2015 by Werner Almesberger
++ * Copyright 2008-2011, 2013-2015 Werner Almesberger
++ *
++ * 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.
++ */
++
++/*
++ * http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf
++ */
++
++/*
++ * A few, erm, shortcuts:
++ *
++ * - we don't bother with the app* states since DFU is all this firmware does
++ * - after DFU_DNLOAD, we just block until things are written, so we never
++ * enter dfuDNLOAD_SYNC or dfuDNBUSY
++ * - no dfuMANIFEST_SYNC, dfuMANIFEST, or dfuMANIFEST_WAIT_RESET
++ * - to keep our buffers small, we only accept EP0-sized blocks
++ */
++
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include "usb.h"
++#include "dfu.h"
++
++#include "board.h"
++
++
++#ifndef NULL
++#define NULL 0
++#endif
++
++#define debug(...)
++#define error(...)
++
++
++#ifndef DFU_ALT_SETTINGS
++#define DFU_ALT_SETTINGS 1
++#endif
++
++#ifndef DFU_ALT_NAME_0_IDX
++#define DFU_ALT_NAME_0_IDX 0
++#endif
++
++#ifndef DFU_ALT_NAME_1_IDX
++#define DFU_ALT_NAME_1_IDX 0
++#endif
++
++#ifndef DFU_ALT_NAME_2_IDX
++#define DFU_ALT_NAME_2_IDX 0
++#endif
++
++
++const uint8_t device_descriptor[] = {
++ 18, /* bLength */
++ USB_DT_DEVICE, /* bDescriptorType */
++ LE(0x100), /* bcdUSB */
++ USB_CLASS_APP_SPEC, /* bDeviceClass */
++ 0x00, /* bDeviceSubClass (per interface) */
++ 0x00, /* bDeviceProtocol (per interface) */
++ EP0_SIZE, /* bMaxPacketSize */
++ LE(DFU_USB_VENDOR), /* idVendor */
++ LE(DFU_USB_PRODUCT), /* idProduct */
++ LE(0x0001), /* bcdDevice */
++ 0, /* iManufacturer */
++ 0, /* iProduct */
++#ifdef HAS_BOARD_SERNUM
++ 1, /* iSerialNumber */
++#else
++ 0, /* iSerialNumber */
++#endif
++ 1 /* bNumConfigurations */
++};
++
++
++const uint8_t config_descriptor[] = {
++ 9, /* bLength */
++ USB_DT_CONFIG, /* bDescriptorType */
++ LE(9+9*DFU_ALT_SETTINGS), /* wTotalLength */
++ 1, /* bNumInterfaces */
++ 1, /* bConfigurationValue (> 0 !) */
++ 0, /* iConfiguration */
++// USB_ATTR_SELF_POWERED | USB_ATTR_BUS_POWERED,
++ USB_ATTR_BUS_POWERED, /* bmAttributes */
++ ((BOARD_MAX_mA)+1)/2, /* bMaxPower */
++
++ /* Interface #0 */
++
++ DFU_ITF_DESCR(0, 0, dfu_proto_dfu, DFU_ALT_NAME_0_IDX)
++#if DFU_ALT_SETTINGS > 1
++ DFU_ITF_DESCR(0, 1, dfu_proto_dfu, DFU_ALT_NAME_1_IDX)
++#endif
++#if DFU_ALT_SETTINGS > 2
++ DFU_ITF_DESCR(0, 2, dfu_proto_dfu, DFU_ALT_NAME_2_IDX)
++#endif
++};
++
++
++static uint16_t next_block = 0;
++static bool did_download;
++
++
++static uint8_t buf[EP0_SIZE];
++
++
++static void block_write(void *user)
++{
++ uint16_t *size = user;
++
++ dfu_flash_ops->write(buf, *size);
++}
++
++
++static bool block_receive(uint16_t length)
++{
++ static uint16_t size;
++
++ if (!dfu_flash_ops->can_write(length)) {
++ dfu.state = dfuERROR;
++ dfu.status = errADDRESS;
++ return 0;
++ }
++ if (length > EP0_SIZE) {
++ dfu.state = dfuERROR;
++ dfu.status = errUNKNOWN;
++ return 0;
++ }
++ size = length;
++ usb_recv(&eps[0], buf, size, block_write, &size);
++ return 1;
++}
++
++
++static bool block_transmit(uint16_t length)
++{
++ uint16_t got;
++
++ if (length > EP0_SIZE) {
++ dfu.state = dfuERROR;
++ dfu.status = errUNKNOWN;
++ return 1;
++ }
++ got = dfu_flash_ops->read(buf, length);
++ if (got < length) {
++ length = got;
++ dfu.state = dfuIDLE;
++ }
++ usb_send(&eps[0], buf, length, NULL, NULL);
++ return 1;
++}
++
++
++static bool my_setup(const struct setup_request *setup)
++{
++ bool ok;
++
++ switch (setup->bmRequestType | setup->bRequest << 8) {
++ case DFU_TO_DEV(DFU_DETACH):
++ debug("DFU_DETACH\n");
++ /*
++ * The DFU spec says thay this is sent in protocol 1 only.
++ * However, dfu-util also sends it to get out of DFU mode,
++ * so we just don't make a fuss and ignore it.
++ */
++ return 1;
++ case DFU_TO_DEV(DFU_DNLOAD):
++ debug("DFU_DNLOAD\n");
++ if (dfu.state == dfuIDLE) {
++ next_block = setup->wValue;
++ dfu_flash_ops->start();
++ }
++ else if (dfu.state != dfuDNLOAD_IDLE) {
++ error("bad state\n");
++ return 0;
++ }
++ if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
++ debug("retransmisson\n");
++ return 1;
++ }
++ if (setup->wValue != next_block) {
++ debug("bad block (%d vs. %d)\n",
++ setup->wValue, next_block);
++ dfu.state = dfuERROR;
++ dfu.status = errUNKNOWN;
++ return 1;
++ }
++ if (!setup->wLength) {
++ debug("DONE\n");
++ dfu_flash_ops->end_write();
++ dfu.state = dfuIDLE;
++ did_download = 1;
++ return 1;
++ }
++ ok = block_receive(setup->wLength);
++ next_block++;
++ dfu.state = dfuDNLOAD_IDLE;
++ return ok;
++ case DFU_FROM_DEV(DFU_UPLOAD):
++ debug("DFU_UPLOAD\n");
++ if (dfu.state == dfuIDLE) {
++ next_block = setup->wValue;
++ dfu_flash_ops->start();
++ }
++ else if (dfu.state != dfuUPLOAD_IDLE)
++ return 0;
++ if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
++ debug("retransmisson\n");
++ /* @@@ try harder */
++ dfu.state = dfuERROR;
++ dfu.status = errUNKNOWN;
++ return 1;
++ }
++ if (setup->wValue != next_block) {
++ debug("bad block (%d vs. %d)\n",
++ setup->wValue, next_block);
++ dfu.state = dfuERROR;
++ dfu.status = errUNKNOWN;
++ return 1;
++ }
++ ok = block_transmit(setup->wLength);
++ next_block++;
++ dfu.state = dfuUPLOAD_IDLE;
++ return ok;
++ case DFU_TO_DEV(DFU_ABORT):
++ debug("DFU_ABORT\n");
++ dfu.state = dfuIDLE;
++ dfu.status = OK;
++ return 1;
++ default:
++ return dfu_setup_common(setup);
++ }
++}
++
++
++static void my_reset(void)
++{
++#if 0
++ /* @@@ not nice -- think about where this should go */
++ extern void run_payload(void);
++
++ if (did_download)
++ run_payload();
++#endif
++}
++
++
++void dfu_init(void)
++{
++ user_setup = my_setup;
++ user_get_descriptor = dfu_my_descr;
++ user_reset = my_reset;
++}
+diff --git a/atusb/usb/dfu.h b/atusb/usb/dfu.h
+new file mode 100644
+index 0000000..bc35bbc
+--- /dev/null
++++ b/atusb/usb/dfu.h
+@@ -0,0 +1,119 @@
++/*
++ * boot/dfu.h - DFU protocol constants and data structures
++ *
++ * Written 2008, 2011, 2013-2015 by Werner Almesberger
++ * Copyright 2008, 2011, 2013-2015 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#ifndef DFU_H
++#define DFU_H
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include "usb.h"
++
++
++enum dfu_request {
++ DFU_DETACH,
++ DFU_DNLOAD,
++ DFU_UPLOAD,
++ DFU_GETSTATUS,
++ DFU_CLRSTATUS,
++ DFU_GETSTATE,
++ DFU_ABORT,
++};
++
++
++enum dfu_status {
++ OK,
++ errTARGET,
++ errFILE,
++ errWRITE,
++ errERASE,
++ errCHECK_ERASED,
++ errPROG,
++ errVERIFY,
++ errADDRESS,
++ errNOTDONE,
++ errFIRMWARE,
++ errVENDOR,
++ errUSBR,
++ errPOR,
++ errUNKNOWN,
++ errSTALLEDPKT,
++};
++
++
++enum dfu_state {
++ appIDLE,
++ appDETACH,
++ dfuIDLE,
++ dfuDNLOAD_SYNC,
++ dfuDNBUSY,
++ dfuDNLOAD_IDLE,
++ dfuMANIFEST_SYNC,
++ dfuMANIFEST,
++ dfuMANIFEST_WAIT_RESET,
++ dfuUPLOAD_IDLE,
++ dfuERROR
++};
++
++enum dfu_itf_proto {
++ dfu_proto_runtime = 1, /* Runtime protocol */
++ dfu_proto_dfu = 2, /* DFU mode protocol */
++};
++
++
++#define DFU_DT_FUNCTIONAL 0x21 /* DFU FUNCTIONAL descriptor type */
++
++
++#define DFU_TO_DEV(req) (0x21 | (req) << 8)
++#define DFU_FROM_DEV(req) (0xa1 | (req) << 8)
++
++
++struct dfu {
++ uint8_t status; /* bStatus */
++ uint8_t toL, toM, toH; /* bwPollTimeout */
++ uint8_t state; /* bState */
++ uint8_t iString;
++};
++
++
++#define DFU_ITF_DESCR(itf, alt, proto, idx) \
++ 9, /* bLength */ \
++ USB_DT_INTERFACE, /* bDescriptorType */ \
++ (itf), /* bInterfaceNumber */ \
++ (alt), /* bAlternateSetting */ \
++ 0, /* bNumEndpoints */ \
++ 0xfe, /* bInterfaceClass (application specific) */ \
++ 0x01, /* bInterfaceSubClass (device fw upgrade) */ \
++ (proto), /* bInterfaceProtocol (dfu_proto_*) */ \
++ (idx), /* iInterface */
++
++
++struct dfu_flash_ops {
++ void (*start)(void);
++ bool (*can_write)(uint16_t size);
++ void (*write)(const uint8_t *buf, uint16_t size);
++ void (*end_write)(void);
++ uint16_t (*read)(uint8_t *buf, uint16_t size);
++};
++
++extern struct dfu dfu;
++extern const struct dfu_flash_ops *dfu_flash_ops;
++
++
++bool dfu_setup_common(const struct setup_request *setup);
++bool dfu_my_descr(uint8_t type, uint8_t index, const uint8_t **reply,
++ uint8_t *size);
++
++void dfu_init(void);
++
++#endif /* !DFU_H */
+diff --git a/atusb/usb/dfu_common.c b/atusb/usb/dfu_common.c
+new file mode 100644
+index 0000000..9b6feef
+--- /dev/null
++++ b/atusb/usb/dfu_common.c
+@@ -0,0 +1,101 @@
++/*
++ * boot/dfu_common.c - DFU protocol engine parts common to App/DFU
++ *
++ * Written 2008-2011, 2013-2014 by Werner Almesberger
++ * Copyright 2008-2011, 2013-2014 Werner Almesberger
++ *
++ * 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.
++ */
++
++/*
++ * http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf
++ */
++
++/*
++ * A few, erm, shortcuts:
++ *
++ * - we don't bother with the app* states since DFU is all this firmware does
++ * - after DFU_DNLOAD, we just block until things are written, so we never
++ * enter dfuDNLOAD_SYNC or dfuDNBUSY
++ * - no dfuMANIFEST_SYNC, dfuMANIFEST, or dfuMANIFEST_WAIT_RESET
++ * - to keep our buffers small, we only accept EP0-sized blocks
++ */
++
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include "usb.h"
++#include "dfu.h"
++
++#include "board.h"
++#include "../sernum.h"
++
++
++#ifndef NULL
++#define NULL 0
++#endif
++
++#define debug(...)
++#define error(...)
++
++
++static const uint8_t functional_descriptor[] = {
++ 9, /* bLength */
++ DFU_DT_FUNCTIONAL, /* bDescriptorType */
++ 0xf, /* bmAttributes (claim omnipotence :-) */
++ LE(0xffff), /* wDetachTimeOut (we're very patient) */
++ LE(EP0_SIZE), /* wTransferSize */
++ LE(0x101), /* bcdDFUVersion */
++};
++
++
++/*
++ * The worst-case activity would be flashing a one page and erasing another
++ * one, would should take less than 10 ms. A 100 ms timeout ought to be plenty.
++ */
++
++struct dfu dfu = {
++ OK, /* bStatus */
++ LE(100), 0, /* bwPollTimeout, 100 ms */
++ dfuIDLE, /* bState */
++ 0, /* iString */
++};
++
++
++bool dfu_setup_common(const struct setup_request *setup)
++{
++ switch (setup->bmRequestType | setup->bRequest << 8) {
++ case DFU_FROM_DEV(DFU_GETSTATUS):
++ debug("DFU_GETSTATUS\n");
++ usb_send(&eps[0], (uint8_t *) &dfu, sizeof(dfu), NULL, NULL);
++ return 1;
++ case DFU_TO_DEV(DFU_CLRSTATUS):
++ debug("DFU_CLRSTATUS\n");
++ dfu.state = dfuIDLE;
++ dfu.status = OK;
++ return 1;
++ case DFU_FROM_DEV(DFU_GETSTATE):
++ debug("DFU_GETSTATE\n");
++ usb_send(&eps[0], &dfu.state, 1, NULL, NULL);
++ return 1;
++ default:
++ error("DFU rt %x, rq%x ?\n",
++ setup->bmRequestType, setup->bRequest);
++ return 0;
++ }
++}
++
++
++bool dfu_my_descr(uint8_t type, uint8_t index, const uint8_t **reply,
++ uint8_t *size)
++{
++ if (type != DFU_DT_FUNCTIONAL)
++ return sernum_get_descr(type, index, reply, size);
++ *reply = functional_descriptor;
++ *size = sizeof(functional_descriptor);
++ return 1;
++}
+diff --git a/atusb/usb/usb.c b/atusb/usb/usb.c
+new file mode 100644
+index 0000000..543d8c2
+--- /dev/null
++++ b/atusb/usb/usb.c
+@@ -0,0 +1,181 @@
++/*
++ * fw/usb/usb.c - USB hardware setup and standard device requests
++ *
++ * Written 2008-2011, 2013, 2015 by Werner Almesberger
++ * Copyright 2008-2011, 2013, 2015 Werner Almesberger
++ *
++ * 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.
++ */
++
++/*
++ * Known issues:
++ * - no suspend/resume
++ * - should support EP clearing and stalling
++ */
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include "usb.h"
++#include "board.h"
++
++
++#ifndef NULL
++#define NULL 0
++#endif
++
++#if 1
++extern void panic(void);
++#define BUG_ON(cond) do { if (cond) panic(); } while (0)
++#else
++#define BUG_ON(cond)
++#endif
++
++bool (*user_setup)(const struct setup_request *setup);
++void (*user_set_interface)(int nth);
++bool (*user_get_descriptor)(uint8_t type, uint8_t index,
++ const uint8_t **reply, uint8_t *size);
++void (*user_reset)(void);
++
++
++void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf,
++ uint8_t size, void (*callback)(void *user), void *user)
++{
++ BUG_ON(ep->state);
++ ep->state = state;
++ ep->buf = buf;
++ ep->end = buf+size;
++ ep->callback = callback;
++ ep->user = user;
++ usb_ep_change(ep);
++}
++
++
++static bool get_descriptor(uint8_t type, uint8_t index, uint16_t length)
++{
++ const uint8_t *reply;
++ uint8_t size;
++
++ switch (type) {
++ case USB_DT_DEVICE:
++ reply = device_descriptor;
++ size = reply[0];
++ break;
++ case USB_DT_CONFIG:
++ if (index)
++ return 0;
++ reply = config_descriptor;
++ size = reply[2];
++ break;
++ default:
++ if (!user_get_descriptor)
++ return 0;
++ if (!user_get_descriptor(type, index, &reply, &size))
++ return 0;
++ }
++ if (length < size)
++ size = length;
++ usb_send(&eps[0], reply, size, NULL, NULL);
++ return 1;
++}
++
++
++bool handle_setup(const struct setup_request *setup)
++{
++ switch (setup->bmRequestType | setup->bRequest << 8) {
++
++ /*
++ * Device request
++ *
++ * See http://www.beyondlogic.org/usbnutshell/usb6.htm
++ */
++
++ case FROM_DEVICE(GET_STATUS):
++ if (setup->wLength != 2)
++ return 0;
++ usb_send(&eps[0], "\000", 2, NULL, NULL);
++ break;
++ case TO_DEVICE(CLEAR_FEATURE):
++ break;
++ case TO_DEVICE(SET_FEATURE):
++ return 0;
++ case TO_DEVICE(SET_ADDRESS):
++ set_addr(setup->wValue);
++ break;
++ case FROM_DEVICE(GET_DESCRIPTOR):
++ case FROM_INTERFACE(GET_DESCRIPTOR):
++ if (!get_descriptor(setup->wValue >> 8, setup->wValue,
++ setup->wLength))
++ return 0;
++ break;
++ case TO_DEVICE(SET_DESCRIPTOR):
++ return 0;
++ case FROM_DEVICE(GET_CONFIGURATION):
++ usb_send(&eps[0], "", 1, NULL, NULL);
++ break;
++ case TO_DEVICE(SET_CONFIGURATION):
++ if (setup->wValue != config_descriptor[5])
++ return 0;
++ break;
++
++ /*
++ * Interface request
++ */
++
++ case FROM_INTERFACE(GET_STATUS):
++ return 0;
++ case TO_INTERFACE(CLEAR_FEATURE):
++ return 0;
++ case TO_INTERFACE(SET_FEATURE):
++ return 0;
++ case FROM_INTERFACE(GET_INTERFACE):
++ return 0;
++ case TO_INTERFACE(SET_INTERFACE):
++ {
++ const uint8_t *interface_descriptor =
++ config_descriptor+9;
++ const uint8_t *p;
++ int i;
++
++ i = 0;
++ for (p = interface_descriptor;
++ p != config_descriptor+config_descriptor[2];
++ p += p[0]) {
++ if (p[1] != USB_DT_INTERFACE)
++ continue;
++ if (p[2] == setup->wIndex &&
++ p[3] == setup->wValue) {
++ if (user_set_interface)
++ user_set_interface(i);
++ return 1;
++ }
++ i++;
++ }
++ return 0;
++ }
++ break;
++
++ /*
++ * Endpoint request
++ */
++
++ case FROM_ENDPOINT(GET_STATUS):
++ return 0;
++ case TO_ENDPOINT(CLEAR_FEATURE):
++ return 0;
++ case TO_ENDPOINT(SET_FEATURE):
++ return 0;
++ case FROM_ENDPOINT(SYNCH_FRAME):
++ return 0;
++
++ default:
++ if (user_setup)
++ return user_setup(setup);
++ return 0;
++ }
++
++ return 1;
++}
+diff --git a/atusb/usb/usb.h b/atusb/usb/usb.h
+new file mode 100644
+index 0000000..cb40f9e
+--- /dev/null
++++ b/atusb/usb/usb.h
+@@ -0,0 +1,189 @@
++/*
++ * fw/usb//usb.h - USB hardware setup and standard device requests
++ *
++ * Written 2008, 2009, 2011, 2013, 2015 by Werner Almesberger
++ * Copyright 2008, 2009, 2011, 2013, 2015 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#ifndef USB_H
++#define USB_H
++
++
++#include <stdbool.h>
++#include <stdint.h>
++
++
++/*
++ * Packet identifier types
++ */
++
++#define PID_OUT 0x1
++#define PID_IN 0x9
++#define PID_SOF 0x5
++#define PID_SETUP 0xd
++#define PID_DATA0 0x3
++#define PID_DATA1 0xb
++#define PID_ACK 0x2
++#define PID_NAK 0xa
++#define PID_STALL 0xe
++
++/*
++ * Descriptor types
++ *
++ * Reuse libusb naming scheme (/usr/include/usb.h)
++ */
++
++#define USB_DT_DEVICE 1
++#define USB_DT_CONFIG 2
++#define USB_DT_STRING 3
++#define USB_DT_INTERFACE 4
++#define USB_DT_ENDPOINT 5
++
++/*
++ * Device classes
++ *
++ * Reuse libusb naming scheme (/usr/include/usb.h)
++ */
++
++#define USB_CLASS_PER_INTERFACE 0
++#define USB_CLASS_COMM 2
++#define USB_CLASS_HID 3
++#define USB_CLASS_MASS_STORAGE 8
++#define USB_CLASS_HUB 9
++#define USB_CLASS_DATA 10
++#define USB_CLASS_APP_SPEC 0xfe
++#define USB_CLASS_VENDOR_SPEC 0xff
++
++/*
++ * Configuration attributes
++ */
++
++#define USB_ATTR_BUS_POWERED 0x80
++#define USB_ATTR_SELF_POWERED 0x40
++#define USB_ATTR_REMOTE_WAKEUP 0x20
++
++/*
++ * Endpoint type
++ */
++
++#define USB_ENDPOINT_TYPE_CONTROL 0
++#define USB_ENDPOINT_TYPE_ISOCHRONOUS 1
++#define USB_ENDPOINT_TYPE_BULK 2
++#define USB_ENDPOINT_TYPE_INTERRUPT 3
++
++/*
++ * Setup request types
++ */
++
++#define TO_DEVICE(req) (0x00 | (req) << 8)
++#define FROM_DEVICE(req) (0x80 | (req) << 8)
++#define TO_INTERFACE(req) (0x01 | (req) << 8)
++#define FROM_INTERFACE(req) (0x81 | (req) << 8)
++#define TO_ENDPOINT(req) (0x02 | (req) << 8)
++#define FROM_ENDPOINT(req) (0x82 | (req) << 8)
++
++/*
++ * Setup requests
++ */
++
++#define GET_STATUS 0x00
++#define CLEAR_FEATURE 0x01
++#define SET_FEATURE 0x03
++#define SET_ADDRESS 0x05
++#define GET_DESCRIPTOR 0x06
++#define SET_DESCRIPTOR 0x07
++#define GET_CONFIGURATION 0x08
++#define SET_CONFIGURATION 0x09
++#define GET_INTERFACE 0x0a
++#define SET_INTERFACE 0x0b
++#define SYNCH_FRAME 0x0c
++
++/*
++ * USB Language ID codes
++ *
++ * http://www.usb.org/developers/docs/USB_LANGIDs.pdf
++ */
++
++#define USB_LANGID_ENGLISH_US 0x409
++
++
++/*
++ * Odd. sdcc seems to think "x" assumes the size of the destination, i.e.,
++ * uint8_t. Hence the cast.
++ */
++
++#define LE(x) ((uint16_t) (x) & 0xff), ((uint16_t) (x) >> 8)
++
++#define LO(x) (((uint8_t *) &(x))[0])
++#define HI(x) (((uint8_t *) &(x))[1])
++
++
++#ifdef LOW_SPEED
++#define EP0_SIZE 8
++#else
++#define EP0_SIZE 64
++#endif
++
++#define EP1_SIZE 64 /* simplify */
++
++
++enum ep_state {
++ EP_IDLE,
++ EP_RX,
++ EP_TX,
++ EP_STALL,
++};
++
++struct ep_descr {
++ enum ep_state state;
++ uint8_t *buf;
++ uint8_t *end;
++ uint8_t size;
++ void (*callback)(void *user);
++ void *user;
++};
++
++struct setup_request {
++ uint8_t bmRequestType;
++ uint8_t bRequest;
++ uint16_t wValue;
++ uint16_t wIndex;
++ uint16_t wLength;
++};
++
++
++extern const uint8_t device_descriptor[];
++extern const uint8_t config_descriptor[];
++extern struct ep_descr eps[];
++
++extern bool (*user_setup)(const struct setup_request *setup);
++extern void (*user_set_interface)(int nth);
++extern bool (*user_get_descriptor)(uint8_t type, uint8_t index,
++ const uint8_t **reply, uint8_t *size);
++extern void (*user_reset)(void);
++
++
++#define usb_left(ep) ((ep)->end-(ep)->buf)
++#define usb_send(ep, buf, size, callback, user) \
++ usb_io(ep, EP_TX, (void *) buf, size, callback, user)
++#define usb_recv(ep, buf, size, callback, user) \
++ usb_io(ep, EP_RX, buf, size, callback, user)
++
++void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf,
++ uint8_t size, void (*callback)(void *user), void *user);
++
++bool handle_setup(const struct setup_request *setup);
++void set_addr(uint8_t addr);
++void usb_ep_change(struct ep_descr *ep);
++void usb_reset(void);
++void usb_init(void);
++
++void ep_init(void);
++
++#endif /* !USB_H */
+diff --git a/atusb/version.h b/atusb/version.h
+new file mode 100644
+index 0000000..8fd6a2c
+--- /dev/null
++++ b/atusb/version.h
+@@ -0,0 +1,23 @@
++/*
++ * fw/version.h - Automatically generated version string
++ *
++ * Written 2008, 2011 by Werner Almesberger
++ * Copyright 2008, 2011 Werner Almesberger
++ *
++ * 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.
++ */
++
++
++#ifndef VERSION_H
++#define VERSION_H
++
++#include <stdint.h>
++
++
++extern const char *build_date;
++extern const uint16_t build_number;
++
++#endif /* !VERSION_H */
+--
+2.26.0
+
diff --git a/libre/linux-libre-firmware/0003-Update-INSTALL-document.patch b/libre/linux-libre-firmware/0003-Update-INSTALL-document.patch
new file mode 100644
index 000000000..038b518ca
--- /dev/null
+++ b/libre/linux-libre-firmware/0003-Update-INSTALL-document.patch
@@ -0,0 +1,44 @@
+From 02054c86c9fce8978cc7372b758339191cc9f6d6 Mon Sep 17 00:00:00 2001
+From: Jason Self <j@jxself.org>
+Date: Thu, 4 Jul 2019 16:34:28 -0700
+Subject: [PATCH 3/8] Update INSTALL document
+
+To include the correct package name for GCC-AVR and improved
+descriptions.
+---
+ INSTALL | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/INSTALL b/INSTALL
+index 7fb1116..9ea6dad 100644
+--- a/INSTALL
++++ b/INSTALL
+@@ -16,14 +16,14 @@ In order to build everything you will need the following on the host
+ system:
+
+ * A C/C++ compiler, like GCC
+- * AVR-GCC
+- * Standard C library for AVR-GCC
+ * Cmake
+ * GNU Bison/YACC
+ * GNU Flex
+ * GNU Gperf
+ * GNU Make
+ * GNU Wget
++ * GNU C cross-compiler for AVR
++ * Standard C library for Atmel AVR
+ * GNU C cross-compiler for ARM:
+ - arm-linux-gnueabi-gcc
+ - arm-linux-gnueabi-ld
+@@ -34,7 +34,7 @@ system:
+
+ On GNU/Linux distros that use apt you can install these with:
+
+- apt install avr-gcc avr-libc binutils-arm-linux-gnueabi \
++ apt install gcc-avr avr-libc binutils-arm-linux-gnueabi \
+ binutils-arm-none-eabi bison cmake flex g++ gcc \
+ gcc-arm-linux-gnueabi gcc-arm-none-eabi gperf make wget
+
+--
+2.26.0
+
diff --git a/libre/linux-libre-firmware/0004-atusb-Build-updates.patch b/libre/linux-libre-firmware/0004-atusb-Build-updates.patch
new file mode 100644
index 000000000..641183577
--- /dev/null
+++ b/libre/linux-libre-firmware/0004-atusb-Build-updates.patch
@@ -0,0 +1,73 @@
+From d6bb7bf71810db1b02dec985e8383594c193f55f Mon Sep 17 00:00:00 2001
+From: Jason Self <j@jxself.org>
+Date: Thu, 4 Jul 2019 16:57:58 -0700
+Subject: [PATCH 4/8] atusb: Build updates
+
+Update the root level Makefile to both invoke "make atusb.dfu" when
+building.
+
+Update the INSTALL document to mention the dependency on dfu-util.
+
+Update the root level Makefile to install atusb.dfu if it's compiled.
+Even though the kernel itself doesn't load the firmware into the
+device this will ensure that pre-compiled firmware is included in the
+release tarballs.
+---
+ INSTALL | 5 +++--
+ Makefile | 3 ++-
+ 2 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/INSTALL b/INSTALL
+index 9ea6dad..70c4381 100644
+--- a/INSTALL
++++ b/INSTALL
+@@ -17,6 +17,7 @@ system:
+
+ * A C/C++ compiler, like GCC
+ * Cmake
++ * dfu-util - Device Firmware Upgrade Utilities
+ * GNU Bison/YACC
+ * GNU Flex
+ * GNU Gperf
+@@ -35,7 +36,7 @@ system:
+ On GNU/Linux distros that use apt you can install these with:
+
+ apt install gcc-avr avr-libc binutils-arm-linux-gnueabi \
+- binutils-arm-none-eabi bison cmake flex g++ gcc \
++ binutils-arm-none-eabi bison cmake dfu-util flex g++ gcc \
+ gcc-arm-linux-gnueabi gcc-arm-none-eabi gperf make wget
+
+ CARL9170 Firmware Configuration
+@@ -48,7 +49,7 @@ atusb: Firmware for the ATUSB IEEE 802.15.4 USB Adapter
+
+ To flash the firmware you need dfu-util on the host. Issue
+
+- make dfu
++ dfu-util -d 20b7:1540 -D atusb.dfu
+
+ right after plugging the device into the USB port while the red led is
+ still on.
+diff --git a/Makefile b/Makefile
+index 8474b30..51f708d 100644
+--- a/Makefile
++++ b/Makefile
+@@ -37,7 +37,7 @@ ath9k_htc: ath9k_htc_toolchain
+ cd ath9k_htc && $(MAKE) -C target_firmware
+
+ atusb:
+- cd atusb && $(MAKE)
++ cd atusb && $(MAKE) atusb.dfu
+
+ av7110:
+ cd av7110 && $(MAKE)
+@@ -103,6 +103,7 @@ install:
+ ln -s ath9k_htc/htc_9271-1.4.0.fw $(prefix)/htc_9271.fw; fi;
+ if [ -a ath9k_htc/target_firmware/htc_7010.fw ]; then $(install_program) -D ath9k_htc/target_firmware/htc_7010.fw $(prefix)/ath9k_htc/htc_7010-1.4.0.fw && \
+ ln -s ath9k_htc/htc_7010-1.4.0.fw $(prefix)/htc_7010.fw; fi;
++ if [ -a atusb/atusb.dfu ]; then $(install_program) -D atusb/atusb.dfu $(prefix)/atusb/atusb.dfu; fi;
+ if [ -a av7110/bootcode.bin ]; then $(install_program) -D av7110/bootcode.bin $(prefix)/av7110/bootcode.bin; fi;
+ if [ -a cis/3CCFEM556.cis ]; then $(install_program) -D cis/3CCFEM556.cis $(prefix)/cis/3CCFEM556.cis; fi;
+ if [ -a cis/3CXEM556.cis ]; then $(install_program) -D cis/3CXEM556.cis $(prefix)/cis/3CXEM556.cis; fi;
+--
+2.26.0
+
diff --git a/libre/linux-libre-firmware/0006-Makefile-Change-spaces-for-atusb-to-tab.patch b/libre/linux-libre-firmware/0006-Makefile-Change-spaces-for-atusb-to-tab.patch
new file mode 100644
index 000000000..8dafb6bda
--- /dev/null
+++ b/libre/linux-libre-firmware/0006-Makefile-Change-spaces-for-atusb-to-tab.patch
@@ -0,0 +1,25 @@
+From 06147f83be16e7ab5e08c280c61e015b60bd0acc Mon Sep 17 00:00:00 2001
+From: Jason Self <j@jxself.org>
+Date: Thu, 4 Jul 2019 17:14:30 -0700
+Subject: [PATCH 6/8] Makefile: Change spaces for atusb to tab
+
+---
+ Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile b/Makefile
+index 51f708d..b5c5d05 100644
+--- a/Makefile
++++ b/Makefile
+@@ -103,7 +103,7 @@ install:
+ ln -s ath9k_htc/htc_9271-1.4.0.fw $(prefix)/htc_9271.fw; fi;
+ if [ -a ath9k_htc/target_firmware/htc_7010.fw ]; then $(install_program) -D ath9k_htc/target_firmware/htc_7010.fw $(prefix)/ath9k_htc/htc_7010-1.4.0.fw && \
+ ln -s ath9k_htc/htc_7010-1.4.0.fw $(prefix)/htc_7010.fw; fi;
+- if [ -a atusb/atusb.dfu ]; then $(install_program) -D atusb/atusb.dfu $(prefix)/atusb/atusb.dfu; fi;
++ if [ -a atusb/atusb.dfu ]; then $(install_program) -D atusb/atusb.dfu $(prefix)/atusb/atusb.dfu; fi;
+ if [ -a av7110/bootcode.bin ]; then $(install_program) -D av7110/bootcode.bin $(prefix)/av7110/bootcode.bin; fi;
+ if [ -a cis/3CCFEM556.cis ]; then $(install_program) -D cis/3CCFEM556.cis $(prefix)/cis/3CCFEM556.cis; fi;
+ if [ -a cis/3CXEM556.cis ]; then $(install_program) -D cis/3CXEM556.cis $(prefix)/cis/3CXEM556.cis; fi;
+--
+2.26.0
+
diff --git a/libre/linux-libre-firmware/0007-Makefile-Add-atusb-to-all.patch b/libre/linux-libre-firmware/0007-Makefile-Add-atusb-to-all.patch
new file mode 100644
index 000000000..1da3c0028
--- /dev/null
+++ b/libre/linux-libre-firmware/0007-Makefile-Add-atusb-to-all.patch
@@ -0,0 +1,25 @@
+From b43de635d524d519e47bcba78d081a0f811584b1 Mon Sep 17 00:00:00 2001
+From: Jason Self <j@jxself.org>
+Date: Thu, 4 Jul 2019 18:34:05 -0700
+Subject: [PATCH 7/8] Makefile: Add atusb to 'all'
+
+---
+ Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile b/Makefile
+index b5c5d05..b5bfb47 100644
+--- a/Makefile
++++ b/Makefile
+@@ -19,7 +19,7 @@ install_program=install
+
+ .PHONY: all test clean install a56 as31 aica ath9k_htc_toolchain ath9k_htc atusb av7110 b43-tools carl9170fw-toolchain carl9170fw cis-tools cis dsp56k ihex2fw isci keyspan_pda openfwwf usbdux
+
+-all: aica ath9k_htc av7110 carl9170fw cis dsp56k isci keyspan_pda openfwwf usbdux
++all: aica ath9k_htc atusb av7110 carl9170fw cis dsp56k isci keyspan_pda openfwwf usbdux
+
+ a56:
+ cd a56 && $(MAKE)
+--
+2.26.0
+
diff --git a/libre/linux-libre-firmware/0008-Makefile-Set-shell-to-bin-bash.patch b/libre/linux-libre-firmware/0008-Makefile-Set-shell-to-bin-bash.patch
new file mode 100644
index 000000000..6029cd5b4
--- /dev/null
+++ b/libre/linux-libre-firmware/0008-Makefile-Set-shell-to-bin-bash.patch
@@ -0,0 +1,25 @@
+From a4d24e3a224a19404bc3ac98389ff376198c85ef Mon Sep 17 00:00:00 2001
+From: Jason Self <j@jxself.org>
+Date: Thu, 4 Jul 2019 18:37:45 -0700
+Subject: [PATCH 8/8] Makefile: Set shell to /bin/bash
+
+---
+ Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile b/Makefile
+index b5bfb47..5e38d94 100644
+--- a/Makefile
++++ b/Makefile
+@@ -13,7 +13,7 @@
+ # You should have received a copy of the GNU General Public License
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+-shell=/bin/sh
++shell=/bin/bash
+ prefix=/lib/firmware
+ install_program=install
+
+--
+2.26.0
+
diff --git a/libre/linux-libre-firmware/PKGBUILD b/libre/linux-libre-firmware/PKGBUILD
index 73aab20d1..68e91458f 100644
--- a/libre/linux-libre-firmware/PKGBUILD
+++ b/libre/linux-libre-firmware/PKGBUILD
@@ -5,13 +5,19 @@
pkgname=linux-libre-firmware
pkgver=1.3.1
-pkgrel=1
+pkgrel=2
epoch=1
pkgdesc='Firmware files for Linux-libre'
arch=(any)
url='https://jxself.org/firmware'
license=(GPL3)
-makedepends=(arm-linux-gnueabi-gcc arm-none-eabi-gcc cmake sh-elf-gcc sh-elf-newlib xtensa-elf-gcc)
+makedepends=(arm-linux-gnueabi-gcc # av7110
+ arm-none-eabi-gcc # aica
+ avr-gcc avr-libc dfu-util # atusb
+ sh-elf-gcc sh-elf-newlib cmake # carl9170fw
+ xtensa-elf-gcc # ath9k_htc
+)
+optdepends=('dfu-util: to flash the atusb firmware')
provides=(linux-firmware)
conflicts=(linux-firmware
linux-firmware-git
@@ -26,37 +32,62 @@ conflicts=(linux-firmware
rt2870usb-fw
rt2x00-rt61-fw
rt2x00-rt71w-fw
- amd-ucode
openfwwf
ath9k-htc-firmware)
replaces=(${conflicts[@]})
options=(!buildflags !makeflags)
-source=("$url/$pkgname-$pkgver.tar.lz"{,.asc}
- 0001-Add-offline-files-and-a-toolchain-option-in-Makefile.patch)
+source=(
+ "$url/$pkgname-$pkgver.tar.lz"{,.asc}
+ 0001-Update-carl9170-to-latest-upstream.patch
+ 0002-Add-firmware-for-the-ATUSB-IEEE-802.15.4-USB-Adapter.patch
+ 0003-Update-INSTALL-document.patch
+ 0004-atusb-Build-updates.patch
+ 0006-Makefile-Change-spaces-for-atusb-to-tab.patch
+ 0007-Makefile-Add-atusb-to-all.patch
+ 0008-Makefile-Set-shell-to-bin-bash.patch
+)
sha512sums=('d827ec7024b4900dcdf6af8cdd4e72b02596d0ade5c2262460cd573785300498e6aa655b9a59cd22e38a6a787826a564ae33acfac52e68df1d082445e66f18df'
'SKIP'
- '1aa4b3a463c0d24f295df2ea13e30d3b11a1af7abef3e03558036b07143b3262178a3b7ab6cc0cc7c255044f833e7df9a3cb18b98978e55737852fe0185dc22e')
+ '07263a406cb15c9f91f41c8fb6d4307875665b8106adf526353d64306611458e11ce311842caa9efa623091c897ec9f87cfe54584c9c47590195867958cf4086'
+ 'a4e8752cfee8dfd0b17ff798d2b5ea7a773ebcf688f7614d1c1a92fe7f092c626a289069a472bc873a77c9cf52f1e37953b0ba47c37a9dfb2a763a695ca77625'
+ '1665ce62de148383fe4f30aa5714ef6bb1dfd4eed110415995069830b36a4b1aafb05e4cd6cf428dc0817e2affb91740e41b3fab8d2385683d6c18790bbf5afd'
+ '0168ba0f3a8f3938a96b7f72d081283ca04b7ba90c5474d8ccf0716d6964137980f49623c5489271158a48827319a20ee4f19bd94f5bb956ee97d5ba637070c1'
+ 'fa006627935beb7ae78fad5b63423a54e4eae384761ff618fff173250fb5d0c7eef9d5a707733d037edc95259ef59c9053c4245b80a004928b6809ea1cb43cf8'
+ '8014d9eccb4c5193d1b4b056e163b5631c207adc78522e413650eb053c90e7b9e333c9e5cb6f73b63985e27020f4c69e7b791176702c55f711f6db95387e6bff'
+ '59560f16978b2fd7d8c828d7609ee4f71cbdc902b70cfb87feca686958dd4df05c61c5e1bfa5e0c2a060f23185a0b9e5d07d487e172764767cdc4b26a6240b79')
validpgpkeys=('F611A908FFA165C699584ED49D0DB31B545A3198') # Jason Self
-prepare() {
- cd $srcdir/$pkgname-$pkgver/src
+prepare(){
+ cd $pkgname-$pkgver/src
- patch -p1 -i $srcdir/0001-Add-offline-files-and-a-toolchain-option-in-Makefile.patch
+ local src
+ for src in "${source[@]}"; do
+ src="${src%%::*}"
+ src="${src##*/}"
+ [[ $src = *.patch ]] || continue
+ echo "Applying patch $src..."
+ patch -Np1 < "$srcdir/$src"
+ done
- # Build carl9170 with the default config
- sed '71s/autogen.sh/autogen.sh --alldefconfig/' -i Makefile
-}
-
-build() {
- cd $srcdir/$pkgname-$pkgver
- make toolchains= -C src
+ # Use our cross compilers and build carl9170fw with the default config
+ sed -e 's|$PWD/../toolchain/inst/bin/||g' \
+ -e 's|${CMAKE_SOURCE_DIR}/toolchain/inst/bin/||g' \
+ -e '/CMAKE_FIND_ROOT_PATH/d' \
+ -e 's/: ath9k_htc_toolchain$/:/' \
+ -e 's/: carl9170fw-toolchain$/:/' \
+ -e '/cd carl9170fw && .\/autogen.sh$/ s/$/ --alldefconfig/' \
+ -i Makefile \
+ ath9k_htc/target_firmware/configure \
+ carl9170fw/extra/sh-elf-linux.cmake
}
-package() {
- cd $srcdir/$pkgname-$pkgver
+build(){
+ make -C $pkgname-$pkgver/src
+}
- install -dm755 $pkgdir/usr/lib/firmware
- make -C src prefix=$pkgdir/usr/lib/firmware install
+package(){
+ cd $pkgname-$pkgver/src
- install -Dm644 src/WHENCE $pkgdir/usr/share/licenses/$pkgname/WHENCE
+ make prefix=$pkgdir/usr/lib/firmware install
+ install -Dm644 WHENCE $pkgdir/usr/share/licenses/$pkgname/WHENCE
}