///////////////////////////////////////////////////////////////////////////////////////////////////////// // // Name: HID.h // Created: Mar 2022 // Version: v1.0 // Author(s): Philip Smart // Description: A HID Class definition, used to instantiate differing input device classes and // present a standard API. // Credits: // Copyright: (c) 2019-2022 Philip Smart // // History: Mar 2022 - Initial write. // v1.01 May 2022 - Initial release version. // v1.02 Jun 2022 - Updates to support Bluetooth keyboard and mouse. The mouse can be // a primary device or a secondary device for hosts which support // keyboard and mouse over one physical port. // // Notes: See Makefile to enable/disable conditional components // ///////////////////////////////////////////////////////////////////////////////////////////////////////// // This source file 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. // // This source file 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 this program. If not, see . ///////////////////////////////////////////////////////////////////////////////////////////////////////// #ifndef HID_H #define HID_H #include #include #include #include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "esp_log.h" #include "esp_system.h" #include "nvs_flash.h" #include "nvs.h" #include "soc/timer_group_struct.h" #include "soc/timer_group_reg.h" #include "driver/timer.h" #include "PS2KeyAdvanced.h" #include "PS2Mouse.h" #include "BTHID.h" #include "LED.h" #include "SWITCH.h" // NB: Macros definitions put inside class for clarity, they are still global scope. // Define a class which acts as the encapsulation object of many base classes which provide input device functionality. class HID { // Macros. // #define NUMELEM(a) (sizeof(a)/sizeof(a[0])) // Constants. #define HID_VERSION 1.02 #define HID_MOUSE_DATA_POLL_DELAY 10 #define MAX_MOUSE_INACTIVITY_TIME 500 * HID_MOUSE_DATA_POLL_DELAY // Categories of configuration possible with the mouse. These are used primarily with the web based UI for rendering selection choices. #define HID_MOUSE_HOST_SCALING_TYPE "host_scaling" #define HID_MOUSE_SCALING_TYPE "mouse_scaling" #define HID_MOUSE_RESOLUTION_TYPE "mouse_resolution" #define HID_MOUSE_SAMPLING_TYPE "mouse_sampling" #define HID_MOUSE_HOST_SCALING_1_1_NAME "1:1" #define HID_MOUSE_HOST_SCALING_1_2_NAME "1:2" #define HID_MOUSE_HOST_SCALING_1_3_NAME "1:3" #define HID_MOUSE_HOST_SCALING_1_4_NAME "1:4" #define HID_MOUSE_HOST_SCALING_1_5_NAME "1:5" // Names for the configuration value settings. #define HID_MOUSE_RESOLUTION_1_1_NAME "1 c/mm" #define HID_MOUSE_RESOLUTION_1_2_NAME "2 c/mm" #define HID_MOUSE_RESOLUTION_1_4_NAME "4 c/mm" #define HID_MOUSE_RESOLUTION_1_8_NAME "8 c/mm" #define HID_MOUSE_SCALING_1_1_NAME "1:1" #define HID_MOUSE_SCALING_2_1_NAME "2:1" #define HID_MOUSE_SAMPLE_RATE_10_NAME "10 S/s" #define HID_MOUSE_SAMPLE_RATE_20_NAME "20 S/s" #define HID_MOUSE_SAMPLE_RATE_40_NAME "40 S/s" #define HID_MOUSE_SAMPLE_RATE_60_NAME "60 S/s" #define HID_MOUSE_SAMPLE_RATE_80_NAME "80 S/s" #define HID_MOUSE_SAMPLE_RATE_100_NAME "100 S/s" #define HID_MOUSE_SAMPLE_RATE_200_NAME "200 S/s" public: // Types of devices the HID class can support. enum HID_DEVICE_TYPES { HID_DEVICE_TYPE_KEYBOARD = 0x00, HID_DEVICE_TYPE_MOUSE = 0x01, HID_DEVICE_TYPE_BLUETOOTH = 0x02, }; // HID class can encapsulate many input device objects, only one at a time though. On startup the device is enumerated and then all // functionality serves the device object. enum HID_INPUT_DEVICE { HID_DEVICE_PS2_KEYBOARD = 0x00, HID_DEVICE_PS2_MOUSE = 0x01, HID_DEVICE_BLUETOOTH = 0x02, HID_DEVICE_BT_KEYBOARD = 0x03, HID_DEVICE_BT_MOUSE = 0x04 }; // Scaling - The host receiving mouse data may have a different resolution to that of the mouse, so we use configurable host side scaling to compensate. The mouse data received // is scaled according to the enum setting. enum HID_MOUSE_HOST_SCALING { HID_MOUSE_HOST_SCALING_1_1 = 0x00, HID_MOUSE_HOST_SCALING_1_2 = 0x01, HID_MOUSE_HOST_SCALING_1_3 = 0x02, HID_MOUSE_HOST_SCALING_1_4 = 0x03, HID_MOUSE_HOST_SCALING_1_5 = 0x04, }; // Resolution - the mouse can digitize movement from 1mm to 1/8mm, the default being 1/4 (ie. 1mm = 4 counts). This allows configuration for a finer or rougher // tracking digitisation. enum HID_MOUSE_RESOLUTION { HID_MOUSE_RESOLUTION_1_1 = PS2Mouse::PS2_MOUSE_RESOLUTION_1_1, HID_MOUSE_RESOLUTION_1_2 = PS2Mouse::PS2_MOUSE_RESOLUTION_1_2, HID_MOUSE_RESOLUTION_1_4 = PS2Mouse::PS2_MOUSE_RESOLUTION_1_4, HID_MOUSE_RESOLUTION_1_8 = PS2Mouse::PS2_MOUSE_RESOLUTION_1_8, }; // Scaling - the mouse can provide linear (1:1 no scaling) or non liner (2:1 scaling) adaptation of the digitised data. This allows configuration for amplification of movements. enum HID_MOUSE_SCALING { HID_MOUSE_SCALING_1_1 = PS2Mouse::PS2_MOUSE_SCALING_1_1, HID_MOUSE_SCALING_2_1 = PS2Mouse::PS2_MOUSE_SCALING_2_1, }; // Sampling rate - the mouse, in streaming mode, the mouse sends with movement updates. This allows for finer or rougher digitisation of movements. The default is 100 samples per // second and the X68000 is fixed at 100 samples per second. enum HID_MOUSE_SAMPLING { HID_MOUSE_SAMPLE_RATE_10 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_10, HID_MOUSE_SAMPLE_RATE_20 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_20, HID_MOUSE_SAMPLE_RATE_40 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_40, HID_MOUSE_SAMPLE_RATE_60 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_60, HID_MOUSE_SAMPLE_RATE_80 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_80, HID_MOUSE_SAMPLE_RATE_100 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_100, HID_MOUSE_SAMPLE_RATE_200 = PS2Mouse::PS2_MOUSE_SAMPLE_RATE_200, }; // Suspend flag. When active, the interface components enter an idle state after completing there latest cycle. bool suspend = false; bool suspended = true; // Element to store mouse data in a queue. The data is actual mouse movements, any control data and private data for the actual mouse is stripped. typedef struct { int16_t xPos; int16_t yPos; uint8_t status; uint8_t wheel; } t_mouseMessageElement; // Prototypes. HID(enum HID_DEVICE_TYPES, NVS *hdlNVS, LED *hdlLED, SWITCH *hdlSWITCH); HID(NVS *hdlNVS); HID(void); virtual ~HID(void); bool isBluetooth(void); void enableBluetooth(void); void disableBluetooth(void); bool isSuspended(bool waitForSuspend); void suspendInterface(bool suspendIf); bool persistConfig(void); uint16_t read(void); void setMouseResolution(enum HID_MOUSE_RESOLUTION resolution); void setMouseHostScaling(enum HID_MOUSE_HOST_SCALING scaling); void setMouseScaling(enum HID_MOUSE_SCALING scaling); void setMouseSampleRate(enum HID_MOUSE_SAMPLING sampleRate); void btStartPairing(void); void btCancelPairing(void); // Method to register an object method for callback with context. template void setDataCallback(A func_ptr, B obj_ptr) { hidCtrl.dataCallback = bind(func_ptr, obj_ptr, std::placeholders::_1); } // Method to suspend input device activity, yielding to the OS until suspend is cleared. inline virtual void yield(uint32_t delay) { // If suspended, go into a permanent loop until the suspend flag is reset. if(this->suspend) { // Suspend the keyboard interface. if(hidCtrl.deviceType == HID_DEVICE_TYPE_KEYBOARD) { printf("SUSPEND\n"); ps2Keyboard->suspend(true); } this->suspended = true; // Sleep while suspended. while(this->suspend) { vTaskDelay(100); } // Release the keyboard interface. if(hidCtrl.deviceType == HID_DEVICE_TYPE_KEYBOARD) ps2Keyboard->suspend(false); this->suspended = false; } else // Otherwise just delay by the required amount for timing and to give other threads a time slice. { vTaskDelay(delay); } return; } // Method to see if the interface must enter suspend mode. // inline virtual bool suspendRequested(void) { return(this->suspend); } // Helper method to identify the sub class, this is used in non volatile key management. // Warning: This method wont work if optimisation for size is enabled on the compiler. const char *getClassName(const std::string& prettyFunction) { // First find the CLASS :: METHOD seperation. size_t colons = prettyFunction.find("::"); // None, then this is not a class. if (colons == std::string::npos) return "::"; // Split out the class name. size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1; size_t end = colons - begin; // Return the name. return(prettyFunction.substr(begin,end).c_str()); } // Template to aid in conversion of an enum to integer. template constexpr typename std::underlying_type::type to_underlying(E e) noexcept { return static_cast::type>(e); } // Method to return the class version number. virtual float version(void) { return(HID_VERSION); } // Method to return the name of this class. virtual std::string ifName(void) { return(className); } protected: private: // Prototypes. void init(const char *className, enum HID_DEVICE_TYPES deviceTypes); bool nvsPersistData(const char *key, void *pData, uint32_t size); bool nvsRetrieveData(const char *key, void *pData, uint32_t size); bool nvsCommitData(void); void checkKeyboard( void ); bool checkPS2Keyboard( void ); bool checkPS2Mouse( void ); void checkMouse( void ); void processPS2Mouse( void ); void checkBTMouse( void ); void mouseReceiveData(uint8_t src, PS2Mouse::MouseData mouseData); IRAM_ATTR static void hidControl( void * pvParameters ); static void btPairingHandler(uint32_t pid, uint8_t trigger); inline uint32_t milliSeconds(void) { return( (uint32_t) (clock() ) ); } enum HOST_CONFIG_MODES { HOST_CONFIG_OFF = 0x00, HOST_CONFIG_SCALING = 0x01, HOST_CONFIG_RESOLUTION = 0x02, }; // Structure to maintain configuration for the HID. // typedef struct { struct { // Mouse data Adjustment and filtering options. // enum HID_MOUSE_RESOLUTION resolution; enum HID_MOUSE_SCALING scaling; enum HID_MOUSE_SAMPLING sampleRate; } mouse; struct { // Host data for adjustment and configuration. enum HID_MOUSE_HOST_SCALING scaling; } host; struct { // Configuration mode time period used to select configuration option. Once the middle key is held, the configuration option starts at 1, after this number of seconds // the configuration option advances to the next configuration item, and so on... uint16_t optionAdvanceDelay; } params; } t_hidConfig; // Structure to maintain an active settings for HID devices. typedef struct { enum HID_INPUT_DEVICE hidDevice; // Active HID device, only one can be active. enum HID_DEVICE_TYPES deviceType; // Type of device which is active. bool ps2Active; // Flag to indicate PS/2 device is online and active. uint32_t noEchoCount = 0L; // Echo back counter, used for testing if a keyboard is online. TickType_t ps2CheckTimer = 0; // Check timer, used for timing periodic keyboard checks. // Mouse control variables. uint32_t noValidMouseMessage = 0; int wheelCnt = 0; uint32_t loopTimer = 0; bool middleKeyPressed = false; PS2Mouse::MouseData mouseData; // Flag to indicate the mouse is active and online. bool active; // Flag to indicate the configuration data has been updated. bool updated; // Configuration mode selected when middle button pressed. enum HOST_CONFIG_MODES configMode; // Mutex to block access during maintenance tasks. SemaphoreHandle_t mutexInternal; // Callback for streaming input devices with data to be processed. std::function dataCallback; } t_hidControl; // Current configuration of the HID. t_hidConfig hidConfig; // Variables to control the HID. t_hidControl hidCtrl; // Handle to the persistent storage api. nvs_handle_t nvsHandle; // Name of this class, used for NVS access. std::string className; // NVS persistence object. NVS *nvs; // LED activity object handle. LED *led; // SWITCH object handle. SWITCH *sw; // Keyboard object for PS/2 data retrieval and management. PS2KeyAdvanced *ps2Keyboard; // Keyboard object for Bluetooth data retrieval and management. BTHID *btHID; // Mouse object for PS/2 data retrieval and management. PS2Mouse *ps2Mouse; // Thread handle for the HID control thread. TaskHandle_t TaskHID = NULL; }; #endif // HID_H