; ====================================================================== ; 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< 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< 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<