/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/common.mk
0,0 → 1,61
# ======================================================================
# Common Makefile for USBtiny applications
#
# Macros to be defined before including this file:
#
# USBTINY - the location of this directory
# TARGET_ARCH - gcc -mmcu= option with AVR device type
# OBJECTS - the objects in addition to the USBtiny objects
# FLASH_CMD - command to upload main.hex to flash
# STACK - maximum stack size (optional)
# FLASH - flash size (optional)
# SRAM - SRAM size (optional)
# SCHEM - Postscript version of the schematic to be generated
#
# Copyright (C) 2006 Dick Streefland
#
# This is free software, licensed under the terms of the GNU General
# Public License as published by the Free Software Foundation.
# ======================================================================
 
CC = avr-gcc
CFLAGS = -Os -g -Wall -I. -I$(USBTINY)
ASFLAGS = -Os -g -Wall -I.
LDFLAGS = -g
MODULES = crc.o int.o usb.o $(OBJECTS)
UTIL = $(USBTINY)/../util
 
main.hex:
 
all: main.hex $(SCHEM)
 
clean:
rm -f main.elf *.o tags *.sch~ gschem.log
 
clobber: clean
rm -f main.hex $(SCHEM)
 
main.elf: $(MODULES)
$(LINK.o) -o $@ $(MODULES)
 
main.hex: main.elf $(UTIL)/check.py
@python $(UTIL)/check.py main.elf $(STACK) $(FLASH) $(SRAM)
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
 
disasm: main.elf
avr-objdump -S main.elf
 
flash: main.hex
$(FLASH_CMD)
 
crc.o: $(USBTINY)/crc.S $(USBTINY)/def.h usbtiny.h
$(COMPILE.c) $(USBTINY)/crc.S
int.o: $(USBTINY)/int.S $(USBTINY)/def.h usbtiny.h
$(COMPILE.c) $(USBTINY)/int.S
usb.o: $(USBTINY)/usb.c $(USBTINY)/def.h $(USBTINY)/usb.h usbtiny.h
$(COMPILE.c) $(USBTINY)/usb.c
 
main.o: $(USBTINY)/usb.h
 
%.ps: %.sch $(UTIL)/sch2ps
$(UTIL)/sch2ps $<
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/crc.S
0,0 → 1,123
; ======================================================================
; Calculate and append CRC
;
; The CRC is calculated 4 bits at a time, using a precomputed table of
; 16 values. Each value is 16 bits, but only the 8 significant bits are
; stored. The table should not cross a 256-byte page. The check.py script
; will check for this.
;
; A bitwise algorithm would be a little smaller, but takes more time.
; In fact, it takes too much time for the USB controller in my laptop.
; The poll frequently is so high, that a lot of time is spent in the
; interrupt handler, sending NAK packets, leaving little time for the
; actual checksum calculation. An 8 bit algoritm would be even faster,
; but requires a lookup table of 512 bytes.
;
; Copyright (C) 2006 Dick Streefland
;
; This is free software, licensed under the terms of the GNU General
; Public License as published by the Free Software Foundation.
; ======================================================================
 
#include "def.h"
 
; ----------------------------------------------------------------------
; void crc(unsigned char *data, unsigned char len);
; ----------------------------------------------------------------------
#define data r24
#define len r22
 
#define b r18
#define tmp r19
#define zl r20
#define crc_l r24
#define crc_h r25
 
.text
.global crc
.type crc, @function
crc:
; crc = 0xffff
movw XL, r24
ldi crc_h, 0xff
ldi crc_l, 0xff
lsl len
breq done
ldi zl, lo8(crc4tab)
ldi ZH, hi8(crc4tab)
 
next_nibble:
; b = (len & 1 ? b >> 4 : *data++)
swap b
sbrs len, 0
ld b, X+
 
; index = (crc ^ b) & 0x0f
mov ZL, crc_l
eor ZL, b
andi ZL, 0x0f
 
; crc >>= 4
swap crc_h
swap crc_l
andi crc_l, 0x0f
mov tmp, crc_h
andi tmp, 0xf0
or crc_l, tmp
andi crc_h, 0x0f
 
; crc ^= crc4tab[index]
add ZL, zl
lpm tmp, Z+
eor crc_h, tmp
andi tmp, 1
eor crc_h, tmp
eor crc_l, tmp
 
; next nibble
dec len
brne next_nibble
 
done:
; crc ^= 0xffff
com crc_l
com crc_h
 
; append crc to buffer
st X+, crc_l
st X+, crc_h
 
ret
 
