Arduino draft finished

master
Stefan Krulj 14 years ago
parent c7cf8448b8
commit 35aeaaea5b
  1. 125
      libcrypt/Makefile
  2. 63
      libcrypt/calib.c
  3. 46
      libcrypt/hmac-sha1/config.h
  4. 131
      libcrypt/hmac-sha1/hmac-sha1.c
  5. 41
      libcrypt/hmac-sha1/hmac-sha1.h
  6. 77
      libcrypt/main.c
  7. 18
      libcrypt/main.h
  8. 68
      libcrypt/mem_eval/mem_eval.c
  9. 56
      libcrypt/mem_eval/mem_eval.h
  10. 883
      libcrypt/sha1/sha1-asm.S
  11. 119
      libcrypt/sha1/sha1.h
  12. 35
      libcrypt/trunc.c
  13. 10
      libcrypt/trunc.h
  14. 67
      libcrypt/usart/usart.c
  15. 21
      libcrypt/usart/usart.h
  16. 58
      libcrypt/usb_callback.c
  17. 36
      libcrypt/usbconfig.h
  18. 102
      libcrypt/usbdrv/Changelog.txt
  19. 69
      libcrypt/usbdrv/CommercialLicense.txt
  20. 12
      libcrypt/usbdrv/License.txt
  21. 154
      libcrypt/usbdrv/Readme.txt
  22. 143
      libcrypt/usbdrv/USBID-License.txt
  23. 26
      libcrypt/usbdrv/asmcommon.inc
  24. 65
      libcrypt/usbdrv/iarcompat.h
  25. 4
      libcrypt/usbdrv/oddebug.c
  26. 9
      libcrypt/usbdrv/oddebug.h
  27. 169
      libcrypt/usbdrv/usbconfig-prototype.h
  28. 578
      libcrypt/usbdrv/usbdrv.c
  29. 126
      libcrypt/usbdrv/usbdrv.h
  30. 210
      libcrypt/usbdrv/usbdrvasm.S
  31. 4
      libcrypt/usbdrv/usbdrvasm.asm
  32. 246
      libcrypt/usbdrv/usbdrvasm12.inc
  33. 23
      libcrypt/usbdrv/usbdrvasm15.inc
  34. 35
      libcrypt/usbdrv/usbdrvasm16.inc
  35. 20
      libcrypt/usbdrv/usbdrvasm165.inc
  36. 3
      usbdrv/.gitignore
  37. 172
      usbdrv/Readme.txt
  38. 149
      usbdrv/USB-ID-FAQ.txt
  39. 154
      usbdrv/USB-IDs-for-free.txt
  40. 628
      usbdrv/usbdrv.c
  41. 750
      usbdrv/usbdrvasm128.inc
  42. 707
      usbdrv/usbdrvasm18-crc.inc
  43. 360
      usbdrv/usbdrvasm20.inc
  44. 144
      usbdrv/usbportability.h

@ -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
};

@ -17,11 +17,11 @@
/* This is the port where the USB bus is connected. When you configure it to
* "B", the registers PORTB, PINB and DDRB will be used.
*/
#define USB_CFG_DMINUS_BIT 4
#define USB_CFG_DMINUS_BIT 4
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
* This may be any bit in the port.
*/
#define USB_CFG_DPLUS_BIT 3
#define USB_CFG_DPLUS_BIT 3
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
* This may be any bit in the port. Please note that D+ must also be connected
* to interrupt pin INT0!
@ -74,7 +74,7 @@
/* Define this to 1 if the device has its own power supply. Set it to 0 if the
* device is powered from the USB bus.
*/
#define USB_CFG_MAX_BUS_POWER 200
#define USB_CFG_MAX_BUS_POWER 50
/* Set this variable to the maximum USB bus power consumption of your device.
* The value is in milliamperes. [It will be divided by two since USB
* communicates power requirements in units of 2 mA.]
@ -103,7 +103,7 @@
#ifndef __ASSEMBLER__
extern void usbEventResetReady(void);
#endif
//#define USB_RESET_HOOK(isReset) if(!isReset){usbEventResetReady();}
#define USB_RESET_HOOK(isReset) if(!isReset){usbEventResetReady();}
/* This macro is a hook if you need to know when an USB RESET occurs. It has
* one parameter which distinguishes between the start of RESET state and its
* end.
@ -115,14 +115,14 @@ extern void usbEventResetReady(void);
/* -------------------------- Device Description --------------------------- */
#define USB_CFG_VENDOR_ID 0x81, 0x17
#define USB_CFG_VENDOR_ID 0x42, 0x42
/* USB vendor ID for the device, low byte first. If you have registered your
* own Vendor ID, define it here. Otherwise you use obdev's free shared
* VID/PID pair. Be sure to read USBID-License.txt for rules!
* This template uses obdev's shared VID/PID pair for HIDs: 0x16c0/0x5df.
* Use this VID/PID pair ONLY if you understand the implications!
*/
#define USB_CFG_DEVICE_ID 0x9f, 0x0c
#define USB_CFG_DEVICE_ID 0x31, 0xe1
/* This is the ID of the product, low byte first. It is interpreted in the
* scope of the vendor ID. If you have registered your own VID with usb.org
* or if you have licensed a PID from somebody else, define it here. Otherwise
@ -131,13 +131,11 @@ extern void usbEventResetReady(void);
* This template uses obdev's shared VID/PID pair for HIDs: 0x16c0/0x5df.
* Use this VID/PID pair ONLY if you understand the implications!
*/
#define USB_CFG_DEVICE_VERSION 0x04, 0x01
#define USB_CFG_DEVICE_VERSION 0x00, 0x01
/* Version number of the device: Minor number first, then major number.
*/
#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't'
#define USB_CFG_VENDOR_NAME_LEN 8
#undef USB_CFG_VENDOR_NAME
#undef USB_CFG_VENDOR_NAME_LEN
#define USB_CFG_VENDOR_NAME 'h', 'a', 'c', 'k', 'e', 'r', 's', 'p', 'a', 'c', 'e', 's', 'h', 'o', 'p','.','c','o','m'
#define USB_CFG_VENDOR_NAME_LEN 19
/* These two values define the vendor name returned by the USB device. The name
* must be given as a list of characters under single quotes. The characters
* are interpreted as Unicode (UTF-16) entities.
@ -146,8 +144,8 @@ extern void usbEventResetReady(void);
* obdev's free shared VID/PID pair. See the file USBID-License.txt for
* details.
*/
#define USB_CFG_DEVICE_NAME 'U', 'S', 'B', 't', 'i', 'n', 'y', 'S', 'P', 'I'
#define USB_CFG_DEVICE_NAME_LEN 10
#define USB_CFG_DEVICE_NAME 'T','r','u','s','t','M','e'
#define USB_CFG_DEVICE_NAME_LEN 7
/* Same as above for the device name. If you don't want a device name, undefine
* the macros. See the file USBID-License.txt before you assign a name if you
* use a shared VID/PID.
@ -165,13 +163,13 @@ extern void usbEventResetReady(void);
#define USB_CFG_DEVICE_SUBCLASS 0
/* See USB specification if you want to conform to an existing device class.
*/
#define USB_CFG_INTERFACE_CLASS 0xff /* HID */
#define USB_CFG_INTERFACE_CLASS 3 /* HID */
#define USB_CFG_INTERFACE_SUBCLASS 0 /* no boot interface */
#define USB_CFG_INTERFACE_PROTOCOL 0 /* no protocol */
/* See USB specification if you want to conform to an existing device class or
* protocol.
*/
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 0 /* total length of report descriptor */
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 35 /* total length of report descriptor */
/* Define this to the length of the HID report descriptor, if you implement
* an HID device. Otherwise don't define it or define it to 0.
* Since this template defines a HID device, it must also specify a HID
@ -255,14 +253,16 @@ extern void usbEventResetReady(void);
/* #define USB_INTR_ENABLE_BIT INT0 */
/* #define USB_INTR_PENDING GIFR */
/* #define USB_INTR_PENDING_BIT INTF0 */
// use PCINT1 instead of INT0
/*
#define USB_INTR_CFG PCMSK
#define USB_INTR_CFG_SET (1<<USB_CFG_DPLUS_BIT)
#define USB_INTR_ENABLE_BIT PCIE
#define USB_INTR_PENDING_BIT PCIF
#define USB_INTR_VECTOR SIG_PIN_CHANGE
*/
//#define USB_INTR_VECTOR INT0_vect
//#define USB_INTR_VECTOR PCINT9_vect
#endif /* __usbconfig_h_included__ */

