;=================================================================================
; USB HID code
; by Hector Martin "marcan"
; based on Microchip USB C18 Firmware Version 1.0
;=================================================================================
	LIST	P=18F4550
	RADIX	DEC

	include	"p18cxxx.inc"
	include "config.inc"
	include "stddefs.inc"
	include	"usbdefs.inc"

;=================================================================================
; Variables
;=================================================================================
USBACS		UDATA_ACS
USBSTATE	RES		1
USB_DEVSTATUS	RES		1
USB_NOINCNT	RES		1
USB_INCNT	RES		1
USB_XBOX_MODE	RES		1

USBDATA		UDATA
USB_BD		RES		4
USB_USTAT	RES		1
USB_BUFFER	RES		EP0MAXPS
USB_ERRORFLAGS	RES		1
USB_CURCONFIG	RES		1
USB_PROTO	RES		1
USB_IDLERATE	RES		1
USB_DEV_REQ	RES		1
USB_ADDR_PEND	RES		1
USB_DESC_PTR	RES		1
USB_BYTES_LEFT	RES		1
HID_REPORT	RES		20	; xbox reports are 20 bytes

	GLOBAL	HID_REPORT,USBSTATE,USB_DEVSTATUS,USB_NOINCNT,USB_INCNT,USB_XBOX_MODE,USB_IDLERATE

;=================================================================================
; Descriptors
;=================================================================================

USBDESCRIPTORS	CODE
	include "usbdesc.inc"

;=================================================================================
; Code
;=================================================================================
	
USBCODE		CODE

#ifdef	USE_USB_WITH_INTERRUPTS
		GLOBAL		ISR_USB
ISR_USB
		movf		USBSTATE,F	; check USB state
		btfss		STATUS,Z	; is module ON?
		goto		USB_TASKS	; yes
		clrf		UIR		; no
		clrf		UEIR
		bcf		PIR2,USBIF
		return
#endif

		GLOBAL		INIT_USB
INIT_USB
#ifdef	USE_STATUS_PINS
		movlw		(((1<<NUM_STATUS_PINS)-1)<<STATUS_SHIFT)^0xFF ; mask with non-status bits set
		andwf		STATUS_TRIS
#endif

		SETSTATE	STATE_OFF
		clrf		USBSTATE	;default to OFF state

		movlw		(1<<UPUEN)|(0<<UTRDIS)|(1<<FSEN)|(0<<PPB0)
		movwf		UCFG		; use high-speed transfers and on-chip transceiver and pullups
		clrf		UCON		; default state of USB module

		clrf		UIE		; mask interrupts
		clrf		UIR		; clear flags
		setf		UEIE		; enable error flag propagation to UERR
		clrf		UEIR		; clear flags

#ifdef	USE_USB_WITH_INTERRUPTS
		setf		UIE		; enable all interrupts
		bcf		PIR2,USBIF	; enable USB interrupt
		bsf		PIE2,USBIE
#endif

		return

; ---- Called periodically
		GLOBAL		DO_USB
DO_USB
		call		CHECK_USB_STATUS
#ifndef	USE_USB_WITH_INTERRUPTS
		movf		USBSTATE,F	; check USB state
		btfss		STATUS,Z	; is module ON?
		goto		USB_TASKS	; yes
#endif
		return				; no

; ---- Enable / disable module according to USB power status
CHECK_USB_STATUS

		bsf		USB_BUSPOWER_TRIS

		movf		USBSTATE,F	; check state
		bnz		STATE_ON

		btfss		USB_BUSPOWER_PIN	; state is OFF here
		return				; still unplugged
		goto		USB_ATTACHED	; we were connected

STATE_ON	btfss		USB_BUSPOWER_PIN	;state is not OFF here
		goto		USB_DETACHED	; someone pulled the plug, retreat.
		return				; still connected

; ---- someone pulled the plug
USB_DETACHED
		SETSTATE	STATE_OFF 	; clear LEDs
		clrf		USBSTATE	; status=OFF

#ifdef USE_USB_WITH_INTERRUPTS
		bcf		PIE2,USBIE
#endif

		bcf		UCON,USBEN	; disable USB
		bcf		UCFG,UPUEN

		return

; ---- USB has been connected
USB_ATTACHED
		SETSTATE	STATE_POWERED
		movlw		STATE_POWERED	; status=POWERED
		movwf		USBSTATE

#ifdef USE_USB_WITH_INTERRUPTS
		clrf		UIR
		clrf		UEIR
		bcf		PIR2,USBIF
		bsf		PIE2,USBIE
#endif

		bsf		UCFG,UPUEN
		bsf		UCON,USBEN	; enable USB

		banksel		USB_CURCONFIG
		clrf		USB_CURCONFIG
		movlw		0x69
		movwf		USB_IDLERATE
		movlw		0x00
		movwf		USB_DEVSTATUS
		movwf		USB_PROTO	; default protocol to report protocol initially
		movlw		NO_REQUEST
		movwf		USB_DEV_REQ	; No device requests in process
		return

; ---- do USB tasks (service *IF bits)
USB_TASKS
	; UERRIF
		btfss		UIR,UERRIF
		bra		UTSK_0
		clrf		UEIR
	; SOFIF
UTSK_0		btfss		UIR,SOFIF
		bra		UTSK_1
		bcf		UIR,SOFIF
	; IDLEIF
UTSK_1		btfss		UIR,IDLEIF
		bra		UTSK_2
		bcf		UIR,IDLEIF
		bsf		UCON,SUSPND	; bus idle for some time, go into SUSPEND mode.
		SETSTATE	STATE_SUSPEND
	; ACTVIF