; ----------------------------------------------------------------------
; CRC table. As bits 1..8 are always zero, omit them.
; ----------------------------------------------------------------------
.section .progmem.crc,"a",@progbits
;;; .align 4 ; avoid crossing a page boundary
crc4tab:
.byte 0x00+0x00
.byte 0xcc+0x01
.byte 0xd8+0x01
.byte 0x14+0x00
.byte 0xf0+0x01
.byte 0x3c+0x00
.byte 0x28+0x00
.byte 0xe4+0x01
.byte 0xa0+0x01
.byte 0x6c+0x00
.byte 0x78+0x00
.byte 0xb4+0x01
.byte 0x50+0x00
.byte 0x9c+0x01
.byte 0x88+0x01
.byte 0x44+0x00
/* ---------------------------------------------------------------------- *\
#!/usr/bin/python
for crc in range(16):
for bit in range(4):
xor = crc & 1
crc >>= 1
if xor:
crc ^= 0xA001 # X^16 + X^15 + X^2 + 1 (reversed)
print "\t.byte\t0x%02x+0x%02x" % (crc >> 8, crc & 0xff)
\* ---------------------------------------------------------------------- */
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/def.h
0,0 → 1,74
// ======================================================================
// Common definitions for the USB driver
//
// Copyright (C) 2006 Dick Streefland
//
// This is free software, licensed under the terms of the GNU General
// Public License as published by the Free Software Foundation.
// ======================================================================
 
#ifdef __ASSEMBLER__
#define __SFR_OFFSET 0
#endif
#include <avr/io.h>
#include "usbtiny.h"
 
// Preprocessor magic
#define CAT2(a,b) CAT2EXP(a, b)
#define CAT2EXP(a,b) a ## b
#define CAT3(a,b,c) CAT3EXP(a, b, c)
#define CAT3EXP(a,b,c) a ## b ## c
 
// I/O Ports
#define USB_IN CAT2(PIN, USBTINY_PORT)
#define USB_OUT CAT2(PORT, USBTINY_PORT)
#define USB_DDR CAT2(DDR, USBTINY_PORT)
 
// I/O bit masks
#define USB_MASK_DMINUS (1 << (USBTINY_DMINUS))
#define USB_MASK_DPLUS (1 << (USBTINY_DPLUS))
#define USB_MASK (USB_MASK_DMINUS | USB_MASK_DPLUS)
 
// Interrupt configuration
#if defined EICRA
# define USB_INT_CONFIG EICRA
#else
# define USB_INT_CONFIG MCUCR
#endif
#define USB_INT_CONFIG_SET ((1 << CAT3(ISC,USBTINY_INT,1)) | (1 << CAT3(ISC,USBTINY_INT,0)))
#if defined SIG_INT0
# define USB_INT_VECTOR CAT2(SIG_INT, USBTINY_INT)
#else
# define USB_INT_VECTOR CAT2(SIG_INTERRUPT, USBTINY_INT)
#endif
 
// Interrupt enable
#if defined GIMSK
# define USB_INT_ENABLE GIMSK
#elif defined EIMSK
# define USB_INT_ENABLE EIMSK
#else
# define USB_INT_ENABLE GICR
#endif
#define USB_INT_ENABLE_BIT CAT2(INT,USBTINY_INT)
 
// Interrupt pending bit
#if defined EIFR
# define USB_INT_PENDING EIFR
#else
# define USB_INT_PENDING GIFR
#endif
#define USB_INT_PENDING_BIT CAT2(INTF,USBTINY_INT)
 
// USB PID values
#define USB_PID_SETUP 0x2d
#define USB_PID_OUT 0xe1
#define USB_PID_IN 0x69
#define USB_PID_DATA0 0xc3
#define USB_PID_DATA1 0x4b
#define USB_PID_ACK 0xd2
#define USB_PID_NAK 0x5a
#define USB_PID_STALL 0x1e
 
// Various constants
#define USB_BUFSIZE 11 // PID + data + CRC
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/int.S
0,0 → 1,562
; ======================================================================
; USB interrupt handler
;
; This is the handler for the interrupt caused by the initial rising edge
; on the D+ USB signal. The NRZI encoding and bit stuffing are removed,
; and the packet is saved in one of the two input buffers. In some cases,
; a reply packet is sent right away.
;
; When a DATA0/DATA1 packet directly follows a SETUP or OUT packet, while
; this interrupt handler is not yet finished, there would be no time to
; return and take another interrupt. In that case, the second packet is
; decoded directly in the same invocation.
;
; This code is *extremely* time critical. For instance, there is not a
; single spare cycle in the receiver loop, and only two in the transmitter
; loop. In addition, the various code paths are laid out in such a way that
; the various USB timeouts are not violated, in particular the maximum time
; between the reception of a packet and the reply, which is 6.5 bit times
; for a detachable cable (TRSPIPD1), and 7.5 bit times for a captive cable
; (TRSPIPD2). The worst-case delay here is 51 cycles, which is just below
; the 52 cycles for a detachable cable.
;
; The interrupt handler must be reached within 34 cycles after D+ goes high
; for the first time, so the interrupts should not be disabled for longer
; than 34-4-2=28 cycles.
;
; The end-of-packet (EOP) is sampled in the second bit, because the USB
; standard allows the EOP to be delayed by up to one bit. As the EOP
; duration is two bits, this is not a problem.
;
; Stack usage including the return address: 11 bytes.
;
; Copyright (C) 2006 Dick Streefland
;
; This is free software, licensed under the terms of the GNU General
; Public License as published by the Free Software Foundation.
; ======================================================================
 
