system:
* A C/C++ compiler, like GCC
+ * AVR-GCC
+ * Standard C library for AVR-GCC
* Cmake
* GNU Bison/YACC
* GNU Flex
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
---------
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
ath9k_htc: ath9k_htc_toolchain
cd ath9k_htc && $(MAKE) -C target_firmware
+atusb:
+ cd atusb && $(MAKE)
+
av7110:
cd av7110 && $(MAKE)
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;
--------------------------------------------------------------------------
+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
--- /dev/null
+#
+# 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
--- /dev/null
+Requires a very recent toolchain, because ATmega32U2 is relatively new.
+
+- Building:
+
+ make
+
+- Uploading the firmware to a Ben (for flashing with the atusb-pgm cable):
+
+ make HOST=<hostname> upload
+
+ Example:
+
+ make HOST=ben upload
+
+ HOST defaults to "jlime".
+
+- Flashing the boot loader:
+
+ Prerequisite: avrdude on the Ben.
+
+ Disconnect the atusb board from USB. Insert the atusb-pgm connector into
+ the Ben. Place the atusb-pgm adapter on the exposed contact pads of the
+ atusb board and push it down. Then run
+
+ make prog
+
+ This takes about 30 seconds. If the programming fails with an error
+ message like "Yikes! Invalid device signature.", verify that the
+ atusb-pgm board is properly connected and placed, then try again.
+
+- Uploading the application:
+
+ Prerequisite: dfu-util installed on the PC.
+
+ Insert atusb into the PC, then run
+
+ make dfu
+
+ Note: since the boot loader resets the USB bus after timing out,
+ this operation can fail with a message like "No DFU capable USB device
+ found". Just retry, and it will eventually get through.
+
+
+HULUSB notes:
+-------------
+To prepare and flash the firmware on a HULUSB device some additional steps are
+needed;
+
+avr-objcopy -O ihex -R .signature -R .fuse -R .eeprom hulusb.elf hulusb.hex
+dfu-programmer at90usb1287 flash hulusb.hex
+dfu-programmer at90usb1287 reset
+
+--------------------------
+
+Making the toolchain:
+
+# patches according to
+# http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=789527
+
+# some gcc prerequisites
+
+apt-get remove avr-libc gcc-avr binutils-avr
+apt-get install libmpfr-dev libmpc-dev
+
+# binutils
+
+wget http://ftp.gnu.org/gnu/binutils/binutils-2.21.tar.bz2
+tar xfj binutils-2.21.tar.bz2
+cd binutils-2.21
+./configure --target=avr --disable-nls
+make
+make install
+
+# gcc
+
+wget http://ftpmirror.gnu.org/gcc/gcc-4.5.2/gcc-4.5.2.tar.bz2
+wget -O gcc_452_avr.patch http://gcc.gnu.org/bugzilla/attachment.cgi?id=23050
+tar xfj gcc-4.5.2.tar.bz2
+cd gcc-4.5.2
+patch -p1 -s <../gcc_452_avr.patch
+mkdir obj-avr
+cd obj-avr
+../configure --target=avr --enable-languages=c \
+ --disable-nls --disable-libssp --with-dwarf2
+make
+make install
+
+wget http://download.savannah.gnu.org/releases/avr-libc/avr-libc-1.7.1.tar.bz2
+tar xfj avr-libc-1.7.1.tar.bz2
+cd avr-libc-1.7.1
+./bootstrap # the automake at the end takes a while
+./configure --build=`./config.guess` --host=avr
+make
+make install
--- /dev/null
+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.)
--- /dev/null
+#!/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"))
--- /dev/null
+#!/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")
--- /dev/null
+#!/bin/sh
+#
+# Plot output of "dec"
+#
+gnuplot -persist <<EOF
+set style data lines
+plot "_wv" using 1:(\$2-4), \
+ "_dig" using 1:(\$2*3.3-4) lw 2, \
+ "_wv" using 1:3, \
+ "_dig" using 1:(\$3*3.3) lw 2, \
+ "_clk" using 1:(\$2+1) lt 7
+EOF
--- /dev/null
+/*
+ * fw/atusb.c - ATUSB initialization and main loop
+ *
+ * Written 2008-2011 by Werner Almesberger
+ * Copyright 2008-2011 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/sleep.h>
+#include <avr/interrupt.h>
+
+#include "usb.h"
+
+#include "board.h"
+#include "sernum.h"
+#include "spi.h"
+#include "atusb/ep0.h"
+
+#ifdef DEBUG
+#include "uart.h"
+#endif
+
+
+int main(void)
+{
+ board_init();
+ board_app_init();
+ reset_rf();
+
+ user_get_descriptor = sernum_get_descr;
+
+ /* now we should be at 8 MHz */
+
+#ifdef DEBUG
+ uart_init();
+ static FILE atben_stdout = FDEV_SETUP_STREAM(uart_write_char, NULL,
+ _FDEV_SETUP_WRITE);
+ stdout = &atben_stdout;
+#endif
+
+ usb_init();
+ ep0_init();
+#ifdef ATUSB
+ timer_init();
+
+ /* move interrupt vectors to 0 */
+ MCUCR = 1 << IVCE;
+ MCUCR = 0;
+#endif
+
+ sei();
+
+ while (1)
+ sleep_mode();
+}
--- /dev/null
+/*
+ * fw/board.c - Board-specific functions (for boot loader and application)
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/boot.h>
+
+#define F_CPU 8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "at86rf230.h"
+#include "board.h"
+#include "spi.h"
+
+
+uint8_t board_sernum[42] = { 42, USB_DT_STRING };
+
+/* ----- Register access --------------------------------------------------- */
+
+void change_state(uint8_t new)
+{
+ while ((reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK) ==
+ TRX_STATUS_TRANSITION);
+ reg_write(REG_TRX_STATE, new);
+}
+
+
+uint8_t reg_read(uint8_t reg)
+{
+ uint8_t value;
+
+ spi_begin();
+ spi_send(AT86RF230_REG_READ | reg);
+ value = spi_recv();
+ spi_end();
+
+ return value;
+}
+
+
+uint8_t subreg_read(uint8_t address, uint8_t mask, uint8_t position)
+{
+ /* Read current register value and mask out subregister. */
+ uint8_t register_value = reg_read(address);
+ register_value &= mask;
+ register_value >>= position; /* Align subregister value. */
+
+ return register_value;
+}
+
+
+void reg_write(uint8_t reg, uint8_t value)
+{
+ spi_begin();
+ spi_send(AT86RF230_REG_WRITE | reg);
+ spi_send(value);
+ spi_end();
+}
+
+
+void subreg_write(uint8_t address, uint8_t mask, uint8_t position, uint8_t value)
+{
+ /* Read current register value and mask area outside the subregister. */
+ uint8_t register_value = reg_read(address);
+ register_value &= ~mask;
+
+ /* Start preparing the new subregister value. shift in place and mask. */
+ value <<= position;
+ value &= mask;
+
+ value |= register_value; /* Set the new subregister value. */
+
+ /* Write the modified register value. */
+ reg_write(address, value);
+}
+
+
+void panic(void)
+{
+ cli();
+ while (1) {
+ SET(LED);
+ _delay_ms(100);
+ CLR(LED);
+ _delay_ms(100);
+ }
+}
+
+
+static char hex(uint8_t nibble)
+{
+ return nibble < 10 ? '0'+nibble : 'a'+nibble-10;
+}
+
+
+void get_sernum(void)
+{
+ uint8_t sig;
+ uint8_t i;
+
+ for (i = 0; i != 10; i++) {
+ sig = boot_signature_byte_get(i+0xe);
+ board_sernum[(i << 2)+2] = hex(sig >> 4);
+ board_sernum[(i << 2)+4] = hex(sig & 0xf);
+ }
+}
--- /dev/null
+/*
+ * fw/board.h - Board-specific functions and definitions
+ *
+ * Written 2008-2011, 2013, 2013 by Werner Almesberger
+ * Copyright 2008-2011, 2013, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef BOARD_H
+#define BOARD_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <atusb/atusb.h>
+
+#ifdef ATUSB
+#include "board_atusb.h"
+#endif
+#ifdef RZUSB
+#include "board_rzusb.h"
+#endif
+#ifdef HULUSB
+#include "board_hulusb.h"
+#endif
+
+#define SET_2(p, b) PORT##p |= 1 << (b)
+#define CLR_2(p, b) PORT##p &= ~(1 << (b))
+#define IN_2(p, b) DDR##p &= ~(1 << (b))
+#define OUT_2(p, b) DDR##p |= 1 << (b)
+#define PIN_2(p, b) ((PIN##p >> (b)) & 1)
+
+#define SET_1(p, b) SET_2(p, b)
+#define CLR_1(p, b) CLR_2(p, b)
+#define IN_1(p, b) IN_2(p, b)
+#define OUT_1(p, b) OUT_2(p, b)
+#define PIN_1(p, b) PIN_2(p, b)
+
+#define SET(n) SET_1(n##_PORT, n##_BIT)
+#define CLR(n) CLR_1(n##_PORT, n##_BIT)
+#define IN(n) IN_1(n##_PORT, n##_BIT)
+#define OUT(n) OUT_1(n##_PORT, n##_BIT)
+#define PIN(n) PIN_1(n##_PORT, n##_BIT)
+
+
+#define USB_VENDOR ATUSB_VENDOR_ID
+#define USB_PRODUCT ATUSB_PRODUCT_ID
+
+#define DFU_USB_VENDOR USB_VENDOR
+#define DFU_USB_PRODUCT USB_PRODUCT
+
+
+#define BOARD_MAX_mA 40
+
+#ifdef BOOT_LOADER
+#define NUM_EPS 1
+#else
+#define NUM_EPS 2
+#endif
+
+#define HAS_BOARD_SERNUM
+
+extern uint8_t board_sernum[42];
+extern uint8_t irq_serial;
+
+
+void reset_rf(void);
+void reset_cpu(void);
+uint8_t read_irq(void);
+void slp_tr(void);
+
+void led(bool on);
+void panic(void);
+
+uint64_t timer_read(void);
+void timer_init(void);
+
+bool gpio(uint8_t port, uint8_t data, uint8_t dir, uint8_t mask, uint8_t *res);
+void gpio_cleanup(void);
+
+void get_sernum(void);
+
+void board_app_init(void);
+
+uint8_t reg_read(uint8_t reg);
+uint8_t subreg_read(uint8_t address, uint8_t mask, uint8_t position);
+void reg_write(uint8_t reg, uint8_t value);
+void subreg_write(uint8_t address, uint8_t mask, uint8_t position, uint8_t value);
+void change_state(uint8_t new);
+
+#endif /* !BOARD_H */
--- /dev/null
+/*
+ * fw/board_app.c - Board-specific functions (for the application)
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+#define F_CPU 8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "at86rf230.h"
+#include "spi.h"
+#include "mac.h"
+#include "board.h"
+
+
+static volatile uint32_t timer_h = 0; /* 2^(16+32) / 8 MHz = ~1.1 years */
+
+
+void reset_cpu(void)
+{
+ WDTCSR = 1 << WDE;
+}
+
+
+uint8_t read_irq(void)
+{
+ return PIN(IRQ_RF);
+}
+
+
+void slp_tr(void)
+{
+ SET(SLP_TR);
+ CLR(SLP_TR);
+}
+
+
+ISR(TIMER1_OVF_vect)
+{
+ timer_h++;
+}
+
+
+uint64_t timer_read(void)
+{
+ uint32_t high;
+ uint8_t low, mid;
+
+ do {
+ if (TIFR1 & (1 << TOV1)) {
+ TIFR1 = 1 << TOV1;
+ timer_h++;
+ }
+ high = timer_h;
+ low = TCNT1L;
+ mid = TCNT1H;
+ }
+ while (TIFR1 & (1 << TOV1));
+
+ /*
+ * We need all these casts because the intermediate results are handled
+ * as if they were signed and thus get sign-expanded. Sounds wrong-ish.
+ */
+ return (uint64_t) high << 16 | (uint64_t) mid << 8 | (uint64_t) low;
+}
+
+
+void timer_init(void)
+{
+ /* configure timer 1 as a free-running CLK counter */
+
+ TCCR1A = 0;
+ TCCR1B = 1 << CS10;
+
+ /* enable timer overflow interrupt */
+
+ TIMSK1 = 1 << TOIE1;
+}
+
+
+bool gpio(uint8_t port, uint8_t data, uint8_t dir, uint8_t mask, uint8_t *res)
+{
+ EIMSK = 0; /* recover INT_RF to ATUSB_GPIO_CLEANUP or an MCU reset */
+
+ switch (port) {
+ case 1:
+ DDRB = (DDRB & ~mask) | dir;
+ PORTB = (PORTB & ~mask) | data;
+ break;
+ case 2:
+ DDRC = (DDRC & ~mask) | dir;
+ PORTC = (PORTC & ~mask) | data;
+ break;
+ case 3:
+ DDRD = (DDRD & ~mask) | dir;
+ PORTD = (PORTD & ~mask) | data;
+ break;
+ default:
+ return 0;
+ }
+
+ /* disable the UART so that we can meddle with these pins as well. */
+ spi_off();
+ _delay_ms(1);
+
+ switch (port) {
+ case 1:
+ res[0] = PINB;
+ res[1] = PORTB;
+ res[2] = DDRB;
+ break;
+ case 2:
+ res[0] = PINC;
+ res[1] = PORTC;
+ res[2] = DDRC;
+ break;
+ case 3:
+ res[0] = PIND;
+ res[1] = PORTD;
+ res[2] = DDRD;
+ break;
+ }
+
+ return 1;
+}
+
+
+void gpio_cleanup(void)
+{
+ EIMSK = 1 << 0;
+}
+
+
+static void done(void *user)
+{
+ led(0);
+}
+
+
+uint8_t irq_serial;
+
+#if defined(ATUSB) || defined(HULUSB)
+ISR(INT0_vect)
+#endif
+#ifdef RZUSB
+ISR(TIMER1_CAPT_vect)
+#endif
+{
+ if (mac_irq) {
+ if (mac_irq())
+ return;
+ }
+ if (eps[1].state == EP_IDLE) {
+ led(1);
+ irq_serial = (irq_serial+1) | 0x80;
+ usb_send(&eps[1], &irq_serial, 1, done, NULL);
+ }
+}
--- /dev/null
+/*
+ * fw/board_atusb.c - ATUSB Board-specific functions (for boot loader and application)
+ *
+ * Written 2016 by Stefan Schmidt
+ * Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/boot.h>
+
+#define F_CPU 8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "at86rf230.h"
+#include "board.h"
+#include "spi.h"
+#include "usb/usb.h"
+
+static bool spi_initialized = 0;
+
+void reset_rf(void)
+{
+ /* set up all the outputs; default port value is 0 */
+
+ DDRB = 0;
+ DDRC = 0;
+ DDRD = 0;
+ PORTB = 0;
+ PORTC = 0;
+ PORTD = 0;
+
+ OUT(LED);
+ OUT(nRST_RF); /* this also resets the transceiver */
+ OUT(SLP_TR);
+
+ spi_init();
+
+ /* AT86RF231 data sheet, 12.4.13, reset pulse width: 625 ns (min) */
+
+ CLR(nRST_RF);
+ _delay_us(2);
+ SET(nRST_RF);
+
+ /* 12.4.14: SPI access latency after reset: 625 ns (min) */
+
+ _delay_us(2);
+
+ /* we must restore TRX_CTRL_0 after each reset (9.6.4) */
+
+ set_clkm();
+}
+
+void led(bool on)
+{
+ if (on)
+ SET(LED);
+ else
+ CLR(LED);
+}
+
+void set_clkm(void)
+{
+ /* switch CLKM to 8 MHz */
+
+ /*
+ * @@@ Note: Atmel advise against changing the external clock in
+ * mid-flight. We should therefore switch to the RC clock first, then
+ * crank up the external clock, and finally switch back to the external
+ * clock. The clock switching procedure is described in the ATmega32U2
+ * data sheet in secton 8.2.2.
+ */
+ spi_begin();
+ spi_send(AT86RF230_REG_WRITE | REG_TRX_CTRL_0);
+ spi_send(CLKM_CTRL_8MHz);
+ spi_end();
+}
+
+void board_init(void)
+{
+ /* Disable the watchdog timer */
+
+ MCUSR = 0; /* Remove override */
+ WDTCSR |= 1 << WDCE; /* Enable change */
+ WDTCSR = 1 << WDCE; /* Disable watchdog while still enabling
+ change */
+
+ CLKPR = 1 << CLKPCE;
+ /* We start with a 1 MHz/8 clock. Disable the prescaler. */
+ CLKPR = 0;
+
+ get_sernum();
+}
+
+void spi_begin(void)
+{
+ if (!spi_initialized)
+ spi_init();
+ CLR(nSS);
+}
+
+void spi_off(void)
+{
+ spi_initialized = 0;
+ UCSR1B = 0;
+}
+
+void spi_init(void)
+{
+ SET(nSS);
+ OUT(SCLK);
+ OUT(MOSI);
+ OUT(nSS);
+ IN(MISO);
+
+ UBRR1 = 0; /* set bit rate to zero to begin */
+ UCSR1C = 1 << UMSEL11 | 1 << UMSEL10;
+ /* set MSPI, MSB first, SPI data mode 0 */
+ UCSR1B = 1 << RXEN1 | 1 << TXEN1;
+ /* enable receiver and transmitter */
+ UBRR1 = 0; /* reconfirm the bit rate */
+
+ spi_initialized = 1;
+}
+
+void usb_init(void)
+{
+ USBCON |= 1 << FRZCLK; /* freeze the clock */
+
+ /* enable the PLL and wait for it to lock */
+ PLLCSR &= ~(1 << PLLP2 | 1 << PLLP1 | 1 << PLLP0);
+ PLLCSR |= 1 << PLLE;
+ while (!(PLLCSR & (1 << PLOCK)));
+
+ USBCON &= ~(1 << USBE); /* reset the controller */
+ USBCON |= 1 << USBE;
+
+ USBCON &= ~(1 << FRZCLK); /* thaw the clock */
+
+ UDCON &= ~(1 << DETACH); /* attach the pull-up */
+ UDIEN = 1 << EORSTE; /* enable device interrupts */
+// UDCON |= 1 << RSTCPU; /* reset CPU on bus reset */
+
+ ep_init();
+}
+
+void board_app_init(void)
+{
+ /* enable INT0, trigger on rising edge */
+ EICRA = 1 << ISC01 | 1 << ISC00;
+ EIMSK = 1 << 0;
+}
--- /dev/null
+/*
+ * fw/board_atusb.h - ATUSB Board-specific functions and definitions
+ *
+ * Written 2016 by Stefan Schmidt
+ * Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef BOARD_ATUSB_H
+#define BOARD_ATUSB_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define LED_PORT B
+#define LED_BIT 6
+#define nRST_RF_PORT C
+#define nRST_RF_BIT 7
+#define SLP_TR_PORT B
+#define SLP_TR_BIT 4
+
+#define SCLK_PORT D
+#define SCLK_BIT 5
+#define MOSI_PORT D
+#define MOSI_BIT 3
+
+#define MISO_PORT D
+#define MISO_BIT 2
+#define nSS_PORT D
+#define nSS_BIT 1
+#define IRQ_RF_PORT D
+#define IRQ_RF_BIT 0
+
+#define SPI_WAIT_DONE() while (!(UCSR1A & 1 << RXC1))
+#define SPI_DATA UDR1
+
+void set_clkm(void);
+void board_init(void);
+
+void spi_begin(void);
+void spi_off(void);
+void spi_init(void);
+
+#endif /* !BOARD_H */
--- /dev/null
+/*
+ * fw/board_hulusb.c - Busware HUL Board-specific functions (for boot loader and application)
+ *
+ * Written 2017 by Filzmaier Josef
+ * Based on fw/board_rzusb written and Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/boot.h>
+
+#define F_CPU 8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "at86rf230.h"
+#include "board.h"
+#include "spi.h"
+#include "usb/usb.h"
+
+static bool spi_initialized = 0;
+
+void reset_rf(void)
+{
+ /* set up all the outputs; default port value is 0 */
+
+ DDRB = 0;
+ DDRC = 0;
+ DDRD = 0;
+ PORTB = 0;
+ PORTC = 0;
+ PORTD = 0;
+
+ OUT(LED_RED);
+ OUT(LED_GREEN);
+ SET(LED_RED); /* Leds are active low on HULUSB board */
+ CLR(LED_GREEN); /* Green Led indicates the dongle is running */
+ OUT(nRST_RF); /* this also resets the transceiver */
+ OUT(SLP_TR);
+
+ spi_init();
+
+ /* AT86RF212 data sheet, Appendix B, p166 Power-On Reset procedure */
+ /*-----------------------------------------------------------------*/
+ CLR(SLP_TR);
+ SET(nRST_RF);
+ SET(nSS);
+ _delay_us(400);
+
+ CLR(nRST_RF);
+ _delay_us(2);
+ SET(nRST_RF);
+
+ /* 5.1.4.5: Wait t10: 625 ns (min) */
+
+ _delay_us(2);
+
+ reg_write(REG_TRX_CTRL_0, 0x19);
+
+ change_state(TRX_CMD_FORCE_TRX_OFF);
+ /*-----------------------------------------------------------------*/
+
+ /* we must restore TRX_CTRL_0 after each reset (7.7.4) */
+
+ set_clkm();
+}
+
+void led_red(bool on) {
+ if (on)
+ CLR(LED_RED);
+ else
+ SET(LED_RED);
+}
+
+void led_green(bool on) {
+ if (on)
+ CLR(LED_GREEN);
+ else
+ SET(LED_GREEN);
+}
+
+void led(bool on)
+{
+ led_red(on);
+}
+
+void set_clkm(void)
+{
+ /* CLKM is not connected on BUSWARE HUL and therefore it is running in
+ * async mode. */
+ reg_write(REG_TRX_CTRL_0, 0x00);
+
+ /* TX_AUTO_CRC_ON, default disabled */
+ subreg_write(SR_TX_AUTO_CRC_ON, 1);
+}
+
+void board_init(void)
+{
+ /* Disable the watchdog timer */
+
+ MCUSR = 0; /* Remove override */
+ WDTCSR |= 1 << WDCE; /* Enable change */
+ WDTCSR = 1 << WDCE; /* Disable watchdog while still enabling
+ change */
+
+ CLKPR = 1 << CLKPCE;
+ /* We start with a 16 MHz/8 clock. Put the prescaler to 2. */
+ CLKPR = 1 << CLKPS0;
+
+ get_sernum();
+}
+
+void spi_begin(void)
+{
+ if (!spi_initialized)
+ spi_init();
+ CLR(nSS);
+}
+
+void spi_off(void)
+{
+ spi_initialized = 0;
+ SPCR &= ~(1 << SPE);
+}
+
+void spi_init(void)
+{
+ SET(nSS);
+ OUT(SCLK);
+ OUT(MOSI);
+ OUT(nSS);
+ IN(MISO);
+
+ SPCR = (1 << SPE) | (1 << MSTR);
+ SPSR = (1 << SPI2X);
+
+ spi_initialized = 1;
+}
+
+void usb_init(void)
+{
+ USBCON |= 1 << FRZCLK; /* freeze the clock */
+
+ /* enable the PLL and wait for it to lock */
+ /* TODO sheet page 50 For Atmel AT90USB128x only. Do not use with Atmel AT90USB64x. */
+ /* FOR 8 XTAL Mhz only!!! */
+ PLLCSR = ((1 << PLLP1) | (1 << PLLP0));
+ PLLCSR |= 1 << PLLE;
+ while (!(PLLCSR & (1 << PLOCK)));
+
+ UHWCON |= (1 << UVREGE);
+
+ USBCON &= ~((1 << USBE) | (1 << OTGPADE)); /* reset the controller */
+ USBCON |= ((1 << USBE) | (1 << OTGPADE));
+
+ USBCON &= ~(1 << FRZCLK); /* thaw the clock */
+
+ UDCON &= ~(1 << DETACH); /* attach the pull-up */
+ UDIEN = 1 << EORSTE; /* enable device interrupts */
+ // UDCON |= 1 << RSTCPU; /* reset CPU on bus reset */
+
+ ep_init();
+}
+
+void board_app_init(void)
+{
+ /* enable INT0, trigger on rising edge */
+ EICRA = 1 << ISC01 | 1 << ISC00;
+ EIMSK = 1 << INT0;
+}
--- /dev/null
+/*
+ * fw/board_hulusb.h - Busware HUL Board-specific functions (for boot loader and application)
+ *
+ * Written 2017 by Filzmaier Josef
+ * Based on fw/board_rzusb written and Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef BOARD_HULUSB_H
+#define BOARD_HULUSB_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define LED_RED_PORT A
+#define LED_GREEN_PORT A
+#define LED_RED_BIT 3
+#define LED_GREEN_BIT 4
+#define LED_PORT LED_RED_PORT
+#define LED_BIT LED_RED_BIT
+
+#define nRST_RF_PORT B
+#define nRST_RF_BIT 5
+#define SLP_TR_PORT B
+#define SLP_TR_BIT 4
+
+#define SCLK_PORT B
+#define SCLK_BIT 1
+#define MOSI_PORT B
+#define MOSI_BIT 2
+
+#define MISO_PORT B
+#define MISO_BIT 3
+#define nSS_PORT B
+#define nSS_BIT 0
+#define IRQ_RF_PORT D
+#define IRQ_RF_BIT 4
+
+#define SR_TX_AUTO_CRC_ON 0x04, 0x20, 5
+#define SR_CHANNEL 0x08, 0x1f, 0
+
+#define RG_CC_CTRL_1 (0x14)
+
+#define SPI_WAIT_DONE() while ((SPSR & (1 << SPIF)) == 0)
+#define SPI_DATA SPDR
+
+void set_clkm(void);
+void board_init(void);
+
+void led_red(bool on);
+void led_green(bool on);
+
+void spi_begin(void);
+void spi_off(void);
+void spi_init(void);
+
+#ifdef DEBUG
+void printStatus(void);
+#define PRINT_STATUS() printStatus()
+#endif
+
+#endif /* !BOARD_HULUSB_H */
--- /dev/null
+/*
+ * fw/board_rzusb.c - RZUSB Board-specific functions (for boot loader and application)
+ *
+ * Written 2016 by Stefan Schmidt
+ * Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/boot.h>
+
+#define F_CPU 8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "at86rf230.h"
+#include "board.h"
+#include "spi.h"
+#include "usb/usb.h"
+
+static bool spi_initialized = 0;
+
+void reset_rf(void)
+{
+ /* set up all the outputs; default port value is 0 */
+
+ DDRB = 0;
+ DDRC = 0;
+ DDRD = 0;
+ PORTB = 0;
+ PORTC = 0;
+ PORTD = 0;
+
+ OUT(LED);
+ OUT(nRST_RF); /* this also resets the transceiver */
+ OUT(SLP_TR);
+
+ spi_init();
+
+ /* AT86RF231 data sheet, 12.4.13, reset pulse width: 625 ns (min) */
+
+ CLR(nRST_RF);
+ _delay_us(2);
+ SET(nRST_RF);
+
+ /* 12.4.14: SPI access latency after reset: 625 ns (min) */
+
+ _delay_us(2);
+
+ /* we must restore TRX_CTRL_0 after each reset (9.6.4) */
+
+ set_clkm();
+}
+
+void led(bool on)
+{
+ if (on)
+ SET(LED);
+ else
+ CLR(LED);
+}
+
+void set_clkm(void)
+{
+ /* switch CLKM to 8 MHz */
+
+ /*
+ * @@@ Note: Atmel advise against changing the external clock in
+ * mid-flight. We should therefore switch to the RC clock first, then
+ * crank up the external clock, and finally switch back to the external
+ * clock. The clock switching procedure is described in the ATmega32U2
+ * data sheet in secton 8.2.2.
+ */
+ spi_begin();
+ spi_send(AT86RF230_REG_WRITE | REG_TRX_CTRL_0);
+ spi_send(0x10);
+ spi_end();
+
+ /* TX_AUTO_CRC_ON, default disabled */
+ spi_begin();
+ spi_send(AT86RF230_REG_WRITE | 0x05);
+ spi_send(0x80);
+ spi_end();
+}
+
+void board_init(void)
+{
+ /* Disable the watchdog timer */
+
+ MCUSR = 0; /* Remove override */
+ WDTCSR |= 1 << WDCE; /* Enable change */
+ WDTCSR = 1 << WDCE; /* Disable watchdog while still enabling
+ change */
+
+ CLKPR = 1 << CLKPCE;
+ /* We start with a 16 MHz/8 clock. Put the prescaler to 2. */
+ CLKPR = 1 << CLKPS0;
+
+ get_sernum();
+}
+
+void spi_begin(void)
+{
+ if (!spi_initialized)
+ spi_init();
+ CLR(nSS);
+}
+
+void spi_off(void)
+{
+ spi_initialized = 0;
+ SPCR &= ~(1 << SPE);
+}
+
+void spi_init(void)
+{
+ SET(nSS);
+ OUT(SCLK);
+ OUT(MOSI);
+ OUT(nSS);
+ IN(MISO);
+
+ SPCR = (1 << SPE) | (1 << MSTR);
+ SPSR = (1 << SPI2X);
+
+ spi_initialized = 1;
+}
+
+void usb_init(void)
+{
+ USBCON |= 1 << FRZCLK; /* freeze the clock */
+
+ /* enable the PLL and wait for it to lock */
+ /* TODO sheet page 50 For Atmel AT90USB128x only. Do not use with Atmel AT90USB64x. */
+ /* FOR 8 XTAL Mhz only!!! */
+ PLLCSR = ((1 << PLLP1) | (1 << PLLP0));
+ PLLCSR |= 1 << PLLE;
+ while (!(PLLCSR & (1 << PLOCK)));
+
+ UHWCON |= (1 << UVREGE);
+
+ USBCON &= ~((1 << USBE) | (1 << OTGPADE)); /* reset the controller */
+ USBCON |= ((1 << USBE) | (1 << OTGPADE));
+
+ USBCON &= ~(1 << FRZCLK); /* thaw the clock */
+
+ UDCON &= ~(1 << DETACH); /* attach the pull-up */
+ UDIEN = 1 << EORSTE; /* enable device interrupts */
+// UDCON |= 1 << RSTCPU; /* reset CPU on bus reset */
+
+ ep_init();
+}
+
+void board_app_init(void)
+{
+ /* enable timer input capture 1, trigger on rising edge */
+ TCCR1B = (1 << ICES1);
+ TIFR1 = (1 << ICF1);
+ TIMSK1 = (1 << ICIE1);
+}
--- /dev/null
+/*
+ * fw/board_rzusb.h - RZUSB Board-specific functions and definitions
+ *
+ * Written 2016 by Stefan Schmidt
+ * Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef BOARD_RZUSB_H
+#define BOARD_RZUSB_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define LED_PORT D
+#define LED_BIT 7
+#define nRST_RF_PORT B
+#define nRST_RF_BIT 5
+#define SLP_TR_PORT B
+#define SLP_TR_BIT 4
+
+#define SCLK_PORT B
+#define SCLK_BIT 1
+#define MOSI_PORT B
+#define MOSI_BIT 2
+
+#define MISO_PORT B
+#define MISO_BIT 3
+#define nSS_PORT B
+#define nSS_BIT 0
+#define IRQ_RF_PORT D
+#define IRQ_RF_BIT 4
+
+#define SPI_WAIT_DONE() while ((SPSR & (1 << SPIF)) == 0)
+#define SPI_DATA SPDR
+
+void set_clkm(void);
+void board_init(void);
+
+void spi_begin(void);
+void spi_off(void);
+void spi_init(void);
+
+#endif /* !BOARD_H */
--- /dev/null
+/*
+ * fw/boot.c - DFU boot loader for ATUSB
+ *
+ * Written 2008-2011 by Werner Almesberger
+ * Copyright 2008-2011 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+
+#define F_CPU 8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "dfu.h"
+
+#include "board.h"
+#include "spi.h"
+#include "atusb/ep0.h"
+
+
+#define MS_TO_LOOPS(ms) ((uint32_t) (ms)*335)
+
+
+static void (*run_payload)(void) = 0;
+
+
+int main(void)
+{
+ /*
+ * pgm_read_byte gets cached and there doesn't seem to be any other
+ * way to dissuade gcc from doing this.
+ */
+ volatile int zero = 0;
+ uint32_t loop = 0;
+
+ board_init();
+ reset_rf();
+
+ /* now we should be at 8 MHz */
+
+ usb_init();
+ dfu_init();
+
+ /* move interrupt vectors to the boot loader */
+ MCUCR = 1 << IVCE;
+ MCUCR = 1 << IVSEL;
+
+ sei();
+
+ led(1);
+
+ while (loop != MS_TO_LOOPS(2500)) {
+ if (dfu.state == dfuIDLE && pgm_read_byte(zero) != 0xff)
+ loop++;
+ else
+ loop = 0;
+ }
+
+ led(0);
+
+ cli();
+
+ usb_reset();
+ run_payload();
+
+ while (1); /* not reached */
+}
--- /dev/null
+/*
+ * 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)
+};
--- /dev/null
+/*
+ * fw/ep0.c - EP0 extension protocol
+ *
+ * Written 2008-2011, 2013 by Werner Almesberger
+ * Copyright 2008-2011, 2013 Werner Almesberger
+ * Copyright 2015-2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <avr/io.h>
+#include <avr/eeprom.h>
+
+#define F_CPU 8000000UL
+#include <util/delay.h>
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#include "usb.h"
+#include "dfu.h"
+
+#include "at86rf230.h"
+#include "atusb/ep0.h"
+#include "version.h"
+#include "board.h"
+#include "sernum.h"
+#include "spi.h"
+#include "mac.h"
+
+#ifdef ATUSB
+#define HW_TYPE ATUSB_HW_TYPE_110131
+#endif
+
+#ifdef RZUSB
+#define HW_TYPE ATUSB_HW_TYPE_RZUSB
+#endif
+
+#ifdef HULUSB
+#define HW_TYPE ATUSB_HW_TYPE_HULUSB
+#endif
+
+#ifdef DEBUG
+#include "uart.h"
+#include <stdio.h>
+#define debug(FORMAT,args...) printf(FORMAT,##args)
+#define error(FORMAT,args...) printf(FORMAT,##args)
+#else
+#define debug(...)
+#define error(...)
+#endif
+
+
+static const uint8_t id[] = { EP0ATUSB_MAJOR, EP0ATUSB_MINOR, HW_TYPE };
+static uint8_t buf[MAX_PSDU+3]; /* command, PHDR, and LQI */
+static uint8_t size;
+
+
+static void do_eeprom_write(void *user)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ eeprom_update_byte((uint8_t*)i, buf[i]);
+}
+
+static void do_buf_write(void *user)
+{
+ uint8_t i;
+
+ spi_begin();
+ for (i = 0; i != size; i++)
+ spi_send(buf[i]);
+ spi_end();
+}
+
+
+#define BUILD_OFFSET 7 /* '#' plus "65535" plus ' ' */
+
+
+static bool my_setup(const struct setup_request *setup)
+{
+ uint16_t req = setup->bmRequestType | setup->bRequest << 8;
+ unsigned tmp;
+ uint8_t i;
+ uint64_t tmp64;
+
+ switch (req) {
+ case ATUSB_FROM_DEV(ATUSB_ID):
+ debug("ATUSB_ID\n");
+ if (setup->wLength > 3)
+ return 0;
+ usb_send(&eps[0], id, setup->wLength, NULL, NULL);
+ return 1;
+ case ATUSB_FROM_DEV(ATUSB_BUILD):
+ debug("ATUSB_BUILD\n");
+ tmp = build_number;
+ for (i = BUILD_OFFSET-2; tmp; i--) {
+ buf[i] = (tmp % 10)+'0';
+ tmp /= 10;
+ }
+ buf[i] = '#';
+ buf[BUILD_OFFSET-1] = ' ';
+ for (size = 0; build_date[size]; size++)
+ buf[BUILD_OFFSET+size] = build_date[size];
+ size += BUILD_OFFSET-i;
+ if (size > setup->wLength)
+ return 0;
+ usb_send(&eps[0], buf+i, size, NULL, NULL);
+ return 1;
+
+ case ATUSB_TO_DEV(ATUSB_RESET):
+ debug("ATUSB_RESET\n");
+ reset_cpu();
+ while (1);
+
+ case ATUSB_TO_DEV(ATUSB_RF_RESET):
+ debug("ATUSB_RF_RESET\n");
+ reset_rf();
+ mac_reset();
+ //ep_send_zlp(EP_CTRL);
+ return 1;
+
+ case ATUSB_FROM_DEV(ATUSB_POLL_INT):
+ debug("ATUSB_POLL_INT\n");
+ if (setup->wLength < 1)
+ return 0;
+ *buf = read_irq();
+ usb_send(&eps[0], buf, 1, NULL, NULL);
+ return 1;
+
+ case ATUSB_FROM_DEV(ATUSB_TIMER):
+ debug("ATUSB_TIMER\n");
+ size = setup->wLength;
+ if (size > sizeof(tmp64))
+ size = sizeof(tmp64);
+ tmp64 = timer_read();
+ memcpy(buf, &tmp64, sizeof(tmp64));
+ usb_send(&eps[0], buf, size, NULL, NULL);
+ return 1;
+
+ case ATUSB_FROM_DEV(ATUSB_GPIO):
+ debug("ATUSB_GPIO\n");
+ if (setup->wLength < 3)
+ return 0;
+ if (!gpio(setup->wIndex, setup->wValue, setup->wValue >> 8,
+ setup->wIndex >> 8, buf))
+ return 0;
+ usb_send(&eps[0], buf, 3, NULL, NULL);
+ return 1;
+ case ATUSB_TO_DEV(ATUSB_GPIO_CLEANUP):
+ gpio_cleanup();
+ return 1;
+
+ case ATUSB_TO_DEV(ATUSB_SLP_TR):
+ debug("ATUSB_SLP_TR\n");
+ slp_tr();
+ return 1;
+
+ case ATUSB_TO_DEV(ATUSB_REG_WRITE):
+ debug("ATUSB_REG_WRITE\n");
+ spi_begin();
+ spi_send(AT86RF230_REG_WRITE | setup->wIndex);
+ spi_send(setup->wValue);
+ spi_end();
+ //ep_send_zlp(EP_CTRL);
+ return 1;
+ case ATUSB_FROM_DEV(ATUSB_REG_READ):
+ debug("ATUSB_REG_READ\n");
+ spi_begin();
+ spi_send(AT86RF230_REG_READ | setup->wIndex);
+ *buf = spi_recv();
+ spi_end();
+ usb_send(&eps[0], buf, 1, NULL, NULL);
+ return 1;
+
+ case ATUSB_TO_DEV(ATUSB_BUF_WRITE):
+ debug("ATUSB_BUF_WRITE\n");
+ if (setup->wLength < 1)
+ return 0;
+ if (setup->wLength > MAX_PSDU)
+ return 0;
+ buf[0] = AT86RF230_BUF_WRITE;
+ buf[1] = setup->wLength;
+ size = setup->wLength+2;
+ usb_recv(&eps[0], buf+2, setup->wLength, do_buf_write, NULL);
+ return 1;
+ case ATUSB_FROM_DEV(ATUSB_BUF_READ):
+ debug("ATUSB_BUF_READ\n");
+ if (setup->wLength < 2) /* PHR+LQI */
+ return 0;
+ if (setup->wLength > MAX_PSDU+2) /* PHR+PSDU+LQI */
+ return 0;
+ spi_begin();
+ spi_send(AT86RF230_BUF_READ);
+ size = spi_recv();
+ if (size >= setup->wLength)
+ size = setup->wLength-1;
+ for (i = 0; i != size+1; i++)
+ buf[i] = spi_recv();
+ spi_end();
+ usb_send(&eps[0], buf, size+1, NULL, NULL);
+ return 1;
+
+ case ATUSB_TO_DEV(ATUSB_SRAM_WRITE):
+ debug("ATUSB_SRAM_WRITE\n");
+ if (setup->wIndex > SRAM_SIZE)
+ return 0;
+ if (setup->wIndex+setup->wLength > SRAM_SIZE)
+ return 0;
+ buf[0] = AT86RF230_SRAM_WRITE;
+ buf[1] = setup->wIndex;
+ size = setup->wLength+2;
+ usb_recv(&eps[0], buf+2, setup->wLength, do_buf_write, NULL);
+ return 1;
+ case ATUSB_FROM_DEV(ATUSB_SRAM_READ):
+ debug("ATUSB_SRAM_READ\n");
+ if (setup->wIndex > SRAM_SIZE)
+ return 0;
+ if (setup->wIndex+setup->wLength > SRAM_SIZE)
+ return 0;
+ spi_begin();
+ spi_send(AT86RF230_SRAM_READ);
+ spi_send(setup->wIndex);
+ for (i = 0; i != setup->wLength; i++)
+ buf[i] = spi_recv();
+ spi_end();
+ usb_send(&eps[0], buf, setup->wLength, NULL, NULL);
+ return 1;
+
+ case ATUSB_TO_DEV(ATUSB_SPI_WRITE):
+ size = setup->wLength+2;
+ if (size > sizeof(buf))
+ return 0;
+ buf[0] = setup->wValue;
+ buf[1] = setup->wIndex;
+ if (setup->wLength)
+ usb_recv(&eps[0], buf+2, setup->wLength,
+ do_buf_write, NULL);
+ else
+ do_buf_write(NULL);
+ return 1;
+ case ATUSB_FROM_DEV(ATUSB_SPI_WRITE2_SYNC):
+ spi_begin();
+ spi_send(setup->wValue);
+ spi_send(setup->wIndex);
+ spi_end();
+ buf[0] = irq_serial;
+ if (setup->wLength)
+ usb_send(&eps[0], buf, 1, NULL, NULL);
+ return 1;
+
+ case ATUSB_FROM_DEV(ATUSB_SPI_READ1):
+ case ATUSB_FROM_DEV(ATUSB_SPI_READ2):
+ spi_begin();
+ spi_send(setup->wValue);
+ if (req == ATUSB_FROM_DEV(ATUSB_SPI_READ2))
+ spi_send(setup->wIndex);
+ for (i = 0; i != setup->wLength; i++)
+ buf[i] = spi_recv();
+ spi_end();
+ usb_send(&eps[0], buf, setup->wLength, NULL, NULL);
+ return 1;
+
+ case ATUSB_TO_DEV(ATUSB_RX_MODE):
+ return mac_rx(setup->wValue);
+ case ATUSB_TO_DEV(ATUSB_TX):
+ return mac_tx(setup->wValue, setup->wIndex, setup->wLength);
+ case ATUSB_TO_DEV(ATUSB_EUI64_WRITE):
+ debug("ATUSB_EUI64_WRITE\n");
+ usb_recv(&eps[0], buf, setup->wLength, do_eeprom_write, NULL);
+ _delay_ms(100);
+ reset_cpu();
+ return 1;
+
+ case ATUSB_FROM_DEV(ATUSB_EUI64_READ):
+ debug("ATUSB_EUI64_READ\n");
+ eeprom_read_block(buf, (const void*)0, 8);
+ usb_send(&eps[0], buf, 8, NULL, NULL);
+ return 1;
+
+ default:
+ error("Unrecognized SETUP: 0x%02x 0x%02x ...\n",
+ setup->bmRequestType, setup->bRequest);
+ return 0;
+ }
+}
+
+
+static bool my_dfu_setup(const struct setup_request *setup)
+{
+ switch (setup->bmRequestType | setup->bRequest << 8) {
+ case DFU_TO_DEV(DFU_DETACH):
+ /* @@@ should use wTimeout */
+ dfu.state = appDETACH;
+ return 1;
+ default:
+ return dfu_setup_common(setup);
+ }
+}
+
+
+static void my_set_interface(int nth)
+{
+ if (nth) {
+ user_setup = my_dfu_setup;
+ user_get_descriptor = dfu_my_descr;
+ dfu.state = appIDLE;
+ } else {
+ user_setup = my_setup;
+ user_get_descriptor = sernum_get_descr;
+ }
+}
+
+
+static void my_reset(void)
+{
+ if (dfu.state == appDETACH)
+ reset_cpu();
+}
+
+
+void ep0_init(void)
+{
+ user_setup = my_setup;
+ user_set_interface = my_set_interface;
+ my_set_interface(0);
+ user_reset = my_reset;
+}
--- /dev/null
+/*
+ * fw/flash.c - Board-specific flash functions
+ *
+ * Written 2011, 2013-2015 by Werner Almesberger
+ * Copyright 2011, 2013-2015 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/boot.h>
+#include <avr/pgmspace.h>
+
+#include "dfu.h"
+#include "board.h"
+
+
+static uint32_t payload;
+
+
+static void flash_start(void)
+{
+ payload = 0;
+}
+
+
+static bool flash_can_write(uint16_t size)
+{
+ return payload+size <= BOOT_ADDR;
+}
+
+
+static void flash_write(const uint8_t *buf, uint16_t size)
+{
+ static uint8_t last;
+ const uint8_t *p;
+
+ for (p = buf; p != buf+size; p++) {
+ if (!(payload & (SPM_PAGESIZE-1))) {
+ boot_page_erase(payload);
+ boot_spm_busy_wait();
+ }
+
+ if (payload & 1)
+ boot_page_fill(payload, last | (*p << 8));
+ else
+ last = *p;
+ payload++;
+
+ if (!(payload & (SPM_PAGESIZE-1))) {
+ boot_page_write(payload-SPM_PAGESIZE);
+ boot_spm_busy_wait();
+ }
+ }
+}
+
+
+static void flash_end_write(void)
+{
+ if (payload & (SPM_PAGESIZE-1)) {
+ boot_page_write(payload & ~(SPM_PAGESIZE-1));
+ boot_spm_busy_wait();
+ }
+ boot_rww_enable();
+}
+
+
+static uint16_t flash_read(uint8_t *buf, uint16_t size)
+{
+ uint16_t got = 0;
+
+ while (size && payload != (uint32_t) FLASHEND+1) {
+ *buf++ = pgm_read_byte(payload);
+ payload++;
+ size--;
+ got++;
+ }
+ return got;
+}
+
+
+static const struct dfu_flash_ops flash_ops = {
+ .start = flash_start,
+ .can_write = flash_can_write,
+ .write = flash_write,
+ .end_write = flash_end_write,
+ .read = flash_read,
+};
+
+
+const struct dfu_flash_ops *dfu_flash_ops = &flash_ops;
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * atusb.h - Definitions shared between kernel and ATUSB firmware
+ *
+ * Written 2013 by Werner Almesberger <werner@almesberger.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2, or
+ * (at your option) any later version.
+ *
+ * This file should be identical for kernel and firmware.
+ * Kernel: drivers/net/ieee802154/atusb.h
+ * Firmware: ben-wpan/atusb/fw/include/atusb/atusb.h
+ */
+
+#ifndef _ATUSB_H
+#define _ATUSB_H
+
+#define ATUSB_VENDOR_ID 0x20b7 /* Qi Hardware*/
+#define ATUSB_PRODUCT_ID 0x1540 /* 802.15.4, device 0 */
+ /* -- - - */
+
+#define ATUSB_BUILD_SIZE 256 /* maximum build version/date message length */
+
+/* Commands to our device. Make sure this is synced with the firmware */
+enum atusb_requests {
+ ATUSB_ID = 0x00, /* system status/control grp */
+ ATUSB_BUILD,
+ ATUSB_RESET,
+ ATUSB_RF_RESET = 0x10, /* debug/test group */
+ ATUSB_POLL_INT,
+ ATUSB_TEST, /* atusb-sil only */
+ ATUSB_TIMER,
+ ATUSB_GPIO,
+ ATUSB_SLP_TR,
+ ATUSB_GPIO_CLEANUP,
+ ATUSB_REG_WRITE = 0x20, /* transceiver group */
+ ATUSB_REG_READ,
+ ATUSB_BUF_WRITE,
+ ATUSB_BUF_READ,
+ ATUSB_SRAM_WRITE,
+ ATUSB_SRAM_READ,
+ ATUSB_SPI_WRITE = 0x30, /* SPI group */
+ ATUSB_SPI_READ1,
+ ATUSB_SPI_READ2,
+ ATUSB_SPI_WRITE2_SYNC,
+ ATUSB_RX_MODE = 0x40, /* HardMAC group */
+ ATUSB_TX,
+ ATUSB_EUI64_WRITE = 0x50, /* Parameter in EEPROM grp */
+ ATUSB_EUI64_READ,
+};
+
+enum {
+ ATUSB_HW_TYPE_100813, /* 2010-08-13 */
+ ATUSB_HW_TYPE_101216, /* 2010-12-16 */
+ ATUSB_HW_TYPE_110131, /* 2011-01-31, ATmega32U2-based */
+ ATUSB_HW_TYPE_RZUSB, /* Atmel Raven USB dongle with at86rf230 */
+ ATUSB_HW_TYPE_HULUSB, /* Busware HUL USB dongle with at86rf212 */
+};
+
+/*
+ * Direction bRequest wValue wIndex wLength
+ *
+ * ->host ATUSB_ID - - 3
+ * ->host ATUSB_BUILD - - #bytes
+ * host-> ATUSB_RESET - - 0
+ *
+ * host-> ATUSB_RF_RESET - - 0
+ * ->host ATUSB_POLL_INT - - 1
+ * host-> ATUSB_TEST - - 0
+ * ->host ATUSB_TIMER - - #bytes (6)
+ * ->host ATUSB_GPIO dir+data mask+p# 3
+ * host-> ATUSB_SLP_TR - - 0
+ * host-> ATUSB_GPIO_CLEANUP - - 0
+ *
+ * host-> ATUSB_REG_WRITE value addr 0
+ * ->host ATUSB_REG_READ - addr 1
+ * host-> ATUSB_BUF_WRITE - - #bytes
+ * ->host ATUSB_BUF_READ - - #bytes
+ * host-> ATUSB_SRAM_WRITE - addr #bytes
+ * ->host ATUSB_SRAM_READ - addr #bytes
+ *
+ * host-> ATUSB_SPI_WRITE byte0 byte1 #bytes
+ * ->host ATUSB_SPI_READ1 byte0 - #bytes
+ * ->host ATUSB_SPI_READ2 byte0 byte1 #bytes
+ * ->host ATUSB_SPI_WRITE2_SYNC byte0 byte1 0/1
+ *
+ * host-> ATUSB_RX_MODE on - 0
+ * host-> ATUSB_TX flags ack_seq #bytes
+ * host-> ATUSB_EUI64_WRITE - - #bytes (8)
+ * ->host ATUSB_EUI64_READ - - #bytes (8)
+ */
+
+#define ATUSB_REQ_FROM_DEV (USB_TYPE_VENDOR | USB_DIR_IN)
+#define ATUSB_REQ_TO_DEV (USB_TYPE_VENDOR | USB_DIR_OUT)
+
+#endif /* !_ATUSB_H */
--- /dev/null
+/*
+ * include/atusb/ep0.h - EP0 extension protocol
+ *
+ * Written 2008-2011, 2013 by Werner Almesberger
+ * Copyright 2008-2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef EP0_H
+#define EP0_H
+
+#include <atusb/atusb.h>
+
+
+/*
+ * EP0 protocol:
+ *
+ * 0.0 initial release
+ * 0.1 addition of ATUSB_TEST
+ * 0.2 First public release
+ * 0.3 ATUSB_EUI64_READ/WRITE for permanent EUI64 handling
+ * Support to run the firmware on Atmel Raven USB dongles
+ * Remove FCS frame check from firmware and leave it to the driver
+ * Use extended operation mode for TX for automatic ACK handling
+ */
+
+#define EP0ATUSB_MAJOR 0 /* EP0 protocol, major revision */
+#define EP0ATUSB_MINOR 3 /* EP0 protocol, minor revision */
+
+
+/*
+ * bmRequestType:
+ *
+ * D7 D6..5 D4...0
+ * | | |
+ * direction (0 = host->dev)
+ * type (2 = vendor)
+ * recipient (0 = device)
+ */
+
+#ifndef USB_TYPE_VENDOR
+#define USB_TYPE_VENDOR 0x40
+#endif
+
+#ifndef USB_DIR_IN
+#define USB_DIR_IN 0x80
+#endif
+
+#ifndef USB_DIR_OUT
+#define USB_DIR_OUT 0x00
+#endif
+
+#define ATUSB_FROM_DEV(req) (ATUSB_REQ_FROM_DEV | (req) << 8)
+#define ATUSB_TO_DEV(req) (ATUSB_REQ_TO_DEV | (req) << 8)
+
+
+void ep0_init(void);
+
+#endif /* !EP0_H */
--- /dev/null
+/*
+ * fw/mac.c - HardMAC functions
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+
+#include "at86rf230.h"
+#include "spi.h"
+#include "board.h"
+#include "mac.h"
+
+#define RX_BUFS 3
+
+
+bool (*mac_irq)(void) = NULL;
+
+
+static uint8_t rx_buf[RX_BUFS][MAX_PSDU+2]; /* PHDR+payload+LQ */
+static uint8_t tx_buf[MAX_PSDU];
+static uint8_t tx_size = 0;
+static bool txing = 0;
+static bool queued_tx_ack = 0;
+static uint8_t next_seq, this_seq, queued_seq;
+
+
+/* ----- Receive buffer management ----------------------------------------- */
+
+
+static uint8_t rx_in = 0, rx_out = 0;
+
+
+static inline void next_buf(uint8_t *index)
+{
+ *index = (*index+1) % RX_BUFS;
+}
+
+
+/* ----- Interrupt handling ------------------------------------------------ */
+
+
+static void rx_done(void *user);
+static void tx_ack_done(void *user);
+
+
+static void usb_next(void)
+{
+ const uint8_t *buf;
+
+ if (rx_in != rx_out) {
+ buf = rx_buf[rx_out];
+ led(1);
+ usb_send(&eps[1], buf, buf[0]+2, rx_done, NULL);
+ }
+
+ if (queued_tx_ack) {
+ usb_send(&eps[1], &queued_seq, 1, tx_ack_done, NULL);
+ queued_tx_ack = 0;
+ }
+}
+
+
+static void tx_ack_done(void *user)
+{
+ usb_next();
+}
+
+static void rx_done(void *user)
+{
+ led(0);
+ next_buf(&rx_out);
+ usb_next();
+#ifdef AT86RF230
+ /* slap at86rf230 - reduce fragmentation issue */
+ change_state(TRX_STATUS_RX_AACK_ON);
+#endif
+}
+
+
+static void receive_frame(void)
+{
+ uint8_t size;
+ uint8_t *buf;
+
+ spi_begin();
+ spi_io(AT86RF230_BUF_READ);
+
+ size = spi_recv();
+ if (!size || (size & 0x80)) {
+ spi_end();
+ return;
+ }
+
+ buf = rx_buf[rx_in];
+ spi_recv_block(buf+1, size+1);
+ spi_end();
+
+ buf[0] = size;
+ next_buf(&rx_in);
+
+ if (eps[1].state == EP_IDLE)
+ usb_next();
+}
+
+
+static bool handle_irq(void)
+{
+ uint8_t irq;
+
+ irq = reg_read(REG_IRQ_STATUS);
+ if (!(irq & IRQ_TRX_END))
+ return 1;
+
+ if (txing) {
+ if (eps[1].state == EP_IDLE) {
+ usb_send(&eps[1], &this_seq, 1, tx_ack_done, NULL);
+ } else {
+ queued_tx_ack = 1;
+ queued_seq = this_seq;
+ }
+ txing = 0;
+ return 1;
+ }
+
+ /* likely */
+ if (eps[1].state == EP_IDLE || rx_in != rx_out)
+ receive_frame();
+
+ return 1;
+}
+
+
+/* ----- TX/RX ------------------------------------------------------------- */
+
+
+bool mac_rx(int on)
+{
+ if (on) {
+ mac_irq = handle_irq;
+ reg_read(REG_IRQ_STATUS);
+ change_state(TRX_CMD_RX_AACK_ON);
+ } else {
+ mac_irq = NULL;
+ change_state(TRX_CMD_FORCE_TRX_OFF);
+ txing = 0;
+ }
+ return 1;
+}
+
+
+static void do_tx(void *user)
+{
+ uint16_t timeout = 0xffff;
+ uint8_t status;
+ uint8_t i;
+
+ /*
+ * If we time out here, the host driver will time out waiting for the
+ * TRX_END acknowledgement.
+ */
+ do {
+ if (!--timeout)
+ return;
+ status = reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK;
+ }
+ while (status != TRX_STATUS_RX_ON && status != TRX_STATUS_RX_AACK_ON);
+
+#ifdef AT86RF231
+ /*
+ * We use TRX_CMD_FORCE_PLL_ON instead of TRX_CMD_PLL_ON because a new
+ * reception may have begun while we were still working on the previous
+ * one.
+ */
+ reg_write(REG_TRX_STATE, TRX_CMD_FORCE_PLL_ON);
+#endif
+#ifdef AT86RF230
+ /*
+ * at86rf230 doesn't support force change, nevetherless this works
+ * somehow
+ */
+ reg_write(REG_TRX_STATE, TRX_CMD_PLL_ON);
+#endif
+#ifdef AT86RF212
+ /*
+ * We use TRX_CMD_FORCE_PLL_ON instead of TRX_CMD_PLL_ON because a new
+ * reception may have begun while we were still working on the previous
+ * one.
+ */
+ reg_write(REG_TRX_STATE, TRX_CMD_FORCE_PLL_ON);
+#endif
+
+ handle_irq();
+
+ spi_begin();
+ spi_send(AT86RF230_BUF_WRITE);
+ spi_send(tx_size+2); /* CRC */
+ for (i = 0; i != tx_size; i++)
+ spi_send(tx_buf[i]);
+ spi_end();
+
+ change_state(TRX_STATUS_TX_ARET_ON);
+
+ slp_tr();
+
+ txing = 1;
+ this_seq = next_seq;
+
+ /*
+ * Wait until we reach BUSY_TX_ARET, so that we command the transition to
+ * RX_AACK_ON which will be executed upon TX completion.
+ */
+ change_state(TRX_CMD_PLL_ON);
+ change_state(TRX_CMD_RX_AACK_ON);
+}
+
+
+bool mac_tx(uint16_t flags, uint8_t seq, uint16_t len)
+{
+ if (len > MAX_PSDU)
+ return 0;
+ tx_size = len;
+ next_seq = seq;
+ usb_recv(&eps[0], tx_buf, len, do_tx, NULL);
+ return 1;
+}
+
+
+void mac_reset(void)
+{
+ mac_irq = NULL;
+ txing = 0;
+ queued_tx_ack = 0;
+ rx_in = rx_out = 0;
+ next_seq = this_seq = queued_seq = 0;
+
+ /* enable CRC and PHY_RSSI (with RX_CRC_VALID) in SPI status return */
+ reg_write(REG_TRX_CTRL_1,
+ TX_AUTO_CRC_ON | SPI_CMD_MODE_PHY_RSSI << SPI_CMD_MODE_SHIFT);
+}
--- /dev/null
+/*
+ * fw/mac.h - HardMAC functions
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef MAC_H
+#define MAC_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+
+extern bool (*mac_irq)(void);
+
+bool mac_rx(int on);
+bool mac_tx(uint16_t flags, uint8_t seq, uint16_t len);
+void mac_reset(void);
+
+#endif /* !MAC_H */
--- /dev/null
+/*
+ * fw/sernum.c - ATUSB serial number
+ *
+ * Written 2008-2011, 2013 by Werner Almesberger
+ * Copyright 2008-2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+
+#include "board.h"
+#include "sernum.h"
+
+
+static const uint8_t string_descriptor_0[] = {
+ 4, /* blength */
+ USB_DT_STRING, /* bDescriptorType */
+ LE(USB_LANGID_ENGLISH_US) /* wLANGID[0] */
+};
+
+
+bool sernum_get_descr(uint8_t type, uint8_t index, const uint8_t **reply,
+ uint8_t *size)
+{
+ if (type != USB_DT_STRING)
+ return 0;
+ switch (index) {
+ case 0:
+ *reply = string_descriptor_0;
+ *size = sizeof(string_descriptor_0);
+ return 1;
+ case 1:
+ *reply = board_sernum;
+ *size = sizeof(board_sernum);
+ return 1;
+ default:
+ return 0;
+ }
+}
--- /dev/null
+/*
+ * fw/sernum.h - ATUSB serial number
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef SERNUM_H
+#define SERNUM_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "board.h"
+
+
+#ifdef HAS_BOARD_SERNUM
+
+bool sernum_get_descr(uint8_t type, uint8_t index, const uint8_t **reply,
+ uint8_t *size);
+
+#else /* HAS_BOARD_SERNUM */
+
+static inline bool sernum_get_descr(uint8_t type, uint8_t index,
+ const uint8_t **reply, uint8_t *size)
+{
+ return 0;
+}
+
+#endif /* !HAS_BOARD_SERNUM */
+
+#endif /* !SERNUM_H */
--- /dev/null
+/*
+ * fw/spi.c - ATmega8 family SPI I/O
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+
+#include "board.h"
+#include "spi.h"
+
+
+uint8_t spi_io(uint8_t v)
+{
+// while (!(UCSR1A & 1 << UDRE1));
+ SPI_DATA = v;
+ SPI_WAIT_DONE();
+ return SPI_DATA;
+}
+
+
+void spi_end(void)
+{
+// while (!(UCSR1A & 1 << TXC1));
+ SET(nSS);
+}
+
+
+void spi_recv_block(uint8_t *buf, uint8_t n)
+{
+ if (!n)
+ return;
+ SPI_DATA = 0;
+ while (--n) {
+ SPI_WAIT_DONE();
+ *buf++ = SPI_DATA;
+ SPI_DATA = 0;
+ }
+ SPI_WAIT_DONE();
+ *buf++ = SPI_DATA;
+}
--- /dev/null
+/*
+ * fw/spi.h - ATmega8 family SPI I/O
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef SPI_H
+#define SPI_H
+
+#include <stdint.h>
+
+
+void spi_begin(void);
+uint8_t spi_io(uint8_t v);
+void spi_end(void);
+void spi_off(void);
+void spi_init(void);
+
+#define spi_send(v) (void) spi_io(v)
+#define spi_recv(v) spi_io(0)
+
+void spi_recv_block(uint8_t *buf, uint8_t n);
+
+#endif /* !SPI_H */
--- /dev/null
+/*
+ * fw/uart.h - Functions needed for debugging over uart
+ *
+ * Code adapted from http://www.roboternetz.de/wissen/index.php/UART_mit_avr-gcc
+ * and http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
+ *
+ * Published under the Creative Commons Share-Alike licence
+ * https://creativecommons.org/licenses/by-sa/2.0/de/
+ *
+ * S. Salewski 2007
+ *
+ * Adapted by
+ * Josef Filzmaier 2017
+ */
+
+#include <avr/io.h>
+#include "uart.h"
+
+#define USART_BAUD 38400UL
+#define F_CPU 8000000UL
+
+#define Wait_USART_Ready() while (!(UCSR1A & (1<<UDRE1)))
+#define UART_UBRR (F_CPU/(16L*USART_BAUD)-1)
+
+// initialize USART, 8N1 mode
+void
+uart_init(void)
+{
+/* TODO: Find a working configuration for uart for the atmega32u2 */
+#if CHIP == at90usb1287
+ CLKPR = (1 << CLKPCE);
+ CLKPR = 0; // clock prescaler == 0, so we have 16 MHz mpu frequency
+ UBRR1 = UART_UBRR;
+ UCSR1C = (1 << UCSZ10) | (1 << UCSZ11);
+ UCSR1B = (1 << TXEN1);
+ do
+ {
+ UDR1;
+ }
+ while (UCSR1A & (1 << RXC1));
+#endif
+
+}
+
+int uart_write_char(char c, FILE* stream)
+{
+ if (c == '\n'){
+ uart_new_line();
+ }
+ else {
+ Wait_USART_Ready();
+ UDR1 = c;
+ }
+ return 0;
+}
+
+void
+uart_new_line(void)
+{
+ Wait_USART_Ready();
+ UDR1 = '\r';
+ Wait_USART_Ready();
+ UDR1 = '\n';
+}
--- /dev/null
+/*
+ * fw/uart.h - Functions needed for debugging over uart
+ *
+ * Code adapted from http://www.roboternetz.de/wissen/index.php/UART_mit_avr-gcc
+ * and http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
+ *
+ * Published under the Creative Commons Share-Alike licence
+ * https://creativecommons.org/licenses/by-sa/2.0/de/
+ *
+ * S. Salewski 2007
+ *
+ * Adapted by
+ * Josef Filzmaier 2017
+ */
+
+#ifndef UART_H_
+#define UART_H_
+
+#include <stdio.h>
+
+void uart_init(void);
+int uart_write_char(char c, FILE* stream);
+void uart_new_line(void);
+
+#endif /* UART_H_ */
--- /dev/null
+/*
+ * fw/usb/atu2.c - Chip-specific driver for Atmel ATxxxU2 USB chips
+ *
+ * Written 2008-2011, 2013-2014 by Werner Almesberger
+ * Copyright 2008-2011, 2013-2014 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * Known issues:
+ * - no suspend/resume
+ * - we don't call back after failed transmissions,
+ * - we don't reset the EP buffer after failed receptions
+ * - enumeration often encounters an error -71 (from which it recovers)
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define F_CPU 8000000UL
+#include <util/delay.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include "usb.h"
+#include "board.h"
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#if 1
+#define BUG_ON(cond) do { if (cond) panic(); } while (0)
+#else
+#define BUG_ON(cond)
+#endif
+
+
+struct ep_descr eps[NUM_EPS];
+
+
+static uint16_t usb_read_word(void)
+{
+ uint8_t low;
+
+ low = UEDATX;
+ return low | UEDATX << 8;
+}
+
+
+static void enable_addr(void *user)
+{
+ while (!(UEINTX & (1 << TXINI)));
+ UDADDR |= 1 << ADDEN;
+}
+
+
+void set_addr(uint8_t addr)
+{
+ UDADDR = addr;
+ usb_send(&eps[0], NULL, 0, enable_addr, NULL);
+}
+
+
+void usb_ep_change(struct ep_descr *ep)
+{
+ if (ep->state == EP_TX) {
+ UENUM = ep-eps;
+ UEIENX |= 1 << TXINE;
+ }
+}
+
+
+static bool ep_setup(void)
+{
+ struct setup_request setup;
+
+ BUG_ON(UEBCLX < 8);
+
+ setup.bmRequestType = UEDATX;
+ setup.bRequest = UEDATX;
+ setup.wValue = usb_read_word();
+ setup.wIndex = usb_read_word();
+ setup.wLength = usb_read_word();
+
+ if (!handle_setup(&setup))
+ return 0;
+ if (!(setup.bmRequestType & 0x80) && eps[0].state == EP_IDLE)
+ usb_send(&eps[0], NULL, 0, NULL, NULL);
+ return 1;
+}
+
+
+static bool ep_rx(struct ep_descr *ep)
+{
+ uint8_t size;
+
+ size = UEBCLX;
+ if (size > ep->end-ep->buf)
+ return 0;
+ while (size--)
+ *ep->buf++ = UEDATX;
+ if (ep->buf == ep->end) {
+ ep->state = EP_IDLE;
+ if (ep->callback)
+ ep->callback(ep->user);
+// if (ep == &eps[0])
+ usb_send(ep, NULL, 0, NULL, NULL);
+ }
+ return 1;
+}
+
+
+static void ep_tx(struct ep_descr *ep)
+{
+ uint8_t size = ep->end-ep->buf;
+ uint8_t left;
+
+ if (size > ep->size)
+ size = ep->size;
+ for (left = size; left; left--)
+ UEDATX = *ep->buf++;
+ if (size == ep->size)
+ return;
+ ep->state = EP_IDLE;
+}
+
+
+static void handle_ep(int n)
+{
+ struct ep_descr *ep = eps+n;
+ uint8_t mask;
+
+ UENUM = n;
+ if (UEINTX & (1 << RXSTPI)) {
+ /* @@@ EP_RX. EP_TX: cancel */
+ ep->state = EP_IDLE;
+ if (!ep_setup())
+ goto stall;
+ UEINTX = ~(1 << RXSTPI);
+ }
+ if (UEINTX & (1 << RXOUTI)) {
+ /* @@ EP_TX: cancel */
+ if (ep->state != EP_RX)
+ goto stall;
+ if (!ep_rx(ep))
+ goto stall;
+ /* @@@ gcc 4.5.2 wants this cast */
+ UEINTX = (uint8_t) ~(1 << RXOUTI | 1 << FIFOCON);
+ }
+ if (UEINTX & (1 << STALLEDI)) {
+ ep->state = EP_IDLE;
+ UEINTX = ~(1 << STALLEDI);
+ }
+ if (UEINTX & (1 << TXINI)) {
+ /* @@ EP_RX: cancel (?) */
+ if (ep->state == EP_TX) {
+ ep_tx(ep);
+ mask = 1 << TXINI;
+ if (n)
+ mask |= 1 << FIFOCON;
+ UEINTX = ~mask;
+ if (ep->state == EP_IDLE && ep->callback)
+ ep->callback(ep->user);
+ } else {
+ UEIENX &= ~(1 << TXINE);
+ }
+ }
+ return;
+
+stall:
+ UEINTX = ~(1 << RXSTPI | 1 << RXOUTI | 1 << STALLEDI);
+ ep->state = EP_IDLE;
+ UECONX |= 1 << STALLRQ;
+}
+
+
+void ep_init(void)
+{
+ UENUM = 0;
+ UECONX = (1 << RSTDT) | (1 << EPEN); /* enable */
+ UECFG0X = 0; /* control, direction is ignored */
+ UECFG1X = 3 << EPSIZE0; /* 64 bytes */
+ UECFG1X |= 1 << ALLOC;
+
+ while (!(UESTA0X & (1 << CFGOK)));
+
+ UEIENX =
+ (1 << RXSTPE) | (1 << RXOUTE) | (1 << STALLEDE) | (1 << TXINE);
+
+ eps[0].state = EP_IDLE;
+ eps[0].size = 64;
+
+#ifndef BOOT_LOADER
+
+ UENUM = 1;
+ UECONX = (1 << RSTDT) | (1 << EPEN); /* enable */
+ UECFG0X = (1 << EPTYPE1) | (1 << EPDIR); /* bulk IN */
+ UECFG1X = 3 << EPSIZE0; /* 64 bytes */
+ UECFG1X |= 1 << ALLOC;
+
+ while (!(UESTA0X & (1 << CFGOK)));
+
+ UEIENX = (1 << STALLEDE) | (1 << TXINE);
+
+ eps[1].state = EP_IDLE;
+ eps[1].size = 64;
+
+#endif
+}
+
+
+ISR(USB_GEN_vect)
+{
+ uint8_t flags;
+
+ flags = UDINT;
+ if (flags & (1 << EORSTI)) {
+ if (user_reset)
+ user_reset();
+ ep_init();
+ UDINT = ~(1 << EORSTI);
+ }
+}
+
+
+ISR(USB_COM_vect)
+{
+ uint8_t flags, i;
+
+ flags = UEINT;
+ for (i = 0; i != NUM_EPS; i++)
+ if (flags & (1 << i))
+ handle_ep(i);
+}
+
+
+void usb_reset(void)
+{
+ UDCON |= 1 << DETACH; /* detach the pull-up */
+ _delay_ms(1);
+}
--- /dev/null
+/*
+ * boot/dfu.c - DFU protocol engine
+ *
+ * Written 2008-2011, 2013-2015 by Werner Almesberger
+ * Copyright 2008-2011, 2013-2015 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf
+ */
+
+/*
+ * A few, erm, shortcuts:
+ *
+ * - we don't bother with the app* states since DFU is all this firmware does
+ * - after DFU_DNLOAD, we just block until things are written, so we never
+ * enter dfuDNLOAD_SYNC or dfuDNBUSY
+ * - no dfuMANIFEST_SYNC, dfuMANIFEST, or dfuMANIFEST_WAIT_RESET
+ * - to keep our buffers small, we only accept EP0-sized blocks
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+#include "dfu.h"
+
+#include "board.h"
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define debug(...)
+#define error(...)
+
+
+#ifndef DFU_ALT_SETTINGS
+#define DFU_ALT_SETTINGS 1
+#endif
+
+#ifndef DFU_ALT_NAME_0_IDX
+#define DFU_ALT_NAME_0_IDX 0
+#endif
+
+#ifndef DFU_ALT_NAME_1_IDX
+#define DFU_ALT_NAME_1_IDX 0
+#endif
+
+#ifndef DFU_ALT_NAME_2_IDX
+#define DFU_ALT_NAME_2_IDX 0
+#endif
+
+
+const uint8_t device_descriptor[] = {
+ 18, /* bLength */
+ USB_DT_DEVICE, /* bDescriptorType */
+ LE(0x100), /* bcdUSB */
+ USB_CLASS_APP_SPEC, /* bDeviceClass */
+ 0x00, /* bDeviceSubClass (per interface) */
+ 0x00, /* bDeviceProtocol (per interface) */
+ EP0_SIZE, /* bMaxPacketSize */
+ LE(DFU_USB_VENDOR), /* idVendor */
+ LE(DFU_USB_PRODUCT), /* idProduct */
+ LE(0x0001), /* bcdDevice */
+ 0, /* iManufacturer */
+ 0, /* iProduct */
+#ifdef HAS_BOARD_SERNUM
+ 1, /* iSerialNumber */
+#else
+ 0, /* iSerialNumber */
+#endif
+ 1 /* bNumConfigurations */
+};
+
+
+const uint8_t config_descriptor[] = {
+ 9, /* bLength */
+ USB_DT_CONFIG, /* bDescriptorType */
+ LE(9+9*DFU_ALT_SETTINGS), /* wTotalLength */
+ 1, /* bNumInterfaces */
+ 1, /* bConfigurationValue (> 0 !) */
+ 0, /* iConfiguration */
+// USB_ATTR_SELF_POWERED | USB_ATTR_BUS_POWERED,
+ USB_ATTR_BUS_POWERED, /* bmAttributes */
+ ((BOARD_MAX_mA)+1)/2, /* bMaxPower */
+
+ /* Interface #0 */
+
+ DFU_ITF_DESCR(0, 0, dfu_proto_dfu, DFU_ALT_NAME_0_IDX)
+#if DFU_ALT_SETTINGS > 1
+ DFU_ITF_DESCR(0, 1, dfu_proto_dfu, DFU_ALT_NAME_1_IDX)
+#endif
+#if DFU_ALT_SETTINGS > 2
+ DFU_ITF_DESCR(0, 2, dfu_proto_dfu, DFU_ALT_NAME_2_IDX)
+#endif
+};
+
+
+static uint16_t next_block = 0;
+static bool did_download;
+
+
+static uint8_t buf[EP0_SIZE];
+
+
+static void block_write(void *user)
+{
+ uint16_t *size = user;
+
+ dfu_flash_ops->write(buf, *size);
+}
+
+
+static bool block_receive(uint16_t length)
+{
+ static uint16_t size;
+
+ if (!dfu_flash_ops->can_write(length)) {
+ dfu.state = dfuERROR;
+ dfu.status = errADDRESS;
+ return 0;
+ }
+ if (length > EP0_SIZE) {
+ dfu.state = dfuERROR;
+ dfu.status = errUNKNOWN;
+ return 0;
+ }
+ size = length;
+ usb_recv(&eps[0], buf, size, block_write, &size);
+ return 1;
+}
+
+
+static bool block_transmit(uint16_t length)
+{
+ uint16_t got;
+
+ if (length > EP0_SIZE) {
+ dfu.state = dfuERROR;
+ dfu.status = errUNKNOWN;
+ return 1;
+ }
+ got = dfu_flash_ops->read(buf, length);
+ if (got < length) {
+ length = got;
+ dfu.state = dfuIDLE;
+ }
+ usb_send(&eps[0], buf, length, NULL, NULL);
+ return 1;
+}
+
+
+static bool my_setup(const struct setup_request *setup)
+{
+ bool ok;
+
+ switch (setup->bmRequestType | setup->bRequest << 8) {
+ case DFU_TO_DEV(DFU_DETACH):
+ debug("DFU_DETACH\n");
+ /*
+ * The DFU spec says thay this is sent in protocol 1 only.
+ * However, dfu-util also sends it to get out of DFU mode,
+ * so we just don't make a fuss and ignore it.
+ */
+ return 1;
+ case DFU_TO_DEV(DFU_DNLOAD):
+ debug("DFU_DNLOAD\n");
+ if (dfu.state == dfuIDLE) {
+ next_block = setup->wValue;
+ dfu_flash_ops->start();
+ }
+ else if (dfu.state != dfuDNLOAD_IDLE) {
+ error("bad state\n");
+ return 0;
+ }
+ if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
+ debug("retransmisson\n");
+ return 1;
+ }
+ if (setup->wValue != next_block) {
+ debug("bad block (%d vs. %d)\n",
+ setup->wValue, next_block);
+ dfu.state = dfuERROR;
+ dfu.status = errUNKNOWN;
+ return 1;
+ }
+ if (!setup->wLength) {
+ debug("DONE\n");
+ dfu_flash_ops->end_write();
+ dfu.state = dfuIDLE;
+ did_download = 1;
+ return 1;
+ }
+ ok = block_receive(setup->wLength);
+ next_block++;
+ dfu.state = dfuDNLOAD_IDLE;
+ return ok;
+ case DFU_FROM_DEV(DFU_UPLOAD):
+ debug("DFU_UPLOAD\n");
+ if (dfu.state == dfuIDLE) {
+ next_block = setup->wValue;
+ dfu_flash_ops->start();
+ }
+ else if (dfu.state != dfuUPLOAD_IDLE)
+ return 0;
+ if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
+ debug("retransmisson\n");
+ /* @@@ try harder */
+ dfu.state = dfuERROR;
+ dfu.status = errUNKNOWN;
+ return 1;
+ }
+ if (setup->wValue != next_block) {
+ debug("bad block (%d vs. %d)\n",
+ setup->wValue, next_block);
+ dfu.state = dfuERROR;
+ dfu.status = errUNKNOWN;
+ return 1;
+ }
+ ok = block_transmit(setup->wLength);
+ next_block++;
+ dfu.state = dfuUPLOAD_IDLE;
+ return ok;
+ case DFU_TO_DEV(DFU_ABORT):
+ debug("DFU_ABORT\n");
+ dfu.state = dfuIDLE;
+ dfu.status = OK;
+ return 1;
+ default:
+ return dfu_setup_common(setup);
+ }
+}
+
+
+static void my_reset(void)
+{
+#if 0
+ /* @@@ not nice -- think about where this should go */
+ extern void run_payload(void);
+
+ if (did_download)
+ run_payload();
+#endif
+}
+
+
+void dfu_init(void)
+{
+ user_setup = my_setup;
+ user_get_descriptor = dfu_my_descr;
+ user_reset = my_reset;
+}
--- /dev/null
+/*
+ * boot/dfu.h - DFU protocol constants and data structures
+ *
+ * Written 2008, 2011, 2013-2015 by Werner Almesberger
+ * Copyright 2008, 2011, 2013-2015 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef DFU_H
+#define DFU_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+
+
+enum dfu_request {
+ DFU_DETACH,
+ DFU_DNLOAD,
+ DFU_UPLOAD,
+ DFU_GETSTATUS,
+ DFU_CLRSTATUS,
+ DFU_GETSTATE,
+ DFU_ABORT,
+};
+
+
+enum dfu_status {
+ OK,
+ errTARGET,
+ errFILE,
+ errWRITE,
+ errERASE,
+ errCHECK_ERASED,
+ errPROG,
+ errVERIFY,
+ errADDRESS,
+ errNOTDONE,
+ errFIRMWARE,
+ errVENDOR,
+ errUSBR,
+ errPOR,
+ errUNKNOWN,
+ errSTALLEDPKT,
+};
+
+
+enum dfu_state {
+ appIDLE,
+ appDETACH,
+ dfuIDLE,
+ dfuDNLOAD_SYNC,
+ dfuDNBUSY,
+ dfuDNLOAD_IDLE,
+ dfuMANIFEST_SYNC,
+ dfuMANIFEST,
+ dfuMANIFEST_WAIT_RESET,
+ dfuUPLOAD_IDLE,
+ dfuERROR
+};
+
+enum dfu_itf_proto {
+ dfu_proto_runtime = 1, /* Runtime protocol */
+ dfu_proto_dfu = 2, /* DFU mode protocol */
+};
+
+
+#define DFU_DT_FUNCTIONAL 0x21 /* DFU FUNCTIONAL descriptor type */
+
+
+#define DFU_TO_DEV(req) (0x21 | (req) << 8)
+#define DFU_FROM_DEV(req) (0xa1 | (req) << 8)
+
+
+struct dfu {
+ uint8_t status; /* bStatus */
+ uint8_t toL, toM, toH; /* bwPollTimeout */
+ uint8_t state; /* bState */
+ uint8_t iString;
+};
+
+
+#define DFU_ITF_DESCR(itf, alt, proto, idx) \
+ 9, /* bLength */ \
+ USB_DT_INTERFACE, /* bDescriptorType */ \
+ (itf), /* bInterfaceNumber */ \
+ (alt), /* bAlternateSetting */ \
+ 0, /* bNumEndpoints */ \
+ 0xfe, /* bInterfaceClass (application specific) */ \
+ 0x01, /* bInterfaceSubClass (device fw upgrade) */ \
+ (proto), /* bInterfaceProtocol (dfu_proto_*) */ \
+ (idx), /* iInterface */
+
+
+struct dfu_flash_ops {
+ void (*start)(void);
+ bool (*can_write)(uint16_t size);
+ void (*write)(const uint8_t *buf, uint16_t size);
+ void (*end_write)(void);
+ uint16_t (*read)(uint8_t *buf, uint16_t size);
+};
+
+extern struct dfu dfu;
+extern const struct dfu_flash_ops *dfu_flash_ops;
+
+
+bool dfu_setup_common(const struct setup_request *setup);
+bool dfu_my_descr(uint8_t type, uint8_t index, const uint8_t **reply,
+ uint8_t *size);
+
+void dfu_init(void);
+
+#endif /* !DFU_H */
--- /dev/null
+/*
+ * boot/dfu_common.c - DFU protocol engine parts common to App/DFU
+ *
+ * Written 2008-2011, 2013-2014 by Werner Almesberger
+ * Copyright 2008-2011, 2013-2014 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf
+ */
+
+/*
+ * A few, erm, shortcuts:
+ *
+ * - we don't bother with the app* states since DFU is all this firmware does
+ * - after DFU_DNLOAD, we just block until things are written, so we never
+ * enter dfuDNLOAD_SYNC or dfuDNBUSY
+ * - no dfuMANIFEST_SYNC, dfuMANIFEST, or dfuMANIFEST_WAIT_RESET
+ * - to keep our buffers small, we only accept EP0-sized blocks
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+#include "dfu.h"
+
+#include "board.h"
+#include "../sernum.h"
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define debug(...)
+#define error(...)
+
+
+static const uint8_t functional_descriptor[] = {
+ 9, /* bLength */
+ DFU_DT_FUNCTIONAL, /* bDescriptorType */
+ 0xf, /* bmAttributes (claim omnipotence :-) */
+ LE(0xffff), /* wDetachTimeOut (we're very patient) */
+ LE(EP0_SIZE), /* wTransferSize */
+ LE(0x101), /* bcdDFUVersion */
+};
+
+
+/*
+ * The worst-case activity would be flashing a one page and erasing another
+ * one, would should take less than 10 ms. A 100 ms timeout ought to be plenty.
+ */
+
+struct dfu dfu = {
+ OK, /* bStatus */
+ LE(100), 0, /* bwPollTimeout, 100 ms */
+ dfuIDLE, /* bState */
+ 0, /* iString */
+};
+
+
+bool dfu_setup_common(const struct setup_request *setup)
+{
+ switch (setup->bmRequestType | setup->bRequest << 8) {
+ case DFU_FROM_DEV(DFU_GETSTATUS):
+ debug("DFU_GETSTATUS\n");
+ usb_send(&eps[0], (uint8_t *) &dfu, sizeof(dfu), NULL, NULL);
+ return 1;
+ case DFU_TO_DEV(DFU_CLRSTATUS):
+ debug("DFU_CLRSTATUS\n");
+ dfu.state = dfuIDLE;
+ dfu.status = OK;
+ return 1;
+ case DFU_FROM_DEV(DFU_GETSTATE):
+ debug("DFU_GETSTATE\n");
+ usb_send(&eps[0], &dfu.state, 1, NULL, NULL);
+ return 1;
+ default:
+ error("DFU rt %x, rq%x ?\n",
+ setup->bmRequestType, setup->bRequest);
+ return 0;
+ }
+}
+
+
+bool dfu_my_descr(uint8_t type, uint8_t index, const uint8_t **reply,
+ uint8_t *size)
+{
+ if (type != DFU_DT_FUNCTIONAL)
+ return sernum_get_descr(type, index, reply, size);
+ *reply = functional_descriptor;
+ *size = sizeof(functional_descriptor);
+ return 1;
+}
--- /dev/null
+/*
+ * fw/usb/usb.c - USB hardware setup and standard device requests
+ *
+ * Written 2008-2011, 2013, 2015 by Werner Almesberger
+ * Copyright 2008-2011, 2013, 2015 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * Known issues:
+ * - no suspend/resume
+ * - should support EP clearing and stalling
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+#include "board.h"
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#if 1
+extern void panic(void);
+#define BUG_ON(cond) do { if (cond) panic(); } while (0)
+#else
+#define BUG_ON(cond)
+#endif
+
+bool (*user_setup)(const struct setup_request *setup);
+void (*user_set_interface)(int nth);
+bool (*user_get_descriptor)(uint8_t type, uint8_t index,
+ const uint8_t **reply, uint8_t *size);
+void (*user_reset)(void);
+
+
+void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf,
+ uint8_t size, void (*callback)(void *user), void *user)
+{
+ BUG_ON(ep->state);
+ ep->state = state;
+ ep->buf = buf;
+ ep->end = buf+size;
+ ep->callback = callback;
+ ep->user = user;
+ usb_ep_change(ep);
+}
+
+
+static bool get_descriptor(uint8_t type, uint8_t index, uint16_t length)
+{
+ const uint8_t *reply;
+ uint8_t size;
+
+ switch (type) {
+ case USB_DT_DEVICE:
+ reply = device_descriptor;
+ size = reply[0];
+ break;
+ case USB_DT_CONFIG:
+ if (index)
+ return 0;
+ reply = config_descriptor;
+ size = reply[2];
+ break;
+ default:
+ if (!user_get_descriptor)
+ return 0;
+ if (!user_get_descriptor(type, index, &reply, &size))
+ return 0;
+ }
+ if (length < size)
+ size = length;
+ usb_send(&eps[0], reply, size, NULL, NULL);
+ return 1;
+}
+
+
+bool handle_setup(const struct setup_request *setup)
+{
+ switch (setup->bmRequestType | setup->bRequest << 8) {
+
+ /*
+ * Device request
+ *
+ * See http://www.beyondlogic.org/usbnutshell/usb6.htm
+ */
+
+ case FROM_DEVICE(GET_STATUS):
+ if (setup->wLength != 2)
+ return 0;
+ usb_send(&eps[0], "\000", 2, NULL, NULL);
+ break;
+ case TO_DEVICE(CLEAR_FEATURE):
+ break;
+ case TO_DEVICE(SET_FEATURE):
+ return 0;
+ case TO_DEVICE(SET_ADDRESS):
+ set_addr(setup->wValue);
+ break;
+ case FROM_DEVICE(GET_DESCRIPTOR):
+ case FROM_INTERFACE(GET_DESCRIPTOR):
+ if (!get_descriptor(setup->wValue >> 8, setup->wValue,
+ setup->wLength))
+ return 0;
+ break;
+ case TO_DEVICE(SET_DESCRIPTOR):
+ return 0;
+ case FROM_DEVICE(GET_CONFIGURATION):
+ usb_send(&eps[0], "", 1, NULL, NULL);
+ break;
+ case TO_DEVICE(SET_CONFIGURATION):
+ if (setup->wValue != config_descriptor[5])
+ return 0;
+ break;
+
+ /*
+ * Interface request
+ */
+
+ case FROM_INTERFACE(GET_STATUS):
+ return 0;
+ case TO_INTERFACE(CLEAR_FEATURE):
+ return 0;
+ case TO_INTERFACE(SET_FEATURE):
+ return 0;
+ case FROM_INTERFACE(GET_INTERFACE):
+ return 0;
+ case TO_INTERFACE(SET_INTERFACE):
+ {
+ const uint8_t *interface_descriptor =
+ config_descriptor+9;
+ const uint8_t *p;
+ int i;
+
+ i = 0;
+ for (p = interface_descriptor;
+ p != config_descriptor+config_descriptor[2];
+ p += p[0]) {
+ if (p[1] != USB_DT_INTERFACE)
+ continue;
+ if (p[2] == setup->wIndex &&
+ p[3] == setup->wValue) {
+ if (user_set_interface)
+ user_set_interface(i);
+ return 1;
+ }
+ i++;
+ }
+ return 0;
+ }
+ break;
+
+ /*
+ * Endpoint request
+ */
+
+ case FROM_ENDPOINT(GET_STATUS):
+ return 0;
+ case TO_ENDPOINT(CLEAR_FEATURE):
+ return 0;
+ case TO_ENDPOINT(SET_FEATURE):
+ return 0;
+ case FROM_ENDPOINT(SYNCH_FRAME):
+ return 0;
+
+ default:
+ if (user_setup)
+ return user_setup(setup);
+ return 0;
+ }
+
+ return 1;
+}
--- /dev/null
+/*
+ * fw/usb//usb.h - USB hardware setup and standard device requests
+ *
+ * Written 2008, 2009, 2011, 2013, 2015 by Werner Almesberger
+ * Copyright 2008, 2009, 2011, 2013, 2015 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef USB_H
+#define USB_H
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+
+/*
+ * Packet identifier types
+ */
+
+#define PID_OUT 0x1
+#define PID_IN 0x9
+#define PID_SOF 0x5
+#define PID_SETUP 0xd
+#define PID_DATA0 0x3
+#define PID_DATA1 0xb
+#define PID_ACK 0x2
+#define PID_NAK 0xa
+#define PID_STALL 0xe
+
+/*
+ * Descriptor types
+ *
+ * Reuse libusb naming scheme (/usr/include/usb.h)
+ */
+
+#define USB_DT_DEVICE 1
+#define USB_DT_CONFIG 2
+#define USB_DT_STRING 3
+#define USB_DT_INTERFACE 4
+#define USB_DT_ENDPOINT 5
+
+/*
+ * Device classes
+ *
+ * Reuse libusb naming scheme (/usr/include/usb.h)
+ */
+
+#define USB_CLASS_PER_INTERFACE 0
+#define USB_CLASS_COMM 2
+#define USB_CLASS_HID 3
+#define USB_CLASS_MASS_STORAGE 8
+#define USB_CLASS_HUB 9
+#define USB_CLASS_DATA 10
+#define USB_CLASS_APP_SPEC 0xfe
+#define USB_CLASS_VENDOR_SPEC 0xff
+
+/*
+ * Configuration attributes
+ */
+
+#define USB_ATTR_BUS_POWERED 0x80
+#define USB_ATTR_SELF_POWERED 0x40
+#define USB_ATTR_REMOTE_WAKEUP 0x20
+
+/*
+ * Endpoint type
+ */
+
+#define USB_ENDPOINT_TYPE_CONTROL 0
+#define USB_ENDPOINT_TYPE_ISOCHRONOUS 1
+#define USB_ENDPOINT_TYPE_BULK 2
+#define USB_ENDPOINT_TYPE_INTERRUPT 3
+
+/*
+ * Setup request types
+ */
+
+#define TO_DEVICE(req) (0x00 | (req) << 8)
+#define FROM_DEVICE(req) (0x80 | (req) << 8)
+#define TO_INTERFACE(req) (0x01 | (req) << 8)
+#define FROM_INTERFACE(req) (0x81 | (req) << 8)
+#define TO_ENDPOINT(req) (0x02 | (req) << 8)
+#define FROM_ENDPOINT(req) (0x82 | (req) << 8)
+
+/*
+ * Setup requests
+ */
+
+#define GET_STATUS 0x00
+#define CLEAR_FEATURE 0x01
+#define SET_FEATURE 0x03
+#define SET_ADDRESS 0x05
+#define GET_DESCRIPTOR 0x06
+#define SET_DESCRIPTOR 0x07
+#define GET_CONFIGURATION 0x08
+#define SET_CONFIGURATION 0x09
+#define GET_INTERFACE 0x0a
+#define SET_INTERFACE 0x0b
+#define SYNCH_FRAME 0x0c
+
+/*
+ * USB Language ID codes
+ *
+ * http://www.usb.org/developers/docs/USB_LANGIDs.pdf
+ */
+
+#define USB_LANGID_ENGLISH_US 0x409
+
+
+/*
+ * Odd. sdcc seems to think "x" assumes the size of the destination, i.e.,
+ * uint8_t. Hence the cast.
+ */
+
+#define LE(x) ((uint16_t) (x) & 0xff), ((uint16_t) (x) >> 8)
+
+#define LO(x) (((uint8_t *) &(x))[0])
+#define HI(x) (((uint8_t *) &(x))[1])
+
+
+#ifdef LOW_SPEED
+#define EP0_SIZE 8
+#else
+#define EP0_SIZE 64
+#endif
+
+#define EP1_SIZE 64 /* simplify */
+
+
+enum ep_state {
+ EP_IDLE,
+ EP_RX,
+ EP_TX,
+ EP_STALL,
+};
+
+struct ep_descr {
+ enum ep_state state;
+ uint8_t *buf;
+ uint8_t *end;
+ uint8_t size;
+ void (*callback)(void *user);
+ void *user;
+};
+
+struct setup_request {
+ uint8_t bmRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+};
+
+
+extern const uint8_t device_descriptor[];
+extern const uint8_t config_descriptor[];
+extern struct ep_descr eps[];
+
+extern bool (*user_setup)(const struct setup_request *setup);
+extern void (*user_set_interface)(int nth);
+extern bool (*user_get_descriptor)(uint8_t type, uint8_t index,
+ const uint8_t **reply, uint8_t *size);
+extern void (*user_reset)(void);
+
+
+#define usb_left(ep) ((ep)->end-(ep)->buf)
+#define usb_send(ep, buf, size, callback, user) \
+ usb_io(ep, EP_TX, (void *) buf, size, callback, user)
+#define usb_recv(ep, buf, size, callback, user) \
+ usb_io(ep, EP_RX, buf, size, callback, user)
+
+void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf,
+ uint8_t size, void (*callback)(void *user), void *user);
+
+bool handle_setup(const struct setup_request *setup);
+void set_addr(uint8_t addr);
+void usb_ep_change(struct ep_descr *ep);
+void usb_reset(void);
+void usb_init(void);
+
+void ep_init(void);
+
+#endif /* !USB_H */
--- /dev/null
+/*
+ * fw/version.h - Automatically generated version string
+ *
+ * Written 2008, 2011 by Werner Almesberger
+ * Copyright 2008, 2011 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef VERSION_H
+#define VERSION_H
+
+#include <stdint.h>
+
+
+extern const char *build_date;
+extern const uint16_t build_number;
+
+#endif /* !VERSION_H */