@ -214,105 +214,3 @@ Scroll down to the bottom to see the most recent changes.
* Release 2008-02-28
- USB_INITIAL_DATATOKEN defaults to USBPID_DATA1 now, which means that we
start with sending USBPID_DATA0.
- Changed defaults in usbconfig-prototype.h
- Added free USB VID/PID pair for MIDI class devices
- Restructured AVR-USB as separate package, not part of PowerSwitch any more.
* Release 2008-04-18
- Restructured usbdrv.c so that it is easier to read and understand.
- Better code optimization with gcc 4.
- If a second interrupt in endpoint is enabled, also add it to config
descriptor.
- Added config option for long transfers (above 254 bytes), see
USB_CFG_LONG_TRANSFERS in usbconfig.h.
- Added 20 MHz module contributed by Jeroen Benschop.
* Release 2008-05-13
- Fixed bug in libs-host/hiddata.c function usbhidGetReport(): length
was not incremented, pointer to length was incremented instead.
- Added code to command line tool(s) which claims an interface. This code
is disabled by default, but may be necessary on newer Linux kernels.
- Added usbconfig.h option "USB_CFG_CHECK_DATA_TOGGLING".
- New header "usbportability.h" prepares ports to other development
environments.
- Long transfers (above 254 bytes) did not work when usbFunctionRead() was
used to supply the data. Fixed this bug. [Thanks to Alexander Neumann!]
- In hiddata.c (example code for sending/receiving data over HID), use
USB_RECIP_DEVICE instead of USB_RECIP_INTERFACE for control transfers so
that we need not claim the interface.
- in usbPoll() loop 20 times polling for RESET state instead of 10 times.
This accounts for the higher clock rates we now support.
- Added a module for 12.8 MHz RC oscillator with PLL in receiver loop.
- Added hook to SOF code so that oscillator can be tuned to USB frame clock.
- Added timeout to waitForJ loop. Helps preventing unexpected hangs.
- Added example code for oscillator tuning to libs-device (thanks to
Henrik Haftmann for the idea to this routine).
- Implemented option USB_CFG_SUPPRESS_INTR_CODE.
* Release 2008-10-22
- Fixed libs-device/osctune.h: OSCCAL is memory address on ATMega88 and
similar, not offset of 0x20 needs to be added.
- Allow distribution under GPLv3 for those who have to link against other
code distributed under GPLv3.
* Release 2008-11-26
- Removed libusb-win32 dependency for hid-data example in Makefile.windows.
It was never required and confused many people.
- Added extern uchar usbRxToken to usbdrv.h.
- Integrated a module with CRC checks at 18 MHz by Lukas Schrittwieser.
* Release 2009-03-23
- Hid-mouse example used settings from hid-data example, fixed that.
- Renamed project to V-USB due to a trademark issue with Atmel(r).
- Changed CommercialLicense.txt and USBID-License.txt to make the
background of USB ID registration clearer.
* Release 2009-04-15
- Changed CommercialLicense.txt to reflect the new range of PIDs from
Jason Kotzin.
- Removed USBID-License.txt in favor of USB-IDs-for-free.txt and
USB-ID-FAQ.txt
- Fixed a bug in the 12.8 MHz module: End Of Packet decection was made in
the center between bit 0 and 1 of each byte. This is where the data lines
are expected to change and the sampled data may therefore be nonsense.
We therefore check EOP ONLY if bits 0 AND 1 have both been read as 0 on D-.
- Fixed a bitstuffing problem in the 16 MHz module: If bit 6 was stuffed,
the unstuffing code in the receiver routine was 1 cycle too long. If
multiple bytes had the unstuffing in bit 6, the error summed up until the
receiver was out of sync.
- Included option for faster CRC routine.
Thanks to Slawomir Fras (BoskiDialer) for this code!
- Updated bits in Configuration Descriptor's bmAttributes according to
USB 1.1 (in particular bit 7, it is a must-be-set bit now).
* Release 2009-08-22
- Moved first DBG1() after odDebugInit() in all examples.
- Use vector INT0_vect instead of SIG_INTERRUPT0 if defined. This makes
V-USB compatible with the new "p" suffix devices (e.g. ATMega328p).
- USB_CFG_CLOCK_KHZ setting is now required in usbconfig.h (no default any
more).
- New option USB_CFG_DRIVER_FLASH_PAGE allows boot loaders on devices with
more than 64 kB flash.
- Built-in configuration descriptor allows custom definition for second
endpoint now.
* Release 2010-07-15
- Fixed bug in usbDriverSetup() which prevented descriptor sizes above 255
bytes.
- Avoid a compiler warning for unused parameter in usbHandleResetHook() when
compiler option -Wextra is enabled.
- Fixed wrong hex value for some IDs in USB-IDs-for-free.txt.
- Keep a define for USBATTR_BUSPOWER, although the flag does not exist
in USB 1.1 any more. Set it to 0. This is for backward compatibility.
* Release 2012-01-09

