From d76eb324df67b07a123d8f27a365b1e5c64d5360 Mon Sep 17 00:00:00 2001 From: jc_gargma Date: Sun, 29 Mar 2020 17:18:54 -0700 Subject: Rebuild with gcc 9.3.0 --- ...-files-and-a-toolchain-option-in-Makefile.patch | 236 - 0001-Update-carl9170-to-latest-upstream.patch | 4982 ++++++++++++++++++++ ...e-for-the-ATUSB-IEEE-802.15.4-USB-Adapter.patch | 4893 +++++++++++++++++++ 0003-Update-INSTALL-document.patch | 44 + 0004-atusb-Build-updates.patch | 73 + 0006-Makefile-Change-spaces-for-atusb-to-tab.patch | 25 + 0007-Makefile-Add-atusb-to-all.patch | 25 + 0008-Makefile-Set-shell-to-bin-bash.patch | 25 + PKGBUILD | 77 +- 9 files changed, 10121 insertions(+), 259 deletions(-) delete mode 100644 0001-Add-offline-files-and-a-toolchain-option-in-Makefile.patch create mode 100644 0001-Update-carl9170-to-latest-upstream.patch create mode 100644 0002-Add-firmware-for-the-ATUSB-IEEE-802.15.4-USB-Adapter.patch create mode 100644 0003-Update-INSTALL-document.patch create mode 100644 0004-atusb-Build-updates.patch create mode 100644 0006-Makefile-Change-spaces-for-atusb-to-tab.patch create mode 100644 0007-Makefile-Add-atusb-to-all.patch create mode 100644 0008-Makefile-Set-shell-to-bin-bash.patch diff --git a/0001-Add-offline-files-and-a-toolchain-option-in-Makefile.patch b/0001-Add-offline-files-and-a-toolchain-option-in-Makefile.patch deleted file mode 100644 index a5dae8a..0000000 --- a/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 -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 ---- - 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" < ${CMAKE_C_FLAGS} -o ") -+ -+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/0001-Update-carl9170-to-latest-upstream.patch b/0001-Update-carl9170-to-latest-upstream.patch new file mode 100644 index 0000000..e56707e --- /dev/null +++ b/0001-Update-carl9170-to-latest-upstream.patch @@ -0,0 +1,4982 @@ +From c164bf7f87f9081fee7e1a186dd7a87a9a020b9e Mon Sep 17 00:00:00 2001 +From: Jason Self +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 +- * Released under the terms of the GNU GPL v2.0. + */ + +-#include + #include + #include + #include +@@ -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] \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 New config with default defined in \n"); + printf(" --savedefconfig Save the minimal current configuration to \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 +- * Released under the terms of the GNU GPL v2.0. + */ + + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -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 +- * Released under the terms of the GNU GPL v2.0. + */ + ++#include ++#include + #include + #include + #include +@@ -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 +- * 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 +- * 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 +-#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 +- * Released under the terms of the GNU GPL v2.0. + */ + + #include +@@ -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 = ¤t_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 ' && 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 : _(""), ++ menu->sym->name : "", + 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 +- * Released under the terms of the GNU GPL v2.0. + */ + + #include +@@ -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 : "", +- prop_get_type_name(prop->type), + next_sym->name ? next_sym->name : ""); +- } 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 : "", ++ next_sym->name ? next_sym->name : ""); ++ } 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 : "", + next_sym->name ? next_sym->name : ""); +- } 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 : "", + next_sym->name ? next_sym->name : ""); +- } 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 : "", ++ next_sym->name ? next_sym->name : ""); ++ } else if (stack->expr) { ++ fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n", ++ prop->file->name, prop->lineno, + sym->name ? sym->name : "", ++ prop_get_type_name(prop->type), + next_sym->name ? next_sym->name : ""); + } 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 : "", ++ prop_get_type_name(prop->type), + next_sym->name ? next_sym->name : ""); + } + } + ++ 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 + * Copyright (C) 2002-2005 Sam Ravnborg +- * +- * Released under the terms of the GNU GPL v2.0. + */ + + #include +@@ -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 +- * 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 + #include + #include + #include +@@ -15,6 +15,9 @@ + #include + + #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); +-} +- +- +-{ +- {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); + +-{ +- "&&" 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; +- } ++{ ++ [^[:blank:]\n]+.* { + alloc_string(yytext, yyleng); +- zconflval.string = text; +- return T_WORD; +- } +- #.* /* comment */ +- \\\n current_file->lineno++; +- [[:blank:]]+ +- . warn_ignored_character(*yytext); +- <> { +- BEGIN(INITIAL); ++ yylval.string = text; ++ return T_ASSIGN_VAL; + } ++ \n { BEGIN(INITIAL); return T_EOL; } ++ . + } + + { +- [^'"\\\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; + } + <> { + 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_-] + } + + <> { ++ 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 +- * Released under the terms of the GNU GPL v2.0. + */ ++%{ + + #include + #include +@@ -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 T_MAINMENU +-%token T_MENU +-%token T_ENDMENU +-%token T_SOURCE +-%token T_CHOICE +-%token T_ENDCHOICE +-%token T_COMMENT +-%token T_CONFIG +-%token T_MENUCONFIG +-%token T_HELP + %token T_HELPTEXT +-%token T_IF +-%token T_ENDIF +-%token T_DEPENDS +-%token T_OPTIONAL +-%token T_PROMPT +-%token T_TYPE +-%token T_DEFAULT +-%token T_SELECT +-%token T_IMPLY +-%token T_RANGE +-%token T_VISIBLE +-%token T_OPTION +-%token T_ON + %token T_WORD + %token 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 T_ASSIGN_VAL + + %left T_OR + %left T_AND +@@ -85,13 +91,15 @@ static struct menu *current_menu, *current_entry; + %nonassoc T_NOT + + %type prompt ++%type nonconst_symbol + %type symbol ++%type type logic_type default + %type expr + %type if_expr +-%type end +-%type option_name ++%type end + %type if_entry menu_entry choice_entry +-%type symbol_option_arg word_opt ++%type word_opt assign_val ++%type 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 ?: ""); ++ } ++ ++ /* 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 ?: ""); ++ + 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 ""; +-} +- +-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/0002-Add-firmware-for-the-ATUSB-IEEE-802.15.4-USB-Adapter.patch b/0002-Add-firmware-for-the-ATUSB-IEEE-802.15.4-USB-Adapter.patch new file mode 100644 index 0000000..aa4cb07 --- /dev/null +++ b/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 +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= 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 < ++ ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#include ++#include ++#include ++ ++#define F_CPU 8000000UL ++#include ++ ++#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 ++#include ++ ++#include ++ ++#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 ++#include ++#include ++ ++#include ++#include ++ ++#define F_CPU 8000000UL ++#include ++ ++#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 ++#include ++ ++#include ++#include ++#include ++ ++#define F_CPU 8000000UL ++#include ++ ++#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 ++#include ++ ++#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 ++#include ++ ++#include ++#include ++#include ++ ++#define F_CPU 8000000UL ++#include ++ ++#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 ++#include ++ ++#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 ++#include ++ ++#include ++#include ++#include ++ ++#define F_CPU 8000000UL ++#include ++ ++#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 ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#define F_CPU 8000000UL ++#include ++ ++#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 ++#include ++#include ++ ++#include ++#include ++ ++#define F_CPU 8000000UL ++#include ++ ++#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 ++#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 ++#include ++ ++#include ++#include ++ ++#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 ++ * ++ * 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 ++ ++ ++/* ++ * 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 ++#include ++#include ++ ++#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 ++#include ++ ++ ++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 ++#include ++ ++#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 ++#include ++ ++#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 ++#include ++ ++#include ++ ++#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 ++ ++ ++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 ++#include "uart.h" ++ ++#define USART_BAUD 38400UL ++#define F_CPU 8000000UL ++ ++#define Wait_USART_Ready() while (!(UCSR1A & (1< ++ ++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 ++#include ++ ++#define F_CPU 8000000UL ++#include ++ ++#include ++#include ++#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 ++#include ++ ++#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 ++#include ++ ++#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 ++#include ++ ++#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 ++#include ++ ++#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 ++#include ++ ++ ++/* ++ * 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 ++ ++ ++extern const char *build_date; ++extern const uint16_t build_number; ++ ++#endif /* !VERSION_H */ +-- +2.26.0 + diff --git a/0003-Update-INSTALL-document.patch b/0003-Update-INSTALL-document.patch new file mode 100644 index 0000000..038b518 --- /dev/null +++ b/0003-Update-INSTALL-document.patch @@ -0,0 +1,44 @@ +From 02054c86c9fce8978cc7372b758339191cc9f6d6 Mon Sep 17 00:00:00 2001 +From: Jason Self +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/0004-atusb-Build-updates.patch b/0004-atusb-Build-updates.patch new file mode 100644 index 0000000..6411835 --- /dev/null +++ b/0004-atusb-Build-updates.patch @@ -0,0 +1,73 @@ +From d6bb7bf71810db1b02dec985e8383594c193f55f Mon Sep 17 00:00:00 2001 +From: Jason Self +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/0006-Makefile-Change-spaces-for-atusb-to-tab.patch b/0006-Makefile-Change-spaces-for-atusb-to-tab.patch new file mode 100644 index 0000000..8dafb6b --- /dev/null +++ b/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 +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/0007-Makefile-Add-atusb-to-all.patch b/0007-Makefile-Add-atusb-to-all.patch new file mode 100644 index 0000000..1da3c00 --- /dev/null +++ b/0007-Makefile-Add-atusb-to-all.patch @@ -0,0 +1,25 @@ +From b43de635d524d519e47bcba78d081a0f811584b1 Mon Sep 17 00:00:00 2001 +From: Jason Self +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/0008-Makefile-Set-shell-to-bin-bash.patch b/0008-Makefile-Set-shell-to-bin-bash.patch new file mode 100644 index 0000000..6029cd5 --- /dev/null +++ b/0008-Makefile-Set-shell-to-bin-bash.patch @@ -0,0 +1,25 @@ +From a4d24e3a224a19404bc3ac98389ff376198c85ef Mon Sep 17 00:00:00 2001 +From: Jason Self +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 . + +-shell=/bin/sh ++shell=/bin/bash + prefix=/lib/firmware + install_program=install + +-- +2.26.0 + diff --git a/PKGBUILD b/PKGBUILD index 1206765..208e25b 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -9,12 +9,18 @@ pkgname=linux-libre-firmware pkgver=1.3.1 -pkgrel=1 +pkgrel=2 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 @@ -29,36 +35,61 @@ conflicts=(linux-firmware rt2870usb-fw rt2x00-rt61-fw rt2x00-rt71w-fw - amd-ucode openfwwf ath9k-htc-firmware) options=(!buildflags !makeflags) -source=("$url/$pkgname-$pkgver.tar.lz"{,.asc} - 0001-Add-offline-files-and-a-toolchain-option-in-Makefile.patch) -sha512sums=('d827ec7024b4900dcdf6af8cdd4e72b02596d0ade5c2262460cd573785300498e6aa655b9a59cd22e38a6a787826a564ae33acfac52e68df1d082445e66f18df' - 'SKIP' - '1aa4b3a463c0d24f295df2ea13e30d3b11a1af7abef3e03558036b07143b3262178a3b7ab6cc0cc7c255044f833e7df9a3cb18b98978e55737852fe0185dc22e') +source=( + "https://jxself.org/firmware/$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 +) +b2sums=('14819ab3f0582d13c8e0024d9e67738ca77cf6aadb7f5c9b603892a4d1e11b09b4efdefc76a6c94a84b0c54b486410fa75028df5b6dab56d6ba8aabe18f81feb' + 'SKIP' + '6ddd6cbe4cd02e98a992188b8cfe1b534950f8835e33ec8921c1eb08fb082b612a6183c440bbbb17cef38e338e9a994df0515f3a59f2e39130bf5d8ffa0ff52b' + '2ea6ce1111d288e2e0b5e141f05a10f96494cd2747ca135448e4ad4b6ca65ffc593b37fd6af294bfebb5e98ffa192f8b45401d1149171c3b6455f41a4e50092f' + '787cd84ab0b6811e22cd6486bacfc5b196cd3b1645ea30ec541e8b4536fea90b41826e0435b4e136a0d5a80c4880e34135ded0173a7ef1616671356c4b309ddd' + 'dc76474982273f64613a8637122176e62fd3ac0a7104bd496e9b23f130626e1133692e2d354b540bb60c7b305e9262269183dd5bd0d0dec8e1a82733912b11b4' + '2ac961369744e19c037490820480d16449390cc3b1b0497d3105ec1242b93c86f0001561b2b1b530f11110d9c52542a0541c2be679d105a563f0a5b3e28d5773' + 'fc265216531b2f6dcd7822da184a2ac701897b8ac2145530afd0f375805ad559d939ed3d8758dd333eab3226bfb52fcb2ff016001a8865937e33eea9615bb416' + '0c425324adb026d1b274a0d4a818697f5a64b5b605efacaf1369f6a95225c37fbf088214eeec4c2e45d1f9676ba14d8afd7a92cb3cf2661bcef46d9bbb60aebc') 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 } -- cgit v1.2.1