Files
SharpKey/main/include/HID.h
2022-09-04 14:51:38 +01:00

386 lines
17 KiB
C++

/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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 <philip.smart@net2net.org>
//
// 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 <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef HID_H
#define HID_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <map>
#include <functional>
#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<typename A, typename B>
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 <typename E> constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept
{
return static_cast<typename std::underlying_type<E>::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<void(t_mouseMessageElement)> 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