From Swordfish Wiki

SwordfishUser: 18F87J50USB

This is an updated version of the USB HID module supplied with the compiler. This version allows the use of the USB module on the new Microchip PIC18F87J50 3.3v part.

The example program shown here is a modification of the basic HID example supplied with the Compiler Help File. It will interact with the HID example program available from here & was developed for the Microchip PIC18F87J50 Full Speed USB Plug-In Module (Part Number: MA180021). The use of the new Mircochip USB HID bootloader is a highly recommended (updated 17/11/08 to correct Bootloader Descriptor Strings issue).

You can download the modified files from here (plus they are listed below for safety). You will need to place them into your UserLibrary Folder to ensure they are used by the compiler, and to ensure they are not overwritten in an update.

Please note

Although the 18F87J50 has 4096 bytes of RAM; due to the part having its USB buffers in RAM BANK 4 for compatibility, Swordfish can only allocate 1024 bytes of RAM automatically (and will report as such on compile).

You can however safely utilize memory above RAM BANK 7 ($0800 hex address and above, up to $0EFF) by directly allocating the memory yourself.

DIM BigBuffer(1000) as byte absolute $0800
DIM SecondBuffer(100) as byte absolute $0BE8 

The limitation of this is that you need to keep track of the size of each variable to calculate the correct address for the next one. So probably the best use for this RAM is for large arrays or structures.

Todos

Thanks

Big thanks to Nigle for pointing out how to use the high RAM area & a work around for UCFG. Thanks to richardb and Huge thanks to David Barker for this great compiler and for getting the ICD working - without it I was just thrashing around in the dark.

Example Code

{
*****************************************************************************
*  Name    : 18F87J50HIDExample.BAS                                         *
*  Author  : Nathan Herbert                                                 *
*  Notice  : Copyright (c) 2008 Nathan (Rangerbob) 	                    *
*          : All Rights Reserved                                            *
*  Date    : 17/11/2008                                                     *
*  Version : 1.1                                                            *
*  Notes   : Modified USB HID example from the Swordfish Help file          *
*          : To show support for 18F87J50 part                              *
*          : Use with Mechanique HIDExample.exe & 18F87J50 PIM              * 
*          : 1.1 - Fix for bootloader Descriptor strings issue              *
*****************************************************************************
}

// device and clock...                                                                                                                                                                                                                                         
Device = 18F87J50
Clock = 48

// 12Mhz crystal on 18F87J50 Board
Config
   xinst = off,
   stvren = on,
   PLLDIV = 3,
   wdten = off,
   cp0 = off,
   CPUDIV = OSC1,
   ieso = off,
   fcmen = off,
   FOSC = HSPLL,
   wdtps = 32768,
   msspmsk = msk5,
   ccp2mx = default,
   wait = off,
   bw = 16,
   mode = mm,
   eashft = off,
   pmpmx = default,
   eccpmx = default

// Enable this option to use with the Microchip USB HID bootloader
'#option org_reset = $1000

// Fix for Bootloader Descriptor Strings Issue - Ensure this is first include
Include "ClearRegisters.bas"

// import the HID module...
Include "usbhid.bas"

// TX report...
Structure TTXReport
   Time As Word
   Message As String
End Structure
Dim TXReport As TTXReport Absolute TXReportRAM  

// RX report...
Structure TRXReport
   LED0 As Bit
   LED1 As Bit
End Structure    
Dim RXReport As TRXReport Absolute RXReportRAM

// alias port pins to LEDs...
// Porte.0 & Porte.1 available as LEDs on 18F87J50 PIM module 
Dim 
   LED0 As PORTE.0,
   LED1 As PORTE.1

// initialise...

// Set all digital
//Configure all I/O pins to use digital input buffers.  The PIC18F87J50 Family devices
//use the ANCONx registers to control this, which is different from other devices which
//use the ADCON1 register for this purpose.

WDTCON.4 = 1 // selcet alternate sfr locations
ANCON0 = $FF
ANCON1 = $FF
WDTCON.4 = 0 // select normal sfr locations

TXReport.Time = 0
Low(LED0)
Low(LED1)

// connect to USB...
Repeat
Until Attached

// main program loop...
While true

	// if we have data, set port values, update message
	// and then reset time counter...
	If DataAvailable Then
		ReadReport
		LED0 = RXReport.LED0
		LED1 = RXReport.LED1
		TXReport.Message = "PORT CHANGED"
		WriteReport
		TXReport.Time = 0
		DelayMS(100)

	// no data, set waiting message...   
 	Else
        TXReport.Message = "WAITING..."
        WriteReport
        Inc(TXReport.Time)
	EndIf
Wend

Module Code

ClearRegisters.bas

{
*****************************************************************************
*  Name    : ClearRegisters.BAS                                             *
*  Author  : Nathan Herbert                                                 *
*  Notice  :	                                                            *
*          : All Rights Reserved                                            *
*  Date    : 17/11/2008                                                     *
*  Version : 1.0                                                            *
*  Notes   : Use with Microchip HID bootloader  to ensure correct operation *
*          : of main program                                                *
*****************************************************************************
}
Module ClearRegisters

#if _device = 18F87J50

	TBLPTR	= %00000000
	TABLAT	= %00000000
	INTCON	= %00000000
	INTCON2 = %11111111
	INTCON3 = %11000000
	TMR0H	= %00000000
	T0CON	= %11111111
	CM1CON	= %00011111
	CM2CON	= %00011111
	ODCON1	= %00000000
	ODCON2	= %00000000
	T1CON	= %00000000
	ODCON3	= %00000000
	TMR2	= %00000000
	T2CON	= %00000000
	IPR3	= %11111111
	PIR3	= %00000000
	PIE3	= %00000000
	IPR2	= %11111111
	PIR2	= %00000000
	PIE2	= %00000000
	IPR1	= %11111111
	PIR1	= %00000000
	PIE1	= %00000000
	UCON 	= %00000000
	UEIR	= %00000000
	UIR		= %00000000
	UCFG	= %00000000
	UEIE	= %00000000
	UIE		= %00000000

#endif

USBHID.bas

{
*****************************************************************************
*  Name    : USBHID.bas                                                     *
*  Author  : David John Barker                                              *
*  Notice  : Copyright (c) 2007 Mecanique                                   *
*          : All Rights Reserved                                            *
*  Date    : 15/09/2008                                                     *
*  Version : 1.1a Branch by Nathan(RangerBob) for 18F87J50 Support          *
*          : 1.1 ISR now uses USB interrupts                                *
*          : Renamed 'Connected' to 'Attached'                              *
*          : 1.0 Release                                                    *
*  Notes   : This HID module provides a simple way for you to transfer      *
*          : blocks of data to and from your PC using the USB HID class.    *
*****************************************************************************
}
Module HID

// import USB modules...
#option USB_USE_HID = true
Include USB_DESCRIPTOR
Include "usbsfr.bas"
Include "usbdefs.bas"
Include "usbsystem.bas"
Include "system.bas"


// interrupt driven service fired every 1 millisecond...
Const 
   PriorityLevel = USB_SERVICE_PRIORITY // ISR priority

// Public buffers - these are just mapped onto USB RAM space, thus saving
// program RAM. You can use a large 256 byte buffer, which can be useful
// for trasnferring large blocks of data using ReadArray() and WriteArray().
// The RX and TX buffers match the HID report size, and are typically use with
// ReaReport() and WriteReport(). However, they can also be used with ReadArray()
// and WriteArray(). Note that these buffers are only available for USB devices 
// that have extended dual port USB RAM...
#if USB_EXTENDED_RAM
Public Const
   BufferRAM = $600,
   TXReportRAM = BufferRAM,
   RXReportRAM = BufferRAM + HIDReportBytesIn
Public Dim   
   Buffer(256) As Byte Absolute BufferRAM,
   TXReport(HIDReportBytesIn) As Byte Absolute TXReportRAM,
   RXReport(HIDReportBytesOut) As Byte Absolute RXReportRAM
#endif
Public Dim Attached As USBSystem.Attached
{
****************************************************************************
* Name    : Service                                                        *
* Purpose : Service a USB connection. You need to call this routine        *
*         : periodically from your main program to maintain a connection   *
*         : (every 1 ms or so). You DO NOT need to call this routine if    *
*         : you have enabled the OnService() interrupt handler (it is on   *
*         : by default)                                                    *
****************************************************************************
}
Public Inline Sub Service()
   USBCheckBusStatus
   USBDriverService
   ClrWDT
End Sub
{
****************************************************************************
* Name    : NoISRService                                                   *
* Purpose : Private service helper routine for maintain USB connection     *
*         : for some blocking calls                                        *
****************************************************************************
}
Inline Sub NoISRService()
   #if Not USB_SERVICE
   Service
   #endif
End Sub
{
****************************************************************************
* Name    : OnService (PRIVATE)                                            *
* Purpose : USB service event handler                                      *
****************************************************************************
}
#if USB_SERVICE
Interrupt OnService(PriorityLevel)
   PIR2Bits.USBIF = 0
   Save(FSR0, FSR1, Service)
	  Service
   Restore 
End Interrupt
#endif
{
****************************************************************************
* Name    : EnableISR                                                      *
* Purpose : Enable the ISR to service USB connection                       *
****************************************************************************
}
Public Inline Sub EnableISR()
   #if USB_SERVICE
   Enable(OnService)
   #endif
End Sub
{
****************************************************************************
* Name    : DisableISR                                                     *
* Purpose : Disable the ISR that services the USB connection               *
****************************************************************************
}
Public Inline Sub DisableISR()
   #if USB_SERVICE
   Disable(OnService)
   #endif
End Sub
{
****************************************************************************
* Name    : WaitForTX (PRIVATE)                                            *
* Purpose : Blocks processing until USB TX buffer is available             *
****************************************************************************
}
Sub WaitForTX()
   While mHIDTxIsBusy <> 0 And Attached
      Service
   Wend 
End Sub
{
****************************************************************************
* Name    : DataAvailable                                                  *
* Purpose : Returns true if RX data is available, false otherwise          *
****************************************************************************
}
Public Function DataAvailable() As Boolean
   DisableISR
   Result = (mHIDRxIsBusy = 0)
   EnableISR
End Function
{
****************************************************************************
* Name    : ReadReport                                                     *
* Purpose : Read a HID report into RXReport buffer                         *
****************************************************************************
}
#if USB_EXTENDED_RAM
Public Sub ReadReport()
   DisableISR
   While mHIDRxIsBusy <> 0 And Attached
      Service
   Wend
   HIDRxReport(@RXReport, HIDReportBytesOut)
   NoISRService
   EnableISR
End Sub
{
****************************************************************************
* Name    : WriteReport                                                    *
* Purpose : Write the TXReport buffer                                      *
****************************************************************************
}
Public Sub WriteReport()
   DisableISR
   WaitForTX
   HIDTxReport(@TXReport, HIDReportBytesIn) 
   NoISRService
   EnableISR
End Sub
#endif
{
****************************************************************************
* Name    : ReadArray                                                      *
* Purpose : Read an array of bytes - pCount is an optional number of bytes *
*         : to read. If pCount is not given, the size of the array is used *
****************************************************************************
}
Public Sub ReadArray(ByRef pBuffer() As Byte, pCount As Word = 0)
   Dim BytesRead As Byte
   Dim Buffer As Word

   Buffer = @pBuffer
   If pCount = 0 Then
      pCount = Bound(pBuffer) + 1
   EndIf  

   DisableISR
   While pCount > 0 And Attached

      // wait for data
      If mHIDRxIsBusy = 0 Then

         // pCount > max report size
         If pCount > HIDReportBytesOut Then
            BytesRead = HIDRxReport(Buffer, HIDReportBytesOut)
            Dec(pCount, BytesRead)
            Inc(Buffer, BytesRead)

         // pCount <= max report size   
         Else
            BytesRead = HIDRxReport(Buffer, pCount.Byte0)
            Dec(pCount.Byte0, BytesRead)
            Inc(Buffer, BytesRead)
         EndIf
      EndIf   
      Service
   Wend
   EnableISR
