/*************************************************************************************************/ /*! * \file * * \brief HID HidApp sample application. * * Copyright (c) 2015-2019 Arm Ltd. * * Copyright (c) 2019 Packetcraft, Inc. * * 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. */ /*************************************************************************************************/ #include #include "wsf_types.h" #include "util/bstream.h" #include "wsf_msg.h" #include "wsf_trace.h" #include "wsf_assert.h" #include "wsf_os.h" #include "hci_api.h" #include "dm_api.h" #include "att_api.h" #include "smp_api.h" #include "app_cfg.h" #include "app_api.h" #include "app_db.h" #include "app_ui.h" #include "app_main.h" #include "svc_ch.h" #include "svc_hid.h" #include "svc_core.h" #include "svc_dis.h" #include "svc_batt.h" #include "gatt/gatt_api.h" #include "bas/bas_api.h" #include "hid/hid_api.h" #include "hidapp/hidapp_api.h" /************************************************************************************************** Configurable Parameters **************************************************************************************************/ /*! configurable parameters for advertising */ static const appAdvCfg_t hidAppAdvCfg = { {60000, 0, 0}, /*! Advertising durations in ms */ { 64, 1600, 0} /*! Advertising intervals in 0.625 ms units */ }; /*! configurable parameters for slave */ static const appSlaveCfg_t hidAppSlaveCfg = { 1, /*! Maximum connections */ }; /*! configurable parameters for security */ static const appSecCfg_t hidAppSecCfg = { DM_AUTH_BOND_FLAG | DM_AUTH_SC_FLAG, /*! Authentication and bonding flags */ 0, /*! Initiator key distribution flags */ DM_KEY_DIST_LTK, /*! Responder key distribution flags */ FALSE, /*! TRUE if Out-of-band pairing data is present */ TRUE /*! TRUE to initiate security upon connection */ }; /*! configurable parameters for connection parameter update */ static const appUpdateCfg_t hidAppUpdateCfg = { 0, /*! Connection idle period in ms before attempting connection parameter update; set to zero to disable */ 640, /*! Minimum connection interval in 1.25ms units */ 800, /*! Maximum connection interval in 1.25ms units */ 3, /*! Connection latency */ 600, /*! Supervision timeout in 10ms units */ 5 /*! Number of update attempts before giving up */ }; /*! battery measurement configuration */ static const basCfg_t hidAppBasCfg = { 30, /*! Battery measurement timer expiration period in seconds */ 1, /*! Perform battery measurement after this many timer periods */ 100 /*! Send battery level notification to peer when below this level. */ }; /************************************************************************************************** Advertising Data **************************************************************************************************/ /*! advertising data, discoverable mode */ static const uint8_t hidAppAdvDataDisc[] = { /*! flags */ 2, /*! length */ DM_ADV_TYPE_FLAGS, /*! AD type */ DM_FLAG_LE_GENERAL_DISC | /*! flags */ DM_FLAG_LE_BREDR_NOT_SUP, /*! manufacturer specific data */ 3, /*! length */ DM_ADV_TYPE_MANUFACTURER, /*! AD type */ UINT16_TO_BYTES(HCI_ID_ARM), /*! company ID */ /*! service UUID list */ 3, /*! length */ DM_ADV_TYPE_16_UUID, /*! AD type */ UINT16_TO_BYTES(ATT_UUID_HID_SERVICE) }; /*! scan data, discoverable mode */ static const uint8_t hidAppScanDataDisc[] = { /*! device name */ 8, /*! length */ DM_ADV_TYPE_LOCAL_NAME, /*! AD type */ 'H', 'I', 'D', ' ', 'A', 'p', 'p' }; /************************************************************************************************** Macros **************************************************************************************************/ /*! WSF message event starting value */ #define HIDAPP_MSG_START 0xA0 /*! WSF message event enumeration */ enum { HIDAPP_BATT_TIMER_IND = HIDAPP_MSG_START, /*! Battery timer expired */ }; /* HidApp TX path flags */ #define HIDAPP_TX_FLAGS_READY 0x01 #define HIDAPP_TX_FLAGS_PENDING 0x02 /* The input report fits in one byte */ #define HIDAPP_KEYBOARD_INPUT_REPORT_LEN 8 #define HIDAPP_MOUSE_INPUT_REPORT_LEN 3 #define HIDAPP_REMOTE_INPUT_REPORT_LEN 1 #define HIDAPP_OUTPUT_REPORT_LEN 1 #define HIDAPP_FEATURE_REPORT_LEN 1 /* Remote Button Identifier bits */ #define REMOTE_USAGE_NONE (0<<0) #define REMOTE_PLAY_PAUSE (1<<0) #define REMOTE_AL_CCC (1<<1) #define REMOTE_SCAN_NEXT (1<<2) #define REMOTE_SCAN_PREVIOUS (1<<3) #define REMOTE_VOLUME_DOWN (1<<4) #define REMOTE_VOLUME_UP (1<<5) #define REMOTE_AC_FORWARD (1<<6) #define REMOTE_AC_BACKWARD (1<<7) /* Keyboard input record message format */ #define KEYBOARD_IR_MODIFIER_POS 0 #define KEYBOARD_IR_RESERVED_POS 1 #define KEYBOARD_IR_KEY_POS 2 #define KEYBOARD_IR_MAX_KEYS 6 /* Keyboard LED Identifier bits */ #define KEYBOARD_LED_NUM_LOCK (1<<0) #define KEYBOARD_LED_CAPS_LOCK (1<<1) #define KEYBOARD_LED_SCROLL_LOCK (1<<2) #define KEYBOARD_LED_COMPOSE (1<<3) #define KEYBOARD_LED_KANA (1<<4) /* Keyboard usage definitions */ #define KEYBOARD_USAGE_NONE 0x00 #define KEYBOARD_USAGE_CAPS_LOCK 0x39 #define KEYBOARD_USAGE_RIGHT_ARROW 0x4F #define KEYBOARD_USAGE_LEFT_ARROW 0x50 #define KEYBOARD_USAGE_DOWN_ARROW 0x51 #define KEYBOARD_USAGE_UP_ARROW 0x52 /* Mouse button bit mask */ #define MOUSE_BUTTON_LEFT (1<<0) #define MOUSE_BUTTON_RIGHT (1<<1) #define MOUSE_BUTTON_MIDDLE (1<<2) /* Mouse input record message format */ #define MOUSE_BUTTON_POS 0 #define MOUSE_X_POS 1 #define MOUSE_Y_POS 2 /* Mouse usage definitions */ #define MOUSE_USAGE_NONE 0x00 #define MOUSE_USAGE_UP_ARROW 0x52 /* HID Report IDs */ #define HIDAPP_REMOTE_REPORT_ID 1 #define HIDAPP_KEYBOARD_REPORT_ID 2 #define HIDAPP_MOUSE_REPORT_ID 3 /* Test button identifiers */ enum { HIDAPP_REMOTE_PLAY_PAUSE_BTN, HIDAPP_REMOTE_SCAN_NEXT_BTN, HIDAPP_REMOTE_SCAN_PREVIOUS_BTN, HIDAPP_REMOTE_VOLUME_DOWN_BTN, HIDAPP_REMOTE_VOLUME_UP_BTN, HIDAPP_MOUSE_LEFT_BTN, HIDAPP_MOUSE_RIGHT_BTN, HIDAPP_KEYBOARD_UP_BTN, HIDAPP_KEYBOARD_DOWN_BTN, HIDAPP_KEYBOARD_LEFT_BTN, HIDAPP_KEYBOARD_RIGHT_BTN, HIDAPP_NUM_TEST_BUTTONS }; /************************************************************************************************** Client Characteristic Configuration Descriptors **************************************************************************************************/ /*! enumeration of client characteristic configuration descriptors */ enum { HIDAPP_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */ HIDAPP_MBI_CCC_HDL, /*! HID Boot Mouse Input characteristic */ HIDAPP_KBI_CCC_HDL, /*! HID Boot Keyboard Input characteristic */ HIDAPP_IN_REMOTE_CCC_HDL, /*! HID Input Report characteristic for remote inputs */ HIDAPP_IN_KEYBOARD_CCC_HDL, /*! HID Input Report characteristic for keyboard inputs */ HIDAPP_IN_MOUSE_CCC_HDL, /*! HID Input Report characteristic for mouse inputs */ HIDAPP_BATT_LVL_CCC_IDX, /*! Battery service, battery level characteristic */ HIDAPP_NUM_CCC_IDX }; /*! client characteristic configuration descriptors settings, indexed by above enumeration */ static const attsCccSet_t hidAppCccSet[HIDAPP_NUM_CCC_IDX] = { /* cccd handle value range security level */ {GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* HIDAPP_GATT_SC_CCC_IDX */ {HID_MOUSE_BOOT_IN_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_MBI_CCC_HDL */ {HID_KEYBOARD_BOOT_IN_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_KBI_CCC_HDL */ {HID_INPUT_REPORT_1_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_IN_REMOTE_CCC_HDL */ {HID_INPUT_REPORT_2_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_IN_KEYBOARD_CCC_HDL */ {HID_INPUT_REPORT_3_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_IN_MOUSE_CCC_HDL */ {BATT_LVL_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE} /* HIDAPP_BATT_LVL_CCC_IDX */ }; /*! HID Report Type/ID and attribute handle map */ static const hidReportIdMap_t hidAppReportIdSet[] = { /* type ID handle */ {HID_REPORT_TYPE_INPUT, HIDAPP_REMOTE_REPORT_ID, HID_INPUT_REPORT_1_HDL}, /* Remote Input Report */ {HID_REPORT_TYPE_INPUT, HIDAPP_KEYBOARD_REPORT_ID, HID_INPUT_REPORT_2_HDL}, /* Keyboard Input Report */ {HID_REPORT_TYPE_OUTPUT, HIDAPP_KEYBOARD_REPORT_ID, HID_OUTPUT_REPORT_HDL}, /* Keyboard Output Report */ {HID_REPORT_TYPE_FEATURE, HIDAPP_KEYBOARD_REPORT_ID, HID_FEATURE_REPORT_HDL}, /* Keyboard Feature Report */ {HID_REPORT_TYPE_INPUT, HIDAPP_MOUSE_REPORT_ID, HID_INPUT_REPORT_3_HDL}, /* Mouse Input Report */ {HID_REPORT_TYPE_INPUT, HID_KEYBOARD_BOOT_ID, HID_KEYBOARD_BOOT_IN_HDL}, /* Boot Keyboard Input Report */ {HID_REPORT_TYPE_OUTPUT, HID_KEYBOARD_BOOT_ID, HID_KEYBOARD_BOOT_OUT_HDL}, /* Boot Keyboard Output Report */ {HID_REPORT_TYPE_INPUT, HID_MOUSE_BOOT_ID, HID_MOUSE_BOOT_IN_HDL}, /* Boot Mouse Input Report */ }; /*! HidApp Report Map (Descriptor) */ const uint8_t hidReportMap[] = { 0x05, 0x0c, /* Usage Page (Consumer Devices) */ 0x09, 0x01, /* Usage (Consumer Control) */ 0xa1, 0x01, /* Collection (Application) */ 0x85, HIDAPP_REMOTE_REPORT_ID, /* report ID (HIDAPP_REMOTE_REPORT_ID) */ 0x15, 0x00, // Logical minimum (0) 0x25, 0x01, // Logical maximum (1) 0x75, 0x01, // Report Size (1) 0x95, 0x01, // Report Count (1) 0x09, 0xCD, // Usage (Play/Pause) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x0A, 0x83, 0x01, // Usage (AL Consumer Control Configuration) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x09, 0xB5, // Usage (Scan Next Track) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x09, 0xB6, // Usage (Scan Previous Track) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x09, 0xEA, // Usage (Volume Down) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x09, 0xE9, // Usage (Volume Up) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x0A, 0x25, 0x02, // Usage (AC Forward) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0x0A, 0x24, 0x02, // Usage (AC Back) 0x81, 0x06, // Input (Data,Value,Relative,Bit Field) 0xC0, // End Collection 0x05, 0x01, /* Usage Page (Generic Desktop) */ 0x09, 0x06, /* Usage (Keyboard) */ 0xA1, 0x01, /* Collection (Application) */ 0x85, HIDAPP_KEYBOARD_REPORT_ID, /* report ID (HIDAPP_KEYBOARD_REPORT_ID) */ 0x75, 0x01, /* Report Size (1) */ 0x95, 0x08, /* Report Count (8) */ 0x05, 0x07, /* Usage Page (Key Codes) */ 0x19, 0xe0, /* Usage Minimum (224) */ 0x29, 0xe7, /* Usage Maximum (231) */ 0x15, 0x00, /* Logical Minimum (0) */ 0x25, 0x01, /* Logical Maximum (1) */ 0x81, 0x02, /* Input (Data, Variable, Absolute) */ 0x95, 0x01, /* Report Count (1) */ 0x75, 0x08, /* Report Size (8) */ 0x81, 0x01, /* Input (Constant) reserved byte(1) */ 0x95, 0x05, /* Report Count (5) */ 0x75, 0x01, /* Report Size (1) */ 0x05, 0x08, /* Usage Page (Page# for LEDs) */ 0x19, 0x01, /* Usage Minimum (1) */ 0x29, 0x05, /* Usage Maximum (5) */ 0x91, 0x02, /* Output (Data, Variable, Absolute), Led report */ 0x95, 0x01, /* Report Count (1) */ 0x75, 0x03, /* Report Size (3) */ 0x91, 0x01, /* Output (Constant), Led report padding */ 0x95, 0x06, /* Report Count (6) */ 0x75, 0x08, /* Report Size (8) */ 0x15, 0x00, /* Logical Minimum (0) */ 0x25, 0x65, /* Logical Maximum (101) */ 0x05, 0x07, /* Usage Page (Key codes) */ 0x19, 0x00, /* Usage Minimum (0) */ 0x29, 0x65, /* Usage Maximum (101) */ 0x81, 0x00, /* Input (Data, Array) Key array(6 bytes) */ 0xC0, /* End Collection (Application) */ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 0x09, 0x02, /* USAGE (Mouse) */ 0xa1, 0x01, /* COLLECTION (Application) */ 0x85, HIDAPP_MOUSE_REPORT_ID, /* report ID (HIDAPP_MOUSE_REPORT_ID) */ 0x09, 0x01, /* USAGE (Pointer) */ 0xa1, 0x00, /* COLLECTION (Physical) */ 0x95, 0x03, /* REPORT_COUNT (3) */ 0x75, 0x01, /* REPORT_SIZE (1) */ 0x05, 0x09, /* USAGE_PAGE (Button) */ 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */ 0x95, 0x01, /* REPORT_COUNT (1) */ 0x75, 0x05, /* REPORT_SIZE (5) */ 0x81, 0x01, /* INPUT (Constant) */ 0x75, 0x08, /* REPORT_SIZE (8) */ 0x95, 0x02, /* REPORT_COUNT (2) */ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 0x09, 0x30, /* USAGE (X) */ 0x09, 0x31, /* USAGE (Y) */ 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ 0x81, 0x06, /* INPUT (Data, Variable, Relative) */ 0xc0, /* End Collection (Physical) */ 0xc0 /* End Collection (Application) */ }; const uint16_t hidReportMapLen = sizeof(hidReportMap); /*! HID Callback prototypes */ static void hidAppOutputCback(dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport); static void hidAppFeatureCback(dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport); static void hidAppInfoCback(dmConnId_t connId, uint8_t type, uint8_t value); /*! HID Profile Configuration */ static const hidConfig_t hidAppHidConfig = { (hidReportIdMap_t*) hidAppReportIdSet, /* Report ID to Attribute Handle map */ sizeof(hidAppReportIdSet)/sizeof(hidReportIdMap_t), /* Size of Report ID to Attribute Handle map */ &hidAppOutputCback, /* Output Report Callback */ &hidAppFeatureCback, /* Feature Report Callback */ &hidAppInfoCback /* Info Callback */ }; /************************************************************************************************** Local Variables **************************************************************************************************/ /*! application control block */ struct { wsfHandlerId_t handlerId; /* task handle */ /* Mouse pending event data */ uint8_t buttonMask; /* pending button mask */ uint8_t xDisplacement; /* pending X Displacement */ uint8_t yDisplacement; /* pending Y Displacement */ uint8_t devSpecific; /* pending Device Specific */ /* Keyboard pending event data */ uint8_t modifier; /* pending key modifiers */ uint8_t keys[KEYBOARD_IR_MAX_KEYS]; /* pending keys */ /* Remote pending event data */ uint8_t btnData; /* pending remote button data */ uint8_t testBtnState; /* identifier of test button action */ uint8_t reportId; /* pending reportId button data */ uint8_t txFlags; /* transmit flags */ uint8_t protocolMode; /* current protocol mode */ uint8_t hostSuspended; /* TRUE if host suspended */ } hidAppCb; /*************************************************************************************************/ /*! * \brief Application DM callback. * * \param pDmEvt DM callback event * * \return None. */ /*************************************************************************************************/ static void hidAppDmCback(dmEvt_t *pDmEvt) { dmEvt_t *pMsg; uint16_t len; len = DmSizeOfEvt(pDmEvt); if ((pMsg = WsfMsgAlloc(len)) != NULL) { memcpy(pMsg, pDmEvt, len); WsfMsgSend(hidAppCb.handlerId, pMsg); } } /*************************************************************************************************/ /*! * \brief Application ATT callback. * * \param pEvt ATT callback event * * \return None. */ /*************************************************************************************************/ static void hidAppAttCback(attEvt_t *pEvt) { attEvt_t *pMsg; if ((pMsg = WsfMsgAlloc(sizeof(attEvt_t) + pEvt->valueLen)) != NULL) { memcpy(pMsg, pEvt, sizeof(attEvt_t)); pMsg->pValue = (uint8_t *) (pMsg + 1); memcpy(pMsg->pValue, pEvt->pValue, pEvt->valueLen); WsfMsgSend(hidAppCb.handlerId, pMsg); } } /*************************************************************************************************/ /*! * \brief Application ATTS client characteristic configuration callback. * * \param pDmEvt DM callback event * * \return None. */ /*************************************************************************************************/ static void hidAppCccCback(attsCccEvt_t *pEvt) { appDbHdl_t dbHdl; /* If CCC not set from initialization and there's a device record and currently bonded */ if ((pEvt->handle != ATT_HANDLE_NONE) && ((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE) && AppCheckBonded((dmConnId_t)pEvt->hdr.param)) { /* Store value in device database. */ AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value); } } /*************************************************************************************************/ /*! * \brief Perform UI actions on connection open. * * \param pMsg Pointer to DM callback event message. * * \return None. */ /*************************************************************************************************/ static void hidAppOpen(dmEvt_t *pMsg) { } /*************************************************************************************************/ /*! * \brief Set up procedures that need to be performed after device reset. * * \param pMsg Pointer to DM callback event message. * * \return None. */ /*************************************************************************************************/ static void hidAppSetup(dmEvt_t *pMsg) { /* set advertising and scan response data for discoverable mode */ AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(hidAppAdvDataDisc), (uint8_t *) hidAppAdvDataDisc); AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(hidAppScanDataDisc), (uint8_t *) hidAppScanDataDisc); /* set advertising and scan response data for connectable mode */ AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(hidAppAdvDataDisc), (uint8_t *) hidAppAdvDataDisc); AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, sizeof(hidAppScanDataDisc), (uint8_t *) hidAppScanDataDisc); /* start advertising; automatically set connectable/discoverable mode and bondable mode */ AppAdvStart(APP_MODE_AUTO_INIT); } /*************************************************************************************************/ /*! * \brief Send example data. * * \param connId Connection identifier. * * \return None. */ /*************************************************************************************************/ static void hidAppMouseSendData(dmConnId_t connId) { uint8_t cccHandle = HIDAPP_IN_MOUSE_CCC_HDL; uint8_t protocolMode; protocolMode = HidGetProtocolMode(); if (protocolMode == HID_PROTOCOL_MODE_BOOT) { cccHandle = HIDAPP_MBI_CCC_HDL; } if (AttsCccEnabled(connId, cccHandle)) { if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_PENDING) { if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY) { uint8_t buffer[HIDAPP_MOUSE_INPUT_REPORT_LEN]; uint8_t reportId = HIDAPP_MOUSE_REPORT_ID; /* mouse record: button mask, x displacement, y displacement, device specific */ buffer[MOUSE_BUTTON_POS] = hidAppCb.buttonMask; buffer[MOUSE_X_POS] = hidAppCb.xDisplacement; buffer[MOUSE_Y_POS] = hidAppCb.yDisplacement; hidAppCb.txFlags &= ~(HIDAPP_TX_FLAGS_READY | HIDAPP_TX_FLAGS_PENDING); if (protocolMode == HID_PROTOCOL_MODE_BOOT) { reportId = HID_MOUSE_BOOT_ID; } /* Send the message */ HidSendInputReport(connId, reportId, HIDAPP_MOUSE_INPUT_REPORT_LEN, buffer); } } } else { hidAppCb.txFlags &= ~HIDAPP_TX_FLAGS_PENDING; } } /*************************************************************************************************/ /*! * \brief Send example data. * * \param connId Connection identifier. * * \return None. */ /*************************************************************************************************/ static void hidAppkeyboardSendData(dmConnId_t connId) { uint8_t cccHandle = HIDAPP_IN_KEYBOARD_CCC_HDL; uint8_t protocolMode; protocolMode = HidGetProtocolMode(); if (protocolMode == HID_PROTOCOL_MODE_BOOT) { cccHandle = HIDAPP_KBI_CCC_HDL; } if (AttsCccEnabled(connId, cccHandle)) { if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_PENDING) { if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY) { uint8_t buffer[HIDAPP_KEYBOARD_INPUT_REPORT_LEN]; uint8_t reportId = HIDAPP_KEYBOARD_REPORT_ID; /* modifier, reserved, keys[6] */ buffer[KEYBOARD_IR_MODIFIER_POS] = hidAppCb.modifier; buffer[KEYBOARD_IR_RESERVED_POS] = 0; memcpy(buffer + KEYBOARD_IR_KEY_POS, hidAppCb.keys, sizeof(hidAppCb.keys)); hidAppCb.txFlags &= ~(HIDAPP_TX_FLAGS_READY | HIDAPP_TX_FLAGS_PENDING); if (protocolMode == HID_PROTOCOL_MODE_BOOT) { reportId = HID_KEYBOARD_BOOT_ID; } /* Send the message */ HidSendInputReport(connId, reportId, HIDAPP_KEYBOARD_INPUT_REPORT_LEN, buffer); } } } else { hidAppCb.txFlags &= ~HIDAPP_TX_FLAGS_PENDING; } } /*************************************************************************************************/ /*! * \brief Send example data. * * \param connId Connection identifier. * * \return None. */ /*************************************************************************************************/ static void hidAppRemoteSendData(dmConnId_t connId) { if (AttsCccEnabled(connId, HIDAPP_IN_REMOTE_CCC_HDL)) { if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_PENDING) { if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY) { uint8_t buffer; /* bitmask of remote buttons */ buffer = hidAppCb.btnData; hidAppCb.txFlags &= ~(HIDAPP_TX_FLAGS_READY | HIDAPP_TX_FLAGS_PENDING); /* Send the message */ HidSendInputReport(connId, hidAppCb.reportId, HIDAPP_REMOTE_INPUT_REPORT_LEN, &buffer); } } } else { hidAppCb.txFlags &= ~HIDAPP_TX_FLAGS_PENDING; } } /*************************************************************************************************/ /*! * \brief Send example data. * * \param connId Connection identifier. * * \return None. */ /*************************************************************************************************/ static void hidAppSendData(dmConnId_t connId) { /* Send the report based on report ID */ switch(hidAppCb.reportId) { case HIDAPP_REMOTE_REPORT_ID: hidAppRemoteSendData(connId); break; case HIDAPP_KEYBOARD_REPORT_ID: hidAppkeyboardSendData(connId); break; case HIDAPP_MOUSE_REPORT_ID: hidAppMouseSendData(connId); break; default: break; } } /*************************************************************************************************/ /*! * \brief Send or queue a remote event to the host * * \param button Remote control depressed button * * \return None. */ /*************************************************************************************************/ static void hidAppRemoteReportEvent(uint8_t button) { dmConnId_t connId; if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE) { /* record key data */ hidAppCb.btnData = button; hidAppCb.reportId = HIDAPP_REMOTE_REPORT_ID; /* Indicate new data is pending */ hidAppCb.txFlags |= HIDAPP_TX_FLAGS_PENDING; /* send the data */ hidAppSendData(connId); } } /*************************************************************************************************/ /*! * \brief Send or queue a keyboard event to the host * * \param modifiers Keyboard modifiers. * \param keys Keyboard depressed keys. * \param numKeys Size of keys parameters in bytes * * \return None. */ /*************************************************************************************************/ static void hidAppKeyboardReportEvent(uint8_t modifiers, uint8_t keys[], uint8_t numKeys) { dmConnId_t connId; if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE) { /* max 6 keys in report map */ if (numKeys > KEYBOARD_IR_MAX_KEYS) { numKeys = KEYBOARD_IR_MAX_KEYS; } /* record key data */ hidAppCb.modifier = modifiers; memset(hidAppCb.keys, 0, sizeof(hidAppCb.keys)); memcpy(hidAppCb.keys, keys, numKeys); hidAppCb.reportId = HIDAPP_KEYBOARD_REPORT_ID; /* indicate new data is pending */ hidAppCb.txFlags |= HIDAPP_TX_FLAGS_PENDING; /* send the data */ hidAppSendData(connId); } } /*************************************************************************************************/ /*! * \brief Report or queue a mouse event to the host. * * \param buttonMask Mouse button mask. * \param xDisplacement Mouse displacement in the x direction. * \param yDisplacement Mouse displacement in the y direction. * * \return None. */ /*************************************************************************************************/ static void hidAppMouseReportEvent(uint8_t buttonMask, uint8_t xDisplacement, uint8_t yDisplacement) { dmConnId_t connId; if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE) { /* record mouse data */ hidAppCb.buttonMask = buttonMask; hidAppCb.xDisplacement = xDisplacement; hidAppCb.yDisplacement = yDisplacement; hidAppCb.devSpecific = 0; hidAppCb.reportId = HIDAPP_MOUSE_REPORT_ID; /* Indicate new data is pending */ hidAppCb.txFlags |= HIDAPP_TX_FLAGS_PENDING; /* send the data */ hidAppSendData(connId); } } /*************************************************************************************************/ /*! * \brief Change the test button. * * \param None. * * \return None. */ /*************************************************************************************************/ static void hidAppTestCycleButton(void) { /* Cycle the test button */ hidAppCb.testBtnState++; if (hidAppCb.testBtnState >= HIDAPP_NUM_TEST_BUTTONS) { hidAppCb.testBtnState = 0; } /* Display the active button */ switch(hidAppCb.testBtnState) { case HIDAPP_REMOTE_PLAY_PAUSE_BTN: APP_TRACE_INFO0("HidApp Test Button: Remote Play/Pause"); break; case HIDAPP_REMOTE_SCAN_NEXT_BTN: APP_TRACE_INFO0("HidApp Test Button: Remote Scan Next"); break; case HIDAPP_REMOTE_SCAN_PREVIOUS_BTN: APP_TRACE_INFO0("HidApp Test Button: Remote Scan Previous"); break; case HIDAPP_REMOTE_VOLUME_DOWN_BTN: APP_TRACE_INFO0("HidApp Test Button: Remote Volume Down"); break; case HIDAPP_REMOTE_VOLUME_UP_BTN: APP_TRACE_INFO0("HidApp Test Button: Remote Volume Up"); break; case HIDAPP_MOUSE_LEFT_BTN: APP_TRACE_INFO0("HidApp Test Button: Mouse Left"); break; case HIDAPP_MOUSE_RIGHT_BTN: APP_TRACE_INFO0("HidApp Test Button: Mouse Right"); break; case HIDAPP_KEYBOARD_UP_BTN: APP_TRACE_INFO0("HidApp Test Button: Keyboard Up"); break; case HIDAPP_KEYBOARD_DOWN_BTN: APP_TRACE_INFO0("HidApp Test Button: Keyboard Down"); break; case HIDAPP_KEYBOARD_LEFT_BTN: APP_TRACE_INFO0("HidApp Test Button: Keyboard Left"); break; case HIDAPP_KEYBOARD_RIGHT_BTN: APP_TRACE_INFO0("HidApp Test Button: Keyboard Right"); break; default: break; } } /*************************************************************************************************/ /*! * \brief Send test button press. * * \param None. * * \return None. */ /*************************************************************************************************/ static void hidAppTestSendButton(void) { uint8_t button; /* Send play command */ switch(hidAppCb.testBtnState) { case HIDAPP_REMOTE_PLAY_PAUSE_BTN: button = REMOTE_PLAY_PAUSE; hidAppRemoteReportEvent(button); break; case HIDAPP_REMOTE_SCAN_NEXT_BTN: button = REMOTE_SCAN_NEXT; hidAppRemoteReportEvent(button); break; case HIDAPP_REMOTE_SCAN_PREVIOUS_BTN: button = REMOTE_SCAN_PREVIOUS; hidAppRemoteReportEvent(button); break; case HIDAPP_REMOTE_VOLUME_DOWN_BTN: button = REMOTE_VOLUME_DOWN; hidAppRemoteReportEvent(button); break; case HIDAPP_REMOTE_VOLUME_UP_BTN: button = REMOTE_VOLUME_UP; hidAppRemoteReportEvent(button); break; case HIDAPP_MOUSE_LEFT_BTN: hidAppMouseReportEvent(MOUSE_BUTTON_LEFT, 0, 0); break; case HIDAPP_MOUSE_RIGHT_BTN: hidAppMouseReportEvent(MOUSE_BUTTON_RIGHT, 0, 0); break; case HIDAPP_KEYBOARD_UP_BTN: button = KEYBOARD_USAGE_UP_ARROW; hidAppKeyboardReportEvent(0, &button, 1); break; case HIDAPP_KEYBOARD_DOWN_BTN: button = KEYBOARD_USAGE_DOWN_ARROW; hidAppKeyboardReportEvent(0, &button, 1); break; case HIDAPP_KEYBOARD_LEFT_BTN: button = KEYBOARD_USAGE_LEFT_ARROW; hidAppKeyboardReportEvent(0, &button, 1); break; case HIDAPP_KEYBOARD_RIGHT_BTN: button = KEYBOARD_USAGE_RIGHT_ARROW; hidAppKeyboardReportEvent(0, &button, 1); break; default: return; } APP_TRACE_INFO0("HidApp: Test Button Sent"); } /*************************************************************************************************/ /*! * \brief Send the corresponding no button. * * \param None. * * \return None. */ /*************************************************************************************************/ static void hidAppTestNoButton(void) { uint8_t button; /* Send play command */ switch(hidAppCb.testBtnState) { case HIDAPP_REMOTE_PLAY_PAUSE_BTN: case HIDAPP_REMOTE_SCAN_NEXT_BTN: case HIDAPP_REMOTE_SCAN_PREVIOUS_BTN: case HIDAPP_REMOTE_VOLUME_DOWN_BTN: case HIDAPP_REMOTE_VOLUME_UP_BTN: button = REMOTE_USAGE_NONE; hidAppRemoteReportEvent(button); break; case HIDAPP_MOUSE_LEFT_BTN: case HIDAPP_MOUSE_RIGHT_BTN: hidAppMouseReportEvent(MOUSE_USAGE_NONE, 0, 0); break; case HIDAPP_KEYBOARD_UP_BTN: case HIDAPP_KEYBOARD_DOWN_BTN: case HIDAPP_KEYBOARD_LEFT_BTN: case HIDAPP_KEYBOARD_RIGHT_BTN: button = KEYBOARD_USAGE_NONE; hidAppKeyboardReportEvent(0, &button, 1); break; default: return; } } /*************************************************************************************************/ /*! * \brief Button press callback. * * \param btn Button press. * * \return None. */ /*************************************************************************************************/ static void hidAppBtnCback(uint8_t btn) { dmConnId_t connId = AppConnIsOpen(); /* button actions when connected */ if (connId != DM_CONN_ID_NONE) { switch (btn) { case APP_UI_BTN_1_SHORT: /* Send test command */ hidAppTestSendButton(); hidAppTestNoButton(); break; case APP_UI_BTN_2_SHORT: /* Change test button */ hidAppTestCycleButton(); break; case APP_UI_BTN_1_MED: case APP_UI_BTN_1_LONG: case APP_UI_BTN_1_EX_LONG: case APP_UI_BTN_2_MED: case APP_UI_BTN_2_LONG: case APP_UI_BTN_2_EX_LONG: /* Send no button */ hidAppTestNoButton(); break; default: break; } } /* button actions when not connected */ else { switch (btn) { case APP_UI_BTN_1_SHORT: /* start or restart advertising */ AppAdvStart(APP_MODE_AUTO_INIT); break; case APP_UI_BTN_1_MED: /* enter discoverable and bondable mode mode */ AppSetBondable(TRUE); AppAdvStart(APP_MODE_DISCOVERABLE); break; case APP_UI_BTN_1_LONG: /* clear all bonding info */ AppSlaveClearAllBondingInfo(); /* restart advertising */ AppAdvStart(APP_MODE_AUTO_INIT); break; default: break; } } } /*************************************************************************************************/ /*! * \brief Process messages from the event handler. * * \param pMsg Pointer to message. * * \return None. */ /*************************************************************************************************/ static void hidAppProcMsg(dmEvt_t *pMsg) { uint8_t uiEvent = APP_UI_NONE; switch(pMsg->hdr.event) { case ATTS_HANDLE_VALUE_CNF: if (pMsg->hdr.status == ATT_SUCCESS) { hidAppCb.txFlags |= HIDAPP_TX_FLAGS_READY; hidAppSendData((dmConnId_t) pMsg->hdr.param); } break; case HIDAPP_BATT_TIMER_IND: BasProcMsg(&pMsg->hdr); break; case ATT_MTU_UPDATE_IND: APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu); break; case DM_RESET_CMPL_IND: AttsCalculateDbHash(); DmSecGenerateEccKeyReq(); hidAppSetup(pMsg); uiEvent = APP_UI_RESET_CMPL; break; case DM_CONN_OPEN_IND: hidAppOpen(pMsg); uiEvent = APP_UI_CONN_OPEN; break; case DM_CONN_CLOSE_IND: uiEvent = APP_UI_CONN_CLOSE; break; case DM_SEC_PAIR_CMPL_IND: DmSecGenerateEccKeyReq(); uiEvent = APP_UI_SEC_PAIR_CMPL; break; case DM_SEC_PAIR_FAIL_IND: DmSecGenerateEccKeyReq(); uiEvent = APP_UI_SEC_PAIR_FAIL; break; case DM_SEC_ENCRYPT_IND: uiEvent = APP_UI_SEC_ENCRYPT; break; case DM_SEC_ENCRYPT_FAIL_IND: uiEvent = APP_UI_SEC_ENCRYPT_FAIL; break; case DM_SEC_AUTH_REQ_IND: AppHandlePasskey(&pMsg->authReq); break; case DM_SEC_ECC_KEY_IND: DmSecSetEccKey(&pMsg->eccMsg.data.key); break; case DM_SEC_COMPARE_IND: AppHandleNumericComparison(&pMsg->cnfInd); break; case DM_PRIV_CLEAR_RES_LIST_IND: APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->hdr.status); break; case DM_VENDOR_SPEC_CMD_CMPL_IND: { #if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2) uint8_t *param_ptr = &pMsg->vendorSpecCmdCmpl.param[0]; switch (pMsg->vendorSpecCmdCmpl.opcode) { case 0xFC20: //read at address { uint32_t read_value; BSTREAM_TO_UINT32(read_value, param_ptr); APP_TRACE_INFO3("VSC 0x%0x complete status %x param %x", pMsg->vendorSpecCmdCmpl.opcode, pMsg->hdr.status, read_value); } break; default: APP_TRACE_INFO2("VSC 0x%0x complete status %x", pMsg->vendorSpecCmdCmpl.opcode, pMsg->hdr.status); break; } #endif } break; default: break; } if (uiEvent != APP_UI_NONE) { AppUiAction(uiEvent); } } /*************************************************************************************************/ /*! * \brief Callback to handle an output report from the host. * * \param connId The connection identifier. * \param id The ID of the report. * \param len The length of the report data in pReport. * \param pReport A buffer containing the report. * * \return None. */ /*************************************************************************************************/ static void hidAppOutputCback(dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport) { /* TODO: process output reports */ } /*************************************************************************************************/ /*! * \brief Callback to handle a feature report from the host. * * \param connId The connection identifier. * \param id The ID of the report. * \param len The length of the report data in pReport. * \param pReport A buffer containing the report. * * \return None. */ /*************************************************************************************************/ static void hidAppFeatureCback(dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport) { /* TODO: process feature reports */ } /*************************************************************************************************/ /*! * \brief Callback to handle a change in protocol mode or control point from the host. * * \param connId The connection identifier. * \param mode The type of information (HID_INFO_CONTROL_POINT or HID_INFO_PROTOCOL_MODE) * \param value The value of the information * * \return None. */ /*************************************************************************************************/ static void hidAppInfoCback(dmConnId_t connId, uint8_t type, uint8_t value) { if (type == HID_INFO_PROTOCOL_MODE) { hidAppCb.protocolMode = value; } else if (type == HID_INFO_CONTROL_POINT) { hidAppCb.hostSuspended = (value == HID_CONTROL_POINT_SUSPEND) ? TRUE : FALSE; } } /*************************************************************************************************/ /*! * \brief Initialize the report attributes to default values for the hidApp. * * \param None. * * \return None. */ /*************************************************************************************************/ static void hidAppReportInit(void) { uint8_t iKeyboardBuffer[HIDAPP_KEYBOARD_INPUT_REPORT_LEN]; uint8_t iMouseBuffer[HIDAPP_MOUSE_INPUT_REPORT_LEN]; uint8_t iRemoteBuffer[HIDAPP_REMOTE_INPUT_REPORT_LEN]; uint8_t oBuffer[HIDAPP_OUTPUT_REPORT_LEN]; uint8_t fBuffer[HIDAPP_FEATURE_REPORT_LEN]; /* Remote Input report */ memset(iRemoteBuffer, 0, HIDAPP_REMOTE_INPUT_REPORT_LEN); AttsSetAttr(HID_INPUT_REPORT_1_HDL, HIDAPP_REMOTE_INPUT_REPORT_LEN, iRemoteBuffer); /* Keyboard Input report */ memset(iKeyboardBuffer, 0, HIDAPP_KEYBOARD_INPUT_REPORT_LEN); AttsSetAttr(HID_INPUT_REPORT_2_HDL, HIDAPP_KEYBOARD_INPUT_REPORT_LEN, iKeyboardBuffer); /* Mouse Input report */ memset(iMouseBuffer, 0, HIDAPP_MOUSE_INPUT_REPORT_LEN); AttsSetAttr(HID_INPUT_REPORT_3_HDL, HIDAPP_MOUSE_INPUT_REPORT_LEN, iMouseBuffer); /* Output report */ memset(oBuffer, 0, HIDAPP_OUTPUT_REPORT_LEN); AttsSetAttr(HID_OUTPUT_REPORT_HDL, HIDAPP_OUTPUT_REPORT_LEN, oBuffer); /* Feature report */ memset(fBuffer, 0, HIDAPP_FEATURE_REPORT_LEN); AttsSetAttr(HID_FEATURE_REPORT_HDL, HIDAPP_FEATURE_REPORT_LEN, fBuffer); } /*************************************************************************************************/ /*! * \brief Application handler init function called during system initialization. * * \param handlerID WSF handler ID. * * \return None. */ /*************************************************************************************************/ void HidAppHandlerInit(wsfHandlerId_t handlerId) { APP_TRACE_INFO0("HidAppHandlerInit"); /* Initialize the control block */ memset(&hidAppCb, 0, sizeof(hidAppCb)); hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY; /* store handler ID */ hidAppCb.handlerId = handlerId; /* Set configuration pointers */ pAppSlaveCfg = (appSlaveCfg_t *) &hidAppSlaveCfg; pAppAdvCfg = (appAdvCfg_t *) &hidAppAdvCfg; pAppSecCfg = (appSecCfg_t *) &hidAppSecCfg; pAppUpdateCfg = (appUpdateCfg_t *) &hidAppUpdateCfg; /* Initialize application framework */ AppSlaveInit(); AppServerInit(); /* initialize battery service server */ BasInit(handlerId, (basCfg_t *) &hidAppBasCfg); } /*************************************************************************************************/ /*! * \brief WSF event handler for application. * * \param event WSF event mask. * \param pMsg WSF message. * * \return None. */ /*************************************************************************************************/ void HidAppHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg) { if (pMsg != NULL) { APP_TRACE_INFO1("HidApp got evt %d", pMsg->event); /* process ATT messages */ if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END) { /* process server-related ATT messages */ AppServerProcAttMsg(pMsg); } /* process DM messages */ else if (pMsg->event <= DM_CBACK_END) { /* process advertising and connection-related messages */ AppSlaveProcDmMsg((dmEvt_t *) pMsg); /* process security-related messages */ AppSlaveSecProcDmMsg((dmEvt_t *) pMsg); } /* perform profile and user interface-related operations */ hidAppProcMsg((dmEvt_t *) pMsg); } } /*************************************************************************************************/ /*! * \brief Test a hidApp button event. * * \param None. * * \return None. */ /*************************************************************************************************/ void HidAppTest(void) { hidAppBtnCback(APP_UI_BTN_1_DOWN); hidAppBtnCback(APP_UI_BTN_1_SHORT); } /*************************************************************************************************/ /*! * \brief Start the application. * * \param None. * * \return None. */ /*************************************************************************************************/ void HidAppStart(void) { #ifdef HID_ATT_DYNAMIC void *pSHdl; #endif /* HID_ATT_DYNAMIC */ /* Register for stack callbacks */ DmRegister(hidAppDmCback); DmConnRegister(DM_CLIENT_ID_APP, hidAppDmCback); AttRegister(hidAppAttCback); AttConnRegister(AppServerConnCback); AttsCccRegister(HIDAPP_NUM_CCC_IDX, (attsCccSet_t *) hidAppCccSet, hidAppCccCback); /* Register for app framework button callbacks */ AppUiBtnRegister(hidAppBtnCback); /* Initialize attribute server database */ SvcCoreGattCbackRegister(GattReadCback, GattWriteCback); SvcCoreAddGroup(); SvcDisAddGroup(); #ifdef HID_ATT_DYNAMIC /* Initialize the dynamic service system */ AttsDynInit(); /* Add the HID service dynamically */ pSHdl = SvcHidAddGroupDyn(); AttsDynRegister(pSHdl, NULL, HidAttsWriteCback); /* Add the Battery service dynamically */ pSHdl = SvcBattAddGroupDyn(); AttsDynRegister(pSHdl, BasReadCback, NULL); #else /* Add the HID service statically */ SvcHidAddGroup(); SvcHidRegister(HidAttsWriteCback, NULL); /* Add the Battery service statically */ SvcBattAddGroup(); SvcBattCbackRegister(BasReadCback, NULL); #endif /* HID_ATT_DYNAMIC */ /* Set Service Changed CCCD index. */ GattSetSvcChangedIdx(HIDAPP_GATT_SC_CCC_IDX); /* Initialize the HID profile */ HidInit(&hidAppHidConfig); /* Initialize the report attributes */ hidAppReportInit(); /* Reset the device */ DmDevReset(); }