From a90344ef4c6ed928114ef9e8e775ad3f37a43604 Mon Sep 17 00:00:00 2001 From: Boris Honman Date: Tue, 13 Aug 2019 22:52:23 -0400 Subject: [PATCH] changed bootloader back to using the minimal usb stack it came with because I have no idea how to get the bootloader code working with the full MLA HID USB stack --- .../nbproject/configurations.xml | 32 +- bootloader/src/VectorRemap.asm | 4 +- bootloader/src/bootloader.c | 35 +- bootloader/src/bootloader.h | 6 +- bootloader/src/main.c | 206 ++- bootloader/src/minimal_usb_driver/usb.h | 55 + .../src/minimal_usb_driver/usb_device.c | 1625 +++++++++++++++++ .../src/minimal_usb_driver/usb_device.h | 699 +++++++ .../src/minimal_usb_driver/usb_device_hid.c | 302 +++ .../src/minimal_usb_driver/usb_device_hid.h | 163 ++ bootloader/src/typedefs.h | 425 +++++ bootloader/src/usb_config.h | 225 +-- bootloader/src/usb_descriptors.c | 388 ++-- bootloader/src/usb_events.c | 85 - common_src/framework/usb/inc/usb_hal_pic18.h | 4 +- common_src/memory.h | 8 +- .../nbproject/configurations.xml | 45 +- firmware/src/main.c | 22 +- firmware/src/usb_config.h | 4 +- 19 files changed, 3823 insertions(+), 510 deletions(-) create mode 100644 bootloader/src/minimal_usb_driver/usb.h create mode 100644 bootloader/src/minimal_usb_driver/usb_device.c create mode 100644 bootloader/src/minimal_usb_driver/usb_device.h create mode 100644 bootloader/src/minimal_usb_driver/usb_device_hid.c create mode 100644 bootloader/src/minimal_usb_driver/usb_device_hid.h create mode 100644 bootloader/src/typedefs.h delete mode 100644 bootloader/src/usb_events.c diff --git a/bootloader/clubdance_v2_bootloader.X/nbproject/configurations.xml b/bootloader/clubdance_v2_bootloader.X/nbproject/configurations.xml index c682c61..54dcad0 100644 --- a/bootloader/clubdance_v2_bootloader.X/nbproject/configurations.xml +++ b/bootloader/clubdance_v2_bootloader.X/nbproject/configurations.xml @@ -2,23 +2,6 @@ - - - - ../../common_src/framework/usb/inc/usb.h - ../../common_src/framework/usb/inc/usb_ch9.h - ../../common_src/framework/usb/inc/usb_common.h - ../../common_src/framework/usb/inc/usb_device.h - ../../common_src/framework/usb/inc/usb_device_hid.h - ../../common_src/framework/usb/inc/usb_hal.h - ../../common_src/framework/usb/inc/usb_hal_pic18.h - - - ../../common_src/framework/usb/src/usb_device.c - ../../common_src/framework/usb/src/usb_device_hid.c - - - ../../common_src/system.c ../../common_src/memory.h @@ -27,16 +10,25 @@ projectFiles="true"> ../src/usb_config.h ../src/bootloader.h + ../src/typedefs.h + + ../src/minimal_usb_driver/usb_device.c + ../src/minimal_usb_driver/usb_device_hid.c + ../src/minimal_usb_driver/usb.h + ../src/minimal_usb_driver/usb_device.h + ../src/minimal_usb_driver/usb_device_hid.h + ../src/usb_descriptors.c - ../src/usb_events.c ../src/main.c ../src/bootloader.c ../src/VectorRemap.asm @@ -100,7 +92,7 @@ + value="..\src;..\..\common_src;..\src\minimal_usb_driver"/> @@ -139,7 +131,7 @@ - + diff --git a/bootloader/src/VectorRemap.asm b/bootloader/src/VectorRemap.asm index 245edbf..d3d3324 100644 --- a/bootloader/src/VectorRemap.asm +++ b/bootloader/src/VectorRemap.asm @@ -20,14 +20,14 @@ ;//High priority interrupt vector remapping PSECT HiVector,class=CODE,delta=1,abs org 0x08 - goto 0x2088 ;Resides at 0x0008 (hardware high priority interrupt vector), and causes PC to jump to 0x2008 upon a high priority interrupt event + goto 0x2008 ;Resides at 0x0008 (hardware high priority interrupt vector), and causes PC to jump to 0x2008 upon a high priority interrupt event ;//Low priority interrupt vector remapping, as well as bootloader mode absolute ;//entry point (located at 0x001C). PSECT LoVector,class=CODE,delta=1,abs org 0x18 - goto 0x2098 ;Resides at 0x0018 (hardware low priority interrupt vector), and causes PC to jump to 0x2018 upon a low priority interrupt event + goto 0x2018 ;Resides at 0x0018 (hardware low priority interrupt vector), and causes PC to jump to 0x2018 upon a low priority interrupt event goto 0x30 ;Resides at 0x001C //Serves as absolute entry point from application program into the bootloader mode diff --git a/bootloader/src/bootloader.c b/bootloader/src/bootloader.c index 12fdc7d..fa4fce4 100644 --- a/bootloader/src/bootloader.c +++ b/bootloader/src/bootloader.c @@ -1,10 +1,7 @@ -#include "bootloader.h" -#include #include "usb.h" -#include "usb_device_hid.h" +#include "bootloader.h" + -#undef uint24_t -#define uint24_t uint32_t //The bootloader version, which the bootloader PC application can do extended query to get. //Value provided is expected to be in the format of BOOTLOADER_VERSION_MAJOR.BOOTLOADER_VERSION_MINOR @@ -145,8 +142,8 @@ typedef union }; } PacketToFromPC; -PacketToFromPC PacketFromPC __at(0x42C); -PacketToFromPC PacketToPC __at(0x46C); +PacketToFromPC PacketFromPC; +PacketToFromPC PacketToPC; unsigned char ProgrammingBuffer[ERASE_PAGE_SIZE]; unsigned char BootState; unsigned int ErasePageTracker; @@ -154,9 +151,6 @@ unsigned char BufferedDataIndex; uint24_t ProgrammedPointer; unsigned char ConfigsLockValue; -USB_VOLATILE USB_HANDLE txHandle; -USB_VOLATILE USB_HANDLE rxHandle; - void WriteFlashBlock(void); void WriteConfigBits(void); void WriteEEPROM(void); @@ -173,10 +167,6 @@ void UserInit(void) ProgrammedPointer = INVALID_ADDRESS; BufferedDataIndex = 0; ConfigsLockValue = 1; - - USBEnableEndpoint(HID_EP, USB_IN_ENABLED|USB_OUT_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP); - rxHandle = HIDRxPacket(HID_EP, (uint8_t *)&PacketFromPC, USB_PACKET_SIZE); - txHandle = 0; } /****************************************************************************** @@ -218,12 +208,11 @@ void ProcessIO(void) { //We are currently in the IDLE state waiting for a command from the //PC software on the USB host. - if(!HIDRxHandleBusy(rxHandle)) //Did we receive a command? + if(!mHIDRxIsBusy()) //Did we receive a command? { //We received a new command from the host. Copy the OUT packet from //the host into a local buffer for processing. - rxHandle = HIDRxPacket(HID_EP, (uint8_t *)&PacketFromPC, USB_PACKET_SIZE); //Also re-arms the OUT endpoint to be able to receive the next packet - //HIDRxReport((char *)&PacketFromPC, USB_PACKET_SIZE); //Also re-arms the OUT endpoint to be able to receive the next packet + HIDRxReport((char *)&PacketFromPC, USB_PACKET_SIZE); //Also re-arms the OUT endpoint to be able to receive the next packet BootState = NOT_IDLE; //Set flag letting state machine know it has a command that needs processing. //Pre-initialize a response packet buffer (only used for some commands) @@ -240,7 +229,7 @@ void ProcessIO(void) case QUERY_DEVICE: //Make sure the USB IN endpoint buffer is available, then load //up a query response packet to send to the host. - if(!HIDTxHandleBusy(txHandle)) + if(!mHIDTxIsBusy()) { //Prepare a response packet, which lets the PC software know about the memory ranges of this device. PacketToPC.Command = QUERY_DEVICE; @@ -266,7 +255,7 @@ void ProcessIO(void) //Init pad bytes to 0x00... Already done after we received the QUERY_DEVICE command (just after calling HIDRxReport()). //Now send the packet to the USB host software, assuming the USB endpoint is available/ready to accept new data. - txHandle = HIDTxPacket(HID_EP, (uint8_t *)&PacketToPC, USB_PACKET_SIZE); + HIDTxReport((char *)&PacketToPC, USB_PACKET_SIZE); BootState = IDLE; } break; @@ -390,9 +379,9 @@ void ProcessIO(void) //Assuming the USB IN (to host) buffer is available/ready, copy the //data over so it can get sent to the USB host software. - if(!HIDTxHandleBusy(txHandle)) + if(!mHIDTxIsBusy()) { - txHandle = HIDTxPacket(HID_EP, (uint8_t *)&PacketToPC, USB_PACKET_SIZE); + HIDTxReport((char *)&PacketToPC, USB_PACKET_SIZE); BootState = IDLE; } break; @@ -428,9 +417,9 @@ void ProcessIO(void) PacketToPC.Config7HMask = 0xFF; //Now actually command USB to send the packet to the host - if(!HIDTxHandleBusy(txHandle)) + if(!mHIDTxIsBusy()) { - txHandle = HIDTxPacket(HID_EP, (uint8_t *)&PacketToPC, USB_PACKET_SIZE); + HIDTxReport((char *)&PacketToPC, USB_PACKET_SIZE); BootState = IDLE; //Packet will be sent, go back to idle state ready for next command from host } break; diff --git a/bootloader/src/bootloader.h b/bootloader/src/bootloader.h index 265c46b..00a58e2 100644 --- a/bootloader/src/bootloader.h +++ b/bootloader/src/bootloader.h @@ -38,9 +38,9 @@ void ProcessIO(void); //#define REMAPPED_APPLICATION_LOW_ISR_VECTOR APP_FW_MEMORY_OFFSET + 0x18 //See VectorRemap.asm #define BOOTLOADER_ABSOLUTE_ENTRY_ADDRESS BOOTLOADER_ENTRYPOINT //Execute a "goto 0x001C" inline assembly instruction, if you want to enter the bootloader mode from the application via software -#define APP_SIGNATURE_ADDRESS APP_FW_MEMORY_OFFSET + 0x06 //0x1C06 and 0x1C07 contains the "signature" WORD, indicating successful erase/program/verify operation -#define APP_SIGNATURE_VALUE 0x600D //leet "GOOD", implying that the erase/program was a success and the bootloader intentionally programmed the APP_SIGNATURE_ADDRESS with this value -#define APP_VERSION_ADDRESS APP_FW_MEMORY_OFFSET + 0x16 //0x1C16 and 0x1C17 should contain the application image firmware version number +//#define APP_SIGNATURE_ADDRESS APP_FW_MEMORY_OFFSET + 0x06 //0x1C06 and 0x1C07 contains the "signature" WORD, indicating successful erase/program/verify operation +//#define APP_SIGNATURE_VALUE 0x600D //leet "GOOD", implying that the erase/program was a success and the bootloader intentionally programmed the APP_SIGNATURE_ADDRESS with this value +//#define APP_VERSION_ADDRESS APP_FW_MEMORY_OFFSET + 0x16 //0x1C16 and 0x1C17 should contain the application image firmware version number #endif /* BOOTLOADER_ */ diff --git a/bootloader/src/main.c b/bootloader/src/main.c index 3143471..89280c0 100644 --- a/bootloader/src/main.c +++ b/bootloader/src/main.c @@ -5,15 +5,18 @@ * Created on August 9, 2019, 6:14 PM */ -#include #include "usb.h" #include "bootloader.h" +unsigned int uint_delay_counter; + /* Private prototypes */ void main(void); void BootMain(void); +void InitializeSystem(void); const unsigned int FlashSignatureWord __at(APP_SIGNATURE_ADDRESS) = APP_SIGNATURE_VALUE; +//const unsigned int FlashSignatureWord __at(APP_SIGNATURE_ADDRESS) = APP_SIGNATURE_VALUE; void main(void) { @@ -24,7 +27,7 @@ void main(void) // normal operation: verify the firmware signature is valid. if it isn't enter // bootloader (fw update) mode so valid firmware can be flashed -//DoFlashSignatureCheck: +DoFlashSignatureCheck: //Check if the application region flash signature is valid if(*(const unsigned int*)APP_SIGNATURE_ADDRESS == APP_SIGNATURE_VALUE) { @@ -62,12 +65,8 @@ void BootMain(void) __at(0x30) // any C initialized user variables (idata memory sections). Therefore, the above is all // the initialization that is required. - // initialize the USB framework - USBDeviceInit(); - USBDeviceAttach(); - - // initialize the bootloader - UserInit(); + //Call other initialization code and (re)enable the USB module + InitializeSystem(); //Execute main loop while(1) @@ -80,4 +79,195 @@ void BootMain(void) __at(0x30) ProcessIO(); //This is where all the actual bootloader related data transfer/self programming takes //place see ProcessIO() function in the BootPIC[xxxx].c file. }//end while +} + +void InitializeSystem(void) +{ + UserInit(); + USBDeviceInit(); +} + +// ****************************************************************************************************** +// ************** USB Callback Functions **************************************************************** +// ****************************************************************************************************** +// The USB firmware stack will call the callback functions USBCBxxx() in response to certain USB related +// events. For example, if the host PC is powering down, it will stop sending out Start of Frame (SOF) +// packets to your device. In response to this, all USB devices are supposed to decrease their power +// consumption from the USB Vbus to <2.5mA* each. The USB module detects this condition (which according +// to the USB specifications is 3+ms of no bus activity/SOF packets) and then calls the USBCBSuspend() +// function. You should modify these callback functions to take appropriate actions for each of these +// conditions. For example, in the USBCBSuspend(), you may wish to add code that will decrease power +// consumption from Vbus to <2.5mA (such as by clock switching, turning off LEDs, putting the +// microcontroller to sleep, etc.). Then, in the USBCBWakeFromSuspend() function, you may then wish to +// add code that undoes the power saving things done in the USBCBSuspend() function. +// +// Note *: The "usb_20.pdf" specs indicate 500uA or 2.5mA, depending upon device classification. However, +// the USB-IF has officially issued an ECN (engineering change notice) changing this to 2.5mA for all +// devices. Make sure to re-download the latest specifications to get all of the newest ECNs. + +/****************************************************************************** + * Function: void USBCBWakeFromSuspend(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: The host may put USB peripheral devices in low power + * suspend mode (by "sending" 3+ms of idle). Once in suspend + * mode, the host may wake the device back up by sending non- + * idle state signalling. + * + * This call back is invoked when a wakeup from USB suspend + * is detected. + * + * Note: Before returning from this function, make sure that the + * oscillator settings are fully compatible with USB module + * operation. + * If using the two-speed startup feature, wakeup and execution + * will occur before the main oscillator + PLL has had a chance + * to start. Device will run from INTOSC (no PLL). However, the + * USB module cannot be clocked and receive correct USB data when + * it is not clocked with the correct frequency clock source. + * Therefore, when using two-speed startup, should execute software + * delay to prevent any other code from executing until the main + * oscillator is ready. + * The host will allow at least 10ms for USB "resume recovery", during + * which it will not try to communicate with the device. + *****************************************************************************/ +void USBCBWakeFromSuspend(void) +{ + //This code delays ~5ms @ 8MHz to execute (using C18 3.21 with full + //optimizations enabled), but takes much less time at 48MHz. This delay + //is to make sure the PLL is enabled and locked, in case two speed startup + //was enabled + DelayRoutine(0x300); //Device will switch clocks (if using two-speed startup) while executing this delay function + + //Primary oscillator and PLL should be running by now. + + //Do not return from this function until the oscillator is correctly configured and + //running in a USB compatible mode/frequency. + + + //Additional code for re-enabling I/O pins and increasing power draw from VBUS + //may be placed here (up to the maximum of 100mA [when unconfigured] or the + //amount specified in the configuration descriptor (when configured). +} + + + +/****************************************************************************** + * Function: void USBCBSuspend(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: Call back that is invoked when a USB suspend is detected + * + * Note: None + *****************************************************************************/ +void USBCBSuspend(void) +{ + Sleep(); // Go to sleep, wake up when a USB activity event occurs + //If using the WDT, should go back to sleep if awoke by WDT instead of USBIF + while((USBIF_FLAG == 0) && (RCONbits.TO == 0)) //If using the WDT, should go back to sleep if awoke by WDT instead of USBIF + { + Sleep(); //Entry into sleep clears WDT count, much like executing ClrWdt() instruction + } + + //After the USB suspend event ends, you should re-configure your I/O pins + //for normal operation mode (which is allowed to consume more current). + //However, it is recommended to put this code in the USBCBWakeFromSuspend() + //function instead of here (so that this function will work with either + //sleeping or clock switching to a lower frequency). +} + + +/******************************************************************* + * Function: void USBCBInitEP(uint8_t ConfigurationIndex) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This function is called when the device becomes + * initialized, which occurs after the host sends a + * SET_CONFIGURATION request. This + * callback function should initialize the endpoints + * for the device's usage according to the current + * configuration. + * + * Note: If the host ever "unconfigures" the device, it will + * set the configuration to '0'. In this case, this + * callback gets called with ConfigurationIndex == 0, where + * the firmware should disable all non-EP0 endpoints (until + * the next non-zero SET_CONFIGURATION request is received, + * which will cause this callback to execute again). + *******************************************************************/ +void USBCBInitEP(uint8_t ConfigurationIndex) +{ + //Check what configuration "index" the host has requested us to select. + //Configuration index 0 is special and represents that the device should be + //un-configured. However, when the host sets the configuration (with index + //matching the valid/implemented configuration from the configuration descriptor), + //the firmware should enable the application endpoints associated with that + //configuration, and (re)initialize all application state variables associated + //with the USB application endpoints operation. + if(ConfigurationIndex == 1) //This application only implements one configuration, with index == 1. + { + //The host sent us a non-zero set configuration index. In this + //case we should prepare the application endpoints to be ready + //to use, and to (re-)initialize any application variables associated + //with the endpoints. + HIDInitEP(); + + //(Re-)Initialize the application variables associated with the USB interface + UserInit(); // See BootPIC[xxxx].c. Initializes the bootloader firmware state machine variables. + } + //else the host set the configuration back to 0 (indicating unconfigured), or + //to some higher (non-implemented value). In either case, we don't need to + //do anything specifically, unless the application requires some kind of + //"safe shutdown" code to execute after the host has deconfigured the device. + +} + + + +/******************************************************************* + * Function: void USBCBCheckOtherReq(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This function is called when the USB stack receives a + * new control transfer SETUP packet from the host. The + * USB stack handles normal USB "Chapter 9" requests internally, + * but some control transfer requests are class specific. In + * order to handle these class specific requests, you must call + * the class handler's firmware control transfer handler function. + * If implementing a composite device with multiple classes + * implemented, call each of the handlers in the below callback. + * + * Note: None + *******************************************************************/ +void USBCBCheckOtherReq(void) +{ + USBCheckHIDRequest(); } \ No newline at end of file diff --git a/bootloader/src/minimal_usb_driver/usb.h b/bootloader/src/minimal_usb_driver/usb.h new file mode 100644 index 0000000..5c8fc23 --- /dev/null +++ b/bootloader/src/minimal_usb_driver/usb.h @@ -0,0 +1,55 @@ +/******************************************************************************* +Copyright 2016 Microchip Technology Inc. (www.microchip.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +To request to license the code under the MLA license (www.microchip.com/mla_license), +please contact mla_licensing@microchip.com +*******************************************************************************/ +#ifndef USB_H +#define USB_H + +/* + * usb.h provides a centralize way to include all files + * required by Microchip USB Firmware. + * + * The order of inclusion is important. + * Dependency conflicts are resolved by the correct ordering. + */ + +#include "typedefs.h" +#include "usb_config.h" +#include "usb_device.h" + +#if defined(USB_USE_HID) // See usb_config.h +#include "usb_device_hid.h" +#endif + + +//These callback functions belong in your main.c (or equivalent) file. The USB +//stack will call these callback functions in response to specific USB bus events, +//such as entry into USB suspend mode, exit from USB suspend mode, and upon +//receiving the "set configuration" control tranfer request, which marks the end +//of the USB enumeration sequence and the start of normal application run mode (and +//where application related variables and endpoints may need to get (re)-initialized. +void USBCBSuspend(void); +void USBCBWakeFromSuspend(void); +void USBCBInitEP(uint8_t ConfigurationIndex); +void USBCBCheckOtherReq(void); + + +//API renaming wrapper functions +#define HIDTxHandleBusy(a) {mHIDTxIsBusy()} +#define HIDRxHandleBusy(a) {mHIDRxIsBusy()} + +#endif //USB_H diff --git a/bootloader/src/minimal_usb_driver/usb_device.c b/bootloader/src/minimal_usb_driver/usb_device.c new file mode 100644 index 0000000..1e9004c --- /dev/null +++ b/bootloader/src/minimal_usb_driver/usb_device.c @@ -0,0 +1,1625 @@ +/******************************************************************************* +Copyright 2016 Microchip Technology Inc. (www.microchip.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +To request to license the code under the MLA license (www.microchip.com/mla_license), +please contact mla_licensing@microchip.com +*******************************************************************************/ + +/** I N C L U D E S **********************************************************/ +#include "usb.h" + + +//Private prototypes (see usb_device.h for public API functions exposed by the USB stack) +void USBCheckStdRequest(void); +void USBSuspend(void); +void USBProtocolResetHandler(void); +void USBWakeFromSuspend(void); +void USBStdGetDscHandler(void); +void USBStdSetCfgHandler(void); +void USBStdGetStatusHandler(void); +void USBStdFeatureReqHandler(void); +void USBCtrlTrfSetupHandler(void); +void USBCtrlTrfInHandler(void); +void USBCtrlTrfTxService(void); +void USBCtrlEPServiceComplete(void); +void LoadBDTandSetUOWN(uint8_t BDTIndexToLoad); +#if !defined(ENABLE_CONTROL_TRANSFERS_WITH_OUT_DATA_STAGE) +#define USBCtrlTrfOutHandler(a) +#endif + + + +/** V A R I A B L E S ********************************************************/ +#ifndef __XC8__ +#pragma udata +#endif +uint8_t bTRNIFCount; +uint8_t ctrl_trf_state; // Control Transfer State +uint8_t ctrl_trf_session_owner; // Current transfer session owner +POINTER pSrc; // Data source pointer +POINTER pDst; // Data destination pointer +WORD_VAL wCount; // Data counter +uint8_t short_pkt_status; // Flag used by Control Transfer Read +CTRL_TRF_SETUP SetupPkt; +bool EP0OutOddNeedsArmingNext; +BDT TempBDT; +uint8_t usb_device_state; // Device States: DETACHED, ATTACHED, ... +USB_DEVICE_STATUS usb_stat; // Global USB flags (remote wakeup armed status, etc.) +uint8_t usb_active_cfg; // Value of current configuration +uint8_t usb_alt_intf[MAX_NUM_INT]; // Array to keep track of the current alternate + // setting for each interface ID + +#ifdef __XC8__ + #pragma warning disable 1090 //Disable unused variable warning. USTATSave may not be used, depending upon user settings, but that is okay/harmless/expected. +#endif +uint8_t USTATSave; +uint16_t Counter; +bool DeviceIsSoftDetached; + +/** USB FIXED LOCATION VARIABLES ***********************************/ +#if defined(__18F14K50) || defined(__18F13K50) || defined(__18LF14K50) || defined(__18LF13K50) + #define BDT_ADDR 0x200 //BDT in bank 2 on these devices +#elif defined(__18F47J53) || defined(__18F46J53) || defined(__18F27J53) || defined(__18F26J53) || defined(__18LF47J53) || defined(__18LF46J53) || defined(__18LF27J53) || defined(__18LF26J53) + #define BDT_ADDR 0xD00 //BDT in Bank 13 on these devices +#elif defined(__18F97J94) || defined(__18F87J94) || defined(__18F67J94) || defined(__18F96J99) || defined(__18F86J99) || defined(__18F66J99) || defined(__18F96J94) || defined(__18F86J94) || defined(__18F66J94) || defined(__18F95J94) || defined(__18F85J94) || defined(__18F65J94) + #define BDT_ADDR 0x100 // The USB BDT is located in Bank 1 on the PIC18F97J94 Family of devices +#elif defined(__16F1459) || defined(__16LF1459) || defined(__16F1455) || defined(__16LF1455) || defined(__16F1454) || defined(__16LF1454) + #define BDT_ADDR 0x20 + #define USB_RAM_BUFF_ADDR 0x40 //EP0 OUT and IN buffers + #define USB_HID_BUFF_OUT_ADDR @0xA0 + #define USB_HID_BUFF_IN_ADDR @0x120 +#else + //Most other PIC18 USB families use bank 4 for the BDT (ex: PIC18F46J50 family, PIC18F87J50 family, PIC18F4550/3 family, etc.) + #define BDT_ADDR 0x400 +#endif + +#ifndef __XC8__ + #pragma udata USB_BDT = BDT_ADDR +#else + #ifndef USB_RAM_BUFF_ADDR + #define USB_RAM_BUFF_ADDR (BDT_ADDR + 12 + (MAX_EP_NUMBER * 8)) + #endif +#endif + +/****************************************************************************** + * Section A: Buffer Descriptor Table - must be allocated by the linker to the + * correct address that the hardware USB module DMA engine will be looking at. + * - 0xX00 - 0xXFF( 256 bytes max) + * - MAX_EP_NUMBER is defined in usb_config.h + * - BDT data type is defined in usb_device.h + *****************************************************************************/ +#ifdef __XC8__ + #define BDT_ADDR_TAG @BDT_ADDR + #define BDT_ADDR_TAG_EP0O_EVEN @BDT_ADDR + #define BDT_ADDR_TAG_EP0O_ODD BDT_ADDR_TAG_EP0O_EVEN+4 + #define BDT_ADDR_TAG_EP0I BDT_ADDR_TAG+8 + #define BDT_ADDR_TAG_EP1O BDT_ADDR_TAG+12 + #define BDT_ADDR_TAG_EP1I BDT_ADDR_TAG+16 + #define USB_EP0_BUFF_ADDR @USB_RAM_BUFF_ADDR + #define USB_EP0_BUFF_ADDR2 USB_EP0_BUFF_ADDR+EP0_BUFF_SIZE + #define USB_CTRL_TRF_DATA_ADDR USB_EP0_BUFF_ADDR2+EP0_BUFF_SIZE + #if defined(USB_USE_HID) + #ifndef USB_HID_BUFF_OUT_ADDR + #define USB_HID_BUFF_OUT_ADDR USB_CTRL_TRF_DATA_ADDR+EP0_BUFF_SIZE + #define USB_HID_BUFF_IN_ADDR USB_HID_BUFF_OUT_ADDR+HID_INT_OUT_EP_SIZE + #endif + #endif +#else + //Else must be using the C18 compiler. The C18 compiler puts things in + //consecutive order with a udata section, so no explicit addresses for + //each BDT/buffer is needed, provided the variables go into the correct + //udata section (with the right start address of the BDT). + #define BDT_ADDR_TAG + #define BDT_ADDR_TAG_EP0O_EVEN + #define BDT_ADDR_TAG_EP0O_ODD + #define BDT_ADDR_TAG_EP0I + #define BDT_ADDR_TAG_EP1O + #define BDT_ADDR_TAG_EP1I + #define USB_EP0_BUFF_ADDR + #define USB_EP0_BUFF_ADDR2 + #define USB_CTRL_TRF_DATA_ADDR + #define USB_HID_BUFF_OUT_ADDR + #define USB_HID_BUFF_IN_ADDR +#endif + +#if(0 <= MAX_EP_NUMBER) +volatile BDT ep0BoEven BDT_ADDR_TAG_EP0O_EVEN; //Endpoint #0 BD Out EVEN +volatile BDT ep0BoOdd BDT_ADDR_TAG_EP0O_ODD; //Endpoint #0 BD Out ODD +volatile BDT ep0Bi BDT_ADDR_TAG_EP0I; //Endpoint #0 BD In +#endif + +#if(1 <= MAX_EP_NUMBER) +volatile BDT ep1Bo BDT_ADDR_TAG_EP1O; //Endpoint #1 BD Out +volatile BDT ep1Bi BDT_ADDR_TAG_EP1I; //Endpoint #1 BD In +#endif + +#if(2 <= MAX_EP_NUMBER) +volatile BDT ep2Bo; //Endpoint #2 BD Out +volatile BDT ep2Bi; //Endpoint #2 BD In +#endif + +#if(3 <= MAX_EP_NUMBER) +volatile BDT ep3Bo; //Endpoint #3 BD Out +volatile BDT ep3Bi; //Endpoint #3 BD In +#endif + +#if(4 <= MAX_EP_NUMBER) +volatile BDT ep4Bo; //Endpoint #4 BD Out +volatile BDT ep4Bi; //Endpoint #4 BD In +#endif + +#if(5 <= MAX_EP_NUMBER) +volatile far BDT ep5Bo; //Endpoint #5 BD Out +volatile far BDT ep5Bi; //Endpoint #5 BD In +#endif + +#if(6 <= MAX_EP_NUMBER) +volatile far BDT ep6Bo; //Endpoint #6 BD Out +volatile far BDT ep6Bi; //Endpoint #6 BD In +#endif + +#if(7 <= MAX_EP_NUMBER) +volatile far BDT ep7Bo; //Endpoint #7 BD Out +volatile far BDT ep7Bi; //Endpoint #7 BD In +#endif + +#if(8 <= MAX_EP_NUMBER) +volatile far BDT ep8Bo; //Endpoint #8 BD Out +volatile far BDT ep8Bi; //Endpoint #8 BD In +#endif + +#if(9 <= MAX_EP_NUMBER) +volatile far BDT ep9Bo; //Endpoint #9 BD Out +volatile far BDT ep9Bi; //Endpoint #9 BD In +#endif + +#if(10 <= MAX_EP_NUMBER) +volatile far BDT ep10Bo; //Endpoint #10 BD Out +volatile far BDT ep10Bi; //Endpoint #10 BD In +#endif + +#if(11 <= MAX_EP_NUMBER) +volatile far BDT ep11Bo; //Endpoint #11 BD Out +volatile far BDT ep11Bi; //Endpoint #11 BD In +#endif + +#if(12 <= MAX_EP_NUMBER) +volatile far BDT ep12Bo; //Endpoint #12 BD Out +volatile far BDT ep12Bi; //Endpoint #12 BD In +#endif + +#if(13 <= MAX_EP_NUMBER) +volatile far BDT ep13Bo; //Endpoint #13 BD Out +volatile far BDT ep13Bi; //Endpoint #13 BD In +#endif + +#if(14 <= MAX_EP_NUMBER) +volatile far BDT ep14Bo; //Endpoint #14 BD Out +volatile far BDT ep14Bi; //Endpoint #14 BD In +#endif + +#if(15 <= MAX_EP_NUMBER) +volatile far BDT ep15Bo; //Endpoint #15 BD Out +volatile far BDT ep15Bi; //Endpoint #15 BD In +#endif + +/****************************************************************************** + * Section B: EP0 endpoint data buffer space - must be located in USB module + * accessible RAM region + *****************************************************************************/ +volatile uint8_t EP0OutEvenBuf[EP0_BUFF_SIZE] USB_EP0_BUFF_ADDR; +volatile uint8_t EP0OutOddBuf[EP0_BUFF_SIZE] USB_EP0_BUFF_ADDR2; +volatile CTRL_TRF_DATA CtrlTrfData USB_CTRL_TRF_DATA_ADDR; + +/****************************************************************************** + * Section C: HID application interrupt endpoint buffers - must be located in + * USB module accessible RAM region + *****************************************************************************/ +#if defined(USB_USE_HID) +volatile unsigned char hid_report_out[HID_INT_OUT_EP_SIZE] USB_HID_BUFF_OUT_ADDR; +volatile unsigned char hid_report_in[HID_INT_IN_EP_SIZE] USB_HID_BUFF_IN_ADDR; +#endif + +#ifndef __XC8__ +#pragma udata +#endif + + + +//If the user is not using an I/O pin for USB +5V VBUS presence detection (ex: because +//they are using a bus powered only application), default to assuming that VBUS +//is effectively always present (which is true for bus powered only applications). +#if !defined(USE_USB_BUS_SENSE_IO) + #define usb_bus_sense 1 +#endif + +//If the user is not using an I/O pin for self powered detection (ex: because +//the application is bus powered only or is dual self/bus powered but uses less +//than 100mA max from VBUS), default to assuming that the device is currently bus +//powered. +#if !defined(USE_SELF_POWER_SENSE_IO) + #define self_power 0 +#endif + +//Most applications only implement one configuration descriptor. If the application +//does use more than one, make sure the USB_MAX_NUM_CONFIG_DSC is set to the value +//actually implemented. Needed for error checking of host requests, to make sure +//they are in bounds/have a legal index. +#if !defined(USB_MAX_NUM_CONFIG_DSC) + #define USB_MAX_NUM_CONFIG_DSC 1 +#endif + + + +/** D E C L A R A T I O N S **************************************************/ +#ifndef __XC8__ +#pragma code +#endif + + +/****************************************************************************** + * Function: void USBDeviceInit(void) + * + * PreCondition: User application code is responsible for making sure the + * currently selected oscillator settings are compatible with + * USB operation, prior to calling this function. The user + * application code must also initialize the VBUS sensing I/O + * pin before calling this code, if it uses VBUS sensing + * (only needed in self powered applications). + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This routine initializes the USB stack state machine + * and turns on the USB module. It should be called only + * once during bootup of the application code. + * + * Note: None + *****************************************************************************/ +void USBDeviceInit(void) +{ + //Check if the USB module is already enabled. If so, disable it and wait + //~100ms+ (>1 second recommended if CDC application firmware on XP), to + //ensure that the host has a chance to see and process the USB device detach + //event. + if(UCONbits.USBEN == 1) + { + //USB module was already on. This is likely because the user applicaiton + //firmware jumped into this bootloader firmware using the absolute + //software entry method, without first turning off the USB module + USBDisableWithLongDelay(); + } + DeviceIsSoftDetached = FALSE; + + //Make initial call to USBCheckBusStatus() which will either do nothing (if + //VBUS is not present), or, will totally re-init everything and perform + //a soft attach event (if VBUS is present, or the user doesn't use + //VBUS sense I/O in their application). Either way, everything gets + //totally re-initialized either now, or in the future, once VBUS 0->1 transition + //is detected. + USBCheckBusStatus(); +} + + +//Turns on the USB module and attaches to the host. Normally, this function should +//only be called after you are already sure that VBUS is present/the USB cable +//is attached to a powered host. +void USBSoftAttach(void) +{ + //Check if we are transitioning from deliberate soft detached to attached + //state. If so, need to delay a relatively long time (ex: 100ms+) to ensure + //enough detached dwell time is met, to ensure the host correctly detects + //the detach/reattach event (very short detachment less than possible via + //a human unplug/reattachment event can sometimes be misprocessed by the host). + if(DeviceIsSoftDetached == TRUE) + { + USBDisableWithLongDelay(); + } + //(Re-)initialize registers and turn on the USB module + UCON = 0; + UCFG = UCFG_VAL; + UIE = 0; + UCONbits.USBEN = 1; + //(Re-)initialize various USB stack variables and other things + USBProtocolResetHandler(); + usb_device_state = ATTACHED_STATE; + DeviceIsSoftDetached = FALSE; +} + +//Disables the USB module and performs a soft detach from the USB host, regardless +//of the detected VBUS status. However, if you call this function, and you are +//not using VBUS I/O sensing in your application you must stop calling USBCheckBusStatus(), +//since it will promptly try to re-enable the USB module. +void USBSoftDetach(void) +{ + //Disable USB module (this doesn't need to be done in the loop, but we + //add it to the loop to deliberately make the loop execute slower) + UCONbits.SUSPND = 0; + UCON = 0x00; //Disable module + usb_device_state = DETACHED_STATE; + DeviceIsSoftDetached = TRUE; +} + +/****************************************************************************** + * Function: void USBCheckBusStatus(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This routine enables/disables the USB module by monitoring + * the USB power signal. + * + * Note: None + *****************************************************************************/ +void USBCheckBusStatus(void) +{ + //Check if the user's firmware deliberately soft detached from the host. + //If so, don't automatically try to reconnect, until the user subsequently + //calls the USBSoftAttach() API. + if(DeviceIsSoftDetached == TRUE) + { + return; + } + + /************************************************************************** + * Bus Attachment & Detachment Detection + * usb_bus_sense is an i/o pin defined in HardwareProfile.h + *************************************************************************/ + #define USB_BUS_ATTACHED 1 + #define USB_BUS_DETACHED 0 + + #ifdef USE_USB_BUS_SENSE_IO + if(usb_bus_sense == USB_BUS_ATTACHED) // Is USB bus attached? + { + if(UCONbits.USBEN == 0) // Is the module off? + { + USBSoftAttach(); // Is off, enable it + } + } + else + { + if(UCONbits.USBEN == 1) // Is the module on? + { + USBSoftDetach(); + DeviceIsSoftDetached = FALSE; + } + }//end if(usb_bus_sense...) + #else + if(UCONbits.USBEN == 0) // Is the module off? + USBSoftAttach(); // Is off, enable it + #endif +}//end USBCheckBusStatus + + + + + + + +/****************************************************************************** + * Function: void USBDeviceTasks(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This routine is the heart of this firmware. It manages + * all USB interrupts/events that need processing. + * + * Note: Device state transitions through the following stages: + * DETACHED -> ATTACHED -> POWERED -> DEFAULT -> + * ADDRESS_PENDING -> ADDRESSED -> CONFIGURED. Once in the + * CONFIGURED state the application is ready to run. Only + * in the CONFIGURED state should the host try to send/receive + * packets on the application endpoints (ex: endpoints > 0). + *****************************************************************************/ +void USBDeviceTasks(void) +{ + static volatile BDT* pBDTEntry; + static uint8_t i; + + //Check for possible changes in VBUS (USB connection) status. + USBCheckBusStatus(); + + /* + * Pointless to continue servicing if USB cable is not even attached. + */ + if(usb_device_state == DETACHED_STATE) return; + + /* + * Task A: Service USB Activity Interrupt + */ + + if(UIRbits.ACTVIF) USBWakeFromSuspend(); + + /* + * Pointless to continue servicing if the device is in suspend mode. + */ + if(UCONbits.SUSPND == 1) return; + + /* + * 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. + */ + if(UIRbits.URSTIF) USBProtocolResetHandler(); + + /* + * Task C: Service other USB interrupts + */ + if(UIRbits.IDLEIF) USBSuspend(); + + //Don't need to actually do anything for SOF packets (except in isochronous devices) + //if(UIRbits.SOFIF && UIEbits.SOFIE) USB_SOF_Handler(); + + //Don't need to actually do anything for STALL packets being sent. For stalls + //on EP0, the host is required to send a new SETUP packet, which the hardware + //accepts (even if the BSTALL bit is set), as STALL is not a valid response to a + //SETUP packet. + //if(UIRbits.STALLIF) USBStallHandler(); + + + /* + * 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) return; + + /* + * Task D: Servicing USB Transaction Complete Interrupt + */ + for(bTRNIFCount = 0; bTRNIFCount < 4; bTRNIFCount++) + { + if(UIRbits.TRNIF) + { + //Check what endpoint and direction the last packet was sent or received on. + USTATSave = USTAT; //AND out any bits other than EP dir + num + ping pong dest + if((USTAT & 0x7C) == EP00_OUT) + { + //Check if it was even or odd EP0 OUT buffer + if(USTATbits.PPBI == 0) + { + //The OUT or SETUP packet arrived on EP0 OUT EVEN endpoint + pBDTEntry = &ep0BoEven; + } + else + { + //The OUT or SETUP packet arrived on EP0 OUT ODD endpoint + pBDTEntry = &ep0BoOdd; + } + + //Allow USTAT FIFO to advance, done with the value + UIRbits.TRNIF = 0; + + //Check if the current EP0 OUT buffer has a SETUP packet (or a regular OUT instead) + if(pBDTEntry->Stat.PID == SETUP_TOKEN) + { + //Copy the 8 bytes of packet data that arrived for the SETUP transaction + //from the USB endpoint buffer, into the global SetupPkt buffer. + for(i = 0; i < sizeof(CTRL_TRF_SETUP); i++) + { + SetupPkt._byte[i] = *pBDTEntry->ADR++; + } + + //Handle the control transfer (parse the 8-byte SETUP command and + //figure out what to do with the host's request) + USBCtrlTrfSetupHandler(); + } + else + { + //Handle the conventional OUT DATA packet the just arrived from the host + USBCtrlTrfOutHandler(USTATSave); + } + } + else if(USTAT == EP00_IN) // EP0 IN + { + //A packet just got sent to the host over the EP0 IN endpoint + UIRbits.TRNIF = 0; + USBCtrlTrfInHandler(); //Figure out what to do now (ex: such as preparing the next IN data packet in the sequence) + } + else + { + //The last packet sent or received was on EP1 or higher. In this + //case the USB stack doesn't need to handle anything, since the + //user's application code is responsible for checking status/arming + //the applicaiton endpoints + UIRbits.TRNIF = 0; //Just clear TRNIF to advance the USTAT FIFO. + } + }//if(UIRbits.TRNIF) + else + { + break; + } + }// end for(bTRNIFCount = 0; bTRNIFCount < 4; bTRNIFCount++) + +}//end USBDeviceTasks() + +/****************************************************************************** + * Function: void USBSuspend(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: When the USB host sends USB suspend signalling, this function + * gets called. In order to be USB compliant, the device + * firmware should configure the application so that it takes + * no more than 2.5mA from the +5VBus supply from the + * USB port during USB suspend events. Bus powered devices can + * meet this by suspending the USB module, and either clock switching + * to a low frequency or sleeping. This is not necessary if the + * application is self powered, or if it doesn't need to meet + * all USB compliance requirements. + * + * Note: None + *****************************************************************************/ +void USBSuspend(void) +{ + static unsigned char UIESave; + #if defined(_PIC14E) + static unsigned char INTCONSave; + #endif + + /* + * 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. + */ + + //After the suspend condition is detected, we should do what is necessary + //to reduce USB VBUS power consumption from the host down to <2.5mA. + //For self powered devices, no action is really strictly necessary. + //For bus powered devices, either the microcontroller must clock switch or + //enter sleep mode to conserve enough power to meet the 2.5mA limit. + + UIESave = UIE; //Save UIE values, only want to wake on certain events + UIE = 0b00000100; //Enabling the ACTVIF interrupt source only + //Since ACTVIF triggers on any bus activity, it will also trigger on USB reset events. + //But that is okay, we want that. + + UIRbits.IDLEIF = 0; //Clear interrupt flag, we are servicing the idle event + UCONbits.SUSPND = 1; // Put USB module in power conserve + // mode, SIE clock inactive + + //Disable all microcontroller wake up sources, except for the one(s) which will + //be used to wake up the microcontroller. At the very least, the USB activity + //detect interrupt should be enabled as a wake up source. + + USBIF_FLAG = 0; //Won't get clear if an enabled and pending wake up source was already triggered + //However, since only the ACTVIF interrupt source is currently enabled, + //only bus activity events will prevent entry into sleep. + + USBIE_BIT = 1; //Set USB wakeup source + + #if defined(_PIC14E) + INTCONbits.PEIE = 1; //Enable interrupts to wake device on PIC16 (but GIE = 0, so no vectoring) + #endif + + //Now call the user's callback function, so that they can configure I/O pins + //for low power states, and then either sleep the microcontroller or clock + //switch to a lower power state. + USBCBSuspend(); + + #if defined(_PIC14E) + INTCONbits.PEIE = 0; //Clear interrupts + #endif + + + USBIE_BIT = 0; + UIE |= UIESave; //Restore UIE to state it was in prior to entering USB suspend (with the ACTVIF enabled as well, used later in stack) + //USB suspend events do not by themselves cause any loss of + //state information inside either the USB device firmware, or + //in the USB host software. +}//end USBSuspend + +/****************************************************************************** + * Function: void USBWakeFromSuspend(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: + * + * Note: None + *****************************************************************************/ +void USBWakeFromSuspend(void) +{ + //Call the user's callback function letting them know of wake up from suspend + //state. The user is responsible for clock switching back to fully USB compatible + //clock settings, prior to returning from the USBCBWakeFromSuspend() function. + USBCBWakeFromSuspend(); + + //Now bring the USB module out of suspend state and clear the interrupt flag. + UCONbits.SUSPND = 0; //Bring USB module hardware out of suspend mode/begin clocking it normally + UIEbits.ACTVIE = 0; //Disable ACTVIF as a USB wake up source (until another USB suspend condition is detected) + while(UIRbits.ACTVIF){UIRbits.ACTVIF = 0;} //May not be immediately clearable if USB module not yet clocked, therefore, using loop to allow osc startup time if necessary + +}//end USBWakeFromSuspend + + + + + + + + +/****************************************************************************** + * Function: void USBProtocolResetHandler(void) + * + * PreCondition: A USB bus reset is received from the host (or we have just + * turned on the USB module). + * + * Input: None + * + * Output: None + * + * Side Effects: Totally re-initializes USB stack variables and several + * USB module hardware settings as well. + * + * Overview: 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. + * + * Note: None + *****************************************************************************/ +void USBProtocolResetHandler(void) +{ + usb_device_state = DEFAULT_STATE; + UEIE = 0; // Not using USB error interrupts (no special handling required anyway) + UIR = 0; // Clears all USB interrupts + UIE = 0b01111011; // Enable all interrupts except ACTVIE + UADDR = 0x00; // Reset to default address + mDisableEP1to7(); // Reset all non-EP0 UEPn registers + UEP0 = EP_CTRL|HSHK_EN; // Init EP0 as a Ctrl EP, see usb_device.h + UCONbits.PPBRST = 1; // Reset ping pong buffer pointers + while(UIRbits.TRNIF == 1) // Flush any pending transactions + { + UIRbits.TRNIF = 0; + ClearWatchdog(); //5 Tcy minimum (2 for call, 2 for return, 1 for clearing) to allow TRNIF to (potentially) reassert + } + UCONbits.PPBRST = 0; + UCONbits.PKTDIS = 0; // Make sure packet processing is enabled + + //Prepare EP0 OUT Even to receive the first SETUP packet + TempBDT.Stat._byte = _DAT0|_BSTALL; + LoadBDTandSetUOWN(EP0_OUT_EVEN_BDT_INDEX); //Configures address/size fields and sets UOWN + EP0OutOddNeedsArmingNext = TRUE; + usb_stat._byte = 0x00; // Clear USB flags (like remote wakeup armed status) + usb_active_cfg = 0; // Clear active configuration + USBCBInitEP(0); // Call application callback function to give it notification + // it is getting un-configured (ex: equiv of set configuration to 0). +}//end USBProtocolResetHandler + + + +/****************************************************************************** + * Function: void USBCtrlTrfSetupHandler(void) + * + * PreCondition: SetupPkt buffer is loaded with valid USB Setup Data + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: 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, the USBCBCheckOtherReq() function + * in main.c should be modified to call all of the new 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. + * + *****************************************************************************/ +void USBCtrlTrfSetupHandler(void) +{ + //If we get to here, that means we just received a new SETUP packet from the + //host. In this case, we should re-initalize any control transfer status + //tracking variables, since only one control transfer can be pending at a + //time (and if the last one didn't fully complete for whatever reason, + //abandon it). + ep0Bi.Stat._byte = _UCPU; + short_pkt_status = SHORT_PKT_NOT_SENT; + + //Make sure none of the EP0 OUT endpoints are still armed (one could still + //be UOWN == 1, if EP0 OUT was previously double armed). + //Clear UOWN on all EP0 OUT BDTs until we are done parsing/processing this SETUP. + if(ep0BoEven.Stat.UOWN == 1) + { + ep0BoEven.Stat._byte = _UCPU; + EP0OutOddNeedsArmingNext = FALSE; + } + if(ep0BoOdd.Stat.UOWN == 1) + { + ep0BoOdd.Stat._byte = _UCPU; + EP0OutOddNeedsArmingNext = TRUE; + } + ctrl_trf_state = WAIT_SETUP; + ctrl_trf_session_owner = MUID_NULL; // Set owner to NULL so stack knows to STALL if no control transfer handlers know how to handle the request + wCount.Val = 0; + UCONbits.PKTDIS = 0; //Clear PKTDIS bit now, since all EP0 IN/OUT endpoints are currently configured to NAK + + //Now that we are done re-initializing state tracking variables, it is + //time to parse the recently received SETUP packet to determine how to respond + //to the host's request (which could be USB 2.0 "Chapter 9" or a USB device + //class specific request). + USBCheckStdRequest(); // Takes care of handling USB "Chapter 9" standard requests + USBCBCheckOtherReq(); // User callback function, which should call any class specific control transfer handler functions + + //Now do final endpoint servicing to complete our response for the request + USBCtrlEPServiceComplete(); + +}//end USBCtrlTrfSetupHandler + + +/****************************************************************************** + * Function: void USBCtrlTrfOutHandler(uint8_t) + * + * PreCondition: None + * + * Input: The USTAT register value for the most recently received + * OUT data packet that we are processing. + * + * Output: None + * + * Side Effects: None + * + * Overview: This routine handles an OUT transaction according to + * which control transfer state is currently active. + * + * Note: Note that if the the control transfer was from + * host to device, the session owner should be notified + * at the end of the transfer to service/consume the + * received data. To do this, the user must implement + * a function called USBCBControlTransferOutDataReady() + * in their application code, which consumes the OUT (from host) + * data that was just received. The USB stack will call this + * user callback function once all the expected bytes are received + * from the host. + * + * This is an optional function, and may be disabled to save code space by + * commenting out the "#define ENABLE_CONTROL_TRANSFERS_WITH_OUT_DATA_STAGE" + * option in usb_config.h, for applications that never use + * host to device control transfers with OUT data stage. + * + *****************************************************************************/ +#if defined (ENABLE_CONTROL_TRANSFERS_WITH_OUT_DATA_STAGE) +void USBCtrlTrfOutHandler(uint8_t USTATValue) +{ + uint8_t bytes_received; + + //Check if a host to device control transfer is in progress (ex: SETUP, OUT, OUT, ..., IN[status]) + if(ctrl_trf_state == CTRL_TRF_RX) + { + //We just received an OUT data packet from the host. Need to copy it + //to the control transfer session owner's receive buffer. + //First: Figure out how many bytes arrived in the packet, and where they went + + //Check if the OUT packet arrived on the odd EP0 OUT endpoint + if(USTATValue & 0x02) + { + //The OUT packet arrived on EP0 OUT Odd + bytes_received = ep0BoOdd.Cnt; + pSrc.bRam = ep0BoOdd.ADR; + } + else + { + //The OUT packet arrived on EP0 OUT Even + bytes_received = ep0BoEven.Cnt; + pSrc.bRam = ep0BoEven.ADR; + } + + //Keep track of how many total bytes have been received in this OUT + //control transfer (host to device), so we know when the host + //is finished sending us all the data. + wCount.Val = wCount.Val + bytes_received; + + //Copy the bytes received from the OUT endpoint buffer, into the user + //specified buffer (the pDst pointer was setup at the start of the control transfer). + while(bytes_received) + { + *pDst.bRam++ = *pSrc.bRam++; + bytes_received--; + }//end while(byte_to_read.word_val) + + + //Check if there are anymore OUT packets in the request or not + if(wCount.Val < SetupPkt.wLength) + { + //If we get to here, then the host is still going to send us more data. + //Re-arm the OUT endpoint to receive the next OUT data packet + if(EP0OutOddNeedsArmingNext == TRUE) + { + if(ep0BoEven.Stat.DTS == 1) + { + TempBDT.Stat._byte = _DAT0 | _DTSEN; + } + else + { + TempBDT.Stat._byte = _DAT1 | _DTSEN; + } + LoadBDTandSetUOWN(EP0_OUT_ODD_BDT_INDEX); + EP0OutOddNeedsArmingNext = FALSE; + } + else + { + if(ep0BoOdd.Stat.DTS == 1) + { + TempBDT.Stat._byte = _DAT0 | _DTSEN; + } + else + { + TempBDT.Stat._byte = _DAT1 | _DTSEN; + } + LoadBDTandSetUOWN(EP0_OUT_EVEN_BDT_INDEX); + EP0OutOddNeedsArmingNext = TRUE; + } + }//if(wCount.Val < SetupPkt.wLength) + else + { + //No more OUT data packets expected, re-arm EP0 OUT to receive the + //next SETUP packet, and then allow the IN status stage to complete + TempBDT.Stat._byte = _BSTALL; + if(EP0OutOddNeedsArmingNext == TRUE) + { + LoadBDTandSetUOWN(EP0_OUT_ODD_BDT_INDEX); + EP0OutOddNeedsArmingNext = FALSE; + } + else + { + LoadBDTandSetUOWN(EP0_OUT_EVEN_BDT_INDEX); + EP0OutOddNeedsArmingNext = TRUE; + } + + //Allow the IN status stage to complete now. + ep0Bi.Cnt = 0; + ep0Bi.Stat._byte = _DAT1|_DTSEN; + ep0Bi.Stat._byte |= _USIE; + + + //Call the application's callback function now to consume/use + //the data that has arrived as a control transfer over EP0 OUT... + USBCBControlTransferOutDataReady(); //<--Implement a real application call back function of this name, to consume the received OUT data... + } + }//if(ctrl_trf_state == CTRL_TRF_RX) + else //In this case the last OUT transaction must have been a status stage of a CTRL_TRF_TX + { + //Don't need to do anything here. EP0 OUT was already double armed (when + //we first processed the intial SETUP packet), so it is already ready + //for a new SETUP packet. + } +}//end USBCtrlTrfOutHandler() +#endif + + +/****************************************************************************** + * Function: void USBCtrlTrfInHandler(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This routine handles an IN transaction according to + * which control transfer state is currently active. + * + * Note: A Set Address Request must not change the actual address + * of the device until the completion of the control + * transfer (ex: after status stage packet is ACKed). The + * end (status stage) 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. + *****************************************************************************/ +void USBCtrlTrfInHandler(void) +{ + //Check if we are in the address pending state. If so, we just completed + //the status stage of the SET ADDRESS command, and we now must switch + //ourself to the new USB device address. + if(usb_device_state == ADR_PENDING_STATE) + { + UADDR = SetupPkt.bDevADR; + if(UADDR > 0) + usb_device_state = ADDRESS_STATE; + else + usb_device_state = DEFAULT_STATE; + }//end if + + //Now check what control transfer state we are in, so we know if the most + //recent IN packet was a data stage packet, or a status stage packet. + if(ctrl_trf_state == CTRL_TRF_TX) + { + //If we get to here, we must have just sent an IN data packet, as part + //of a data stage (ex: SETUP, IN, IN [one of these data IN's just occurred], OUT[status, probably hasn't occurred yet]) + USBCtrlTrfTxService(); + + //Check if we have finisihed sending a short packet or not. + if(short_pkt_status == SHORT_PKT_SENT) + { + //If a short packet has been sent, don't want to send any more, + //stall next time if host is tries to read more data than it originally requested. + ep0Bi.Stat._byte = _BSTALL; + ep0Bi.Stat._byte |= _USIE; + } + else + { + //We still have one or more IN data packets to send. Arm the IN endpoint now. + if(ep0Bi.Stat.DTS == 0) + ep0Bi.Stat._byte = _DAT1|_DTSEN; + else + ep0Bi.Stat._byte = _DAT0|_DTSEN; + + ep0Bi.Stat._byte |= _USIE; + }//end if(...)else + } + else // CTRL_TRF_RX + { + //The status stage of a host to device control transfer just completed (SETUP, OUT, OUT,...IN[status, just completed]) + //Don't need to do anything here, already armed EP0 OUT to be ready for the + //next SETUP packet after the last OUT packet arrived. + } + +}//end USBCtrlTrfInHandler + + +/****************************************************************************** + * Function: void USBCtrlTrfTxService(void) + * + * PreCondition: pSrc, wCount, and usb_stat.ctrl_trf_mem are setup properly. + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This routine should be called from only two places. + * One from USBCtrlEPServiceComplete() and one from + * USBCtrlTrfInHandler(). It takes care of managing a + * device to host (IN to host) control transfer over multiple + * USB transactions. + * + * Note: None + *****************************************************************************/ +void USBCtrlTrfTxService(void) +{ + static uint8_t bytes_to_send; + + //First, have to figure out how many bytes of data to send in the transaction + //Check if the number of byte remaining to be sent it less than one full width + //packet worth of data payload. + bytes_to_send = EP0_BUFF_SIZE; + if(wCount.Val < EP0_BUFF_SIZE) + { + //The data remaining is less than a full width packet. We will send a + //short packet on the next transaction. + bytes_to_send = wCount.Val; + if(short_pkt_status == SHORT_PKT_NOT_SENT) + { + short_pkt_status = SHORT_PKT_PENDING; + } + else if(short_pkt_status == SHORT_PKT_PENDING) + { + short_pkt_status = SHORT_PKT_SENT; + } + } + + //Setup BDT so USB module knows how big of a USB packet to send IN to the host on the next EP0 IN transaction + ep0Bi.Cnt = bytes_to_send; + + //Keep track of how many bytes still need to be sent in this control transfer + wCount.Val -= bytes_to_send; + + //Now copy the data from the original source location (which was previously + //setup by the Chapter 9 or class specific control transfer handler code), to + //the USB endpoint buffer. + pDst.bRam = (uint8_t*)&CtrlTrfData; // Set destination pointer + if(usb_stat.ctrl_trf_mem == _ROM) // Determine type of memory source + { + while(bytes_to_send) + { + *pDst.bRam = *pSrc.bRom; + pDst.bRam++; + pSrc.bRom++; + bytes_to_send--; + }//end while(byte_to_send.Val) + } + else // RAM + { + while(bytes_to_send) + { + *pDst.bRam = *pSrc.bRam; + pDst.bRam++; + pSrc.bRam++; + bytes_to_send--; + }//end while(byte_to_send.Val) + }//end if(usb_stat.ctrl_trf_mem == _ROM) + +}//end USBCtrlTrfTxService + + + +/****************************************************************************** + * Function: void USBCtrlEPServiceComplete(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: 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. + * + * Note: None + *****************************************************************************/ +void USBCtrlEPServiceComplete(void) +{ + //Check if any USB chapter 9 or device class specific control transfer handlers + //knew how to handle the request or not. + if(ctrl_trf_session_owner == MUID_NULL) + { + //If we get to here, that means no request handlers knew how to handle + //the host's control transfer request. In this case, we are supposed + //to send STALL response to the host, to let it know of the situation. + ep0Bi.Stat._byte = _BSTALL; + ep0Bi.Stat._byte |= _USIE; + TempBDT.Stat._byte = _BSTALL; + if(EP0OutOddNeedsArmingNext == TRUE) + { + LoadBDTandSetUOWN(EP0_OUT_ODD_BDT_INDEX); + EP0OutOddNeedsArmingNext = FALSE; + } + else + { + LoadBDTandSetUOWN(EP0_OUT_EVEN_BDT_INDEX); + EP0OutOddNeedsArmingNext = TRUE; + } + } + else // A module has claimed ownership of the control transfer session. + { + if(SetupPkt.DataDir == DEV_TO_HOST) + { + /* + * If we get to here, this means a control read is occurring: + * ... | + * + * To handle this case, we need to arm both EP0 IN and OUT (twice) + * so as to handle the case where the host performs early termination + * (ex: when it sends the OUT status stage before is receives all + * of the requested data). + * + * 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. + */ + + //Set state machine to keep track of what we are doing accross multiple USB packets. + ctrl_trf_state = CTRL_TRF_TX; + + //Boundary check how many bytes the transfer handler is expecting + //to send IN to the host, versus the number that the host is actually + //requesting. We should always send the lesser of the two. + if(SetupPkt.wLength < wCount.Val) + wCount.Val = SetupPkt.wLength; + + //Allow the IN transaction hanlder function to execute, so as to copy + //the requested IN data into the proper USB IN endpoint buffer. + USBCtrlTrfTxService(); + + //Double arm EP0 OUT (even and odd both active at the same time): Once + //for the 0-byte status stage OUT packet, and once for the next 8-byte SETUP packet + TempBDT.Stat._byte = _DAT1 | _DTSEN; //DTS = 1 for the status stage, DTS ignored/irrelevant for SETUP packets + LoadBDTandSetUOWN(EP0_OUT_ODD_BDT_INDEX); + LoadBDTandSetUOWN(EP0_OUT_EVEN_BDT_INDEX); + + //Arm EP0 IN to send the first packet worth of data that the host has + //requested for this particular control transfer. The USBCtrlTrfTxService() + //function will have already copied the data into the CtrlTrfData[] buffer. + ep0Bi.ADR = (uint8_t*)&CtrlTrfData; + ep0Bi.Stat._byte = _DAT1|_DTSEN; + ep0Bi.Stat._byte |= _USIE; + }//if(SetupPkt.DataDir == DEV_TO_HOST) + else //else we must be (SetupPkt.DataDir == HOST_TO_DEV) + { + /* + * Control Write (with data stage): + * ... | + * + * Certain host to device requests may not have any data stage, such + * as the "set address" request: + * | + */ + + //Keep track of what we are doing, accross multiple USB packets. + ctrl_trf_state = CTRL_TRF_RX; + + //Prepare OUT EP to receive either the first DATA1 OUT data packet in the host + //to device control transfer, or the SETUP packet (if no data stage). + //We only arm one of the EP0 OUT buffers for this. + TempBDT.Stat._byte = _BSTALL; //Assume initially we will get a SETUP + //Check the length of the transfer, if is not 0, then the next packet will be a normal OUT instead + if(SetupPkt.wLength == 0) + { + TempBDT.Stat._byte = _DAT1|_DTSEN; //Prepare for normal OUT packet instead + } + //Check which EP0 out needs arming, and arm it. + if(EP0OutOddNeedsArmingNext == TRUE) + { + LoadBDTandSetUOWN(EP0_OUT_ODD_BDT_INDEX); + EP0OutOddNeedsArmingNext = FALSE; + } + else + { + LoadBDTandSetUOWN(EP0_OUT_EVEN_BDT_INDEX); + EP0OutOddNeedsArmingNext = TRUE; + } + + //Check if there is any actual OUT data in this transfer. If there + //is no data stage, then we can proceed to arm the status stage now. + if(SetupPkt.wLength == 0) + { + //Arm the status stage 0-byte IN packet + ep0Bi.Cnt = 0; + ep0Bi.Stat._byte = _DAT1|_DTSEN; + ep0Bi.Stat._byte |= _USIE; + } + //else + //{ + // //If there is OUT data pending to be received, we wait until we have + // //received the host's data before arming the status stage + //} + }//end if(SetupPkt.DataDir == DEV_TO_HOST) + }//end if(ctrl_trf_session_owner == MUID_NULL) + +}//end USBCtrlEPServiceComplete + + + +/****************************************************************************** + * Function: void USBCheckStdRequest(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This routine checks the setup data packet to see if it + * knows how to handle it + * + * Note: None + *****************************************************************************/ +void USBCheckStdRequest(void) +{ + if(SetupPkt.RequestType != STANDARD) return; + + switch(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 */ + break; + case GET_DSC: + USBStdGetDscHandler(); + break; + case SET_CFG: + USBStdSetCfgHandler(); + break; + case GET_CFG: + ctrl_trf_session_owner = MUID_USB9; + pSrc.bRam = (uint8_t*)&usb_active_cfg; // Set Source + usb_stat.ctrl_trf_mem = _RAM; // Set memory type + //LSB(wCount) = 1; // Set data count + wCount.v[0] = 1; + break; + case GET_STATUS: + USBStdGetStatusHandler(); + break; + case CLR_FEATURE: + case SET_FEATURE: + USBStdFeatureReqHandler(); + break; + case GET_INTF: + ctrl_trf_session_owner = MUID_USB9; + pSrc.bRam = (uint8_t*)&usb_alt_intf+SetupPkt.bIntfID; // Set source + usb_stat.ctrl_trf_mem = _RAM; // Set memory type + wCount.v[0] = 1; // Set data count + break; + case SET_INTF: + ctrl_trf_session_owner = MUID_USB9; + usb_alt_intf[SetupPkt.bIntfID] = SetupPkt.bAltID; + break; + case SET_DSC: + case SYNCH_FRAME: + default: + break; + }//end switch + +}//end USBCheckStdRequest + +/****************************************************************************** + * Function: void USBStdGetDscHandler(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: 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 usb_descriptors.c are declared correctly. + * + * Note: None + *****************************************************************************/ +void USBStdGetDscHandler(void) +{ + if(SetupPkt.bmRequestType == 0x80) + { + switch(SetupPkt.bDscType) + { + case DSC_DEV: + ctrl_trf_session_owner = MUID_USB9; + pSrc.bRom = (ROM uint8_t*)&device_dsc; + wCount.v[0] = sizeof(device_dsc); // Set data count + break; + case DSC_CFG: + //Error check to make sure the host is requesting a legal/implemented configuration descriptor + if(SetupPkt.bDscIndex < USB_MAX_NUM_CONFIG_DSC) + { + ctrl_trf_session_owner = MUID_USB9; + pSrc.bRom = (ROM BYTE*)&CFG01; + wCount.Val = sizeof(CFG01); // Set data count + } + break; + case DSC_STR: + ctrl_trf_session_owner = MUID_USB9; + pSrc.bRom = *(USB_SD_Ptr+SetupPkt.bDscIndex); + wCount.Val = *pSrc.bRom; // Set data count + break; + }//end switch + + usb_stat.ctrl_trf_mem = _ROM; // Set memory type + }//end if +}//end USBStdGetDscHandler + +/****************************************************************************** + * Function: void USBStdSetCfgHandler(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This routine first disables all endpoints by clearing + * UEP registers. It then configures (initializes) endpoints + * specified in the modifiable section. + * + * Note: None + *****************************************************************************/ +void USBStdSetCfgHandler(void) +{ + static unsigned char i; + + ctrl_trf_session_owner = MUID_USB9; + //Initially disable all endpoints (except EP0, which we are still using) + mDisableEP1to7(); // See usb_device.h + //Reset alternate interface selection settings to default value + for(i = 0; i < MAX_NUM_INT; i++) + { + usb_alt_intf[i] = 0; + } + + //Save the current configuration number that we have been set to + usb_active_cfg = SetupPkt.bCfgValue; + + //Call the user's set configuration handler function. Note: We pass + //the user's set configuration handler the configuration number, since + //the USB host could either configure us (set to !0), or de-configure + //us (set to == 0). The application firmware should disable endpoints + //when getting de-configured. + USBCBInitEP(usb_active_cfg); + + if(SetupPkt.bCfgValue == 0) + { + usb_device_state = ADDRESS_STATE; + } + else + { + usb_device_state = CONFIGURED_STATE; + }//end if(SetupPkt.bcfgValue == 0) +}//end USBStdSetCfgHandler + + +/****************************************************************************** + * Function: void USBStdGetStatusHandler(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This routine handles the standard GET_STATUS request + * + * Note: None + *****************************************************************************/ +void USBStdGetStatusHandler(void) +{ + CtrlTrfData._byte0 = 0; // Initialize content + CtrlTrfData._byte1 = 0; + + switch(SetupPkt.Recipient) + { + 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) // self_power defined in HardwareProfile.h + CtrlTrfData._byte0 |= 0b00000001; // Set bit0 + if(usb_stat.RemoteWakeup == 1) // usb_stat defined in usbmmap.c + CtrlTrfData._byte0|=0b00000010; // Set bit1 + break; + case RCPT_INTF: + ctrl_trf_session_owner = MUID_USB9; // No data to update + break; + case RCPT_EP: + ctrl_trf_session_owner = MUID_USB9; + /* + * _byte0: bit0: Halt Status [0] Not Halted [1] Halted + */ + pDst.bRam = (uint8_t*)&ep0BoEven+(SetupPkt.EPNum*8)+(SetupPkt.EPDir*4)+4; //+4 is to skip past the EP0 OUT ODD BDT entry + if(*pDst.bRam & _BSTALL) // Use _BSTALL as a bit mask + CtrlTrfData._byte0=0x01;// Set bit0 + break; + }//end switch + + if(ctrl_trf_session_owner == MUID_USB9) + { + pSrc.bRam = (uint8_t*)&CtrlTrfData; // Set Source + usb_stat.ctrl_trf_mem = _RAM; // Set memory type + wCount.v[0] = 2; // Set data count + }//end if(...) +}//end USBStdGetStatusHandler + + +/****************************************************************************** + * Function: void USBStdFeatureReqHandler(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This routine handles the standard SET & CLEAR FEATURES + * requests + * + * Note: None + *****************************************************************************/ +void USBStdFeatureReqHandler(void) +{ + //Check if the host GET STATUS request was for the device's remote wakeup armed status + if((SetupPkt.bFeature == DEVICE_REMOTE_WAKEUP)&&(SetupPkt.Recipient == RCPT_DEV)) + { + ctrl_trf_session_owner = MUID_USB9; + if(SetupPkt.bRequest == SET_FEATURE) + usb_stat.RemoteWakeup = 1; + else + usb_stat.RemoteWakeup = 0; + }//end if + + //Check if the host GET STATUS request was for the endpoint halt status of an endpoint + if((SetupPkt.bFeature == ENDPOINT_HALT)&&(SetupPkt.Recipient == RCPT_EP)&&(SetupPkt.EPNum != 0)) + { + ctrl_trf_session_owner = MUID_USB9; + /* Must do address calculation here */ + pDst.bRam = (uint8_t*)&ep0BoEven+(SetupPkt.EPNum*8)+(SetupPkt.EPDir*4)+4; + + if(SetupPkt.bRequest == SET_FEATURE) + { + *pDst.bRam = _BSTALL; + *pDst.bRam |= _USIE; + } + else + { + if(SetupPkt.EPDir == 1) // IN + *pDst.bRam = _UCPU|_DAT1; + else + { + *pDst.bRam = _DAT0|_DTSEN; + *pDst.bRam |= _USIE; + } + }//end if + }//end if +}//end USBStdFeatureReqHandler + + + +//This is a helper function for copying prepared BDT entry data from the global +//TempBDT buffer, into the real BDT EP0 OUT (even or odd) entry of the user's +//specified index. Once copied, this function also sets the UOWN bit in the +//real BDT entry. This helper function helps to reduce code space, by minimizing +//use of pointers in the rest of the code. This function is only intended to +//be used to arm EP0 OUT (even or odd). +void LoadBDTandSetUOWN(uint8_t BDTIndexToLoad) +{ + static volatile BDT* pBDTEntry; + + //Setup the byte count and destination address values. Since this function + //is only intended to be used for EP0 OUT even/odd, the size should always + //be the endpoint size (the host determines that actual size of packet sent), + //and the address should always be the respective even/odd USB packet buffer. + TempBDT.Cnt = EP0_BUFF_SIZE; + TempBDT.ADR = (uint8_t*)&EP0OutOddBuf[0]; + if(BDTIndexToLoad == EP0_OUT_EVEN_BDT_INDEX) + { + TempBDT.ADR = (uint8_t*)&EP0OutEvenBuf[0]; + pBDTEntry = (volatile BDT*)BDT_ADDR; + } + else + { + pBDTEntry = (volatile BDT*)(BDT_ADDR + 4); + } + + //Copy the global TempBDT contents into the user specified actual BDT location + *pBDTEntry = TempBDT; + + //Now set the UOWN bit to arm the endpoint + pBDTEntry->Stat.UOWN = 1; +} + + + +//Disable the USB module and then wait a long time (long enough for the host +//to correctly detect the USB detach event, process the plug and play event, +//and get itself back into a state ready for a new USB attach event. +void USBDisableWithLongDelay(void) +{ + UCONbits.SUSPND = 0; //Make sure not in suspend mode + UCON = 0x00; //Disable USB module + DelayRoutine(0xFFFF); //Wait long time for host to recognize detach event + usb_device_state = DETACHED_STATE; +} + + +//Helper function to execute some blocking delay. +void DelayRoutine(unsigned int DelayAmount) +{ + while(DelayAmount) + { + ClearWatchdog(); + DelayAmount--; + } +} + + + +//Helper function to reduce code size when built with C18. The ClrWdt() is an +//inline assembly macro, and on the C18 compiler, if you execute an inline asm +//instruction in a C function, it prevents the compiler from implementing +//optimizations to that particular function (since the compiler doesn't know what +//the user did in the inline asm). Therefore, inline asm is more efficient if +//implemented outside of large C functions, when using C18. +void ClearWatchdog(void) +{ + ClrWdt(); +} + + +/** EOF usb_device.c *************************************************************/ diff --git a/bootloader/src/minimal_usb_driver/usb_device.h b/bootloader/src/minimal_usb_driver/usb_device.h new file mode 100644 index 0000000..d2749c1 --- /dev/null +++ b/bootloader/src/minimal_usb_driver/usb_device.h @@ -0,0 +1,699 @@ +/******************************************************************************* +Copyright 2016 Microchip Technology Inc. (www.microchip.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +To request to license the code under the MLA license (www.microchip.com/mla_license), +please contact mla_licensing@microchip.com +*******************************************************************************/ + +#ifndef _USB_DEVICE_H +#define _USB_DEVICE_H + +/** I N C L U D E S **********************************************************/ +#include "usb.h" + + + +//Hardware abstraction. The USBIF and USBIE bits reside in different registers +//on different USB microcontrollers. +#if defined(__18F45K50) || defined(__18F25K50) || defined(__18F24K50) || defined(__18LF45K50) || defined(__18LF25K50) || defined(__18LF24K50) + #define USBIF_FLAG PIR3bits.USBIF + #define USBIE_BIT PIE3bits.USBIE +#else + #define USBIF_FLAG PIR2bits.USBIF + #define USBIE_BIT PIE2bits.USBIE +#endif + + +/** D E F I N I T I O N S ****************************************************/ +/****************************************************************************** + * Standard Request Codes + * USB 2.0 Spec Ref Table 9-4 + *****************************************************************************/ +#define GET_STATUS 0 +#define CLR_FEATURE 1 +#define SET_FEATURE 3 +#define SET_ADR 5 +#define GET_DSC 6 +#define SET_DSC 7 +#define GET_CFG 8 +#define SET_CFG 9 +#define GET_INTF 10 +#define SET_INTF 11 +#define SYNCH_FRAME 12 + +/* Standard Feature Selectors */ +#define DEVICE_REMOTE_WAKEUP 0x01 +#define ENDPOINT_HALT 0x00 + + +/* UCFG Initialization Parameters */ +#define _PPBM0 0x00 // Pingpong Buffer Mode 0 - ping pong bufferring disabled +#define _PPBM1 0x01 // Pingpong Buffer Mode 1 - ping pong on EP0 OUT only +#define _PPBM2 0x02 // Pingpong Buffer Mode 2 - ping pong on all endpoints +#define _LS 0x00 // Use Low-Speed USB Mode +#define _FS 0x04 // Use Full-Speed USB Mode +#define _TRINT 0x00 // Use internal transceiver +#define _TREXT 0x08 // Use external transceiver +#define _PUEN 0x10 // Use internal pull-up resistor +#define _OEMON 0x40 // Use SIE output indicator +#define _UTEYE 0x80 // Use Eye-Pattern test + +/* UEPn Initialization Parameters */ +#define EP_CTRL 0x06 // Cfg Control pipe for this ep +#define EP_OUT 0x0C // Cfg OUT only pipe for this ep +#define EP_IN 0x0A // Cfg IN only pipe for this ep +#define EP_OUT_IN 0x0E // Cfg both OUT & IN pipes for this ep +#define HSHK_EN 0x10 // Enable handshake packet + // Handshake should be disable for isoch + +/****************************************************************************** + * USB - PICmicro Endpoint Definitions + * PICmicro EP Address Format: X:EP3:EP2:EP1:EP0:DIR:PPBI:X + * This is used when checking the value read from USTAT + * + * NOTE: These definitions are not used in the descriptors. + * EP addresses used in the descriptors have different format and + * are defined in: usb_device.h + *****************************************************************************/ +#define OUT 0 +#define IN 1 + +#define PIC_EP_NUM_MASK 0b01111000 +#define PIC_EP_DIR_MASK 0b00000100 + +#define EP00_OUT ((0x00<<3)|(OUT<<2)) +#define EP00_IN ((0x00<<3)|(IN<<2)) +#define EP01_OUT ((0x01<<3)|(OUT<<2)) +#define EP01_IN ((0x01<<3)|(IN<<2)) +#define EP02_OUT ((0x02<<3)|(OUT<<2)) +#define EP02_IN ((0x02<<3)|(IN<<2)) +#define EP03_OUT ((0x03<<3)|(OUT<<2)) +#define EP03_IN ((0x03<<3)|(IN<<2)) +#define EP04_OUT ((0x04<<3)|(OUT<<2)) +#define EP04_IN ((0x04<<3)|(IN<<2)) +#define EP05_OUT ((0x05<<3)|(OUT<<2)) +#define EP05_IN ((0x05<<3)|(IN<<2)) +#define EP06_OUT ((0x06<<3)|(OUT<<2)) +#define EP06_IN ((0x06<<3)|(IN<<2)) +#define EP07_OUT ((0x07<<3)|(OUT<<2)) +#define EP07_IN ((0x07<<3)|(IN<<2)) +#define EP08_OUT ((0x08<<3)|(OUT<<2)) +#define EP08_IN ((0x08<<3)|(IN<<2)) +#define EP09_OUT ((0x09<<3)|(OUT<<2)) +#define EP09_IN ((0x09<<3)|(IN<<2)) +#define EP10_OUT ((0x0A<<3)|(OUT<<2)) +#define EP10_IN ((0x0A<<3)|(IN<<2)) +#define EP11_OUT ((0x0B<<3)|(OUT<<2)) +#define EP11_IN ((0x0B<<3)|(IN<<2)) +#define EP12_OUT ((0x0C<<3)|(OUT<<2)) +#define EP12_IN ((0x0C<<3)|(IN<<2)) +#define EP13_OUT ((0x0D<<3)|(OUT<<2)) +#define EP13_IN ((0x0D<<3)|(IN<<2)) +#define EP14_OUT ((0x0E<<3)|(OUT<<2)) +#define EP14_IN ((0x0E<<3)|(IN<<2)) +#define EP15_OUT ((0x0F<<3)|(OUT<<2)) +#define EP15_IN ((0x0F<<3)|(IN<<2)) + +#define EP0_OUT_EVEN_BDT_INDEX 0 +#define EP0_OUT_ODD_BDT_INDEX 1 + + +/* Buffer Descriptor Status Register Initialization Parameters */ +#define _BSTALL 0x04 //Buffer Stall enable +#define _DTSEN 0x08 //Data Toggle Synch enable +#define _INCDIS 0x10 //Address increment disable +#define _KEN 0x20 //SIE keeps buff descriptors enable +#define _DAT0 0x00 //DATA0 packet expected next +#define _DAT1 0x40 //DATA1 packet expected next +#define _DTSMASK 0x40 //DTS Mask +#define _USIE 0x80 //SIE owns buffer +#define _UCPU 0x00 //CPU owns buffer + +/* USB Device States - To be used with [byte usb_device_state] */ +#define DETACHED_STATE 0 +#define ATTACHED_STATE 1 +#define POWERED_STATE 2 +#define DEFAULT_STATE 3 +#define ADR_PENDING_STATE 4 +#define ADDRESS_STATE 5 +#define CONFIGURED_STATE 6 + +/* Memory Types for Control Transfer - used in USB_DEVICE_STATUS */ +#define _RAM 0 +#define _ROM 1 + +/* Descriptor Types */ +#define DSC_DEV 0x01 +#define DSC_CFG 0x02 +#define DSC_STR 0x03 +#define DSC_INTF 0x04 +#define DSC_EP 0x05 + +/****************************************************************************** + * USB Endpoint Definitions + * USB Standard EP Address Format: DIR:X:X:X:EP3:EP2:EP1:EP0 + * This is used in the descriptors. See usb_descriptors.c + * + * NOTE: Do not use these values for checking against USTAT. + * To check against USTAT, use values defined in "usb_device.h" + *****************************************************************************/ +#define _EP01_OUT 0x01 +#define _EP01_IN 0x81 +#define _EP02_OUT 0x02 +#define _EP02_IN 0x82 +#define _EP03_OUT 0x03 +#define _EP03_IN 0x83 +#define _EP04_OUT 0x04 +#define _EP04_IN 0x84 +#define _EP05_OUT 0x05 +#define _EP05_IN 0x85 +#define _EP06_OUT 0x06 +#define _EP06_IN 0x86 +#define _EP07_OUT 0x07 +#define _EP07_IN 0x87 +#define _EP08_OUT 0x08 +#define _EP08_IN 0x88 +#define _EP09_OUT 0x09 +#define _EP09_IN 0x89 +#define _EP10_OUT 0x0A +#define _EP10_IN 0x8A +#define _EP11_OUT 0x0B +#define _EP11_IN 0x8B +#define _EP12_OUT 0x0C +#define _EP12_IN 0x8C +#define _EP13_OUT 0x0D +#define _EP13_IN 0x8D +#define _EP14_OUT 0x0E +#define _EP14_IN 0x8E +#define _EP15_OUT 0x0F +#define _EP15_IN 0x8F + +/* Configuration Attributes */ +#define _DEFAULT 0x01<<7 //Default Value (Bit 7 is set) +#define _SELF 0x01<<6 //Self-powered (Supports if set) +#define _RWU 0x01<<5 //Remote Wakeup (Supports if set) + +/* Endpoint Transfer Type */ +#define _CTRL 0x00 //Control Transfer +#define _ISO 0x01 //Isochronous Transfer +#define _BULK 0x02 //Bulk Transfer +#define _INT 0x03 //Interrupt Transfer + +/* Isochronous Endpoint Synchronization Type */ +#define _NS 0x00<<2 //No Synchronization +#define _AS 0x01<<2 //Asynchronous +#define _AD 0x02<<2 //Adaptive +#define _SY 0x03<<2 //Synchronous + +/* Isochronous Endpoint Usage Type */ +#define _DE 0x00<<4 //Data endpoint +#define _FE 0x01<<4 //Feedback endpoint +#define _IE 0x02<<4 //Implicit feedback Data endpoint + + + + +/** T Y P E S ****************************************************************/ +typedef union _USB_DEVICE_STATUS +{ + uint8_t _byte; + struct + { + unsigned RemoteWakeup:1;// [0]Disabled [1]Enabled: See usb_device.c,usb9.c + unsigned ctrl_trf_mem:1;// [0]RAM [1]ROM + }; +} USB_DEVICE_STATUS; + +typedef union _BD_STAT +{ + uint8_t _byte; + struct{ + unsigned BC8:1; + unsigned BC9:1; + unsigned BSTALL:1; //Buffer Stall Enable + unsigned DTSEN:1; //Data Toggle Synch Enable + unsigned INCDIS:1; //Address Increment Disable + unsigned KEN:1; //BD Keep Enable + unsigned DTS:1; //Data Toggle Synch Value + unsigned UOWN:1; //USB Ownership + }; + struct{ + unsigned :2; + unsigned PID0:1; + unsigned PID1:1; + unsigned PID2:1; + unsigned PID3:1; + unsigned :2; + }; + struct{ + unsigned :2; + unsigned PID:4; //Packet Identifier + unsigned :2; + }; +} BD_STAT; //Buffer Descriptor Status Register + +typedef union _BDT +{ + struct + { + BD_STAT Stat; + uint8_t Cnt; + uint8_t ADRL; //Buffer Address Low + uint8_t ADRH; //Buffer Address High + }; + struct + { + unsigned :8; + unsigned :8; + uint8_t* ADR; //Buffer Address + }; +} BDT; //Buffer Descriptor Table + + +/****************************************************************************** + * CTRL_TRF_SETUP: + * + * Every setup packet has 8 bytes. + * However, the buffer size has to equal the EP0_BUFF_SIZE value specified + * in usb_config.h + * The value of EP0_BUFF_SIZE can be 8, 16, 32, or 64. + * + * First 8 bytes are defined to be directly addressable to improve speed + * and reduce code size. + * Bytes beyond the 8th byte have to be accessed using indirect addressing. + *****************************************************************************/ +typedef union _CTRL_TRF_SETUP +{ + /** Array for indirect addressing ****************************************/ + struct + { + uint8_t _byte[EP0_BUFF_SIZE]; + }; + + /** Standard Device Requests *********************************************/ + struct + { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + }; + struct + { + unsigned :8; + unsigned :8; + WORD_VAL W_Value; + WORD_VAL W_Index; + WORD_VAL W_Length; + }; + struct + { + unsigned Recipient:5; //Device,Interface,Endpoint,Other + unsigned RequestType:2; //Standard,Class,Vendor,Reserved + unsigned DataDir:1; //Host-to-device,Device-to-host + unsigned :8; + uint8_t bFeature; //DEVICE_REMOTE_WAKEUP,ENDPOINT_HALT + unsigned :8; + unsigned :8; + unsigned :8; + unsigned :8; + unsigned :8; + }; + struct + { + unsigned :8; + unsigned :8; + uint8_t bDscIndex; //For Configuration and String DSC Only + uint8_t bDscType; //Device,Configuration,String + uint16_t wLangID; //Language ID + unsigned :8; + unsigned :8; + }; + struct + { + unsigned :8; + unsigned :8; + uint8_t bDevADR; //Device Address 0-127 + uint8_t bDevADRH; //Must equal zero + unsigned :8; + unsigned :8; + unsigned :8; + unsigned :8; + }; + struct + { + unsigned :8; + unsigned :8; + uint8_t bCfgValue; //Configuration Value 0-255 + uint8_t bCfgRSD; //Must equal zero (Reserved) + unsigned :8; + unsigned :8; + unsigned :8; + unsigned :8; + }; + struct + { + unsigned :8; + unsigned :8; + uint8_t bAltID; //Alternate Setting Value 0-255 + uint8_t bAltID_H; //Must equal zero + uint8_t bIntfID; //Interface Number Value 0-255 + uint8_t bIntfID_H; //Must equal zero + unsigned :8; + unsigned :8; + }; + struct + { + unsigned :8; + unsigned :8; + unsigned :8; + unsigned :8; + uint8_t bEPID; //Endpoint ID (Number & Direction) + uint8_t bEPID_H; //Must equal zero + unsigned :8; + unsigned :8; + }; + struct + { + unsigned :8; + unsigned :8; + unsigned :8; + unsigned :8; + unsigned EPNum:4; //Endpoint Number 0-15 + unsigned :3; + unsigned EPDir:1; //Endpoint Direction: 0-OUT, 1-IN + unsigned :8; + unsigned :8; + unsigned :8; + }; + /** End: Standard Device Requests ****************************************/ + +} CTRL_TRF_SETUP; + +/****************************************************************************** + * CTRL_TRF_DATA: + * + * Buffer size has to equal the EP0_BUFF_SIZE value specified + * in usb_config.h + * The value of EP0_BUFF_SIZE can be 8, 16, 32, or 64. + * + * First 8 bytes are defined to be directly addressable to improve speed + * and reduce code size. + * Bytes beyond the 8th byte have to be accessed using indirect addressing. + *****************************************************************************/ +typedef union _CTRL_TRF_DATA +{ + /** Array for indirect addressing ****************************************/ + struct + { + uint8_t _byte[EP0_BUFF_SIZE]; + }; + + /** First 8-byte direct addressing ***************************************/ + struct + { + uint8_t _byte0; + uint8_t _byte1; + uint8_t _byte2; + uint8_t _byte3; + uint8_t _byte4; + uint8_t _byte5; + uint8_t _byte6; + uint8_t _byte7; + }; + struct + { + uint16_t _word0; + uint16_t _word1; + uint16_t _word2; + uint16_t _word3; + }; + +} CTRL_TRF_DATA; + + + + +/****************************************************************************** + * USB Device Descriptor Structure + *****************************************************************************/ +typedef struct _USB_DEV_DSC +{ + uint8_t bLength; uint8_t bDscType; uint16_t bcdUSB; + uint8_t bDevCls; uint8_t bDevSubCls; uint8_t bDevProtocol; + uint8_t bMaxPktSize0; uint16_t idVendor; uint16_t idProduct; + uint16_t bcdDevice; uint8_t iMFR; uint8_t iProduct; + uint8_t iSerialNum; uint8_t bNumCfg; +} USB_DEV_DSC; + +/****************************************************************************** + * USB Configuration Descriptor Structure + *****************************************************************************/ +typedef struct _USB_CFG_DSC +{ + uint8_t bLength; uint8_t bDscType; uint16_t wTotalLength; + uint8_t bNumIntf; uint8_t bCfgValue; uint8_t iCfg; + uint8_t bmAttributes; uint8_t bMaxPower; +} USB_CFG_DSC; + +/****************************************************************************** + * USB Interface Descriptor Structure + *****************************************************************************/ +typedef struct _USB_INTF_DSC +{ + uint8_t bLength; uint8_t bDscType; uint8_t bIntfNum; + uint8_t bAltSetting; uint8_t bNumEPs; uint8_t bIntfCls; + uint8_t bIntfSubCls; uint8_t bIntfProtocol; uint8_t iIntf; +} USB_INTF_DSC; + +/****************************************************************************** + * USB Endpoint Descriptor Structure + *****************************************************************************/ +typedef struct _USB_EP_DSC +{ + uint8_t bLength; uint8_t bDscType; uint8_t bEPAdr; + uint8_t bmAttributes; uint16_t wMaxPktSize; uint8_t bInterval; +} USB_EP_DSC; + + + + +/****************************************************************************** + * Macro: void mInitializeUSBDriver(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: Configures the USB module, definition of UCFG_VAL can be + * found in usb_config.h + * + * 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. + * + * Note: None + *****************************************************************************/ +#define mInitializeUSBDriver() {UCFG = UCFG_VAL; \ + usb_device_state = DETACHED_STATE; \ + USBProtocolResetHandler();} + +/****************************************************************************** + * Macro: void mDisableEP1to15(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This macro disables all endpoints except EP0. + * This macro should be called when the host sends a RESET + * signal or a SET_CONFIGURATION request. + * + * Note: None + *****************************************************************************/ +//#if defined(__18F14K50) || defined(__18F13K50) || defined(__18LF14K50) || defined(__18LF13K50) +// #define mDisableEP1to15() ClearArray((uint8_t*)&UEP1,7); +//#else +// #define mDisableEP1to15() ClearArray((uint8_t*)&UEP1,15); +//#endif + +//Using below instead to save code space. Dedicated bootloader project, +//will never use UEP3+, therefore no need to really mess with those registers +#define mDisableEP1to7() UEP1=0x00;UEP2=0x00;UEP3=0x00;\ + UEP4=0x00;UEP5=0x00;UEP6=0x00;UEP7=0x00; +// UEP8=0x00;UEP9=0x00;UEP10=0x00;UEP11=0x00;\ +// UEP12=0x00;UEP13=0x00;UEP14=0x00;UEP15=0x00; + +/****************************************************************************** + * Macro: void mUSBBufferReady(buffer_dsc) + * + * PreCondition: IN Endpoint: Buffer is loaded and ready to be sent. + * OUT Endpoint: Buffer is free to be written to by SIE. + * + * Input: byte 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 + * usb_config.h (#define HID_BD_OUT ep1Bo) + * + * Output: None + * + * Side Effects: None + * + * Overview: 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. + * + * Note: None + *****************************************************************************/ +#define mUSBBufferReady(buffer_dsc) \ +{ \ + buffer_dsc.Stat._byte &= _DTSMASK; /* Save only DTS bit */ \ + buffer_dsc.Stat.DTS = !buffer_dsc.Stat.DTS; /* Toggle DTS bit */ \ + buffer_dsc.Stat._byte |= _DTSEN; /* Configure other settings */ \ + buffer_dsc.Stat._byte |= _USIE; /* Turn ownership to SIE */ \ +} + + + + +/** D E F I N I T I O N S ****************************************************/ +/* + * MUID = Microchip USB Class ID + * Used to identify which of the USB classes owns the current + * session of control transfer over EP0 + */ +#define MUID_NULL 0 +#define MUID_USB9 1 +#define MUID_HID 2 +#define MUID_CDC 3 + + +/* Control Transfer States */ +#define WAIT_SETUP 0 +#define CTRL_TRF_TX 1 +#define CTRL_TRF_RX 2 + +/* Short Packet States - Used by Control Transfer Read - CTRL_TRF_TX */ +#define SHORT_PKT_NOT_SENT 0 +#define SHORT_PKT_PENDING 1 +#define SHORT_PKT_SENT 2 + +/* USB PID: Token Types - See chapter 8 in the USB specification */ +#define SETUP_TOKEN 0b00001101 +#define OUT_TOKEN 0b00000001 +#define IN_TOKEN 0b00001001 + +/* bmRequestType Definitions */ +#define HOST_TO_DEV 0 +#define DEV_TO_HOST 1 + +#define STANDARD 0x00 +#define CLASS 0x01 +#define VENDOR 0x02 + +#define RCPT_DEV 0 +#define RCPT_INTF 1 +#define RCPT_EP 2 +#define RCPT_OTH 3 + + + +/** E X T E R N S ************************************************************/ +extern uint8_t ctrl_trf_session_owner; +extern POINTER pSrc; +extern POINTER pDst; +extern WORD_VAL wCount; +extern uint8_t usb_device_state; +extern USB_DEVICE_STATUS usb_stat; +extern uint8_t usb_active_cfg; +extern uint8_t usb_alt_intf[MAX_NUM_INT]; + +extern volatile BDT ep0Bo; //Endpoint #0 BD Out +extern volatile BDT ep0Bi; //Endpoint #0 BD In +extern volatile BDT ep1Bo; //Endpoint #1 BD Out +extern volatile BDT ep1Bi; //Endpoint #1 BD In +extern volatile BDT ep2Bo; //Endpoint #2 BD Out +extern volatile BDT ep2Bi; //Endpoint #2 BD In +extern volatile BDT ep3Bo; //Endpoint #3 BD Out +extern volatile BDT ep3Bi; //Endpoint #3 BD In +extern volatile BDT ep4Bo; //Endpoint #4 BD Out +extern volatile BDT ep4Bi; //Endpoint #4 BD In +extern volatile BDT ep5Bo; //Endpoint #5 BD Out +extern volatile BDT ep5Bi; //Endpoint #5 BD In +extern volatile BDT ep6Bo; //Endpoint #6 BD Out +extern volatile BDT ep6Bi; //Endpoint #6 BD In +extern volatile BDT ep7Bo; //Endpoint #7 BD Out +extern volatile BDT ep7Bi; //Endpoint #7 BD In +extern volatile BDT ep8Bo; //Endpoint #8 BD Out +extern volatile BDT ep8Bi; //Endpoint #8 BD In +extern volatile BDT ep9Bo; //Endpoint #9 BD Out +extern volatile BDT ep9Bi; //Endpoint #9 BD In +extern volatile BDT ep10Bo; //Endpoint #10 BD Out +extern volatile BDT ep10Bi; //Endpoint #10 BD In +extern volatile BDT ep11Bo; //Endpoint #11 BD Out +extern volatile BDT ep11Bi; //Endpoint #11 BD In +extern volatile BDT ep12Bo; //Endpoint #12 BD Out +extern volatile BDT ep12Bi; //Endpoint #12 BD In +extern volatile BDT ep13Bo; //Endpoint #13 BD Out +extern volatile BDT ep13Bi; //Endpoint #13 BD In +extern volatile BDT ep14Bo; //Endpoint #14 BD Out +extern volatile BDT ep14Bi; //Endpoint #14 BD In +extern volatile BDT ep15Bo; //Endpoint #15 BD Out +extern volatile BDT ep15Bi; //Endpoint #15 BD In + +extern CTRL_TRF_SETUP SetupPkt; +volatile extern CTRL_TRF_DATA CtrlTrfData; + + +#if defined(USB_USE_HID) +extern volatile unsigned char hid_report_out[HID_INT_OUT_EP_SIZE]; +extern volatile unsigned char hid_report_in[HID_INT_IN_EP_SIZE]; +#endif + + +extern ROM USB_DEV_DSC device_dsc; +extern ROM uint8_t CFG01[CONFIG_DESC_TOTAL_LEN]; +extern ROM const unsigned char *ROM USB_CD_Ptr[]; +extern ROM unsigned char* ROM USB_SD_Ptr[]; + + + + +/** P U B L I C P R O T O T Y P E S *****************************************/ +void USBDeviceInit(void); +void USBCheckBusStatus(void); +void USBSoftAttach(void); +void USBSoftDetach(void); +void USBDeviceTasks(void); +void USBDisableWithLongDelay(void); +void DelayRoutine(unsigned int DelayAmount); +void ClearWatchdog(void); +#define USBGetDeviceState() usb_device_state +#define USBIsDeviceSuspended() UCONbits.SUSPND + + +#endif //_USB_DEVICE_H diff --git a/bootloader/src/minimal_usb_driver/usb_device_hid.c b/bootloader/src/minimal_usb_driver/usb_device_hid.c new file mode 100644 index 0000000..39524c4 --- /dev/null +++ b/bootloader/src/minimal_usb_driver/usb_device_hid.c @@ -0,0 +1,302 @@ +/******************************************************************************* +Copyright 2016 Microchip Technology Inc. (www.microchip.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +To request to license the code under the MLA license (www.microchip.com/mla_license), +please contact mla_licensing@microchip.com +*******************************************************************************/ + +/** I N C L U D E S **********************************************************/ +#include "usb.h" + +#ifdef USB_USE_HID + +/** V A R I A B L E S ********************************************************/ +#ifndef __XC8__ +#pragma udata +#endif +uint8_t idle_rate; +uint8_t active_protocol; // [0] Boot Protocol [1] Report Protocol +uint8_t hid_rpt_rx_len; + +/** P R I V A T E P R O T O T Y P E S ***************************************/ +void HIDGetReportHandler(void); +void HIDSetReportHandler(void); + +/** D E C L A R A T I O N S **************************************************/ +#ifndef __XC8__ +#pragma code +#endif + +/** C L A S S S P E C I F I C R E Q ****************************************/ +/****************************************************************************** + * Function: void USBCheckHIDRequest(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This routine checks the setup data packet to see if it + * knows how to handle it + * + * Note: None + *****************************************************************************/ +void USBCheckHIDRequest(void) +{ + if(SetupPkt.Recipient != RCPT_INTF) return; + if(SetupPkt.bIntfID != HID_INTF_ID) return; + + /* + * 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) + { + switch(SetupPkt.bDscType) + { + case DSC_HID: + ctrl_trf_session_owner = MUID_HID; + pSrc.bRom = &CFG01[18]; //18 is a magic number (offset from start of configuration descriptor, to the start of the HID descriptor) + wCount.Val = sizeof(USB_HID_DSC); + break; + case DSC_RPT: + ctrl_trf_session_owner = MUID_HID; + mUSBGetHIDRptDscAdr(pSrc.bRom); // See usb_config.h + mUSBGetHIDRptDscSize(wCount.Val); // See usb_config.h + break; + case DSC_PHY: + // ctrl_trf_session_owner = MUID_HID; + break; + }//end switch(SetupPkt.bDscType) + usb_stat.ctrl_trf_mem = _ROM; + }//end if(SetupPkt.bRequest == GET_DSC) + + if(SetupPkt.RequestType != CLASS) return; + switch(SetupPkt.bRequest) + { + case GET_REPORT: + HIDGetReportHandler(); + break; + case SET_REPORT: + HIDSetReportHandler(); + break; + case GET_IDLE: + ctrl_trf_session_owner = MUID_HID; + pSrc.bRam = (uint8_t*)&idle_rate; // Set source + usb_stat.ctrl_trf_mem = _RAM; // Set memory type + wCount.v[0] = 1; // Set data count + break; + case SET_IDLE: + ctrl_trf_session_owner = MUID_HID; + //idle_rate = MSB(SetupPkt.W_Value); + idle_rate = SetupPkt.W_Value.v[1]; + break; + case GET_PROTOCOL: + ctrl_trf_session_owner = MUID_HID; + pSrc.bRam = (uint8_t*)&active_protocol;// Set source + usb_stat.ctrl_trf_mem = _RAM; // Set memory type + wCount.v[0] = 1; // Set data count + break; + case SET_PROTOCOL: + ctrl_trf_session_owner = MUID_HID; + //active_protocol = LSB(SetupPkt.W_Value); + active_protocol = SetupPkt.W_Value.v[0]; + break; + }//end switch(SetupPkt.bRequest) + +}//end USBCheckHIDRequest + +void HIDGetReportHandler(void) +{ + // ctrl_trf_session_owner = MUID_HID; +}//end HIDGetReportHandler + +void HIDSetReportHandler(void) +{ + // ctrl_trf_session_owner = MUID_HID; + // pDst.bRam = (byte*)&hid_report_out; +}//end HIDSetReportHandler + +/** U S E R A P I ***********************************************************/ + +/****************************************************************************** + * Function: void HIDInitEP(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: 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. + * See USBStdSetCfgHandler() in usb9.c for examples. + * + * Note: None + *****************************************************************************/ +void HIDInitEP(void) +{ + hid_rpt_rx_len =0; + + HID_UEP = EP_OUT_IN|HSHK_EN; // Enable 2 data pipes + + //Arm the OUT interrupt endpoint so the host can send the first packet of data. + HID_BD_OUT.Cnt = sizeof(hid_report_out); // Set buffer size + HID_BD_OUT.ADR = (uint8_t*)&hid_report_out; // Set buffer address + HID_BD_OUT.Stat._byte = _DAT0|_DTSEN; // Set status + HID_BD_OUT.Stat._byte |= _USIE; + + /* + * 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 = (uint8_t*)&hid_report_in; // Set buffer address + HID_BD_IN.Stat._byte = _UCPU|_DAT1; // Set status + +}//end HIDInitEP + +/****************************************************************************** + * Function: void HIDTxReport(char *buffer, byte len) + * + * PreCondition: 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 + * + * Output: None + * + * Side Effects: None + * + * Overview: Use this macro to transfer data located in data memory. + * + * Remember: mHIDTxIsBusy() must return false before user + * can call this function. + * Unexpected behavior will occur if this function is called + * when mHIDTxIsBusy() == 0 + * + * Typical Usage: + * if(!mHIDTxIsBusy()) + * HIDTxReport(buffer, 3); + * + * Note: None + *****************************************************************************/ +void HIDTxReport(char *buffer, uint8_t len) +{ + uint8_t i; + + /* + * Value of len should be equal to or smaller than HID_INT_IN_EP_SIZE. + * This check forces the value of len to meet the precondition. + */ + if(len > HID_INT_IN_EP_SIZE) + len = HID_INT_IN_EP_SIZE; + + /* + * Copy data from user's buffer to a USB module accessible RAM packet buffer + */ + for (i = 0; i < len; i++) + hid_report_in[i] = buffer[i]; + + HID_BD_IN.Cnt = len; + mUSBBufferReady(HID_BD_IN); + +}//end HIDTxReport + +/****************************************************************************** + * Function: byte HIDRxReport(char *buffer, byte len) + * + * PreCondition: 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. + * + * Side Effects: 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(). + * + * Overview: 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. + * + * Note: 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. + *****************************************************************************/ +uint8_t HIDRxReport(char *buffer, uint8_t len) +{ + hid_rpt_rx_len = 0; + + if(!mHIDRxIsBusy()) + { + /* + * Adjust the expected number of bytes to equal + * the actual number of bytes received. + */ + if(len > HID_BD_OUT.Cnt) + len = HID_BD_OUT.Cnt; + + /* + * Copy data from dual-ram buffer to user's buffer + */ + for(hid_rpt_rx_len = 0; hid_rpt_rx_len < len; hid_rpt_rx_len++) + buffer[hid_rpt_rx_len] = hid_report_out[hid_rpt_rx_len]; + + /* + * Prepare dual-ram buffer for next OUT transaction + */ + HID_BD_OUT.Cnt = sizeof(hid_report_out); + mUSBBufferReady(HID_BD_OUT); + }//end if + + return hid_rpt_rx_len; + +}//end HIDRxReport + +#endif //def USB_USE_HID + +/** EOF hid.c ***************************************************************/ diff --git a/bootloader/src/minimal_usb_driver/usb_device_hid.h b/bootloader/src/minimal_usb_driver/usb_device_hid.h new file mode 100644 index 0000000..12729d7 --- /dev/null +++ b/bootloader/src/minimal_usb_driver/usb_device_hid.h @@ -0,0 +1,163 @@ +/******************************************************************************* +Copyright 2016 Microchip Technology Inc. (www.microchip.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +To request to license the code under the MLA license (www.microchip.com/mla_license), +please contact mla_licensing@microchip.com +*******************************************************************************/ +#ifndef HID_H +#define HID_H + +/** I N C L U D E S **********************************************************/ +#include "typedefs.h" + +/** D E F I N I T I O N S ****************************************************/ + +/* Class-Specific Requests */ +#define GET_REPORT 0x01 +#define GET_IDLE 0x02 +#define GET_PROTOCOL 0x03 +#define SET_REPORT 0x09 +#define SET_IDLE 0x0A +#define SET_PROTOCOL 0x0B + +/* Class Descriptor Types */ +#define DSC_HID 0x21 +#define DSC_RPT 0x22 +#define DSC_PHY 0x23 + +/* Protocol Selection */ +#define BOOT_PROTOCOL 0x00 +#define RPT_PROTOCOL 0x01 + + +/* HID Interface Class Code */ +#define HID_INTF 0x03 + +/* HID Interface Class SubClass Codes */ +#define BOOT_INTF_SUBCLASS 0x01 + +/* HID Interface Class Protocol Codes */ +#define HID_PROTOCOL_NONE 0x00 +#define HID_PROTOCOL_KEYBOAD 0x01 +#define HID_PROTOCOL_MOUSE 0x02 + +/****************************************************************************** + * Macro: (bit) mHIDRxIsBusy(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This macro is used to check if HID OUT endpoint is + * busy (owned by SIE) or not. + * Typical Usage: if(mHIDRxIsBusy()) + * + * Note: None + *****************************************************************************/ +#define mHIDRxIsBusy() HID_BD_OUT.Stat.UOWN + +/****************************************************************************** + * Macro: (bit) mHIDTxIsBusy(void) + * + * PreCondition: None + * + * Input: None + * + * Output: None + * + * Side Effects: None + * + * Overview: This macro is used to check if HID IN endpoint is + * busy (owned by SIE) or not. + * Typical Usage: if(mHIDTxIsBusy()) + * + * Note: None + *****************************************************************************/ +#define mHIDTxIsBusy() HID_BD_IN.Stat.UOWN + +/****************************************************************************** + * Macro: uint8_t mHIDGetRptRxLength(void) + * + * PreCondition: None + * + * Input: None + * + * Output: mHIDGetRptRxLength returns hid_rpt_rx_len + * + * Side Effects: None + * + * Overview: mHIDGetRptRxLength is used to retrieve the number of bytes + * copied to user's buffer by the most recent call to + * HIDRxReport function. + * + * Note: None + *****************************************************************************/ +#define mHIDGetRptRxLength() hid_rpt_rx_len + + +/* HID macros */ +#define mUSBGetHIDDscAdr(ptr) \ +{ \ + if(usb_active_cfg == 1) \ + ptr = (ROM uint8_t*)&cfg01.hid_i00a00; \ +} + +#define mUSBGetHIDRptDscAdr(ptr) \ +{ \ + if(usb_active_cfg == 1) \ + ptr = (ROM uint8_t*)&hid_rpt01; \ +} + +#define mUSBGetHIDRptDscSize(count) \ +{ \ + if(usb_active_cfg == 1) \ + count = sizeof(hid_rpt01); \ +} + + + +/** S T R U C T U R E S ******************************************************/ +typedef struct _USB_HID_DSC_HEADER +{ + uint8_t bDscType; + uint16_t wDscLength; +} USB_HID_DSC_HEADER; + +typedef struct _USB_HID_DSC +{ + uint8_t bLength; uint8_t bDscType; uint16_t bcdHID; + uint8_t bCountryCode; uint8_t bNumDsc; + USB_HID_DSC_HEADER hid_dsc_header[HID_NUM_OF_DSC]; + /* + * HID_NUM_OF_DSC is defined in autofiles\usb_config.h + */ +} USB_HID_DSC; + +/** E X T E R N S ************************************************************/ +extern uint8_t hid_rpt_rx_len; +extern ROM uint8_t hid_rpt01[HID_RPT01_SIZE]; + + +/** P U B L I C P R O T O T Y P E S *****************************************/ +void HIDInitEP(void); +void USBCheckHIDRequest(void); +void HIDTxReport(char *buffer, uint8_t len); +uint8_t HIDRxReport(char *buffer, uint8_t len); + +#endif //HID_H diff --git a/bootloader/src/typedefs.h b/bootloader/src/typedefs.h new file mode 100644 index 0000000..bcdba27 --- /dev/null +++ b/bootloader/src/typedefs.h @@ -0,0 +1,425 @@ +/******************************************************************************* +Copyright 2016 Microchip Technology Inc. (www.microchip.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +To request to license the code under the MLA license (www.microchip.com/mla_license), +please contact mla_licensing@microchip.com +*******************************************************************************/ + +#ifndef __CUSTOMIZED_TYPE_DEFS_H_ +#define __CUSTOMIZED_TYPE_DEFS_H_ + +#include +#define ROM const +#define rom +#include +#include +#ifndef Nop() +#define Nop() {asm("NOP");} +#endif +#ifndef ClrWdt() +#define ClrWdt() {asm("CLRWDT");} +#endif +#ifndef Reset() +#define Reset() {asm("RESET");} +#endif +#ifndef Sleep() +#define Sleep() {asm("SLEEP");} +#endif + +#define __EXTENSION + +#if !defined(__PACKED) + #define __PACKED +#endif + +/* get compiler defined type definitions (NULL, size_t, etc) */ +#include +#include +#include +#include + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define PUBLIC /* Function attributes */ +#define PROTECTED +#define PRIVATE static + +/* INT is processor specific in length may vary in size */ +typedef signed int INT; +typedef signed char INT8; +typedef signed short int INT16; +typedef signed long int INT32; + +/* UINT is processor specific in length may vary in size */ +typedef unsigned int UINT; +typedef unsigned char UINT8; +typedef unsigned short int UINT16; +typedef unsigned long int UINT32; /* other name for 32-bit integer */ + + +typedef union +{ + UINT8 Val; + struct + { + __EXTENSION UINT8 b0:1; + __EXTENSION UINT8 b1:1; + __EXTENSION UINT8 b2:1; + __EXTENSION UINT8 b3:1; + __EXTENSION UINT8 b4:1; + __EXTENSION UINT8 b5:1; + __EXTENSION UINT8 b6:1; + __EXTENSION UINT8 b7:1; + } bits; +} UINT8_VAL, UINT8_BITS; + +typedef union +{ + UINT16 Val; + UINT8 v[2] __PACKED; + struct __PACKED + { + UINT8 LB; + UINT8 HB; + } byte; + struct __PACKED + { + __EXTENSION UINT8 b0:1; + __EXTENSION UINT8 b1:1; + __EXTENSION UINT8 b2:1; + __EXTENSION UINT8 b3:1; + __EXTENSION UINT8 b4:1; + __EXTENSION UINT8 b5:1; + __EXTENSION UINT8 b6:1; + __EXTENSION UINT8 b7:1; + __EXTENSION UINT8 b8:1; + __EXTENSION UINT8 b9:1; + __EXTENSION UINT8 b10:1; + __EXTENSION UINT8 b11:1; + __EXTENSION UINT8 b12:1; + __EXTENSION UINT8 b13:1; + __EXTENSION UINT8 b14:1; + __EXTENSION UINT8 b15:1; + } bits; +} UINT16_VAL, UINT16_BITS; + +typedef union +{ + UINT32 Val; + UINT16 w[2] __PACKED; + UINT8 v[4] __PACKED; + struct __PACKED + { + UINT16 LW; + UINT16 HW; + } word; + struct __PACKED + { + UINT8 LB; + UINT8 HB; + UINT8 UB; + UINT8 MB; + } byte; + struct __PACKED + { + UINT16_VAL low; + UINT16_VAL high; + }wordUnion; + struct __PACKED + { + __EXTENSION UINT8 b0:1; + __EXTENSION UINT8 b1:1; + __EXTENSION UINT8 b2:1; + __EXTENSION UINT8 b3:1; + __EXTENSION UINT8 b4:1; + __EXTENSION UINT8 b5:1; + __EXTENSION UINT8 b6:1; + __EXTENSION UINT8 b7:1; + __EXTENSION UINT8 b8:1; + __EXTENSION UINT8 b9:1; + __EXTENSION UINT8 b10:1; + __EXTENSION UINT8 b11:1; + __EXTENSION UINT8 b12:1; + __EXTENSION UINT8 b13:1; + __EXTENSION UINT8 b14:1; + __EXTENSION UINT8 b15:1; + __EXTENSION UINT8 b16:1; + __EXTENSION UINT8 b17:1; + __EXTENSION UINT8 b18:1; + __EXTENSION UINT8 b19:1; + __EXTENSION UINT8 b20:1; + __EXTENSION UINT8 b21:1; + __EXTENSION UINT8 b22:1; + __EXTENSION UINT8 b23:1; + __EXTENSION UINT8 b24:1; + __EXTENSION UINT8 b25:1; + __EXTENSION UINT8 b26:1; + __EXTENSION UINT8 b27:1; + __EXTENSION UINT8 b28:1; + __EXTENSION UINT8 b29:1; + __EXTENSION UINT8 b30:1; + __EXTENSION UINT8 b31:1; + } bits; +} UINT32_VAL; + +/***********************************************************************************/ + +/* Alternate definitions */ +typedef void VOID; + +typedef char CHAR8; +typedef unsigned char UCHAR8; + +typedef unsigned char BYTE; /* 8-bit unsigned */ +typedef unsigned short int WORD; /* 16-bit unsigned */ +typedef unsigned long DWORD; /* 32-bit unsigned */ +//typedef unsigned long long QWORD; /* 64-bit unsigned */ +typedef signed char CHAR; /* 8-bit signed */ +typedef signed short int SHORT; /* 16-bit signed */ +typedef signed long LONG; /* 32-bit signed */ +//typedef signed long long LONGLONG; /* 64-bit signed */ +typedef union +{ + BYTE Val; + struct __PACKED + { + __EXTENSION BYTE b0:1; + __EXTENSION BYTE b1:1; + __EXTENSION BYTE b2:1; + __EXTENSION BYTE b3:1; + __EXTENSION BYTE b4:1; + __EXTENSION BYTE b5:1; + __EXTENSION BYTE b6:1; + __EXTENSION BYTE b7:1; + } bits; +} BYTE_VAL, BYTE_BITS; + +typedef union +{ + WORD Val; + BYTE v[2] __PACKED; + struct __PACKED + { + BYTE LB; + BYTE HB; + } byte; + struct __PACKED + { + __EXTENSION BYTE b0:1; + __EXTENSION BYTE b1:1; + __EXTENSION BYTE b2:1; + __EXTENSION BYTE b3:1; + __EXTENSION BYTE b4:1; + __EXTENSION BYTE b5:1; + __EXTENSION BYTE b6:1; + __EXTENSION BYTE b7:1; + __EXTENSION BYTE b8:1; + __EXTENSION BYTE b9:1; + __EXTENSION BYTE b10:1; + __EXTENSION BYTE b11:1; + __EXTENSION BYTE b12:1; + __EXTENSION BYTE b13:1; + __EXTENSION BYTE b14:1; + __EXTENSION BYTE b15:1; + } bits; +} WORD_VAL, WORD_BITS; + +typedef union +{ + DWORD Val; + WORD w[2] __PACKED; + BYTE v[4] __PACKED; + struct __PACKED + { + WORD LW; + WORD HW; + } word; + struct __PACKED + { + BYTE LB; + BYTE HB; + BYTE UB; + BYTE MB; + } byte; + struct __PACKED + { + WORD_VAL low; + WORD_VAL high; + }wordUnion; + struct __PACKED + { + __EXTENSION BYTE b0:1; + __EXTENSION BYTE b1:1; + __EXTENSION BYTE b2:1; + __EXTENSION BYTE b3:1; + __EXTENSION BYTE b4:1; + __EXTENSION BYTE b5:1; + __EXTENSION BYTE b6:1; + __EXTENSION BYTE b7:1; + __EXTENSION BYTE b8:1; + __EXTENSION BYTE b9:1; + __EXTENSION BYTE b10:1; + __EXTENSION BYTE b11:1; + __EXTENSION BYTE b12:1; + __EXTENSION BYTE b13:1; + __EXTENSION BYTE b14:1; + __EXTENSION BYTE b15:1; + __EXTENSION BYTE b16:1; + __EXTENSION BYTE b17:1; + __EXTENSION BYTE b18:1; + __EXTENSION BYTE b19:1; + __EXTENSION BYTE b20:1; + __EXTENSION BYTE b21:1; + __EXTENSION BYTE b22:1; + __EXTENSION BYTE b23:1; + __EXTENSION BYTE b24:1; + __EXTENSION BYTE b25:1; + __EXTENSION BYTE b26:1; + __EXTENSION BYTE b27:1; + __EXTENSION BYTE b28:1; + __EXTENSION BYTE b29:1; + __EXTENSION BYTE b30:1; + __EXTENSION BYTE b31:1; + } bits; +} DWORD_VAL; + +/* MPLAB C Compiler for PIC18 does not support 64-bit integers */ +/*typedef union +{ + QWORD Val; + DWORD d[2] __PACKED; + WORD w[4] __PACKED; + BYTE v[8] __PACKED; + struct __PACKED + { + DWORD LD; + DWORD HD; + } dword; + struct __PACKED + { + WORD LW; + WORD HW; + WORD UW; + WORD MW; + } word; + struct __PACKED + { + __EXTENSION BYTE b0:1; + __EXTENSION BYTE b1:1; + __EXTENSION BYTE b2:1; + __EXTENSION BYTE b3:1; + __EXTENSION BYTE b4:1; + __EXTENSION BYTE b5:1; + __EXTENSION BYTE b6:1; + __EXTENSION BYTE b7:1; + __EXTENSION BYTE b8:1; + __EXTENSION BYTE b9:1; + __EXTENSION BYTE b10:1; + __EXTENSION BYTE b11:1; + __EXTENSION BYTE b12:1; + __EXTENSION BYTE b13:1; + __EXTENSION BYTE b14:1; + __EXTENSION BYTE b15:1; + __EXTENSION BYTE b16:1; + __EXTENSION BYTE b17:1; + __EXTENSION BYTE b18:1; + __EXTENSION BYTE b19:1; + __EXTENSION BYTE b20:1; + __EXTENSION BYTE b21:1; + __EXTENSION BYTE b22:1; + __EXTENSION BYTE b23:1; + __EXTENSION BYTE b24:1; + __EXTENSION BYTE b25:1; + __EXTENSION BYTE b26:1; + __EXTENSION BYTE b27:1; + __EXTENSION BYTE b28:1; + __EXTENSION BYTE b29:1; + __EXTENSION BYTE b30:1; + __EXTENSION BYTE b31:1; + __EXTENSION BYTE b32:1; + __EXTENSION BYTE b33:1; + __EXTENSION BYTE b34:1; + __EXTENSION BYTE b35:1; + __EXTENSION BYTE b36:1; + __EXTENSION BYTE b37:1; + __EXTENSION BYTE b38:1; + __EXTENSION BYTE b39:1; + __EXTENSION BYTE b40:1; + __EXTENSION BYTE b41:1; + __EXTENSION BYTE b42:1; + __EXTENSION BYTE b43:1; + __EXTENSION BYTE b44:1; + __EXTENSION BYTE b45:1; + __EXTENSION BYTE b46:1; + __EXTENSION BYTE b47:1; + __EXTENSION BYTE b48:1; + __EXTENSION BYTE b49:1; + __EXTENSION BYTE b50:1; + __EXTENSION BYTE b51:1; + __EXTENSION BYTE b52:1; + __EXTENSION BYTE b53:1; + __EXTENSION BYTE b54:1; + __EXTENSION BYTE b55:1; + __EXTENSION BYTE b56:1; + __EXTENSION BYTE b57:1; + __EXTENSION BYTE b58:1; + __EXTENSION BYTE b59:1; + __EXTENSION BYTE b60:1; + __EXTENSION BYTE b61:1; + __EXTENSION BYTE b62:1; + __EXTENSION BYTE b63:1; + } bits; +} QWORD_VAL; +*/ +#undef __EXTENSION + +#ifndef uint24_t + #define uint24_t uint32_t +#endif + +typedef void(*pFunc)(void); + +typedef union _POINTER +{ + struct + { + BYTE bLow; + BYTE bHigh; + //BYTE bUpper; + }; + uint16_t _word; // bLow & bHigh + + //pFunc _pFunc; // Usage: ptr.pFunc(); Init: ptr.pFunc = &; + + BYTE* bRam; // Ram byte pointer: 2 bytes pointer pointing + // to 1 byte of data + uint16_t* wRam; // Ram word poitner: 2 bytes poitner pointing + // to 2 bytes of data + + ROM BYTE* bRom; // Size depends on compiler setting + ROM WORD* wRom; + //ROM near BYTE* nbRom; // Near = 2 bytes pointer + //ROM near uint16_t* nwRom; + //ROM far BYTE* fbRom; // Far = 3 bytes pointer + //ROM far uint16_t* fwRom; +} POINTER; + + +#endif /* __CUSTOMIZED_TYPE_DEFS_H_ */ \ No newline at end of file diff --git a/bootloader/src/usb_config.h b/bootloader/src/usb_config.h index d2611f7..7f835ea 100644 --- a/bootloader/src/usb_config.h +++ b/bootloader/src/usb_config.h @@ -1,180 +1,89 @@ -/************************************************************************* - * Copyright (C) 2019 by Justin Byers - * - * This file is part of clubdance_v2. - * - * clubdance_v2 is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * clubdance_v2 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with clubdance_v2. If not, see . - *************************************************************************/ -/** - * @file usb_config.h - * @author Justin Byers - * @date 6 Aug 2019 - * @brief Configuration of the MLA USB framework. - * - * Descriptor specific type definitions are defined in: usbd.h - */ +/******************************************************************************* +Copyright 2016 Microchip Technology Inc. (www.microchip.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +To request to license the code under the MLA license (www.microchip.com/mla_license), +please contact mla_licensing@microchip.com +*******************************************************************************/ + #ifndef USBCFG_H #define USBCFG_H -#include +//---------------------------------------------------------------------------------------------------------- +//User configurable options +//---------------------------------------------------------------------------------------------------------- -/** DEFINITIONS ****************************************************/ -#define USB_EP0_BUFF_SIZE 8 // Valid Options: 8, 16, 32, or 64 bytes. - // Using larger options take more SRAM, but - // does not provide much advantage in most types - // of applications. Exceptions to this, are applications - // that use EP0 IN or OUT for sending large amounts of - // application related data. - -#define USB_MAX_NUM_INT 1 //Set this number to match the maximum interface number used in the descriptors for this firmware project -#define USB_MAX_EP_NUMBER 1 //Set this number to match the maximum endpoint number used in the descriptors for this firmware project +//When defined/enabled, this option allows the boot up code to check an I/O pin +//(as defined by the sw2() macro in HardwareProfile.h), and if logic low, execution +//stays within the bootloader, allowing the user to update the firmware. If this +//option is not enabled, then the only method of entering the bootloader will +//be from the application firmware project, by executing a goto 0x001C operation. +//Enabling the I/O pin to enter the bootloader is recommended, since it is more +//robust/recoverable (compared to software only entry into the bootloader), in +//the event of a failed erase/program/verify operation, or an otherwise corrupted +//application firmware image is loaded. +#define ENABLE_IO_PIN_CHECK_BOOTLOADER_ENTRY //Uncomment if you wish to enable I/O pin entry method into bootloader mode + //Make sure proper sw2() macro definition is provided in HardwareProfile.h -//Device descriptor - if these two definitions are not defined then -// a const USB_DEVICE_DESCRIPTOR variable by the exact name of device_dsc -// must exist. -#define USB_USER_DEVICE_DESCRIPTOR &device_dsc -#define USB_USER_DEVICE_DESCRIPTOR_INCLUDE extern const USB_DEVICE_DESCRIPTOR device_dsc - -//Configuration descriptors - if these two definitions do not exist then -// a const BYTE *const variable named exactly USB_CD_Ptr[] must exist. -#define USB_USER_CONFIG_DESCRIPTOR USB_CD_Ptr -#define USB_USER_CONFIG_DESCRIPTOR_INCLUDE extern const uint8_t *const USB_CD_Ptr[] +//Option to allow blinking of LED to show USB bus status. May be optionally +//commented out to save code space (and/or if there are no LEDs available on +//the actual target application board). If this option is uncommented, you must +//provide the proper LED pin definitions in the HardwareProfile.h file. +#define ENABLE_USB_LED_BLINK_STATUS -//------------------------------------------------------------------------------ -//Select an endpoint ping-pong bufferring mode. Some microcontrollers only -//support certain modes. For most applications, it is recommended to use either -//the USB_PING_PONG__FULL_PING_PONG or USB_PING_PONG__EP0_OUT_ONLY options. -//The other settings are supported on some devices, but they are not -//recommended, as they offer inferior control transfer timing performance. -//See inline code comments in usb_device.c for additional details. -//Enabling ping pong bufferring on an endpoint generally increases firmware -//overhead somewhat, but when both buffers are used simultaneously in the -//firmware, can offer better sustained bandwidth, especially for OUT endpoints. -//------------------------------------------------------ -//#define USB_PING_PONG_MODE USB_PING_PONG__NO_PING_PONG //Not recommended -//#define USB_PING_PONG_MODE USB_PING_PONG__FULL_PING_PONG //A good all around setting -#define USB_PING_PONG_MODE USB_PING_PONG__EP0_OUT_ONLY //Another good setting -//#define USB_PING_PONG_MODE USB_PING_PONG__ALL_BUT_EP0 //Not recommended -//------------------------------------------------------------------------------ +//USB VBUS sensing and USB Bus/Self power sensing options. +//--------------------------------------------------------- +//#define USE_SELF_POWER_SENSE_IO //Leave commented if device is bus powered only (or self powered only, uncomment for some types of dual powered devices) +//#define USE_USB_BUS_SENSE_IO //If the device is self powered, this needs to uncommented if making a fully compliant USB design -//------------------------------------------------------------------------------ -//Select a USB stack operating mode. In the USB_INTERRUPT mode, the USB stack -//main task handler gets called only when necessary as an interrupt handler. -//This can potentially minimize CPU utilization, but adds context saving -//and restoring overhead associated with interrupts, which can potentially -//decrease performance. -//When the USB_POLLING mode is selected, the USB stack main task handler -//(ex: USBDeviceTasks()) must be called periodically by the application firmware -//at a minimum rate as described in the inline code comments in usb_device.c. -//------------------------------------------------------ -#define USB_POLLING -//#define USB_INTERRUPT -//------------------------------------------------------------------------------ + + +//---------------------------------------------------------------------------------------------------------- +//Other semi-configurable settings that tell the USB stack how to operate (but usually don't need changing) +//---------------------------------------------------------------------------------------------------------- +#define MAX_EP_NUMBER 1 // EP0 and EP1 are the only EPs used in this application +#define MAX_NUM_INT 1 // For tracking Alternate Setting - make sure this matches the number of interfaces implemented in the device +#define EP0_BUFF_SIZE 8 // Valid Options: 8, 16, 32, or 64 bytes. + // There is little advantage in using + // more than 8 bytes on EP0 IN/OUT in most cases. +#define USB_MAX_NUM_CONFIG_DSC 1 // Number of configurations that this firmware implements +//#define ENABLE_CONTROL_TRANSFERS_WITH_OUT_DATA_STAGE //Commented out to save code size, since this bootloader firmware doesn't use OUT control transfers with data stage + + +#define CONFIG_DESC_TOTAL_LEN 41 //Make sure this matches the size of your configuration descriptor + all subordinate + //descriptors returned by the get descriptor(configuration) request /* Parameter definitions are defined in usb_device.h */ -#define USB_PULLUP_OPTION USB_PULLUP_ENABLE -//#define USB_PULLUP_OPTION USB_PULLUP_DISABLED +#define MODE_PP _PPBM1 //This code is only written to support _PPBM1 mode only (ping pong on EP0 OUT buffer only). Do not change. +#define UCFG_VAL _PUEN|_TRINT|_FS|MODE_PP -#define USB_TRANSCEIVER_OPTION USB_INTERNAL_TRANSCEIVER -//External Transceiver support is not available on all product families. Please -// refer to the product family datasheet for more information if this feature -// is available on the target processor. -//#define USB_TRANSCEIVER_OPTION USB_EXTERNAL_TRANSCEIVER - -#define USB_SPEED_OPTION USB_FULL_SPEED -//#define USB_SPEED_OPTION USB_LOW_SPEED //(this mode is only supported on some microcontrollers) - -//------------------------------------------------------------------------------------------------------------------ -//Option to enable auto-arming of the status stage of control transfers, if no -//"progress" has been made for the USB_STATUS_STAGE_TIMEOUT value. -//If progress is made (any successful transactions completing on EP0 IN or OUT) -//the timeout counter gets reset to the USB_STATUS_STAGE_TIMEOUT value. -// -//During normal control transfer processing, the USB stack or the application -//firmware will call USBCtrlEPAllowStatusStage() as soon as the firmware is finished -//processing the control transfer. Therefore, the status stage completes as -//quickly as is physically possible. The USB_ENABLE_STATUS_STAGE_TIMEOUTS -//feature, and the USB_STATUS_STAGE_TIMEOUT value are only relevant, when: -//1. The application uses the USBDeferStatusStage() API function, but never calls -// USBCtrlEPAllowStatusStage(). Or: -//2. The application uses host to device (OUT) control transfers with data stage, -// and some abnormal error occurs, where the host might try to abort the control -// transfer, before it has sent all of the data it claimed it was going to send. -// -//If the application firmware never uses the USBDeferStatusStage() API function, -//and it never uses host to device control transfers with data stage, then -//it is not required to enable the USB_ENABLE_STATUS_STAGE_TIMEOUTS feature. - -#define USB_ENABLE_STATUS_STAGE_TIMEOUTS //Comment this out to disable this feature. - -//Section 9.2.6 of the USB 2.0 specifications indicate that: -//1. Control transfers with no data stage: Status stage must complete within -// 50ms of the start of the control transfer. -//2. Control transfers with (IN) data stage: Status stage must complete within -// 50ms of sending the last IN data packet in fullfilment of the data stage. -//3. Control transfers with (OUT) data stage: No specific status stage timing -// requirement. However, the total time of the entire control transfer (ex: -// including the OUT data stage and IN status stage) must not exceed 5 seconds. -// -//Therefore, if the USB_ENABLE_STATUS_STAGE_TIMEOUTS feature is used, it is suggested -//to set the USB_STATUS_STAGE_TIMEOUT value to timeout in less than 50ms. If the -//USB_ENABLE_STATUS_STAGE_TIMEOUTS feature is not enabled, then the USB_STATUS_STAGE_TIMEOUT -//parameter is not relevant. - -#define USB_STATUS_STAGE_TIMEOUT (uint8_t)45 //Approximate timeout in milliseconds, except when - //USB_POLLING mode is used, and USBDeviceTasks() is called at < 1kHz - //In this special case, the timeout becomes approximately: -//Timeout(in milliseconds) = ((1000 * (USB_STATUS_STAGE_TIMEOUT - 1)) / (USBDeviceTasks() polling frequency in Hz)) -//------------------------------------------------------------------------------------------------------------------ - -#define USB_SUPPORT_DEVICE - -#define USB_NUM_STRING_DESCRIPTORS 3 //Set this number to match the total number of string descriptors that are implemented in the usb_descriptors.c file - -/******************************************************************* - * Event disable options - * Enable a definition to suppress a specific event. By default - * all events are sent. - *******************************************************************/ -//#define USB_DISABLE_SUSPEND_HANDLER -//#define USB_DISABLE_WAKEUP_FROM_SUSPEND_HANDLER -//#define USB_DISABLE_SOF_HANDLER -//#define USB_DISABLE_TRANSFER_TERMINATED_HANDLER -//#define USB_DISABLE_ERROR_HANDLER -//#define USB_DISABLE_NONSTANDARD_EP0_REQUEST_HANDLER -//#define USB_DISABLE_SET_DESCRIPTOR_HANDLER -//#define USB_DISABLE_SET_CONFIGURATION_HANDLER -//#define USB_DISABLE_TRANSFER_COMPLETE_HANDLER - -/** DEVICE CLASS USAGE *********************************************/ +//Device class and endpoint definitions #define USB_USE_HID -/** ENDPOINTS ALLOCATION *******************************************/ - /* HID */ #define HID_INTF_ID 0x00 +#define HID_UEP UEP1 +#define HID_BD_OUT ep1Bo #define HID_INT_OUT_EP_SIZE 64 +#define HID_BD_IN ep1Bi #define HID_INT_IN_EP_SIZE 64 -#define HID_NUM_OF_DSC 1 -#define HID_RPT01_SIZE 29 +#define HID_NUM_OF_DSC 1 //Just the Report descriptor (no physical descriptor present) +#define HID_RPT01_SIZE 29 //Make sure this matches the size of your HID report descriptor + -#define HID_EP 1 -#define HID_IN_EP HID_EP -#define HID_OUT_EP HID_EP -/** DEFINITIONS ****************************************************/ #endif //USBCFG_H diff --git a/bootloader/src/usb_descriptors.c b/bootloader/src/usb_descriptors.c index dd9ec9c..e4018e4 100644 --- a/bootloader/src/usb_descriptors.c +++ b/bootloader/src/usb_descriptors.c @@ -17,142 +17,159 @@ To request to license the code under the MLA license (www.microchip.com/mla_lice please contact mla_licensing@microchip.com *******************************************************************************/ -/******************************************************************** --usb_descriptors.c- -------------------------------------------------------------------- -Filling in the descriptor values in the usb_descriptors.c file: -------------------------------------------------------------------- - -[Device Descriptors] -The device descriptor is defined as a USB_DEVICE_DESCRIPTOR type. -This type is defined in usb_ch9.h Each entry into this structure -needs to be the correct length for the data type of the entry. - -[Configuration Descriptors] -The configuration descriptor was changed in v2.x from a structure -to a uint8_t array. Given that the configuration is now a byte array -each byte of multi-byte fields must be listed individually. This -means that for fields like the total size of the configuration where -the field is a 16-bit value "64,0," is the correct entry for a -configuration that is only 64 bytes long and not "64," which is one -too few bytes. - -The configuration attribute must always have the _DEFAULT -definition at the minimum. Additional options can be ORed -to the _DEFAULT attribute. Available options are _SELF and _RWU. -These definitions are defined in the usb_device.h file. The -_SELF tells the USB host that this device is self-powered. The -_RWU tells the USB host that this device supports Remote Wakeup. - -[Endpoint Descriptors] -Like the configuration descriptor, the endpoint descriptors were -changed in v2.x of the stack from a structure to a uint8_t array. As -endpoint descriptors also has a field that are multi-byte entities, -please be sure to specify both bytes of the field. For example, for -the endpoint size an endpoint that is 64 bytes needs to have the size -defined as "64,0," instead of "64," - -Take the following example: - // Endpoint Descriptor // - 0x07, //the size of this descriptor // - USB_DESCRIPTOR_ENDPOINT, //Endpoint Descriptor - _EP02_IN, //EndpointAddress - _INT, //Attributes - 0x08,0x00, //size (note: 2 bytes) - 0x02, //Interval - -The first two parameters are self-explanatory. They specify the -length of this endpoint descriptor (7) and the descriptor type. -The next parameter identifies the endpoint, the definitions are -defined in usb_device.h and has the following naming -convention: -_EP<##>_ -where ## is the endpoint number and dir is the direction of -transfer. The dir has the value of either 'OUT' or 'IN'. -The next parameter identifies the type of the endpoint. Available -options are _BULK, _INT, _ISO, and _CTRL. The _CTRL is not -typically used because the default control transfer endpoint is -not defined in the USB descriptors. When _ISO option is used, -addition options can be ORed to _ISO. Example: -_ISO|_AD|_FE -This describes the endpoint as an isochronous pipe with adaptive -and feedback attributes. See usb_device.h and the USB -specification for details. The next parameter defines the size of -the endpoint. The last parameter in the polling interval. - -------------------------------------------------------------------- -Adding a USB String -------------------------------------------------------------------- -A string descriptor array should have the following format: - -rom struct{byte bLength;byte bDscType;word string[size];}sdxxx={ -sizeof(sdxxx),DSC_STR,}; - -The above structure provides a means for the C compiler to -calculate the length of string descriptor sdxxx, where xxx is the -index number. The first two bytes of the descriptor are descriptor -length and type. The rest are string texts which must be -in the unicode format. The unicode format is achieved by declaring -each character as a word type. The whole text string is declared -as a word array with the number of characters equals to . - has to be manually counted and entered into the array -declaration. Let's study this through an example: -if the string is "USB" , then the string descriptor should be: -(Using index 02) -rom struct{byte bLength;byte bDscType;word string[3];}sd002={ -sizeof(sd002),DSC_STR,'U','S','B'}; - -A USB project may have multiple strings and the firmware supports -the management of multiple strings through a look-up table. -The look-up table is defined as: -rom const unsigned char *rom USB_SD_Ptr[]={&sd000,&sd001,&sd002}; - -The above declaration has 3 strings, sd000, sd001, and sd002. -Strings can be removed or added. sd000 is a specialized string -descriptor. It defines the language code, usually this is -US English (0x0409). The index of the string must match the index -position of the USB_SD_Ptr array, &sd000 must be in position -USB_SD_Ptr[0], &sd001 must be in position USB_SD_Ptr[1] and so on. -The look-up table USB_SD_Ptr is used by the get string handler -function. - -------------------------------------------------------------------- - -The look-up table scheme also applies to the configuration -descriptor. A USB device may have multiple configuration -descriptors, i.e. CFG01, CFG02, etc. To add a configuration -descriptor, user must implement a structure similar to CFG01. -The next step is to add the configuration descriptor name, i.e. -cfg01, cfg02,.., to the look-up table USB_CD_Ptr. USB_CD_Ptr[0] -is a dummy place holder since configuration 0 is the un-configured -state according to the definition in the USB specification. - -********************************************************************/ - /********************************************************************* - * Descriptor specific type definitions are defined in: - * usb_device.h + * -usb_descriptors.c- + * This file contains the USB descriptor information. It is used + * in conjunction with the usb_descriptors.h file. When a descriptor is added + * or removed from the main configuration descriptor, i.e. CFG01, + * the user must also change the descriptor structure defined in + * the usb_descriptors.h file. The structure is used to calculate the + * descriptor size, i.e. sizeof(CFG01). + * + * A typical configuration descriptor consists of: + * At least one configuration descriptor (USB_CFG_DSC) + * One or more interface descriptors (USB_INTF_DSC) + * One or more endpoint descriptors (USB_EP_DSC) + * + * Naming Convention: + * To resolve ambiguity, the naming convention are as followed: + * - USB_CFG_DSC type should be named cdxx, where xx is the + * configuration number. This number should match the actual + * index value of this configuration. + * - USB_INTF_DSC type should be named ia, where yy is the + * interface number and zz is the alternate interface number. + * - USB_EP_DSC type should be named ep<##>_ia, where + * ## is the endpoint number and d is the direction of transfer. + * The interface name should also be listed as a suffix to identify + * which interface does the endpoint belong to. + * + * Example: + * If a device has one configuration, two interfaces; interface 0 + * has two endpoints (in and out), and interface 1 has one endpoint(in). + * Then the CFG01 structure in the usb_descriptors.h should be: + * + * #define CFG01 ROM struct \ + * { USB_CFG_DSC cd01; \ + * USB_INTF_DSC i00a00; \ + * USB_EP_DSC ep01o_i00a00; \ + * USB_EP_DSC ep01i_i00a00; \ + * USB_INTF_DSC i01a00; \ + * USB_EP_DSC ep02i_i01a00; \ + * } cfg01 + * + * Note the hierarchy of the descriptors above, it follows the USB + * specification requirement. All endpoints belonging to an interface + * should be listed immediately after that interface. + * + * ------------------------------------------------------------------- + * Filling in the descriptor values in the usb_descriptors.c file: + * ------------------------------------------------------------------- + * Most items should be self-explanatory, however, a few will be + * explained for clarification. + * + * [Configuration Descriptor(USB_CFG_DSC)] + * The configuration attribute must always have the _DEFAULT + * definition at the minimum. Additional options can be ORed + * to the _DEFAULT attribute. Available options are _SELF and _RWU. + * These definitions are defined in the usb_device.h file. The + * _SELF tells the USB host that this device is self-powered. The + * _RWU tells the USB host that this device supports Remote Wakeup. + * + * [Endpoint Descriptor(USB_EP_DSC)] + * Assume the following example: + * sizeof(USB_EP_DSC),DSC_EP,_EP01_OUT,_BULK,64,0x00 + * + * The first two parameters are self-explanatory. They specify the + * length of this endpoint descriptor (7) and the descriptor type. + * The next parameter identifies the endpoint, the definitions are + * defined in usb_device.h and has the following naming + * convention: + * _EP<##>_ + * where ## is the endpoint number and dir is the direction of + * transfer. The dir has the value of either 'OUT' or 'IN'. + * The next parameter identifies the type of the endpoint. Available + * options are _BULK, _INT, _ISO, and _CTRL. The _CTRL is not + * typically used because the default control transfer endpoint is + * not defined in the USB descriptors. When _ISO option is used, + * addition options can be ORed to _ISO. Example: + * _ISO|_AD|_FE + * This describes the endpoint as an isochronous pipe with adaptive + * and feedback attributes. See usb_device.h and the USB + * specification for details. The next parameter defines the size of + * the endpoint. The last parameter in the polling interval. + * + * ------------------------------------------------------------------- + * Adding a USB String + * ------------------------------------------------------------------- + * A string descriptor array should have the following format: + * + * ROM struct{uint8_t bLength;uint8_t bDscType;uint16_t string[size];}sdxxx={ + * sizeof(sdxxx),DSC_STR,}; + * + * The above structure provides a means for the C compiler to + * calculate the length of string descriptor sdxxx, where xxx is the + * index number. The first two bytes of the descriptor are descriptor + * length and type. The rest are string texts which must be + * in the unicode format. The unicode format is achieved by declaring + * each character as a word type. The whole text string is declared + * as a word array with the number of characters equals to . + * has to be manually counted and entered into the array + * declaration. Let's study this through an example: + * if the string is "USB" , then the string descriptor should be: + * (Using index 02) + * ROM struct{byte bLength;uint8_t bDscType;uint16_t string[3];}sd002={ + * sizeof(sd002),DSC_STR,'U','S','B'}; + * + * A USB project may have multiple strings and the firmware supports + * the management of multiple strings through a look-up table. + * The look-up table is defined as: + * ROM const unsigned char *ROM USB_SD_Ptr[]={&sd000,&sd001,&sd002}; + * + * The above declaration has 3 strings, sd000, sd001, and sd002. + * Strings can be removed or added. sd000 is a specialized string + * descriptor. It defines the language code, usually this is + * US English (0x0409). The index of the string must match the index + * position of the USB_SD_Ptr array, &sd000 must be in position + * USB_SD_Ptr[0], &sd001 must be in position USB_SD_Ptr[1] and so on. + * The look-up table USB_SD_Ptr is used by the get string handler + * function in usb9.c. + * + * ------------------------------------------------------------------- + * + * The look-up table scheme also applies to the configuration + * descriptor. A USB device may have multiple configuration + * descriptors, i.e. CFG01, CFG02, etc. To add a configuration + * descriptor, user must implement a structure similar to CFG01. + * The next step is to add the configuration descriptor name, i.e. + * cfg01, cfg02,.., to the look-up table USB_CD_Ptr. USB_CD_Ptr[0] + * is a dummy place holder since configuration 0 is the un-configured + * state according to the definition in the USB specification. * - * Configuration options are defined in: - * usb_config.h ********************************************************************/ -/** INCLUDES *******************************************************/ + + +/** I N C L U D E S *************************************************/ #include "usb.h" -#include "usb_device_hid.h" + + +/** C O N S T A N T S ************************************************/ +#ifndef __XC8__ +#pragma romdata +#endif /* Device Descriptor */ -const USB_DEVICE_DESCRIPTOR device_dsc= +ROM USB_DEV_DSC device_dsc= { - 0x12, // Size of this descriptor in bytes - USB_DESCRIPTOR_DEVICE, // DEVICE descriptor type + sizeof(USB_DEV_DSC), // Size of this descriptor in bytes + DSC_DEV, // DEVICE descriptor type 0x0200, // USB Spec Release Number in BCD format 0x00, // Class Code 0x00, // Subclass code 0x00, // Protocol code - USB_EP0_BUFF_SIZE, // Max packet size for EP0, see usb_config.h - 0x04D8, // Vendor ID, see usb_config.h - 0x003C, // Product ID, see usb_config.h + EP0_BUFF_SIZE, // Max packet size for EP0, see usb_config.h + 0x04D8, // Vendor ID: Microchip + 0x003C, // Product ID: HID Bootloader 0x0101, // Device release number in BCD format 0x01, // Manufacturer string index 0x02, // Product string index @@ -160,13 +177,13 @@ const USB_DEVICE_DESCRIPTOR device_dsc= 0x01 // Number of possible configurations }; -/* Configuration 1 Descriptor */ -const uint8_t configDescriptor1[]={ +/* Configuration 1 Descriptors */ +ROM uint8_t CFG01[CONFIG_DESC_TOTAL_LEN]={ /* Configuration Descriptor */ - 0x09,//sizeof(USB_CFG_DSC), // Size of this descriptor in bytes - USB_DESCRIPTOR_CONFIGURATION, // CONFIGURATION descriptor type - 0x29, // Total length of data for this cfg - LSB - 0x00, // Total length of data for this cfg - MSB + sizeof(USB_CFG_DSC), // Size of this descriptor in bytes + DSC_CFG, // CONFIGURATION descriptor type + (uint8_t)CONFIG_DESC_TOTAL_LEN, // Total length of data for this cfg - LSB + (uint8_t)(CONFIG_DESC_TOTAL_LEN>>8), // Total length of data for this cfg - MSB 1, // Number of interfaces in this cfg 1, // Index value of this configuration 0, // Configuration string index @@ -174,78 +191,65 @@ const uint8_t configDescriptor1[]={ 50, // Max power consumption (2X mA) /* Interface Descriptor */ - 0x09,//sizeof(USB_INTF_DSC), // Size of this descriptor in bytes - USB_DESCRIPTOR_INTERFACE, // INTERFACE descriptor type + sizeof(USB_INTF_DSC), // Size of this descriptor in bytes + DSC_INTF, // INTERFACE descriptor type 0, // Interface Number 0, // Alternate Setting Number 2, // Number of endpoints in this intf HID_INTF, // Class code - 0, // Subclass code - 0, // Protocol code + 0, // Subclass code, no subclass + 0, // Protocol code, no protocol 0, // Interface string index /* HID Class-Specific Descriptor */ - 0x09,//sizeof(USB_HID_DSC)+3, // Size of this descriptor in bytes RRoj hack + sizeof(USB_HID_DSC), // Size of this descriptor in bytes DSC_HID, // HID descriptor type 0x11, // HID Spec Release Number in BCD format (0x0111 = v1.11) - LSB - 0x01, // HID Spec Release Number in BCD format (0x0111 = v1.11) - MSB + 0x01, // HID Spec Release Number in BCD format (0x0111 = v1.11) - MSB 0x00, // Country Code (0x00 for Not supported) - HID_NUM_OF_DSC, // Number of class descriptors, see usbcfg.h + HID_NUM_OF_DSC, // Number of class descriptors, see usb_config.h DSC_RPT, // Report descriptor type - DESC_CONFIG_WORD(HID_RPT01_SIZE), //sizeof(hid_rpt01), // Size of the report descriptor + (uint8_t)HID_RPT01_SIZE, // Size of the report descriptor - LSB + (uint8_t)((uint16_t)HID_RPT01_SIZE >> 8), // Size of the report descriptor - MSB + + /* Endpoint Descriptor */ + sizeof(USB_EP_DSC), //Endpoint descriptor size + DSC_EP, //Type of descriptor (endpoint) + _EP01_IN, //Endpoint number + direction + _INT, //Endpoint transfer type implemented + HID_INT_IN_EP_SIZE, //LSB - endpoint size + 0x00, //MSB - endpoint size + 0x01, //bInterval /* Endpoint Descriptor */ - 0x07,/*sizeof(USB_EP_DSC)*/ - USB_DESCRIPTOR_ENDPOINT, //Endpoint Descriptor - _EP01_IN, //EndpointAddress - _INTERRUPT, //Attributes - DESC_CONFIG_WORD(HID_INT_IN_EP_SIZE), //size - 0x01, //Interval - - /* Endpoint Descriptor */ - 0x07,/*sizeof(USB_EP_DSC)*/ - USB_DESCRIPTOR_ENDPOINT, //Endpoint Descriptor - _EP01_OUT, //EndpointAddress - _INTERRUPT, //Attributes - DESC_CONFIG_WORD(HID_INT_OUT_EP_SIZE), //size - 0x01, //Interval + sizeof(USB_EP_DSC), //Endpoint descriptor size + DSC_EP, //Type of descriptor (endpoint) + _EP01_OUT, //Endpoint number + direction + _INT, //Endpoint transfer type implemented + HID_INT_OUT_EP_SIZE, //LSB - endpoint size + 0x00, //MSB - endpoint size + 0x01 //bInterval }; +ROM struct{uint8_t bLength;uint8_t bDscType;uint16_t string[1];}sd000={ +sizeof(sd000),DSC_STR,0x0409}; -//Language code string descriptor -const struct{uint8_t bLength;uint8_t bDscType;uint16_t string[1];}sd000={ -sizeof(sd000),USB_DESCRIPTOR_STRING,{0x0409 -}}; +ROM struct{uint8_t bLength;uint8_t bDscType;uint16_t string[25];}sd001={ +sizeof(sd001),DSC_STR, +'M','i','c','r','o','c','h','i','p',' ', +'T','e','c','h','n','o','l','o','g','y',' ','I','n','c','.'}; -//Manufacturer string descriptor -const struct{uint8_t bLength;uint8_t bDscType;uint16_t string[25];}sd001={ -sizeof(sd001),USB_DESCRIPTOR_STRING, -{'M','i','c','r','o','c','h','i','p',' ', -'T','e','c','h','n','o','l','o','g','y',' ','I','n','c','.' -}}; +ROM struct{uint8_t bLength;uint8_t bDscType;uint16_t string[18];}sd002={ +sizeof(sd002),DSC_STR, +'H','I','D',' ','U','S','B',' ','B','o','o', +'t','l','o','a','d','e','r'}; -//Product string descriptor -const struct{uint8_t bLength;uint8_t bDscType;uint16_t string[18];}sd002={ -sizeof(sd002),USB_DESCRIPTOR_STRING, -{'H','I','D',' ','U','S','B',' ','B','o','o', -'t','l','o','a','d','e','r' -}}; - -//Array of configuration descriptors -const uint8_t *const USB_CD_Ptr[]= +ROM uint8_t hid_rpt01[HID_RPT01_SIZE]= +// First byte in each row is the "item". First byte's two least significant +// bits are the number of data bytes that follow, but encoded (0=0, 1=1, 2=2, 3=4 bytes). +// bSize should match number of bytes that follow, or REPORT descriptor parser won't work. The bytes +// that follow in each item line are data bytes { - (const uint8_t *const)&configDescriptor1 -}; - -//Array of string descriptors -const uint8_t *const USB_SD_Ptr[]= -{ - (const uint8_t *const)&sd000, - (const uint8_t *const)&sd001, - (const uint8_t *const)&sd002 -}; - -const struct{uint8_t report[HID_RPT01_SIZE];}hid_rpt01={{ 0x06, 0x00, 0xFF, // Usage Page = 0xFF00 (Vendor Defined Page 1) 0x09, 0x01, // Usage (Vendor Usage 1) 0xA1, 0x01, // Collection (Application) @@ -260,7 +264,19 @@ const struct{uint8_t report[HID_RPT01_SIZE];}hid_rpt01={{ 0x29, 0x40, // Usage Maximum //64 output usages total (0x01 to 0x40) 0x91, 0x00, // Output (Data, Array, Abs): Instantiates output packet fields. Uses same report size and count as "Input" fields, since nothing new/different was specified to the parser since the "Input" item. 0xC0 // End Collection -} -}; -/** EOF usb_descriptors.c ***************************************************/ +}; + +ROM unsigned char* ROM USB_SD_Ptr[]= +{ + (ROM const unsigned char *ROM)&sd000, + (ROM const unsigned char *ROM)&sd001, + (ROM const unsigned char *ROM)&sd002 +}; + + +#ifndef __XC8__ +#pragma code +#endif + +/** EOF usb_descriptors.c ****************************************************/ diff --git a/bootloader/src/usb_events.c b/bootloader/src/usb_events.c deleted file mode 100644 index 47674b6..0000000 --- a/bootloader/src/usb_events.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "usb.h" -#include "usb_device_hid.h" -#include "bootloader.h" - -/******************************************************************* - * Function: bool USER_USB_CALLBACK_EVENT_HANDLER( - * USB_EVENT event, void *pdata, uint16_t size) - * - * PreCondition: None - * - * Input: USB_EVENT event - the type of event - * void *pdata - pointer to the event data - * uint16_t size - size of the event data - * - * Output: None - * - * Side Effects: None - * - * Overview: This function is called from the USB stack to - * notify a user application that a USB event - * occured. This callback is in interrupt context - * when the USB_INTERRUPT option is selected. - * - * Note: None - *******************************************************************/ -bool USER_USB_CALLBACK_EVENT_HANDLER(USB_EVENT event, void *pdata, uint16_t size) -{ - switch( (int) event ) - { - case EVENT_TRANSFER: - break; - - case EVENT_SOF: - break; - - case EVENT_SUSPEND: - //Call the hardware platform specific handler for suspend events for - //possible further action (like optionally going reconfiguring the application - //for lower power states and going to sleep during the suspend event). This - //would normally be done in USB compliant bus powered applications, although - //no further processing is needed for purely self powered applications that - //don't consume power from the host. - //asm("SLEEP"); // sleep right now, execution starts on next line when woken up - // we have been woken up. if the watchdog was the cause go back to sleep - //while ((PIR2bits.USBIF == 0) && (RCONbits.TO == 0)) - //{ - // asm("SLEEP"); - //} - break; - - case EVENT_RESUME: - // restore any hardware states (i.e. IO pin config) that were changed on suspend - break; - - case EVENT_CONFIGURED: - // When the device is configured, we should (re)initialize our state variables - UserInit(); - break; - - case EVENT_SET_DESCRIPTOR: - break; - - case EVENT_EP0_REQUEST: - /* We have received a non-standard USB request. The HID driver - * needs to check to see if the request was for it. */ - USBCheckHIDRequest(); - break; - - case EVENT_BUS_ERROR: - break; - - case EVENT_TRANSFER_TERMINATED: - break; - - default: - break; - } - return true; -} - - -/******************************************************************************* - End of File -*/ - diff --git a/common_src/framework/usb/inc/usb_hal_pic18.h b/common_src/framework/usb/inc/usb_hal_pic18.h index 375a66e..8fde8b8 100644 --- a/common_src/framework/usb/inc/usb_hal_pic18.h +++ b/common_src/framework/usb/inc/usb_hal_pic18.h @@ -334,7 +334,7 @@ typedef union __USTAT #define USBHALGetLastDirection(stat) stat.direction #define USBHALGetLastPingPong(stat) stat.ping_pong - +/* typedef union _POINTER { struct @@ -359,7 +359,7 @@ typedef union _POINTER //rom far byte* fbRom; // Far = 3 bytes pointer //rom far word* fwRom; } POINTER; - +*/ // ***************************************************************************** // ***************************************************************************** // Section: Interface Routines diff --git a/common_src/memory.h b/common_src/memory.h index a1ad1dd..fc1f5c1 100644 --- a/common_src/memory.h +++ b/common_src/memory.h @@ -27,11 +27,15 @@ #ifndef MEMORY_ #define MEMORY_ -#define APP_FW_MEMORY_OFFSET 0x2080 +#define APP_FW_MEMORY_OFFSET 0x2000 #define BOOTLOADER_ENTRYPOINT 0x001C -#define APP_FW_VERSION_ADDRESS (APP_FW_MEMORY_OFFSET + 0x0016) +#define APP_FW_VERSION_ADDRESS (APP_FW_MEMORY_OFFSET + 0x16) + +#define APP_SIGNATURE_ADDRESS (APP_FW_MEMORY_OFFSET + 0x06) //0x1C06 and 0x1C07 contains the "signature" WORD, indicating successful erase/program/verify operation +#define APP_SIGNATURE_VALUE 0x600D //leet "GOOD", implying that the erase/program was a success and the bootloader intentionally programmed the APP_SIGNATURE_ADDRESS with this value +#define APP_VERSION_ADDRESS APP_FW_VERSION_ADDRESS //0x1C16 and 0x1C17 should contain the application image firmware version number #endif /* MEMORY_ */ diff --git a/firmware/clubdance_v2.X/nbproject/configurations.xml b/firmware/clubdance_v2.X/nbproject/configurations.xml index fe5df09..5779212 100644 --- a/firmware/clubdance_v2.X/nbproject/configurations.xml +++ b/firmware/clubdance_v2.X/nbproject/configurations.xml @@ -95,16 +95,46 @@ false + + + + + + - + + + + + + + - + + + + + + + - + + + + + + + - + + + + + + + @@ -142,7 +172,7 @@ - + @@ -153,7 +183,7 @@ - + @@ -264,7 +294,6 @@ - ../../bootloader/clubdance_v2_bootloader.X/dist/default/production/clubdance_v2_bootloader.X.production.hex false false diff --git a/firmware/src/main.c b/firmware/src/main.c index 1a526db..a7eeef2 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -24,33 +24,33 @@ */ #pragma warning disable 1510 -//#include "usb.h" -//#include "padhal.h" -//#include "dancepad.h" +#include "usb.h" +#include "padhal.h" +#include "dancepad.h" #include "memory.h" -//#ifdef LINK_FOR_BOOTLOADER +#ifdef LINK_FOR_BOOTLOADER // only define this if building fw to be used with the bootloader -const unsigned int VersionWord __at(APP_FW_VERSION_ADDRESS) = 0x0100; -//#endif +const uint16_t VersionWord __at(0x2016) = 0x0100; +#endif void main(void) { // initialize sensor HAL & the dancepad driver - /*PADHAL_Initialize(); + PADHAL_Initialize(); DANCEPAD_Initialize(); // initialize the USB framework USBDeviceInit(); - USBDeviceAttach();*/ + USBDeviceAttach(); while(1) { // do nothing if: not connected to USB host, or the host put us in suspend state - //if((USBGetDeviceState() < CONFIGURED_STATE) | USBIsDeviceSuspended()) - // continue; + if((USBGetDeviceState() < CONFIGURED_STATE) | USBIsDeviceSuspended()) + continue; // run application specific tasks - //DANCEPAD_Tasks(); + DANCEPAD_Tasks(); } } diff --git a/firmware/src/usb_config.h b/firmware/src/usb_config.h index 4ea0fb7..1af0ef2 100644 --- a/firmware/src/usb_config.h +++ b/firmware/src/usb_config.h @@ -80,8 +80,8 @@ //(ex: USBDeviceTasks()) must be called periodically by the application firmware //at a minimum rate as described in the inline code comments in usb_device.c. //------------------------------------------------------ -#define USB_POLLING -//#define USB_INTERRUPT +//#define USB_POLLING +#define USB_INTERRUPT //------------------------------------------------------------------------------ /* Parameter definitions are defined in usb_device.h */