From dd4bc9ff49b9a7075e579fdd62fd930d27a9a7df Mon Sep 17 00:00:00 2001
From: Jason Self <j@jxself.org>
Date: Thu, 4 Jul 2019 15:55:48 -0700
Subject: [PATCH 2/8] Add firmware for the ATUSB IEEE 802.15.4 USB Adapter

http://shop.sysmocom.de/products/atusb/
---
 INSTALL                     |  20 +-
 Makefile                    |   6 +-
 WHENCE                      |  12 ++
 atusb/Makefile              | 236 +++++++++++++++++++++
 atusb/README                |  94 +++++++++
 atusb/an/README             |  25 +++
 atusb/an/dec.py             | 127 ++++++++++++
 atusb/an/get.py             |  31 +++
 atusb/an/plot               |  12 ++
 atusb/atusb.c               |  63 ++++++
 atusb/board.c               | 120 +++++++++++
 atusb/board.h               |  95 +++++++++
 atusb/board_app.c           | 173 ++++++++++++++++
 atusb/board_atusb.c         | 162 +++++++++++++++
 atusb/board_atusb.h         |  48 +++++
 atusb/board_hulusb.c        | 179 ++++++++++++++++
 atusb/board_hulusb.h        |  66 ++++++
 atusb/board_rzusb.c         | 169 +++++++++++++++
 atusb/board_rzusb.h         |  48 +++++
 atusb/boot.c                |  77 +++++++
 atusb/descr.c               | 104 ++++++++++
 atusb/ep0.c                 | 338 ++++++++++++++++++++++++++++++
 atusb/flash.c               |  97 +++++++++
 atusb/include/at86rf230.h   | 402 ++++++++++++++++++++++++++++++++++++
 atusb/include/atusb/atusb.h |  97 +++++++++
 atusb/include/atusb/ep0.h   |  64 ++++++
 atusb/mac.c                 | 250 ++++++++++++++++++++++
 atusb/mac.h                 |  26 +++
 atusb/sernum.c              |  47 +++++
 atusb/sernum.h              |  37 ++++
 atusb/spi.c                 |  51 +++++
 atusb/spi.h                 |  30 +++
 atusb/uart.c                |  64 ++++++
 atusb/uart.h                |  25 +++
 atusb/usb/atu2.c            | 247 ++++++++++++++++++++++
 atusb/usb/dfu.c             | 260 +++++++++++++++++++++++
 atusb/usb/dfu.h             | 119 +++++++++++
 atusb/usb/dfu_common.c      | 101 +++++++++
 atusb/usb/usb.c             | 181 ++++++++++++++++
 atusb/usb/usb.h             | 189 +++++++++++++++++
 atusb/version.h             |  23 +++
 42 files changed, 4512 insertions(+), 3 deletions(-)
 create mode 100644 atusb/Makefile
 create mode 100644 atusb/README
 create mode 100644 atusb/an/README
 create mode 100755 atusb/an/dec.py
 create mode 100755 atusb/an/get.py
 create mode 100755 atusb/an/plot
 create mode 100644 atusb/atusb.c
 create mode 100644 atusb/board.c
 create mode 100644 atusb/board.h
 create mode 100644 atusb/board_app.c
 create mode 100644 atusb/board_atusb.c
 create mode 100644 atusb/board_atusb.h
 create mode 100644 atusb/board_hulusb.c
 create mode 100644 atusb/board_hulusb.h
 create mode 100644 atusb/board_rzusb.c
 create mode 100644 atusb/board_rzusb.h
 create mode 100644 atusb/boot.c
 create mode 100644 atusb/descr.c
 create mode 100644 atusb/ep0.c
 create mode 100644 atusb/flash.c
 create mode 100644 atusb/include/at86rf230.h
 create mode 100644 atusb/include/atusb/atusb.h
 create mode 100644 atusb/include/atusb/ep0.h
 create mode 100644 atusb/mac.c
 create mode 100644 atusb/mac.h
 create mode 100644 atusb/sernum.c
 create mode 100644 atusb/sernum.h
 create mode 100644 atusb/spi.c
 create mode 100644 atusb/spi.h
 create mode 100644 atusb/uart.c
 create mode 100644 atusb/uart.h
 create mode 100644 atusb/usb/atu2.c
 create mode 100644 atusb/usb/dfu.c
 create mode 100644 atusb/usb/dfu.h
 create mode 100644 atusb/usb/dfu_common.c
 create mode 100644 atusb/usb/usb.c
 create mode 100644 atusb/usb/usb.h
 create mode 100644 atusb/version.h

diff --git a/INSTALL b/INSTALL
index 74c5cfd..7fb1116 100644
--- a/INSTALL
+++ b/INSTALL
@@ -16,6 +16,8 @@ In order to build everything you will need the following on the host
 system:
 
     * A C/C++ compiler, like GCC
+    * AVR-GCC
+    * Standard C library for AVR-GCC
     * Cmake
     * GNU Bison/YACC
     * GNU Flex
@@ -32,13 +34,27 @@ system:
 
 On GNU/Linux distros that use apt you can install these with:
 
-    apt install binutils-arm-linux-gnueabi binutils-arm-none-eabi bison \
-    cmake flex g++ gcc gcc-arm-linux-gnueabi gcc-arm-none-eabi gperf make wget
+    apt install avr-gcc avr-libc binutils-arm-linux-gnueabi \
+    binutils-arm-none-eabi bison cmake flex g++ gcc \
+    gcc-arm-linux-gnueabi gcc-arm-none-eabi gperf make wget
 
 CARL9170 Firmware Configuration
+-------------------------------
 When building the carl9170 firmware you will be prompted with
 configuration questions.
 
+atusb: Firmware for the ATUSB IEEE 802.15.4 USB Adapter
+-------------------------------------------------------
+
+To flash the firmware you need dfu-util on the host. Issue
+
+    make dfu
+
+right after plugging the device into the USB port while the red led is
+still on.
+
+Refer to the included README file for more information.
+
 Licensing
 ---------
 
diff --git a/Makefile b/Makefile
index 21d16fb..8474b30 100644
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@ shell=/bin/sh
 prefix=/lib/firmware
 install_program=install
 
-.PHONY:	all test clean install a56 as31 aica ath9k_htc_toolchain ath9k_htc av7110 b43-tools carl9170fw-toolchain carl9170fw cis-tools cis dsp56k ihex2fw isci keyspan_pda openfwwf usbdux
+.PHONY:	all test clean install a56 as31 aica ath9k_htc_toolchain ath9k_htc atusb av7110 b43-tools carl9170fw-toolchain carl9170fw cis-tools cis dsp56k ihex2fw isci keyspan_pda openfwwf usbdux
 
 all: aica ath9k_htc av7110 carl9170fw cis dsp56k isci keyspan_pda openfwwf usbdux
 
@@ -36,6 +36,9 @@ ath9k_htc_toolchain:
 ath9k_htc: ath9k_htc_toolchain
 	cd ath9k_htc && $(MAKE) -C target_firmware
 
+atusb:
+	cd atusb && $(MAKE)
+
 av7110:
 	cd av7110 && $(MAKE)
 
@@ -81,6 +84,7 @@ clean:
 	if [ -a as31/Makefile ]; then cd as31 && $(MAKE) clean; fi;
 	cd ath9k_htc && $(MAKE) toolchain-clean
 	cd ath9k_htc && $(MAKE) -C target_firmware clean
+	cd atusb && $(MAKE) clean
 	cd av7110 && $(MAKE) clean
 	cd carl9170fw/toolchain && $(MAKE) clean
 	if [ -a carl9170fw/Makefile ]; then cd carl9170fw && $(MAKE) clean; fi;
diff --git a/WHENCE b/WHENCE
index 2932155..756de43 100644
--- a/WHENCE
+++ b/WHENCE
@@ -112,6 +112,18 @@ From https://github.com/qca/open-ath9k-htc-firmware
 
 --------------------------------------------------------------------------
 
+atusb: Firmware for the ATUSB IEEE 802.15.4 USB Adapter
+http://shop.sysmocom.de/products/atusb/
+
+From http://projects.qi-hardware.com/index.php/p/ben-wpan/source/tree/master/atusb/fw
+
+License: GPL-2.0-or-later
+
+Version: Based on commit 805db6ebf5d80692158acadf88e239da9d3e67af 
+dated September 13 2017
+
+--------------------------------------------------------------------------
+
 Driver: b43 - OpenFWWF -- Free firmware for some Broadcom 43xx series WLAN chips
 
 License: GPLv2
