Commits

Trammell Hudson committed 521c6b2 Draft

import from seven segment project

  • Participants

Comments (0)

Files changed (7)

+# Hey Emacs, this is a -*- makefile -*-
+#----------------------------------------------------------------------------
+# WinAVR Makefile Template written by Eric B. Weddington, Jörg Wunsch, et al.
+#
+# Released to the Public Domain
+#
+# Additional material for this makefile was written by:
+# Peter Fleury
+# Tim Henigan
+# Colin O'Flynn
+# Reiner Patommel
+# Markus Pfaff
+# Sander Pool
+# Frederik Rouleau
+# Carlos Lamas
+#
+#----------------------------------------------------------------------------
+# On command line:
+#
+# make all = Make software.
+#
+# make clean = Clean out built project files.
+#
+# make coff = Convert ELF to AVR COFF.
+#
+# make extcoff = Convert ELF to AVR Extended COFF.
+#
+# make program = Download the hex file to the device, using avrdude.
+#                Please customize the avrdude settings below first!
+#
+# make debug = Start either simulavr or avarice as specified for debugging, 
+#              with avr-gdb or avr-insight as the front end for debugging.
+#
+# make filename.s = Just compile filename.c into the assembler code only.
+#
+# make filename.i = Create a preprocessed source file for use in submitting
+#                   bug reports to the GCC project.
+#
+# To rebuild project do "make clean" then "make all".
+#----------------------------------------------------------------------------
+
+
+# Target file name (without extension).
+TARGET = alphawatch
+
+
+# List C source files here. (C dependencies are automatically generated.)
+SRC =	$(TARGET).c \
+	bits.c \
+	usb_serial.c \
+
+# MCU name, you MUST set this to match the board you are using
+# type "make clean" after changing this, so all files will be rebuilt
+#
+#MCU = at90usb162       # Teensy 1.0
+MCU = atmega32u4        # Teensy 2.0
+#MCU = at90usb646       # Teensy++ 1.0
+#MCU = at90usb1286      # Teensy++ 2.0
+
+
+# Processor frequency.
+#   Normally the first thing your program should do is set the clock prescaler,
+#   so your program will run at the correct speed.  You should also set this
+#   variable to same clock speed.  The _delay_ms() macro uses this, and many
+#   examples use this variable to calculate timings.  Do not add a "UL" here.
+F_CPU = 16000000
+
+
+# Output format. (can be srec, ihex, binary)
+FORMAT = ihex
+
+
+# Object files directory
+#     To put object files in current directory, use a dot (.), do NOT make
+#     this an empty or blank macro!
+OBJDIR = .
+
+
+# List C++ source files here. (C dependencies are automatically generated.)
+CPPSRC = 
+
+
+# List Assembler source files here.
+#     Make them always end in a capital .S.  Files ending in a lowercase .s
+#     will not be considered source files but generated files (assembler
+#     output from the compiler), and will be deleted upon "make clean"!
+#     Even though the DOS/Win* filesystem matches both .s and .S the same,
+#     it will preserve the spelling of the filenames, and gcc itself does
+#     care about how the name is spelled on its command-line.
+ASRC =
+
+
+# Optimization level, can be [0, 1, 2, 3, s]. 
+#     0 = turn off optimization. s = optimize for size.
+#     (Note: 3 is not always the best optimization level. See avr-libc FAQ.)
+OPT = 3
+
+
+# Debugging format.
+#     Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs.
+#     AVR Studio 4.10 requires dwarf-2.
+#     AVR [Extended] COFF format requires stabs, plus an avr-objcopy run.
+DEBUG = dwarf-2
+
+
+# List any extra directories to look for include files here.
+#     Each directory must be seperated by a space.
+#     Use forward slashes for directory separators.
+#     For a directory that has spaces, enclose it in quotes.
+EXTRAINCDIRS = 
+
+
+# Compiler flag to set the C Standard level.
+#     c89   = "ANSI" C
+#     gnu89 = c89 plus GCC extensions
+#     c99   = ISO C99 standard (not yet fully implemented)
+#     gnu99 = c99 plus GCC extensions
+CSTANDARD = -std=gnu99
+
+
+# Place -D or -U options here for C sources
+CDEFS = -DF_CPU=$(F_CPU)UL
+
+
+# Place -D or -U options here for ASM sources
+ADEFS = -DF_CPU=$(F_CPU)
+
+
+# Place -D or -U options here for C++ sources
+CPPDEFS = -DF_CPU=$(F_CPU)UL
+#CPPDEFS += -D__STDC_LIMIT_MACROS
+#CPPDEFS += -D__STDC_CONSTANT_MACROS
+
+
+
+#---------------- Compiler Options C ----------------
+#  -g*:          generate debugging information
+#  -O*:          optimization level
+#  -f...:        tuning, see GCC manual and avr-libc documentation
+#  -Wall...:     warning level
+#  -Wa,...:      tell GCC to pass this to the assembler.
+#    -adhlns...: create assembler listing
+CFLAGS = -g$(DEBUG)
+CFLAGS += $(CDEFS)
+CFLAGS += -O$(OPT)
+CFLAGS += -funsigned-char
+CFLAGS += -funsigned-bitfields
+CFLAGS += -ffunction-sections
+CFLAGS += -fpack-struct
+CFLAGS += -fshort-enums
+CFLAGS += -Wall
+CFLAGS += -Wstrict-prototypes
+#CFLAGS += -mshort-calls
+#CFLAGS += -fno-unit-at-a-time
+#CFLAGS += -Wundef
+#CFLAGS += -Wunreachable-code
+#CFLAGS += -Wsign-compare
+CFLAGS += -Wa,-adhlns=$(<:%.c=$(OBJDIR)/%.lst)
+CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
+CFLAGS += $(CSTANDARD)
+
+
+#---------------- Compiler Options C++ ----------------
+#  -g*:          generate debugging information
+#  -O*:          optimization level
+#  -f...:        tuning, see GCC manual and avr-libc documentation
+#  -Wall...:     warning level
+#  -Wa,...:      tell GCC to pass this to the assembler.
+#    -adhlns...: create assembler listing
+CPPFLAGS = -g$(DEBUG)
+CPPFLAGS += $(CPPDEFS)
+CPPFLAGS += -O$(OPT)
+CPPFLAGS += -funsigned-char
+CPPFLAGS += -funsigned-bitfields
+CPPFLAGS += -fpack-struct
+CPPFLAGS += -fshort-enums
+CPPFLAGS += -fno-exceptions
+CPPFLAGS += -Wall
+CPPFLAGS += -Wundef
+#CPPFLAGS += -mshort-calls
+#CPPFLAGS += -fno-unit-at-a-time
+#CPPFLAGS += -Wstrict-prototypes
+#CPPFLAGS += -Wunreachable-code
+#CPPFLAGS += -Wsign-compare
+CPPFLAGS += -Wa,-adhlns=$(<:%.cpp=$(OBJDIR)/%.lst)
+CPPFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
+#CPPFLAGS += $(CSTANDARD)
+
+
+#---------------- Assembler Options ----------------
+#  -Wa,...:   tell GCC to pass this to the assembler.
+#  -adhlns:   create listing
+#  -gstabs:   have the assembler create line number information; note that
+#             for use in COFF files, additional information about filenames
+#             and function names needs to be present in the assembler source
+#             files -- see avr-libc docs [FIXME: not yet described there]
+#  -listing-cont-lines: Sets the maximum number of continuation lines of hex 
+#       dump that will be displayed for a given single line of source input.
+ASFLAGS = $(ADEFS) -Wa,-adhlns=$(<:%.S=$(OBJDIR)/%.lst),-gstabs,--listing-cont-lines=100
+
+
+#---------------- Library Options ----------------
+# Minimalistic printf version
+PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min
+
+# Floating point printf version (requires MATH_LIB = -lm below)
+PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt
+
+# If this is left blank, then it will use the Standard printf version.
+PRINTF_LIB = 
+#PRINTF_LIB = $(PRINTF_LIB_MIN)
+#PRINTF_LIB = $(PRINTF_LIB_FLOAT)
+
+
+# Minimalistic scanf version
+SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min
+
+# Floating point + %[ scanf version (requires MATH_LIB = -lm below)
+SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt
+
+# If this is left blank, then it will use the Standard scanf version.
+SCANF_LIB = 
+#SCANF_LIB = $(SCANF_LIB_MIN)
+#SCANF_LIB = $(SCANF_LIB_FLOAT)
+
+
+MATH_LIB = -lm
+
+
+# List any extra directories to look for libraries here.
+#     Each directory must be seperated by a space.
+#     Use forward slashes for directory separators.
+#     For a directory that has spaces, enclose it in quotes.
+EXTRALIBDIRS = 
+
+
+
+#---------------- External Memory Options ----------------
+
+# 64 KB of external RAM, starting after internal RAM (ATmega128!),
+# used for variables (.data/.bss) and heap (malloc()).
+#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff
+
+# 64 KB of external RAM, starting after internal RAM (ATmega128!),
+# only used for heap (malloc()).
+#EXTMEMOPTS = -Wl,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff
+
+EXTMEMOPTS =
+
+
+
+#---------------- Linker Options ----------------
+#  -Wl,...:     tell GCC to pass this to linker.
+#    -Map:      create map file
+#    --cref:    add cross reference to  map file
+LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
+LDFLAGS += -Wl,--relax
+LDFLAGS += -Wl,--gc-sections
+LDFLAGS += $(EXTMEMOPTS)
+LDFLAGS += $(patsubst %,-L%,$(EXTRALIBDIRS))
+LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)
+#LDFLAGS += -T linker_script.x
+
+
+
+#---------------- Programming Options (avrdude) ----------------
+
+# Programming hardware
+# Type: avrdude -c ?
+# to get a full listing.
+#
+AVRDUDE_PROGRAMMER = stk500v2
+
+# com1 = serial port. Use lpt1 to connect to parallel port.
+AVRDUDE_PORT = com1    # programmer connected to serial device
+
+AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex
+#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep
+
+
+# Uncomment the following if you want avrdude's erase cycle counter.
+# Note that this counter needs to be initialized first using -Yn,
+# see avrdude manual.
+#AVRDUDE_ERASE_COUNTER = -y
+
+# Uncomment the following if you do /not/ wish a verification to be
+# performed after programming the device.
+#AVRDUDE_NO_VERIFY = -V
+
+# Increase verbosity level.  Please use this when submitting bug
+# reports about avrdude. See <http://savannah.nongnu.org/projects/avrdude> 
+# to submit bug reports.
+#AVRDUDE_VERBOSE = -v -v
+
+AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
+AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY)
+AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE)
+AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER)
+
+
+
+#---------------- Debugging Options ----------------
+
+# For simulavr only - target MCU frequency.
+DEBUG_MFREQ = $(F_CPU)
+
+# Set the DEBUG_UI to either gdb or insight.
+# DEBUG_UI = gdb
+DEBUG_UI = insight
+
+# Set the debugging back-end to either avarice, simulavr.
+DEBUG_BACKEND = avarice
+#DEBUG_BACKEND = simulavr
+
+# GDB Init Filename.
+GDBINIT_FILE = __avr_gdbinit
+
+# When using avarice settings for the JTAG
+JTAG_DEV = /dev/com1
+
+# Debugging port used to communicate between GDB / avarice / simulavr.
+DEBUG_PORT = 4242
+
+# Debugging host used to communicate between GDB / avarice / simulavr, normally
+#     just set to localhost unless doing some sort of crazy debugging when 
+#     avarice is running on a different computer.
+DEBUG_HOST = localhost
+
+
+
+#============================================================================
+
+
+# Define programs and commands.
+SHELL = sh
+AVR_PATH = /Applications/Arduino.app/Contents//Resources/Java/hardware/tools/avr
+CC = $(AVR_PATH)/bin/avr-gcc
+OBJCOPY = $(AVR_PATH)/bin/avr-objcopy
+OBJDUMP = $(AVR_PATH)/bin/avr-objdump
+SIZE = $(AVR_PATH)/bin/avr-size
+AR = $(AVR_PATH)/bin/avr-ar rcs
+NM = $(AVR_PATH)/bin/avr-nm
+AVRDUDE = $(AVR_PATH)/bin/avrdude
+REMOVE = rm -f
+REMOVEDIR = rm -rf
+COPY = cp
+WINSHELL = cmd
+
+
+# Define Messages
+# English
+MSG_ERRORS_NONE = Errors: none
+MSG_BEGIN = -------- begin --------
+MSG_END = --------  end  --------
+MSG_SIZE_BEFORE = Size before: 
+MSG_SIZE_AFTER = Size after:
+MSG_COFF = Converting to AVR COFF:
+MSG_EXTENDED_COFF = Converting to AVR Extended COFF:
+MSG_FLASH = Creating load file for Flash:
+MSG_EEPROM = Creating load file for EEPROM:
+MSG_EXTENDED_LISTING = Creating Extended Listing:
+MSG_SYMBOL_TABLE = Creating Symbol Table:
+MSG_LINKING = Linking:
+MSG_COMPILING = Compiling C:
+MSG_COMPILING_CPP = Compiling C++:
+MSG_ASSEMBLING = Assembling:
+MSG_CLEANING = Cleaning project:
+MSG_CREATING_LIBRARY = Creating library:
+
+
+
+
+# Define all object files.
+OBJ = $(SRC:%.c=$(OBJDIR)/%.o) $(CPPSRC:%.cpp=$(OBJDIR)/%.o) $(ASRC:%.S=$(OBJDIR)/%.o) 
+
+# Define all listing files.
+LST = $(SRC:%.c=$(OBJDIR)/%.lst) $(CPPSRC:%.cpp=$(OBJDIR)/%.lst) $(ASRC:%.S=$(OBJDIR)/%.lst) 
+
+
+# Compiler flags to generate dependency files.
+GENDEPFLAGS = -MMD -MP -MF .dep/$(@F).d
+
+## Include the files from avr/include/
+#CFLAGS += -I$(AVR_PATH)/include
+
+# Combine all necessary flags and optional flags.
+# Add target processor to flags.
+ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)
+ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)
+ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)
+
+
+
+
+
+# Default target.
+all: begin gccversion sizebefore build sizeafter end
+
+# Change the build target to build a HEX file or a library.
+build: elf hex eep lss sym
+#build: lib
+
+
+elf: $(TARGET).elf
+hex: $(TARGET).hex
+eep: $(TARGET).eep
+lss: $(TARGET).lss
+sym: $(TARGET).sym
+LIBNAME=lib$(TARGET).a
+lib: $(LIBNAME)
+
+
+
+# Eye candy.
+# AVR Studio 3.x does not check make's exit code but relies on
+# the following magic strings to be generated by the compile job.
+begin:
+	@echo
+	@echo $(MSG_BEGIN)
+
+end:
+	@echo $(MSG_END)
+	@echo
+
+
+# Display size of file.
+HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex
+#ELFSIZE = $(SIZE) --mcu=$(MCU) --format=avr $(TARGET).elf
+ELFSIZE = $(SIZE) $(TARGET).elf
+
+sizebefore:
+	@if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); \
+	2>/dev/null; echo; fi
+
+sizeafter:
+	@if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); \
+	2>/dev/null; echo; fi
+
+
+
+# Display compiler version information.
+gccversion : 
+	@$(CC) --version
+
+
+
+# Program the device.  
+program: $(TARGET).hex $(TARGET).eep
+	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM)
+
+
+# Generate avr-gdb config/init file which does the following:
+#     define the reset signal, load the target file, connect to target, and set 
+#     a breakpoint at main().
+gdb-config: 
+	@$(REMOVE) $(GDBINIT_FILE)
+	@echo define reset >> $(GDBINIT_FILE)
+	@echo SIGNAL SIGHUP >> $(GDBINIT_FILE)
+	@echo end >> $(GDBINIT_FILE)
+	@echo file $(TARGET).elf >> $(GDBINIT_FILE)
+	@echo target remote $(DEBUG_HOST):$(DEBUG_PORT)  >> $(GDBINIT_FILE)
+ifeq ($(DEBUG_BACKEND),simulavr)
+	@echo load  >> $(GDBINIT_FILE)
+endif
+	@echo break main >> $(GDBINIT_FILE)
+
+debug: gdb-config $(TARGET).elf
+ifeq ($(DEBUG_BACKEND), avarice)
+	@echo Starting AVaRICE - Press enter when "waiting to connect" message displays.
+	@$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program --file \
+	$(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT)
+	@$(WINSHELL) /c pause
+
+else
+	@$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \
+	$(DEBUG_MFREQ) --port $(DEBUG_PORT)
+endif
+	@$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE)
+
+
+
+
+# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
+COFFCONVERT = $(OBJCOPY) --debugging
+COFFCONVERT += --change-section-address .data-0x800000
+COFFCONVERT += --change-section-address .bss-0x800000
+COFFCONVERT += --change-section-address .noinit-0x800000
+COFFCONVERT += --change-section-address .eeprom-0x810000
+
+
+
+coff: $(TARGET).elf
+	@echo
+	@echo $(MSG_COFF) $(TARGET).cof
+	$(COFFCONVERT) -O coff-avr $< $(TARGET).cof
+
+
+extcoff: $(TARGET).elf
+	@echo
+	@echo $(MSG_EXTENDED_COFF) $(TARGET).cof
+	$(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof
+
+
+
+# Create final output files (.hex, .eep) from ELF output file.
+%.hex: %.elf
+	@echo
+	@echo $(MSG_FLASH) $@
+	$(OBJCOPY) -O $(FORMAT) -R .eeprom -R .fuse -R .lock -R .signature $< $@
+
+%.eep: %.elf
+	@echo
+	@echo $(MSG_EEPROM) $@
+	-$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
+	--change-section-lma .eeprom=0 --no-change-warnings -O $(FORMAT) $< $@ || exit 0
+
+# Create extended listing file from ELF output file.
+%.lss: %.elf
+	@echo
+	@echo $(MSG_EXTENDED_LISTING) $@
+	$(OBJDUMP) -h -S -z $< > $@
+
+# Create a symbol table from ELF output file.
+%.sym: %.elf
+	@echo
+	@echo $(MSG_SYMBOL_TABLE) $@
+	$(NM) -n $< > $@
+
+
+
+# Create library from object files.
+.SECONDARY : $(TARGET).a
+.PRECIOUS : $(OBJ)
+%.a: $(OBJ)
+	@echo
+	@echo $(MSG_CREATING_LIBRARY) $@
+	$(AR) $@ $(OBJ)
+
+
+# Link: create ELF output file from object files.
+.SECONDARY : $(TARGET).elf
+.PRECIOUS : $(OBJ)
+%.elf: $(OBJ)
+	@echo
+	@echo $(MSG_LINKING) $@
+	$(CC) $(ALL_CFLAGS) $^ --output $@ $(LDFLAGS)
+
+
+# Compile: create object files from C source files.
+$(OBJDIR)/%.o : %.c
+	@echo
+	@echo $(MSG_COMPILING) $<
+	$(CC) -c $(ALL_CFLAGS) $< -o $@ 
+
+
+# Compile: create object files from C++ source files.
+$(OBJDIR)/%.o : %.cpp
+	@echo
+	@echo $(MSG_COMPILING_CPP) $<
+	$(CC) -c $(ALL_CPPFLAGS) $< -o $@ 
+
+
+# Compile: create assembler files from C source files.
+%.s : %.c
+	$(CC) -S $(ALL_CFLAGS) $< -o $@
+
+
+# Compile: create assembler files from C++ source files.
+%.s : %.cpp
+	$(CC) -S $(ALL_CPPFLAGS) $< -o $@
+
+
+# Assemble: create object files from assembler source files.
+$(OBJDIR)/%.o : %.S
+	@echo
+	@echo $(MSG_ASSEMBLING) $<
+	$(CC) -c $(ALL_ASFLAGS) $< -o $@
+
+
+# Create preprocessed source for use in sending a bug report.
+%.i : %.c
+	$(CC) -E -mmcu=$(MCU) -I. $(CFLAGS) $< -o $@ 
+
+
+# Target: clean project.
+clean: begin clean_list end
+
+clean_list :
+	@echo
+	@echo $(MSG_CLEANING)
+	$(REMOVE) $(TARGET).hex
+	$(REMOVE) $(TARGET).eep
+	$(REMOVE) $(TARGET).cof
+	$(REMOVE) $(TARGET).elf
+	$(REMOVE) $(TARGET).map
+	$(REMOVE) $(TARGET).sym
+	$(REMOVE) $(TARGET).lss
+	$(REMOVE) $(SRC:%.c=$(OBJDIR)/%.o)
+	$(REMOVE) $(SRC:%.c=$(OBJDIR)/%.lst)
+	$(REMOVE) $(SRC:.c=.s)
+	$(REMOVE) $(SRC:.c=.d)
+	$(REMOVE) $(SRC:.c=.i)
+	$(REMOVEDIR) .dep
+
+
+# Create object files directory
+$(shell mkdir $(OBJDIR) 2>/dev/null)
+
+
+# Include the dependency files.
+-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)
+
+
+# Listing of phony targets.
+.PHONY : all begin finish end sizebefore sizeafter gccversion \
+build elf hex eep lss sym coff extcoff \
+clean clean_list program debug gdb-config
+/**
+ * \file Seven segment driver
+ *
+ */
+
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+#include <avr/eeprom.h>
+#include <stdint.h>
+#include <string.h>
+#include <util/delay.h>
+#include "usb_serial.h"
+#include "bits.h"
+
+
+#define LED			0xD6
+
+
+void send_str(const char *s);
+uint8_t recv_str(char *buf, uint8_t size);
+void parse_and_execute_command(const char *buf, uint8_t num);
+
+static uint8_t
+hexdigit(
+	uint8_t x
+)
+{
+	x &= 0xF;
+	if (x < 0xA)
+		return x + '0' - 0x0;
+	else
+		return x + 'A' - 0xA;
+}
+
+
+
+// Send a string to the USB serial port.  The string must be in
+// flash memory, using PSTR
+//
+void send_str(const char *s)
+{
+	char c;
+	while (1) {
+		c = pgm_read_byte(s++);
+		if (!c) break;
+		usb_serial_putchar(c);
+	}
+}
+
+
+
+/*  4
+ * 5 3
+ *  7
+ * 0 2
+ *  1 
+ *
+ * 6 is not used since D6 drives the onboard LED.
+ */
+static const uint8_t digits[] = {
+	1 << 3 | 1 << 1 | 1 << 0 | 1 << 5 | 1 << 4 | 1 << 2, // 0
+	1 << 3 | 1 << 2, // 1
+	1 << 4 | 1 << 1 | 1 << 7 | 1 << 0 | 1 << 3, // 2
+	1 << 4 | 1 << 2 | 1 << 7 | 1 << 3 | 1 << 1, // 3
+	1 << 5 | 1 << 7 | 1 << 2 | 1 << 3, // 4
+	1 << 4 | 1 << 5 | 1 << 7 | 1 << 2 | 1 << 1, // 5
+	1 << 4 | 1 << 0 | 1 << 7 | 1 << 2 | 1 << 5 | 1 << 1, // 6
+	1 << 4 | 1 << 2 | 1 << 3, // 7
+	1 << 3 | 1 << 1 | 1 << 0 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 7, // 8
+	1 << 5 | 1 << 7 | 1 << 2 | 1 << 3 | 1 << 4, // 9
+};
+
+static const uint8_t chases[] = {
+	1 << 4, // chase
+	1 << 5, // chase
+	1 << 0, // chase
+	1 << 1, // chase
+	1 << 2, // chase
+	1 << 3, // chase
+};
+
+
+
+
+/** "Frame buffer" of output LEDs.
+ * Since we can't drive all LEDs simultaneously, instead draw them from
+ * this frame buffer.
+ */
+static uint8_t outputs[4];
+
+/** Brightness goes from 0 to 15 */
+static uint8_t brightness = 15;
+
+
+/** PWM the output */
+static void
+draw(void)
+{
+	uint8_t mask = 1;
+	if (brightness >= 16)
+		brightness = 15;
+
+	for (uint8_t i = 0 ; i < 7 ; i++, mask <<= 1)
+	{
+		// skip output 6
+		if (i == 6)
+			mask <<= 1;
+
+		DDRC = outputs[0] & mask;
+		DDRD = outputs[1] & mask;
+		DDRB = outputs[2] & mask;
+		DDRF = outputs[3] & mask;
+		_delay_us(brightness);
+
+		DDRB = 0;
+		DDRC = 0;
+		DDRD = 0;
+		DDRF = 0;
+
+		if (brightness < 15)
+			_delay_us(15 - brightness);
+	}
+
+}
+
+
+static void
+report_count(void)
+{
+	for (uint16_t min = 0 ; min < 512 ; min++)
+	{
+		const uint8_t count = eeprom_read_byte((void*) min);
+		if (count == 0)
+			continue;
+		char buf[8];
+		uint8_t off = 0;
+		buf[off++] = hexdigit(min >> 8);
+		buf[off++] = hexdigit(min >> 4);
+		buf[off++] = hexdigit(min >> 0);
+		buf[off++] = '=';
+		buf[off++] = hexdigit(count >> 4);
+		buf[off++] = hexdigit(count >> 0);
+		buf[off++] = '\r';
+		buf[off++] = '\n';
+		usb_serial_write(buf, off);
+	}
+}
+
+
+static void
+zero_count(void)
+{
+	for (uint16_t min = 0 ; min < 512 ; min++)
+	{
+		eeprom_write_byte((void*) min, 0);
+	}
+}
+
+
+int
+main(void)
+{
+	// set for 16 MHz clock
+#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
+	CPU_PRESCALE(0);
+
+	// Disable the ADC
+	ADMUX = 0;
+
+	// initialize the USB, and then wait for the host
+	// to set configuration.  If the Teensy is powered
+	// without a PC connected to the USB port, this 
+	// will wait forever.
+	usb_init();
+
+	// LED is an output; will be pulled down once connected
+	ddr(LED, 1);
+	out(LED, 1);
+
+	// Timer1 is used for psudeo random numbers
+	TCCR1B = 0
+		| 0 << CS12
+		| 0 << CS12
+		| 1 << CS10
+		;
+
+        // Timer 0 is used for a 64 Hz control loop timer.
+        // Clk/1024 == 15.625 KHz, count up to 244
+        // CTC mode resets the counter when it hits the top
+        TCCR0A = 0
+                | 1 << WGM01 // select CTC
+                | 0 << WGM00
+                ;
+
+        TCCR0B = 0
+                | 0 << WGM02
+                | 1 << CS02 // select Clk/1024
+                | 0 << CS01
+                | 0 << CS00
+                ;
+
+        OCR0A = 244;
+        sbi(TIFR0, OCF0A); // reset the overflow bit
+
+
+	while (!usb_configured())
+		;
+
+	_delay_ms(1000);
+
+	// wait for the user to run their terminal emulator program
+	// which sets DTR to indicate it is ready to receive.
+	while (!(usb_serial_get_control() & USB_SERIAL_DTR))
+		;
+
+	// discard anything that was received prior.  Sometimes the
+	// operating system or other software will send a modem
+	// "AT command", which can still be buffered.
+	usb_serial_flush_input();
+
+	send_str(PSTR("seven segment\r\n"));
+	DDRC = 1 << 7;
+	PORTC = 0;
+
+	outputs[0] = digits[0];
+	outputs[1] = digits[1];
+	outputs[2] = digits[2];
+	outputs[3] = digits[3];
+
+	for (uint16_t i = 0 ; i < 64 ; )
+	{
+		draw();
+		if (bit_is_clear(TIFR0, OCF0A))
+			continue;
+		sbi(TIFR0, OCF0A);
+		i++;
+	}
+
+	uint8_t chase = 0;
+	uint16_t ticks = 0;
+	uint16_t ms = 0;
+	uint16_t sec = TCNT1;
+
+	uint16_t total_count = 0;
+	uint16_t minute_count = 0;
+	uint16_t minute_current = 0;
+
+	for ( ; minute_current < 512 ; minute_current++)
+	{
+		const uint8_t count = eeprom_read_byte((void*) minute_current);
+		if (count == 0)
+			break;
+	}
+	
+	while (1)
+	{
+		int c = usb_serial_getchar();
+		if (c != -1)
+		{
+			if (c == '+')
+			{
+				if (brightness != 16)
+					brightness++;
+			} else
+			if (c == '-')
+			{
+				if (brightness != 0)
+					brightness --;
+			}
+			else
+			if (c == ' ')
+			{
+				total_count++;
+				minute_count++;
+			}
+			else
+			if (c == '?')
+			{
+				report_count();
+			} else
+			if (c == '!')
+			{
+				zero_count();
+				minute_count = total_count = 0;
+			}
+		}
+
+		if (bit_is_clear(TIFR0, OCF0A))
+		{
+			draw();
+			continue;
+		}
+
+		sbi(TIFR0, OCF0A); // reset the bit
+		ticks++;
+		if (++ms == 200)
+		{
+			ms = 0;
+			if (++sec == 60)
+			{
+				// Write minute_count
+				eeprom_write_byte(minute_current++, minute_count);
+				sec = 0;
+				minute_count = 0;
+				chase = 1;
+			}
+		}
+
+		if (chase)
+		{
+			if (ticks % 16 != 0)
+				continue;
+			outputs[0] = chases[(chase+0) % 6];
+			outputs[1] = chases[(chase+1) % 6];
+			outputs[2] = chases[(chase+2) % 6];
+			outputs[3] = chases[(chase+3) % 6];
+			if (chase++ == 6*2)
+				chase = 0;
+		
+			continue;
+		}
+
+		uint16_t o = total_count;
+		outputs[3] = digits[o % 10]; o /= 10;
+		outputs[2] = digits[o % 10]; o /= 10;
+		outputs[1] = digits[o % 10]; o /= 10;
+		outputs[0] = digits[o % 10]; o /= 10;
+	}
+}
+/** \file Access to AVR pins via constants.
+ *
+ * ddr(0xA3, 1) == enable DDRA |= (1 << 3)
+ * out(0xA3, 1) == PORTA |= (1 << 3)
+ * in(0xA3) == PINA & (1 << 3)
+ */
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include "bits.h"
+
+void
+__bits_ddr(
+	const uint8_t id,
+	const uint8_t value
+)
+{
+	__inline_ddr(id, value);
+}
+
+
+void
+__bits_out(
+	const uint8_t id,
+	const uint8_t value
+)
+{
+	__inline_out(id, value);
+}
+
+
+uint8_t
+__bits_in(
+	const uint8_t id
+)
+{
+	return __inline_in(id);
+}
+/** \file
+ * Easy access to AVR ports.
+ *
+ * For compile time constants, the operations will be single
+ * instruction.  For runtime constants a function call will
+ * be required.
+ */
+#ifndef _prom_bits_h_
+#define _prom_bits_h_
+
+#include <stdint.h>
+
+#define sbi(PORT, PIN) ((PORT) |=  (1 << (PIN)))
+#define cbi(PORT, PIN) ((PORT) &= ~(1 << (PIN)))
+#define array_count(ARRAY) (sizeof(ARRAY) / sizeof(*(ARRAY)))
+#define _STR(X) #X
+#define STR(X) _STR(X)
+
+
+
+/*
+#if defined(__AVR_AT90USB162__) \
+	case 0xA: func(cat(prefix, A), pin); break; \
+#endif
+*/
+
+#define cat(x,y) x ## y
+
+#define BITS_FOR_ALL(prefix, func, port, pin) do { \
+	switch (port) \
+	{ \
+	case 0xB: func(cat(prefix, B), pin); break; \
+	case 0xC: func(cat(prefix, C), pin); break; \
+	case 0xD: func(cat(prefix, D), pin); break; \
+	case 0xE: func(cat(prefix, E), pin); break; \
+	case 0xF: func(cat(prefix, F), pin); break; \
+	} \
+} while (0)
+
+
+/** Function call version for non-compile time constants */
+extern void
+__bits_out(
+	const uint8_t port,
+	uint8_t value
+);
+
+
+static inline void
+__inline_out(
+	const uint8_t id,
+	const uint8_t value
+)
+{
+	const uint8_t port = (id >> 4) & 0xF;
+	const uint8_t pin = (id >> 0) & 0xF;
+
+	if (value)
+		BITS_FOR_ALL(PORT,sbi, port, pin);
+	else
+		BITS_FOR_ALL(PORT,cbi, port, pin);
+}
+
+
+static inline void
+out(
+	const uint8_t port,
+	uint8_t value
+)
+{
+	if (__builtin_constant_p(port)
+	&&  __builtin_constant_p(value))
+		__inline_out(port, value);
+	else
+		__bits_out(port, value);
+}
+
+
+extern void
+__bits_ddr(
+	const uint8_t port,
+	const uint8_t value
+);
+
+
+static inline void
+__inline_ddr(
+	const uint8_t id,
+	const uint8_t value
+)
+{
+	const uint8_t port = (id >> 4) & 0xF;
+	const uint8_t pin = (id >> 0) & 0xF;
+
+	if (value)
+		BITS_FOR_ALL(DDR,sbi, port, pin);
+	else
+		BITS_FOR_ALL(DDR,cbi, port, pin);
+}
+
+
+static inline void
+ddr(
+	const uint8_t port,
+	uint8_t value
+)
+{
+	if (__builtin_constant_p(port)
+	&&  __builtin_constant_p(value))
+		__inline_ddr(port, value);
+	else
+		__bits_ddr(port, value);
+}
+
+
+extern uint8_t
+__bits_in(
+	const uint8_t port
+);
+
+
+static inline uint8_t
+__inline_in(
+	const uint8_t id
+)
+{
+	const uint8_t port = (id >> 4) & 0xF;
+	const uint8_t pin = (id >> 0) & 0xF;
+
+	BITS_FOR_ALL(PIN,return bit_is_set, port, pin);
+	return -1; // unreached?
+}
+
+
+static inline uint8_t
+in(
+	const uint8_t port
+)
+{
+	if (__builtin_constant_p(port))
+		return __inline_in(port);
+	else
+		return __bits_in(port);
+}
+
+
+#endif
+/*
+ * Prototypes, structure definitions and macros.
+ *
+ * Copyright (c) 2000-2003 Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * This library is covered by the LGPL, read LICENSE for details.
+ *
+ * This file (and only this file) may alternatively be licensed under the
+ * BSD license as well, read LICENSE for details.
+ */
+#ifndef __USB_H__
+#define __USB_H__
+
+#include <avr/io.h>
+#include <stdint.h>
+
+/*
+ * USB spec information
+ *
+ * This is all stuff grabbed from various USB specs and is pretty much
+ * not subject to change
+ */
+
+/*
+ * Device and/or Interface Class codes
+ */
+#define USB_CLASS_PER_INTERFACE		0	/* for DeviceClass */
+#define USB_CLASS_AUDIO			1
+#define USB_CLASS_COMM			2
+#define USB_CLASS_HID			3
+#define USB_CLASS_PRINTER		7
+#define USB_CLASS_PTP			6
+#define USB_CLASS_MASS_STORAGE		8
+#define USB_CLASS_HUB			9
+#define USB_CLASS_DATA			10
+#define USB_CLASS_VENDOR_SPEC		0xff
+
+/*
+ * Descriptor types
+ */
+#define USB_DT_DEVICE			0x01
+#define USB_DT_CONFIG			0x02
+#define USB_DT_STRING			0x03
+#define USB_DT_INTERFACE		0x04
+#define USB_DT_ENDPOINT			0x05
+
+#define USB_DT_HID			0x21
+#define USB_DT_REPORT			0x22
+#define USB_DT_PHYSICAL			0x23
+#define USB_DT_HUB			0x29
+
+/*
+ * Descriptor sizes per descriptor type
+ */
+#define USB_DT_DEVICE_SIZE		18
+#define USB_DT_CONFIG_SIZE		9
+#define USB_DT_INTERFACE_SIZE		9
+#define USB_DT_ENDPOINT_SIZE		7
+#define USB_DT_ENDPOINT_AUDIO_SIZE	9	/* Audio extension */
+#define USB_DT_HUB_NONVAR_SIZE		7
+
+/* All standard descriptors have these 2 fields in common */
+struct usb_descriptor_header {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+};
+
+/* String descriptor */
+struct usb_string_descriptor {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint16_t wData[1];
+};
+
+/* HID descriptor */
+struct usb_hid_descriptor {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint16_t bcdHID;
+	uint8_t  bCountryCode;
+	uint8_t  bNumDescriptors;
+	/* uint8_t  bReportDescriptorType; */
+	/* uint16_t wDescriptorLength; */
+	/* ... */
+};
+
+/* Endpoint descriptor */
+#define USB_MAXENDPOINTS	32
+struct usb_endpoint_descriptor {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint8_t  bEndpointAddress;
+	uint8_t  bmAttributes;
+	uint16_t wMaxPacketSize;
+	uint8_t  bInterval;
+	uint8_t  bRefresh;
+	uint8_t  bSynchAddress;
+
+	unsigned char *extra;	/* Extra descriptors */
+	int extralen;
+};
+
+#define USB_ENDPOINT_ADDRESS_MASK	0x0f    /* in bEndpointAddress */
+#define USB_ENDPOINT_DIR_MASK		0x80
+
+#define USB_ENDPOINT_TYPE_MASK		0x03    /* in bmAttributes */
+#define USB_ENDPOINT_TYPE_CONTROL	0
+#define USB_ENDPOINT_TYPE_ISOCHRONOUS	1
+#define USB_ENDPOINT_TYPE_BULK		2
+#define USB_ENDPOINT_TYPE_INTERRUPT	3
+
+/* Interface descriptor */
+#define USB_MAXINTERFACES	32
+struct usb_interface_descriptor {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint8_t  bInterfaceNumber;
+	uint8_t  bAlternateSetting;
+	uint8_t  bNumEndpoints;
+	uint8_t  bInterfaceClass;
+	uint8_t  bInterfaceSubClass;
+	uint8_t  bInterfaceProtocol;
+	uint8_t  iInterface;
+
+	struct usb_endpoint_descriptor endpoint;
+
+	unsigned char *extra;	/* Extra descriptors */
+	int extralen;
+};
+
+#define USB_MAXALTSETTING	128	/* Hard limit */
+struct usb_interface {
+	struct usb_interface_descriptor *altsetting;
+
+	int num_altsetting;
+};
+
+/* Configuration descriptor information.. */
+#define USB_MAXCONFIG		8
+struct usb_config_descriptor {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint16_t wTotalLength;
+	uint8_t  bNumInterfaces;
+	uint8_t  bConfigurationValue;
+	uint8_t  iConfiguration;
+	uint8_t  bmAttributes;
+	uint8_t  MaxPower;
+
+	struct usb_interface interfaces[];
+
+#if 0
+	unsigned char *extra;	/* Extra descriptors */
+	int extralen;
+#endif
+};
+
+/* Device descriptor */
+struct usb_device_descriptor {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint16_t bcdUSB;
+	uint8_t  bDeviceClass;
+	uint8_t  bDeviceSubClass;
+	uint8_t  bDeviceProtocol;
+	uint8_t  bMaxPacketSize0;
+	uint16_t idVendor;
+	uint16_t idProduct;
+	uint16_t bcdDevice;
+	uint8_t  iManufacturer;
+	uint8_t  iProduct;
+	uint8_t  iSerialNumber;
+	uint8_t  bNumConfigurations;
+};
+
+struct usb_ctrl_setup {
+	uint8_t  bRequestType;
+	uint8_t  bRequest;
+	uint16_t wValue;
+	uint16_t wIndex;
+	uint16_t wLength;
+};
+
+/*
+ * Standard requests
+ */
+#define USB_REQ_GET_STATUS		0x00
+#define USB_REQ_CLEAR_FEATURE		0x01
+/* 0x02 is reserved */
+#define USB_REQ_SET_FEATURE		0x03
+/* 0x04 is reserved */
+#define USB_REQ_SET_ADDRESS		0x05
+#define USB_REQ_GET_DESCRIPTOR		0x06
+#define USB_REQ_SET_DESCRIPTOR		0x07
+#define USB_REQ_GET_CONFIGURATION	0x08
+#define USB_REQ_SET_CONFIGURATION	0x09
+#define USB_REQ_GET_INTERFACE		0x0A
+#define USB_REQ_SET_INTERFACE		0x0B
+#define USB_REQ_SYNCH_FRAME		0x0C
+
+#define USB_TYPE_STANDARD		(0x00 << 5)
+#define USB_TYPE_CLASS			(0x01 << 5)
+#define USB_TYPE_VENDOR			(0x02 << 5)
+#define USB_TYPE_RESERVED		(0x03 << 5)
+
+#define USB_RECIP_DEVICE		0x00
+#define USB_RECIP_INTERFACE		0x01
+#define USB_RECIP_ENDPOINT		0x02
+#define USB_RECIP_OTHER			0x03
+
+/*
+ * Various libusb API related stuff
+ */
+
+#define USB_ENDPOINT_IN			0x80
+#define USB_ENDPOINT_OUT		0x00
+
+/* Error codes */
+#define USB_ERROR_BEGIN			500000
+
+/*
+ * This is supposed to look weird. This file is generated from autoconf
+ * and I didn't want to make this too complicated.
+ */
+#if 0
+#define USB_LE16_TO_CPU(x) do { x = ((x & 0xff) << 8) | ((x & 0xff00) >> 8); } while(0)
+#else
+#define USB_LE16_TO_CPU(x)
+#endif
+
+/* Data types */
+struct usb_device;
+struct usb_bus;
+
+
+struct usb_dev_handle;
+typedef struct usb_dev_handle usb_dev_handle;
+
+/* Variables */
+extern struct usb_bus *usb_busses;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Function prototypes */
+
+/* usb.c */
+usb_dev_handle *usb_open(struct usb_device *dev);
+int usb_close(usb_dev_handle *dev);
+int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf,
+	size_t buflen);
+int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf,
+	size_t buflen);
+
+/* descriptors.c */
+int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep,
+	unsigned char type, unsigned char index, void *buf, int size);
+int usb_get_descriptor(usb_dev_handle *udev, unsigned char type,
+	unsigned char index, void *buf, int size);
+
+/* <arch>.c */
+int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size,
+	int timeout);
+int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size,
+	int timeout);
+int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size,
+        int timeout);
+int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size,
+        int timeout);
+int usb_control_msg(usb_dev_handle *dev, int requesttype, int request,
+	int value, int index, char *bytes, int size, int timeout);
+int usb_set_configuration(usb_dev_handle *dev, int configuration);
+int usb_claim_interface(usb_dev_handle *dev, int interface);
+int usb_release_interface(usb_dev_handle *dev, int interface);
+int usb_set_altinterface(usb_dev_handle *dev, int alternate);
+int usb_resetep(usb_dev_handle *dev, unsigned int ep);
+int usb_clear_halt(usb_dev_handle *dev, unsigned int ep);
+int usb_reset(usb_dev_handle *dev);
+
+#if 0
+#define LIBUSB_HAS_GET_DRIVER_NP 1
+int usb_get_driver_np(usb_dev_handle *dev, int interface, char *name,
+	unsigned int namelen);
+#define LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP 1
+int usb_detach_kernel_driver_np(usb_dev_handle *dev, int interface);
+#endif
+
+char *usb_strerror(void);
+
+void usb_init(void);
+void usb_set_debug(int level);
+int usb_find_busses(void);
+int usb_find_devices(void);
+struct usb_device *usb_device(usb_dev_handle *dev);
+struct usb_bus *usb_get_busses(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __USB_H__ */
+
+/* USB Serial Example for Teensy USB Development Board
+ * http://www.pjrc.com/teensy/usb_serial.html
+ * Copyright (c) 2008,2010,2011 PJRC.COM, LLC
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Version 1.0: Initial Release
+// Version 1.1: support Teensy++
+// Version 1.2: fixed usb_serial_available
+// Version 1.3: added transmit bandwidth test
+// Version 1.4: added usb_serial_write
+// Version 1.5: add support for Teensy 2.0
+// Version 1.6: fix zero length packet bug
+// Version 1.7: fix usb_serial_set_control
+
+#define USB_SERIAL_PRIVATE_INCLUDE
+#include "usb_serial.h"
+
+#include <usb.h>
+
+
+/**************************************************************************
+ *
+ *  Configurable Options
+ *
+ **************************************************************************/
+
+// You can change these to give your code its own name.  On Windows,
+// these are only used before an INF file (driver install) is loaded.
+#define STR_MANUFACTURER	L"Your Name"
+#define STR_PRODUCT		L"USB Serial"
+
+// All USB serial devices are supposed to have a serial number
+// (according to Microsoft).  On windows, a new COM port is created
+// for every unique serial/vendor/product number combination.  If
+// you program 2 identical boards with 2 different serial numbers
+// and they are assigned COM7 and COM8, each will always get the
+// same COM port number because Windows remembers serial numbers.
+//
+// On Mac OS-X, a device file is created automatically which
+// incorperates the serial number, eg, /dev/cu-usbmodem12341
+//
+// Linux by default ignores the serial number, and creates device
+// files named /dev/ttyACM0, /dev/ttyACM1... in the order connected.
+// Udev rules (in /etc/udev/rules.d) can define persistent device
+// names linked to this serial number, as well as permissions, owner
+// and group settings.
+#define STR_SERIAL_NUMBER	L"12345"
+
+// Mac OS-X and Linux automatically load the correct drivers.  On
+// Windows, even though the driver is supplied by Microsoft, an
+// INF file is needed to load the driver.  These numbers need to
+// match the INF file.
+#define VENDOR_ID		0x16C0
+#define PRODUCT_ID		0x047A
+
+// When you write data, it goes into a USB endpoint buffer, which
+// is transmitted to the PC when it becomes full, or after a timeout
+// with no more writes.  Even if you write in exactly packet-size
+// increments, this timeout is used to send a "zero length packet"
+// that tells the PC no more data is expected and it should pass
+// any buffered data to the application that may be waiting.  If
+// you want data sent immediately, call usb_serial_flush_output().
+#define TRANSMIT_FLUSH_TIMEOUT	5   /* in milliseconds */
+
+// If the PC is connected but not "listening", this is the length
+// of time before usb_serial_getchar() returns with an error.  This
+// is roughly equivilant to a real UART simply transmitting the
+// bits on a wire where nobody is listening, except you get an error
+// code which you can ignore for serial-like discard of data, or
+// use to know your data wasn't sent.
+#define TRANSMIT_TIMEOUT	25   /* in milliseconds */
+
+// USB devices are supposed to implment a halt feature, which is
+// rarely (if ever) used.  If you comment this line out, the halt
+// code will be removed, saving 116 bytes of space (gcc 4.3.0).
+// This is not strictly USB compliant, but works with all major
+// operating systems.
+#define SUPPORT_ENDPOINT_HALT
+
+
+
+/**************************************************************************
+ *
+ *  Endpoint Buffer Configuration
+ *
+ **************************************************************************/
+
+// These buffer sizes are best for most applications, but perhaps if you
+// want more buffering on some endpoint at the expense of others, this
+// is where you can make such changes.  The AT90USB162 has only 176 bytes
+// of DPRAM (USB buffers) and only endpoints 3 & 4 can double buffer.
+
+#define ENDPOINT0_SIZE		16
+#define CDC_ACM_ENDPOINT	2
+#define CDC_RX_ENDPOINT		3
+#define CDC_TX_ENDPOINT		4
+#if defined(__AVR_AT90USB162__)
+#define CDC_ACM_SIZE		16
+#define CDC_ACM_BUFFER		EP_SINGLE_BUFFER
+#define CDC_RX_SIZE		32
+#define CDC_RX_BUFFER 		EP_DOUBLE_BUFFER
+#define CDC_TX_SIZE		32
+#define CDC_TX_BUFFER		EP_DOUBLE_BUFFER
+#else
+#define CDC_ACM_SIZE		16
+#define CDC_ACM_BUFFER		EP_SINGLE_BUFFER
+#define CDC_RX_SIZE		64
+#define CDC_RX_BUFFER 		EP_DOUBLE_BUFFER
+#define CDC_TX_SIZE		64
+#define CDC_TX_BUFFER		EP_DOUBLE_BUFFER
+#endif
+
+static const uint8_t PROGMEM endpoint_config_table[] = {
+	0,
+	1, EP_TYPE_INTERRUPT_IN,  EP_SIZE(CDC_ACM_SIZE) | CDC_ACM_BUFFER,
+	1, EP_TYPE_BULK_OUT,      EP_SIZE(CDC_RX_SIZE) | CDC_RX_BUFFER,
+	1, EP_TYPE_BULK_IN,       EP_SIZE(CDC_TX_SIZE) | CDC_TX_BUFFER
+};
+
+
+/**************************************************************************
+ *
+ *  Descriptor Data
+ *
+ **************************************************************************/
+
+// Descriptors are the data that your computer reads when it auto-detects
+// this USB device (called "enumeration" in USB lingo).  The most commonly
+// changed items are editable at the top of this file.  Changing things
+// in here should only be done by those who've read chapter 9 of the USB
+// spec and relevant portions of any USB class specifications!
+
+static struct usb_device_descriptor PROGMEM device_descriptor = {
+	.bLength		= sizeof(device_descriptor),
+	.bDescriptorType	= 1,
+	.bcdUSB			= 0x0200,
+	.bDeviceClass		= USB_CLASS_COMM,
+	.bDeviceSubClass	= 0,
+	.bDeviceProtocol	= 0,
+	.bMaxPacketSize0	= ENDPOINT0_SIZE,
+	.idVendor		= VENDOR_ID,
+	.idProduct		= PRODUCT_ID,
+	.bcdDevice		= 0x0100,
+	.iManufacturer		= 1,
+	.iProduct		= 2,
+	.iSerialNumber		= 3,
+	.bNumConfigurations	= 1,
+};
+
+
+#define CONFIG1_DESC_SIZE (9+9+5+5+4+5+7+9+7+7)
+#if 1
+static uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = {
+	// configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10
+	9, 					// bLength;
+	2,					// bDescriptorType;
+	LSB(CONFIG1_DESC_SIZE),			// wTotalLength
+	MSB(CONFIG1_DESC_SIZE),
+	2,					// bNumInterfaces
+	1,					// bConfigurationValue
+	0,					// iConfiguration
+	0xC0,					// bmAttributes
+	50,					// bMaxPower
+
+	// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
+	9,					// bLength
+	4,					// bDescriptorType
+	0,					// bInterfaceNumber
+	0,					// bAlternateSetting
+	1,					// bNumEndpoints
+	0x02,					// bInterfaceClass
+	0x02,					// bInterfaceSubClass
+	0x01,					// bInterfaceProtocol
+	0,					// iInterface
+	// CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26
+	5,					// bFunctionLength
+	0x24,					// bDescriptorType
+	0x00,					// bDescriptorSubtype
+	0x10, 0x01,				// bcdCDC
+	// Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27
+	5,					// bFunctionLength
+	0x24,					// bDescriptorType
+	0x01,					// bDescriptorSubtype
+	0x01,					// bmCapabilities
+	1,					// bDataInterface
+	// Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28
+	4,					// bFunctionLength
+	0x24,					// bDescriptorType
+	0x02,					// bDescriptorSubtype
+	0x06,					// bmCapabilities
+	// Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33
+	5,					// bFunctionLength
+	0x24,					// bDescriptorType
+	0x06,					// bDescriptorSubtype
+	0,					// bMasterInterface
+	1,					// bSlaveInterface0
+	// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
+	7,					// bLength
+	5,					// bDescriptorType
+	CDC_ACM_ENDPOINT | 0x80,		// bEndpointAddress
+	0x03,					// bmAttributes (0x03=intr)
+	CDC_ACM_SIZE, 0,			// wMaxPacketSize
+	64,					// bInterval
+	// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
+	9,					// bLength
+	4,					// bDescriptorType
+	1,					// bInterfaceNumber
+	0,					// bAlternateSetting
+	2,					// bNumEndpoints
+	0x0A,					// bInterfaceClass
+	0x00,					// bInterfaceSubClass
+	0x00,					// bInterfaceProtocol
+	0,					// iInterface
+	// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
+	7,					// bLength
+	5,					// bDescriptorType
+	CDC_RX_ENDPOINT,			// bEndpointAddress
+	0x02,					// bmAttributes (0x02=bulk)
+	CDC_RX_SIZE, 0,				// wMaxPacketSize
+	0,					// bInterval
+	// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
+	7,					// bLength
+	5,					// bDescriptorType
+	CDC_TX_ENDPOINT | 0x80,			// bEndpointAddress
+	0x02,					// bmAttributes (0x02=bulk)
+	CDC_TX_SIZE, 0,				// wMaxPacketSize
+	0					// bInterval
+};
+#else
+static struct usb_config_descriptor PROGMEM config1_descriptor = {
+	.bLength		= USB_DT_CONFIG_SIZE,
+	.bDescriptorType	= USB_DT_CONFIG,
+	.wTotalLength		= CONFIG1_DESC_SIZE, // \todo compute
+	.bNumInterfaces		= 2,
+	.bConfigurationValue	= 1, // \todo ?
+	.iConfiguration		= 0, // \todo ?
+	.bmAttributes		= 0xC0, // \todo ?
+	.bMaxPower		= 50, // mA?
+	.interfaces		= {
+		{
+			.bLength		= USB_DT_INTERFACE_SIZE,
+			.bDescriptorType	= USB_DT_INTERFACE,
+			.bInterfaceNumber	= 0,
+			.bAlternateSetting	= 0,
+			.bNumEndPoints		= 0,
+			.bInterfaceClass	= USB_CLASS_COMM,
+			.bInterfaceSubClass	= USB_CLASS_COMM,
+			.bInterfaceProtocol	= 1, // \todo ?
+			.iInterface		= 0,
+			.endpoint		= {
+				.bLength		= USB_
+		},
+		{
+		},
+	},
+};
+#endif
+
+// If you're desperate for a little extra code memory, these strings
+// can be completely removed if iManufacturer, iProduct, iSerialNumber
+// in the device desciptor are changed to zeros.
+struct usb_string_descriptor_struct {
+	uint8_t bLength;
+	uint8_t bDescriptorType;
+	int16_t wString[];
+};
+static struct usb_string_descriptor_struct PROGMEM string0 = {
+	4,
+	3,
+	{0x0409}
+};
+static struct usb_string_descriptor_struct PROGMEM string1 = {
+	sizeof(STR_MANUFACTURER),
+	3,
+	STR_MANUFACTURER
+};
+static struct usb_string_descriptor_struct PROGMEM string2 = {
+	sizeof(STR_PRODUCT),
+	3,
+	STR_PRODUCT
+};
+static struct usb_string_descriptor_struct PROGMEM string3 = {
+	sizeof(STR_SERIAL_NUMBER),
+	3,
+	STR_SERIAL_NUMBER
+};
+
+// This table defines which descriptor data is sent for each specific
+// request from the host (in wValue and wIndex).
+static struct descriptor_list_struct {
+	uint16_t	wValue;
+	uint16_t	wIndex;
+	const uint8_t	*addr;
+	uint8_t		length;
+} PROGMEM descriptor_list[] = {
+	{0x0100, 0x0000, (const void *) &device_descriptor, sizeof(device_descriptor)},
+	{0x0200, 0x0000, config1_descriptor, sizeof(config1_descriptor)},
+	{0x0300, 0x0000, (const uint8_t *)&string0, 4},
+	{0x0301, 0x0409, (const uint8_t *)&string1, sizeof(STR_MANUFACTURER)},
+	{0x0302, 0x0409, (const uint8_t *)&string2, sizeof(STR_PRODUCT)},
+	{0x0303, 0x0409, (const uint8_t *)&string3, sizeof(STR_SERIAL_NUMBER)}
+};
+#define NUM_DESC_LIST (sizeof(descriptor_list)/sizeof(struct descriptor_list_struct))
+
+
+/**************************************************************************
+ *
+ *  Variables - these are the only non-stack RAM usage
+ *
+ **************************************************************************/
+
+// zero when we are not configured, non-zero when enumerated
+static volatile uint8_t usb_configuration=0;
+
+// the time remaining before we transmit any partially full
+// packet, or send a zero length packet.
+static volatile uint8_t transmit_flush_timer=0;
+static uint8_t transmit_previous_timeout=0;
+
+// serial port settings (baud rate, control signals, etc) set
+// by the PC.  These are ignored, but kept in RAM.
+static uint8_t cdc_line_coding[7]={0x00, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x08};
+static uint8_t cdc_line_rtsdtr=0;
+
+
+/**************************************************************************
+ *
+ *  Public Functions - these are the API intended for the user
+ *
+ **************************************************************************/
+
+// initialize USB serial
+void usb_init(void)
+{
+	HW_CONFIG();
+        USB_FREEZE();				// enable USB
+        PLL_CONFIG();				// config PLL, 16 MHz xtal
+        while (!(PLLCSR & (1<<PLOCK))) ;	// wait for PLL lock
+        USB_CONFIG();				// start USB clock
+        UDCON = 0;				// enable attach resistor
+	usb_configuration = 0;
+	cdc_line_rtsdtr = 0;
+        UDIEN = (1<<EORSTE)|(1<<SOFE);
+	sei();
+}
+
+// return 0 if the USB is not configured, or the configuration
+// number selected by the HOST
+uint8_t usb_configured(void)
+{
+	return usb_configuration;
+}
+
+// get the next character, or -1 if nothing received
+int16_t usb_serial_getchar(void)
+{
+	uint8_t c, intr_state;
+
+	// interrupts are disabled so these functions can be
+	// used from the main program or interrupt context,
+	// even both in the same program!
+	intr_state = SREG;
+	cli();
+	if (!usb_configuration) {
+		SREG = intr_state;
+		return -1;
+	}
+	UENUM = CDC_RX_ENDPOINT;
+	retry:
+	c = UEINTX;
+	if (!(c & (1<<RWAL))) {
+		// no data in buffer
+		if (c & (1<<RXOUTI)) {
+			UEINTX = 0x6B;
+			goto retry;
+		}	
+		SREG = intr_state;
+		return -1;
+	}
+	// take one byte out of the buffer
+	c = UEDATX;
+	// if buffer completely used, release it
+	if (!(UEINTX & (1<<RWAL))) UEINTX = 0x6B;
+	SREG = intr_state;
+	return c;
+}
+
+// number of bytes available in the receive buffer
+uint8_t usb_serial_available(void)
+{
+	uint8_t n=0, i, intr_state;
+
+	intr_state = SREG;
+	cli();
+	if (usb_configuration) {
+		UENUM = CDC_RX_ENDPOINT;
+		n = UEBCLX;
+		if (!n) {
+			i = UEINTX;
+			if (i & (1<<RXOUTI) && !(i & (1<<RWAL))) UEINTX = 0x6B;
+		}
+	}
+	SREG = intr_state;
+	return n;
+}
+
+// discard any buffered input
+void usb_serial_flush_input(void)
+{
+	uint8_t intr_state;
+
+	if (usb_configuration) {
+		intr_state = SREG;
+		cli();
+		UENUM = CDC_RX_ENDPOINT;
+		while ((UEINTX & (1<<RWAL))) {
+			UEINTX = 0x6B; 
+		}
+		SREG = intr_state;
+	}
+}
+
+
+#if 0
+/**
+ * Copy the receive buffer into a user space array, of at least 64 bytes.
+ *
+ * \return Number of bytes copied in this packet, 0 if none, -1 on error.
+ */
+int8_t
+usb_serial_recv(
+	uint8_t * buf
+)
+{
+	// interrupts are disabled so these functions can be
+	// used from the main program or interrupt context,
+	// even both in the same program!
+	const uint8_t intr_state = SREG;
+	cli();
+	if (!usb_configuration)
+	{
+		SREG = intr_state;
+		return -1;
+	}
+
+	int offset = 0;
+
+	UENUM = CDC_RX_ENDPOINT;
+	while (1)
+	{
+		if (bit_is_clear(UEINTX, RWAL))
+		{
+			// No data in the buffer .
+			if (bit_is_clear(UEINTX, RXOUTI))
+			{
+				UEINTX = 0x6B;
+				goto retry;
+			}	
+			SREG = intr_state;
+			return -1;
+		}
+
+		// take one byte out of the buffer
+		c = UEDATX;
+		// if buffer completely used, release it
+	if (!(UEINTX & (1<<RWAL))) UEINTX = 0x6B;
+	SREG = intr_state;
+	return c;
+}
+#endif
+
+// transmit a character.  0 returned on success, -1 on error
+int8_t usb_serial_putchar(uint8_t c)
+{
+	uint8_t timeout, intr_state;
+
+	// if we're not online (enumerated and configured), error
+	if (!usb_configuration) return -1;
+	// interrupts are disabled so these functions can be
+	// used from the main program or interrupt context,
+	// even both in the same program!
+	intr_state = SREG;
+	cli();
+	UENUM = CDC_TX_ENDPOINT;
+	// if we gave up due to timeout before, don't wait again
+	if (transmit_previous_timeout) {
+		if (!(UEINTX & (1<<RWAL))) {
+			SREG = intr_state;
+			return -1;
+		}
+		transmit_previous_timeout = 0;
+	}
+	// wait for the FIFO to be ready to accept data
+	timeout = UDFNUML + TRANSMIT_TIMEOUT;
+	while (1) {
+		// are we ready to transmit?
+		if (UEINTX & (1<<RWAL)) break;
+		SREG = intr_state;
+		// have we waited too long?  This happens if the user
+		// is not running an application that is listening
+		if (UDFNUML == timeout) {
+			transmit_previous_timeout = 1;
+			return -1;
+		}
+		// has the USB gone offline?
+		if (!usb_configuration) return -1;
+		// get ready to try checking again
+		intr_state = SREG;
+		cli();
+		UENUM = CDC_TX_ENDPOINT;
+	}
+	// actually write the byte into the FIFO
+	UEDATX = c;
+	// if this completed a packet, transmit it now!
+	if (!(UEINTX & (1<<RWAL))) UEINTX = 0x3A;
+	transmit_flush_timer = TRANSMIT_FLUSH_TIMEOUT;
+	SREG = intr_state;
+	return 0;
+}
+
+
+// transmit a character, but do not wait if the buffer is full,
+//   0 returned on success, -1 on buffer full or error 
+int8_t usb_serial_putchar_nowait(uint8_t c)
+{
+	uint8_t intr_state;
+
+	if (!usb_configuration) return -1;
+	intr_state = SREG;
+	cli();
+	UENUM = CDC_TX_ENDPOINT;
+	if (!(UEINTX & (1<<RWAL))) {
+		// buffer is full
+		SREG = intr_state;
+		return -1;
+	}
+	// actually write the byte into the FIFO
+	UEDATX = c;
+		// if this completed a packet, transmit it now!
+	if (!(UEINTX & (1<<RWAL))) UEINTX = 0x3A;
+	transmit_flush_timer = TRANSMIT_FLUSH_TIMEOUT;
+	SREG = intr_state;
+	return 0;
+}
+
+// transmit a buffer.
+//  0 returned on success, -1 on error
+// This function is optimized for speed!  Each call takes approx 6.1 us overhead
+// plus 0.25 us per byte.  12 Mbit/sec USB has 8.67 us per-packet overhead and
+// takes 0.67 us per byte.  If called with 64 byte packet-size blocks, this function
+// can transmit at full USB speed using 43% CPU time.  The maximum theoretical speed
+// is 19 packets per USB frame, or 1216 kbytes/sec.  However, bulk endpoints have the
+// lowest priority, so any other USB devices will likely reduce the speed.  Speed
+// can also be limited by how quickly the PC-based software reads data, as the host
+// controller in the PC will not allocate bandwitdh without a pending read request.
+// (thanks to Victor Suarez for testing and feedback and initial code)
+
+int8_t usb_serial_write(const uint8_t *buffer, uint16_t size)
+{
+	uint8_t timeout, intr_state, write_size;
+
+	// if we're not online (enumerated and configured), error
+	if (!usb_configuration) return -1;
+	// interrupts are disabled so these functions can be
+	// used from the main program or interrupt context,
+	// even both in the same program!
+	intr_state = SREG;
+	cli();
+	UENUM = CDC_TX_ENDPOINT;
+	// if we gave up due to timeout before, don't wait again
+	if (transmit_previous_timeout) {
+		if (!(UEINTX & (1<<RWAL))) {
+			SREG = intr_state;
+			return -1;
+		}
+		transmit_previous_timeout = 0;
+	}
+	// each iteration of this loop transmits a packet
+	while (size) {
+		// wait for the FIFO to be ready to accept data
+		timeout = UDFNUML + TRANSMIT_TIMEOUT;
+		while (1) {
+			// are we ready to transmit?
+			if (UEINTX & (1<<RWAL)) break;
+			SREG = intr_state;
+			// have we waited too long?  This happens if the user
+			// is not running an application that is listening
+			if (UDFNUML == timeout) {
+				transmit_previous_timeout = 1;
+				return -1;
+			}
+			// has the USB gone offline?
+			if (!usb_configuration) return -1;
+			// get ready to try checking again
+			intr_state = SREG;
+			cli();
+			UENUM = CDC_TX_ENDPOINT;
+		}
+
+		// compute how many bytes will fit into the next packet
+		write_size = CDC_TX_SIZE - UEBCLX;
+		if (write_size > size) write_size = size;
+		size -= write_size;
+
+		// write the packet
+		switch (write_size) {
+			#if (CDC_TX_SIZE == 64)
+			case 64: UEDATX = *buffer++;
+			case 63: UEDATX = *buffer++;
+			case 62: UEDATX = *buffer++;
+			case 61: UEDATX = *buffer++;
+			case 60: UEDATX = *buffer++;
+			case 59: UEDATX = *buffer++;
+			case 58: UEDATX = *buffer++;
+			case 57: UEDATX = *buffer++;
+			case 56: UEDATX = *buffer++;
+			case 55: UEDATX = *buffer++;
+			case 54: UEDATX = *buffer++;
+			case 53: UEDATX = *buffer++;
+			case 52: UEDATX = *buffer++;
+			case 51: UEDATX = *buffer++;
+			case 50: UEDATX = *buffer++;
+			case 49: UEDATX = *buffer++;
+			case 48: UEDATX = *buffer++;
+			case 47: UEDATX = *buffer++;
+			case 46: UEDATX = *buffer++;
+			case 45: UEDATX = *buffer++;
+			case 44: UEDATX = *buffer++;
+			case 43: UEDATX = *buffer++;
+			case 42: UEDATX = *buffer++;
+			case 41: UEDATX = *buffer++;
+			case 40: UEDATX = *buffer++;
+			case 39: UEDATX = *buffer++;
+			case 38: UEDATX = *buffer++;
+			case 37: UEDATX = *buffer++;
+			case 36: UEDATX = *buffer++;
+			case 35: UEDATX = *buffer++;
+			case 34: UEDATX = *buffer++;
+			case 33: UEDATX = *buffer++;
+			#endif
+			#if (CDC_TX_SIZE >= 32)
+			case 32: UEDATX = *buffer++;
+			case 31: UEDATX = *buffer++;
+			case 30: UEDATX = *buffer++;
+			case 29: UEDATX = *buffer++;
+			case 28: UEDATX = *buffer++;
+			case 27: UEDATX = *buffer++;
+			case 26: UEDATX = *buffer++;
+			case 25: UEDATX = *buffer++;
+			case 24: UEDATX = *buffer++;
+			case 23: UEDATX = *buffer++;
+			case 22: UEDATX = *buffer++;
+			case 21: UEDATX = *buffer++;
+			case 20: UEDATX = *buffer++;
+			case 19: UEDATX = *buffer++;
+			case 18: UEDATX = *buffer++;
+			case 17: UEDATX = *buffer++;
+			#endif
+			#if (CDC_TX_SIZE >= 16)
+			case 16: UEDATX = *buffer++;
+			case 15: UEDATX = *buffer++;
+			case 14: UEDATX = *buffer++;
+			case 13: UEDATX = *buffer++;
+			case 12: UEDATX = *buffer++;
+			case 11: UEDATX = *buffer++;
+			case 10: UEDATX = *buffer++;
+			case  9: UEDATX = *buffer++;
+			#endif
+			case  8: UEDATX = *buffer++;
+			case  7: UEDATX = *buffer++;
+			case  6: UEDATX = *buffer++;
+			case  5: UEDATX = *buffer++;
+			case  4: UEDATX = *buffer++;
+			case  3: UEDATX = *buffer++;
+			case  2: UEDATX = *buffer++;
+			default:
+			case  1: UEDATX = *buffer++;
+			case  0: break;
+		}
+		// if this completed a packet, transmit it now!
+		if (!(UEINTX & (1<<RWAL))) UEINTX = 0x3A;
+		transmit_flush_timer = TRANSMIT_FLUSH_TIMEOUT;
+		SREG = intr_state;
+	}
+	return 0;
+}
+
+
+// immediately transmit any buffered output.
+// This doesn't actually transmit the data - that is impossible!
+// USB devices only transmit when the host allows, so the best
+// we can do is release the FIFO buffer for when the host wants it
+void usb_serial_flush_output(void)
+{
+	uint8_t intr_state;
+
+	intr_state = SREG;
+	cli();
+	if (transmit_flush_timer) {
+		UENUM = CDC_TX_ENDPOINT;
+		UEINTX = 0x3A;
+		transmit_flush_timer = 0;
+	}
+	SREG = intr_state;
+}
+
+// functions to read the various async serial settings.  These
+// aren't actually used by USB at all (communication is always
+// at full USB speed), but they are set by the host so we can
+// set them properly if we're converting the USB to a real serial
+// communication
+uint32_t usb_serial_get_baud(void)
+{
+	return *(uint32_t *)cdc_line_coding;
+}
+uint8_t usb_serial_get_stopbits(void)
+{
+	return cdc_line_coding[4];
+}
+uint8_t usb_serial_get_paritytype(void)
+{
+	return cdc_line_coding[5];
+}
+uint8_t usb_serial_get_numbits(void)
+{
+	return cdc_line_coding[6];
+}
+uint8_t usb_serial_get_control(void)
+{
+	return cdc_line_rtsdtr;
+}
+// write the control signals, DCD, DSR, RI, etc
+// There is no CTS signal.  If software on the host has transmitted
+// data to you but you haven't been calling the getchar function,
+// it remains buffered (either here or on the host) and can not be
+// lost because you weren't listening at the right time, like it
+// would in real serial communication.
+int8_t usb_serial_set_control(uint8_t signals)
+{
+	uint8_t intr_state;
+
+	intr_state = SREG;
+	cli();
+	if (!usb_configuration) {
+		// we're not enumerated/configured
+		SREG = intr_state;
+		return -1;
+	}
+
+	UENUM = CDC_ACM_ENDPOINT;
+	if (!(UEINTX & (1<<RWAL))) {
+		// unable to write
+		// TODO; should this try to abort the previously
+		// buffered message??
+		SREG = intr_state;
+		return -1;
+	}
+	UEDATX = 0xA1;
+	UEDATX = 0x20;
+	UEDATX = 0;
+	UEDATX = 0;
+	UEDATX = 0; // 0 seems to work nicely.  what if this is 1??
+	UEDATX = 0;
+	UEDATX = 1;
+	UEDATX = 0;
+	UEDATX = signals;
+	UEINTX = 0x3A;
+	SREG = intr_state;
+	return 0;
+}
+
+
+
+/**************************************************************************
+ *
+ *  Private Functions - not intended for general user consumption....
+ *
+ **************************************************************************/
+
+
+// USB Device Interrupt - handle all device-level events
+// the transmit buffer flushing is triggered by the start of frame
+//
+ISR(USB_GEN_vect)
+{
+	uint8_t intbits, t;
+
+        intbits = UDINT;
+        UDINT = 0;
+        if (intbits & (1<<EORSTI)) {
+		UENUM = 0;
+		UECONX = 1;
+		UECFG0X = EP_TYPE_CONTROL;
+		UECFG1X = EP_SIZE(ENDPOINT0_SIZE) | EP_SINGLE_BUFFER;
+		UEIENX = (1<<RXSTPE);
+		usb_configuration = 0;
+		cdc_line_rtsdtr = 0;
+        }
+	if (intbits & (1<<SOFI)) {
+		if (usb_configuration) {
+			t = transmit_flush_timer;
+			if (t) {
+				transmit_flush_timer = --t;
+				if (!t) {
+					UENUM = CDC_TX_ENDPOINT;
+					UEINTX = 0x3A;
+				}
+			}
+		}
+	}
+}
+
+
+// Misc functions to wait for ready and send/receive packets
+static inline void usb_wait_in_ready(void)
+{
+	while (!(UEINTX & (1<<TXINI))) ;
+}
+static inline void usb_send_in(void)
+{
+	UEINTX = ~(1<<TXINI);
+}
+static inline void usb_wait_receive_out(void)
+{
+	while (!(UEINTX & (1<<RXOUTI))) ;
+}
+static inline void usb_ack_out(void)
+{
+	UEINTX = ~(1<<RXOUTI);
+}
+
+
+
+// USB Endpoint Interrupt - endpoint 0 is handled here.  The
+// other endpoints are manipulated by the user-callable
+// functions, and the start-of-frame interrupt.
+//
+ISR(USB_COM_vect)
+{
+        uint8_t intbits;
+	const uint8_t *list;
+        const uint8_t *cfg;
+	uint8_t i, n, len, en;
+	uint8_t *p;
+	uint8_t bmRequestType;