UTSK_2		btfss		UIR,ACTVIF
		bra		UTSK_3
		bcf		UIR,ACTVIF
		bcf		UCON,SUSPND	; bus not idle, unsuspend
		SETSTATE	STATE_UNSUSPEND
	; STALLIF
UTSK_3		btfss		UIR,STALLIF
		bra		UTSK_4
		bcf		UIR,STALLIF
	; URSTIF
UTSK_4		btfss		UIR,URSTIF
		bra		UTSK_5
		bcf		UIR,URSTIF

		bcf		UIR,TRNIF	; clear TRNIF 4 times to clear USTAT FIFO
		bcf		UIR,TRNIF
		bcf		UIR,TRNIF
		bcf		UIR,TRNIF
		movlw		16		; clear all endpoint registers
		lfsr		FSR0,UEP0
UTSK_EPCLR	clrf		POSTINC0
		decfsz		WREG,F
		bra		UTSK_EPCLR
		
		banksel		BUFFER_DESCRIPTORS
		movlw		EP0MAXPS		; EP0 OUT gets a buffer
		movwf		BD0OCNT
		movlw		low EP0OBUF		; set buffer address
		movwf		BD0OADRL
		movlw		high EP0OBUF
		movwf		BD0OADRH
		movlw		(1<<UOWN)|(0<<DTS)|(1<<DTSEN)	; set UOWN bit (USB can write)
		movwf		BD0OSTAT

		movlw		low EP0IBUF		; EP0 IN gets a buffer, set buffer address
		movwf		BD0IADRL
		movlw		high EP0IBUF
		movwf		BD0IADRH
		movlw		(0<<UOWN)|(0<<DTS)|(1<<DTSEN)	; clear UOWN bit (MCU can write)
		movwf		BD0ISTAT

		clrf		UADDR			; clear USB Address (0=default address)
		clrf		UIR			; clear interrupt flags

		movlw		(1<<EPHSHK)|(0<<EPCONDIS)|(1<<EPOUTEN)|(1<<EPINEN)
		movwf		UEP0			; EP0 is a control pipe - enable in,out,setup

		movlw		STATE_DEFAULT
		movwf		USBSTATE
		movlw		0x01
		movwf		USB_DEVSTATUS		; self powered, remote wakeup disabled
		SETSTATE	STATE_DEFAULT
	;TRNIF
UTSK_5		btfss		UIR,TRNIF
		bra		UTSK_6

		movlw		0x04
		movwf		FSR0H
		movf		USTAT,W		; USTAT is basically the BD address (after masking)
		andlw		b'01111100'
		movwf		FSR0L
		banksel		USB_BD
		movf		POSTINC0,W
		movwf		USB_BD+0
		movf		POSTINC0,W
		movwf		USB_BD+1
		movf		POSTINC0,W
		movwf		USB_BD+2
		movf		POSTINC0,W
		movwf		USB_BD+3
		movf		USTAT,W		; save USTAT
		movwf		USB_USTAT
		bcf		UIR,TRNIF	; clear TRNIF and advance USTAT FIFO

		movf		USB_BD+0,W
		andlw		b'00111100'	; extract PID bits
		movwf		T0

		movlw		TOKEN_SETUP	; SETUP token
		cpfseq		T0
		bra		TRNIF_0

		call		PROCESS_SETUP
		bra		TRNIF_2

TRNIF_0		movlw		TOKEN_IN	; IN token
		cpfseq		T0
		bra		TRNIF_1

		call		PROCESS_IN
		bra		TRNIF_2

TRNIF_1		movlw		TOKEN_OUT	; OUT token
		cpfseq		T0
		bra		TRNIF_2

		call		PROCESS_OUT

TRNIF_2		bra		UTSK_5		; process any more transactions pending (loop)


UTSK_6		bcf		PIR2,USBIF

		return


; transaction processing
PROCESS_SETUP
		banksel		USB_BD		; load buffer address
		movf		USB_BD+BDADRH,W
		movwf		FSR0H
		movf		USB_BD+BDADRL,W
		movwf		FSR0L

		
		lfsr		FSR1,USB_BUFFER

		movlw		EP0MAXPS	; copy buffer
PSETUP_CPLOOP	movff		POSTINC0,POSTINC1
		decfsz		WREG,F
		bra		PSETUP_CPLOOP
		
		banksel		BUFFER_DESCRIPTORS
		movlw		EP0MAXPS
		movwf		BD0OCNT		; reset the byte count
		movlw		(0<<UOWN)|(0<<DTS)|(1<<DTSEN)
		movwf		BD0ISTAT	; return the in buffer to us (dequeue any pending requests)

		banksel		USB_BUFFER+bmRequestType
		movlw		0x21
		cpfseq		USB_BUFFER+bmRequestType		; if bmRequestType==0x21
		bra		PSETUP_RTN21
		movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)	; DTS=DATA1
		bra		PSETUP_RTW
PSETUP_RTN21	movlw		(1<<UOWN)|(0<<DTS)|(1<<DTSEN)	; else DTS=DATA0
PSETUP_RTW	banksel		BUFFER_DESCRIPTORS
		movwf		BD0OSTAT			; set DATA0/DATA1 and UOWN back to USB SIE

		bcf		UCON,PKTDIS	; assuming there is nothing to dequeue, enable packet transfer

		banksel		USB_DEV_REQ

		movlw		NO_REQUEST
		movwf		USB_DEV_REQ	; clear the device request

		movf		USB_BUFFER+bmRequestType,W
		andlw		0x60		; extract request type bits
		movwf		T0

		movlw		STANDARD	; standard request
		subwf		T0,W
		btfsc		STATUS,Z
		goto		STANDARD_REQUEST

		movlw		CLASS		; class request
		subwf		T0,W
		btfsc		STATUS,Z
		goto		CLASS_REQUEST

		movlw		VENDOR		; vendor request
		subwf		T0,W
		btfsc		STATUS,Z
		goto		VENDOR_REQUEST

		bsf		UEP0,EPSTALL	; unknown - stall endpoint to signify Request Error

		return