diff --git a/atusb/Makefile b/atusb/Makefile
new file mode 100644
index 0000000..c79cb26
--- /dev/null
+++ b/atusb/Makefile
@@ -0,0 +1,236 @@
+#
+# Makefile - Makefile of the ATUSB firmware
+#
+# Written 2010-2011, 2013 by Werner Almesberger
+# Copyright 2010-2011, 2013 by Werner Almesberger
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+
+SHELL = /bin/bash
+
+NAME = atusb
+DEBUG = false
+
+CFLAGS = -g -mmcu=$(CHIP) -DBOOT_ADDR=$(BOOT_ADDR) \
+	 -Wall -Wextra -Wshadow -Werror -Wno-unused-parameter \
+	 -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes
+
+ifeq ($(DEBUG),true)
+CFLAGS += -DDEBUG
+endif
+
+ifeq ($(NAME),rzusb)
+CHIP=at90usb1287
+CFLAGS += -DRZUSB -DAT86RF230
+else ifeq ($(NAME),hulusb)
+CHIP=at90usb1287
+CFLAGS += -DHULUSB -DAT86RF212
+else
+CHIP=atmega32u2
+CFLAGS += -DATUSB -DAT86RF231
+endif
+HOST=jlime
+BOOT_ADDR=0x7000
+
+AVR_PREFIX = $(BIN_PATH) avr-
+CC = $(AVR_PREFIX)gcc
+OBJCOPY = $(AVR_PREFIX)objcopy
+#OBJDUMP = $(AVR_PREFIX)objdump
+SIZE = $(AVR_PREFIX)size
+
+# BCD notion is 0xJJMM with JJ being major and MM being minor. Thus 0x0020 is
+# version 0.2 */
+USB_BCD_VERSION = 0030
+USB_VENDOR_ID = 20b7
+USB_PRODUCT_ID = 1540
+USB_ID = $(USB_VENDOR_ID):$(USB_PRODUCT_ID)
+
+OBJS = atusb.o board.o board_app.o sernum.o spi.o descr.o ep0.o \
+       dfu_common.o usb.o app-atu2.o mac.o
+BOOT_OBJS = boot.o board.o sernum.o spi.o flash.o dfu.o \
+            dfu_common.o usb.o boot-atu2.o
+
+ifeq ($(DEBUG),true)
+OBJS +=  uart.o
+endif
+
+ifeq ($(NAME),rzusb)
+OBJS += board_rzusb.o
+BOOT_OBJS += board_rzusb.o
+else ifeq ($(NAME),hulusb)
+OBJS += board_hulusb.o
+BOOT_OBJS += board_hulusb.o
+else
+OBJS += board_atusb.o
+BOOT_OBJS += board_atusb.o
+endif
+
+
+vpath %.c usb/
+
+CFLAGS += -Iinclude -Iusb -I.
+
+# ----- Verbosity control -----------------------------------------------------
+
+CC_normal	:= $(CC)
+BUILD_normal	:=
+DEPEND_normal	:= $(CPP) $(CFLAGS) -MM -MG
+
+CC_quiet	= @echo "  CC       " $@ && $(CC_normal)
+BUILD_quiet	= @echo "  BUILD    " $@ && $(BUILD_normal)
+DEPEND_quiet	= @$(DEPEND_normal)
+
+ifeq ($(V),1)
+    CC		= $(CC_normal)
+    BUILD	= $(BUILD_normal)
+    DEPEND	= $(DEPEND_normal)
+else
+    CC		= $(CC_quiet)
+    BUILD	= $(BUILD_quiet)
+    DEPEND	= $(DEPEND_quiet)
+endif
+
+# ----- Rules -----------------------------------------------------------------
+
+.PHONY:		all clean upload prog dfu update version.c bindist
+.PHONY:		prog-app prog-read on off reset
+
+all:		$(NAME).bin boot.hex
+
+$(NAME).elf:	$(OBJS)
+		$(MAKE) version.o
+		$(CC) $(CFLAGS) -o $@ $(OBJS) version.o
+		$(SIZE) $@
+
+boot.elf:	$(BOOT_OBJS)
+		$(CC) $(CFLAGS) -o $@ $(BOOT_OBJS) \
+		  -Wl,--section-start=.text=$(BOOT_ADDR)
+		$(SIZE) $@
+
+%.bin:		%.elf
+		$(BUILD) $(OBJCOPY) -j .text -j .data -O binary $< $@
+		@echo "build #`cat .version`, `ls -l $@`"
+
+%.dfu:		%.bin
+		cp $(NAME).bin $(NAME).dfu
+		dfu-suffix -a $(NAME).dfu -d 0x$(USB_BCD_VERSION) \
+		  -p 0x$(USB_PRODUCT_ID) -v 0x$(USB_VENDOR_ID)
+
+%.hex:		%.elf
+		$(BUILD) $(OBJCOPY) -j .text -j .data -O ihex $< $@
+		@echo "Size: `$(SIZE) -A boot.hex | sed '/Total */s///p;d'` B"
+
+# ----- Cleanup ---------------------------------------------------------------
+
+clean:
+		rm -f $(NAME).bin $(NAME).elf $(NAME).dfu
+		rm -f $(OBJS) $(OBJS:.o=.d)
+		rm -f boot.hex boot.elf
+		rm -f $(BOOT_OBJS) $(BOOT_OBJS:.o=.d)
+		rm -f version.c version.d version.o
+
+# ----- Build version ---------------------------------------------------------
+
+version.c:
+		@if [ -f .version ]; then \
+		    v=`cat .version`; \
+		    expr $$v + 1 >.version; \
+		else \
+		    echo 0 >.version; \
+		fi
+		@[ -s .version ] || echo 0 >.version
+		@echo '/* MACHINE-GENERATED. DO NOT EDIT ! */' >version.c
+		@echo '#include "version.h"' >>version.c
+		@echo "const char *build_date = \"`date`\";" >>version.c
+		@echo "const uint16_t build_number = `cat .version`;" \
+		  >>version.c
+
+# ----- Dependencies ----------------------------------------------------------
+
+MKDEP =									\
+	$(DEPEND) $< |							\
+	  sed 								\
+	    -e 's|^$(basename $(notdir $<)).o:|$@:|'			\
+	    -e '/^\(.*:\)\? */{p;s///;s/ *\\\?$$/ /;s/  */:\n/g;H;}'	\
+	    -e '$${g;p;}'						\
+	    -e d >$(basename $@).d;					\
+	  [ "$${PIPESTATUS[*]}" = "0 0" ] ||				\
+	  { rm -f $(basename $@).d; exit 1; }
+
+%.o:            %.c
+		$(CC) $(CFLAGS) -Os -c $<
+		$(MKDEP)
+
+-include $(OBJS:.o=.d)
+
+# ----- Object file variants --------------------------------------------------
+
+app-%.o:	usb/%.c
+		$(CC) $(CFLAGS) -Os -o $@ -c $<
+		$(MKDEP)
+
+boot-%.o:	usb/%.c
+		$(CC) $(CFLAGS) -DBOOT_LOADER -Os -o $@ -c $<
+		$(MKDEP)
+
+# ----- Distribution ----------------------------------------------------------
+
+BINDIST_BASE=http://downloads.qi-hardware.com/people/werner/wpan/bindist
+ATUSB_BIN_NAME=atusb-`git rev-parse HEAD | cut -c 1-7`.bin
+
+bindist:
+		qippl atusb.bin wpan/bindist/$(ATUSB_BIN_NAME)
+		@echo $(BINDIST_BASE)/$(ATUSB_BIN_NAME)
+		@echo md5sum: `md5sum atusb.bin | sed 's/ .*//'`
+		@echo atrf-id: \
+		  `sed '/.*number = \(.*\);/s//#\1/p;d' version.c` \
+		  `sed '/.*date = "\(.*\)";/s//\1/p;d' version.c`
+
+# ----- Programming and device control ----------------------------------------
+
+upload:		$(NAME).bin boot.hex
+		scp $(NAME).bin boot.hex $(HOST):
+
+# lfuse: external clock, slow start-up
+# hfuse: 4 kB boot loader, reset into boot loader
+# lock: allow everything but SPM to the boot loader
+#       Note: when trying to program 0xef, we get back 0x2f, failing
+#	      verification. So we just program 0x2f.
+
+prog-app:
+		ssh $(HOST) avrdude -F -p $(CHIP) -c nanonote_atusb -e \
+		  -U flash:w:atusb.bin:r \
+		  -U lfuse:w:0x60:m
+
+prog:
+		ssh $(HOST) avrdude -F -p $(CHIP) -c nanonote_atusb -e \
+		  -U flash:w:boot.hex:i \
+		  -U lfuse:w:0x60:m \
+		  -U hfuse:w:0xd8:m \
+		  -U lock:w:0x2f:m
+
+prog-read:
+		ssh $(HOST) avrdude -F -p $(CHIP) -c nanonote_atusb \
+		  -U flash:r:mcu.bin:r
+
+dfu:		$(NAME).dfu
+		dfu-util -d $(USB_ID) -D $(NAME).dfu
+
+update:		$(NAME).bin
+		-atrf-reset -a
+		usbwait -r -i 0.01 -t 5 $(USB_ID)
+		$(MAKE) dfu
+
+on:
+		ssh $(HOST) poke 0x10010318 4
+
+off:
+		ssh $(HOST) poke 0x10010314 4
+
+reset:
+		ssh $(HOST) poke 0x10010318 2048
+		ssh $(HOST) poke 0x10010314 2048
diff --git a/atusb/README b/atusb/README
new file mode 100644
index 0000000..99ceb22
--- /dev/null
+++ b/atusb/README
@@ -0,0 +1,94 @@
+Requires a very recent toolchain, because ATmega32U2 is relatively new.
+
+- Building:
+
+  make
+
+- Uploading the firmware to a Ben (for flashing with the atusb-pgm cable):
+
+  make HOST=<hostname> upload
+
+  Example:
+
+  make HOST=ben upload
+
+  HOST defaults to "jlime".
+
+- Flashing the boot loader:
+
+  Prerequisite: avrdude on the Ben.
+
+  Disconnect the atusb board from USB. Insert the atusb-pgm connector into
+  the Ben. Place the atusb-pgm adapter on the exposed contact pads of the
+  atusb board and push it down. Then run
+
+  make prog
+
+  This takes about 30 seconds. If the programming fails with an error
+  message like "Yikes!  Invalid device signature.", verify that the
+  atusb-pgm board is properly connected and placed, then try again.
+
+- Uploading the application:
+
+  Prerequisite: dfu-util installed on the PC.
+
+  Insert atusb into the PC, then run
+
+  make dfu
+
+  Note: since the boot loader resets the USB bus after timing out,
+  this operation can fail with a message like "No DFU capable USB device
+  found". Just retry, and it will eventually get through.
+
+
+HULUSB notes:
+-------------
+To prepare and flash the firmware on a HULUSB device some additional steps are
+needed;
+
+avr-objcopy -O ihex -R .signature -R .fuse -R .eeprom hulusb.elf hulusb.hex
+dfu-programmer at90usb1287 flash hulusb.hex
+dfu-programmer at90usb1287 reset
+
+--------------------------
+
+Making the toolchain:
+
+# patches according to
+# http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=789527
+
+# some gcc prerequisites
+
+apt-get remove avr-libc gcc-avr binutils-avr
+apt-get install libmpfr-dev libmpc-dev
+
+# binutils
+
+wget http://ftp.gnu.org/gnu/binutils/binutils-2.21.tar.bz2
+tar xfj binutils-2.21.tar.bz2 
+cd binutils-2.21
+./configure --target=avr --disable-nls
+make
+make install
+
+# gcc
+
+wget http://ftpmirror.gnu.org/gcc/gcc-4.5.2/gcc-4.5.2.tar.bz2
+wget -O gcc_452_avr.patch http://gcc.gnu.org/bugzilla/attachment.cgi?id=23050
+tar xfj gcc-4.5.2.tar.bz2
+cd gcc-4.5.2
+patch -p1 -s <../gcc_452_avr.patch
+mkdir obj-avr
+cd obj-avr
+../configure --target=avr --enable-languages=c \
+    --disable-nls --disable-libssp --with-dwarf2
+make
+make install
+
+wget http://download.savannah.gnu.org/releases/avr-libc/avr-libc-1.7.1.tar.bz2
+tar xfj avr-libc-1.7.1.tar.bz2 
+cd avr-libc-1.7.1
+./bootstrap	# the automake at the end takes a while
+./configure --build=`./config.guess` --host=avr
+make
+make install
diff --git a/atusb/an/README b/atusb/an/README
new file mode 100644
index 0000000..8e0d2fc
--- /dev/null
+++ b/atusb/an/README
@@ -0,0 +1,25 @@
+workflow:
+
+- connect zprobe (note: it currently inverts because it didn't have any
+  other chips around. this may change later.)
+
+- capture the USB signals at an interesting moment with a sample rate of
+  50 MSa/s
+
+- zoom into the frame(s) of interest
+
+- download the data with
+  ./get.py
+
+- decode with
+  ./dec.py
+
+  For manual decoding, set the coders to D+ and D- (we need D- for SE0
+  and SE1 detection), then click on a rising clock edge left of the
+  packet and move the cursor to the right.
+
+- if there are problems with the clock, the analog signal and digital
+  signals derived from it can be examined after running dec.py with
+  ./plot
+
+  (Note that the digital zprobe hides any analog anomalies.)
diff --git a/atusb/an/dec.py b/atusb/an/dec.py
new file mode 100755
index 0000000..8534857
--- /dev/null
+++ b/atusb/an/dec.py
@@ -0,0 +1,127 @@
+#!/usr/bin/python
+
+from tmc.wave import *
+from tmc.dxplore import dxplore
+from tmc.decode import d_usb_stream
+
+
+#
+# Clock recovery: we assume that each change in the wave is triggered by a
+# clock edge. We know the clock's nominal period and resynchronize on each
+# edge. Additionally, we can obtain a list of times when a timing violation
+# has occurred.
+#
+# Note that the timing violations logic doesn't make much sense in its present
+# form, since it mainly measures noise (particularly if we're digitizing slow
+# edges) and not clock drift.
+#
+# A more useful metric would be accumulated error from some point of reference
+# or at least the timing of same edges, to eliminate (generally harmless) time
+# offsets introduced by digitizing.
+#
+# So it would probably make more sense for "recover" not to check for timing
+# violations at all, and leave this to more specialized functions.
+#
+def recover(self, period, min = None, max = None, t0 = None):
+    if t0 is None:
+	t0 = self.data[0]
+    v = not self.initial
+    res = []
+    violations = []
+    for t in self.data:
+	v = not v
+	if t <= t0:
+	    continue
+	n = 0
+	while t0 < t-period/2:
+	    res.append(t0)
+	    t0 += period
+	    n += 1
+	if min is not None:
+	    if t0-t > n*min:
+		violations.append(t)
+	if max is not None:
+	    if t-t0 > n*max:
+		violations.append(t)
+	t0 = t
+    return res, violations
+
+
+#
+# Load the analog waves saved by get.py
+#
+wv = waves()
+wv.load("_wv")
+
+#
+# Digitize the waves and save the result.
+#
+dp = wv[0].digitize(1.5, 1.8)
+dm = wv[1].digitize(1.5, 1.8)
+wv = waves(dp, dm, dp-dm)
+wv.save("_dig")
+
+#
+# Also record the differential signal.
+#
+wd = wv[1]-wv[0]
+dd = wd.digitize(-0.5, 0.5)
+wd.save("_diff")
+
+#
+# Run clock recovery on D+/D-. We only need one, but check both to be sure.
+#
+#p = 1/1.5e6
+p = 1/12e6
+dp_t, viol = recover(dp, p, p*0.9, p*1.1)
+print viol
+dm_t, viol = recover(dm, p, p*.9, p*1.1, t0 = dp.data[0])
+print viol
+
+#
+# Shift the clock by half a period, add a few periods to get steady state and
+# SE0s (if any), and then sample the data lines.
+#
+clk = map(lambda t: t+p/2, dp_t)
+clk.extend((clk[-1]+p, clk[-1]+2*p, clk[-1]+3*p))
+dp_bv = dp.get(clk)
+dm_bv = dm.get(clk)
+
+#
+# Save a wave with the recovered clock to make it easier to find the bits in
+# analog graphs.
+#
+dd.data = dp_t;
+dd.save("_clk")
+
+#
+# For decoding, we need a fake bit clock. We generate it by doubling each data
+# bit and generating a L->H transition during this bit.
+#
+dpd = []
+dmd = []
+dck = []
+
+# err, silly, seems that we've mixed up D+ and D- all over the place :-)
+print d_usb_stream(dm_bv[:], dp_bv[:])
+
+for v in dp_bv:
+    dpd.append(v)
+    dpd.append(v)
+    dck.append(0)
+    dck.append(1)
+
+for v in dm_bv:
+    dmd.append(v)
+    dmd.append(v)
+
+#
+# Display the reconstructed digital signal. Note that the absolute time is only
+# correct at the beginning and that relative time is only accurate over
+# intervals in which no significant clock resynchronization has occurred.
+#
+# In fact, dxplore should probably have an option to either turn off time
+# entirely or to display a user-provided time axis. The latter may be a bit
+# tricky to implement.
+#
+dxplore((dmd, dpd, dck), 0, p/2, labels = ("D+", "D-", "CLK"))
diff --git a/atusb/an/get.py b/atusb/an/get.py
new file mode 100755
index 0000000..685e00f
--- /dev/null
+++ b/atusb/an/get.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+from tmc.scope import rigol_ds1000c
+
+#-800, +1600
+s = rigol_ds1000c()
+#s.debug = False
+
+pos = s.hor.pos
+scale = s.hor.scale
+t0 = pos-scale*s.div_hor/2
+t1 = pos+scale*s.div_hor/2
+print t0, t1
+
+#zoom = 10
+#step = scale/s.samples_per_div/zoom
+#print step
+step = 4e-9
+step = 2e-9
+
+w = s.wave((s.ch[0], s.ch[1]), start = t0, end = t1, step = step)
+w[0] = 3.3-w[0]
+w[1] = 3.3-w[1]
+
+s.hor.pos = pos
+s.hor.scale = scale
+
+w[0].label = "D+";
+w[1].label = "D-";
+
+w.save("_wv")
diff --git a/atusb/an/plot b/atusb/an/plot
new file mode 100755
index 0000000..1dea789
--- /dev/null
+++ b/atusb/an/plot
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# Plot output of "dec"
+#
+gnuplot -persist <<EOF
+set style data lines
+plot "_wv" using 1:(\$2-4), \
+  "_dig" using 1:(\$2*3.3-4) lw 2, \
+  "_wv" using 1:3, \
+  "_dig" using 1:(\$3*3.3) lw 2, \
+  "_clk" using 1:(\$2+1) lt 7
+EOF
diff --git a/atusb/atusb.c b/atusb/atusb.c
new file mode 100644
index 0000000..28faf40
--- /dev/null
+++ b/atusb/atusb.c
@@ -0,0 +1,63 @@
+/*
+ * fw/atusb.c - ATUSB initialization and main loop
+ *
+ * Written 2008-2011 by Werner Almesberger
+ * Copyright 2008-2011 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/sleep.h>
+#include <avr/interrupt.h>
+
+#include "usb.h"
+
+#include "board.h"
+#include "sernum.h"
+#include "spi.h"
+#include "atusb/ep0.h"
+
+#ifdef DEBUG
+#include "uart.h"
+#endif
+
+
+int main(void)
+{
+	board_init();
+	board_app_init();
+	reset_rf();
+
+	user_get_descriptor = sernum_get_descr;
+
+	/* now we should be at 8 MHz */
+
+#ifdef DEBUG
+	uart_init();
+	static FILE atben_stdout = FDEV_SETUP_STREAM(uart_write_char, NULL,
+						     _FDEV_SETUP_WRITE);
+	stdout = &atben_stdout;
+#endif
+
+	usb_init();
+	ep0_init();
+#ifdef ATUSB
+	timer_init();
+
+	/* move interrupt vectors to 0 */
+	MCUCR = 1 << IVCE;
+	MCUCR = 0;
+#endif
+
+	sei();
+
+	while (1)
+		sleep_mode();
+}
diff --git a/atusb/board.c b/atusb/board.c
new file mode 100644
index 0000000..c3b8d26
--- /dev/null
+++ b/atusb/board.c
@@ -0,0 +1,120 @@
+/*
+ * fw/board.c - Board-specific functions (for boot loader and application)
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/boot.h>
+
+#define F_CPU   8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "at86rf230.h"
+#include "board.h"
+#include "spi.h"
+
+
+uint8_t board_sernum[42] = { 42, USB_DT_STRING };
+
+/* ----- Register access --------------------------------------------------- */
+
+void change_state(uint8_t new)
+{
+	while ((reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK) ==
+		TRX_STATUS_TRANSITION);
+	reg_write(REG_TRX_STATE, new);
+}
+
+
+uint8_t reg_read(uint8_t reg)
+{
+	uint8_t value;
+
+	spi_begin();
+	spi_send(AT86RF230_REG_READ | reg);
+	value = spi_recv();
+	spi_end();
+
+	return value;
+}
+
+
+uint8_t subreg_read(uint8_t address, uint8_t mask, uint8_t position)
+{
+	/* Read current register value and mask out subregister. */
+	uint8_t register_value = reg_read(address);
+	register_value &= mask;
+	register_value >>= position; /* Align subregister value. */
+
+	return register_value;
+}
+
+
+void reg_write(uint8_t reg, uint8_t value)
+{
+	spi_begin();
+	spi_send(AT86RF230_REG_WRITE | reg);
+	spi_send(value);
+	spi_end();
+}
+
+
+void subreg_write(uint8_t address, uint8_t mask, uint8_t position, uint8_t value)
+{
+	/* Read current register value and mask area outside the subregister. */
+	uint8_t register_value = reg_read(address);
+	register_value &= ~mask;
+
+	/* Start preparing the new subregister value. shift in place and mask. */
+	value <<= position;
+	value &= mask;
+
+	value |= register_value; /* Set the new subregister value. */
+
+	/* Write the modified register value. */
+	reg_write(address, value);
+}
+
+
+void panic(void)
+{
+	cli();
+	while (1) {
+		SET(LED);
+		_delay_ms(100);
+		CLR(LED);
+		_delay_ms(100);
+	}
+}
+
+
+static char hex(uint8_t nibble)
+{
+	return nibble < 10 ? '0'+nibble : 'a'+nibble-10;
+}
+
+
+void get_sernum(void)
+{
+	uint8_t sig;
+	uint8_t i;
+
+	for (i = 0; i != 10; i++) {
+		sig = boot_signature_byte_get(i+0xe);
+		board_sernum[(i << 2)+2] = hex(sig >> 4);
+		board_sernum[(i << 2)+4] = hex(sig & 0xf);
+	}
+}
diff --git a/atusb/board.h b/atusb/board.h
new file mode 100644
index 0000000..dbcd410
--- /dev/null
+++ b/atusb/board.h
@@ -0,0 +1,95 @@
+/*
+ * fw/board.h - Board-specific functions and definitions
+ *
+ * Written 2008-2011, 2013, 2013 by Werner Almesberger
+ * Copyright 2008-2011, 2013, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef BOARD_H
+#define	BOARD_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <atusb/atusb.h>
+
+#ifdef ATUSB
+#include "board_atusb.h"
+#endif
+#ifdef RZUSB
+#include "board_rzusb.h"
+#endif
+#ifdef HULUSB
+#include "board_hulusb.h"
+#endif
+
+#define	SET_2(p, b)	PORT##p |= 1 << (b)
+#define	CLR_2(p, b)	PORT##p &= ~(1 << (b))
+#define	IN_2(p, b)	DDR##p &= ~(1 << (b))
+#define	OUT_2(p, b)	DDR##p |= 1 << (b)
+#define	PIN_2(p, b)	((PIN##p >> (b)) & 1)
+
+#define	SET_1(p, b)	SET_2(p, b)
+#define	CLR_1(p, b)	CLR_2(p, b)
+#define	IN_1(p, b)	IN_2(p, b)
+#define	OUT_1(p, b)	OUT_2(p, b)
+#define	PIN_1(p, b)	PIN_2(p, b)
+
+#define	SET(n)		SET_1(n##_PORT, n##_BIT)
+#define	CLR(n)		CLR_1(n##_PORT, n##_BIT)
+#define	IN(n)		IN_1(n##_PORT, n##_BIT)
+#define	OUT(n)		OUT_1(n##_PORT, n##_BIT)
+#define	PIN(n)		PIN_1(n##_PORT, n##_BIT)
+
+
+#define	USB_VENDOR	ATUSB_VENDOR_ID
+#define	USB_PRODUCT	ATUSB_PRODUCT_ID
+
+#define	DFU_USB_VENDOR	USB_VENDOR
+#define	DFU_USB_PRODUCT	USB_PRODUCT
+
+
+#define	BOARD_MAX_mA	40
+
+#ifdef BOOT_LOADER
+#define	NUM_EPS	1
+#else
+#define	NUM_EPS	2
+#endif
+
+#define	HAS_BOARD_SERNUM
+
+extern uint8_t board_sernum[42];
+extern uint8_t irq_serial;
+
+
+void reset_rf(void);
+void reset_cpu(void);
+uint8_t read_irq(void);
+void slp_tr(void);
+
+void led(bool on);
+void panic(void);
+
+uint64_t timer_read(void);
+void timer_init(void);
+
+bool gpio(uint8_t port, uint8_t data, uint8_t dir, uint8_t mask, uint8_t *res);
+void gpio_cleanup(void);
+
+void get_sernum(void);
+
+void board_app_init(void);
+
+uint8_t reg_read(uint8_t reg);
+uint8_t subreg_read(uint8_t address, uint8_t mask, uint8_t position);
+void reg_write(uint8_t reg, uint8_t value);
+void subreg_write(uint8_t address, uint8_t mask, uint8_t position, uint8_t value);
+void change_state(uint8_t new);
+
+#endif /* !BOARD_H */
diff --git a/atusb/board_app.c b/atusb/board_app.c
new file mode 100644
index 0000000..1fa9bf4
--- /dev/null
+++ b/atusb/board_app.c
@@ -0,0 +1,173 @@
+/*
+ * fw/board_app.c - Board-specific functions (for the application)
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+#define F_CPU   8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "at86rf230.h"
+#include "spi.h"
+#include "mac.h"
+#include "board.h"
+
+
+static volatile uint32_t timer_h = 0;	/* 2^(16+32) / 8 MHz = ~1.1 years */
+
+
+void reset_cpu(void)
+{
+	WDTCSR = 1 << WDE;
+}
+
+
+uint8_t read_irq(void)
+{
+	return PIN(IRQ_RF);
+}
+
+
+void slp_tr(void)
+{
+	SET(SLP_TR);
+	CLR(SLP_TR);
+}
+
+
+ISR(TIMER1_OVF_vect)
+{
+	timer_h++;
+}
+
+
+uint64_t timer_read(void)
+{
+	uint32_t high;
+	uint8_t low, mid;
+
+	do {
+		if (TIFR1 & (1 << TOV1)) {
+			TIFR1 = 1 << TOV1;
+			timer_h++;
+		}
+		high = timer_h;
+		low = TCNT1L;
+		mid = TCNT1H;
+	}
+	while (TIFR1 & (1 << TOV1));
+
+	/*
+	 * We need all these casts because the intermediate results are handled
+	 * as if they were signed and thus get sign-expanded. Sounds wrong-ish.
+	 */
+	return (uint64_t) high << 16 | (uint64_t) mid << 8 | (uint64_t) low;
+}
+
+
+void timer_init(void)
+{
+	/* configure timer 1 as a free-running CLK counter */
+
+	TCCR1A = 0;
+	TCCR1B = 1 << CS10;
+
+	/* enable timer overflow interrupt */
+
+	TIMSK1 = 1 << TOIE1;
+}
+
+
+bool gpio(uint8_t port, uint8_t data, uint8_t dir, uint8_t mask, uint8_t *res)
+{
+	EIMSK = 0; /* recover INT_RF to ATUSB_GPIO_CLEANUP or an MCU reset */
+
+	switch (port) {
+	case 1:
+		DDRB = (DDRB & ~mask) | dir;
+		PORTB = (PORTB & ~mask) | data;
+		break;
+	case 2:
+		DDRC = (DDRC & ~mask) | dir;
+		PORTC = (PORTC & ~mask) | data;
+		break;
+	case 3:
+		DDRD = (DDRD & ~mask) | dir;
+		PORTD = (PORTD & ~mask) | data;
+		break;
+	default:
+		return 0;
+	}
+
+	/* disable the UART so that we can meddle with these pins as well. */
+	spi_off();
+	_delay_ms(1);
+
+	switch (port) {
+	case 1:
+		res[0] = PINB;
+		res[1] = PORTB;
+		res[2] = DDRB;
+		break;
+	case 2:
+		res[0] = PINC;
+		res[1] = PORTC;
+		res[2] = DDRC;
+		break;
+	case 3:
+		res[0] = PIND;
+		res[1] = PORTD;
+		res[2] = DDRD;
+		break;
+	}
+
+	return 1;
+}
+
+
+void gpio_cleanup(void)
+{
+	EIMSK = 1 << 0;
+}
+
+
+static void done(void *user)
+{
+	led(0);
+}
+
+
+uint8_t irq_serial;
+
+#if defined(ATUSB) || defined(HULUSB)
+ISR(INT0_vect)
+#endif
+#ifdef RZUSB
+ISR(TIMER1_CAPT_vect)
+#endif
+{
+	if (mac_irq) {
+		if (mac_irq())
+			return;
+	}
+	if (eps[1].state == EP_IDLE) {
+		led(1);
+		irq_serial = (irq_serial+1) | 0x80;
+		usb_send(&eps[1], &irq_serial, 1, done, NULL);
+	}
+}
diff --git a/atusb/board_atusb.c b/atusb/board_atusb.c
new file mode 100644
index 0000000..a02fb7f
--- /dev/null
+++ b/atusb/board_atusb.c
@@ -0,0 +1,162 @@
+/*
+ * fw/board_atusb.c - ATUSB Board-specific functions (for boot loader and application)
+ *
+ * Written 2016 by Stefan Schmidt
+ * Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/boot.h>
+
+#define F_CPU   8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "at86rf230.h"
+#include "board.h"
+#include "spi.h"
+#include "usb/usb.h"
+
+static bool spi_initialized = 0;
+
+void reset_rf(void)
+{
+	/* set up all the outputs; default port value is 0 */
+
+	DDRB = 0;
+	DDRC = 0;
+	DDRD = 0;
+	PORTB = 0;
+	PORTC = 0;
+	PORTD = 0;
+
+	OUT(LED);
+	OUT(nRST_RF);   /* this also resets the transceiver */
+	OUT(SLP_TR);
+
+	spi_init();
+
+	/* AT86RF231 data sheet, 12.4.13, reset pulse width: 625 ns (min) */
+
+	CLR(nRST_RF);
+	_delay_us(2);
+	SET(nRST_RF);
+
+	/* 12.4.14: SPI access latency after reset: 625 ns (min) */
+
+	_delay_us(2);
+
+	/* we must restore TRX_CTRL_0 after each reset (9.6.4) */
+
+	set_clkm();
+}
+
+void led(bool on)
+{
+	if (on)
+		SET(LED);
+	else
+		CLR(LED);
+}
+
+void set_clkm(void)
+{
+	/* switch CLKM to 8 MHz */
+
+	/*
+	 * @@@ Note: Atmel advise against changing the external clock in
+	 * mid-flight. We should therefore switch to the RC clock first, then
+	 * crank up the external clock, and finally switch back to the external
+	 * clock. The clock switching procedure is described in the ATmega32U2
+	 * data sheet in secton 8.2.2.
+	 */
+	spi_begin();
+	spi_send(AT86RF230_REG_WRITE | REG_TRX_CTRL_0);
+	spi_send(CLKM_CTRL_8MHz);
+	spi_end();
+}
+
+void board_init(void)
+{
+	/* Disable the watchdog timer */
+
+	MCUSR = 0;		/* Remove override */
+	WDTCSR |= 1 << WDCE;	/* Enable change */
+	WDTCSR = 1 << WDCE;	/* Disable watchdog while still enabling
+				   change */
+
+	CLKPR = 1 << CLKPCE;
+	/* We start with a 1 MHz/8 clock. Disable the prescaler. */
+	CLKPR = 0;
+
+	get_sernum();
+}
+
+void spi_begin(void)
+{
+	if (!spi_initialized)
+		spi_init();
+	CLR(nSS);
+}
+
+void spi_off(void)
+{
+	spi_initialized = 0;
+	UCSR1B = 0;
+}
+
+void spi_init(void)
+{
+	SET(nSS);
+	OUT(SCLK);
+	OUT(MOSI);
+	OUT(nSS);
+	IN(MISO);
+
+	UBRR1 = 0;	/* set bit rate to zero to begin */
+	UCSR1C = 1 << UMSEL11 | 1 << UMSEL10;
+			/* set MSPI, MSB first, SPI data mode 0 */
+	UCSR1B = 1 << RXEN1 | 1 << TXEN1;
+			/* enable receiver and transmitter */
+	UBRR1 = 0;	/* reconfirm the bit rate */
+
+	spi_initialized = 1;
+}
+
+void usb_init(void)
+{
+	USBCON |= 1 << FRZCLK;		/* freeze the clock */
+
+	/* enable the PLL and wait for it to lock */
+	PLLCSR &= ~(1 << PLLP2 | 1 << PLLP1 | 1 << PLLP0);
+	PLLCSR |= 1 << PLLE;
+	while (!(PLLCSR & (1 << PLOCK)));
+
+	USBCON &= ~(1 << USBE);		/* reset the controller */
+	USBCON |= 1 << USBE;
+
+	USBCON &= ~(1 << FRZCLK);	/* thaw the clock */
+
+	UDCON &= ~(1 << DETACH);	/* attach the pull-up */
+	UDIEN = 1 << EORSTE;		/* enable device interrupts  */
+//	UDCON |= 1 << RSTCPU;		/* reset CPU on bus reset */
+
+	ep_init();
+}
+
+void board_app_init(void)
+{
+	/* enable INT0, trigger on rising edge */
+	EICRA = 1 << ISC01 | 1 << ISC00;
+	EIMSK = 1 << 0;
+}
diff --git a/atusb/board_atusb.h b/atusb/board_atusb.h
new file mode 100644
index 0000000..e5974c7
--- /dev/null
+++ b/atusb/board_atusb.h
@@ -0,0 +1,48 @@
+/*
+ * fw/board_atusb.h - ATUSB Board-specific functions and definitions
+ *
+ * Written 2016 by Stefan Schmidt
+ * Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef BOARD_ATUSB_H
+#define	BOARD_ATUSB_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define	LED_PORT	B
+#define	LED_BIT		  6
+#define	nRST_RF_PORT	C
+#define	nRST_RF_BIT	  7
+#define	SLP_TR_PORT	B
+#define	SLP_TR_BIT	  4
+
+#define SCLK_PORT	D
+#define SCLK_BIT	  5
+#define	MOSI_PORT	D
+#define	MOSI_BIT	  3
+
+#define	MISO_PORT	D
+#define	MISO_BIT	  2
+#define	nSS_PORT	D
+#define	nSS_BIT		  1
+#define	IRQ_RF_PORT	D
+#define	IRQ_RF_BIT	  0
+
+#define SPI_WAIT_DONE()	while (!(UCSR1A & 1 << RXC1))
+#define SPI_DATA	UDR1
+
+void set_clkm(void);
+void board_init(void);
+
+void spi_begin(void);
+void spi_off(void);
+void spi_init(void);
+
+#endif /* !BOARD_H */
diff --git a/atusb/board_hulusb.c b/atusb/board_hulusb.c
new file mode 100644
index 0000000..084714e
--- /dev/null
+++ b/atusb/board_hulusb.c
@@ -0,0 +1,179 @@
+/*
+ * fw/board_hulusb.c - Busware HUL Board-specific functions (for boot loader and application)
+ *
+ * Written 2017 by Filzmaier Josef
+ * Based on fw/board_rzusb written and Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/boot.h>
+
+#define F_CPU   8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "at86rf230.h"
+#include "board.h"
+#include "spi.h"
+#include "usb/usb.h"
+
+static bool spi_initialized = 0;
+
+void reset_rf(void)
+{
+	/* set up all the outputs; default port value is 0 */
+
+	DDRB = 0;
+	DDRC = 0;
+	DDRD = 0;
+	PORTB = 0;
+	PORTC = 0;
+	PORTD = 0;
+
+	OUT(LED_RED);
+	OUT(LED_GREEN);
+	SET(LED_RED); /* Leds are active low on HULUSB board */
+	CLR(LED_GREEN); /* Green Led indicates the dongle is running */
+	OUT(nRST_RF);   /* this also resets the transceiver */
+	OUT(SLP_TR);
+
+	spi_init();
+
+	/* AT86RF212 data sheet, Appendix B, p166 Power-On Reset procedure */
+	/*-----------------------------------------------------------------*/
+	CLR(SLP_TR);
+	SET(nRST_RF);
+	SET(nSS);
+	_delay_us(400);
+
+	CLR(nRST_RF);
+	_delay_us(2);
+	SET(nRST_RF);
+
+	/* 5.1.4.5: Wait t10: 625 ns (min) */
+
+	_delay_us(2);
+
+	reg_write(REG_TRX_CTRL_0, 0x19);
+
+	change_state(TRX_CMD_FORCE_TRX_OFF);
+	/*-----------------------------------------------------------------*/
+
+	/* we must restore TRX_CTRL_0 after each reset (7.7.4) */
+
+	set_clkm();
+}
+
+void led_red(bool on) {
+	if (on)
+		CLR(LED_RED);
+	else
+		SET(LED_RED);
+}
+
+void led_green(bool on) {
+	if (on)
+		CLR(LED_GREEN);
+	else
+		SET(LED_GREEN);
+}
+
+void led(bool on)
+{
+	led_red(on);
+}
+
+void set_clkm(void)
+{
+	/* CLKM is not connected on BUSWARE HUL and therefore it is running in
+	 * async mode. */
+	reg_write(REG_TRX_CTRL_0, 0x00);
+
+	/* TX_AUTO_CRC_ON, default disabled */
+	subreg_write(SR_TX_AUTO_CRC_ON, 1);
+}
+
+void board_init(void)
+{
+	/* Disable the watchdog timer */
+
+	MCUSR = 0;		/* Remove override */
+	WDTCSR |= 1 << WDCE;	/* Enable change */
+	WDTCSR = 1 << WDCE;	/* Disable watchdog while still enabling
+	change */
+
+	CLKPR = 1 << CLKPCE;
+	/* We start with a 16 MHz/8 clock. Put the prescaler to 2. */
+	CLKPR = 1 << CLKPS0;
+
+	get_sernum();
+}
+
+void spi_begin(void)
+{
+	if (!spi_initialized)
+		spi_init();
+	CLR(nSS);
+}
+
+void spi_off(void)
+{
+	spi_initialized = 0;
+	SPCR &= ~(1 << SPE);
+}
+
+void spi_init(void)
+{
+	SET(nSS);
+	OUT(SCLK);
+	OUT(MOSI);
+	OUT(nSS);
+	IN(MISO);
+
+	SPCR = (1 << SPE) | (1 << MSTR);
+	SPSR = (1 << SPI2X);
+
+	spi_initialized = 1;
+}
+
+void usb_init(void)
+{
+	USBCON |= 1 << FRZCLK;		/* freeze the clock */
+
+	/* enable the PLL and wait for it to lock */
+	/* TODO sheet page 50 For Atmel AT90USB128x only. Do not use with Atmel AT90USB64x. */
+	/*  FOR 8 XTAL Mhz only!!! */
+	PLLCSR = ((1 << PLLP1) | (1 << PLLP0));
+	PLLCSR |= 1 << PLLE;
+	while (!(PLLCSR & (1 << PLOCK)));
+
+	UHWCON |= (1 << UVREGE);
+
+	USBCON &= ~((1 << USBE) | (1 << OTGPADE));		/* reset the controller */
+	USBCON |= ((1 << USBE) | (1 << OTGPADE));
+
+	USBCON &= ~(1 << FRZCLK);	/* thaw the clock */
+
+	UDCON &= ~(1 << DETACH);	/* attach the pull-up */
+	UDIEN = 1 << EORSTE;		/* enable device interrupts  */
+	//	UDCON |= 1 << RSTCPU;		/* reset CPU on bus reset */
+
+	ep_init();
+}
+
+void board_app_init(void)
+{
+	/* enable INT0, trigger on rising edge */
+	EICRA = 1 << ISC01 | 1 << ISC00;
+	EIMSK = 1 << INT0;
+}
diff --git a/atusb/board_hulusb.h b/atusb/board_hulusb.h
new file mode 100644
index 0000000..a1dadf0
--- /dev/null
+++ b/atusb/board_hulusb.h
@@ -0,0 +1,66 @@
+/*
+ * fw/board_hulusb.h - Busware HUL Board-specific functions (for boot loader and application)
+ *
+ * Written 2017 by Filzmaier Josef
+ * Based on fw/board_rzusb written and Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef BOARD_HULUSB_H
+#define	BOARD_HULUSB_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define LED_RED_PORT		A
+#define LED_GREEN_PORT		A
+#define LED_RED_BIT		3
+#define LED_GREEN_BIT 		4
+#define LED_PORT		LED_RED_PORT
+#define LED_BIT		  	LED_RED_BIT
+
+#define nRST_RF_PORT		B
+#define nRST_RF_BIT	  	5
+#define SLP_TR_PORT		B
+#define SLP_TR_BIT	  	4
+
+#define SCLK_PORT		B
+#define SCLK_BIT	  	1
+#define MOSI_PORT		B
+#define MOSI_BIT	  	2
+
+#define MISO_PORT		B
+#define MISO_BIT	  	3
+#define nSS_PORT		B
+#define nSS_BIT		  	0
+#define IRQ_RF_PORT		D
+#define IRQ_RF_BIT	  	4
+
+#define SR_TX_AUTO_CRC_ON	0x04, 0x20, 5
+#define SR_CHANNEL		0x08, 0x1f, 0
+
+#define RG_CC_CTRL_1		(0x14)
+
+#define SPI_WAIT_DONE()	while ((SPSR & (1 << SPIF)) == 0)
+#define SPI_DATA	SPDR
+
+void set_clkm(void);
+void board_init(void);
+
+void led_red(bool on);
+void led_green(bool on);
+
+void spi_begin(void);
+void spi_off(void);
+void spi_init(void);
+
+#ifdef DEBUG
+void printStatus(void);
+#define PRINT_STATUS() printStatus()
+#endif
+
+#endif /* !BOARD_HULUSB_H */
diff --git a/atusb/board_rzusb.c b/atusb/board_rzusb.c
new file mode 100644
index 0000000..e83d6fa
--- /dev/null
+++ b/atusb/board_rzusb.c
@@ -0,0 +1,169 @@
+/*
+ * fw/board_rzusb.c - RZUSB Board-specific functions (for boot loader and application)
+ *
+ * Written 2016 by Stefan Schmidt
+ * Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/boot.h>
+
+#define F_CPU   8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "at86rf230.h"
+#include "board.h"
+#include "spi.h"
+#include "usb/usb.h"
+
+static bool spi_initialized = 0;
+
+void reset_rf(void)
+{
+	/* set up all the outputs; default port value is 0 */
+
+	DDRB = 0;
+	DDRC = 0;
+	DDRD = 0;
+	PORTB = 0;
+	PORTC = 0;
+	PORTD = 0;
+
+	OUT(LED);
+	OUT(nRST_RF);   /* this also resets the transceiver */
+	OUT(SLP_TR);
+
+	spi_init();
+
+	/* AT86RF231 data sheet, 12.4.13, reset pulse width: 625 ns (min) */
+
+	CLR(nRST_RF);
+	_delay_us(2);
+	SET(nRST_RF);
+
+	/* 12.4.14: SPI access latency after reset: 625 ns (min) */
+
+	_delay_us(2);
+
+	/* we must restore TRX_CTRL_0 after each reset (9.6.4) */
+
+	set_clkm();
+}
+
+void led(bool on)
+{
+	if (on)
+		SET(LED);
+	else
+		CLR(LED);
+}
+
+void set_clkm(void)
+{
+	/* switch CLKM to 8 MHz */
+
+	/*
+	 * @@@ Note: Atmel advise against changing the external clock in
+	 * mid-flight. We should therefore switch to the RC clock first, then
+	 * crank up the external clock, and finally switch back to the external
+	 * clock. The clock switching procedure is described in the ATmega32U2
+	 * data sheet in secton 8.2.2.
+	 */
+	spi_begin();
+	spi_send(AT86RF230_REG_WRITE | REG_TRX_CTRL_0);
+	spi_send(0x10);
+	spi_end();
+
+	/* TX_AUTO_CRC_ON, default disabled */
+	spi_begin();
+	spi_send(AT86RF230_REG_WRITE | 0x05);
+	spi_send(0x80);
+	spi_end();
+}
+
+void board_init(void)
+{
+	/* Disable the watchdog timer */
+
+	MCUSR = 0;		/* Remove override */
+	WDTCSR |= 1 << WDCE;	/* Enable change */
+	WDTCSR = 1 << WDCE;	/* Disable watchdog while still enabling
+				   change */
+
+	CLKPR = 1 << CLKPCE;
+	/* We start with a 16 MHz/8 clock. Put the prescaler to 2. */
+	CLKPR = 1 << CLKPS0;
+
+	get_sernum();
+}
+
+void spi_begin(void)
+{
+	if (!spi_initialized)
+		spi_init();
+	CLR(nSS);
+}
+
+void spi_off(void)
+{
+	spi_initialized = 0;
+	SPCR &= ~(1 << SPE);
+}
+
+void spi_init(void)
+{
+	SET(nSS);
+	OUT(SCLK);
+	OUT(MOSI);
+	OUT(nSS);
+	IN(MISO);
+
+	SPCR = (1 << SPE) | (1 << MSTR);
+	SPSR = (1 << SPI2X);
+
+	spi_initialized = 1;
+}
+
+void usb_init(void)
+{
+	USBCON |= 1 << FRZCLK;		/* freeze the clock */
+
+	/* enable the PLL and wait for it to lock */
+	/* TODO sheet page 50 For Atmel AT90USB128x only. Do not use with Atmel AT90USB64x. */
+	/*  FOR 8 XTAL Mhz only!!! */
+	PLLCSR = ((1 << PLLP1) | (1 << PLLP0));
+	PLLCSR |= 1 << PLLE;
+	while (!(PLLCSR & (1 << PLOCK)));
+
+	UHWCON |= (1 << UVREGE);
+
+	USBCON &= ~((1 << USBE) | (1 << OTGPADE));		/* reset the controller */
+	USBCON |= ((1 << USBE) | (1 << OTGPADE));
+
+	USBCON &= ~(1 << FRZCLK);	/* thaw the clock */
+
+	UDCON &= ~(1 << DETACH);	/* attach the pull-up */
+	UDIEN = 1 << EORSTE;		/* enable device interrupts  */
+//	UDCON |= 1 << RSTCPU;		/* reset CPU on bus reset */
+
+	ep_init();
+}
+
+void board_app_init(void)
+{
+	/* enable timer input capture 1, trigger on rising edge */
+	TCCR1B = (1 << ICES1);
+	TIFR1 = (1 << ICF1);
+	TIMSK1 = (1 << ICIE1);
+}
diff --git a/atusb/board_rzusb.h b/atusb/board_rzusb.h
new file mode 100644
index 0000000..c2e518f
--- /dev/null
+++ b/atusb/board_rzusb.h
@@ -0,0 +1,48 @@
+/*
+ * fw/board_rzusb.h - RZUSB Board-specific functions and definitions
+ *
+ * Written 2016 by Stefan Schmidt
+ * Copyright 2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef BOARD_RZUSB_H
+#define	BOARD_RZUSB_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define	LED_PORT	D
+#define	LED_BIT		  7
+#define	nRST_RF_PORT	B
+#define	nRST_RF_BIT	  5
+#define	SLP_TR_PORT	B
+#define	SLP_TR_BIT	  4
+
+#define SCLK_PORT	B
+#define SCLK_BIT	  1
+#define	MOSI_PORT	B
+#define	MOSI_BIT	  2
+
+#define	MISO_PORT	B
+#define	MISO_BIT	  3
+#define	nSS_PORT	B
+#define	nSS_BIT		  0
+#define	IRQ_RF_PORT	D
+#define	IRQ_RF_BIT	  4
+
+#define SPI_WAIT_DONE()	while ((SPSR & (1 << SPIF)) == 0)
+#define SPI_DATA	SPDR
+
+void set_clkm(void);
+void board_init(void);
+
+void spi_begin(void);
+void spi_off(void);
+void spi_init(void);
+
+#endif /* !BOARD_H */
diff --git a/atusb/boot.c b/atusb/boot.c
new file mode 100644
index 0000000..6826ac6
--- /dev/null
+++ b/atusb/boot.c
@@ -0,0 +1,77 @@
+/*
+ * fw/boot.c - DFU boot loader for ATUSB
+ *
+ * Written 2008-2011 by Werner Almesberger
+ * Copyright 2008-2011 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdint.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+
+#define F_CPU   8000000UL
+#include <util/delay.h>
+
+#include "usb.h"
+#include "dfu.h"
+
+#include "board.h"
+#include "spi.h"
+#include "atusb/ep0.h"
+
+
+#define	MS_TO_LOOPS(ms) ((uint32_t) (ms)*335)
+
+
+static void (*run_payload)(void) = 0;
+
+
+int main(void)
+{
+	/*
+	 * pgm_read_byte gets cached and there doesn't seem to be any other
+	 * way to dissuade gcc from doing this.
+	 */
+	volatile int zero = 0;
+	uint32_t loop = 0;
+
+	board_init();
+	reset_rf();
+
+	/* now we should be at 8 MHz */
+
+	usb_init();
+	dfu_init();
+
+	/* move interrupt vectors to the boot loader */
+	MCUCR = 1 << IVCE;
+	MCUCR = 1 << IVSEL;
+
+	sei();
+
+	led(1);
+
+	while (loop != MS_TO_LOOPS(2500)) {
+		if (dfu.state == dfuIDLE && pgm_read_byte(zero) != 0xff)
+			loop++;
+		else
+			loop = 0;
+	}
+
+	led(0);
+
+	cli();
+
+	usb_reset();
+	run_payload();
+
+	while (1);	/* not reached */
+}
diff --git a/atusb/descr.c b/atusb/descr.c
new file mode 100644
index 0000000..f96b0ee
--- /dev/null
+++ b/atusb/descr.c
@@ -0,0 +1,104 @@
+/*
+ * fw/descr.c - USB descriptors
+ *
+ * Written 2008-2011, 2014 by Werner Almesberger
+ * Copyright 2008-2011, 2014 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include "usb.h"
+#include "dfu.h"
+#include "board.h"
+
+
+#define LE(x) ((uint16_t) (x) & 0xff), ((uint16_t) (x) >> 8)
+
+/*
+ * Device descriptor
+ */
+
+const uint8_t device_descriptor[18] = {
+	18,			/* bLength */
+	USB_DT_DEVICE,		/* bDescriptorType */
+	LE(0x200),		/* bcdUSB */
+	USB_CLASS_VENDOR_SPEC,	/* bDeviceClass */
+	0x00,			/* bDeviceSubClass */
+	0x00,			/* bDeviceProtocol */
+	EP0_SIZE,		/* bMaxPacketSize */
+	LE(USB_VENDOR),		/* idVendor */
+	LE(USB_PRODUCT),	/* idProduct */
+	LE(0x0001),		/* bcdDevice */
+	0,			/* iManufacturer */
+	0,			/* iProduct */
+#ifdef HAS_BOARD_SERNUM
+	1,			/* iSerialNumber */
+#else
+	0,			/* iSerialNumber */
+#endif
+	1			/* bNumConfigurations */
+};
+
+
+/*
+ * Our configuration
+ *
+ * We're always bus-powered.
+ */
+
+const uint8_t config_descriptor[] = {
+	9,			/* bLength */
+	USB_DT_CONFIG,		/* bDescriptorType */
+#if 0
+	LE(9+9+7+7),		/* wTotalLength */
+#else
+	LE(9+9+7+9),		/* wTotalLength */
+#endif
+	2,			/* bNumInterfaces */
+	1,			/* bConfigurationValue (> 0 !) */
+	0,			/* iConfiguration */
+	USB_ATTR_BUS_POWERED,	/* bmAttributes */
+	((BOARD_MAX_mA)+1)/2,	/* bMaxPower */
+
+	/* Interface #0 */
+
+	9,			/* bLength */
+	USB_DT_INTERFACE,	/* bDescriptorType */
+	0,			/* bInterfaceNumber */
+	0,			/* bAlternateSetting */
+	1,			/* bNumEndpoints */
+	USB_CLASS_VENDOR_SPEC,	/* bInterfaceClass */
+	0,			/* bInterfaceSubClass */
+	0,			/* bInterfaceProtocol */
+	0,			/* iInterface */
+
+#if 0
+	/* EP OUT */
+
+	7,			/* bLength */
+	USB_DT_ENDPOINT,	/* bDescriptorType */
+	0x01,			/* bEndPointAddress */
+	0x02,			/* bmAttributes (bulk) */
+	LE(EP1_SIZE),		/* wMaxPacketSize */
+	0,			/* bInterval */
+#endif
+
+#if 1
+	/* EP IN */
+
+	7,			/* bLength */
+	USB_DT_ENDPOINT,	/* bDescriptorType */
+	0x81,			/* bEndPointAddress */
+	0x02,			/* bmAttributes (bulk) */
+	LE(EP1_SIZE),		/* wMaxPacketSize */
+	0,			/* bInterval */
+#endif
+
+	/* Interface #1 */
+
+	DFU_ITF_DESCR(1, 0, dfu_proto_runtime, 0)
+};
diff --git a/atusb/ep0.c b/atusb/ep0.c
new file mode 100644
index 0000000..fa43f3b
--- /dev/null
+++ b/atusb/ep0.c
@@ -0,0 +1,338 @@
+/*
+ * fw/ep0.c - EP0 extension protocol
+ *
+ * Written 2008-2011, 2013 by Werner Almesberger
+ * Copyright 2008-2011, 2013 Werner Almesberger
+ * Copyright 2015-2016 Stefan Schmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <avr/io.h>
+#include <avr/eeprom.h>
+
+#define F_CPU   8000000UL
+#include <util/delay.h>
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#include "usb.h"
+#include "dfu.h"
+
+#include "at86rf230.h"
+#include "atusb/ep0.h"
+#include "version.h"
+#include "board.h"
+#include "sernum.h"
+#include "spi.h"
+#include "mac.h"
+
+#ifdef ATUSB
+#define	HW_TYPE		ATUSB_HW_TYPE_110131
+#endif
+
+#ifdef RZUSB
+#define	HW_TYPE		ATUSB_HW_TYPE_RZUSB
+#endif
+
+#ifdef HULUSB
+#define HW_TYPE		ATUSB_HW_TYPE_HULUSB
+#endif
+
+#ifdef DEBUG
+#include "uart.h"
+#include <stdio.h>
+#define debug(FORMAT,args...) printf(FORMAT,##args)
+#define error(FORMAT,args...) printf(FORMAT,##args)
+#else
+#define debug(...)
+#define error(...)
+#endif
+
+
+static const uint8_t id[] = { EP0ATUSB_MAJOR, EP0ATUSB_MINOR, HW_TYPE };
+static uint8_t buf[MAX_PSDU+3]; /* command, PHDR, and LQI */
+static uint8_t size;
+
+
+static void do_eeprom_write(void *user)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		eeprom_update_byte((uint8_t*)i, buf[i]);
+}
+
+static void do_buf_write(void *user)
+{
+	uint8_t i;
+
+	spi_begin();
+	for (i = 0; i != size; i++)
+		spi_send(buf[i]);
+	spi_end();
+}
+
+
+#define	BUILD_OFFSET	7	/* '#' plus "65535" plus ' ' */
+
+
+static bool my_setup(const struct setup_request *setup)
+{
+	uint16_t req = setup->bmRequestType | setup->bRequest << 8;
+	unsigned tmp;
+	uint8_t i;
+	uint64_t tmp64;
+
+	switch (req) {
+	case ATUSB_FROM_DEV(ATUSB_ID):
+		debug("ATUSB_ID\n");
+		if (setup->wLength > 3)
+			return 0;
+		usb_send(&eps[0], id, setup->wLength, NULL, NULL);
+		return 1;
+	case ATUSB_FROM_DEV(ATUSB_BUILD):
+		debug("ATUSB_BUILD\n");
+		tmp = build_number;
+		for (i = BUILD_OFFSET-2; tmp; i--) {
+			buf[i] = (tmp % 10)+'0';
+			tmp /= 10;
+		}
+		buf[i] = '#';
+		buf[BUILD_OFFSET-1] = ' ';
+		for (size = 0; build_date[size]; size++)
+			buf[BUILD_OFFSET+size] = build_date[size];
+		size += BUILD_OFFSET-i;
+		if (size > setup->wLength)
+			return 0;
+		usb_send(&eps[0], buf+i, size, NULL, NULL);
+		return 1;
+
+	case ATUSB_TO_DEV(ATUSB_RESET):
+		debug("ATUSB_RESET\n");
+		reset_cpu();
+		while (1);
+
+	case ATUSB_TO_DEV(ATUSB_RF_RESET):
+		debug("ATUSB_RF_RESET\n");
+		reset_rf();
+		mac_reset();
+		//ep_send_zlp(EP_CTRL);
+		return 1;
+
+	case ATUSB_FROM_DEV(ATUSB_POLL_INT):
+		debug("ATUSB_POLL_INT\n");
+		if (setup->wLength < 1)
+			return 0;
+		*buf = read_irq();
+		usb_send(&eps[0], buf, 1, NULL, NULL);
+		return 1;
+
+	case ATUSB_FROM_DEV(ATUSB_TIMER):
+		debug("ATUSB_TIMER\n");
+		size = setup->wLength;
+		if (size > sizeof(tmp64))
+			size = sizeof(tmp64);
+		tmp64 = timer_read();
+		memcpy(buf, &tmp64, sizeof(tmp64));
+		usb_send(&eps[0], buf, size, NULL, NULL);
+		return 1;
+
+	case ATUSB_FROM_DEV(ATUSB_GPIO):
+		debug("ATUSB_GPIO\n");
+		if (setup->wLength < 3)
+			return 0;
+		if (!gpio(setup->wIndex, setup->wValue, setup->wValue >> 8,
+		    setup->wIndex >> 8, buf))
+			return 0;
+		usb_send(&eps[0], buf, 3, NULL, NULL);
+		return 1;
+	case ATUSB_TO_DEV(ATUSB_GPIO_CLEANUP):
+		gpio_cleanup();
+		return 1;
+
+	case ATUSB_TO_DEV(ATUSB_SLP_TR):
+		debug("ATUSB_SLP_TR\n");
+		slp_tr();
+		return 1;
+
+	case ATUSB_TO_DEV(ATUSB_REG_WRITE):
+		debug("ATUSB_REG_WRITE\n");
+		spi_begin();
+		spi_send(AT86RF230_REG_WRITE | setup->wIndex);
+		spi_send(setup->wValue);
+		spi_end();
+		//ep_send_zlp(EP_CTRL);
+		return 1;
+	case ATUSB_FROM_DEV(ATUSB_REG_READ):
+		debug("ATUSB_REG_READ\n");
+		spi_begin();
+		spi_send(AT86RF230_REG_READ | setup->wIndex);
+		*buf = spi_recv();
+		spi_end();
+		usb_send(&eps[0], buf, 1, NULL, NULL);
+		return 1;
+
+	case ATUSB_TO_DEV(ATUSB_BUF_WRITE):
+		debug("ATUSB_BUF_WRITE\n");
+		if (setup->wLength < 1)
+			return 0;
+		if (setup->wLength > MAX_PSDU)
+			return 0;
+		buf[0] = AT86RF230_BUF_WRITE;
+		buf[1] = setup->wLength;
+		size = setup->wLength+2;
+		usb_recv(&eps[0], buf+2, setup->wLength, do_buf_write, NULL);
+		return 1;
+	case ATUSB_FROM_DEV(ATUSB_BUF_READ):
+		debug("ATUSB_BUF_READ\n");
+		if (setup->wLength < 2)			/* PHR+LQI */
+			return 0;
+		if (setup->wLength > MAX_PSDU+2)	/* PHR+PSDU+LQI */
+			return 0;
+		spi_begin();
+		spi_send(AT86RF230_BUF_READ);
+		size = spi_recv();
+		if (size >= setup->wLength)
+			size = setup->wLength-1;
+		for (i = 0; i != size+1; i++)
+			buf[i] = spi_recv();
+		spi_end();
+		usb_send(&eps[0], buf, size+1, NULL, NULL);
+		return 1;
+
+	case ATUSB_TO_DEV(ATUSB_SRAM_WRITE):
+		debug("ATUSB_SRAM_WRITE\n");
+		if (setup->wIndex > SRAM_SIZE)
+			return 0;
+		if (setup->wIndex+setup->wLength > SRAM_SIZE)
+			return 0;
+		buf[0] = AT86RF230_SRAM_WRITE;
+		buf[1] = setup->wIndex;
+		size = setup->wLength+2;
+		usb_recv(&eps[0], buf+2, setup->wLength, do_buf_write, NULL);
+		return 1;
+	case ATUSB_FROM_DEV(ATUSB_SRAM_READ):
+		debug("ATUSB_SRAM_READ\n");
+		if (setup->wIndex > SRAM_SIZE)
+			return 0;
+		if (setup->wIndex+setup->wLength > SRAM_SIZE)
+			return 0;
+		spi_begin();
+		spi_send(AT86RF230_SRAM_READ);
+		spi_send(setup->wIndex);
+		for (i = 0; i != setup->wLength; i++)
+			buf[i] = spi_recv();
+		spi_end();
+		usb_send(&eps[0], buf, setup->wLength, NULL, NULL);
+		return 1;
+
+	case ATUSB_TO_DEV(ATUSB_SPI_WRITE):
+		size = setup->wLength+2;
+		if (size > sizeof(buf))
+			return 0;
+		buf[0] = setup->wValue;
+		buf[1] = setup->wIndex;
+		if (setup->wLength)
+			usb_recv(&eps[0], buf+2, setup->wLength,
+			    do_buf_write, NULL);
+		else
+			do_buf_write(NULL);
+		return 1;
+	case ATUSB_FROM_DEV(ATUSB_SPI_WRITE2_SYNC):
+		spi_begin();
+		spi_send(setup->wValue);
+		spi_send(setup->wIndex);
+		spi_end();
+		buf[0] = irq_serial;
+		if (setup->wLength)
+			usb_send(&eps[0], buf, 1, NULL, NULL);
+		return 1;
+
+	case ATUSB_FROM_DEV(ATUSB_SPI_READ1):
+	case ATUSB_FROM_DEV(ATUSB_SPI_READ2):
+		spi_begin();
+		spi_send(setup->wValue);
+		if (req == ATUSB_FROM_DEV(ATUSB_SPI_READ2))
+			spi_send(setup->wIndex);
+		for (i = 0; i != setup->wLength; i++)
+			buf[i] = spi_recv();
+		spi_end();
+		usb_send(&eps[0], buf, setup->wLength, NULL, NULL);
+		return 1;
+
+	case ATUSB_TO_DEV(ATUSB_RX_MODE):
+		return mac_rx(setup->wValue);
+	case ATUSB_TO_DEV(ATUSB_TX):
+		return mac_tx(setup->wValue, setup->wIndex, setup->wLength);
+	case ATUSB_TO_DEV(ATUSB_EUI64_WRITE):
+		debug("ATUSB_EUI64_WRITE\n");
+		usb_recv(&eps[0], buf, setup->wLength, do_eeprom_write, NULL);
+		_delay_ms(100);
+		reset_cpu();
+		return 1;
+
+	case ATUSB_FROM_DEV(ATUSB_EUI64_READ):
+		debug("ATUSB_EUI64_READ\n");
+		eeprom_read_block(buf, (const void*)0, 8);
+		usb_send(&eps[0], buf, 8, NULL, NULL);
+		return 1;
+
+	default:
+		error("Unrecognized SETUP: 0x%02x 0x%02x ...\n",
+		    setup->bmRequestType, setup->bRequest);
+		return 0;
+	}
+}
+
+
+static bool my_dfu_setup(const struct setup_request *setup)
+{
+	switch (setup->bmRequestType | setup->bRequest << 8) {
+	case DFU_TO_DEV(DFU_DETACH):
+		/* @@@ should use wTimeout */
+		dfu.state = appDETACH;
+		return 1;
+	default:
+		return dfu_setup_common(setup);
+	}
+}
+
+
+static void my_set_interface(int nth)
+{
+	if (nth) {
+		user_setup = my_dfu_setup;
+		user_get_descriptor = dfu_my_descr;
+		dfu.state = appIDLE;
+	} else {
+		user_setup = my_setup;
+		user_get_descriptor = sernum_get_descr;
+	}
+}
+
+
+static void my_reset(void)
+{
+	if (dfu.state == appDETACH)
+		reset_cpu();
+}
+
+
+void ep0_init(void)
+{
+	user_setup = my_setup;
+	user_set_interface = my_set_interface;
+	my_set_interface(0);
+	user_reset = my_reset;
+}
diff --git a/atusb/flash.c b/atusb/flash.c
new file mode 100644
index 0000000..1f8e59d
--- /dev/null
+++ b/atusb/flash.c
@@ -0,0 +1,97 @@
+/*
+ * fw/flash.c - Board-specific flash functions
+ *
+ * Written 2011, 2013-2015 by Werner Almesberger
+ * Copyright 2011, 2013-2015 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/boot.h>
+#include <avr/pgmspace.h>
+
+#include "dfu.h"
+#include "board.h"
+
+
+static uint32_t payload;
+
+
+static void flash_start(void)
+{
+	payload = 0;
+}
+
+
+static bool flash_can_write(uint16_t size)
+{
+	return payload+size <= BOOT_ADDR;
+}
+
+
+static void flash_write(const uint8_t *buf, uint16_t size)
+{
+	static uint8_t last;
+	const uint8_t *p;
+
+	for (p = buf; p != buf+size; p++) {
+		if (!(payload & (SPM_PAGESIZE-1))) {
+			boot_page_erase(payload);
+			boot_spm_busy_wait();
+		}
+
+		if (payload & 1)
+			boot_page_fill(payload, last | (*p << 8));
+		else
+			last = *p;
+		payload++;
+
+		if (!(payload & (SPM_PAGESIZE-1))) {
+			boot_page_write(payload-SPM_PAGESIZE);
+			boot_spm_busy_wait();
+		}
+	}
+}
+
+
+static void flash_end_write(void)
+{
+	if (payload & (SPM_PAGESIZE-1)) {
+		boot_page_write(payload & ~(SPM_PAGESIZE-1));
+		boot_spm_busy_wait();
+	}
+	boot_rww_enable();
+}
+
+
+static uint16_t flash_read(uint8_t *buf, uint16_t size)
+{
+	uint16_t got = 0;
+
+	while (size && payload != (uint32_t) FLASHEND+1) {
+		*buf++ = pgm_read_byte(payload);
+		payload++;
+		size--;
+		got++;
+	}
+	return got;
+}
+
+
+static const struct dfu_flash_ops flash_ops = {
+	.start		= flash_start,
+	.can_write	= flash_can_write,
+	.write		= flash_write,
+	.end_write	= flash_end_write,
+	.read		= flash_read,
+};
+
+
+const struct dfu_flash_ops *dfu_flash_ops = &flash_ops;
diff --git a/atusb/include/at86rf230.h b/atusb/include/at86rf230.h
new file mode 100644
index 0000000..4c3ae22
--- /dev/null
+++ b/atusb/include/at86rf230.h
@@ -0,0 +1,402 @@
+/*
+ * include/at86rf230.h - AT86RF230/AT86RF231 protocol and register definitions
+ *
+ * Written 2008-2011 by Werner Almesberger
+ * Copyright 2008-2011 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef AT86RF230_H
+#define	AT86RF230_H
+
+enum {
+	AT86RF230_REG_WRITE	= 0xc0, /* 11... */
+	AT86RF230_REG_READ	= 0x80,	/* 10... */
+	AT86RF230_BUF_WRITE	= 0x60,	/* 011... */
+	AT86RF230_BUF_READ	= 0x20,	/* 001... */
+	AT86RF230_SRAM_WRITE	= 0x40,	/* 010... */
+	AT86RF230_SRAM_READ	= 0x00 	/* 000... */
+};
+
+#define	MAX_PSDU	127	/* octets, see AT86RF230 manual section 8.1  */
+#define	SRAM_SIZE	128
+
+
+/* --- Registers ----------------------------------------------------------- */
+
+enum {
+	REG_TRX_STATUS		= 0x01,
+	REG_TRX_STATE		= 0x02,
+	REG_TRX_CTRL_0		= 0x03,
+
+	REG_TRX_CTRL_1		= 0x04,	/* 231 only */
+
+	REG_PHY_TX_PWR		= 0x05,
+	REG_PHY_RSSI		= 0x06,
+	REG_PHY_ED_LEVEL	= 0x07,
+	REG_PHY_CC_CCA		= 0x08,
+	REG_CCA_THRES		= 0x09,
+
+	REG_RX_CTRL		= 0x0a,	/* 231 only */
+	REG_SFD_VALUE		= 0x0b,	/* 231 only */
+	REG_TRX_CTRL_2		= 0x0c,	/* 231 only */
+	REG_ANT_DIV		= 0x0d,	/* 231 only */
+
+	REG_IRQ_MASK		= 0x0e,
+	REG_IRQ_STATUS		= 0x0f,
+	REG_VREG_CTRL		= 0x10,
+	REG_BATMON		= 0x11,
+	REG_XOSC_CTRL		= 0x12,
+
+	REG_RX_SYN		= 0x15,	/* 231 only */
+	REG_XAH_CTRL_1		= 0x17,	/* 231 only */
+	REG_FTN_CTRL		= 0x18,	/* 231 only */
+
+	REG_PLL_CF		= 0x1a,
+	REL_PLL_DCU		= 0x1b,
+	REG_PART_NUM		= 0x1c,
+	REG_VERSION_NUM		= 0x1d,
+	REG_MAN_ID_0		= 0x1e,
+	REG_MAN_ID_1		= 0x1f,
+	REG_SHORT_ADDR_0	= 0x20,
+	REG_SHORT_ADDR_1	= 0x21,
+	REG_PAN_ID_0		= 0x22,
+	REG_PAN_ID_1		= 0x23,
+	REG_IEEE_ADDR_0		= 0x24,
+	REG_IEEE_ADDR_1		= 0x25,
+	REG_IEEE_ADDR_2		= 0x26,
+	REG_IEEE_ADDR_3		= 0x27,
+	REG_IEEE_ADDR_4		= 0x28,
+	REG_IEEE_ADDR_5		= 0x29,
+	REG_IEEE_ADDR_6		= 0x2a,
+	REG_IEEE_ADDR_7		= 0x2b,
+
+	REG_XAH_CTRL_0		= 0x2c,	/* XAH_CTRL in 230 */
+	REG_CSMA_SEED_0		= 0x2d,
+	REG_CSMA_SEED_1		= 0x2e,
+	REG_CSMA_BE		= 0x2f,	/* 231 only */
+
+	REG_CONT_TX_0		= 0x36,
+	REG_CONT_TX_1		= 0x3d,	/* 230 only */
+};
+
+/* --- TRX_STATUS --- ------------------------------------------------------ */
+
+#define	CCA_DONE	(1 << 7)
+#define	CCA_STATUS	(1 << 6)
+
+#define	TRX_STATUS_SHIFT	0
+#define	TRX_STATUS_MASK		0x1f
+
+enum {
+	TRX_STATUS_P_ON			= 0x00,	/* reset default */
+	TRX_STATUS_BUSY_RX		= 0x01,
+	TRX_STATUS_BUSY_TX		= 0x02,
+	TRX_STATUS_RX_ON		= 0x06,
+	TRX_STATUS_TRX_OFF		= 0x08,
+	TRX_STATUS_PLL_ON		= 0x09,
+	TRX_STATUS_SLEEP		= 0x0f,
+	TRX_STATUS_BUSY_RX_AACK		= 0x11,
+	TRX_STATUS_BUSY_TX_ARET		= 0x12,
+	TRX_STATUS_RX_AACK_ON		= 0x16,
+	TRX_STATUS_TX_ARET_ON		= 0x19,
+	TRX_STATUS_RX_ON_NOCLK		= 0x1c,
+	TRX_STATUS_RX_AACK_ON_NOCLK	= 0x1d,
+	TRX_STATUS_BUSY_RX_AACK_NOCLK	= 0x1e,
+	TRX_STATUS_TRANSITION		= 0x1f	/* ..._IN_PROGRESS */
+};
+
+/* --- TRX_STATE ----------------------------------------------------------- */
+
+#define	TRAC_STATUS_SHIFT	5
+#define	TRAC_STATUS_MASK	7
+
+enum {
+	TRAC_STATUS_SUCCESS			= 0,	/* reset default */
+	TRAC_STATUS_SUCCESS_DATA_PENDING	= 1,
+	TRAC_STATUS_SUCCESS_WAIT_FOR_ACK	= 2,	/* 231 only */
+	TRAC_STATUS_CHANNEL_ACCESS_FAILURE	= 3,
+	TRAC_STATUS_NO_ACK			= 5,
+	TRAC_STATUS_INVALID			= 7
+};
+
+#define	TRX_CMD_SHIFT	0
+#define	TRX_CMD_MASK	0x1f
+
+enum {
+	TRX_CMD_NOP		= 0x00,	/* reset default */
+	TRX_CMD_TX_START	= 0x02,
+	TRX_CMD_FORCE_TRX_OFF	= 0x03,
+	TRX_CMD_FORCE_PLL_ON	= 0x04,	/* 231 only */
+	TRX_CMD_RX_ON		= 0x06,
+	TRX_CMD_TRX_OFF		= 0x08,
+	TRX_CMD_PLL_ON		= 0x09,
+	TRX_CMD_RX_AACK_ON	= 0x16,
+	TRX_CMD_TX_ARET_ON	= 0x19,
+};
+
+/* --- TRX_CTRL_0 ---------------------------------------------------------- */
+
+#define	PAD_IO_SHIFT	6
+#define	PAD_IO_MASK	3
+
+enum {
+	PAD_IO_2mA,	/* reset default */
+	PAD_IO_4mA,
+	PAD_IO_6mA,
+	PAD_IO_8mA
+};
+
+#define	PAD_IO_CLKM_SHIFT	4
+#define	PAD_IO_CLKM_MASK	3
+
+enum {
+	PAD_IO_CLKM_2mA,
+	PAD_IO_CLKM_4mA,	/* reset default */
+	PAD_IO_CLKM_5mA,
+	PAD_IO_CLKM_8mA,
+};
+
+#define	CLKM_SHA_SEL		(1 << 3)
+
+#define	CLKM_CTRL_SHIFT	0
+#define	CLKM_CTRL_MASK	7
+
+enum {
+	CLKM_CTRL_OFF	= 0,
+	CLKM_CTRL_1MHz	= 1,	/* reset default */
+	CLKM_CTRL_2MHz	= 2,
+	CLKM_CTRL_4MHz	= 3,
+	CLKM_CTRL_8MHz	= 4,
+	CLKM_CTRL_16MHz	= 5
+};
+
+/* --- TRX_CTRL_1 (231 only) ----------------------------------------------- */
+
+#define	PA_EXT_EN		(1 << 7)
+#define	IRQ_2_EXT_EN		(1 << 6)
+#define	TX_AUTO_CRC_ON		(1 << 5)	/* 231 location */
+#define	RX_BL_CTRL		(1 << 4)
+
+#define	SPI_CMD_MODE_SHIFT	2
+#define	SPI_CMD_MODE_MASK	3
+
+enum {
+	SPI_CMD_MODE_EMPTY	= 0,	/* reset default */
+	SPI_CMD_MODE_TRX_STATUS	= 1,
+	SPI_CMD_MODE_PHY_RSSI	= 2,
+	SPI_CMD_MODE_IRQ_STATUS	= 3,
+};
+
+#define	IRQ_MASK_MODE		(1 << 1)
+#define	IRQ_POLARITY		(1 << 0)
+
+/* --- PHY_TX_PWR ---------------------------------------------------------- */
+
+#define	TX_AUTO_CRC_ON_230	(1 << 7)	/* 230 location */
+
+#define	PA_BUF_LT_SHIFT	6
+#define	PA_BUF_LT_MASK	3
+
+#define	PA_LT_SHIFT	4
+#define	PA_LT_MASK	3
+
+#define	TX_PWR_SHIFT	0
+#define	TX_PWR_MASK	0x0f
+
+/* --- PHY_RSSI ------------------------------------------------------------ */
+
+#define	RX_CRC_VALID	(1 << 7)
+
+#define	RND_VALUE_SHIFT	5		/* 231 only */
+#define	RND_VALUE_MASK	3
+
+#define	RSSI_SHIFT	0
+#define	RSSI_MASK	0x1f
+
+/* --- PHY_CC_CCA ---------------------------------------------------------- */
+
+#define	CCA_REQUEST	(1 << 7)
+
+#define	CCA_MODE_SHIFT	5
+#define	CCA_MODE_MASK	3
+
+enum {
+	CCA_MODE_CARRIER_OR_ENERGY	= 0,	/* 231 only */
+	CCA_MODE_ENERGY			= 1,	/* reset default */
+	CCA_MODE_CARRIER		= 2,
+	CCA_MODE_CARRIER_AND_ENERGY	= 3
+};
+
+#define	CHANNEL_SHIFT	0
+#define	CHANNEL_MASK	0x1f
+
+/* --- CCA_THRES ----------------------------------------------------------- */
+
+#define	CCA_ED_THRES_SHIFT	0
+#define	CCA_ED_THRES_MASK	0x0f
+
+/* --- RX_CTRL (231 only) -------------------------------------------------- */
+
+#define	PDT_THRES_SHIFT	0
+#define	PDT_THRES_MASK	0x0f
+
+enum {
+	PDT_THRES_DEFAULT	= 0x07,	/* reset default */
+	PDT_THRES_DIVERSITY	= 0x03,
+};
+
+/* --- TRX_CTRL_2 (231 only) ----------------------------------------------- */
+
+#define	RX_SAFE_MODE		(1 << 7)
+
+#define	OQPSK_DATA_RATE_SHIFT	0
+#define	OQPSK_DATA_RATE_MASK	3
+
+enum {
+	OQPSK_DATA_RATE_250	= 0,	/* reset default */
+	OQPSK_DATA_RATE_500	= 1,
+	OQPSK_DATA_RATE_1000	= 2,
+	OQPSK_DATA_RATE_2000	= 3
+};
+
+/* --- ANT_DIV (231 only) -------------------------------------------------- */
+
+#define	ANT_SEL		(1 << 7)
+#define	ANT_DIV_EN	(1 << 3)
+#define	ANT_EXT_SW_EN	(1 << 2)
+
+#define	ANT_CTRL_SHIFT	0
+#define	ANT_CTRL_MASK	3
+
+enum {
+	ANT_CTRL_ANT_0	= 1,
+	ANT_CTRL_ANT_1	= 2,
+	ANT_CTRL_NODIV	= 3,	/* reset default */
+};
+
+/* --- IRQ_MASK/IRQ_STATUS ------------------------------------------------- */
+
+enum {
+	IRQ_PLL_LOCK	= 1 << 0,
+	IRQ_PLL_UNLOCK	= 1 << 1,
+	IRQ_RX_START	= 1 << 2,
+	IRQ_TRX_END	= 1 << 3,
+	IRQ_CCA_ED_DONE	= 1 << 4,	/* 231 only */
+	IRQ_AMI		= 1 << 5,	/* 231 only */
+	IRQ_TRX_UR	= 1 << 6,
+	IRQ_BAT_LOW	= 1 << 7
+};
+
+/* --- VREG_CTRL ----------------------------------------------------------- */
+
+#define	AVREG_EXT	(1 << 7)
+#define	AVDD_OK		(1 << 6)
+#define	DVREG_EXT	(1 << 3)
+#define	DVDD_OK		(1 << 2)
+
+/* --- BATMON -------------------------------------------------------------- */
+
+#define	BATMON_OK	(1 << 5)
+#define	BATMON_HR	(1 << 4)
+
+#define	BATMON_VTH_SHIFT	0
+#define	BATMON_VTH_MASK		0x0f
+
+/* --- XOSC_CTRL ----------------------------------------------------------- */
+
+#define	XTAL_MODE_SHIFT	4
+#define	XTAL_MODE_MASK	0x0f
+
+enum {
+	XTAL_MODE_OFF	= 0x0,	/* 230 only */
+	XTAL_MODE_EXT	= 0x4,
+	XTAL_MODE_INT	= 0xf	/* reset default */
+};
+
+#define	XTAL_TRIM_SHIFT	4
+#define	XTAL_TRIM_MASK	0x0f
+
+/* --- RX_SYN (231 only) --------------------------------------------------- */
+
+#define	RX_PDT_DIS		(1 << 7)
+
+#define	RX_PDT_LEVEL_SHIFT	0
+#define	RX_PDT_LEVEL_MASK	0xf
+
+/* --- XAH_CTRL_1 (231 only) ----------------------------------------------- */
+
+#define	AACK_FLTR_RES_FT	(1 << 5)
+#define	AACK_UPLD_RES_FT	(1 << 4)
+#define	AACK_ACK_TIME		(1 << 2)
+#define	AACK_PROM_MODE		(1 << 1)
+
+/* --- FTN_CTRL (231 only) ------------------------------------------------- */
+
+#define	FTN_START		(1 << 7)
+
+/* --- PLL_CF -------------------------------------------------------------- */
+
+#define	PLL_CF_START	(1 << 7)
+
+/* --- PLL_DCU ------------------------------------------------------------- */
+
+#define	PLL_DCU_START	(1 << 7)
+
+/* --- XAH_CTRL_0 (XAH_CTRL in 230) ---------------------------------------- */
+
+#define	MAX_FRAME_RETRIES_SHIFT	4
+#define	MAX_FRAME_RETRIES_MASK	0x0f
+
+#define	MAX_CSMA_RETRIES_SHIFT	1
+#define	MAX_CSMA_RETRIES_MASK	0x07
+
+#define	SLOTTED_OPERATION	(1 << 0)	/* 231 only */
+
+/* --- CSMA_SEED_1 --------------------------------------------------------- */
+
+#define	MIN_BE_SHIFT_230	6	/* 230 location */
+#define	MIN_BE_MASK_230		3
+
+#define	AACK_FVN_MODE_SHIFT	6	/* 231 only */
+#define	AACK_FVN_MODE_MASK	3
+
+enum {
+	AACK_FVN_MODE_0		= 0,
+	AACK_FVN_MODE_01	= 1,	/* reset default */
+	AACK_FVN_MODE_012	= 2,
+	AACK_FVN_MODE_ANY	= 3
+};
+
+#define	AACK_SET_PD		(1 << 5)
+#define	AACK_DIS_ACK		(1 << 4)	/* 231 only */
+#define	I_AM_COORD		(1 << 3)
+
+#define CSMA_SEED_1_SHIFT	0
+#define	CSMA_SEED_1_MASK	7
+
+/* --- CSMA_BE ------------------------------------------------------------- */
+
+#define	MAX_BE_SHIFT		4
+#define	MAX_BE_MASK		0x0f
+
+#define	MIN_BE_SHIFT		0	/* 231 location */
+#define	MIN_BE_MASK		0x0f
+
+/* --- REG_CONT_TX_0 ------------------------------------------------------- */
+
+#define	CONT_TX_MAGIC		0x0f
+
+/* --- REG_CONT_TX_1 (230 only) -------------------------------------------- */
+
+#define	CONT_TX_MOD		0x00	/* modulated */
+#define	CONT_TX_M2M		0x10	/* f_CH-2 MHz */
+#define	CONT_TX_M500K		0x80	/* f_CH-0.5 MHz */
+#define	CONT_TX_P500K		0xc0	/* f_CH+0.5 MHz */
+
+#endif /* !AT86RF230_H */
diff --git a/atusb/include/atusb/atusb.h b/atusb/include/atusb/atusb.h
new file mode 100644
index 0000000..555d14b
--- /dev/null
+++ b/atusb/include/atusb/atusb.h
@@ -0,0 +1,97 @@
+/*
+ * atusb.h - Definitions shared between kernel and ATUSB firmware
+ *
+ * Written 2013 by Werner Almesberger <werner@almesberger.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2, or
+ * (at your option) any later version.
+ *
+ * This file should be identical for kernel and firmware.
+ * Kernel: drivers/net/ieee802154/atusb.h
+ * Firmware: ben-wpan/atusb/fw/include/atusb/atusb.h
+ */
+
+#ifndef	_ATUSB_H
+#define	_ATUSB_H
+
+#define ATUSB_VENDOR_ID	0x20b7	/* Qi Hardware*/
+#define ATUSB_PRODUCT_ID 0x1540	/* 802.15.4, device 0 */
+				/*     -- -         - */
+
+#define ATUSB_BUILD_SIZE 256	/* maximum build version/date message length */
+
+/* Commands to our device. Make sure this is synced with the firmware */
+enum atusb_requests {
+	ATUSB_ID			= 0x00,	/* system status/control grp */
+	ATUSB_BUILD,
+	ATUSB_RESET,
+	ATUSB_RF_RESET			= 0x10,	/* debug/test group */
+	ATUSB_POLL_INT,
+	ATUSB_TEST,			/* atusb-sil only */
+	ATUSB_TIMER,
+	ATUSB_GPIO,
+	ATUSB_SLP_TR,
+	ATUSB_GPIO_CLEANUP,
+	ATUSB_REG_WRITE			= 0x20,	/* transceiver group */
+	ATUSB_REG_READ,
+	ATUSB_BUF_WRITE,
+	ATUSB_BUF_READ,
+	ATUSB_SRAM_WRITE,
+	ATUSB_SRAM_READ,
+	ATUSB_SPI_WRITE			= 0x30,	/* SPI group */
+	ATUSB_SPI_READ1,
+	ATUSB_SPI_READ2,
+	ATUSB_SPI_WRITE2_SYNC,
+	ATUSB_RX_MODE			= 0x40, /* HardMAC group */
+	ATUSB_TX,
+	ATUSB_EUI64_WRITE		= 0x50, /* Parameter in EEPROM grp */
+	ATUSB_EUI64_READ,
+};
+
+enum {
+	ATUSB_HW_TYPE_100813,	/* 2010-08-13 */
+	ATUSB_HW_TYPE_101216,	/* 2010-12-16 */
+	ATUSB_HW_TYPE_110131,	/* 2011-01-31, ATmega32U2-based */
+	ATUSB_HW_TYPE_RZUSB,	/* Atmel Raven USB dongle with at86rf230 */
+	ATUSB_HW_TYPE_HULUSB,	/* Busware HUL USB dongle with at86rf212 */
+};
+
+/*
+ * Direction	bRequest		wValue		wIndex	wLength
+ *
+ * ->host	ATUSB_ID		-		-	3
+ * ->host	ATUSB_BUILD		-		-	#bytes
+ * host->	ATUSB_RESET		-		-	0
+ *
+ * host->	ATUSB_RF_RESET		-		-	0
+ * ->host	ATUSB_POLL_INT		-		-	1
+ * host->	ATUSB_TEST		-		-	0
+ * ->host	ATUSB_TIMER		-		-	#bytes (6)
+ * ->host	ATUSB_GPIO		dir+data	mask+p#	3
+ * host->	ATUSB_SLP_TR		-		-	0
+ * host->	ATUSB_GPIO_CLEANUP	-		-	0
+ *
+ * host->	ATUSB_REG_WRITE		value		addr	0
+ * ->host	ATUSB_REG_READ		-		addr	1
+ * host->	ATUSB_BUF_WRITE		-		-	#bytes
+ * ->host	ATUSB_BUF_READ		-		-	#bytes
+ * host->	ATUSB_SRAM_WRITE	-		addr	#bytes
+ * ->host	ATUSB_SRAM_READ		-		addr	#bytes
+ *
+ * host->	ATUSB_SPI_WRITE		byte0		byte1	#bytes
+ * ->host	ATUSB_SPI_READ1		byte0		-	#bytes
+ * ->host	ATUSB_SPI_READ2		byte0		byte1	#bytes
+ * ->host	ATUSB_SPI_WRITE2_SYNC	byte0		byte1	0/1
+ *
+ * host->	ATUSB_RX_MODE		on		-	0
+ * host->	ATUSB_TX		flags		ack_seq	#bytes
+ * host->	ATUSB_EUI64_WRITE	-		-	#bytes (8)
+ * ->host	ATUSB_EUI64_READ	-		-	#bytes (8)
+ */
+
+#define ATUSB_REQ_FROM_DEV	(USB_TYPE_VENDOR | USB_DIR_IN)
+#define ATUSB_REQ_TO_DEV	(USB_TYPE_VENDOR | USB_DIR_OUT)
+
+#endif /* !_ATUSB_H */
diff --git a/atusb/include/atusb/ep0.h b/atusb/include/atusb/ep0.h
new file mode 100644
index 0000000..7777345
--- /dev/null
+++ b/atusb/include/atusb/ep0.h
@@ -0,0 +1,64 @@
+/*
+ * include/atusb/ep0.h - EP0 extension protocol
+ *
+ * Written 2008-2011, 2013 by Werner Almesberger
+ * Copyright 2008-2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef EP0_H
+#define EP0_H
+
+#include <atusb/atusb.h>
+
+
+/*
+ * EP0 protocol:
+ *
+ * 0.0	initial release
+ * 0.1  addition of ATUSB_TEST
+ * 0.2  First public release
+ * 0.3	ATUSB_EUI64_READ/WRITE for permanent EUI64 handling
+ * 	Support to run the firmware on Atmel Raven USB dongles
+ * 	Remove FCS frame check from firmware and leave it to the driver
+ * 	Use extended operation mode for TX for automatic ACK handling
+ */
+
+#define EP0ATUSB_MAJOR	0	/* EP0 protocol, major revision */
+#define EP0ATUSB_MINOR	3	/* EP0 protocol, minor revision */
+
+
+/*
+ * bmRequestType:
+ *
+ * D7 D6..5 D4...0
+ * |  |     |
+ * direction (0 = host->dev)
+ *    type (2 = vendor)
+ *          recipient (0 = device)
+ */
+
+#ifndef USB_TYPE_VENDOR
+#define	USB_TYPE_VENDOR		0x40
+#endif
+
+#ifndef USB_DIR_IN
+#define	USB_DIR_IN		0x80
+#endif
+
+#ifndef USB_DIR_OUT
+#define	USB_DIR_OUT		0x00
+#endif
+
+#define	ATUSB_FROM_DEV(req)	(ATUSB_REQ_FROM_DEV | (req) << 8)
+#define	ATUSB_TO_DEV(req)	(ATUSB_REQ_TO_DEV | (req) << 8)
+
+
+void ep0_init(void);
+
+#endif /* !EP0_H */
diff --git a/atusb/mac.c b/atusb/mac.c
new file mode 100644
index 0000000..835002c
--- /dev/null
+++ b/atusb/mac.c
@@ -0,0 +1,250 @@
+/*
+ * fw/mac.c - HardMAC functions
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+
+#include "at86rf230.h"
+#include "spi.h"
+#include "board.h"
+#include "mac.h"
+
+#define	RX_BUFS	3
+
+
+bool (*mac_irq)(void) = NULL;
+
+
+static uint8_t rx_buf[RX_BUFS][MAX_PSDU+2]; /* PHDR+payload+LQ */
+static uint8_t tx_buf[MAX_PSDU];
+static uint8_t tx_size = 0;
+static bool txing = 0;
+static bool queued_tx_ack = 0;
+static uint8_t next_seq, this_seq, queued_seq;
+
+
+/* ----- Receive buffer management ----------------------------------------- */
+
+
+static uint8_t rx_in = 0, rx_out = 0;
+
+
+static inline void next_buf(uint8_t *index)
+{
+	*index = (*index+1) % RX_BUFS;
+}
+
+
+/* ----- Interrupt handling ------------------------------------------------ */
+
+
+static void rx_done(void *user);
+static void tx_ack_done(void *user);
+
+
+static void usb_next(void)
+{
+	const uint8_t *buf;
+
+	if (rx_in != rx_out) {
+		buf = rx_buf[rx_out];
+		led(1);
+		usb_send(&eps[1], buf, buf[0]+2, rx_done, NULL);
+	}
+
+	if (queued_tx_ack) {
+		usb_send(&eps[1], &queued_seq, 1, tx_ack_done, NULL);
+		queued_tx_ack = 0;	
+	}
+}
+
+
+static void tx_ack_done(void *user)
+{
+	usb_next();
+}
+
+static void rx_done(void *user)
+{
+	led(0);
+	next_buf(&rx_out);
+	usb_next();
+#ifdef AT86RF230
+	/* slap at86rf230 - reduce fragmentation issue */
+	change_state(TRX_STATUS_RX_AACK_ON);
+#endif
+}
+
+
+static void receive_frame(void)
+{
+	uint8_t size;
+	uint8_t *buf;
+
+	spi_begin();
+	spi_io(AT86RF230_BUF_READ);
+
+	size = spi_recv();
+	if (!size || (size & 0x80)) {
+		spi_end();
+		return;
+	}
+
+	buf = rx_buf[rx_in];
+	spi_recv_block(buf+1, size+1);
+	spi_end();
+
+	buf[0] = size;
+	next_buf(&rx_in);
+
+	if (eps[1].state == EP_IDLE)
+		usb_next();
+}
+
+
+static bool handle_irq(void)
+{
+	uint8_t irq;
+
+	irq = reg_read(REG_IRQ_STATUS);
+	if (!(irq & IRQ_TRX_END))
+		return 1;
+
+	if (txing) {
+		if (eps[1].state == EP_IDLE) {
+			usb_send(&eps[1], &this_seq, 1, tx_ack_done, NULL);
+		} else {
+			queued_tx_ack = 1;
+			queued_seq = this_seq;
+		}
+		txing = 0;
+		return 1;
+	}
+
+	/* likely */
+	if (eps[1].state == EP_IDLE || rx_in != rx_out)
+		receive_frame();
+
+	return 1;
+}
+
+
+/* ----- TX/RX ------------------------------------------------------------- */
+
+
+bool mac_rx(int on)
+{
+	if (on) {
+		mac_irq = handle_irq;
+		reg_read(REG_IRQ_STATUS);
+		change_state(TRX_CMD_RX_AACK_ON);
+	} else {
+		mac_irq = NULL;
+		change_state(TRX_CMD_FORCE_TRX_OFF);
+		txing = 0;
+	}
+	return 1;
+}
+
+
+static void do_tx(void *user)
+{
+	uint16_t timeout = 0xffff;
+	uint8_t status;
+	uint8_t i;
+
+	/*
+	 * If we time out here, the host driver will time out waiting for the
+	 * TRX_END acknowledgement.
+	 */
+	do {
+		if (!--timeout)
+			return;
+		status = reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK;
+	}
+	while (status != TRX_STATUS_RX_ON && status != TRX_STATUS_RX_AACK_ON);
+
+#ifdef AT86RF231
+	/*
+	 * We use TRX_CMD_FORCE_PLL_ON instead of TRX_CMD_PLL_ON because a new
+	 * reception may have begun while we were still working on the previous
+	 * one.
+	 */
+	reg_write(REG_TRX_STATE, TRX_CMD_FORCE_PLL_ON);
+#endif
+#ifdef AT86RF230
+	/*
+	 * at86rf230 doesn't support force change, nevetherless this works
+	 * somehow
+	 */
+	reg_write(REG_TRX_STATE, TRX_CMD_PLL_ON);
+#endif
+#ifdef AT86RF212
+	/*
+	* We use TRX_CMD_FORCE_PLL_ON instead of TRX_CMD_PLL_ON because a new
+	* reception may have begun while we were still working on the previous
+	* one.
+	*/
+	reg_write(REG_TRX_STATE, TRX_CMD_FORCE_PLL_ON);
+#endif
+
+	handle_irq();
+
+	spi_begin();
+	spi_send(AT86RF230_BUF_WRITE);
+	spi_send(tx_size+2); /* CRC */
+	for (i = 0; i != tx_size; i++)
+		spi_send(tx_buf[i]);
+	spi_end();
+
+	change_state(TRX_STATUS_TX_ARET_ON);
+
+	slp_tr();
+
+	txing = 1;
+	this_seq = next_seq;
+
+	/*
+	 * Wait until we reach BUSY_TX_ARET, so that we command the transition to
+	 * RX_AACK_ON which will be executed upon TX completion.
+	 */
+	change_state(TRX_CMD_PLL_ON);
+	change_state(TRX_CMD_RX_AACK_ON);
+}
+
+
+bool mac_tx(uint16_t flags, uint8_t seq, uint16_t len)
+{
+	if (len > MAX_PSDU)
+		return 0;
+	tx_size = len;
+	next_seq = seq;
+	usb_recv(&eps[0], tx_buf, len, do_tx, NULL);
+	return 1;
+}
+
+
+void mac_reset(void)
+{
+	mac_irq = NULL;
+	txing = 0;
+	queued_tx_ack = 0;
+	rx_in = rx_out = 0;
+	next_seq = this_seq = queued_seq = 0;
+
+	/* enable CRC and PHY_RSSI (with RX_CRC_VALID) in SPI status return */
+	reg_write(REG_TRX_CTRL_1,
+	    TX_AUTO_CRC_ON | SPI_CMD_MODE_PHY_RSSI << SPI_CMD_MODE_SHIFT);
+}
diff --git a/atusb/mac.h b/atusb/mac.h
new file mode 100644
index 0000000..f3c92fb
--- /dev/null
+++ b/atusb/mac.h
@@ -0,0 +1,26 @@
+/*
+ * fw/mac.h - HardMAC functions
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef MAC_H
+#define	MAC_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+
+extern bool (*mac_irq)(void);
+
+bool mac_rx(int on);
+bool mac_tx(uint16_t flags, uint8_t seq, uint16_t len);
+void mac_reset(void);
+
+#endif /* !MAC_H */
diff --git a/atusb/sernum.c b/atusb/sernum.c
new file mode 100644
index 0000000..41e434c
--- /dev/null
+++ b/atusb/sernum.c
@@ -0,0 +1,47 @@
+/*
+ * fw/sernum.c - ATUSB serial number
+ *
+ * Written 2008-2011, 2013 by Werner Almesberger
+ * Copyright 2008-2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+
+#include "board.h"
+#include "sernum.h"
+
+
+static const uint8_t string_descriptor_0[] = {
+	4,				/* blength */
+	USB_DT_STRING,			/* bDescriptorType */
+	LE(USB_LANGID_ENGLISH_US)	/* wLANGID[0]  */
+};
+
+
+bool sernum_get_descr(uint8_t type, uint8_t index, const uint8_t **reply,
+    uint8_t *size)
+{
+	if (type != USB_DT_STRING)
+		return 0;
+	switch (index) {
+	case 0:
+		*reply = string_descriptor_0;
+		*size = sizeof(string_descriptor_0);
+		return 1;
+	case 1:
+		*reply = board_sernum;
+		*size = sizeof(board_sernum);
+		return 1;
+	default:
+		return 0;
+	}
+}
diff --git a/atusb/sernum.h b/atusb/sernum.h
new file mode 100644
index 0000000..31a8e27
--- /dev/null
+++ b/atusb/sernum.h
@@ -0,0 +1,37 @@
+/*
+ * fw/sernum.h - ATUSB serial number
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef SERNUM_H
+#define	SERNUM_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "board.h"
+
+
+#ifdef HAS_BOARD_SERNUM
+
+bool sernum_get_descr(uint8_t type, uint8_t index, const uint8_t **reply,
+    uint8_t *size);
+
+#else /* HAS_BOARD_SERNUM */
+
+static inline bool sernum_get_descr(uint8_t type, uint8_t index,
+    const uint8_t **reply, uint8_t *size)
+{
+	return 0;
+}
+
+#endif /* !HAS_BOARD_SERNUM */
+
+#endif /* !SERNUM_H */
diff --git a/atusb/spi.c b/atusb/spi.c
new file mode 100644
index 0000000..3fa5715
--- /dev/null
+++ b/atusb/spi.c
@@ -0,0 +1,51 @@
+/*
+ * fw/spi.c - ATmega8 family SPI I/O
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <avr/io.h>
+
+#include "board.h"
+#include "spi.h"
+
+
+uint8_t spi_io(uint8_t v)
+{
+//      while (!(UCSR1A & 1 << UDRE1));
+	SPI_DATA = v;
+	SPI_WAIT_DONE();
+	return SPI_DATA;
+}
+
+
+void spi_end(void)
+{
+//      while (!(UCSR1A & 1 << TXC1));
+	SET(nSS);
+}
+
+
+void spi_recv_block(uint8_t *buf, uint8_t n)
+{
+	if (!n)
+		return;
+	SPI_DATA = 0;
+	while (--n) {
+		SPI_WAIT_DONE();
+		*buf++ = SPI_DATA;
+		SPI_DATA = 0;
+	}
+	SPI_WAIT_DONE();
+	*buf++ = SPI_DATA;
+}
diff --git a/atusb/spi.h b/atusb/spi.h
new file mode 100644
index 0000000..6e04f4e
--- /dev/null
+++ b/atusb/spi.h
@@ -0,0 +1,30 @@
+/*
+ * fw/spi.h - ATmega8 family SPI I/O
+ *
+ * Written 2011, 2013 by Werner Almesberger
+ * Copyright 2011, 2013 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef SPI_H
+#define	SPI_H
+
+#include <stdint.h>
+
+
+void spi_begin(void);
+uint8_t spi_io(uint8_t v);
+void spi_end(void);
+void spi_off(void);
+void spi_init(void);
+
+#define	spi_send(v)	(void) spi_io(v)
+#define	spi_recv(v)	spi_io(0)
+
+void spi_recv_block(uint8_t *buf, uint8_t n);
+
+#endif /* !SPI_H */
diff --git a/atusb/uart.c b/atusb/uart.c
new file mode 100644
index 0000000..44bec27
--- /dev/null
+++ b/atusb/uart.c
@@ -0,0 +1,64 @@
+/*
+ * fw/uart.h - Functions needed for debugging over uart
+ *
+ * Code adapted from http://www.roboternetz.de/wissen/index.php/UART_mit_avr-gcc
+ * and http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
+ *
+ * Published under the Creative Commons Share-Alike licence
+ * https://creativecommons.org/licenses/by-sa/2.0/de/
+ *
+ * S. Salewski 2007
+ *
+ * Adapted by
+ * Josef Filzmaier 2017
+ */
+
+#include <avr/io.h>
+#include "uart.h"
+
+#define USART_BAUD 38400UL
+#define F_CPU 8000000UL
+
+#define Wait_USART_Ready() while (!(UCSR1A & (1<<UDRE1)))
+#define UART_UBRR (F_CPU/(16L*USART_BAUD)-1)
+
+// initialize USART, 8N1 mode
+void
+uart_init(void)
+{
+/* TODO: Find a working configuration for uart for the atmega32u2 */
+#if CHIP == at90usb1287
+	CLKPR = (1 << CLKPCE);
+	CLKPR = 0; // clock prescaler == 0, so we have 16 MHz mpu frequency
+	UBRR1 = UART_UBRR;
+	UCSR1C = (1 << UCSZ10) | (1 << UCSZ11);
+	UCSR1B = (1 << TXEN1);
+	do
+	{
+		UDR1;
+	}
+	while (UCSR1A & (1 << RXC1));
+#endif
+
+}
+
+int uart_write_char(char c, FILE* stream)
+{
+	if (c == '\n'){
+		uart_new_line();
+	}
+	else {
+		Wait_USART_Ready();
+		UDR1 = c;
+	}
+	return 0;
+}
+
+void
+uart_new_line(void)
+{
+	Wait_USART_Ready();
+	UDR1 = '\r';
+	Wait_USART_Ready();
+	UDR1 = '\n';
+}
diff --git a/atusb/uart.h b/atusb/uart.h
new file mode 100644
index 0000000..4810f9c
--- /dev/null
+++ b/atusb/uart.h
@@ -0,0 +1,25 @@
+/*
+ * fw/uart.h - Functions needed for debugging over uart
+ *
+ * Code adapted from http://www.roboternetz.de/wissen/index.php/UART_mit_avr-gcc
+ * and http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
+ *
+ * Published under the Creative Commons Share-Alike licence
+ * https://creativecommons.org/licenses/by-sa/2.0/de/
+ *
+ * S. Salewski 2007
+ *
+ * Adapted by
+ * Josef Filzmaier 2017
+ */
+
+#ifndef UART_H_
+#define	UART_H_
+
+#include <stdio.h>
+
+void uart_init(void);
+int uart_write_char(char c, FILE* stream);
+void uart_new_line(void);
+
+#endif /* UART_H_ */
diff --git a/atusb/usb/atu2.c b/atusb/usb/atu2.c
new file mode 100644
index 0000000..98158bf
--- /dev/null
+++ b/atusb/usb/atu2.c
@@ -0,0 +1,247 @@
+/*
+ * fw/usb/atu2.c - Chip-specific driver for Atmel ATxxxU2 USB chips
+ *
+ * Written 2008-2011, 2013-2014 by Werner Almesberger
+ * Copyright 2008-2011, 2013-2014 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * Known issues:
+ * - no suspend/resume
+ * - we don't call back after failed transmissions,
+ * - we don't reset the EP buffer after failed receptions
+ * - enumeration often encounters an error -71 (from which it recovers)
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define F_CPU   8000000UL
+#include <util/delay.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include "usb.h"
+#include "board.h"
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#if 1
+#define BUG_ON(cond)    do { if (cond) panic(); } while (0)
+#else
+#define BUG_ON(cond)
+#endif
+
+
+struct ep_descr eps[NUM_EPS];
+
+
+static uint16_t usb_read_word(void)
+{
+	uint8_t low;
+
+	low = UEDATX;
+	return low | UEDATX << 8;
+}
+
+
+static void enable_addr(void *user)
+{
+	while (!(UEINTX & (1 << TXINI)));
+	UDADDR |= 1 << ADDEN;
+}
+
+
+void set_addr(uint8_t addr)
+{
+	UDADDR = addr;
+	usb_send(&eps[0], NULL, 0, enable_addr, NULL);
+}
+
+
+void usb_ep_change(struct ep_descr *ep)
+{
+	if (ep->state == EP_TX) {
+		UENUM = ep-eps;
+		UEIENX |= 1 << TXINE;
+	}
+}
+
+
+static bool ep_setup(void)
+{
+	struct setup_request setup;
+
+	BUG_ON(UEBCLX < 8);
+
+	setup.bmRequestType = UEDATX;
+	setup.bRequest = UEDATX;
+	setup.wValue = usb_read_word();
+	setup.wIndex = usb_read_word();
+	setup.wLength = usb_read_word();
+
+	if (!handle_setup(&setup))
+		return 0;
+	if (!(setup.bmRequestType & 0x80) && eps[0].state == EP_IDLE)
+		usb_send(&eps[0], NULL, 0, NULL, NULL);
+	return 1;
+}
+
+
+static bool ep_rx(struct ep_descr *ep)
+{
+	uint8_t size;
+
+	size = UEBCLX;
+	if (size > ep->end-ep->buf)
+		return 0;
+	while (size--)
+		*ep->buf++ = UEDATX;
+	if (ep->buf == ep->end) {
+		ep->state = EP_IDLE;
+		if (ep->callback)
+			ep->callback(ep->user);
+//		if (ep == &eps[0])
+			usb_send(ep, NULL, 0, NULL, NULL);
+	}
+	return 1;
+}
+
+
+static void ep_tx(struct ep_descr *ep)
+{
+	uint8_t size = ep->end-ep->buf;
+	uint8_t left;
+
+	if (size > ep->size)
+		size = ep->size;
+	for (left = size; left; left--)
+		UEDATX = *ep->buf++;
+	if (size == ep->size)
+		return;
+	ep->state = EP_IDLE;
+}
+
+
+static void handle_ep(int n)
+{
+	struct ep_descr *ep = eps+n;
+	uint8_t mask;
+
+	UENUM = n;
+	if (UEINTX & (1 << RXSTPI)) {
+		/* @@@ EP_RX. EP_TX: cancel */
+		ep->state = EP_IDLE;
+		if (!ep_setup())
+			goto stall;
+		UEINTX = ~(1 << RXSTPI);
+	}
+	if (UEINTX & (1 << RXOUTI)) {
+		/* @@ EP_TX: cancel */
+		if (ep->state != EP_RX)
+			goto stall;
+		if (!ep_rx(ep))
+			goto stall;
+		/* @@@ gcc 4.5.2 wants this cast */
+		UEINTX = (uint8_t) ~(1 << RXOUTI | 1 << FIFOCON);
+	}
+	if (UEINTX & (1 << STALLEDI)) {
+		ep->state = EP_IDLE;
+		UEINTX = ~(1 << STALLEDI);
+	}
+	if (UEINTX & (1 << TXINI)) {
+		/* @@ EP_RX: cancel (?) */
+		if (ep->state == EP_TX) {
+			ep_tx(ep);
+			mask = 1 << TXINI;
+			if (n)
+				mask |= 1 << FIFOCON;
+			UEINTX = ~mask;
+			if (ep->state == EP_IDLE && ep->callback)
+				ep->callback(ep->user);
+		} else {
+			UEIENX &= ~(1 << TXINE);
+		}
+	}
+	return;
+
+stall:
+	UEINTX = ~(1 << RXSTPI | 1 << RXOUTI | 1 << STALLEDI);
+	ep->state = EP_IDLE;
+	UECONX |= 1 << STALLRQ;
+}
+
+
+void ep_init(void)
+{
+	UENUM = 0;
+	UECONX = (1 << RSTDT) | (1 << EPEN);	/* enable */
+	UECFG0X = 0;	/* control, direction is ignored */
+	UECFG1X = 3 << EPSIZE0;	/* 64 bytes */
+	UECFG1X |= 1 << ALLOC;
+
+	while (!(UESTA0X & (1 << CFGOK)));
+
+	UEIENX =
+	    (1 << RXSTPE) | (1 << RXOUTE) | (1 << STALLEDE) | (1 << TXINE);
+
+	eps[0].state = EP_IDLE;
+	eps[0].size = 64;
+
+#ifndef BOOT_LOADER
+
+	UENUM = 1;
+	UECONX = (1 << RSTDT) | (1 << EPEN);	/* enable */
+	UECFG0X = (1 << EPTYPE1) | (1 << EPDIR); /* bulk IN */
+	UECFG1X = 3 << EPSIZE0;	/* 64 bytes */
+	UECFG1X |= 1 << ALLOC;
+
+	while (!(UESTA0X & (1 << CFGOK)));
+
+	UEIENX = (1 << STALLEDE) | (1 << TXINE);
+
+	eps[1].state = EP_IDLE;
+	eps[1].size = 64;
+
+#endif
+}
+
+
+ISR(USB_GEN_vect)
+{
+	uint8_t flags;
+
+	flags = UDINT;
+	if (flags & (1 << EORSTI)) {
+		if (user_reset)
+			user_reset();
+		ep_init();
+		UDINT = ~(1 << EORSTI);
+	}
+}
+
+
+ISR(USB_COM_vect)
+{
+	uint8_t flags, i;
+
+	flags = UEINT;
+	for (i = 0; i != NUM_EPS; i++)
+		if (flags & (1 << i))
+			handle_ep(i);
+}
+
+
+void usb_reset(void)
+{
+	UDCON |= 1 << DETACH;	/* detach the pull-up */
+	_delay_ms(1);
+}
diff --git a/atusb/usb/dfu.c b/atusb/usb/dfu.c
new file mode 100644
index 0000000..c84a28d
--- /dev/null
+++ b/atusb/usb/dfu.c
@@ -0,0 +1,260 @@
+/*
+ * boot/dfu.c - DFU protocol engine
+ *
+ * Written 2008-2011, 2013-2015 by Werner Almesberger
+ * Copyright 2008-2011, 2013-2015 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf
+ */
+
+/*
+ * A few, erm, shortcuts:
+ *
+ * - we don't bother with the app* states since DFU is all this firmware does
+ * - after DFU_DNLOAD, we just block until things are written, so we never
+ *   enter dfuDNLOAD_SYNC or dfuDNBUSY
+ * - no dfuMANIFEST_SYNC, dfuMANIFEST, or dfuMANIFEST_WAIT_RESET
+ * - to keep our buffers small, we only accept EP0-sized blocks
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+#include "dfu.h"
+
+#include "board.h"
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define debug(...)
+#define error(...)
+
+
+#ifndef DFU_ALT_SETTINGS
+#define	DFU_ALT_SETTINGS	1
+#endif
+
+#ifndef DFU_ALT_NAME_0_IDX
+#define	DFU_ALT_NAME_0_IDX	0
+#endif
+
+#ifndef DFU_ALT_NAME_1_IDX
+#define	DFU_ALT_NAME_1_IDX	0
+#endif
+
+#ifndef DFU_ALT_NAME_2_IDX
+#define	DFU_ALT_NAME_2_IDX	0
+#endif
+
+
+const uint8_t device_descriptor[] = {
+	18,			/* bLength */
+	USB_DT_DEVICE,		/* bDescriptorType */
+	LE(0x100),		/* bcdUSB */
+	USB_CLASS_APP_SPEC,	/* bDeviceClass */
+	0x00,			/* bDeviceSubClass (per interface) */
+	0x00,			/* bDeviceProtocol (per interface) */
+	EP0_SIZE,		/* bMaxPacketSize */
+	LE(DFU_USB_VENDOR),	/* idVendor */
+	LE(DFU_USB_PRODUCT),	/* idProduct */
+	LE(0x0001),		/* bcdDevice */
+	0,			/* iManufacturer */
+	0,			/* iProduct */
+#ifdef HAS_BOARD_SERNUM
+	1,			/* iSerialNumber */
+#else
+	0,			/* iSerialNumber */
+#endif
+	1			/* bNumConfigurations */
+};
+
+
+const uint8_t config_descriptor[] = {
+	9,			/* bLength */
+	USB_DT_CONFIG,		/* bDescriptorType */
+	LE(9+9*DFU_ALT_SETTINGS), /* wTotalLength */
+	1,			/* bNumInterfaces */
+	1,			/* bConfigurationValue (> 0 !) */
+	0,			/* iConfiguration */
+//	USB_ATTR_SELF_POWERED | USB_ATTR_BUS_POWERED,
+	USB_ATTR_BUS_POWERED,	/* bmAttributes */
+	((BOARD_MAX_mA)+1)/2,	/* bMaxPower */
+
+	/* Interface #0 */
+
+	DFU_ITF_DESCR(0, 0, dfu_proto_dfu, DFU_ALT_NAME_0_IDX)
+#if DFU_ALT_SETTINGS > 1
+	DFU_ITF_DESCR(0, 1, dfu_proto_dfu, DFU_ALT_NAME_1_IDX)
+#endif
+#if DFU_ALT_SETTINGS > 2
+	DFU_ITF_DESCR(0, 2, dfu_proto_dfu, DFU_ALT_NAME_2_IDX)
+#endif
+};
+
+
+static uint16_t next_block = 0;
+static bool did_download;
+
+
+static uint8_t buf[EP0_SIZE];
+
+
+static void block_write(void *user)
+{
+	uint16_t *size = user;
+
+	dfu_flash_ops->write(buf, *size);
+}
+
+
+static bool block_receive(uint16_t length)
+{
+	static uint16_t size;
+
+	if (!dfu_flash_ops->can_write(length)) {
+		dfu.state = dfuERROR;	
+		dfu.status = errADDRESS;
+		return 0;
+	}
+	if (length > EP0_SIZE) {
+		dfu.state = dfuERROR;	
+		dfu.status = errUNKNOWN;
+		return 0;
+	}
+	size = length;
+	usb_recv(&eps[0], buf, size, block_write, &size);
+	return 1;
+}
+
+
+static bool block_transmit(uint16_t length)
+{
+	uint16_t got;
+
+	if (length > EP0_SIZE) {
+		dfu.state = dfuERROR;	
+		dfu.status = errUNKNOWN;
+		return 1;
+	}
+	got = dfu_flash_ops->read(buf, length);
+	if (got < length) {
+		length = got;
+		dfu.state = dfuIDLE;
+	}
+	usb_send(&eps[0], buf, length, NULL, NULL);
+	return 1;
+}
+
+
+static bool my_setup(const struct setup_request *setup)
+{
+	bool ok;
+
+	switch (setup->bmRequestType | setup->bRequest << 8) {
+	case DFU_TO_DEV(DFU_DETACH):
+		debug("DFU_DETACH\n");
+		/*
+		 * The DFU spec says thay this is sent in protocol 1 only.
+		 * However, dfu-util also sends it to get out of DFU mode,
+		 * so we just don't make a fuss and ignore it.
+		 */
+		return 1;
+	case DFU_TO_DEV(DFU_DNLOAD):
+		debug("DFU_DNLOAD\n");
+		if (dfu.state == dfuIDLE) {
+			next_block = setup->wValue;
+			dfu_flash_ops->start();
+		}
+		else if (dfu.state != dfuDNLOAD_IDLE) {
+			error("bad state\n");
+			return 0;
+		}
+		if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
+			debug("retransmisson\n");
+			return 1;
+		}
+		if (setup->wValue != next_block) {
+			debug("bad block (%d vs. %d)\n",
+			    setup->wValue, next_block);
+			dfu.state = dfuERROR;
+			dfu.status = errUNKNOWN;
+			return 1;
+		}
+		if (!setup->wLength) {
+			debug("DONE\n");
+			dfu_flash_ops->end_write();
+			dfu.state = dfuIDLE;
+			did_download = 1;
+			return 1;
+		}
+		ok = block_receive(setup->wLength);
+		next_block++;
+		dfu.state = dfuDNLOAD_IDLE;
+		return ok;
+	case DFU_FROM_DEV(DFU_UPLOAD):
+		debug("DFU_UPLOAD\n");
+		if (dfu.state == dfuIDLE) {
+			next_block = setup->wValue;
+			dfu_flash_ops->start();
+		}
+		else if (dfu.state != dfuUPLOAD_IDLE)
+			return 0;
+		if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
+			debug("retransmisson\n");
+			/* @@@ try harder */
+			dfu.state = dfuERROR;
+			dfu.status = errUNKNOWN;
+			return 1;
+		}
+		if (setup->wValue != next_block) {
+			debug("bad block (%d vs. %d)\n",
+			    setup->wValue, next_block);
+			dfu.state = dfuERROR;
+			dfu.status = errUNKNOWN;
+			return 1;
+		}
+		ok = block_transmit(setup->wLength);
+		next_block++;
+		dfu.state = dfuUPLOAD_IDLE;
+		return ok;
+	case DFU_TO_DEV(DFU_ABORT):
+		debug("DFU_ABORT\n");
+		dfu.state = dfuIDLE;
+		dfu.status = OK;
+		return 1;
+	default:
+		return dfu_setup_common(setup);
+	}
+}
+
+
+static void my_reset(void)
+{
+#if 0
+	/* @@@ not nice -- think about where this should go */
+	extern void run_payload(void);
+
+	if (did_download)
+		run_payload();
+#endif
+}
+
+
+void dfu_init(void)
+{
+	user_setup = my_setup;
+	user_get_descriptor = dfu_my_descr;
+	user_reset = my_reset;
+}
diff --git a/atusb/usb/dfu.h b/atusb/usb/dfu.h
new file mode 100644
index 0000000..bc35bbc
--- /dev/null
+++ b/atusb/usb/dfu.h
@@ -0,0 +1,119 @@
+/*
+ * boot/dfu.h - DFU protocol constants and data structures
+ *
+ * Written 2008, 2011, 2013-2015 by Werner Almesberger
+ * Copyright 2008, 2011, 2013-2015 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef DFU_H
+#define DFU_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+
+
+enum dfu_request {
+	DFU_DETACH,
+	DFU_DNLOAD,
+	DFU_UPLOAD,
+	DFU_GETSTATUS,
+	DFU_CLRSTATUS,
+	DFU_GETSTATE,
+	DFU_ABORT,
+};
+
+
+enum dfu_status {
+	OK,
+	errTARGET,
+	errFILE,
+	errWRITE,
+	errERASE,
+	errCHECK_ERASED,
+	errPROG,
+	errVERIFY,
+	errADDRESS,
+	errNOTDONE,
+	errFIRMWARE,
+	errVENDOR,
+	errUSBR,
+	errPOR,
+	errUNKNOWN,
+	errSTALLEDPKT,
+};
+
+
+enum dfu_state {
+	appIDLE,
+	appDETACH,
+	dfuIDLE,
+	dfuDNLOAD_SYNC,
+	dfuDNBUSY,
+	dfuDNLOAD_IDLE,
+	dfuMANIFEST_SYNC,
+	dfuMANIFEST,
+	dfuMANIFEST_WAIT_RESET,
+	dfuUPLOAD_IDLE,
+	dfuERROR
+};
+
+enum dfu_itf_proto {
+	dfu_proto_runtime	= 1,	/* Runtime protocol */
+	dfu_proto_dfu		= 2,	/* DFU mode protocol */
+};
+
+
+#define DFU_DT_FUNCTIONAL	0x21	/* DFU FUNCTIONAL descriptor type */
+
+
+#define	DFU_TO_DEV(req)		(0x21 | (req) << 8)
+#define	DFU_FROM_DEV(req)	(0xa1 | (req) << 8)
+
+
+struct dfu {
+	uint8_t status;		/* bStatus */
+	uint8_t toL, toM, toH;	/* bwPollTimeout */
+	uint8_t state;		/* bState */
+	uint8_t iString;
+};
+
+
+#define	DFU_ITF_DESCR(itf, alt, proto, idx)				     \
+	9,			/* bLength */				     \
+	USB_DT_INTERFACE,	/* bDescriptorType */			     \
+	(itf),			/* bInterfaceNumber */			     \
+	(alt),			/* bAlternateSetting */			     \
+	0,			/* bNumEndpoints */			     \
+	0xfe,			/* bInterfaceClass (application specific) */ \
+	0x01,			/* bInterfaceSubClass (device fw upgrade) */ \
+	(proto),		/* bInterfaceProtocol (dfu_proto_*) */	     \
+	(idx),			/* iInterface */
+
+
+struct dfu_flash_ops {
+	void (*start)(void);
+	bool (*can_write)(uint16_t size);
+	void (*write)(const uint8_t *buf, uint16_t size);
+	void (*end_write)(void);
+	uint16_t (*read)(uint8_t *buf, uint16_t size);
+};
+
+extern struct dfu dfu;
+extern const struct dfu_flash_ops *dfu_flash_ops;
+
+
+bool dfu_setup_common(const struct setup_request *setup);
+bool dfu_my_descr(uint8_t type, uint8_t index, const uint8_t **reply,
+    uint8_t *size);
+
+void dfu_init(void);
+
+#endif /* !DFU_H */
diff --git a/atusb/usb/dfu_common.c b/atusb/usb/dfu_common.c
new file mode 100644
index 0000000..9b6feef
--- /dev/null
+++ b/atusb/usb/dfu_common.c
@@ -0,0 +1,101 @@
+/*
+ * boot/dfu_common.c - DFU protocol engine parts common to App/DFU
+ *
+ * Written 2008-2011, 2013-2014 by Werner Almesberger
+ * Copyright 2008-2011, 2013-2014 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf
+ */
+
+/*
+ * A few, erm, shortcuts:
+ *
+ * - we don't bother with the app* states since DFU is all this firmware does
+ * - after DFU_DNLOAD, we just block until things are written, so we never
+ *   enter dfuDNLOAD_SYNC or dfuDNBUSY
+ * - no dfuMANIFEST_SYNC, dfuMANIFEST, or dfuMANIFEST_WAIT_RESET
+ * - to keep our buffers small, we only accept EP0-sized blocks
+ */
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+#include "dfu.h"
+
+#include "board.h"
+#include "../sernum.h"
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define debug(...)
+#define error(...)
+
+
+static const uint8_t functional_descriptor[] = {
+	9,			/* bLength */
+	DFU_DT_FUNCTIONAL,	/* bDescriptorType */
+	0xf,			/* bmAttributes (claim omnipotence :-) */
+	LE(0xffff),		/* wDetachTimeOut (we're very patient) */
+	LE(EP0_SIZE),		/* wTransferSize */
+	LE(0x101),		/* bcdDFUVersion */
+};
+
+
+/*
+ * The worst-case activity would be flashing a one page and erasing another
+ * one, would should take less than 10 ms. A 100 ms timeout ought to be plenty.
+ */
+
+struct dfu dfu = {
+	OK,			/* bStatus */
+	LE(100), 0,		/* bwPollTimeout, 100 ms */
+	dfuIDLE,		/* bState */
+	0,			/* iString */
+};
+
+
+bool dfu_setup_common(const struct setup_request *setup)
+{
+	switch (setup->bmRequestType | setup->bRequest << 8) {
+	case DFU_FROM_DEV(DFU_GETSTATUS):
+		debug("DFU_GETSTATUS\n");
+		usb_send(&eps[0], (uint8_t *) &dfu, sizeof(dfu), NULL, NULL);
+		return 1;
+	case DFU_TO_DEV(DFU_CLRSTATUS):
+		debug("DFU_CLRSTATUS\n");
+		dfu.state = dfuIDLE;
+		dfu.status = OK;
+		return 1;
+	case DFU_FROM_DEV(DFU_GETSTATE):
+		debug("DFU_GETSTATE\n");
+		usb_send(&eps[0], &dfu.state, 1, NULL, NULL);
+		return 1;
+	default:
+		error("DFU rt %x, rq%x ?\n",
+		    setup->bmRequestType, setup->bRequest);
+		return 0;
+	}
+}
+
+
+bool dfu_my_descr(uint8_t type, uint8_t index, const uint8_t **reply,
+    uint8_t *size)
+{
+	if (type != DFU_DT_FUNCTIONAL)
+		return sernum_get_descr(type, index, reply, size);
+	*reply = functional_descriptor;
+	*size = sizeof(functional_descriptor);
+	return 1;
+}
diff --git a/atusb/usb/usb.c b/atusb/usb/usb.c
new file mode 100644
index 0000000..543d8c2
--- /dev/null
+++ b/atusb/usb/usb.c
@@ -0,0 +1,181 @@
+/*
+ * fw/usb/usb.c - USB hardware setup and standard device requests
+ *
+ * Written 2008-2011, 2013, 2015 by Werner Almesberger
+ * Copyright 2008-2011, 2013, 2015 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * Known issues:
+ * - no suspend/resume
+ * - should support EP clearing and stalling
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+#include "board.h"
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#if 1
+extern void panic(void);
+#define BUG_ON(cond)	do { if (cond) panic(); } while (0)
+#else
+#define BUG_ON(cond)
+#endif
+
+bool (*user_setup)(const struct setup_request *setup);
+void (*user_set_interface)(int nth);
+bool (*user_get_descriptor)(uint8_t type, uint8_t index,
+    const uint8_t **reply, uint8_t *size);
+void (*user_reset)(void);
+
+
+void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf,
+    uint8_t size, void (*callback)(void *user), void *user)
+{
+	BUG_ON(ep->state);
+	ep->state = state;
+	ep->buf = buf;
+	ep->end = buf+size;
+	ep->callback = callback;
+	ep->user = user;
+	usb_ep_change(ep);
+}
+
+
+static bool get_descriptor(uint8_t type, uint8_t index, uint16_t length)
+{
+	const uint8_t *reply;
+	uint8_t size;
+
+	switch (type) {
+	case USB_DT_DEVICE:
+		reply = device_descriptor;
+		size = reply[0];
+		break;
+	case USB_DT_CONFIG:
+		if (index)
+			return 0;
+		reply = config_descriptor;
+		size = reply[2];
+		break;
+	default:
+		if (!user_get_descriptor)
+			return 0;
+		if (!user_get_descriptor(type, index, &reply, &size))
+			return 0;
+	}
+	if (length < size)
+		size = length;
+	usb_send(&eps[0], reply, size, NULL, NULL);
+	return 1;
+}
+
+
+bool handle_setup(const struct setup_request *setup)
+{
+	switch (setup->bmRequestType | setup->bRequest << 8) {
+
+	/*
+	 * Device request
+	 *
+	 * See http://www.beyondlogic.org/usbnutshell/usb6.htm
+	 */
+
+	case FROM_DEVICE(GET_STATUS):
+		if (setup->wLength != 2)
+			return 0;
+		usb_send(&eps[0], "\000", 2, NULL, NULL);
+		break;
+	case TO_DEVICE(CLEAR_FEATURE):
+		break;
+	case TO_DEVICE(SET_FEATURE):
+		return 0;
+	case TO_DEVICE(SET_ADDRESS):
+		set_addr(setup->wValue);
+		break;
+	case FROM_DEVICE(GET_DESCRIPTOR):
+	case FROM_INTERFACE(GET_DESCRIPTOR):
+		if (!get_descriptor(setup->wValue >> 8, setup->wValue,
+		    setup->wLength))
+			return 0;
+		break;
+	case TO_DEVICE(SET_DESCRIPTOR):
+		return 0;
+	case FROM_DEVICE(GET_CONFIGURATION):
+		usb_send(&eps[0], "", 1, NULL, NULL);
+		break;
+	case TO_DEVICE(SET_CONFIGURATION):
+		if (setup->wValue != config_descriptor[5])
+			return 0;
+		break;
+
+	/*
+	 * Interface request
+	 */
+
+	case FROM_INTERFACE(GET_STATUS):
+		return 0;
+	case TO_INTERFACE(CLEAR_FEATURE):
+		return 0;
+	case TO_INTERFACE(SET_FEATURE):
+		return 0;
+	case FROM_INTERFACE(GET_INTERFACE):
+		return 0;
+	case TO_INTERFACE(SET_INTERFACE):
+		{
+			const uint8_t *interface_descriptor =
+			    config_descriptor+9;
+			const uint8_t *p;
+			int i;
+
+			i = 0;
+			for (p = interface_descriptor;
+			    p != config_descriptor+config_descriptor[2];
+			    p += p[0]) {
+				if (p[1] != USB_DT_INTERFACE)
+					continue;
+				if (p[2] == setup->wIndex &&
+				    p[3] == setup->wValue) {
+					if (user_set_interface)
+						user_set_interface(i);
+					return 1;
+				}
+				i++;
+			}
+			return 0;
+		}
+		break;
+
+	/*
+	 * Endpoint request
+	 */
+
+	case FROM_ENDPOINT(GET_STATUS):
+		return 0;
+	case TO_ENDPOINT(CLEAR_FEATURE):
+		return 0;
+	case TO_ENDPOINT(SET_FEATURE):
+		return 0;
+	case FROM_ENDPOINT(SYNCH_FRAME):
+		return 0;
+
+	default:
+		if (user_setup)
+			return user_setup(setup);
+		return 0;
+	}
+
+	return 1;
+}
diff --git a/atusb/usb/usb.h b/atusb/usb/usb.h
new file mode 100644
index 0000000..cb40f9e
--- /dev/null
+++ b/atusb/usb/usb.h
@@ -0,0 +1,189 @@
+/*
+ * fw/usb//usb.h - USB hardware setup and standard device requests
+ *
+ * Written 2008, 2009, 2011, 2013, 2015 by Werner Almesberger
+ * Copyright 2008, 2009, 2011, 2013, 2015 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef USB_H
+#define USB_H
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+
+/*
+ * Packet identifier types
+ */
+
+#define	PID_OUT		0x1
+#define	PID_IN		0x9
+#define	PID_SOF		0x5
+#define	PID_SETUP	0xd
+#define	PID_DATA0	0x3
+#define	PID_DATA1	0xb
+#define	PID_ACK		0x2
+#define	PID_NAK		0xa
+#define	PID_STALL	0xe
+
+/*
+ * Descriptor types
+ *
+ * Reuse libusb naming scheme (/usr/include/usb.h)
+ */
+
+#define	USB_DT_DEVICE		1
+#define	USB_DT_CONFIG		2
+#define	USB_DT_STRING		3
+#define	USB_DT_INTERFACE	4
+#define	USB_DT_ENDPOINT		5
+
+/*
+ * Device classes
+ *
+ * Reuse libusb naming scheme (/usr/include/usb.h)
+ */
+
+#define USB_CLASS_PER_INTERFACE	0
+#define	USB_CLASS_COMM		2
+#define	USB_CLASS_HID		3
+#define	USB_CLASS_MASS_STORAGE	8
+#define	USB_CLASS_HUB		9
+#define	USB_CLASS_DATA		10
+#define	USB_CLASS_APP_SPEC	0xfe
+#define USB_CLASS_VENDOR_SPEC	0xff
+
+/*
+ * Configuration attributes
+ */
+
+#define	USB_ATTR_BUS_POWERED	0x80
+#define	USB_ATTR_SELF_POWERED	0x40
+#define	USB_ATTR_REMOTE_WAKEUP	0x20
+
+/*
+ * Endpoint type
+ */
+
+#define	USB_ENDPOINT_TYPE_CONTROL	0
+#define	USB_ENDPOINT_TYPE_ISOCHRONOUS	1
+#define	USB_ENDPOINT_TYPE_BULK		2
+#define	USB_ENDPOINT_TYPE_INTERRUPT	3
+
+/*
+ * Setup request types
+ */
+
+#define	TO_DEVICE(req)		(0x00 | (req) << 8)
+#define	FROM_DEVICE(req)	(0x80 | (req) << 8)
+#define	TO_INTERFACE(req)	(0x01 | (req) << 8)
+#define	FROM_INTERFACE(req)	(0x81 | (req) << 8)
+#define	TO_ENDPOINT(req)	(0x02 | (req) << 8)
+#define	FROM_ENDPOINT(req)	(0x82 | (req) << 8)
+
+/*
+ * Setup requests
+ */
+
+#define	GET_STATUS		0x00
+#define	CLEAR_FEATURE		0x01
+#define	SET_FEATURE		0x03
+#define	SET_ADDRESS		0x05
+#define	GET_DESCRIPTOR		0x06
+#define	SET_DESCRIPTOR		0x07
+#define	GET_CONFIGURATION	0x08
+#define	SET_CONFIGURATION	0x09
+#define	GET_INTERFACE		0x0a
+#define	SET_INTERFACE		0x0b
+#define	SYNCH_FRAME		0x0c
+
+/*
+ * USB Language ID codes
+ *
+ * http://www.usb.org/developers/docs/USB_LANGIDs.pdf
+ */
+
+#define	USB_LANGID_ENGLISH_US	0x409
+
+
+/*
+ * Odd. sdcc seems to think "x" assumes the size of the destination, i.e.,
+ * uint8_t. Hence the cast.
+ */
+
+#define LE(x) ((uint16_t) (x) & 0xff), ((uint16_t) (x) >> 8)
+
+#define LO(x) (((uint8_t *) &(x))[0])
+#define HI(x) (((uint8_t *) &(x))[1])
+
+
+#ifdef LOW_SPEED
+#define	EP0_SIZE	8
+#else
+#define	EP0_SIZE	64
+#endif
+
+#define	EP1_SIZE	64	/* simplify */
+
+
+enum ep_state {
+	EP_IDLE,
+	EP_RX,
+	EP_TX,
+	EP_STALL,
+};
+
+struct ep_descr {
+	enum ep_state state;
+	uint8_t *buf;
+	uint8_t *end;
+	uint8_t size;
+	void (*callback)(void *user);
+	void *user;
+};
+
+struct setup_request {
+	uint8_t bmRequestType;
+	uint8_t bRequest;
+	uint16_t wValue;
+	uint16_t wIndex;
+	uint16_t wLength;
+};
+
+
+extern const uint8_t device_descriptor[];
+extern const uint8_t config_descriptor[];
+extern struct ep_descr eps[];
+
+extern bool (*user_setup)(const struct setup_request *setup);
+extern void (*user_set_interface)(int nth);
+extern bool (*user_get_descriptor)(uint8_t type, uint8_t index,
+    const uint8_t **reply, uint8_t *size);
+extern void (*user_reset)(void);
+
+
+#define	usb_left(ep) ((ep)->end-(ep)->buf)
+#define usb_send(ep, buf, size, callback, user) \
+	usb_io(ep, EP_TX, (void *) buf, size, callback, user)
+#define usb_recv(ep, buf, size, callback, user) \
+	usb_io(ep, EP_RX, buf, size, callback, user)
+
+void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf,
+    uint8_t size, void (*callback)(void *user), void *user);
+
+bool handle_setup(const struct setup_request *setup);
+void set_addr(uint8_t addr);
+void usb_ep_change(struct ep_descr *ep);
+void usb_reset(void);
+void usb_init(void);
+
+void ep_init(void);
+
+#endif /* !USB_H */
diff --git a/atusb/version.h b/atusb/version.h
new file mode 100644
index 0000000..8fd6a2c
--- /dev/null
+++ b/atusb/version.h
@@ -0,0 +1,23 @@
+/*
+ * fw/version.h - Automatically generated version string
+ *
+ * Written 2008, 2011 by Werner Almesberger
+ * Copyright 2008, 2011 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef VERSION_H
+#define VERSION_H
+
+#include <stdint.h>
+
+
+extern const char *build_date;
+extern const uint16_t build_number;
+
+#endif /* !VERSION_H */
-- 
2.26.0