#include "def.h"
 
; ----------------------------------------------------------------------
; local data
; ----------------------------------------------------------------------
.data
tx_ack: .byte USB_PID_ACK ; ACK packet
tx_nak: .byte USB_PID_NAK ; NAK packet
.lcomm token_pid, 1 ; PID of most recent token packet
 
; ----------------------------------------------------------------------
; register definitions
; ----------------------------------------------------------------------
// receiver:
#define count r16
#define usbmask r17
#define odd r18
#define byte r19
#define fixup r20
#define even r22
 
// transmitter:
#define output odd
#define done fixup
#define next even
 
// control:
#define pid odd
#define addr usbmask
#define tmp fixup
 
#define nop2 rjmp .+0 // not .+2 for some strange reason
 
; ----------------------------------------------------------------------
; interrupt handler
; ----------------------------------------------------------------------
.text
.global USB_INT_VECTOR
.type USB_INT_VECTOR, @function
; ----------------------------------------------------------------------
; This handler must be reached no later than 34 cycles after D+ goes high
; for the first time.
; ----------------------------------------------------------------------
USB_INT_VECTOR:
; save registers
push count
push usbmask
push odd
push YH
push YL
in count, SREG
push count
 
; ----------------------------------------------------------------------
; Synchronize to the pattern 10101011 on D+. This code must be reached
; no later than 47 cycles after D+ goes high for the first time.
; ----------------------------------------------------------------------
sync:
; wait until D+ == 0
sbic USB_IN, USBTINY_DPLUS
rjmp sync ; jump if D+ == 1
resync:
; sync on 0-->1 transition on D+ with a 2 cycle resolution
sbic USB_IN, USBTINY_DPLUS
rjmp sync6 ; jump if D+ == 1
sbic USB_IN, USBTINY_DPLUS
rjmp sync6 ; jump if D+ == 1
sbic USB_IN, USBTINY_DPLUS
rjmp sync6 ; jump if D+ == 1
sbic USB_IN, USBTINY_DPLUS
rjmp sync6 ; jump if D+ == 1
sbic USB_IN, USBTINY_DPLUS
rjmp sync6 ; jump if D+ == 1
ldi count, 1<<USB_INT_PENDING_BIT
out USB_INT_PENDING, count
rjmp return ; ==> false start, bail out
 
sync6:
; we are now between -1 and +1 cycle from the center of the bit
; following the 0-->1 transition
lds YL, usb_rx_off
clr YH
subi YL, lo8(-(usb_rx_buf)) ; Y = & usb_rx_buf[usb_rx_off]
sbci YH, hi8(-(usb_rx_buf))
ldi count, USB_BUFSIZE ; limit on number of bytes to receive
ldi usbmask, USB_MASK ; why is there no eori instruction?
ldi odd, USB_MASK_DPLUS
 
sync7:
; the last sync bit should also be 1
sbis USB_IN, USBTINY_DPLUS ; bit 7 of sync byte?
rjmp resync ; no, wait for next transition
push byte
push fixup
push even
 
; ----------------------------------------------------------------------
; receiver loop
; ----------------------------------------------------------------------
in even, USB_IN ; sample bit 0
ldi byte, 0x80 ; load sync byte for correct unstuffing
rjmp rxentry ; 2 cycles
 