STANDARD_REQUEST
		banksel		USB_BUFFER
		; GET_STATUS
SREQ_0		movlw		GET_STATUS
		cpfseq		USB_BUFFER+bRequest
		bra		SREQ_1

		movf		USB_BUFFER+bmRequestType,W
		andlw		0x1F		; extract request recipient bits
		movwf		T0

			; RECIPIENT_DEVICE
SREQ_0_0		movlw		RECIPIENT_DEVICE
			cpfseq		T0
			bra		SREQ_0_1

			banksel		BUFFER_DESCRIPTORS
			movf		BD0IADRH,W		; get buffer pointer
			movwf		FSR0H
			movf		BD0IADRL,W
			movwf		FSR0L
			movf		USB_DEVSTATUS,W		; copy device status byte to EP0 buffer
			movwf		POSTINC0
			clrf		INDF0			; data2=0x00

			movlw		0x02			; byte count = 2
			movwf		BD0ICNT
			movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
			movwf		BD0ISTAT		; send packet as DATA1, set UOWN
			return

			; RECIPIENT_INTERFACE
SREQ_0_1		movlw		RECIPIENT_INTERFACE
			cpfseq		T0
			bra		SREQ_0_2

				; STATE_ADDRESS <TODO: remove to optimize>
SREQ_0_1_0			movlw		STATE_ADDRESS
				cpfseq		USBSTATE
				bra		SREQ_0_1_1

				bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
				return

				; STATE_CONFIG
SREQ_0_1_1			movlw		STATE_CONFIGURED
				cpfseq		USBSTATE
				bra		SREQ_0_1_2

				movlw		NUM_INTERFACES	; make sure the interface exists
				cpfslt		USB_BUFFER+wIndex
				bra		SREQ_0_1_2	; stall EP0 otherwise

				banksel		BUFFER_DESCRIPTORS
				movf		BD0IADRH,W	; get buffer pointer
				movwf		FSR0H
				movf		BD0IADRL,W
				movwf		FSR0L
				clrf		POSTINC0	; data=0x0000

				clrf		INDF0
				movlw		0x02		; set byte count to 2
				movwf		BD0ICNT
				movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
				movwf		BD0ISTAT	; send packet as DATA1, set UOWN
				return

				; other state (error) <TODO: this added here - check>
SREQ_0_1_2			bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
				return

			; RECIPIENT_ENDPOINT
SREQ_0_2		movlw		RECIPIENT_ENDPOINT
			cpfseq		T0
			bra		SREQ_0_3

				; STATE_ADDRESS
SREQ_0_2_0			movlw		STATE_ADDRESS
				cpfseq		USBSTATE
				bra		SREQ_0_2_1

				movf		USB_BUFFER+wIndex,W	; get EP
				andlw		0x0F			; strip off direction bit

				btfss		STATUS,Z	; is it EP0?
				bra		SREQ_0_2_2	; no, stall

				banksel		BUFFER_DESCRIPTORS
				movf		BD0IADRH,W	; get buffer pointer
				movwf		FSR0H
				movf		BD0IADRL,W
				movwf		FSR0L
				movlw		0x00		; data1=0x00
				btfsc		UEP0,EPSTALL	; check if endpoint is stalled
				movlw		0x01		; if so, data1=0x01
				movwf		POSTINC0
				clrf		INDF0		; data2=0x00

				movlw		0x02		; set byte count to 2
				movwf		BD0ICNT
				movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
				movwf		BD0ISTAT	; send packet as DATA1, set UOWN
				return


				; STATE_CONFIGURED
SREQ_0_2_1			movlw		STATE_CONFIGURED
				cpfseq		USBSTATE
				bra		SREQ_0_2_2

				banksel		BUFFER_DESCRIPTORS
				movf		BD0IADRH,W	; get buffer pointer into FSR0
				movwf		FSR0H
				movf		BD0IADRL,W
				movwf		FSR0L

				lfsr		FSR1,UEP0	; and UEP0 into FSR1

				banksel		USB_BUFFER
				movf		USB_BUFFER+wIndex,W	; get EP
				andlw		0x0F		; and strip off direction bit

				movf		PLUSW1,W	; get UEPx where x=W
				movwf		T0
				andlw		(1<<EPOUTEN)|(1<<EPINEN)	; mask off EPOUTEN and EPINEN
				btfsc		STATUS,Z	; if endpoint is disabled
				bra		SREQ_0_2_2	; stall

				movlw		0x00		; data1=0x00
				btfsc		T0,EPSTALL	; check if endpoint is stalled
				movlw		0x01		; if so, data1=0x01
				movwf		POSTINC0
				clrf		INDF0		; data2=0x00

				banksel		BUFFER_DESCRIPTORS
				movlw		0x02		; set byte count to 2
				movwf		BD0ICNT
				movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
				movwf		BD0ISTAT	; send packet as DATA1, set UOWN
				return

				; other state (error)
SREQ_0_2_2			bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
				return

			; other recipient (error)
SREQ_0_3		bsf		UEP0, EPSTALL
			return

		; CLEAR_FEATURE