End Sub
{
****************************************************************************
* Name    : WriteArray                                                     *
* Purpose : Write an array of bytes - pCount is an optional number of      *
*         : to write. If pCount is not given, the array size is used       *
*         : Bytes are transfered in HIDReportBytesIn sized packets - 8, 16 *
*         : 32 or 64 bytes. If you pass an array which is only dimensioned *
*         : 4 bytes (for example, DIM Array(4) AS BYTE  then a             *
*         : 'HIDReportBytesIn' sized data packet is still transferred.     *
*         : This isn't really a problem, as the host application should    *
*         : know what to do with the data anyway.                          *
****************************************************************************
}
Public Sub WriteArray(ByRef pBuffer() As Byte, pLength As Word = 0)
   Dim Buffer As Word

   Buffer = @pBuffer
   If pLength = 0 Then
      pLength = Bound(pBuffer) + 1
   EndIf   

   DisableISR
   While (pLength > HIDReportBytesIn) And Attached
      WaitForTX
      HIDTxReport(Buffer, HIDReportBytesIn) 
      Dec(pLength, HIDReportBytesIn)
      Inc(Buffer, HIDReportBytesIn)
      Service
   Wend
   If pLength.Byte0 > 0 Then
      WaitForTX
      HIDTxReport(Buffer, HIDReportBytesIn)
      Service
   EndIf
   EnableISR
End Sub
{
****************************************************************************
* Name    : Initialize                                                     *
* Purpose : Initialize the USB module                                      *
****************************************************************************
}
Sub Initialize()

	#if _device = 18F87J50

 		//On the PIC18F87J50 Family of USB microcontrollers, the PLL will not power up and be enabled
		//by default, even if a PLL enabled oscillator configuration is selected (such as HS+PLL).
		//This allows the device to power up at a lower initial operating frequency, which can be
		//advantageous when powered from a source which is not gauranteed to be adequate for 48MHz
		//operation.  On these devices, user firmware needs to manually set the OSCTUNE<PLLEN> bit to
		//power up the PLL.  

		//Enable the PLL and wait 2+ms until the PLL locks before enabling USB module
		OSCTUNE.6 = 1
		DelayMS(2)

	#endif

   mInitializeUSBDriver()
   #if USB_SERVICE
	   #if USB_SERVICE_PRIORITY = ipLow
		   #if _device = 18F87J50
			   IPR2.4 = 0
		   #else
			   IPR2.5 = 0
		   #endif
	   #endif        
	   PIR2Bits.USBIF = 0 // clear USBIF
	   PIE2Bits.USBIE = 1 // enable USB Int

	   Service
	   UIE = $7F   // Enable all interrupts
	   UEIE = $9F  // Unmask all USB error interrupts  
	   Enable(OnService)     
   #endif
End Sub

// initialize the module...
Initialize


USBSFR.bas

{
*****************************************************************************
*  Name    : USBSFR.bas                                                     *
*  Author  : David John Barker                                              *
*  Notice  : Copyright (c) 2007 Mecanique                                   *
*          : All Rights Reserved                                            *
*  Date    : 15/09/2008                                                     *
*  Version : 1.0a Branch by Nathan(RangerBob) for 18F87J50 Support          *
*          : 1.0                                                            *
*  Notes   : This module implements SFRs as structures, which allows you to *
*          : refer to bits using their respective datasheet names. For      *
*          : example, you can use something like UIRBits.STALLIF = 0. Note  *
*          : that if you want to access as a complete byte, you should use  *
*          : UIRBits._byte = $00 - this is because the compiler sees the    *
*          : variables as structures and not bytes                          *
*****************************************************************************
}
Module USBSFR

// UFRML
Structure TUFRML
  _byte As Byte
  FRM0 As _byte.0
  FRM1 As _byte.1
  FRM2 As _byte.2
  FRM3 As _byte.3
  FRM4 As _byte.4
  FRM5 As _byte.5
  FRM6 As _byte.6
  FRM7 As _byte.7
End Structure

// UFRMH
Structure TUFRMH
  _byte As Byte
  FRM8 As _byte.0
  FRM9 As _byte.1
  FRM10 As _byte.2
End Structure

// UIR
Structure TUIR
  _byte As Byte
  URSTIF As _byte.0
  UERRIF As _byte.1
  ACTVIF As _byte.2
  TRNIF As _byte.3
  IDLEIF As _byte.4
  STALLIF As _byte.5
  SOFIF As _byte.6
End Structure

// UIE
Structure TUIE
  _byte As Byte
  URSTIE As _byte.0
  UERRIE As _byte.1
  ACTVIE As _byte.2
  TRNIE As _byte.3
  IDLEIE As _byte.4
  STALLIE As _byte.5
  SOFIE As _byte.6
End Structure

// UEIR
Structure TUEIR
  _byte As Byte
  PIDEF As _byte.0
  CRC5EF As _byte.1
  CRC16EF As _byte.2
  DFN8EF As _byte.3
  BTOEF As _byte.4
  BTSEF As _byte.7
End Structure

// UEIE
Structure TUEIE
  _byte As Byte
  PIDEE As _byte.0
  CRC5EE As _byte.1
  CRC16EE As _byte.2
  DFN8EE As _byte.3
  BTOEE As _byte.4
  BTSEE As _byte.7
End Structure

// USTAT
Structure TUSTAT
  _byte As Byte
  PPBI As _byte.1
  DIR As _byte.2
  ENDP0 As _byte.3
  ENDP1 As _byte.4
  ENDP2 As _byte.5
  ENDP3 As _byte.6
End Structure

// UCON
Structure TUCON
  _byte As Byte
  SUSPND As _byte.1
  RESUME As _byte.2
  USBEN As _byte.3
  PKTDIS As _byte.4
  SE0 As _byte.5
  PPBRST As _byte.6
End Structure

// UADDR
Structure TUADDR
  _byte As Byte
  ADDR0 As _byte.0
  ADDR1 As _byte.1
  ADDR2 As _byte.2
  ADDR3 As _byte.3
  ADDR4 As _byte.4
  ADDR5 As _byte.5
  ADDR6 As _byte.6
End Structure

// UCFG
Structure TUCFG
  _byte As Byte
  PPB0 As _byte.0
  PPB1 As _byte.1
  FSEN As _byte.2
  UTRDIS As _byte.3
  UPUEN As _byte.4
'  UOEMON As _byte.6
  UTEYE As _byte.7
End Structure

// UEP
Structure TUEP
  _byte As Byte
  EPSTALL As _byte.0
  EPINEN As _byte.1
  EPOUTEN As _byte.2
  EPCONDIS As _byte.3
  EPHSHK As _byte.4
End Structure

#if _device = 18F87J50
// PIR2
Structure TPIR2
  _byte As Byte
  CCP2IF As _byte.0
  TMR3IF As _byte.1
  LVDIF As _byte.2
  BCLIF As _byte.3
  USBIF As _byte.4
  CMIF As _byte.5
  OSCFIF As _byte.7
  HLVDIF As _byte.2
End Structure
#else
// PIR2
Structure TPIR2
  _byte As Byte
  CCP2IF As _byte.0
  TMR3IF As _byte.1
  LVDIF As _byte.2
  BCLIF As _byte.3
  EEIF As _byte.4
  USBIF As _byte.5
  CMIF As _byte.6
  OSCFIF As _byte.7
  HLVDIF As _byte.2
End Structure
#endif

// INTCON
Structure TINTCON
  _byte As Byte
  RBIF As _byte.0
  INT0IF As _byte.1
  TMR0IF As _byte.2
  RBIE As _byte.3
  INT0IE As _byte.4
  TMR0IE As _byte.5
  PEIE As _byte.6
  GIE As _byte.7

  INT0F As _byte.1
  T0IF As _byte.2
  INT0E As _byte.4
  T0IE As _byte.5
  GIEL As _byte.6
  GIEH As _byte.7
End Structure

#if _device = 18F87J50
// PIE2
Structure TPIE2
  _byte As Byte
  CCP2IE As _byte.0
  TMR3IE As _byte.1
  LVDIE As _byte.2
  BCLIE As _byte.3
  USBIE As _byte.4
  CMIE As _byte.5
  OSCFIE As _byte.7
  HLVDIE As _byte.2
End Structure
#else
// PIE2
Structure TPIE2
  _byte As Byte
  CCP2IE As _byte.0
  TMR3IE As _byte.1
  LVDIE As _byte.2
  BCLIE As _byte.3
  EEIE As _byte.4
  USBIE As _byte.5
  CMIE As _byte.6
  OSCFIE As _byte.7
  HLVDIE As _byte.2
End Structure
#endif

// public aliases...

#if _device = 18F87J50
  Public Dim 
   UFRMLBits As TUFRML Absolute $0F60,
   UFRMHBits As TUFRMH Absolute $0F61,
   UIRBits As TUIR Absolute $0F62,
   UIEBits As TUIE Absolute $0F5C,
   UEIRBits As TUEIR Absolute $0F63,
   UEIEBits As TUEIE Absolute $0F5D,
   USTATBits As TUSTAT Absolute $0F64,
   UCONBits As TUCON Absolute $0F65,
   UADDRBits As TUADDR Absolute $0F5E,               
   UCFGBits As TUCFG Absolute $0F5F,
   UEP0Bits As TUEP Absolute $0F4C,
   UEP1Bits As TUEP Absolute $0F4D,
   UEP2Bits As TUEP Absolute $0F4E,
   UEP3Bits As TUEP Absolute $0F4F,
   UEP4Bits As TUEP Absolute $0F50,
   UEP5Bits As TUEP Absolute $0F51,
   UEP6Bits As TUEP Absolute $0F52,
   UEP7Bits As TUEP Absolute $0F53,
   UEP8Bits As TUEP Absolute $0F54,
   UEP9Bits As TUEP Absolute $0F55,
   UEP10Bits As TUEP Absolute $0F56,
   UEP11Bits As TUEP Absolute $0F57,
   UEP12Bits As TUEP Absolute $0F58,
   UEP13Bits As TUEP Absolute $0F59,
   UEP14Bits As TUEP Absolute $0F5A,
   UEP15Bits As TUEP Absolute $0F5B,
   PIE2Bits As TPIE2 Absolute $0FA0,
   PIR2Bits As TPIR2 Absolute $0FA1, 
   INTCONBits As TINTCON Absolute $0FF2
#else
Public Dim 
   UFRMLBits As TUFRML Absolute $0F66,
   UFRMHBits As TUFRMH Absolute $0F67,
   UIRBits As TUIR Absolute $0F68,
   UIEBits As TUIE Absolute $0F69,
   UEIRBits As TUEIR Absolute $0F6A,
   UEIEBits As TUEIE Absolute $0F6B,
   USTATBits As TUSTAT Absolute $0F6C,
   UCONBits As TUCON Absolute $0F6D,
   UADDRBits As TUADDR Absolute $0F6E,               
   UCFGBits As TUCFG Absolute $0F6F,
   UEP0Bits As TUEP Absolute $0F70,
   UEP1Bits As TUEP Absolute $0F71,
   UEP2Bits As TUEP Absolute $0F72,
   UEP3Bits As TUEP Absolute $0F73,
   UEP4Bits As TUEP Absolute $0F74,
   UEP5Bits As TUEP Absolute $0F75,
   UEP6Bits As TUEP Absolute $0F76,
   UEP7Bits As TUEP Absolute $0F77,
   UEP8Bits As TUEP Absolute $0F78,
   UEP9Bits As TUEP Absolute $0F79,
   UEP10Bits As TUEP Absolute $0F7A,
   UEP11Bits As TUEP Absolute $0F7B,
   UEP12Bits As TUEP Absolute $0F7C,
   UEP13Bits As TUEP Absolute $0F7D,
   UEP14Bits As TUEP Absolute $0F7E,
   UEP15Bits As TUEP Absolute $0F7F,
   PIE2Bits As TPIE2 Absolute $0FA0,
   PIR2Bits As TPIR2 Absolute $0FA1, 
   INTCONBits As TINTCON Absolute $0FF2
#endif          

USBSystem.bas

{
*****************************************************************************
*  Name    : USBSystem.Bas                                                  *
*  Author  : David John Barker                                              *
*  Notice  : Copyright (c) 2007 Mecanique                                   *
*          : All Rights Reserved                                            *
*  Date    : 15/09/2008                                                     *
*  Version : 1.2a Branch by Nathan(RangerBob) for 18F87J50 Support          *
*          : 1.2 Added GetPID() to correctly mask BD_STAT PID               *
*          :     Added string index check in USBStdGetDscHandler()          *
*          :     Added 'Attached' flag                                      *      
*          : 1.1 Fixed RAM allocation for smaller USB devices               *
*          : 1.0 Release                                                    *
*  Notes   : This module is based on the Microchip USB C18 Firmware Version *
*          : 1.0 by Rawin Rojvanit. Whilst many of the routines could be    *
*          : optimised for the Swordfish compiler, it was felt that         *
*          : maintaining the code as close to possible to the original C    *
*          : version would make it much easier to make changes and update   *
*          : the firmware. In addition, all the documentation relating to   *
*          : the original version can be easily mapped to this module       *
*          : -------------------------------------------------------------- *
*          : IT IS STRONGLY advised you do not make changes to this module  *
*          : unless you are familiar with USB or the original Microchip     *
*          : firmware                                                       *
*****************************************************************************
}
Module USBSystem

// import modules...
Include "USBConfig.bas"     // configurations
Include "USBSFR.bas"        // USB Special Function Registers (SFR)
Include "USBDefs.bas"       // constants, structures etc
Include USB_DESCRIPTOR      // descriptor file

// general module variables...
Dim                                                                      'usbmap.c
   usb_device_state As Byte,          // Device States: DETACHED, ATTACHED, ...
   usb_stat As USB_DEVICE_STATUS,     // Global USB flags
   usb_active_cfg As Byte,            // Value of current configuration
   usb_alt_intf(MAX_NUM_INT) As Byte, // Array to keep track of the current alternate setting for each interface ID  
   usb_Attached As Boolean            // device attached flag 

// public...
Public Dim
   Attached As usb_Attached            // USB device attached flag

// fixed location variables
// Cannot Modify MAXRAM or USB_RAM_EP as 18F87J50 uses bank 4 for USB buffers - to look at
#if _device = 18F87J50
	#if USB_EXTENDED_RAM
   		#variable _maxram = $0400     
	#else
   		#variable _maxram = $0200     
	#endif
	#define EP_SIZE = 4                   // endpoint size (byte)
	#define USB_RAM_EP = $400             // USB RAM start location
#else
	#if USB_EXTENDED_RAM
   		#variable _maxram = $0400     
	#else
   		#variable _maxram = $0200     
	#endif
	#define EP_SIZE = 4                   // endpoint size (byte)
	#define USB_RAM_EP = $400             // USB RAM start location
#endif

// endpoint 0, OUT and IN
#if(0 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep0Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 0),  
   ep0Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 1)  
#endif

// endpoint 1, OUT and IN
#if(1 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep1Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 2),        
   ep1Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 3)         
#endif

// endpoint 2, OUT and IN
#if(2 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep2Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 4),         
   ep2Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 5)         
#endif

// endpoint 3, OUT and IN
#if(3 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep3Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 6),         
   ep3Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 7)         
#endif

// endpoint 4, OUT and IN
#if(4 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep4Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 8),         
   ep4Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 9)         
#endif

// endpoint 5, OUT and IN
#if(5 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep5Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 10),         
   ep5Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 11)        
#endif

// endpoint 6, OUT and IN
#if(6 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep6Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 12),         
   ep6Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 13)         
#endif

// endpoint 7, OUT and IN
#if(7 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep7Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 14),         
   ep7Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 15)         
