parent
c7cf8448b8
commit
35aeaaea5b
@ -0,0 +1,125 @@ |
||||
#
|
||||
# Targets:
|
||||
# <none> generate flash file
|
||||
# install generate flash
|
||||
# download download flash
|
||||
# check shows md5 and sha1 sum of uploaded code
|
||||
# clean remove obj- and temporary files
|
||||
#
|
||||
# change PROJNAME for new projects
|
||||
# add your object files to OBJS
|
||||
#
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# project specific things
|
||||
# change these definitions for new projects
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
PROJNAME = arduino_HOTP
|
||||
OBJS = main.o \
|
||||
trunc.o \
|
||||
calib.o \
|
||||
usb_callback.o \
|
||||
sha1/sha1-asm.o \
|
||||
hmac-sha1/hmac-sha1.o \
|
||||
mem_eval/mem_eval.o \
|
||||
usbdrv/usbdrv.o \
|
||||
usbdrv/oddebug.o \
|
||||
usbdrv/usbdrvasm.o \
|
||||
usart/usart.o
|
||||
|
||||
INCLUDE = -Isha1 -Ihmac-sha1 -Iusbdrv -Iusart -Imem_eval
|
||||
|
||||
DEBUG = -DDEBUG -DF_CPU=16000000
|
||||
#DEBUG = -DDEBUG -DCALIB -DF_CPU=16000000
|
||||
|
||||
# target architecture
|
||||
MCU = atmega168
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# macros for the tools
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
BAUD = 19200
|
||||
|
||||
BINFORMAT = ihex
|
||||
|
||||
# programmer format, this depends on programmer or bootloader you're using
|
||||
#PROGFORMAT = dasa2
|
||||
#PROGFORMAT = dapa
|
||||
#PROGFORMAT = stk200
|
||||
#PROGFORMAT = usbtiny
|
||||
PROGFORMAT = stk500v2
|
||||
|
||||
# programmer-device-file
|
||||
#IFCTYPE = /dev/ttyS0
|
||||
#IFCTYPE = /dev/ttyS1
|
||||
#IFCTYPE = /dev/ttyACM0
|
||||
#IFCTYPE = /dev/ttyACM1
|
||||
IFCTYPE = /dev/ttyUSB0
|
||||
#IFCTYPE = /dev/ttyUSB1
|
||||
|
||||
# Tools
|
||||
AS = avr-as
|
||||
ASLD = avr-gcc -x assembler
|
||||
LD = avr-ld
|
||||
CC = avr-gcc
|
||||
OBJCOPY = avr-objcopy
|
||||
#PROGR = uisp
|
||||
#PROGR = at16prog
|
||||
PROGR = lboot
|
||||
#PROGR = avrdude
|
||||
|
||||
# Flags
|
||||
CFLAGS = -Wall -Os -mmcu=$(MCU) $(DEBUG)
|
||||
LDFLAGS = -Wl,-Map=$*.map -mmcu=$(MCU)
|
||||
OCFLAGS = -O $(BINFORMAT)
|
||||
# at16prog
|
||||
#PRFLAGS = $(IFCTYPE) -dprog=$(PROGFORMAT) -dpart=$(MCU)
|
||||
# avrdude
|
||||
#PRFLAGS = -F -p $(MCU) -P $(IFCTYPE) -c $(PROGFORMAT) -b $(BAUD)
|
||||
PRFLAGS = -d $(IFCTYPE) -b $(BAUD)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# the targets
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
all: $(PROJNAME).hex $(PROJNAME).elf $(OBJS) |
||||
|
||||
%.hex: %.elf |
||||
$(OBJCOPY) $(OCFLAGS) $< $@
|
||||
@chmod ugo-x $@
|
||||
|
||||
%.elf: $(OBJS) |
||||
$(CC) $(OBJS) $(LIBS) $(LDFLAGS) -o $@
|
||||
|
||||
%.o: %.c |
||||
$(CC) $(CFLAGS) $(INCLUDE) -Wa,-a=$*.list -c -o $@ $<
|
||||
|
||||
%.o: %.S |
||||
$(CC) $(CFLAGS) $(INCLUDE) -Wa,-a=$*.list -c -o $@ $<
|
||||
|
||||
install: $(PROJNAME).hex |
||||
$(PROGR) $(PRFLAGS) -p $(PROJNAME).hex
|
||||
#$(PROGR) $(PRFLAGS) -U flash:w:$(PROJNAME).hex
|
||||
|
||||
download: $(PROJNAME).down.hex |
||||
echo ""
|
||||
|
||||
$(PROJNAME).down.hex: |
||||
$(PROGR) $(PRFLAGS) -U flash:r:$(PROJNAME).down.hex:i
|
||||
|
||||
check:$(PROJNAME).down.hex $(PROJNAME).hex |
||||
$(PROGR) $(PRFLAGS) -v $(PROJNAME).hex
|
||||
|
||||
|
||||
TMP=*.hex *.elf *.map *.list *.o *~
|
||||
|
||||
define TMPCLEAN |
||||
@cd $(dir); rm -f $(TMP) |
||||
|
||||
endef |
||||
SCHLIMM=cd $(dir);echo $(dir)$(TMP)
|
||||
|
||||
clean: |
||||
$(foreach dir,$(dir $(OBJS)), $(TMPCLEAN))
|
||||
@ -0,0 +1,63 @@ |
||||
#ifdef CALIB |
||||
|
||||
/*
|
||||
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 usbEventResetReady(void){ |
||||
calibrateOscillator(); |
||||
/*first read the oscilator; if the value differs then we change it*/ |
||||
if(eeprom_read_byte(0)!=OSCCAL){ |
||||
/* store the calibrated value in EEPROM */ |
||||
eeprom_write_byte(0, OSCCAL); |
||||
}
|
||||
} |
||||
|
||||
/* 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){ |
||||
uint8_t step = 128; |
||||
uint8_t 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; |
||||
} |
||||
|
||||
#else |
||||
void usbEventResetReady(void){} |
||||
#endif //CALIB
|
||||
@ -0,0 +1,46 @@ |
||||
/* config.h */ |
||||
/*
|
||||
This file is part of the AVR-Crypto-Lib. |
||||
Copyright (C) 2008 Daniel Otte (daniel.otte@rub.de) |
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
#ifndef __CONFIG_H__ |
||||
#define __CONFIG_H__ |
||||
#include <avr/io.h> |
||||
//#define F_CPU 20000000
|
||||
// #define F_CPU 16000000 /* oscillator-frequency in Hz */
|
||||
// #define F_CPU 14745600
|
||||
|
||||
#define DEBUG_METHOD uart |
||||
|
||||
/*#include "uart_defs.h"
|
||||
|
||||
#define UART0_I 1 |
||||
#ifndef UART0_BAUD_RATE |
||||
#define UART0_BAUD_RATE 115200 |
||||
#endif |
||||
#define UART0_PARATY UART_PARATY_NONE |
||||
#define UART0_STOPBITS UART_STOPBITS_1 |
||||
#define UART0_DATABITS UART_DATABITS_8 |
||||
#define UART0_RXBUFFER_SIZE 255 |
||||
#define UART0_TXBUFFER_SIZE 120 |
||||
#define UART0_SWFLOWCTRL 1 |
||||
#define UART0_THRESH_LOW 0 |
||||
#define UART0_THRESH_HIGH 32*/ |
||||
|
||||
#define CLI_AUTO_HELP |
||||
|
||||
#endif |
||||
|
||||
@ -0,0 +1,131 @@ |
||||
/* hmac-sha1.c */ |
||||
/*
|
||||
This file is part of the AVR-Crypto-Lib. |
||||
Copyright (C) 2008 Daniel Otte (daniel.otte@rub.de) |
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
/**
|
||||
*
|
||||
* implementation of HMAC as described in RFC2104 |
||||
* Author: Daniel Otte |
||||
* email: daniel.otte@rub.de |
||||
* License: GPLv3 or later |
||||
**/ |
||||
|
||||
/*
|
||||
* hmac = hash ( k^opad , hash( k^ipad , msg)) |
||||
*/ |
||||
|
||||
#include <stdint.h> |
||||
#include <string.h> |
||||
#include "config.h" |
||||
#include "sha1.h" |
||||
#include "hmac-sha1.h" |
||||
|
||||
#define IPAD 0x36 |
||||
#define OPAD 0x5C |
||||
|
||||
|
||||
#ifndef HMAC_SHORTONLY |
||||
|
||||
void hmac_sha1_init(hmac_sha1_ctx_t *s, const void* key, uint16_t keylength_b){ |
||||
uint8_t buffer[SHA1_BLOCK_BYTES]; |
||||
uint8_t i; |
||||
|
||||
memset(buffer, 0, SHA1_BLOCK_BYTES); |
||||
if (keylength_b > SHA1_BLOCK_BITS){ |
||||
sha1((void*)buffer, key, keylength_b); |
||||
} else { |
||||
memcpy(buffer, key, (keylength_b+7)/8); |
||||
} |
||||
|
||||
for (i=0; i<SHA1_BLOCK_BYTES; ++i){ |
||||
buffer[i] ^= IPAD; |
||||
} |
||||
sha1_init(&(s->a)); |
||||
sha1_nextBlock(&(s->a), buffer); |
||||
|
||||
for (i=0; i<SHA1_BLOCK_BYTES; ++i){ |
||||
buffer[i] ^= IPAD^OPAD; |
||||
} |
||||
sha1_init(&(s->b)); |
||||
sha1_nextBlock(&(s->b), buffer); |
||||
|
||||
|
||||
#if defined SECURE_WIPE_BUFFER |
||||
memset(buffer, 0, SHA1_BLOCK_BYTES); |
||||
#endif |
||||
} |
||||
|
||||
void hmac_sha1_nextBlock(hmac_sha1_ctx_t *s, const void* block){ |
||||
sha1_nextBlock(&(s->a), block); |
||||
} |
||||
void hmac_sha1_lastBlock(hmac_sha1_ctx_t *s, const void* block, uint16_t length_b){ |
||||
while(length_b>=SHA1_BLOCK_BITS){ |
||||
sha1_nextBlock(&s->a, block); |
||||
block = (uint8_t*)block + SHA1_BLOCK_BYTES; |
||||
length_b -= SHA1_BLOCK_BITS; |
||||
} |
||||
sha1_lastBlock(&s->a, block, length_b); |
||||
} |
||||
|
||||
void hmac_sha1_final(void* dest, hmac_sha1_ctx_t *s){ |
||||
sha1_ctx2hash(dest, &s->a); |
||||
sha1_lastBlock(&s->b, dest, SHA1_HASH_BITS); |
||||
sha1_ctx2hash(dest, &(s->b)); |
||||
} |
||||
|
||||
#endif |
||||
|
||||
/*
|
||||
* keylength in bits! |
||||
* message length in bits! |
||||
*/ |
||||
void hmac_sha1(void* dest, const void* key, uint16_t keylength_b, const void* msg, uint32_t msglength_b){ /* a one-shot*/ |
||||
sha1_ctx_t s; |
||||
uint8_t i; |
||||
uint8_t buffer[SHA1_BLOCK_BYTES]; |
||||
|
||||
memset(buffer, 0, SHA1_BLOCK_BYTES); |
||||
|
||||
/* if key is larger than a block we have to hash it*/ |
||||
if (keylength_b > SHA1_BLOCK_BITS){ |
||||
sha1((void*)buffer, key, keylength_b); |
||||
} else { |
||||
memcpy(buffer, key, (keylength_b+7)/8); |
||||
} |
||||
|
||||
for (i=0; i<SHA1_BLOCK_BYTES; ++i){ |
||||
buffer[i] ^= IPAD; |
||||
} |
||||
sha1_init(&s); |
||||
sha1_nextBlock(&s, buffer); |
||||
while (msglength_b >= SHA1_BLOCK_BITS){ |
||||
sha1_nextBlock(&s, msg); |
||||
msg = (uint8_t*)msg + SHA1_BLOCK_BYTES; |
||||
msglength_b -= SHA1_BLOCK_BITS; |
||||
} |
||||
sha1_lastBlock(&s, msg, msglength_b); |
||||
/* since buffer still contains key xor ipad we can do ... */ |
||||
for (i=0; i<SHA1_BLOCK_BYTES; ++i){ |
||||
buffer[i] ^= IPAD ^ OPAD; |
||||
} |
||||
sha1_ctx2hash(dest, &s); /* save inner hash temporary to dest */ |
||||
sha1_init(&s); |
||||
sha1_nextBlock(&s, buffer); |
||||
sha1_lastBlock(&s, dest, SHA1_HASH_BITS); |
||||
sha1_ctx2hash(dest, &s); |
||||
} |
||||
|
||||
@ -0,0 +1,41 @@ |
||||
/* hmac-sha1.h */ |
||||
/*
|
||||
This file is part of the AVR-Crypto-Lib. |
||||
Copyright (C) 2008 Daniel Otte (daniel.otte@rub.de) |
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
#ifndef HMACSHA1_H_ |
||||
#define HMACSHA1_H_ |
||||
|
||||
#include "sha1.h" |
||||
|
||||
#define HMAC_SHA1_BITS SHA1_HASH_BITS |
||||
#define HMAC_SHA1_BYTES SHA1_HASH_BYTES |
||||
#define HMAC_SHA1_BLOCK_BITS SHA1_BLOCK_BITS |
||||
#define HMAC_SHA1_BLOCK_BYTES SHA1_BLOCK_BYTES |
||||
|
||||
typedef struct{ |
||||
sha1_ctx_t a, b; |
||||
} hmac_sha1_ctx_t; |
||||
|
||||
|
||||
void hmac_sha1_init(hmac_sha1_ctx_t *s, const void* key, uint16_t keylength_b); |
||||
void hmac_sha1_nextBlock(hmac_sha1_ctx_t *s, const void* block); |
||||
void hmac_sha1_lastBlock(hmac_sha1_ctx_t *s, const void* block, uint16_t length_b); |
||||
void hmac_sha1_final(void* dest, hmac_sha1_ctx_t *s); |
||||
|
||||
void hmac_sha1(void* dest, const void* key, uint16_t keylength_b, const void* msg, uint32_t msglength_b); |
||||
|
||||
#endif /*HMACSHA1_H_*/ |
||||
@ -0,0 +1,77 @@ |
||||
/*
|
||||
* This code is based on seveal VUSB-Examples the |
||||
* HID was taken from the CapsLocker code from macetech |
||||
* http://macetech.com/blog/?q=node/46
|
||||
*
|
||||
*/ |
||||
#include "main.h" |
||||
|
||||
#define SECRET {'J', 'e', 'f', 'e'}; |
||||
#define SECLEN 20 |
||||
|
||||
#define MSG "what do ya want for nothing?" |
||||
|
||||
#include <avr/io.h> |
||||
#include <avr/wdt.h> |
||||
#include <avr/eeprom.h> |
||||
#include <avr/interrupt.h> |
||||
#include <avr/pgmspace.h> |
||||
#include <util/delay.h> |
||||
#include <stdlib.h> |
||||
|
||||
#include "usbdrv.h" |
||||
|
||||
uint8_t reportBuffer[2]; /* buffer for HID reports */ |
||||
uint8_t idleRate; |
||||
|
||||
void buildReport(uint8_t mod, uint8_t key){ |
||||
reportBuffer[0] = mod; |
||||
reportBuffer[1] = key; |
||||
} |
||||
|
||||
|
||||
|
||||
int main(void){ |
||||
#ifdef CALIB |
||||
uint8_t calibrationValue; |
||||
#endif |
||||
|
||||
uint8_t *p; |
||||
uint16_t siz; |
||||
uint8_t sec[SECLEN]=SECRET; |
||||
|
||||
InitializeMemory(); |
||||
|
||||
initUSART(); |
||||
tx('+'); |
||||
sendunum( get_otp_from_cnt(777,8,sec,SECLEN) ); |
||||
|
||||
#ifdef CALIB |
||||
/* oszillator calibration */ |
||||
calibrationValue = eeprom_read_byte(0); /* calibration value from last time */ |
||||
if(calibrationValue != 0xff){ |
||||
OSCCAL = calibrationValue; |
||||
} |
||||
#endif //CALLIB
|
||||
//odDebugInit();
|
||||
usbDeviceDisconnect(); |
||||
_delay_ms(300); /* mustn't be that exact */ |
||||
usbDeviceConnect(); |
||||
|
||||
usbInit(); |
||||
sei(); |
||||
for(;;){ /* main event loop */ |
||||
usbPoll(); |
||||
/* we can send another key */ |
||||
if(usbInterruptIsReady()){ |
||||
buildReport(0x00, 'A'); |
||||
usbSetInterrupt(reportBuffer, sizeof(reportBuffer)); |
||||
} |
||||
|
||||
AnalyzeMemory(&p,&siz); |
||||
sendunum(siz); |
||||
_delay_ms(2000); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
@ -0,0 +1,18 @@ |
||||
#include "sha1/sha1.h" |
||||
#include "hmac-sha1/hmac-sha1.h" |
||||
#include "mem_eval/mem_eval.h" |
||||
#include "trunc.h" |
||||
#include "usart.h" |
||||
|
||||
#include <util/delay.h> |
||||
#include <avr/io.h> |
||||
|
||||
#define _PORT_BTN B |
||||
#define _NUM_BTN 3 |
||||
|
||||
#define PORT_BTN PORT_PORT_BTN |
||||
#define DDR_BTN DDR_PORT_BTN |
||||
#define P_BTN P_PORT_BTN_NUM_BTN |
||||
#define BTN_VECT INT_NUM_BTN_vect |
||||
|
||||
|
||||
@ -0,0 +1,68 @@ |
||||
/*==========================================================================*/ |
||||
/* Project: AVR-GCC SRAM Memory Evaluation */ |
||||
/* Title: Memory Init and Anlyze Functions */ |
||||
/* Purpose: SRAM memory ressource analysis of Atmel programs */ |
||||
/* */ |
||||
/* File: mem_eval.c */
|
||||
/* Revision 0.1 */ |
||||
/* Date: 10/2003 */ |
||||
/* */ |
||||
/* Description: Provides an initialization and memory analyzation function */ |
||||
/* Software: AVR-GCC */ |
||||
/* Hardware: AVR AT90 and ATmega series */ |
||||
/* */ |
||||
/* Authors: Wilfried Elmenreich */ |
||||
/* TU Vienna, Real Time Group */ |
||||
/*==========================================================================*/ |
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// HEADER FILES //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "mem_eval.h" |
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// FUNCTIONS //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void InitializeMemory() |
||||
{ |
||||
uint8_t *memptr; |
||||
|
||||
for(memptr = EndOfUsedMem(); memptr < GetStackPtr(); memptr++) |
||||
*memptr = (uint16_t)memptr & 0xFF;
|
||||
} |
||||
|
||||
void AnalyzeMemory(uint8_t **largest_begin,uint16_t *largest_size) |
||||
{ |
||||
uint8_t *memptr, *endptr, *section_begin; |
||||
uint16_t section_size; |
||||
|
||||
*largest_size=0; |
||||
memptr = *largest_begin = EndOfUsedMem(); |
||||
endptr = GetStackPtr(); |
||||
|
||||
while(memptr < endptr) |
||||
{ |
||||
if (*memptr == ((uint16_t)memptr & 0xFF)) |
||||
{ |
||||
section_begin = memptr; |
||||
while(1) |
||||
{ |
||||
memptr++; |
||||
//*largest_begin = (uint8_t *) 1;
|
||||
if ((*memptr != ((uint16_t)memptr & 0xFF)) || (memptr == endptr-1))
|
||||
{ |
||||
//*largest_begin = (uint8_t *) 2;
|
||||
if ((section_size = (memptr - section_begin)) > *largest_size) |
||||
{ |
||||
*largest_size = section_size; |
||||
*largest_begin = section_begin; |
||||
} |
||||
break; |
||||
} |
||||
} /* while(1) */ |
||||
} |
||||
memptr++; |
||||
} /* while(memptr < endptr) */ |
||||
} |
||||
@ -0,0 +1,56 @@ |
||||
/*==========================================================================*/ |
||||
/* Project: AVR-GCC SRAM Memory Evaluation */ |
||||
/* Title: Memory Pointer Functions */ |
||||
/* Purpose: SRAM memory ressource analysis of Atmel programs */ |
||||
/* */ |
||||
/* File: mem_eval.h */
|
||||
/* Revision 0.1 */ |
||||
/* Date: 10/2003 */ |
||||
/* */ |
||||
/* Description: Provides macros for reading the stackpointer and the end of */ |
||||
/* variable data segment */ |
||||
/* Software: AVR-GCC */ |
||||
/* Hardware: AVR AT90 and ATmega series */ |
||||
/* */ |
||||
/* Authors: Wilfried Elmenreich, Wolfgang Haidinger */ |
||||
/* TU Vienna, Real Time Group */ |
||||
/*==========================================================================*/ |
||||
|
||||
#ifndef MEMEVAL |
||||
#define MEMEVAL |
||||
|
||||
#include <avr/io.h> |
||||
|
||||
#ifdef __AVR_AT90S4433__ |
||||
#undef SPH |
||||
#endif |
||||
|
||||
#ifdef SPH |
||||
#define GetStackPtr()({uint8_t *__StackPtr; \ |
||||
asm volatile("in\t%A0,__SP_L__\n\t" \
|
||||
"in\t%B0,__SP_H__" \
|
||||
: "=r" (__StackPtr) \
|
||||
: /*"I" (SPL), "I" (SPH)*/ \
|
||||
); \
|
||||
__StackPtr;}) |
||||
#else |
||||
#define GetStackPtr() ({uint8_t *__StackPtr; \ |
||||
asm volatile("in\t%A0,__SP_L__\n\t" \
|
||||
"clr\t%B0" \
|
||||
: "=r" (__StackPtr) \
|
||||
: /* "I" (SPL) */ ); \
|
||||
__StackPtr;}) |
||||
#endif |
||||
|
||||
#define EndOfUsedMem()({uint8_t *__EndOfUsedMemPtr; \ |
||||
asm volatile("ldi\t%A0,lo8(__bss_end)\n\t" \
|
||||
"ldi\t%B0,hi8(__bss_end)" \
|
||||
: "=r" (__EndOfUsedMemPtr) \
|
||||
); \
|
||||
__EndOfUsedMemPtr;}) |
||||
#endif |
||||
|
||||
|
||||
extern void InitializeMemory(void); |
||||
extern void AnalyzeMemory(uint8_t **largest_begin,uint16_t *largest_size); |
||||
|
||||
@ -0,0 +1,883 @@ |
||||
/* sha1-asm.S */ |
||||
/* |
||||
This file is part of the AVR-Crypto-Lib. |
||||
Copyright (C) 2008 Daniel Otte (daniel.otte@rub.de)
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
/* |
||||
* Author: Daniel Otte |
||||
* |
||||
* License: GPLv3 or later |
||||
*/ |
||||
; SHA1 implementation in assembler for AVR
|
||||
SHA1_BLOCK_BITS = 512 |
||||
SHA1_HASH_BITS = 160 |
||||
|
||||
.macro precall
|
||||
/* push r18 - r27, r30 - r31*/ |
||||
push r0 |
||||
push r1 |
||||
push r18 |
||||
push r19 |
||||
push r20 |
||||
push r21 |
||||
push r22 |
||||
push r23 |
||||
push r24 |
||||
push r25 |
||||
push r26 |
||||
push r27 |
||||
push r30 |
||||
push r31 |
||||
clr r1 |
||||
.endm |
||||
|
||||
.macro postcall
|
||||
pop r31 |
||||
pop r30 |
||||
pop r27 |
||||
pop r26 |
||||
pop r25 |
||||
pop r24 |
||||
pop r23 |
||||
pop r22 |
||||
pop r21 |
||||
pop r20 |
||||
pop r19 |
||||
pop r18 |
||||
pop r1 |
||||
pop r0 |
||||
.endm |
||||
|
||||
|
||||
.macro hexdump length |
||||
push r27 |
||||
push r26 |
||||
ldi r25, '\r' |
||||
mov r24, r25 |
||||
call uart_putc |
||||
ldi r25, '\n' |
||||
mov r24, r25 |
||||
call uart_putc |
||||
pop r26 |
||||
pop r27 |
||||
movw r24, r26 |
||||
.if \length > 16 |
||||
ldi r22, lo8(16) |
||||
ldi r23, hi8(16) |
||||
push r27 |
||||
push r26 |
||||
call uart_hexdump |
||||
pop r26 |
||||
pop r27 |
||||
adiw r26, 16 |
||||
hexdump \length-16 |
||||
.else |
||||
ldi r22, lo8(\length) |
||||
ldi r23, hi8(\length) |
||||
call uart_hexdump |
||||
.endif |
||||
.endm |
||||
|
||||
.macro delay
|
||||
/* |
||||
push r0 |
||||
push r1 |
||||
clr r0 |
||||
1: clr r1 |
||||
2: dec r1 |
||||
brne 2b |
||||
dec r0 |
||||
brne 1b |
||||
pop r1 |
||||
pop r0 // */ |
||||
.endm |
||||
|
||||
/* X points to Block */ |
||||
.macro dbg_hexdump length |
||||
/* |
||||
precall |
||||
hexdump \length |
||||
postcall |
||||
// */ |
||||
.endm |
||||
|
||||
|
||||
|
||||
.section .text |
||||
|
||||
SPL = 0x3D |
||||
SPH = 0x3E |
||||
SREG = 0x3F |
||||
|
||||
|
||||
;
|
||||
;sha1_ctx_t is:
|
||||
;
|
||||
; [h0][h1][h2][h3][h4][length]
|
||||
; hn is 32 bit large, length is 64 bit large
|
||||
|
||||
;###########################################################
|
||||
|
||||
.global sha1_ctx2hash
|
||||
; === sha1_ctx2hash ===
|
||||
; this function converts a state into a normal hash (bytestring)
|
||||
; param1: the 16-bit destination pointer
|
||||
; given in r25,r24 (r25 is most significant)
|
||||
; param2: the 16-bit pointer to sha1_ctx structure
|
||||
; given in r23,r22
|
||||
sha1_ctx2hash: |
||||
movw r26, r22 |
||||
movw r30, r24 |
||||
ldi r21, 5 |
||||
sbiw r26, 4 |
||||
1: |
||||
ldi r20, 4 |
||||
adiw r26, 8 |
||||
2: |
||||
ld r0, -X |
||||
st Z+, r0 |
||||
dec r20 |
||||
brne 2b |
||||
|
||||
dec r21 |
||||
brne 1b |
||||
|
||||
ret |
||||
|
||||
;###########################################################
|
||||
|
||||
.global sha1
|
||||
; === sha1 ===
|
||||
; this function calculates SHA-1 hashes from messages in RAM
|
||||
; param1: the 16-bit hash destination pointer
|
||||
; given in r25,r24 (r25 is most significant)
|
||||
; param2: the 16-bit pointer to message
|
||||
; given in r23,r22
|
||||
; param3: 32-bit length value (length of message in bits)
|
||||
; given in r21,r20,r19,r18
|
||||
sha1: |
||||
sha1_prolog: |
||||
push r8 |
||||
push r9 |
||||
push r10 |
||||
push r11 |
||||
push r12 |
||||
push r13 |
||||
push r16 |
||||
push r17 |
||||
in r30, SPL |
||||
in r31, SPH |
||||
sbiw r30, 5*4+8 |
||||
in r0, SREG |
||||
cli |
||||
out SPL, r30 |
||||
out SREG, r0 |
||||
out SPH, r31 |
||||
|
||||
push r25 |
||||
push r24 |
||||
adiw r30, 1 |
||||
movw r16, r30 |
||||
|
||||
movw r8, r18 /* backup of length*/ |
||||
movw r10, r20 |
||||
|
||||
movw r12, r22 /* backup pf msg-ptr */ |
||||
|
||||
movw r24, r16 |
||||
rcall sha1_init |
||||
/* if length >= 512 */ |
||||
1: |
||||
tst r11 |
||||
brne 2f |
||||
tst r10 |
||||
breq 4f |
||||
2: |
||||
movw r24, r16 |
||||
movw r22, r12 |
||||
rcall sha1_nextBlock |
||||
ldi r19, 64 |
||||
add r12, r19 |
||||
adc r13, r1 |
||||
/* length -= 512 */ |
||||
ldi r19, 0x02 |
||||
sub r9, r19 |
||||
sbc r10, r1 |
||||
sbc r11, r1 |
||||
rjmp 1b |
||||
|
||||
4: |
||||
movw r24, r16 |
||||
movw r22, r12 |
||||
movw r20, r8 |
||||
rcall sha1_lastBlock |
||||
|
||||
pop r24 |
||||
pop r25 |
||||
movw r22, r16 |
||||
rcall sha1_ctx2hash |
||||
|
||||
sha1_epilog: |
||||
in r30, SPL |
||||
in r31, SPH |
||||
adiw r30, 5*4+8 |
||||
in r0, SREG |
||||
cli |
||||
out SPL, r30 |
||||
out SREG, r0 |
||||
out SPH, r31 |
||||
pop r17 |
||||
pop r16 |
||||
pop r13 |
||||
pop r12 |
||||
pop r11 |
||||
pop r10 |
||||
pop r9 |
||||
pop r8 |
||||
ret |
||||
|
||||
;###########################################################
|
||||
|
||||
|
||||
; block MUST NOT be larger than 64 bytes
|
||||
|
||||
.global sha1_lastBlock
|
||||
; === sha1_lastBlock ===
|
||||
; this function does padding & Co. for calculating SHA-1 hashes
|
||||
; param1: the 16-bit pointer to sha1_ctx structure
|
||||
; given in r25,r24 (r25 is most significant)
|
||||
; param2: an 16-bit pointer to 64 byte block to hash
|
||||
; given in r23,r22
|
||||
; param3: an 16-bit integer specifing length of block in bits
|
||||
; given in r21,r20
|
||||
sha1_lastBlock_localSpace = (SHA1_BLOCK_BITS/8+1) |
||||
|
||||
|
||||
sha1_lastBlock: |
||||
cpi r21, 0x02 |
||||
brlo sha1_lastBlock_prolog |
||||
push r25 |
||||
push r24 |
||||
push r23 |
||||
push r22 |
||||
push r21 |
||||
push r20 |
||||
rcall sha1_nextBlock |
||||
pop r20 |
||||
pop r21 |
||||
pop r22 |
||||
pop r23 |
||||
pop r24 |
||||
pop r25 |
||||
subi r21, 2 |
||||
ldi r19, 64 |
||||
sub r22, r19 |
||||
sbc r23, r1 |
||||
rjmp sha1_lastBlock |
||||
sha1_lastBlock_prolog: |
||||
/* allocate space on stack */ |
||||
in r30, SPL |
||||
in r31, SPH |
||||
in r0, SREG |
||||
subi r30, lo8(64) |
||||
sbci r31, hi8(64) /* ??? */ |
||||
cli |
||||
out SPL, r30 |
||||
out SREG, r0 |
||||
out SPH, r31 |
||||
|
||||
adiw r30, 1 /* SP points to next free byte on stack */ |
||||
mov r18, r20 /* r20 = LSB(length) */ |
||||
lsr r18 |
||||
lsr r18 |
||||
lsr r18 |
||||
bst r21, 0 /* may be we should explain this ... */ |
||||
bld r18, 5 /* now: r18 == length/8 (aka. length in bytes) */ |
||||
|
||||
|
||||
movw r26, r22 /* X points to begin of msg */ |
||||
tst r18 |
||||
breq sha1_lastBlock_post_copy |
||||
mov r1, r18 |
||||
sha1_lastBlock_copy_loop: |
||||
ld r0, X+ |
||||
st Z+, r0 |
||||
dec r1 |
||||
brne sha1_lastBlock_copy_loop |
||||
sha1_lastBlock_post_copy: |
||||
sha1_lastBlock_insert_stuffing_bit: |
||||
ldi r19, 0x80 |
||||
mov r0,r19 |
||||
ldi r19, 0x07 |
||||
and r19, r20 /* if we are in bitmode */ |
||||
breq 2f /* no bitmode */ |
||||
1: |
||||
lsr r0 |
||||
dec r19 |
||||
brne 1b |
||||
ld r19, X |
||||
/* maybe we should do some ANDing here, just for safety */ |
||||
or r0, r19 |
||||
2: |
||||
st Z+, r0 |
||||
inc r18 |
||||
|
||||
/* checking stuff here */ |
||||
cpi r18, 64-8+1 |
||||
brsh 0f |
||||
rjmp sha1_lastBlock_insert_zeros |
||||
0: |
||||
/* oh shit, we landed here */ |
||||
/* first we have to fill it up with zeros */ |
||||
ldi r19, 64 |
||||
sub r19, r18 |
||||
breq 2f |
||||
1: |
||||
st Z+, r1 |
||||
dec r19 |
||||
brne 1b |
||||
2: |
||||
sbiw r30, 63 |
||||
sbiw r30, 1 |
||||
movw r22, r30 |
||||
|
||||
push r31 |
||||
push r30 |
||||
push r25 |
||||
push r24 |
||||
push r21 |
||||
push r20 |
||||
rcall sha1_nextBlock |
||||
pop r20 |
||||
pop r21 |
||||
pop r24 |
||||
pop r25 |
||||
pop r30 |
||||
pop r31 |
||||
|
||||
/* now we should subtract 512 from length */ |
||||
movw r26, r24 |
||||
adiw r26, 4*5+1 /* we can skip the lowest byte */ |
||||
ld r19, X |
||||
subi r19, hi8(512) |
||||
st X+, r19 |
||||
ldi r18, 6 |
||||
1: |
||||
ld r19, X |
||||
sbci r19, 0 |
||||
st X+, r19 |
||||
dec r18 |
||||
brne 1b |
||||
|
||||
; clr r18 /* not neccessary ;-) */
|
||||
/* reset Z pointer to begin of block */ |
||||
|
||||
sha1_lastBlock_insert_zeros: |
||||
ldi r19, 64-8 |
||||
sub r19, r18 |
||||
breq sha1_lastBlock_insert_length |
||||
clr r1 |
||||
1: |
||||
st Z+, r1 /* r1 is still zero */ |
||||
dec r19 |
||||
brne 1b |
||||
|
||||
; rjmp sha1_lastBlock_epilog
|
||||
sha1_lastBlock_insert_length: |
||||
movw r26, r24 /* X points to state */ |
||||
adiw r26, 5*4 /* X points to (state.length) */ |
||||
adiw r30, 8 /* Z points one after the last byte of block */ |
||||
ld r0, X+ |
||||
add r0, r20 |
||||
st -Z, r0 |
||||
ld r0, X+ |
||||
adc r0, r21 |
||||
st -Z, r0 |
||||
ldi r19, 6 |
||||
1: |
||||
ld r0, X+ |
||||
adc r0, r1 |
||||
st -Z, r0 |
||||
dec r19 |
||||
brne 1b |
||||
|
||||
sbiw r30, 64-8 |
||||
movw r22, r30 |
||||
rcall sha1_nextBlock |
||||
|
||||
sha1_lastBlock_epilog: |
||||
in r30, SPL |
||||
in r31, SPH |
||||
in r0, SREG |
||||
adiw r30, 63 ; lo8(64)
|
||||
adiw r30, 1 ; hi8(64)
|
||||
cli |
||||
out SPL, r30 |
||||
out SREG, r0 |
||||
out SPH, r31 |
||||
clr r1 |
||||
ret |
||||
|
||||
/**/ |
||||
;###########################################################
|
||||
|
||||
.global sha1_nextBlock
|
||||
; === sha1_nextBlock ===
|
||||
; this is the core function for calculating SHA-1 hashes
|
||||
; param1: the 16-bit pointer to sha1_ctx structure
|
||||
; given in r25,r24 (r25 is most significant)
|
||||
; param2: an 16-bit pointer to 64 byte block to hash
|
||||
; given in r23,r22
|
||||
sha1_nextBlock_localSpace = (16+5+1)*4 ; 16 32-bit values for w array and 5 32-bit values for a array (total 84 byte)
|
||||
|
||||
xtmp = 0 |
||||
xNULL = 1 |
||||
W1 = 10 |
||||
W2 = 11 |
||||
T1 = 12 |
||||
T2 = 13 |
||||
T3 = 14 |
||||
T4 = 15 |
||||
LoopC = 16 |
||||
S = 17 |
||||
tmp1 = 18 |
||||
tmp2 = 19 |
||||
tmp3 = 20 |
||||
tmp4 = 21 |
||||
F1 = 22 |
||||
F2 = 23 |
||||
F3 = 24 |
||||
F4 = 25 |
||||
|
||||
/* byteorder: high number <--> high significance */ |
||||
sha1_nextBlock: |
||||
; initial, let's make some space ready for local vars
|
||||
/* replace push & pop by mem ops? */ |
||||
push r10 |
||||
push r11 |
||||
push r12 |
||||
push r13 |
||||
push r14 |
||||
push r15 |
||||
push r16 |
||||
push r17 |
||||
push r28 |
||||
push r29 |
||||
in r20, SPL |
||||
in r21, SPH |
||||
movw r18, r20 ;backup SP
|
||||
; movw r26, r20 ; X points to free space on stack /* maybe removeable? */
|
||||
movw r30, r22 ; Z points to message
|
||||
subi r20, lo8(sha1_nextBlock_localSpace) ;sbiw can do only up to 63
|
||||
sbci r21, hi8(sha1_nextBlock_localSpace) |
||||
movw r26, r20 ; X points to free space on stack
|
||||
in r0, SREG |
||||
cli ; we want to be uninterrupted while updating SP
|
||||
out SPL, r20 |
||||
out SREG, r0 |
||||
out SPH, r21 |
||||
|
||||
push r18 |
||||
push r19 /* push old SP on new stack */ |
||||
push r24 |
||||
push r25 /* param1 will be needed later */ |
||||
|
||||
/* load a[] with state */ |
||||
movw 28, r24 /* load pointer to state in Y */ |
||||
adiw r26, 1 ; X++
|
||||
|
||||
ldi LoopC, 5*4 |
||||
1: ld tmp1, Y+ |
||||
st X+, tmp1 |
||||
dec LoopC |
||||
brne 1b |
||||
|
||||
movw W1, r26 /* save pointer to w[0] */ |
||||
/* load w[] with endian fixed message */ |
||||
/* we might also use the changeendian32() function at bottom */ |
||||
movw r30, r22 /* mv param2 (ponter to msg) to Z */ |
||||
ldi LoopC, 16 |
||||
1: |
||||
ldd tmp1, Z+3 |
||||
st X+, tmp1 |
||||
ldd tmp1, Z+2 |
||||
st X+, tmp1 |
||||
ldd tmp1, Z+1 |
||||
st X+, tmp1 |
||||
ld tmp1, Z |
||||
st X+, tmp1 |
||||
adiw r30, 4 |
||||
dec LoopC |
||||
brne 1b |
||||
|
||||
;clr LoopC /* LoopC is named t in FIPS 180-2 */
|
||||
clr xtmp |
||||
sha1_nextBlock_mainloop: |
||||
mov S, LoopC |
||||
lsl S |
||||
lsl S |
||||
andi S, 0x3C /* S is a bytepointer so *4 */ |
||||
/* load w[s] */ |
||||
movw r26, W1 |
||||
add r26, S /* X points at w[s] */ |
||||
adc r27, xNULL |
||||
ld T1, X+ |
||||
ld T2, X+ |
||||
ld T3, X+ |
||||
ld T4, X+ |
||||
|
||||
/* |
||||
push r26 |
||||
push r27 |
||||
push T4 |
||||
push T3 |
||||
push T2 |
||||
push T1 |
||||
in r26, SPL |
||||
in r27, SPH |
||||
adiw r26, 1 |
||||
dbg_hexdump 4 |
||||
pop T1 |
||||
pop T2 |
||||
pop T3 |
||||
pop T4 |
||||
pop r27 |
||||
pop r26 |
||||
*/ |
||||
|
||||
cpi LoopC, 16 |
||||
brlt sha1_nextBlock_mainloop_core |
||||
/* update w[s] */ |
||||
ldi tmp1, 2*4 |
||||
rcall 1f |
||||
ldi tmp1, 8*4 |
||||
rcall 1f |
||||
ldi tmp1, 13*4 |
||||
rcall 1f |
||||
rjmp 2f |
||||
1: /* this might be "outsourced" to save the jump above */ |
||||
add tmp1, S |
||||
andi tmp1, 0x3f |
||||
movw r26, W1 |
||||
add r26, tmp1 |
||||
adc r27, xNULL |
||||
ld tmp2, X+ |
||||
eor T1, tmp2 |
||||
ld tmp2, X+ |
||||
eor T2, tmp2 |
||||
ld tmp2, X+ |
||||
eor T3, tmp2 |
||||
ld tmp2, X+ |
||||
eor T4, tmp2 |
||||
ret |
||||
2: /* now we just hav to do a ROTL(T) and save T back */ |
||||
mov tmp2, T4 |
||||
rol tmp2 |
||||
rol T1 |
||||
rol T2 |
||||
rol T3 |
||||
rol T4 |
||||
movw r26, W1 |
||||
add r26, S |
||||
adc r27, xNULL |
||||
st X+, T1 |
||||
st X+, T2 |
||||
st X+, T3 |
||||
st X+, T4 |
||||
|
||||
sha1_nextBlock_mainloop_core: /* ther core function; T=ROTL5(a) ....*/ |
||||
/* T already contains w[s] */ |
||||
movw r26, W1 |
||||
sbiw r26, 4*1 /* X points at a[4] aka e */ |
||||
ld tmp1, X+ |
||||
add T1, tmp1 |
||||
ld tmp1, X+ |
||||
adc T2, tmp1 |
||||
ld tmp1, X+ |
||||
adc T3, tmp1 |
||||
ld tmp1, X+ |
||||
adc T4, tmp1 /* T = w[s]+e */ |
||||
sbiw r26, 4*5 /* X points at a[0] aka a */ |
||||
ld F1, X+ |
||||
ld F2, X+ |
||||
ld F3, X+ |
||||
ld F4, X+ |
||||
mov tmp1, F4 /* X points at a[1] aka b */ |
||||
ldi tmp2, 5 |
||||
1: |
||||
rol tmp1 |
||||
rol F1 |
||||
rol F2 |
||||
rol F3 |
||||
rol F4 |
||||
dec tmp2 |
||||
brne 1b |
||||
|
||||
add T1, F1 |
||||
adc T2, F2 |
||||
adc T3, F3 |
||||
adc T4, F4 /* T = ROTL(a,5) + e + w[s] */ |
||||
|
||||
/* now we have to do this fucking conditional stuff */ |
||||
ldi r30, lo8(sha1_nextBlock_xTable) |
||||
ldi r31, hi8(sha1_nextBlock_xTable) |
||||
add r30, xtmp |
||||
adc r31, xNULL |
||||
lpm tmp1, Z |
||||
cp tmp1, LoopC |
||||
brne 1f |
||||
inc xtmp |
||||
1: ldi r30, lo8(sha1_nextBlock_KTable) |
||||
ldi r31, hi8(sha1_nextBlock_KTable) |
||||
lsl xtmp |
||||
lsl xtmp |
||||
add r30, xtmp |
||||
adc r31, xNULL |
||||
lsr xtmp |
||||
lsr xtmp |
||||
|
||||
lpm tmp1, Z+ |
||||
add T1, tmp1 |
||||
lpm tmp1, Z+ |
||||
adc T2, tmp1 |
||||
lpm tmp1, Z+ |
||||
adc T3, tmp1 |
||||
lpm tmp1, Z+ |
||||
adc T4, tmp1 |
||||
/* T = ROTL(a,5) + e + kt + w[s] */ |
||||
|
||||
/* Z-4 is just pointing to kt ... */ |
||||
movw r28, r26 /* copy X in Y */ |
||||
adiw r30, 3*4 /* now Z points to the rigth locatin in our jump-vector-table */ |
||||
lsr r31 |
||||
ror r30 |
||||
|
||||
icall |
||||
mov F1, tmp1 |
||||
icall |
||||
mov F2, tmp1 |
||||
icall |
||||
mov F3, tmp1 |
||||
icall |
||||
|
||||
add T1, F1 |
||||
adc T2, F2 |
||||
adc T3, F3 |
||||
adc T4, tmp1 /* T = ROTL5(a) + f_t(b,c,d) + e + k_t + w[s] */ |
||||
/* X points still at a[1] aka b, Y points at a[2] aka c */ |
||||
/* update a[] */ |
||||
sha1_nextBlock_update_a: |
||||
/*first we move all vars in a[] "one up" e=d, d=c, c=b, b=a*/ |
||||
//adiw r28, 3*4 /* Y should point at a[4] aka e */ |
||||
movw r28, W1 |
||||
sbiw r28, 4 |
||||
|
||||
ldi tmp2, 4*4 |
||||
1: |
||||
ld tmp1, -Y |
||||
std Y+4, tmp1 |
||||
dec tmp2 |
||||
brne 1b |
||||
/* Y points at a[0] aka a*/ |
||||
|
||||
movw r28, W1 |
||||
sbiw r28, 5*4 |
||||
/* store T in a[0] aka a */ |
||||
st Y+, T1 |
||||
st Y+, T2 |
||||
st Y+, T3 |
||||
st Y+, T4 |
||||
/* Y points at a[1] aka b*/ |
||||
|
||||
/* rotate c */ |
||||
ldd T1, Y+1*4 |
||||
ldd T2, Y+1*4+1 |
||||
ldd T3, Y+1*4+2 |
||||
ldd T4, Y+1*4+3 |
||||
mov tmp1, T1 |
||||
ldi tmp2, 2 |
||||
1: ror tmp1 |
||||
ror T4 |
||||
ror T3 |
||||
ror T2 |
||||
ror T1 |
||||
dec tmp2 |
||||
brne 1b |
||||
std Y+1*4+0, T1 |
||||
std Y+1*4+1, T2 |
||||
std Y+1*4+2, T3 |
||||
std Y+1*4+3, T4 |
||||
/* |
||||
push r27 |
||||
push r26 |
||||
movw r26, W1 |
||||
sbiw r26, 4*5 |
||||
dbg_hexdump 4*5 |
||||
pop r26 |
||||
pop r27 |
||||
*/ |
||||
inc LoopC |
||||
cpi LoopC, 80 |
||||
brge 1f |
||||
rjmp sha1_nextBlock_mainloop |
||||
/**************************************/ |
||||
1: |
||||
/* littel patch */ |
||||
sbiw r28, 4 |
||||
|
||||
/* add a[] to state and inc length */ |
||||
pop r27 |
||||
pop r26 /* now X points to state (and Y still at a[0]) */ |
||||
ldi tmp4, 5 |
||||
1: clc |
||||
ldi tmp3, 4 |
||||
2: ld tmp1, X |
||||
ld tmp2, Y+ |
||||
adc tmp1, tmp2 |
||||
st X+, tmp1 |
||||
dec tmp3 |
||||
brne 2b |
||||
dec tmp4 |
||||
brne 1b |
||||
|
||||
/* now length += 512 */ |
||||
adiw r26, 1 /* we skip the least significant byte */ |
||||
ld tmp1, X |
||||
ldi tmp2, hi8(512) /* 2 */ |
||||
add tmp1, tmp2 |
||||
st X+, tmp1 |
||||
ldi tmp2, 6 |
||||
1: |
||||
ld tmp1, X |
||||
adc tmp1, xNULL |
||||
st X+, tmp1 |
||||
dec tmp2 |
||||
brne 1b |
||||
|
||||
; EPILOG
|
||||
sha1_nextBlock_epilog: |
||||
/* now we should clean up the stack */ |
||||
pop r21 |
||||
pop r20 |
||||
in r0, SREG |
||||
cli ; we want to be uninterrupted while updating SP
|
||||
out SPL, r20 |
||||
out SREG, r0 |
||||
out SPH, r21 |
||||
|
||||
clr r1 |
||||
pop r29 |
||||
pop r28 |
||||
pop r17 |
||||
pop r16 |
||||
pop r15 |
||||
pop r14 |
||||
pop r13 |
||||
pop r12 |
||||
pop r11 |
||||
pop r10 |
||||
ret |
||||
|
||||
sha1_nextBlock_xTable: |
||||
.byte 20,40,60,0 |
||||
sha1_nextBlock_KTable: |
||||
.int 0x5a827999
|
||||
.int 0x6ed9eba1
|
||||
.int 0x8f1bbcdc
|
||||
.int 0xca62c1d6
|
||||
sha1_nextBlock_JumpTable: |
||||
rjmp sha1_nextBlock_Ch |
||||
nop |
||||
rjmp sha1_nextBlock_Parity |
||||
nop |
||||
rjmp sha1_nextBlock_Maj |
||||
nop |
||||
rjmp sha1_nextBlock_Parity |
||||
|
||||
/* X and Y still point at a[1] aka b ; return value in tmp1 */ |
||||
sha1_nextBlock_Ch: |
||||
ld tmp1, Y+ |
||||
mov tmp2, tmp1 |
||||
com tmp2 |
||||
ldd tmp3, Y+3 /* load from c */ |
||||
and tmp1, tmp3 |
||||
ldd tmp3, Y+7 /* load from d */ |
||||
and tmp2, tmp3 |
||||
eor tmp1, tmp2 |
||||
ret |
||||
|
||||
sha1_nextBlock_Maj: |
||||
ld tmp1, Y+ |
||||
mov tmp2, tmp1 |
||||
ldd tmp3, Y+3 /* load from c */ |
||||
and tmp1, tmp3 |
||||
ldd tmp4, Y+7 /* load from d */ |
||||
and tmp2, tmp4 |
||||
eor tmp1, tmp2 |
||||
and tmp3, tmp4 |
||||
eor tmp1, tmp3 |
||||
ret |
||||
|
||||
sha1_nextBlock_Parity: |
||||
ld tmp1, Y+ |
||||
ldd tmp2, Y+3 /* load from c */ |
||||
eor tmp1, tmp2 |
||||
ldd tmp2, Y+7 /* load from d */ |
||||
eor tmp1, tmp2 |
||||
ret |
||||
/* |
||||
ch_str: .asciz "\r\nCh" |
||||
maj_str: .asciz "\r\nMaj" |
||||
parity_str: .asciz "\r\nParity" |
||||
*/ |
||||
;###########################################################
|
||||
|
||||
.global sha1_init
|
||||
;void sha1_init(sha1_ctx_t *state){
|
||||
; DEBUG_S("\r\nSHA1_INIT");
|
||||
; state->h[0] = 0x67452301;
|
||||
; state->h[1] = 0xefcdab89;
|
||||
; state->h[2] = 0x98badcfe;
|
||||
; state->h[3] = 0x10325476;
|
||||
; state->h[4] = 0xc3d2e1f0;
|
||||
; state->length = 0;
|
||||
;}
|
||||
; param1: (Func3,r24) 16-bit pointer to sha1_ctx_t struct in ram
|
||||
; modifys: Z(r30,r31), Func1, r22
|
||||
sha1_init: |
||||
movw r26, r24 ; (24,25) --> (26,27) load X with param1
|
||||
ldi r30, lo8((sha1_init_vector)) |
||||
ldi r31, hi8((sha1_init_vector)) |
||||
ldi r22, 5*4 /* bytes to copy */ |
||||
sha1_init_vloop: |
||||
lpm r23, Z+ |
||||
st X+, r23 |
||||
dec r22 |
||||
brne sha1_init_vloop |
||||
ldi r22, 8 |
||||
sha1_init_lloop: |
||||
st X+, r1 |
||||
dec r22 |
||||
brne sha1_init_lloop |
||||
ret |
||||
|
||||
sha1_init_vector: |
||||
.int 0x67452301;
|
||||
.int 0xefcdab89;
|
||||
.int 0x98badcfe;
|
||||
.int 0x10325476;
|
||||
.int 0xc3d2e1f0;
|
||||
|
||||
@ -0,0 +1,119 @@ |
||||
/* sha1.h */ |
||||
/*
|
||||
This file is part of the AVR-Crypto-Lib. |
||||
Copyright (C) 2008 Daniel Otte (daniel.otte@rub.de) |
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
/**
|
||||
* \file sha1.h |
||||
* \author Daniel Otte |
||||
* \email daniel.otte@rub.de |
||||
* \date 2006-10-08 |
||||
* \license GPLv3 or later |
||||
* \brief SHA-1 declaration. |
||||
* \ingroup SHA-1 |
||||
*
|
||||
*/ |
||||
|
||||
#ifndef SHA1_H_ |
||||
#define SHA1_H_ |
||||
|
||||
#include <stdint.h> |
||||
/** \def SHA1_HASH_BITS
|
||||
* definees the size of a SHA-1 hash in bits
|
||||
*/ |
||||
|
||||
/** \def SHA1_HASH_BYTES
|
||||
* definees the size of a SHA-1 hash in bytes
|
||||
*/ |
||||
|
||||
/** \def SHA1_BLOCK_BITS
|
||||
* definees the size of a SHA-1 input block in bits
|
||||
*/ |
||||
|
||||
/** \def SHA1_BLOCK_BYTES
|
||||
* definees the size of a SHA-1 input block in bytes
|
||||
*/ |
||||
#define SHA1_HASH_BITS 160 |
||||
#define SHA1_HASH_BYTES (SHA1_HASH_BITS/8) |
||||
#define SHA1_BLOCK_BITS 512 |
||||
#define SHA1_BLOCK_BYTES (SHA1_BLOCK_BITS/8) |
||||
|
||||
/** \typedef sha1_ctx_t
|
||||
* \brief SHA-1 context type |
||||
*
|
||||
* A vatiable of this type may hold the state of a SHA-1 hashing process |
||||
*/ |
||||
typedef struct { |
||||
uint32_t h[5]; |
||||
uint64_t length; |
||||
} sha1_ctx_t; |
||||
|
||||
/** \typedef sha1_hash_t
|
||||
* \brief hash value type |
||||
* A variable of this type may hold a SHA-1 hash value
|
||||
*/ |
||||
/*
|
||||
typedef uint8_t sha1_hash_t[SHA1_HASH_BITS/8]; |
||||
*/ |
||||
|
||||
/** \fn sha1_init(sha1_ctx_t *state)
|
||||
* \brief initializes a SHA-1 context |
||||
* This function sets a ::sha1_ctx_t variable to the initialization vector |
||||
* for SHA-1 hashing. |
||||
* \param state pointer to the SHA-1 context variable |
||||
*/ |
||||
void sha1_init(sha1_ctx_t *state); |
||||
|
||||
/** \fn sha1_nextBlock(sha1_ctx_t *state, const void* block)
|
||||
* \brief process one input block |
||||
* This function processes one input block and updates the hash context
|
||||
* accordingly |
||||
* \param state pointer to the state variable to update |
||||
* \param block pointer to the message block to process |
||||
*/ |
||||
void sha1_nextBlock (sha1_ctx_t *state, const void* block); |
||||
|
||||
/** \fn sha1_lastBlock(sha1_ctx_t *state, const void* block, uint16_t length_b)
|
||||
* \brief processes the given block and finalizes the context |
||||
* This function processes the last block in a SHA-1 hashing process. |
||||
* The block should have a maximum length of a single input block. |
||||
* \param state pointer to the state variable to update and finalize |
||||
* \param block pointer to themessage block to process |
||||
* \param length_b length of the message block in bits
|
||||
*/ |
||||
void sha1_lastBlock (sha1_ctx_t *state, const void* block, uint16_t length_b); |
||||
|
||||
/** \fn sha1_ctx2hash(sha1_hash_t *dest, sha1_ctx_t *state)
|
||||
* \brief convert a state variable into an actual hash value |
||||
* Writes the hash value corresponding to the state to the memory pointed by dest. |
||||
* \param dest pointer to the hash value destination |
||||
* \param state pointer to the hash context |
||||
*/
|
||||
void sha1_ctx2hash (void *dest, sha1_ctx_t *state); |
||||
|
||||
/** \fn sha1(sha1_hash_t *dest, const void* msg, uint32_t length_b)
|
||||
* \brief hashing a message which in located entirely in RAM |
||||
* This function automatically hashes a message which is entirely in RAM with |
||||
* the SHA-1 hashing algorithm. |
||||
* \param dest pointer to the hash value destination |
||||
* \param msg pointer to the message which should be hashed |
||||
* \param length_b length of the message in bits |
||||
*/
|
||||
void sha1(void *dest, const void* msg, uint32_t length_b); |
||||
|
||||
|
||||
|
||||
#endif /*SHA1_H_*/ |
||||
@ -0,0 +1,35 @@ |
||||
|
||||
#include "trunc.h" |
||||
|
||||
int32_t get_otp_from_cnt(uint64_t cnt, uint8_t digits, uint8_t* secret, uint8_t sec_len){ |
||||
uint8_t tmp[8]={((uint8_t*)&cnt)[7],((uint8_t*)&cnt)[6],((uint8_t*)&cnt)[5],((uint8_t*)&cnt)[4],((uint8_t*)&cnt)[3],((uint8_t*)&cnt)[2],((uint8_t*)&cnt)[1],((uint8_t*)&cnt)[0]}; |
||||
/*Step 1 generate HMAC-SHA1*/ |
||||
uint8_t hmac_result[20]; |
||||
|
||||
hmac_sha1(hmac_result, secret, sec_len*8, tmp, 64); |
||||
return get_otp(hmac_result,digits); |
||||
} |
||||
|
||||
int32_t get_otp(uint8_t* hmac, uint8_t digits){ |
||||
uint8_t n; |
||||
uint32_t dt; |
||||
|
||||
/*Step 1 generate HMAC-SHA1*/ |
||||
/*Step 2 get DT(HMAC) */ |
||||
/* get the lower 4-bits and treat them as a number n*/ |
||||
/* DT is the 31bit integer at HMAC[n]+HMAC[n+1]+HMAC[n+2]+HMAC[n+3]*/ |
||||
/* the MSB is ignored */ |
||||
n=hmac[SHA1_SIZE-1] & 0x0F; |
||||
dt= (uint32_t)(hmac[n ]&0x7F)<<(uint32_t)24 | |
||||
(uint32_t)(hmac[n+1])<<(uint32_t)16 |
|
||||
(uint32_t)(hmac[n+2])<<(uint32_t)8 |
|
||||
(uint32_t)(hmac[n+3]); |
||||
/*Step 3 return dt mod 10^digits*/ |
||||
switch(digits){ |
||||
case 6: return dt % 1000000; |
||||
case 7: return dt % 10000000; |
||||
default: case 8: return dt % 100000000; |
||||
} |
||||
} |
||||
|
||||
|
||||
@ -0,0 +1,10 @@ |
||||
#ifndef _TRUNC_H_ |
||||
#define _TRUNC_H_ |
||||
|
||||
#include "hmac-sha1/hmac-sha1.h" |
||||
#define SHA1_SIZE 20 |
||||
|
||||
int32_t get_otp(uint8_t* hmac, uint8_t digits); |
||||
int32_t get_otp_from_cnt(uint64_t cnt, uint8_t digits, uint8_t* secret, uint8_t sec_len); |
||||
|
||||
#endif |
||||
@ -0,0 +1,67 @@ |
||||
#ifdef DEBUG |
||||
#include "usart.h" |
||||
|
||||
void initUSART(){ |
||||
UBRR0 = (unsigned char) MYUBRR; |
||||
|
||||
/*enable USART write and read*/ |
||||
UCSR0B |= /*(1<<RXCIE)|*/(1<<RXEN0)|(1<<TXEN0); |
||||
/*odd parity, 1 stop bit, 8bit words*/ |
||||
UCSR0C |= (1<<UPM01)|(1<<UPM00)|(1<<UCSZ01)|(1<<UCSZ00); |
||||
} |
||||
|
||||
void tx( char data ){ |
||||
while ( !( UCSR0A & (1<<UDRE0)) ); |
||||
UDR0 = data; |
||||
} |
||||
|
||||
void sendstr( char* str){ |
||||
while(str[0]!=0){ |
||||
tx(str[0]); |
||||
str++; |
||||
} |
||||
} |
||||
|
||||
void sendunum(uint32_t num){ |
||||
char text[11] = {0,0,0,0,0,0,0,0,0,0,0}; |
||||
uint32_t tmp=1; |
||||
char leadingzero=0; |
||||
char digit=0; |
||||
char i; |
||||
if(num==0){ |
||||
tx('0'); |
||||
return; |
||||
} |
||||
for(i=1;i<=10;i++){ |
||||
digit = '0'+((num/tmp)%10); |
||||
tmp*=10; |
||||
if(digit=='0'){ |
||||
leadingzero++;
|
||||
}else{ |
||||
leadingzero=0; |
||||
} |
||||
text[10-i]=digit; |
||||
} |
||||
sendstr( text+leadingzero ); |
||||
} |
||||
|
||||
void tx_hex(uint8_t c){ |
||||
if( ((c&0xF0)>>4) <0x0A ){ |
||||
tx('0'+((c&0xF0)>>4) ); |
||||
}else{ |
||||
tx('A'-0x0A+((c&0xF0)>>4)); |
||||
} |
||||
|
||||
if( (c&0x0F)<0x0A ){ |
||||
tx('0'+(c&0x0F)); |
||||
}else{ |
||||
tx('A'-0x0A+(c&0x0F)); |
||||
} |
||||
} |
||||
|
||||
char rx( void ){ |
||||
while ( !(UCSR0A & (1<<RXC0)) ); |
||||
return UDR0; |
||||
} |
||||
#endif //DEBUG
|
||||
|
||||
@ -0,0 +1,21 @@ |
||||
|
||||
#ifndef _USART_H |
||||
#define _USART_H |
||||
|
||||
#include <avr/io.h> |
||||
|
||||
#ifdef DEBUG |
||||
#define MYUBRR 8 /*BAUD=115200*/ |
||||
|
||||
void initUSART(); |
||||
void tx( char data ); |
||||
void sendstr( char* str); |
||||
void sendunum(uint32_t num); |
||||
void tx_hex(uint8_t c); |
||||
char rx( void ); |
||||
|
||||
#endif //DEBUG
|
||||
|
||||
|
||||
#endif //_USART_H
|
||||
|
||||
@ -0,0 +1,58 @@ |
||||
|
||||
#include <avr/io.h> |
||||
#include <avr/pgmspace.h> |
||||
#include "usbdrv.h" |
||||
|
||||
extern void buildReport(uint8_t mod,uint8_t key); |
||||
extern uint8_t reportBuffer[2]; |
||||
extern uint8_t idleRate; |
||||
|
||||
uint8_t usbFunctionSetup(uint8_t data[8]){ |
||||
usbRequest_t *rq = (void *)data; |
||||
usbMsgPtr = reportBuffer; |
||||
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */ |
||||
if(rq->bRequest == USBRQ_HID_GET_REPORT){ /* wValue: ReportType (highbyte), ReportID (lowbyte) */ |
||||
/* we only have one report type, so don't look at wValue */ |
||||
buildReport(0x00,0x00); |
||||
return sizeof(reportBuffer); |
||||
}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; |
||||
} |
||||
|
||||
/* We use a simplifed keyboard report descriptor which does not support the
|
||||
* boot protocol. We don't allow setting status LEDs and we only allow one |
||||
* simultaneous key press (except modifiers). We can therefore use short |
||||
* 2 byte input reports. |
||||
* The report descriptor has been created with usb.org's "HID Descriptor Tool" |
||||
* which can be downloaded from http://www.usb.org/developers/hidpage/.
|
||||
* Redundant entries (such as LOGICAL_MINIMUM and USAGE_PAGE) have been omitted |
||||
* for the second INPUT item. |
||||
*/ |
||||
PROGMEM const char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { /* USB report descriptor */ |
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x06, // USAGE (Keyboard)
|
||||
0xa1, 0x01, // COLLECTION (Application)
|
||||
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, 0x01, // REPORT_COUNT (1)
|
||||
0x75, 0x08, // REPORT_SIZE (8)
|
||||
0x25, 0x65, // LOGICAL_MAXIMUM (101)
|
||||
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
|
||||
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
|
||||
0x81, 0x00, // INPUT (Data,Ary,Abs)
|
||||
0xc0 // END_COLLECTION
|
||||
}; |
||||
@ -1,18 +1,16 @@ |
||||
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: |
||||
OBJECTIVE DEVELOPMENT GmbH's AVR-USB driver software is distributed under the |
||||
terms and conditions of the GNU GPL version 2, see the text 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. |
||||
Use the form at http://www.obdev.at/avrusb/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/ |
||||
- a reference to http://www.obdev.at/avrusb/ |
||||
|
||||
(3) If you improve the driver firmware itself, please give us a free license |
||||
to your modifications for our commercial license offerings. |
||||
@ -0,0 +1,154 @@ |
||||
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/avrusb/ |
||||
|
||||
This directory contains the USB firmware only. Copy it as-is to your own |
||||
project and add your own version of "usbconfig.h". A template for your own |
||||
"usbconfig.h" can be found in "usbconfig-prototype.h" in this directory. |
||||
|
||||
|
||||
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. |
||||
iarcompat.h ............ Compatibility definitions for IAR C-compiler. |
||||
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. |
||||
USBID-License.txt ...... Terms and conditions for using particular USB ID |
||||
values for particular purposes. |
||||
|
||||
(*) ... These files should be linked to your project. |
||||
|
||||
|
||||
CPU CORE CLOCK FREQUENCY |
||||
======================== |
||||
We supply assembler modules for clock frequencies of 12 MHz, 15 MHz, 16 MHz and |
||||
16.5 MHz. Other clock rates are not supported. The actual clock rate must be |
||||
configured in usbdrv.h unless you use the default 12 MHz. |
||||
|
||||
12 MHz Clock |
||||
This is the traditional clock rate of AVR-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. |
||||
|
||||
16.5 MHz Clock |
||||
The assembler module for this clock rate differs from the other modules because |
||||
it has 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 be |
||||
used since the 8 MHz RC oscillator cannot be trimmed up to 16.5 MHz. This |
||||
includes the very popular ATTiny25, ATTiny45, ATTiny85 series as well as the |
||||
ATTiny26. |
||||
|
||||
We recommend that you obtain appropriate calibration values for 16.5 MHz core |
||||
clock at programming time and store it in flash or EEPROM or compute the value |
||||
from a reference clock at run time. Atmel's 8 MHz calibration is much more |
||||
precise than the guaranteed 10% and it's therefore often possible to work with |
||||
a fixed offset from this value, but it may be out of range. |
||||
|
||||
|
||||
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 a single VID/PID pair for free. If you want to use |
||||
your own VID and PID instead of our's, define the macros "USB_CFG_VENDOR_ID" |
||||
and "USB_CFG_DEVICE_ID" accordingly in "usbconfig.h". |
||||
|
||||
To use our predefined VID/PID pair, you MUST conform to a couple of |
||||
requirements. See the file "USBID-License.txt" for details. |
||||
|
||||
Objective Development also has some offerings which include product IDs. See |
||||
http://www.obdev.at/avrusb/ for details. |
||||
|
||||
|
||||
HOST DRIVER |
||||
=========== |
||||
You have received this driver together with an example device implementation |
||||
and an example host driver. The host driver is based on libusb and compiles |
||||
on various Unix flavors (Linux, BSD, Mac OS X). It also compiles natively on |
||||
Windows using MinGW (see www.mingw.org) and libusb-win32 (see |
||||
libusb-win32.sourceforge.net). The "Automator" project contains a native |
||||
Windows host driver (not based on libusb) for Human Interface Devices. |
||||
|
||||
|
||||
DEVELOPMENT SYSTEM |
||||
================== |
||||
This driver has been developed and optimized for the GNU compiler version 3 |
||||
(gcc 3). It does work well with gcc 4, but with bigger code size. We recommend |
||||
that you use the GNU compiler suite because it is freely available. AVR-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. |
||||
|
||||
|
||||
USING AVR-USB FOR FREE |
||||
====================== |
||||
The AVR firmware driver is published under the GNU General Public License |
||||
Version 2 (GPL2). See the file "License.txt" for details. |
||||
|
||||
If you decide for the free GPL2, we STRONGLY ENCOURAGE you to do the following |
||||
things IN ADDITION to the obligations from the GPL2: |
||||
|
||||
(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/avrusb/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/avrusb/ |
||||
|
||||
(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 AVR-USB |
||||
=============================== |
||||
If you don't want to publish your source code under the terms of the GPL2, |
||||
you can simply pay money for AVR-USB. As an additional benefit you get |
||||
USB PIDs for free, licensed exclusively to you. See the file |
||||
"CommercialLicense.txt" for details. |
||||
|
||||
@ -0,0 +1,143 @@ |
||||
Royalty-Free Non-Exclusive License USB Product-ID |
||||
================================================= |
||||
|
||||
Version 2006-06-19 |
||||
|
||||
OBJECTIVE DEVELOPMENT Software GmbH hereby grants you the non-exclusive |
||||
right to use three USB.org vendor-ID (VID) / product-ID (PID) pairs with |
||||
products based on Objective Development's firmware-only USB driver for |
||||
Atmel AVR microcontrollers: |
||||
|
||||
* VID = 5824 (=0x16c0) / PID = 1500 (=0x5dc) for devices implementing no |
||||
USB device class (vendor-class devices with USB class = 0xff). Devices |
||||
using this pair will be referred to as "VENDOR CLASS" devices. |
||||
|
||||
* VID = 5824 (=0x16c0) / PID = 1503 (=0x5df) for HID class devices |
||||
(excluding mice and keyboards). Devices using this pair will be referred |
||||
to as "HID CLASS" devices. |
||||
|
||||
* VID = 5824 (=0x16c0) / PID = 1505 (=0x5e1) for CDC class modem devices |
||||
Devices using this pair will be referred to as "CDC-ACM CLASS" devices. |
||||
|
||||
Since the granted right is non-exclusive, the same VID/PID pairs may be |
||||
used by many companies and individuals for different products. To avoid |
||||
conflicts, your device and host driver software MUST adhere to the rules |
||||
outlined below. |
||||
|
||||
OBJECTIVE DEVELOPMENT Software GmbH has licensed these VID/PID pairs from |
||||
Wouter van Ooijen (see www.voti.nl), who has licensed the VID from the USB |
||||
Implementers Forum, Inc. (see www.usb.org). The VID is registered for the |
||||
company name "Van Ooijen Technische Informatica". |
||||
|
||||
|
||||
RULES AND RESTRICTIONS |
||||
====================== |
||||
|
||||
(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/avrusb/". |
||||
|
||||
(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) Matching of device-specific drivers MUST be based on the textual |
||||
manufacturer and product identification in addition to the usual VID/PID |
||||
matching. This means that operating system features which are based on |
||||
VID/PID matching only (e.g. Windows kernel level drivers, automatic actions |
||||
when the device is plugged in etc) MUST NOT be used. The driver matching |
||||
MUST be a comparison of the entire strings, NOT a sub-string match. For |
||||
CDC-ACM CLASS devices, a generic class driver should be used and the |
||||
matching is based on the USB device class. |
||||
|
||||
(6) The extent to which VID/PID matching is allowed for non device-specific |
||||
drivers or features depends on the operating system and particular VID/PID |
||||
pair used: |
||||
|
||||
* Mac OS X, Linux, FreeBSD and other Unixes: No VID/PID matching is |
||||
required and hence no VID/PID-only matching is allowed at all. |
||||
|
||||
* Windows: The operating system performs VID/PID matching for the kernel |
||||
level driver. You are REQUIRED to use libusb-win32 (see |
||||
http://libusb-win32.sourceforge.net/) as the kernel level driver for |
||||
VENDOR CLASS devices. HID CLASS devices all use the generic HID class |
||||
driver shipped with Windows, except mice and keyboards. You therefore |
||||
MUST NOT use any of the shared VID/PID pairs for mice or keyboards. |
||||
CDC-ACM CLASS devices require a ".inf" file which matches on the VID/PID |
||||
pair. This ".inf" file MUST load the "usbser" driver to configure the |
||||
device as modem (COM-port). |
||||
|
||||
(7) OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any |
||||
problems which are caused by the shared use of these VID/PID pairs. You |
||||
have been warned that the sharing of VID/PID pairs may cause problems. If |
||||
you want to avoid them, get your own VID/PID pair for exclusive use. |
||||
|
||||
|
||||
HOW TO IMPLEMENT THESE RULES |
||||
============================ |
||||
|
||||
The following rules are for VENDOR CLASS and HID CLASS devices. CDC-ACM |
||||
CLASS devices use the operating system's class driver and don't need a |
||||
custom driver. |
||||
|
||||
The host driver MUST iterate over all devices with the given VID/PID |
||||
numbers in their device descriptors and query the string representation for |
||||
the manufacturer name in USB language 0x0409 (English/US). It MUST compare |
||||
the ENTIRE string with your textual manufacturer identification chosen in |
||||
(2) above. A substring search for your domain or e-mail address is NOT |
||||
acceptable. The driver MUST NOT touch the device (other than querying the |
||||
descriptors) unless the strings match. |
||||
|
||||
For all USB devices with matching VID/PID and textual manufacturer |
||||
identification, the host driver must query the textual product |
||||
identification and string-compare it with the name of the product it can |
||||
control. It may only initialize the device if the product matches exactly. |
||||
|
||||
Objective Development provides examples for these matching rules with the |
||||
"PowerSwitch" project (using libusb) and with the "Automator" project |
||||
(using Windows calls on Windows and libusb on Unix). |
||||
|
||||
|
||||
Technical Notes: |
||||
================ |
||||
|
||||
Sharing the same VID/PID pair among devices is possible as long as ALL |
||||
drivers which match the VID/PID also perform matching on the textual |
||||
identification strings. This is easy on all operating systems except |
||||
Windows, since Windows establishes a static connection between the VID/PID |
||||
pair and a kernel level driver. All devices with the same VID/PID pair must |
||||
therefore use THE SAME kernel level driver. |
||||
|
||||
We therefore demand that you use libusb-win32 for VENDOR CLASS devices. |
||||
This is a generic kernel level driver which allows all types of USB access |
||||
for user space applications. This is only a partial solution of the |
||||
problem, though, because different device drivers may come with different |
||||
versions of libusb-win32 and they may not work with the libusb version of |
||||
the respective other driver. You are therefore encouraged to test your |
||||
driver against a broad range of libusb-win32 versions. Do not use new |
||||
features in new versions, or check for their existence before you use them. |
||||
When a new libusb-win32 becomes available, make sure that your driver is |
||||
compatible with it. |
||||
|
||||
For HID CLASS devices it is necessary that all those devices bind to the |
||||
same kernel driver: Microsoft's generic USB HID driver. This is true for |
||||
all HID devices except those with a specialized driver. Currently, the only |
||||
HIDs with specialized drivers are mice and keyboards. You therefore MUST |
||||
NOT use a shared VID/PID with mouse and keyboard devices. |
||||
|
||||
Sharing the same VID/PID among different products is unusual and probably |
||||
violates the USB specification. If you do it, you do it at your own risk. |
||||
|
||||
To avoid possible incompatibilities, we highly recommend that you get your |
||||
own VID/PID pair if you intend to sell your product. Objective |
||||
Development's commercial licenses for AVR-USB include a PID for |
||||
unrestricted exclusive use. |
||||
@ -0,0 +1,65 @@ |
||||
/* Name: iarcompat.h
|
||||
* Project: AVR USB driver |
||||
* 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) or proprietary (CommercialLicense.txt) |
||||
* This Revision: $Id: iarcompat.h 533 2008-02-28 15:35:25Z cs $ |
||||
*/ |
||||
|
||||
/*
|
||||
General Description: |
||||
This header is included when we compile with the IAR C-compiler and assembler. |
||||
It defines macros for cross compatibility between gcc and IAR-cc. |
||||
|
||||
Thanks to Oleg Semyonov for his help with the IAR tools port! |
||||
*/ |
||||
|
||||
#ifndef __iarcompat_h_INCLUDED__ |
||||
#define __iarcompat_h_INCLUDED__ |
||||
|
||||
#if defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ |
||||
|
||||
/* Enable bit definitions */ |
||||
#ifndef ENABLE_BIT_DEFINITIONS |
||||
# define ENABLE_BIT_DEFINITIONS 1 |
||||
#endif |
||||
|
||||
/* Include IAR headers */ |
||||
#include <ioavr.h> |
||||
#ifndef __IAR_SYSTEMS_ASM__ |
||||
# include <inavr.h> |
||||
#endif |
||||
|
||||
#define __attribute__(arg) |
||||
|
||||
#ifdef __IAR_SYSTEMS_ASM__ |
||||
# define __ASSEMBLER__ |
||||
#endif |
||||
|
||||
#ifdef __HAS_ELPM__ |
||||
# define PROGMEM __farflash |
||||
#else |
||||
# define PROGMEM __flash |
||||
#endif |
||||
|
||||
#define PRG_RDB(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() |
||||
|
||||
/* 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. |
||||
*/ |
||||
|
||||
#endif /* defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ */ |
||||
#endif /* __iarcompat_h_INCLUDED__ */ |
||||
@ -0,0 +1,578 @@ |
||||
/* Name: usbdrv.c
|
||||
* Project: AVR USB driver |
||||
* 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) or proprietary (CommercialLicense.txt) |
||||
* This Revision: $Id: usbdrv.c 530 2008-02-28 15:34:04Z cs $ |
||||
*/ |
||||
|
||||
#include "iarcompat.h" |
||||
#ifndef __IAR_SYSTEMS_ICC__ |
||||
# include <avr/io.h> |
||||
# include <avr/pgmspace.h> |
||||
#endif |
||||
#include "usbdrv.h" |
||||
#include "oddebug.h" |
||||
|
||||
/*
|
||||
General Description: |
||||
This module implements the C-part of the USB driver. See usbdrv.h for a |
||||
documentation of the entire driver. |
||||
*/ |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
/* raw USB registers / interface to assembler code: */ |
||||
uchar usbRxBuf[2*USB_BUFSIZE]; /* raw RX buffer: PID, 8 bytes data, 2 bytes CRC */ |
||||
uchar usbInputBufOffset; /* offset in usbRxBuf used for low level receiving */ |
||||
uchar usbDeviceAddr; /* assigned during enumeration, defaults to 0 */ |
||||
uchar usbNewDeviceAddr; /* device ID which should be set after status phase */ |
||||
uchar usbConfiguration; /* currently selected configuration. Administered by driver, but not used */ |
||||
volatile schar usbRxLen; /* = 0; number of bytes in usbRxBuf; 0 means free, -1 for flow control */ |
||||
uchar usbCurrentTok; /* last token received or endpoint number for last OUT token if != 0 */ |
||||
uchar usbRxToken; /* token for data we received; or endpont number for last OUT */ |
||||
uchar usbMsgLen = 0xff; /* remaining number of bytes, no msg to send if -1 (see usbMsgPtr) */ |
||||
volatile uchar usbTxLen = USBPID_NAK; /* number of bytes to transmit with next IN token or handshake token */ |
||||
uchar usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen contains handshake token */ |
||||
#if USB_COUNT_SOF |
||||
volatile uchar usbSofCount; /* incremented by assembler module every SOF */ |
||||
#endif |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
||||
volatile uchar usbTxLen1 = USBPID_NAK; /* TX count for endpoint 1 */ |
||||
uchar usbTxBuf1[USB_BUFSIZE]; /* TX data for endpoint 1 */ |
||||
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||
volatile uchar usbTxLen3 = USBPID_NAK; /* TX count for endpoint 3 */ |
||||
uchar usbTxBuf3[USB_BUFSIZE]; /* TX data for endpoint 3 */ |
||||
# endif |
||||
#endif |
||||
|
||||
/* USB status registers / not shared with asm code */ |
||||
uchar *usbMsgPtr; /* data to transmit next -- ROM or RAM address */ |
||||
static uchar usbMsgFlags; /* flag values see below */ |
||||
|
||||
#define USB_FLG_TX_PACKET (1<<0) |
||||
/* Leave free 6 bits after TX_PACKET. This way we can increment usbMsgFlags to toggle TX_PACKET */ |
||||
#define USB_FLG_MSGPTR_IS_ROM (1<<6) |
||||
#define USB_FLG_USE_DEFAULT_RW (1<<7) |
||||
|
||||
/*
|
||||
optimizing hints: |
||||
- do not post/pre inc/dec integer values in operations |
||||
- assign value of PRG_RDB() to register variables and don't use side effects in arg |
||||
- use narrow scope for variables which should be in X/Y/Z register |
||||
- assign char sized expressions to variables to force 8 bit arithmetics |
||||
*/ |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
#if USB_CFG_DESCR_PROPS_STRINGS == 0 |
||||
|
||||
#if USB_CFG_DESCR_PROPS_STRING_0 == 0 |
||||
#undef USB_CFG_DESCR_PROPS_STRING_0 |
||||
#define USB_CFG_DESCR_PROPS_STRING_0 sizeof(usbDescriptorString0) |
||||
PROGMEM const char usbDescriptorString0[] = { /* language descriptor */ |
||||
4, /* sizeof(usbDescriptorString0): length of descriptor in bytes */ |
||||
3, /* descriptor type */ |
||||
0x09, 0x04, /* language index (0x0409 = US-English) */ |
||||
}; |
||||
#endif |
||||
|
||||
#if USB_CFG_DESCR_PROPS_STRING_VENDOR == 0 && USB_CFG_VENDOR_NAME_LEN |
||||
#undef USB_CFG_DESCR_PROPS_STRING_VENDOR |
||||
#define USB_CFG_DESCR_PROPS_STRING_VENDOR sizeof(usbDescriptorStringVendor) |
||||
PROGMEM const int usbDescriptorStringVendor[] = { |
||||
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_VENDOR_NAME_LEN), |
||||
USB_CFG_VENDOR_NAME |
||||
}; |
||||
#endif |
||||
|
||||
#if USB_CFG_DESCR_PROPS_STRING_PRODUCT == 0 && USB_CFG_DEVICE_NAME_LEN |
||||
#undef USB_CFG_DESCR_PROPS_STRING_PRODUCT |
||||
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT sizeof(usbDescriptorStringDevice) |
||||
PROGMEM const int usbDescriptorStringDevice[] = { |
||||
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_DEVICE_NAME_LEN), |
||||
USB_CFG_DEVICE_NAME |
||||
}; |
||||
#endif |
||||
|
||||
#if USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER == 0 && USB_CFG_SERIAL_NUMBER_LEN |
||||
#undef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER |
||||
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER sizeof(usbDescriptorStringSerialNumber) |
||||
PROGMEM const int usbDescriptorStringSerialNumber[] = { |
||||
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LEN), |
||||
USB_CFG_SERIAL_NUMBER |
||||
}; |
||||
#endif |
||||
|
||||
#endif /* USB_CFG_DESCR_PROPS_STRINGS == 0 */ |
||||
|
||||
#if USB_CFG_DESCR_PROPS_DEVICE == 0 |
||||
#undef USB_CFG_DESCR_PROPS_DEVICE |
||||
#define USB_CFG_DESCR_PROPS_DEVICE sizeof(usbDescriptorDevice) |
||||
PROGMEM const char usbDescriptorDevice[] = { /* USB device descriptor */ |
||||
18, /* sizeof(usbDescriptorDevice): length of descriptor in bytes */ |
||||
USBDESCR_DEVICE, /* descriptor type */ |
||||
0x10, 0x01, /* USB version supported */ |
||||
USB_CFG_DEVICE_CLASS, |
||||
USB_CFG_DEVICE_SUBCLASS, |
||||
0, /* protocol */ |
||||
8, /* max packet size */ |
||||
/* the following two casts affect the first byte of the constant only, but
|
||||
* that's sufficient to avoid a warning with the default values. |
||||
*/ |
||||
(char)USB_CFG_VENDOR_ID,/* 2 bytes */ |
||||
(char)USB_CFG_DEVICE_ID,/* 2 bytes */ |
||||
USB_CFG_DEVICE_VERSION, /* 2 bytes */ |
||||
USB_CFG_DESCR_PROPS_STRING_VENDOR != 0 ? 1 : 0, /* manufacturer string index */ |
||||
USB_CFG_DESCR_PROPS_STRING_PRODUCT != 0 ? 2 : 0, /* product string index */ |
||||
USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER != 0 ? 3 : 0, /* serial number string index */ |
||||
1, /* number of configurations */ |
||||
}; |
||||
#endif |
||||
|
||||
#if USB_CFG_DESCR_PROPS_HID_REPORT != 0 && USB_CFG_DESCR_PROPS_HID == 0 |
||||
#undef USB_CFG_DESCR_PROPS_HID |
||||
#define USB_CFG_DESCR_PROPS_HID 9 /* length of HID descriptor in config descriptor below */ |
||||
#endif |
||||
|
||||
#if USB_CFG_DESCR_PROPS_CONFIGURATION == 0 |
||||
#undef USB_CFG_DESCR_PROPS_CONFIGURATION |
||||
#define USB_CFG_DESCR_PROPS_CONFIGURATION sizeof(usbDescriptorConfiguration) |
||||
PROGMEM const char usbDescriptorConfiguration[] = { /* USB configuration descriptor */ |
||||
9, /* sizeof(usbDescriptorConfiguration): length of descriptor in bytes */ |
||||
USBDESCR_CONFIG, /* descriptor type */ |
||||
18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT + (USB_CFG_DESCR_PROPS_HID & 0xff), 0, |
||||
/* total length of data returned (including inlined descriptors) */ |
||||
1, /* number of interfaces in this configuration */ |
||||
1, /* index of this configuration */ |
||||
0, /* configuration name string index */ |
||||
#if USB_CFG_IS_SELF_POWERED |
||||
USBATTR_SELFPOWER, /* attributes */ |
||||
#else |
||||
(char)USBATTR_BUSPOWER, /* attributes */ |
||||
#endif |
||||
USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */ |
||||
/* interface descriptor follows inline: */ |
||||
9, /* sizeof(usbDescrInterface): length of descriptor in bytes */ |
||||
USBDESCR_INTERFACE, /* descriptor type */ |
||||
0, /* index of this interface */ |
||||
0, /* alternate setting for this interface */ |
||||
USB_CFG_HAVE_INTRIN_ENDPOINT, /* endpoints excl 0: number of endpoint descriptors to follow */ |
||||
USB_CFG_INTERFACE_CLASS, |
||||
USB_CFG_INTERFACE_SUBCLASS, |
||||
USB_CFG_INTERFACE_PROTOCOL, |
||||
0, /* string index for interface */ |
||||
#if (USB_CFG_DESCR_PROPS_HID & 0xff) /* HID descriptor */ |
||||
9, /* sizeof(usbDescrHID): length of descriptor in bytes */ |
||||
USBDESCR_HID, /* descriptor type: HID */ |
||||
0x01, 0x01, /* BCD representation of HID version */ |
||||
0x00, /* target country code */ |
||||
0x01, /* number of HID Report (or other HID class) Descriptor infos to follow */ |
||||
0x22, /* descriptor type: report */ |
||||
USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH, 0, /* total length of report descriptor */ |
||||
#endif |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT /* endpoint descriptor for endpoint 1 */ |
||||
7, /* sizeof(usbDescrEndpoint) */ |
||||
USBDESCR_ENDPOINT, /* descriptor type = endpoint */ |
||||
(char)0x81, /* IN endpoint number 1 */ |
||||
0x03, /* attrib: Interrupt endpoint */ |
||||
8, 0, /* maximum packet size */ |
||||
USB_CFG_INTR_POLL_INTERVAL, /* in ms */ |
||||
#endif |
||||
}; |
||||
#endif |
||||
|
||||
/* We don't use prog_int or prog_int16_t for compatibility with various libc
|
||||
* versions. Here's an other compatibility hack: |
||||
*/ |
||||
#ifndef PRG_RDB |
||||
#define PRG_RDB(addr) pgm_read_byte(addr) |
||||
#endif |
||||
|
||||
typedef union{ |
||||
unsigned word; |
||||
uchar *ptr; |
||||
uchar bytes[2]; |
||||
}converter_t; |
||||
/* We use this union to do type conversions. This is better optimized than
|
||||
* type casts in gcc 3.4.3 and much better than using bit shifts to build |
||||
* ints from chars. Byte ordering is not a problem on an 8 bit platform. |
||||
*/ |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
static inline void usbResetDataToggling(void) |
||||
{ |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
||||
USB_SET_DATATOKEN1(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ |
||||
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||
USB_SET_DATATOKEN3(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ |
||||
# endif |
||||
#endif |
||||
} |
||||
|
||||
static inline void usbResetStall(void) |
||||
{ |
||||
#if USB_CFG_IMPLEMENT_HALT && USB_CFG_HAVE_INTRIN_ENDPOINT |
||||
usbTxLen1 = USBPID_NAK; |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||
usbTxLen3 = USBPID_NAK; |
||||
#endif |
||||
#endif |
||||
} |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
||||
USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len) |
||||
{ |
||||
uchar *p, i; |
||||
|
||||
#if USB_CFG_IMPLEMENT_HALT |
||||
if(usbTxLen1 == USBPID_STALL) |
||||
return; |
||||
#endif |
||||
#if 0 /* No runtime checks! Caller is responsible for valid data! */
|
||||
if(len > 8) /* interrupt transfers are limited to 8 bytes */ |
||||
len = 8; |
||||
#endif |
||||
if(usbTxLen1 & 0x10){ /* packet buffer was empty */ |
||||
usbTxBuf1[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ |
||||
}else{ |
||||
usbTxLen1 = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ |
||||
} |
||||
p = usbTxBuf1 + 1; |
||||
for(i=len;i--;) |
||||
*p++ = *data++; |
||||
usbCrc16Append(&usbTxBuf1[1], len); |
||||
usbTxLen1 = len + 4; /* len must be given including sync byte */ |
||||
DBG2(0x21, usbTxBuf1, len + 3); |
||||
} |
||||
#endif |
||||
|
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||
USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len) |
||||
{ |
||||
uchar *p, i; |
||||
|
||||
if(usbTxLen3 & 0x10){ /* packet buffer was empty */ |
||||
usbTxBuf3[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ |
||||
}else{ |
||||
usbTxLen3 = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ |
||||
} |
||||
p = usbTxBuf3 + 1; |
||||
for(i=len;i--;) |
||||
*p++ = *data++; |
||||
usbCrc16Append(&usbTxBuf3[1], len); |
||||
usbTxLen3 = len + 4; /* len must be given including sync byte */ |
||||
DBG2(0x23, usbTxBuf3, len + 3); |
||||
} |
||||
#endif |
||||
|
||||
|
||||
static uchar usbRead(uchar *data, uchar len) |
||||
{ |
||||
#if USB_CFG_IMPLEMENT_FN_READ |
||||
if(usbMsgFlags & USB_FLG_USE_DEFAULT_RW){ |
||||
#endif |
||||
uchar i = len, *r = usbMsgPtr; |
||||
if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ |
||||
while(i--){ |
||||
uchar c = PRG_RDB(r); /* assign to char size variable to enforce byte ops */ |
||||
*data++ = c; |
||||
r++; |
||||
} |
||||
}else{ /* RAM data */ |
||||
while(i--) |
||||
*data++ = *r++; |
||||
} |
||||
usbMsgPtr = r; |
||||
return len; |
||||
#if USB_CFG_IMPLEMENT_FN_READ |
||||
}else{ |
||||
if(len != 0) /* don't bother app with 0 sized reads */ |
||||
return usbFunctionRead(data, len); |
||||
return 0; |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
|
||||
#define GET_DESCRIPTOR(cfgProp, staticName) \ |
||||
if(cfgProp){ \
|
||||
if((cfgProp) & USB_PROP_IS_RAM) \
|
||||
flags &= ~USB_FLG_MSGPTR_IS_ROM; \
|
||||
if((cfgProp) & USB_PROP_IS_DYNAMIC){ \
|
||||
replyLen = usbFunctionDescriptor(rq); \
|
||||
}else{ \
|
||||
replyData = (uchar *)(staticName); \
|
||||
SET_REPLY_LEN((cfgProp) & 0xff); \
|
||||
} \
|
||||
} |
||||
/* We use if() instead of #if in the macro above because #if can't be used
|
||||
* in macros and the compiler optimizes constant conditions anyway. |
||||
*/ |
||||
|
||||
|
||||
/* Don't make this function static to avoid inlining.
|
||||
* The entire function would become too large and exceed the range of |
||||
* relative jumps. |
||||
* 2006-02-25: Either gcc 3.4.3 is better than the gcc used when the comment |
||||
* above was written, or other parts of the code have changed. We now get |
||||
* better results with an inlined function. Test condition: PowerSwitch code. |
||||
*/ |
||||
static void usbProcessRx(uchar *data, uchar len) |
||||
{ |
||||
usbRequest_t *rq = (void *)data; |
||||
uchar replyLen = 0, flags = USB_FLG_USE_DEFAULT_RW; |
||||
/* We use if() cascades because the compare is done byte-wise while switch()
|
||||
* is int-based. The if() cascades are therefore more efficient. |
||||
*/ |
||||
/* usbRxToken can be:
|
||||
* 0x2d 00101101 (USBPID_SETUP for endpoint 0) |
||||
* 0xe1 11100001 (USBPID_OUT for endpoint 0) |
||||
* 0xff 11111111 (USBPID_OUT for endpoint 1) |
||||
*/ |
||||
DBG2(0x10 + ((usbRxToken >> 1) & 3), data, len); /* SETUP0=12; OUT0=10; OUT1=13 */ |
||||
#ifdef USB_RX_USER_HOOK |
||||
USB_RX_USER_HOOK(data, len) |
||||
#endif |
||||
#if USB_CFG_IMPLEMENT_FN_WRITEOUT |
||||
if(usbRxToken < 0x10){ /* endpoint number in usbRxToken */ |
||||
usbFunctionWriteOut(data, len); |
||||
return; /* no reply expected, hence no usbMsgPtr, usbMsgFlags, usbMsgLen set */ |
||||
} |
||||
#endif |
||||
if(usbRxToken == (uchar)USBPID_SETUP){ |
||||
usbTxLen = USBPID_NAK; /* abort pending transmit */ |
||||
if(len == 8){ /* Setup size must be always 8 bytes. Ignore otherwise. */ |
||||
uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; |
||||
if(type == USBRQ_TYPE_STANDARD){ |
||||
#define SET_REPLY_LEN(len) replyLen = (len); usbMsgPtr = replyData |
||||
/* This macro ensures that replyLen and usbMsgPtr are always set in the same way.
|
||||
* That allows optimization of common code in if() branches */ |
||||
uchar *replyData = usbTxBuf + 9; /* there is 3 bytes free space at the end of the buffer */ |
||||
replyData[0] = 0; /* common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ |
||||
if(rq->bRequest == USBRQ_GET_STATUS){ /* 0 */ |
||||
uchar __attribute__((__unused__)) recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ |
||||
#if USB_CFG_IS_SELF_POWERED |
||||
if(recipient == USBRQ_RCPT_DEVICE) |
||||
replyData[0] = USB_CFG_IS_SELF_POWERED; |
||||
#endif |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT && USB_CFG_IMPLEMENT_HALT |
||||
if(recipient == USBRQ_RCPT_ENDPOINT && rq->wIndex.bytes[0] == 0x81) /* request status for endpoint 1 */ |
||||
replyData[0] = usbTxLen1 == USBPID_STALL; |
||||
#endif |
||||
replyData[1] = 0; |
||||
SET_REPLY_LEN(2); |
||||
}else if(rq->bRequest == USBRQ_SET_ADDRESS){ /* 5 */ |
||||
usbNewDeviceAddr = rq->wValue.bytes[0]; |
||||
#ifdef USB_SET_ADDRESS_HOOK |
||||
USB_SET_ADDRESS_HOOK(); |
||||
#endif |
||||
}else if(rq->bRequest == USBRQ_GET_DESCRIPTOR){ /* 6 */ |
||||
flags = USB_FLG_MSGPTR_IS_ROM | USB_FLG_USE_DEFAULT_RW; |
||||
if(rq->wValue.bytes[1] == USBDESCR_DEVICE){ /* 1 */ |
||||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) |
||||
}else if(rq->wValue.bytes[1] == USBDESCR_CONFIG){ /* 2 */ |
||||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) |
||||
}else if(rq->wValue.bytes[1] == USBDESCR_STRING){ /* 3 */ |
||||
#if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC |
||||
if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) |
||||
flags &= ~USB_FLG_MSGPTR_IS_ROM; |
||||
replyLen = usbFunctionDescriptor(rq); |
||||
#else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ |
||||
if(rq->wValue.bytes[0] == 0){ /* descriptor index */ |
||||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) |
||||
}else if(rq->wValue.bytes[0] == 1){ |
||||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) |
||||
}else if(rq->wValue.bytes[0] == 2){ |
||||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_PRODUCT, usbDescriptorStringDevice) |
||||
}else if(rq->wValue.bytes[0] == 3){ |
||||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) |
||||
}else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ |
||||
replyLen = usbFunctionDescriptor(rq); |
||||
} |
||||
#endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ |
||||
#if USB_CFG_DESCR_PROPS_HID_REPORT /* only support HID descriptors if enabled */ |
||||
}else if(rq->wValue.bytes[1] == USBDESCR_HID){ /* 0x21 */ |
||||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) |
||||
}else if(rq->wValue.bytes[1] == USBDESCR_HID_REPORT){ /* 0x22 */ |
||||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) |
||||
#endif /* USB_CFG_DESCR_PROPS_HID_REPORT */ |
||||
}else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ |
||||
replyLen = usbFunctionDescriptor(rq); |
||||
} |
||||
}else if(rq->bRequest == USBRQ_GET_CONFIGURATION){ /* 8 */ |
||||
replyData = &usbConfiguration; /* send current configuration value */ |
||||
SET_REPLY_LEN(1); |
||||
}else if(rq->bRequest == USBRQ_SET_CONFIGURATION){ /* 9 */ |
||||
usbConfiguration = rq->wValue.bytes[0]; |
||||
usbResetStall(); |
||||
}else if(rq->bRequest == USBRQ_GET_INTERFACE){ /* 10 */ |
||||
SET_REPLY_LEN(1); |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
||||
}else if(rq->bRequest == USBRQ_SET_INTERFACE){ /* 11 */ |
||||
usbResetDataToggling(); |
||||
usbResetStall(); |
||||
# if USB_CFG_IMPLEMENT_HALT |
||||
}else if(rq->bRequest == USBRQ_CLEAR_FEATURE || rq->bRequest == USBRQ_SET_FEATURE){ /* 1|3 */ |
||||
if(rq->wValue.bytes[0] == 0 && rq->wIndex.bytes[0] == 0x81){ /* feature 0 == HALT for endpoint == 1 */ |
||||
usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; |
||||
usbResetDataToggling(); |
||||
} |
||||
# endif |
||||
#endif |
||||
}else{ |
||||
/* the following requests can be ignored, send default reply */ |
||||
/* 1: CLEAR_FEATURE, 3: SET_FEATURE, 7: SET_DESCRIPTOR */ |
||||
/* 12: SYNCH_FRAME */ |
||||
} |
||||
#undef SET_REPLY_LEN |
||||
}else{ /* not a standard request -- must be vendor or class request */ |
||||
replyLen = usbFunctionSetup(data); |
||||
} |
||||
#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE |
||||
if(replyLen == 0xff){ /* use user-supplied read/write function */ |
||||
if((rq->bmRequestType & USBRQ_DIR_MASK) == USBRQ_DIR_DEVICE_TO_HOST){ |
||||
replyLen = rq->wLength.bytes[0]; /* IN transfers only */ |
||||
} |
||||
flags &= ~USB_FLG_USE_DEFAULT_RW; /* we have no valid msg, use user supplied read/write functions */ |
||||
}else /* The 'else' prevents that we limit a replyLen of 0xff to the maximum transfer len. */ |
||||
#endif |
||||
if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */ |
||||
replyLen = rq->wLength.bytes[0]; |
||||
} |
||||
/* make sure that data packets which are sent as ACK to an OUT transfer are always zero sized */ |
||||
}else{ /* DATA packet from out request */ |
||||
#if USB_CFG_IMPLEMENT_FN_WRITE |
||||
if(!(usbMsgFlags & USB_FLG_USE_DEFAULT_RW)){ |
||||
uchar rval = usbFunctionWrite(data, len); |
||||
replyLen = 0xff; |
||||
if(rval == 0xff){ /* an error occurred */ |
||||
usbMsgLen = 0xff; /* cancel potentially pending data packet for ACK */ |
||||
usbTxLen = USBPID_STALL; |
||||
}else if(rval != 0){ /* This was the final package */ |
||||
replyLen = 0; /* answer with a zero-sized data packet */ |
||||
} |
||||
flags = 0; /* start with a DATA1 package, stay with user supplied write() function */ |
||||
} |
||||
#endif |
||||
} |
||||
usbMsgFlags = flags; |
||||
usbMsgLen = replyLen; |
||||
} |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
static void usbBuildTxBlock(void) |
||||
{ |
||||
uchar wantLen, len, txLen, token; |
||||
|
||||
wantLen = usbMsgLen; |
||||
if(wantLen > 8) |
||||
wantLen = 8; |
||||
usbMsgLen -= wantLen; |
||||
token = USBPID_DATA1; |
||||
if(usbMsgFlags & USB_FLG_TX_PACKET) |
||||
token = USBPID_DATA0; |
||||
usbMsgFlags++; |
||||
len = usbRead(usbTxBuf + 1, wantLen); |
||||
if(len <= 8){ /* valid data packet */ |
||||
usbCrc16Append(&usbTxBuf[1], len); |
||||
txLen = len + 4; /* length including sync byte */ |
||||
if(len < 8) /* a partial package identifies end of message */ |
||||
usbMsgLen = 0xff; |
||||
}else{ |
||||
txLen = USBPID_STALL; /* stall the endpoint */ |
||||
usbMsgLen = 0xff; |
||||
} |
||||
usbTxBuf[0] = token; |
||||
usbTxLen = txLen; |
||||
DBG2(0x20, usbTxBuf, txLen-1); |
||||
} |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
static inline uchar isNotSE0(void) |
||||
{ |
||||
uchar rval; |
||||
/* We want to do
|
||||
* return (USBIN & USBMASK); |
||||
* here, but the compiler does int-expansion acrobatics. |
||||
* We can avoid this by assigning to a char-sized variable. |
||||
*/ |
||||
rval = USBIN & USBMASK; |
||||
return rval; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
USB_PUBLIC void usbPoll(void) |
||||
{ |
||||
schar len; |
||||
uchar i; |
||||
|
||||
if((len = usbRxLen) > 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 - 3); |
||||
#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 != 0xff){ /* transmit data pending? */ |
||||
usbBuildTxBlock(); |
||||
} |
||||
} |
||||
for(i = 10; i > 0; i--){ |
||||
if(isNotSE0()) |
||||
break; |
||||
} |
||||
if(i == 0){ /* RESET condition, called multiple times during reset */ |
||||
usbNewDeviceAddr = 0; |
||||
usbDeviceAddr = 0; |
||||
usbResetStall(); |
||||
DBG1(0xff, 0, 0); |
||||
} |
||||
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(); |
||||
} |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
@ -1,10 +1,10 @@ |
||||
/* Name: usbdrvasm.asm |
||||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||
* Project: AVR USB driver |
||||
* 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) |
||||
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) |
||||
* This Revision: $Id$ |
||||
*/ |
||||
|
||||
@ -1,3 +0,0 @@ |
||||
*.hex |
||||
*.elf |
||||
*.o |
||||
@ -1,172 +0,0 @@ |
||||
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. |
||||
|
||||
@ -1,149 +0,0 @@ |
||||
Version 2009-08-22 |
||||
|
||||
========================== |
||||
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 (Clay Logic, www.claylogic.com). 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. |
||||
@ -1,154 +0,0 @@ |
||||
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. |
||||
@ -1,628 +0,0 @@ |
||||
/* Name: usbdrv.c
|
||||
* 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) |
||||
* This Revision: $Id$ |
||||
*/ |
||||
|
||||
#include "usbportability.h" |
||||
#include "usbdrv.h" |
||||
#include "oddebug.h" |
||||
|
||||
/*
|
||||
General Description: |
||||
This module implements the C-part of the USB driver. See usbdrv.h for a |
||||
documentation of the entire driver. |
||||
*/ |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
/* raw USB registers / interface to assembler code: */ |
||||
uchar usbRxBuf[2*USB_BUFSIZE]; /* raw RX buffer: PID, 8 bytes data, 2 bytes CRC */ |
||||
uchar usbInputBufOffset; /* offset in usbRxBuf used for low level receiving */ |
||||
uchar usbDeviceAddr; /* assigned during enumeration, defaults to 0 */ |
||||
uchar usbNewDeviceAddr; /* device ID which should be set after status phase */ |
||||
uchar usbConfiguration; /* currently selected configuration. Administered by driver, but not used */ |
||||
volatile schar usbRxLen; /* = 0; number of bytes in usbRxBuf; 0 means free, -1 for flow control */ |
||||
uchar usbCurrentTok; /* last token received or endpoint number for last OUT token if != 0 */ |
||||
uchar usbRxToken; /* token for data we received; or endpont number for last OUT */ |
||||
volatile uchar usbTxLen = USBPID_NAK; /* number of bytes to transmit with next IN token or handshake token */ |
||||
uchar usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen contains handshake token */ |
||||
#if USB_COUNT_SOF |
||||
volatile uchar usbSofCount; /* incremented by assembler module every SOF */ |
||||
#endif |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE |
||||
usbTxStatus_t usbTxStatus1; |
||||
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||
usbTxStatus_t usbTxStatus3; |
||||
# endif |
||||
#endif |
||||
#if USB_CFG_CHECK_DATA_TOGGLING |
||||
uchar usbCurrentDataToken;/* when we check data toggling to ignore duplicate packets */ |
||||
#endif |
||||
|
||||
/* USB status registers / not shared with asm code */ |
||||
uchar *usbMsgPtr; /* data to transmit next -- ROM or RAM address */ |
||||
static usbMsgLen_t usbMsgLen = USB_NO_MSG; /* remaining number of bytes */ |
||||
static uchar usbMsgFlags; /* flag values see below */ |
||||
|
||||
#define USB_FLG_MSGPTR_IS_ROM (1<<6) |
||||
#define USB_FLG_USE_USER_RW (1<<7) |
||||
|
||||
/*
|
||||
optimizing hints: |
||||
- do not post/pre inc/dec integer values in operations |
||||
- assign value of USB_READ_FLASH() to register variables and don't use side effects in arg |
||||
- use narrow scope for variables which should be in X/Y/Z register |
||||
- assign char sized expressions to variables to force 8 bit arithmetics |
||||
*/ |
||||
|
||||
/* -------------------------- String Descriptors --------------------------- */ |
||||
|
||||
#if USB_CFG_DESCR_PROPS_STRINGS == 0 |
||||
|
||||
#if USB_CFG_DESCR_PROPS_STRING_0 == 0 |
||||
#undef USB_CFG_DESCR_PROPS_STRING_0 |
||||
#define USB_CFG_DESCR_PROPS_STRING_0 sizeof(usbDescriptorString0) |
||||
PROGMEM const char usbDescriptorString0[] = { /* language descriptor */ |
||||
4, /* sizeof(usbDescriptorString0): length of descriptor in bytes */ |
||||
3, /* descriptor type */ |
||||
0x09, 0x04, /* language index (0x0409 = US-English) */ |
||||
}; |
||||
#endif |
||||
|
||||
#if USB_CFG_DESCR_PROPS_STRING_VENDOR == 0 && USB_CFG_VENDOR_NAME_LEN |
||||
#undef USB_CFG_DESCR_PROPS_STRING_VENDOR |
||||
#define USB_CFG_DESCR_PROPS_STRING_VENDOR sizeof(usbDescriptorStringVendor) |
||||
PROGMEM const int usbDescriptorStringVendor[] = { |
||||
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_VENDOR_NAME_LEN), |
||||
USB_CFG_VENDOR_NAME |
||||
}; |
||||
#endif |
||||
|
||||
#if USB_CFG_DESCR_PROPS_STRING_PRODUCT == 0 && USB_CFG_DEVICE_NAME_LEN |
||||
#undef USB_CFG_DESCR_PROPS_STRING_PRODUCT |
||||
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT sizeof(usbDescriptorStringDevice) |
||||
PROGMEM const int usbDescriptorStringDevice[] = { |
||||
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_DEVICE_NAME_LEN), |
||||
USB_CFG_DEVICE_NAME |
||||
}; |
||||
#endif |
||||
|
||||
#if USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER == 0 && USB_CFG_SERIAL_NUMBER_LEN |
||||
#undef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER |
||||
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER sizeof(usbDescriptorStringSerialNumber) |
||||
PROGMEM const int usbDescriptorStringSerialNumber[] = { |
||||
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LEN), |
||||
USB_CFG_SERIAL_NUMBER |
||||
}; |
||||
#endif |
||||
|
||||
#endif /* USB_CFG_DESCR_PROPS_STRINGS == 0 */ |
||||
|
||||
/* --------------------------- Device Descriptor --------------------------- */ |
||||
|
||||
#if USB_CFG_DESCR_PROPS_DEVICE == 0 |
||||
#undef USB_CFG_DESCR_PROPS_DEVICE |
||||
#define USB_CFG_DESCR_PROPS_DEVICE sizeof(usbDescriptorDevice) |
||||
PROGMEM const char usbDescriptorDevice[] = { /* USB device descriptor */ |
||||
18, /* sizeof(usbDescriptorDevice): length of descriptor in bytes */ |
||||
USBDESCR_DEVICE, /* descriptor type */ |
||||
0x10, 0x01, /* USB version supported */ |
||||
USB_CFG_DEVICE_CLASS, |
||||
USB_CFG_DEVICE_SUBCLASS, |
||||
0, /* protocol */ |
||||
8, /* max packet size */ |
||||
/* the following two casts affect the first byte of the constant only, but
|
||||
* that's sufficient to avoid a warning with the default values. |
||||
*/ |
||||
(char)USB_CFG_VENDOR_ID,/* 2 bytes */ |
||||
(char)USB_CFG_DEVICE_ID,/* 2 bytes */ |
||||
USB_CFG_DEVICE_VERSION, /* 2 bytes */ |
||||
USB_CFG_DESCR_PROPS_STRING_VENDOR != 0 ? 1 : 0, /* manufacturer string index */ |
||||
USB_CFG_DESCR_PROPS_STRING_PRODUCT != 0 ? 2 : 0, /* product string index */ |
||||
USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER != 0 ? 3 : 0, /* serial number string index */ |
||||
1, /* number of configurations */ |
||||
}; |
||||
#endif |
||||
|
||||
/* ----------------------- Configuration Descriptor ------------------------ */ |
||||
|
||||
#if USB_CFG_DESCR_PROPS_HID_REPORT != 0 && USB_CFG_DESCR_PROPS_HID == 0 |
||||
#undef USB_CFG_DESCR_PROPS_HID |
||||
#define USB_CFG_DESCR_PROPS_HID 9 /* length of HID descriptor in config descriptor below */ |
||||
#endif |
||||
|
||||
#if USB_CFG_DESCR_PROPS_CONFIGURATION == 0 |
||||
#undef USB_CFG_DESCR_PROPS_CONFIGURATION |
||||
#define USB_CFG_DESCR_PROPS_CONFIGURATION sizeof(usbDescriptorConfiguration) |
||||
PROGMEM const char usbDescriptorConfiguration[] = { /* USB configuration descriptor */ |
||||
9, /* sizeof(usbDescriptorConfiguration): length of descriptor in bytes */ |
||||
USBDESCR_CONFIG, /* descriptor type */ |
||||
18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT3 + |
||||
(USB_CFG_DESCR_PROPS_HID & 0xff), 0, |
||||
/* total length of data returned (including inlined descriptors) */ |
||||
1, /* number of interfaces in this configuration */ |
||||
1, /* index of this configuration */ |
||||
0, /* configuration name string index */ |
||||
#if USB_CFG_IS_SELF_POWERED |
||||
(1 << 7) | USBATTR_SELFPOWER, /* attributes */ |
||||
#else |
||||
(1 << 7), /* attributes */ |
||||
#endif |
||||
USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */ |
||||
/* interface descriptor follows inline: */ |
||||
9, /* sizeof(usbDescrInterface): length of descriptor in bytes */ |
||||
USBDESCR_INTERFACE, /* descriptor type */ |
||||
0, /* index of this interface */ |
||||
0, /* alternate setting for this interface */ |
||||
USB_CFG_HAVE_INTRIN_ENDPOINT + USB_CFG_HAVE_INTRIN_ENDPOINT3, /* endpoints excl 0: number of endpoint descriptors to follow */ |
||||
USB_CFG_INTERFACE_CLASS, |
||||
USB_CFG_INTERFACE_SUBCLASS, |
||||
USB_CFG_INTERFACE_PROTOCOL, |
||||
0, /* string index for interface */ |
||||
#if (USB_CFG_DESCR_PROPS_HID & 0xff) /* HID descriptor */ |
||||
9, /* sizeof(usbDescrHID): length of descriptor in bytes */ |
||||
USBDESCR_HID, /* descriptor type: HID */ |
||||
0x01, 0x01, /* BCD representation of HID version */ |
||||
0x00, /* target country code */ |
||||
0x01, /* number of HID Report (or other HID class) Descriptor infos to follow */ |
||||
0x22, /* descriptor type: report */ |
||||
USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH, 0, /* total length of report descriptor */ |
||||
#endif |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT /* endpoint descriptor for endpoint 1 */ |
||||
7, /* sizeof(usbDescrEndpoint) */ |
||||
USBDESCR_ENDPOINT, /* descriptor type = endpoint */ |
||||
(char)0x81, /* IN endpoint number 1 */ |
||||
0x03, /* attrib: Interrupt endpoint */ |
||||
8, 0, /* maximum packet size */ |
||||
USB_CFG_INTR_POLL_INTERVAL, /* in ms */ |
||||
#endif |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 /* endpoint descriptor for endpoint 3 */ |
||||
7, /* sizeof(usbDescrEndpoint) */ |
||||
USBDESCR_ENDPOINT, /* descriptor type = endpoint */ |
||||
(char)(0x80 | USB_CFG_EP3_NUMBER), /* IN endpoint number 3 */ |
||||
0x03, /* attrib: Interrupt endpoint */ |
||||
8, 0, /* maximum packet size */ |
||||
USB_CFG_INTR_POLL_INTERVAL, /* in ms */ |
||||
#endif |
||||
}; |
||||
#endif |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
static inline void usbResetDataToggling(void) |
||||
{ |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE |
||||
USB_SET_DATATOKEN1(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ |
||||
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||
USB_SET_DATATOKEN3(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ |
||||
# endif |
||||
#endif |
||||
} |
||||
|
||||
static inline void usbResetStall(void) |
||||
{ |
||||
#if USB_CFG_IMPLEMENT_HALT && USB_CFG_HAVE_INTRIN_ENDPOINT |
||||
usbTxLen1 = USBPID_NAK; |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||
usbTxLen3 = USBPID_NAK; |
||||
#endif |
||||
#endif |
||||
} |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
#if !USB_CFG_SUPPRESS_INTR_CODE |
||||
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
||||
static void usbGenericSetInterrupt(uchar *data, uchar len, usbTxStatus_t *txStatus) |
||||
{ |
||||
uchar *p; |
||||
char i; |
||||
|
||||
#if USB_CFG_IMPLEMENT_HALT |
||||
if(usbTxLen1 == USBPID_STALL) |
||||
return; |
||||
#endif |
||||
if(txStatus->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 = (uchar *)(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 = 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, *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++ = *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 |
||||
} |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
@ -1,750 +0,0 @@ |
||||
/* Name: usbdrvasm128.inc |
||||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||
* Author: Christian Starkjohann |
||||
* Creation Date: 2008-10-11 |
||||
* Tabsize: 4 |
||||
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH |
||||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||
* This Revision: $Id$ |
||||
*/ |
||||
|
||||
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||
* appropriate implementation! |
||||
*/ |
||||
|
||||
/* |
||||
General Description: |
||||
This file is the 12.8 MHz version of the USB driver. It is intended for use |
||||
with the internal RC oscillator. Although 12.8 MHz is outside the guaranteed |
||||
calibration range of the oscillator, almost all AVRs can reach this frequency. |
||||
This version contains a phase locked loop in the receiver routine to cope with |
||||
slight clock rate deviations of up to +/- 1%. |
||||
|
||||
See usbdrv.h for a description of the entire driver. |
||||
|
||||
LIMITATIONS |
||||
=========== |
||||
Although it may seem very handy to save the crystal and use the internal |
||||
RC oscillator of the CPU, this method (and this module) has some serious |
||||
limitations: |
||||
(1) The guaranteed calibration range of the oscillator is only 8.1 MHz. |
||||
They typical range is 14.5 MHz and most AVRs can actually reach this rate. |
||||
(2) Writing EEPROM and Flash may be unreliable (short data lifetime) since |
||||
the write procedure is timed from the RC oscillator. |
||||
(3) End Of Packet detection (SE0) should be in bit 1, bit it is only checked |
||||
if bits 0 and 1 both read as 0 on D- and D+ read as 0 in the middle. This may |
||||
cause problems with old hubs which delay SE0 by up to one cycle. |
||||
(4) Code size is much larger than that of the other modules. |
||||
|
||||
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! |
||||
|
||||
Implementation notes: |
||||
====================== |
||||
min frequency: 67 cycles for 8 bit -> 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<<USB_INTR_PENDING_BIT;[03] int0 occurred during TX -- clear pending flag |
||||
USB_STORE_PENDING(x2) ;[04] |
||||
ori x1, USBIDLE ;[05] |
||||
in x2, USBDDR ;[06] |
||||
cbr x2, USBMASK ;[07] set both pins to input |
||||
mov x3, x1 ;[08] |
||||
cbr x3, USBMASK ;[09] configure no pullup on both pins |
||||
lpm ;[10] |
||||
lpm ;[13] |
||||
out USBOUT, x1 ;[16] <-- out J (idle) -- end of SE0 (EOP signal) |
||||
out USBDDR, x2 ;[17] <-- release bus now |
||||
out USBOUT, x3 ;[18] <-- ensure no pull-up resistors are active |
||||
rjmp doReturn |
||||
|
||||
|
||||
|
||||
/***************************************************************************** |
||||
The following PHP script generates a code skeleton for the receiver routine: |
||||
|
||||
<?php |
||||
|
||||
function printCmdBuffer($thisBit) |
||||
{ |
||||
global $cycle; |
||||
|
||||
$nextBit = ($thisBit + 1) % 8; |
||||
$s = ob_get_contents(); |
||||
ob_end_clean(); |
||||
$s = str_replace("#", $thisBit, $s); |
||||
$s = str_replace("@", $nextBit, $s); |
||||
$lines = explode("\n", $s); |
||||
for($i = 0; $i < count($lines); $i++){ |
||||
$s = $lines[$i]; |
||||
if(ereg("\\[([0-9-][0-9])\\]", $s, $regs)){ |
||||
$c = $cycle + (int)$regs[1]; |
||||
$s = ereg_replace("\\[[0-9-][0-9]\\]", sprintf("[%02d]", $c), $s); |
||||
} |
||||
if(strlen($s) > 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] |
||||
<?php |
||||
}else{ |
||||
?> |
||||
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] |
||||
<?php |
||||
} |
||||
printCmdBuffer($bitNum); |
||||
} |
||||
|
||||
$bitStartCycles = array(1, 9, 17, 26, 34, 42, 51, 59); |
||||
for($i = 0; $i < 16; $i++){ |
||||
$bit = $i % 8; |
||||
$emitClrCode = ($i + (int)($i / 8)) % 2; |
||||
$cycle = $bitStartCycles[$bit]; |
||||
if($emitClrCode){ |
||||
printf("bit%dAfterClr:\n", $bit); |
||||
}else{ |
||||
printf("bit%dAfterSet:\n", $bit); |
||||
} |
||||
ob_start(); |
||||
echo " ***** ;[-1]\n"; |
||||
printCmdBuffer($bit); |
||||
printBit(!$emitClrCode, $bit); |
||||
if($i == 7) |
||||
echo "\n"; |
||||
} |
||||
|
||||
?> |
||||
*****************************************************************************/ |
||||
@ -1,707 +0,0 @@ |
||||
/* Name: usbdrvasm18.inc |
||||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||
* Author: Lukas Schrittwieser (based on 20 MHz usbdrvasm20.inc by Jeroen Benschop) |
||||
* Creation Date: 2009-01-20 |
||||
* Tabsize: 4 |
||||
* Copyright: (c) 2008 by Lukas Schrittwieser and OBJECTIVE DEVELOPMENT Software GmbH |
||||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||
* Revision: $Id$ |
||||
*/ |
||||
|
||||
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||
* appropriate implementation! |
||||
*/ |
||||
|
||||
/* |
||||
General Description: |
||||
This file is the 18 MHz version of the asssembler part of the USB driver. It |
||||
requires a 18 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, [sofError], bitcnt(x5), shift, x1, x2, x3, x4, cnt, ZL, ZH] = 14 bytes |
||||
;nominal frequency: 18 MHz -> 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<<USBPLUS ;[6] [rx loop init] current line state is K state. D+=="1", D-=="0" |
||||
push x5 ;[7] |
||||
push cnt ;[9] |
||||
ldi cnt, USB_BUFSIZE ;[11] |
||||
|
||||
|
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
; receives the pid byte |
||||
; there is no real unstuffing algorithm implemented here as a stuffing bit is impossible in the pid byte. |
||||
; That's because the last four bits of the byte are the inverted of the first four bits. If we detect a |
||||
; unstuffing condition something went wrong and abort |
||||
; shift has to be initialized to 0x80 |
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
|
||||
; pid bit 0 - used for even more register saving (we need the z pointer) |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||
eor x2, x1 ;[2] generate inverted of actual bit |
||||
sbrc x2, USBMINUS ;[3] if the bit is set we received a zero |
||||
sec ;[4] |
||||
ror shift ;[5] we perform no unstuffing check here as this is the first bit |
||||
mov x2, x1 ;[6] |
||||
push ZL ;[7] |
||||
;[8] |
||||
push ZH ;[9] |
||||
;[10] |
||||
ldi x3, 0xFE ;[11] x3 is the high order crc value |
||||
|
||||
|
||||
bitloopPid: |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||
breq nse0 ;[2] both lines are low so handle se0 |
||||
eor x2, x1 ;[3] generate inverted of actual bit |
||||
sbrc x2, USBMINUS ;[4] set the carry if we received a zero |
||||
sec ;[5] |
||||
ror shift ;[6] |
||||
ldi ZL, 0x54 ;[7] ZL is the low order crc value |
||||
ser x4 ;[8] the is no bit stuffing check here as the pid bit can't be stuffed. if so |
||||
; some error occured. In this case the paket is discarded later on anyway. |
||||
mov x2, x1 ;[9] prepare for the next cycle |
||||
brcc bitloopPid ;[10] while 0s drop out of shift we get the next bit |
||||
eor x4, shift ;[11] invert all bits in shift and store result in x4 |
||||
|
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
; receives data bytes and calculates the crc |
||||
; the last USBIN state has to be in x2 |
||||
; this is only the first half, due to branch distanc limitations the second half of the loop is near the end |
||||
; of this asm file |
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
|
||||
rxDataStart: |
||||
in x1, USBIN ;[0] sample line state (note: a se0 check is not useful due to bit dribbling) |
||||
ser x5 ;[1] prepare the unstuff marker register |
||||
eor x2, x1 ;[2] generates the inverted of the actual bit |
||||
bst x2, USBMINUS ;[3] copy the bit from x2 |
||||
bld shift, 0 ;[4] and store it in shift |
||||
mov x2, shift ;[5] make a copy of shift for unstuffing check |
||||
andi x2, 0xF9 ;[6] mask the last six bits, if we got six zeros (which are six ones in fact) |
||||
breq unstuff0 ;[7] then Z is set now and we branch to the unstuffing handler |
||||
didunstuff0: |
||||
subi cnt, 1 ;[8] cannot use dec because it doesn't affect the carry flag |
||||
brcs nOverflow ;[9] Too many bytes received. Ignore packet |
||||
st Y+, x4 ;[10] store the last received byte |
||||
;[11] st needs two cycles |
||||
|
||||
; bit1 |
||||
in x2, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] check for se0 during bit 0 |
||||
breq nse0 ;[2] |
||||
andi x2, USBMASK ;[3] check se0 during bit 1 |
||||
breq nse0 ;[4] |
||||
eor x1, x2 ;[5] |
||||
bst x1, USBMINUS ;[6] |
||||
bld shift, 1 ;[7] |
||||
mov x1, shift ;[8] |
||||
andi x1, 0xF3 ;[9] |
||||
breq unstuff1 ;[10] |
||||
didunstuff1: |
||||
nop ;[11] |
||||
|
||||
; bit2 |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] check for se0 (as there is nothing else to do here |
||||
breq nOverflow ;[2] |
||||
eor x2, x1 ;[3] generates the inverted of the actual bit |
||||
bst x2, USBMINUS ;[4] |
||||
bld shift, 2 ;[5] store the bit |
||||
mov x2, shift ;[6] |
||||
andi x2, 0xE7 ;[7] if we have six zeros here (which means six 1 in the stream) |
||||
breq unstuff2 ;[8] the next bit is a stuffing bit |
||||
didunstuff2: |
||||
nop2 ;[9] |
||||
;[10] |
||||
nop ;[11] |
||||
|
||||
; bit3 |
||||
in x2, USBIN ;[0] sample line state |
||||
andi x2, USBMASK ;[1] check for se0 |
||||
breq nOverflow ;[2] |
||||
eor x1, x2 ;[3] |
||||
bst x1, USBMINUS ;[4] |
||||
bld shift, 3 ;[5] |
||||
mov x1, shift ;[6] |
||||
andi x1, 0xCF ;[7] |
||||
breq unstuff3 ;[8] |
||||
didunstuff3: |
||||
nop ;[9] |
||||
rjmp rxDataBit4 ;[10] |
||||
;[11] |
||||
|
||||
; the avr branch instructions allow an offset of +63 insturction only, so we need this |
||||
; 'local copy' of se0 |
||||
nse0: |
||||
rjmp se0 ;[4] |
||||
;[5] |
||||
; the same same as for se0 is needed for overflow and StuffErr |
||||
nOverflow: |
||||
stuffErr: |
||||
rjmp overflow |
||||
|
||||
|
||||
unstuff0: ;[8] this is the branch delay of breq unstuffX |
||||
andi x1, USBMASK ;[9] do an se0 check here (if the last crc byte ends with 5 one's we might end up here |
||||
breq didunstuff0 ;[10] event tough the message is complete -> 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<<USB_INTR_PENDING_BIT;[5] int0 occurred during TX -- clear pending flag |
||||
USB_STORE_PENDING(x2) ;[6] |
||||
ori x1, USBIDLE ;[7] |
||||
in x2, USBDDR ;[8] |
||||
cbr x2, USBMASK ;[9] set both pins to input |
||||
mov x3, x1 ;[10] |
||||
cbr x3, USBMASK ;[11] configure no pullup on both pins |
||||
ldi x4, 4 ;[12] |
||||
se0Delay: |
||||
dec x4 ;[13] [16] [19] [22] |
||||
brne se0Delay ;[14] [17] [20] [23] |
||||
out USBOUT, x1 ;[24] <-- out J (idle) -- end of SE0 (EOP signal) |
||||
out USBDDR, x2 ;[25] <-- release bus now |
||||
out USBOUT, x3 ;[26] <-- ensure no pull-up resistors are active |
||||
rjmp doReturn |
||||
|
||||
bitstuffN: |
||||
eor x1, x4 ;[8] generate a zero |
||||
ldi x2, 0 ;[9] reset the bit stuffing history |
||||
nop2 ;[10] |
||||
out USBOUT, x1 ;[0] <-- send the stuffing bit |
||||
rjmp didStuffN ;[1] |
||||
|
||||
bitstuff7: |
||||
eor x1, x4 ;[5] |
||||
ldi x2, 0 ;[6] reset bit stuffing history |
||||
clc ;[7] fill a zero into the shift register |
||||
rol shift ;[8] compensate for ror shift at branch destination |
||||
rjmp didStuff7 ;[9] |
||||
;[10] jump delay |
||||
|
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
; receives data bytes and calculates the crc |
||||
; second half of the data byte receiver loop |
||||
; most parts of the crc algorithm are here |
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
|
||||
nOverflow2: |
||||
rjmp overflow |
||||
|
||||
rxDataBit4: |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] check for se0 |
||||
breq nOverflow2 ;[2] |
||||
eor x2, x1 ;[3] |
||||
bst x2, USBMINUS ;[4] |
||||
bld shift, 4 ;[5] |
||||
mov x2, shift ;[6] |
||||
andi x2, 0x9F ;[7] |
||||
breq unstuff4 ;[8] |
||||
didunstuff4: |
||||
nop2 ;[9][10] |
||||
nop ;[11] |
||||
|
||||
; bit5 |
||||
in x2, USBIN ;[0] sample line state |
||||
ldi ZH, hi8(usbCrcTableHigh);[1] use the table for the higher byte |
||||
eor x1, x2 ;[2] |
||||
bst x1, USBMINUS ;[3] |
||||
bld shift, 5 ;[4] |
||||
mov x1, shift ;[5] |
||||
andi x1, 0x3F ;[6] |
||||
breq unstuff5 ;[7] |
||||
didunstuff5: |
||||
lpm x4, Z ;[8] load the higher crc xor-byte and store it for later use |
||||
;[9] lpm needs 3 cycles |
||||
;[10] |
||||
ldi ZH, hi8(usbCrcTableLow);[11] load the lower crc xor byte adress |
||||
|
||||
; bit6 |
||||
in x1, USBIN ;[0] sample line state |
||||
eor x2, x1 ;[1] |
||||
bst x2, USBMINUS ;[2] |
||||
bld shift, 6 ;[3] |
||||
mov x2, shift ;[4] |
||||
andi x2, 0x7E ;[5] |
||||
breq unstuff6 ;[6] |
||||
didunstuff6: |
||||
lpm ZL, Z ;[7] load the lower xor crc byte |
||||
;[8] lpm needs 3 cycles |
||||
;[9] |
||||
eor ZL, x3 ;[10] xor the old high crc byte with the low xor-byte |
||||
mov x3, x4 ;[11] move the new high order crc value from temp to its destination |
||||
|
||||
; bit7 |
||||
in x2, USBIN ;[0] sample line state |
||||
eor x1, x2 ;[1] |
||||
bst x1, USBMINUS ;[2] |
||||
bld shift, 7 ;[3] now shift holds the complete but inverted data byte |
||||
mov x1, shift ;[4] |
||||
andi x1, 0xFC ;[5] |
||||
breq unstuff7 ;[6] |
||||
didunstuff7: |
||||
eor x5, shift ;[7] x5 marks all bits which have not been inverted by the unstuffing subs |
||||
mov x4, x5 ;[8] keep a copy of the data byte it will be stored during next bit0 |
||||
eor ZL, x4 ;[9] feed the actual byte into the crc algorithm |
||||
rjmp rxDataStart ;[10] next byte |
||||
;[11] during the reception of the next byte this one will be fed int the crc algorithm |
||||
|
||||
unstuff4: ;[9] this is the jump delay of rjmp unstuffX |
||||
ori shift, 0x10 ;[10] invert the last received bit to prevent furhter unstuffing |
||||
andi x5, 0xEF ;[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 stuffErr2 ;[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 didunstuff4 ;[7] |
||||
;[8] jump delay of rjmp didunstuffX |
||||
|
||||
unstuff5: ;[8] this is the jump delay of rjmp unstuffX |
||||
nop ;[9] |
||||
ori shift, 0x20 ;[10] invert the last received bit to prevent furhter unstuffing |
||||
andi x5, 0xDF ;[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 stuffErr2 ;[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 |
||||
nop ;[5] |
||||
rjmp didunstuff5 ;[6] |
||||
;[7] jump delay of rjmp didunstuffX |
||||
|
||||
unstuff6: ;[7] this is the jump delay of rjmp unstuffX |
||||
nop2 ;[8] |
||||
;[9] |
||||
ori shift, 0x40 ;[10] invert the last received bit to prevent furhter unstuffing |
||||
andi x5, 0xBF ;[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 stuffErr2 ;[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 |
||||
rjmp didunstuff6 ;[5] |
||||
;[6] jump delay of rjmp didunstuffX |
||||
|
||||
unstuff7: ;[7] this is the jump delay of rjmp unstuffX |
||||
nop ;[8] |
||||
nop ;[9] |
||||
ori shift, 0x80 ;[10] invert the last received bit to prevent furhter unstuffing |
||||
andi x5, 0x7F ;[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 stuffErr2 ;[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 |
||||
rjmp didunstuff7 ;[5] |
||||
;[6] jump delay of rjmp didunstuff7 |
||||
|
||||
; local copy of the stuffErr desitnation for the second half of the receiver loop |
||||
stuffErr2: |
||||
rjmp stuffErr |
||||
|
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
; The crc table follows. It has to be aligned to enable a fast loading of the needed bytes. |
||||
; There are two tables of 256 entries each, the low and the high byte table. |
||||
; Table values were generated with the following C code: |
||||
/* |
||||
#include <stdio.h> |
||||
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 |
||||
|
||||
@ -1,360 +0,0 @@ |
||||
/* 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) |
||||
* Revision: $Id$ |
||||
*/ |
||||
|
||||
/* 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<<USBPLUS ;[13] current line state is K state. D+=="1", D-=="0" |
||||
bit0: |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||
rjmp handleBit ;[2] make bit0 14 cycles long |
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; Process bit7. However, bit 6 still may need unstuffing. |
||||
;---------------------------------------------------------------------------- |
||||
|
||||
b6checkUnstuff: |
||||
dec bitcnt ;[9] |
||||
breq unstuff6 ;[10] |
||||
bit7: |
||||
subi cnt, 1 ;[11] cannot use dec becaus it does not affect the carry flag |
||||
brcs overflow ;[12] Too many bytes received. Ignore packet |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||
cpse x1, x2 ;[2] when previous line state equals current line state, handle "1" |
||||
rjmp b7handle0 ;[3] when line state differs, handle "0" |
||||
sec ;[4] |
||||
ror shift ;[5] shift "1" into the data |
||||
st y+, shift ;[6] store the data into the buffer |
||||
ldi shift, 0x40 ;[7] reset data for receiving the next byte |
||||
subi leap, 0x55 ;[9] trick to introduce a leap cycle every 3 bytes |
||||
brcc nextInst ;[10 or 11] it will fail after 85 bytes. However low speed can only receive 11 |
||||
dec bitcnt ;[11 or 12] |
||||
brne bit0 ;[12 or 13] |
||||
ldi x1, 1 ;[13 or 14] unstuffing bit 7 |
||||
in bitcnt, USBIN ;[0] sample stuff bit |
||||
rjmp unstuff ;[1] |
||||
|
||||
b7handle0: |
||||
mov x2,x1 ;[5] Set x2 to current line state |
||||
ldi bitcnt, 6 ;[6] |
||||
lsr shift ;[7] shift "0" into the data |
||||
st y+, shift ;[8] store data into the buffer |
||||
ldi shift, 0x40 ;[10] reset data for receiving the next byte |
||||
subi leap, 0x55 ;[11] trick to introduce a leap cycle every 3 bytes |
||||
brcs bit0 ;[12] it will fail after 85 bytes. However low speed can only receive 11 |
||||
rjmp bit0 ;[13] |
||||
|
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; Handle unstuff |
||||
; x1==0xFF indicate unstuffing bit6 |
||||
;---------------------------------------------------------------------------- |
||||
|
||||
unstuff6: |
||||
ldi x1,0xFF ;[12] indicate unstuffing bit 6 |
||||
in bitcnt, USBIN ;[0] sample stuff bit |
||||
nop ;[1] fix timing |
||||
unstuff: ;b0-5 b6 b7 |
||||
mov x2,bitcnt ;[3] [2] [3] Set x2 to match line state |
||||
subi leap2, 0x55 ;[4] [3] [4] delay loop |
||||
brcs nextInst ;[5] [4] [5] add one cycle every three stuff bits |
||||
sbci leap2,0 ;[6] [5] [6] |
||||
ldi bitcnt,6 ;[7] [6] [7] reset bit stuff counter |
||||
andi x2, USBMASK ;[8] [7] [8] only keep D+ and D- |
||||
cpi x1,0 ;[9] [8] [9] |
||||
brmi bit7 ;[10] [9] [10] finished unstuffing bit6 When x1<0 |
||||
breq bitloop ;[11] --- [11] finished unstuffing bit0-5 when x1=0 |
||||
nop ;--- --- [12] |
||||
in x1, USBIN ;--- --- [0] sample line state for bit0 |
||||
andi x1, USBMASK ;--- --- [1] filter only D+ and D- bits |
||||
rjmp handleBit ;--- --- [2] make bit0 14 cycles long |
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; Receiver loop (numbers in brackets are cycles within byte after instr) |
||||
;---------------------------------------------------------------------------- |
||||
bitloop: |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||
breq se0 ;[2] both lines are low so handle se0 |
||||
handleBit: |
||||
cpse x1, x2 ;[3] when previous line state equals current line state, handle "1" |
||||
rjmp handle0 ;[4] when line state differs, handle "0" |
||||
sec ;[5] |
||||
ror shift ;[6] shift "1" into the data |
||||
brcs b6checkUnstuff ;[7] When after shift C is set, next bit is bit7 |
||||
nop2 ;[8] |
||||
dec bitcnt ;[10] |
||||
brne bitloop ;[11] |
||||
ldi x1,0 ;[12] indicate unstuff for bit other than bit6 or bit7 |
||||
in bitcnt, USBIN ;[0] sample stuff bit |
||||
rjmp unstuff ;[1] |
||||
|
||||
handle0: |
||||
mov x2, x1 ;[6] Set x2 to current line state |
||||
ldi bitcnt, 6 ;[7] reset unstuff counter. |
||||
lsr shift ;[8] shift "0" into the data |
||||
brcs bit7 ;[9] When after shift C is set, next bit is bit7 |
||||
nop ;[10] |
||||
rjmp bitloop ;[11] |
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; End of receive loop. Now start handling EOP |
||||
;---------------------------------------------------------------------------- |
||||
|
||||
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 |
||||
; 7.5 bit times is 100 cycles. This implementation arrives a bit later at se0 |
||||
; then specified in the include file but there is plenty of time |
||||
|
||||
bitstuffN: |
||||
eor x1, x4 ;[8] |
||||
ldi x2, 0 ;[9] |
||||
nop2 ;[10] |
||||
out USBOUT, x1 ;[12] <-- out |
||||
rjmp didStuffN ;[0] |
||||
|
||||
bitstuff7: |
||||
eor x1, x4 ;[6] |
||||
ldi x2, 0 ;[7] Carry is zero due to brcc |
||||
rol shift ;[8] compensate for ror shift at branch destination |
||||
nop2 ;[9] |
||||
rjmp didStuff7 ;[11] |
||||
|
||||
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, 0x49 ;[-4] [10] binary 01001001 |
||||
txBitLoop: |
||||
sbrs shift, 0 ;[-3] [10] [11] |
||||
eor x1, x4 ;[-2] [11] [12] |
||||
out USBOUT, x1 ;[-1] [12] [13] <-- out N |
||||
ror shift ;[0] [13] [14] |
||||
ror x2 ;[1] |
||||
didStuffN: |
||||
nop2 ;[2] |
||||
nop ;[4] |
||||
cpi x2, 0xfc ;[5] |
||||
brcc bitstuffN ;[6] |
||||
lsr bitcnt ;[7] |
||||
brcc txBitLoop ;[8] |
||||
brne txBitLoop ;[9] |
||||
|
||||
sbrs shift, 0 ;[10] |
||||
eor x1, x4 ;[11] |
||||
didStuff7: |
||||
out USBOUT, x1 ;[-1] [13] <-- out 7 |
||||
ror shift ;[0] [14] |
||||
ror x2 ;[1] |
||||
nop ;[2] |
||||
cpi x2, 0xfc ;[3] |
||||
brcc bitstuff7 ;[4] |
||||
ld shift, y+ ;[5] |
||||
dec cnt ;[7] |
||||
brne txByteLoop ;[8] |
||||
;make SE0: |
||||
cbr x1, USBMASK ;[9] prepare SE0 [spec says EOP may be 25 to 30 cycles] |
||||
lds x2, usbNewDeviceAddr;[10] |
||||
lsl x2 ;[12] we compare with left shifted address |
||||
out USBOUT, x1 ;[13] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle |
||||
subi YL, 20 + 2 ;[0] Only assign address on data packets, not ACK/NAK in x3 |
||||
sbci YH, 0 ;[1] |
||||
;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 ;[2] |
||||
sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer |
||||
skipAddrAssign: |
||||
;end of usbDeviceAddress transfer |
||||
ldi x2, 1<<USB_INTR_PENDING_BIT;[4] int0 occurred during TX -- clear pending flag |
||||
USB_STORE_PENDING(x2) ;[5] |
||||
ori x1, USBIDLE ;[6] |
||||
in x2, USBDDR ;[7] |
||||
cbr x2, USBMASK ;[8] set both pins to input |
||||
mov x3, x1 ;[9] |
||||
cbr x3, USBMASK ;[10] configure no pullup on both pins |
||||
ldi x4, 5 ;[11] |
||||
se0Delay: |
||||
dec x4 ;[12] [15] [18] [21] [24] |
||||
brne se0Delay ;[13] [16] [19] [22] [25] |
||||
out USBOUT, x1 ;[26] <-- out J (idle) -- end of SE0 (EOP signal) |
||||
out USBDDR, x2 ;[27] <-- release bus now |
||||
out USBOUT, x3 ;[28] <-- ensure no pull-up resistors are active |
||||
rjmp doReturn |
||||
@ -1,144 +0,0 @@ |
||||
/* Name: usbportability.h
|
||||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||
* Author: Christian Starkjohann |
||||
* Creation Date: 2008-06-17 |
||||
* Tabsize: 4 |
||||
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH |
||||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||
* This Revision: $Id$ |
||||
*/ |
||||
|
||||
/*
|
||||
General Description: |
||||
This header is intended to contain all (or at least most of) the compiler |
||||
and library dependent stuff. The C code is written for avr-gcc and avr-libc. |
||||
The API of other development environments is converted to gcc's and avr-libc's |
||||
API by means of defines. |
||||
|
||||
This header also contains all system includes since they depend on the |
||||
development environment. |
||||
|
||||
Thanks to Oleg Semyonov for his help with the IAR tools port! |
||||
*/ |
||||
|
||||
#ifndef __usbportability_h_INCLUDED__ |
||||
#define __usbportability_h_INCLUDED__ |
||||
|
||||
/* We check explicitly for IAR and CodeVision. Default is avr-gcc/avr-libc. */ |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
#if defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ /* check for IAR */ |
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
#ifndef ENABLE_BIT_DEFINITIONS |
||||
# define ENABLE_BIT_DEFINITIONS 1 /* Enable bit definitions */ |
||||
#endif |
||||
|
||||
/* Include IAR headers */ |
||||
#include <ioavr.h> |
||||
#ifndef __IAR_SYSTEMS_ASM__ |
||||
# include <inavr.h> |
||||
#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 <io.h> |
||||
#include <delay.h> |
||||
|
||||
#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 <avr/io.h> |
||||
#ifdef __ASSEMBLER__ |
||||
# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ |
||||
#else |
||||
# include <avr/pgmspace.h> |
||||
#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__ */ |
||||
Loading…
Reference in new issue