SREQ_1		movlw		CLEAR_FEATURE
		cpfseq		USB_BUFFER+bRequest
		bra		SREQ_1b

		clrf		T1	; T1 is CLEAR/SET flag
		bra		SREQ_1x

		; SET_FEATURE
SREQ_1b		movlw		SET_FEATURE
		cpfseq		USB_BUFFER+bRequest
		bra		SREQ_2

		setf		T1	; T1 is CLEAR/SET flag

		; either CLEAR_FEATURE or SET_FEATURE (as determined by T0)
SREQ_1x		movf		USB_BUFFER+bmRequestType,W
		andlw		0x1F		; extract request recipient bits
		movwf		T0

			; RECIPIENT_DEVICE
SREQ_1_0		movlw		RECIPIENT_DEVICE
			cpfseq		T0
			bra		SREQ_1_1

				; DEVICE_REMOTE_WAKEUP
SREQ_1_0_0			movlw		DEVICE_REMOTE_WAKEUP
				cpfseq		USB_BUFFER+bRequest,W
				bra		SREQ_1_0_1

				bcf		USB_DEVSTATUS,1	; clear if CLEAR_FEATURE
				btfsc		T1,0		; but if SET_FEATURE
				bsf		USB_DEVSTATUS,1	; then set it

				banksel		BUFFER_DESCRIPTORS
				clrf		BD0ICNT		; byte count = 0
				movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
				movwf		BD0ISTAT	; send packet as DATA1, set UOWN

				return

SREQ_1_0_1			; other feature (error)
				bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
				

			; RECIPIENT_ENDPOINT
SREQ_1_1		movlw		RECIPIENT_ENDPOINT
			cpfseq		T0
			bra		SREQ_1_2

				; ENDPOINT_HALT <TODO: added this check - appropriate? >
; SREQ_1_1_0			movlw		ENDPOINT_HALT
; 				cpfseq		USB_BUFFER+bRequest,W
; 				bra		SREQ_1_1_1

; <BIG TODO: should EP0 support support the ENDPOINT_HALT feature?>
; Docs on EPSTALL bit for SETUP transfers boil down to: 
;/* *************** */
;/* Stall Endpoint. ***************************************************** */
;/* Sets the stall bit in the Endpoint Control Register.  For the control */
;/* Endpoint, this implements a Protocol stall and is used when the request */
;/* is invalid for the current device state.  For non-control Endpoints,  */
;/* this is a Functional Stall, meaning that the device needs outside     */
;/* intervention and trying again later won't help until it's been serviced. */
;/* enter with endpoint # to stall in Wreg.                               */
;/* ********************************************************************* */
; So setting EPSTALL on EP0 should only be temporary anyway (protocol stall),
; not semi-permanent (functional stall). Since EP0 should never functional
; stall anyway, why support ENDPOINT_HALT (to clear the stall)?

; Only Microchip knows.

					; STATE_ADDRESS
SREQ_1_1_0_0				movlw		STATE_ADDRESS
					cpfseq		USBSTATE
					bra		SREQ_1_1_0_1

					movf		USB_BUFFER+wIndex,W	; get EP
					andlw		0x0F		; strip off direction bit
	
					btfss		STATUS,Z	; is it EP0?
					bra		SREQ_1_1_0_2	; no, stall
	
					btfss		T1,0		; CLEAR_FEATURE?
					bcf		UEP0,EPSTALL	; yes, unstall EP0

					btfsc		T1,0		; SET_FEATURE?
					bsf		UEP0,EPSTALL	; yes, stall EP0

					banksel		BUFFER_DESCRIPTORS
					clrf		BD0ICNT		; byte count = 0
					movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
					movwf		BD0ISTAT	; send packet as DATA1, set UOWN
					return

	
	
					; STATE_CONFIGURED
SREQ_1_1_0_1				movlw		STATE_CONFIGURED
					cpfseq		USBSTATE
					bra		SREQ_1_1_0_2
		
					lfsr		FSR0,UEP0	; put UEP0 into FSR0
	
					movf		USB_BUFFER+wIndex,W	; get EP
					andlw		0x0F		; and strip off direction bit
	
					movf		PLUSW0,W	; get UEPx where x=W
					andlw		(1<<EPOUTEN)|(1<<EPINEN)	; mask off EPOUTEN and EPINEN
					btfsc		STATUS,Z	; if endpoint is disabled
					bra		SREQ_1_1_0_2	; stall

					movf		USB_BUFFER+wIndex,W	; get EP
					andlw		0x0F		; and strip off direction bit
	
					btfss		T1,0		; CLEAR_FEATURE?
					bcf		PLUSW0,EPSTALL	; yes, unstall EPx

					btfsc		T1,0		; SET_FEATURE?
					bsf		PLUSW0,EPSTALL	; yes, stall EPx

					banksel		BUFFER_DESCRIPTORS
					clrf		BD0ICNT		; byte count = 0
					movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
					movwf		BD0ISTAT	; send packet as DATA1, set UOWN
					return

					; other state (error)
SREQ_1_1_0_2				bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
					return

				; other feature (error)
SREQ_1_1_1			bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
				return

			; other recipient (error)
SREQ_1_2		bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
			return

		; SET_ADDRESS
SREQ_2		movlw		SET_ADDRESS
		cpfseq		USB_BUFFER+bRequest
		bra		SREQ_3

		btfsc		USB_BUFFER+wValue,7	; if device address is illegal (>127), send Request Error
		bra		SREQ_2x

		movlw		SET_ADDRESS		; processing SET_ADDRESS
		movwf		USB_DEV_REQ

		movff		USB_BUFFER+wValue,USB_ADDR_PEND	;save the new address

		banksel		BUFFER_DESCRIPTORS
		clrf		BD0ICNT		; byte count = 0
		movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
		movwf		BD0ISTAT	; send packet as DATA1, set UOWN
		return