#endif

// endpoint 8, OUT and IN
#if(8 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep8Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 16),         
   ep8Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 17)         
#endif

// endpoint 9, OUT and IN
#if(9 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep9Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 18),         
   ep9Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 19)         
#endif

// endpoint 10, OUT and IN
#if(10 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep10Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 20),        
   ep10Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 21)       
#endif

// endpoint 11, OUT and IN
#if(11 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep11Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 22),       
   ep11Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 23)        
#endif

// endpoint 12, OUT and IN
#if(12 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep12Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 24),       
   ep12Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 25)       
#endif

// endpoint 13, OUT and IN
#if(13 <= MAX_EP_NUMBER)
Public Dim                                                                      'usbmap.c
   ep13Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 26),     
   ep13Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 27)      
#endif

// endpoint 14, OUT and IN
#if(14 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep14Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 28),      
   ep14Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 29)       
#endif

// endpoint 15, OUT and IN
#if(15 <= MAX_EP_NUMBER)
Dim                                                                             'usbmap.c
   ep15Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 30),       
   ep15Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 31)        
#endif

// located after enpoints...
#define USB_RAM_BUFF = USB_RAM_EP + EP_SIZE * (MAX_EP_NUMBER + 1) * 2
Dim                                                                             'usbmap.c
   SetupPkt As CTRL_TRF_SETUP Absolute USB_RAM_BUFF,                    
   CtrlTrfData As CTRL_TRF_DATA Absolute (USB_RAM_BUFF + USB_EP0_BUFF_SIZE) 

// HID - located after endpoint buffers...
#if USB_USE_HID
#define USB_RAM_HID = USB_RAM_BUFF + USB_EP0_BUFF_SIZE * 2
Dim                                                                             'usbmap.c
   hid_report_out(HID_INT_OUT_EP_SIZE) As Byte Absolute USB_RAM_HID,
   hid_report_in(HID_INT_IN_EP_SIZE) As Byte Absolute (USB_RAM_HID + HID_INT_OUT_EP_SIZE),
   hid_report_feature(USB_EP0_BUFF_SIZE) As Byte Absolute (USB_RAM_HID + HID_INT_OUT_EP_SIZE + HID_INT_IN_EP_SIZE)
Dim                                                                             'usbcfg.h
   HID_UEP As UEP1,
   HID_BD_OUT As ep1Bo,
   HID_BD_IN As ep1Bi
#endif // USB_USE_HID

// CDC - located after endpoint buffers...
#if USB_USE_CDC
#define USB_RAM_CDC = USB_RAM_BUFF + USB_EP0_BUFF_SIZE * 2
Dim                                                                             'usbmap.c
   cdc_notice(CDC_INT_EP_SIZE) As Byte Absolute USB_RAM_CDC,
   cdc_data_rx(CDC_BULK_OUT_EP_SIZE) As Byte Absolute (USB_RAM_CDC + CDC_INT_EP_SIZE),
   cdc_data_tx(CDC_BULK_IN_EP_SIZE) As Byte Absolute (USB_RAM_CDC + CDC_INT_EP_SIZE + CDC_BULK_OUT_EP_SIZE)
Dim
   CDC_COMM_UEP As UEP2,                                                        'usbcfg.h
   CDC_INT_BD_IN As ep2Bi,
   CDC_DATA_UEP As UEP3,
   CDC_BULK_BD_OUT As ep3Bo,
   CDC_BULK_BD_IN As ep3Bi
#endif // USB_USE_CDC

Dim                                                                             'usbctrltr.h and usbctrltr.c
   ctrl_trf_state As Byte,            // control transfer state                   
   ctrl_trf_session_owner As Byte,    // session owner
   pSrc As POINTER,                   // source pointer
   pDst As POINTER,                   // destination pointer
   wCount As Word

// originally coded in ROM, we have to use RAM in Swordfish...
Dim                                                                             'usbdsc.c
   USB_CD_Ptr(2) As Word,  // {&cfg01,&cfg01};
   #if USB_HAVE_SERIAL_STRING
   USB_SD_Ptr(4) As Word   // {&sd000,&sd001,&sd002,&sd003}; 
   #else
   USB_SD_Ptr(3) As Word   // {&sd000,&sd001,&sd002}; 
   #endif
{
****************************************************************************
* Name    : SetRAMPtr                                                      *
* Purpose : Helper routine for setting RAM                                 *
****************************************************************************
}
Inline Sub SetRAMPtr(pAddress As FSR0, pValue As POSTINC0)
End Sub  
{
****************************************************************************
* Name    : GetRAMPtr                                                      *
* Purpose : Helper routine for getting RAM byte                            *
****************************************************************************
}
Inline Function GetRAMPtr(pAddress As FSR1) As POSTINC1
End Function   
{
****************************************************************************
* Name    : ReadROM                                                        *
* Purpose : Helper routine for getting ROM byte                            *
****************************************************************************
}
Inline Function ReadROM() As TABLAT
   ASM-
      TBLRD *+
   End ASM
End Function 
{
****************************************************************************
* Name    :GetROMPtr                                                       *
* Purpose : Helper routine for getting ROM byte                            *
****************************************************************************
}
Inline Function GetROMPtr(pAddress As TABLEPTR) As TABLAT
   ReadROM
End Function 
{
****************************************************************************
* Name    : GetRequestType                                                 *
* Purpose : Helper routine to obtain USB request type                      *
****************************************************************************
}
Function GetRequestType() As Byte                                               
   result = (SetupPkt.RequestType_f2 >> 5) And %00000011
End Function
{
****************************************************************************
* Name    : GetRecipient                                                   *
* Purpose : Helper routine to get USB recipient                            *
****************************************************************************
}
Function GetRecipient() As Byte                                                 
   result = SetupPkt.Recipient_f5 And $1F
End Function
{
****************************************************************************
* Name    : GetEPNum                                                       *
* Purpose : Helper routineto get USB EP number                             *
****************************************************************************
}
Function GetEPNum() As Byte                                                     
   result = SetupPkt.EPNum_f4 And $0F
End Function
{
****************************************************************************
* Name    : GetPID (version 1.2)                                           *
* Purpose : Helper routineto get USB PID                                   *
****************************************************************************
}
Function GetPID() As Byte   
   result = (ep0Bo.Stat.PID >> 2) And $0F
End Function
{
****************************************************************************
* Name    : mInitializeUSBDriver                                           *
* Purpose : Configures the USB module                                      *
*         : This register determines: USB Speed, On-chip pull-up resistor  *
*         : selection, On-chip tranceiver selection, bus eye pattern       *
*         : generation mode, Ping-pong buffering mode selection            *                                                               
****************************************************************************
}
Public Sub mInitializeUSBDriver()                                               'usbdrv.h

	#if _device = 18F87J50
		ASM                                     
		    MOVLW 20                            
    	    MOVLB 15                            
    	    MOVWF UCFG
		End ASM               
	#else
		UCFG = UCFG_VAL
	#endif


   usb_device_state = DETACHED_STATE
   usb_stat._byte = $00
   usb_active_cfg = $00
End Sub
{
****************************************************************************
* Name    : mDisableEP1to15                                                *
* Purpose : This macro disables all endpoints except EP0. This macro       *
*         : should be called when the host sends a RESET signal or a       *
*         : SET_CONFIGURATION request.                                     *
****************************************************************************
}
Sub mDisableEP1to15()                                                           'usbdrv.h
   UEP1 = $00
   UEP2 = $00
   UEP3 = $00
   UEP4 = $00
   UEP5 = $00
   UEP6 = $00
   UEP7 = $00
   UEP8 = $00
   UEP9 = $00
   UEP10 = $00
   UEP11 = $00
   UEP12 = $00
   UEP13 = $00
   UEP14 = $00
   UEP15 = $00
End Sub
{
****************************************************************************
* Name    : mUSBBufferReady                                                *
* Require : IN Endpoint: Buffer is loaded and ready to be sent.            *
*         : OUT Endpoint: Buffer is free to be written to by SIE.          *
* Input   : buffer_dsc: Root name of the buffer descriptor group. i.e.     *
*         : ep0Bo, ep1Bi, ... Declared IN usbmmap.c. Names can be          *
*         : remapped for readability, see examples in usbcfg.h             *
* Purpose : This macro should be called each time after:                   *
*         :         1. A non-EP0 IN endpoint buffer is populated with data *
*         :         2. A non-EP0 OUT endpoint buffer is read.              *
*         :         This macro turns the buffer ownership to SIE for       *
*         :         servicing. It also toggles the DTS bit for             *
*         :         synchronization.                                       *
****************************************************************************
}
Sub mUSBBufferReady(ByRef buffer_dsc As BDT)                                    'usbdrv.h
   buffer_dsc.Stat._byte = buffer_dsc.Stat._byte And _DTSMASK        // Save only DTS bit
   buffer_dsc.Stat.DTS = Not buffer_dsc.Stat.DTS                     // Toggle DTS bit 
   buffer_dsc.Stat._byte = buffer_dsc.Stat._byte Or _USIE Or _DTSEN  // Turn ownership to SIE 
End Sub
{
****************************************************************************
* Name    : mUSBCheckAdrPendingState                                       *
* Purpose : Specialized checking routine, it checks if the device is in    *
*         : the ADDRESS PENDING STATE and services it if it is.            *
****************************************************************************
}
Sub mUSBCheckAdrPendingState()                                                  'usb9.h
   If usb_device_state = ADR_PENDING_STATE Then
      UADDR = SetupPkt.bDevADR     
      If (UADDR > 0) Then                      
         usb_device_state = ADDRESS_STATE
      Else     
         usb_device_state = DEFAULT_STATE
      EndIf
   EndIf
End Sub

//  ============================ HID CLASS ============================ 
#if USB_USE_HID

Dim hid_rpt_rx_len As Byte 

// make public for CDC module...
Public Dim
   HIDRXBytesAvailable As HID_BD_OUT.Cnt
{
****************************************************************************
* Name    : mUSBGetHIDDscAdr                                               *
* Purpose : Get HID descriptor address                                     *
****************************************************************************
}   
Sub mUSBGetHIDDscAdr()                                                          'usbcfg.h
   If usb_active_cfg = 1 Then
      pSrc.bRom = USB_CD_Ptr(1) // @cfg01.hid_i00a00 
      Inc(pSrc.bRom,18)
   EndIf  
End Sub
{
****************************************************************************
* Name    : mUSBGetHIDRptDscAdr                                            *
* Purpose : Get HID descriptor report address                              *
****************************************************************************
}
Sub mUSBGetHIDRptDscAdr()                                                       'usbcfg.h
   If usb_active_cfg = 1 Then
      pSrc.bRom = @hid_rpt01 
   EndIf
End Sub
{
****************************************************************************
* Name    : mUSBGetHIDRptDscSize                                           *
* Purpose : Get HID report descriptor size                                 *
****************************************************************************
}
Sub mUSBGetHIDRptDscSize()                                                      'usbcfg.h
   If usb_active_cfg = 1 Then
      wCount = SizeOf(hid_rpt01) 
   EndIf
End Sub
{
****************************************************************************
* Name    : mHIDRxIsBusy                                                   *
* Purpose : This macro is used to check if HID OUT endpoint is busy (owned *
*         : by SIE) or not.                                                *
****************************************************************************
}
Public Dim mHIDRxIsBusy As HID_BD_OUT.Stat.UOWN                                 'hid.h
{
****************************************************************************
* Name    : mHIDTxIsBusy                                                   *
* Purpose : This macro is used to check if HID IN endpoint is busy (owned  * 
*         : by SIE) or not.                                                *
****************************************************************************
}
Public Dim mHIDTxIsBusy As HID_BD_IN.Stat.UOWN                                  'hid.h
{
****************************************************************************
* Name    : HandleControlOutReport                                         *
* Purpose : Checks to see if an output or Feature report has arrived on    *
*         : the control pipe. if yes, extracts and uses the data.          *
****************************************************************************
}
Sub HandleControlOutReport()                                                    'user_generic_hid.c
	Dim count As Byte

	// Find out if an Output or Feature report has arrived on the control pipe.
	// Get the report type from the Setup packet.
	Select SetupPkt.W_Value.Byte1
		Case 0x02: // Output report 

			// Get the report ID from the Setup packet.
    		Select SetupPkt.W_Value.Byte0
				Case 0: // Report ID 0

					// This example application copies the Output report data 
					// to hid_report_in. 
					// (Assumes Input and Output reports are the same length.)
					// A "real" application would do something more useful with the data.

				    // wCount holds the number of bytes read in the Data stage.
					// This example assumes the report fits in one transaction.	

					count = 0
					While count < HID_OUTPUT_REPORT_BYTES
						hid_report_in(count) = hid_report_out(count)
						Inc(count)
					Wend
					// The number of bytes in the report (from usbcfg.h).
			End Select

		Case 0x03: // Feature report 

			// Get the report ID from the Setup packet.

    		Select SetupPkt.W_Value.Byte0
				Case 0: // Report ID 0

				// The Feature report data is in hid_report_feature.
				// This example application just sends the data back in the next
				// Get_Report request for a Feature report.			

			    // wCount holds the number of bytes read in the Data stage.
				// This example assumes the report fits in one transaction.	
			End Select
   End Select
End Sub

