From 2d168f4a3ab685f9c6a89f85c4467f5dd85a9859 Mon Sep 17 00:00:00 2001 From: Mike Trudeau Date: Fri, 13 Feb 2026 20:33:17 +0000 Subject: [PATCH] migrate --- .gitignore | 8 + README.md | 16 + app/Makefile | 53 ++ app/build/.gitignore | 3 + app/build/Dockerfile.dapper | 10 + app/build/Makefile | 22 + app/build/scripts/cli | 7 + app/build/scripts/entry | 13 + app/cmd/cli/cli.go | 208 ++++++ app/go.mod | 5 + app/go.sum | 2 + app/pkg/device/device.go | 106 +++ app/pkg/device/keycodes/keycodes.go | 175 +++++ app/pkg/device/report3/report3.go | 139 ++++ app/pkg/device/types.go | 21 + classic/README.md | 59 ++ classic/bl/.gitignore | 1 + classic/bl/Makefile | 30 + classic/bl/beeon/Makefile.inc | 60 ++ classic/bl/beeon/bootloaderconfig.h | 259 ++++++++ classic/build/.gitignore | 3 + classic/build/Dockerfile.dapper | 9 + classic/build/Makefile | 22 + classic/build/scripts/beeon | 18 + classic/build/scripts/entry | 13 + classic/fw/Makefile | 41 ++ classic/fw/main.c | 458 +++++++++++++ classic/fw/usbconfig.h | 394 ++++++++++++ classic/fw/usbdrv/Changelog.txt | 329 ++++++++++ classic/fw/usbdrv/CommercialLicense.txt | 166 +++++ classic/fw/usbdrv/License.txt | 361 +++++++++++ classic/fw/usbdrv/Readme.txt | 172 +++++ classic/fw/usbdrv/USB-ID-FAQ.txt | 149 +++++ classic/fw/usbdrv/USB-IDs-for-free.txt | 154 +++++ classic/fw/usbdrv/asmcommon.inc | 187 ++++++ classic/fw/usbdrv/oddebug.c | 49 ++ classic/fw/usbdrv/oddebug.h | 122 ++++ classic/fw/usbdrv/usbconfig-prototype.h | 384 +++++++++++ classic/fw/usbdrv/usbdrv.c | 628 ++++++++++++++++++ classic/fw/usbdrv/usbdrv.h | 746 +++++++++++++++++++++ classic/fw/usbdrv/usbdrvasm.S | 392 +++++++++++ classic/fw/usbdrv/usbdrvasm.asm | 20 + classic/fw/usbdrv/usbdrvasm12.inc | 392 +++++++++++ classic/fw/usbdrv/usbdrvasm128.inc | 749 +++++++++++++++++++++ classic/fw/usbdrv/usbdrvasm15.inc | 422 ++++++++++++ classic/fw/usbdrv/usbdrvasm16.inc | 345 ++++++++++ classic/fw/usbdrv/usbdrvasm165.inc | 452 +++++++++++++ classic/fw/usbdrv/usbdrvasm18-crc.inc | 706 ++++++++++++++++++++ classic/fw/usbdrv/usbdrvasm20.inc | 359 +++++++++++ classic/fw/usbdrv/usbportability.h | 143 +++++ classic/hw/classic-v1-rescue.lib | 191 ++++++ classic/hw/classic-v1.cmp | 101 +++ classic/hw/classic-v1.kicad_pcb | 822 ++++++++++++++++++++++++ classic/hw/classic-v1.net | 277 ++++++++ classic/hw/classic-v1.pdf | Bin 0 -> 21841 bytes classic/hw/classic-v1.pro | 32 + classic/hw/classic-v1.sch | 337 ++++++++++ classic/hw/img/classic-v1-bottom.png | Bin 0 -> 11716 bytes classic/hw/img/classic-v1-top.png | Bin 0 -> 21098 bytes classic/hw/img/classic-v1.jpg | Bin 0 -> 232987 bytes classic/sn/Makefile | 44 ++ classic/sn/go.mod | 5 + classic/sn/go.sum | 2 + classic/sn/sn.go | 177 +++++ 64 files changed, 11570 insertions(+) create mode 100644 .gitignore create mode 100644 app/Makefile create mode 100644 app/build/.gitignore create mode 100644 app/build/Dockerfile.dapper create mode 100644 app/build/Makefile create mode 100755 app/build/scripts/cli create mode 100755 app/build/scripts/entry create mode 100644 app/cmd/cli/cli.go create mode 100644 app/go.mod create mode 100644 app/go.sum create mode 100644 app/pkg/device/device.go create mode 100644 app/pkg/device/keycodes/keycodes.go create mode 100644 app/pkg/device/report3/report3.go create mode 100644 app/pkg/device/types.go create mode 100644 classic/README.md create mode 100644 classic/bl/.gitignore create mode 100644 classic/bl/Makefile create mode 100644 classic/bl/beeon/Makefile.inc create mode 100644 classic/bl/beeon/bootloaderconfig.h create mode 100644 classic/build/.gitignore create mode 100644 classic/build/Dockerfile.dapper create mode 100644 classic/build/Makefile create mode 100755 classic/build/scripts/beeon create mode 100755 classic/build/scripts/entry create mode 100644 classic/fw/Makefile create mode 100644 classic/fw/main.c create mode 100644 classic/fw/usbconfig.h create mode 100644 classic/fw/usbdrv/Changelog.txt create mode 100644 classic/fw/usbdrv/CommercialLicense.txt create mode 100644 classic/fw/usbdrv/License.txt create mode 100644 classic/fw/usbdrv/Readme.txt create mode 100644 classic/fw/usbdrv/USB-ID-FAQ.txt create mode 100644 classic/fw/usbdrv/USB-IDs-for-free.txt create mode 100644 classic/fw/usbdrv/asmcommon.inc create mode 100644 classic/fw/usbdrv/oddebug.c create mode 100644 classic/fw/usbdrv/oddebug.h create mode 100644 classic/fw/usbdrv/usbconfig-prototype.h create mode 100644 classic/fw/usbdrv/usbdrv.c create mode 100644 classic/fw/usbdrv/usbdrv.h create mode 100644 classic/fw/usbdrv/usbdrvasm.S create mode 100644 classic/fw/usbdrv/usbdrvasm.asm create mode 100644 classic/fw/usbdrv/usbdrvasm12.inc create mode 100644 classic/fw/usbdrv/usbdrvasm128.inc create mode 100644 classic/fw/usbdrv/usbdrvasm15.inc create mode 100644 classic/fw/usbdrv/usbdrvasm16.inc create mode 100644 classic/fw/usbdrv/usbdrvasm165.inc create mode 100644 classic/fw/usbdrv/usbdrvasm18-crc.inc create mode 100644 classic/fw/usbdrv/usbdrvasm20.inc create mode 100644 classic/fw/usbdrv/usbportability.h create mode 100644 classic/hw/classic-v1-rescue.lib create mode 100644 classic/hw/classic-v1.cmp create mode 100644 classic/hw/classic-v1.kicad_pcb create mode 100644 classic/hw/classic-v1.net create mode 100644 classic/hw/classic-v1.pdf create mode 100644 classic/hw/classic-v1.pro create mode 100644 classic/hw/classic-v1.sch create mode 100644 classic/hw/img/classic-v1-bottom.png create mode 100644 classic/hw/img/classic-v1-top.png create mode 100644 classic/hw/img/classic-v1.jpg create mode 100644 classic/sn/Makefile create mode 100644 classic/sn/go.mod create mode 100644 classic/sn/go.sum create mode 100644 classic/sn/sn.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b4fb6b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# ---> VisualStudioCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + diff --git a/README.md b/README.md index 1fc8633..64f0d25 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,18 @@ # beeon +## Overview +The classic device was created several years ago to fill a single need, preventing my work computer from auto locking due to inactivity. *Subverting security policy comes with a responsibility to ensure that you lock your computer (CTRL+ALT+DEL) everytime you walk away!* + +## classic notes +### flash bootloader to board +`avrdude -c USBasp -p attiny85 -U flash:w:beeon-classic-v1-micronucleus-2.04.hex:i -B 10` +`avrdude -c USBasp -p attiny85 -U lfuse:w:0xe1:m -U hfuse:w:0xdd:m -U efuse:w:0xfe:m` +### upload new firmware onto board +`micronucleus --run {firmware.hex}` +### clear out any existing firmware +`micronucleus --erase-only` +### upload firmware with random serial number +*first create random serial number file* +`beeon-classic-sn -mk -mk-p=Z-ZZ > beeon-sn.txt` +`beeon-classic-sn | micronucleus --run -` + diff --git a/app/Makefile b/app/Makefile new file mode 100644 index 0000000..cfc790f --- /dev/null +++ b/app/Makefile @@ -0,0 +1,53 @@ +## +## +## + +.PHONY: help all linux darwin windows cli + +help: ## This help. + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) + +.DEFAULT_GOAL := all + +bin/: + @mkdir -p bin + +all: ## build linux, darwin & windows +all: linux darwin windows + +cli: linux-cli windows-cli darwin-cli + +linux: linux-cli +windows: windows-cli +darwin: darwin-cli + +linux-cli: bin/ ## build linux binary + @GOOS=linux GOARCH=amd64 CGO_ENABLED=1 \ + go build \ + -installsuffix cgo -ldflags=" -s -w" \ + -o bin/beeon-cli-linux-amd64 \ + microjelly.com/beeon/app/cmd/cli + +darwin-cli: bin/ ## build darwin binary + @GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 \ + CC=o64-clang CXX=o64-clang++ \ + LDFLAGS="-linkmode external -s" \ + go build \ + -installsuffix cgo -ldflags=" -s -w" \ + -o bin/beeon-cli-darwin-amd64 \ + microjelly.com/beeon/app/cmd/cli + +windows-cli: bin/ ## build windows binary + @GOOS=windows GOARCH=amd64 CGO_ENABLED=1 \ + CC=x86_64-w64-mingw32-gcc \ + go build \ + -installsuffix cgo -ldflags=" -s -w" \ + -o bin/beeon-cli-windows-amd64.exe \ + microjelly.com/beeon/app/cmd/cli + +mod-tidy: + @go clean --modcache + @GOPROXY=direct GOSUMDB=off go mod tidy + +clean: + @rm -rf bin \ No newline at end of file diff --git a/app/build/.gitignore b/app/build/.gitignore new file mode 100644 index 0000000..1a6fd01 --- /dev/null +++ b/app/build/.gitignore @@ -0,0 +1,3 @@ +Dockerfile.dapper[0-9]* +.dapper +bin/ \ No newline at end of file diff --git a/app/build/Dockerfile.dapper b/app/build/Dockerfile.dapper new file mode 100644 index 0000000..93081c4 --- /dev/null +++ b/app/build/Dockerfile.dapper @@ -0,0 +1,10 @@ +FROM dockercore/golang-cross:latest +RUN apt-get update -qq \ + && apt-get install -y -q --no-install-recommends libusb-1.0-0-dev + +ENV DAPPER_SOURCE /source +ENV DAPPER_OUTPUT ./build/bin +WORKDIR ${DAPPER_SOURCE} + +ENTRYPOINT ["./build/scripts/entry"] +CMD ["cli"] \ No newline at end of file diff --git a/app/build/Makefile b/app/build/Makefile new file mode 100644 index 0000000..85510a0 --- /dev/null +++ b/app/build/Makefile @@ -0,0 +1,22 @@ +TARGETS := $(shell ls scripts) + +.dapper: + @echo Downloading dapper + @curl -sL https://releases.rancher.com/dapper/latest/dapper-`uname -s`-`uname -m` > .dapper.tmp + @@chmod +x .dapper.tmp + @./.dapper.tmp -v + @mv .dapper.tmp .dapper + +$(TARGETS): .dapper + cd ../ && $(PWD)/.dapper -f build/Dockerfile.dapper $@ + @yes | docker image prune > /dev/null + +.PHONY: $(TARGETS) +.DEFAULT_GOAL := cli + +clean: + @rm -rf $(PWD)/bin $(PWD)/.dapper $(PWD)/Dockerfile.dapper[0-9]* + @docker rmi app:master + +shell-bind: .dapper + cd ../ && $(PWD)/.dapper -f build/Dockerfile.dapper -m bind -s \ No newline at end of file diff --git a/app/build/scripts/cli b/app/build/scripts/cli new file mode 100755 index 0000000..7986f7c --- /dev/null +++ b/app/build/scripts/cli @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +## multi-platform cli build +make cli +cp bin/*cli* ${DAPPER_OUTPUT}/ +ls -la ${DAPPER_OUTPUT}/*cli* diff --git a/app/build/scripts/entry b/app/build/scripts/entry new file mode 100755 index 0000000..8c92812 --- /dev/null +++ b/app/build/scripts/entry @@ -0,0 +1,13 @@ +#!/bin/bash +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +mkdir -p ${DAPPER_OUTPUT} +if [ -e ${DIR}/$1 ]; then + ${DIR}/"$@" +else + exec "$@" +fi + +chown -R $DAPPER_UID:$DAPPER_GID ${DAPPER_OUTPUT} \ No newline at end of file diff --git a/app/cmd/cli/cli.go b/app/cmd/cli/cli.go new file mode 100644 index 0000000..2a92b6c --- /dev/null +++ b/app/cmd/cli/cli.go @@ -0,0 +1,208 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + "strings" + + "microjelly.com/beeon/app/pkg/device" + "microjelly.com/beeon/app/pkg/device/keycodes" + "microjelly.com/beeon/app/pkg/device/report3" +) + +var ( + appVersion string = "1.01" + vendorID uint16 = 0x16d0 + productID uint16 = 0x09fa +) + +func main() { + + var ( + action string = "none" + serialNumber string = "" + inState string = "" + newState report3.State = report3.StateUnset + inRate string = "" + newRate report3.Rate = report3.RateUnset + newMode report3.Mode = report3.ModeUnset + inMouseMode string = "" + newMouseMode report3.MouseMode = report3.MouseModeUnset + inKeyCode uint = 256 + newKeyCode keycodes.KeyCode = keycodes.KeyUnset + ) + + debugFlag := flag.Bool("debug", false, "enable debug logging") + listFlag := flag.Bool("list", false, "list connected devices") + readFlag := flag.Bool("read", false, "read device config") + flag.StringVar(&serialNumber, "serial", "", "target specific device by serial_number") + flag.StringVar(&inState, "state", "", "change device state ") + flag.StringVar(&inRate, "rate", "", "change device rate ") + flag.StringVar(&inMouseMode, "mouse", "", "change to mouse mode and set mode ") + flag.UintVar(&inKeyCode, "keyboard", 256, "change to keyboard mode and set keycod") + flag.Parse() + + if *debugFlag { + device.SetLogLevel(device.LogDebug) + } + + if *listFlag { + action = "list" + } else if *readFlag { + action = "read" + } + + log.Printf("beeon-cli core:%s usb:%t", device.Version(), device.UsbSupported()) + + if serialNumber != "" { + log.Printf("serial:%s", serialNumber) + } + + if action == "list" { + devs, err := device.Enumerate(vendorID, productID) + if err != nil { + panic(err) + } + for i, _dev := range devs { + log.Printf("[%d] %s:v%s:%s:%s", + i, + _dev.Serial, + fmt.Sprintf("%d.%02d", (_dev.Version&0xff00)>>8, (_dev.Version&0x00ff)), + _dev.Manufacturer, + _dev.Product, + ) + } + os.Exit(0) + } + + if action == "read" { + r3, err := device.ReadReport3(vendorID, productID, serialNumber) + if err != nil { + log.Printf(err.Error()) + os.Exit(1) + } + log.Printf("%+v", r3) + } + + if inMouseMode != "" { + switch strings.ToLower(inMouseMode) { + case "circle": + newMouseMode = report3.MouseModeCircle + newMode = report3.ModeMouse + action = "write" + case "random": + newMouseMode = report3.MouseModeRandom + newMode = report3.ModeMouse + action = "write" + default: + log.Printf("invalid mouse-mode %s", inMouseMode) + } + } + + if inKeyCode != 256 && action == "none" { + newKeyCode = keycodes.KeyCode(inKeyCode) + newMode = report3.ModeKeyboard + action = "write" + } + + if inState != "" { + switch strings.ToLower(inState) { + case "idle": + newState = report3.StateIdle + action = "write" + case "active": + newState = report3.StateActive + action = "write" + default: + log.Printf("invalid state %s", inState) + } + } + + if inRate != "" { + switch strings.ToLower(inRate) { + case "extra-slow": + newRate = report3.RateExtraSlow + action = "write" + case "slow": + newRate = report3.RateSlow + action = "write" + case "normal": + newRate = report3.RateNormal + action = "write" + case "fast": + newRate = report3.RateFast + action = "write" + default: + log.Printf("invalid rate %s", inRate) + } + } + + if action == "none" { + flag.PrintDefaults() + os.Exit(1) + } + + if action == "write" { + hasChange := false + whatChanged := []string{} + r3, err := device.ReadReport3(vendorID, productID, serialNumber) + if err != nil { + log.Printf(err.Error()) + os.Exit(1) + } + + if (newMode != report3.ModeUnset) && (r3.Mode != newMode) { + whatChanged = append(whatChanged, fmt.Sprintf("Mode(%s>%s)", r3.Mode, newMode)) + r3.Mode = newMode + hasChange = true + } else { + r3.Mode = report3.ModeUnset + } + + if newKeyCode != keycodes.KeyUnset && (newKeyCode != r3.KeyCode) { + whatChanged = append(whatChanged, fmt.Sprintf("KeyCode(%s>%s)", r3.KeyCode, newKeyCode)) + r3.KeyCode = newKeyCode + hasChange = true + } else { + r3.KeyCode = keycodes.KeyUnset + } + + if newMouseMode != report3.MouseModeUnset && (newMouseMode != r3.MouseMode) { + whatChanged = append(whatChanged, fmt.Sprintf("MouseMode(%s>%s)", r3.MouseMode, newMouseMode)) + r3.MouseMode = newMouseMode + hasChange = true + } else { + r3.MouseMode = report3.MouseModeUnset + } + + if newState != report3.StateUnset && (newState != r3.State) { + whatChanged = append(whatChanged, fmt.Sprintf("State(%s>%s)", r3.State, newState)) + r3.State = newState + hasChange = true + } else { + r3.State = report3.StateUnset + } + + if newRate != report3.RateUnset && (newRate != r3.Rate) { + whatChanged = append(whatChanged, fmt.Sprintf("Rate(%s>%s)", r3.Rate, newRate)) + r3.Rate = newRate + hasChange = true + } else { + r3.Rate = report3.RateUnset + } + + if hasChange { + err := device.WriteReport3(vendorID, productID, serialNumber, r3.Bytes()) + if err != nil { + log.Printf(err.Error()) + os.Exit(1) + } + log.Printf("changes made {%s}", strings.Join(whatChanged, "; ")) + } else { + log.Printf("no changes") + } + } + +} diff --git a/app/go.mod b/app/go.mod new file mode 100644 index 0000000..9fbbc13 --- /dev/null +++ b/app/go.mod @@ -0,0 +1,5 @@ +module microjelly.com/beeon/app + +go 1.13 + +require github.com/microjelly/hid v0.0.0-20200814141453-3602713b3885 diff --git a/app/go.sum b/app/go.sum new file mode 100644 index 0000000..0336e84 --- /dev/null +++ b/app/go.sum @@ -0,0 +1,2 @@ +github.com/microjelly/hid v0.0.0-20200814141453-3602713b3885 h1:UdpWecEd/pxs7YgX+gYnROdB4ID6oKIuv2GnVhhXofk= +github.com/microjelly/hid v0.0.0-20200814141453-3602713b3885/go.mod h1:bPSrfoMz72p90YvMJSC/hCd0xL7MebXTyM8oWhtsgu8= diff --git a/app/pkg/device/device.go b/app/pkg/device/device.go new file mode 100644 index 0000000..88ee5df --- /dev/null +++ b/app/pkg/device/device.go @@ -0,0 +1,106 @@ +package device + +import ( + "errors" + "log" + + "github.com/microjelly/hid" + "microjelly.com/beeon/app/pkg/device/report3" +) + +var ( + _devices []Info + _logLevel LogLevel = LogNone +) + +// Version - returns the version of this module +func Version() string { + return "v1.0.1" +} + +// UsbSupported - returns true if usb is supported on this platform +func UsbSupported() bool { + return hid.Supported() +} + +// SetLogLevel - sets the logging level +func SetLogLevel(level LogLevel) { + _logLevel = level +} + +// Enumerate - gets connected devices +func Enumerate(vendorID uint16, productID uint16) ([]Info, error) { + + var ( + _devices []Info = nil + pSN string = "" + ) + + for _, dev := range hid.Enumerate(vendorID, productID) { + if dev.Serial != pSN { + _devices = append(_devices, Info{ + Serial: dev.Serial, + Version: dev.Release, + Manufacturer: dev.Manufacturer, + Product: dev.Product, + }) + pSN = dev.Serial + } + } + + return _devices, nil +} + +func open(vendorID uint16, productID uint16, serial string) (*hid.Device, error) { + + devices := hid.Enumerate(vendorID, productID) + for _, dev := range devices { + if (serial != "" && dev.Serial == serial) || serial == "" { + _log(LogInfo, "opening device [%s]", dev.Serial) + return dev.Open() + } + } + return nil, errors.New("device not found") +} + +// ReadReport3 - get device configuration +func ReadReport3(vendorID uint16, productID uint16, serial string) (*report3.Report, error) { + + report := []byte{0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} + dev, err := open(vendorID, productID, serial) + if err != nil { + return nil, err + } + defer dev.Close() + _, err = dev.GetFeatureReport(report) + if err != nil { + return nil, err + } + r := report3.New(report) + _log(LogDebug, "read %v > %v", report, r) + return r, nil +} + +// WriteReport3 - get device configuration +func WriteReport3(vendorID uint16, productID uint16, serial string, report []byte) error { + + dev, err := open(vendorID, productID, serial) + if err != nil { + return err + } + defer dev.Close() + + _log(LogDebug, "write %v > %v", report, report3.New(report)) + _, err = dev.SendFeatureReport(report) + if err != nil { + return err + } + + return nil +} + +func _log(level LogLevel, format string, v ...interface{}) { + if level >= _logLevel { + log.Printf(format, v...) + } +} diff --git a/app/pkg/device/keycodes/keycodes.go b/app/pkg/device/keycodes/keycodes.go new file mode 100644 index 0000000..a0ffc7a --- /dev/null +++ b/app/pkg/device/keycodes/keycodes.go @@ -0,0 +1,175 @@ +// The contents of this file is free and unencumbered software released into +// the public domain. Refer to for more information. +// Src: https://github.com/yakshaveinc/go-keycodes/blob/master/keycodes.go + +package keycodes + +import "fmt" + +type KeyCode uint8 + +func (k KeyCode) String() string { + if k == KeyUnset { + return "unset" + } + return fmt.Sprintf("Key(%d)", k) +} + +// note: similar keys have different key codes, like Enter and +// Keypad Enter + +// cross-platform key codes, compatible with SDL 2 and USB HID speccy +// https://hg.libsdl.org/SDL/file/default/include/SDL_scancode.h +// http://www.usb.org/developers/hidpage/Hut1_12v2.pdf (page 53) + +// key for codes 0 - 3 are not present on keyboards, they are: +// 0 - Reserved (no event) +// 1 - ErrorRollOver +// 2 - POSTFail +// 3 - ErrorUndefined + +const ( + KeyA KeyCode = 4 + iota + KeyB + KeyC + KeyD + KeyE + KeyF + KeyG + KeyH + KeyI + KeyJ + KeyK + KeyL + KeyM + KeyN + KeyO + KeyP + KeyQ + KeyR + KeyS + KeyT + KeyU + KeyV + KeyW + KeyX + KeyY + KeyZ +) +const ( + Key1 KeyCode = 30 + iota + Key2 + Key3 + Key4 + Key5 + Key6 + Key7 + Key8 + Key9 + Key0 +) +const ( + // choice is to use Enter name instead of Return + // and the key code is different from Keypad Enter + KeyEnter KeyCode = 40 + iota + KeyEscape + KeyBackspace + KeyTab + KeySpace + // keypad minus has different key code + KeyMinus + KeyEquals + KeyLeftBracket + KeyRightBracket + KeyBackslash +) + +// key code number 50 is skipped, because it is unclear +// where is the key, and what is its name and function + +const ( + // different name from SDL2 for brevity + KeyColon KeyCode = 51 + iota + KeyApostrophe + // KeyTilde is an alias + KeyGrave + KeyCommad + // KeyDot is an alias, keypad period is a different + KeyPeriod + Slash + CapsLock +) +const KeyTilde = KeyGrave +const KeyDot = KeyPeriod + +const ( + KeyF1 KeyCode = 58 + iota + KeyF2 + KeyF3 + KeyF4 + KeyF5 + KeyF6 + KeyF7 + KeyF8 + KeyF9 + KeyF10 + KeyF11 + KeyF12 +) +const ( + KeyPrintScreen KeyCode = 70 + iota + KeyScrollLock + KeyPause + KeyInsert + KeyHome + KeyPageUp + KeyDelete + KeyEnd + KeyPageDown + KeyRight + KeyLeft + KeyDown + KeyUp +) +const ( + KeyNumLock KeyCode = 83 + iota + KeyKpDivide + KeyKpMultiply + KeyKpMinus + KeyKpPlus + KeyKpEnter + KeyKp1 + KeyKp2 + KeyKp3 + KeyKp4 + KeyKp5 + KeyKp6 + KeyKp7 + KeyKp8 + KeyKp9 + KeyKp0 + // KeyKpDot is an alias + KeyKpPeriod +) +const KeyKpDot = KeyKpPeriod + +// key code 100 is skipped, because I can not find the key +// key code 101 is not present on Mac +// key codes 102-223 are not present on PC + +const ( + KeyLCtrl KeyCode = 224 + iota + KeyLShift + KeyLAlt + // KeyLWin is an alias + KeyLGUI + KeyRCtrl + KeyRShift + KeyRAlt + // KeyRWin is an alias + KeyRGUI +) +const KeyLWin = KeyLGUI +const KeyRWin = KeyRGUI + +const KeyUnset = 255 diff --git a/app/pkg/device/report3/report3.go b/app/pkg/device/report3/report3.go new file mode 100644 index 0000000..be50e90 --- /dev/null +++ b/app/pkg/device/report3/report3.go @@ -0,0 +1,139 @@ +package report3 + +import ( + "fmt" + + "microjelly.com/beeon/app/pkg/device/keycodes" +) + +type State uint8 + +const ( + StateIdle = State(10) + StateActive = State(20) + StateUnset = State(255) +) + +func (r State) String() string { + switch r { + case StateActive: + return "active" + case StateIdle: + return "idle" + case StateUnset: + return "unset" + default: + return "unknown" + } +} + +type Mode uint8 + +const ( + ModeMouse = Mode(10) + ModeKeyboard = Mode(20) + ModeUnset = Mode(255) +) + +func (r Mode) String() string { + switch r { + case ModeMouse: + return "mouse" + case ModeKeyboard: + return "keyboard" + case ModeUnset: + return "unset" + default: + return "unknown" + } +} + +type Rate uint8 + +const ( + RateFast = Rate(1) + RateNormal = Rate(8) + RateSlow = Rate(16) + RateExtraSlow = Rate(32) + RateUnset = Rate(255) +) + +func (r Rate) String() string { + switch r { + case RateFast: + return "fast" + case RateNormal: + return "normal" + case RateSlow: + return "slow" + case RateExtraSlow: + return "extra-slow" + case RateUnset: + return "unset" + default: + return fmt.Sprintf("custom (%d)", r) + } +} + +type MouseMode uint8 + +const ( + MouseModeCircle = MouseMode(10) + MouseModeRandom = MouseMode(20) + MouseModeUnset = MouseMode(255) +) + +func (r MouseMode) String() string { + switch r { + case MouseModeCircle: + return "circle" + case MouseModeRandom: + return "random" + case MouseModeUnset: + return "unset" + default: + return "unknown" + } +} + +type Report struct { + State State + Mode Mode + Rate Rate + KeyCode keycodes.KeyCode + MouseMode MouseMode +} + +func (r Report) String() string { + switch r.Mode { + case ModeMouse: + return fmt.Sprintf("{Mode: %s, MouseMode: %s, Rate: %s, State: %s}", r.Mode, r.MouseMode, r.Rate, r.State) + case ModeKeyboard: + return fmt.Sprintf("{Mode: %s, KeyCode: %s, Rate: %s, State: %s}", r.Mode, r.KeyCode, r.Rate, r.State) + case ModeUnset: + return fmt.Sprintf("{Mode: %s, MouseMode: %s, KeyCode: %s, Rate: %s, State: %s}", r.Mode, r.MouseMode, r.KeyCode, r.Rate, r.State) + default: + return "{invalid}" + } +} + +func New(b []byte) *Report { + r := new(Report) + r.State = State(b[1]) + r.Mode = Mode(b[2]) + r.Rate = Rate(b[3]) + r.KeyCode = keycodes.KeyCode(b[4]) + r.MouseMode = MouseMode(b[5]) + return r +} + +func (r Report) Bytes() []byte { + b := make([]byte, 6) + b[0] = 3 + b[1] = uint8(r.State) + b[2] = uint8(r.Mode) + b[3] = uint8(r.Rate) + b[4] = uint8(r.KeyCode) + b[5] = uint8(r.MouseMode) + return b +} diff --git a/app/pkg/device/types.go b/app/pkg/device/types.go new file mode 100644 index 0000000..cd5ca54 --- /dev/null +++ b/app/pkg/device/types.go @@ -0,0 +1,21 @@ +package device + +// Info - short list of details +type Info struct { + Serial string + Manufacturer string + Version uint16 + Product string +} + +// LogLevel - something +type LogLevel uint + +// LogNone - disabled logging +// LogInfo - basic logging +// LogDebug - even more +const ( + LogDebug LogLevel = iota + LogInfo + LogNone +) diff --git a/classic/README.md b/classic/README.md new file mode 100644 index 0000000..3e0298d --- /dev/null +++ b/classic/README.md @@ -0,0 +1,59 @@ +[finished-product-image]: hw/img/classic-v1.jpg "BEEON - Classic; v1" +[pcb-top-image]: hw/img/classic-v1-top.png "BEEON - Classic; v1; pcb-top" +[pcb-bottom-image]: hw/img/classic-v1-bottom.png "BEEON - Classic; v1; pcb-bottom" + +# classic hardware + +## Overview +The classic device was created several years ago to fill a single need, preventing my work computer from auto locking due to inactivity. *Subverting security policy comes with a responsibility to ensure that you lock your computer (CTRL+ALT+DEL) everytime you walk away!* + +Alternatively the device can also be used as a low-pin USB development platform, by soldering a pin header to the exposed pads on the bottom you can access 3 GPIOS, VCC (3v3+), GND and the RESET pin of the ATTINY85. Making it a very low-pin count development board with USB capabilities. + +--- +## Firmware +The firmware makes use of the outstanding V-USB software-only implementation of low-speed USB devices. If you plan on making changes or releasing your own devices please change the vendor/device names as well as the vendor/product ids within the [usbconfig.h](fw/v1/usbconfig.h) file. + +`V-USB` is released under the [GPLv2](https://github.com/obdev/v-usb/blob/master/usbdrv/License.txt) license. + +--- +## Bootloader +Micronucleus is the bootloader that is currently in use by the beeon-classic devices. You will need a copy of the commandline tool for your operating system inorder to upload new firmware onto the device. v2.04 is the version that we are currently using. + +| OS | Link | +| -- | -- | +| Windows | [Download](https://github.com/micronucleus/micronucleus/raw/master/commandline/micronucleus.exe) | +| Linux/Darwin | [Build](https://github.com/micronucleus/micronucleus/tree/master/commandline) | +`Micronucleus` is released under the [GPLv2](https://github.com/micronucleus/micronucleus/blob/master/License.txt) license. + +--- +## BOM +| Reference(s) | Package | Description | Manufacturer | Part Number | **Cost | +| ------------ | -------: | ------------------------------ | ----------------- | -----------------: | -----: | +| SW1 | | Switch | E-Switch | *TL1014BF180QG | $#.## | +| D1 | 1206 | LED | OSRAM Opto | *LG N971-KN-1 | $#.## | +| J1 | | 6 PIN 2.54mm (Optional Header) | Harwin | M20-7820646 | $0.95 | +| J2 | | USB | CNC-Tech | 1001-011-01101 | $0.77 | +| R1 | 0805 | 47 Ohm Resistor | Vishay/Dale | CRCW080547R0FKEA | $0.10 | +| R2 | 0805 | 1K5 Ohm Resistor | Vishay/Dale | CRCW08051K50FKEB | $0.17 | +| R3 | 0805 | 68 Ohm Resistor | Vishay/Dale | CRCW080568R0FKEA | $0.10 | +| R4 | 0805 | 68 Ohm Resistor | Vishay/Dale | CRCW080568R0FKEA | $0.10 | +| U1 | SOIC-8 | ATtiny85 MCU | Atmel | ATtiny85-20SU | $1.13 | +| U2 | SOT-23-5 | 1 00mA 3.3 LDO | Texas Instruments | TPS79133DBVR | $1.32 | +| C1 | 1206 | 10uF 10v Tantalum Capacitor | AVX | F931A106KAA | $0.49 | +| C2 | 1206 | 0.1uF 10v MLCC Capacitor | Vishay/Vitramon | VJ1206Y104KXQPW1BC | $0.29 | +| C3 | 1206 | 0.01uF 10v MLCC Capacitor | Vishay/Vitramon | VJ1206Y103KXJCW1BC | $0.25 | +| C4 | 1206 | 1uF 16v Tantalum Capacitor | AVX | F931C105KAA | $0.51 | + +**obsolete part (need to find replacement)* +***cost last updated on 8/14/2020* + +--- +## PCB +[`Order from OSH Park`](https://oshpark.com/shared_projects/amtuUCdw) +![alt text][pcb-top-image] ![alt text][pcb-bottom-image] + +--- +## Finished Product +| ![alt text][finished-product-image] | +| :---------------------------------- | + diff --git a/classic/bl/.gitignore b/classic/bl/.gitignore new file mode 100644 index 0000000..05764df --- /dev/null +++ b/classic/bl/.gitignore @@ -0,0 +1 @@ +/work \ No newline at end of file diff --git a/classic/bl/Makefile b/classic/bl/Makefile new file mode 100644 index 0000000..b82ff9c --- /dev/null +++ b/classic/bl/Makefile @@ -0,0 +1,30 @@ +## +## +## + +MN_VERSION=2.04 + +.PHONY: help beeon + +help: ## This help. + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) + +.DEFAULT_GOAL := beeon + +work/: + @mkdir -p work + +work/${MN_VERSION}.tar.gz: work/ + @wget -O work/${MN_VERSION}.tar.gz https://github.com/micronucleus/micronucleus/archive/${MN_VERSION}.tar.gz + +work/micronucleus-${MN_VERSION}/firmware/Makefile: work/${MN_VERSION}.tar.gz + @echo "Unpacking/Patching micronucleus-${MN_VERSION}" + @cd work && tar zxf ${MN_VERSION}.tar.gz + @cp -r beeon/ work/micronucleus-${MN_VERSION}/firmware/configuration/ + +beeon: work/micronucleus-${MN_VERSION}/firmware/Makefile ## build bootloader + @echo "Building" + @make -C work/micronucleus-${MN_VERSION}/firmware CONFIG=beeon + +clean: + @rm -rf work \ No newline at end of file diff --git a/classic/bl/beeon/Makefile.inc b/classic/bl/beeon/Makefile.inc new file mode 100644 index 0000000..ee7252d --- /dev/null +++ b/classic/bl/beeon/Makefile.inc @@ -0,0 +1,60 @@ +# Name: Makefile +# Project: Micronucleus +# License: GNU GPL v2 (see License.txt) + +# Controller type: ATtiny 85 - 16.5 MHz +# Configuration: Default +# Last Change: Mar 16,2014 + + +F_CPU = 16500000 +DEVICE = attiny85 + +# hexadecimal address for bootloader section to begin. To calculate the best value: +# - make clean; make main.hex; ### output will list data: 2124 (or something like that) +# - for the size of your device (8kb = 1024 * 8 = 8192) subtract above value 2124... = 6068 +# - How many pages in is that? 6068 / 64 (tiny85 page size in bytes) = 94.8125 +# - round that down to 94 - our new bootloader address is 94 * 64 = 6016, in hex = 1780 +BOOTLOADER_ADDRESS = 1980 + +FUSEOPT = -U lfuse:w:0xe1:m -U hfuse:w:0xdd:m -U efuse:w:0xfe:m +FUSEOPT_DISABLERESET = -U lfuse:w:0xe1:m -U efuse:w:0xfe:m -U hfuse:w:0x5d:m + +#--------------------------------------------------------------------- +# ATtiny85 +#--------------------------------------------------------------------- +# Fuse extended byte: +# 0xFE = - - - - - 1 1 0 +# ^ +# | +# +---- SELFPRGEN (enable self programming flash) +# +# Fuse high byte: +# 0xdd = 1 1 0 1 1 1 0 1 +# ^ ^ ^ ^ ^ \-+-/ +# | | | | | +------ BODLEVEL 2..0 (brownout trigger level -> 2.7V) +# | | | | +---------- EESAVE (preserve EEPROM on Chip Erase -> not preserved) +# | | | +-------------- WDTON (watchdog timer always on -> disable) +# | | +---------------- SPIEN (enable serial programming -> enabled) +# | +------------------ DWEN (debug wire enable) +# +-------------------- RSTDISBL (disable external reset -> enabled) +# +# Fuse high byte ("no reset": external reset disabled, can't program through SPI anymore) +# 0x5d = 0 1 0 1 1 1 0 1 +# ^ ^ ^ ^ ^ \-+-/ +# | | | | | +------ BODLEVEL 2..0 (brownout trigger level -> 2.7V) +# | | | | +---------- EESAVE (preserve EEPROM on Chip Erase -> not preserved) +# | | | +-------------- WDTON (watchdog timer always on -> disable) +# | | +---------------- SPIEN (enable serial programming -> enabled) +# | +------------------ DWEN (debug wire enable) +# +-------------------- RSTDISBL (disable external reset -> disabled!) +# +# Fuse low byte: +# 0xe1 = 1 1 1 0 0 0 0 1 +# ^ ^ \+/ \--+--/ +# | | | +------- CKSEL 3..0 (clock selection -> HF PLL) +# | | +--------------- SUT 1..0 (BOD enabled, fast rising power) +# | +------------------ CKOUT (clock output on CKOUT pin -> disabled) +# +-------------------- CKDIV8 (divide clock by 8 -> don't divide) + +############################################################################### diff --git a/classic/bl/beeon/bootloaderconfig.h b/classic/bl/beeon/bootloaderconfig.h new file mode 100644 index 0000000..383bb1d --- /dev/null +++ b/classic/bl/beeon/bootloaderconfig.h @@ -0,0 +1,259 @@ +/* Name: bootloaderconfig.h + * Micronucleus configuration file. + * This file (together with some settings in Makefile.inc) configures the boot loader + * according to the hardware. + * + * Controller type: ATtiny 85 - 16.5 MHz + * Configuration: Default configuration + * USB D- : PB3 + * USB D+ : PB4 + * Entry : Always + * LED : None + * OSCCAL : Stays at 16 MHz + * Note: Uses 16.5 MHz V-USB implementation with PLL + * Last Change: Mar 16,2014 + * + * License: GNU GPL v2 (see License.txt + */ +#ifndef __bootloaderconfig_h_included__ +#define __bootloaderconfig_h_included__ + +/* ------------------------------------------------------------------------- */ +/* Hardware configuration. */ +/* Change this according to your CPU and USB configuration */ +/* ------------------------------------------------------------------------- */ + +#define USB_CFG_IOPORTNAME B + /* This is the port where the USB bus is connected. When you configure it to + * "B", the registers PORTB, PINB and DDRB will be used. + */ + +#define USB_CFG_DMINUS_BIT 3 +/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. + * This may be any bit in the port. + */ +#define USB_CFG_DPLUS_BIT 4 +/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. + * This may be any bit in the port, but must be configured as a pin change interrupt. + */ + +#define USB_CFG_CLOCK_KHZ (F_CPU/1000) +/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000, + * 16500, 18000 and 20000. The 12.8 MHz and 16.5 MHz versions of the code + * require no crystal, they tolerate +/- 1% deviation from the nominal + * frequency. All other rates require a precision of 2000 ppm and thus a + * crystal! + * Since F_CPU should be defined to your actual clock rate anyway, you should + * not need to modify this setting. + */ + +/* ------------- Set up interrupt configuration (CPU specific) -------------- */ +/* The register names change quite a bit in the ATtiny family. Pay attention */ +/* to the manual. Note that the interrupt flag system is still used even though */ +/* interrupts are disabled. So this has to be configured correctly. */ + + +// setup interrupt for Pin Change for D+ +#define USB_INTR_CFG PCMSK +#define USB_INTR_CFG_SET (1 << USB_CFG_DPLUS_BIT) +#define USB_INTR_CFG_CLR 0 +#define USB_INTR_ENABLE GIMSK +#define USB_INTR_ENABLE_BIT PCIE +#define USB_INTR_PENDING GIFR +#define USB_INTR_PENDING_BIT PCIF +#define USB_INTR_VECTOR PCINT0_vect + +/* ------------------------------------------------------------------------- */ +/* Configuration relevant to the CPU the bootloader is running on */ +/* ------------------------------------------------------------------------- */ + +// how many milliseconds should host wait till it sends another erase or write? +// needs to be above 4.5 (and a whole integer) as avr freezes for 4.5ms +#define MICRONUCLEUS_WRITE_SLEEP 5 + + +/* ---------------------- feature / code size options ---------------------- */ +/* Configure the behavior of the bootloader here */ +/* ------------------------------------------------------------------------- */ + +/* + * Define Bootloader entry condition + * + * If the entry condition is not met, the bootloader will not be activated and the user program + * is executed directly after a reset. If no user program has been loaded, the bootloader + * is always active. + * + * ENTRY_ALWAYS Always activate the bootloader after reset. Requires the least + * amount of code. + * + * ENTRY_WATCHDOG Activate the bootloader after a watchdog reset. This can be used + * to enter the bootloader from the user program. + * Adds 22 bytes. + * + * ENTRY_EXT_RESET Activate the bootloader after an external reset was issued by + * pulling the reset pin low. It may be necessary to add an external + * pull-up resistor to the reset pin if this entry method appears to + * behave unreliably. + * Adds 22 bytes. + * + * ENTRY_JUMPER Activate the bootloader when a specific pin is pulled low by an + * external jumper. + * Adds 34 bytes. + * + * JUMPER_PIN Pin the jumper is connected to. (e.g. PB0) + * JUMPER_PORT Port out register for the jumper (e.g. PORTB) + * JUMPER_DDR Port data direction register for the jumper (e.g. DDRB) + * JUMPER_INP Port inout register for the jumper (e.g. PINB) + * + */ + +#define ENTRYMODE ENTRY_JUMPER + +#define JUMPER_PIN PB2 +#define JUMPER_PORT PORTB +#define JUMPER_DDR DDRB +#define JUMPER_INP PINB + +/* + Internal implementation, don't change this unless you want to add an entrymode. +*/ + +#define ENTRY_ALWAYS 1 +#define ENTRY_WATCHDOG 2 +#define ENTRY_EXT_RESET 3 +#define ENTRY_JUMPER 4 + +#if ENTRYMODE==ENTRY_ALWAYS + #define bootLoaderInit() + #define bootLoaderExit() + #define bootLoaderStartCondition() 1 +#elif ENTRYMODE==ENTRY_WATCHDOG + #define bootLoaderInit() + #define bootLoaderExit() + #define bootLoaderStartCondition() (MCUSR&_BV(WDRF)) +#elif ENTRYMODE==ENTRY_EXT_RESET + #define bootLoaderInit() + #define bootLoaderExit() + #define bootLoaderStartCondition() (MCUSR&_BV(EXTRF)) +#elif ENTRYMODE==ENTRY_JUMPER + // Enable pull up on jumper pin and delay to stabilize input + #define bootLoaderInit() {JUMPER_DDR&=~_BV(JUMPER_PIN);JUMPER_PORT|=_BV(JUMPER_PIN);_delay_ms(1);} + #define bootLoaderExit() {JUMPER_PORT&=~_BV(JUMPER_PIN);} + #define bootLoaderStartCondition() (!(JUMPER_INP&_BV(JUMPER_PIN))) +#else + #error "No entry mode defined" +#endif + +/* + * Define bootloader timeout value. + * + * The bootloader will only time out if a user program was loaded. + * + * AUTO_EXIT_NO_USB_MS The bootloader will exit after this delay if no USB is connected. + * Set to 0 to disable + * Adds ~6 bytes. + * (This will wait for an USB SE0 reset from the host) + * + * AUTO_EXIT_MS The bootloader will exit after this delay if no USB communication + * from the host tool was received. + * Set to 0 to disable + * + * All values are approx. in milliseconds + */ + +#define AUTO_EXIT_NO_USB_MS 0 +#define AUTO_EXIT_MS 6000 + + /* + * Defines the setting of the RC-oscillator calibration after quitting the bootloader. (OSCCAL) + * + * OSCCAL_RESTORE_DEFAULT Set this to '1' to revert to OSCCAL factore calibration after bootlaoder exit. + * This is 8 MHz +/-2% on most devices or 16 MHz on the ATtiny 85 with activated PLL. + * Adds ~14 bytes. + * + * OSCCAL_SAVE_CALIB Set this to '1' to save the OSCCAL calibration during program upload. + * This value will be reloaded after reset and will also be used for the user + * program unless "OSCCAL_RESTORE_DEFAULT" is active. This allows calibrate the internal + * RC oscillator to the F_CPU target frequency +/-1% from the USB timing. Please note + * that only true if the ambient temperature does not change. + * Adds ~38 bytes. + * + * OSCCAL_HAVE_XTAL Set this to '1' if you have an external crystal oscillator. In this case no attempt + * will be made to calibrate the oscillator. You should deactivate both options above + * if you use this to avoid redundant code. + * + * If both options are selected, OSCCAL_RESTORE_DEFAULT takes precedence. + * + * If no option is selected, OSCCAL will be left untouched and stays at either factory calibration or F_CPU depending + * on whether the bootloader was activated. This will take the least memory. You can use this if your program + * comes with its own OSCCAL calibration or an external clock source is used. + */ + +#define OSCCAL_RESTORE_DEFAULT 0 +#define OSCCAL_SAVE_CALIB 1 +#define OSCCAL_HAVE_XTAL 0 + +/* + * Defines handling of an indicator LED while the bootloader is active. + * + * LED_MODE Define behavior of attached LED or suppress LED code. + * + * NONE Do not generate LED code (gains 18 bytes). + * ACTIVE_HIGH LED is on when output pin is high. This will toggle bettwen 1 and 0. + * ACTIVE_LOW LED is on when output pin is low. This will toggle between Z and 0. + * + * LED_DDR,LED_PORT,LED_PIN Where is your LED connected? + * + */ + +#define LED_MODE NONE + +#define LED_DDR DDRB +#define LED_PORT PORTB +#define LED_PIN PB1 + +/* + * This is the implementation of the LED code. Change the configuration above unless you want to + * change the led behavior + * + * LED_INIT Called once after bootloader entry + * LED_EXIT Called once during bootloader exit + * LED_MACRO Called in the main loop with the idle counter as parameter. + * Use to define pattern. +*/ + +#define NONE 0 +#define ACTIVE_HIGH 1 +#define ACTIVE_LOW 2 + +#if LED_MODE==ACTIVE_HIGH + #define LED_INIT(x) LED_DDR |= _BV(LED_PIN); + #define LED_EXIT(x) {LED_DDR &=~_BV(LED_PIN);LED_PORT &=~_BV(LED_PIN);} + #define LED_MACRO(x) if ( x & 0x4c ) {LED_PORT&=~_BV(LED_PIN);} else {LED_PORT|=_BV(LED_PIN);} +#elif LED_MODE==ACTIVE_LOW + #define LED_INIT(x) LED_PORT &=~_BV(LED_PIN); + #define LED_EXIT(x) LED_DDR &=~_BV(LED_PIN); + #define LED_MACRO(x) if ( x & 0x4c ) {LED_DDR&=~_BV(LED_PIN);} else {LED_DDR|=_BV(LED_PIN);} +#elif LED_MODE==NONE + #define LED_INIT(x) + #define LED_EXIT(x) + #define LED_MACRO(x) +#endif + +/* --------------------------------------------------------------------------- */ +/* Micronucleus internal configuration. Do not change anything below this line */ +/* --------------------------------------------------------------------------- */ + +// Microcontroller vectortable entries in the flash +#define RESET_VECTOR_OFFSET 0 + +// number of bytes before the boot loader vectors to store the tiny application vector table +#define TINYVECTOR_RESET_OFFSET 4 +#define TINYVECTOR_OSCCAL_OFFSET 6 + +/* ------------------------------------------------------------------------ */ +// postscript are the few bytes at the end of programmable memory which store tinyVectors +#define POSTSCRIPT_SIZE 6 +#define PROGMEM_SIZE (BOOTLOADER_ADDRESS - POSTSCRIPT_SIZE) /* max size of user program */ + +#endif /* __bootloader_h_included__ */ diff --git a/classic/build/.gitignore b/classic/build/.gitignore new file mode 100644 index 0000000..1a6fd01 --- /dev/null +++ b/classic/build/.gitignore @@ -0,0 +1,3 @@ +Dockerfile.dapper[0-9]* +.dapper +bin/ \ No newline at end of file diff --git a/classic/build/Dockerfile.dapper b/classic/build/Dockerfile.dapper new file mode 100644 index 0000000..ecc1b37 --- /dev/null +++ b/classic/build/Dockerfile.dapper @@ -0,0 +1,9 @@ +FROM golang:alpine3.12 +RUN apk --update add make git bash gcc-avr avr-libc + +ENV DAPPER_SOURCE /source +ENV DAPPER_OUTPUT ./build/bin +WORKDIR ${DAPPER_SOURCE} + +ENTRYPOINT ["./build/scripts/entry"] +CMD ["beeon"] \ No newline at end of file diff --git a/classic/build/Makefile b/classic/build/Makefile new file mode 100644 index 0000000..0e169c0 --- /dev/null +++ b/classic/build/Makefile @@ -0,0 +1,22 @@ +TARGETS := $(shell ls scripts) + +.dapper: + @echo Downloading dapper + @curl -sL https://releases.rancher.com/dapper/latest/dapper-`uname -s`-`uname -m` > .dapper.tmp + @@chmod +x .dapper.tmp + @./.dapper.tmp -v + @mv .dapper.tmp .dapper + +$(TARGETS): .dapper + cd ../ && $(PWD)/.dapper -f build/Dockerfile.dapper $@ + @yes | docker image prune > /dev/null + +.PHONY: $(TARGETS) +.DEFAULT_GOAL := beeon + +clean: + @rm -rf $(PWD)/bin $(PWD)/.dapper $(PWD)/Dockerfile.dapper[0-9]* + @docker rmi classic:master + +shell-bind: .dapper + cd ../ && $(PWD)/.dapper -f build/Dockerfile.dapper -m bind -s \ No newline at end of file diff --git a/classic/build/scripts/beeon b/classic/build/scripts/beeon new file mode 100755 index 0000000..18a5d84 --- /dev/null +++ b/classic/build/scripts/beeon @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +# micronucleus bootloader (v1 hardware) +MN_VERSION=2.04 +make -C bl +cp bl/work/micronucleus-${MN_VERSION}/firmware/main.hex ${DAPPER_OUTPUT}/beeon-classic-v1-micronucleus-${MN_VERSION}.hex +ls -la ${DAPPER_OUTPUT}/beeon-classic-v1-micronucleus-${MN_VERSION}.hex + +# classic v1 firmware +make -C fw +cp fw/main.hex ${DAPPER_OUTPUT}/beeon-classic-v1.hex +ls -al ${DAPPER_OUTPUT}/beeon-classic-v1.hex + +## firmware serial number patcher +make -C sn +cp sn/bin/* ${DAPPER_OUTPUT}/ +ls -la ${DAPPER_OUTPUT}/beeon-classic-sn-* diff --git a/classic/build/scripts/entry b/classic/build/scripts/entry new file mode 100755 index 0000000..8c92812 --- /dev/null +++ b/classic/build/scripts/entry @@ -0,0 +1,13 @@ +#!/bin/bash +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +mkdir -p ${DAPPER_OUTPUT} +if [ -e ${DIR}/$1 ]; then + ${DIR}/"$@" +else + exec "$@" +fi + +chown -R $DAPPER_UID:$DAPPER_GID ${DAPPER_OUTPUT} \ No newline at end of file diff --git a/classic/fw/Makefile b/classic/fw/Makefile new file mode 100644 index 0000000..33ef4a5 --- /dev/null +++ b/classic/fw/Makefile @@ -0,0 +1,41 @@ +DEVICE = attiny85 +F_CPU = 16500000 + +CFLAGS = -Iusbdrv -I. -DDEBUG_LEVEL=0 +OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o +COMPILE = avr-gcc -Wall -Os -DF_CPU=$(F_CPU) $(CFLAGS) -mmcu=$(DEVICE) + +.PHONY: help beeon + +help: ## This help. + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) + +.DEFAULT_GOAL := beeon + +beeon: main.hex ## build firmware + @echo "done" + +main.hex: main.elf + avr-objcopy -j .text -j .data -O ihex main.elf main.hex + avr-size main.hex + +main.elf: $(OBJECTS) + $(COMPILE) -o main.elf $(OBJECTS) + +clean: ## cleanup build output + -rm -f main.hex main.lst main.obj main.cof main.list main.map main.eep.hex main.elf *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s + +usbdrv/usbdrv.o: + $(COMPILE) $(SERIAL) -c usbdrv/usbdrv.c -o $@ + +.c.o: + $(COMPILE) -c $< -o $@ + +.S.o: + $(COMPILE) -x assembler-with-cpp -c $< -o $@ + +.c.s: + $(COMPILE) -S $< -o $@ + +cpp: + $(COMPILE) -E main.c diff --git a/classic/fw/main.c b/classic/fw/main.c new file mode 100644 index 0000000..388f293 --- /dev/null +++ b/classic/fw/main.c @@ -0,0 +1,458 @@ +#include +#include +#include +#include /* for sei() */ +#include /* for _delay_ms() */ +#include + +#include /* required by usbdrv.h */ +#include "usbdrv.h" + +#define BIT_KEY 2 +#define BIT_LED 1 + +#define EEPROM_OSC 0 +#define EEPROM_DM 1 +#define EEPROM_DR 2 +#define EEPROM_KK 3 +#define EEPROM_MM 4 + +#define MOUSE_B 1 +#define MOUSE_X 2 +#define MOUSE_Y 3 +#define MOUSE_W 4 + +#define DS_IDLE 10 +#define DS_ACTIVE 20 + +#define DR_FAST 1 +#define DR_NORMAL 8 +#define DR_SLOW 16 +#define DR_EXSLOW 32 + +#define DM_MOUSE 10 +#define DM_KEYBOARD 20 + +#define MM_CIRCLE 10 +#define MM_RANDOM 20 + +#define LED_MAX 9 + +#define FR_DS 1 +#define FR_DM 2 +#define FR_DR 3 +#define FR_KK 4 +#define FR_MM 5 + +#define KK_0 0x27 +#define KK_CAPS 0x39 +#define KK_DEFAULT KK_0 + +/* ------------------------------------------------------------------------- */ +/* ----------------------------- USB interface ----------------------------- */ +/* ------------------------------------------------------------------------- */ + +PROGMEM const char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { + +// Mouse +// ------------------------------------------------------------------------- + 0x05, 0x01,// Usage Page (Generic Desktop), | + 0x09, 0x02,// Usage (Mouse), | + 0xA1, 0x01,// Collection (Application), | + 0x09, 0x01,// Usage (Pointer) | + 0xA1, 0x00,// Collection (Physical), | +// ------------------------------------------------------------------------- + 0x85, 0x01,// Report Id (1). | +// ------------------------------------------------------------------------- + 0x05, 0x09,// Usage Page (Buttons), | + 0x19, 0x01,// Usage Minimum (01), | + 0x29, 0x03,// Usage Maximun (03), | + 0x15, 0x00,// Logical Minimum (0), | + 0x25, 0x01,// Logical Maximum (1), | + 0x95, 0x03,// Report Count (3), | + 0x75, 0x01,// Report Size (1), | + 0x81, 0x02,// Input (Data, Var, Abs), | + 0x95, 0x01,// Report Count (1), | + 0x75, 0x05,// Report Size (5), | + 0x81, 0x03,// Input (Constant), ;5 bit padding | +// ------------------------------------------------------------------------- + 0x05, 0x01,// Usage Page (Generic Desktop), | + 0x09, 0x30,// Usage (X), | + 0x09, 0x31,// Usage (Y), | + 0x09, 0x38,// Usage (Wheel), | + 0x15, 0x81,// Logical Minimum (-127), | + 0x25, 0x7F,// Logical Maximum (127), | + 0x75, 0x08,// Report Size (8), | + 0x95, 0x03,// Report Count (3), | + 0x81, 0x06,// Input (Data, Var, Rel) | +// ------------------------------------------------------------------------- + 0xC0,// End Collection, | +// ------------------------------------------------------------------------- +// Configuration interface +// ------------------------------------------------------------------------- + 0x09, 0x00,// USAGE (Vendor Usage 1) | +// ------------------------------------------------------------------------- + 0x85, 0x03,// Report Id (3) | +// ------------------------------------------------------------------------- + 0x15, 0x00,// LOGICAL_MINIMUM (0) | + 0x26, 0xff, 0x00,// LOGICAL_MAXIMUM (255) | + 0x75, 0x08,// REPORT_SIZE (8) | + 0x95, 0x05,// REPORT_COUNT (5) | + 0xb1, 0x02,// FEATURE (Data,Var,Abs) | + 0xC0,// End Collection | +// ------------------------------------------------------------------------- + +// Keyboard +// ------------------------------------------------------------------------- + 0x05, 0x01,// USAGE_PAGE (Generic Desktop) | + 0x09, 0x06,// USAGE (Keyboard) | + 0xa1, 0x01,// COLLECTION (Application) | +// ------------------------------------------------------------------------- + 0x85, 0x02,// Report Id (2) | +// ------------------------------------------------------------------------- + 0x05, 0x07,// USAGE_PAGE (Keyboard) | + 0x19, 0xe0,// USAGE_MINIMUM (Keyboard LeftControl) | + 0x29, 0xe7,// USAGE_MAXIMUM (Keyboard Right GUI) | + 0x15, 0x00,// LOGICAL_MINIMUM (0) | + 0x25, 0x01,// LOGICAL_MAXIMUM (1) | + 0x75, 0x01,// REPORT_SIZE (1) | + 0x95, 0x08,// REPORT_COUNT (8) | + 0x81, 0x02,// INPUT (Data,Var,Abs) | +// ------------------------------------------------------------------------- + 0x95, 0x05,// REPORT_COUNT (5) | + 0x75, 0x01,// REPORT_SIZE (1) | + 0x05, 0x08,// USAGE_PAGE (LEDs) | + 0x19, 0x01,// USAGE_MINIMUM (Num Lock) | + 0x29, 0x05,// USAGE_MAXIMUM (Kana) | + 0x91, 0x02,// OUTPUT (Data,Var,Abs) | +// ------------------------------------------------------------------------- + 0x95, 0x01,// REPORT_COUNT (1) | + 0x75, 0x03,// REPORT_SIZE (3) | + 0x91, 0x03,// OUTPUT (Cnst,Var,Abs) | +// ------------------------------------------------------------------------- + 0x95, 0x06,// REPORT_COUNT (6) | + 0x75, 0x08,// REPORT_SIZE (8) | + 0x15, 0x00,// LOGICAL_MINIMUM (0) | + 0x25, 0x65,// LOGICAL_MAXIMUM (101) | + 0x05, 0x07,// USAGE_PAGE (Keyboard) | + 0x19, 0x00,// USAGE_MINIMUM (Reserved (no event indicated)) | + 0x29, 0x65,// USAGE_MAXIMUM (Keyboard Application) | + 0x81, 0x00,// INPUT (Data,Ary,Abs) | +// ------------------------------------------------------------------------- + 0xc0,// END_COLLECTION | +// ------------------------------------------------------------------------- +}; + + + +static uchar currentAddress; +static uchar bytesRemaining; + +static uchar mouseReportBuffer[5] = { 1, 0,0,0,0 }; +static uchar keyboardReportBuffer[8] = { 2, 0,0,0,0,0,0,0 }; +static uchar featureReportBuffer[6] = { 3, 0,0,0,0,0 }; + +static int sinus = 7 << 6, cosinus = 0; +static uchar idleRate; /* repeat rate for keyboards, never used for mice */ + +static uchar deviceStatus = DS_IDLE; +static uchar deviceRate = DR_NORMAL; +static uchar deviceMode = DM_MOUSE; +static uchar mouseMode = MM_RANDOM; +static uchar keyboardKey = KK_DEFAULT; +static uchar runToken = 1; + +static void toggleDeviceStatus() +{ + if(deviceStatus==DS_ACTIVE){ + + deviceStatus = DS_IDLE; + PORTB |= 1 << BIT_LED; /* LED on */ + }else{ + + deviceStatus = DS_ACTIVE; + PORTB &= ~(1 << BIT_LED); /* LED off */ + } +} + + +static void keyPoll(void) +{ +static uchar keyMirror; +uchar key; + + key = PINB & (1 << BIT_KEY); + if(keyMirror != key){ /* status changed */ + keyMirror = key; + if(!key) toggleDeviceStatus(); + } +} + +static void timerPoll(void) +{ +static int timerCnt; +static int timerCnt2; +static int timerLed; + + if(TIFR & (1 << TOV1)){ + TIFR = (1 << TOV1); /* clear overflow */ + keyPoll(); + if(++timerCnt >= deviceRate){ + timerCnt = 0; + if(++timerCnt2 >= deviceRate){ + timerCnt2 = 0; + runToken = 1; + } + } + if(++timerLed >= LED_MAX){ + timerLed = 0; + if(deviceStatus==DS_ACTIVE && (PINB & (1 << BIT_KEY)) ){ + PORTB &= ~(1 << BIT_LED); /* LED off */ + } + } + } +} + +/* The following function advances sin/cos by a fixed angle + * and stores the difference to the previous coordinates in the report + * descriptor. + * The algorithm is the simulation of a second order differential equation. + */ +static void advanceCircleByFixedAngle(void) +{ +char d; + +#define DIVIDE_BY_64(val) (val + (val > 0 ? 32 : -32)) >> 6 /* rounding divide */ + + mouseReportBuffer[MOUSE_X] = d = DIVIDE_BY_64(cosinus); + sinus += d; + mouseReportBuffer[MOUSE_Y] = d = DIVIDE_BY_64(sinus); + cosinus -= d; +} + +static void randomMouse(void) +{ + + mouseReportBuffer[MOUSE_X] = ((rand() / (RAND_MAX / 254 + 1))-128)/8; + mouseReportBuffer[MOUSE_Y] = ((rand() / (RAND_MAX / 254 + 1))-128)/8; +} + +/* ------------------------------------------------------------------------- */ + +/* usbFunctionWrite() is called when the host sends a chunk of data to the + * device. For more information see the documentation in usbdrv/usbdrv.h. + */ +uchar usbFunctionWrite(uchar *data, uchar len) { + + uchar i; + + if(bytesRemaining == 0) + return 1; /* end of transfer */ + if(len > bytesRemaining) + len = bytesRemaining; + + for(i = 0; i < len; i++) { + + if(currentAddress==FR_DS && data[i]!=0xff && data[i]!=deviceStatus) toggleDeviceStatus(); + else if(currentAddress==FR_DM && data[i]!=0xff && data[i]!=deviceMode && (data[i]==DM_MOUSE||data[i]==DM_KEYBOARD) ) { + eeprom_write_byte(EEPROM_DM, data[i]); + deviceMode=data[i]; + } else if(currentAddress==FR_DR && data[i]!=0xff && data[i]!=deviceRate) { + eeprom_write_byte(EEPROM_DR, data[i]); + deviceRate=data[i]; + } else if(currentAddress==FR_KK && data[i]!=0xff && data[i]!=keyboardKey) { + eeprom_write_byte(EEPROM_KK, data[i]); + keyboardKey=data[i]; + } else if(currentAddress==FR_MM && data[i]!=0xff && data[i]!=mouseMode && (data[i]==MM_CIRCLE||data[i]==MM_RANDOM) ) { + eeprom_write_byte(EEPROM_MM, data[i]); + mouseMode=data[i]; + } + + currentAddress++; + } + + currentAddress += len; + bytesRemaining -= len; + return bytesRemaining == 0; /* return 1 if this was the last chunk */ +} + +usbMsgLen_t usbFunctionSetup(uchar data[8]) +{ +usbRequest_t *rq = (void *)data; + + /* The following requests are never used. But since they are required by + * the specification, we implement them in this example. + */ + if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */ + if(rq->bRequest == USBRQ_HID_GET_REPORT){ + /* wValue: ReportType (highbyte), ReportID (lowbyte) */ + if (rq->wValue.bytes[0] == 1) { + usbMsgPtr = (void *)&mouseReportBuffer; + return sizeof(mouseReportBuffer); + } else if (rq->wValue.bytes[0] == 2) { + usbMsgPtr = (void *)&keyboardReportBuffer; + return sizeof(keyboardReportBuffer); + } else if (rq->wValue.bytes[0] == 3) { + featureReportBuffer[FR_DS] = deviceStatus; + featureReportBuffer[FR_DM] = deviceMode; + featureReportBuffer[FR_DR] = deviceRate; + featureReportBuffer[FR_KK] = keyboardKey; + featureReportBuffer[FR_MM] = mouseMode; + usbMsgPtr = (void *)&featureReportBuffer; + return sizeof(featureReportBuffer); + } + return 0; + } else if(rq->bRequest == USBRQ_HID_SET_REPORT){ + if (rq->wValue.bytes[0] == 3) { + bytesRemaining = rq->wLength.word; + currentAddress = 0; + return USB_NO_MSG; + } + } else if(rq->bRequest == USBRQ_HID_GET_IDLE){ + usbMsgPtr = &idleRate; + return 1; + }else if(rq->bRequest == USBRQ_HID_SET_IDLE){ + idleRate = rq->wValue.bytes[1]; + } + }else{ + /* no vendor specific requests implemented */ + } + return 0; /* default for not implemented requests: return no data back to host */ +} + +/* ------------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------------- */ +/* ------------------------ Oscillator Calibration ------------------------- */ +/* ------------------------------------------------------------------------- */ + +/* Calibrate the RC oscillator to 8.25 MHz. The core clock of 16.5 MHz is + * derived from the 66 MHz peripheral clock by dividing. Our timing reference + * is the Start Of Frame signal (a single SE0 bit) available immediately after + * a USB RESET. We first do a binary search for the OSCCAL value and then + * optimize this value with a neighboorhod search. + * This algorithm may also be used to calibrate the RC oscillator directly to + * 12 MHz (no PLL involved, can therefore be used on almost ALL AVRs), but this + * is wide outside the spec for the OSCCAL value and the required precision for + * the 12 MHz clock! Use the RC oscillator calibrated to 12 MHz for + * experimental purposes only! + */ +static void calibrateOscillator(void) +{ +uchar step = 128; +uchar trialValue = 0, optimumValue; +int x, optimumDev, targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5); + + /* do a binary search: */ + do{ + OSCCAL = trialValue + step; + x = usbMeasureFrameLength(); /* proportional to current real frequency */ + if(x < targetValue) /* frequency still too low */ + trialValue += step; + step >>= 1; + }while(step > 0); + /* We have a precision of +/- 1 for optimum OSCCAL here */ + /* now do a neighborhood search for optimum value */ + optimumValue = trialValue; + optimumDev = x; /* this is certainly far away from optimum */ + for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){ + x = usbMeasureFrameLength() - targetValue; + if(x < 0) + x = -x; + if(x < optimumDev){ + optimumDev = x; + optimumValue = OSCCAL; + } + } + OSCCAL = optimumValue; +} +/* +Note: This calibration algorithm may try OSCCAL values of up to 192 even if +the optimum value is far below 192. It may therefore exceed the allowed clock +frequency of the CPU in low voltage designs! +You may replace this search algorithm with any other algorithm you like if +you have additional constraints such as a maximum CPU clock. +For version 5.x RC oscillators (those with a split range of 2x128 steps, e.g. +ATTiny25, ATTiny45, ATTiny85), it may be useful to search for the optimum in +both regions. +*/ + +void hadUsbReset(void) +{ + calibrateOscillator(); + eeprom_write_byte(EEPROM_OSC, OSCCAL); /* store the calibrated value in EEPROM */ +} + +static void timerInit(void) +{ + TCCR1 = 0x0b; /* select clock: 16.5M/1k -> overflow rate = 16.5M/256k = 62.94 Hz */ +} + +int __attribute__((noreturn)) main(void) +{ +uchar i; +uchar calibrationValue; + + PORTB |= 1 << BIT_KEY; /* pull-up on key input */ + DDRB |= 1 << BIT_LED; /* output for LED */ + + calibrationValue = eeprom_read_byte(EEPROM_OSC); /* calibration value from last time */ + if(calibrationValue != 0xff){ + OSCCAL = calibrationValue; + } + + i = eeprom_read_byte(EEPROM_DM); + if(i != 0xff) deviceMode = i; + + i = eeprom_read_byte(EEPROM_DR); + if(i != 0xff) deviceRate = i; + else deviceRate = DR_EXSLOW; + + i = eeprom_read_byte(EEPROM_KK); + if(i != 0xff) keyboardKey = i; + + i = eeprom_read_byte(EEPROM_MM); + if(i != 0xff) mouseMode = i; + + usbInit(); + usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */ + for(i=0;i<20;i++){ /* 300 ms disconnect */ + _delay_ms(15); + } + usbDeviceConnect(); + + wdt_enable(WDTO_1S); + + timerInit(); + usbInit(); + sei(); + + PORTB |= 1 << BIT_LED; /* LED on */ + + for(;;){ /* main event loop */ + wdt_reset(); + usbPoll(); + if(usbInterruptIsReady() && keyboardReportBuffer[2]!=0) { + + keyboardReportBuffer[2] = 0; + usbSetInterrupt((void *)&keyboardReportBuffer, sizeof(keyboardReportBuffer)); + } else if(usbInterruptIsReady() && (deviceStatus==DS_ACTIVE) && runToken){ + + if(deviceMode==DM_MOUSE) { + + if(mouseMode==MM_CIRCLE) advanceCircleByFixedAngle(); + else randomMouse(); + PORTB |= 1 << BIT_LED; /* LED on */ + usbSetInterrupt((void *)&mouseReportBuffer, sizeof(mouseReportBuffer)); + } else { + + keyboardReportBuffer[2] = keyboardKey; + usbSetInterrupt((void *)&keyboardReportBuffer, sizeof(keyboardReportBuffer)); + } + runToken = 0; + } + timerPoll(); + } +} + +/* ------------------------------------------------------------------------- */ diff --git a/classic/fw/usbconfig.h b/classic/fw/usbconfig.h new file mode 100644 index 0000000..ce1cb6f --- /dev/null +++ b/classic/fw/usbconfig.h @@ -0,0 +1,394 @@ +/* Name: usbconfig.h + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2005-04-01 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +#ifndef __usbconfig_h_included__ +#define __usbconfig_h_included__ + +/* +General Description: +This file is an example configuration (with inline documentation) for the USB +driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is +also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may +wire the lines to any other port, as long as D+ is also wired to INT0 (or any +other hardware interrupt, as long as it is the highest level interrupt, see +section at the end of this file). +*/ + +/* ---------------------------- Hardware Config ---------------------------- */ + +#define USB_CFG_IOPORTNAME B +/* This is the port where the USB bus is connected. When you configure it to + * "B", the registers PORTB, PINB and DDRB will be used. + */ +#define USB_CFG_DMINUS_BIT 3 +/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. + * This may be any bit in the port. + */ +#define USB_CFG_DPLUS_BIT 4 +/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. + * This may be any bit in the port. Please note that D+ must also be connected + * to interrupt pin INT0! [You can also use other interrupts, see section + * "Optional MCU Description" below, or you can connect D- to the interrupt, as + * it is required if you use the USB_COUNT_SOF feature. If you use D- for the + * interrupt, the USB interrupt will also be triggered at Start-Of-Frame + * markers every millisecond.] + */ +#define USB_CFG_CLOCK_KHZ (F_CPU/1000) +/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000, + * 16500, 18000 and 20000. The 12.8 MHz and 16.5 MHz versions of the code + * require no crystal, they tolerate +/- 1% deviation from the nominal + * frequency. All other rates require a precision of 2000 ppm and thus a + * crystal! + * Since F_CPU should be defined to your actual clock rate anyway, you should + * not need to modify this setting. + */ +#define USB_CFG_CHECK_CRC 0 +/* Define this to 1 if you want that the driver checks integrity of incoming + * data packets (CRC checks). CRC checks cost quite a bit of code size and are + * currently only available for 18 MHz crystal clock. You must choose + * USB_CFG_CLOCK_KHZ = 18000 if you enable this option. + */ + +/* ----------------------- Optional Hardware Config ------------------------ */ + +/* #define USB_CFG_PULLUP_IOPORTNAME D */ +/* If you connect the 1.5k pullup resistor from D- to a port pin instead of + * V+, you can connect and disconnect the device from firmware by calling + * the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). + * This constant defines the port on which the pullup resistor is connected. + */ +/* #define USB_CFG_PULLUP_BIT 4 */ +/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined + * above) where the 1.5k pullup resistor is connected. See description + * above for details. + */ + +/* --------------------------- Functional Range ---------------------------- */ + +#define USB_CFG_HAVE_INTRIN_ENDPOINT 1 +/* Define this to 1 if you want to compile a version with two endpoints: The + * default control endpoint 0 and an interrupt-in endpoint (any other endpoint + * number). + */ +#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 +/* Define this to 1 if you want to compile a version with three endpoints: The + * default control endpoint 0, an interrupt-in endpoint 3 (or the number + * configured below) and a catch-all default interrupt-in endpoint as above. + * You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature. + */ +#define USB_CFG_EP3_NUMBER 3 +/* If the so-called endpoint 3 is used, it can now be configured to any other + * endpoint number (except 0) with this macro. Default if undefined is 3. + */ +/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */ +/* The above macro defines the startup condition for data toggling on the + * interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1. + * Since the token is toggled BEFORE sending any data, the first packet is + * sent with the oposite value of this configuration! + */ +#define USB_CFG_IMPLEMENT_HALT 0 +/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature + * for endpoint 1 (interrupt endpoint). Although you may not need this feature, + * it is required by the standard. We have made it a config option because it + * bloats the code considerably. + */ +#define USB_CFG_SUPPRESS_INTR_CODE 0 +/* Define this to 1 if you want to declare interrupt-in endpoints, but don't + * want to send any data over them. If this macro is defined to 1, functions + * usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if + * you need the interrupt-in endpoints in order to comply to an interface + * (e.g. HID), but never want to send any data. This option saves a couple + * of bytes in flash memory and the transmit buffers in RAM. + */ +#define USB_CFG_INTR_POLL_INTERVAL 50 +/* If you compile a version with endpoint 1 (interrupt-in), this is the poll + * interval. The value is in milliseconds and must not be less than 10 ms for + * low speed devices. + */ +#define USB_CFG_IS_SELF_POWERED 0 +/* Define this to 1 if the device has its own power supply. Set it to 0 if the + * device is powered from the USB bus. + */ +#define USB_CFG_MAX_BUS_POWER 50 +/* Set this variable to the maximum USB bus power consumption of your device. + * The value is in milliamperes. [It will be divided by two since USB + * communicates power requirements in units of 2 mA.] + */ +#define USB_CFG_IMPLEMENT_FN_WRITE 1 +/* Set this to 1 if you want usbFunctionWrite() to be called for control-out + * transfers. Set it to 0 if you don't need it and want to save a couple of + * bytes. + */ +#define USB_CFG_IMPLEMENT_FN_READ 0 +/* Set this to 1 if you need to send control replies which are generated + * "on the fly" when usbFunctionRead() is called. If you only want to send + * data from a static buffer, set it to 0 and return the data from + * usbFunctionSetup(). This saves a couple of bytes. + */ +#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0 +/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints. + * You must implement the function usbFunctionWriteOut() which receives all + * interrupt/bulk data sent to any endpoint other than 0. The endpoint number + * can be found in 'usbRxToken'. + */ +#define USB_CFG_HAVE_FLOWCONTROL 0 +/* Define this to 1 if you want flowcontrol over USB data. See the definition + * of the macros usbDisableAllRequests() and usbEnableAllRequests() in + * usbdrv.h. + */ +#define USB_CFG_DRIVER_FLASH_PAGE 0 +/* If the device has more than 64 kBytes of flash, define this to the 64 k page + * where the driver's constants (descriptors) are located. Or in other words: + * Define this to 1 for boot loaders on the ATMega128. + */ +#define USB_CFG_LONG_TRANSFERS 0 +/* Define this to 1 if you want to send/receive blocks of more than 254 bytes + * in a single control-in or control-out transfer. Note that the capability + * for long transfers increases the driver size. + */ +/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */ +/* This macro is a hook if you want to do unconventional things. If it is + * defined, it's inserted at the beginning of received message processing. + * If you eat the received message and don't want default processing to + * proceed, do a return after doing your things. One possible application + * (besides debugging) is to flash a status LED on each packet. + */ +#define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} +/* This macro is a hook if you need to know when an USB RESET occurs. It has + * one parameter which distinguishes between the start of RESET state and its + * end. + */ +/* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */ +/* This macro (if defined) is executed when a USB SET_ADDRESS request was + * received. + */ +#define USB_COUNT_SOF 0 +/* define this macro to 1 if you need the global variable "usbSofCount" which + * counts SOF packets. This feature requires that the hardware interrupt is + * connected to D- instead of D+. + */ +/* #ifdef __ASSEMBLER__ + * macro myAssemblerMacro + * in YL, TCNT0 + * sts timer0Snapshot, YL + * endm + * #endif + * #define USB_SOF_HOOK myAssemblerMacro + * This macro (if defined) is executed in the assembler module when a + * Start Of Frame condition is detected. It is recommended to define it to + * the name of an assembler macro which is defined here as well so that more + * than one assembler instruction can be used. The macro may use the register + * YL and modify SREG. If it lasts longer than a couple of cycles, USB messages + * immediately after an SOF pulse may be lost and must be retried by the host. + * What can you do with this hook? Since the SOF signal occurs exactly every + * 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in + * designs running on the internal RC oscillator. + * Please note that Start Of Frame detection works only if D- is wired to the + * interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES! + */ +#define USB_CFG_CHECK_DATA_TOGGLING 0 +/* define this macro to 1 if you want to filter out duplicate data packets + * sent by the host. Duplicates occur only as a consequence of communication + * errors, when the host does not receive an ACK. Please note that you need to + * implement the filtering yourself in usbFunctionWriteOut() and + * usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable + * for each control- and out-endpoint to check for duplicate packets. + */ +#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 1 +/* define this macro to 1 if you want the function usbMeasureFrameLength() + * compiled in. This function can be used to calibrate the AVR's RC oscillator. + */ +#define USB_USE_FAST_CRC 0 +/* The assembler module has two implementations for the CRC algorithm. One is + * faster, the other is smaller. This CRC routine is only used for transmitted + * messages where timing is not critical. The faster routine needs 31 cycles + * per byte while the smaller one needs 61 to 69 cycles. The faster routine + * may be worth the 32 bytes bigger code size if you transmit lots of data and + * run the AVR close to its limit. + */ + +/* -------------------------- Device Description --------------------------- */ + +#define USB_CFG_VENDOR_ID 0xd0, 0x16 /* 0x16d0 */ +/* USB vendor ID for the device, low byte first. If you have registered your + * own Vendor ID, define it here. Otherwise you may use one of obdev's free + * shared VID/PID pairs. Be sure to read USB-IDs-for-free.txt for rules! + * *** IMPORTANT NOTE *** + * This template uses obdev's shared VID/PID pair for Vendor Class devices + * with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand + * the implications! + */ +#define USB_CFG_DEVICE_ID 0xFA, 0x09 /* 0x09FA - microjelly */ +/* This is the ID of the product, low byte first. It is interpreted in the + * scope of the vendor ID. If you have registered your own VID with usb.org + * or if you have licensed a PID from somebody else, define it here. Otherwise + * you may use one of obdev's free shared VID/PID pairs. See the file + * USB-IDs-for-free.txt for details! + * *** IMPORTANT NOTE *** + * This template uses obdev's shared VID/PID pair for Vendor Class devices + * with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand + * the implications! + */ +#define USB_CFG_DEVICE_VERSION 0x01, 0x01 +/* Version number of the device: Minor number first, then major number. + */ +#define USB_CFG_VENDOR_NAME 'm', 'i', 'c', 'r', 'o', 'j', 'e', 'l', 'l', 'y', '.', 'c', 'o', 'm' +#define USB_CFG_VENDOR_NAME_LEN 14 +/* These two values define the vendor name returned by the USB device. The name + * must be given as a list of characters under single quotes. The characters + * are interpreted as Unicode (UTF-16) entities. + * If you don't want a vendor name string, undefine these macros. + * ALWAYS define a vendor name containing your Internet domain name if you use + * obdev's free shared VID/PID pair. See the file USB-IDs-for-free.txt for + * details. + */ +#define USB_CFG_DEVICE_NAME 'B','E','E','O','N',' ','-',' ','C','L','A','S','S','I','C' +#define USB_CFG_DEVICE_NAME_LEN 15 +/* Same as above for the device name. If you don't want a device name, undefine + * the macros. See the file USB-IDs-for-free.txt before you assign a name if + * you use a shared VID/PID. + */ + +#ifndef USB_CFG_SERIAL_NUMBER +#define USB_CFG_SERIAL_NUMBER 'B','E','E','#','-','#','#','-','#','#','#','#','#','#','#' +#endif + +#define USB_CFG_SERIAL_NUMBER_LEN 15 +/* Same as above for the serial number. If you don't want a serial number, + * undefine the macros. + * It may be useful to provide the serial number through other means than at + * compile time. See the section about descriptor properties below for how + * to fine tune control over USB descriptors such as the string descriptor + * for the serial number. + */ +#define USB_CFG_DEVICE_CLASS 0 +#define USB_CFG_DEVICE_SUBCLASS 0 +/* See USB specification if you want to conform to an existing device class. + * Class 0xff is "vendor specific". + */ +#define USB_CFG_INTERFACE_CLASS 3 +#define USB_CFG_INTERFACE_SUBCLASS 0 +#define USB_CFG_INTERFACE_PROTOCOL 0 +/* See USB specification if you want to conform to an existing device class or + * protocol. The following classes must be set at interface level: + * HID class is 3, no subclass and protocol required (but may be useful!) + * CDC class is 2, use subclass 2 and protocol 1 for ACM + */ +#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 128 +/* Define this to the length of the HID report descriptor, if you implement + * an HID device. Otherwise don't define it or define it to 0. + * If you use this define, you must add a PROGMEM character array named + * "usbHidReportDescriptor" to your code which contains the report descriptor. + * Don't forget to keep the array and this define in sync! + */ + +/* #define USB_PUBLIC static */ +/* Use the define above if you #include usbdrv.c instead of linking against it. + * This technique saves a couple of bytes in flash memory. + */ + +/* ------------------- Fine Control over USB Descriptors ------------------- */ +/* If you don't want to use the driver's default USB descriptors, you can + * provide our own. These can be provided as (1) fixed length static data in + * flash memory, (2) fixed length static data in RAM or (3) dynamically at + * runtime in the function usbFunctionDescriptor(). See usbdrv.h for more + * information about this function. + * Descriptor handling is configured through the descriptor's properties. If + * no properties are defined or if they are 0, the default descriptor is used. + * Possible properties are: + * + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched + * at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is + * used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if + * you want RAM pointers. + * + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found + * in static memory is in RAM, not in flash memory. + * + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), + * the driver must know the descriptor's length. The descriptor itself is + * found at the address of a well known identifier (see below). + * List of static descriptor names (must be declared PROGMEM if in flash): + * char usbDescriptorDevice[]; + * char usbDescriptorConfiguration[]; + * char usbDescriptorHidReport[]; + * char usbDescriptorString0[]; + * int usbDescriptorStringVendor[]; + * int usbDescriptorStringDevice[]; + * int usbDescriptorStringSerialNumber[]; + * Other descriptors can't be provided statically, they must be provided + * dynamically at runtime. + * + * Descriptor properties are or-ed or added together, e.g.: + * #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) + * + * The following descriptors are defined: + * USB_CFG_DESCR_PROPS_DEVICE + * USB_CFG_DESCR_PROPS_CONFIGURATION + * USB_CFG_DESCR_PROPS_STRINGS + * USB_CFG_DESCR_PROPS_STRING_0 + * USB_CFG_DESCR_PROPS_STRING_VENDOR + * USB_CFG_DESCR_PROPS_STRING_PRODUCT + * USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER + * USB_CFG_DESCR_PROPS_HID + * USB_CFG_DESCR_PROPS_HID_REPORT + * USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) + * + * Note about string descriptors: String descriptors are not just strings, they + * are Unicode strings prefixed with a 2 byte header. Example: + * int serialNumberDescriptor[] = { + * USB_STRING_DESCRIPTOR_HEADER(6), + * 'S', 'e', 'r', 'i', 'a', 'l' + * }; + */ + +#define USB_CFG_DESCR_PROPS_DEVICE 0 +#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 +#define USB_CFG_DESCR_PROPS_STRINGS 0 +#define USB_CFG_DESCR_PROPS_STRING_0 0 +#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 +#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 +#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 +#define USB_CFG_DESCR_PROPS_HID 0 +#define USB_CFG_DESCR_PROPS_HID_REPORT 0 +#define USB_CFG_DESCR_PROPS_UNKNOWN 0 + + +#define usbMsgPtr_t unsigned short +/* If usbMsgPtr_t is not defined, it defaults to 'uchar *'. We define it to + * a scalar type here because gcc generates slightly shorter code for scalar + * arithmetics than for pointer arithmetics. Remove this define for backward + * type compatibility or define it to an 8 bit type if you use data in RAM only + * and all RAM is below 256 bytes (tiny memory model in IAR CC). + */ + +/* ----------------------- Optional MCU Description ------------------------ */ + +/* The following configurations have working defaults in usbdrv.h. You + * usually don't need to set them explicitly. Only if you want to run + * the driver on a device which is not yet supported or with a compiler + * which is not fully supported (such as IAR C) or if you use a differnt + * interrupt than INT0, you may have to define some of these. + */ +/* #define USB_INTR_CFG MCUCR */ +/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */ +/* #define USB_INTR_CFG_CLR 0 */ +/* #define USB_INTR_ENABLE GIMSK */ +/* #define USB_INTR_ENABLE_BIT INT0 */ +/* #define USB_INTR_PENDING GIFR */ +/* #define USB_INTR_PENDING_BIT INTF0 */ +/* #define USB_INTR_VECTOR INT0_vect */ + +#define USB_INTR_CFG PCMSK +#define USB_INTR_CFG_SET (1 << USB_CFG_DPLUS_BIT) +#define USB_INTR_CFG_CLR 0 +#define USB_INTR_ENABLE GIMSK +#define USB_INTR_ENABLE_BIT PCIE +#define USB_INTR_PENDING GIFR +#define USB_INTR_PENDING_BIT PCIF +#define USB_INTR_VECTOR PCINT0_vect + +#endif /* __usbconfig_h_included__ */ diff --git a/classic/fw/usbdrv/Changelog.txt b/classic/fw/usbdrv/Changelog.txt new file mode 100644 index 0000000..79b5215 --- /dev/null +++ b/classic/fw/usbdrv/Changelog.txt @@ -0,0 +1,329 @@ +This file documents changes in the firmware-only USB driver for atmel's AVR +microcontrollers. New entries are always appended to the end of the file. +Scroll down to the bottom to see the most recent changes. + +2005-04-01: + - Implemented endpoint 1 as interrupt-in endpoint. + - Moved all configuration options to usbconfig.h which is not part of the + driver. + - Changed interface for usbVendorSetup(). + - Fixed compatibility with ATMega8 device. + - Various minor optimizations. + +2005-04-11: + - Changed interface to application: Use usbFunctionSetup(), usbFunctionRead() + and usbFunctionWrite() now. Added configuration options to choose which + of these functions to compile in. + - Assembler module delivers receive data non-inverted now. + - Made register and bit names compatible with more AVR devices. + +2005-05-03: + - Allow address of usbRxBuf on any memory page as long as the buffer does + not cross 256 byte page boundaries. + - Better device compatibility: works with Mega88 now. + - Code optimization in debugging module. + - Documentation updates. + +2006-01-02: + - Added (free) default Vendor- and Product-IDs bought from voti.nl. + - Added USBID-License.txt file which defines the rules for using the free + shared VID/PID pair. + - Added Readme.txt to the usbdrv directory which clarifies administrative + issues. + +2006-01-25: + - Added "configured state" to become more standards compliant. + - Added "HALT" state for interrupt endpoint. + - Driver passes the "USB Command Verifier" test from usb.org now. + - Made "serial number" a configuration option. + - Minor optimizations, we now recommend compiler option "-Os" for best + results. + - Added a version number to usbdrv.h + +2006-02-03: + - New configuration variable USB_BUFFER_SECTION for the memory section where + the USB rx buffer will go. This defaults to ".bss" if not defined. Since + this buffer MUST NOT cross 256 byte pages (not even touch a page at the + end), the user may want to pass a linker option similar to + "-Wl,--section-start=.mybuffer=0x800060". + - Provide structure for usbRequest_t. + - New defines for USB constants. + - Prepared for HID implementations. + - Increased data size limit for interrupt transfers to 8 bytes. + - New macro usbInterruptIsReady() to query interrupt buffer state. + +2006-02-18: + - Ensure that the data token which is sent as an ack to an OUT transfer is + always zero sized. This fixes a bug where the host reports an error after + sending an out transfer to the device, although all data arrived at the + device. + - Updated docs in usbdrv.h to reflect changed API in usbFunctionWrite(). + +* Release 2006-02-20 + + - Give a compiler warning when compiling with debugging turned on. + - Added Oleg Semyonov's changes for IAR-cc compatibility. + - Added new (optional) functions usbDeviceConnect() and usbDeviceDisconnect() + (also thanks to Oleg!). + - Rearranged tests in usbPoll() to save a couple of instructions in the most + likely case that no actions are pending. + - We need a delay between the SET ADDRESS request until the new address + becomes active. This delay was handled in usbPoll() until now. Since the + spec says that the delay must not exceed 2ms, previous versions required + aggressive polling during the enumeration phase. We have now moved the + handling of the delay into the interrupt routine. + - We must not reply with NAK to a SETUP transaction. We can only achieve this + by making sure that the rx buffer is empty when SETUP tokens are expected. + We therefore don't pass zero sized data packets from the status phase of + a transfer to usbPoll(). This change MAY cause troubles if you rely on + receiving a less than 8 bytes long packet in usbFunctionWrite() to + identify the end of a transfer. usbFunctionWrite() will NEVER be called + with a zero length. + +* Release 2006-03-14 + + - Improved IAR C support: tiny memory model, more devices + - Added template usbconfig.h file under the name usbconfig-prototype.h + +* Release 2006-03-26 + + - Added provision for one more interrupt-in endpoint (endpoint 3). + - Added provision for one interrupt-out endpoint (endpoint 1). + - Added flowcontrol macros for USB. + - Added provision for custom configuration descriptor. + - Allow ANY two port bits for D+ and D-. + - Merged (optional) receive endpoint number into global usbRxToken variable. + - Use USB_CFG_IOPORTNAME instead of USB_CFG_IOPORT. We now construct the + variable name from the single port letter instead of computing the address + of related ports from the output-port address. + +* Release 2006-06-26 + + - Updated documentation in usbdrv.h and usbconfig-prototype.h to reflect the + new features. + - Removed "#warning" directives because IAR does not understand them. Use + unused static variables instead to generate a warning. + - Do not include when compiling with IAR. + - Introduced USB_CFG_DESCR_PROPS_* in usbconfig.h to configure how each + USB descriptor should be handled. It is now possible to provide descriptor + data in Flash, RAM or dynamically at runtime. + - STALL is now a status in usbTxLen* instead of a message. We can now conform + to the spec and leave the stall status pending until it is cleared. + - Made usbTxPacketCnt1 and usbTxPacketCnt3 public. This allows the + application code to reset data toggling on interrupt pipes. + +* Release 2006-07-18 + + - Added an #if !defined __ASSEMBLER__ to the warning in usbdrv.h. This fixes + an assembler error. + - usbDeviceDisconnect() takes pull-up resistor to high impedance now. + +* Release 2007-02-01 + + - Merged in some code size improvements from usbtiny (thanks to Dick + Streefland for these optimizations!) + - Special alignment requirement for usbRxBuf not required any more. Thanks + again to Dick Streefland for this hint! + - Reverted to "#warning" instead of unused static variables -- new versions + of IAR CC should handle this directive. + - Changed Open Source license to GNU GPL v2 in order to make linking against + other free libraries easier. We no longer require publication of the + circuit diagrams, but we STRONGLY encourage it. If you improve the driver + itself, PLEASE grant us a royalty free license to your changes for our + commercial license. + +* Release 2007-03-29 + + - New configuration option "USB_PUBLIC" in usbconfig.h. + - Set USB version number to 1.10 instead of 1.01. + - Code used USB_CFG_DESCR_PROPS_STRING_DEVICE and + USB_CFG_DESCR_PROPS_STRING_PRODUCT inconsistently. Changed all occurrences + to USB_CFG_DESCR_PROPS_STRING_PRODUCT. + - New assembler module for 16.5 MHz RC oscillator clock with PLL in receiver + code. + - New assembler module for 16 MHz crystal. + - usbdrvasm.S contains common code only, clock-specific parts have been moved + to usbdrvasm12.S, usbdrvasm16.S and usbdrvasm165.S respectively. + +* Release 2007-06-25 + + - 16 MHz module: Do SE0 check in stuffed bits as well. + +* Release 2007-07-07 + + - Define hi8(x) for IAR compiler to limit result to 8 bits. This is necessary + for negative values. + - Added 15 MHz module contributed by V. Bosch. + - Interrupt vector name can now be configured. This is useful if somebody + wants to use a different hardware interrupt than INT0. + +* Release 2007-08-07 + + - Moved handleIn3 routine in usbdrvasm16.S so that relative jump range is + not exceeded. + - More config options: USB_RX_USER_HOOK(), USB_INITIAL_DATATOKEN, + USB_COUNT_SOF + - USB_INTR_PENDING can now be a memory address, not just I/O + +* Release 2007-09-19 + + - Split out common parts of assembler modules into separate include file + - Made endpoint numbers configurable so that given interface definitions + can be matched. See USB_CFG_EP3_NUMBER in usbconfig-prototype.h. + - Store endpoint number for interrupt/bulk-out so that usbFunctionWriteOut() + can handle any number of endpoints. + - Define usbDeviceConnect() and usbDeviceDisconnect() even if no + USB_CFG_PULLUP_IOPORTNAME is defined. Directly set D+ and D- to 0 in this + case. + +* Release 2007-12-01 + + - Optimize usbDeviceConnect() and usbDeviceDisconnect() for less code size + when USB_CFG_PULLUP_IOPORTNAME is not defined. + +* Release 2007-12-13 + + - Renamed all include-only assembler modules from *.S to *.inc so that + people don't add them to their project sources. + - Distribute leap bits in tx loop more evenly for 16 MHz module. + - Use "macro" and "endm" instead of ".macro" and ".endm" for IAR + - Avoid compiler warnings for constant expr range by casting some values in + USB descriptors. + +* Release 2008-01-21 + + - Fixed bug in 15 and 16 MHz module where the new address set with + SET_ADDRESS was already accepted at the next NAK or ACK we send, not at + the next data packet we send. This caused problems when the host polled + too fast. Thanks to Alexander Neumann for his help and patience debugging + this issue! + +* Release 2008-02-05 + + - Fixed bug in 16.5 MHz module where a register was used in the interrupt + handler before it was pushed. This bug was introduced with version + 2007-09-19 when common parts were moved to a separate file. + - Optimized CRC routine (thanks to Reimar Doeffinger). + +* Release 2008-02-16 + + - Removed outdated IAR compatibility stuff (code sections). + - Added hook macros for USB_RESET_HOOK() and USB_SET_ADDRESS_HOOK(). + - Added optional routine usbMeasureFrameLength() for calibration of the + internal RC oscillator. + +* Release 2008-02-28 + + - USB_INITIAL_DATATOKEN defaults to USBPID_DATA1 now, which means that we + start with sending USBPID_DATA0. + - Changed defaults in usbconfig-prototype.h + - Added free USB VID/PID pair for MIDI class devices + - Restructured AVR-USB as separate package, not part of PowerSwitch any more. + +* Release 2008-04-18 + + - Restructured usbdrv.c so that it is easier to read and understand. + - Better code optimization with gcc 4. + - If a second interrupt in endpoint is enabled, also add it to config + descriptor. + - Added config option for long transfers (above 254 bytes), see + USB_CFG_LONG_TRANSFERS in usbconfig.h. + - Added 20 MHz module contributed by Jeroen Benschop. + +* Release 2008-05-13 + + - Fixed bug in libs-host/hiddata.c function usbhidGetReport(): length + was not incremented, pointer to length was incremented instead. + - Added code to command line tool(s) which claims an interface. This code + is disabled by default, but may be necessary on newer Linux kernels. + - Added usbconfig.h option "USB_CFG_CHECK_DATA_TOGGLING". + - New header "usbportability.h" prepares ports to other development + environments. + - Long transfers (above 254 bytes) did not work when usbFunctionRead() was + used to supply the data. Fixed this bug. [Thanks to Alexander Neumann!] + - In hiddata.c (example code for sending/receiving data over HID), use + USB_RECIP_DEVICE instead of USB_RECIP_INTERFACE for control transfers so + that we need not claim the interface. + - in usbPoll() loop 20 times polling for RESET state instead of 10 times. + This accounts for the higher clock rates we now support. + - Added a module for 12.8 MHz RC oscillator with PLL in receiver loop. + - Added hook to SOF code so that oscillator can be tuned to USB frame clock. + - Added timeout to waitForJ loop. Helps preventing unexpected hangs. + - Added example code for oscillator tuning to libs-device (thanks to + Henrik Haftmann for the idea to this routine). + - Implemented option USB_CFG_SUPPRESS_INTR_CODE. + +* Release 2008-10-22 + + - Fixed libs-device/osctune.h: OSCCAL is memory address on ATMega88 and + similar, not offset of 0x20 needs to be added. + - Allow distribution under GPLv3 for those who have to link against other + code distributed under GPLv3. + +* Release 2008-11-26 + + - Removed libusb-win32 dependency for hid-data example in Makefile.windows. + It was never required and confused many people. + - Added extern uchar usbRxToken to usbdrv.h. + - Integrated a module with CRC checks at 18 MHz by Lukas Schrittwieser. + +* Release 2009-03-23 + + - Hid-mouse example used settings from hid-data example, fixed that. + - Renamed project to V-USB due to a trademark issue with Atmel(r). + - Changed CommercialLicense.txt and USBID-License.txt to make the + background of USB ID registration clearer. + +* Release 2009-04-15 + + - Changed CommercialLicense.txt to reflect the new range of PIDs from + Jason Kotzin. + - Removed USBID-License.txt in favor of USB-IDs-for-free.txt and + USB-ID-FAQ.txt + - Fixed a bug in the 12.8 MHz module: End Of Packet decection was made in + the center between bit 0 and 1 of each byte. This is where the data lines + are expected to change and the sampled data may therefore be nonsense. + We therefore check EOP ONLY if bits 0 AND 1 have both been read as 0 on D-. + - Fixed a bitstuffing problem in the 16 MHz module: If bit 6 was stuffed, + the unstuffing code in the receiver routine was 1 cycle too long. If + multiple bytes had the unstuffing in bit 6, the error summed up until the + receiver was out of sync. + - Included option for faster CRC routine. + Thanks to Slawomir Fras (BoskiDialer) for this code! + - Updated bits in Configuration Descriptor's bmAttributes according to + USB 1.1 (in particular bit 7, it is a must-be-set bit now). + +* Release 2009-08-22 + + - Moved first DBG1() after odDebugInit() in all examples. + - Use vector INT0_vect instead of SIG_INTERRUPT0 if defined. This makes + V-USB compatible with the new "p" suffix devices (e.g. ATMega328p). + - USB_CFG_CLOCK_KHZ setting is now required in usbconfig.h (no default any + more). + - New option USB_CFG_DRIVER_FLASH_PAGE allows boot loaders on devices with + more than 64 kB flash. + - Built-in configuration descriptor allows custom definition for second + endpoint now. + +* Release 2010-07-15 + + - Fixed bug in usbDriverSetup() which prevented descriptor sizes above 255 + bytes. + - Avoid a compiler warning for unused parameter in usbHandleResetHook() when + compiler option -Wextra is enabled. + - Fixed wrong hex value for some IDs in USB-IDs-for-free.txt. + - Keep a define for USBATTR_BUSPOWER, although the flag does not exist + in USB 1.1 any more. Set it to 0. This is for backward compatibility. + +* Release 2012-01-09 + + - Define a separate (defined) type for usbMsgPtr so that projects using a + tiny memory model can define it to an 8 bit type in usbconfig.h. This + change also saves a couple of bytes when using a scalar 16 bit type. + - Inserted "const" keyword for all PROGMEM declarations because new GCC + requires it. + - Fixed problem with dependence of usbportability.h on usbconfig.h. This + problem occurred with IAR CC only. + - Prepared repository for github.com. + +* Release 2012-12-06 \ No newline at end of file diff --git a/classic/fw/usbdrv/CommercialLicense.txt b/classic/fw/usbdrv/CommercialLicense.txt new file mode 100644 index 0000000..de1a2b0 --- /dev/null +++ b/classic/fw/usbdrv/CommercialLicense.txt @@ -0,0 +1,166 @@ +V-USB Driver Software License Agreement +Version 2012-07-09 + +THIS LICENSE AGREEMENT GRANTS YOU CERTAIN RIGHTS IN A SOFTWARE. YOU CAN +ENTER INTO THIS AGREEMENT AND ACQUIRE THE RIGHTS OUTLINED BELOW BY PAYING +THE AMOUNT ACCORDING TO SECTION 4 ("PAYMENT") TO OBJECTIVE DEVELOPMENT. + + +1 DEFINITIONS + +1.1 "OBJECTIVE DEVELOPMENT" shall mean OBJECTIVE DEVELOPMENT Software GmbH, +Grosse Schiffgasse 1A/7, 1020 Wien, AUSTRIA. + +1.2 "You" shall mean the Licensee. + +1.3 "V-USB" shall mean all files included in the package distributed under +the name "vusb" by OBJECTIVE DEVELOPMENT (http://www.obdev.at/vusb/) +unless otherwise noted. This includes the firmware-only USB device +implementation for Atmel AVR microcontrollers, some simple device examples +and host side software examples and libraries. + + +2 LICENSE GRANTS + +2.1 Source Code. OBJECTIVE DEVELOPMENT shall furnish you with the source +code of V-USB. + +2.2 Distribution and Use. OBJECTIVE DEVELOPMENT grants you the +non-exclusive right to use, copy and distribute V-USB with your hardware +product(s), restricted by the limitations in section 3 below. + +2.3 Modifications. OBJECTIVE DEVELOPMENT grants you the right to modify +the source code and your copy of V-USB according to your needs. + +2.4 USB IDs. OBJECTIVE DEVELOPMENT furnishes you with one or two USB +Product ID(s), sent to you in e-mail. These Product IDs are reserved +exclusively for you. OBJECTIVE DEVELOPMENT has obtained USB Product ID +ranges under the Vendor ID 5824 from Wouter van Ooijen (Van Ooijen +Technische Informatica, www.voti.nl) and under the Vendor ID 8352 from +Jason Kotzin (now flirc.tv, Inc.). Both owners of the Vendor IDs have +obtained these IDs from the USB Implementers Forum, Inc. (www.usb.org). +OBJECTIVE DEVELOPMENT disclaims all liability which might arise from the +assignment of USB IDs. + +2.5 USB Certification. Although not part of this agreement, we want to make +it clear that you cannot become USB certified when you use V-USB or a USB +Product ID assigned by OBJECTIVE DEVELOPMENT. AVR microcontrollers don't +meet the electrical specifications required by the USB specification and +the USB Implementers Forum certifies only members who bought a Vendor ID of +their own. + + +3 LICENSE RESTRICTIONS + +3.1 Number of Units. Only one of the following three definitions is +applicable. Which one is determined by the amount you pay to OBJECTIVE +DEVELOPMENT, see section 4 ("Payment") below. + +Hobby License: You may use V-USB according to section 2 above in no more +than 5 hardware units. These units must not be sold for profit. + +Entry Level License: You may use V-USB according to section 2 above in no +more than 150 hardware units. + +Professional License: You may use V-USB according to section 2 above in +any number of hardware units, except for large scale production ("unlimited +fair use"). Quantities below 10,000 units are not considered large scale +production. If your reach quantities which are obviously large scale +production, you must pay a license fee of 0.10 EUR per unit for all units +above 10,000. + +3.2 Rental. You may not rent, lease, or lend V-USB or otherwise encumber +any copy of V-USB, or any of the rights granted herein. + +3.3 Transfer. You may not transfer your rights under this Agreement to +another party without OBJECTIVE DEVELOPMENT's prior written consent. If +such consent is obtained, you may permanently transfer this License to +another party. The recipient of such transfer must agree to all terms and +conditions of this Agreement. + +3.4 Reservation of Rights. OBJECTIVE DEVELOPMENT retains all rights not +expressly granted. + +3.5 Non-Exclusive Rights. Your license rights under this Agreement are +non-exclusive. + +3.6 Third Party Rights. This Agreement cannot grant you rights controlled +by third parties. In particular, you are not allowed to use the USB logo or +other trademarks owned by the USB Implementers Forum, Inc. without their +consent. Since such consent depends on USB certification, it should be +noted that V-USB will not pass certification because it does not +implement checksum verification and the microcontroller ports do not meet +the electrical specifications. + + +4 PAYMENT + +The payment amount depends on the variation of this agreement (according to +section 3.1) into which you want to enter. Concrete prices are listed on +OBJECTIVE DEVELOPMENT's web site, usually at +http://www.obdev.at/vusb/license.html. You agree to pay the amount listed +there to OBJECTIVE DEVELOPMENT or OBJECTIVE DEVELOPMENT's payment processor +or reseller. + + +5 COPYRIGHT AND OWNERSHIP + +V-USB is protected by copyright laws and international copyright +treaties, as well as other intellectual property laws and treaties. V-USB +is licensed, not sold. + + +6 TERM AND TERMINATION + +6.1 Term. This Agreement shall continue indefinitely. However, OBJECTIVE +DEVELOPMENT may terminate this Agreement and revoke the granted license and +USB-IDs if you fail to comply with any of its terms and conditions. + +6.2 Survival of Terms. All provisions regarding secrecy, confidentiality +and limitation of liability shall survive termination of this agreement. + + +7 DISCLAIMER OF WARRANTY AND LIABILITY + +LIMITED WARRANTY. V-USB IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, OBJECTIVE +DEVELOPMENT AND ITS SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND +NON-INFRINGEMENT, WITH REGARD TO V-USB, AND THE PROVISION OF OR FAILURE +TO PROVIDE SUPPORT SERVICES. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL +RIGHTS. YOU MAY HAVE OTHERS, WHICH VARY FROM STATE/JURISDICTION TO +STATE/JURISDICTION. + +LIMITATION OF LIABILITY. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, +IN NO EVENT SHALL OBJECTIVE DEVELOPMENT OR ITS SUPPLIERS BE LIABLE FOR ANY +SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER +(INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, +BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY +LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE V-USB OR THE +PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF OBJECTIVE +DEVELOPMENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY +CASE, OBJECTIVE DEVELOPMENT'S ENTIRE LIABILITY UNDER ANY PROVISION OF THIS +AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR V-USB. + + +8 MISCELLANEOUS TERMS + +8.1 Marketing. OBJECTIVE DEVELOPMENT has the right to mention for marketing +purposes that you entered into this agreement. + +8.2 Entire Agreement. This document represents the entire agreement between +OBJECTIVE DEVELOPMENT and you. It may only be modified in writing signed by +an authorized representative of both, OBJECTIVE DEVELOPMENT and you. + +8.3 Severability. In case a provision of these terms and conditions should +be or become partly or entirely invalid, ineffective, or not executable, +the validity of all other provisions shall not be affected. + +8.4 Applicable Law. This agreement is governed by the laws of the Republic +of Austria. + +8.5 Responsible Courts. The responsible courts in Vienna/Austria will have +exclusive jurisdiction regarding all disputes in connection with this +agreement. + diff --git a/classic/fw/usbdrv/License.txt b/classic/fw/usbdrv/License.txt new file mode 100644 index 0000000..4460cfb --- /dev/null +++ b/classic/fw/usbdrv/License.txt @@ -0,0 +1,361 @@ +OBJECTIVE DEVELOPMENT GmbH's V-USB driver software is distributed under the +terms and conditions of the GNU GPL version 2 or the GNU GPL version 3. It is +your choice whether you apply the terms of version 2 or version 3. The full +text of GPLv2 is included below. In addition to the requirements in the GPL, +we STRONGLY ENCOURAGE you to do the following: + +(1) Publish your entire project on a web site and drop us a note with the URL. +Use the form at http://www.obdev.at/vusb/feedback.html for your submission. + +(2) Adhere to minimum publication standards. Please include AT LEAST: + - a circuit diagram in PDF, PNG or GIF format + - full source code for the host software + - a Readme.txt file in ASCII format which describes the purpose of the + project and what can be found in which directories and which files + - a reference to http://www.obdev.at/vusb/ + +(3) If you improve the driver firmware itself, please give us a free license +to your modifications for our commercial license offerings. + + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/classic/fw/usbdrv/Readme.txt b/classic/fw/usbdrv/Readme.txt new file mode 100644 index 0000000..970dc66 --- /dev/null +++ b/classic/fw/usbdrv/Readme.txt @@ -0,0 +1,172 @@ +This is the Readme file to Objective Development's firmware-only USB driver +for Atmel AVR microcontrollers. For more information please visit +http://www.obdev.at/vusb/ + +This directory contains the USB firmware only. Copy it as-is to your own +project and add all .c and .S files to your project (these files are marked +with an asterisk in the list below). Then copy usbconfig-prototype.h as +usbconfig.h to your project and edit it according to your configuration. + + +TECHNICAL DOCUMENTATION +======================= +The technical documentation (API) for the firmware driver is contained in the +file "usbdrv.h". Please read all of it carefully! Configuration options are +documented in "usbconfig-prototype.h". + +The driver consists of the following files: + Readme.txt ............. The file you are currently reading. + Changelog.txt .......... Release notes for all versions of the driver. + usbdrv.h ............... Driver interface definitions and technical docs. +* usbdrv.c ............... High level language part of the driver. Link this + module to your code! +* usbdrvasm.S ............ Assembler part of the driver. This module is mostly + a stub and includes one of the usbdrvasm*.S files + depending on processor clock. Link this module to + your code! + usbdrvasm*.inc ......... Assembler routines for particular clock frequencies. + Included by usbdrvasm.S, don't link it directly! + asmcommon.inc .......... Common assembler routines. Included by + usbdrvasm*.inc, don't link it directly! + usbconfig-prototype.h .. Prototype for your own usbdrv.h file. +* oddebug.c .............. Debug functions. Only used when DEBUG_LEVEL is + defined to a value greater than 0. Link this module + to your code! + oddebug.h .............. Interface definitions of the debug module. + usbportability.h ....... Header with compiler-dependent stuff. + usbdrvasm.asm .......... Compatibility stub for IAR-C-compiler. Use this + module instead of usbdrvasm.S when you assembler + with IAR's tools. + License.txt ............ Open Source license for this driver. + CommercialLicense.txt .. Optional commercial license for this driver. + USB-ID-FAQ.txt ......... General infos about USB Product- and Vendor-IDs. + USB-IDs-for-free.txt ... List and terms of use for free shared PIDs. + +(*) ... These files should be linked to your project. + + +CPU CORE CLOCK FREQUENCY +======================== +We supply assembler modules for clock frequencies of 12 MHz, 12.8 MHz, 15 MHz, +16 MHz, 16.5 MHz 18 MHz and 20 MHz. Other clock rates are not supported. The +actual clock rate must be configured in usbconfig.h. + +12 MHz Clock +This is the traditional clock rate of V-USB because it's the lowest clock +rate where the timing constraints of the USB spec can be met. + +15 MHz Clock +Similar to 12 MHz, but some NOPs inserted. On the other hand, the higher clock +rate allows for some loops which make the resulting code size somewhat smaller +than the 12 MHz version. + +16 MHz Clock +This clock rate has been added for users of the Arduino board and other +ready-made boards which come with a fixed 16 MHz crystal. It's also an option +if you need the slightly higher clock rate for performance reasons. Since +16 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code +is somewhat tricky and has to insert a leap cycle every third byte. + +12.8 MHz and 16.5 MHz Clock +The assembler modules for these clock rates differ from the other modules +because they have been built for an RC oscillator with only 1% precision. The +receiver code inserts leap cycles to compensate for clock deviations. 1% is +also the precision which can be achieved by calibrating the internal RC +oscillator of the AVR. Please note that only AVRs with internal 64 MHz PLL +oscillator can reach 16.5 MHz with the RC oscillator. This includes the very +popular ATTiny25, ATTiny45, ATTiny85 series as well as the ATTiny26. Almost +all AVRs can reach 12.8 MHz, although this is outside the specified range. + +See the EasyLogger example at http://www.obdev.at/vusb/easylogger.html for +code which calibrates the RC oscillator based on the USB frame clock. + +18 MHz Clock +This module is closer to the USB specification because it performs an on the +fly CRC check for incoming packets. Packets with invalid checksum are +discarded as required by the spec. If you also implement checks for data +PID toggling on application level (see option USB_CFG_CHECK_DATA_TOGGLING +in usbconfig.h for more info), this ensures data integrity. Due to the CRC +tables and alignment requirements, this code is bigger than modules for other +clock rates. To activate this module, you must define USB_CFG_CHECK_CRC to 1 +and USB_CFG_CLOCK_KHZ to 18000 in usbconfig.h. + +20 MHz Clock +This module is for people who won't do it with less than the maximum. Since +20 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code +uses similar tricks as the 16 MHz module to insert leap cycles. + + +USB IDENTIFIERS +=============== +Every USB device needs a vendor- and a product-identifier (VID and PID). VIDs +are obtained from usb.org for a price of 1,500 USD. Once you have a VID, you +can assign PIDs at will. + +Since an entry level cost of 1,500 USD is too high for most small companies +and hobbyists, we provide some VID/PID pairs for free. See the file +USB-IDs-for-free.txt for details. + +Objective Development also has some license offerings which include product +IDs. See http://www.obdev.at/vusb/ for details. + + +DEVELOPMENT SYSTEM +================== +This driver has been developed and optimized for the GNU compiler version 3 +and 4. We recommend that you use the GNU compiler suite because it is freely +available. V-USB has also been ported to the IAR compiler and assembler. It +has been tested with IAR 4.10B/W32 and 4.12A/W32 on an ATmega8 with the +"small" and "tiny" memory model. Not every release is tested with IAR CC and +the driver may therefore fail to compile with IAR. Please note that gcc is +more efficient for usbdrv.c because this module has been deliberately +optimized for gcc. + +Gcc version 3 produces smaller code than version 4 due to new optimizing +capabilities which don't always improve things on 8 bit CPUs. The code size +generated by gcc 4 can be reduced with the compiler options +-fno-move-loop-invariants, -fno-tree-scev-cprop and +-fno-inline-small-functions in addition to -Os. On devices with more than +8k of flash memory, we also recommend the linker option --relax (written as +-Wl,--relax for gcc) to convert absolute calls into relative where possible. + +For more information about optimizing options see: + + http://www.tty1.net/blog/2008-04-29-avr-gcc-optimisations_en.html + +These optimizations are good for gcc 4.x. Version 3.x of gcc does not support +most of these options and produces good code anyway. + + +USING V-USB FOR FREE +==================== +The AVR firmware driver is published under the GNU General Public License +Version 2 (GPL2) and the GNU General Public License Version 3 (GPL3). It is +your choice whether you apply the terms of version 2 or version 3. + +If you decide for the free GPL2 or GPL3, we STRONGLY ENCOURAGE you to do the +following things IN ADDITION to the obligations from the GPL: + +(1) Publish your entire project on a web site and drop us a note with the URL. +Use the form at http://www.obdev.at/vusb/feedback.html for your submission. +If you don't have a web site, you can publish the project in obdev's +documentation wiki at +http://www.obdev.at/goto.php?t=vusb-wiki&p=hosted-projects. + +(2) Adhere to minimum publication standards. Please include AT LEAST: + - a circuit diagram in PDF, PNG or GIF format + - full source code for the host software + - a Readme.txt file in ASCII format which describes the purpose of the + project and what can be found in which directories and which files + - a reference to http://www.obdev.at/vusb/ + +(3) If you improve the driver firmware itself, please give us a free license +to your modifications for our commercial license offerings. + + +COMMERCIAL LICENSES FOR V-USB +============================= +If you don't want to publish your source code under the terms of the GPL, +you can simply pay money for V-USB. As an additional benefit you get +USB PIDs for free, reserved exclusively to you. See the file +"CommercialLicense.txt" for details. + diff --git a/classic/fw/usbdrv/USB-ID-FAQ.txt b/classic/fw/usbdrv/USB-ID-FAQ.txt new file mode 100644 index 0000000..a4a6bd6 --- /dev/null +++ b/classic/fw/usbdrv/USB-ID-FAQ.txt @@ -0,0 +1,149 @@ +Version 2012-07-09 + +========================== +WHY DO WE NEED THESE IDs? +========================== + +USB is more than a low level protocol for data transport. It also defines a +common set of requests which must be understood by all devices. And as part +of these common requests, the specification defines data structures, the +USB Descriptors, which are used to describe the properties of the device. + +From the perspective of an operating system, it is therefore possible to find +out basic properties of a device (such as e.g. the manufacturer and the name +of the device) without a device-specific driver. This is essential because +the operating system can choose a driver to load based on this information +(Plug-And-Play). + +Among the most important properties in the Device Descriptor are the USB +Vendor- and Product-ID. Both are 16 bit integers. The most simple form of +driver matching is based on these IDs. The driver announces the Vendor- and +Product-IDs of the devices it can handle and the operating system loads the +appropriate driver when the device is connected. + +It is obvious that this technique only works if the pair Vendor- plus +Product-ID is unique: Only devices which require the same driver can have the +same pair of IDs. + + +===================================================== +HOW DOES THE USB STANDARD ENSURE THAT IDs ARE UNIQUE? +===================================================== + +Since it is so important that USB IDs are unique, the USB Implementers Forum, +Inc. (usb.org) needs a way to enforce this legally. It is not forbidden by +law to build a device and assign it any random numbers as IDs. Usb.org +therefore needs an agreement to regulate the use of USB IDs. The agreement +binds only parties who agreed to it, of course. Everybody else is free to use +any numbers for their IDs. + +So how can usb.org ensure that every manufacturer of USB devices enters into +an agreement with them? They do it via trademark licensing. Usb.org has +registered the trademark "USB", all associated logos and related terms. If +you want to put an USB logo on your product or claim that it is USB +compliant, you must license these trademarks from usb.org. And this is where +you enter into an agreement. See the "USB-IF Trademark License Agreement and +Usage Guidelines for the USB-IF Logo" at +http://www.usb.org/developers/logo_license/. + +Licensing the USB trademarks requires that you buy a USB Vendor-ID from +usb.org (one-time fee of ca. 2,000 USD), that you become a member of usb.org +(yearly fee of ca. 4,000 USD) and that you meet all the technical +specifications from the USB spec. + +This means that most hobbyists and small companies will never be able to +become USB compliant, just because membership is so expensive. And you can't +be compliant with a driver based on V-USB anyway, because the AVR's port pins +don't meet the electrical specifications for USB. So, in principle, all +hobbyists and small companies are free to choose any random numbers for their +IDs. They have nothing to lose... + +There is one exception worth noting, though: If you use a sub-component which +implements USB, the vendor of the sub-components may guarantee USB +compliance. This might apply to some or all of FTDI's solutions. + + +======================================================================= +WHY SHOULD YOU OBTAIN USB IDs EVEN IF YOU DON'T LICENSE USB TRADEMARKS? +======================================================================= + +You have learned in the previous section that you are free to choose any +numbers for your IDs anyway. So why not do exactly this? There is still the +technical issue. If you choose IDs which are already in use by somebody else, +operating systems will load the wrong drivers and your device won't work. +Even if you choose IDs which are not currently in use, they may be in use in +the next version of the operating system or even after an automatic update. + +So what you need is a pair of Vendor- and Product-IDs for which you have the +guarantee that no USB compliant product uses them. This implies that no +operating system will ever ship with drivers responsible for these IDs. + + +============================================== +HOW DOES OBJECTIVE DEVELOPMENT HANDLE USB IDs? +============================================== + +Objective Development gives away pairs of USB-IDs with their V-USB licenses. +In order to ensure that these IDs are unique, Objective Development has an +agreement with the company/person who has bought the USB Vendor-ID from +usb.org. This agreement ensures that a range of USB Product-IDs is reserved +for assignment by Objective Development and that the owner of the Vendor-ID +won't give it to anybody else. + +This means that you have to trust three parties to ensure uniqueness of +your IDs: + + - Objective Development, that they don't give the same PID to more than + one person. + - The owner of the Vendor-ID that they don't assign PIDs from the range + assigned to Objective Development to anybody else. + - Usb.org that they don't assign the same Vendor-ID a second time. + + +================================== +WHO IS THE OWNER OF THE VENDOR-ID? +================================== + +Objective Development has obtained ranges of USB Product-IDs under two +Vendor-IDs: Under Vendor-ID 5824 from Wouter van Ooijen (Van Ooijen +Technische Informatica, www.voti.nl) and under Vendor-ID 8352 from Jason +Kotzin (now flirc.tv, Inc.). Both VID owners have received their Vendor-ID +directly from usb.org. + + +========================================================================= +CAN I USE USB-IDs FROM OBJECTIVE DEVELOPMENT WITH OTHER DRIVERS/HARDWARE? +========================================================================= + +The short answer is: Yes. All you get is a guarantee that the IDs are never +assigned to anybody else. What more do you need? + + +============================ +WHAT ABOUT SHARED ID PAIRS? +============================ + +Objective Development has reserved some PID/VID pairs for shared use. You +have no guarantee of uniqueness for them, except that no USB compliant device +uses them. In order to avoid technical problems, we must ensure that all +devices with the same pair of IDs use the same driver on kernel level. For +details, see the file USB-IDs-for-free.txt. + + +====================================================== +I HAVE HEARD THAT SUB-LICENSING OF USB-IDs IS ILLEGAL? +====================================================== + +A 16 bit integer number cannot be protected by copyright laws. It is not +sufficiently complex. And since none of the parties involved entered into the +USB-IF Trademark License Agreement, we are not bound by this agreement. So +there is no reason why it should be illegal to sub-license USB-IDs. + + +============================================= +WHO IS LIABLE IF THERE ARE INCOMPATIBILITIES? +============================================= + +Objective Development disclaims all liabilities which might arise from the +assignment of IDs. If you guarantee product features to your customers +without proper disclaimer, YOU are liable for that. diff --git a/classic/fw/usbdrv/USB-IDs-for-free.txt b/classic/fw/usbdrv/USB-IDs-for-free.txt new file mode 100644 index 0000000..d46517d --- /dev/null +++ b/classic/fw/usbdrv/USB-IDs-for-free.txt @@ -0,0 +1,154 @@ +Version 2009-08-22 + +=========================== +FREE USB-IDs FOR SHARED USE +=========================== + +Objective Development has reserved a set of USB Product-IDs for use according +to the guidelines outlined below. For more information about the concept of +USB IDs please see the file USB-ID-FAQ.txt. Objective Development guarantees +that the IDs listed below are not used by any USB compliant devices. + + +==================== +MECHANISM OF SHARING +==================== + +From a technical point of view, two different devices can share the same USB +Vendor- and Product-ID if they require the same driver on operating system +level. We make use of this fact by assigning separate IDs for various device +classes. On application layer, devices must be distinguished by their textual +name or serial number. We offer separate sets of IDs for discrimination by +textual name and for serial number. + +Examples for shared use of USB IDs are included with V-USB in the "examples" +subdirectory. + + +====================================== +IDs FOR DISCRIMINATION BY TEXTUAL NAME +====================================== + +If you use one of the IDs listed below, your device and host-side software +must conform to these rules: + +(1) The USB device MUST provide a textual representation of the manufacturer +and product identification. The manufacturer identification MUST be available +at least in USB language 0x0409 (English/US). + +(2) The textual manufacturer identification MUST contain either an Internet +domain name (e.g. "mycompany.com") registered and owned by you, or an e-mail +address under your control (e.g. "myname@gmx.net"). You can embed the domain +name or e-mail address in any string you like, e.g. "Objective Development +http://www.obdev.at/vusb/". + +(3) You are responsible for retaining ownership of the domain or e-mail +address for as long as any of your products are in use. + +(4) You may choose any string for the textual product identification, as long +as this string is unique within the scope of your textual manufacturer +identification. + +(5) Application side device look-up MUST be based on the textual manufacturer +and product identification in addition to VID/PID matching. The driver +matching MUST be a comparison of the entire strings, NOT a sub-string match. + +(6) For devices which implement a particular USB device class (e.g. HID), the +operating system's default class driver MUST be used. If an operating system +driver for Vendor Class devices is needed, this driver must be libusb or +libusb-win32 (see http://libusb.org/ and +http://libusb-win32.sourceforge.net/). + +Table if IDs for discrimination by textual name: + +PID dec (hex) | VID dec (hex) | Description of use +==============+===============+============================================ +1500 (0x05dc) | 5824 (0x16c0) | For Vendor Class devices with libusb +--------------+---------------+-------------------------------------------- +1503 (0x05df) | 5824 (0x16c0) | For generic HID class devices (which are + | | NOT mice, keyboards or joysticks) +--------------+---------------+-------------------------------------------- +1505 (0x05e1) | 5824 (0x16c0) | For CDC-ACM class devices (modems) +--------------+---------------+-------------------------------------------- +1508 (0x05e4) | 5824 (0x16c0) | For MIDI class devices +--------------+---------------+-------------------------------------------- + +Note that Windows caches the textual product- and vendor-description for +mice, keyboards and joysticks. Name-bsed discrimination is therefore not +recommended for these device classes. + + +======================================= +IDs FOR DISCRIMINATION BY SERIAL NUMBER +======================================= + +If you use one of the IDs listed below, your device and host-side software +must conform to these rules: + +(1) The USB device MUST provide a textual representation of the serial +number, unless ONLY the operating system's default class driver is used. +The serial number string MUST be available at least in USB language 0x0409 +(English/US). + +(2) The serial number MUST start with either an Internet domain name (e.g. +"mycompany.com") registered and owned by you, or an e-mail address under your +control (e.g. "myname@gmx.net"), both terminated with a colon (":") character. +You MAY append any string you like for further discrimination of your devices. + +(3) You are responsible for retaining ownership of the domain or e-mail +address for as long as any of your products are in use. + +(5) Application side device look-up MUST be based on the serial number string +in addition to VID/PID matching. The matching must start at the first +character of the serial number string and include the colon character +terminating your domain or e-mail address. It MAY stop anywhere after that. + +(6) For devices which implement a particular USB device class (e.g. HID), the +operating system's default class driver MUST be used. If an operating system +driver for Vendor Class devices is needed, this driver must be libusb or +libusb-win32 (see http://libusb.org/ and +http://libusb-win32.sourceforge.net/). + +(7) If ONLY the operating system's default class driver is used, e.g. for +mice, keyboards, joysticks, CDC or MIDI devices and no discrimination by an +application is needed, the serial number may be omitted. + + +Table if IDs for discrimination by serial number string: + +PID dec (hex) | VID dec (hex) | Description of use +===============+===============+=========================================== +10200 (0x27d8) | 5824 (0x16c0) | For Vendor Class devices with libusb +---------------+---------------+------------------------------------------- +10201 (0x27d9) | 5824 (0x16c0) | For generic HID class devices (which are + | | NOT mice, keyboards or joysticks) +---------------+---------------+------------------------------------------- +10202 (0x27da) | 5824 (0x16c0) | For USB Mice +---------------+---------------+------------------------------------------- +10203 (0x27db) | 5824 (0x16c0) | For USB Keyboards +---------------+---------------+------------------------------------------- +10204 (0x27dc) | 5824 (0x16c0) | For USB Joysticks +---------------+---------------+------------------------------------------- +10205 (0x27dd) | 5824 (0x16c0) | For CDC-ACM class devices (modems) +---------------+---------------+------------------------------------------- +10206 (0x27de) | 5824 (0x16c0) | For MIDI class devices +---------------+---------------+------------------------------------------- + + +================= +ORIGIN OF USB-IDs +================= + +OBJECTIVE DEVELOPMENT Software GmbH has obtained all VID/PID pairs listed +here from Wouter van Ooijen (see www.voti.nl) for exclusive disposition. +Wouter van Ooijen has obtained the VID from the USB Implementers Forum, Inc. +(see www.usb.org). The VID is registered for the company name "Van Ooijen +Technische Informatica". + + +========== +DISCLAIMER +========== + +OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any +problems which are caused by the shared use of these VID/PID pairs. diff --git a/classic/fw/usbdrv/asmcommon.inc b/classic/fw/usbdrv/asmcommon.inc new file mode 100644 index 0000000..d2a4f7c --- /dev/null +++ b/classic/fw/usbdrv/asmcommon.inc @@ -0,0 +1,187 @@ +/* Name: asmcommon.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2007-11-05 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file contains assembler code which is shared among the USB driver +implementations for different CPU cocks. Since the code must be inserted +in the middle of the module, it's split out into this file and #included. + +Jump destinations called from outside: + sofError: Called when no start sequence was found. + se0: Called when a package has been successfully received. + overflow: Called when receive buffer overflows. + doReturn: Called after sending data. + +Outside jump destinations used by this module: + waitForJ: Called to receive an already arriving packet. + sendAckAndReti: + sendNakAndReti: + sendCntAndReti: + usbSendAndReti: + +The following macros must be defined before this file is included: + .macro POP_STANDARD + .endm + .macro POP_RETI + .endm +*/ + +#define token x1 + +overflow: + ldi x2, 1< 0 + +#warning "Never compile production devices with debugging enabled" + +static void uartPutc(char c) +{ + while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ + ODDBG_UDR = c; +} + +static uchar hexAscii(uchar h) +{ + h &= 0xf; + if(h >= 10) + h += 'a' - (uchar)10 - '0'; + h += '0'; + return h; +} + +static void printHex(uchar c) +{ + uartPutc(hexAscii(c >> 4)); + uartPutc(hexAscii(c)); +} + +void odDebug(uchar prefix, uchar *data, uchar len) +{ + printHex(prefix); + uartPutc(':'); + while(len--){ + uartPutc(' '); + printHex(*data++); + } + uartPutc('\r'); + uartPutc('\n'); +} + +#endif diff --git a/classic/fw/usbdrv/oddebug.h b/classic/fw/usbdrv/oddebug.h new file mode 100644 index 0000000..851f84d --- /dev/null +++ b/classic/fw/usbdrv/oddebug.h @@ -0,0 +1,122 @@ +/* Name: oddebug.h + * Project: AVR library + * Author: Christian Starkjohann + * Creation Date: 2005-01-16 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +#ifndef __oddebug_h_included__ +#define __oddebug_h_included__ + +/* +General Description: +This module implements a function for debug logs on the serial line of the +AVR microcontroller. Debugging can be configured with the define +'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging +calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is +2, DBG1 and DBG2 logs will be printed. + +A debug log consists of a label ('prefix') to indicate which debug log created +the output and a memory block to dump in hex ('data' and 'len'). +*/ + + +#ifndef F_CPU +# define F_CPU 12000000 /* 12 MHz */ +#endif + +/* make sure we have the UART defines: */ +#include "usbportability.h" + +#ifndef uchar +# define uchar unsigned char +#endif + +#if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ +# warning "Debugging disabled because device has no UART" +# undef DEBUG_LEVEL +#endif + +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 0 +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +# define DBG1(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG1(prefix, data, len) +#endif + +#if DEBUG_LEVEL > 1 +# define DBG2(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG2(prefix, data, len) +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +extern void odDebug(uchar prefix, uchar *data, uchar len); + +/* Try to find our control registers; ATMEL likes to rename these */ + +#if defined UBRR +# define ODDBG_UBRR UBRR +#elif defined UBRRL +# define ODDBG_UBRR UBRRL +#elif defined UBRR0 +# define ODDBG_UBRR UBRR0 +#elif defined UBRR0L +# define ODDBG_UBRR UBRR0L +#endif + +#if defined UCR +# define ODDBG_UCR UCR +#elif defined UCSRB +# define ODDBG_UCR UCSRB +#elif defined UCSR0B +# define ODDBG_UCR UCSR0B +#endif + +#if defined TXEN +# define ODDBG_TXEN TXEN +#else +# define ODDBG_TXEN TXEN0 +#endif + +#if defined USR +# define ODDBG_USR USR +#elif defined UCSRA +# define ODDBG_USR UCSRA +#elif defined UCSR0A +# define ODDBG_USR UCSR0A +#endif + +#if defined UDRE +# define ODDBG_UDRE UDRE +#else +# define ODDBG_UDRE UDRE0 +#endif + +#if defined UDR +# define ODDBG_UDR UDR +#elif defined UDR0 +# define ODDBG_UDR UDR0 +#endif + +static inline void odDebugInit(void) +{ + ODDBG_UCR |= (1<len & 0x10){ /* packet buffer was empty */ + txStatus->buffer[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ + }else{ + txStatus->len = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ + } + p = txStatus->buffer + 1; + i = len; + do{ /* if len == 0, we still copy 1 byte, but that's no problem */ + *p++ = *data++; + }while(--i > 0); /* loop control at the end is 2 bytes shorter than at beginning */ + usbCrc16Append(&txStatus->buffer[1], len); + txStatus->len = len + 4; /* len must be given including sync byte */ + DBG2(0x21 + (((int)txStatus >> 3) & 3), txStatus->buffer, len + 3); +} + +USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len) +{ + usbGenericSetInterrupt(data, len, &usbTxStatus1); +} +#endif + +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len) +{ + usbGenericSetInterrupt(data, len, &usbTxStatus3); +} +#endif +#endif /* USB_CFG_SUPPRESS_INTR_CODE */ + +/* ------------------ utilities for code following below ------------------- */ + +/* Use defines for the switch statement so that we can choose between an + * if()else if() and a switch/case based implementation. switch() is more + * efficient for a LARGE set of sequential choices, if() is better in all other + * cases. + */ +#if USB_CFG_USE_SWITCH_STATEMENT +# define SWITCH_START(cmd) switch(cmd){{ +# define SWITCH_CASE(value) }break; case (value):{ +# define SWITCH_CASE2(v1,v2) }break; case (v1): case(v2):{ +# define SWITCH_CASE3(v1,v2,v3) }break; case (v1): case(v2): case(v3):{ +# define SWITCH_DEFAULT }break; default:{ +# define SWITCH_END }} +#else +# define SWITCH_START(cmd) {uchar _cmd = cmd; if(0){ +# define SWITCH_CASE(value) }else if(_cmd == (value)){ +# define SWITCH_CASE2(v1,v2) }else if(_cmd == (v1) || _cmd == (v2)){ +# define SWITCH_CASE3(v1,v2,v3) }else if(_cmd == (v1) || _cmd == (v2) || (_cmd == v3)){ +# define SWITCH_DEFAULT }else{ +# define SWITCH_END }} +#endif + +#ifndef USB_RX_USER_HOOK +#define USB_RX_USER_HOOK(data, len) +#endif +#ifndef USB_SET_ADDRESS_HOOK +#define USB_SET_ADDRESS_HOOK() +#endif + +/* ------------------------------------------------------------------------- */ + +/* We use if() instead of #if in the macro below because #if can't be used + * in macros and the compiler optimizes constant conditions anyway. + * This may cause problems with undefined symbols if compiled without + * optimizing! + */ +#define GET_DESCRIPTOR(cfgProp, staticName) \ + if(cfgProp){ \ + if((cfgProp) & USB_PROP_IS_RAM) \ + flags = 0; \ + if((cfgProp) & USB_PROP_IS_DYNAMIC){ \ + len = usbFunctionDescriptor(rq); \ + }else{ \ + len = USB_PROP_LENGTH(cfgProp); \ + usbMsgPtr = (usbMsgPtr_t)(staticName); \ + } \ + } + +/* usbDriverDescriptor() is similar to usbFunctionDescriptor(), but used + * internally for all types of descriptors. + */ +static inline usbMsgLen_t usbDriverDescriptor(usbRequest_t *rq) +{ +usbMsgLen_t len = 0; +uchar flags = USB_FLG_MSGPTR_IS_ROM; + + SWITCH_START(rq->wValue.bytes[1]) + SWITCH_CASE(USBDESCR_DEVICE) /* 1 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) + SWITCH_CASE(USBDESCR_CONFIG) /* 2 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) + SWITCH_CASE(USBDESCR_STRING) /* 3 */ +#if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC + if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) + flags = 0; + len = usbFunctionDescriptor(rq); +#else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ + SWITCH_START(rq->wValue.bytes[0]) + SWITCH_CASE(0) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) + SWITCH_CASE(1) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) + SWITCH_CASE(2) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_PRODUCT, usbDescriptorStringDevice) + SWITCH_CASE(3) + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) + SWITCH_DEFAULT + if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ + len = usbFunctionDescriptor(rq); + } + SWITCH_END +#endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ +#if USB_CFG_DESCR_PROPS_HID_REPORT /* only support HID descriptors if enabled */ + SWITCH_CASE(USBDESCR_HID) /* 0x21 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) + SWITCH_CASE(USBDESCR_HID_REPORT)/* 0x22 */ + GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) +#endif + SWITCH_DEFAULT + if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ + len = usbFunctionDescriptor(rq); + } + SWITCH_END + usbMsgFlags = flags; + return len; +} + +/* ------------------------------------------------------------------------- */ + +/* usbDriverSetup() is similar to usbFunctionSetup(), but it's used for + * standard requests instead of class and custom requests. + */ +static inline usbMsgLen_t usbDriverSetup(usbRequest_t *rq) +{ +usbMsgLen_t len = 0; +uchar *dataPtr = usbTxBuf + 9; /* there are 2 bytes free space at the end of the buffer */ +uchar value = rq->wValue.bytes[0]; +#if USB_CFG_IMPLEMENT_HALT +uchar index = rq->wIndex.bytes[0]; +#endif + + dataPtr[0] = 0; /* default reply common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ + SWITCH_START(rq->bRequest) + SWITCH_CASE(USBRQ_GET_STATUS) /* 0 */ + uchar recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ + if(USB_CFG_IS_SELF_POWERED && recipient == USBRQ_RCPT_DEVICE) + dataPtr[0] = USB_CFG_IS_SELF_POWERED; +#if USB_CFG_IMPLEMENT_HALT + if(recipient == USBRQ_RCPT_ENDPOINT && index == 0x81) /* request status for endpoint 1 */ + dataPtr[0] = usbTxLen1 == USBPID_STALL; +#endif + dataPtr[1] = 0; + len = 2; +#if USB_CFG_IMPLEMENT_HALT + SWITCH_CASE2(USBRQ_CLEAR_FEATURE, USBRQ_SET_FEATURE) /* 1, 3 */ + if(value == 0 && index == 0x81){ /* feature 0 == HALT for endpoint == 1 */ + usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; + usbResetDataToggling(); + } +#endif + SWITCH_CASE(USBRQ_SET_ADDRESS) /* 5 */ + usbNewDeviceAddr = value; + USB_SET_ADDRESS_HOOK(); + SWITCH_CASE(USBRQ_GET_DESCRIPTOR) /* 6 */ + len = usbDriverDescriptor(rq); + goto skipMsgPtrAssignment; + SWITCH_CASE(USBRQ_GET_CONFIGURATION) /* 8 */ + dataPtr = &usbConfiguration; /* send current configuration value */ + len = 1; + SWITCH_CASE(USBRQ_SET_CONFIGURATION) /* 9 */ + usbConfiguration = value; + usbResetStall(); + SWITCH_CASE(USBRQ_GET_INTERFACE) /* 10 */ + len = 1; +#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE + SWITCH_CASE(USBRQ_SET_INTERFACE) /* 11 */ + usbResetDataToggling(); + usbResetStall(); +#endif + SWITCH_DEFAULT /* 7=SET_DESCRIPTOR, 12=SYNC_FRAME */ + /* Should we add an optional hook here? */ + SWITCH_END + usbMsgPtr = (usbMsgPtr_t)dataPtr; +skipMsgPtrAssignment: + return len; +} + +/* ------------------------------------------------------------------------- */ + +/* usbProcessRx() is called for every message received by the interrupt + * routine. It distinguishes between SETUP and DATA packets and processes + * them accordingly. + */ +static inline void usbProcessRx(uchar *data, uchar len) +{ +usbRequest_t *rq = (void *)data; + +/* usbRxToken can be: + * 0x2d 00101101 (USBPID_SETUP for setup data) + * 0xe1 11100001 (USBPID_OUT: data phase of setup transfer) + * 0...0x0f for OUT on endpoint X + */ + DBG2(0x10 + (usbRxToken & 0xf), data, len + 2); /* SETUP=1d, SETUP-DATA=11, OUTx=1x */ + USB_RX_USER_HOOK(data, len) +#if USB_CFG_IMPLEMENT_FN_WRITEOUT + if(usbRxToken < 0x10){ /* OUT to endpoint != 0: endpoint number in usbRxToken */ + usbFunctionWriteOut(data, len); + return; + } +#endif + if(usbRxToken == (uchar)USBPID_SETUP){ + if(len != 8) /* Setup size must be always 8 bytes. Ignore otherwise. */ + return; + usbMsgLen_t replyLen; + usbTxBuf[0] = USBPID_DATA0; /* initialize data toggling */ + usbTxLen = USBPID_NAK; /* abort pending transmit */ + usbMsgFlags = 0; + uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; + if(type != USBRQ_TYPE_STANDARD){ /* standard requests are handled by driver */ + replyLen = usbFunctionSetup(data); + }else{ + replyLen = usbDriverSetup(rq); + } +#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE + if(replyLen == USB_NO_MSG){ /* use user-supplied read/write function */ + /* do some conditioning on replyLen, but on IN transfers only */ + if((rq->bmRequestType & USBRQ_DIR_MASK) != USBRQ_DIR_HOST_TO_DEVICE){ + if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ + replyLen = rq->wLength.bytes[0]; + }else{ + replyLen = rq->wLength.word; + } + } + usbMsgFlags = USB_FLG_USE_USER_RW; + }else /* The 'else' prevents that we limit a replyLen of USB_NO_MSG to the maximum transfer len. */ +#endif + if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ + if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */ + replyLen = rq->wLength.bytes[0]; + }else{ + if(replyLen > rq->wLength.word) /* limit length to max */ + replyLen = rq->wLength.word; + } + usbMsgLen = replyLen; + }else{ /* usbRxToken must be USBPID_OUT, which means data phase of setup (control-out) */ +#if USB_CFG_IMPLEMENT_FN_WRITE + if(usbMsgFlags & USB_FLG_USE_USER_RW){ + uchar rval = usbFunctionWrite(data, len); + if(rval == 0xff){ /* an error occurred */ + usbTxLen = USBPID_STALL; + }else if(rval != 0){ /* This was the final package */ + usbMsgLen = 0; /* answer with a zero-sized data packet */ + } + } +#endif + } +} + +/* ------------------------------------------------------------------------- */ + +/* This function is similar to usbFunctionRead(), but it's also called for + * data handled automatically by the driver (e.g. descriptor reads). + */ +static uchar usbDeviceRead(uchar *data, uchar len) +{ + if(len > 0){ /* don't bother app with 0 sized reads */ +#if USB_CFG_IMPLEMENT_FN_READ + if(usbMsgFlags & USB_FLG_USE_USER_RW){ + len = usbFunctionRead(data, len); + }else +#endif + { + uchar i = len; + usbMsgPtr_t r = usbMsgPtr; + if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ + do{ + uchar c = USB_READ_FLASH(r); /* assign to char size variable to enforce byte ops */ + *data++ = c; + r++; + }while(--i); + }else{ /* RAM data */ + do{ + *data++ = *((uchar *)r); + r++; + }while(--i); + } + usbMsgPtr = r; + } + } + return len; +} + +/* ------------------------------------------------------------------------- */ + +/* usbBuildTxBlock() is called when we have data to transmit and the + * interrupt routine's transmit buffer is empty. + */ +static inline void usbBuildTxBlock(void) +{ +usbMsgLen_t wantLen; +uchar len; + + wantLen = usbMsgLen; + if(wantLen > 8) + wantLen = 8; + usbMsgLen -= wantLen; + usbTxBuf[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* DATA toggling */ + len = usbDeviceRead(usbTxBuf + 1, wantLen); + if(len <= 8){ /* valid data packet */ + usbCrc16Append(&usbTxBuf[1], len); + len += 4; /* length including sync byte */ + if(len < 12) /* a partial package identifies end of message */ + usbMsgLen = USB_NO_MSG; + }else{ + len = USBPID_STALL; /* stall the endpoint */ + usbMsgLen = USB_NO_MSG; + } + usbTxLen = len; + DBG2(0x20, usbTxBuf, len-1); +} + +/* ------------------------------------------------------------------------- */ + +static inline void usbHandleResetHook(uchar notResetState) +{ +#ifdef USB_RESET_HOOK +static uchar wasReset; +uchar isReset = !notResetState; + + if(wasReset != isReset){ + USB_RESET_HOOK(isReset); + wasReset = isReset; + } +#else + notResetState = notResetState; // avoid compiler warning +#endif +} + +/* ------------------------------------------------------------------------- */ + +USB_PUBLIC void usbPoll(void) +{ +schar len; +uchar i; + + len = usbRxLen - 3; + if(len >= 0){ +/* We could check CRC16 here -- but ACK has already been sent anyway. If you + * need data integrity checks with this driver, check the CRC in your app + * code and report errors back to the host. Since the ACK was already sent, + * retries must be handled on application level. + * unsigned crc = usbCrc16(buffer + 1, usbRxLen - 3); + */ + usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len); +#if USB_CFG_HAVE_FLOWCONTROL + if(usbRxLen > 0) /* only mark as available if not inactivated */ + usbRxLen = 0; +#else + usbRxLen = 0; /* mark rx buffer as available */ +#endif + } + if(usbTxLen & 0x10){ /* transmit system idle */ + if(usbMsgLen != USB_NO_MSG){ /* transmit data pending? */ + usbBuildTxBlock(); + } + } + for(i = 20; i > 0; i--){ + uchar usbLineStatus = USBIN & USBMASK; + if(usbLineStatus != 0) /* SE0 has ended */ + goto isNotReset; + } + /* RESET condition, called multiple times during reset */ + usbNewDeviceAddr = 0; + usbDeviceAddr = 0; + usbResetStall(); + DBG1(0xff, 0, 0); +isNotReset: + usbHandleResetHook(i); +} + +/* ------------------------------------------------------------------------- */ + +USB_PUBLIC void usbInit(void) +{ +#if USB_INTR_CFG_SET != 0 + USB_INTR_CFG |= USB_INTR_CFG_SET; +#endif +#if USB_INTR_CFG_CLR != 0 + USB_INTR_CFG &= ~(USB_INTR_CFG_CLR); +#endif + USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT); + usbResetDataToggling(); +#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE + usbTxLen1 = USBPID_NAK; +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 + usbTxLen3 = USBPID_NAK; +#endif +#endif +} + +/* ------------------------------------------------------------------------- */ diff --git a/classic/fw/usbdrv/usbdrv.h b/classic/fw/usbdrv/usbdrv.h new file mode 100644 index 0000000..3fe84d5 --- /dev/null +++ b/classic/fw/usbdrv/usbdrv.h @@ -0,0 +1,746 @@ +/* Name: usbdrv.h + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +#ifndef __usbdrv_h_included__ +#define __usbdrv_h_included__ +#include "usbconfig.h" +#include "usbportability.h" + +/* +Hardware Prerequisites: +======================= +USB lines D+ and D- MUST be wired to the same I/O port. We recommend that D+ +triggers the interrupt (best achieved by using INT0 for D+), but it is also +possible to trigger the interrupt from D-. If D- is used, interrupts are also +triggered by SOF packets. D- requires a pull-up of 1.5k to +3.5V (and the +device must be powered at 3.5V) to identify as low-speed USB device. A +pull-down or pull-up of 1M SHOULD be connected from D+ to +3.5V to prevent +interference when no USB master is connected. If you use Zener diodes to limit +the voltage on D+ and D-, you MUST use a pull-down resistor, not a pull-up. +We use D+ as interrupt source and not D- because it does not trigger on +keep-alive and RESET states. If you want to count keep-alive events with +USB_COUNT_SOF, you MUST use D- as an interrupt source. + +As a compile time option, the 1.5k pull-up resistor on D- can be made +switchable to allow the device to disconnect at will. See the definition of +usbDeviceConnect() and usbDeviceDisconnect() further down in this file. + +Please adapt the values in usbconfig.h according to your hardware! + +The device MUST be clocked at exactly 12 MHz, 15 MHz, 16 MHz or 20 MHz +or at 12.8 MHz resp. 16.5 MHz +/- 1%. See usbconfig-prototype.h for details. + + +Limitations: +============ +Robustness with respect to communication errors: +The driver assumes error-free communication. It DOES check for errors in +the PID, but does NOT check bit stuffing errors, SE0 in middle of a byte, +token CRC (5 bit) and data CRC (16 bit). CRC checks can not be performed due +to timing constraints: We must start sending a reply within 7 bit times. +Bit stuffing and misplaced SE0 would have to be checked in real-time, but CPU +performance does not permit that. The driver does not check Data0/Data1 +toggling, but application software can implement the check. + +Input characteristics: +Since no differential receiver circuit is used, electrical interference +robustness may suffer. The driver samples only one of the data lines with +an ordinary I/O pin's input characteristics. However, since this is only a +low speed USB implementation and the specification allows for 8 times the +bit rate over the same hardware, we should be on the safe side. Even the spec +requires detection of asymmetric states at high bit rate for SE0 detection. + +Number of endpoints: +The driver supports the following endpoints: + +- Endpoint 0, the default control endpoint. +- Any number of interrupt- or bulk-out endpoints. The data is sent to + usbFunctionWriteOut() and USB_CFG_IMPLEMENT_FN_WRITEOUT must be defined + to 1 to activate this feature. The endpoint number can be found in the + global variable 'usbRxToken'. +- One default interrupt- or bulk-in endpoint. This endpoint is used for + interrupt- or bulk-in transfers which are not handled by any other endpoint. + You must define USB_CFG_HAVE_INTRIN_ENDPOINT in order to activate this + feature and call usbSetInterrupt() to send interrupt/bulk data. +- One additional interrupt- or bulk-in endpoint. This was endpoint 3 in + previous versions of this driver but can now be configured to any endpoint + number. You must define USB_CFG_HAVE_INTRIN_ENDPOINT3 in order to activate + this feature and call usbSetInterrupt3() to send interrupt/bulk data. The + endpoint number can be set with USB_CFG_EP3_NUMBER. + +Please note that the USB standard forbids bulk endpoints for low speed devices! +Most operating systems allow them anyway, but the AVR will spend 90% of the CPU +time in the USB interrupt polling for bulk data. + +Maximum data payload: +Data payload of control in and out transfers may be up to 254 bytes. In order +to accept payload data of out transfers, you need to implement +'usbFunctionWrite()'. + +USB Suspend Mode supply current: +The USB standard limits power consumption to 500uA when the bus is in suspend +mode. This is not a problem for self-powered devices since they don't need +bus power anyway. Bus-powered devices can achieve this only by putting the +CPU in sleep mode. The driver does not implement suspend handling by itself. +However, the application may implement activity monitoring and wakeup from +sleep. The host sends regular SE0 states on the bus to keep it active. These +SE0 states can be detected by using D- as the interrupt source. Define +USB_COUNT_SOF to 1 and use the global variable usbSofCount to check for bus +activity. + +Operation without an USB master: +The driver behaves neutral without connection to an USB master if D- reads +as 1. To avoid spurious interrupts, we recommend a high impedance (e.g. 1M) +pull-down or pull-up resistor on D+ (interrupt). If Zener diodes are used, +use a pull-down. If D- becomes statically 0, the driver may block in the +interrupt routine. + +Interrupt latency: +The application must ensure that the USB interrupt is not disabled for more +than 25 cycles (this is for 12 MHz, faster clocks allow longer latency). +This implies that all interrupt routines must either have the "ISR_NOBLOCK" +attribute set (see "avr/interrupt.h") or be written in assembler with "sei" +as the first instruction. + +Maximum interrupt duration / CPU cycle consumption: +The driver handles all USB communication during the interrupt service +routine. The routine will not return before an entire USB message is received +and the reply is sent. This may be up to ca. 1200 cycles @ 12 MHz (= 100us) if +the host conforms to the standard. The driver will consume CPU cycles for all +USB messages, even if they address another (low-speed) device on the same bus. + +*/ + +/* ------------------------------------------------------------------------- */ +/* --------------------------- Module Interface ---------------------------- */ +/* ------------------------------------------------------------------------- */ + +#define USBDRV_VERSION 20121206 +/* This define uniquely identifies a driver version. It is a decimal number + * constructed from the driver's release date in the form YYYYMMDD. If the + * driver's behavior or interface changes, you can use this constant to + * distinguish versions. If it is not defined, the driver's release date is + * older than 2006-01-25. + */ + + +#ifndef USB_PUBLIC +#define USB_PUBLIC +#endif +/* USB_PUBLIC is used as declaration attribute for all functions exported by + * the USB driver. The default is no attribute (see above). You may define it + * to static either in usbconfig.h or from the command line if you include + * usbdrv.c instead of linking against it. Including the C module of the driver + * directly in your code saves a couple of bytes in flash memory. + */ + +#ifndef __ASSEMBLER__ +#ifndef uchar +#define uchar unsigned char +#endif +#ifndef schar +#define schar signed char +#endif +/* shortcuts for well defined 8 bit integer types */ + +#if USB_CFG_LONG_TRANSFERS /* if more than 254 bytes transfer size required */ +# define usbMsgLen_t unsigned +#else +# define usbMsgLen_t uchar +#endif +/* usbMsgLen_t is the data type used for transfer lengths. By default, it is + * defined to uchar, allowing a maximum of 254 bytes (255 is reserved for + * USB_NO_MSG below). If the usbconfig.h defines USB_CFG_LONG_TRANSFERS to 1, + * a 16 bit data type is used, allowing up to 16384 bytes (the rest is used + * for flags in the descriptor configuration). + */ +#define USB_NO_MSG ((usbMsgLen_t)-1) /* constant meaning "no message" */ + +#ifndef usbMsgPtr_t +#define usbMsgPtr_t uchar * +#endif +/* Making usbMsgPtr_t a define allows the user of this library to define it to + * an 8 bit type on tiny devices. This reduces code size, especially if the + * compiler supports a tiny memory model. + * The type can be a pointer or scalar type, casts are made where necessary. + * Although it's paradoxical, Gcc 4 generates slightly better code for scalar + * types than for pointers. + */ + +struct usbRequest; /* forward declaration */ + +USB_PUBLIC void usbInit(void); +/* This function must be called before interrupts are enabled and the main + * loop is entered. We exepct that the PORT and DDR bits for D+ and D- have + * not been changed from their default status (which is 0). If you have changed + * them, set both back to 0 (configure them as input with no internal pull-up). + */ +USB_PUBLIC void usbPoll(void); +/* This function must be called at regular intervals from the main loop. + * Maximum delay between calls is somewhat less than 50ms (USB timeout for + * accepting a Setup message). Otherwise the device will not be recognized. + * Please note that debug outputs through the UART take ~ 0.5ms per byte + * at 19200 bps. + */ +extern usbMsgPtr_t usbMsgPtr; +/* This variable may be used to pass transmit data to the driver from the + * implementation of usbFunctionWrite(). It is also used internally by the + * driver for standard control requests. + */ +USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]); +/* This function is called when the driver receives a SETUP transaction from + * the host which is not answered by the driver itself (in practice: class and + * vendor requests). All control transfers start with a SETUP transaction where + * the host communicates the parameters of the following (optional) data + * transfer. The SETUP data is available in the 'data' parameter which can + * (and should) be casted to 'usbRequest_t *' for a more user-friendly access + * to parameters. + * + * If the SETUP indicates a control-in transfer, you should provide the + * requested data to the driver. There are two ways to transfer this data: + * (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data + * block and return the length of the data in 'usbFunctionSetup()'. The driver + * will handle the rest. Or (2) return USB_NO_MSG in 'usbFunctionSetup()'. The + * driver will then call 'usbFunctionRead()' when data is needed. See the + * documentation for usbFunctionRead() for details. + * + * If the SETUP indicates a control-out transfer, the only way to receive the + * data from the host is through the 'usbFunctionWrite()' call. If you + * implement this function, you must return USB_NO_MSG in 'usbFunctionSetup()' + * to indicate that 'usbFunctionWrite()' should be used. See the documentation + * of this function for more information. If you just want to ignore the data + * sent by the host, return 0 in 'usbFunctionSetup()'. + * + * Note that calls to the functions usbFunctionRead() and usbFunctionWrite() + * are only done if enabled by the configuration in usbconfig.h. + */ +USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq); +/* You need to implement this function ONLY if you provide USB descriptors at + * runtime (which is an expert feature). It is very similar to + * usbFunctionSetup() above, but it is called only to request USB descriptor + * data. See the documentation of usbFunctionSetup() above for more info. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT +USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len); +/* This function sets the message which will be sent during the next interrupt + * IN transfer. The message is copied to an internal buffer and must not exceed + * a length of 8 bytes. The message may be 0 bytes long just to indicate the + * interrupt status to the host. + * If you need to transfer more bytes, use a control read after the interrupt. + */ +#define usbInterruptIsReady() (usbTxLen1 & 0x10) +/* This macro indicates whether the last interrupt message has already been + * sent. If you set a new interrupt message before the old was sent, the + * message already buffered will be lost. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len); +#define usbInterruptIsReady3() (usbTxLen3 & 0x10) +/* Same as above for endpoint 3 */ +#endif +#endif /* USB_CFG_HAVE_INTRIN_ENDPOINT */ +#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* simplified interface for backward compatibility */ +#define usbHidReportDescriptor usbDescriptorHidReport +/* should be declared as: PROGMEM char usbHidReportDescriptor[]; */ +/* If you implement an HID device, you need to provide a report descriptor. + * The HID report descriptor syntax is a bit complex. If you understand how + * report descriptors are constructed, we recommend that you use the HID + * Descriptor Tool from usb.org, see http://www.usb.org/developers/hidpage/. + * Otherwise you should probably start with a working example. + */ +#endif /* USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH */ +#if USB_CFG_IMPLEMENT_FN_WRITE +USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len); +/* This function is called by the driver to provide a control transfer's + * payload data (control-out). It is called in chunks of up to 8 bytes. The + * total count provided in the current control transfer can be obtained from + * the 'length' property in the setup data. If an error occurred during + * processing, return 0xff (== -1). The driver will answer the entire transfer + * with a STALL token in this case. If you have received the entire payload + * successfully, return 1. If you expect more data, return 0. If you don't + * know whether the host will send more data (you should know, the total is + * provided in the usbFunctionSetup() call!), return 1. + * NOTE: If you return 0xff for STALL, 'usbFunctionWrite()' may still be called + * for the remaining data. You must continue to return 0xff for STALL in these + * calls. + * In order to get usbFunctionWrite() called, define USB_CFG_IMPLEMENT_FN_WRITE + * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. + */ +#endif /* USB_CFG_IMPLEMENT_FN_WRITE */ +#if USB_CFG_IMPLEMENT_FN_READ +USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len); +/* This function is called by the driver to ask the application for a control + * transfer's payload data (control-in). It is called in chunks of up to 8 + * bytes each. You should copy the data to the location given by 'data' and + * return the actual number of bytes copied. If you return less than requested, + * the control-in transfer is terminated. If you return 0xff, the driver aborts + * the transfer with a STALL token. + * In order to get usbFunctionRead() called, define USB_CFG_IMPLEMENT_FN_READ + * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. + */ +#endif /* USB_CFG_IMPLEMENT_FN_READ */ + +extern uchar usbRxToken; /* may be used in usbFunctionWriteOut() below */ +#if USB_CFG_IMPLEMENT_FN_WRITEOUT +USB_PUBLIC void usbFunctionWriteOut(uchar *data, uchar len); +/* This function is called by the driver when data is received on an interrupt- + * or bulk-out endpoint. The endpoint number can be found in the global + * variable usbRxToken. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT to 1 in + * usbconfig.h to get this function called. + */ +#endif /* USB_CFG_IMPLEMENT_FN_WRITEOUT */ +#ifdef USB_CFG_PULLUP_IOPORTNAME +#define usbDeviceConnect() ((USB_PULLUP_DDR |= (1<device, 1=device->host + * t ..... type: 0=standard, 1=class, 2=vendor, 3=reserved + * r ..... recipient: 0=device, 1=interface, 2=endpoint, 3=other + */ + +/* USB setup recipient values */ +#define USBRQ_RCPT_MASK 0x1f +#define USBRQ_RCPT_DEVICE 0 +#define USBRQ_RCPT_INTERFACE 1 +#define USBRQ_RCPT_ENDPOINT 2 + +/* USB request type values */ +#define USBRQ_TYPE_MASK 0x60 +#define USBRQ_TYPE_STANDARD (0<<5) +#define USBRQ_TYPE_CLASS (1<<5) +#define USBRQ_TYPE_VENDOR (2<<5) + +/* USB direction values: */ +#define USBRQ_DIR_MASK 0x80 +#define USBRQ_DIR_HOST_TO_DEVICE (0<<7) +#define USBRQ_DIR_DEVICE_TO_HOST (1<<7) + +/* USB Standard Requests */ +#define USBRQ_GET_STATUS 0 +#define USBRQ_CLEAR_FEATURE 1 +#define USBRQ_SET_FEATURE 3 +#define USBRQ_SET_ADDRESS 5 +#define USBRQ_GET_DESCRIPTOR 6 +#define USBRQ_SET_DESCRIPTOR 7 +#define USBRQ_GET_CONFIGURATION 8 +#define USBRQ_SET_CONFIGURATION 9 +#define USBRQ_GET_INTERFACE 10 +#define USBRQ_SET_INTERFACE 11 +#define USBRQ_SYNCH_FRAME 12 + +/* USB descriptor constants */ +#define USBDESCR_DEVICE 1 +#define USBDESCR_CONFIG 2 +#define USBDESCR_STRING 3 +#define USBDESCR_INTERFACE 4 +#define USBDESCR_ENDPOINT 5 +#define USBDESCR_HID 0x21 +#define USBDESCR_HID_REPORT 0x22 +#define USBDESCR_HID_PHYS 0x23 + +//#define USBATTR_BUSPOWER 0x80 // USB 1.1 does not define this value any more +#define USBATTR_BUSPOWER 0 +#define USBATTR_SELFPOWER 0x40 +#define USBATTR_REMOTEWAKE 0x20 + +/* USB HID Requests */ +#define USBRQ_HID_GET_REPORT 0x01 +#define USBRQ_HID_GET_IDLE 0x02 +#define USBRQ_HID_GET_PROTOCOL 0x03 +#define USBRQ_HID_SET_REPORT 0x09 +#define USBRQ_HID_SET_IDLE 0x0a +#define USBRQ_HID_SET_PROTOCOL 0x0b + +/* ------------------------------------------------------------------------- */ + +#endif /* __usbdrv_h_included__ */ diff --git a/classic/fw/usbdrv/usbdrvasm.S b/classic/fw/usbdrv/usbdrvasm.S new file mode 100644 index 0000000..32ce8ef --- /dev/null +++ b/classic/fw/usbdrv/usbdrvasm.S @@ -0,0 +1,392 @@ +/* Name: usbdrvasm.S + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2007-06-13 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* +General Description: +This module is the assembler part of the USB driver. This file contains +general code (preprocessor acrobatics and CRC computation) and then includes +the file appropriate for the given clock rate. +*/ + +#define __SFR_OFFSET 0 /* used by avr-libc's register definitions */ +#include "usbportability.h" +#include "usbdrv.h" /* for common defs */ + +/* register names */ +#define x1 r16 +#define x2 r17 +#define shift r18 +#define cnt r19 +#define x3 r20 +#define x4 r21 +#define x5 r22 +#define bitcnt x5 +#define phase x4 +#define leap x4 + +/* Some assembler dependent definitions and declarations: */ + +#ifdef __IAR_SYSTEMS_ASM__ + extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset + extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen + extern usbTxBuf, usbTxStatus1, usbTxStatus3 +# if USB_COUNT_SOF + extern usbSofCount +# endif + public usbCrc16 + public usbCrc16Append + + COMMON INTVEC +# ifndef USB_INTR_VECTOR + ORG INT0_vect +# else /* USB_INTR_VECTOR */ + ORG USB_INTR_VECTOR +# undef USB_INTR_VECTOR +# endif /* USB_INTR_VECTOR */ +# define USB_INTR_VECTOR usbInterruptHandler + rjmp USB_INTR_VECTOR + RSEG CODE + +#else /* __IAR_SYSTEMS_ASM__ */ + +# ifndef USB_INTR_VECTOR /* default to hardware interrupt INT0 */ +# ifdef INT0_vect +# define USB_INTR_VECTOR INT0_vect // this is the "new" define for the vector +# else +# define USB_INTR_VECTOR SIG_INTERRUPT0 // this is the "old" vector +# endif +# endif + .text + .global USB_INTR_VECTOR + .type USB_INTR_VECTOR, @function + .global usbCrc16 + .global usbCrc16Append +#endif /* __IAR_SYSTEMS_ASM__ */ + + +#if USB_INTR_PENDING < 0x40 /* This is an I/O address, use in and out */ +# define USB_LOAD_PENDING(reg) in reg, USB_INTR_PENDING +# define USB_STORE_PENDING(reg) out USB_INTR_PENDING, reg +#else /* It's a memory address, use lds and sts */ +# define USB_LOAD_PENDING(reg) lds reg, USB_INTR_PENDING +# define USB_STORE_PENDING(reg) sts USB_INTR_PENDING, reg +#endif + +#define usbTxLen1 usbTxStatus1 +#define usbTxBuf1 (usbTxStatus1 + 1) +#define usbTxLen3 usbTxStatus3 +#define usbTxBuf3 (usbTxStatus3 + 1) + + +;---------------------------------------------------------------------------- +; Utility functions +;---------------------------------------------------------------------------- + +#ifdef __IAR_SYSTEMS_ASM__ +/* Register assignments for usbCrc16 on IAR cc */ +/* Calling conventions on IAR: + * First parameter passed in r16/r17, second in r18/r19 and so on. + * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) + * Result is passed in r16/r17 + * In case of the "tiny" memory model, pointers are only 8 bit with no + * padding. We therefore pass argument 1 as "16 bit unsigned". + */ +RTMODEL "__rt_version", "3" +/* The line above will generate an error if cc calling conventions change. + * The value "3" above is valid for IAR 4.10B/W32 + */ +# define argLen r18 /* argument 2 */ +# define argPtrL r16 /* argument 1 */ +# define argPtrH r17 /* argument 1 */ + +# define resCrcL r16 /* result */ +# define resCrcH r17 /* result */ + +# define ptrL ZL +# define ptrH ZH +# define ptr Z +# define byte r22 +# define bitCnt r19 +# define polyL r20 +# define polyH r21 +# define scratch r23 + +#else /* __IAR_SYSTEMS_ASM__ */ +/* Register assignments for usbCrc16 on gcc */ +/* Calling conventions on gcc: + * First parameter passed in r24/r25, second in r22/23 and so on. + * Callee must preserve r1-r17, r28/r29 + * Result is passed in r24/r25 + */ +# define argLen r22 /* argument 2 */ +# define argPtrL r24 /* argument 1 */ +# define argPtrH r25 /* argument 1 */ + +# define resCrcL r24 /* result */ +# define resCrcH r25 /* result */ + +# define ptrL XL +# define ptrH XH +# define ptr x +# define byte r18 +# define bitCnt r19 +# define polyL r20 +# define polyH r21 +# define scratch r23 + +#endif + +#if USB_USE_FAST_CRC + +; This implementation is faster, but has bigger code size +; Thanks to Slawomir Fras (BoskiDialer) for this code! +; It implements the following C pseudo-code: +; unsigned table(unsigned char x) +; { +; unsigned value; +; +; value = (unsigned)x << 6; +; value ^= (unsigned)x << 7; +; if(parity(x)) +; value ^= 0xc001; +; return value; +; } +; unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen) +; { +; unsigned crc = 0xffff; +; +; while(argLen--) +; crc = table(lo8(crc) ^ *argPtr++) ^ hi8(crc); +; return ~crc; +; } + +; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen); +; argPtr r24+25 / r16+r17 +; argLen r22 / r18 +; temp variables: +; byte r18 / r22 +; scratch r23 +; resCrc r24+r25 / r16+r17 +; ptr X / Z +usbCrc16: + mov ptrL, argPtrL + mov ptrH, argPtrH + ldi resCrcL, 0xFF + ldi resCrcH, 0xFF + rjmp usbCrc16LoopTest +usbCrc16ByteLoop: + ld byte, ptr+ + eor resCrcL, byte ; resCrcL is now 'x' in table() + mov byte, resCrcL ; compute parity of 'x' + swap byte + eor byte, resCrcL + mov scratch, byte + lsr byte + lsr byte + eor byte, scratch + inc byte + lsr byte + andi byte, 1 ; byte is now parity(x) + mov scratch, resCrcL + mov resCrcL, resCrcH + eor resCrcL, byte ; low byte of if(parity(x)) value ^= 0xc001; + neg byte + andi byte, 0xc0 + mov resCrcH, byte ; high byte of if(parity(x)) value ^= 0xc001; + clr byte + lsr scratch + ror byte + eor resCrcH, scratch + eor resCrcL, byte + lsr scratch + ror byte + eor resCrcH, scratch + eor resCrcL, byte +usbCrc16LoopTest: + subi argLen, 1 + brsh usbCrc16ByteLoop + com resCrcL + com resCrcH + ret + +#else /* USB_USE_FAST_CRC */ + +; This implementation is slower, but has less code size +; +; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen); +; argPtr r24+25 / r16+r17 +; argLen r22 / r18 +; temp variables: +; byte r18 / r22 +; bitCnt r19 +; poly r20+r21 +; scratch r23 +; resCrc r24+r25 / r16+r17 +; ptr X / Z +usbCrc16: + mov ptrL, argPtrL + mov ptrH, argPtrH + ldi resCrcL, 0 + ldi resCrcH, 0 + ldi polyL, lo8(0xa001) + ldi polyH, hi8(0xa001) + com argLen ; argLen = -argLen - 1: modified loop to ensure that carry is set + ldi bitCnt, 0 ; loop counter with starnd condition = end condition + rjmp usbCrcLoopEntry +usbCrcByteLoop: + ld byte, ptr+ + eor resCrcL, byte +usbCrcBitLoop: + ror resCrcH ; carry is always set here (see brcs jumps to here) + ror resCrcL + brcs usbCrcNoXor + eor resCrcL, polyL + eor resCrcH, polyH +usbCrcNoXor: + subi bitCnt, 224 ; (8 * 224) % 256 = 0; this loop iterates 8 times + brcs usbCrcBitLoop +usbCrcLoopEntry: + subi argLen, -1 + brcs usbCrcByteLoop +usbCrcReady: + ret +; Thanks to Reimar Doeffinger for optimizing this CRC routine! + +#endif /* USB_USE_FAST_CRC */ + +; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len); +usbCrc16Append: + rcall usbCrc16 + st ptr+, resCrcL + st ptr+, resCrcH + ret + +#undef argLen +#undef argPtrL +#undef argPtrH +#undef resCrcL +#undef resCrcH +#undef ptrL +#undef ptrH +#undef ptr +#undef byte +#undef bitCnt +#undef polyL +#undef polyH +#undef scratch + + +#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH +#ifdef __IAR_SYSTEMS_ASM__ +/* Register assignments for usbMeasureFrameLength on IAR cc */ +/* Calling conventions on IAR: + * First parameter passed in r16/r17, second in r18/r19 and so on. + * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) + * Result is passed in r16/r17 + * In case of the "tiny" memory model, pointers are only 8 bit with no + * padding. We therefore pass argument 1 as "16 bit unsigned". + */ +# define resL r16 +# define resH r17 +# define cnt16L r30 +# define cnt16H r31 +# define cntH r18 + +#else /* __IAR_SYSTEMS_ASM__ */ +/* Register assignments for usbMeasureFrameLength on gcc */ +/* Calling conventions on gcc: + * First parameter passed in r24/r25, second in r22/23 and so on. + * Callee must preserve r1-r17, r28/r29 + * Result is passed in r24/r25 + */ +# define resL r24 +# define resH r25 +# define cnt16L r24 +# define cnt16H r25 +# define cntH r26 +#endif +# define cnt16 cnt16L + +; extern unsigned usbMeasurePacketLength(void); +; returns time between two idle strobes in multiples of 7 CPU clocks +.global usbMeasureFrameLength +usbMeasureFrameLength: + ldi cntH, 6 ; wait ~ 10 ms for D- == 0 + clr cnt16L + clr cnt16H +usbMFTime16: + dec cntH + breq usbMFTimeout +usbMFWaitStrobe: ; first wait for D- == 0 (idle strobe) + sbiw cnt16, 1 ;[0] [6] + breq usbMFTime16 ;[2] + sbic USBIN, USBMINUS ;[3] + rjmp usbMFWaitStrobe ;[4] +usbMFWaitIdle: ; then wait until idle again + sbis USBIN, USBMINUS ;1 wait for D- == 1 + rjmp usbMFWaitIdle ;2 + ldi cnt16L, 1 ;1 represents cycles so far + clr cnt16H ;1 +usbMFWaitLoop: + in cntH, USBIN ;[0] [7] + adiw cnt16, 1 ;[1] + breq usbMFTimeout ;[3] + andi cntH, USBMASK ;[4] + brne usbMFWaitLoop ;[5] +usbMFTimeout: +#if resL != cnt16L + mov resL, cnt16L + mov resH, cnt16H +#endif + ret + +#undef resL +#undef resH +#undef cnt16 +#undef cnt16L +#undef cnt16H +#undef cntH + +#endif /* USB_CFG_HAVE_MEASURE_FRAME_LENGTH */ + +;---------------------------------------------------------------------------- +; Now include the clock rate specific code +;---------------------------------------------------------------------------- + +#ifndef USB_CFG_CLOCK_KHZ +# ifdef F_CPU +# define USB_CFG_CLOCK_KHZ (F_CPU/1000) +# else +# error "USB_CFG_CLOCK_KHZ not defined in usbconfig.h and no F_CPU set!" +# endif +#endif + +#if USB_CFG_CHECK_CRC /* separate dispatcher for CRC type modules */ +# if USB_CFG_CLOCK_KHZ == 18000 +# include "usbdrvasm18-crc.inc" +# else +# error "USB_CFG_CLOCK_KHZ is not one of the supported crc-rates!" +# endif +#else /* USB_CFG_CHECK_CRC */ +# if USB_CFG_CLOCK_KHZ == 12000 +# include "usbdrvasm12.inc" +# elif USB_CFG_CLOCK_KHZ == 12800 +# include "usbdrvasm128.inc" +# elif USB_CFG_CLOCK_KHZ == 15000 +# include "usbdrvasm15.inc" +# elif USB_CFG_CLOCK_KHZ == 16000 +# include "usbdrvasm16.inc" +# elif USB_CFG_CLOCK_KHZ == 16500 +# include "usbdrvasm165.inc" +# elif USB_CFG_CLOCK_KHZ == 20000 +# include "usbdrvasm20.inc" +# else +# error "USB_CFG_CLOCK_KHZ is not one of the supported non-crc-rates!" +# endif +#endif /* USB_CFG_CHECK_CRC */ diff --git a/classic/fw/usbdrv/usbdrvasm.asm b/classic/fw/usbdrv/usbdrvasm.asm new file mode 100644 index 0000000..fb66934 --- /dev/null +++ b/classic/fw/usbdrv/usbdrvasm.asm @@ -0,0 +1,20 @@ +/* Name: usbdrvasm.asm + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2006-03-01 + * Tabsize: 4 + * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* +General Description: +The IAR compiler/assembler system prefers assembler files with file extension +".asm". We simply provide this file as an alias for usbdrvasm.S. + +Thanks to Oleg Semyonov for his help with the IAR tools port! +*/ + +#include "usbdrvasm.S" + +end diff --git a/classic/fw/usbdrv/usbdrvasm12.inc b/classic/fw/usbdrv/usbdrvasm12.inc new file mode 100644 index 0000000..d3bd056 --- /dev/null +++ b/classic/fw/usbdrv/usbdrvasm12.inc @@ -0,0 +1,392 @@ +/* Name: usbdrvasm12.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 12 MHz version of the asssembler part of the USB driver. It +requires a 12 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! + + +Timing constraints according to spec (in bit times): +timing subject min max CPUcycles +--------------------------------------------------------------------------- +EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2 16 16-128 +EOP of IN to sync pattern of DATA0 (rx, then tx) 2 7.5 16-60 +DATAx (rx) to ACK/NAK/STALL (tx) 2 7.5 16-60 +*/ + +;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! +;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled +;max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable +;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes +;Numbers in brackets are maximum cycles since SOF. +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt + push YL ;2 [35] push only what is necessary to sync with edge ASAP + in YL, SREG ;1 [37] + push YL ;2 [39] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of 1/4 bit which meets the spec. + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push YH ;2 [2] + lds YL, usbInputBufOffset;2 [4] + clr YH ;1 [5] + subi YL, lo8(-(usbRxBuf));1 [6] + sbci YH, hi8(-(usbRxBuf));1 [7] + + sbis USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early] + rjmp haveTwoBitsK ;2 [10] + pop YH ;2 [11] undo the push from before + rjmp waitForK ;2 [13] this was not the end of sync, retry +haveTwoBitsK: +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- + push shift ;2 [16] + push x1 ;2 [12] + push x2 ;2 [14] + + in x1, USBIN ;1 [17] <-- sample bit 0 + ldi shift, 0xff ;1 [18] + bst x1, USBMINUS ;1 [19] + bld shift, 0 ;1 [20] + push x3 ;2 [22] + push cnt ;2 [24] + + in x2, USBIN ;1 [25] <-- sample bit 1 + ser x3 ;1 [26] [inserted init instruction] + eor x1, x2 ;1 [27] + bst x1, USBMINUS ;1 [28] + bld shift, 1 ;1 [29] + ldi cnt, USB_BUFSIZE;1 [30] [inserted init instruction] + rjmp rxbit2 ;2 [32] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +unstuff0: ;1 (branch taken) + andi x3, ~0x01 ;1 [15] + mov x1, x2 ;1 [16] x2 contains last sampled (stuffed) bit + in x2, USBIN ;1 [17] <-- sample bit 1 again + ori shift, 0x01 ;1 [18] + rjmp didUnstuff0 ;2 [20] + +unstuff1: ;1 (branch taken) + mov x2, x1 ;1 [21] x1 contains last sampled (stuffed) bit + andi x3, ~0x02 ;1 [22] + ori shift, 0x02 ;1 [23] + nop ;1 [24] + in x1, USBIN ;1 [25] <-- sample bit 2 again + rjmp didUnstuff1 ;2 [27] + +unstuff2: ;1 (branch taken) + andi x3, ~0x04 ;1 [29] + ori shift, 0x04 ;1 [30] + mov x1, x2 ;1 [31] x2 contains last sampled (stuffed) bit + nop ;1 [32] + in x2, USBIN ;1 [33] <-- sample bit 3 + rjmp didUnstuff2 ;2 [35] + +unstuff3: ;1 (branch taken) + in x2, USBIN ;1 [34] <-- sample stuffed bit 3 [one cycle too late] + andi x3, ~0x08 ;1 [35] + ori shift, 0x08 ;1 [36] + rjmp didUnstuff3 ;2 [38] + +unstuff4: ;1 (branch taken) + andi x3, ~0x10 ;1 [40] + in x1, USBIN ;1 [41] <-- sample stuffed bit 4 + ori shift, 0x10 ;1 [42] + rjmp didUnstuff4 ;2 [44] + +unstuff5: ;1 (branch taken) + andi x3, ~0x20 ;1 [48] + in x2, USBIN ;1 [49] <-- sample stuffed bit 5 + ori shift, 0x20 ;1 [50] + rjmp didUnstuff5 ;2 [52] + +unstuff6: ;1 (branch taken) + andi x3, ~0x40 ;1 [56] + in x1, USBIN ;1 [57] <-- sample stuffed bit 6 + ori shift, 0x40 ;1 [58] + rjmp didUnstuff6 ;2 [60] + +; extra jobs done during bit interval: +; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs] +; bit 1: se0 check +; bit 2: overflow check +; bit 3: recovery from delay [bit 0 tasks took too long] +; bit 4: none +; bit 5: none +; bit 6: none +; bit 7: jump, eor +rxLoop: + eor x3, shift ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others + in x1, USBIN ;1 [1] <-- sample bit 0 + st y+, x3 ;2 [3] store data + ser x3 ;1 [4] + nop ;1 [5] + eor x2, x1 ;1 [6] + bst x2, USBMINUS;1 [7] + bld shift, 0 ;1 [8] + in x2, USBIN ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed) + andi x2, USBMASK ;1 [10] + breq se0 ;1 [11] SE0 check for bit 1 + andi shift, 0xf9 ;1 [12] +didUnstuff0: + breq unstuff0 ;1 [13] + eor x1, x2 ;1 [14] + bst x1, USBMINUS;1 [15] + bld shift, 1 ;1 [16] +rxbit2: + in x1, USBIN ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed) + andi shift, 0xf3 ;1 [18] + breq unstuff1 ;1 [19] do remaining work for bit 1 +didUnstuff1: + subi cnt, 1 ;1 [20] + brcs overflow ;1 [21] loop control + eor x2, x1 ;1 [22] + bst x2, USBMINUS;1 [23] + bld shift, 2 ;1 [24] + in x2, USBIN ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed) + andi shift, 0xe7 ;1 [26] + breq unstuff2 ;1 [27] +didUnstuff2: + eor x1, x2 ;1 [28] + bst x1, USBMINUS;1 [29] + bld shift, 3 ;1 [30] +didUnstuff3: + andi shift, 0xcf ;1 [31] + breq unstuff3 ;1 [32] + in x1, USBIN ;1 [33] <-- sample bit 4 + eor x2, x1 ;1 [34] + bst x2, USBMINUS;1 [35] + bld shift, 4 ;1 [36] +didUnstuff4: + andi shift, 0x9f ;1 [37] + breq unstuff4 ;1 [38] + nop2 ;2 [40] + in x2, USBIN ;1 [41] <-- sample bit 5 + eor x1, x2 ;1 [42] + bst x1, USBMINUS;1 [43] + bld shift, 5 ;1 [44] +didUnstuff5: + andi shift, 0x3f ;1 [45] + breq unstuff5 ;1 [46] + nop2 ;2 [48] + in x1, USBIN ;1 [49] <-- sample bit 6 + eor x2, x1 ;1 [50] + bst x2, USBMINUS;1 [51] + bld shift, 6 ;1 [52] +didUnstuff6: + cpi shift, 0x02 ;1 [53] + brlo unstuff6 ;1 [54] + nop2 ;2 [56] + in x2, USBIN ;1 [57] <-- sample bit 7 + eor x1, x2 ;1 [58] + bst x1, USBMINUS;1 [59] + bld shift, 7 ;1 [60] +didUnstuff7: + cpi shift, 0x04 ;1 [61] + brsh rxLoop ;2 [63] loop control +unstuff7: + andi x3, ~0x80 ;1 [63] + ori shift, 0x80 ;1 [64] + in x2, USBIN ;1 [65] <-- sample stuffed bit 7 + nop ;1 [66] + rjmp didUnstuff7 ;2 [68] + +macro POP_STANDARD ; 12 cycles + pop cnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;---------------------------------------------------------------------------- +; Transmitting data +;---------------------------------------------------------------------------- + +txByteLoop: +txBitloop: +stuffN1Delay: ; [03] + ror shift ;[-5] [11] [59] + brcc doExorN1 ;[-4] [60] + subi x4, 1 ;[-3] + brne commonN1 ;[-2] + lsl shift ;[-1] compensate ror after rjmp stuffDelay + nop ;[00] stuffing consists of just waiting 8 cycles + rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear + +sendNakAndReti: ;0 [-19] 19 cycles until SOP + ldi x3, USBPID_NAK ;1 [-18] + rjmp usbSendX3 ;2 [-16] +sendAckAndReti: ;0 [-19] 19 cycles until SOP + ldi x3, USBPID_ACK ;1 [-18] + rjmp usbSendX3 ;2 [-16] +sendCntAndReti: ;0 [-17] 17 cycles until SOP + mov x3, cnt ;1 [-16] +usbSendX3: ;0 [-16] + ldi YL, 20 ;1 [-15] 'x3' is R20 + ldi YH, 0 ;1 [-14] + ldi cnt, 2 ;1 [-13] +; rjmp usbSendAndReti fallthrough + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 +; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 +; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte +;uses: x1...x2, x4, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x4 = bitstuff cnt] +;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) +usbSendAndReti: + in x2, USBDDR ;[-12] 12 cycles until SOP + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS ;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + out USBDDR, x2 ;[-8] <--- acquire bus + in x1, USBOUT ;[-7] port mirror for tx loop + ldi shift, 0x40 ;[-6] sync byte is first byte sent (we enter loop after ror) + ldi x2, USBMASK ;[-5] + push x4 ;[-4] +doExorN1: + eor x1, x2 ;[-2] [06] [62] + ldi x4, 6 ;[-1] [07] [63] +commonN1: +stuffN2Delay: + out USBOUT, x1 ;[00] [08] [64] <--- set bit + ror shift ;[01] + brcc doExorN2 ;[02] + subi x4, 1 ;[03] + brne commonN2 ;[04] + lsl shift ;[05] compensate ror after rjmp stuffDelay + rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear +doExorN2: + eor x1, x2 ;[04] [12] + ldi x4, 6 ;[05] [13] +commonN2: + nop ;[06] [14] + subi cnt, 171 ;[07] [15] trick: (3 * 171) & 0xff = 1 + out USBOUT, x1 ;[08] [16] <--- set bit + brcs txBitloop ;[09] [25] [41] + +stuff6Delay: + ror shift ;[42] [50] + brcc doExor6 ;[43] + subi x4, 1 ;[44] + brne common6 ;[45] + lsl shift ;[46] compensate ror after rjmp stuffDelay + nop ;[47] stuffing consists of just waiting 8 cycles + rjmp stuff6Delay ;[48] after ror, C bit is reliably clear +doExor6: + eor x1, x2 ;[45] [53] + ldi x4, 6 ;[46] +common6: +stuff7Delay: + ror shift ;[47] [55] + out USBOUT, x1 ;[48] <--- set bit + brcc doExor7 ;[49] + subi x4, 1 ;[50] + brne common7 ;[51] + lsl shift ;[52] compensate ror after rjmp stuffDelay + rjmp stuff7Delay ;[53] after ror, C bit is reliably clear +doExor7: + eor x1, x2 ;[51] [59] + ldi x4, 6 ;[52] +common7: + ld shift, y+ ;[53] + tst cnt ;[55] + out USBOUT, x1 ;[56] <--- set bit + brne txByteLoop ;[57] + +;make SE0: + cbr x1, USBMASK ;[58] prepare SE0 [spec says EOP may be 15 to 18 cycles] + lds x2, usbNewDeviceAddr;[59] + lsl x2 ;[61] we compare with left shifted address + subi YL, 2 + 20 ;[62] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[63] + out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[01] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 12.5625 MHz +max frequency: 69.286 cycles for 8 bit -> 12.99 MHz +nominal frequency: 12.77 MHz ( = sqrt(min * max)) + +sampling positions: (next even number in range [+/- 0.5]) +cycle index range: 0 ... 66 +bits: +.5, 8.875, 17.25, 25.625, 34, 42.375, 50.75, 59.125 +[0/1], [9], [17], [25/+26], [34], [+42/43], [51], [59] + +bit number: 0 1 2 3 4 5 6 7 +spare cycles 1 2 1 2 1 1 1 0 + +operations to perform: duration cycle + ---------------- + eor fix, shift 1 -> 00 + andi phase, USBMASK 1 -> 08 + breq se0 1 -> 16 (moved to 11) + st y+, data 2 -> 24, 25 + mov data, fix 1 -> 33 + ser data 1 -> 41 + subi cnt, 1 1 -> 49 + brcs overflow 1 -> 50 + +layout of samples and operations: +[##] = sample bit +<##> = sample phase +*##* = operation + +0: *00* [01] 02 03 04 <05> 06 07 +1: *08* [09] 10 11 12 <13> 14 15 *16* +2: [17] 18 19 20 <21> 22 23 +3: *24* *25* [26] 27 28 29 <30> 31 32 +4: *33* [34] 35 36 37 <38> 39 40 +5: *41* [42] 43 44 45 <46> 47 48 +6: *49* *50* [51] 52 53 54 <55> 56 57 58 +7: [59] 60 61 62 <63> 64 65 66 +*****************************************************************************/ + +/* we prefer positive expressions (do if condition) instead of negative + * (skip if condition), therefore use defines for skip instructions: + */ +#define ifioclr sbis +#define ifioset sbic +#define ifrclr sbrs +#define ifrset sbrc + +/* The registers "fix" and "data" swap their meaning during the loop. Use + * defines to keep their name constant. + */ +#define fix x2 +#define data x1 +#undef phase /* phase has a default definition to x4 */ +#define phase x3 + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt, r0 + push YL ;2 push only what is necessary to sync with edge ASAP + in YL, SREG ;1 + push YL ;2 +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of 1/4 bit which meets the spec. + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS ;[0] + rjmp foundK ;[1] +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError + +foundK: +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push YH ;[2] + lds YL, usbInputBufOffset;[4] + clr YH ;[6] + subi YL, lo8(-(usbRxBuf));[7] + sbci YH, hi8(-(usbRxBuf));[8] + + sbis USBIN, USBMINUS ;[9] we want two bits K [we want to sample at 8 + 4 - 1.5 = 10.5] + rjmp haveTwoBitsK ;[10] + pop YH ;[11] undo the push from before + rjmp waitForK ;[13] this was not the end of sync, retry +haveTwoBitsK: +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +#define fix x2 +#define data x1 + + push shift ;[12] + push x1 ;[14] + push x2 ;[16] + ldi shift, 0x80 ;[18] prevent bit-unstuffing but init low bits to 0 + ifioset USBIN, USBMINUS ;[19] [01] <--- bit 0 [10.5 + 8 = 18.5] + ori shift, 1<<0 ;[02] + push x3 ;[03] + push cnt ;[05] + push r0 ;[07] + ifioset USBIN, USBMINUS ;[09] <--- bit 1 + ori shift, 1<<1 ;[10] + ser fix ;[11] + ldi cnt, USB_BUFSIZE ;[12] + mov data, shift ;[13] + lsl shift ;[14] + nop2 ;[15] + ifioset USBIN, USBMINUS ;[17] <--- bit 2 + ori data, 3<<2 ;[18] store in bit 2 AND bit 3 + eor shift, data ;[19] do nrzi decoding + andi data, 1<<3 ;[20] + in phase, USBIN ;[21] <- phase + brne jumpToEntryAfterSet ;[22] if USBMINS at bit 3 was 1 + nop ;[23] + rjmp entryAfterClr ;[24] +jumpToEntryAfterSet: + rjmp entryAfterSet ;[24] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +#undef fix +#define fix x1 +#undef data +#define data x2 + +bit7IsSet: + ifrclr phase, USBMINUS ;[62] check phase only if D- changed + lpm ;[63] + in phase, USBIN ;[64] <- phase (one cycle too late) + ori shift, 1 << 7 ;[65] + nop ;[66] +;;;;rjmp bit0AfterSet ; -> [00] == [67] moved block up to save jump +bit0AfterSet: + eor fix, shift ;[00] +#undef fix +#define fix x2 +#undef data +#define data x1 /* we now have result in data, fix is reset to 0xff */ + ifioclr USBIN, USBMINUS ;[01] <--- sample 0 + rjmp bit0IsClr ;[02] + andi shift, ~(7 << 0) ;[03] + breq unstuff0s ;[04] + in phase, USBIN ;[05] <- phase + rjmp bit1AfterSet ;[06] +unstuff0s: + in phase, USBIN ;[06] <- phase (one cycle too late) + andi fix, ~(1 << 0) ;[07] + ifioclr USBIN, USBMINUS ;[00] + ifioset USBIN, USBPLUS ;[01] + rjmp bit0IsClr ;[02] executed if first expr false or second true +se0AndStore: ; executed only if both bits 0 + st y+, x1 ;[15/17] cycles after start of byte + rjmp se0 ;[17/19] + +bit0IsClr: + ifrset phase, USBMINUS ;[04] check phase only if D- changed + lpm ;[05] + in phase, USBIN ;[06] <- phase (one cycle too late) + ori shift, 1 << 0 ;[07] +bit1AfterClr: + andi phase, USBMASK ;[08] + ifioset USBIN, USBMINUS ;[09] <--- sample 1 + rjmp bit1IsSet ;[10] + breq se0AndStore ;[11] if D- was 0 in bits 0 AND 1 and D+ was 0 in between, we have SE0 + andi shift, ~(7 << 1) ;[12] + in phase, USBIN ;[13] <- phase + breq unstuff1c ;[14] + rjmp bit2AfterClr ;[15] +unstuff1c: + andi fix, ~(1 << 1) ;[16] + nop2 ;[08] + nop2 ;[10] +bit1IsSet: + ifrclr phase, USBMINUS ;[12] check phase only if D- changed + lpm ;[13] + in phase, USBIN ;[14] <- phase (one cycle too late) + ori shift, 1 << 1 ;[15] + nop ;[16] +bit2AfterSet: + ifioclr USBIN, USBMINUS ;[17] <--- sample 2 + rjmp bit2IsClr ;[18] + andi shift, ~(7 << 2) ;[19] + breq unstuff2s ;[20] + in phase, USBIN ;[21] <- phase + rjmp bit3AfterSet ;[22] +unstuff2s: + in phase, USBIN ;[22] <- phase (one cycle too late) + andi fix, ~(1 << 2) ;[23] + nop2 ;[16] + nop2 ;[18] +bit2IsClr: + ifrset phase, USBMINUS ;[20] check phase only if D- changed + lpm ;[21] + in phase, USBIN ;[22] <- phase (one cycle too late) + ori shift, 1 << 2 ;[23] +bit3AfterClr: + st y+, data ;[24] +entryAfterClr: + ifioset USBIN, USBMINUS ;[26] <--- sample 3 + rjmp bit3IsSet ;[27] + andi shift, ~(7 << 3) ;[28] + breq unstuff3c ;[29] + in phase, USBIN ;[30] <- phase + rjmp bit4AfterClr ;[31] +unstuff3c: + in phase, USBIN ;[31] <- phase (one cycle too late) + andi fix, ~(1 << 3) ;[32] + nop2 ;[25] + nop2 ;[27] +bit3IsSet: + ifrclr phase, USBMINUS ;[29] check phase only if D- changed + lpm ;[30] + in phase, USBIN ;[31] <- phase (one cycle too late) + ori shift, 1 << 3 ;[32] +bit4AfterSet: + mov data, fix ;[33] undo this move by swapping defines +#undef fix +#define fix x1 +#undef data +#define data x2 + ifioclr USBIN, USBMINUS ;[34] <--- sample 4 + rjmp bit4IsClr ;[35] + andi shift, ~(7 << 4) ;[36] + breq unstuff4s ;[37] + in phase, USBIN ;[38] <- phase + rjmp bit5AfterSet ;[39] +unstuff4s: + in phase, USBIN ;[39] <- phase (one cycle too late) + andi fix, ~(1 << 4) ;[40] + nop2 ;[33] + nop2 ;[35] +bit4IsClr: + ifrset phase, USBMINUS ;[37] check phase only if D- changed + lpm ;[38] + in phase, USBIN ;[39] <- phase (one cycle too late) + ori shift, 1 << 4 ;[40] +bit5AfterClr: + ser data ;[41] + ifioset USBIN, USBMINUS ;[42] <--- sample 5 + rjmp bit5IsSet ;[43] + andi shift, ~(7 << 5) ;[44] + breq unstuff5c ;[45] + in phase, USBIN ;[46] <- phase + rjmp bit6AfterClr ;[47] +unstuff5c: + in phase, USBIN ;[47] <- phase (one cycle too late) + andi fix, ~(1 << 5) ;[48] + nop2 ;[41] + nop2 ;[43] +bit5IsSet: + ifrclr phase, USBMINUS ;[45] check phase only if D- changed + lpm ;[46] + in phase, USBIN ;[47] <- phase (one cycle too late) + ori shift, 1 << 5 ;[48] +bit6AfterSet: + subi cnt, 1 ;[49] + brcs jumpToOverflow ;[50] + ifioclr USBIN, USBMINUS ;[51] <--- sample 6 + rjmp bit6IsClr ;[52] + andi shift, ~(3 << 6) ;[53] + cpi shift, 2 ;[54] + in phase, USBIN ;[55] <- phase + brlt unstuff6s ;[56] + rjmp bit7AfterSet ;[57] + +jumpToOverflow: + rjmp overflow + +unstuff6s: + andi fix, ~(1 << 6) ;[50] + lpm ;[51] +bit6IsClr: + ifrset phase, USBMINUS ;[54] check phase only if D- changed + lpm ;[55] + in phase, USBIN ;[56] <- phase (one cycle too late) + ori shift, 1 << 6 ;[57] + nop ;[58] +bit7AfterClr: + ifioset USBIN, USBMINUS ;[59] <--- sample 7 + rjmp bit7IsSet ;[60] + andi shift, ~(1 << 7) ;[61] + cpi shift, 4 ;[62] + in phase, USBIN ;[63] <- phase + brlt unstuff7c ;[64] + rjmp bit0AfterClr ;[65] -> [00] == [67] +unstuff7c: + andi fix, ~(1 << 7) ;[58] + nop ;[59] + rjmp bit7IsSet ;[60] + +bit7IsClr: + ifrset phase, USBMINUS ;[62] check phase only if D- changed + lpm ;[63] + in phase, USBIN ;[64] <- phase (one cycle too late) + ori shift, 1 << 7 ;[65] + nop ;[66] +;;;;rjmp bit0AfterClr ; -> [00] == [67] moved block up to save jump +bit0AfterClr: + eor fix, shift ;[00] +#undef fix +#define fix x2 +#undef data +#define data x1 /* we now have result in data, fix is reset to 0xff */ + ifioset USBIN, USBMINUS ;[01] <--- sample 0 + rjmp bit0IsSet ;[02] + andi shift, ~(7 << 0) ;[03] + breq unstuff0c ;[04] + in phase, USBIN ;[05] <- phase + rjmp bit1AfterClr ;[06] +unstuff0c: + in phase, USBIN ;[06] <- phase (one cycle too late) + andi fix, ~(1 << 0) ;[07] + ifioclr USBIN, USBMINUS ;[00] + ifioset USBIN, USBPLUS ;[01] + rjmp bit0IsSet ;[02] executed if first expr false or second true + rjmp se0AndStore ;[03] executed only if both bits 0 +bit0IsSet: + ifrclr phase, USBMINUS ;[04] check phase only if D- changed + lpm ;[05] + in phase, USBIN ;[06] <- phase (one cycle too late) + ori shift, 1 << 0 ;[07] +bit1AfterSet: + andi shift, ~(7 << 1) ;[08] compensated by "ori shift, 1<<1" if bit1IsClr + ifioclr USBIN, USBMINUS ;[09] <--- sample 1 + rjmp bit1IsClr ;[10] + breq unstuff1s ;[11] + nop2 ;[12] do not check for SE0 if bit 0 was 1 + in phase, USBIN ;[14] <- phase (one cycle too late) + rjmp bit2AfterSet ;[15] +unstuff1s: + in phase, USBIN ;[13] <- phase + andi fix, ~(1 << 1) ;[14] + lpm ;[07] + nop2 ;[10] +bit1IsClr: + ifrset phase, USBMINUS ;[12] check phase only if D- changed + lpm ;[13] + in phase, USBIN ;[14] <- phase (one cycle too late) + ori shift, 1 << 1 ;[15] + nop ;[16] +bit2AfterClr: + ifioset USBIN, USBMINUS ;[17] <--- sample 2 + rjmp bit2IsSet ;[18] + andi shift, ~(7 << 2) ;[19] + breq unstuff2c ;[20] + in phase, USBIN ;[21] <- phase + rjmp bit3AfterClr ;[22] +unstuff2c: + in phase, USBIN ;[22] <- phase (one cycle too late) + andi fix, ~(1 << 2) ;[23] + nop2 ;[16] + nop2 ;[18] +bit2IsSet: + ifrclr phase, USBMINUS ;[20] check phase only if D- changed + lpm ;[21] + in phase, USBIN ;[22] <- phase (one cycle too late) + ori shift, 1 << 2 ;[23] +bit3AfterSet: + st y+, data ;[24] +entryAfterSet: + ifioclr USBIN, USBMINUS ;[26] <--- sample 3 + rjmp bit3IsClr ;[27] + andi shift, ~(7 << 3) ;[28] + breq unstuff3s ;[29] + in phase, USBIN ;[30] <- phase + rjmp bit4AfterSet ;[31] +unstuff3s: + in phase, USBIN ;[31] <- phase (one cycle too late) + andi fix, ~(1 << 3) ;[32] + nop2 ;[25] + nop2 ;[27] +bit3IsClr: + ifrset phase, USBMINUS ;[29] check phase only if D- changed + lpm ;[30] + in phase, USBIN ;[31] <- phase (one cycle too late) + ori shift, 1 << 3 ;[32] +bit4AfterClr: + mov data, fix ;[33] undo this move by swapping defines +#undef fix +#define fix x1 +#undef data +#define data x2 + ifioset USBIN, USBMINUS ;[34] <--- sample 4 + rjmp bit4IsSet ;[35] + andi shift, ~(7 << 4) ;[36] + breq unstuff4c ;[37] + in phase, USBIN ;[38] <- phase + rjmp bit5AfterClr ;[39] +unstuff4c: + in phase, USBIN ;[39] <- phase (one cycle too late) + andi fix, ~(1 << 4) ;[40] + nop2 ;[33] + nop2 ;[35] +bit4IsSet: + ifrclr phase, USBMINUS ;[37] check phase only if D- changed + lpm ;[38] + in phase, USBIN ;[39] <- phase (one cycle too late) + ori shift, 1 << 4 ;[40] +bit5AfterSet: + ser data ;[41] + ifioclr USBIN, USBMINUS ;[42] <--- sample 5 + rjmp bit5IsClr ;[43] + andi shift, ~(7 << 5) ;[44] + breq unstuff5s ;[45] + in phase, USBIN ;[46] <- phase + rjmp bit6AfterSet ;[47] +unstuff5s: + in phase, USBIN ;[47] <- phase (one cycle too late) + andi fix, ~(1 << 5) ;[48] + nop2 ;[41] + nop2 ;[43] +bit5IsClr: + ifrset phase, USBMINUS ;[45] check phase only if D- changed + lpm ;[46] + in phase, USBIN ;[47] <- phase (one cycle too late) + ori shift, 1 << 5 ;[48] +bit6AfterClr: + subi cnt, 1 ;[49] + brcs overflow ;[50] + ifioset USBIN, USBMINUS ;[51] <--- sample 6 + rjmp bit6IsSet ;[52] + andi shift, ~(3 << 6) ;[53] + cpi shift, 2 ;[54] + in phase, USBIN ;[55] <- phase + brlt unstuff6c ;[56] + rjmp bit7AfterClr ;[57] +unstuff6c: + andi fix, ~(1 << 6) ;[50] + lpm ;[51] +bit6IsSet: + ifrclr phase, USBMINUS ;[54] check phase only if D- changed + lpm ;[55] + in phase, USBIN ;[56] <- phase (one cycle too late) + ori shift, 1 << 6 ;[57] +bit7AfterSet: + ifioclr USBIN, USBMINUS ;[59] <--- sample 7 + rjmp bit7IsClr ;[60] + andi shift, ~(1 << 7) ;[61] + cpi shift, 4 ;[62] + in phase, USBIN ;[63] <- phase + brlt unstuff7s ;[64] + rjmp bit0AfterSet ;[65] -> [00] == [67] +unstuff7s: + andi fix, ~(1 << 7) ;[58] + nop ;[59] + rjmp bit7IsClr ;[60] + +macro POP_STANDARD ; 14 cycles + pop r0 + pop cnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;---------------------------------------------------------------------------- +; Transmitting data +;---------------------------------------------------------------------------- + +txByteLoop: +txBitloop: +stuffN1Delay: ; [03] + ror shift ;[-5] [11] [63] + brcc doExorN1 ;[-4] [64] + subi x3, 1 ;[-3] + brne commonN1 ;[-2] + lsl shift ;[-1] compensate ror after rjmp stuffDelay + nop ;[00] stuffing consists of just waiting 8 cycles + rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear + +sendNakAndReti: + ldi cnt, USBPID_NAK ;[-19] + rjmp sendCntAndReti ;[-18] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov r0, cnt ;[-16] + ldi YL, 0 ;[-15] R0 address is 0 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 +; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 +; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte +;uses: x1...x3, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x3 = bitstuff cnt] +;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) +usbSendAndReti: + in x2, USBDDR ;[-10] 10 cycles until SOP + ori x2, USBMASK ;[-9] + sbi USBOUT, USBMINUS ;[-8] prepare idle state; D+ and D- must have been 0 (no pullups) + out USBDDR, x2 ;[-6] <--- acquire bus + in x1, USBOUT ;[-5] port mirror for tx loop + ldi shift, 0x40 ;[-4] sync byte is first byte sent (we enter loop after ror) + ldi x2, USBMASK ;[-3] +doExorN1: + eor x1, x2 ;[-2] [06] [62] + ldi x3, 6 ;[-1] [07] [63] +commonN1: +stuffN2Delay: + out USBOUT, x1 ;[00] [08] [64] <--- set bit + ror shift ;[01] + brcc doExorN2 ;[02] + subi x3, 1 ;[03] + brne commonN2 ;[04] + lsl shift ;[05] compensate ror after rjmp stuffDelay + rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear +doExorN2: + eor x1, x2 ;[04] [12] + ldi x3, 6 ;[05] [13] +commonN2: + nop2 ;[06] [14] + subi cnt, 171 ;[08] [16] trick: (3 * 171) & 0xff = 1 + out USBOUT, x1 ;[09] [17] <--- set bit + brcs txBitloop ;[10] [27] [44] + +stuff6Delay: + ror shift ;[45] [53] + brcc doExor6 ;[46] + subi x3, 1 ;[47] + brne common6 ;[48] + lsl shift ;[49] compensate ror after rjmp stuffDelay + nop ;[50] stuffing consists of just waiting 8 cycles + rjmp stuff6Delay ;[51] after ror, C bit is reliably clear +doExor6: + eor x1, x2 ;[48] [56] + ldi x3, 6 ;[49] +common6: +stuff7Delay: + ror shift ;[50] [58] + out USBOUT, x1 ;[51] <--- set bit + brcc doExor7 ;[52] + subi x3, 1 ;[53] + brne common7 ;[54] + lsl shift ;[55] compensate ror after rjmp stuffDelay + rjmp stuff7Delay ;[56] after ror, C bit is reliably clear +doExor7: + eor x1, x2 ;[54] [62] + ldi x3, 6 ;[55] +common7: + ld shift, y+ ;[56] + nop ;[58] + tst cnt ;[59] + out USBOUT, x1 ;[60] [00]<--- set bit + brne txByteLoop ;[61] [01] +;make SE0: + cbr x1, USBMASK ;[02] prepare SE0 [spec says EOP may be 15 to 18 cycles] + lds x2, usbNewDeviceAddr;[03] + lsl x2 ;[05] we compare with left shifted address + subi YL, 2 + 0 ;[06] Only assign address on data packets, not ACK/NAK in r0 + sbci YH, 0 ;[07] + out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[01] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 0) + echo "$s\n"; + } +} + +function printBit($isAfterSet, $bitNum) +{ + ob_start(); + if($isAfterSet){ +?> + ifioclr USBIN, USBMINUS ;[00] <--- sample + rjmp bit#IsClr ;[01] + andi shift, ~(7 << #) ;[02] + breq unstuff#s ;[03] + in phase, USBIN ;[04] <- phase + rjmp bit@AfterSet ;[05] +unstuff#s: + in phase, USBIN ;[05] <- phase (one cycle too late) + andi fix, ~(1 << #) ;[06] + nop2 ;[-1] + nop2 ;[01] +bit#IsClr: + ifrset phase, USBMINUS ;[03] check phase only if D- changed + lpm ;[04] + in phase, USBIN ;[05] <- phase (one cycle too late) + ori shift, 1 << # ;[06] + + ifioset USBIN, USBMINUS ;[00] <--- sample + rjmp bit#IsSet ;[01] + andi shift, ~(7 << #) ;[02] + breq unstuff#c ;[03] + in phase, USBIN ;[04] <- phase + rjmp bit@AfterClr ;[05] +unstuff#c: + in phase, USBIN ;[05] <- phase (one cycle too late) + andi fix, ~(1 << #) ;[06] + nop2 ;[-1] + nop2 ;[01] +bit#IsSet: + ifrclr phase, USBMINUS ;[03] check phase only if D- changed + lpm ;[04] + in phase, USBIN ;[05] <- phase (one cycle too late) + ori shift, 1 << # ;[06] + +*****************************************************************************/ diff --git a/classic/fw/usbdrv/usbdrvasm15.inc b/classic/fw/usbdrv/usbdrvasm15.inc new file mode 100644 index 0000000..33bcf0e --- /dev/null +++ b/classic/fw/usbdrv/usbdrvasm15.inc @@ -0,0 +1,422 @@ +/* Name: usbdrvasm15.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: contributed by V. Bosch + * Creation Date: 2007-08-06 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 15 MHz version of the asssembler part of the USB driver. It +requires a 15 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! +*/ + +;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 15 MHz -> 10.0 cycles per bit, 80.0 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + +;---------------------------------------------------------------------------- +; order of registers pushed: +; YL, SREG [sofError] YH, shift, x1, x2, x3, bitcnt, cnt, x4 +;---------------------------------------------------------------------------- +USB_INTR_VECTOR: + push YL ;2 push only what is necessary to sync with edge ASAP + in YL, SREG ;1 + push YL ;2 +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +; +; sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +; sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +;------------------------------------------------------------------------------- +; The following code results in a sampling window of < 1/4 bit +; which meets the spec. +;------------------------------------------------------------------------------- +waitForK: ;- + sbis USBIN, USBMINUS ;1 [00] <-- sample + rjmp foundK ;2 [01] + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +;------------------------------------------------------------------------------ +; {3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for +; center sampling] +; we have 1 bit time for setup purposes, then sample again. +; Numbers in brackets are cycles from center of first sync (double K) +; bit after the instruction +;------------------------------------------------------------------------------ +foundK: ;- [02] + lds YL, usbInputBufOffset;2 [03+04] tx loop + push YH ;2 [05+06] + clr YH ;1 [07] + subi YL, lo8(-(usbRxBuf)) ;1 [08] [rx loop init] + sbci YH, hi8(-(usbRxBuf)) ;1 [09] [rx loop init] + push shift ;2 [10+11] + ser shift ;1 [12] + sbis USBIN, USBMINUS ;1 [-1] [13] <--sample:we want two bits K (sample 1 cycle too early) + rjmp haveTwoBitsK ;2 [00] [14] + pop shift ;2 [15+16] undo the push from before + pop YH ;2 [17+18] undo the push from before + rjmp waitForK ;2 [19+20] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 20 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: ;- [01] + push x1 ;2 [02+03] + push x2 ;2 [04+05] + push x3 ;2 [06+07] + push bitcnt ;2 [08+09] + in x1, USBIN ;1 [00] [10] <-- sample bit 0 + bst x1, USBMINUS ;1 [01] + bld shift, 0 ;1 [02] + push cnt ;2 [03+04] + ldi cnt, USB_BUFSIZE ;1 [05] + push x4 ;2 [06+07] tx loop + rjmp rxLoop ;2 [08] +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +unstuff0: ;- [07] (branch taken) + andi x3, ~0x01 ;1 [08] + mov x1, x2 ;1 [09] x2 contains last sampled (stuffed) bit + in x2, USBIN ;1 [00] [10] <-- sample bit 1 again + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 1 + ori shift, 0x01 ;1 [03] 0b00000001 + nop ;1 [04] + rjmp didUnstuff0 ;2 [05] +;----------------------------------------------------- +unstuff1: ;- [05] (branch taken) + mov x2, x1 ;1 [06] x1 contains last sampled (stuffed) bit + andi x3, ~0x02 ;1 [07] + ori shift, 0x02 ;1 [08] 0b00000010 + nop ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 2 again + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 2 + rjmp didUnstuff1 ;2 [03] +;----------------------------------------------------- +unstuff2: ;- [05] (branch taken) + andi x3, ~0x04 ;1 [06] + ori shift, 0x04 ;1 [07] 0b00000100 + mov x1, x2 ;1 [08] x2 contains last sampled (stuffed) bit + nop ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample bit 3 + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 3 + rjmp didUnstuff2 ;2 [03] +;----------------------------------------------------- +unstuff3: ;- [00] [10] (branch taken) + in x2, USBIN ;1 [01] [11] <-- sample stuffed bit 3 one cycle too late + andi x2, USBMASK ;1 [02] + breq se0Hop ;1 [03] SE0 check for stuffed bit 3 + andi x3, ~0x08 ;1 [04] + ori shift, 0x08 ;1 [05] 0b00001000 + rjmp didUnstuff3 ;2 [06] +;---------------------------------------------------------------------------- +; extra jobs done during bit interval: +; +; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs], +; overflow check, jump to the head of rxLoop +; bit 1: SE0 check +; bit 2: SE0 check, recovery from delay [bit 0 tasks took too long] +; bit 3: SE0 check, recovery from delay [bit 0 tasks took too long] +; bit 4: SE0 check, none +; bit 5: SE0 check, none +; bit 6: SE0 check, none +; bit 7: SE0 check, reconstruct: x3 is 0 at bit locations we changed, 1 at others +;---------------------------------------------------------------------------- +rxLoop: ;- [09] + in x2, USBIN ;1 [00] [10] <-- sample bit 1 (or possibly bit 0 stuffed) + andi x2, USBMASK ;1 [01] + brne SkipSe0Hop ;1 [02] +se0Hop: ;- [02] + rjmp se0 ;2 [03] SE0 check for bit 1 +SkipSe0Hop: ;- [03] + ser x3 ;1 [04] + andi shift, 0xf9 ;1 [05] 0b11111001 + breq unstuff0 ;1 [06] +didUnstuff0: ;- [06] + eor x1, x2 ;1 [07] + bst x1, USBMINUS ;1 [08] + bld shift, 1 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 2 (or possibly bit 1 stuffed) + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 2 + andi shift, 0xf3 ;1 [03] 0b11110011 + breq unstuff1 ;1 [04] do remaining work for bit 1 +didUnstuff1: ;- [04] + eor x2, x1 ;1 [05] + bst x2, USBMINUS ;1 [06] + bld shift, 2 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 3 (or possibly bit 2 stuffed) + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 3 + andi shift, 0xe7 ;1 [03] 0b11100111 + breq unstuff2 ;1 [04] +didUnstuff2: ;- [04] + eor x1, x2 ;1 [05] + bst x1, USBMINUS ;1 [06] + bld shift, 3 ;1 [07] +didUnstuff3: ;- [07] + andi shift, 0xcf ;1 [08] 0b11001111 + breq unstuff3 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 4 + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 4 + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 4 ;1 [05] +didUnstuff4: ;- [05] + andi shift, 0x9f ;1 [06] 0b10011111 + breq unstuff4 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 5 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 5 + eor x1, x2 ;1 [03] + bst x1, USBMINUS ;1 [04] + bld shift, 5 ;1 [05] +didUnstuff5: ;- [05] + andi shift, 0x3f ;1 [06] 0b00111111 + breq unstuff5 ;1 [07] + nop2 ;2 [08+09] + in x1, USBIN ;1 [00] [10] <-- sample bit 6 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 6 + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 6 ;1 [05] +didUnstuff6: ;- [05] + cpi shift, 0x02 ;1 [06] 0b00000010 + brlo unstuff6 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 7 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 7 + eor x1, x2 ;1 [03] + bst x1, USBMINUS ;1 [04] + bld shift, 7 ;1 [05] +didUnstuff7: ;- [05] + cpi shift, 0x04 ;1 [06] 0b00000100 + brlo unstuff7 ;1 [07] + eor x3, shift ;1 [08] reconstruct: x3 is 0 at bit locations we changed, 1 at others + nop ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 0 + st y+, x3 ;2 [01+02] store data + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 0 ;1 [05] + subi cnt, 1 ;1 [06] + brcs overflow ;1 [07] + rjmp rxLoop ;2 [08] +;----------------------------------------------------- +unstuff4: ;- [08] + andi x3, ~0x10 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 4 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 4 + ori shift, 0x10 ;1 [03] + rjmp didUnstuff4 ;2 [04] +;----------------------------------------------------- +unstuff5: ;- [08] + ori shift, 0x20 ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 5 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 5 + andi x3, ~0x20 ;1 [03] + rjmp didUnstuff5 ;2 [04] +;----------------------------------------------------- +unstuff6: ;- [08] + andi x3, ~0x40 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 6 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 6 + ori shift, 0x40 ;1 [03] + rjmp didUnstuff6 ;2 [04] +;----------------------------------------------------- +unstuff7: ;- [08] + andi x3, ~0x80 ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 7 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 7 + ori shift, 0x80 ;1 [03] + rjmp didUnstuff7 ;2 [04] + +macro POP_STANDARD ; 16 cycles + pop x4 + pop cnt + pop bitcnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;--------------------------------------------------------------------------- +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies +;--------------------------------------------------------------------------- +bitstuffN: ;- [04] + eor x1, x4 ;1 [05] + clr x2 ;1 [06] + nop ;1 [07] + rjmp didStuffN ;1 [08] +;--------------------------------------------------------------------------- +bitstuff6: ;- [04] + eor x1, x4 ;1 [05] + clr x2 ;1 [06] + rjmp didStuff6 ;1 [07] +;--------------------------------------------------------------------------- +bitstuff7: ;- [02] + eor x1, x4 ;1 [03] + clr x2 ;1 [06] + nop ;1 [05] + rjmp didStuff7 ;1 [06] +;--------------------------------------------------------------------------- +sendNakAndReti: ;- [-19] + ldi x3, USBPID_NAK ;1 [-18] + rjmp sendX3AndReti ;1 [-17] +;--------------------------------------------------------------------------- +sendAckAndReti: ;- [-17] + ldi cnt, USBPID_ACK ;1 [-16] +sendCntAndReti: ;- [-16] + mov x3, cnt ;1 [-15] +sendX3AndReti: ;- [-15] + ldi YL, 20 ;1 [-14] x3==r20 address is 20 + ldi YH, 0 ;1 [-13] + ldi cnt, 2 ;1 [-12] +; rjmp usbSendAndReti fallthrough +;--------------------------------------------------------------------------- +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +;We need not to match the transfer rate exactly because the spec demands +;only 1.5% precision anyway. +usbSendAndReti: ;- [-13] 13 cycles until SOP + in x2, USBDDR ;1 [-12] + ori x2, USBMASK ;1 [-11] + sbi USBOUT, USBMINUS ;2 [-09-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;1 [-08] port mirror for tx loop + out USBDDR, x2 ;1 [-07] <- acquire bus + ; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;1 [-06] exor mask + ldi shift, 0x80 ;1 [-05] sync byte is first byte sent + ldi bitcnt, 6 ;1 [-04] +txBitLoop: ;- [-04] [06] + sbrs shift, 0 ;1 [-03] [07] + eor x1, x4 ;1 [-02] [08] + ror shift ;1 [-01] [09] +didStuffN: ;- [09] + out USBOUT, x1 ;1 [00] [10] <-- out N + ror x2 ;1 [01] + cpi x2, 0xfc ;1 [02] + brcc bitstuffN ;1 [03] + dec bitcnt ;1 [04] + brne txBitLoop ;1 [05] + sbrs shift, 0 ;1 [06] + eor x1, x4 ;1 [07] + ror shift ;1 [08] +didStuff6: ;- [08] + nop ;1 [09] + out USBOUT, x1 ;1 [00] [10] <-- out 6 + ror x2 ;1 [01] + cpi x2, 0xfc ;1 [02] + brcc bitstuff6 ;1 [03] + sbrs shift, 0 ;1 [04] + eor x1, x4 ;1 [05] + ror shift ;1 [06] + ror x2 ;1 [07] +didStuff7: ;- [07] + ldi bitcnt, 6 ;1 [08] + cpi x2, 0xfc ;1 [09] + out USBOUT, x1 ;1 [00] [10] <-- out 7 + brcc bitstuff7 ;1 [01] + ld shift, y+ ;2 [02+03] + dec cnt ;1 [04] + brne txBitLoop ;1 [05] +makeSE0: + cbr x1, USBMASK ;1 [06] prepare SE0 [spec says EOP may be 19 to 23 cycles] + lds x2, usbNewDeviceAddr;2 [07+08] + lsl x2 ;1 [09] we compare with left shifted address +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + out USBOUT, x1 ;1 [00] [10] <-- out SE0-- from now 2 bits==20 cycl. until bus idle + subi YL, 20 + 2 ;1 [01] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;1 [02] + breq skipAddrAssign ;1 [03] + sts usbDeviceAddr, x2 ;2 [04+05] if not skipped: SE0 is one cycle longer +;---------------------------------------------------------------------------- +;end of usbDeviceAddress transfer +skipAddrAssign: ;- [03/04] + ldi x2, 1< 10.6666666 cycles per bit, 85.333333333 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt + push YL ;[-25] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-23] + push YL ;[-22] + push YH ;[-20] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-15] + rjmp foundK ;[-14] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-12] +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push bitcnt ;[-12] +; [---] ;[-11] + lds YL, usbInputBufOffset;[-10] +; [---] ;[-9] + clr YH ;[-8] + subi YL, lo8(-(usbRxBuf));[-7] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-6] [rx loop init] + push shift ;[-5] +; [---] ;[-4] + ldi bitcnt, 0x55 ;[-3] [rx loop init] + sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) + rjmp haveTwoBitsK ;[-1] + pop shift ;[0] undo the push from before + pop bitcnt ;[2] undo the push from before + rjmp waitForK ;[4] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 21 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[1] + push x2 ;[3] + push x3 ;[5] + ldi shift, 0 ;[7] + ldi x3, 1<<4 ;[8] [rx loop init] first sample is inverse bit, compensate that + push x4 ;[9] == leap + + in x1, USBIN ;[11] <-- sample bit 0 + andi x1, USBMASK ;[12] + bst x1, USBMINUS ;[13] + bld shift, 7 ;[14] + push cnt ;[15] + ldi leap, 0 ;[17] [rx loop init] + ldi cnt, USB_BUFSIZE;[18] [rx loop init] + rjmp rxbit1 ;[19] arrives at [21] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +; duration of unstuffing code should be 10.66666667 cycles. We adjust "leap" +; accordingly to approximate this value in the long run. + +unstuff6: + andi x2, USBMASK ;[03] + ori x3, 1<<6 ;[04] will not be shifted any more + andi shift, ~0x80;[05] + mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6 + subi leap, -1 ;[07] total duration = 11 bits -> subtract 1/3 + rjmp didUnstuff6 ;[08] + +unstuff7: + ori x3, 1<<7 ;[09] will not be shifted any more + in x2, USBIN ;[00] [10] re-sample bit 7 + andi x2, USBMASK ;[01] + andi shift, ~0x80;[02] + subi leap, 2 ;[03] total duration = 10 bits -> add 1/3 + rjmp didUnstuff7 ;[04] + +unstuffEven: + ori x3, 1<<6 ;[09] will be shifted right 6 times for bit 0 + in x1, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x1, USBMASK ;[02] + breq se0 ;[03] + subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 + nop2 ;[05] + rjmp didUnstuffE ;[06] + +unstuffOdd: + ori x3, 1<<5 ;[09] will be shifted right 4 times for bit 1 + in x2, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x2, USBMASK ;[02] + breq se0 ;[03] + subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 + nop2 ;[05] + rjmp didUnstuffO ;[06] + +rxByteLoop: + andi x1, USBMASK ;[03] + eor x2, x1 ;[04] + subi leap, 1 ;[05] + brpl skipLeap ;[06] + subi leap, -3 ;1 one leap cycle every 3rd byte -> 85 + 1/3 cycles per byte + nop ;1 +skipLeap: + subi x2, 1 ;[08] + ror shift ;[09] +didUnstuff6: + cpi shift, 0xfc ;[10] + in x2, USBIN ;[00] [11] <-- sample bit 7 + brcc unstuff6 ;[01] + andi x2, USBMASK ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] +didUnstuff7: + cpi shift, 0xfc ;[06] + brcc unstuff7 ;[07] + eor x3, shift ;[08] reconstruct: x3 is 1 at bit locations we changed, 0 at others + st y+, x3 ;[09] store data +rxBitLoop: + in x1, USBIN ;[00] [11] <-- sample bit 0/2/4 + andi x1, USBMASK ;[01] + eor x2, x1 ;[02] + andi x3, 0x3f ;[03] topmost two bits reserved for 6 and 7 + subi x2, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffEven ;[07] +didUnstuffE: + lsr x3 ;[08] + lsr x3 ;[09] +rxbit1: + in x2, USBIN ;[00] [10] <-- sample bit 1/3/5 + andi x2, USBMASK ;[01] + breq se0 ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffOdd ;[07] +didUnstuffO: + subi bitcnt, 0xab;[08] == addi 0x55, 0x55 = 0x100/3 + brcs rxBitLoop ;[09] + + subi cnt, 1 ;[10] + in x1, USBIN ;[00] [11] <-- sample bit 6 + brcc rxByteLoop ;[01] + rjmp overflow + +macro POP_STANDARD ; 14 cycles + pop cnt + pop x4 + pop x3 + pop x2 + pop x1 + pop shift + pop bitcnt + endm +macro POP_RETI ; 7 cycles + pop YH + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies + +bitstuffN: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] + nop2 ;[7] + nop ;[9] + out USBOUT, x1 ;[10] <-- out + rjmp didStuffN ;[0] + +bitstuff6: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] Carry is zero due to brcc + rol shift ;[7] compensate for ror shift at branch destination + rjmp didStuff6 ;[8] + +bitstuff7: + ldi x2, 0 ;[2] Carry is zero due to brcc + rjmp didStuff7 ;[3] + + +sendNakAndReti: + ldi x3, USBPID_NAK ;[-18] + rjmp sendX3AndReti ;[-17] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov x3, cnt ;[-16] +sendX3AndReti: + ldi YL, 20 ;[-15] x3==r20 address is 20 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +;We don't match the transfer rate exactly (don't insert leap cycles every third +;byte) because the spec demands only 1.5% precision anyway. +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-6] exor mask + ldi shift, 0x80 ;[-5] sync byte is first byte sent +txByteLoop: + ldi bitcnt, 0x35 ;[-4] [6] binary 0011 0101 +txBitLoop: + sbrs shift, 0 ;[-3] [7] + eor x1, x4 ;[-2] [8] + out USBOUT, x1 ;[-1] [9] <-- out N + ror shift ;[0] [10] + ror x2 ;[1] +didStuffN: + cpi x2, 0xfc ;[2] + brcc bitstuffN ;[3] + lsr bitcnt ;[4] + brcc txBitLoop ;[5] + brne txBitLoop ;[6] + + sbrs shift, 0 ;[7] + eor x1, x4 ;[8] +didStuff6: + out USBOUT, x1 ;[-1] [9] <-- out 6 + ror shift ;[0] [10] + ror x2 ;[1] + cpi x2, 0xfc ;[2] + brcc bitstuff6 ;[3] + ror shift ;[4] +didStuff7: + ror x2 ;[5] + sbrs x2, 7 ;[6] + eor x1, x4 ;[7] + nop ;[8] + cpi x2, 0xfc ;[9] + out USBOUT, x1 ;[-1][10] <-- out 7 + brcc bitstuff7 ;[0] [11] + ld shift, y+ ;[1] + dec cnt ;[3] + brne txByteLoop ;[4] +;make SE0: + cbr x1, USBMASK ;[5] prepare SE0 [spec says EOP may be 21 to 25 cycles] + lds x2, usbNewDeviceAddr;[6] + lsl x2 ;[8] we compare with left shifted address + subi YL, 20 + 2 ;[9] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[10] + out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[0] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< max 52 cycles interrupt disable +;max stack usage: [ret(2), r0, SREG, YL, YH, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 16.5 MHz -> 11 cycles per bit +; 16.3125 MHz < F_CPU < 16.6875 MHz (+/- 1.1%) +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], r0, YH, shift, x1, x2, x3, x4, cnt + push YL ;[-23] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-21] + push YL ;[-20] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-15] + rjmp foundK ;[-14] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-12] +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push r0 ;[-12] +; [---] ;[-11] + push YH ;[-10] +; [---] ;[-9] + lds YL, usbInputBufOffset;[-8] +; [---] ;[-7] + clr YH ;[-6] + subi YL, lo8(-(usbRxBuf));[-5] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-4] [rx loop init] + mov r0, x2 ;[-3] [rx loop init] + sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) + rjmp haveTwoBitsK ;[-1] + pop YH ;[0] undo the pushes from before + pop r0 ;[2] + rjmp waitForK ;[4] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 22 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: ;[1] + push shift ;[1] + push x1 ;[3] + push x2 ;[5] + push x3 ;[7] + ldi shift, 0xff ;[9] [rx loop init] + ori x3, 0xff ;[10] [rx loop init] == ser x3, clear zero flag + + in x1, USBIN ;[11] <-- sample bit 0 + bst x1, USBMINUS ;[12] + bld shift, 0 ;[13] + push x4 ;[14] == phase +; [---] ;[15] + push cnt ;[16] +; [---] ;[17] + ldi phase, 0 ;[18] [rx loop init] + ldi cnt, USB_BUFSIZE;[19] [rx loop init] + rjmp rxbit1 ;[20] +; [---] ;[21] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +/* +byte oriented operations done during loop: +bit 0: store data +bit 1: SE0 check +bit 2: overflow check +bit 3: catch up +bit 4: rjmp to achieve conditional jump range +bit 5: PLL +bit 6: catch up +bit 7: jump, fixup bitstuff +; 87 [+ 2] cycles +------------------------------------------------------------------ +*/ +continueWithBit5: + in x2, USBIN ;[055] <-- bit 5 + eor r0, x2 ;[056] + or phase, r0 ;[057] + sbrc phase, USBMINUS ;[058] + lpm ;[059] optional nop3; modifies r0 + in phase, USBIN ;[060] <-- phase + eor x1, x2 ;[061] + bst x1, USBMINUS ;[062] + bld shift, 5 ;[063] + andi shift, 0x3f ;[064] + in x1, USBIN ;[065] <-- bit 6 + breq unstuff5 ;[066] *** unstuff escape + eor phase, x1 ;[067] + eor x2, x1 ;[068] + bst x2, USBMINUS ;[069] + bld shift, 6 ;[070] +didUnstuff6: ;[ ] + in r0, USBIN ;[071] <-- phase + cpi shift, 0x02 ;[072] + brlo unstuff6 ;[073] *** unstuff escape +didUnstuff5: ;[ ] + nop2 ;[074] +; [---] ;[075] + in x2, USBIN ;[076] <-- bit 7 + eor x1, x2 ;[077] + bst x1, USBMINUS ;[078] + bld shift, 7 ;[079] +didUnstuff7: ;[ ] + eor r0, x2 ;[080] + or phase, r0 ;[081] + in r0, USBIN ;[082] <-- phase + cpi shift, 0x04 ;[083] + brsh rxLoop ;[084] +; [---] ;[085] +unstuff7: ;[ ] + andi x3, ~0x80 ;[085] + ori shift, 0x80 ;[086] + in x2, USBIN ;[087] <-- sample stuffed bit 7 + nop ;[088] + rjmp didUnstuff7 ;[089] +; [---] ;[090] + ;[080] + +unstuff5: ;[067] + eor phase, x1 ;[068] + andi x3, ~0x20 ;[069] + ori shift, 0x20 ;[070] + in r0, USBIN ;[071] <-- phase + mov x2, x1 ;[072] + nop ;[073] + nop2 ;[074] +; [---] ;[075] + in x1, USBIN ;[076] <-- bit 6 + eor r0, x1 ;[077] + or phase, r0 ;[078] + eor x2, x1 ;[079] + bst x2, USBMINUS ;[080] + bld shift, 6 ;[081] no need to check bitstuffing, we just had one + in r0, USBIN ;[082] <-- phase + rjmp didUnstuff5 ;[083] +; [---] ;[084] + ;[074] + +unstuff6: ;[074] + andi x3, ~0x40 ;[075] + in x1, USBIN ;[076] <-- bit 6 again + ori shift, 0x40 ;[077] + nop2 ;[078] +; [---] ;[079] + rjmp didUnstuff6 ;[080] +; [---] ;[081] + ;[071] + +unstuff0: ;[013] + eor r0, x2 ;[014] + or phase, r0 ;[015] + andi x2, USBMASK ;[016] check for SE0 + in r0, USBIN ;[017] <-- phase + breq didUnstuff0 ;[018] direct jump to se0 would be too long + andi x3, ~0x01 ;[019] + ori shift, 0x01 ;[020] + mov x1, x2 ;[021] mov existing sample + in x2, USBIN ;[022] <-- bit 1 again + rjmp didUnstuff0 ;[023] +; [---] ;[024] + ;[014] + +unstuff1: ;[024] + eor r0, x1 ;[025] + or phase, r0 ;[026] + andi x3, ~0x02 ;[027] + in r0, USBIN ;[028] <-- phase + ori shift, 0x02 ;[029] + mov x2, x1 ;[030] + rjmp didUnstuff1 ;[031] +; [---] ;[032] + ;[022] + +unstuff2: ;[035] + eor r0, x2 ;[036] + or phase, r0 ;[037] + andi x3, ~0x04 ;[038] + in r0, USBIN ;[039] <-- phase + ori shift, 0x04 ;[040] + mov x1, x2 ;[041] + rjmp didUnstuff2 ;[042] +; [---] ;[043] + ;[033] + +unstuff3: ;[043] + in x2, USBIN ;[044] <-- bit 3 again + eor r0, x2 ;[045] + or phase, r0 ;[046] + andi x3, ~0x08 ;[047] + ori shift, 0x08 ;[048] + nop ;[049] + in r0, USBIN ;[050] <-- phase + rjmp didUnstuff3 ;[051] +; [---] ;[052] + ;[042] + +unstuff4: ;[053] + andi x3, ~0x10 ;[054] + in x1, USBIN ;[055] <-- bit 4 again + ori shift, 0x10 ;[056] + rjmp didUnstuff4 ;[057] +; [---] ;[058] + ;[048] + +rxLoop: ;[085] + eor x3, shift ;[086] reconstruct: x3 is 0 at bit locations we changed, 1 at others + in x1, USBIN ;[000] <-- bit 0 + st y+, x3 ;[001] +; [---] ;[002] + eor r0, x1 ;[003] + or phase, r0 ;[004] + eor x2, x1 ;[005] + in r0, USBIN ;[006] <-- phase + ser x3 ;[007] + bst x2, USBMINUS ;[008] + bld shift, 0 ;[009] + andi shift, 0xf9 ;[010] +rxbit1: ;[ ] + in x2, USBIN ;[011] <-- bit 1 + breq unstuff0 ;[012] *** unstuff escape + andi x2, USBMASK ;[013] SE0 check for bit 1 +didUnstuff0: ;[ ] Z only set if we detected SE0 in bitstuff + breq se0 ;[014] + eor r0, x2 ;[015] + or phase, r0 ;[016] + in r0, USBIN ;[017] <-- phase + eor x1, x2 ;[018] + bst x1, USBMINUS ;[019] + bld shift, 1 ;[020] + andi shift, 0xf3 ;[021] +didUnstuff1: ;[ ] + in x1, USBIN ;[022] <-- bit 2 + breq unstuff1 ;[023] *** unstuff escape + eor r0, x1 ;[024] + or phase, r0 ;[025] + subi cnt, 1 ;[026] overflow check + brcs overflow ;[027] + in r0, USBIN ;[028] <-- phase + eor x2, x1 ;[029] + bst x2, USBMINUS ;[030] + bld shift, 2 ;[031] + andi shift, 0xe7 ;[032] +didUnstuff2: ;[ ] + in x2, USBIN ;[033] <-- bit 3 + breq unstuff2 ;[034] *** unstuff escape + eor r0, x2 ;[035] + or phase, r0 ;[036] + eor x1, x2 ;[037] + bst x1, USBMINUS ;[038] + in r0, USBIN ;[039] <-- phase + bld shift, 3 ;[040] + andi shift, 0xcf ;[041] +didUnstuff3: ;[ ] + breq unstuff3 ;[042] *** unstuff escape + nop ;[043] + in x1, USBIN ;[044] <-- bit 4 + eor x2, x1 ;[045] + bst x2, USBMINUS ;[046] + bld shift, 4 ;[047] +didUnstuff4: ;[ ] + eor r0, x1 ;[048] + or phase, r0 ;[049] + in r0, USBIN ;[050] <-- phase + andi shift, 0x9f ;[051] + breq unstuff4 ;[052] *** unstuff escape + rjmp continueWithBit5;[053] +; [---] ;[054] + +macro POP_STANDARD ; 16 cycles + pop cnt + pop x4 + pop x3 + pop x2 + pop x1 + pop shift + pop YH + pop r0 + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies + +bitstuff7: + eor x1, x4 ;[4] + ldi x2, 0 ;[5] + nop2 ;[6] C is zero (brcc) + rjmp didStuff7 ;[8] + +bitstuffN: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] + lpm ;[7] 3 cycle NOP, modifies r0 + out USBOUT, x1 ;[10] <-- out + rjmp didStuffN ;[0] + +#define bitStatus x3 + +sendNakAndReti: + ldi cnt, USBPID_NAK ;[-19] + rjmp sendCntAndReti ;[-18] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov r0, cnt ;[-16] + ldi YL, 0 ;[-15] R0 address is 0 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-6] exor mask + ldi shift, 0x80 ;[-5] sync byte is first byte sent + ldi bitStatus, 0xff ;[-4] init bit loop counter, works for up to 12 bytes +byteloop: +bitloop: + sbrs shift, 0 ;[8] [-3] + eor x1, x4 ;[9] [-2] + out USBOUT, x1 ;[10] [-1] <-- out + ror shift ;[0] + ror x2 ;[1] +didStuffN: + cpi x2, 0xfc ;[2] + brcc bitstuffN ;[3] + nop ;[4] + subi bitStatus, 37 ;[5] 256 / 7 ~=~ 37 + brcc bitloop ;[6] when we leave the loop, bitStatus has almost the initial value + sbrs shift, 0 ;[7] + eor x1, x4 ;[8] + ror shift ;[9] +didStuff7: + out USBOUT, x1 ;[10] <-- out + ror x2 ;[0] + cpi x2, 0xfc ;[1] + brcc bitstuff7 ;[2] + ld shift, y+ ;[3] + dec cnt ;[5] + brne byteloop ;[6] +;make SE0: + cbr x1, USBMASK ;[7] prepare SE0 [spec says EOP may be 21 to 25 cycles] + lds x2, usbNewDeviceAddr;[8] + lsl x2 ;[10] we compare with left shifted address + out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + subi YL, 2 ;[0] Only assign address on data packets, not ACK/NAK in r0 + sbci YH, 0 ;[1] + breq skipAddrAssign ;[2] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 12 cycles per bit +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts +;register use in receive loop to receive the data bytes: +; shift assembles the byte currently being received +; x1 holds the D+ and D- line state +; x2 holds the previous line state +; cnt holds the number of bytes left in the receive buffer +; x3 holds the higher crc byte (see algorithm below) +; x4 is used as temporary register for the crc algorithm +; x5 is used for unstuffing: when unstuffing the last received bit is inverted in shift (to prevent further +; unstuffing calls. In the same time the corresponding bit in x5 is cleared to mark the bit as beening iverted +; zl lower crc value and crc table index +; zh used for crc table accesses + +;-------------------------------------------------------------------------------------------------------------- +; CRC mods: +; table driven crc checker, Z points to table in prog space +; ZL is the lower crc byte, x3 is the higher crc byte +; x4 is used as temp register to store different results +; the initialization of the crc register is not 0xFFFF but 0xFE54. This is because during the receipt of the +; first data byte an virtual zero data byte is added to the crc register, this results in the correct initial +; value of 0xFFFF at beginning of the second data byte before the first data byte is added to the crc. +; The magic number 0xFE54 results form the crc table: At tabH[0x54] = 0xFF = crcH (required) and +; tabL[0x54] = 0x01 -> crcL = 0x01 xor 0xFE = 0xFF +; bitcnt is renamed to x5 and is used for unstuffing purposes, the unstuffing works like in the 12MHz version +;-------------------------------------------------------------------------------------------------------------- +; CRC algorithm: +; The crc register is formed by x3 (higher byte) and ZL (lower byte). The algorithm uses a 'reversed' form +; i.e. that it takes the least significant bit first and shifts to the right. So in fact the highest order +; bit seen from the polynomial devision point of view is the lsb of ZL. (If this sounds strange to you i +; propose a research on CRC :-) ) +; Each data byte received is xored to ZL, the lower crc byte. This byte now builds the crc +; table index. Next the new high byte is loaded from the table and stored in x4 until we have space in x3 +; (its destination). +; Afterwards the lower table is loaded from the table and stored in ZL (the old index is overwritten as +; we don't need it anymore. In fact this is a right shift by 8 bits.) Now the old crc high value is xored +; to ZL, this is the second shift of the old crc value. Now x4 (the temp reg) is moved to x3 and the crc +; calculation is done. +; Prior to the first byte the two CRC register have to be initialized to 0xFFFF (as defined in usb spec) +; however the crc engine also runs during the receipt of the first byte, therefore x3 and zl are initialized +; to a magic number which results in a crc value of 0xFFFF after the first complete byte. +; +; This algorithm is split into the extra cycles of the different bits: +; bit7: XOR the received byte to ZL +; bit5: load the new high byte to x4 +; bit6: load the lower xor byte from the table, xor zl and x3, store result in zl (=the new crc low value) +; move x4 (the new high byte) to x3, the crc value is ready +; + + +macro POP_STANDARD ; 18 cycles + pop ZH + pop ZL + pop cnt + pop x5 + pop x3 + pop x2 + pop x1 + pop shift + pop x4 + endm +macro POP_RETI ; 7 cycles + pop YH + pop YL + out SREG, YL + pop YL + endm + +macro CRC_CLEANUP_AND_CHECK + ; the last byte has already been xored with the lower crc byte, we have to do the table lookup and xor + ; x3 is the higher crc byte, zl the lower one + ldi ZH, hi8(usbCrcTableHigh);[+1] get the new high byte from the table + lpm x2, Z ;[+2][+3][+4] + ldi ZH, hi8(usbCrcTableLow);[+5] get the new low xor byte from the table + lpm ZL, Z ;[+6][+7][+8] + eor ZL, x3 ;[+7] xor the old high byte with the value from the table, x2:ZL now holds the crc value + cpi ZL, 0x01 ;[+8] if the crc is ok we have a fixed remainder value of 0xb001 in x2:ZL (see usb spec) + brne ignorePacket ;[+9] detected a crc fault -> paket is ignored and retransmitted by the host + cpi x2, 0xb0 ;[+10] + brne ignorePacket ;[+11] detected a crc fault -> paket is ignored and retransmitted by the host + endm + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG, YH, [sofError], x4, shift, x1, x2, x3, x5, cnt, ZL, ZH + push YL ;[-28] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-26] + push YL ;[-25] + push YH ;[-23] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-17] + rjmp foundK ;[-16] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-15] +;{3, 5} after falling D- edge, average delay: 4 cycles +;bit0 should be at 30 (2.5 bits) for center sampling. Currently at 4 so 26 cylces till bit 0 sample +;use 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push x4 ;[-14] +; [---] ;[-13] + lds YL, usbInputBufOffset;[-12] used to toggle the two usb receive buffers +; [---] ;[-11] + clr YH ;[-10] + subi YL, lo8(-(usbRxBuf));[-9] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-8] [rx loop init] + push shift ;[-7] +; [---] ;[-6] + ldi shift, 0x80 ;[-5] the last bit is the end of byte marker for the pid receiver loop + clc ;[-4] the carry has to be clear for receipt of pid bit 0 + sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) + rjmp haveTwoBitsK ;[-2] + pop shift ;[-1] undo the push from before + pop x4 ;[1] + rjmp waitForK ;[3] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 24 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[0] + push x2 ;[2] + push x3 ;[4] crc high byte + ldi x2, 1< jump back and store the byte + ori shift, 0x01 ;[11] invert the last received bit to prevent furhter unstuffing + in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + andi x5, 0xFE ;[1] mark this bit as inverted (will be corrected before storing shift) + eor x1, x2 ;[2] x1 and x2 have to be different because the stuff bit is always a zero + andi x1, USBMASK ;[3] mask the interesting bits + breq stuffErr ;[4] if the stuff bit is a 1-bit something went wrong + mov x1, x2 ;[5] the next bit expects the last state to be in x1 + rjmp didunstuff0 ;[6] + ;[7] jump delay of rjmp didunstuffX + +unstuff1: ;[11] this is the jump delay of breq unstuffX + in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + ori shift, 0x02 ;[1] invert the last received bit to prevent furhter unstuffing + andi x5, 0xFD ;[2] mark this bit as inverted (will be corrected before storing shift) + eor x2, x1 ;[3] x1 and x2 have to be different because the stuff bit is always a zero + andi x2, USBMASK ;[4] mask the interesting bits + breq stuffErr ;[5] if the stuff bit is a 1-bit something went wrong + mov x2, x1 ;[6] the next bit expects the last state to be in x2 + nop2 ;[7] + ;[8] + rjmp didunstuff1 ;[9] + ;[10] jump delay of rjmp didunstuffX + +unstuff2: ;[9] this is the jump delay of breq unstuffX + ori shift, 0x04 ;[10] invert the last received bit to prevent furhter unstuffing + andi x5, 0xFB ;[11] mark this bit as inverted (will be corrected before storing shift) + in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero + andi x1, USBMASK ;[2] mask the interesting bits + breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong + mov x1, x2 ;[4] the next bit expects the last state to be in x1 + nop2 ;[5] + ;[6] + rjmp didunstuff2 ;[7] + ;[8] jump delay of rjmp didunstuffX + +unstuff3: ;[9] this is the jump delay of breq unstuffX + ori shift, 0x08 ;[10] invert the last received bit to prevent furhter unstuffing + andi x5, 0xF7 ;[11] mark this bit as inverted (will be corrected before storing shift) + in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero + andi x2, USBMASK ;[2] mask the interesting bits + breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong + mov x2, x1 ;[4] the next bit expects the last state to be in x2 + nop2 ;[5] + ;[6] + rjmp didunstuff3 ;[7] + ;[8] jump delay of rjmp didunstuffX + + + +; the include has to be here due to branch distance restirctions +#define __USE_CRC__ +#include "asmcommon.inc" + + + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies +; 7.5 bit times is 90 cycles. ...there is plenty of time + + +sendNakAndReti: + ldi x3, USBPID_NAK ;[-18] + rjmp sendX3AndReti ;[-17] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov x3, cnt ;[-16] +sendX3AndReti: + ldi YL, 20 ;[-15] x3==r20 address is 20 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent + +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-6] <- acquire bus + ldi x2, 0 ;[-6] init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-5] exor mask + ldi shift, 0x80 ;[-4] sync byte is first byte sent +txByteLoop: + ldi bitcnt, 0x40 ;[-3]=[9] binary 01000000 +txBitLoop: ; the loop sends the first 7 bits of the byte + sbrs shift, 0 ;[-2]=[10] if we have to send a 1 don't change the line state + eor x1, x4 ;[-1]=[11] + out USBOUT, x1 ;[0] + ror shift ;[1] + ror x2 ;[2] transfers the last sent bit to the stuffing history +didStuffN: + nop ;[3] + nop ;[4] + cpi x2, 0xfc ;[5] if we sent six consecutive ones + brcc bitstuffN ;[6] + lsr bitcnt ;[7] + brne txBitLoop ;[8] restart the loop while the 1 is still in the bitcount + +; transmit bit 7 + sbrs shift, 0 ;[9] + eor x1, x4 ;[10] +didStuff7: + ror shift ;[11] + out USBOUT, x1 ;[0] transfer bit 7 to the pins + ror x2 ;[1] move the bit into the stuffing history + cpi x2, 0xfc ;[2] + brcc bitstuff7 ;[3] + ld shift, y+ ;[4] get next byte to transmit + dec cnt ;[5] decrement byte counter + brne txByteLoop ;[7] if we have more bytes start next one + ;[8] branch delay + +;make SE0: + cbr x1, USBMASK ;[8] prepare SE0 [spec says EOP may be 25 to 30 cycles] + lds x2, usbNewDeviceAddr;[9] + lsl x2 ;[11] we compare with left shifted address + out USBOUT, x1 ;[0] <-- out SE0 -- from now 2 bits = 24 cycles until bus idle + subi YL, 20 + 2 ;[1] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[2] +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[3] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< +int main (int argc, char **argv) +{ + int i, j; + for (i=0; i<512; i++){ + unsigned short crc = i & 0xff; + for(j=0; j<8; j++) crc = (crc >> 1) ^ ((crc & 1) ? 0xa001 : 0); + if((i & 7) == 0) printf("\n.byte "); + printf("0x%02x, ", (i > 0xff ? (crc >> 8) : crc) & 0xff); + if(i == 255) printf("\n"); + } + return 0; +} + +// Use the following algorithm to compute CRC values: +ushort computeCrc(uchar *msg, uchar msgLen) +{ + uchar i; + ushort crc = 0xffff; + for(i = 0; i < msgLen; i++) + crc = usbCrcTable16[lo8(crc) ^ msg[i]] ^ hi8(crc); + return crc; +} +*/ + +.balign 256 +usbCrcTableLow: +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 + +; .balign 256 +usbCrcTableHigh: +.byte 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2 +.byte 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04 +.byte 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E +.byte 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8 +.byte 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A +.byte 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC +.byte 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6 +.byte 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10 +.byte 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32 +.byte 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4 +.byte 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE +.byte 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38 +.byte 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA +.byte 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C +.byte 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26 +.byte 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0 +.byte 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62 +.byte 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4 +.byte 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE +.byte 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68 +.byte 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA +.byte 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C +.byte 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76 +.byte 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0 +.byte 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92 +.byte 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54 +.byte 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E +.byte 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98 +.byte 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A +.byte 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C +.byte 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86 +.byte 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 + diff --git a/classic/fw/usbdrv/usbdrvasm20.inc b/classic/fw/usbdrv/usbdrvasm20.inc new file mode 100644 index 0000000..5027edd --- /dev/null +++ b/classic/fw/usbdrv/usbdrvasm20.inc @@ -0,0 +1,359 @@ +/* Name: usbdrvasm20.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Jeroen Benschop + * Based on usbdrvasm16.inc from Christian Starkjohann + * Creation Date: 2008-03-05 + * Tabsize: 4 + * Copyright: (c) 2008 by Jeroen Benschop and OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 20 MHz version of the asssembler part of the USB driver. It +requires a 20 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! +*/ + +#define leap2 x3 +#ifdef __IAR_SYSTEMS_ASM__ +#define nextInst $+2 +#else +#define nextInst .+0 +#endif + +;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 20 MHz -> 13.333333 cycles per bit, 106.666667 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts +;register use in receive loop: +; shift assembles the byte currently being received +; x1 holds the D+ and D- line state +; x2 holds the previous line state +; x4 (leap) is used to add a leap cycle once every three bytes received +; X3 (leap2) is used to add a leap cycle once every three stuff bits received +; bitcnt is used to determine when a stuff bit is due +; cnt holds the number of bytes left in the receive buffer + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt + push YL ;[-28] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-26] + push YL ;[-25] + push YH ;[-23] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-19] + rjmp foundK ;[-18] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-16] +;{3, 5} after falling D- edge, average delay: 4 cycles +;bit0 should be at 34 for center sampling. Currently at 4 so 30 cylces till bit 0 sample +;use 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push bitcnt ;[-16] +; [---] ;[-15] + lds YL, usbInputBufOffset;[-14] +; [---] ;[-13] + clr YH ;[-12] + subi YL, lo8(-(usbRxBuf));[-11] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init] + push shift ;[-9] +; [---] ;[-8] + ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected + nop2 ;[-6] +; [---] ;[-5] + ldi bitcnt, 5 ;[-4] [rx loop init] + sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) + rjmp haveTwoBitsK ;[-2] + pop shift ;[-1] undo the push from before + pop bitcnt ;[1] + rjmp waitForK ;[3] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 27 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[0] + push x2 ;[2] + push x3 ;[4] (leap2) + ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit + push x4 ;[7] == leap + ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received + push cnt ;[10] + ldi cnt, USB_BUFSIZE ;[12] [rx loop init] + ldi x2, 1< +#ifndef __IAR_SYSTEMS_ASM__ +# include +#endif + +#define __attribute__(arg) /* not supported on IAR */ + +#ifdef __IAR_SYSTEMS_ASM__ +# define __ASSEMBLER__ /* IAR does not define standard macro for asm */ +#endif + +#ifdef __HAS_ELPM__ +# define PROGMEM __farflash +#else +# define PROGMEM __flash +#endif + +#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) + +/* The following definitions are not needed by the driver, but may be of some + * help if you port a gcc based project to IAR. + */ +#define cli() __disable_interrupt() +#define sei() __enable_interrupt() +#define wdt_reset() __watchdog_reset() +#define _BV(x) (1 << (x)) + +/* assembler compatibility macros */ +#define nop2 rjmp $+2 /* jump to next instruction */ +#define XL r26 +#define XH r27 +#define YL r28 +#define YH r29 +#define ZL r30 +#define ZH r31 +#define lo8(x) LOW(x) +#define hi8(x) (((x)>>8) & 0xff) /* not HIGH to allow XLINK to make a proper range check */ + +/* Depending on the device you use, you may get problems with the way usbdrv.h + * handles the differences between devices. Since IAR does not use #defines + * for MCU registers, we can't check for the existence of a particular + * register with an #ifdef. If the autodetection mechanism fails, include + * definitions for the required USB_INTR_* macros in your usbconfig.h. See + * usbconfig-prototype.h and usbdrv.h for details. + */ + +/* ------------------------------------------------------------------------- */ +#elif __CODEVISIONAVR__ /* check for CodeVision AVR */ +/* ------------------------------------------------------------------------- */ +/* This port is not working (yet) */ + +/* #define F_CPU _MCU_CLOCK_FREQUENCY_ seems to be defined automatically */ + +#include +#include + +#define __attribute__(arg) /* not supported on IAR */ + +#define PROGMEM __flash +#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) + +#ifndef __ASSEMBLER__ +static inline void cli(void) +{ + #asm("cli"); +} +static inline void sei(void) +{ + #asm("sei"); +} +#endif +#define _delay_ms(t) delay_ms(t) +#define _BV(x) (1 << (x)) +#define USB_CFG_USE_SWITCH_STATEMENT 1 /* macro for if() cascase fails for unknown reason */ + +#define macro .macro +#define endm .endmacro +#define nop2 rjmp .+0 /* jump to next instruction */ + +/* ------------------------------------------------------------------------- */ +#else /* default development environment is avr-gcc/avr-libc */ +/* ------------------------------------------------------------------------- */ + +#include +#ifdef __ASSEMBLER__ +# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ +#else +# include +#endif + +#if USB_CFG_DRIVER_FLASH_PAGE +# define USB_READ_FLASH(addr) pgm_read_byte_far(((long)USB_CFG_DRIVER_FLASH_PAGE << 16) | (long)(addr)) +#else +# define USB_READ_FLASH(addr) pgm_read_byte(addr) +#endif + +#define macro .macro +#define endm .endm +#define nop2 rjmp .+0 /* jump to next instruction */ + +#endif /* development environment */ + +/* for conveniecne, ensure that PRG_RDB exists */ +#ifndef PRG_RDB +# define PRG_RDB(addr) USB_READ_FLASH(addr) +#endif +#endif /* __usbportability_h_INCLUDED__ */ diff --git a/classic/hw/classic-v1-rescue.lib b/classic/hw/classic-v1-rescue.lib new file mode 100644 index 0000000..338831f --- /dev/null +++ b/classic/hw/classic-v1-rescue.lib @@ -0,0 +1,191 @@ +EESchema-LIBRARY Version 2.4 +#encoding utf-8 +# +# ATTINY85 +# +DEF ATTINY85 U 0 40 Y Y 1 F N +F0 "U" -50 0 60 H V C CNN +F1 "ATTINY85" 0 750 60 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +S 400 -600 -450 650 0 1 0 N +X PB5/NRES 1 700 -450 300 L 50 50 1 1 B +X PB3/ADC1 2 700 -150 300 L 50 50 1 1 B +X PB4/ADC2 3 700 -300 300 L 50 50 1 1 B +X GND 4 -750 -450 300 R 50 50 1 1 w +X PB0/MOSI 5 700 500 300 L 50 50 1 1 B +X PB1/MISO 6 700 350 300 L 50 50 1 1 B +X PB2/SCK/ADC1 7 700 200 300 L 50 50 1 1 B +X VCC 8 -750 -150 300 R 50 50 1 1 W +X PB5/NRES 1 700 -450 300 L 50 50 1 2 B +X PB3/ADC1 2 700 -150 300 L 50 50 1 2 B +X PB4/ADC2 3 700 -300 300 L 50 50 1 2 B +X GND 4 -750 -450 300 R 50 50 1 2 w +X PB0/MOSI 5 700 500 300 L 50 50 1 2 B +X PB1/MISO 6 700 350 300 L 50 50 1 2 B +X PB2/SCK/ADC1 7 700 200 300 L 50 50 1 2 B +X VCC 8 -750 -150 300 R 50 50 1 2 W +ENDDRAW +ENDDEF +# +# C +# +DEF C C 0 10 N Y 1 F N +F0 "C" 0 100 40 H V L CNN +F1 "C" 6 -85 40 H V L CNN +F2 "" 38 -150 30 H V C CNN +F3 "" 0 0 60 H V C CNN +$FPLIST + SM* + C? + C1-1 +$ENDFPLIST +DRAW +P 2 0 1 20 -80 -30 80 -30 N +P 2 0 1 20 -80 30 80 30 N +X ~ 1 0 200 170 D 40 40 1 1 P +X ~ 2 0 -200 170 U 40 40 1 1 P +ENDDRAW +ENDDEF +# +# CP1 +# +DEF CP1 C 0 10 N N 1 F N +F0 "C" 50 100 50 H V L CNN +F1 "CP1" 50 -100 50 H V L CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +$FPLIST + CP* + SM* +$ENDFPLIST +DRAW +A 0 -200 180 563 1236 0 1 15 N 100 -50 -100 -50 +T 0 -50 100 80 0 0 0 + Normal 0 C C +P 4 0 1 15 -100 50 100 50 50 50 50 50 N +X ~ 1 0 200 150 D 40 40 1 1 P +X ~ 2 0 -200 180 U 40 40 1 1 P +ENDDRAW +ENDDEF +# +# ESWITCH +# +DEF ESWITCH SW 0 40 Y Y 1 F N +F0 "SW" -100 250 60 H V C CNN +F1 "ESWITCH" 0 -250 60 H V C CNN +F2 "" -750 700 60 H V C CNN +F3 "" -750 700 60 H V C CNN +DRAW +S -200 -200 200 200 0 1 0 N +P 2 0 1 0 -200 -100 200 -100 N +P 2 0 1 0 0 -50 0 -100 N +P 2 0 1 0 0 -50 50 50 N +P 2 0 1 0 0 50 0 100 N +P 2 0 1 0 200 100 -200 100 N +X ~ 1 -500 100 300 R 50 50 1 1 P +X ~ 1 500 100 300 L 50 50 1 1 P +X ~ 2 -500 -100 300 R 50 50 1 1 P +X ~ 2 500 -100 300 L 50 50 1 1 P +ENDDRAW +ENDDEF +# +# ISP +# +DEF ISP J 0 40 Y Y 1 F N +F0 "J" -250 600 60 H V C CNN +F1 "ISP" -200 -550 60 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +S 0 550 -300 -500 0 1 0 N +X MISO 1 300 400 300 L 50 50 1 1 W +X +VCC 2 300 250 300 L 50 50 1 1 w +X SCK 3 300 100 300 L 50 50 1 1 B +X MOSI 4 300 -50 300 L 50 50 1 1 B +X RESET 5 300 -200 300 L 50 50 1 1 B +X GND 6 300 -350 300 L 50 50 1 1 B +ENDDRAW +ENDDEF +# +# LDO +# +DEF LDO U 0 40 Y Y 1 F N +F0 "U" -200 350 60 H V C CNN +F1 "LDO" 0 -350 60 H V C CNN +F2 "" -800 650 60 H V C CNN +F3 "" -800 650 60 H V C CNN +DRAW +S -250 -250 250 250 0 1 0 N +X IN 1 -550 150 300 R 50 50 1 1 W +X GND 2 -550 0 300 R 50 50 1 1 w +X EN 3 -550 -150 300 R 50 50 1 1 I +X BP 4 550 -150 300 L 50 50 1 1 P +X OUT 5 550 150 300 L 50 50 1 1 w +ENDDRAW +ENDDEF +# +# LED +# +DEF LED D 0 40 Y N 1 F N +F0 "D" 0 100 50 H V C CNN +F1 "LED" 0 -100 50 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +$FPLIST + LED-3MM + LED-5MM + LED-10MM + LED-0603 + LED-0805 + LED-1206 + LEDV +$ENDFPLIST +DRAW +P 2 0 1 0 50 50 50 -50 N +P 3 0 1 0 -50 50 50 0 -50 -50 F +P 3 0 1 0 65 -40 110 -80 105 -55 N +P 3 0 1 0 80 -25 125 -65 120 -40 N +X A 1 -200 0 150 R 40 40 1 1 P +X K 2 200 0 150 L 40 40 1 1 P +ENDDRAW +ENDDEF +# +# R +# +DEF R R 0 0 N Y 1 F N +F0 "R" 80 0 40 V V C CNN +F1 "R" 7 1 40 V V C CNN +F2 "" -70 0 30 V V C CNN +F3 "" 0 0 30 H V C CNN +$FPLIST + R? + SM0603 + SM0805 + R?-* + SM1206 +$ENDFPLIST +DRAW +S -40 150 40 -150 0 1 12 N +X ~ 1 0 250 100 D 60 60 1 1 P +X ~ 2 0 -250 100 U 60 60 1 1 P +ENDDRAW +ENDDEF +# +# USB +# +DEF USB J 0 40 Y Y 1 F N +F0 "J" 0 -350 60 H V C CNN +F1 "USB" 0 -250 60 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +P 4 0 1 0 200 -400 200 50 -200 50 -200 -400 N +X GND 1 -150 350 300 D 50 50 1 1 w +X D+ 2 50 350 300 D 50 50 1 1 B +X D- 3 150 350 300 D 50 50 1 1 B +X VBUS 4 -50 350 300 D 50 50 1 1 W +ENDDRAW +ENDDEF +# +#End Library diff --git a/classic/hw/classic-v1.cmp b/classic/hw/classic-v1.cmp new file mode 100644 index 0000000..96bccff --- /dev/null +++ b/classic/hw/classic-v1.cmp @@ -0,0 +1,101 @@ +Cmp-Mod V01 Created by CvPcb (2013-07-07 BZR 4022)-stable date = 8/24/2014 12:22:47 PM + +BeginCmp +TimeStamp = /53F78D86; +Reference = C1; +ValeurCmp = 10uF; +IdModule = SM1206POL; +EndCmp + +BeginCmp +TimeStamp = /53F78E1D; +Reference = C2; +ValeurCmp = .1uF; +IdModule = SM1206; +EndCmp + +BeginCmp +TimeStamp = /53F78DE2; +Reference = C3; +ValeurCmp = .01uF; +IdModule = SM1206; +EndCmp + +BeginCmp +TimeStamp = /53F78E77; +Reference = C4; +ValeurCmp = 1uF; +IdModule = SM1206POL; +EndCmp + +BeginCmp +TimeStamp = /53F78DB1; +Reference = D1; +ValeurCmp = LED; +IdModule = LED-1206; +EndCmp + +BeginCmp +TimeStamp = /53F78CDD; +Reference = J1; +ValeurCmp = ISP; +IdModule = TRUDEAU_ISP_SMD_CLASSIC; +EndCmp + +BeginCmp +TimeStamp = /53F78CBD; +Reference = J2; +ValeurCmp = USB; +IdModule = TRUDEAU-USB-A-SMT-MALE; +EndCmp + +BeginCmp +TimeStamp = /53F78DA1; +Reference = R1; +ValeurCmp = 47; +IdModule = SM0805; +EndCmp + +BeginCmp +TimeStamp = /53F78FA7; +Reference = R2; +ValeurCmp = 1K5; +IdModule = SM0805; +EndCmp + +BeginCmp +TimeStamp = /53F78FA1; +Reference = R3; +ValeurCmp = 68; +IdModule = SM0805; +EndCmp + +BeginCmp +TimeStamp = /53F78F69; +Reference = R4; +ValeurCmp = 68; +IdModule = SM0805; +EndCmp + +BeginCmp +TimeStamp = /53F78CF3; +Reference = SW1; +ValeurCmp = ESWITCH; +IdModule = TRUDEAU-E-SWITCH; +EndCmp + +BeginCmp +TimeStamp = /53F78C6C; +Reference = U1; +ValeurCmp = ATTINY85; +IdModule = TRUDEAU-SOIC8-WIDE; +EndCmp + +BeginCmp +TimeStamp = /53F78C7B; +Reference = U2; +ValeurCmp = LDO; +IdModule = TRUDEAU-SOT23-5; +EndCmp + +EndListe diff --git a/classic/hw/classic-v1.kicad_pcb b/classic/hw/classic-v1.kicad_pcb new file mode 100644 index 0000000..905eb73 --- /dev/null +++ b/classic/hw/classic-v1.kicad_pcb @@ -0,0 +1,822 @@ +(kicad_pcb (version 20171130) (host pcbnew "(5.1.6)-1") + + (general + (thickness 1.6) + (drawings 4) + (tracks 160) + (zones 0) + (modules 14) + (nets 14) + ) + + (page A3) + (layers + (0 F.Cu signal) + (31 B.Cu signal) + (32 B.Adhes user) + (33 F.Adhes user) + (34 B.Paste user) + (35 F.Paste user) + (36 B.SilkS user) + (37 F.SilkS user) + (38 B.Mask user) + (39 F.Mask user) + (40 Dwgs.User user) + (41 Cmts.User user) + (42 Eco1.User user) + (43 Eco2.User user) + (44 Edge.Cuts user) + ) + + (setup + (last_trace_width 0.254) + (user_trace_width 0.127) + (trace_clearance 0.127) + (zone_clearance 0.254) + (zone_45_only no) + (trace_min 0.127) + (via_size 0.889) + (via_drill 0.635) + (via_min_size 0.555) + (via_min_drill 0.3352) + (user_via 0.555 0.3352) + (uvia_size 0.508) + (uvia_drill 0.127) + (uvias_allowed no) + (uvia_min_size 0.254) + (uvia_min_drill 0.063) + (edge_width 0.1) + (segment_width 0.2) + (pcb_text_width 0.3) + (pcb_text_size 1.5 1.5) + (mod_edge_width 0.15) + (mod_text_size 1 1) + (mod_text_width 0.15) + (pad_size 1.75 2.8) + (pad_drill 0) + (pad_to_mask_clearance 0) + (aux_axis_origin 0 0) + (visible_elements 7FFFFFFF) + (pcbplotparams + (layerselection 0x00030_ffffffff) + (usegerberextensions true) + (usegerberattributes true) + (usegerberadvancedattributes true) + (creategerberjobfile true) + (excludeedgelayer true) + (linewidth 0.150000) + (plotframeref false) + (viasonmask false) + (mode 1) + (useauxorigin false) + (hpglpennumber 1) + (hpglpenspeed 20) + (hpglpendiameter 15.000000) + (psnegative false) + (psa4output false) + (plotreference true) + (plotvalue true) + (plotinvisibletext false) + (padsonsilk false) + (subtractmaskfromsilk false) + (outputformat 1) + (mirror false) + (drillshape 0) + (scaleselection 1) + (outputdirectory "gerbers/")) + ) + + (net 0 "") + (net 1 GND) + (net 2 N-0000013) + (net 3 N-000006) + (net 4 N-000007) + (net 5 N-000009) + (net 6 PB0) + (net 7 PB1) + (net 8 PB2) + (net 9 PB3) + (net 10 PB4) + (net 11 PB5) + (net 12 VBUS) + (net 13 VCC) + + (net_class Default "This is the default net class." + (clearance 0.127) + (trace_width 0.254) + (via_dia 0.889) + (via_drill 0.635) + (uvia_dia 0.508) + (uvia_drill 0.127) + (add_net GND) + (add_net N-0000013) + (add_net N-000006) + (add_net N-000007) + (add_net N-000009) + (add_net PB0) + (add_net PB1) + (add_net PB2) + (add_net PB3) + (add_net PB4) + (add_net PB5) + (add_net VBUS) + (add_net VCC) + ) + + (module MICROJELLY-USB-A-SMT-MALE (layer F.Cu) (tedit 53FA1903) (tstamp 53F79302) + (at 229.451 150.419 270) + (path /53F78CBD) + (attr smd) + (fp_text reference J2 (at -4.369 25.5525 270) (layer B.SilkS) hide + (effects (font (size 1.27 1.27) (thickness 0.0889))) + ) + (fp_text value USB (at 1.7143 25.5906 270) (layer B.SilkS) hide + (effects (font (size 1.27 1.27) (thickness 0.0889))) + ) + (fp_line (start -3.99796 2.26822) (end 3.99796 2.26822) (layer F.SilkS) (width 0.2032)) + (pad 2 smd rect (at 1.02616 3.66776 270) (size 1.19888 1.99898) (layers F.Cu F.Paste F.Mask) + (net 4 N-000007)) + (pad 3 smd rect (at -1.02616 3.66776 270) (size 1.19888 1.99898) (layers F.Cu F.Paste F.Mask) + (net 3 N-000006)) + (pad 1 smd rect (at 3.49758 3.66776 270) (size 1.19888 1.99898) (layers F.Cu F.Paste F.Mask) + (net 1 GND)) + (pad P$1 thru_hole oval (at 5.84962 1.09982 270) (size 1.4986 2.99974) (drill 0.79756) (layers *.Cu *.Mask F.Paste F.SilkS)) + (pad P$3 thru_hole oval (at -5.84962 1.09982 270) (size 1.4986 2.99974) (drill 0.79756) (layers *.Cu *.Mask F.Paste F.SilkS)) + (pad 4 smd rect (at -3.49758 3.66776 270) (size 1.19888 1.99898) (layers F.Cu F.Paste F.Mask) + (net 12 VBUS)) + (pad P$4 thru_hole circle (at 2.24028 1.14808 270) (size 1.099998 1.099998) (drill 1.099998) (layers *.Cu *.Mask F.SilkS)) + (pad P$5 thru_hole circle (at -2.24282 1.1684 270) (size 1.099998 1.099998) (drill 1.099998) (layers *.Cu *.Mask F.SilkS)) + (pad P$4 thru_hole circle (at -5.84962 0.40132 270) (size 0.79756 0.79756) (drill 0.79756) (layers *.Cu F.Paste F.SilkS F.Mask)) + (pad P$5 thru_hole circle (at -5.84962 0.78232 270) (size 0.79756 0.79756) (drill 0.79756) (layers *.Cu F.Paste F.SilkS F.Mask)) + (pad P$6 thru_hole circle (at -5.84962 1.84404 270) (size 0.79756 0.79756) (drill 0.79756) (layers *.Cu F.Paste F.SilkS F.Mask)) + (pad P$7 thru_hole circle (at -5.84962 1.49352 270) (size 0.79756 0.79756) (drill 0.79756) (layers *.Cu F.Paste F.SilkS F.Mask)) + (pad P$2 thru_hole circle (at 5.84962 0.34544 270) (size 0.79756 0.79756) (drill 0.79756) (layers *.Cu F.Paste F.SilkS F.Mask)) + (pad P$3 thru_hole circle (at 5.84962 0.73152 270) (size 0.79756 0.79756) (drill 0.79756) (layers *.Cu F.Paste F.SilkS F.Mask)) + (pad P$4 thru_hole circle (at 5.84962 1.86944 270) (size 0.79756 0.79756) (drill 0.79756) (layers *.Cu F.Paste F.SilkS F.Mask)) + (pad P$5 thru_hole circle (at 5.84962 1.46812 270) (size 0.79756 0.79756) (drill 0.79756) (layers *.Cu F.Paste F.SilkS F.Mask)) + ) + + (module MICROJELLY_ISP_SMD_CLASSIC (layer B.Cu) (tedit 53FA18ED) (tstamp 53FA1098) + (at 213.944 150.444 270) + (path /53F78CDD) + (fp_text reference J1 (at 4.0261 9.9312 270) (layer B.SilkS) hide + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (fp_text value ISP (at -1.4857 9.8677 270) (layer B.SilkS) hide + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (fp_text user VCC (at 6.2105 -5.4358) (layer B.SilkS) + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (fp_text user GND (at 3.81 -5.334) (layer B.SilkS) + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (fp_text user PB0 (at 1.321 -5.258) (layer B.SilkS) + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (fp_text user PB1 (at -1.2952 -5.1437) (layer B.SilkS) + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (fp_text user PB2 (at -3.81 -5.334) (layer B.SilkS) + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (fp_text user PB5 (at -6.35 -5.334) (layer B.SilkS) + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (pad 5 smd rect (at -6.35 -1.778 270) (size 1.75 2.8) (layers B.Cu B.Paste B.Mask) + (net 11 PB5)) + (pad 3 smd rect (at -3.81 -1.778 270) (size 1.75 2.8) (layers B.Cu B.Paste B.Mask) + (net 8 PB2)) + (pad 1 smd rect (at -1.27 -1.778 270) (size 1.75 2.8) (layers B.Cu B.Paste B.Mask) + (net 7 PB1)) + (pad 4 smd rect (at 1.27 -1.778 270) (size 1.75 2.8) (layers B.Cu B.Paste B.Mask) + (net 6 PB0)) + (pad 6 smd rect (at 3.81 -1.778 270) (size 1.75 2.8) (layers B.Cu B.Paste B.Mask) + (net 1 GND)) + (pad 2 smd rect (at 6.35 -1.778 270) (size 1.75 2.8) (layers B.Cu B.Paste B.Mask) + (net 13 VCC)) + ) + + (module MICROJELLY-SOT23-5 (layer F.Cu) (tedit 53FA14B2) (tstamp 53F7930F) + (at 220.815 148.895 90) + (path /53F78C7B) + (attr smd) + (fp_text reference U2 (at -0.0633 -0.0128 180) (layer F.SilkS) + (effects (font (size 0.635 0.635) (thickness 0.127))) + ) + (fp_text value LDO (at -2.9716 -16.7768 90) (layer F.SilkS) hide + (effects (font (size 0.635 0.635) (thickness 0.127))) + ) + (fp_line (start -1.524 -0.889) (end 1.524 -0.889) (layer F.SilkS) (width 0.127)) + (fp_line (start -1.524 0.889) (end -1.524 -0.889) (layer F.SilkS) (width 0.127)) + (fp_line (start 1.524 0.889) (end -1.524 0.889) (layer F.SilkS) (width 0.127)) + (fp_line (start 1.524 -0.889) (end 1.524 0.889) (layer F.SilkS) (width 0.127)) + (pad 1 smd rect (at -0.9525 1.27 90) (size 0.508 0.762) (layers F.Cu F.Paste F.Mask) + (net 12 VBUS)) + (pad 3 smd rect (at 0.9525 1.27 90) (size 0.508 0.762) (layers F.Cu F.Paste F.Mask) + (net 1 GND)) + (pad 5 smd rect (at -0.9525 -1.27 90) (size 0.508 0.762) (layers F.Cu F.Paste F.Mask) + (net 13 VCC)) + (pad 2 smd rect (at 0 1.27 90) (size 0.508 0.762) (layers F.Cu F.Paste F.Mask) + (net 1 GND)) + (pad 4 smd rect (at 0.9525 -1.27 90) (size 0.508 0.762) (layers F.Cu F.Paste F.Mask) + (net 2 N-0000013)) + (model smd/SOT23_5.wrl + (at (xyz 0 0 0)) + (scale (xyz 0.1 0.1 0.1)) + (rotate (xyz 0 0 0)) + ) + ) + + (module MICROJELLY-SOIC8-WIDE (layer F.Cu) (tedit 53FA144B) (tstamp 53F79321) + (at 214.859 148.704 180) + (descr "module CMS SOJ 8 pins etroit") + (tags "CMS SOJ") + (path /53F78C6C) + (attr smd) + (fp_text reference U1 (at -1.4347 -1.0163 180) (layer F.SilkS) + (effects (font (size 1.143 1.143) (thickness 0.1524))) + ) + (fp_text value ATTINY85 (at 10.643 -4.6231 270) (layer F.SilkS) hide + (effects (font (size 0.889 0.889) (thickness 0.1524))) + ) + (fp_line (start 2.667 -1.905) (end 2.667 1.905) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.667 -1.905) (end -2.667 1.778) (layer F.SilkS) (width 0.127)) + (fp_line (start 2.667 -1.905) (end -2.667 -1.905) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.667 1.905) (end 2.667 1.905) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.667 1.778) (end -2.667 1.905) (layer F.SilkS) (width 0.127)) + (fp_circle (center -2.11328 1.3335) (end -2.3876 1.24714) (layer F.SilkS) (width 0.15)) + (pad 8 smd rect (at -1.905 -3.40106 180) (size 0.59944 2.32) (layers F.Cu F.Paste F.Mask) + (net 13 VCC)) + (pad 1 smd rect (at -1.905 3.40106 180) (size 0.59944 2.32) (layers F.Cu F.Paste F.Mask) + (net 11 PB5)) + (pad 7 smd rect (at -0.635 -3.40106 180) (size 0.59944 2.32) (layers F.Cu F.Paste F.Mask) + (net 8 PB2)) + (pad 6 smd rect (at 0.635 -3.40106 180) (size 0.59944 2.32) (layers F.Cu F.Paste F.Mask) + (net 7 PB1)) + (pad 5 smd rect (at 1.905 -3.40106 180) (size 0.59944 2.32) (layers F.Cu F.Paste F.Mask) + (net 6 PB0)) + (pad 2 smd rect (at -0.635 3.40106 180) (size 0.59944 2.32) (layers F.Cu F.Paste F.Mask) + (net 9 PB3)) + (pad 3 smd rect (at 0.635 3.40106 180) (size 0.59944 2.32) (layers F.Cu F.Paste F.Mask) + (net 10 PB4)) + (pad 4 smd rect (at 1.905 3.40106 180) (size 0.59944 2.32) (layers F.Cu F.Paste F.Mask) + (net 1 GND)) + (model smd/cms_so8.wrl + (at (xyz 0 0 0)) + (scale (xyz 0.5 0.32 0.5)) + (rotate (xyz 0 0 0)) + ) + ) + + (module MICROJELLY-E-SWITCH (layer F.Cu) (tedit 525C1661) (tstamp 53F79332) + (at 207.162 147.625 270) + (path /53F78CF3) + (attr smd) + (fp_text reference SW1 (at 0 0 270) (layer F.SilkS) + (effects (font (size 0.0004 0.0004) (thickness 0.00012))) + ) + (fp_text value ESWITCH (at 0 0 270) (layer F.SilkS) + (effects (font (size 0.0004 0.0004) (thickness 0.00012))) + ) + (fp_line (start 1.27 0.635) (end 1.27 -1.27) (layer F.SilkS) (width 0.127)) + (fp_line (start -1.27 0.635) (end 1.27 0.635) (layer F.SilkS) (width 0.127)) + (fp_line (start -1.27 -1.27) (end -1.27 0.635) (layer F.SilkS) (width 0.127)) + (fp_line (start 2.3495 -1.7526) (end -2.3495 -1.7526) (layer F.SilkS) (width 0.127)) + (fp_line (start 2.3495 1.7526) (end 2.3495 -1.7526) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.3495 1.7526) (end 2.3495 1.7526) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.3495 -1.7526) (end -2.3495 1.7526) (layer F.SilkS) (width 0.127)) + (pad 1 smd rect (at -2.6289 0.8763 270) (size 1.5494 0.9906) (layers F.Cu F.Paste F.Mask) + (net 8 PB2)) + (pad 1 smd rect (at 2.6289 0.8763 270) (size 1.5494 0.9906) (layers F.Cu F.Paste F.Mask) + (net 8 PB2)) + (pad 2 smd rect (at -2.6289 -0.8763 270) (size 1.5494 0.9906) (layers F.Cu F.Paste F.Mask) + (net 1 GND)) + (pad 2 smd rect (at 2.6289 -0.8763 270) (size 1.5494 0.9906) (layers F.Cu F.Paste F.Mask) + (net 1 GND)) + (pad P$5 thru_hole circle (at -0.01778 -1.39192 270) (size 0.762 0.762) (drill 0.762) (layers *.Cu *.Mask F.SilkS)) + (pad P$6 thru_hole circle (at -0.01524 1.36144 270) (size 0.762 0.762) (drill 0.762) (layers *.Cu *.Mask F.SilkS)) + ) + + (module SM1206POL (layer F.Cu) (tedit 53FA1462) (tstamp 53F79341) + (at 210.579 147.574 90) + (path /53F78D86) + (attr smd) + (fp_text reference C1 (at -0.0889 -0.2797 180) (layer F.SilkS) + (effects (font (size 0.762 0.762) (thickness 0.127))) + ) + (fp_text value 10uF (at -1.143 -6.4519 90) (layer F.SilkS) hide + (effects (font (size 0.762 0.762) (thickness 0.127))) + ) + (fp_line (start -0.889 -1.143) (end -2.54 -1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 2.54 1.143) (end 0.889 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 2.54 -1.143) (end 2.54 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 0.889 -1.143) (end 2.54 -1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.54 1.143) (end -0.889 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.54 -1.143) (end -2.54 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.794 1.143) (end -2.54 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.794 -1.143) (end -2.794 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.54 -1.143) (end -2.794 -1.143) (layer F.SilkS) (width 0.127)) + (pad 1 smd rect (at -1.651 0 90) (size 1.524 2.032) (layers F.Cu F.Paste F.Mask) + (net 13 VCC)) + (pad 2 smd rect (at 1.651 0 90) (size 1.524 2.032) (layers F.Cu F.Paste F.Mask) + (net 1 GND)) + (model smd/chip_cms_pol.wrl + (at (xyz 0 0 0)) + (scale (xyz 0.17 0.16 0.16)) + (rotate (xyz 0 0 0)) + ) + ) + + (module SM1206POL (layer F.Cu) (tedit 53FA14A0) (tstamp 53F79350) + (at 220.853 154.927 180) + (path /53F78E77) + (attr smd) + (fp_text reference C4 (at 0 0 180) (layer F.SilkS) + (effects (font (size 0.762 0.762) (thickness 0.127))) + ) + (fp_text value 1uF (at 16.7767 4.9781 270) (layer F.SilkS) hide + (effects (font (size 0.762 0.762) (thickness 0.127))) + ) + (fp_line (start -0.889 -1.143) (end -2.54 -1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 2.54 1.143) (end 0.889 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 2.54 -1.143) (end 2.54 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 0.889 -1.143) (end 2.54 -1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.54 1.143) (end -0.889 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.54 -1.143) (end -2.54 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.794 1.143) (end -2.54 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.794 -1.143) (end -2.794 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.54 -1.143) (end -2.794 -1.143) (layer F.SilkS) (width 0.127)) + (pad 1 smd rect (at -1.651 0 180) (size 1.524 2.032) (layers F.Cu F.Paste F.Mask) + (net 13 VCC)) + (pad 2 smd rect (at 1.651 0 180) (size 1.524 2.032) (layers F.Cu F.Paste F.Mask) + (net 1 GND)) + (model smd/chip_cms_pol.wrl + (at (xyz 0 0 0)) + (scale (xyz 0.17 0.16 0.16)) + (rotate (xyz 0 0 0)) + ) + ) + + (module SM1206 (layer F.Cu) (tedit 53FA14A5) (tstamp 53F7935C) + (at 220.904 152.082) + (path /53F78DE2) + (attr smd) + (fp_text reference C3 (at 0 0) (layer F.SilkS) + (effects (font (size 0.762 0.762) (thickness 0.127))) + ) + (fp_text value .01uF (at -16.8912 -3.2126 90) (layer F.SilkS) hide + (effects (font (size 0.762 0.762) (thickness 0.127))) + ) + (fp_line (start -0.889 -1.143) (end -2.54 -1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 2.54 1.143) (end 0.889 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 2.54 -1.143) (end 2.54 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 0.889 -1.143) (end 2.54 -1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.54 1.143) (end -0.889 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.54 -1.143) (end -2.54 1.143) (layer F.SilkS) (width 0.127)) + (pad 1 smd rect (at -1.651 0) (size 1.524 2.032) (layers F.Cu F.Paste F.Mask) + (net 2 N-0000013)) + (pad 2 smd rect (at 1.651 0) (size 1.524 2.032) (layers F.Cu F.Paste F.Mask) + (net 1 GND)) + (model smd/chip_cms.wrl + (at (xyz 0 0 0)) + (scale (xyz 0.17 0.16 0.16)) + (rotate (xyz 0 0 0)) + ) + ) + + (module SM1206 (layer F.Cu) (tedit 53FA14B6) (tstamp 53F79368) + (at 220.955 145.606) + (path /53F78E1D) + (attr smd) + (fp_text reference C2 (at 0 0) (layer F.SilkS) + (effects (font (size 0.762 0.762) (thickness 0.127))) + ) + (fp_text value .1uF (at -16.7898 7.3655 90) (layer F.SilkS) hide + (effects (font (size 0.762 0.762) (thickness 0.127))) + ) + (fp_line (start -0.889 -1.143) (end -2.54 -1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 2.54 1.143) (end 0.889 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 2.54 -1.143) (end 2.54 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start 0.889 -1.143) (end 2.54 -1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.54 1.143) (end -0.889 1.143) (layer F.SilkS) (width 0.127)) + (fp_line (start -2.54 -1.143) (end -2.54 1.143) (layer F.SilkS) (width 0.127)) + (pad 1 smd rect (at -1.651 0) (size 1.524 2.032) (layers F.Cu F.Paste F.Mask) + (net 12 VBUS)) + (pad 2 smd rect (at 1.651 0) (size 1.524 2.032) (layers F.Cu F.Paste F.Mask) + (net 1 GND)) + (model smd/chip_cms.wrl + (at (xyz 0 0 0)) + (scale (xyz 0.17 0.16 0.16)) + (rotate (xyz 0 0 0)) + ) + ) + + (module SM0805 (layer F.Cu) (tedit 53FA1471) (tstamp 53F79375) + (at 210.185 155.321 270) + (path /53F78DA1) + (attr smd) + (fp_text reference R1 (at 0.0127 0.0254) (layer F.SilkS) + (effects (font (size 0.50038 0.50038) (thickness 0.10922))) + ) + (fp_text value 47 (at -3.4925 5.9055 270) (layer F.SilkS) hide + (effects (font (size 0.50038 0.50038) (thickness 0.10922))) + ) + (fp_line (start 1.524 0.762) (end 0.508 0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start 1.524 -0.762) (end 1.524 0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start 0.508 -0.762) (end 1.524 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -1.524 -0.762) (end -0.508 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -1.524 0.762) (end -1.524 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -0.508 0.762) (end -1.524 0.762) (layer F.SilkS) (width 0.09906)) + (fp_circle (center -1.651 0.762) (end -1.651 0.635) (layer F.SilkS) (width 0.09906)) + (pad 1 smd rect (at -0.9525 0 270) (size 0.889 1.397) (layers F.Cu F.Paste F.Mask) + (net 7 PB1)) + (pad 2 smd rect (at 0.9525 0 270) (size 0.889 1.397) (layers F.Cu F.Paste F.Mask) + (net 5 N-000009)) + (model smd/chip_cms.wrl + (at (xyz 0 0 0)) + (scale (xyz 0.1 0.1 0.1)) + (rotate (xyz 0 0 0)) + ) + ) + + (module SM0805 (layer F.Cu) (tedit 53FA1491) (tstamp 53F79382) + (at 214.541 155.423 270) + (path /53F78F69) + (attr smd) + (fp_text reference R4 (at -0.0385 -0.0255) (layer F.SilkS) + (effects (font (size 0.50038 0.50038) (thickness 0.10922))) + ) + (fp_text value 68 (at -5.7916 10.2996 270) (layer F.SilkS) hide + (effects (font (size 0.50038 0.50038) (thickness 0.10922))) + ) + (fp_line (start 1.524 0.762) (end 0.508 0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start 1.524 -0.762) (end 1.524 0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start 0.508 -0.762) (end 1.524 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -1.524 -0.762) (end -0.508 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -1.524 0.762) (end -1.524 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -0.508 0.762) (end -1.524 0.762) (layer F.SilkS) (width 0.09906)) + (fp_circle (center -1.651 0.762) (end -1.651 0.635) (layer F.SilkS) (width 0.09906)) + (pad 1 smd rect (at -0.9525 0 270) (size 0.889 1.397) (layers F.Cu F.Paste F.Mask) + (net 10 PB4)) + (pad 2 smd rect (at 0.9525 0 270) (size 0.889 1.397) (layers F.Cu F.Paste F.Mask) + (net 4 N-000007)) + (model smd/chip_cms.wrl + (at (xyz 0 0 0)) + (scale (xyz 0.1 0.1 0.1)) + (rotate (xyz 0 0 0)) + ) + ) + + (module SM0805 (layer F.Cu) (tedit 53FA148B) (tstamp 53F7938F) + (at 212.344 155.372 270) + (path /53F78FA1) + (attr smd) + (fp_text reference R3 (at 0.0506 0.0127) (layer F.SilkS) + (effects (font (size 0.50038 0.50038) (thickness 0.10922))) + ) + (fp_text value 68 (at -6.4518 8.2423 270) (layer F.SilkS) hide + (effects (font (size 0.50038 0.50038) (thickness 0.10922))) + ) + (fp_line (start 1.524 0.762) (end 0.508 0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start 1.524 -0.762) (end 1.524 0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start 0.508 -0.762) (end 1.524 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -1.524 -0.762) (end -0.508 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -1.524 0.762) (end -1.524 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -0.508 0.762) (end -1.524 0.762) (layer F.SilkS) (width 0.09906)) + (fp_circle (center -1.651 0.762) (end -1.651 0.635) (layer F.SilkS) (width 0.09906)) + (pad 1 smd rect (at -0.9525 0 270) (size 0.889 1.397) (layers F.Cu F.Paste F.Mask) + (net 9 PB3)) + (pad 2 smd rect (at 0.9525 0 270) (size 0.889 1.397) (layers F.Cu F.Paste F.Mask) + (net 3 N-000006)) + (model smd/chip_cms.wrl + (at (xyz 0 0 0)) + (scale (xyz 0.1 0.1 0.1)) + (rotate (xyz 0 0 0)) + ) + ) + + (module SM0805 (layer F.Cu) (tedit 53FA1494) (tstamp 53F7939C) + (at 216.751 155.423 270) + (path /53F78FA7) + (attr smd) + (fp_text reference R2 (at 0.025 0.0128) (layer F.SilkS) + (effects (font (size 0.50038 0.50038) (thickness 0.10922))) + ) + (fp_text value 1K5 (at -6.325 12.6747 270) (layer F.SilkS) hide + (effects (font (size 0.50038 0.50038) (thickness 0.10922))) + ) + (fp_line (start 1.524 0.762) (end 0.508 0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start 1.524 -0.762) (end 1.524 0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start 0.508 -0.762) (end 1.524 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -1.524 -0.762) (end -0.508 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -1.524 0.762) (end -1.524 -0.762) (layer F.SilkS) (width 0.09906)) + (fp_line (start -0.508 0.762) (end -1.524 0.762) (layer F.SilkS) (width 0.09906)) + (fp_circle (center -1.651 0.762) (end -1.651 0.635) (layer F.SilkS) (width 0.09906)) + (pad 1 smd rect (at -0.9525 0 270) (size 0.889 1.397) (layers F.Cu F.Paste F.Mask) + (net 13 VCC)) + (pad 2 smd rect (at 0.9525 0 270) (size 0.889 1.397) (layers F.Cu F.Paste F.Mask) + (net 3 N-000006)) + (model smd/chip_cms.wrl + (at (xyz 0 0 0)) + (scale (xyz 0.1 0.1 0.1)) + (rotate (xyz 0 0 0)) + ) + ) + + (module LED-1206 (layer F.Cu) (tedit 53FA1456) (tstamp 53F793C6) + (at 207.162 154.242 90) + (descr "LED 1206 smd package") + (tags "LED1206 SMD") + (path /53F78DB1) + (attr smd) + (fp_text reference D1 (at 0.1148 -2.946 90) (layer F.SilkS) hide + (effects (font (size 0.762 0.762) (thickness 0.0889))) + ) + (fp_text value LED (at 0.1783 -2.8952 90) (layer F.SilkS) hide + (effects (font (size 0.762 0.762) (thickness 0.0889))) + ) + (fp_line (start 1.5494 -0.7493) (end 1.5494 0.7493) (layer F.SilkS) (width 0.1016)) + (fp_line (start -1.5494 -0.7493) (end 1.5494 -0.7493) (layer F.SilkS) (width 0.1016)) + (fp_line (start -1.5494 0.7493) (end -1.5494 -0.7493) (layer F.SilkS) (width 0.1016)) + (fp_line (start 1.5494 0.7493) (end -1.5494 0.7493) (layer F.SilkS) (width 0.1016)) + (fp_line (start 0.44958 0.6985) (end 0.44958 0.44958) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.44958 0.44958) (end 0.59944 0.44958) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.59944 0.6985) (end 0.59944 0.44958) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.44958 0.6985) (end 0.59944 0.6985) (layer F.SilkS) (width 0.06604)) + (fp_line (start -0.89916 -0.54864) (end -0.89916 -0.6985) (layer F.SilkS) (width 0.06604)) + (fp_line (start -0.89916 -0.6985) (end -0.79756 -0.6985) (layer F.SilkS) (width 0.06604)) + (fp_line (start -0.79756 -0.54864) (end -0.79756 -0.6985) (layer F.SilkS) (width 0.06604)) + (fp_line (start -0.89916 -0.54864) (end -0.79756 -0.54864) (layer F.SilkS) (width 0.06604)) + (fp_line (start -0.89916 0.6985) (end -0.89916 -0.49784) (layer F.SilkS) (width 0.06604)) + (fp_line (start -0.89916 -0.49784) (end -0.79756 -0.49784) (layer F.SilkS) (width 0.06604)) + (fp_line (start -0.79756 0.6985) (end -0.79756 -0.49784) (layer F.SilkS) (width 0.06604)) + (fp_line (start -0.89916 0.6985) (end -0.79756 0.6985) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.79756 -0.54864) (end 0.79756 -0.6985) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.79756 -0.6985) (end 0.89916 -0.6985) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.89916 -0.54864) (end 0.89916 -0.6985) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.79756 -0.54864) (end 0.89916 -0.54864) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.79756 0.6985) (end 0.79756 -0.49784) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.79756 -0.49784) (end 0.89916 -0.49784) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.89916 0.6985) (end 0.89916 -0.49784) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.79756 0.6985) (end 0.89916 0.6985) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.44958 0.6985) (end 0.44958 0.44958) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.44958 0.44958) (end 0.79756 0.44958) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.79756 0.6985) (end 0.79756 0.44958) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.44958 0.6985) (end 0.79756 0.6985) (layer F.SilkS) (width 0.06604)) + (fp_line (start -0.09906 0.09906) (end -0.09906 -0.09906) (layer F.SilkS) (width 0.06604)) + (fp_line (start -0.09906 -0.09906) (end 0.09906 -0.09906) (layer F.SilkS) (width 0.06604)) + (fp_line (start 0.09906 0.09906) (end 0.09906 -0.09906) (layer F.SilkS) (width 0.06604)) + (fp_line (start -0.09906 0.09906) (end 0.09906 0.09906) (layer F.SilkS) (width 0.06604)) + (fp_arc (start 0 0) (end 0.54864 0.49784) (angle 95.4) (layer F.SilkS) (width 0.1016)) + (fp_arc (start 0 0) (end -0.54864 0.49784) (angle 84.5) (layer F.SilkS) (width 0.1016)) + (fp_arc (start 0 0) (end -0.54864 -0.49784) (angle 95.4) (layer F.SilkS) (width 0.1016)) + (fp_arc (start 0 0) (end 0.54864 -0.49784) (angle 84.5) (layer F.SilkS) (width 0.1016)) + (pad 1 smd rect (at -1.41986 0 90) (size 1.59766 1.80086) (layers F.Cu F.Paste F.Mask) + (net 5 N-000009)) + (pad 2 smd rect (at 1.41986 0 90) (size 1.59766 1.80086) (layers F.Cu F.Paste F.Mask) + (net 1 GND)) + ) + + (gr_line (start 205.0542 157.7594) (end 205.0542 143.1323) (angle 90) (layer Edge.Cuts) (width 0.1)) + (gr_line (start 231.0142 157.7594) (end 231.0142 143.1323) (angle 90) (layer Edge.Cuts) (width 0.1)) + (gr_line (start 205.0542 157.7594) (end 231.0142 157.7594) (angle 90) (layer Edge.Cuts) (width 0.1)) + (gr_line (start 205.0542 143.1323) (end 231.0142 143.1323) (angle 90) (layer Edge.Cuts) (width 0.1)) + + (via (at 208.413 148.8872) (size 0.889) (layers F.Cu B.Cu) (net 1)) + (via (at 207.162 154.2112) (size 0.889) (layers F.Cu B.Cu) (net 1)) + (via (at 207.4421 146.3538) (size 0.889) (layers F.Cu B.Cu) (net 1)) + (via (at 211.4867 147.2913) (size 0.889) (layers F.Cu B.Cu) (net 1)) + (via (at 222.1048 144.0128) (size 0.889) (layers F.Cu B.Cu) (net 1)) + (via (at 223.0533 148.62) (size 0.889) (layers F.Cu B.Cu) (net 1)) + (segment (start 220.2183 153.4024) (end 221.5387 152.082) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 220.2183 154.927) (end 220.2183 153.4024) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.555 152.082) (end 221.5387 152.082) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 219.202 154.927) (end 220.2183 154.927) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 208.0383 150.2539) (end 208.0383 149.2249) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 208.0753 149.2249) (end 208.0383 149.2249) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 208.413 148.8872) (end 208.0753 149.2249) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 212.954 145.3029) (end 212.954 146.7172) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 207.162 152.8221) (end 207.162 154.2112) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 208.0383 144.9961) (end 208.0383 146.0251) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 207.7708 146.0251) (end 208.0383 146.0251) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 207.4421 146.3538) (end 207.7708 146.0251) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 210.579 145.923) (end 210.579 146.4219) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 210.8743 146.7172) (end 211.4867 146.7172) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 210.579 146.4219) (end 210.8743 146.7172) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 211.4867 146.7172) (end 212.954 146.7172) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 211.4867 147.2913) (end 211.4867 146.7172) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.606 145.606) (end 222.606 144.3357) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.4277 144.3357) (end 222.606 144.3357) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.1048 144.0128) (end 222.4277 144.3357) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.085 147.9425) (end 222.085 148.895) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.085 148.895) (end 222.7203 148.895) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.9953 148.62) (end 222.7203 148.895) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 223.0533 148.62) (end 222.9953 148.62) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.7203 150.6464) (end 222.555 150.8117) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.7203 148.895) (end 222.7203 150.6464) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.555 152.082) (end 222.555 151.4468) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.555 151.4468) (end 222.555 150.8117) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 224.5294 153.4212) (end 224.5294 153.9166) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 222.555 151.4468) (end 224.5294 153.4212) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 225.7832 153.9166) (end 224.5294 153.9166) (width 0.254) (layer F.Cu) (net 1)) + (segment (start 219.253 152.082) (end 219.253 150.8117) (width 0.254) (layer F.Cu) (net 2)) + (segment (start 219.545 147.9425) (end 220.1803 147.9425) (width 0.254) (layer F.Cu) (net 2)) + (segment (start 220.1803 150.2171) (end 220.1803 147.9425) (width 0.254) (layer F.Cu) (net 2)) + (segment (start 219.5857 150.8117) (end 220.1803 150.2171) (width 0.254) (layer F.Cu) (net 2)) + (segment (start 219.253 150.8117) (end 219.5857 150.8117) (width 0.254) (layer F.Cu) (net 2)) + (segment (start 216.751 156.3755) (end 216.751 156.0261) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 225.7832 149.3928) (end 224.5294 149.3928) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 217.3344 156.6095) (end 216.751 156.0261) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 225.0662 156.6095) (end 217.3344 156.6095) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 227.037 154.6387) (end 225.0662 156.6095) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 227.037 153.2119) (end 227.037 154.6387) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 226.888 153.0629) (end 227.037 153.2119) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 225.3961 153.0629) (end 226.888 153.0629) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 224.5294 152.1962) (end 225.3961 153.0629) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 224.5294 149.3928) (end 224.5294 152.1962) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 213.2968 156.0814) (end 213.2968 156.3245) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 213.7016 155.6766) (end 213.2968 156.0814) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 216.4015 155.6766) (end 213.7016 155.6766) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 216.751 156.0261) (end 216.4015 155.6766) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 212.344 156.3245) (end 213.2968 156.3245) (width 0.254) (layer F.Cu) (net 3)) + (segment (start 214.541 156.3755) (end 214.541 157.0743) (width 0.254) (layer F.Cu) (net 4)) + (segment (start 225.7832 151.4452) (end 227.037 151.4452) (width 0.254) (layer F.Cu) (net 4)) + (segment (start 227.4183 151.8265) (end 227.037 151.4452) (width 0.254) (layer F.Cu) (net 4)) + (segment (start 227.4183 154.7966) (end 227.4183 151.8265) (width 0.254) (layer F.Cu) (net 4)) + (segment (start 225.1406 157.0743) (end 227.4183 154.7966) (width 0.254) (layer F.Cu) (net 4)) + (segment (start 214.541 157.0743) (end 225.1406 157.0743) (width 0.254) (layer F.Cu) (net 4)) + (segment (start 208.6206 155.6619) (end 209.2322 156.2735) (width 0.254) (layer F.Cu) (net 5)) + (segment (start 207.162 155.6619) (end 208.6206 155.6619) (width 0.254) (layer F.Cu) (net 5)) + (segment (start 210.185 156.2735) (end 209.2322 156.2735) (width 0.254) (layer F.Cu) (net 5)) + (via (at 212.0719 151.4767) (size 0.889) (layers F.Cu B.Cu) (net 6)) + (segment (start 212.954 152.1051) (end 212.954 150.6908) (width 0.254) (layer F.Cu) (net 6)) + (segment (start 213.8304 151.4767) (end 212.0719 151.4767) (width 0.254) (layer B.Cu) (net 6)) + (segment (start 214.0677 151.714) (end 213.8304 151.4767) (width 0.254) (layer B.Cu) (net 6)) + (segment (start 212.0719 151.1411) (end 212.0719 151.4767) (width 0.254) (layer F.Cu) (net 6)) + (segment (start 212.5222 150.6908) (end 212.0719 151.1411) (width 0.254) (layer F.Cu) (net 6)) + (segment (start 212.954 150.6908) (end 212.5222 150.6908) (width 0.254) (layer F.Cu) (net 6)) + (segment (start 215.722 151.714) (end 214.0677 151.714) (width 0.254) (layer B.Cu) (net 6)) + (via (at 213.4489 150.0759) (size 0.889) (layers F.Cu B.Cu) (net 7)) + (segment (start 215.1663 153.5194) (end 214.224 153.5194) (width 0.254) (layer F.Cu) (net 7)) + (segment (start 215.4939 153.847) (end 215.1663 153.5194) (width 0.254) (layer F.Cu) (net 7)) + (segment (start 215.4939 155.0233) (end 215.4939 153.847) (width 0.254) (layer F.Cu) (net 7)) + (segment (start 215.3478 155.1694) (end 215.4939 155.0233) (width 0.254) (layer F.Cu) (net 7)) + (segment (start 210.2871 155.1694) (end 215.3478 155.1694) (width 0.254) (layer F.Cu) (net 7)) + (segment (start 210.185 155.0673) (end 210.2871 155.1694) (width 0.254) (layer F.Cu) (net 7)) + (segment (start 210.185 154.3685) (end 210.185 155.0673) (width 0.254) (layer F.Cu) (net 7)) + (segment (start 214.224 152.1051) (end 214.224 150.6908) (width 0.254) (layer F.Cu) (net 7)) + (segment (start 214.0677 149.701) (end 213.4489 150.0759) (width 0.254) (layer B.Cu) (net 7)) + (segment (start 214.0677 149.174) (end 214.0677 149.701) (width 0.254) (layer B.Cu) (net 7)) + (segment (start 214.224 150.5385) (end 213.4489 150.0759) (width 0.254) (layer F.Cu) (net 7)) + (segment (start 214.224 150.6908) (end 214.224 150.5385) (width 0.254) (layer F.Cu) (net 7)) + (segment (start 215.722 149.174) (end 214.0677 149.174) (width 0.254) (layer B.Cu) (net 7)) + (segment (start 214.224 152.1051) (end 214.224 153.5194) (width 0.254) (layer F.Cu) (net 7)) + (via (at 212.2994 149.7861) (size 0.889) (layers F.Cu B.Cu) (net 8)) + (via (at 206.1128 148.735) (size 0.889) (layers F.Cu B.Cu) (net 8)) + (segment (start 215.494 152.1051) (end 215.494 150.6908) (width 0.254) (layer F.Cu) (net 8)) + (segment (start 215.722 146.634) (end 214.0677 146.634) (width 0.254) (layer B.Cu) (net 8)) + (segment (start 212.2994 149.5861) (end 212.2994 149.7861) (width 0.254) (layer B.Cu) (net 8)) + (segment (start 214.0677 147.8178) (end 212.2994 149.5861) (width 0.254) (layer B.Cu) (net 8)) + (segment (start 214.0677 146.634) (end 214.0677 147.8178) (width 0.254) (layer B.Cu) (net 8)) + (segment (start 206.9639 149.5861) (end 206.1128 148.735) (width 0.254) (layer B.Cu) (net 8)) + (segment (start 212.2994 149.5861) (end 206.9639 149.5861) (width 0.254) (layer B.Cu) (net 8)) + (segment (start 206.2857 148.9079) (end 206.2857 150.2539) (width 0.254) (layer F.Cu) (net 8)) + (segment (start 206.1128 148.735) (end 206.2857 148.9079) (width 0.254) (layer F.Cu) (net 8)) + (segment (start 206.4441 148.4037) (end 206.1128 148.735) (width 0.254) (layer F.Cu) (net 8)) + (segment (start 206.4441 146.1835) (end 206.4441 148.4037) (width 0.254) (layer F.Cu) (net 8)) + (segment (start 206.2857 146.0251) (end 206.4441 146.1835) (width 0.254) (layer F.Cu) (net 8)) + (segment (start 206.2857 144.9961) (end 206.2857 146.0251) (width 0.254) (layer F.Cu) (net 8)) + (segment (start 212.7427 149.3428) (end 212.2994 149.7861) (width 0.254) (layer F.Cu) (net 8)) + (segment (start 214.146 149.3428) (end 212.7427 149.3428) (width 0.254) (layer F.Cu) (net 8)) + (segment (start 215.494 150.6908) (end 214.146 149.3428) (width 0.254) (layer F.Cu) (net 8)) + (segment (start 210.395 143.8886) (end 215.494 143.8886) (width 0.254) (layer F.Cu) (net 9)) + (segment (start 208.909 145.3746) (end 210.395 143.8886) (width 0.254) (layer F.Cu) (net 9)) + (segment (start 208.909 146.3325) (end 208.909 145.3746) (width 0.254) (layer F.Cu) (net 9)) + (segment (start 207.2371 148.0044) (end 208.909 146.3325) (width 0.254) (layer F.Cu) (net 9)) + (segment (start 207.2371 151.1208) (end 207.2371 148.0044) (width 0.254) (layer F.Cu) (net 9)) + (segment (start 207.8852 151.7689) (end 207.2371 151.1208) (width 0.254) (layer F.Cu) (net 9)) + (segment (start 209.6934 151.7689) (end 207.8852 151.7689) (width 0.254) (layer F.Cu) (net 9)) + (segment (start 212.344 154.4195) (end 209.6934 151.7689) (width 0.254) (layer F.Cu) (net 9)) + (segment (start 215.494 145.3029) (end 215.494 143.8886) (width 0.254) (layer F.Cu) (net 9)) + (segment (start 212.9231 148.0181) (end 214.224 146.7172) (width 0.254) (layer F.Cu) (net 10)) + (segment (start 209.6325 148.0181) (end 212.9231 148.0181) (width 0.254) (layer F.Cu) (net 10)) + (segment (start 209.3086 148.342) (end 209.6325 148.0181) (width 0.254) (layer F.Cu) (net 10)) + (segment (start 209.3086 150.325) (end 209.3086 148.342) (width 0.254) (layer F.Cu) (net 10)) + (segment (start 212.7043 153.7207) (end 209.3086 150.325) (width 0.254) (layer F.Cu) (net 10)) + (segment (start 213.7912 153.7207) (end 212.7043 153.7207) (width 0.254) (layer F.Cu) (net 10)) + (segment (start 214.541 154.4705) (end 213.7912 153.7207) (width 0.254) (layer F.Cu) (net 10)) + (segment (start 214.224 145.3029) (end 214.224 146.7172) (width 0.254) (layer F.Cu) (net 10)) + (via (at 219.1995 144.0085) (size 0.889) (layers F.Cu B.Cu) (net 11)) + (segment (start 216.764 145.3029) (end 216.764 143.8886) (width 0.254) (layer F.Cu) (net 11)) + (segment (start 219.114 144.094) (end 219.1995 144.0085) (width 0.254) (layer B.Cu) (net 11)) + (segment (start 215.722 144.094) (end 219.114 144.094) (width 0.254) (layer B.Cu) (net 11)) + (segment (start 219.0796 143.8886) (end 219.1995 144.0085) (width 0.254) (layer F.Cu) (net 11)) + (segment (start 216.764 143.8886) (end 219.0796 143.8886) (width 0.254) (layer F.Cu) (net 11)) + (segment (start 219.304 145.606) (end 220.3203 145.606) (width 0.254) (layer F.Cu) (net 12)) + (segment (start 221.4497 147.1074) (end 221.6357 146.9214) (width 0.254) (layer F.Cu) (net 12)) + (segment (start 221.4497 149.8475) (end 221.4497 147.1074) (width 0.254) (layer F.Cu) (net 12)) + (segment (start 220.3203 145.606) (end 221.6357 146.9214) (width 0.254) (layer F.Cu) (net 12)) + (segment (start 221.6357 146.9214) (end 225.7832 146.9214) (width 0.254) (layer F.Cu) (net 12)) + (segment (start 222.085 149.8475) (end 221.4497 149.8475) (width 0.254) (layer F.Cu) (net 12)) + (via (at 217.7418 153.4713) (size 0.889) (layers F.Cu B.Cu) (net 13)) + (segment (start 216.751 154.4705) (end 216.751 153.7717) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 217.266 155.1693) (end 216.751 155.1693) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 218.2962 156.1995) (end 217.266 155.1693) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 220.2152 156.1995) (end 218.2962 156.1995) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 221.4877 154.927) (end 220.2152 156.1995) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 222.504 154.927) (end 221.4877 154.927) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 216.751 154.4705) (end 216.751 155.1693) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 219.545 149.8475) (end 218.9097 149.8475) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 218.0664 150.6908) (end 218.9097 149.8475) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 216.764 150.6908) (end 218.0664 150.6908) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 216.764 152.1051) (end 216.764 150.6908) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 210.579 149.225) (end 211.8493 149.225) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 212.1128 148.9615) (end 211.8493 149.225) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 215.0347 148.9615) (end 212.1128 148.9615) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 216.764 150.6908) (end 215.0347 148.9615) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 216.764 152.1051) (end 216.764 153.5194) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 217.3763 153.8368) (end 217.7418 153.4713) (width 0.254) (layer B.Cu) (net 13)) + (segment (start 217.3763 156.794) (end 217.3763 153.8368) (width 0.254) (layer B.Cu) (net 13)) + (segment (start 216.764 153.7587) (end 216.751 153.7717) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 216.764 153.5194) (end 216.764 153.7587) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 217.6937 153.5194) (end 216.764 153.5194) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 217.7418 153.4713) (end 217.6937 153.5194) (width 0.254) (layer F.Cu) (net 13)) + (segment (start 215.722 156.794) (end 217.3763 156.794) (width 0.254) (layer B.Cu) (net 13)) + + (zone (net 1) (net_name GND) (layer B.Cu) (tstamp 53FA13A2) (hatch edge 0.508) + (connect_pads (clearance 0.254)) + (min_thickness 0.127) + (fill (arc_segments 16) (thermal_gap 0.254) (thermal_bridge_width 0.254)) + (polygon + (pts + (xy 205.0415 143.1544) (xy 231.0257 143.1671) (xy 230.9876 157.7721) (xy 205.0415 157.7086) + ) + ) + (filled_polygon + (pts + (xy 230.6467 157.3919) (xy 230.204154 157.3919) (xy 230.204154 156.26862) (xy 230.204154 144.56938) (xy 230.122949 144.161133) + (xy 229.891696 143.815038) (xy 229.545601 143.583785) (xy 229.137354 143.50258) (xy 227.565006 143.50258) (xy 227.156759 143.583785) + (xy 226.810664 143.815038) (xy 226.579411 144.161133) (xy 226.498206 144.56938) (xy 226.579411 144.977627) (xy 226.810664 145.323722) + (xy 227.156759 145.554975) (xy 227.565006 145.63618) (xy 229.137354 145.63618) (xy 229.545601 145.554975) (xy 229.891696 145.323722) + (xy 230.122949 144.977627) (xy 230.204154 144.56938) (xy 230.204154 156.26862) (xy 230.122949 155.860373) (xy 229.891696 155.514278) + (xy 229.545601 155.283025) (xy 229.170569 155.208426) (xy 229.170569 152.487481) (xy 229.150249 152.438302) (xy 229.150249 148.004381) + (xy 229.018458 147.685423) (xy 228.77464 147.441179) (xy 228.455913 147.308832) (xy 228.110801 147.308531) (xy 227.791843 147.440322) + (xy 227.547599 147.68414) (xy 227.415252 148.002867) (xy 227.414951 148.347979) (xy 227.546742 148.666937) (xy 227.79056 148.911181) + (xy 228.109287 149.043528) (xy 228.454399 149.043829) (xy 228.773357 148.912038) (xy 229.017601 148.66822) (xy 229.149948 148.349493) + (xy 229.150249 148.004381) (xy 229.150249 152.438302) (xy 229.038778 152.168523) (xy 228.79496 151.924279) (xy 228.476233 151.791932) + (xy 228.131121 151.791631) (xy 227.812163 151.923422) (xy 227.567919 152.16724) (xy 227.435572 152.485967) (xy 227.435271 152.831079) + (xy 227.567062 153.150037) (xy 227.81088 153.394281) (xy 228.129607 153.526628) (xy 228.474719 153.526929) (xy 228.793677 153.395138) + (xy 229.037921 153.15132) (xy 229.170268 152.832593) (xy 229.170569 152.487481) (xy 229.170569 155.208426) (xy 229.137354 155.20182) + (xy 227.565006 155.20182) (xy 227.156759 155.283025) (xy 226.810664 155.514278) (xy 226.579411 155.860373) (xy 226.498206 156.26862) + (xy 226.579411 156.676867) (xy 226.810664 157.022962) (xy 227.156759 157.254215) (xy 227.565006 157.33542) (xy 229.137354 157.33542) + (xy 229.545601 157.254215) (xy 229.891696 157.022962) (xy 230.122949 156.676867) (xy 230.204154 156.26862) (xy 230.204154 157.3919) + (xy 217.439555 157.3919) (xy 217.439555 157.225917) (xy 217.546403 157.204664) (xy 217.690609 157.108309) (xy 217.786964 156.964103) + (xy 217.8208 156.794) (xy 217.8208 154.233369) (xy 217.892706 154.233432) (xy 218.172874 154.117668) (xy 218.387415 153.903502) + (xy 218.503667 153.623536) (xy 218.503932 153.320394) (xy 218.388168 153.040226) (xy 218.174002 152.825685) (xy 217.894036 152.709433) + (xy 217.590894 152.709168) (xy 217.352248 152.807775) (xy 217.391007 152.769084) (xy 217.439445 152.652431) (xy 217.439555 152.526122) + (xy 217.439555 150.776122) (xy 217.39132 150.659385) (xy 217.302084 150.569993) (xy 217.185431 150.521555) (xy 217.059122 150.521445) + (xy 214.259122 150.521445) (xy 214.142385 150.56968) (xy 214.052993 150.658916) (xy 214.004555 150.775569) (xy 214.004445 150.901878) + (xy 214.004445 151.068669) (xy 214.000503 151.066036) (xy 213.8304 151.0322) (xy 212.704865 151.0322) (xy 212.504102 150.831085) + (xy 212.224136 150.714833) (xy 211.920994 150.714568) (xy 211.640826 150.830332) (xy 211.426285 151.044498) (xy 211.310033 151.324464) + (xy 211.309768 151.627606) (xy 211.425532 151.907774) (xy 211.639698 152.122315) (xy 211.919664 152.238567) (xy 212.222806 152.238832) + (xy 212.502974 152.123068) (xy 212.705195 151.9212) (xy 213.646281 151.9212) (xy 213.75339 152.028308) (xy 213.753391 152.028309) + (xy 213.897597 152.124664) (xy 214.004445 152.145917) (xy 214.004445 152.651878) (xy 214.05268 152.768615) (xy 214.141916 152.858007) + (xy 214.258569 152.906445) (xy 214.384878 152.906555) (xy 217.184878 152.906555) (xy 217.260092 152.875476) (xy 217.096185 153.039098) + (xy 217.086895 153.061469) (xy 217.059122 153.061445) (xy 215.864875 153.0615) (xy 215.7855 153.140875) (xy 215.7855 154.1905) + (xy 215.8055 154.1905) (xy 215.8055 154.3175) (xy 215.7855 154.3175) (xy 215.7855 155.367125) (xy 215.864875 155.4465) + (xy 216.9318 155.446549) (xy 216.9318 155.601445) (xy 215.6585 155.601445) (xy 215.6585 155.367125) (xy 215.6585 154.3175) + (xy 215.6585 154.1905) (xy 215.6585 153.140875) (xy 215.579125 153.0615) (xy 214.384878 153.061445) (xy 214.258569 153.061555) + (xy 214.141916 153.109993) (xy 214.05268 153.199385) (xy 214.004445 153.316122) (xy 214.0045 154.111125) (xy 214.083875 154.1905) + (xy 215.6585 154.1905) (xy 215.6585 154.3175) (xy 214.083875 154.3175) (xy 214.0045 154.396875) (xy 214.004445 155.191878) + (xy 214.05268 155.308615) (xy 214.141916 155.398007) (xy 214.258569 155.446445) (xy 214.384878 155.446555) (xy 215.579125 155.4465) + (xy 215.6585 155.367125) (xy 215.6585 155.601445) (xy 214.259122 155.601445) (xy 214.142385 155.64968) (xy 214.052993 155.738916) + (xy 214.004555 155.855569) (xy 214.004445 155.981878) (xy 214.004445 157.3919) (xy 205.4217 157.3919) (xy 205.4217 149.057815) + (xy 205.466432 149.166074) (xy 205.680598 149.380615) (xy 205.960564 149.496867) (xy 206.246298 149.497116) (xy 206.649591 149.900409) + (xy 206.793797 149.996764) (xy 206.793798 149.996764) (xy 206.9639 150.0306) (xy 211.57594 150.0306) (xy 211.653032 150.217174) + (xy 211.867198 150.431715) (xy 212.147164 150.547967) (xy 212.450306 150.548232) (xy 212.730474 150.432468) (xy 212.759694 150.403299) + (xy 212.802532 150.506974) (xy 213.016698 150.721515) (xy 213.296664 150.837767) (xy 213.599806 150.838032) (xy 213.879974 150.722268) + (xy 214.094515 150.508102) (xy 214.168811 150.329175) (xy 214.258569 150.366445) (xy 214.384878 150.366555) (xy 217.184878 150.366555) + (xy 217.301615 150.31832) (xy 217.391007 150.229084) (xy 217.439445 150.112431) (xy 217.439555 149.986122) (xy 217.439555 148.236122) + (xy 217.39132 148.119385) (xy 217.302084 148.029993) (xy 217.185431 147.981555) (xy 217.059122 147.981445) (xy 214.479648 147.981445) + (xy 214.510458 147.826555) (xy 217.184878 147.826555) (xy 217.301615 147.77832) (xy 217.391007 147.689084) (xy 217.439445 147.572431) + (xy 217.439555 147.446122) (xy 217.439555 145.696122) (xy 217.39132 145.579385) (xy 217.302084 145.489993) (xy 217.185431 145.441555) + (xy 217.059122 145.441445) (xy 214.259122 145.441445) (xy 214.142385 145.48968) (xy 214.052993 145.578916) (xy 214.004555 145.695569) + (xy 214.004445 145.821878) (xy 214.004445 146.202082) (xy 213.897597 146.223336) (xy 213.753391 146.319691) (xy 213.657036 146.463897) + (xy 213.6232 146.634) (xy 213.6232 147.633682) (xy 212.23284 149.024041) (xy 212.148494 149.023968) (xy 211.868326 149.139732) + (xy 211.866454 149.1416) (xy 209.252541 149.1416) (xy 209.252541 147.468889) (xy 209.146424 147.212069) (xy 208.950105 147.015406) + (xy 208.69347 146.908842) (xy 208.415589 146.908599) (xy 208.158769 147.014716) (xy 207.962106 147.211035) (xy 207.855542 147.46767) + (xy 207.855299 147.745551) (xy 207.961416 148.002371) (xy 208.157735 148.199034) (xy 208.41437 148.305598) (xy 208.692251 148.305841) + (xy 208.949071 148.199724) (xy 209.145734 148.003405) (xy 209.252298 147.74677) (xy 209.252541 147.468889) (xy 209.252541 149.1416) + (xy 207.148018 149.1416) (xy 206.874683 148.868265) (xy 206.874932 148.584094) (xy 206.759168 148.303926) (xy 206.545002 148.089385) + (xy 206.378179 148.020114) (xy 206.392374 148.005945) (xy 206.498938 147.74931) (xy 206.499181 147.471429) (xy 206.393064 147.214609) + (xy 206.196745 147.017946) (xy 205.94011 146.911382) (xy 205.662229 146.911139) (xy 205.4217 147.010524) (xy 205.4217 143.4998) + (xy 214.004445 143.4998) (xy 214.004445 145.031878) (xy 214.05268 145.148615) (xy 214.141916 145.238007) (xy 214.258569 145.286445) + (xy 214.384878 145.286555) (xy 217.184878 145.286555) (xy 217.301615 145.23832) (xy 217.391007 145.149084) (xy 217.439445 145.032431) + (xy 217.439555 144.906122) (xy 217.439555 144.5385) (xy 218.651885 144.5385) (xy 218.767298 144.654115) (xy 219.047264 144.770367) + (xy 219.350406 144.770632) (xy 219.630574 144.654868) (xy 219.845115 144.440702) (xy 219.961367 144.160736) (xy 219.961632 143.857594) + (xy 219.845868 143.577426) (xy 219.768377 143.4998) (xy 230.6467 143.4998) (xy 230.6467 157.3919) + ) + ) + ) +) diff --git a/classic/hw/classic-v1.net b/classic/hw/classic-v1.net new file mode 100644 index 0000000..ed66577 --- /dev/null +++ b/classic/hw/classic-v1.net @@ -0,0 +1,277 @@ +(export (version D) + (design + (source "classic-v1.sch") + (date "8/14/2020 1:20:06 PM") + (tool "Eeschema (5.1.6)-1") + (sheet (number 1) (name /) (tstamps /) + (title_block + (title) + (company) + (rev) + (date "22 aug 2014") + (source classic-v1.sch) + (comment (number 1) (value "")) + (comment (number 2) (value "")) + (comment (number 3) (value "")) + (comment (number 4) (value ""))))) + (components + (comp (ref U1) + (value ATTINY85) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part ATTINY85) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78C6C)) + (comp (ref U2) + (value LDO) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part LDO) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78C7B)) + (comp (ref J2) + (value USB) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part USB) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78CBD)) + (comp (ref J1) + (value ISP) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part ISP) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78CDD)) + (comp (ref SW1) + (value ESWITCH) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part ESWITCH) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78CF3)) + (comp (ref C1) + (value 10uF) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part CP1) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78D86)) + (comp (ref R1) + (value 47) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part R) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78DA1)) + (comp (ref D1) + (value LED) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part LED) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78DB1)) + (comp (ref C3) + (value .01uF) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part C) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78DE2)) + (comp (ref C2) + (value .1uF) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part C) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78E1D)) + (comp (ref C4) + (value 1uF) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part CP1) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78E77)) + (comp (ref R4) + (value 68) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part R) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78F69)) + (comp (ref R3) + (value 68) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part R) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78FA1)) + (comp (ref R2) + (value 1K5) + (footprint ~) + (datasheet ~) + (libsource (lib classic-v1-rescue) (part R) (description "")) + (sheetpath (names /) (tstamps /)) + (tstamp 53F78FA7))) + (libparts + (libpart (lib classic-v1-rescue) (part ATTINY85) + (fields + (field (name Reference) U) + (field (name Value) ATTINY85)) + (pins + (pin (num 1) (name PB5/NRES) (type BiDi)) + (pin (num 2) (name PB3/ADC1) (type BiDi)) + (pin (num 3) (name PB4/ADC2) (type BiDi)) + (pin (num 4) (name GND) (type power_out)) + (pin (num 5) (name PB0/MOSI) (type BiDi)) + (pin (num 6) (name PB1/MISO) (type BiDi)) + (pin (num 7) (name PB2/SCK/ADC1) (type BiDi)) + (pin (num 8) (name VCC) (type power_in)))) + (libpart (lib classic-v1-rescue) (part C) + (footprints + (fp SM*) + (fp C?) + (fp C1-1)) + (fields + (field (name Reference) C) + (field (name Value) C)) + (pins + (pin (num 1) (name ~) (type passive)) + (pin (num 2) (name ~) (type passive)))) + (libpart (lib classic-v1-rescue) (part CP1) + (footprints + (fp CP*) + (fp SM*)) + (fields + (field (name Reference) C) + (field (name Value) CP1)) + (pins + (pin (num 1) (name ~) (type passive)) + (pin (num 2) (name ~) (type passive)))) + (libpart (lib classic-v1-rescue) (part ESWITCH) + (fields + (field (name Reference) SW) + (field (name Value) ESWITCH)) + (pins + (pin (num 1) (name ~) (type passive)) + (pin (num 2) (name ~) (type passive)))) + (libpart (lib classic-v1-rescue) (part ISP) + (fields + (field (name Reference) J) + (field (name Value) ISP)) + (pins + (pin (num 1) (name MISO) (type power_in)) + (pin (num 2) (name +VCC) (type power_out)) + (pin (num 3) (name SCK) (type BiDi)) + (pin (num 4) (name MOSI) (type BiDi)) + (pin (num 5) (name RESET) (type BiDi)) + (pin (num 6) (name GND) (type BiDi)))) + (libpart (lib classic-v1-rescue) (part LDO) + (fields + (field (name Reference) U) + (field (name Value) LDO)) + (pins + (pin (num 1) (name IN) (type power_in)) + (pin (num 2) (name GND) (type power_out)) + (pin (num 3) (name EN) (type input)) + (pin (num 4) (name BP) (type passive)) + (pin (num 5) (name OUT) (type power_out)))) + (libpart (lib classic-v1-rescue) (part LED) + (footprints + (fp LED-3MM) + (fp LED-5MM) + (fp LED-10MM) + (fp LED-0603) + (fp LED-0805) + (fp LED-1206) + (fp LEDV)) + (fields + (field (name Reference) D) + (field (name Value) LED)) + (pins + (pin (num 1) (name A) (type passive)) + (pin (num 2) (name K) (type passive)))) + (libpart (lib classic-v1-rescue) (part R) + (footprints + (fp R?) + (fp SM0603) + (fp SM0805) + (fp R?-*) + (fp SM1206)) + (fields + (field (name Reference) R) + (field (name Value) R)) + (pins + (pin (num 1) (name ~) (type passive)) + (pin (num 2) (name ~) (type passive)))) + (libpart (lib classic-v1-rescue) (part USB) + (fields + (field (name Reference) J) + (field (name Value) USB)) + (pins + (pin (num 1) (name GND) (type power_out)) + (pin (num 2) (name D+) (type BiDi)) + (pin (num 3) (name D-) (type BiDi)) + (pin (num 4) (name VBUS) (type power_in))))) + (libraries + (library (logical classic-v1-rescue) + (uri "classic-v1-rescue.lib"))) + (nets + (net (code 1) (name PB2) + (node (ref SW1) (pin 1)) + (node (ref SW1) (pin 1)) + (node (ref J1) (pin 3)) + (node (ref U1) (pin 7))) + (net (code 2) (name VCC) + (node (ref U2) (pin 5)) + (node (ref C1) (pin 1)) + (node (ref C4) (pin 1)) + (node (ref R2) (pin 1)) + (node (ref J1) (pin 2)) + (node (ref U1) (pin 8))) + (net (code 3) (name PB3) + (node (ref U1) (pin 2)) + (node (ref R3) (pin 1))) + (net (code 4) (name PB4) + (node (ref R4) (pin 1)) + (node (ref U1) (pin 3))) + (net (code 5) (name PB5) + (node (ref U1) (pin 1)) + (node (ref J1) (pin 5))) + (net (code 6) (name GND) + (node (ref J2) (pin 1)) + (node (ref D1) (pin 2)) + (node (ref C1) (pin 2)) + (node (ref SW1) (pin 2)) + (node (ref U2) (pin 3)) + (node (ref C2) (pin 2)) + (node (ref C4) (pin 2)) + (node (ref U2) (pin 2)) + (node (ref C3) (pin 2)) + (node (ref U1) (pin 4)) + (node (ref J1) (pin 6)) + (node (ref SW1) (pin 2))) + (net (code 7) (name PB1) + (node (ref R1) (pin 1)) + (node (ref U1) (pin 6)) + (node (ref J1) (pin 1))) + (net (code 8) (name PB0) + (node (ref J1) (pin 4)) + (node (ref U1) (pin 5))) + (net (code 9) (name "Net-(J2-Pad3)") + (node (ref R3) (pin 2)) + (node (ref R2) (pin 2)) + (node (ref J2) (pin 3))) + (net (code 10) (name "Net-(D1-Pad1)") + (node (ref R1) (pin 2)) + (node (ref D1) (pin 1))) + (net (code 11) (name "Net-(J2-Pad2)") + (node (ref J2) (pin 2)) + (node (ref R4) (pin 2))) + (net (code 12) (name "Net-(C3-Pad1)") + (node (ref U2) (pin 4)) + (node (ref C3) (pin 1))) + (net (code 13) (name VBUS) + (node (ref J2) (pin 4)) + (node (ref C2) (pin 1)) + (node (ref U2) (pin 1))))) \ No newline at end of file diff --git a/classic/hw/classic-v1.pdf b/classic/hw/classic-v1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..27d21caa5282c66cc381148c32df2c0a2a29e2be GIT binary patch literal 21841 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 literal 0 HcmV?d00001 diff --git a/classic/hw/img/classic-v1-top.png b/classic/hw/img/classic-v1-top.png new file mode 100644 index 0000000000000000000000000000000000000000..1e417a5e830b7bfc831a82abedd561baa82753a5 GIT binary patch literal 21098 zcmV)rK$*XZP)xdE%$EYN-?1rNFb055MYMXnUD$jB_#R%$RwF06XHzBCnP{1p~is0 zU>k#bmy0CJvekQg($nkr^t1O~zdz2s_w^=Aj+n{%ypKM5@1A?kF6->Q_FCWdU5LOK z2m}Nm_z(FHIRkasvgWUz&1psic#D6H( z>ktA000e~7UHD)B9nKLUAORKwE&zbQ6h9;0y#J7y3W4EFGGxl+L>ht3_67v>`tI<$ z1QBpH(@q35x#rguiNwf9ypjEZ`fK{e3r^D0(`XL@`hG==Ohe{DDUb3jPjEIPiO4*! zq(!13_oy2I-l3dEd@CPOn=CyV8j1Cz*TQst_(TugVjvmyi}gpiIsmGYkz zK|sjj)M!1vQPQ%cC27V@A|NU3lX~~wp^b{N7TLMRze!XrrL!4o1_60~=_KshH}rz0 zywzH`#(xv9O0Dy=ty>5{2np%AEza^)<<>S^%4wdM=hWYu?%Q2G(U;iBaLN~P0>wf$ z_5lb$GF23{zC*rl*H!QCr>^4 z&FJ5s;M9E&0eXjgmBqXElBVAO*hv z7Z=$MM}#m?-@p5CG5d&+lbP4y-tn$QS*jg}@826d^hlS(xG}^S#ztMduJzhC%}o+; z9DM506R&LVcbPDT5FsUV`)%`=uWHINVqkvwy~EwTsYIMmwXpU!1L?$bD@|6%VAc)5 z0HDKJYP4p6jA3oboN9J4*O|wfO0qS``-(}LV=o3GDI}Ot567C-dx}Vwq1Em zTN2Sw7=G)K$95k%+U)zeR!Np$zWV&`{(=3+yILycC`mZu^XIf3KH1F>-*)wtH*C0Y zXfVI>+By4n2b*5*DaLRr#VY2eqt*H3Z=bK#u(}uneo4IeY|lUc%ia=7Lxc=tt!}=e z?VayiSRKq6$HtYVlKA51_iWzQ=Z||H0ZGepOCBO7VUou2td1jyU8ZhGJ>r9F3;&V@W{Lwp_o^syIz%cXWcn?g0XVBvy<8 zBE*p%zI)xgi$Wd83K^uSEwiu}*N z{V~V$$rvIssE{!P%#xIh0Yo4mpmLM_z}KF9apS8VbB#HDk7KdhR6N<*W?5Wr+qZMu z{sV$@L&TpD&&B4daMY@~fvH?$c8#;~vk#*Zm7Lh)6_2vhL$i9On$!WZM@JabVSfWZTmlD@~Tt z1_2S!a&2U+rdWVnu>MokOK!d9ycM0#Y<$^&-8MY|aE`@36K7fR$C1if9Dqi?S4F3a znNG4yY0U+4j-2C6jrTJy1Y|poSMh-;4r7(&Gd~O(W6muvGywwUsSc`r8`i0oIYw(U z_6h)Fs8nK4JsCuhSD4h3TUwm4KqU)1;|tc=aRkT+fPi9v;TVs!;mCpH0s#V~inM zJa6t$81`2K!MW0!GgkJ!!5}ooTy?>k554);IL%y-@7__3jPj;7-jN~W$a7$v0U`j^ zy+wcsfXEnQz&K}!#5gj|Sh1rthGX=Vj58u*xsH@pOMCf>o7a*tGqlwUFG>=nFF&<2 z45P_xlz%Tlz{0FXD6Glx27a;dP%(_D&SFRqkO3=!mzper3;+c1GIvM!d$RF3Bget| z?T9R3&pmZ&{f6eboz6Au)~#K#_(WfS$#s<0xzYO zUp#lNF=RP-;&}Yblie1usf>37Oii9rl7jY>R+-Xz_-bQFNsRNfABL2c+7Krkf-F=m zD$^=gMmJ7{q$LI_>W86B$r3O5C4ZoA=%N+N-~P+jZ+h%C+p}geLWr1W>hhb{pEz`C z$BrQn?K3&9Z=fJxrn4o>7p%W>rO{@nKY045TUM=Edj3U=kzvpA{*6y;rP)R5&VUdB zM-1ZpTVFf){tqpgztEXul{?y+3eO7yW=i>E@R(*=+J^rZ{0Ie*oLCHr1Ga_!qL?pWCRt*`$S z-)PkWBCwEmbwywN`#o1&HD|@Da;YMCJxGJ~qbLG4U>5{L27;>Ex2wAGxt<`(E#yCN?_#QrVLiN0D_w{#v@YZjA{ipvfZw?Vz$l_Q%^yu-Y9^;n93VoWq zLqJ4XW)jjCGHa?hGys68#qP*4k|lA%0RX`<3(`V~tE z%rf=wG1_NNh`8W3xL(N{=&O3*w92M5%@D9S>aHlC_pT4#^2ql$63`pIU^UtZ9I>#z zlT^>-D-6(MVtn9uywq0(2EK4xp{d0xx6W}28>1$M1W=1&5{!+n+sYgo*gfw3}#pkU=5!Qz4+y1VDoW>G!{V*cf7* zTdu8@&f-*B>||HMfmE)Ez!=~_J6|2RJ5_0{E!UEk%#uvy%Ch;f!yyA2Y5xD;CxnP9 zR|$lD-Gev1`^trF^Lu)G+FM%s`v={U^ReH1?=uf>{?_*&Xyp~u>|7YBGyWP6`>_as z1BQ`$=!q@@5KNTpO0Kgos{)_^<^UrC0)xO^?i@T4?l~M-Utpj4&FMOP>JNVZ6MMHEe&9#X z{d;YNXH~{JNEu*>OCaJx_$6-0wnL-x90&~&64{o|9LyoEpl1sg>o?$4ktY@+8bgZo z|A}KYDKg_YeS>8JJeJV} z047sW5Idg3gq0+bAwxhmb2GcTBYZqm{VuPp_HVR@T9YgQKn=AHh#vnfogEYiA_%`;(lk&7fC=s4A4Pq<5Mrc^z@GAri!qvhkyLM0bR7wW>SDW zBM+P+#5rDSb`k<(%;>X{L^D-LCyiq+c>Da;d8}D04G3PT^w6$vgsPt*i}ldK?8P^) z{ZDBa7v6HlSvVARAMfL5B2qLQ+phWIfd~P?wz+LFM6j$8QD_7rK!&!%v^KWQ#F$S3 zxIo+CwykZ4*G{s-a1D@>IS$u~Y=`4)CTVIZG}UD`B5JKH+nU?f5f6n%YeWQu(Bx<2 zL9tOYR1gMW0|x*@Cf11!_SuM=O{D=0F-!6f{N`IqP0m11RR}I@`N$u91wue%K*m_f zyFPxSK%uoJBFD8~*|_`Oe}2rhYyzMW0rLX&W(?6t7gpsM=&vg%OIpyswA00!87 zGC80>KHVzI3VoGo48CI;K zF+z$j{l)jTZQJdl?OPR&EdX5w%EC$K{Ozayt(puJ!)(CV+-_=X3WFp|Qlm``vyX^~ zw!>YQ<5;&%4Nu?r-57KnX4#B!&IRWngphRw03i6 zckL-vDp{H)VdydmK*b*6F}Zc~z~)W;#l;-a8}X^j_>X^d-2AXUC=6}Jp$-=rJK_dX zlWCP2WgBJ2D}&KQ$|#)?jE1HWBw+*qwqtu`KdOcWenaVubWTc}@p)ea#JSdKX4K^N z1sX7`*H)IUC?&}>ie=mE@WJrG`;RM7?ekmT`(JL~^3=}W!2yZ<%pSL9vS*Qn(gw>$ zOY3}D>2ksB!Mv+1+85g!@-Vl-LWX2WNa2=!WGqeMEKZ9s695=vw9?1+2gvO-)uVb& zfDE}1bt?&tF_z6vozk6_bbPPuH(9nw;;b>OQh>@bLnfm%9Sz?lAjdd)oaQ=BAjNSo zlg8SZ(Re#>xLw%@SB(H+foNdk2}s(!sjYL{rk--MW%LA!1q4E}fuPBU?y;DeQMy$M zhpHuRcbzJSn}ybFf^~_22{v%bzx1|_!R~{@>-SiNp*YrIpwe_2#ZrnTam^QMjETeK zEP2spqF6}vY#60-8luJSCCp{Un4}shog*+~3>U&FdB`w{;uMl%j0}(m&emsk?c98z z>r@|m@YJxCT{E7D0kfscI0FJA!?}>cA=2A--WLwVd6KPMzwqb&(~U~ijFLh>j4>)3 zwoVX%3?M^kTgXu5O6Rlo-4}UBjbF7kmLs2eV9O&9Y%Fk_WWWNsciZ9X?zrUqD=x)J zFqcHYIcw?+H$Q!F0tD#@-LhS`RI%4wb+M3Qik;{@BFnLl?eF%$+)wW z)t|(A2mqFrE?MkeIyg`Tz^>yxoAzyQ;gyLHn@B(cq-S}^SdzvWC2$(Y90W!BnyWTk zb^C=%>8J18{Oq2W7wzj@wtV5}=aGfdMytN&PAJ4UgUM4YiOBIBVF{zP(OL+cKCK>s zw9eE7%P=Bj85vUmaLyIoYA6-~p#-7a-fCQJw%Z^s8JcD{DYVKh`%JJj%5<75w>QwS z$UF`WZx#{}B52Ta<}|25ie?j=G4 zbb;T=@oE}jQYp}%{L}Xz+Hw55UwG)^8`c2B1+8;`<}^@&v_n>g20pt!YQgn`f`+ zPWKTR0&2Auy3{-fCnlR(0^&lr^vNWIR{fgWksvSF( zHox%eZ|U!;_MaLQVuH*OB9gXE1TF2QD{oo%C%^Uui;IT!I79frsF5iCq2Z+XNiL+R zxniK6N{3X?5NVji;GR~B?-N6ID(Z$JcMJ%D1!-J`fo5!OGy?$)kY!t1X=BJMdt8b< zP2(Vx;Pgb@U<%vZAh07xPaHme)B?*;&KXM~JAcFCBm267C~CEv43RNr%gZ|spE&-h z|L?3(&+VQRl(}SAHC=K_y5LQ zn_DZ6XKlD{_5b>-qu2I+%GK$;8yF<@a(b*;AL za=tM-8HxxD3}^KW&kq5ynr+9kI7!3kb!M-SAsel2n|7stkzRHCMJiYS`2XI0XrSu~ zd;QcqRN?h*DqGiG(yUeC`$c0^YbE!9b+oA4uIQAuRfHNC z;>(-*!bq{H@tk9t0Wet2XLvaR$imAo*2PAgiL`upceX8#LjZ8Nx45#-4Hhe{W~*Dp zR)_{vC0*=6B@siEA(OCBtPxS!uwe_7h$M)k2T%ItlH=NWnk7M4q}mzCE7{_?-qDlM ziNcL|&6-=5E<~@oXVP$#?Ty z<(X!HX%;0}hyVzT3y!8?7+@nElrioVyKs^wVSGlIy@<#{YuAp0uk6?j06upun4Z$8 z$YySxW4-T#i<3kb+Yo|kYt4{Ji(3FN?j#EYWVA5^X{tLr^7npl5whZ#37q4;-PNHe z=YX@?5yk5R-xOZd{^m9fQe9d`!%^+1Q+ zDWi5}6z6he9y$S)DrIR|d6vc1XymZ#j4y%fTb(089{8rYHCaul3!{U8EXQ`G{Py4d z^c^30(=G44X7?+H@W-!Qf9r;y`|yp%n9qFqlRI8I@ST5nNLpgpa|*;cGyTlpR6_bRB$S_Z{!qa%BgotcrIcOJ;Aedfdi3kC;qJjRb?)65bym>KqO0HA#jMgfM zCYusO$Q2Iu=Zx13CC;(#tj8&QiW9YD7CoIn2?@Eaj^x{Q4v37XHqJ)Tk#zeSXQ56` zXT<^p1T+eu5-#;Ms6Z*madcR;Kw2=yjWJ0W8#1H&OnC;SY*{nogHTmQHHKbVmgBpK zxaXC_fBWfg`X#5oXUKA_*IwGc`;|jR8!ot3TFCmzsFrqHJ3iMz`J<*&WhxG$Gk$U| zqQw9cco`;{AtWL$u(`z=v0@=Zl_eQW$CF0{Qc!b?B$Pk~5xHQT6|@ot5CsZD#+-Hj zNCINyRMRX30IK_Ris?uxGERU=GZg0c%w*WzQ0TFNOo8G|aFCi5(FDtQ&3it92beUZ z1V~UD;(`mravU;{rHN5lnkCWTkdQnL^Q3Bu%(dbyGZIlm1lkzRd0>JhjI%fmXiyB0 zt}*f9IRc3%A_1(wH~a2459>M`3K{*5Piz2EFd{xs?`9E#seM;U?CQ3@|Bb_bbD1%g z#t9HrtBL{GmdhQXAhE&IiAmDvY(fHS@5AIDBhAwHFg~P%zSl%jY0Moe=ZPNUyPMS6 z42GjX#tBA!#j-7Fi7ZL_qiX%z2)Q1CtU1zb=q!};`Mi~u2E_y)7Ii4C2KYgLA-OV`@K|G|4a)7y( ztK9h2;E6!@dc1kHe}fH`G!4(JO$4Q66A<{0t3GD)ouy-vxw!Sfi_a`r(vpoq zH%u^9WZV3}-oXvG-SWf_UpVnvZzE88Zen-|fFqWeZ1}OXX1wmE^?WyrQbhDUFAO8c zx97}h$+J9;lp+NH9KZ%<3C0Dp62^fcpmglr(UU{!6aouW-=1jWh2Gn;bSkZSy})@S zm5KA*^;P@HENyIsMq0SYTCWELLO!b*7?DgtBtUAJ?-_VDGZ6yoO^+~e8F54?h#L$N z;|v*NoHZbOM8@+<{p{$j>eHEtTkLs@#8pz-#db-C9N%tgD_?wh%jyd|;%J(LRWjh0 zh3i74tzz4uc=C(k`00veH(q@hNy#W?$wKX~^S{^;9zrat~h@BQLuzx%Pzyr-qT z($rRZYVM75JqR|5Cg6tO-)oI)dUEsp8x_A4MLD> zMTX2EW5B8^sBcSzz;#S}=()_q2&_LlLA2!>=c+pQO%0EgMLHN#NiuyVv>`|mt(7*l zO^^tRb(3CKOs&p~J4|QA5JrRC3na|!eczKx^PDnc3=vz}%R=%Wf8(hw+jhR|12@0v zZC5<_t!L8IEMJs->N9JR$s(%FIsWgz+Br0cE!KiK-dWtFMRqbH4NYoh5kN8+w0T`^QBMjgO=W&7IWr&PV0~jiL z&w0V;hCswYSdJ}i%Lm^r`KOKiH^yXfno$k_78DC1rY2*+WS!*}5i^q+AP4Nz)T42t za#gtCv@&_7wrtzcUTMANmJJVmXQR=?5DlTq2|3kSWzegX!bmL_S27TT`H8Y`8+!O1sFGGIIG$f4--f4ytMNkBk?Jk1cY5&-025cKJXbfC^0 zWI*f0tS#MqFy5S)YP+>$9$Wjbe|^uK&gKSc#w~fsFs_F6=m8-j6S7&(>&cH6#@8HJ zY}D(0>xqbQ?t(8ZiwkZ^$8zi$Db_^b5oDMq`6XAb0e}}bzUB)jRLKpuUfR>uzvH$2 z4%&=ipySJ~y5PKJ^X~oTqds$CMoH&WHUvQk5t$^@xsaTIY_LB1JMVt(;jItNf6~~ie&=nu)#d`JvZ!45sM+cbs~kfB%h0Pvz~ z*CcWB^u{d}?q@2y36{f5S}y6736UcYSofMn&lQpVY&7+q$q~cI5B1vP%)~4 zF{E|jORD+d(j-%vW`McM&tJCWm;d`a_HH{ARTG3WK=oGm@xJsw?z!{hHwjDr{U3hI zwk+4TkL*41b02xr;oT?qzIM#A*ui7=-+y_R%bbQ`X8|iWJGE2w)Q@23`W|CUW`;3F z^~cVjWoKO`NLVc*MMRKilq*V%LW2yD*LY)$0WjJqGNy2hrwW-2lDH@%BN;bfPW$*< zI7@Y|vN(-{FoM8!yr$-s3bd5`5^&;tn3B4&>5bg|5P%60SA&t~B%@UXu`$MWY!|#D z&{StAK?VB!ixxk1-==}V>P>fCZotfT^hb8LhGim>QuOo;{N*3~GXk?ga^Ro)qklm_ z4Z7ksNog4(j^u)H7;l_QitM)uxdKPe6miC-Z5079Wao<0xR6mi?+Q#yi}l_QF4CHi z0q2|yDWzSP?|60afd`MdOaK65n2^5e@;TSt)DeY>3&t2TxrZxlV<+rgLKn_s=f_c`(yWn~HKHa*?fter$FeOb`*Ag*`nrXH z22>g+uJ2jga(oxk!Qx>CjKi&m?tON^RByia%F{yjfohyI0TdLTm;y7+OXwkE1;*cJ zE+CRIj%BOdAb=svKo{(v#U1#}H!J`EP`RoO23mLEIS5TAAjS}Z*$!W_yj0_gAp!(4 z;FUygPX@p^XN)t>82~o7TFX|Jt3x?sHT*Xj@JpgpmH^0S7HgY$p6Y`mxP>x%aO*cR zodDqW>SLm8`>LZz4MIfMDKUvapfZ)DwSaUSL;z6Nt&m3Ui9}hFDy?nDcEGE#tpXB& z&8(8~aF&>YqN>Gto!)bUh-dm zeGL~@(QnT1n_oQ;r(`=b1sM{N25mGMLq^lor$VFYjbTj^H5E4ysbm0#29+OHvpUiP zi?JYMs!=ikLrTl69|Y$EP^~7-;ZPP-GhX+M7gr8t;Q$O(RWzytMR=7n&KXXra%eyZ z#sx2m)dadXgG6v?v@xIubXT)HFUbN#^vmTFX4mm(Cr7!|yQIYDMo|?3foLM#Dq4X` zH{0A6QVwIh444sx<))q00u$zyJdsgTt9#xeH%(1F2(4{trgkY6=BczD&xP|A zc_^&nec~LYz$l^F?!~SrN@Xdeo)SO`w#{AdjVSq6BVJ>IU4Hetq+c&s=yfa_25E(=qxPLs=;L28No^7^56kVuq=DnKhra7^MivvHdyNH^zfyXcS&yL zMM<~ux&;7$MBtU&46o3xaAyA!%|{^TQ~I|JrpWMQ#(k7!2Z6OR=s;YB8(vitj$ku zzx1ZHE7vb7`NnaRk!uYRNJ&jC?!x7cB}qCqV_0fVTRJk!&R~SYv$%3jXIV>5hMxKH z77GLbrdhUR*@7)cE;!OZqjDWAL2u671qT|%nh3!yBZc6MGYLWp4%wvhgN%D+Ur3RqS+yF*Lj@llApp^^LJc#ww!E<8xffr~NH1vjJO0~6 zW9YbjUp)U}_m}={XNgGjqBn*d$S(-b(Eu_$3;xrUAR|6$cP-NyXqD;key%lkJpexzUQ7myUX}0GYM+>Jp zjS`jTg|R~fS)6deY{%l9u>hZWXv_AO_F1;fQ`Of$;MlI#y1A{g>8V{WJ^q@}hKRV} z2$(`9q^Q$TPpDNjWVY5cFf_crY0xO_rnd4xPgSd&qc`5#LWV44zULa?11fW?!^sqE zmZ!qT;LrTxS&?a-zG49Y8Ka3b7^BSy)*yfy3W~^(2BVcR+GvIvv@x|9@dzp*0wQgU z0i$&R;sXG)ciZ9i&XyJHmt=7|!?hj}9{<6MCy(}8Akbi#MLp1~byFjxLqLOlK@+Sl z!!iU089M?;EU+b$2$+~Wf#HyCfB%pRp2aLhCWZWP|M9|v6d=Mh=}(6|@EKqNX#x?| zu;*+D!cawIh@hZkGmMEbT2*T;fT$lyh{(3QB+5SUn{NpR!`jCas)8oJ>>S#CTmlz> zG{U0zKS0iMEgb>?I=J)5!ChToAfYIQnHH2|z)IZXz_l?KU$OevKJtrx#VrsHH7Axa zMPf;z_Qh?bZ3!v0Rt+UC_8nx*a%{$#Qc5LR9adlalnHJ*He;Blc@Z^2hH;RrziQ>N z1E*Z@Y|qt73(MkC$Uao2g=E81E!o=vYxyD>(j^}En(kL0%Q5|BVCxmCFG85dcS_xJW|Qbpa$$uYOJRLv20 zBLwD3Tjg5kD%Hb{L_h&kRZUYf&ZI^FdG^(Cf>@!Hj4{(NEQ}^;F)qfkMl+6<#iYg9 zh(egsVnx;3F_Olq(d3pqY1>+xG@j|YC4imm>bw7&PYW)U&NIjoFb=o`CV(La*oY}X z1x5j@KvN270)bn@!_>T>8E6K!52XOA1epbVDE?T49o@1gEvr!@Bn^}1%&GQAK{etyX@TcjI%jjK)Z`gsidzXwdSy@L zIu4^2?KR>GahMJD2a_oZ05p{)7i6w6Q$@S(x1 zFd~X01p-E)I@y(kff`Y|mx6$&sUGhEMZi4EAON@IIi3pyGbyr4K!RK4a!J^jrCD6n zHrjXI(OIgvfH2e_Km6maT38K1DVo!6-*kH?0hbaE>=}IanNzml1#428>r2)&U3%r5 zEJrD5)5cS~b`9B*lcAyx`R&(tELvVs3P|UV{-mp~FB7cr51TYepq87eo0=-0{`{{D z^hRl-3KO_sP-2{`EH4nAT4~2~p7_DW$A9{~hwfzOJ7kbie#Na9U3T+DNt7~%wN2T! zRi3M?#>8Ya`4wAA{2!nAC#5Dz97V{%y#GHfUA)Xs6FoI-#TD*Zh4QKy%%-bCIm3H zEe3j{hyL}RmE(FWf6r87xq~0l}8%N@5&Z=%rU}H`FJeB8`u$__@2ccH8 z%e*dh3qx>x&+$D1$R^P(0`g1cqh{N|fz1Z=9Bc7v??#>qLnaK0v?_?gVQd!FkNrVa z#j!?)Nuo#I4gs>v1c8boBQ2E|)Bz)obxEwlKt-X^S``cS#6}B3o|&b~OV{1h9tMiB z!p~1YP$L|yrC;Jxk9H3P+0&lVs0qy&7Dc1S4?Y$=i)=a{4;4gbCJa;<>d^^ABYf*c7Gcf# zUNjDiJ#jeGkO3MnhQdIPre+D;= zEiVbhlwozig8w;)@+_;bTPa{+Z@Dl+w-8X9 z+)DlR<=c~( zPv>cV=O=C*?5&q!FC6N1xqz=bg8$T)+6sfBC&0-M>)G zo7RY)NsnEZ`z67}^aTVQmp2%Sjnc*#E;*3ax)!|{1H?GJ0hmOw5DZbtm+O}MX0p7P z`VmG5Ja#1N=}!etgC(+E$MM|px`lv_?>Z%q>VGe~l;ug9$Hk)%+0W8G2w;q{!D^r1 za%9Je7HG1V&}6_s2Wx%x={zAbP6#W$Te)I&teFF5a>FFkhQ`sItxpZCqXe#}%h)qU!R zP+>_SEs8T%-xiASIT<6Qv^^K&nMPoX_D%E`6t+HWZ#*6xgMuNH?d&Xdp2x1%iJ0wM zJ+WdH3n2n922;CsYE?84W@!3Np<8L302I3ALlFL~>y|Ob2luJ_HqBc!=g+?OTPoMz z`r8MNALxGfCvMwt{aV|RFFv+y_mKnZmvl^p=_3FDtu+w2WiO60qZ+I)Xtar{fm`xz z&q2iEs6G=JJD~^!AV73Gn~o12!v^oGVO>LAR%PqF?26o+9#!OIf;g)B%J?SE9!>jy zi*6AyU||@4@h`u-U~b1y5C&o7S*5#v_nS>EemEHP_V&-D(l{cjELXWgM7QLnQQR;U z06^s`8VbC!Z@CU4#^LF*92&;_=wy1N)^w=jcmPhOM?1ZB5ZJR?v?f(71T==`FKOq} zGG-D+00IJVYy>zot6og4TL@qmx>XJH{AXFWfCO@myz68SN7htovRpfj(!D265on9c z8FsCJJ10?sh;G@VDyR`=sMb2FhHlA|jtz)O5Y7z#J*K5-L_$Wvh*M!Le;<{WF@k$K zRFM;p3X$zt#}4&|gAo&aJS3waSy;Vc!D;x0r_ik=P5{w%9k+b$=~kV8a10i0Z10BQ zphN@`h2y)@u~n{;C>FpFc>`Tk%cGdMk3@t;05FRaZO|)wUdfAs33bc2Yzq7;ItDUr_!yc@a#Ko z$)_MxdEPKu8`Ci1q}Ev_C$jmd7A>CGZ#1H!>)0=WfueGFW7I5eIU-k+R%K%hu|}o< z1mw7`<=95+IEaeqY5{~LAjX+(i^&Q~kOA99AvpjbK$a$qF~@Vp>lPV`t09y=;w)i+MC7=x?Kwu9cudTmhye@Y_~4$N zY!dJS5!e=X_lC=A?g7l=1b`gR9jjYd%Sv)fVA+;*tf___kpL~HC~km7{5~h$nQurd z!O3cOZOcQ`UY@f+U98NkS+b^AtXiPJm{E1q2ncIA-P3E&XdbF0 z&*NApu~y0efHB6gU2^jy*~v=L}b}==_+gU3)?-f zV!Mtp6bIp0m>wcgzHH69taN^wOk7|Q0a9buUG2$;POm zQ1WbQK(u9Zqd{0!9eGha%BWZ_r5oEyheB}*0DL{Ck}b{?KEB4_X)4yR@kSt}uw9D_ zuC;0GdWgt*t*|OhS#S#hYdm@=XQ*2kVV!HFB8*0AxklowfOezRfT6HgXm`6a{yrF=PY3_uq~@^kAQ~ zt!OaFb7eEmnE+RBt^PRDg8*{H|NVoN*76hv01-(`MAg{guH(7Jn7A6M zd>p=rfB^=B(Xj(Pc{WvoI5K?s>0K(m@9>2TZr_arRS%ECD+M3&lC=Mbu(fNajB*?9scfGXv)oEu=0C$vtY^UWm}%yd2Ij5Ti<)dzTH(D*qE2N zs3v#G6>H!9@mo};|K)S{?%sFM9#;*k(H{$>O4DkluDj#XYu~ovsrz1f^8ObsVG&WD zq+D>zwf&}Y5=KTF#3EL25HVM3@sjzgFJ1b=!`q^2LS%}xsX8pUw%S{cMD&BfDgx%D z&Rg8M_OfNqKfEm%jJRNAh;zmTGg?zE|J+D!!H7*7$A>(UCK92A@o^fHO9zl4GYtZ{XedjvF7o|KJb5 z_9!w@vYX;ONb?tNx#Q~heDb#L-j|Mc%Ts-s7YUeMDuz!=_m$7T3K@Jh}& zI6NU@p6QcEd*e8V~&p_WYCE`m6mMIS^EbLbvK!(gs98ZUC5ICEqs6 zv^RUd^t(T&m07ZS!C(LBSA&6&42*dh5zuA6U-G@OUmdDeO8y7`+Z{=iEj)kT-~Rd6 zY;74bIzWqA)qF@7MJ0f_yt%^W51Hct&@AW8;Y+h5K5x-HuQWCGk{D;Fjt^v+k(^Ho zR6{Ui+UK^kc2ta>G#FQG)@hPkwhV{jQ^)(pYl*4Vx|j}{JV|=q*^o(%cLEZ?D9t|n zskhztuTS1{_e1S&J9iq!bJWrJ%9Df)kA_ts@?|$}IKJ%VM z=g(If-}h^Gj9;!0AxqV&3(x!7U;pswCpKe-uRg!~k{d7ZD*mtj@rU9dE?6fEjIwYf zA_F2WSl`M1d%yfxo-3m@=PXOq`m5Le-e>=_R(cH0Re?2dd~4GaJ6?Wzmu=YqU^z0Z zChIO=^*ev~v4XTi+7=fgi<2T5%NXO8+^x@D{@?>Yv3;uy9m!zoRpf7f?!zaK_7_=3&;H6k zj2UIdxo?+hRFTtFScNx;DRL^$I~X9_vwKbqK5+MwbL=^(Q+;*dp~M6Ntu>V!oAr5r z{@=g!>Hqn$1xq{BM4YokBsuR%`k#Je>(9@(y0-k(_B|NqxI@O0AX+g04#oulal!BY z=Lc6`y!6m(d;7clZP!lY)F@Rmn1}#CYg2A6t-tQ#Z`~CdV-AG__kZn)k9_(a4}a@H zuk5N^snNwz8N;X=Uw-SQOII&={^3_$SN2u=fAFP8Kk>)!`SDkOYneq`O{|U zYg)HUyV%Qye--j9F!y~xni`CX}ABh7N1bC)V|!oG9PUU^}6 z3VECB>GMha^e>00B6#6UX~>QeD4t5HJ}32t<~rQA;_9 zBE=XG0S9*U$f-1{UbZ1OrhzXAMt>#lXbBJOQ5l4B#_Ba(z$jR{tm(?@+QU#yW+@}W zV-KAQA_4$phy-Q??5R(Oc>;ff$oN*>>o3V)i9*Bq}q-=od0YIYQj0?-2IG9p=#j+4J z1ICbrwI%%RC6ruS?q;f1m{FPdKXVeu_W$BFU zZ|#T{18*$S_Oto)ZIUk|R5vQV&zh6sJO9z>+9kcjd~fu3i81Gn-o?=(JZY zbT2Skr%~9S4X$0a{Nn4@R0jginbMQijHq!B)j5hMa?0UYLK`Auz-U4?UL&y<{HBr1 z2oaGs#3p{Z(P%KHjxriS=vDjUsOV1ZVRq&U{y`}+BZh=2p$KVxVz0=Qf{u6^Q2@1K6+pR&wY_Au+o znUbmSSPG0`;sFE_`|Z2``1o5scvGI?3omXX6+w-k3RkaO^`YN+$Gu;D`m&oZFuYcL zWrA)42`Ox+=5r@QY}haUm#$Ri->oFgA;H+~LtfOp|QwRcntQ z?B2igXa!0S{_traSllwdrLA(;cfR(?Klp_mFYn)Z|6$Hr&d5X-6#O#~LdG56<5Hw? z8dR(0O66>LASMavASh#6q`kOP?T(Kg+#LMcA6|d;6K(rmJyMv5i_V{a#VzYzeg4(B zd+(yol^t#N(XP-Mm!(67ERe_dAOG@SJ-+>gJ+@;Z!!*t+ZB3?tc|bu7nXSEo9Xr@7 zKoliO2YZKJ*nhJ7&8ZDiX_UiG-nL` zvTKad%Gj=*gyH#Db|;Z8j^zkIhK?WVd+qUVsQC>^+L8;AL{^MCY%!J))}UVG8<#picML-7+2Z1XB^Qh6}du6|&RgUjku4A;0tKm%8tj~aA6&sRP zCRdDGPU}234wI9|s{qx;$3i;7LTx}PR#fkp#Fa$_L<#}sFK+wB-~8woe*G=){^*VK z7R_06$J8s^5H>xJT^xJRk?HX8d;i8_dzH8sQAxra~FZ4CFmhxO1Lq=(p<_OFf z%F^5hT(GF)SO4VwT(I5Sj+{8$_tsy$HjT5NeEZpr4{b9#kHUB+vApr!&eLSa<~_Fa z=8yjByG|bIU3JOQr+>8h@n>J$v}t>k=YRYc|7Fv&uf2A7=jF>5z5(Z!TDgPKT4lSA zRw}Av56|oqr^wO)Kwt=6M|*?8P+DS`1f_1^8be(0xeH2IaPUmhwi<2|AWQNO|L!}p zOzqmT|HR=_9Sd5bYNVBBY%J{^B4z+8Q??@?`QGzy{or-GHt!$k8T!=U{PGXJ^ytYW zr(EBm5e9$Z?9LL5!(_0x+I_6AsjbvDuc@V@a(H*wTi$>5y9$+H|=DIHfsqcvxLCFKw8!B4@LDTNTZ?Xs@vA>-+nX- z!Z~v*bLTe?4TSf9{qZ0OY87DXZ8Ky5AdZrb1uZ}S8*kb1;{G2${p9NT%QjrS^5J`) zGn5-cj_(?xJe_Sd2Ld7q;1K9A>Omg;9}q zp_PiO5gB6uQ=?{_?1~alFgC48H-n=D)%m$6`Jf==1OV$W5J#=Ojuum&;m@ zs@}jjQot&$P5nL9p}t_>_9N@&t^DtIedOu;H$VUAD;#(nM2=r@Es@F;%v#81wu25C z!vwcIj|-M1SrSAxvk)Oa%gfb-T4mX`%2gbMM(Y%)$(4(jHnp@_W1!ySMJej<%MKj~ z2BXYIKJ^Yhjbb69w1w?joR8bB0F5CoIU-2Qa!a1$*SHmpHdKvNUPG@dQaNb}@}K_n zy8!raU;3#8!5I7cU*As%8px2zbuNL6dU<9|MldFT9ov8MNB{cRl{a75b+G$gAG_Ic zt#5qpM^PHv!pfCmzzE289mjP6KuB&o_7gwcbo}6{q5jZvEWhmS*nHsN&f{&ZEnIM; z4YGPRCIJDIrR0Ji*>m!hjk_1GoO|2PU&R<+eEsS;OdkC1)0lxV#w+`7$&Z6D&*~UM zs=Wo%E;nRIlV+UTo+|_|X1DlzV^yzj=@T&<5)qxIXM7^9@-5p^xr(cyF#FQ@ksT8Yl#6NqwIt0AyU`X};>x^<9Vh4j<~W zP#PlFwSz3pK!5SB9~v;5UfQFaFkq};bRIjqBcek{M2s`X6Gg~t{C$ie$7Ryu z0LW%jsc)QC#a_vmwx#kss)j_A1HI*~3vPMKJe8BhEklG<0JUqq(#A;63X0c;T`dK& zrtHoSoY&lHJ#ha~3nz`{O@+r|438h^PLipj1jbOQELEPTkjPXPD;5zIaO^Zr!Z=*8 zV!_8gb@kyFNjlyHFcL(S#EUM!`Ah%nxxI%Dxx!BJ_@n>zPE$Q}>hMuvTeX*^PPNE5 z)46iXrOpd3|H2=C$M)Sk&D%R$KJaTdzWUgsT!;c(TKrpp2pD5=7_PeF((SJW_kQQ0 ziYTWlZEtV;_#eDu$CHl%02f?H2?#1vS}6oT#L9a&5Qdb1pRK zqsRJozGC7;v&n`ADNK{P-^k~UwQA%v5HVoYFy8q}cb-k`&LAR7V%N!J0q7wW%+M(? zMyt{=_R5~LtUS--Y6wIbnKi4L-gMjCETJ^Z?tb=(9fyw0Yi)6C>rmIp&N*#@v!mTT zt>sdfrU-D^>Q(!WA3xUJvwHD)KX>glj<#;RrQ_iK;I$nCHu4FL`RQ#enk?t7_9;LS zM~2}@@<^zE5Qlk|*Vl=OLCJ`iC3z!>g@DSf$@lfgw`?uO?S&mXcN{wW`@i%-X^G3PYTLeJV6qVKRBST@P?UW(Q)Q&oY8a1% z05sA_yt3~~&-J~+M59TbMNsDnu8USW3{?;)F4(xHBN?5WoFpMbovVyeK*-WOtSaO% zL06HfB=;jV>WwGJQxyc0ndGQ4mxM$nO-32wngo9InNS}T1Wv}G)bq~UnGFGdihE`=!H7W|KX=BQc zZ11oow_e=8W8-T(3U>o*REVif;;cXWv?kvdLX2IF3f*FiF^9J z6tSH>jDUW0uUD=U7&fWt=fXL`00S0t(0&D;UM0zG5*%t+g%X()shW z(MhbsP_s$64kop+7+@HyZ9DsC(q9QoO!5E)xyqH`=$9n7CgkrzEu|1+&KFdeI}=pT zLM)bam8o#3Ix^?1-~yOn%SARB(d48{DYC-qw>`IPRZ7jJ#ydS*c_V>l2#_Xe7T2{Z zNyif#afXpr2lj@Gm-tINJ3sbwZ~Mo4@5_}EoHN8sslxY=E7fRxdvnuZ7-e}bh4_`X zzjgWi1)9`>y43~lc&`PIKjC`V68;E%lt1KVWNUe1m$CDwG^CZraOyw-g zvOLYE-u})7K>(voo@H)nQmukoqAYvviBlI|+}zwO-+JYh>z6M(arL zECS+Ocx9j0V}cW3Mj(*&Rnxn_ap?UYTGHHxOXqj2SkS4BA)+-)7U{yATDrI~@>#Ct zG&c#xG-(5RuwUK%je~td>14hJINOCVjW#YgufDMNuHX4rDft+%W}~MF_~WnZ6Nh_T zaI`i8_?D-3E?c)~-ij5Ios~Feo1WNS=sgEM*jwH9{QkMimN%}kTpJmqxf8Y3kWZpjwVooN1j*_dl?h}Drisll;BNhhWPoBwMzEQH$9yS9q>do~f#q11EvWAO zkHd5-bwL0EX-nIY=TgSrG?Jy+)LJ#%KLiDold0I!b;b1dBS9Rip){As#3Y*_0ST~< z!>ea^#VUj$&LnZ5fsG&lm)U^Mv|nkIh-Qdo=8qmVL>N^73=pz7PD82}Gypa=yB7ff z$8uAdgEnQ@SaB0Yo>tt>CNssfeO^{d}juEeNM?a#9Vrg%Zy*)84PHl zODF(9Ls9qaXTUi94>{+B0MLL&1ja$Y&x{fAA9C&s8c_c~>Nf9ioNF~(0000bbVXQn zWMOn=I%9HWVRU5xGB7bTEigDOGBi{%HaayoIx#gXFfckWFdv7jqW}N^C3HntbYx+4 zWjbwdWNBu305UK!G%YYVEiyDzF*Z6iH##vjD=;uRFfeY~6CVHo002ovPDHLkV1k;y BUZwy5 literal 0 HcmV?d00001 diff --git a/classic/hw/img/classic-v1.jpg b/classic/hw/img/classic-v1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..04ac7930cd7aad48b2095bac54e040a3028a954f GIT binary patch literal 232987 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 z�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
oz<_L9J*0A3Md| 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 + mem.SetBinary(uint32(indexSN+6), newSN) + + mem.DumpIntelHex(os.Stdout, 16) +} + +func randSeq(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = snLetters[rand.Intn(len(snLetters))] + } + return string(b) +} + +func contains(a []string, x string) bool { + for _, n := range a { + if x == n { + return true + } + } + return false +} + +func snBytes(i string) []byte { + + bytes := make([]byte, len(i)*2) + r := []byte(i) + idx := 0 + for _, rx := range r { + bytes[idx] = rx + idx = idx + 2 + } + return bytes + +} + +func snString(i string) string { + o := "{ \"SN\": [" + r := []rune(i) + c := "" + for _, rx := range r { + o = fmt.Sprintf("%s%s-%d, 0", o, c, rx) + c = ", " + } + return o + " ] }" +} + +func popLine(f *os.File) ([]byte, error) { + fi, err := f.Stat() + if err != nil { + return nil, err + } + buf := bytes.NewBuffer(make([]byte, 0, fi.Size())) + + _, err = f.Seek(0, io.SeekStart) + if err != nil { + return nil, err + } + _, err = io.Copy(buf, f) + if err != nil { + return nil, err + } + + line, err := buf.ReadBytes('\n') + if err != nil && err != io.EOF { + return nil, err + } + + _, err = f.Seek(0, io.SeekStart) + if err != nil { + return nil, err + } + nw, err := io.Copy(f, buf) + if err != nil { + return nil, err + } + err = f.Truncate(nw) + if err != nil { + return nil, err + } + err = f.Sync() + if err != nil { + return nil, err + } + + _, err = f.Seek(0, io.SeekStart) + if err != nil { + return nil, err + } + return line, nil +}