SREQ_2x		bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
		return

		; GET_DESCRIPTOR
SREQ_3		movlw		GET_DESCRIPTOR
		cpfseq		USB_BUFFER+bRequest
		bra		SREQ_4

		movlw		GET_DESCRIPTOR	; processing a GET_DESCRIPTOR request
		movwf		USB_DEV_REQ

			; DEVICE
SREQ_3_0		movlw		DEVICE
			cpfseq		USB_BUFFER+wValueHigh
			bra		SREQ_3_1

			movlw		(DESC_DEVICE-DESCRIPTOR_BEGIN)
			movwf		USB_DESC_PTR
			call		READ_DESCRIPTOR	; get descriptor length
			movwf		USB_BYTES_LEFT

			movf		USB_BUFFER+wLengthHigh,F	; asking for all of it?
			btfss		STATUS,Z
			goto		SEND_DESCRIPTOR_PACKET		; yes (>255)

			movf		USB_BUFFER+wLength,W
			cpfsgt		USB_BYTES_LEFT
			goto		SEND_DESCRIPTOR_PACKET		; yes (>length)

			movwf		USB_BYTES_LEFT			; no (so only send that)
			goto		SEND_DESCRIPTOR_PACKET

			; CONFIGURATION
SREQ_3_1		movlw		CONFIGURATION
			cpfseq		USB_BUFFER+wValueHigh
			bra		SREQ_3_2

SREQ_3_1_0		movlw		0
			cpfseq		USB_BUFFER+wValue		; conf descriptor 0
			bra		SREQ_3_1_1

			movlw		DESC_CONF1-DESCRIPTOR_BEGIN
			btfsc		USB_XBOX_MODE,0
			movlw		DESC_CONF2-DESCRIPTOR_BEGIN
			bra		SREQ_3_1_F

SREQ_3_1_1		bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
			return

SREQ_3_1_F		addlw		0x02		; add offset for wTotalLength
			movwf		USB_DESC_PTR
			call		READ_DESCRIPTOR	; get total descriptor length
			movwf		USB_BYTES_LEFT
			movlw		0x02
			subwf		USB_DESC_PTR,F	; subtract offset back to normal

			movf		USB_BUFFER+wLengthHigh,F	; asking for all of it?
			btfss		STATUS,Z
			goto		SEND_DESCRIPTOR_PACKET		; yes (>255)

			movf		USB_BUFFER+wLength,W
			cpfsgt		USB_BYTES_LEFT
			goto		SEND_DESCRIPTOR_PACKET		; yes (>length)

			movwf		USB_BYTES_LEFT			; no (so only send that)
			goto		SEND_DESCRIPTOR_PACKET

			; STRING
SREQ_3_2		movlw		STRING
			cpfseq		USB_BUFFER+wValueHigh
			bra		SREQ_3_3

SREQ_3_2_0		movlw		0
			cpfseq		USB_BUFFER+wValue		; string descriptor 0
			bra		SREQ_3_2_1
			movlw		DESC_STRING0-DESCRIPTOR_BEGIN
			bra		SREQ_3_2_F

SREQ_3_2_1		movlw		1
			cpfseq		USB_BUFFER+wValue		; string descriptor 1
			bra		SREQ_3_2_2
			movlw		DESC_STRING1-DESCRIPTOR_BEGIN
			bra		SREQ_3_2_F

SREQ_3_2_2		movlw		2
			cpfseq		USB_BUFFER+wValue		; string descriptor 2
			bra		SREQ_3_2_3
			movlw		DESC_STRING2-DESCRIPTOR_BEGIN
			bra		SREQ_3_2_F

SREQ_3_2_3		bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
			return

SREQ_3_2_F		movwf		USB_DESC_PTR
			call		READ_DESCRIPTOR	; get total descriptor length
			movwf		USB_BYTES_LEFT

			movf		USB_BUFFER+wLengthHigh,F	; asking for all of it?
			btfss		STATUS,Z
			goto		SEND_DESCRIPTOR_PACKET		; yes (>255)

			movf		USB_BUFFER+wLength,W
			cpfsgt		USB_BYTES_LEFT
			goto		SEND_DESCRIPTOR_PACKET		; yes (>length)

			movwf		USB_BYTES_LEFT			; no (so only send that)
			goto		SEND_DESCRIPTOR_PACKET


			; HID
SREQ_3_3		movlw		HID
			cpfseq		USB_BUFFER+wValueHigh
			bra		SREQ_3_4

SREQ_3_3_0		movlw		0
			cpfseq		USB_BUFFER+wValue		; conf descriptor 0
			bra		SREQ_3_3_1
			movlw		DESC_HID1-DESCRIPTOR_BEGIN
			bra		SREQ_3_3_F

SREQ_3_3_1		bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
			return

SREQ_3_3_F		movwf		USB_DESC_PTR
			call		READ_DESCRIPTOR	; get total descriptor length
			movwf		USB_BYTES_LEFT

			movf		USB_BUFFER+wLengthHigh,F	; asking for all of it?
			btfss		STATUS,Z
			goto		SEND_DESCRIPTOR_PACKET		; yes (>255)

			movf		USB_BUFFER+wLength,W
			cpfsgt		USB_BYTES_LEFT
			goto		SEND_DESCRIPTOR_PACKET		; yes (>length)

			movwf		USB_BYTES_LEFT			; no (so only send that)
			goto		SEND_DESCRIPTOR_PACKET

			; REPORT