{
****************************************************************************
* Name    : HIDGetReportHandler                                            *
* Purpose : Called on receiving a Setup packet with a GET_REPORT request.  *
* 			 : Extracts the report type and the report ID of the report the   *
*         : device will send in the Data stage of the transfer. Calls a    *
*         : function to provide the data.	                                 *
****************************************************************************
}
Sub HIDGetReportHandler()                                                       'hid.c

   // The report type is in the high byte of the setup packet's Value field.
	// 3 = Feature; 1 = Input.

   Select SetupPkt.W_Value.Byte1
		// Input report
      Case 1: 

			// Find out which report ID was specified.
			// The report ID is in the low byte (LSB) of the Value field.

			// This example supports Report ID 0.

    		Select SetupPkt.W_Value.Byte0
				Case 0: // Report ID 0
					// HID will handle the request.
               ctrl_trf_session_owner = MUID_HID

					// Provide the data to send.
               pSrc.bRam = @hid_report_in
	            wCount.Byte0 = HID_INPUT_REPORT_BYTES // The number of bytes in the report 
				Case 1: // Report ID 1
					// Place similar code to handle Report ID 1 here.
			End Select

		// Feature report
      Case 3: 

			// The report ID is in the low byte of the Setup packet's Value field.
			// This example supports Report ID 0.

    		Select SetupPkt.W_Value.Byte0
				Case 0: // Report ID 0

					// The HID class code will handle the request.
			      ctrl_trf_session_owner = MUID_HID

					// Provide the data to send.
               pSrc.bRam = @hid_report_feature
               wCount.Byte0 = HID_FEATURE_REPORT_BYTES // The number of bytes in the report
			End Select
   End Select

	// The data is in RAM.
   usb_stat.ctrl_trf_mem = _RAM               // Set memory type
End Sub
{
****************************************************************************
* Name    : HIDSetReportHandler                                            *
* Purpose : Called on receiving a Setup packet with a SET_REPORT request.  *
* 			 : Extracts the report type and the report ID of the report the   *
*         : host will send IN the Data stage of the transfer. Places the   *
*         : report type and report ID IN out_report_status.	               *
****************************************************************************
}
Sub HIDSetReportHandler()                                                       'hid.c
	// The report type is in the high byte of the Setup packet's Value field.
	// 3 = Feature; 2 = Output.

   Select SetupPkt.W_Value.Byte1
		Case 2: // Output report

			// The report ID is in the low byte of the Setup packet's Value field.
			// This example supports Report ID 0.

    		Select SetupPkt.W_Value.Byte0
				Case 0: // Report ID 0

				// HID will handle the request.
		      ctrl_trf_session_owner = MUID_HID

				// Save the report type and report ID in out_report_status.

				//out_report_status = SetOutReportStatus
				//	(MSB(SetupPkt.W_Value), 
				//	LSB(SetupPkt.W_Value));

				// When the report arrives in the data stage, the data will be   
				// stored in hid_report_out (defined in usbmmap.c).

			   pDst.bRam = @hid_report_out

			End Select

		Case 3: // Feature report

			// Find out which report ID was specified.
			// The report ID is in the low byte (LSB) of the Value field.

			// This example supports Report ID 0.
    		Select SetupPkt.W_Value.Byte0
				Case 0: // Report ID 0

				// HID will handle the request.
			   ctrl_trf_session_owner = MUID_HID

				// Save the report type and report ID in out_report_status.

				//out_report_status = SetOutReportStatus
				//	(MSB(SetupPkt.W_Value), 
				//	LSB(SetupPkt.W_Value));

				// When the report arrives in the data stage, the data will be  
				// stored in hid_report_feature (defined in usbmmap.c).
		      pDst.bRam = @hid_report_feature
         End Select
   End Select
End Sub
{
****************************************************************************
* Name    : USBCheckHIDRequest                                             *
* Purpose : This routine checks the setup data packet to see if it knows   *
*         : how to handle it                                               *
****************************************************************************
}
// variables -  used in USBCheckHIDRequest                                      'hid.c
Dim idle_rate As Byte        
Dim active_protocol As Byte   // [0] Boot Protocol [1] Report Protocol
Sub USBCheckHIDRequest()                                                        'hid.c
   // The packet must be directed to an interface with the same ID as the 
	// device's HID interface.

   If GetRecipient <> RCPT_INTF Then
      Exit
   EndIf
   If SetupPkt.bIntfID <> HID_INTF_ID Then
      Exit
   EndIf

   // There are two standard requests that hid.c may support.
   // 1. GET_DSC(DSC_HID,DSC_RPT,DSC_PHY);
   // 2. SET_DSC(DSC_HID,DSC_RPT,DSC_PHY);
   If SetupPkt.bRequest = GET_DSC Then
      // Request for a descriptor.
      Select SetupPkt.bDscType

			// HID descriptor.
         Case DSC_HID:
            ctrl_trf_session_owner = MUID_HID
            mUSBGetHIDDscAdr()        // See usbcfg.h
            wCount = SizeOf(USB_HID_DSC)

   		// Report descriptor.
         Case DSC_RPT:
            ctrl_trf_session_owner = MUID_HID
            mUSBGetHIDRptDscAdr()     // See usbcfg.h
            mUSBGetHIDRptDscSize()    // See usbcfg.h

			// Physical descriptor.
         Case DSC_PHY:
            // ctrl_trf_session_owner = MUID_HID
       End Select
       usb_stat.ctrl_trf_mem = _ROM
    EndIf

    If GetRequestType <> CLASS Then
       Exit
    EndIf

	// HID-specific requests.
   Select SetupPkt.bRequest

         // required for all HID tranfers
        Case GET_REPORT:
            HIDGetReportHandler() 

         // optional - needed for control transfers only
        Case SET_REPORT:
            HIDSetReportHandler() 

        // optional
        Case GET_IDLE:
            ctrl_trf_session_owner = MUID_HID
            pSrc.bRam = @idle_rate             // Set source
            usb_stat.ctrl_trf_mem = _RAM       // Set memory type
            wCount.Byte0 = 1                   // Set data count

        // optional - except for keyboards using the boot protocol
        Case SET_IDLE:
            ctrl_trf_session_owner = MUID_HID
            idle_rate = SetupPkt.W_Value.Byte1

        // required for HIDs that support the boot protocol
        Case GET_PROTOCOL:
            ctrl_trf_session_owner = MUID_HID
            pSrc.bRam = @active_protocol       // Set source
            usb_stat.ctrl_trf_mem = _RAM       // Set memory type
            wCount.Byte0 = 1                   // Set data count

        // required for HIDs that support the boot protocol
        Case SET_PROTOCOL:
            ctrl_trf_session_owner = MUID_HID
            active_protocol = SetupPkt.W_Value.Byte0
   End Select
End Sub
{
****************************************************************************
* Name    : HIDInitEP                                                      *
* Purpose : HIDInitEP initializes HID endpoints, buffer descriptors,       *
*         : internal state-machine, and variables. It should be called     *
*         : after the USB host has sent OUT a SET_CONFIGURATION request.   *                                                            
****************************************************************************
}
Sub HIDInitEP()                                                                 'hid.c
    hid_rpt_rx_len =0

    HID_UEP = EP_OUT_IN Or HSHK_EN                // Enable 2 data pipes

    HID_BD_OUT.Cnt = SizeOf(hid_report_out)    // Set buffer size
    HID_BD_OUT.ADR = @hid_report_out    // Set buffer address
    HID_BD_OUT.Stat._byte = _USIE Or _DAT0 Or _DTSEN // Set status

    (*
      Do not have to init Cnt of IN pipes here.
      Reason:  Number of bytes to send to the host
               varies from one transaction to
               another. Cnt should equal the exact
               number of bytes to transmit for
               a given IN transaction.
               This number of bytes will only
               be known right before the data is
               sent.
    *)
    HID_BD_IN.ADR = @hid_report_in      // Set buffer address
    HID_BD_IN.Stat._byte = _UCPU Or _DAT1         // Set status
End Sub
{
****************************************************************************
* Name    : HIDTxReport                                                    *
* Require : mHIDTxIsBusy() must return false. Value of 'len' must be equal *
*         : to or smaller than HID_INT_IN_EP_SIZE for an interrupt         *
*         : endpoint, the largest buffer size is 64 bytes.                 *
* Input:  : buffer  : POINTER to the starting location of data bytes       *
*         : len     : Number of bytes to be transferred                    *
* Purpose : Use this macro to transfer data located in data memory.        *
*         : NOTE : mHIDTxIsBusy() must return false before user can call   *
*         : this function. Unexpected behavior will occur if this function *
*         : when mHIDTxIsBusy                                              *
****************************************************************************
}
Public Sub HIDTxReport(Buffer As FSR1, len As Byte)                            'hid.c
	Dim i As Byte

	If len > HID_INT_IN_EP_SIZE Then
      len = HID_INT_IN_EP_SIZE
	EndIf

   // Copy data from user's buffer to dual-ram buffer
   FSR0 = @hid_report_in
   i = 0
   While i < len
    	POSTINC0 = POSTINC1
      Inc(i)
   Wend

   HID_BD_IN.Cnt = len
   mUSBBufferReady(HID_BD_IN)
End Sub
{
****************************************************************************
* Name    : HIDRxReport                                                    *
* Require : Value of input argument 'len' should be smaller than the       *
*         : maximum endpoint size responsible for receiving report data    *
*         : from USB host for HID CLASS. Input argument 'buffer' should    *
*         : point to a buffer area that is bigger or equal to the size     *
*         : specified by 'len'.                                            *
* Input   : buffer  : POINTER to where received bytes are to be stored     *
*         : len     : The number of bytes expected.                        *
* Output  : The number of bytes copied to buffer. Publicly accessible      *
*         : variable hid_rpt_rx_len is updated with the number of bytes    *
*         : copied to buffer. Once HIDRxReport is called, subsequent       *
*         : retrieval of hid_rpt_rx_len can be done by calling macro       *
*         : mHIDGetRptRxLength().                                          *
* Purpose:: HIDRxReport copies a string of bytes received through          *
*         : USB HID OUT endpoint to a user's specified location. It is a   *
*         : non-blocking function. It does not wait for data if there is   *
*         : no data available. Instead it returns '0' to notify the        *
*         : caller that there is no data available. If the actual number   *
*         : of bytes received is larger than the number of bytes expected  *
*         : (len), only the expected number of bytes specified will be     *
*         : copied to buffer. If the actual number of bytes received is    *
*         : smaller than the number of bytes expected (len), only the      *
*         : actual number of bytes received will be copied to buffer.      *                                                        
****************************************************************************
}
Public Function HIDRxReport(Buffer As FSR0, len As Byte) As Byte               'hid.c
   hid_rpt_rx_len = 0

   If mHIDRxIsBusy = 0 Then
      // Adjust the expected number of bytes to equal
      // the actual number of bytes received.
      If len > HID_BD_OUT.Cnt Then
         len = HID_BD_OUT.Cnt
      EndIf   

      // Copy data from dual-ram buffer to user's buffer
      FSR1 = @hid_report_out
      hid_rpt_rx_len = 0
      While hid_rpt_rx_len < len
         POSTINC0 = POSTINC1
         Inc(hid_rpt_rx_len)
      Wend   

      // Prepare dual-ram buffer for next OUT transaction
      HID_BD_OUT.Cnt = SizeOf(hid_report_out)
      mUSBBufferReady(HID_BD_OUT)
   EndIf
   result = hid_rpt_rx_len
End Function
#endif // USB_USE_HID
//  ========================== END HID CLASS ========================== 

//  ============================ CDC CLASS ============================ 
#if USB_USE_CDC
Dim                                                                             'cdc.c
   cdc_trf_state As Byte,            // States are defined cdc.h
   pCDCSrc As POINTER,               // Dedicated source pointer
   pCDCDst As POINTER,               // Dedicated destination pointer
   cdc_tx_len As Word,               // total tx length
   cdc_mem_type As Byte,             // _ROM, _RAM
   line_coding As CDC_LINE_CODING,   // Buffer to store line coding information
   control_signal_bitmap As CDC_CONTROL_SIGNAL_BITMAP

// make public for CDC module...
Public Dim
   CDCControlSignal As control_signal_bitmap,
   CDCDataRX As cdc_data_rx,
   CDCDataTX As cdc_data_tx,
   CDCRXBytesAvailable As CDC_BULK_BD_OUT.Cnt

// SEND_ENCAPSULATED_COMMAND and GET_ENCAPSULATED_RESPONSE are required
// requests according to the CDC specification.
// However, it is not really being used here, therefore a dummy buffer is
// used for conformance.
Const dummy_length = $08
Dim dummy_encapsulated_cmd_response(dummy_length) As Byte
{
****************************************************************************
* Name    : mUSBUSARTIsTxTrfReady                                          *
* Purpose : This macro is used to check if the CDC CLASS is ready to send  *
*         : more data.                                                     *
****************************************************************************
}
Public Function mUSBUSARTIsTxTrfReady() As Boolean                              'cdc.h
   result = (cdc_trf_state = CDC_TX_READY)
End Function
{
****************************************************************************
* Name    : mCDCUsartRxIsBusy                                              *
* Purpose : This macro is used to check if CDC bulk OUT endpoint is busy   *
*         : (owned by SIE) or not.                                         *
****************************************************************************
}
Public Dim mCDCUsartRxIsBusy As CDC_BULK_BD_OUT.Stat.UOWN                       'cdc.h
{
****************************************************************************
* Name    : mCDCUsartTxIsBusy                                              *
* Purpose : This macro is used to check if CDC bulk IN endpoint is busy    *
*         : (owned by SIE) or not.                                         *
****************************************************************************
}
Public Dim mCDCUsartTxIsBusy As CDC_BULK_BD_IN.Stat.UOWN                        'cdc.h
{
****************************************************************************
* Name    : mUSBUSARTTxRam                                                 *
* Require : cdc_trf_state must be IN the CDC_TX_READY state.               *
* Purpose : Use this macro to transfer data located IN data memory. Use    *
*         : this macro when:                                               *
*         :    1. Data stream is not null-terminated                       *
*         :    2. Transfer length is known                                 *
*         : Remember: cdc_trf_state must == CDC_TX_READY. The actual       * 
*         : transfer is handled by CDCTxService().                         *
****************************************************************************
}
Public Sub mUSBUSARTTxRam(pData As Word,len As Word)                            'cdc.h
   pCDCSrc.bRam = pData
   cdc_tx_len = len
   cdc_mem_type = _RAM
   cdc_trf_state = CDC_TX_BUSY