@ -1,5 +1,5 @@
V-USB Driver Software License Agreement
Version 2009-08-03
AVR-USB Driver Software License Agreement
Version 2006-07-24
THIS LICENSE AGREEMENT GRANTS YOU CERTAIN RIGHTS IN A SOFTWARE. YOU CAN
ENTER INTO THIS AGREEMENT AND ACQUIRE THE RIGHTS OUTLINED BELOW BY PAYING
@ -13,41 +13,30 @@ Grosse Schiffgasse 1A/7, 1020 Wien, AUSTRIA.
1.2 "You" shall mean the Licensee.
1.3 "V-USB" shall mean all files included in the package distributed under
the name "vusb" by OBJECTIVE DEVELOPMENT (http://www.obdev.at/vusb/)
unless otherwise noted. This includes the firmware-only USB device
implementation for Atmel AVR microcontrollers, some simple device examples
and host side software examples and libraries.
1.3 "AVR-USB" shall mean the firmware-only USB device implementation for
Atmel AVR microcontrollers distributed by OBJECTIVE DEVELOPMENT and
consisting of the files usbdrv.c, usbdrv.h, usbdrvasm.S, oddebug.c,
oddebug.h, usbdrvasm.asm, iarcompat.h and usbconfig-prototype.h.
2 LICENSE GRANTS
2.1 Source Code. OBJECTIVE DEVELOPMENT shall furnish you with the source
code of V-USB.
code of AVR-USB.
2.2 Distribution and Use. OBJECTIVE DEVELOPMENT grants you the
non-exclusive right to use, copy and distribute V-USB with your hardware
non-exclusive right to use and distribute AVR-USB with your hardware
product(s), restricted by the limitations in section 3 below.
2.3 Modifications. OBJECTIVE DEVELOPMENT grants you the right to modify
the source code and your copy of V-USB according to your needs.
2.4 USB IDs. OBJECTIVE DEVELOPMENT furnishes you with one or two USB
Product ID(s), sent to you in e-mail. These Product IDs are reserved
exclusively for you. OBJECTIVE DEVELOPMENT has obtained USB Product ID
ranges under the Vendor ID 5824 from Wouter van Ooijen (Van Ooijen
Technische Informatica, www.voti.nl) and under the Vendor ID 8352 from
Jason Kotzin (Clay Logic, www.claylogic.com). Both owners of the Vendor IDs
have obtained these IDs from the USB Implementers Forum, Inc.
(www.usb.org). OBJECTIVE DEVELOPMENT disclaims all liability which might
arise from the assignment of USB IDs.
2.5 USB Certification. Although not part of this agreement, we want to make
it clear that you cannot become USB certified when you use V-USB or a USB
Product ID assigned by OBJECTIVE DEVELOPMENT. AVR microcontrollers don't
meet the electrical specifications required by the USB specification and
the USB Implementers Forum certifies only members who bought a Vendor ID of
their own.
your copy of AVR-USB according to your needs.
2.4 USB IDs. OBJECTIVE DEVELOPMENT grants you the exclusive rights to use
USB Product ID(s) sent to you in e-mail after receiving your payment in
conjunction with USB Vendor ID 5824. OBJECTIVE DEVELOPMENT has acquired an
exclusive license for this pair of USB identifiers from Wouter van Ooijen
(www.voti.nl), who has licensed the VID from the USB Implementers Forum,
Inc. (www.usb.org).
3 LICENSE RESTRICTIONS
@ -56,21 +45,21 @@ their own.
applicable. Which one is determined by the amount you pay to OBJECTIVE
DEVELOPMENT, see section 4 ("Payment") below.
Hobby License: You may use V-USB according to section 2 above in no more
Hobby License: You may use AVR-USB according to section 2 above in no more
than 5 hardware units. These units must not be sold for profit.
Entry Level License: You may use V-USB according to section 2 above in no
Entry Level License: You may use AVR-USB according to section 2 above in no
more than 150 hardware units.
Professional License: You may use V-USB according to section 2 above in
Professional License: You may use AVR-USB according to section 2 above in
any number of hardware units, except for large scale production ("unlimited
fair use"). Quantities below 10,000 units are not considered large scale
production. If your reach quantities which are obviously large scale
production, you must pay a license fee of 0.10 EUR per unit for all units
above 10,000.
3.2 Rental. You may not rent, lease, or lend V-USB or otherwise encumber
any copy of V-USB, or any of the rights granted herein.
3.2 Rental. You may not rent, lease, or lend AVR-USB or otherwise encumber
any copy of AVR-USB, or any of the rights granted herein.
3.3 Transfer. You may not transfer your rights under this Agreement to
another party without OBJECTIVE DEVELOPMENT's prior written consent. If
@ -88,7 +77,7 @@ non-exclusive.
by third parties. In particular, you are not allowed to use the USB logo or
other trademarks owned by the USB Implementers Forum, Inc. without their
consent. Since such consent depends on USB certification, it should be
noted that V-USB will not pass certification because it does not
noted that AVR-USB will not pass certification because it does not
implement checksum verification and the microcontroller ports do not meet
the electrical specifications.
@ -98,15 +87,15 @@ the electrical specifications.
The payment amount depends on the variation of this agreement (according to
section 3.1) into which you want to enter. Concrete prices are listed on
OBJECTIVE DEVELOPMENT's web site, usually at
http://www.obdev.at/vusb/license.html. You agree to pay the amount listed
http://www.obdev.at/avrusb/license.html. You agree to pay the amount listed
there to OBJECTIVE DEVELOPMENT or OBJECTIVE DEVELOPMENT's payment processor
or reseller.
5 COPYRIGHT AND OWNERSHIP
V-USB is protected by copyright laws and international copyright
treaties, as well as other intellectual property laws and treaties. V-USB
AVR-USB is protected by copyright laws and international copyright
treaties, as well as other intellectual property laws and treaties. AVR-USB
is licensed, not sold.
@ -122,12 +111,12 @@ and limitation of liability shall survive termination of this agreement.
7 DISCLAIMER OF WARRANTY AND LIABILITY
LIMITED WARRANTY. V-USB IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
LIMITED WARRANTY. AVR-USB IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, OBJECTIVE
DEVELOPMENT AND ITS SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES, EITHER
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
NON-INFRINGEMENT, WITH REGARD TO V-USB, AND THE PROVISION OF OR FAILURE
NON-INFRINGEMENT, WITH REGARD TO AVR-USB, AND THE PROVISION OF OR FAILURE
TO PROVIDE SUPPORT SERVICES. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL
RIGHTS. YOU MAY HAVE OTHERS, WHICH VARY FROM STATE/JURISDICTION TO
STATE/JURISDICTION.
@ -137,11 +126,11 @@ IN NO EVENT SHALL OBJECTIVE DEVELOPMENT OR ITS SUPPLIERS BE LIABLE FOR ANY
SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER
(INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS,
BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY
LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE V-USB OR THE
LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE AVR-USB OR THE
PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF OBJECTIVE
DEVELOPMENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY
CASE, OBJECTIVE DEVELOPMENT'S ENTIRE LIABILITY UNDER ANY PROVISION OF THIS
AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR V-USB.
AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR AVR-USB.
8 MISCELLANEOUS TERMS

@ -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.

@ -1,10 +1,10 @@
/* Name: asmcommon.inc
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Project: AVR USB driver
* Author: Christian Starkjohann
* Creation Date: 2007-11-05
* Tabsize: 4
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* Revision: $Id$
*/
@ -103,11 +103,8 @@ sofError:
reti
handleData:
#if USB_CFG_CHECK_CRC
CRC_CLEANUP_AND_CHECK ; jumps to ignorePacket if CRC error
#endif
lds shift, usbCurrentTok;[18]
tst shift ;[20]
lds token, usbCurrentTok;[18]
tst token ;[20]
breq doReturn ;[21]
lds x2, usbRxLen ;[22]
tst x2 ;[24]
@ -116,11 +113,8 @@ handleData:
; recognized if usbPoll() was called less frequently than once every 4 ms.
cpi cnt, 4 ;[26] zero sized data packets are status phase only -- ignore and ack
brmi sendAckAndReti ;[27] keep rx buffer clean -- we must not NAK next SETUP
#if USB_CFG_CHECK_DATA_TOGGLING
sts usbCurrentDataToken, token ; store for checking by C code
#endif
sts usbRxLen, cnt ;[28] store received data, swap buffers
sts usbRxToken, shift ;[30]
sts usbRxToken, token ;[30]
lds x2, usbInputBufOffset;[32] swap buffers
ldi cnt, USB_BUFSIZE ;[34]
sub cnt, x2 ;[35]
@ -137,11 +131,7 @@ handleIn:
ldi x1, USBPID_NAK ;[34] prepare value for usbTxLen
#if USB_CFG_HAVE_INTRIN_ENDPOINT
andi x3, 0xf ;[35] x3 contains endpoint
#if USB_CFG_SUPPRESS_INTR_CODE
brne sendNakAndReti ;[36]
#else
brne handleIn1 ;[36]
#endif
#endif
lds cnt, usbTxLen ;[37]
sbrc cnt, 4 ;[39] all handshake tokens have bit 4 set
@ -160,7 +150,7 @@ handleIn:
; RAM this way and avoid potential problems with endless retries. The rest of
; the driver assumes error-free transfers anyway.
#if !USB_CFG_SUPPRESS_INTR_CODE && USB_CFG_HAVE_INTRIN_ENDPOINT /* placed here due to relative jump range */
#if USB_CFG_HAVE_INTRIN_ENDPOINT /* placed here due to relative jump range */
handleIn1: ;[38]
#if USB_CFG_HAVE_INTRIN_ENDPOINT3
; 2006-06-10 as suggested by O.Tamura: support second INTR IN / BULK IN endpoint
@ -174,8 +164,9 @@ handleIn1: ;[38]
ldi YL, lo8(usbTxBuf1) ;[46]
ldi YH, hi8(usbTxBuf1) ;[47]
rjmp usbSendAndReti ;[48] 50 + 12 = 62 until SOP
#endif
#if USB_CFG_HAVE_INTRIN_ENDPOINT3
#if USB_CFG_HAVE_INTRIN_ENDPOINT && USB_CFG_HAVE_INTRIN_ENDPOINT3
handleIn3:
lds cnt, usbTxLen3 ;[41]
sbrc cnt, 4 ;[43]
@ -185,4 +176,3 @@ handleIn3:
ldi YH, hi8(usbTxBuf3) ;[48]
rjmp usbSendAndReti ;[49] 51 + 12 = 63 until SOP
#endif
#endif

@ -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__ */

@ -4,8 +4,8 @@
* Creation Date: 2005-01-16
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id$
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: oddebug.c 275 2007-03-20 09:58:28Z cs $
*/
#include "oddebug.h"

@ -4,8 +4,8 @@
* Creation Date: 2005-01-16
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id$
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: oddebug.h 275 2007-03-20 09:58:28Z cs $
*/
#ifndef __oddebug_h_included__
@ -29,7 +29,10 @@ the output and a memory block to dump in hex ('data' and 'len').
#endif
/* make sure we have the UART defines: */
#include "usbportability.h"
#include "iarcompat.h"
#ifndef __IAR_SYSTEMS_ICC__
# include <avr/io.h>
#endif
#ifndef uchar
# define uchar unsigned char

@ -1,11 +1,11 @@
/* Name: usbconfig.h
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Project: AVR USB driver
* Author: Christian Starkjohann
* Creation Date: 2005-04-01
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id$
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbconfig-prototype.h 532 2008-02-28 15:35:05Z cs $
*/
#ifndef __usbconfig_h_included__
@ -14,14 +14,12 @@
/*
General Description:
This file is an example configuration (with inline documentation) for the USB
driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is
also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may
wire the lines to any other port, as long as D+ is also wired to INT0 (or any
other hardware interrupt, as long as it is the highest level interrupt, see
section at the end of this file).
+ To create your own usbconfig.h file, copy this file to your project's
+ firmware source directory) and rename it to "usbconfig.h".
+ Then edit it accordingly.
driver. It configures AVR-USB for an ATMega8 with USB D+ connected to Port D
bit 2 (which is also hardware interrupt 0) and USB D- to Port D bit 0. You may
wire the lines to any other port, as long as D+ is also wired to INT0.
To create your own usbconfig.h file, copy this file to the directory
containing "usbdrv" (that is your project firmware source directory) and
rename it to "usbconfig.h". Then edit it accordingly.
*/
/* ---------------------------- Hardware Config ---------------------------- */
@ -30,7 +28,7 @@ section at the end of this file).
/* This is the port where the USB bus is connected. When you configure it to
* "B", the registers PORTB, PINB and DDRB will be used.
*/
#define USB_CFG_DMINUS_BIT 4
#define USB_CFG_DMINUS_BIT 0
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
* This may be any bit in the port.
*/
@ -43,20 +41,12 @@ section at the end of this file).
* interrupt, the USB interrupt will also be triggered at Start-Of-Frame
* markers every millisecond.]
*/
#define USB_CFG_CLOCK_KHZ (F_CPU/1000)
/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000,
* 16500, 18000 and 20000. The 12.8 MHz and 16.5 MHz versions of the code
* require no crystal, they tolerate +/- 1% deviation from the nominal
* frequency. All other rates require a precision of 2000 ppm and thus a
* crystal!
* Since F_CPU should be defined to your actual clock rate anyway, you should
* not need to modify this setting.
*/
#define USB_CFG_CHECK_CRC 0
/* Define this to 1 if you want that the driver checks integrity of incoming
* data packets (CRC checks). CRC checks cost quite a bit of code size and are
* currently only available for 18 MHz crystal clock. You must choose
* USB_CFG_CLOCK_KHZ = 18000 if you enable this option.
/* #define USB_CFG_CLOCK_KHZ (F_CPU/1000) */
/* Clock rate of the AVR in MHz. Legal values are 12000, 15000, 16000 or 16500.
* The 16.5 MHz version of the code requires no crystal, it tolerates +/- 1%
* deviation from the nominal frequency. All other rates require a precision
* of 2000 ppm and thus a crystal!
* Default if not specified: 12 MHz
*/
/* ----------------------- Optional Hardware Config ------------------------ */
@ -75,7 +65,7 @@ section at the end of this file).
/* --------------------------- Functional Range ---------------------------- */
#define USB_CFG_HAVE_INTRIN_ENDPOINT 0
#define USB_CFG_HAVE_INTRIN_ENDPOINT 1
/* Define this to 1 if you want to compile a version with two endpoints: The
* default control endpoint 0 and an interrupt-in endpoint (any other endpoint
* number).
@ -90,11 +80,9 @@ section at the end of this file).
/* If the so-called endpoint 3 is used, it can now be configured to any other
* endpoint number (except 0) with this macro. Default if undefined is 3.
*/
/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */
/* #define USB_INITIAL_DATATOKEN USBPID_DATA0 */
/* The above macro defines the startup condition for data toggling on the
* interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1.
* Since the token is toggled BEFORE sending any data, the first packet is
* sent with the oposite value of this configuration!
* interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA0.
*/
#define USB_CFG_IMPLEMENT_HALT 0
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature
@ -102,15 +90,7 @@ section at the end of this file).
* it is required by the standard. We have made it a config option because it
* bloats the code considerably.
*/
#define USB_CFG_SUPPRESS_INTR_CODE 0
/* Define this to 1 if you want to declare interrupt-in endpoints, but don't
* want to send any data over them. If this macro is defined to 1, functions
* usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if
* you need the interrupt-in endpoints in order to comply to an interface
* (e.g. HID), but never want to send any data. This option saves a couple
* of bytes in flash memory and the transmit buffers in RAM.
*/
#define USB_CFG_INTR_POLL_INTERVAL 10
#define USB_CFG_INTR_POLL_INTERVAL 20
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll
* interval. The value is in milliseconds and must not be less than 10 ms for
* low speed devices.
@ -146,16 +126,6 @@ section at the end of this file).
* of the macros usbDisableAllRequests() and usbEnableAllRequests() in
* usbdrv.h.
*/
#define USB_CFG_DRIVER_FLASH_PAGE 0
/* If the device has more than 64 kBytes of flash, define this to the 64 k page
* where the driver's constants (descriptors) are located. Or in other words:
* Define this to 1 for boot loaders on the ATMega128.
*/
#define USB_CFG_LONG_TRANSFERS 0
/* Define this to 1 if you want to send/receive blocks of more than 254 bytes
* in a single control-in or control-out transfer. Note that the capability
* for long transfers increases the driver size.
*/
/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */
/* This macro is a hook if you want to do unconventional things. If it is
* defined, it's inserted at the beginning of received message processing.
@ -177,86 +147,47 @@ section at the end of this file).
* counts SOF packets. This feature requires that the hardware interrupt is
* connected to D- instead of D+.
*/
/* #ifdef __ASSEMBLER__
* macro myAssemblerMacro
* in YL, TCNT0
* sts timer0Snapshot, YL
* endm
* #endif
* #define USB_SOF_HOOK myAssemblerMacro
* This macro (if defined) is executed in the assembler module when a
* Start Of Frame condition is detected. It is recommended to define it to
* the name of an assembler macro which is defined here as well so that more
* than one assembler instruction can be used. The macro may use the register
* YL and modify SREG. If it lasts longer than a couple of cycles, USB messages
* immediately after an SOF pulse may be lost and must be retried by the host.
* What can you do with this hook? Since the SOF signal occurs exactly every
* 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in
* designs running on the internal RC oscillator.
* Please note that Start Of Frame detection works only if D- is wired to the
* interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES!
*/
#define USB_CFG_CHECK_DATA_TOGGLING 0
/* define this macro to 1 if you want to filter out duplicate data packets
* sent by the host. Duplicates occur only as a consequence of communication
* errors, when the host does not receive an ACK. Please note that you need to
* implement the filtering yourself in usbFunctionWriteOut() and
* usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable
* for each control- and out-endpoint to check for duplicate packets.
*/
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0
/* define this macro to 1 if you want the function usbMeasureFrameLength()
* compiled in. This function can be used to calibrate the AVR's RC oscillator.
*/
#define USB_USE_FAST_CRC 0
/* The assembler module has two implementations for the CRC algorithm. One is
* faster, the other is smaller. This CRC routine is only used for transmitted
* messages where timing is not critical. The faster routine needs 31 cycles
* per byte while the smaller one needs 61 to 69 cycles. The faster routine
* may be worth the 32 bytes bigger code size if you transmit lots of data and
* run the AVR close to its limit.
*/
/* -------------------------- Device Description --------------------------- */
#define USB_CFG_VENDOR_ID 0xc0, 0x16 /* = 0x16c0 = 5824 = voti.nl */
#define USB_CFG_VENDOR_ID 0xc0, 0x16
/* USB vendor ID for the device, low byte first. If you have registered your
* own Vendor ID, define it here. Otherwise you may use one of obdev's free
* shared VID/PID pairs. Be sure to read USB-IDs-for-free.txt for rules!
* *** IMPORTANT NOTE ***
* This template uses obdev's shared VID/PID pair for Vendor Class devices
* with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand
* the implications!
*/
#define USB_CFG_DEVICE_ID 0xdc, 0x05 /* = 0x05dc = 1500 */
* own Vendor ID, define it here. Otherwise you use obdev's free shared
* VID/PID pair. Be sure to read USBID-License.txt for rules!
* This template uses obdev's shared VID/PID pair for HIDs: 0x16c0/0x5df.
* Use this VID/PID pair ONLY if you understand the implications!
*/
#define USB_CFG_DEVICE_ID 0xdf, 0x05
/* This is the ID of the product, low byte first. It is interpreted in the
* scope of the vendor ID. If you have registered your own VID with usb.org
* or if you have licensed a PID from somebody else, define it here. Otherwise
* you may use one of obdev's free shared VID/PID pairs. See the file
* USB-IDs-for-free.txt for details!
* *** IMPORTANT NOTE ***
* This template uses obdev's shared VID/PID pair for Vendor Class devices
* with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand
* the implications!
* you use obdev's free shared VID/PID pair. Be sure to read the rules in
* USBID-License.txt!
* This template uses obdev's shared VID/PID pair for HIDs: 0x16c0/0x5df.
* Use this VID/PID pair ONLY if you understand the implications!
*/
#define USB_CFG_DEVICE_VERSION 0x00, 0x01
/* Version number of the device: Minor number first, then major number.
*/
#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't'
#define USB_CFG_VENDOR_NAME_LEN 8
#define USB_CFG_VENDOR_NAME 'w', 'w', 'w', '.', 'o', 'b', 'd', 'e', 'v', '.', 'a', 't'
#define USB_CFG_VENDOR_NAME_LEN 12
/* These two values define the vendor name returned by the USB device. The name
* must be given as a list of characters under single quotes. The characters
* are interpreted as Unicode (UTF-16) entities.
* If you don't want a vendor name string, undefine these macros.
* ALWAYS define a vendor name containing your Internet domain name if you use
* obdev's free shared VID/PID pair. See the file USB-IDs-for-free.txt for
* obdev's free shared VID/PID pair. See the file USBID-License.txt for
* details.
*/
#define USB_CFG_DEVICE_NAME 'T', 'e', 'm', 'p', 'l', 'a', 't', 'e'
#define USB_CFG_DEVICE_NAME_LEN 8
/* Same as above for the device name. If you don't want a device name, undefine
* the macros. See the file USB-IDs-for-free.txt before you assign a name if
* you use a shared VID/PID.
* the macros. See the file USBID-License.txt before you assign a name if you
* use a shared VID/PID.
*/
/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */
@ -267,23 +198,23 @@ section at the end of this file).
* to fine tune control over USB descriptors such as the string descriptor
* for the serial number.
*/
#define USB_CFG_DEVICE_CLASS 0xff /* set to 0 if deferred to interface */
#define USB_CFG_DEVICE_CLASS 0
#define USB_CFG_DEVICE_SUBCLASS 0
/* See USB specification if you want to conform to an existing device class.
* Class 0xff is "vendor specific".
*/
#define USB_CFG_INTERFACE_CLASS 0 /* define class here if not at device level */
#define USB_CFG_INTERFACE_CLASS 3 /* HID */
#define USB_CFG_INTERFACE_SUBCLASS 0
#define USB_CFG_INTERFACE_PROTOCOL 0
/* See USB specification if you want to conform to an existing device class or
* protocol. The following classes must be set at interface level:
* HID class is 3, no subclass and protocol required (but may be useful!)
* CDC class is 2, use subclass 2 and protocol 1 for ACM
* protocol.
* This template defines a HID class device. If you implement a vendor class
* device, set USB_CFG_INTERFACE_CLASS to 0 and USB_CFG_DEVICE_CLASS to 0xff.
*/
/* #define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 42 */
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 42 /* total length of report descriptor */
/* Define this to the length of the HID report descriptor, if you implement
* an HID device. Otherwise don't define it or define it to 0.
* If you use this define, you must add a PROGMEM character array named
* Since this template defines a HID device, it must also specify a HID
* report descriptor length. You must add a PROGMEM character array named
* "usbHidReportDescriptor" to your code which contains the report descriptor.
* Don't forget to keep the array and this define in sync!
*/
@ -303,9 +234,7 @@ section at the end of this file).
* no properties are defined or if they are 0, the default descriptor is used.
* Possible properties are:
* + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched
* at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is
* used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if
* you want RAM pointers.
* at runtime via usbFunctionDescriptor().
* + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found
* in static memory is in RAM, not in flash memory.
* + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash),
@ -337,12 +266,6 @@ section at the end of this file).
* USB_CFG_DESCR_PROPS_HID_REPORT
* USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver)
*
* Note about string descriptors: String descriptors are not just strings, they
* are Unicode strings prefixed with a 2 byte header. Example:
* int serialNumberDescriptor[] = {
* USB_STRING_DESCRIPTOR_HEADER(6),
* 'S', 'e', 'r', 'i', 'a', 'l'
* };
*/
#define USB_CFG_DESCR_PROPS_DEVICE 0
@ -371,6 +294,6 @@ section at the end of this file).
/* #define USB_INTR_ENABLE_BIT INT0 */
/* #define USB_INTR_PENDING GIFR */
/* #define USB_INTR_PENDING_BIT INTF0 */
/* #define USB_INTR_VECTOR INT0_vect */
/* #define USB_INTR_VECTOR SIG_INTERRUPT0 */
#endif /* __usbconfig_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,17 +1,17 @@
/* Name: usbdrv.h
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id$
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbdrv.h 536 2008-02-28 21:11:35Z cs $
*/
#ifndef __usbdrv_h_included__
#define __usbdrv_h_included__
#include "usbconfig.h"
#include "usbportability.h"
#include "../usbconfig.h"
#include "iarcompat.h"
/*
Hardware Prerequisites:
@ -34,8 +34,8 @@ usbDeviceConnect() and usbDeviceDisconnect() further down in this file.
Please adapt the values in usbconfig.h according to your hardware!
The device MUST be clocked at exactly 12 MHz, 15 MHz, 16 MHz or 20 MHz
or at 12.8 MHz resp. 16.5 MHz +/- 1%. See usbconfig-prototype.h for details.
The device MUST be clocked at exactly 12 MHz, 15 MHz or 16 MHz
or at 16.5 MHz +/- 1%. See usbconfig-prototype.h for details.
Limitations:
@ -105,9 +105,9 @@ interrupt routine.
Interrupt latency:
The application must ensure that the USB interrupt is not disabled for more
than 25 cycles (this is for 12 MHz, faster clocks allow longer latency).
This implies that all interrupt routines must either have the "ISR_NOBLOCK"
attribute set (see "avr/interrupt.h") or be written in assembler with "sei"
as the first instruction.
This implies that all interrupt routines must either be declared as "INTERRUPT"
instead of "SIGNAL" (see "avr/signal.h") or that they are written in assembler
with "sei" as the first instruction.
Maximum interrupt duration / CPU cycle consumption:
The driver handles all USB communication during the interrupt service
@ -122,7 +122,7 @@ USB messages, even if they address another (low-speed) device on the same bus.
/* --------------------------- Module Interface ---------------------------- */
/* ------------------------------------------------------------------------- */
#define USBDRV_VERSION 20120109
#define USBDRV_VERSION 20080228
/* This define uniquely identifies a driver version. It is a decimal number
* constructed from the driver's release date in the form YYYYMMDD. If the
* driver's behavior or interface changes, you can use this constant to
@ -150,26 +150,11 @@ USB messages, even if they address another (low-speed) device on the same bus.
#endif
/* shortcuts for well defined 8 bit integer types */
#if USB_CFG_LONG_TRANSFERS /* if more than 254 bytes transfer size required */
# define usbMsgLen_t unsigned
#else
# define usbMsgLen_t uchar
#endif
/* usbMsgLen_t is the data type used for transfer lengths. By default, it is
* defined to uchar, allowing a maximum of 254 bytes (255 is reserved for
* USB_NO_MSG below). If the usbconfig.h defines USB_CFG_LONG_TRANSFERS to 1,
* a 16 bit data type is used, allowing up to 16384 bytes (the rest is used
* for flags in the descriptor configuration).
*/
#define USB_NO_MSG ((usbMsgLen_t)-1) /* constant meaning "no message" */
struct usbRequest; /* forward declaration */
USB_PUBLIC void usbInit(void);
/* This function must be called before interrupts are enabled and the main
* loop is entered. We exepct that the PORT and DDR bits for D+ and D- have
* not been changed from their default status (which is 0). If you have changed
* them, set both back to 0 (configure them as input with no internal pull-up).
* loop is entered.
*/
USB_PUBLIC void usbPoll(void);
/* This function must be called at regular intervals from the main loop.
@ -183,7 +168,7 @@ extern uchar *usbMsgPtr;
* implementation of usbFunctionWrite(). It is also used internally by the
* driver for standard control requests.
*/
USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]);
USB_PUBLIC uchar usbFunctionSetup(uchar data[8]);
/* This function is called when the driver receives a SETUP transaction from
* the host which is not answered by the driver itself (in practice: class and
* vendor requests). All control transfers start with a SETUP transaction where
@ -196,21 +181,21 @@ USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]);
* requested data to the driver. There are two ways to transfer this data:
* (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data
* block and return the length of the data in 'usbFunctionSetup()'. The driver
* will handle the rest. Or (2) return USB_NO_MSG in 'usbFunctionSetup()'. The
* driver will then call 'usbFunctionRead()' when data is needed. See the
* will handle the rest. Or (2) return 0xff in 'usbFunctionSetup()'. The driver
* will then call 'usbFunctionRead()' when data is needed. See the
* documentation for usbFunctionRead() for details.
*
* If the SETUP indicates a control-out transfer, the only way to receive the
* data from the host is through the 'usbFunctionWrite()' call. If you
* implement this function, you must return USB_NO_MSG in 'usbFunctionSetup()'
* to indicate that 'usbFunctionWrite()' should be used. See the documentation
* of this function for more information. If you just want to ignore the data
* sent by the host, return 0 in 'usbFunctionSetup()'.
* implement this function, you must return 0xff in 'usbFunctionSetup()' to
* indicate that 'usbFunctionWrite()' should be used. See the documentation of
* this function for more information. If you just want to ignore the data sent
* by the host, return 0 in 'usbFunctionSetup()'.
*
* Note that calls to the functions usbFunctionRead() and usbFunctionWrite()
* are only done if enabled by the configuration in usbconfig.h.
*/
USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq);
USB_PUBLIC uchar usbFunctionDescriptor(struct usbRequest *rq);
/* You need to implement this function ONLY if you provide USB descriptors at
* runtime (which is an expert feature). It is very similar to
* usbFunctionSetup() above, but it is called only to request USB descriptor
@ -224,6 +209,7 @@ USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len);
* interrupt status to the host.
* If you need to transfer more bytes, use a control read after the interrupt.
*/
extern volatile uchar usbTxLen1;
#define usbInterruptIsReady() (usbTxLen1 & 0x10)
/* This macro indicates whether the last interrupt message has already been
* sent. If you set a new interrupt message before the old was sent, the
@ -231,6 +217,7 @@ USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len);
*/
#if USB_CFG_HAVE_INTRIN_ENDPOINT3
USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len);
extern volatile uchar usbTxLen3;
#define usbInterruptIsReady3() (usbTxLen3 & 0x10)
/* Same as above for endpoint 3 */
#endif
@ -275,14 +262,11 @@ USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len);
* to 1 in usbconfig.h and return 0xff in usbFunctionSetup()..
*/
#endif /* USB_CFG_IMPLEMENT_FN_READ */
extern uchar usbRxToken; /* may be used in usbFunctionWriteOut() below */
#if USB_CFG_IMPLEMENT_FN_WRITEOUT
USB_PUBLIC void usbFunctionWriteOut(uchar *data, uchar len);
/* This function is called by the driver when data is received on an interrupt-
* or bulk-out endpoint. The endpoint number can be found in the global
* variable usbRxToken. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT to 1 in
* usbconfig.h to get this function called.
/* This function is called by the driver when data on interrupt-out or bulk-
* out endpoint 1 is received. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT
* to 1 in usbconfig.h to get this function called.
*/
#endif /* USB_CFG_IMPLEMENT_FN_WRITEOUT */
#ifdef USB_CFG_PULLUP_IOPORTNAME
@ -343,12 +327,6 @@ extern volatile uchar usbSofCount;
* the macro USB_COUNT_SOF is defined to a value != 0.
*/
#endif
#if USB_CFG_CHECK_DATA_TOGGLING
extern uchar usbCurrentDataToken;
/* This variable can be checked in usbFunctionWrite() and usbFunctionWriteOut()
* to ignore duplicate packets.
*/
#endif
#define USB_STRING_DESCRIPTOR_HEADER(stringLength) ((2*(stringLength)+2) | (3<<8))
/* This macro builds a descriptor header for a string descriptor given the
@ -375,9 +353,7 @@ extern volatile schar usbRxLen;
#define USB_SET_DATATOKEN1(token) usbTxBuf1[0] = token
#define USB_SET_DATATOKEN3(token) usbTxBuf3[0] = token
/* These two macros can be used by application software to reset data toggling
* for interrupt-in endpoints 1 and 3. Since the token is toggled BEFORE
* sending data, you must set the opposite value of the token which should come
* first.
* for interrupt-in endpoints 1 and 3.
*/
#endif /* __ASSEMBLER__ */
@ -390,18 +366,16 @@ extern volatile schar usbRxLen;
* about the various methods to define USB descriptors. If you do nothing,
* the default descriptors will be used.
*/
#define USB_PROP_IS_DYNAMIC (1 << 14)
#define USB_PROP_IS_DYNAMIC (1 << 8)
/* If this property is set for a descriptor, usbFunctionDescriptor() will be
* used to obtain the particular descriptor. Data directly returned via
* usbMsgPtr are FLASH data by default, combine (OR) with USB_PROP_IS_RAM to
* return RAM data.
* used to obtain the particular descriptor.
*/
#define USB_PROP_IS_RAM (1 << 15)
#define USB_PROP_IS_RAM (1 << 9)
/* If this property is set for a descriptor, the data is read from RAM
* memory instead of Flash. The property is used for all methods to provide
* external descriptors.
*/
#define USB_PROP_LENGTH(len) ((len) & 0x3fff)
#define USB_PROP_LENGTH(len) ((len) & 0xff)
/* If a static external descriptor is used, this is the total length of the
* descriptor in bytes.
*/
@ -513,22 +487,22 @@ const int usbDescriptorStringSerialNumber[];
#if !defined __ASSEMBLER__ && (!defined USB_CFG_VENDOR_ID || !defined USB_CFG_DEVICE_ID)
#warning "You should define USB_CFG_VENDOR_ID and USB_CFG_DEVICE_ID in usbconfig.h"
/* If the user has not defined IDs, we default to obdev's free IDs.
* See USB-IDs-for-free.txt for details.
* See USBID-License.txt for details.
*/
#endif
/* make sure we have a VID and PID defined, byte order is lowbyte, highbyte */
#ifndef USB_CFG_VENDOR_ID
# define USB_CFG_VENDOR_ID 0xc0, 0x16 /* = 0x16c0 = 5824 = voti.nl */
# define USB_CFG_VENDOR_ID 0xc0, 0x16 /* 5824 in dec, stands for VOTI */
#endif
#ifndef USB_CFG_DEVICE_ID
# if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH
# define USB_CFG_DEVICE_ID 0xdf, 0x05 /* = 0x5df = 1503, shared PID for HIDs */
# define USB_CFG_DEVICE_ID 0xdf, 0x05 /* 1503 in dec, shared PID for HIDs */
# elif USB_CFG_INTERFACE_CLASS == 2
# define USB_CFG_DEVICE_ID 0xe1, 0x05 /* = 0x5e1 = 1505, shared PID for CDC Modems */
# define USB_CFG_DEVICE_ID 0xe1, 0x05 /* 1505 in dec, shared PID for CDC Modems */
# else
# define USB_CFG_DEVICE_ID 0xdc, 0x05 /* = 0x5dc = 1500, obdev's free PID */
# define USB_CFG_DEVICE_ID 0xdc, 0x05 /* 1500 in dec, obdev's free PID */
# endif
#endif
@ -558,10 +532,6 @@ const int usbDescriptorStringSerialNumber[];
#define USB_CFG_EP3_NUMBER 3
#endif
#ifndef USB_CFG_HAVE_INTRIN_ENDPOINT3
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0
#endif
#define USB_BUFSIZE 11 /* PID, 8 bytes data, 2 bytes CRC */
/* ----- Try to find registers and bits responsible for ext interrupt 0 ----- */
@ -574,14 +544,7 @@ const int usbDescriptorStringSerialNumber[];
# endif
#endif
#ifndef USB_INTR_CFG_SET /* allow user to override our default */
# if defined(USB_COUNT_SOF) || defined(USB_SOF_HOOK)
# define USB_INTR_CFG_SET (1 << ISC01) /* cfg for falling edge */
/* If any SOF logic is used, the interrupt must be wired to D- where
* we better trigger on falling edge
*/
# else
# define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) /* cfg for rising edge */
# endif
# define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) /* cfg for rising edge */
#endif
#ifndef USB_INTR_CFG_CLR /* allow user to override our default */
# define USB_INTR_CFG_CLR 0 /* no bits to clear */
@ -639,22 +602,12 @@ at90s1200, attiny11, attiny12, attiny15, attiny28: these have no RAM
#define USBPID_STALL 0x1e
#ifndef USB_INITIAL_DATATOKEN
#define USB_INITIAL_DATATOKEN USBPID_DATA1
#define USB_INITIAL_DATATOKEN USBPID_DATA0
#endif
#ifndef __ASSEMBLER__
typedef struct usbTxStatus{
volatile uchar len;
uchar buffer[USB_BUFSIZE];
}usbTxStatus_t;
extern usbTxStatus_t usbTxStatus1, usbTxStatus3;
#define usbTxLen1 usbTxStatus1.len
#define usbTxBuf1 usbTxStatus1.buffer
#define usbTxLen3 usbTxStatus3.len
#define usbTxBuf3 usbTxStatus3.buffer
extern uchar usbTxBuf1[USB_BUFSIZE], usbTxBuf3[USB_BUFSIZE];
typedef union usbWord{
unsigned word;
@ -718,8 +671,7 @@ typedef struct usbRequest{
#define USBDESCR_HID_REPORT 0x22
#define USBDESCR_HID_PHYS 0x23
//#define USBATTR_BUSPOWER 0x80 // USB 1.1 does not define this value any more
#define USBATTR_BUSPOWER 0
#define USBATTR_BUSPOWER 0x80
#define USBATTR_SELFPOWER 0x40
#define USBATTR_REMOTEWAKE 0x20

@ -1,10 +1,10 @@
/* Name: usbdrvasm.S
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Project: AVR USB driver
* Author: Christian Starkjohann
* Creation Date: 2007-06-13
* Tabsize: 4
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* Revision: $Id$
*/
@ -15,9 +15,16 @@ general code (preprocessor acrobatics and CRC computation) and then includes
the file appropriate for the given clock rate.
*/
#define __SFR_OFFSET 0 /* used by avr-libc's register definitions */
#include "usbportability.h"
#include "usbdrv.h" /* for common defs */
#include "iarcompat.h"
#ifndef __IAR_SYSTEMS_ASM__
/* configs for io.h */
# define __SFR_OFFSET 0
# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */
# include <avr/io.h> /* for CPU I/O register definitions and vectors */
# define macro .macro /* GNU Assembler macro definition */
# define endm .endm /* End of GNU Assembler macro definition */
#endif /* __IAR_SYSTEMS_ASM__ */
#include "usbdrv.h" /* for common defs */
/* register names */
#define x1 r16
@ -26,17 +33,27 @@ the file appropriate for the given clock rate.
#define cnt r19
#define x3 r20
#define x4 r21
#define x5 r22
#define bitcnt x5
#define bitcnt r22
#define phase x4
#define leap x4
/* Some assembler dependent definitions and declarations: */
#ifdef __IAR_SYSTEMS_ASM__
# 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 */
extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset
extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen
extern usbTxBuf, usbTxStatus1, usbTxStatus3
extern usbTxBuf, usbMsgLen, usbTxLen1, usbTxBuf1, usbTxLen3, usbTxBuf3
# if USB_COUNT_SOF
extern usbSofCount
# endif
@ -56,12 +73,10 @@ the file appropriate for the given clock rate.
#else /* __IAR_SYSTEMS_ASM__ */
# define nop2 rjmp .+0 /* jump to next instruction */
# ifndef USB_INTR_VECTOR /* default to hardware interrupt INT0 */
# ifdef INT0_vect
# define USB_INTR_VECTOR INT0_vect // this is the "new" define for the vector
# else
# define USB_INTR_VECTOR SIG_INTERRUPT0 // this is the "old" vector
# endif
# define USB_INTR_VECTOR SIG_INTERRUPT0
# endif
.text
.global USB_INTR_VECTOR
@ -79,11 +94,6 @@ the file appropriate for the given clock rate.
# define USB_STORE_PENDING(reg) sts USB_INTR_PENDING, reg
#endif
#define usbTxLen1 usbTxStatus1
#define usbTxBuf1 (usbTxStatus1 + 1)
#define usbTxLen3 usbTxStatus3
#define usbTxBuf3 (usbTxStatus3 + 1)
;----------------------------------------------------------------------------
; Utility functions
@ -143,93 +153,16 @@ RTMODEL "__rt_version", "3"
#endif
#if USB_USE_FAST_CRC
; This implementation is faster, but has bigger code size
; Thanks to Slawomir Fras (BoskiDialer) for this code!
; It implements the following C pseudo-code:
; unsigned table(unsigned char x)
; {
; unsigned value;
;
; value = (unsigned)x << 6;
; value ^= (unsigned)x << 7;
; if(parity(x))
; value ^= 0xc001;
; return value;
; }
; unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen)
; {
; unsigned crc = 0xffff;
;
; while(argLen--)
; crc = table(lo8(crc) ^ *argPtr++) ^ hi8(crc);
; return ~crc;
; }
; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen);
; argPtr r24+25 / r16+r17
; argLen r22 / r18
; extern unsigned usbCrc16(unsigned char *data, unsigned char len);
; data: r24/25
; len: r22
; temp variables:
; byte r18 / r22
; scratch r23
; resCrc r24+r25 / r16+r17
; ptr X / Z
usbCrc16:
mov ptrL, argPtrL
mov ptrH, argPtrH
ldi resCrcL, 0xFF
ldi resCrcH, 0xFF
rjmp usbCrc16LoopTest
usbCrc16ByteLoop:
ld byte, ptr+
eor resCrcL, byte ; resCrcL is now 'x' in table()
mov byte, resCrcL ; compute parity of 'x'
swap byte
eor byte, resCrcL
mov scratch, byte
lsr byte
lsr byte
eor byte, scratch
inc byte
lsr byte
andi byte, 1 ; byte is now parity(x)
mov scratch, resCrcL
mov resCrcL, resCrcH
eor resCrcL, byte ; low byte of if(parity(x)) value ^= 0xc001;
neg byte
andi byte, 0xc0
mov resCrcH, byte ; high byte of if(parity(x)) value ^= 0xc001;
clr byte
lsr scratch
ror byte
eor resCrcH, scratch
eor resCrcL, byte
lsr scratch
ror byte
eor resCrcH, scratch
eor resCrcL, byte
usbCrc16LoopTest:
subi argLen, 1
brsh usbCrc16ByteLoop
com resCrcL
com resCrcH
ret
#else /* USB_USE_FAST_CRC */
; This implementation is slower, but has less code size
;
; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen);
; argPtr r24+25 / r16+r17
; argLen r22 / r18
; temp variables:
; byte r18 / r22
; bitCnt r19
; poly r20+r21
; scratch r23
; resCrc r24+r25 / r16+r17
; ptr X / Z
; r18: data byte
; r19: bit counter
; r20/21: polynomial
; r23: scratch
; r24/25: crc-sum
; r26/27=X: ptr
usbCrc16:
mov ptrL, argPtrL
mov ptrH, argPtrH
@ -237,30 +170,27 @@ usbCrc16:
ldi resCrcH, 0
ldi polyL, lo8(0xa001)
ldi polyH, hi8(0xa001)
com argLen ; argLen = -argLen - 1: modified loop to ensure that carry is set
ldi bitCnt, 0 ; loop counter with starnd condition = end condition
rjmp usbCrcLoopEntry
usbCrcByteLoop:
com argLen ; argLen = -argLen - 1
crcByteLoop:
subi argLen, -1
brcc crcReady ; modified loop to ensure that carry is set below
ld byte, ptr+
ldi bitCnt, -8 ; strange loop counter to ensure that carry is set where we need it
eor resCrcL, byte
usbCrcBitLoop:
ror resCrcH ; carry is always set here (see brcs jumps to here)
crcBitLoop:
ror resCrcH ; carry is always set here
ror resCrcL
brcs usbCrcNoXor
brcs crcNoXor
eor resCrcL, polyL
eor resCrcH, polyH
usbCrcNoXor:
subi bitCnt, 224 ; (8 * 224) % 256 = 0; this loop iterates 8 times
brcs usbCrcBitLoop
usbCrcLoopEntry:
subi argLen, -1
brcs usbCrcByteLoop
usbCrcReady:
crcNoXor:
subi bitCnt, -1
brcs crcBitLoop
rjmp crcByteLoop
crcReady:
ret
; Thanks to Reimar Doeffinger for optimizing this CRC routine!
#endif /* USB_USE_FAST_CRC */
; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len);
usbCrc16Append:
rcall usbCrc16
@ -361,33 +291,17 @@ usbMFTimeout:
;----------------------------------------------------------------------------
#ifndef USB_CFG_CLOCK_KHZ
# ifdef F_CPU
# define USB_CFG_CLOCK_KHZ (F_CPU/1000)
# else
# error "USB_CFG_CLOCK_KHZ not defined in usbconfig.h and no F_CPU set!"
# endif
# define USB_CFG_CLOCK_KHZ 12000
#endif
#if USB_CFG_CHECK_CRC /* separate dispatcher for CRC type modules */
# if USB_CFG_CLOCK_KHZ == 18000
# include "usbdrvasm18-crc.inc"
# else
# error "USB_CFG_CLOCK_KHZ is not one of the supported crc-rates!"
# endif
#else /* USB_CFG_CHECK_CRC */
# if USB_CFG_CLOCK_KHZ == 12000
# include "usbdrvasm12.inc"
# elif USB_CFG_CLOCK_KHZ == 12800
# include "usbdrvasm128.inc"
# elif USB_CFG_CLOCK_KHZ == 15000
# include "usbdrvasm15.inc"
# elif USB_CFG_CLOCK_KHZ == 16000
# include "usbdrvasm16.inc"
# elif USB_CFG_CLOCK_KHZ == 16500
# include "usbdrvasm165.inc"
# elif USB_CFG_CLOCK_KHZ == 20000
# include "usbdrvasm20.inc"
# else
# error "USB_CFG_CLOCK_KHZ is not one of the supported non-crc-rates!"
# endif
#endif /* USB_CFG_CHECK_CRC */
#if USB_CFG_CLOCK_KHZ == 12000
# include "usbdrvasm12.inc"
#elif USB_CFG_CLOCK_KHZ == 15000
# include "usbdrvasm15.inc"
#elif USB_CFG_CLOCK_KHZ == 16000
# include "usbdrvasm16.inc"
#elif USB_CFG_CLOCK_KHZ == 16500
# include "usbdrvasm165.inc"
#else
# error "USB_CFG_CLOCK_KHZ is not one of the supported rates!"
#endif