SREQ_3_4		movlw		REPORT
			cpfseq		USB_BUFFER+wValueHigh
			bra		SREQ_3_5

			btfsc		USB_XBOX_MODE,0
			bra		SREQ_3_4_1

SREQ_3_4_0		movlw		0
			cpfseq		USB_BUFFER+wValue		; report descriptor 0
			bra		SREQ_3_4_1
			movlw		DESC_REPORT1-DESCRIPTOR_BEGIN
			bra		SREQ_3_4_F

SREQ_3_4_1		bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
			return

SREQ_3_4_F		movwf		USB_DESC_PTR
			call		READ_DESCRIPTOR	; get total descriptor length
			addlw		0
			movwf		USB_BYTES_LEFT

			incf		USB_DESC_PTR,F	; jump length bytes
			incf		USB_DESC_PTR,F	; jump length bytes

			movf		USB_BUFFER+wLengthHigh,F	; asking for all of it?
			btfss		STATUS,Z
			goto		SEND_DESCRIPTOR_PACKET		; yes (>255)

			movf		USB_BUFFER+wLength,W
			cpfsgt		USB_BYTES_LEFT
			goto		SEND_DESCRIPTOR_PACKET		; yes (>length)

			movwf		USB_BYTES_LEFT			; no (so only send that)
			goto		SEND_DESCRIPTOR_PACKET

			; other descriptor (error)
SREQ_3_5		bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
			return

		; GET_CONFIGURATION
SREQ_4		movlw		GET_CONFIGURATION
		cpfseq		USB_BUFFER+bRequest
		bra		SREQ_5

		banksel		BUFFER_DESCRIPTORS
		movf		BD0IADRH,W
		movwf		FSR0H
		movf		BD0IADRL,W
		movwf		FSR0L		; buffer address -> FSR0

		banksel		USB_CURCONFIG	; get current config into buffer
		movf		USB_CURCONFIG,W
		movwf		INDF0

		banksel		BUFFER_DESCRIPTORS
		movlw		0x01		; size = 0x01
		movwf		BD0ICNT
		movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
		movwf		BD0ISTAT	; send packet as DATA1, set UOWN

		return

		; SET_CONFIGURATION
SREQ_5		movlw		SET_CONFIGURATION
		cpfseq		USB_BUFFER+bRequest
		bra		SREQ_6

		movlw		NUM_CONFIGURATIONS	; >NUM_CONFIGURATIONS?
		cpfsgt		USB_BUFFER+wValue
		bra		SREQ_5_OK		; no, OK

		bsf		UEP0,EPSTALL		; yes, error. Stall EP0 to indicate Request Error.
		return

SREQ_5_OK	movf		USB_BUFFER+wValue,W
		movwf		USB_CURCONFIG		; ==0?
		bnz		SREQ_5_CONFIG		; no, configured

SREQ_5_UNCONFIG		SETSTATE	STATE_ADDRESS		; yes, unconfigured.
			movlw		STATE_ADDRESS
			movwf		USBSTATE
			bra		SREQ_5_DONE

SREQ_5_CONFIG		SETSTATE	STATE_CONFIGURED
			movlw		STATE_CONFIGURED
			movwf		USBSTATE

			banksel		BUFFER_DESCRIPTORS
			movlw		20		; byte count = 20
			movwf		BD1ICNT
			movlw		LOW EP1IBUF	; give EP1 a buffer
			movwf		BD1IADRL
			movlw		HIGH EP1IBUF
			movwf		BD1IADRH
			movlw		(0<<UOWN)|(1<<DTS)|(1<<DTSEN)	; clear UOWN, buffer is MCU's
			movwf		BD1ISTAT
			movlw		(1<<EPHSHK)|(0<<EPCONDIS)|(0<<EPOUTEN)|(1<<EPINEN)	; only enable for interrupt in transfers
			movwf		UEP1

			movf		USB_XBOX_MODE,F
			bz		SREQ_5_DONE

			movlw		6		; byte count = 6
			movwf		BD2OCNT
			movlw		LOW EP2OBUF	; give EP2 a buffer
			movwf		BD2OADRL
			movlw		HIGH EP2OBUF
			movwf		BD2OADRH
			movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)	; set UOWN, buffer is SIE's
			movwf		BD2OSTAT
			movlw		(1<<EPHSHK)|(1<<EPCONDIS)|(1<<EPOUTEN)|(0<<EPINEN)	; only enable for interrupt out transfers
			movwf		UEP2

SREQ_5_DONE	banksel		BUFFER_DESCRIPTORS
		clrf		BD0ICNT		; byte count = 0
		movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
		movwf		BD0ISTAT	; send packet as DATA1, set UOWN

		return

		; GET_INTERFACE
SREQ_6		movlw		GET_INTERFACE
		cpfseq		USB_BUFFER+bRequest
		bra		SREQ_7

		movlw		STATE_CONFIGURED
		cpfseq		USBSTATE
		bra		SREQ_6_ERR	; only in configured state
	
SREQ_6_CONF		movf		USB_BUFFER+wIndex,F
			bnz		SREQ_6_ERR	; only interface 0 allowed

			banksel		BUFFER_DESCRIPTORS
			movf		BD0IADRH,W
			movwf		FSR0H
			movf		BD0IADRL,W
			movwf		FSR0L		; buffer address -> FSR0
	
			clrf		INDF0		; response is always 0
	
			movlw		0x01		; size = 0x01
			movwf		BD0ICNT
			movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
			movwf		BD0ISTAT	; send packet as DATA1, set UOWN
	
			return