rxloop:
in even, USB_IN ; sample bit 0
or fixup, byte
st Y+, fixup ; 2 cycles
rxentry:
clr fixup
andi even, USB_MASK
eor odd, even
subi odd, 1
in odd, USB_IN ; sample bit 1
andi odd, USB_MASK
breq eop ; ==> EOP detected
ror byte
cpi byte, 0xfc
brcc skip0
skipped0:
eor even, odd
subi even, 1
in even, USB_IN ; sample bit 2
andi even, USB_MASK
ror byte
cpi byte, 0xfc
brcc skip1
skipped1:
eor odd, even
subi odd, 1
ror byte
in odd, USB_IN ; sample bit 3
andi odd, USB_MASK
cpi byte, 0xfc
brcc skip2
eor even, odd
subi even, 1
ror byte
skipped2:
cpi byte, 0xfc
in even, USB_IN ; sample bit 4
andi even, USB_MASK
brcc skip3
eor odd, even
subi odd, 1
ror byte
skipped4:
cpi byte, 0xfc
skipped3:
brcc skip4
in odd, USB_IN ; sample bit 5
andi odd, USB_MASK
eor even, odd
subi even, 1
ror byte
skipped5:
cpi byte, 0xfc
brcc skip5
dec count
in even, USB_IN ; sample bit 6
brmi overflow ; ==> overflow
andi even, USB_MASK
eor odd, even
subi odd, 1
ror byte
skipped6:
cpi byte, 0xfc
brcc skip6
in odd, USB_IN ; sample bit 7
andi odd, USB_MASK
eor even, odd
subi even, 1
ror byte
cpi byte, 0xfc
brcs rxloop ; 2 cycles
rjmp skip7
 
eop:
rjmp eop2
overflow:
rjmp ignore
 
; ----------------------------------------------------------------------
; out-of-line code to skip stuffing bits
; ----------------------------------------------------------------------
skip0: ; 1+6 cycles
eor even, usbmask
in odd, USB_IN ; resample bit 1
andi odd, USB_MASK
cbr byte, (1<<7)
sbr fixup, (1<<0)
rjmp skipped0
 
skip1: ; 2+5 cycles
cbr byte, (1<<7)
sbr fixup, (1<<1)
in even, USB_IN ; resample bit 2
andi even, USB_MASK
eor odd, usbmask
rjmp skipped1
 
skip2: ; 3+7 cycles
cbr byte, (1<<7)
sbr fixup, (1<<2)
eor even, usbmask
in odd, USB_IN ; resample bit 3
andi odd, USB_MASK
eor even, odd
subi even, 1
ror byte
rjmp skipped2
 
skip3: ; 4+7 cycles
cbr byte, (1<<7)
sbr fixup, (1<<3)
eor odd, usbmask
ori byte, 1
in even, USB_IN ; resample bit 4
andi even, USB_MASK
eor odd, even
subi odd, 1
ror byte
rjmp skipped3
 
skip4: ; 5 cycles
cbr byte, (1<<7)
sbr fixup, (1<<4)
eor even, usbmask
rjmp skipped4
 
skip5: ; 5 cycles
cbr byte, (1<<7)
sbr fixup, (1<<5)
eor odd, usbmask
rjmp skipped5
 
skip6: ; 5 cycles
cbr byte, (1<<7)
sbr fixup, (1<<6)
eor even, usbmask
rjmp skipped6
 
skip7: ; 7 cycles
cbr byte, (1<<7)
sbr fixup, (1<<7)
eor odd, usbmask
nop2
rjmp rxloop
 
; ----------------------------------------------------------------------
; end-of-packet detected (worst-case: 3 cycles after end of SE0)
; ----------------------------------------------------------------------
eop2:
; clear pending interrupt (SE0+3)
ldi byte, 1<<USB_INT_PENDING_BIT
out USB_INT_PENDING, byte ; clear pending bit at end of packet
; ignore packets shorter than 3 bytes
subi count, USB_BUFSIZE
neg count ; count = packet length
cpi count, 3
brlo ignore
; get PID
sub YL, count
ld pid, Y
; check for DATA0/DATA1 first, as this is the critical path (SE0+12)
cpi pid, USB_PID_DATA0
breq is_data ; handle DATA0 packet
cpi pid, USB_PID_DATA1
breq is_data ; handle DATA1 packet
; check ADDR (SE0+16)
ldd addr, Y+1
andi addr, 0x7f
lds tmp, usb_address
cp addr, tmp ; is this packet for me?
brne ignore ; no, ignore
; check for other PIDs (SE0+23)
cpi pid, USB_PID_IN
breq is_in ; handle IN packet
cpi pid, USB_PID_SETUP
breq is_setup_out ; handle SETUP packet
cpi pid, USB_PID_OUT
breq is_setup_out ; handle OUT packet
 
; ----------------------------------------------------------------------
; exit point for ignored packets
; ----------------------------------------------------------------------
ignore:
clr tmp
sts token_pid, tmp
pop even
pop fixup
pop byte
rjmp return
 
; ----------------------------------------------------------------------
; Handle SETUP/OUT (SE0+30)
; ----------------------------------------------------------------------
is_setup_out:
sts token_pid, pid ; save PID of token packet
pop even
pop fixup
pop byte
in count, USB_INT_PENDING ; next packet already started?
sbrc count, USB_INT_PENDING_BIT
rjmp sync ; yes, get it right away (SE0+42)
 