End Sub
{
****************************************************************************
* Name    : mUSBUSARTTxRom                                                 *
* Require : cdc_trf_state must be IN the CDC_TX_READY state.               *
* Purpose : Use this macro to transfer data located IN data memory. Use    *
*         : this macro when:                                               *
*         :    1. Data stream is not null-terminated                       *
*         :    2. Transfer length is known                                 *
*         : Remember: cdc_trf_state must == CDC_TX_READY. The actual       * 
*         : transfer is handled by CDCTxService().                         *
****************************************************************************
}
Public Sub mUSBUSARTTxRom(pData As Word,len As Word)                            'cdc.h
   pCDCSrc.bRom = pData
   cdc_tx_len = len
   cdc_mem_type = _ROM
   cdc_trf_state = CDC_TX_BUSY
End Sub
{
****************************************************************************
* Name    : USBCheckCDCRequest                                             *
* Purpose : This routine checks the setup data packet to see if it knows   *
*         : how to use it                                                  *
****************************************************************************
}
Sub USBCheckCDCRequest()                                                        'cdc.c

   // If request recipient is not an interface then return
   If GetRecipient <> RCPT_INTF Then
      Exit
   EndIf

   // If request type is not class-specific then return
   If GetRequestType <> CLASS Then
      Exit
   EndIf

   // Interface ID must match interface numbers associated with
   // CDC class, else return
   If (SetupPkt.bIntfID <> CDC_COMM_INTF_ID) And (SetupPkt.bIntfID <> CDC_DATA_INTF_ID) Then
      Exit
   EndIf

   Select SetupPkt.bRequest
      Case SEND_ENCAPSULATED_COMMAND:
         ctrl_trf_session_owner = MUID_CDC
         pSrc.bRam = @dummy_encapsulated_cmd_response
         usb_stat.ctrl_trf_mem = _RAM
         wCount.Byte0 = dummy_length

      Case GET_ENCAPSULATED_RESPONSE:
         ctrl_trf_session_owner = MUID_CDC

         // Populate dummy_encapsulated_cmd_response first.
         pDst.bRam = @dummy_encapsulated_cmd_response

'      case SET_COMM_FEATURE:                 // Optional
'      case GET_COMM_FEATURE:                  // Optional
'      case CLEAR_COMM_FEATURE:                // Optional

      Case SET_LINE_CODING:
         ctrl_trf_session_owner = MUID_CDC
         pDst.bRam = @LINE_CODING    // Set destination

      Case GET_LINE_CODING:
         ctrl_trf_session_owner = MUID_CDC
         pSrc.bRam = @LINE_CODING           // Set source
         usb_stat.ctrl_trf_mem = _RAM       // Set memory type
         wCount.Byte0 = LINE_CODING_LENGTH  // Set data count    

      Case SET_CONTROL_LINE_STATE:
         ctrl_trf_session_owner = MUID_CDC
         control_signal_bitmap._byte = SetupPkt.W_Value.Byte0

'      case SEND_BREAK:                        // Optional
   End Select
End Sub
{
****************************************************************************
* Name    : CDCInitEP                                                      *
* Purpose : CDCInitEP initializes CDC endpoints, buffer descriptors,       *
*         : internal state-machine, and variables. It should be called     *
*         : after the USB host has sent OUT a SET_CONFIGURATION request.   *
****************************************************************************
}
Sub CDCInitEP()                                                                 'cdc.c
   // abstract line coding information
   LINE_CODING.dwDTERate = 115200      // baud rate
   LINE_CODING.bCharFormat = $00       // 1 stop bit
   LINE_CODING.bParityType = $00       // None
   LINE_CODING.bDataBits = $08         // 5,6,7,8, or 16

   cdc_trf_state = CDC_TX_READY

   CDC_COMM_UEP = EP_IN Or HSHK_EN               // Enable 1 Comm pipe
   CDC_DATA_UEP = EP_OUT_IN Or HSHK_EN           // Enable 2 data pipes
   (*
      Do not have to init Cnt of IN pipes here.
      Reason:  Number of bytes to send to the host varies from one transaction 
               to another. Cnt should equal the exact number of bytes to 
               transmit for a given IN transaction. This number of bytes will 
               only be known right before the data is sent.
    *) 
    CDC_INT_BD_IN.ADR = @cdc_notice               // Set buffer address
    CDC_INT_BD_IN.Stat._byte = _UCPU Or _DAT1     // Set status

    CDC_BULK_BD_OUT.Cnt = SizeOf(cdc_data_rx)     // Set buffer size
    CDC_BULK_BD_OUT.ADR = @cdc_data_rx            // Set buffer address
    CDC_BULK_BD_OUT.Stat._byte = _USIE Or _DAT0 Or _DTSEN // Set status

    CDC_BULK_BD_IN.ADR = @cdc_data_tx             // Set buffer address
    CDC_BULK_BD_IN.Stat._byte = _UCPU Or _DAT1    // Set status
End Sub
{
****************************************************************************
* Name    : CDCRXPrepareForNext                                            *
* Purpose : Prepare for next CDC transmission                              *
****************************************************************************
}
Public Sub CDCRXPrepareForNext()
   CDC_BULK_BD_OUT.Cnt = SizeOf(cdc_data_rx)
   mUSBBufferReady(CDC_BULK_BD_OUT)
End Sub 
{
****************************************************************************
* Name    : CDCTxService                                                   *
* Purpose : CDCTxService handles device-to-host transaction(s). This       *
*         : function should be called once per Main program loop.          *
****************************************************************************
} 
Public Sub CDCTxService()                                                       'cdc.c
   Dim byte_to_send As Byte

   If mCDCUsartTxIsBusy <> 0 Then
      Exit
   EndIf

   //   Completing stage is necessary while [ mCDCUSartTxIsBusy()==1 ].
   //   By having this stage, user can always check cdc_trf_state,
   //   and not having to call mCDCUsartTxIsBusy() directly.
   If cdc_trf_state = CDC_TX_COMPLETING Then
      cdc_trf_state = CDC_TX_READY
   EndIf    

   // If CDC_TX_READY state, nothing to do, just return.
   If cdc_trf_state = CDC_TX_READY Then
      Exit
   EndIf

   // If CDC_TX_BUSY_ZLP state, send zero length packet
   If cdc_trf_state = CDC_TX_BUSY_ZLP Then
      CDC_BULK_BD_IN.Cnt = 0
      cdc_trf_state = CDC_TX_COMPLETING
   Else
      If cdc_trf_state = CDC_TX_BUSY Then

         // First, have to figure out how many byte of data to send.
       	If cdc_tx_len > SizeOf(cdc_data_tx) Then
   	      byte_to_send = SizeOf(cdc_data_tx)
    	   Else
    	      byte_to_send = cdc_tx_len
    	   EndIf

         // Next, load the number of bytes to send to Cnt in buffer descriptor
         CDC_BULK_BD_IN.Cnt = byte_to_send

         // Subtract the number of bytes just about to be sent from the total.
      	cdc_tx_len = cdc_tx_len - byte_to_send

         pCDCDst.bRam = @cdc_data_tx     // Set destination pointer
         If cdc_mem_type = _ROM Then     // Determine type of memory source
            TABLEPTR = pCDCSrc.bRom
            FSR0 = pCDCDst.bRam
            Inc(pCDCDst.bRam, byte_to_send)
            Inc(pCDCSrc.bRam, byte_to_send)
            While byte_to_send > 0
               POSTINC0 = ReadROM
               Dec(byte_to_send)
            Wend
(*            
            while byte_to_send > 0
               SetRAMPtr(pCDCDst.bRam,GetROMPtr(pCDCSrc.bRom))
               inc(pCDCDst.bRam)
               inc(pCDCSrc.bRom)
               dec(byte_to_send)
            wend
*)
         Else // _RAM - optimised...
            FSR0 = pCDCDst.bRam
            FSR1 = pCDCSrc.bRam
            Inc(pCDCDst.bRam, byte_to_send)
            Inc(pCDCSrc.bRam, byte_to_send)
            While byte_to_send > 0
               POSTINC0 = POSTINC1
               Dec(byte_to_send)
            Wend
         EndIf

         // Lastly, determine if a zero length packet state is necessary.
         // See explanation in USB Specification 2.0: Section 5.8.3
         If cdc_tx_len = 0 Then
            If CDC_BULK_BD_IN.Cnt = SizeOf(cdc_data_tx) Then
               cdc_trf_state = CDC_TX_BUSY_ZLP
            Else
               cdc_trf_state = CDC_TX_COMPLETING
            EndIf
         EndIf 
      EndIf
   EndIf

   // Both CDC_TX_BUSY and CDC_TX_BUSY_ZLP states use the following macro
   mUSBBufferReady(CDC_BULK_BD_IN)        
End Sub  
(*
  ========================== END CDC CLASS ========================== 
*) 
#endif //USB_USE_CDC
{
****************************************************************************
* Name    : USBStdGetDscHandler                                            *
* Purpose : This routine handles the STANDARD GET_DESCRIPTOR request. It   *
*         : utilizes tables dynamically looks up descriptor size. This     *
*         : routine should never have to be modified if the tables in      *
*         : usbdsc.c are declared correctly.                               *
****************************************************************************
}
Sub USBStdGetDscHandler()                                                       'usb9.c
   If SetupPkt.bmRequestType = $80 Then
      Select SetupPkt.bDscType
         Case DSC_DEV:
            ctrl_trf_session_owner = MUID_USB9
            pSrc.bRom = @device_dsc // djb USB_DD_Ptr  // @device_dsc
            wCount = SizeOf(device_dsc) // djb USB_DD_Size    // sizeof(device_dsc)
         Case DSC_CFG:
            ctrl_trf_session_owner = MUID_USB9
            pSrc.bRom = USB_CD_Ptr(SetupPkt.bDscIndex)
            wCount = GetROMPtr(pSrc.wRom + 2)              // Set data count
         Case DSC_STR:
            ctrl_trf_session_owner = MUID_USB9

            // ensure we have a valid string index - version 1.2...
            If SetupPkt.bDscIndex > Bound(USB_SD_Ptr) Then 
               wCount = 0
            Else
               pSrc.bRom = USB_SD_Ptr(SetupPkt.bDscIndex)
               wCount = GetROMPtr(pSrc.bRom)              // Set data count
            EndIf
      End Select
      usb_stat.ctrl_trf_mem = _ROM                     // Set memory type
   EndIf
End Sub
{
****************************************************************************
* Name    : USBStdSetCfgHandler                                            *
* Purpose : This routine first disables all endpoints by clearing UEP      *
*         : registers. It then configures (initializes) endpoints specified* 
*         : in the modifiable section.                                     *
****************************************************************************
}
Sub USBStdSetCfgHandler()                                                       'usb9.c
   ctrl_trf_session_owner = MUID_USB9
   mDisableEP1to15()
   Clear(usb_alt_intf)
   usb_active_cfg = SetupPkt.bCfgValue
   If SetupPkt.bCfgValue = 0 Then
      usb_device_state = ADDRESS_STATE
   Else
      usb_device_state = CONFIGURED_STATE

      // Modifiable Section
      #if USB_USE_HID  
      HIDInitEP()
      #endif

      #if USB_USE_CDC
      CDCInitEP()
      #endif        
      // End modifiable section
   EndIf
End Sub
{
****************************************************************************
* Name    : USBStdGetStatusHandler                                         *
* Purpose : This routine handles the STANDARD GET_STATUS request           *
****************************************************************************
}
Sub USBStdGetStatusHandler()                                                    'usb9.c
   CtrlTrfData._byte0 = 0                         // Initialize content
   CtrlTrfData._byte1 = 0

   Select GetRecipient
      Case RCPT_DEV:
         ctrl_trf_session_owner = MUID_USB9
         // _byte0: bit0: Self-Powered Status [0] Bus-Powered [1] Self-Powered
         //         bit1: RemoteWakeup        [0] Disabled    [1] Enabled
         If self_power = 1 Then                     // self_power defined in io_cfg.h
            CtrlTrfData._byte0 = CtrlTrfData._byte0 Or %000000001    // Set bit0

            If usb_stat.RemoteWakeup = 1 Then          // usb_stat defined in usbmmap.c
                CtrlTrfData._byte0 = CtrlTrfData._byte0 Or $00000010     // Set bit1
            EndIf
         EndIf
      Case RCPT_INTF:
         ctrl_trf_session_owner = MUID_USB9     // No data to update
      Case RCPT_EP:
         ctrl_trf_session_owner = MUID_USB9
         // _byte0: bit0: Halt Status [0] Not Halted [1] Halted
         pDst.bRam = @ep0Bo + (GetEPNum * 8) + (SetupPkt.EPDir * 4)
         If (GetRAMPtr(pDst.bRam) And _BSTALL) <> 0 Then    // Use _BSTALL as a bit mask
            CtrlTrfData._byte0 = $01// Set bit0
         EndIf
   End Select

   If ctrl_trf_session_owner = MUID_USB9 Then
      pSrc.bRam = @CtrlTrfData            // Set Source
      usb_stat.ctrl_trf_mem = _RAM        // Set memory type
      wCount.Byte0 = 2                   // Set data count
   EndIf