SREQ_6_ERR		bsf		UEP0,EPSTALL		; Stall EP0 to indicate Request Error.
			return

		; SET_INTERFACE
SREQ_7		movlw		SET_INTERFACE
		cpfseq		USB_BUFFER+bRequest
		bra		SREQ_8

		movlw		STATE_CONFIGURED
		cpfseq		USBSTATE
		bra		SREQ_7_ERR	; only in configured state
	
			movlw		NUM_INTERFACES		; <NUM_INTERFACES?
			cpfslt		USB_BUFFER+wValue
			bra		SREQ_7_ERR		; no, error
	
			banksel		BUFFER_DESCRIPTORS	; yes, OK. Only one interface, nothing to do.
			clrf		BD0ICNT		; byte count = 0
			movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
			movwf		BD0ISTAT	; send packet as DATA1, set UOWN
	
			return

SREQ_7_ERR		bsf		UEP0,EPSTALL		; Stall EP0 to indicate Request Error.
			return

		; other request (error)
SREQ_8		bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
		return


CLASS_REQUEST
		banksel		USB_BUFFER

		; GET_REPORT
CREQ_0		movlw		GET_REPORT
		cpfseq		USB_BUFFER+bRequest
		bra		CREQ_1

		banksel		BUFFER_DESCRIPTORS
		movf		BD0IADRH,W
		movwf		FSR0H
		movf		BD0IADRL,W
		movwf		FSR0L

		lfsr		FSR1,HID_REPORT

		movlw		20
CREQ_0_LOOP	movff		POSTINC1,POSTINC0
		decfsz		WREG,F
		bra		CREQ_0_LOOP
		
		movlw		20
		movwf		BD0ICNT
		movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
		movwf		BD0ISTAT	; send packet as DATA1, set UOWN

		return

		; SET_REPORT
CREQ_1		movlw		SET_REPORT
		cpfseq		USB_BUFFER+bRequest
		bra		CREQ_2

		movlw		SET_REPORT
		movwf		USB_DEV_REQ	; processing a SET_REPORT request

		return

		; GET_PROTOCOL
CREQ_2		; commented out as this is not a Boot subclass device, and thus has only one protocol
; 		movlw		GET_PROTOCOL
; 		cpfseq		USB_BUFFER+bRequest
; 		bra		CREQ_3
; 
; 		banksel		BUFFER_DESCRIPTORS
; 		movf		BD0IADRH,W
; 		movwf		FSR0H
; 		movf		BD0IADRL,W
; 		movwf		FSR0L
; 
; 		banksel		USB_PROTO
; 		movf		USB_PROTO,W
; 		movwf		INDF0
; 		
; 		movlw		1
; 		movwf		BD0ICNT
; 		movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
; 		movwf		BD0ISTAT	; send packet as DATA1, set UOWN
; 
; 		return
; 
; 		; SET_PROTOCOL
; CREQ_3		movlw		SET_PROTOCOL
; 		cpfseq		USB_BUFFER+bRequest
; 		bra		CREQ_4
; 
; 		movf		USB_BUFFER+wValue,W	; update new protocol value
; 		movwf		USB_PROTO
; 
; 		movlw		0
; 		movwf		BD0ICNT
; 		movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
; 		movwf		BD0ISTAT	; send packet as DATA1, set UOWN

		; GET_IDLE
CREQ_4		movlw		GET_IDLE
		cpfseq		USB_BUFFER+bRequest
		bra		CREQ_5

		banksel		BUFFER_DESCRIPTORS
		movf		BD0IADRH,W
		movwf		FSR0H
		movf		BD0IADRL,W
		movwf		FSR0L

		banksel		USB_IDLERATE
		movf		USB_IDLERATE,W
		movwf		INDF0
		
		banksel		BUFFER_DESCRIPTORS
		movlw		1
		movwf		BD0ICNT
		movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
		movwf		BD0ISTAT	; send packet as DATA1, set UOWN

		return


		; SET_IDLE
CREQ_5		movlw		SET_IDLE
		cpfseq		USB_BUFFER+bRequest
		bra		CREQ_6

		movf		USB_BUFFER+wValue+1,W	; update new idle rate value
		movwf		USB_IDLERATE

		banksel		BUFFER_DESCRIPTORS
		movlw		0
		movwf		BD0ICNT
		movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
		movwf		BD0ISTAT	; send packet as DATA1, set UOWN

		return

		; other request (error)
CREQ_6		bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
		return

VENDOR_REQUEST		; noone supported
		bsf		UEP0, EPSTALL	; stall EP0 to signify Request Error
		return

PROCESS_IN
		banksel		USB_USTAT
		movf		USB_USTAT, W
		andlw		0x18		; extract the EP bits
		movwf		T0

		; EP0
PIN_0		movlw		EP0
		cpfseq		T0
		bra		PIN_1

			; SET_ADDRESS
PIN_0_0			movlw		SET_ADDRESS
			cpfseq		USB_DEV_REQ
			bra		PIN_0_1

			movf		USB_ADDR_PEND,W
			movwf		UADDR

			btfss		STATUS,Z	; is address 0?
			bra		PIN_0_0_NZ

			SETSTATE	STATE_DEFAULT
			movlw		STATE_DEFAULT
			movwf		USBSTATE
			return

PIN_0_0_NZ		SETSTATE	STATE_ADDRESS
			movlw		STATE_ADDRESS
			movwf		USBSTATE
			return

			; GET_DESCRIPTOR
PIN_0_1			movlw		GET_DESCRIPTOR
			cpfseq		USB_DEV_REQ
			bra		PIN_0_2

			goto		SEND_DESCRIPTOR_PACKET

			; something else