; ----------------------------------------------------------------------
; restore registers and return from interrupt
; ----------------------------------------------------------------------
return:
pop count
out SREG, count
pop YL
pop YH
pop odd
pop usbmask
pop count
reti
 
; ----------------------------------------------------------------------
; Handle IN (SE0+26)
; ----------------------------------------------------------------------
is_in:
lds count, usb_tx_len
tst count ; data ready?
breq nak ; no, reply with NAK
lds tmp, usb_rx_len
tst tmp ; unprocessed input packet?
brne nak ; yes, don't send old data for new packet
sts usb_tx_len, tmp ; buffer is available again (after reti)
ldi YL, lo8(usb_tx_buf)
ldi YH, hi8(usb_tx_buf)
rjmp send_packet ; SE0+40, SE0 --> SOP <= 51
 
; ----------------------------------------------------------------------
; Handle DATA0/DATA1 (SE0+17)
; ----------------------------------------------------------------------
is_data:
lds pid, token_pid
tst pid ; data following our SETUP/OUT
breq ignore ; no, ignore
lds tmp, usb_rx_len
tst tmp ; buffer free?
brne nak ; no, reply with NAK
sts usb_rx_len, count ; pass buffer length
sts usb_rx_token, pid ; pass PID of token (SETUP or OUT)
lds count, usb_rx_off ; switch to other input buffer
ldi tmp, USB_BUFSIZE
sub tmp, count
sts usb_rx_off, tmp
 
; ----------------------------------------------------------------------
; send ACK packet (SE0+35)
; ----------------------------------------------------------------------
ack:
ldi YL, lo8(tx_ack)
ldi YH, hi8(tx_ack)
rjmp send_token
 
; ----------------------------------------------------------------------
; send NAK packet (SE0+36)
; ----------------------------------------------------------------------
nak:
ldi YL, lo8(tx_nak)
ldi YH, hi8(tx_nak)
send_token:
ldi count, 1 ; SE0+40, SE0 --> SOP <= 51
 
; ----------------------------------------------------------------------
; acquire the bus and send a packet (11 cycles to SOP)
; ----------------------------------------------------------------------
send_packet:
in output, USB_OUT
cbr output, USB_MASK
ori output, USB_MASK_DMINUS
in usbmask, USB_DDR
ori usbmask, USB_MASK
out USB_OUT, output ; idle state
out USB_DDR, usbmask ; acquire bus
ldi usbmask, USB_MASK
ldi byte, 0x80 ; start with sync byte
 
; ----------------------------------------------------------------------
; transmitter loop
; ----------------------------------------------------------------------
txloop:
sbrs byte, 0
eor output, usbmask
out USB_OUT, output ; output bit 0
ror byte
ror done
stuffed0:
cpi done, 0xfc
brcc stuff0
sbrs byte, 0
eor output, usbmask
ror byte
stuffed1:
out USB_OUT, output ; output bit 1
ror done
cpi done, 0xfc
brcc stuff1
sbrs byte, 0
eor output, usbmask
ror byte
nop
stuffed2:
out USB_OUT, output ; output bit 2
ror done
cpi done, 0xfc
brcc stuff2
sbrs byte, 0
eor output, usbmask
ror byte
nop
stuffed3:
out USB_OUT, output ; output bit 3
ror done
cpi done, 0xfc
brcc stuff3
sbrs byte, 0
eor output, usbmask
ld next, Y+ ; 2 cycles
out USB_OUT, output ; output bit 4
ror byte
ror done
stuffed4:
cpi done, 0xfc
brcc stuff4
sbrs byte, 0
eor output, usbmask
ror byte
stuffed5:
out USB_OUT, output ; output bit 5
ror done
cpi done, 0xfc
brcc stuff5
sbrs byte, 0
eor output, usbmask
ror byte
stuffed6:
ror done
out USB_OUT, output ; output bit 6
cpi done, 0xfc
brcc stuff6
sbrs byte, 0
eor output, usbmask
ror byte
mov byte, next
stuffed7:
ror done
out USB_OUT, output ; output bit 7
cpi done, 0xfc
brcc stuff7
dec count
brpl txloop ; 2 cycles
 
rjmp gen_eop
 
; ----------------------------------------------------------------------
; out-of-line code to insert stuffing bits
; ----------------------------------------------------------------------
stuff0: ; 2+3
eor output, usbmask
clr done
out USB_OUT, output
rjmp stuffed0
 
stuff1: ; 3
eor output, usbmask
rjmp stuffed1
 
stuff2: ; 3
eor output, usbmask
rjmp stuffed2
 
stuff3: ; 3
eor output, usbmask
rjmp stuffed3
 
stuff4: ; 2+3
eor output, usbmask
clr done
out USB_OUT, output
rjmp stuffed4
 
