; Software License Agreement ; ; The software supplied herewith by Microchip Technology Incorporated (the "Company") ; for its PICmicro® Microcontroller is intended and supplied to you, the Company’s ; customer, for use solely and exclusively on Microchip PICmicro Microcontroller ; products. ; ; The software is owned by the Company and/or its supplier, and is protected under ; applicable copyright laws. All rights are reserved. Any use in violation of the ; foregoing restrictions may subject the user to criminal sanctions under applicable ; laws, as well as to civil liability for the breach of the terms and conditions of ; this license. ; ; THIS SOFTWARE IS PROVIDED IN AN "AS IS" CONDITION. NO WARRANTIES, WHETHER EXPRESS, ; IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF ; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE ; COMPANY SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR ; CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. ; ; ############################################################################### ; filename: USB_CH9.ASM ; USB Human Interface Device (HID) class specific ; commands ; ; Implements the chapter 9 enumeration commands for Microchip's ; PIC16C7x5 parts. ; ; Control is routed here when a token done interrupt is received and ; the bit 5 of the RequestType byte is set (0x20). Prior to this, the ; EP0 buffer has been copied to common RAM. Data in the ; ; ############################################################################### ; ; Author: Dan Butler ; Company: Microchip Technology Inc ; ; Revision: 1.10 ; Date: 07-December-2000 ; Assembled using MPASM 2.30.07 ; Revision History: ; 23-August-2000 DZB Changed descriptor pointers to 16 bits. ; 24-August-2000 DZB Moved EP1 & 2 configuration from USBReset ; to Set_Configuration to implement requirement in ; USB V1.1 spec paragraph 5.3.1.2 ; 28-August-2000 DZB Force data toggle on OUT packets in PutUSB ; ;################################################################################ ; ; include files: ; P16C745.inc Rev 1.00 ; usb_defs.inc Rev 1.00 ; ;################################################################################ #include #include "usb_defs.inc" errorlevel -302 ; supress "register not in bank0, check page bits" message #define COUNTERRORS ; comment this out to reclaim space occupied by error counters ;#define FUNCTIONIDS ; comment this out to reclaim space occupied by maintaining the USWSTAT register unbanked udata_shr ; these will get assigned to unbanked RAM (0x70-0x7F) BufferDescriptor res 3 BufferData res 8 temp res 1 ; short term temp register used in Get Interface global BufferDescriptor global BufferData global temp global EP0_maxLength global EP0_start global EP0_end global copy_string_descriptor_to_EP0 bank2 udata USBMaskedInterrupts res 1 USB_Curr_Config res 1 USB_status_device res 1 ; status of device USB_dev_req res 1 USB_address_pending res 1 USBMaskedErrors res 1 PIDs res 1 EP0_start res 2 ; pointer to first byte of data to send EP0_end res 2 ; pointer to last byt of data to send EP0_maxLength res 1 bufindex res 1 USB_Interface res 3 ; allow 3 interfaces to have alternate endpoints inner res 1 outer res 1 dest_ptr res 1 ; used in buffer copies for Get and source_ptr res 1 ; put USB calls counter res 1 bytecounter res 1 ; saved copy that will be returned in W RP_save res 1 ; save bank bits while copying buffers ;EP_save res 1 IS_IDLE res 1 USB_USTAT res 1 ; copy of the USTAT register before clearing TOK_DNE global USB_Curr_Config global USB_status_device global USB_dev_req global USB_Interface #ifdef COUNTERRORS USB_PID_ERR res 2 ; 16 bit counters for each error condition USB_CRC5_ERR res 2 USB_CRC16_ERR res 2 USB_DFN8_ERR res 2 USB_BTO_ERR res 2 USB_WRT_ERR res 2 USB_OWN_ERR res 2 USB_BTS_ERR res 2 #endif extern Config_desc_index extern Descriptions extern string_index extern StringDescriptors extern CheckVendor extern SetConfiguration ;extern ClassSpecificRequest extern GetClassSpecificDescriptor extern Check_Class_Specific_IN extern Get_Report_Descriptor ; ********************************************************************** ; This section contains the functions to interface with the main ; application. ; ********************************************************************** interface code ; ********************************************************************** ; Enter with Endpoint number in W reg and buffer pointer in IRP+FSR. ; Checks the semaphore for the OUT endpoint, and copies the buffer ; if available. Restores the bank bits as we found them. ; ; This function can be assembled for Endpoint 1 or 2 by changing the ; OUTENDPOINT variable below. ; ; Returns the bytecount in the W register and return status in the carry ; bit as follows: ; 0 - no buffer available, ; 1 - Buffer copied and buffer made available for next transfer. ; Also, number of bytes moved is returned in W reg ; ********************************************************************** OUTENDPOINT set 1 INENDPOINT set 1 GetEP#V(OUTENDPOINT) global GetEP#V(OUTENDPOINT) movf STATUS,w banksel RP_save ; Bank 2 movwf RP_save ; Reg page stored on page 2 movf FSR,w movwf dest_ptr ; save the buffer destination pointer bsf STATUS,RP0 ; bank 3 btfsc BD#V(OUTENDPOINT)OST,UOWN ; Has the buffer been filled? goto nobuffer ; nope, OWN = 1 ==> SIE owns the buffer ; Yep: OWN = 0 ==> PIC owns the buffer movf BD#V(OUTENDPOINT)OBC,w ; get byte count bcf STATUS,RP0 ; bank 2 movwf counter movwf bytecounter ; # of bytes that will be moved btfsc STATUS,Z ; is it a zero length buffer? goto exitgetloop ; yes, bail out now and avoid the rush bsf STATUS,RP0 ; bank 2 movf BD#V(OUTENDPOINT)OAL,w ; get address pointer bcf STATUS,RP0 ; bank 2 movwf source_ptr ; This loop operates with the direct bank bits set to bank 2, while IRP ; gets switched as needed to for the buffer copy getEPloop bsf STATUS,IRP ; select high banks on INDF movf source_ptr,w ; get source pointer movwf FSR movf INDF,w movwf temp ; in common RAM to avoid paging issues movf dest_ptr,w movwf FSR btfss RP_save,IRP ; should it be zero? bcf STATUS,IRP ; yes: make it so. movf temp,w ; no, get the byte we read. movwf INDF ; store it incf dest_ptr,f incf source_ptr,f decfsz counter,f goto getEPloop exitgetloop bsf STATUS,RP0 ; bank 3 movf BD#V(OUTENDPOINT)OST,w andlw 0x40 ; save only the data 0/1 bit xorlw 0x40 ; toggle the data o/1 bit iorlw 0x88 ; set owns bit and DTS bit movwf BD#V(OUTENDPOINT)OST movlw 0x08 ; reset byte counter movwf BD#V(OUTENDPOINT)OBC bcf STATUS,RP0 ; bank 2 movf bytecounter,w ; return # of bytes moved in W reg movwf temp ; need in unbanked RAM movf RP_save,w ; restore bank bits movwf STATUS movf temp,w ; get bytecount from unbanked RAM bsf STATUS,C ; signal success return nobuffer movf RP_save,w ; restore bank bits movwf STATUS bcf STATUS,C return ; ****************************************************************** ; Enter with bytecount in W and buffer pointer in IRP+FSR. ; the bytecount is encoded in the lower nybble of W. ; ; This function can be assembled for Endpoint 1 or 2 by changing the ; INENDPOINT variable below. ; ; Tests the owns bit for the IN side of the specified Endpoint. ; If we own the buffer, the buffer pointed to by the FSR is copied ; to the EPn In buffer, then the owns bit is set so the data will be ; TX'd next time polled. ; ; Returns the status in the carry bit as follows: ; 1 - buffer available and copied. ; 0 - buffer not available (try again later) ; ****************************************************************** PutEP#V(INENDPOINT) global PutEP#V(INENDPOINT) movwf temp ; save Bytecount temporarily in common RAM movf STATUS,w ; save bank bits before we trash them banksel RP_save ; switch to bank 2 movwf RP_save movf temp,w andlw 0x0F ; extract byte count. movwf counter movf FSR,w movwf source_ptr movf counter,w ; prepare to copy the byte count banksel BD1IST ; bank 3 btfsc BD#V(INENDPOINT)IST,UOWN ; is the buffer already full? goto nobufferputep1 ; yes - don't write over it movwf BD#V(INENDPOINT)IBC ; set byte count in BD btfsc STATUS,Z ; is it a zero length buffer? goto exitputloop ; yes, bail out now and avoid the rush movf BD#V(INENDPOINT)IAL,w ; get address pointer bcf STATUS,RP0 ; back to bank 2 movwf dest_ptr ; This loop operates with the direct bits set to bank 2, while IRP ; gets switched as needed to for the buffer copy PutEPloop bsf STATUS,IRP ; assume IRP is set btfss RP_save,IRP ; should it be zero? bcf STATUS,IRP ; yes: make it so. movf source_ptr,w movwf FSR movf INDF,w movwf temp bsf STATUS,IRP ; select high banks on INDF movf dest_ptr,w movwf FSR movf temp,w ; no, get the byte we read. movwf INDF ; store it incf dest_ptr,f incf source_ptr,f decfsz counter,f goto PutEPloop exitputloop bsf STATUS,RP0 ; back to bank 3 movf BD#V(INENDPOINT)IST,w andlw 0x40 ; save only the data 0/1 bit xorlw 0x40 ; toggle the data o/1 bit iorlw 0x88 ; set owns bit and DTS bit movwf BD#V(INENDPOINT)IST movf RP_save,w ; restore bank bits the way we found them movwf STATUS bsf STATUS,C ; set carry to show success return nobufferputep1 bcf STATUS,C return ; ********************************************************************* ; 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. ; ********************************************************************* StallUSBEP bsf STATUS,IRP ; select banks 2/3 andlw 0x03 ; try to keep things under control addlw low UEP0 ; add address of endpoint control reg movwf FSR bsf INDF,EP_STALL ; set stall bit return ; ********************************************************************* ; Unstall 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. ; ********************************************************************* UnstallUSBEP bsf STATUS,IRP ; select banks 2/3 andlw 0x03 ; try to keep things under control addlw low UEP0 ; add address of endpoint control reg movwf FSR bcf INDF,EP_STALL ; clear stall bit return ; ********************************************************************* ; CheckSleep ; Checks the USB Sleep bit. If the bit is set, the ; 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. ; ********************************************************************* CheckSleep global CheckSleep movf STATUS,w ; save the register bank bits movwf RP_save banksel IS_IDLE btfss IS_IDLE,0 ; test the bus idle bit goto nosleeptoday bsf STATUS,RP0 ; point to bank 3 bcf UIR,ACTIVITY bsf UIE,ACTIVITY ; enable the USB activity interrupt bsf UCTRL,SUSPND ; put USB regulator and transciever in low power state sleep ; and go to sleep nop bcf UCTRL,SUSPND bcf UIR,UIDLE bsf UIE,UIDLE bcf UIR,ACTIVITY bsf UIE,ACTIVITY comf PORTA,f nosleeptoday movf RP_save,w ; restore bank bits movwf STATUS return ; ********************************************************************* ; Remote Wakeup ; Checks USB_status_device to see if the host enabled Remote Wakeup ; If so, perform Remote wakeup and disable remote wakeup feature ; It is called by PortBChange ; ********************************************************************* RemoteWakeup global RemoteWakeup movf STATUS,w ; save the register bank bits bsf STATUS,RP1 bcf STATUS,RP0 movwf RP_save btfss USB_status_device, 1 goto RemoteEnd clrf inner movlw 0xB0 movwf outer bsf STATUS, RP0 bsf UCTRL, 2 bcf STATUS, RP0 incfsz inner,f goto $-1 incfsz outer,f goto $-3 bsf STATUS,RP0 bcf UCTRL, 2 bcf STATUS,RP0 bcf USB_status_device, 1 RemoteEnd movf RP_save,w ; restore bank bits movwf STATUS return ; ********************************************************************* ; USB Soft Detach ; Clears the DEV_ATT bit, electrically disconnecting the device to the bus. ; This removes the device from the bus, then reconnects so it can be ; re-enumerated by the host. This is envisioned as a last ditch effort ; by the software. ; ********************************************************************* SoftDetachUSB banksel UCTRL bcf UCTRL,DEV_ATT ; set attach bit banksel outer clrf outer OuterLoop clrwdt ; clear the watchdog timer clrf inner InnerLoop incfsz inner,f goto InnerLoop incfsz outer,f goto OuterLoop pagesel InitUSB call InitUSB ; reinitialize the USB peripheral return core code ; The functions below are the core functions ; ****************************************************************** ; USB interrupt triggered, OK, why? ; OK, we've identified that we're servicing a USB interrupt. ; ****************************************************************** ServiceUSBInt global ServiceUSBInt bcf PIR1,USBIF ; clear USB interrupt flag banksel UIR ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw SVCUSBINT ; iorwf USWSTAT,f ; place in upper 6 bits of register #endif movf UIR,w ; get the USB interrupt register andwf UIE,w ; mask off the disabled interrupts banksel USBMaskedInterrupts ; select bank 2 movwf USBMaskedInterrupts btfsc USBMaskedInterrupts,TOK_DNE ; was it a token done? goto TokenDone btfsc USBMaskedInterrupts,USB_RST goto USBReset btfsc USBMaskedInterrupts,STALL goto USBStall btfsc USBMaskedInterrupts,UERR goto USBError btfsc USBMaskedInterrupts,UIDLE goto USBSleep btfsc USBMaskedInterrupts,ACTIVITY goto USBActivity return ; ****************************************************************** ; USB Reset interrupt triggered (SE0) ; initialize the Buffer Descriptor Table, ; Transition to the DEFAULT state, ; Set address to 0 ; enable the USB ; ****************************************************************** USBReset #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw SVCRESET ; iorwf USWSTAT,f ; place in upper 6 bits of register banksel USB_Curr_Config ; select bank 3 #endif clrf USB_Curr_Config clrf IS_IDLE banksel UIR ; select bank 3 bcf UIR,TOK_DNE ; hit this 4 times to clear out the bcf UIR,TOK_DNE ; USTAT FIFO bcf UIR,TOK_DNE bcf UIR,TOK_DNE movlw 0x88 ; set owns bit (SIE can write) movwf BD0OST movlw USB_Buffer ; Endpoint 0 OUT gets a buffer movwf BD0OAL ; set up buffer address movlw 8 movwf BD0OBC ; set byte count movlw 0x08 ; Clear owns bit (PIC can write) movwf BD0IST movlw USB_Buffer+8 ; Endpoint 0 OUT gets a buffer movwf BD0IAL ; set up buffer address ; movlw 8 ; no need to do these here... Byte Count gets ; movwf BD0OBC ; set in when the buffer is filled. clrf UADDR ; set USB Address to 0 bcf UIR,USB_RST ; clear reset flag ; Set up the Endpoint Control Registers. The following patterns are defined ; ENDPT_DISABLED - endpoint not used ; ENDPT_IN_ONLY - endpoint supports IN transactions only ; ENDPT_OUT_ONLY - endpoint supports OUT transactions only ; ENDPT_CONTROL - Supports IN, OUT and CONTROL transactions - Only use with EP0 ; ENDPT_NON_CONTROL - Supports both IN and OUT transactions movlw ENDPT_CONTROL movwf UEP0 ; endpoint 0 is a control pipe and requires an ACK movlw 0x3B ; enable all interrupts except activity movwf UIE movlw 0xFF ; enable all error interrupts movwf UEIE movlw 0xfc andwf USWSTAT,f ; save high order 6 bits movlw DEFAULT_STATE iorwf USWSTAT,f bcf STATUS,RP0 ; select bank 2 bcf STATUS,RP1 ; bank 0 return ; to keep straight with host controller tests ; ****************************************************************** ; Enable Wakeup on interupt and Activity interrupt then put the ; device to sleep to save power. Activity on the D+/D- lines will ; set the ACTIVITY interrupt, waking up the part. ; ****************************************************************** USBSleep banksel UIR ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw SVCSLEEP iorwf USWSTAT,f ; place in upper 6 bits of register #endif bcf UIR,UIDLE clrf STATUS ; bank 0 banksel IS_IDLE bsf IS_IDLE, 0 return ; ****************************************************************** ; This is activated by the STALL bit in the UIR register. It really ; just tells us that the SIE sent a STALL handshake. So far, Don't c; see that any action is required. Clear the bit and move on. ; ****************************************************************** USBStall banksel UIR ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw SVCSTALL iorwf USWSTAT,f ; place in upper 6 bits of register #endif bcf UIR,STALL ; clear the stall bit clrf STATUS ; bank 0 return ; ****************************************************************** ; The SIE detected an error. This code increments the appropriate ; error counter and clears the flag. ; ****************************************************************** USBError #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw SVCERROR iorwf USWSTAT,f ; place in upper 6 bits of register #endif #ifdef COUNTERRORS movf UEIR,w ; get the error register andwf UEIE,w ; mask with the enables clrf UEIR bcf STATUS,RP0 ; bank 2 movwf USBMaskedErrors btfss USBMaskedErrors,PID_ERR goto CRC5Error INCREMENT16 USB_PID_ERR CRC5Error btfss USBMaskedErrors,CRC5 goto CRC16Error INCREMENT16 USB_CRC5_ERR CRC16Error btfss USBMaskedErrors,CRC16 goto DFN8Error INCREMENT16 USB_CRC16_ERR DFN8Error btfss USBMaskedErrors,DFN8 goto BTOError INCREMENT16 USB_DFN8_ERR BTOError btfss USBMaskedErrors,BTO_ERR goto WRTError INCREMENT16 USB_BTO_ERR WRTError btfss USBMaskedErrors,WRT_ERR goto OWNError INCREMENT16 USB_WRT_ERR OWNError btfss USBMaskedErrors,OWN_ERR goto BTSError INCREMENT16 USB_OWN_ERR BTSError btfss USBMaskedErrors,BTS_ERR goto EndError INCREMENT16 USB_BTS_ERR EndError #endif bsf STATUS,RP0 ; bank 3 bcf UIR,UERR clrf STATUS ; bank 0 return ; ****************************************************************** ; Service the Activity Interrupt. This is only enabled when the ; device is put to sleep as a result of inactivity on the bus. This ; code wakes up the part, disables the activity interrupt and reenables ; the idle interrupt. ; ****************************************************************** USBActivity banksel UIR ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw SVCACTIVITY iorwf USWSTAT,f ; place in upper 6 bits of register #endif bcf UIR,ACTIVITY bcf UIE,ACTIVITY bcf UIR,UIDLE bsf UIE,UIDLE banksel IS_IDLE clrf IS_IDLE clrf STATUS ; bank 0 return ; ****************************************************************** ; Process token done interrupt... Most of the work gets done through ; this interrupt. Token Done is signaled in response to an In, Out, ; or Setup transaction. ; ****************************************************************** TokenDone #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw SVCTOKENDONE iorwf USWSTAT,f ; place in upper 6 bits of register #endif COPYBUFFERDESCRIPTOR ; copy BD from dual port to unbanked RAM banksel USTAT ; select bank 3 movf USTAT,w ; copy USTAT register before... bcf UIR,TOK_DNE ; clearing the token done interrupt. bcf STATUS,RP0 ; bank 2 movwf USB_USTAT ; Save USTAT in bank 2 ; check UOWN bit here if desired movf BufferDescriptor,w ; get the first byte of the BD andlw 0x3c ; save the PIDs movwf PIDs xorlw TOKEN_IN btfsc STATUS,Z goto TokenInPID movf PIDs,w xorlw TOKEN_OUT btfsc STATUS,Z goto TokenOutPID movf PIDs,w xorlw TOKEN_SETUP btfsc STATUS,Z goto TokenSetupPID return ; should never get here... ; ****************************************************************** ; Process out tokens ; For EP0, just turn the buffer around. There should be no EP0 ; tokens to deal with. ; EP1 and EP2 have live data destined for the application ; ****************************************************************** TokenOutPID #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw TOKENOUT iorwf USWSTAT,f ; place in upper 6 bits of register banksel USB_USTAT ; select bank 3 #endif movf USB_USTAT,w ; get the status register btfss STATUS,Z ; was it EP0? goto tryEP1 ; no, try EP1 movlw 0x08 ; it's EP0.. buffer already copied, movwf BD0OBC ; just reset the buffer movlw 0x88 movwf BD0OST ; set OWN and DTS Bit bcf STATUS,RP0 ; bank 2 movf USB_dev_req,w xorlw VEND_SET_MEMORY btfss STATUS,Z return tryEP1 bcf PORTA,0 xorlw 0x08 ; was it EP1? btfss STATUS,Z goto tryEP2 bcf STATUS,RP0 ; back to bank2 ; **** Add Callout here to service EP1 in transactions. **** return tryEP2 movf USB_USTAT,w xorlw 0x10 ; was it EP2? btfsc STATUS,Z return ; unrecognized EP (Should never take this exit) ; **** Add Callout here to service EP2 in transactions. **** return ; ****************************************************************** ; Process in tokens ; ****************************************************************** TokenInPID #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw TOKENIN iorwf USWSTAT,f ; place in upper 6 bits of register banksel USB_USTAT ; select bank 3 #endif ; Assumes EP0 vars are setup in a previous call to setup. EP0_in movf USB_USTAT,w ; get the status register andlw 0x18 ; save only EP bits (we already know it's an IN) btfss STATUS,Z ; was it EP0? goto tryEP1in ; no, try EP1 movf USB_dev_req,w xorlw GET_DESCRIPTOR btfss STATUS,Z goto check_GSD call copy_descriptor_to_EP0 goto exitEP0in ; Check for Get String Descriptor check_GSD movf USB_dev_req,w xorlw GET_STRING_DESCRIPTOR btfss STATUS,Z goto check_SA call copy_string_descriptor_to_EP0 goto exitEP0in ; Check for Set Address check_SA movf USB_dev_req,w xorlw SET_ADDRESS btfss STATUS,Z goto check_SF call finish_set_address goto exitEP0in check_SF movf USB_dev_req,w xorlw SET_FEATURE btfss STATUS,Z goto check_CF call Send_0Len_pkt goto exitEP0in check_CF movf USB_dev_req,w xorlw CLEAR_FEATURE btfss STATUS,Z goto Class_Specific call Send_0Len_pkt goto exitEP0in Class_Specific pagesel Check_Class_Specific_IN goto Check_Class_Specific_IN exitEP0in return ; ****************************************************************** ; though not required, it might be nice to have a callback function here ; that would take some action like setting up the next buffer when the ; previous one is complete. Not necessary because the same functionality ; can be provided through the PutUSB call. ; ****************************************************************** tryEP1in xorlw 0x08 ; was it EP1? btfss STATUS,Z goto tryEP2in bcf STATUS,RP0 ; back to bank2 return tryEP2in return ; ****************************************************************** ; Return a zero length packet on EP0 In ; ****************************************************************** Send_0Len_pkt banksel BD0IBC ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw ZEROLENPACKET iorwf USWSTAT,f ; place in upper 6 bits of register #endif global Send_0Len_pkt clrf BD0IBC ; set byte count to 0 movlw 0xc8 movwf BD0IST ; set owns bit bcf STATUS,RP0 ; back to bank 2 clrf USB_dev_req return ; ******************************************************************** ; process setup tokens ; ****************************************************************** TokenSetupPID banksel USWSTAT ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw TOKENSETUP iorwf USWSTAT,f ; place in upper 6 bits of register #endif bsf STATUS,IRP ; indirectly to pages 2/3 movf BufferDescriptor+ADDRESS,w ; get the status register movwf FSR ; save in the FSR. movf INDF,w movwf BufferData ; in shared RAM incf FSR,f movf INDF,w movwf BufferData+1 incf FSR,f movf INDF,w movwf BufferData+2 incf FSR,f movf INDF,w movwf BufferData+3 incf FSR,f movf INDF,w movwf BufferData+4 incf FSR,f movf INDF,w movwf BufferData+5 incf FSR,f movf INDF,w movwf BufferData+6 incf FSR,f movf INDF,w movwf BufferData+7 movlw 0x88 ; set UOWNs bit back to SIE movwf BD0OST movlw 0x08 movwf BD0OBC ; reset the byte count too. movwf BD0IST ; return the in buffer to us (dequeue any pending requests) bcf UCTRL,PKT_DIS ; Assuming there is nothing to dequeue, clear the packet disable bit bcf STATUS,RP0 ; bank 2 clrf USB_dev_req ; clear the device request.. movf BufferData+bmRequestType,w xorlw 0x00 ; test for host to device tokens btfsc STATUS,Z goto HostToDevice movf BufferData+bmRequestType,w xorlw 0x01 ; test for host to Interface tokens btfsc STATUS,Z goto HostToInterface movf BufferData+bmRequestType,w xorlw 0x02 ; test for host to Endpoint tokens btfsc STATUS,Z goto HostToEndpoint movf BufferData+bmRequestType,w xorlw 0x80 ; test for device to Host tokens btfsc STATUS,Z goto DeviceToHost movf BufferData+bmRequestType,w xorlw 0x81 ; test for device to Interface tokens btfsc STATUS,Z goto InterfaceToHost movf BufferData+bmRequestType,w xorlw 0x82 ; test for device to Endpoint tokens btfsc STATUS,Z goto EndpointToHost pagesel GetClassSpecificDescriptor movf BufferData+bmRequestType,w andlw 0x60 ; mask off type bits xorlw 0x20 ; test for class specific btfsc STATUS,Z ; was it a standard request? goto CheckForVendorRequest goto GetClassSpecificDescriptor ; nope, see if it was a class specific request CheckForVendorRequest movf BufferData+bmRequestType,w andlw 0x60 ; mask off type bits xorlw 0x40 ; test for vendor specific btfss STATUS,Z ; was it a standard request? goto CheckForStandardRequest pagesel CheckVendor goto CheckVendor ; nope, see if it was a vendor specific return ; see file USB_VEND.ASM for code. ; now test bRequest to see what the request was. CheckForStandardRequest ; bmRequestType told us it was a Host to Device transfer. Now look at ; the specifics to see what's up HostToDevice movf BufferData+bRequest,w ; what was our request xorlw CLEAR_FEATURE btfsc STATUS,Z goto Clear_Device_Feature movf BufferData+bRequest,w ; was our request Set Address xorlw SET_ADDRESS btfsc STATUS,Z goto Set_Address movf BufferData+bRequest,w ; was our request Set Configuration xorlw SET_CONFIGURATION btfsc STATUS,Z goto Set_Configuration movf BufferData+bRequest,w ; was our request Set Feature xorlw SET_FEATURE btfsc STATUS,Z goto Set_Device_Feature goto wrongstate HostToInterface movf BufferData+bRequest,w ; what was our request xorlw CLEAR_FEATURE btfsc STATUS,Z goto Clear_Interface_Feature movf BufferData+bRequest,w ; was our request Set Interface xorlw SET_INTERFACE btfsc STATUS,Z goto Set_Interface movf BufferData+bRequest,w ; was our request Set Feature xorlw SET_FEATURE btfsc STATUS,Z goto Set_Interface_Feature HostToEndpoint movf BufferData+bRequest,w ; what was our request xorlw CLEAR_FEATURE btfsc STATUS,Z goto Clear_Endpoint_Feature movf BufferData+bRequest,w ; was our request Set Feature xorlw SET_FEATURE btfsc STATUS,Z goto Set_Endpoint_Feature DeviceToHost movf BufferData+bRequest,w ; what was our request xorlw GET_CONFIGURATION btfsc STATUS,Z goto Get_Configuration movf BufferData+bRequest,w ; was our request Get Decriptor? xorlw GET_DESCRIPTOR btfsc STATUS,Z goto Get_Descriptor movf BufferData+bRequest,w ; was our request Get Status? xorlw GET_STATUS btfsc STATUS,Z goto Get_Device_Status InterfaceToHost movf BufferData+bRequest,w ; was our request Get Interface? xorlw GET_INTERFACE btfsc STATUS,Z goto Get_Interface movf BufferData+bRequest,w ; was our request Get Status? xorlw GET_STATUS btfsc STATUS,Z goto Get_Interface_Status movf BufferData+bRequest,w ; was our request Get Decriptor? xorlw GET_DESCRIPTOR btfsc STATUS,Z goto Get_Descriptor EndpointToHost movf BufferData+bRequest,w ; was our request Get Status? xorlw GET_STATUS btfsc STATUS,Z goto Get_Endpoint_Status pagesel wrongstate ; unrecognised token, stall EP0 goto wrongstate return ; ****************************************************************** ; Get Descriptor ; Handles the three different Get Descriptor commands ; ****************************************************************** Get_Descriptor #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw GETDESCRIPTOR iorwf USWSTAT,f ; place in upper 6 bits of register bcf STATUS,RP0 ; select bank 2 #endif btfss BufferData+(wValue+1),5 ; get descriptor type goto GetCh9Descriptor pagesel GetClassSpecificDescriptor goto GetClassSpecificDescriptor pagesel Get_Report_Descriptor ; this tests for an 81 06 00 22 movf BufferData+(wValue+1),w ; request, which seems to be undefined, xorlw 0x22 ; but it won't enumerate without it btfsc STATUS,Z goto Get_Report_Descriptor GetCh9Descriptor movlw high StartGDIndex ; set up PCLATH with the current address movwf PCLATH ; set up pclath for the computed goto movf BufferData+(wValue+1),w ; get descriptor type andlw 0x03 ; minor attempt to keep things under control addlw low StartGDIndex btfsc STATUS,C ; was there an overflow? incf PCLATH,f ; yes, bump PCLATH movwf PCL ; adjust PC StartGDIndex goto wrongstate ; 0 goto Get_Device_Descriptor ; 1 goto Get_Config_Descriptor ; 2 goto Get_String_Descriptor ; 3 ; goto Get_Interface_Descriptor; 4 - not used ; goto Get_EndPoint_Descriptor ; 5 - not used ; goto wrongstate ; 6 ; goto wrongstate ; 7 ; ********************************************************************* ; Looks up the offset of the device descriptor via the low order byte ; of wValue. The pointers are set up and the data is copied to the ; buffer, then the flags are set. ; ; EP0_start points to the first word to transfer ; EP0_end points to the last, limited to the least of the message length ; or the number of bytes requested in the message (wLength). ; EP0_maxLength is the number of bytes to transfer at a time, 8 bytes ; ****************************************************************** Get_Device_Descriptor pagesel Descriptions ; set up PCLATH for call below movlw GET_DESCRIPTOR movwf USB_dev_req ; currently processing a get descriptor request movlw 8 movwf EP0_maxLength movf BufferData+wValue,w ; get descriptor index movwf EP0_start clrf EP0_start+1 ; clear high order byte call Descriptions ; get length of device descriptor movwf EP0_end ; save length clrf EP0_end+1 movf BufferData+(wLength+1),f ; move it to itself, check for non zero. btfss STATUS,Z ; if zero, we need to compare EP0_end to requested length. goto DeviceEndPtr ; if not, no need to compare. EP0_end is shorter than request length subwf BufferData+wLength,w ; compare against requested length btfss STATUS,C movwf EP0_end ; save it. DeviceEndPtr movf EP0_start,w addwf EP0_end,f pagesel copy_descriptor_to_EP0 call copy_descriptor_to_EP0 return ; ********************************************************************* ; Looks up the offset of the config descriptor via the low order byte ; of wValue. The pointers are set up and the data is copied to the ; buffer, then the flags are set. ; ; EP0_start points to the first word to transfer ; EP0_end points to the last, limited to the least of the message length ; or the number of bytes requested in the message (wLength). ; EP0_maxLength is the number of bytes to transfer at a time, 8 bytes ; ****************************************************************** Get_Config_Descriptor pagesel Descriptions ; set up PCLATH for call below movlw GET_DESCRIPTOR movwf USB_dev_req ; currently processing a get descriptor request movf BufferData+wValue,w ; get descriptor index call Config_desc_index ; translate index to offset into descriptor table movwf EP0_start movf BufferData+wValue,w addlw 1 ; point to high order byte call Config_desc_index ; translate index to offset into descriptor table movwf EP0_start+1 movlw 2 ; bump pointer by to to get the complete descriptor addwf EP0_start,f ; length, not just config descriptor btfsc STATUS,C incf EP0_start+1,f call Descriptions ; get length of the config descriptor movwf EP0_end ; Get message length movlw 2 ; move EP0_start pointer back to beginning subwf EP0_start,f btfss STATUS,C decf EP0_start+1,f movf EP0_end+1,w subwf BufferData+(wLength+1),w ; Compare high order bytes btfsc STATUS,Z goto CmpLowerByte ; Compare lower byte when equal btfsc STATUS,C ; if zero, we need to compare EP0_end to requested length. goto ConfigEndPtr ; if not, no need to compare. EP0_end is shorter than request length goto LimitSize CmpLowerByte movf EP0_end,w subwf BufferData+wLength,w ; compare against requested length btfsc STATUS,C goto ConfigEndPtr LimitSize movf BufferData+wLength,w ; if requested length is shorter.. movwf EP0_end ; save it. movf BufferData+(wLength+1),w ; if requested length is shorter.. movwf EP0_end+1 ; save it. ConfigEndPtr clrf EP0_end+1 ; clear high order byte movf EP0_start,w ; add start address to convert length addwf EP0_end,f ; to end pointer btfsc STATUS,C ; did it overflow? incf EP0_end,f movf EP0_start+1,w addwf EP0_end+1,f movlw 8 movwf EP0_maxLength pagesel copy_descriptor_to_EP0 call copy_descriptor_to_EP0 return ; ****************************************************************** ; Set up to return String descriptors ; Looks up the offset of the string descriptor via the low order byte ; of wValue. The pointers are set up and the data is copied to the ; buffer, then the flags are set. ; ****************************************************************** Get_String_Descriptor pagesel StringDescriptors movlw GET_STRING_DESCRIPTOR movwf USB_dev_req ; currently processing a get descriptor request movf BufferData+wLength,w movwf EP0_maxLength rlf BufferData+wValue,w ; get descriptor index * 2 call string_index ; translate index to offset into movwf EP0_start ; descriptor table addlw 1 ; point to high order byte call string_index ; translate index to offset into movwf EP0_start+1 ; descriptor table call StringDescriptors ; get length of the config descriptor movwf EP0_end ; save length subwf BufferData+wLength,w ; compare against requested length movf BufferData+wLength,w ; if requested length is shorter.. btfss STATUS,C movwf EP0_end ; save it. movlw 8 ; each transfer may be 8 bytes long movwf EP0_maxLength clrf EP0_end+1 ; clear high order byte movf EP0_start,w ; add low order bytes addwf EP0_end,f btfsc STATUS,C ; did it overflow? incf EP0_end+1,f ; add high order bytes movf EP0_start+1,w addwf EP0_end+1,f pagesel copy_string_descriptor_to_EP0 call copy_string_descriptor_to_EP0 return ; ****************************************************************** ; Stalls the EP0 endpoint to signal that the command was not recognised. ; This gets reset as the result of a Setup Transaction. ; ****************************************************************** wrongstate global wrongstate banksel BD0IST ; bsf BD0IST,BSTALL ; set endpoint stall bit bsf UEP0,EP_STALL bcf STATUS,RP0 ; back to page 2 return ; ****************************************************************** ; Loads the device status byte into the EP0 In Buffer. ; ****************************************************************** Get_Device_Status banksel BD0IAL ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw GETSTATUS iorwf USWSTAT,f ; place in upper 6 bits of register #endif movf BD0IAL,w ; get buffer pointer movwf FSR banksel USB_status_device ; select bank 2 bsf STATUS,IRP ; select indirectly banks 2-3 movf USB_status_device,w ; get device status byte movwf INDF incf FSR,f clrf INDF banksel BD0IBC ; select bank 3 movlw 0x02 movwf BD0IBC ; set byte count to 2 movlw 0xC8 movwf BD0IST ; Data 1 packet, set owns bit bcf STATUS,RP0 ; select bank 2 return ; ****************************************************************** ; A do nothing response. Always returns a two byte record, with all ; bits zero. ; ****************************************************************** Get_Interface_Status banksel BD0IAL ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw GETSTATUS iorwf USWSTAT,f ; place in upper 6 bits of register #endif movf BD0IAL,w ; get buffer pointer movwf FSR clrf INDF incf FSR,f clrf INDF movlw 0x02 movwf BD0IBC ; set byte count to 2 movlw 0xC8 movwf BD0IST ; Data 1 packet, set owns bit bcf STATUS,RP0 ; select bank 2 return ; ****************************************************************** ; Returns the Endpoint stall bit via a 2 byte in buffer ; ****************************************************************** Get_Endpoint_Status #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw GETSTATUS iorwf USWSTAT,f ; place in upper 6 bits of register bcf STATUS,RP0 #endif banksel UEP0 ; select bank 3 movlw 0x0f andwf BufferData+wIndex,w ; get endpoint, strip off direction bit xorlw 0x01 ; is it EP1? btfsc STATUS,Z goto get_EP1_status movlw 0x0f andwf BufferData+wIndex,w ; get endpoint, strip off direction bit xorlw 0x02 ; is it EP2? btfss STATUS,Z goto wrongstate get_EP2_status bcf STATUS,C btfsc UEP2,EP_STALL bsf STATUS,C goto build_status_buffer get_EP1_status bcf STATUS,C btfsc UEP1,EP_STALL bsf STATUS,C ; build_status_buffer bsf STATUS,IRP ; point to bank 3 movf BD0IAL,w ; get address of buffer movwf FSR clrf INDF ; clear byte 0 in buffer rlf INDF,f ; rotate in carry bit (EP_stall bit) incf FSR,f ; bump pointer clrf INDF ; clear byte movlw 0x02 movwf BD0IBC ; set byte count to 2 movlw 0xC8 movwf BD0IST ; Data 1 packet, set owns bit bcf STATUS,RP0 ; select bank 2 return ; ********************************************************************* ; The low order byte of wValue now has the new device address as assigned ; from the host. Save it in the UADDR, transition to the ADDRESSED state ; and clear the current configuration. ; This assumes the SIE has already sent the status stage of the transaction ; as implied by Figure 3-35 of the DOS (Rev A-7) ; ****************************************************************** Set_Address #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw SETADDRESS iorwf USWSTAT,f ; place in upper 6 bits of register banksel USB_dev_req ; select bank 3 #endif movlw SET_ADDRESS movwf USB_dev_req ; currently processing a get descriptor request movf BufferData+wValue,w ; new address in low order byte of wValue movwf USB_address_pending banksel BD0IBC ; select bank 3 movlw 0x00 movwf BD0IBC movlw 0xc8 movwf BD0IST ; write the whole mess back bcf STATUS,RP0 ; back to page 2 return finish_set_address #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw FINISHSETADDRESS iorwf USWSTAT,f ; place in upper 6 bits of register banksel USB_dev_req ; select bank 2 #endif clrf USB_dev_req ; no request pending clrf USB_Curr_Config ; make sure current configuration is 0 movf USB_address_pending,w banksel UADDR ; select bank 3 movwf UADDR ; set the device address btfsc STATUS,Z ; was address 0? goto endfinishsetaddr; yes: don't change state movlw 0xfc andwf USWSTAT,f movlw ADDRESS_STATE ; non-zero: transition to addressed state iorwf USWSTAT,f ; transition to addressed state endfinishsetaddr bcf STATUS,RP0 ; bank 2 return ; ****************************************************************** ; only feature valid for device feature is Device Remote wakeup ; ****************************************************************** Clear_Device_Feature #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw CLEARFEATURE iorwf USWSTAT,f ; place in upper 6 bits of register bcf STATUS,RP0 ; select bank 2 #endif movf BufferData+wValue,w xorlw 0x01 ; was it a Device Remote wakeup? If not, return STALL, btfss STATUS,Z ; since we only implement this feature on this device. goto wrongstate right_state_clear_feature banksel USB_status_device bcf USB_status_device,1 ; set device remote wakeup call Send_0Len_pkt return ; ****************************************************************** ; Only endpoint feature is Endpoint halt. ; ****************************************************************** Clear_Endpoint_Feature #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw CLEARFEATURE iorwf USWSTAT,f ; place in upper 6 bits of register bcf STATUS,RP0 #endif banksel UEP0 movlw 0x0f andwf BufferData+wIndex,w ; mask off the EP number bits xorlw 0x01 ; is it EP1? btfsc STATUS,Z goto ClearEP1 movlw 0x0f andwf BufferData+wIndex,w ; mask off the EP number bits xorlw 0x02 ; is it EP2? btfss STATUS,Z goto wrongstate ClearEP2 bcf UEP2,EP_STALL ; set the stall bit goto $+2 ; skip the second stall ClearEP1 bcf UEP1,EP_STALL ; set the stall bit call Send_0Len_pkt return Clear_Interface_Feature #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw CLEARFEATURE iorwf USWSTAT,f ; place in upper 6 bits of register bcf STATUS,RP0 #endif return ; ****************************************************************** ; only feature valid for device feature is Device Remote wakeup ; ****************************************************************** Set_Device_Feature #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw SETFEATURE iorwf USWSTAT,f ; place in upper 6 bits of register bcf STATUS,RP0 ; select bank 2 #endif movf BufferData+wValue,w ; get high order byte of wValue xorlw 0x01 ; was it a Device Remote wakeup? btfss STATUS,Z goto wrongstate ; request error banksel USB_status_device ; select appropriate bank bsf USB_status_device,1 ; set device remote wakeup call Send_0Len_pkt return ; ****************************************************************** ; Only endpoint feature is Endpoint halt. ; ****************************************************************** Set_Endpoint_Feature #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw SETFEATURE iorwf USWSTAT,f ; place in upper 6 bits of register bcf STATUS,RP0 #endif banksel UEP0 movlw 0x0f andwf BufferData+wIndex,w ; mask off the EP number bits xorlw 0x01 ; is it EP1? btfsc STATUS,Z goto StallEP1 movlw 0x0f andwf BufferData+wIndex,w ; mask off the EP number bits xorlw 0x02 ; is it EP2? btfss STATUS,Z goto wrongstate StallEP2 bsf UEP2,EP_STALL ; set the stall bit goto $+2 ; skip the second stall StallEP1 bsf UEP1,EP_STALL ; set the stall bit call Send_0Len_pkt return Set_Interface_Feature #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw SETFEATURE iorwf USWSTAT,f ; place in upper 6 bits of register bcf STATUS,RP0 ; select bank 2 #endif goto wrongstate ; invalid request return ; ******************************************************************** ; Get configuration returns a single byte Data1 packet indicating the ; configuration in use. ; Default State - undefined ; Addressed State - returns 0 ; Configured state - returns current configured state. ; ****************************************************************** Get_Configuration banksel BD0IAL ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw GETCONFIG iorwf USWSTAT,f ; place in upper 6 bits of register #endif movf low BD0IAL,w ; get address of buffer movwf FSR banksel USB_Curr_Config ; select bank 2 bsf STATUS,IRP ; indirectly to banks 2-3 movf USB_Curr_Config,w movwf INDF ; write byte to buffer banksel BD0IBC ; select bank 3 movlw 0x01 movwf BD0IBC ; set byte count to 1 movlw 0xc8 ; DATA1 packet, DTS enabled movwf BD0IST ; give buffer back to SIE bcf STATUS,RP0 ; back to bank 2 return ; ****************************************************************** ; Set configuration uses the configuration selected by the low order ; byte of wValue. Sets up a zero length data1 packet as a reply. ; ****************************************************************** Set_Configuration ; All we do is set a meaningless number. This'll ; need more code here to actually give meaning to each configuration ; we choose. #ifdef FUNCTIONIDS banksel USWSTAT ; select bank 3 movlw 0x03 ; save state bits andwf USWSTAT,f movlw SETCONFIG iorwf USWSTAT,f ; place in upper 6 bits of register bcf STATUS,RP0 #endif movf BufferData+wValue,w ; is it a valid configuration? sublw NUM_CONFIGURATIONS btfss STATUS,C ; if config <= num configs, request appears valid goto wrongstate movf BufferData+wValue,w movwf USB_Curr_Config ; store new state in configuration banksel USWSTAT ; select bank 3 movlw 0xfc andwf USWSTAT,f ; clear status bits movlw CONFIG_STATE ; Assume configured state movf BufferData+wValue,f ; run it through the ALU to test for 0 btfsc STATUS,Z ; was the configuration zero? movlw ADDRESS_STATE ; yes: set addressed state iorwf USWSTAT,f ; save new state. ; These configure the EP1 and EP2 endpoints. Change these as necessary ; for your application. movlw 0x88 ; set own bit of EP1 (SIE can write) movwf BD1OST movlw USB_Buffer+0x10 ; Endpoint 1 OUT gets a buffer movwf BD1OAL ; set up buffer address movlw 8 movwf BD1OBC ; set byte count movlw 0x08 ; set own bit of EP1 (PIC can write) movwf BD1IST movlw USB_Buffer+0x18 ; Endpoint 1 IN gets a buffer movwf BD1IAL ; set up buffer address movlw 0x88 ; set own bit of EP2 (SIE can write) movwf BD2OST movlw USB_Buffer+0x20 ; Endpoint 2 OUT gets a buffer movwf BD2OAL ; set up buffer address movlw 8 movwf BD2OBC ; set byte count movlw 0x08 ; set own bit of EP2 (PIC can write) movwf BD2IST movlw USB_Buffer+0x20 ; EP1 In and EP2 In share a buffer movwf BD2IAL ; set up buffer address ; Set up the Endpoint Control Registers. The following patterns are defined ; ENDPT_DISABLED - endpoint not used ; ENDPT_IN_ONLY - endpoint supports IN transactions only ; ENDPT_OUT_ONLY - endpoint supports OUT transactions only ; ENDPT_CONTROL - Supports IN, OUT and CONTROL transactions - Only use with EP0 ; ENDPT_NON_CONTROL - Supports both IN and OUT transactions movlw ENDPT_NON_CONTROL movwf UEP1 ; enable EP's 1 and 2 for In and Outs... movlw ENDPT_NON_CONTROL movwf UEP2 bcf STATUS,RP0 ; bank 2 call Send_0Len_pkt pagesel SetConfiguration movf USB_Curr_Config,w call SetConfiguration ; callout to set the configuration pagesel Set_Configuration return ; ******************************************************************** ; Get interface returns a single byte Data1 packet indicating the ; interface in use. ; Default State - undefined ; Addressed State - Not valid - returns stall ; Configured state - returns current configured state. ; ****************************************************************** Get_Interface banksel USWSTAT ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw GETINTERFACE iorwf USWSTAT,f ; place in upper 6 bits of register #endif movf USWSTAT,w ; Not valid in the addressed state bcf STATUS,RP0 ; bank 2 andlw 0x03 xorlw ADDRESS_STATE btfsc STATUS,Z goto wrongstate movf BufferData+wIndex,w ; get interface ID addlw low USB_Interface movwf FSR bsf STATUS,IRP movf INDF,w movwf temp ; store in temp register bsf STATUS,RP0 ; bank 3 movf BD0IAL,w ; get address of buffer movwf FSR movf temp,w ; load temp movwf INDF ; write byte to buffer movlw 0x01 movwf BD0IBC ; set byte count to 1 movlw 0xc8 ; DATA1 packet, DTS enabled movwf BD0IST ; give buffer back to SIE bcf STATUS,RP0 ; select bank 2 return ; ****************************************************************** ; Set configuration uses the configuration selected by the low order ; byte of wValue. Sets up a zero length data1 packet as a reply. ; ****************************************************************** Set_Interface banksel USWSTAT ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw SETINTERFACE iorwf USWSTAT,f ; place in upper 6 bits of register #endif movf USWSTAT,w ; test to make sure we're configured bcf STATUS,RP0 ; bank2 andlw 0x03 xorlw CONFIG_STATE btfss STATUS,Z goto wrongstate movf BufferData+wIndex,w ; get interface addlw USB_Interface ; add offset to array movwf FSR bsf STATUS,IRP ; indirectly to banks 2-3 movf BufferData+wValue,w ; get alternate interface movwf INDF ; store in array ; All we do is set a meaningless number. This'll ; need more code here to actually give meaning to each configuration ; we choose. call Send_0Len_pkt return ; ********************************************************************* ; copies the next chunk of buffer descriptor over to the EP0 In buffer. ; Inputs: ; EP0_start - points to first byte of configuration table to transfer ; EP0_end - total number of bytes to transfer ; EP0_maxLength - maximum number of bytes that can be sent during ; a single transfer ; ; toggles the data0/1 bit before setting the UOWN bit over to SIE. ; ****************************************************************** copy_descriptor_to_EP0 global copy_descriptor_to_EP0 banksel BD0IAL ; select bank 3 #ifdef FUNCTIONIDS movlw 0x03 ; save state bits andwf USWSTAT,f movlw COPYDESC2EP0 iorwf USWSTAT,f ; place in upper 6 bits of register #endif bsf STATUS,IRP ; make sure the FSR will point to pages 2/3 movf BD0IAL,w ; get buffer address movwf FSR bsf STATUS,IRP ; indirectly to banks 2-3 bcf STATUS,RP0 ; SELECTBANK2 clrf bufindex ; bufindex = 0 pagesel Descriptions gdd_loop movf bufindex,w ; while (bufindex < EP0_maxLength) subwf EP0_maxLength,w ; && (EP0_start < EP0_end) btfsc STATUS,Z goto end_gdd_loop movf EP0_start+1,w ; second half of above comparison subwf EP0_end+1,w ; (EP0_start < EP0_end) btfsc STATUS,Z ; each is 16 bits goto gdd_cmp_loworder ; high order bytes equal, check low order byte btfsc STATUS,C goto gdd_copy_loop goto end_gdd_loop_short_packet gdd_cmp_loworder movf EP0_start,w subwf EP0_end,w btfsc STATUS,Z goto end_gdd_loop_short_packet gdd_copy_loop call Descriptions movwf INDF incf bufindex,f incf FSR,f incfsz EP0_start,f goto gdd_loop incf EP0_start+1,f goto gdd_loop end_gdd_loop_short_packet clrf USB_dev_req ; we're sending a short packet, clear the device request end_gdd_loop movf bufindex,w ; write number of bytes to byte count bsf STATUS,RP0 ; SELECTBANK3 movwf BD0IBC movlw (0x01<