PIN_0_2			return

		; EP1
PIN_1		movlw		EP1
		cpfseq		T0
		bra		PIN_2

		return

		; EP2
PIN_2		movlw		EP2
		cpfseq		T0
		bra		PIN_3

		return

PIN_3		return



PROCESS_OUT
		banksel		USB_USTAT
		movf		USB_USTAT, W
		andlw		0x18		; extract the EP bits
		movwf		T0


		; EP0
POUT_0		movlw		EP0
		cpfseq		T0
		bra		POUT_1

			; SET_REPORT
POUT_0_0		movlw		SET_REPORT
			cpfseq		USB_DEV_REQ
			bra		POUT_0_1

			movlw		NO_REQUEST
			movwf		USB_DEV_REQ	; clear device request

			banksel		BUFFER_DESCRIPTORS
			movf		BD0OADRH,W
			movwf		FSR0H
			movf		BD0OADRL,W
			movwf		FSR0L
			movf		INDF0,W
			; <TODO: do something with OUT requests, like use them for the pad lights>

POUT_0_1		; something else

		banksel		BUFFER_DESCRIPTORS
		movlw		EP0MAXPS
		movwf		BD0OCNT
		movlw		(1<<UOWN)|(1<<DTSEN)
		movwf		BD0OSTAT
		clrf		BD0ICNT
		movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
		movwf		BD0ISTAT
		return

		; EP1
POUT_1		movlw		EP1
		cpfseq		T0
		bra		POUT_2

		return

		; EP2
POUT_2		movlw		EP2
		cpfseq		T0
		bra		POUT_3

		banksel		BUFFER_DESCRIPTORS
		movf		BD2OADRH,W
		movwf		FSR0H
		movf		BD2OADRL,W
		movwf		FSR0L
		movf		INDF0,W
		; <TODO: do something with OUT requests, like use them for the pad lights>

		movlw		6
		movwf		BD2OCNT		; reset the byte count

		movf		BD2OSTAT, W
		xorlw		(1<<DTS)		; toggle the DATA01 bit
		andlw		(1<<DTS)		; clear the PID bits
		iorlw		(1<<UOWN)|(1<<DTSEN)	; set UOWN and DTSEN bits
		movwf		BD2OSTAT

		;movlw		(1<<UOWN)|(1<<DTS)|(1<<DTSEN)
		;movwf		BD2OSTAT	; return the buffer to the SIE

		incf		USB_INCNT,F


		return

POUT_3		return



SEND_DESCRIPTOR_PACKET
		banksel		USB_BYTES_LEFT

		movlw		EP0MAXPS	; packet <max?
		cpfslt		USB_BYTES_LEFT
		bra		SDESC_LONG

		movlw		NO_REQUEST	; short packet, so clear current request
		movwf		USB_DEV_REQ

		movf		USB_BYTES_LEFT,W
		clrf		USB_BYTES_LEFT

		bra		SDESC_DO

SDESC_LONG	movlw		EP0MAXPS
		movwf		T0
		subwf		USB_BYTES_LEFT,F

SDESC_DO
		banksel		BUFFER_DESCRIPTORS
		movwf		BD0ICNT		; store byte count
		movf		BD0IADRH,W	; and put address
		movwf		FSR0H		; into FSR0
		movf		BD0IADRL,W
		movwf		FSR0L

		banksel		USB_DESC_PTR
SDESC_LOOP	call		READ_DESCRIPTOR	; loop copying descriptor to buffer
		movwf		POSTINC0
		incf		USB_DESC_PTR,F
		decfsz		T0,F
		bra		SDESC_LOOP

		banksel		BUFFER_DESCRIPTORS
		movf		BD0ISTAT, W
		xorlw		(1<<DTS)		; toggle the DATA01 bit
		andlw		(1<<DTS)		; clear the PID bits
		iorlw		(1<<UOWN)|(1<<DTSEN)	; set UOWN and DTSEN bits
		movwf		BD0ISTAT

		return

		GLOBAL		SEND_REPORT_LOOP,SEND_REPORT_SAFE

SEND_REPORT_LOOP
		call		SEND_REPORT_SAFE
		btfsc		WREG,0
		return

			;256 cycles
		movlw	0x55
DELAYREP
		decfsz	WREG, f
		goto	DELAYREP

		bra		SEND_REPORT_LOOP


SEND_REPORT_SAFE

		banksel		BUFFER_DESCRIPTORS

		btfsc		BD1ISTAT,UOWN	; do we own it?
		bra		NOT_OWNED	; nope

		call		SEND_REPORT	; yes

		clrf		USB_NOINCNT

		btg		HID_REPORT+10,0

		retlw		1

NOT_OWNED

		incfsz		USB_NOINCNT,F
		retlw		0
		decf		USB_NOINCNT,F
		retlw		0


SEND_REPORT	banksel		BUFFER_DESCRIPTORS
		movf		BD1IADRH,W
		movwf		FSR0H
		movf		BD1IADRL,W
		movwf		FSR0L

		lfsr		FSR1,HID_REPORT

		movlw		20
SREP_0_LOOP	movff		POSTINC1,POSTINC0
		decfsz		WREG,F
		bra		SREP_0_LOOP
		
		movlw		20
		movwf		BD1ICNT

		movf		BD1ISTAT, W
		xorlw		(1<<DTS)		; toggle the DATA01 bit
		andlw		(1<<DTS)		; clear the PID bits
		iorlw		(1<<UOWN)|(1<<DTSEN)	; set UOWN and DTSEN bits
		movwf		BD1ISTAT

		return

		END