stuff5: ; 3
eor output, usbmask
rjmp stuffed5
 
stuff6: ; 3
eor output, usbmask
rjmp stuffed6
 
stuff7: ; 3
eor output, usbmask
rjmp stuffed7
 
; ----------------------------------------------------------------------
; generate EOP, release the bus, and return from interrupt
; ----------------------------------------------------------------------
gen_eop:
cbr output, USB_MASK
out USB_OUT, output ; output SE0 for 2 bit times
pop even
pop fixup
pop byte
ldi count, 1<<USB_INT_PENDING_BIT
out USB_INT_PENDING, count ; interrupt was triggered by transmit
pop YH ; this is the saved SREG
pop YL
in usbmask, USB_DDR
mov count, output
ori output, USB_MASK_DMINUS
out USB_OUT, output ; output J state for 1 bit time
cbr usbmask, USB_MASK
out SREG, YH
pop YH
pop odd ; is the same register as output!
nop
out USB_DDR, usbmask ; release bus
out USB_OUT, count ; disable D- pullup
pop usbmask
pop count
reti
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/usb.c
0,0 → 1,418
// ======================================================================
// USB driver
//
// Entry points:
// usb_init() - enable the USB interrupt
// usb_poll() - poll for incoming packets and process them
//
// This code communicates with the interrupt handler through a number of
// global variables, including two input buffers and one output buffer.
// Packets are queued for transmission by copying them into the output
// buffer. The interrupt handler will transmit such a packet on the
// reception of an IN packet.
//
// Standard SETUP packets are handled here. Non-standard SETUP packets
// are forwarded to the application code by calling usb_setup(). The
// macros USBTINY_CALLBACK_IN and USBTINY_CALLBACK_OUT control whether
// the callback functions usb_in() and usb_out() will be called for IN
// and OUT transfers.
//
// Maximum stack usage (gcc 3.4.3 & 4.1.0) of usb_poll(): 5 bytes plus
// possible additional stack usage in usb_setup(), usb_in() or usb_out().
//
// Copyright (C) 2006 Dick Streefland
//
// This is free software, licensed under the terms of the GNU General
// Public License as published by the Free Software Foundation.
// ======================================================================
 
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include "def.h"
#include "usb.h"
 
#define LE(word) (word) & 0xff, (word) >> 8
 
// ----------------------------------------------------------------------
// USB constants
// ----------------------------------------------------------------------
 
enum
{
DESCRIPTOR_TYPE_DEVICE = 1,
DESCRIPTOR_TYPE_CONFIGURATION,
DESCRIPTOR_TYPE_STRING,
DESCRIPTOR_TYPE_INTERFACE,
DESCRIPTOR_TYPE_ENDPOINT,
};
 
// ----------------------------------------------------------------------
// Interrupt handler interface
// ----------------------------------------------------------------------
 
byte_t usb_rx_buf[2*USB_BUFSIZE]; // two input buffers
byte_t usb_rx_off; // buffer offset: 0 or USB_BUFSIZE
byte_t usb_rx_len; // buffer size, 0 means empty
byte_t usb_rx_token; // PID of token packet: SETUP or OUT
 
byte_t usb_tx_buf[USB_BUFSIZE]; // output buffer
byte_t usb_tx_len; // output buffer size, 0 means empty
 
byte_t usb_address; // assigned USB address
 
// ----------------------------------------------------------------------
// Local data
// ----------------------------------------------------------------------
 
enum
{
TX_STATE_IDLE = 0, // transmitter idle
TX_STATE_RAM, // usb_tx_data is a RAM address
TX_STATE_ROM, // usb_tx_data is a ROM address
TX_STATE_CALLBACK, // call usb_in() to obtain transmit data
};
 
static byte_t usb_tx_state; // TX_STATE_*, see enum above
static byte_t usb_tx_total; // total transmit size
static byte_t* usb_tx_data; // pointer to data to transmit
static byte_t new_address; // new device address
 
#if defined USBTINY_VENDOR_NAME
struct
{
byte_t length;
byte_t type;
int string[sizeof(USBTINY_VENDOR_NAME)-1];
} string_vendor PROGMEM =
{
2 * sizeof(USBTINY_VENDOR_NAME),
DESCRIPTOR_TYPE_STRING,
{ CAT2(L, USBTINY_VENDOR_NAME) }
};
# define VENDOR_NAME_ID 1
#else
# define VENDOR_NAME_ID 0
#endif
 
#if defined USBTINY_DEVICE_NAME
struct
{
byte_t length;
byte_t type;
int string[sizeof(USBTINY_DEVICE_NAME)-1];
} string_device PROGMEM =
{
2 * sizeof(USBTINY_DEVICE_NAME),
DESCRIPTOR_TYPE_STRING,
{ CAT2(L, USBTINY_DEVICE_NAME) }
};
# define DEVICE_NAME_ID 2
#else
# define DEVICE_NAME_ID 0
#endif
 