@ -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,11 +1,11 @@
/* Name: usbdrvasm12.inc
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Project: AVR USB driver
* Author: Christian Starkjohann
* Creation Date: 2004-12-29
* Tabsize: 4
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id$
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbdrvasm12.inc 483 2008-02-05 15:05:32Z cs $
*/
/* Do not link this file! Link usbdrvasm.S instead, which includes the
@ -48,13 +48,10 @@ USB_INTR_VECTOR:
;----------------------------------------------------------------------------
;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.
;first part has no timeout because it waits for IDLE or SE1 (== disconnected)
waitForJ:
inc YL
sbis USBIN, USBMINUS
brne waitForJ ; just make sure we have ANY timeout
sbis USBIN, USBMINUS ;1 [40] wait for D- == 1
rjmp waitForJ ;2
waitForK:
;The following code results in a sampling window of 1/4 bit which meets the spec.
sbis USBIN, USBMINUS
@ -72,9 +69,6 @@ waitForK:
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]
@ -256,12 +250,12 @@ macro POP_STANDARD ; 12 cycles
pop x1
pop shift
pop YH
endm
endm
macro POP_RETI ; 5 cycles
pop YL
out SREG, YL
pop YL
endm
endm
#include "asmcommon.inc"
@ -269,16 +263,25 @@ macro POP_RETI ; 5 cycles
; Transmitting data
;----------------------------------------------------------------------------
txByteLoop:
txBitloop:
stuffN1Delay: ; [03]
ror shift ;[-5] [11] [59]
brcc doExorN1 ;[-4] [60]
subi x4, 1 ;[-3]
brne commonN1 ;[-2]
lsl shift ;[-1] compensate ror after rjmp stuffDelay
nop ;[00] stuffing consists of just waiting 8 cycles
rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear
bitstuff0: ;1 (for branch taken)
eor x1, x4 ;1
ldi x2, 0 ;1
out USBOUT, x1 ;1 <-- out
rjmp didStuff0 ;2 branch back 2 cycles earlier
bitstuff1: ;1 (for branch taken)
eor x1, x4 ;1
rjmp didStuff1 ;2 we know that C is clear, jump back to do OUT and ror 0 into x2
bitstuff2: ;1 (for branch taken)
eor x1, x4 ;1
rjmp didStuff2 ;2 jump back 4 cycles earlier and do out and ror 0 into x2
bitstuff3: ;1 (for branch taken)
eor x1, x4 ;1
rjmp didStuff3 ;2 jump back earlier and ror 0 into x2
bitstuff4: ;1 (for branch taken)
eor x1, x4 ;1
ldi x2, 0 ;1
out USBOUT, x1 ;1 <-- out
rjmp didStuff4 ;2 jump back 2 cycles earlier
sendNakAndReti: ;0 [-19] 19 cycles until SOP
ldi x3, USBPID_NAK ;1 [-18]
@ -303,91 +306,122 @@ usbSendX3: ;0 [-16]
;usbSend:
;pointer to data in 'Y'
;number of bytes in 'cnt' -- including sync byte
;uses: x1...x2, x4, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x4 = bitstuff cnt]
;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction)
usbSendAndReti:
in x2, USBDDR ;[-12] 12 cycles until SOP
ori x2, USBMASK ;[-11]
sbi USBOUT, USBMINUS ;[-10] prepare idle state; D+ and D- must have been 0 (no pullups)
out USBDDR, x2 ;[-8] <--- acquire bus
in x1, USBOUT ;[-7] port mirror for tx loop
ldi shift, 0x40 ;[-6] sync byte is first byte sent (we enter loop after ror)
ldi x2, USBMASK ;[-5]
push x4 ;[-4]
doExorN1:
eor x1, x2 ;[-2] [06] [62]
ldi x4, 6 ;[-1] [07] [63]
commonN1:
stuffN2Delay:
out USBOUT, x1 ;[00] [08] [64] <--- set bit
ror shift ;[01]
brcc doExorN2 ;[02]
subi x4, 1 ;[03]
brne commonN2 ;[04]
lsl shift ;[05] compensate ror after rjmp stuffDelay
rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear
doExorN2:
eor x1, x2 ;[04] [12]
ldi x4, 6 ;[05] [13]
commonN2:
nop ;[06] [14]
subi cnt, 171 ;[07] [15] trick: (3 * 171) & 0xff = 1
out USBOUT, x1 ;[08] [16] <--- set bit
brcs txBitloop ;[09] [25] [41]
stuff6Delay:
ror shift ;[42] [50]
brcc doExor6 ;[43]
subi x4, 1 ;[44]
brne common6 ;[45]
lsl shift ;[46] compensate ror after rjmp stuffDelay
nop ;[47] stuffing consists of just waiting 8 cycles
rjmp stuff6Delay ;[48] after ror, C bit is reliably clear
doExor6:
eor x1, x2 ;[45] [53]
ldi x4, 6 ;[46]
common6:
stuff7Delay:
ror shift ;[47] [55]
out USBOUT, x1 ;[48] <--- set bit
brcc doExor7 ;[49]
subi x4, 1 ;[50]
brne common7 ;[51]
lsl shift ;[52] compensate ror after rjmp stuffDelay
rjmp stuff7Delay ;[53] after ror, C bit is reliably clear
doExor7:
eor x1, x2 ;[51] [59]
ldi x4, 6 ;[52]
common7:
ld shift, y+ ;[53]
tst cnt ;[55]
out USBOUT, x1 ;[56] <--- set bit
brne txByteLoop ;[57]
;uses: x1...x4, shift, cnt, Y
;Numbers in brackets are time since first bit of sync pattern is sent
usbSendAndReti: ;0 [-13] timing: 13 cycles until SOP
in x2, USBDDR ;1 [-12]
ori x2, USBMASK ;1 [-11]
sbi USBOUT, USBMINUS;2 [-9] prepare idle state; D+ and D- must have been 0 (no pullups)
in x1, USBOUT ;1 [-8] port mirror for tx loop
out USBDDR, x2 ;1 [-7] <- acquire bus
; need not init x2 (bitstuff history) because sync starts with 0
push x4 ;2 [-5]
ldi x4, USBMASK ;1 [-4] exor mask
ldi shift, 0x80 ;1 [-3] sync byte is first byte sent
txLoop: ; [62]
sbrs shift, 0 ;1 [-2] [62]
eor x1, x4 ;1 [-1] [63]
out USBOUT, x1 ;1 [0] <-- out bit 0
ror shift ;1 [1]
ror x2 ;1 [2]
didStuff0:
cpi x2, 0xfc ;1 [3]
brsh bitstuff0 ;1 [4]
sbrs shift, 0 ;1 [5]
eor x1, x4 ;1 [6]
ror shift ;1 [7]
didStuff1:
out USBOUT, x1 ;1 [8] <-- out bit 1
ror x2 ;1 [9]
cpi x2, 0xfc ;1 [10]
brsh bitstuff1 ;1 [11]
sbrs shift, 0 ;1 [12]
eor x1, x4 ;1 [13]
ror shift ;1 [14]
didStuff2:
ror x2 ;1 [15]
out USBOUT, x1 ;1 [16] <-- out bit 2
cpi x2, 0xfc ;1 [17]
brsh bitstuff2 ;1 [18]
sbrs shift, 0 ;1 [19]
eor x1, x4 ;1 [20]
ror shift ;1 [21]
didStuff3:
ror x2 ;1 [22]
cpi x2, 0xfc ;1 [23]
out USBOUT, x1 ;1 [24] <-- out bit 3
brsh bitstuff3 ;1 [25]
nop2 ;2 [27]
ld x3, y+ ;2 [29]
sbrs shift, 0 ;1 [30]
eor x1, x4 ;1 [31]
out USBOUT, x1 ;1 [32] <-- out bit 4
ror shift ;1 [33]
ror x2 ;1 [34]
didStuff4:
cpi x2, 0xfc ;1 [35]
brsh bitstuff4 ;1 [36]
sbrs shift, 0 ;1 [37]
eor x1, x4 ;1 [38]
ror shift ;1 [39]
didStuff5:
out USBOUT, x1 ;1 [40] <-- out bit 5
ror x2 ;1 [41]
cpi x2, 0xfc ;1 [42]
brsh bitstuff5 ;1 [43]
sbrs shift, 0 ;1 [44]
eor x1, x4 ;1 [45]
ror shift ;1 [46]
didStuff6:
ror x2 ;1 [47]
out USBOUT, x1 ;1 [48] <-- out bit 6
cpi x2, 0xfc ;1 [49]
brsh bitstuff6 ;1 [50]
sbrs shift, 0 ;1 [51]
eor x1, x4 ;1 [52]
ror shift ;1 [53]
didStuff7:
ror x2 ;1 [54]
cpi x2, 0xfc ;1 [55]
out USBOUT, x1 ;1 [56] <-- out bit 7
brsh bitstuff7 ;1 [57]
mov shift, x3 ;1 [58]
dec cnt ;1 [59]
brne txLoop ;1/2 [60/61]
;make SE0:
cbr x1, USBMASK ;[58] prepare SE0 [spec says EOP may be 15 to 18 cycles]
lds x2, usbNewDeviceAddr;[59]
lsl x2 ;[61] we compare with left shifted address
subi YL, 2 + 20 ;[62] Only assign address on data packets, not ACK/NAK in x3
sbci YH, 0 ;[63]
out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle
cbr x1, USBMASK ;1 [61] prepare SE0 [spec says EOP may be 15 to 18 cycles]
pop x4 ;2 [63]
;brackets are cycles from start of SE0 now
out USBOUT, x1 ;1 [0] <-- 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
lds x2, usbNewDeviceAddr;2 [2]
lsl x2; ;1 [3] we compare with left shifted address
subi YL, 20 + 2 ;1 [4] Only assign address on data packets, not ACK/NAK in x3
sbci YH, 0 ;1 [5]
breq skipAddrAssign ;2 [7]
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
pop x4 ;[10]
nop2 ;[12]
nop2 ;[14]
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
ldi x2, 1<<USB_INTR_PENDING_BIT;1 [8] int0 occurred during TX -- clear pending flag
USB_STORE_PENDING(x2) ;1 [9]
ori x1, USBIDLE ;1 [10]
in x2, USBDDR ;1 [11]
cbr x2, USBMASK ;1 [12] set both pins to input
mov x3, x1 ;1 [13]
cbr x3, USBMASK ;1 [14] configure no pullup on both pins
out USBOUT, x1 ;1 [15] <-- out J (idle) -- end of SE0 (EOP signal)
out USBDDR, x2 ;1 [16] <-- release bus now
out USBOUT, x3 ;1 [17] <-- ensure no pull-up resistors are active
rjmp doReturn
bitstuff5: ;1 (for branch taken)
eor x1, x4 ;1
rjmp didStuff5 ;2 same trick as in bitstuff1...
bitstuff6: ;1 (for branch taken)
eor x1, x4 ;1
rjmp didStuff6 ;2 same trick as above...
bitstuff7: ;1 (for branch taken)
eor x1, x4 ;1
rjmp didStuff7 ;2 same trick as above...

@ -1,10 +1,10 @@
/* Name: usbdrvasm15.inc
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Project: AVR USB driver
* Author: contributed by V. Bosch
* Creation Date: 2007-08-06
* Tabsize: 4
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* Revision: $Id$
*/
@ -43,13 +43,11 @@ USB_INTR_VECTOR:
;
; 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
; first part has no timeout because it waits for IDLE or SE1 (== disconnected)
;-------------------------------------------------------------------------------
waitForJ: ;-
sbis USBIN, USBMINUS ;1 <-- sample: wait for D- == 1
rjmp waitForJ ;2
;-------------------------------------------------------------------------------
; The following code results in a sampling window of < 1/4 bit
; which meets the spec.
@ -72,9 +70,6 @@ waitForK: ;-
inc YL
sts usbSofCount, YL
#endif /* USB_COUNT_SOF */
#ifdef USB_SOF_HOOK
USB_SOF_HOOK
#endif
rjmp sofError
;------------------------------------------------------------------------------
; {3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for
@ -294,12 +289,12 @@ macro POP_STANDARD ; 16 cycles
pop x1
pop shift
pop YH
endm
endm
macro POP_RETI ; 5 cycles
pop YL
out SREG, YL
pop YL
endm
endm
#include "asmcommon.inc"

@ -1,10 +1,10 @@
/* Name: usbdrvasm16.inc
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Project: AVR USB driver
* Author: Christian Starkjohann
* Creation Date: 2007-06-15
* Tabsize: 4
* Copyright: (c) 2007 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)
* Revision: $Id$
*/
@ -41,13 +41,10 @@ USB_INTR_VECTOR:
;----------------------------------------------------------------------------
;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.
;first part has no timeout because it waits for IDLE or SE1 (== disconnected)
waitForJ:
inc YL
sbis USBIN, USBMINUS
brne waitForJ ; just make sure we have ANY timeout
sbis USBIN, USBMINUS ;[-18] wait for D- == 1
rjmp waitForJ
waitForK:
;The following code results in a sampling window of < 1/4 bit which meets the spec.
sbis USBIN, USBMINUS ;[-15]
@ -67,9 +64,6 @@ waitForK:
inc YL
sts usbSofCount, YL
#endif /* USB_COUNT_SOF */
#ifdef USB_SOF_HOOK
USB_SOF_HOOK
#endif
rjmp sofError
foundK: ;[-12]
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling]
@ -117,15 +111,12 @@ haveTwoBitsK:
; Receiver loop (numbers in brackets are cycles within byte after instr)
;----------------------------------------------------------------------------
; duration of unstuffing code should be 10.66666667 cycles. We adjust "leap"
; accordingly to approximate this value in the long run.
unstuff6:
andi x2, USBMASK ;[03]
ori x3, 1<<6 ;[04] will not be shifted any more
andi shift, ~0x80;[05]
mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6
subi leap, -1 ;[07] total duration = 11 bits -> subtract 1/3
subi leap, 3 ;[07] since this is a short (10 cycle) bit, enforce leap bit
rjmp didUnstuff6 ;[08]
unstuff7:
@ -133,7 +124,7 @@ unstuff7:
in x2, USBIN ;[00] [10] re-sample bit 7
andi x2, USBMASK ;[01]
andi shift, ~0x80;[02]
subi leap, 2 ;[03] total duration = 10 bits -> add 1/3
subi leap, 3 ;[03] since this is a short (10 cycle) bit, enforce leap bit
rjmp didUnstuff7 ;[04]
unstuffEven:
@ -142,8 +133,8 @@ unstuffEven:
andi shift, ~0x80;[01]
andi x1, USBMASK ;[02]
breq se0 ;[03]
subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3
nop2 ;[05]
subi leap, 3 ;[04] since this is a short (10 cycle) bit, enforce leap bit
nop ;[05]
rjmp didUnstuffE ;[06]
unstuffOdd:
@ -152,8 +143,8 @@ unstuffOdd:
andi shift, ~0x80;[01]
andi x2, USBMASK ;[02]
breq se0 ;[03]
subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3
nop2 ;[05]
subi leap, 3 ;[04] since this is a short (10 cycle) bit, enforce leap bit
nop ;[05]
rjmp didUnstuffO ;[06]
rxByteLoop:
@ -217,13 +208,13 @@ macro POP_STANDARD ; 14 cycles
pop x1
pop shift
pop bitcnt
endm
endm
macro POP_RETI ; 7 cycles
pop YH
pop YL
out SREG, YL
pop YL
endm
endm
#include "asmcommon.inc"

