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