End Sub
{
****************************************************************************
* Name    : USBStdFeatureReqHandler                                        *
* Purpose : This routine handles the STANDARD SET & clear FEATURES requests*                                                               
****************************************************************************
}
Sub USBStdFeatureReqHandler()                                                   'usb9.c
   If (SetupPkt.bFeature = DEVICE_REMOTE_WAKEUP) And (GetRecipient = RCPT_DEV) Then
      ctrl_trf_session_owner = MUID_USB9
      If SetupPkt.bRequest = SET_FEATURE Then
         usb_stat.RemoteWakeup = 1
      Else
         usb_stat.RemoteWakeup = 0
      EndIf
   EndIf

   If (SetupPkt.bFeature = ENDPOINT_HALT) And (GetRecipient = RCPT_EP) And (GetEPNum <> 0) Then
      ctrl_trf_session_owner = MUID_USB9
      // Must do address calculation here
      pDst.bRam = @ep0Bo + (GetEPNum * 8) + (SetupPkt.EPDir * 4)
      If SetupPkt.bRequest = SET_FEATURE Then
         SetRAMPtr(pDst.bRam, _USIE Or _BSTALL)
      Else
         If SetupPkt.EPDir = 1 Then// IN
            SetRAMPtr(pDst.bRam, _UCPU)
         Else
            SetRAMPtr(pDst.bRam, _USIE Or _DAT0 Or _DTSEN)
         EndIf
      EndIf
   EndIf
End Sub
{
****************************************************************************
* Name    : USBCheckStdRequest                                             *
* Purpose : This routine checks the setup data packet to see if it knows   *
*         : how to handle it                                               *
****************************************************************************
}
Sub USBCheckStdRequest()                                                        'usb9.c
    If GetRequestType <> STANDARD Then
       Exit
    EndIf

    Select SetupPkt.bRequest
        Case SET_ADR:
            ctrl_trf_session_owner = MUID_USB9
            usb_device_state = ADR_PENDING_STATE       // Update state only
            // See USBCtrlTrfInHandler() in usbctrltrf.c for the next step 
        Case GET_DSC:
            USBStdGetDscHandler()
        Case SET_CFG:
            USBStdSetCfgHandler()
        Case GET_CFG:
            ctrl_trf_session_owner = MUID_USB9
            pSrc.bRam = @usb_active_cfg                 // Set Source
            usb_stat.ctrl_trf_mem = _RAM                // Set memory type
            wCount.Byte0 = 1                             // Set data count
        Case GET_STATUS:
            USBStdGetStatusHandler()
        Case CLR_FEATURE:
            USBStdFeatureReqHandler() 
        Case CLR_FEATURE
            USBStdFeatureReqHandler()
        Case GET_INTF:
            ctrl_trf_session_owner = MUID_USB9
            pSrc.bRam = @usb_alt_intf + SetupPkt.bIntfID  // Set source
            usb_stat.ctrl_trf_mem = _RAM                // Set memory type
            wCount.Byte0 = 1                            // Set data count
        Case SET_INTF:
            ctrl_trf_session_owner = MUID_USB9
            usb_alt_intf(SetupPkt.bIntfID) = SetupPkt.bAltID
        Case SET_DSC:
        Case SYNCH_FRAME:
   End Select
End Sub
{
****************************************************************************
* Name    : USBCtrlTrfTxService                                            *
* Require : pSrc, wCount, and usb_stat.ctrl_trf_mem are setup properly.    *
* Purpose : This routine should be called from only two places. One from   *
*         : USBCtrlEPServiceComplete() and one from USBCtrlTrfInHandler(). *
*         : It takes care of managing a transfer over multiple USB         *
*         : transactions. This routine works with isochronous endpoint     *
*         : larger than 256 bytes and is shown here as an example of how to* 
*         : deal with BC9 and BC8. IN reality, a control endpoint can never*
*         : be larger than 64 bytes.                                       *
****************************************************************************
}          
Sub USBCtrlTrfTxService()                                                       'usbctrltr.c

   // This code executes in the Data stage of a Control Read transfer.
	Dim byte_to_send As Word

   // First, have to figure out how many byte of data to send.
   If wCount < EP0BufferSize Then
      byte_to_send = wCount
   Else   
      byte_to_send = EP0BufferSize
   EndIf   

   // Next, load the number of bytes to send to BC9..0 in buffer descriptor
   ep0Bi.Stat.BC9 = 0
   ep0Bi.Stat.BC8 = 0
   ep0Bi.Stat._byte = ep0Bi.Stat._byte Or byte_to_send.Byte1
   ep0Bi.Cnt = byte_to_send.Byte0

   // Subtract the number of bytes just about to be sent from the total.
   wCount = wCount - byte_to_send

   pDst.bRam = @CtrlTrfData              // Set destination pointer
   If usb_stat.ctrl_trf_mem = _ROM Then  // Determine type of memory source

      TABLEPTR = pSrc.bRom
      FSR0 = pDst.bRam
      Inc(pSrc.bRom,byte_to_send)
      While byte_to_send > 0
         POSTINC0 = ReadROM
         Dec(byte_to_send)
      Wend

   Else // RAM
      FSR1 = pSrc.bRam
      FSR0 = pDst.bRam
      Inc(pSrc.bRam,byte_to_send)
      While byte_to_send > 0
         POSTINC0 = POSTINC1
         Dec(byte_to_send)
      Wend
   EndIf
End Sub
{
****************************************************************************
* Name    : USBCtrlEPServiceComplete                                       *
* Purpose : This routine wrap up the ramaining tasks IN servicing a Setup  *
*         : Request. Its main task is to set the endpoint controls         *
*         : appropriately for a given situation. See code below.           *
*         : There are three main scenarios:                                *
*         :   a) There was no handler for the Request, IN this case a STALL*
*         :      should be sent OUT.                                       *
*         :   b) The host has requested a read control transfer, endpoints *
*         :      are required to be setup IN a specific way.               *
*         :   c) The host has requested a write control transfer, or a     *
*         :      control data stage is not required, endpoints are required*
*         :      to be setup IN a specific way.                            *
*         : Packet processing is resumed by clearing PKTDIS bit.           *                                                    
****************************************************************************
} 
Sub USBCtrlEPServiceComplete()                                                  'usbctrltr.c
   // PKTDIS bit is set when a Setup Transaction is received.
   // Clear before modifying ep0Bi.Stat or ep0Bo.Stat.
   UCONBits.PKTDIS = 0

   If ctrl_trf_session_owner = MUID_NULL Then
      // If no one knows how to service this request then stall.
      // Must also prepare EP0 to receive the next SETUP transaction.
      ep0Bo.Cnt = EP0BufferSize
      ep0Bo.ADR = @SetupPkt

      ep0Bo.Stat._byte = _USIE Or _BSTALL
      ep0Bi.Stat._byte = _USIE Or _BSTALL

    // A module has claimed ownership of the control transfer session.
    Else
       If SetupPkt.DataDir = DEV_TO_HOST Then

          If SetupPkt.wLength < wCount Then
             wCount = SetupPkt.wLength
          EndIf 
          USBCtrlTrfTxService()
          ctrl_trf_state = CTRL_TRF_TX

          // Control Read:
          // <SETUP[0]><IN[1]><IN[0]>...<OUT[1]> | <SETUP[0]>
          // 1. Prepare OUT EP to respond to early termination
          // NOTE:
          // If something went wrong during the control transfer,
          // the last status stage may not be sent by the host.
          // When this happens, two different things could happen
          // depending on the host.
          // a) The host could send out a RESET.
          // b) The host could send out a new SETUP transaction
          //    without sending a RESET first.
          // To properly handle case (b), the OUT EP must be setup
          // to receive either a zero length OUT transaction, or a
          // new SETUP transaction.

          // Since the SETUP transaction requires the DTS bit to be
          // DAT0 while the zero length OUT status requires the DTS
          // bit to be DAT1, the DTS bit check by the hardware should
          // be disabled. This way the SIE could accept either of
          // the two transactions.

          // Furthermore, the Cnt byte should be set to prepare for
          // the SETUP data (8-byte or more), and the buffer address
          // should be pointed to SetupPkt.
          ep0Bo.Cnt = EP0BufferSize 
          ep0Bo.ADR = @SetupPkt            
          ep0Bo.Stat._byte = _USIE           // Note: DTSEN is 0!

          // 2. Prepare IN EP to transfer data, Cnt should have
          //    been initialized by responsible request owner.
          ep0Bi.ADR = @CtrlTrfData
          ep0Bi.Stat._byte = _USIE Or _DAT1 Or _DTSEN

       // (SetupPkt.DataDir == HOST_TO_DEV)
       Else

          ctrl_trf_state = CTRL_TRF_RX
          // Control Write:
          // <SETUP[0]><OUT[1]><OUT[0]>...<IN[1]> | <SETUP[0]>
          // 1. Prepare IN EP to respond to early termination
          //    This is the same as a Zero Length Packet Response
          //    for control transfer without a data stage
          ep0Bi.Cnt = 0
          ep0Bi.Stat._byte = _USIE Or _DAT1 Or _DTSEN

          // Prepare OUT EP to receive data.
          ep0Bo.Cnt = EP0BufferSize
          ep0Bo.ADR = @CtrlTrfData
          ep0Bo.Stat._byte = _USIE Or _DAT1 Or _DTSEN
       EndIf
    EndIf   
End Sub
{
****************************************************************************
* Name    : USBCtrlTrfSetupHandler                                         *
* Require : SetupPkt buffer is loaded with valid USB Setup Data            *
* Purpose : This routine is a task dispatcher and has 3 stages.            *
*         :      1. It initializes the control transfer state machine.     *
*         :      2. It calls on each of the module that may know how to    *
*         :         service the Setup Request from the host.               *
*         :         module Example: USB9, HID, CDC, MSD, ...               *
*         :         as new classes are added, ClassReqHandler table IN     *
*         :         usbdsc.c should be updated to call all available       *
*         :         CLASS handlers.                                        *
*         :      3. Once each of the modules has had a chance to check if  *
*         :         it is responsible for servicing the request, stage 3   *
*         :         then checks direction of the transfer to determine how *
*         :         to prepare EP0 for the control transfer.               *
*         :         Refer to USBCtrlEPServiceComplete() for more details.  *
* Note    : Microchip USB Firmware has three different states for the      *
*         : control transfer state machine:                                *
*         :      1. WAIT_SETUP                                             *
*         :      2. CTRL_TRF_TX                                            *
*         :      3. CTRL_TRF_RX                                            *
*         : A Control Transfer is composed of many USB transactions. When  *
*         : transferring data over multiple transactions, it is important  *
*         : to keep track of data source, data destination, and data count *
*         : These three parameters are stored IN pSrc,pDst, and wCount. A  *
*         : flag is used to note if the data source is from ROM or RAM.    *
****************************************************************************
} 
Sub USBCtrlTrfSetupHandler()                                                    'usbctrltr.c

   // Stage 1
   ctrl_trf_state = WAIT_SETUP
   ctrl_trf_session_owner = MUID_NULL     // Set owner to NULL
   wCount = 0

   // Stage 2 */
   USBCheckStdRequest()                   // See system\usb9\usb9.c

   // Modifiable Section
   #if USB_USE_HID 
   USBCheckHIDRequest
   #endif

   #if USB_USE_CDC
   USBCheckCDCRequest
   #endif
   // End Modifiable Section

   // Stage 3 
   USBCtrlEPServiceComplete()
End Sub
{
****************************************************************************
* Name    : USBCtrlTrfRxService                                            *
* Require : pDst and wCount are setup properly. pSrc is always &CtrlTrfData*
*         : usb_stat.ctrl_trf_mem is always _RAM. wCount should be set to  *
*         : 0 at the start of each control transfer.                       *
* Purpose : This routine is only partially complete. Check for new version *
*         : of the firmware.                                               *
****************************************************************************
} 
Sub USBCtrlTrfRxService()                                                       'usbctrltr.c
    // This code executes in the Data stage of a Control Write transfer.
	 Dim byte_to_read As Word

    byte_to_read.Byte1 = $03 And ep0Bo.Stat._byte    // Filter out last 2 bits
    byte_to_read.Byte0 = ep0Bo.Cnt

    // Accumulate total number of bytes read
    wCount = wCount + byte_to_read

    pSrc.bRam = @CtrlTrfData
    While byte_to_read > 0
       SetRAMPtr(pDst.bRam,GetRAMPtr(pSrc.bRam))
       Inc(pDst.bRam)
       Inc(pSrc.bRam)
       Dec(byte_to_read)
    Wend
End Sub
{
****************************************************************************
* Name    : USBPrepareForNextSetupTrf                                      *
* Purpose : The routine forces EP0 OUT to be ready for a new Setup         *
*         : transaction, and forces EP0 IN to be owned by CPU.             *                                                  
****************************************************************************
} 
Sub USBPrepareForNextSetupTrf()                                                 'usbctrltr.c
    ctrl_trf_state = WAIT_SETUP            // See usbctrltrf.h
    ep0Bo.Cnt = EP0BufferSize              // Defined in usbcfg.h
    ep0Bo.ADR = @SetupPkt
    ep0Bo.Stat._byte = _USIE Or _DAT0 Or _DTSEN  // EP0 buff dsc init, see usbmmap.h
    ep0Bi.Stat._byte = _UCPU               // EP0 IN buffer initialization
End Sub
{
****************************************************************************
* Name    : USBCtrlTrfOutHandler                                           *
* Purpose : This routine handles an OUT transaction according to which     *
*         : control transfer state is currently active. Note that if the   *
*         : the control transfer was from host to device, the session owner*
*         : should be notified at the end of each OUT transaction to       *
*         : service the received data.                                     *
****************************************************************************
}
Sub USBCtrlTrfOutHandler()                                                      'usbctrltr.c
   // Direction of data packet: host to device.
   If ctrl_trf_state = CTRL_TRF_RX Then
      // It's the Data stage of a Control Write transfer.
      USBCtrlTrfRxService()

      // for control transfers only
'		if ctrl_trf_session_owner = MUID_HID then
'   	   // The only HID-class request with a host-to-device Data stage
'			// is Set_Report.
'			HandleControlOutReport()
'		endif

      // Don't have to worry about overwriting _KEEP bit
      // because if _KEEP was set, TRNIF would not have been
      // generated in the first place.
      If ep0Bo.Stat.DTS = 0 Then
         ep0Bo.Stat._byte = _USIE Or _DAT1 Or _DTSEN
      Else
         ep0Bo.Stat._byte = _USIE Or _DAT0 Or _DTSEN
      EndIf
   Else
	   // It's the Status stage of a Control Read transfer.
      USBPrepareForNextSetupTrf()
   EndIf