#if defined USBTINY_SERIAL
struct
{
byte_t length;
byte_t type;
int string[sizeof(USBTINY_SERIAL)-1];
} string_serial PROGMEM =
{
2 * sizeof(USBTINY_SERIAL),
DESCRIPTOR_TYPE_STRING,
{ CAT2(L, USBTINY_SERIAL) }
};
# define SERIAL_ID 3
#else
# define SERIAL_ID 0
#endif
 
#if VENDOR_NAME_ID || DEVICE_NAME_ID || SERIAL_ID
static byte_t string_langid [] PROGMEM =
{
4, // bLength
DESCRIPTOR_TYPE_STRING, // bDescriptorType (string)
LE(0x0409), // wLANGID[0] (American English)
};
#endif
 
// Device Descriptor
static byte_t descr_device [18] PROGMEM =
{
18, // bLength
DESCRIPTOR_TYPE_DEVICE, // bDescriptorType
LE(0x0110), // bcdUSB
USBTINY_DEVICE_CLASS, // bDeviceClass
USBTINY_DEVICE_SUBCLASS, // bDeviceSubClass
USBTINY_DEVICE_PROTOCOL, // bDeviceProtocol
8, // bMaxPacketSize0
LE(USBTINY_VENDOR_ID), // idVendor
LE(USBTINY_DEVICE_ID), // idProduct
LE(USBTINY_DEVICE_VERSION), // bcdDevice
VENDOR_NAME_ID, // iManufacturer
DEVICE_NAME_ID, // iProduct
SERIAL_ID, // iSerialNumber
1, // bNumConfigurations
};
 
// Configuration Descriptor
static byte_t descr_config [] PROGMEM =
{
9, // bLength
DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType
LE(9+9+7*USBTINY_ENDPOINT), // wTotalLength
1, // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
(USBTINY_MAX_POWER ? 0x80 : 0xc0), // bmAttributes
(USBTINY_MAX_POWER + 1) / 2, // MaxPower
 
// Standard Interface Descriptor
9, // bLength
DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType
0, // bInterfaceNumber
0, // bAlternateSetting
USBTINY_ENDPOINT, // bNumEndpoints
USBTINY_INTERFACE_CLASS, // bInterfaceClass
USBTINY_INTERFACE_SUBCLASS, // bInterfaceSubClass
USBTINY_INTERFACE_PROTOCOL, // bInterfaceProtocol
0, // iInterface
 
#if USBTINY_ENDPOINT
// Additional Endpoint
7, // bLength
DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType
USBTINY_ENDPOINT_ADDRESS, // bEndpointAddress
USBTINY_ENDPOINT_TYPE, // bmAttributes
LE(8), // wMaxPacketSize
USBTINY_ENDPOINT_INTERVAL, // bInterval
#endif
};
 
// ----------------------------------------------------------------------
// Inspect an incoming packet.
// ----------------------------------------------------------------------
static void usb_receive ( byte_t* data, byte_t rx_len )
{
byte_t len;
byte_t type;
byte_t limit;
 
usb_tx_state = TX_STATE_RAM;
len = 0;
if ( usb_rx_token == USB_PID_SETUP )
{
limit = data[6];
if ( data[7] )
{
limit = 255;
}
type = data[0] & 0x60;
if ( type == 0x00 )
{ // Standard request
if ( data[1] == 0 ) // GET_STATUS
{
len = 2;
#if USBTINY_MAX_POWER == 0
data[0] = (data[0] == 0x80);
#else
data[0] = 0;
#endif
data[1] = 0;
}
else if ( data[1] == 5 ) // SET_ADDRESS
{
new_address = data[2];
}
else if ( data[1] == 6 ) // GET_DESCRIPTOR
{
usb_tx_state = TX_STATE_ROM;
if ( data[3] == 1 )
{ // DEVICE
data = (byte_t*) &descr_device;
len = sizeof(descr_device);
}
else if ( data[3] == 2 )
{ // CONFIGURATION
data = (byte_t*) &descr_config;
len = sizeof(descr_config);
}
#if VENDOR_NAME_ID || DEVICE_NAME_ID || SERIAL_ID
else if ( data[3] == 3 )
{ // STRING
if ( data[2] == 0 )
{
data = (byte_t*) &string_langid;
len = sizeof(string_langid);
}
#if VENDOR_NAME_ID
else if ( data[2] == VENDOR_NAME_ID )
{
data = (byte_t*) &string_vendor;
len = sizeof(string_vendor);
}
#endif
#if DEVICE_NAME_ID
else if ( data[2] == DEVICE_NAME_ID )
{
data = (byte_t*) &string_device;
len = sizeof(string_device);
}
#endif
#if SERIAL_ID
else if ( data[2] == SERIAL_ID )
{
data = (byte_t*) &string_serial;
len = sizeof(string_serial);
}
#endif
}
#endif
}
else if ( data[1] == 8 ) // GET_CONFIGURATION
{
data[0] = 1; // return bConfigurationValue
len = 1;
}
else if ( data[1] == 10 ) // GET_INTERFACE
{
data[0] = 0;
len = 1;
}
}
else
{ // Class or Vendor request
len = usb_setup( data );
#if USBTINY_CALLBACK_IN
if ( len == 0xff )
{
usb_tx_state = TX_STATE_CALLBACK;
}
#endif
}
if ( len > limit )
{
len = limit;
}
usb_tx_data = data;
}
#if USBTINY_CALLBACK_OUT
else if ( rx_len > 0 )
{ // usb_rx_token == USB_PID_OUT
usb_out( data, rx_len );
}
#endif
usb_tx_total = len;
usb_tx_buf[0] = USB_PID_DATA0; // next data packet will be DATA1
}
 