@ -1,10 +1,10 @@
/* Name: usbdrvasm165.inc
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Project: AVR USB driver
* Author: Christian Starkjohann
* Creation Date: 2007-04-22
* Tabsize: 4
* Copyright: (c) 2007 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)
* Revision: $Id$
*/
@ -46,13 +46,10 @@ USB_INTR_VECTOR:
;----------------------------------------------------------------------------
;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.
;first part has no timeout because it waits for IDLE or SE1 (== disconnected)
waitForJ:
inc YL
sbis USBIN, USBMINUS
brne waitForJ ; just make sure we have ANY timeout
sbis USBIN, USBMINUS ;[-18] wait for D- == 1
rjmp waitForJ
waitForK:
;The following code results in a sampling window of < 1/4 bit which meets the spec.
sbis USBIN, USBMINUS ;[-15]
@ -72,9 +69,6 @@ waitForK:
inc YL
sts usbSofCount, YL
#endif /* USB_COUNT_SOF */
#ifdef USB_SOF_HOOK
USB_SOF_HOOK
#endif
rjmp sofError
foundK: ;[-12]
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling]
@ -340,12 +334,12 @@ macro POP_STANDARD ; 16 cycles
pop shift
pop YH
pop r0
endm
endm
macro POP_RETI ; 5 cycles
pop YL
out SREG, YL
pop YL
endm
endm
#include "asmcommon.inc"

3
usbdrv/.gitignore vendored

@ -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…
Cancel
Save