End Sub
{
****************************************************************************
* Name    : USBCtrlTrfInHandler                                            *
* Purpose : This routine handles an IN transaction according to which      *
*         : control transfer state is currently active. A Set Address      *
*         : Request must not change the acutal address of the device until *
*         : the completion of the control transfer. The end of the control *
*         : transfer for Set Address. Request is an in transaction.        *
*         : Therefore it is necessary to service this unique situation when*
*         :  the condition is right. Macro mUSBCheckAdrPendingState is     *
*         : defined in usb9.h and its function is to specifically service  *
*         : this event.                                                    *
****************************************************************************
}
Sub USBCtrlTrfInHandler()                                                       'usbctrltr.c
   // Direction of data packet: device to host.
   mUSBCheckAdrPendingState()         // Must check if in ADR_PENDING_STATE

   If ctrl_trf_state = CTRL_TRF_TX Then
      // It's the Data stage of a Control Read transfer.
      USBCtrlTrfTxService()
      If ep0Bi.Stat.DTS = 0 Then
         ep0Bi.Stat._byte = _USIE Or _DAT1 Or _DTSEN
      Else   
         ep0Bi.Stat._byte = _USIE Or _DAT0 Or _DTSEN
      EndIf
    Else // CTRL_TRF_RX
       // It's the Status stage of a Control Write transfer.
       USBPrepareForNextSetupTrf()
    EndIf
End Sub
{
****************************************************************************
* Name    : USBCtrlEPService                                               *
* Require : USTAT is loaded with a valid endpoint address                  *
* Purpose : USBCtrlEPService checks for three transaction types that it    *
*         : knows how to service and services them.                        *
*         :    1. EP0 SETUP                                                *
*         :    2. EP0 OUT                                                  *
*         :    3. EP0 IN                                                   *
*         : It ignores all other types (i.e. EP1, EP2, etc.)               *
****************************************************************************
}
Sub USBCtrlEPService()                                                          'usbctrltr.c
   If USTAT = EP00_OUT Then
      If GetPID = SETUP_TOKEN Then           // EP0 SETUP
         USBCtrlTrfSetupHandler()
      Else                                   // EP0 OUT
         USBCtrlTrfOutHandler()
      EndIf
    ElseIf USTAT = EP00_IN Then              // EP0 IN
        USBCtrlTrfInHandler()
    EndIf
End Sub
{
****************************************************************************
* Name    : USBModuleEnable                                                *
* Purpose : This routine enables the USB module. An end designer should    *
*         : never have to call this routine manually. This routine should  *
*         : only be called from USBCheckBusStatus().                       *
****************************************************************************
}
Sub USBModuleEnable()                                                           'usbdrv.c
   UCON = 0
   UIE = 0                                // Mask all USB interrupts
   UCONBits.USBEN = 1                     // Enable module & attach to bus
   usb_device_state = ATTACHED_STATE      // Defined in usbmmap.c & .h
End Sub
{
****************************************************************************
* Name    : USBModuleDisable                                               *
* Purpose : This routine disables the USB module. An end designer should   *
*         : never have to call this routine manually. This routine should  *
*         : only be called from USBCheckBusStatus().                       *
****************************************************************************
}
Sub USBModuleDisable()                                                          'usbdrv.c
   UCON = 0                               // Disable module & detach from bus
   UIE = 0                                // Mask all USB interrupts
   usb_device_state = DETACHED_STATE      // Defined in usbmmap.c & .h
End Sub
{
****************************************************************************
* Name    : USBCheckBusStatus                                              *
* Purpose : This routine enables/disables the USB module by monitoring the *
*         : USB power signal.                                              *
****************************************************************************
}
Public Sub USBCheckBusStatus()                                                  'usbdrv.c
   // Bus Attachment & Detachment Detection
   // usb_bus_sense is an i/o pin defined in io_cfg.h
   Const
      USB_BUS_ATTACHED = 1,
      USB_BUS_DETACHED = 0

   If usb_bus_sense = USB_BUS_ATTACHED Then    // Is USB bus attached?
       If UCONBits.USBEN = 0 Then              // Is the module off?
           USBModuleEnable()                   // Is off, enable it
       EndIf
   Else
      If UCONBits.USBEN = 1 Then               // Is the module on?
         USBModuleDisable()                    // Is on, disable it
      EndIf
   EndIf

   // After enabling the USB module, it takes some time for the voltage
   // on the D+ or D- line to rise high enough to get out of the SE0 condition.
   // The USB Reset interrupt should not be unmasked until the SE0 condition is
   // cleared. This helps preventing the firmware from misinterpreting this
   // unique event as a USB bus reset from the USB host.
    If usb_device_state = ATTACHED_STATE Then
       If UCONBits.SE0 = 0 Then
          UIR = 0                        // Clear all USB interrupts
          UIE = 0                        // Mask all USB interrupts
          UIEBits.URSTIE = 1             // Unmask RESET interrupt
          UIEBits.IDLEIE = 1             // Unmask IDLE interrupt
          usb_device_state = POWERED_STATE
       EndIf                           // else wait until SE0 is cleared
    EndIf
End Sub
{
****************************************************************************
* Name    : USBSoftDetach                                                  *
* Purpose : The device will have to be re-enumerated to function again     *
*         : USBSoftDetach electrically disconnects the device from the bus *
*         : This is done by stop supplying Vusb voltage to pull-up resistor*
*         : The pull-down resistors on the host side will pull both        *
*         : differential signal lines low and the host registers the event *
*         : as a disconnect.                                               *
*         : Since the USB cable is not physically disconnected, the power  *
*         : supply through the cable can still be sensed by the device. The*
*         : next time USBCheckBusStatus() function is called, it will      *
*         : reconnect the device back to the bus.                          *
****************************************************************************
}
Sub USBSoftDetach()                                                             'usbdrv.c
   USBModuleDisable()
End Sub
{
****************************************************************************
* Name    : USBWakeFromSuspend                                             *
* Purpose :                                                                *
****************************************************************************
}
Sub USBWakeFromSuspend()                                                        'usbdrv.c
   // If using clock switching, this is the place to restore the
   // original clock frequency.
   UCONBits.SUSPND = 0
   UIEBits.ACTVIE = 0
   UIRBits.ACTVIF = 0
End Sub
{
****************************************************************************
* Name    : USBProtocolResetHandler                                        *
* Require : A USB bus reset is received from the host                      *
* Purpose : Currently, this routine flushes any pending USB transactions.  *
*         : It empties OUT the USTAT FIFO. This action might not be        *
*         : desirable in some applications.                                *
*         : Once a USB bus reset is received from the host, this routine   *
*         : should be called. It resets the device address to zero,        *
*         : disables all non-EP0 endpoints, initializes EP0 to be ready for*
*         : default communication, clears all USB interrupt flags, unmasks *
*         : applicable USB interrupts, and reinitializes internal          *
*         : state-machine variables.                                       *
****************************************************************************
}
Sub USBProtocolResetHandler()                                                   'usbdrv.bas
   UEIR = 0                       // Clear all USB error flags
   UIR = 0                        // Clears all USB interrupts
   UEIE = %10011111               // Unmask all USB error interrupts
   UIE = %01111011                // Enable all interrupts except ACTVIE

   UADDR = $00                    // Reset to default address
   mDisableEP1to15()              // Reset all non-EP0 UEPn registers
   UEP0 = EP_CTRL Or HSHK_EN      // Init EP0 as a Ctrl EP, see usbdrv.h

   While UIRBits.TRNIF = 1        // Flush any pending transactions
      UIRBits.TRNIF = 0
   Wend    

   UCONBits.PKTDIS = 0            // Make sure packet processing is enabled
   USBPrepareForNextSetupTrf()    // Declared in usbctrltrf.c

   usb_stat.RemoteWakeup = 0      // Default status flag to disable
   usb_active_cfg = 0             // Clear active configuration
   usb_device_state = DEFAULT_STATE
End Sub
{
****************************************************************************
* Name    : USBRemoteWakeup                                                *
* Purpose : This function should be called by user when the device is waken*
*         : up by an external stimulus other than ACTIVIF. Please read the *
*         : note below to understand the limitations.                      *
*         : The modifiable section IN this routine should be changed to    *
*         : meet the application needs. Current implementation temporary   *
*         : blocks other functions from executing for a period of 1-13 ms  *
*         : depending on the core frequency. According to USB 2.0          *
*         : specification section 7.1.7.7,                                 *
*         :    "The remote wakeup device must hold the resume signaling    *
*         :     for at lest 1 ms but for no more than 15 ms."              *
*         : The idea here is to use a delay counter loop, using a common   *
*         : value that would work over a wide range of core frequencies.   *
*         :    ==========================================================  *
*         :    Core Freq(MHz)      MIP         RESUME Signal Period (ms)   *
*         :    ==========================================================  *
*         :        48              12          1.05                        *
*         :         4              1           12.6                        *
*         :    ==========================================================  *
*         : These timing could be incorrect when using code optimization   *
*         : or extended instruction mode, or when having other interrupts  *
*         : enabled. Make sure to verify using the MPLAB SIM's Stopwatch   *
****************************************************************************
}
Sub USBRemoteWakeup()                                                           'usbdrv.c
   If usb_stat.RemoteWakeup = 1 Then      // Check if RemoteWakeup function
                                          // has been enabled by the host.
      USBWakeFromSuspend()                // Unsuspend USB modue
      UCONBits.RESUME = 1                 // Start RESUME signaling

      // Modifiable Section 
      // Set RESUME line for 1-13 ms
      // End Modifiable Section 

      UCONBits.RESUME = 0
   EndIf
End Sub
{
****************************************************************************
* Name    : USBSuspend                                                     *
* Purpose :                                                                *
****************************************************************************
}
Sub USBSuspend()                                                                'usbdrv.c
   // NOTE: Do not clear UIRbits.ACTVIF here!
   // Reason:
   // ACTVIF is only generated once an IDLEIF has been generated.
   // This is a 1:1 ratio interrupt generation.
   // For every IDLEIF, there will be only one ACTVIF regardless of
   // the number of subsequent bus transitions.
   //
   // If the ACTIF is cleared here, a problem could occur when:
   // [       IDLE       ][bus activity ->
   // <--- 3 ms ----->     ^
   //                ^     ACTVIF=1
   //                IDLEIF=1
   //  #           #           #           #   (#=Program polling flags)
   //                          ^
   //                          This polling loop will see both
   //                          IDLEIF=1 and ACTVIF=1.
   //                          However, the program services IDLEIF first
   //                          because ACTIVIE=0.
   //                          If this routine clears the only ACTIVIF,
   //                          then it can never get out of the suspend
   //                          mode.             
   UIEBits.ACTVIE = 1                     // Enable bus activity interrupt
   UIRBits.IDLEIF = 0
   UCONBits.SUSPND = 1                    // Put USB module in power conserve
                                          // mode, SIE clock inactive
   // At this point the PIC can go into sleep,idle, or
   // switch to a slower clock, etc.
   // Modifiable Section 
'   PIR2Bits.USBIF = 0
'   INTCONBits.RBIF = 0
'   PIE2Bits.USBIE = 1                     // Set USB wakeup source
'   INTCONBits.RBIE = 1                    // Set sw2,3 wakeup source
'   asm-                                   // Goto sleep
'   sleep
'   end asm
'   if INTCONBits.RBIF = 1 then            // Check if external stimulus
'      USBRemoteWakeup()                   // If yes, attempt RWU
'   endif
'   PIE2Bits.USBIE = 0
'   INTCONBits.RBIE = 0
   // End Modifiable Section 
End Sub
{
****************************************************************************
* Name    : USB_SOF_Handler                                                *
* Purpose : The USB host sends OUT a SOF packet to full-speed devices every*
*         : 1 ms. This interrupt may be useful for isochronous pipes. End  *
*         : designers should implement callback routine as necessary.      *                                                         
****************************************************************************
}
Sub USB_SOF_Handler()                                                           'usbdrv.c
   UIRBits.SOFIF = 0
   usb_Attached = (usb_device_state = CONFIGURED_STATE) 
End Sub
{
****************************************************************************
* Name    : USBStallHandler                                                *
* Require : A STALL packet is sent to the host by the SIE.                 *
* Purpose : The STALLIF is set anytime the SIE sends OUT a STALL packet    *
*         : regardless of which endpoint causes it. A Setup transaction    *
*         : overrides the STALL function. A stalled endpoint stops stalling*
*         : once it receives a setup packet. In this case, the SIE will    *
*         : accept the Setup packet and set the TRNIF flag to notify the   *
*         : firmware. STALL function for that particular endpoint pipe will*
*         : be automatically disabled (direction specific).                *
*         : There are a few reasons for an endpoint to be stalled.         *
*         :    1. When a non-supported USB request is received.            *
*         :       Example: GET_DESCRIPTOR(DEVICE_QUALIFIER)                *
*         :    2. When an endpoint is currently halted.                    *
*         :    3. When the device CLASS specifies that an endpoint must    *
*         :       stall in response to a specific event.                   *
*         : UEPn.EPSTALL can be scanned to see which endpoint causes the   *
*         : stall event.                                                   *
****************************************************************************
}
Sub USBStallHandler()                                                           'usbdrv.c
   // Does not really have to do anything here,
   // even for the control endpoint.
   // All BDs of Endpoint 0 are owned by SIE right now,
   // but once a Setup Transaction is received, the ownership
   // for EP0_OUT will be returned to CPU.
   // When the Setup Transaction is serviced, the ownership
   // for EP0_IN will then be forced back to CPU by firmware.
   If UEP0Bits.EPSTALL = 1 Then
      USBPrepareForNextSetupTrf()        // Firmware work-around
      UEP0Bits.EPSTALL = 0
   EndIf
   UIRBits.STALLIF = 0