// ----------------------------------------------------------------------
// Load the transmit buffer with the next packet.
// ----------------------------------------------------------------------
static void usb_transmit ( void )
{
byte_t len;
byte_t* src;
byte_t* dst;
byte_t i;
byte_t b;
 
usb_tx_buf[0] ^= (USB_PID_DATA0 ^ USB_PID_DATA1);
len = usb_tx_total;
if ( len > 8 )
{
len = 8;
}
dst = usb_tx_buf + 1;
if ( len > 0 )
{
#if USBTINY_CALLBACK_IN
if ( usb_tx_state == TX_STATE_CALLBACK )
{
len = usb_in( dst, len );
}
else
#endif
{
src = usb_tx_data;
if ( usb_tx_state == TX_STATE_RAM )
{
for ( i = 0; i < len; i++ )
{
*dst++ = *src++;
}
}
else // usb_tx_state == TX_STATE_ROM
{
for ( i = 0; i < len; i++ )
{
b = pgm_read_byte( src );
src++;
*dst++ = b;
}
}
usb_tx_data = src;
}
usb_tx_total -= len;
}
crc( usb_tx_buf + 1, len );
usb_tx_len = len + 3;
if ( len < 8 )
{ // this is the last packet
usb_tx_state = TX_STATE_IDLE;
}
}
 
// ----------------------------------------------------------------------
// Initialize the low-level USB driver.
// ----------------------------------------------------------------------
extern void usb_init ( void )
{
USB_INT_CONFIG |= USB_INT_CONFIG_SET;
USB_INT_ENABLE |= (1 << USB_INT_ENABLE_BIT);
sei();
}
 
// ----------------------------------------------------------------------
// Poll USB driver:
// - check for incoming USB packets
// - refill an empty transmit buffer
// - check for USB bus reset
// ----------------------------------------------------------------------
extern void usb_poll ( void )
{
byte_t i;
 
// check for incoming USB packets
if ( usb_rx_len != 0 )
{
usb_receive( usb_rx_buf + USB_BUFSIZE - usb_rx_off + 1, usb_rx_len - 3 );
usb_tx_len = 0; // abort pending transmission
usb_rx_len = 0; // accept next packet
}
// refill an empty transmit buffer, when the transmitter is active
if ( usb_tx_len == 0 )
{
if ( usb_tx_state != TX_STATE_IDLE )
{
usb_transmit();
}
else
{ // change the USB address at the end of a transfer
usb_address = new_address;
}
}
// check for USB bus reset
for ( i = 10; i > 0 && ! (USB_IN & USB_MASK_DMINUS); i-- )
{
}
if ( i == 0 )
{ // SE0 for more than 2.5uS is a reset
cli();
usb_tx_len=0;
usb_rx_len=0;
new_address = 0;
sei();
}
}
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/usb.h
0,0 → 1,28
// ======================================================================
// Public interface of the USB driver
//
// Copyright (C) 2006 Dick Streefland
//
// This is free software, licensed under the terms of the GNU General
// Public License as published by the Free Software Foundation.
// ======================================================================
 
#ifndef USB_H
#define USB_H
 
typedef unsigned char byte_t;
typedef unsigned int uint_t;
 
// usb.c
extern void usb_init ( void );
extern void usb_poll ( void );
 
// crc.S
extern void crc ( byte_t* data, byte_t len );
 
// application callback functions
extern byte_t usb_setup ( byte_t data[8] );
extern void usb_out ( byte_t* data, byte_t len );
extern byte_t usb_in ( byte_t* data, byte_t len );
 
#endif // USB_H