End Sub
{
****************************************************************************
* Name    : USBErrorHandler                                                *
* Purpose : The purpose of this interrupt is mainly for debugging during   *
*         : development. Check UEIR to see which error causes the interrupt*
****************************************************************************
}
Sub USBErrorHandler()                                                           'usbdrv.c
   UIRBits.UERRIF = 0
End Sub
{
****************************************************************************
* Name    : USBDriverService                                               *
* Purpose : This routine is the heart of this firmware. It manages all USB *
*         : interrupts. Device state transitions through the following     *
*         : stages: DETACHED -> ATTACHED -> POWERED -> DEFAULT ->          *
*         : ADDRESS_PENDING -> ADDRESSED -> CONFIGURED -> READY            *
****************************************************************************
}

Public Sub USBDriverService()                                                   'usbdrv.c
   // Pointless to continue servicing if USB cable is not even attached.
   If usb_device_state = DETACHED_STATE Then
      Exit
   EndIf

   // Task A: Service USB Activity Interrupt
   If (UIRBits.ACTVIF <> 0) And (UIEBits.ACTVIE <> 0) Then
       USBWakeFromSuspend()
   EndIf   

   // Pointless to continue servicing if the device is in suspend mode.
   If UCONBits.SUSPND = 1 Then
       usb_Attached = false
       Exit
   EndIf

   // Task B: Service USB Bus Reset Interrupt.
   // When bus reset is received during suspend, ACTVIF will be set first,
   // once the UCONbits.SUSPND is clear, then the URSTIF bit will be asserted.
   // This is why URSTIF is checked after ACTVIF.
   // The USB reset flag is masked when the USB state is in
   // DETACHED_STATE or ATTACHED_STATE, and therefore cannot
   // cause a USB reset event during these two states.

   If (UIRBits.URSTIF <> 0) And (UIEBits.URSTIE <> 0) Then
       USBProtocolResetHandler()
   EndIf   

   // Task C: Service other USB interrupts
   If (UIRBits.IDLEIF <> 0) And (UIEBits.IDLEIE <> 0) Then 
      USBSuspend()
   EndIf   
   If (UIRBits.SOFIF <> 0) And (UIEBits.SOFIE <> 0) Then
      USB_SOF_Handler()
   EndIf   
   If (UIRBits.STALLIF <> 0) And (UIEBits.STALLIE <> 0) Then
      USBStallHandler()
   EndIf   
   If (UIRBits.UERRIF <> 0) And (UIEBits.UERRIE <> 0) Then
      USBErrorHandler()
   EndIf   

   // Pointless to continue servicing if the host has not sent a bus reset.
   // Once bus reset is received, the device transitions into the DEFAULT
   // state and is ready for communication.
   If usb_device_state < DEFAULT_STATE Then
      Exit
   EndIf

   // Task D: Servicing USB Transaction Complete Interrupt
   If (UIRBits.TRNIF <> 0) And (UIEBits.TRNIE <> 0) Then

      // USBCtrlEPService only services transactions over EP0.
      // It ignores all other EP transactions.
      USBCtrlEPService()

      // Other EP can be serviced later by responsible device class firmware.
      // Each device driver knows when an OUT or IN transaction is ready by
      // checking the buffer ownership bit.
      // An OUT EP should always be owned by SIE until the data is ready.
      // An IN EP should always be owned by CPU until the data is ready.
      // Because of this logic, it is not necessary to save the USTAT value
      // of non-EP0 transactions.
      UIRBits.TRNIF = 0
    EndIf    
End Sub

// version 1.2
usb_Attached = false

// initialise descriptor pointers...
USB_CD_Ptr(0) = @cfg01
USB_CD_Ptr(1) = @cfg01  // configuration
USB_SD_Ptr(0) = @sd000  // language string
USB_SD_Ptr(1) = @sd001  // manufacturer string
USB_SD_Ptr(2) = @sd002  // product string
#if USB_HAVE_SERIAL_STRING
USB_SD_Ptr(3) = @sd003  // serial number string
#endif

// HID defaults...
#if USB_USE_HID
idle_rate = 0
active_protocol = 0
#endif

// CDC defaults...
#if USB_USE_CDC
cdc_tx_len = 0
cdc_mem_type = _RAM
cdc_trf_state = CDC_TX_READY
#endif // USB_USE_CDC

USBConfig.bas

{
*****************************************************************************
*  Name    : USBConfig.bas                                                  *
*  Author  : David John Barker                                              *
*  Notice  : Copyright (c) 2007 Mecanique                                   *
*          : All Rights Reserved                                            *
*  Date    : 15/09/2008                                                     *
*  Version : 1.1a Branch by Nathan(RangerBob) for 18F87J50 Support          *
*          : 1.1 Removed USB_SERVICE_US as ISR now uses USB interrupts      *
*          : 1.0 Release                                                    *
*  Notes   : USB configuration options                                      *
*          : ==================== DO NOT EDIT THIS FILE =================== * 
*          : Change the options FROM YOU MAIN PROGRAM. For example,         *
*          : #option USB_VID = $1234                                        *
*          : This will AUTOMATICALLY override these default values. DONT    *
*          : FORGET to add options BEFORE any include files!!               *
*****************************************************************************
}
Module USBConfig

// ***************************************************************************
// device version (device_dsc)
// ***************************************************************************
#option USB_DEVICE_VERSION = $0110 // (BCD)
#if Not (USB_DEVICE_VERSION in (0 to 65535))
   #error USB_DEVICE_VERSION, "Invalid option. USB_DEVICE_VERSION must be between 0 and 65535."
#endif

// ***************************************************************************
// descriptor string index (device_desc)
// ***************************************************************************
#define USB_MANUFACTURER_INDEX = 1
#define USB_PRODUCT_INDEX      = 2
#variable USB_SERIAL_INDEX     = 0

#option USB_HAVE_SERIAL_STRING = false
#if Not (USB_HAVE_SERIAL_STRING in (true, false))
   #error USB_HAVE_SERIAL_STRING, "Invalid option. USB_HAVE_SERIAL_STRING must be TRUE or FALSE."
#endif
#if USB_HAVE_SERIAL_STRING
   #variable USB_SERIAL_INDEX = 3
#endif

// ***************************************************************************
// bus power (cfg01)
// ***************************************************************************
#option USB_BUS_POWER = 50 // x2                 
#if Not (USB_BUS_POWER in (50 to 250))
   #error USB_BUS_POWER, "Invalid option. USB_BUS_POWER must be between 50 and 250."
#endif

// ***************************************************************************
// link in HID code option...
// ***************************************************************************
#option USB_USE_HID = false                                                     
#if Not (USB_USE_HID in (true, false))
   #error USB_USE_HID, "Invalid option. USB_USE_HID must be TRUE or FALSE."
#endif

// ***************************************************************************
// link in CDC code option...
// ***************************************************************************
#option USB_USE_CDC = false
#if Not (USB_USE_CDC in (true, false))
   #error USB_USE_CDC, "Invalid option. USB_USE_CDC must be TRUE or FALSE."
#endif

// ***************************************************************************
// default option for CDC...
// ***************************************************************************
#if USB_USE_CDC
   #option USB_DESCRIPTOR = "CDCDescriptor.bas"     // name of descriptor file
   #option USB_VID = $1781                          // VID - 6017 is Mecanique
   #option USB_PID = $07D1                          // PID - 2001
   #define MAX_EP_NUMBER = 3           // UEP1 minimum, up to 16                   'usbcfg.h

// ***************************************************************************
// default options for HID...
// ***************************************************************************
#else
   #option USB_DESCRIPTOR = "HIDDescriptor.bas"     // name of descriptor file
   #option USB_VID = $1781                          // VID - 6017 is Mecanique
   #option USB_PID = $07D0                          // PID - 2000
   #define MAX_EP_NUMBER = 1           // UEP1 minimum, up to 16                   'usbcfg.h
#endif

// ***************************************************************************
// verify VID...
// ***************************************************************************
#if Not (USB_VID in (0 to 65535))
   #error USB_VID, "Invalid option. USB_VID must be between 0 and 65535."
#endif

// ***************************************************************************
// verify PID...
// ***************************************************************************
#if Not (USB_PID in (0 to 65535))
   #error USB_PID, "Invalid option. USB_PID must be between 0 and 65535."
#endif

// ***************************************************************************
// CDC options
// ***************************************************************************
#option CDC_RX_SIZE = 64    // CDCDataRX
#if Not (CDC_RX_SIZE in (8, 16, 32, 64))
   #error CDC_RX_SIZE, "Invalid option. CDC_RX_SIZE must be 8, 16, 32 or 64."
#endif

#option CDC_TX_SIZE = 64    // CDCDataTX
#if Not (CDC_TX_SIZE in (8, 16, 32, 64))
   #error CDC_TX_SIZE, "Invalid option. CDC_TX_SIZE must be 8, 16, 32 or 64."
#endif

// ***************************************************************************
// HID buffer size
// ***************************************************************************
#option HID_BUFFER_SIZE = 64                    // HID EP1 can be 8, 16, 32 or 64
#if Not (HID_BUFFER_SIZE in (8, 16, 32, 64))
   #error HID_BUFFER_SIZE, "Invalid option. HID_BUFFER_SIZE must be 8, 16, 32 or 64."
#endif

// will equal HID_BUFFER_SIZE unless overridden in main program...
#option HID_REPORT_BYTES_IN = HID_BUFFER_SIZE  // HID Report bytes in
#if Not (HID_REPORT_BYTES_IN in (8, 16, 32, 64))
   #error HID_REPORT_BYTES_IN, "Invalid option. HID_REPORT_BYTES_IN must be 8, 16, 32 or 64."
#endif

// will equal HID_BUFFER_SIZE unless overridden in main program...
#option HID_REPORT_BYTES_OUT = HID_BUFFER_SIZE // HID_Report bytes out
#if Not (HID_REPORT_BYTES_OUT in (8, 16, 32, 64))
   #error HID_REPORT_BYTES_OUT, "Invalid option. HID_REPORT_BYTES_OUT must be 8, 16, 32 or 64."
#endif

// ***************************************************************************
// HID EP Polling
// ***************************************************************************
#option HID_EP_OUT_POLLING_MS = 1   // HID - EP01 - 1ms (EP01Out)
#if Not (HID_EP_OUT_POLLING_MS in (1 to 10))
   #error HID_EP_OUT_POLLING_MS, "Invalid option. HID_EP_OUT_POLLING_MS must be between 1 and 10."
#endif

#option HID_EP_IN_POLLING_MS = 1   // HID - EP01 - 1ms (EP01In)
#if Not (HID_EP_IN_POLLING_MS in (1 to 10))
   #error HID_EP_IN_POLLING_MS, "Invalid option. HID_EP_IN_POLLING_MS must be between 1 and 10."
#endif

// ***************************************************************************
// self power sensing...
// ***************************************************************************
#option USB_POWER_SENSE_IO = false                                              'usbcfg.h
#option USB_POWER_SENSE_PIN = PORTA.2
#if IsOption(USB_POWER_SENSE_PIN) And Not IsValidPortPin(USB_POWER_SENSE_PIN) 
   #error USB_POWER_SENSE_PIN, "Invalid option. USB_POWER_SENSE_PIN must be a valid port pin."
#endif

#if USB_POWER_SENSE_IO                                                          'io_cfg.h
Public Dim self_power As USB_POWER_SENSE_PIN.USB_POWER_SENSE_PIN@
#else
Public Const self_power = 1
#endif

// ***************************************************************************
// bus sense...
// ***************************************************************************
#option USB_BUS_SENSE_IO = false                                                'usbcfg.h
#option USB_BUS_SENSE_PIN = PORTA.1
#if IsOption(USB_BUS_SENSE_PIN) And Not IsValidPortPin(USB_BUS_SENSE_PIN) 
   #error USB_BUS_SENSE_PIN, "Invalid option. USB_BUS_SENSE_PIN must be a valid port pin."
#endif

#if USB_BUS_SENSE_IO                                                            'io_cfg.h
Public Dim usb_bus_sense As USB_BUS_SENSE_PIN.USB_BUS_SENSE_PIN@
#else
Public Const usb_bus_sense = 1
#endif

// ***************************************************************************
// Use interrupt polling...
// ***************************************************************************
#option USB_SERVICE = true
#if Not (USB_SERVICE in (true, false))
   #error USB_SERVICE, "Invalid option. USB_SERVICE must be TRUE or FALSE."
#endif

#option USB_SERVICE_PRIORITY = ipHigh 
// set interrupt priority level...
#if IsOption(USB_SERVICE_PRIORITY) And Not (USB_SERVICE_PRIORITY in (ipLow, ipHigh))
   #error USB_SERVICE_PRIORITY, "Invalid option. Priority must be ipHigh or ipLow."
#endif


// ***************************************************************************
// Timeout between CONFIGURED_STATE and setting 'USBSystem.Connected'
// ***************************************************************************
#option USB_CONNECT_MS = 1200
#if Not (USB_CONNECT_MS in (0 to 5000))
   #error USB_CONNECT_MS, "Invalid option. USB_CONNECT_MS must be between 0 and 5000 ms."
#endif

// ***************************************************************************
// does device support extended dual port RAM
// ***************************************************************************
#if _device in (18F2455, 18F2550, 18F4455, 18F4550, 18F87J50)
   #define USB_EXTENDED_RAM = true
#else
   #define USB_EXTENDED_RAM = false
#endif

Retrieved from https://www.sfcompiler.co.uk/wiki/pmwiki.php?n=SwordfishUser.18F87J50USB
Page last modified on November 17, 2008, at 12:10 PM