2024-11-19 21:24:24 +00:00
|
|
|
/*
|
|
|
|
|
* Copyright 2015-2018 Hillcrest Laboratories, Inc.
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License and
|
|
|
|
|
* any applicable agreements you may have with Hillcrest Laboratories, Inc.
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @file sh2.c
|
|
|
|
|
* @author David Wheeler
|
|
|
|
|
* @date 22 Sept 2015
|
|
|
|
|
* @brief API Definition for Hillcrest SH-2 Sensor Hub.
|
|
|
|
|
*
|
|
|
|
|
* The sh2 API provides functions for opening a session with
|
|
|
|
|
* the sensor hub and performing all supported operations with it.
|
|
|
|
|
* This includes enabling sensors and reading events as well as
|
|
|
|
|
* other housekeeping functions.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "sh2.h"
|
|
|
|
|
#include "sh2_err.h"
|
|
|
|
|
#include "shtp.h"
|
|
|
|
|
#include "sh2_util.h"
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Private type definitions
|
|
|
|
|
|
|
|
|
|
#define GUID_EXECUTABLE (1)
|
|
|
|
|
#define GUID_SENSORHUB (2)
|
|
|
|
|
|
|
|
|
|
// executable/device channel responses
|
|
|
|
|
#define EXECUTABLE_DEVICE_CMD_RESET (1)
|
|
|
|
|
#define EXECUTABLE_DEVICE_CMD_ON (2)
|
|
|
|
|
#define EXECUTABLE_DEVICE_CMD_SLEEP (3)
|
|
|
|
|
|
|
|
|
|
// executable/device channel responses
|
|
|
|
|
#define EXECUTABLE_DEVICE_RESP_RESET_COMPLETE (1)
|
|
|
|
|
|
|
|
|
|
// Tags for sensorhub app advertisements.
|
|
|
|
|
#define TAG_SH2_VERSION (0x80)
|
|
|
|
|
#define TAG_SH2_REPORT_LENGTHS (0x81)
|
|
|
|
|
|
|
|
|
|
// Max length of sensorhub version string.
|
|
|
|
|
#define MAX_VER_LEN (16)
|
|
|
|
|
|
|
|
|
|
// Max number of report ids supported
|
|
|
|
|
#define SH2_MAX_REPORT_IDS (64)
|
|
|
|
|
|
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
|
#define PACKED_STRUCT struct
|
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
|
#elif defined(__GNUC__)
|
|
|
|
|
#define PACKED_STRUCT struct __attribute__((packed))
|
|
|
|
|
#else
|
|
|
|
|
#define PACKED_STRUCT __packed struct
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define ADVERT_TIMEOUT_US (200000)
|
|
|
|
|
|
|
|
|
|
// Command and Subcommand values
|
|
|
|
|
#define SH2_CMD_ERRORS 1
|
|
|
|
|
#define SH2_CMD_COUNTS 2
|
|
|
|
|
#define SH2_COUNTS_GET_COUNTS 0
|
|
|
|
|
#define SH2_COUNTS_CLEAR_COUNTS 1
|
|
|
|
|
#define SH2_CMD_TARE 3
|
|
|
|
|
#define SH2_TARE_TARE_NOW 0
|
|
|
|
|
#define SH2_TARE_PERSIST_TARE 1
|
|
|
|
|
#define SH2_TARE_SET_REORIENTATION 2
|
|
|
|
|
#define SH2_CMD_INITIALIZE 4
|
|
|
|
|
#define SH2_INIT_SYSTEM 1
|
|
|
|
|
#define SH2_INIT_UNSOLICITED 0x80
|
|
|
|
|
// #define SH2_CMD_FRS 5 /* Depreciated */
|
|
|
|
|
#define SH2_CMD_DCD 6
|
|
|
|
|
#define SH2_CMD_ME_CAL 7
|
|
|
|
|
#define SH2_CMD_DCD_SAVE 9
|
|
|
|
|
#define SH2_CMD_GET_OSC_TYPE 0x0A
|
|
|
|
|
#define SH2_CMD_CLEAR_DCD_AND_RESET 0x0B
|
|
|
|
|
#define SH2_CMD_CAL 0x0C
|
|
|
|
|
#define SH2_CAL_START 0
|
|
|
|
|
#define SH2_CAL_FINISH 1
|
|
|
|
|
#define SH2_CMD_BOOTLOADER 0x0D /* SH-2 Reference Manual 6.4.12 */
|
|
|
|
|
#define SH2_BL_MODE_REQ 0
|
|
|
|
|
#define SH2_BL_STATUS_REQ 1
|
|
|
|
|
#define SH2_CMD_INTERACTIVE_ZRO 0x0E /* SH-2 Reference Manual 6.4.13 */
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_COMMAND_REQ
|
|
|
|
|
#define SENSORHUB_COMMAND_REQ (0xF2)
|
|
|
|
|
#define COMMAND_PARAMS (9)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t seq;
|
|
|
|
|
uint8_t command;
|
|
|
|
|
uint8_t p[COMMAND_PARAMS];
|
|
|
|
|
} CommandReq_t;
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_COMMAND_RESP
|
|
|
|
|
#define SENSORHUB_COMMAND_RESP (0xF1)
|
|
|
|
|
#define RESPONSE_VALUES (11)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t seq;
|
|
|
|
|
uint8_t command;
|
|
|
|
|
uint8_t commandSeq;
|
|
|
|
|
uint8_t respSeq;
|
|
|
|
|
uint8_t r[RESPONSE_VALUES];
|
|
|
|
|
} CommandResp_t;
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_PROD_ID_REQ
|
|
|
|
|
#define SENSORHUB_PROD_ID_REQ (0xF9)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t reserved;
|
|
|
|
|
} ProdIdReq_t;
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_PROD_ID_RESP
|
|
|
|
|
#define SENSORHUB_PROD_ID_RESP (0xF8)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t resetCause;
|
|
|
|
|
uint8_t swVerMajor;
|
|
|
|
|
uint8_t swVerMinor;
|
|
|
|
|
uint32_t swPartNumber;
|
|
|
|
|
uint32_t swBuildNumber;
|
|
|
|
|
uint16_t swVerPatch;
|
|
|
|
|
uint8_t reserved0;
|
|
|
|
|
uint8_t reserved1;
|
|
|
|
|
} ProdIdResp_t;
|
|
|
|
|
|
|
|
|
|
// Report definitions
|
|
|
|
|
// Bit fields for Feature Report flags
|
|
|
|
|
#define FEAT_CHANGE_SENSITIVITY_RELATIVE (1)
|
|
|
|
|
#define FEAT_CHANGE_SENSITIVITY_ABSOLUTE (0)
|
|
|
|
|
#define FEAT_CHANGE_SENSITIVITY_ENABLED (2)
|
|
|
|
|
#define FEAT_CHANGE_SENSITIVITY_DISABLED (0)
|
|
|
|
|
#define FEAT_WAKE_ENABLED (4)
|
|
|
|
|
#define FEAT_WAKE_DISABLED (0)
|
|
|
|
|
#define FEAT_ALWAYS_ON_ENABLED (8)
|
|
|
|
|
#define FEAT_ALWAYS_ON_DISABLED (0)
|
|
|
|
|
|
|
|
|
|
// GET_FEATURE_REQ
|
|
|
|
|
#define SENSORHUB_GET_FEATURE_REQ (0xFE)
|
|
|
|
|
typedef PACKED_STRUCT{
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t featureReportId;
|
|
|
|
|
} GetFeatureReq_t;
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_GET_FEATURE_RESP
|
|
|
|
|
#define SENSORHUB_GET_FEATURE_RESP (0xFC)
|
|
|
|
|
typedef PACKED_STRUCT{
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t featureReportId; // sensor id
|
|
|
|
|
uint8_t flags; // FEAT_... values
|
|
|
|
|
uint16_t changeSensitivity;
|
|
|
|
|
uint32_t reportInterval_uS;
|
|
|
|
|
uint32_t batchInterval_uS;
|
|
|
|
|
uint32_t sensorSpecific;
|
|
|
|
|
} GetFeatureResp_t;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct sh2_s sh2_t;
|
|
|
|
|
|
|
|
|
|
typedef int (sh2_OpStart_t)(sh2_t *pSh2);
|
|
|
|
|
typedef void (sh2_OpRx_t)(sh2_t *pSh2, const uint8_t *payload, uint16_t len);
|
|
|
|
|
|
|
|
|
|
typedef struct sh2_Op_s {
|
|
|
|
|
uint32_t timeout_us;
|
|
|
|
|
sh2_OpStart_t *start;
|
|
|
|
|
sh2_OpRx_t *rx;
|
|
|
|
|
} sh2_Op_t;
|
|
|
|
|
|
|
|
|
|
// Parameters and state information for the operation in progress
|
|
|
|
|
typedef union {
|
|
|
|
|
struct {
|
|
|
|
|
CommandReq_t req;
|
|
|
|
|
} sendCmd;
|
|
|
|
|
struct {
|
|
|
|
|
sh2_ProductIds_t *pProdIds;
|
|
|
|
|
uint8_t nextEntry;
|
|
|
|
|
uint8_t expectedEntries;
|
|
|
|
|
} getProdIds;
|
|
|
|
|
struct {
|
|
|
|
|
sh2_SensorConfig_t *pConfig;
|
|
|
|
|
sh2_SensorId_t sensorId;
|
|
|
|
|
} getSensorConfig;
|
|
|
|
|
struct {
|
|
|
|
|
const sh2_SensorConfig_t *pConfig;
|
|
|
|
|
sh2_SensorId_t sensorId;
|
|
|
|
|
} setSensorConfig;
|
|
|
|
|
struct {
|
|
|
|
|
uint16_t frsType;
|
|
|
|
|
uint32_t *pData;
|
|
|
|
|
uint16_t *pWords;
|
|
|
|
|
uint16_t nextOffset;
|
|
|
|
|
} getFrs;
|
|
|
|
|
struct {
|
|
|
|
|
uint16_t frsType;
|
|
|
|
|
uint32_t *pData;
|
|
|
|
|
uint16_t words;
|
|
|
|
|
uint16_t offset;
|
|
|
|
|
} setFrs;
|
|
|
|
|
struct {
|
|
|
|
|
uint8_t severity;
|
|
|
|
|
sh2_ErrorRecord_t *pErrors;
|
|
|
|
|
uint16_t *pNumErrors;
|
|
|
|
|
uint16_t errsRead;
|
|
|
|
|
} getErrors;
|
|
|
|
|
struct {
|
|
|
|
|
sh2_SensorId_t sensorId;
|
|
|
|
|
sh2_Counts_t *pCounts;
|
|
|
|
|
} getCounts;
|
|
|
|
|
struct {
|
|
|
|
|
uint8_t sensors;
|
|
|
|
|
} calConfig;
|
|
|
|
|
struct {
|
|
|
|
|
uint8_t *pSensors;
|
|
|
|
|
} getCalConfig;
|
|
|
|
|
struct {
|
|
|
|
|
sh2_SensorId_t sensorId;
|
|
|
|
|
} forceFlush;
|
|
|
|
|
struct {
|
|
|
|
|
sh2_OscType_t *pOscType;
|
|
|
|
|
} getOscType;
|
|
|
|
|
struct {
|
|
|
|
|
uint32_t interval_us;
|
|
|
|
|
} startCal;
|
|
|
|
|
struct {
|
|
|
|
|
sh2_CalStatus_t status;
|
|
|
|
|
} finishCal;
|
|
|
|
|
} sh2_OpData_t;
|
|
|
|
|
|
|
|
|
|
// Max length of an FRS record, words.
|
|
|
|
|
#define MAX_FRS_WORDS (72)
|
|
|
|
|
|
|
|
|
|
struct sh2_s {
|
|
|
|
|
// Pointer to the SHTP HAL
|
|
|
|
|
sh2_Hal_t *pHal;
|
|
|
|
|
|
|
|
|
|
// associated SHTP instance
|
|
|
|
|
void *pShtp;
|
|
|
|
|
|
|
|
|
|
volatile bool resetComplete;
|
|
|
|
|
bool advertDone;
|
|
|
|
|
uint8_t executableChan;
|
|
|
|
|
uint8_t controlChan;
|
|
|
|
|
char version[MAX_VER_LEN+1];
|
|
|
|
|
|
|
|
|
|
// Report lengths
|
|
|
|
|
struct {
|
|
|
|
|
uint8_t id;
|
|
|
|
|
uint8_t len;
|
|
|
|
|
} report[SH2_MAX_REPORT_IDS];
|
|
|
|
|
|
|
|
|
|
// Multi-step operation support
|
|
|
|
|
const sh2_Op_t *pOp;
|
|
|
|
|
int opStatus;
|
|
|
|
|
sh2_OpData_t opData;
|
|
|
|
|
uint8_t lastCmdId;
|
|
|
|
|
uint8_t cmdSeq;
|
|
|
|
|
uint8_t nextCmdSeq;
|
|
|
|
|
|
|
|
|
|
// Event callback and it's cookie
|
|
|
|
|
sh2_EventCallback_t *eventCallback;
|
|
|
|
|
void * eventCookie;
|
|
|
|
|
|
|
|
|
|
// Sensor callback and it's cookie
|
|
|
|
|
sh2_SensorCallback_t *sensorCallback;
|
|
|
|
|
void * sensorCookie;
|
|
|
|
|
|
|
|
|
|
// Storage space for reading sensor metadata
|
|
|
|
|
uint32_t frsData[MAX_FRS_WORDS];
|
|
|
|
|
uint16_t frsDataLen;
|
|
|
|
|
|
|
|
|
|
// Stats
|
|
|
|
|
uint32_t execBadPayload;
|
|
|
|
|
uint32_t emptyPayloads;
|
|
|
|
|
uint32_t unknownReportIds;
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define SENSORHUB_BASE_TIMESTAMP_REF (0xFB)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint32_t timebase;
|
|
|
|
|
} BaseTimestampRef_t;
|
|
|
|
|
|
|
|
|
|
#define SENSORHUB_TIMESTAMP_REBASE (0xFA)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
int32_t timebase;
|
|
|
|
|
} TimestampRebase_t;
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_FORCE_SENSOR_FLUSH
|
|
|
|
|
#define SENSORHUB_FORCE_SENSOR_FLUSH (0xF0)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t sensorId;
|
|
|
|
|
} ForceFlushReq_t;
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_FLUSH_COMPLETED
|
|
|
|
|
#define SENSORHUB_FLUSH_COMPLETED (0xEF)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t sensorId;
|
|
|
|
|
} ForceFlushResp_t;
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Private data
|
|
|
|
|
|
|
|
|
|
// SH2 state
|
|
|
|
|
sh2_t _sh2;
|
|
|
|
|
|
|
|
|
|
// SH2 Async Event Message
|
|
|
|
|
static sh2_AsyncEvent_t sh2AsyncEvent;
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Private functions
|
|
|
|
|
|
|
|
|
|
// SH-2 transaction phases
|
|
|
|
|
static int opStart(sh2_t *pSh2, const sh2_Op_t *pOp)
|
|
|
|
|
{
|
|
|
|
|
// return error if another operation already in progress
|
|
|
|
|
if (pSh2->pOp) return SH2_ERR_OP_IN_PROGRESS;
|
|
|
|
|
|
|
|
|
|
// Establish this operation as the new operation in progress
|
|
|
|
|
pSh2->pOp = pOp;
|
|
|
|
|
pSh2->opStatus = SH2_OK;
|
|
|
|
|
int rc = pOp->start(pSh2); // Call start method
|
|
|
|
|
if (rc != SH2_OK) {
|
|
|
|
|
// Unregister this operation
|
|
|
|
|
pSh2->opStatus = rc;
|
|
|
|
|
pSh2->pOp = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void opRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
if ((pSh2->pOp != 0) && // An operation is in progress
|
|
|
|
|
(pSh2->pOp->rx != 0)) { // and it has an rx method
|
|
|
|
|
pSh2->pOp->rx(pSh2, payload, len); // Call receive method
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void sensorhubAdvertHdlr(void *cookie, uint8_t tag, uint8_t len, uint8_t *value)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = (sh2_t *)cookie;
|
|
|
|
|
|
|
|
|
|
switch (tag) {
|
|
|
|
|
case TAG_SH2_VERSION:
|
|
|
|
|
strcpy(pSh2->version, (const char *)value);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TAG_SH2_REPORT_LENGTHS:
|
|
|
|
|
{
|
|
|
|
|
uint8_t reports = len/2;
|
|
|
|
|
if (reports > SH2_MAX_REPORT_IDS) {
|
|
|
|
|
// Hub gave us more report lengths than we can store!
|
|
|
|
|
reports = SH2_MAX_REPORT_IDS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int n = 0; n < reports; n++) {
|
|
|
|
|
pSh2->report[n].id = value[n*2];
|
|
|
|
|
pSh2->report[n].len = value[n*2 + 1];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
|
{
|
|
|
|
|
// 0 tag indicates end of advertisements for this app
|
|
|
|
|
// At this time, the SHTP layer can give us our channel numbers
|
|
|
|
|
pSh2->executableChan = shtp_chanNo(pSh2->pShtp, "executable", "device");
|
|
|
|
|
pSh2->controlChan = shtp_chanNo(pSh2->pShtp, "sensorhub", "control");
|
|
|
|
|
|
|
|
|
|
pSh2->advertDone = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void sensorhubControlHdlr(void *cookie, uint8_t *payload, uint16_t len, uint32_t timestamp)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = (sh2_t *)cookie;
|
|
|
|
|
|
|
|
|
|
uint16_t cursor = 0;
|
|
|
|
|
uint32_t count = 0;
|
|
|
|
|
CommandResp_t * pResp = 0;
|
|
|
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
|
pSh2->emptyPayloads++;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (cursor < len) {
|
|
|
|
|
// Get next report id
|
|
|
|
|
count++;
|
|
|
|
|
uint8_t reportId = payload[cursor];
|
|
|
|
|
|
|
|
|
|
// Determine report length
|
|
|
|
|
uint8_t reportLen = 0;
|
|
|
|
|
for (int n = 0; n < SH2_MAX_REPORT_IDS; n++) {
|
|
|
|
|
if (pSh2->report[n].id == reportId) {
|
|
|
|
|
reportLen = pSh2->report[n].len;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (reportLen == 0) {
|
|
|
|
|
// An unrecognized report id
|
|
|
|
|
pSh2->unknownReportIds++;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Check for unsolicited initialize response
|
|
|
|
|
if (reportId == SENSORHUB_COMMAND_RESP) {
|
|
|
|
|
pResp = (CommandResp_t *)(payload+cursor);
|
|
|
|
|
if ((pResp->command == (SH2_CMD_INITIALIZE | SH2_INIT_UNSOLICITED)) &&
|
|
|
|
|
(pResp->r[1] == SH2_INIT_SYSTEM)) {
|
|
|
|
|
// This is an unsolicited INIT message.
|
|
|
|
|
// Is it time to call reset callback?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // Check for Get Feature Response
|
|
|
|
|
else if (reportId == SENSORHUB_GET_FEATURE_RESP) {
|
|
|
|
|
if (pSh2->eventCallback) {
|
|
|
|
|
GetFeatureResp_t * pGetFeatureResp;
|
|
|
|
|
pGetFeatureResp = (GetFeatureResp_t *)(payload + cursor);
|
|
|
|
|
|
|
|
|
|
sh2AsyncEvent.eventId = SH2_GET_FEATURE_RESP;
|
|
|
|
|
sh2AsyncEvent.sh2SensorConfigResp.sensorId = pGetFeatureResp->featureReportId;
|
|
|
|
|
sh2AsyncEvent.sh2SensorConfigResp.sensorConfig.changeSensitivityEnabled = ((pGetFeatureResp->flags & FEAT_CHANGE_SENSITIVITY_ENABLED) != 0);
|
|
|
|
|
sh2AsyncEvent.sh2SensorConfigResp.sensorConfig.changeSensitivityRelative = ((pGetFeatureResp->flags & FEAT_CHANGE_SENSITIVITY_RELATIVE) != 0);
|
|
|
|
|
sh2AsyncEvent.sh2SensorConfigResp.sensorConfig.wakeupEnabled = ((pGetFeatureResp->flags & FEAT_WAKE_ENABLED) != 0);
|
|
|
|
|
sh2AsyncEvent.sh2SensorConfigResp.sensorConfig.alwaysOnEnabled = ((pGetFeatureResp->flags & FEAT_ALWAYS_ON_ENABLED) != 0);
|
|
|
|
|
sh2AsyncEvent.sh2SensorConfigResp.sensorConfig.changeSensitivity = pGetFeatureResp->changeSensitivity;
|
|
|
|
|
sh2AsyncEvent.sh2SensorConfigResp.sensorConfig.reportInterval_us = pGetFeatureResp->reportInterval_uS;
|
|
|
|
|
sh2AsyncEvent.sh2SensorConfigResp.sensorConfig.batchInterval_us = pGetFeatureResp->batchInterval_uS;
|
|
|
|
|
sh2AsyncEvent.sh2SensorConfigResp.sensorConfig.sensorSpecific = pGetFeatureResp->sensorSpecific;
|
|
|
|
|
|
|
|
|
|
pSh2->eventCallback(pSh2->eventCookie, &sh2AsyncEvent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hand off to operation in progress, if any
|
|
|
|
|
opRx(pSh2, payload+cursor, reportLen);
|
|
|
|
|
cursor += reportLen;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int opCompleted(sh2_t *pSh2, int status)
|
|
|
|
|
{
|
|
|
|
|
// Record status
|
|
|
|
|
pSh2->opStatus = status;
|
|
|
|
|
|
|
|
|
|
// Signal that op is done.
|
|
|
|
|
pSh2->pOp = 0;
|
|
|
|
|
|
|
|
|
|
return SH2_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int opProcess(sh2_t *pSh2, const sh2_Op_t *pOp)
|
|
|
|
|
{
|
|
|
|
|
int status = SH2_OK;
|
|
|
|
|
uint32_t start_us = 0;
|
|
|
|
|
|
|
|
|
|
start_us = pSh2->pHal->getTimeUs(pSh2->pHal);
|
|
|
|
|
|
|
|
|
|
status = opStart(&_sh2, pOp);
|
|
|
|
|
if (status != SH2_OK) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t now_us = start_us;
|
|
|
|
|
|
|
|
|
|
// While op not complete and not timed out.
|
|
|
|
|
while ((pSh2->pOp != 0) &&
|
|
|
|
|
((pOp->timeout_us == 0) ||
|
|
|
|
|
((now_us-start_us) < pOp->timeout_us))) {
|
|
|
|
|
// Service SHTP to poll the device.
|
|
|
|
|
shtp_service(pSh2->pShtp);
|
|
|
|
|
|
|
|
|
|
// Update the time
|
|
|
|
|
now_us = pSh2->pHal->getTimeUs(pSh2->pHal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pSh2->pOp != 0) {
|
|
|
|
|
// Operation has timed out. Clean up.
|
|
|
|
|
pSh2->pOp = 0;
|
|
|
|
|
pSh2->opStatus = SH2_ERR_TIMEOUT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pSh2->opStatus;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t getReportLen(sh2_t *pSh2, uint8_t reportId)
|
|
|
|
|
{
|
|
|
|
|
for (int n = 0; n < SH2_MAX_REPORT_IDS; n++) {
|
|
|
|
|
if (pSh2->report[n].id == reportId) {
|
|
|
|
|
return pSh2->report[n].len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Produce 64-bit microsecond timestamp for a sensor event
|
|
|
|
|
static uint64_t touSTimestamp(uint32_t hostInt, int32_t referenceDelta, uint16_t delay)
|
|
|
|
|
{
|
|
|
|
|
static uint32_t lastHostInt = 0;
|
|
|
|
|
static uint32_t rollovers = 0;
|
|
|
|
|
uint64_t timestamp;
|
|
|
|
|
|
|
|
|
|
// Count times hostInt timestamps rolled over to produce upper bits
|
|
|
|
|
if (hostInt < lastHostInt) {
|
|
|
|
|
rollovers++;
|
|
|
|
|
}
|
|
|
|
|
lastHostInt = hostInt;
|
|
|
|
|
|
|
|
|
|
timestamp = ((uint64_t)rollovers << 32);
|
|
|
|
|
timestamp += hostInt + (referenceDelta + delay) * 100;
|
|
|
|
|
|
|
|
|
|
return timestamp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void sensorhubInputHdlr(sh2_t *pSh2, uint8_t *payload, uint16_t len, uint32_t timestamp)
|
|
|
|
|
{
|
|
|
|
|
sh2_SensorEvent_t event;
|
|
|
|
|
uint16_t cursor = 0;
|
|
|
|
|
|
|
|
|
|
uint32_t referenceDelta;
|
|
|
|
|
|
|
|
|
|
referenceDelta = 0;
|
|
|
|
|
|
|
|
|
|
while (cursor < len) {
|
|
|
|
|
// Get next report id
|
|
|
|
|
uint8_t reportId = payload[cursor];
|
|
|
|
|
|
|
|
|
|
// Determine report length
|
|
|
|
|
uint8_t reportLen = getReportLen(pSh2, reportId);
|
|
|
|
|
if (reportLen == 0) {
|
|
|
|
|
// An unrecognized report id
|
|
|
|
|
pSh2->unknownReportIds++;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (reportId == SENSORHUB_BASE_TIMESTAMP_REF) {
|
|
|
|
|
const BaseTimestampRef_t *rpt = (const BaseTimestampRef_t *)(payload+cursor);
|
|
|
|
|
|
|
|
|
|
// store base timestamp reference
|
|
|
|
|
referenceDelta = -rpt->timebase;
|
|
|
|
|
}
|
|
|
|
|
else if (reportId == SENSORHUB_TIMESTAMP_REBASE) {
|
|
|
|
|
const TimestampRebase_t *rpt = (const TimestampRebase_t *)(payload+cursor);
|
|
|
|
|
|
|
|
|
|
referenceDelta += rpt->timebase;
|
|
|
|
|
}
|
|
|
|
|
else if (reportId == SENSORHUB_FLUSH_COMPLETED) {
|
|
|
|
|
// Route this as if it arrived on command channel.
|
|
|
|
|
opRx(pSh2, payload+cursor, reportLen);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
uint8_t *pReport = payload+cursor;
|
|
|
|
|
uint16_t delay = ((pReport[2] & 0xFC) << 6) + pReport[3];
|
|
|
|
|
event.timestamp_uS = touSTimestamp(timestamp, referenceDelta, delay);
|
|
|
|
|
event.reportId = reportId;
|
|
|
|
|
memcpy(event.report, pReport, reportLen);
|
|
|
|
|
event.len = reportLen;
|
|
|
|
|
if (pSh2->sensorCallback != 0) {
|
|
|
|
|
pSh2->sensorCallback(pSh2->sensorCookie, &event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cursor += reportLen;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void sensorhubInputNormalHdlr(void *cookie, uint8_t *payload, uint16_t len, uint32_t timestamp)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = (sh2_t *)cookie;
|
|
|
|
|
|
|
|
|
|
sensorhubInputHdlr(pSh2, payload, len, timestamp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void sensorhubInputWakeHdlr(void *cookie, uint8_t *payload, uint16_t len, uint32_t timestamp)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = (sh2_t *)cookie;
|
|
|
|
|
|
|
|
|
|
sensorhubInputHdlr(pSh2, payload, len, timestamp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void sensorhubInputGyroRvHdlr(void *cookie, uint8_t *payload, uint16_t len, uint32_t timestamp)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = (sh2_t *)cookie;
|
|
|
|
|
sh2_SensorEvent_t event;
|
|
|
|
|
uint16_t cursor = 0;
|
|
|
|
|
|
|
|
|
|
uint8_t reportId = SH2_GYRO_INTEGRATED_RV;
|
|
|
|
|
uint8_t reportLen = getReportLen(pSh2, reportId);
|
|
|
|
|
|
|
|
|
|
while (cursor < len) {
|
|
|
|
|
event.timestamp_uS = timestamp;
|
|
|
|
|
event.reportId = reportId;
|
|
|
|
|
memcpy(event.report, payload+cursor, reportLen);
|
|
|
|
|
event.len = reportLen;
|
|
|
|
|
|
|
|
|
|
if (pSh2->sensorCallback != 0) {
|
|
|
|
|
pSh2->sensorCallback(pSh2->sensorCookie, &event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursor += reportLen;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void executableAdvertHdlr(void *cookie, uint8_t tag, uint8_t len, uint8_t *value)
|
|
|
|
|
{
|
|
|
|
|
// Ignore. No known TLV tags for this app.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void executableDeviceHdlr(void *cookie, uint8_t *payload, uint16_t len, uint32_t timestamp)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = (sh2_t *)cookie;
|
|
|
|
|
|
|
|
|
|
// Discard if length is bad
|
|
|
|
|
if (len != 1) {
|
|
|
|
|
pSh2->execBadPayload++;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (payload[0]) {
|
|
|
|
|
case EXECUTABLE_DEVICE_RESP_RESET_COMPLETE:
|
|
|
|
|
// reset process is now done.
|
|
|
|
|
pSh2->resetComplete = true;
|
|
|
|
|
|
|
|
|
|
// Notify client that reset is complete.
|
|
|
|
|
sh2AsyncEvent.eventId = SH2_RESET;
|
|
|
|
|
if (pSh2->eventCallback) {
|
|
|
|
|
pSh2->eventCallback(pSh2->eventCookie, &sh2AsyncEvent);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
pSh2->execBadPayload++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sendExecutable(sh2_t *pSh2, uint8_t cmd)
|
|
|
|
|
{
|
|
|
|
|
return shtp_send(pSh2->pShtp, pSh2->executableChan, &cmd, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sendCtrl(sh2_t *pSh2, const uint8_t *data, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
return shtp_send(pSh2->pShtp, pSh2->controlChan, data, len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int16_t toQ14(double x)
|
|
|
|
|
{
|
|
|
|
|
int16_t retval = (int16_t)(x * (1<<14));
|
|
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Get Product ID support
|
|
|
|
|
|
|
|
|
|
// Get Product ID Op handler
|
|
|
|
|
static int getProdIdStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
int rc = SH2_OK;
|
|
|
|
|
ProdIdReq_t req;
|
|
|
|
|
|
|
|
|
|
pSh2->opData.getProdIds.nextEntry = 0;
|
|
|
|
|
pSh2->opData.getProdIds.expectedEntries = 4; // Most products supply 4 product ids.
|
|
|
|
|
// When the first arrives, we'll know if
|
|
|
|
|
// we need to adjust this.
|
|
|
|
|
|
|
|
|
|
// Set up request to issue
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
req.reportId = SENSORHUB_PROD_ID_REQ;
|
|
|
|
|
rc = sendCtrl(pSh2, (uint8_t *)&req, sizeof(req));
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void getProdIdRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
ProdIdResp_t *resp = (ProdIdResp_t *)payload;
|
|
|
|
|
|
|
|
|
|
// skip this if it isn't the product id response.
|
|
|
|
|
if (resp->reportId != SENSORHUB_PROD_ID_RESP) return;
|
|
|
|
|
|
|
|
|
|
// Store this product id, if we can
|
|
|
|
|
sh2_ProductIds_t *pProdIds = pSh2->opData.getProdIds.pProdIds;
|
|
|
|
|
|
|
|
|
|
if (pProdIds) {
|
|
|
|
|
// Store the product id response
|
|
|
|
|
if (pSh2->opData.getProdIds.nextEntry < pSh2->opData.getProdIds.expectedEntries) {
|
|
|
|
|
sh2_ProductId_t *pProdId = &pProdIds->entry[pSh2->opData.getProdIds.nextEntry];
|
|
|
|
|
|
|
|
|
|
pProdId->resetCause = resp->resetCause;
|
|
|
|
|
pProdId->swVersionMajor = resp->swVerMajor;
|
|
|
|
|
pProdId->swVersionMinor = resp->swVerMinor;
|
|
|
|
|
pProdId->swPartNumber = resp->swPartNumber;
|
|
|
|
|
pProdId->swBuildNumber = resp->swBuildNumber;
|
|
|
|
|
pProdId->swVersionPatch = resp->swVerPatch;
|
|
|
|
|
pProdId->reserved0 = resp->reserved0;
|
|
|
|
|
pProdId->reserved1 = resp->reserved1;
|
|
|
|
|
|
|
|
|
|
if (pProdId->swPartNumber == 10004095) {
|
|
|
|
|
// FSP200 has 5 product id entries
|
|
|
|
|
pSh2->opData.getProdIds.expectedEntries = 5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pSh2->opData.getProdIds.nextEntry++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Complete this operation if there is no storage for more product ids
|
|
|
|
|
if ((pSh2->opData.getProdIds.pProdIds == 0) ||
|
|
|
|
|
(pSh2->opData.getProdIds.nextEntry >= pSh2->opData.getProdIds.expectedEntries)) {
|
|
|
|
|
|
|
|
|
|
pSh2->opData.getProdIds.pProdIds->numEntries = pSh2->opData.getProdIds.nextEntry;
|
|
|
|
|
opCompleted(pSh2, SH2_OK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t getProdIdOp = {
|
|
|
|
|
.start = getProdIdStart,
|
|
|
|
|
.rx = getProdIdRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Set Sensor Config
|
|
|
|
|
|
|
|
|
|
static int getSensorConfigStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
int rc = SH2_OK;
|
|
|
|
|
GetFeatureReq_t req;
|
|
|
|
|
|
|
|
|
|
// set up request to issue
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
req.reportId = SENSORHUB_GET_FEATURE_REQ;
|
|
|
|
|
req.featureReportId = pSh2->opData.getSensorConfig.sensorId;
|
|
|
|
|
rc = sendCtrl(pSh2, (uint8_t *)&req, sizeof(req));
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void getSensorConfigRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
GetFeatureResp_t *resp = (GetFeatureResp_t *)payload;
|
|
|
|
|
sh2_SensorConfig_t *pConfig;
|
|
|
|
|
|
|
|
|
|
// skip this if it isn't the response we're waiting for.
|
|
|
|
|
if (resp->reportId != SENSORHUB_GET_FEATURE_RESP) return;
|
|
|
|
|
if (resp->featureReportId != pSh2->opData.getSensorConfig.sensorId) return;
|
|
|
|
|
|
|
|
|
|
// Copy out data
|
|
|
|
|
pConfig = pSh2->opData.getSensorConfig.pConfig;
|
|
|
|
|
|
|
|
|
|
pConfig->changeSensitivityEnabled = ((resp->flags & FEAT_CHANGE_SENSITIVITY_ENABLED) != 0);
|
|
|
|
|
pConfig->changeSensitivityRelative = ((resp->flags & FEAT_CHANGE_SENSITIVITY_RELATIVE) != 0);
|
|
|
|
|
pConfig->wakeupEnabled = ((resp->flags & FEAT_WAKE_ENABLED) != 0);
|
|
|
|
|
pConfig->alwaysOnEnabled = ((resp->flags & FEAT_ALWAYS_ON_ENABLED) != 0);
|
|
|
|
|
pConfig->changeSensitivity = resp->changeSensitivity;
|
|
|
|
|
pConfig->reportInterval_us = resp->reportInterval_uS;
|
|
|
|
|
pConfig->batchInterval_us = resp->batchInterval_uS;
|
|
|
|
|
pConfig->sensorSpecific = resp->sensorSpecific;
|
|
|
|
|
|
|
|
|
|
// Complete this operation
|
|
|
|
|
opCompleted(pSh2, SH2_OK);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t getSensorConfigOp = {
|
|
|
|
|
.start = getSensorConfigStart,
|
|
|
|
|
.rx = getSensorConfigRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Set Sensor Config
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_SET_FEATURE_CMD
|
|
|
|
|
#define SENSORHUB_SET_FEATURE_CMD (0xFD)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId; // 0xFD
|
|
|
|
|
uint8_t featureReportId; // sensor id
|
|
|
|
|
uint8_t flags; // FEAT_... values
|
|
|
|
|
uint16_t changeSensitivity;
|
|
|
|
|
uint32_t reportInterval_uS;
|
|
|
|
|
uint32_t batchInterval_uS;
|
|
|
|
|
uint32_t sensorSpecific;
|
|
|
|
|
} SetFeatureReport_t;
|
|
|
|
|
|
|
|
|
|
static int setSensorConfigStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
SetFeatureReport_t req;
|
|
|
|
|
uint8_t flags = 0;
|
|
|
|
|
int rc;
|
|
|
|
|
sh2_SensorConfig_t *pConfig = pSh2->opData.getSensorConfig.pConfig;
|
|
|
|
|
|
|
|
|
|
if (pConfig->changeSensitivityEnabled) flags |= FEAT_CHANGE_SENSITIVITY_ENABLED;
|
|
|
|
|
if (pConfig->changeSensitivityRelative) flags |= FEAT_CHANGE_SENSITIVITY_RELATIVE;
|
|
|
|
|
if (pConfig->wakeupEnabled) flags |= FEAT_WAKE_ENABLED;
|
|
|
|
|
if (pConfig->alwaysOnEnabled) flags |= FEAT_ALWAYS_ON_ENABLED;
|
|
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
req.reportId = SENSORHUB_SET_FEATURE_CMD;
|
|
|
|
|
req.featureReportId = pSh2->opData.setSensorConfig.sensorId;
|
|
|
|
|
req.flags = flags;
|
|
|
|
|
req.changeSensitivity = pConfig->changeSensitivity;
|
|
|
|
|
req.reportInterval_uS = pConfig->reportInterval_us;
|
|
|
|
|
req.batchInterval_uS = pConfig->batchInterval_us;
|
|
|
|
|
req.sensorSpecific = pConfig->sensorSpecific;
|
|
|
|
|
|
|
|
|
|
rc = sendCtrl(pSh2, (uint8_t *)&req, sizeof(req));
|
|
|
|
|
opCompleted(pSh2, rc);
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t setSensorConfigOp = {
|
|
|
|
|
.start = setSensorConfigStart,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Get FRS.
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_FRS_WRITE_REQ
|
|
|
|
|
#define SENSORHUB_FRS_WRITE_REQ (0xF7)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t reserved;
|
|
|
|
|
uint16_t length;
|
|
|
|
|
uint16_t frsType;
|
|
|
|
|
} FrsWriteReq_t;
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_FRS_WRITE_DATA_REQ
|
|
|
|
|
#define SENSORHUB_FRS_WRITE_DATA_REQ (0xF6)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t reserved;
|
|
|
|
|
uint16_t offset;
|
|
|
|
|
uint32_t data0;
|
|
|
|
|
uint32_t data1;
|
|
|
|
|
} FrsWriteDataReq_t;
|
|
|
|
|
|
|
|
|
|
// FRS write status values
|
|
|
|
|
#define FRS_WRITE_STATUS_RECEIVED (0)
|
|
|
|
|
#define FRS_WRITE_STATUS_UNRECOGNIZED_FRS_TYPE (1)
|
|
|
|
|
#define FRS_WRITE_STATUS_BUSY (2)
|
|
|
|
|
#define FRS_WRITE_STATUS_WRITE_COMPLETED (3)
|
|
|
|
|
#define FRS_WRITE_STATUS_READY (4)
|
|
|
|
|
#define FRS_WRITE_STATUS_FAILED (5)
|
|
|
|
|
#define FRS_WRITE_STATUS_NOT_READY (6) // data received when not in write mode
|
|
|
|
|
#define FRS_WRITE_STATUS_INVALID_LENGTH (7)
|
|
|
|
|
#define FRS_WRITE_STATUS_RECORD_VALID (8)
|
|
|
|
|
#define FRS_WRITE_STATUS_INVALID_RECORD (9)
|
|
|
|
|
#define FRS_WRITE_STATUS_DEVICE_ERROR (10)
|
|
|
|
|
#define FRS_WRITE_STATUS_READ_ONLY (11)
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_FRS_WRITE_RESP
|
|
|
|
|
#define SENSORHUB_FRS_WRITE_RESP (0xF5)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t status;
|
|
|
|
|
uint16_t wordOffset;
|
|
|
|
|
} FrsWriteResp_t;
|
|
|
|
|
|
|
|
|
|
// RESP_FRS_READ_REQ
|
|
|
|
|
#define SENSORHUB_FRS_READ_REQ (0xF4)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t reserved;
|
|
|
|
|
uint16_t readOffset;
|
|
|
|
|
uint16_t frsType;
|
|
|
|
|
uint16_t blockSize;
|
|
|
|
|
} FrsReadReq_t;
|
|
|
|
|
|
|
|
|
|
// Get Datalen portion of len_status field
|
|
|
|
|
#define FRS_READ_DATALEN(x) ((x >> 4) & 0x0F)
|
|
|
|
|
|
|
|
|
|
// Get status portion of len_status field
|
|
|
|
|
#define FRS_READ_STATUS(x) ((x) & 0x0F)
|
|
|
|
|
|
|
|
|
|
// Status values
|
|
|
|
|
#define FRS_READ_STATUS_NO_ERROR 0
|
|
|
|
|
#define FRS_READ_STATUS_UNRECOGNIZED_FRS_TYPE 1
|
|
|
|
|
#define FRS_READ_STATUS_BUSY 2
|
|
|
|
|
#define FRS_READ_STATUS_READ_RECORD_COMPLETED 3
|
|
|
|
|
#define FRS_READ_STATUS_OFFSET_OUT_OF_RANGE 4
|
|
|
|
|
#define FRS_READ_STATUS_RECORD_EMPTY 5
|
|
|
|
|
#define FRS_READ_STATUS_READ_BLOCK_COMPLETED 6
|
|
|
|
|
#define FRS_READ_STATUS_READ_BLOCK_AND_RECORD_COMPLETED 7
|
|
|
|
|
#define FRS_READ_STATUS_DEVICE_ERROR 8
|
|
|
|
|
|
|
|
|
|
// SENSORHUB_FRS_READ_RESP
|
|
|
|
|
#define SENSORHUB_FRS_READ_RESP (0xF3)
|
|
|
|
|
typedef PACKED_STRUCT {
|
|
|
|
|
uint8_t reportId;
|
|
|
|
|
uint8_t len_status; // See FRS_READ... macros above
|
|
|
|
|
uint16_t wordOffset;
|
|
|
|
|
uint32_t data0;
|
|
|
|
|
uint32_t data1;
|
|
|
|
|
uint16_t frsType;
|
|
|
|
|
uint8_t reserved0;
|
|
|
|
|
uint8_t reserved1;
|
|
|
|
|
} FrsReadResp_t;
|
|
|
|
|
|
|
|
|
|
static int getFrsStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
int rc = SH2_OK;
|
|
|
|
|
FrsReadReq_t req;
|
|
|
|
|
|
|
|
|
|
pSh2->opData.getFrs.nextOffset = 0;
|
|
|
|
|
|
|
|
|
|
// set up request to issue
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
req.reportId = SENSORHUB_FRS_READ_REQ;
|
|
|
|
|
req.reserved = 0;
|
|
|
|
|
req.readOffset = 0; // read from start
|
|
|
|
|
req.frsType = pSh2->opData.getFrs.frsType;
|
|
|
|
|
req.blockSize = 0; // read all avail data
|
|
|
|
|
|
|
|
|
|
rc = sendCtrl(pSh2, (uint8_t *)&req, sizeof(req));
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void getFrsRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
FrsReadResp_t *resp = (FrsReadResp_t *)payload;
|
|
|
|
|
uint8_t status;
|
|
|
|
|
|
|
|
|
|
// skip this if it isn't the response we're looking for
|
|
|
|
|
if (resp->reportId != SENSORHUB_FRS_READ_RESP) return;
|
|
|
|
|
|
|
|
|
|
// Check for errors: Unrecognized FRS type, Busy, Out of range, Device error
|
|
|
|
|
status = FRS_READ_STATUS(resp->len_status);
|
|
|
|
|
if ((status == FRS_READ_STATUS_UNRECOGNIZED_FRS_TYPE) ||
|
|
|
|
|
(status == FRS_READ_STATUS_BUSY) ||
|
|
|
|
|
(status == FRS_READ_STATUS_OFFSET_OUT_OF_RANGE) ||
|
|
|
|
|
(status == FRS_READ_STATUS_DEVICE_ERROR)
|
|
|
|
|
) {
|
|
|
|
|
// Operation failed
|
|
|
|
|
opCompleted(pSh2, SH2_ERR_HUB);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status == FRS_READ_STATUS_RECORD_EMPTY) {
|
|
|
|
|
// Empty record, return zero length.
|
|
|
|
|
*(pSh2->opData.getFrs.pWords) = 0;
|
|
|
|
|
opCompleted(pSh2, SH2_OK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store the contents from this response
|
|
|
|
|
uint16_t offset = resp->wordOffset;
|
|
|
|
|
|
|
|
|
|
// check for missed offsets, resulting in error.
|
|
|
|
|
if (offset != pSh2->opData.getFrs.nextOffset) {
|
|
|
|
|
// Some data was dropped.
|
|
|
|
|
*(pSh2->opData.getFrs.pWords) = 0;
|
|
|
|
|
opCompleted(pSh2, SH2_ERR_IO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// store first word, if we have room
|
|
|
|
|
if ((*(pSh2->opData.getFrs.pWords) == 0) ||
|
|
|
|
|
(offset <= *(pSh2->opData.getFrs.pWords))) {
|
|
|
|
|
pSh2->opData.getFrs.pData[offset] = resp->data0;
|
|
|
|
|
pSh2->opData.getFrs.nextOffset = offset+1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// store second word if there is one and we have room
|
|
|
|
|
if ((FRS_READ_DATALEN(resp->len_status) == 2) &&
|
|
|
|
|
((*(pSh2->opData.getFrs.pWords) == 0) ||
|
|
|
|
|
(offset <= *(pSh2->opData.getFrs.pWords)))) {
|
|
|
|
|
pSh2->opData.getFrs.pData[offset+1] = resp->data1;
|
|
|
|
|
pSh2->opData.getFrs.nextOffset = offset+2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If read is done, complete the operation
|
|
|
|
|
if ((status == FRS_READ_STATUS_READ_RECORD_COMPLETED) ||
|
|
|
|
|
(status == FRS_READ_STATUS_READ_BLOCK_COMPLETED) ||
|
|
|
|
|
(status == FRS_READ_STATUS_READ_BLOCK_AND_RECORD_COMPLETED)) {
|
|
|
|
|
*(pSh2->opData.getFrs.pWords) = pSh2->opData.getFrs.nextOffset;
|
|
|
|
|
|
|
|
|
|
opCompleted(pSh2, SH2_OK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t getFrsOp = {
|
|
|
|
|
.start = getFrsStart,
|
|
|
|
|
.rx = getFrsRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Support for sh2_getMetadata
|
|
|
|
|
|
|
|
|
|
const static struct {
|
|
|
|
|
sh2_SensorId_t sensorId;
|
|
|
|
|
uint16_t recordId;
|
|
|
|
|
} sensorToRecordMap[] = {
|
2025-04-13 19:34:41 +01:00
|
|
|
{ SH2_RAW_ACCELEROMETER, SH2_FRS_ID_META_RAW_ACCELEROMETER },
|
|
|
|
|
{ SH2_ACCELEROMETER, SH2_FRS_ID_META_ACCELEROMETER },
|
|
|
|
|
{ SH2_LINEAR_ACCELERATION, SH2_FRS_ID_META_LINEAR_ACCELERATION },
|
|
|
|
|
{ SH2_GRAVITY, SH2_FRS_ID_META_GRAVITY },
|
|
|
|
|
{ SH2_RAW_GYROSCOPE, SH2_FRS_ID_META_RAW_GYROSCOPE },
|
|
|
|
|
{ SH2_GYROSCOPE_CALIBRATED, SH2_FRS_ID_META_GYROSCOPE_CALIBRATED },
|
|
|
|
|
{ SH2_GYROSCOPE_UNCALIBRATED, SH2_FRS_ID_META_GYROSCOPE_UNCALIBRATED },
|
|
|
|
|
{ SH2_RAW_MAGNETOMETER, SH2_FRS_ID_META_RAW_MAGNETOMETER },
|
|
|
|
|
{ SH2_MAGNETIC_FIELD_CALIBRATED, SH2_FRS_ID_META_MAGNETIC_FIELD_CALIBRATED },
|
|
|
|
|
{ SH2_MAGNETIC_FIELD_UNCALIBRATED, SH2_FRS_ID_META_MAGNETIC_FIELD_UNCALIBRATED },
|
|
|
|
|
{ SH2_ROTATION_VECTOR, SH2_FRS_ID_META_ROTATION_VECTOR },
|
|
|
|
|
{ SH2_GAME_ROTATION_VECTOR, SH2_FRS_ID_META_GAME_ROTATION_VECTOR },
|
|
|
|
|
{ SH2_GEOMAGNETIC_ROTATION_VECTOR, SH2_FRS_ID_META_GEOMAGNETIC_ROTATION_VECTOR },
|
|
|
|
|
{ SH2_PRESSURE, SH2_FRS_ID_META_PRESSURE },
|
|
|
|
|
{ SH2_AMBIENT_LIGHT, SH2_FRS_ID_META_AMBIENT_LIGHT },
|
|
|
|
|
{ SH2_HUMIDITY, SH2_FRS_ID_META_HUMIDITY },
|
|
|
|
|
{ SH2_PROXIMITY, SH2_FRS_ID_META_PROXIMITY },
|
|
|
|
|
{ SH2_TEMPERATURE, SH2_FRS_ID_META_TEMPERATURE },
|
|
|
|
|
{ SH2_TAP_DETECTOR, SH2_FRS_ID_META_TAP_DETECTOR },
|
|
|
|
|
{ SH2_STEP_DETECTOR, SH2_FRS_ID_META_STEP_DETECTOR },
|
|
|
|
|
{ SH2_STEP_COUNTER, SH2_FRS_ID_META_STEP_COUNTER },
|
|
|
|
|
{ SH2_SIGNIFICANT_MOTION, SH2_FRS_ID_META_SIGNIFICANT_MOTION },
|
|
|
|
|
{ SH2_STABILITY_CLASSIFIER, SH2_FRS_ID_META_STABILITY_CLASSIFIER },
|
|
|
|
|
{ SH2_SHAKE_DETECTOR, SH2_FRS_ID_META_SHAKE_DETECTOR },
|
|
|
|
|
{ SH2_FLIP_DETECTOR, SH2_FRS_ID_META_FLIP_DETECTOR },
|
|
|
|
|
{ SH2_PICKUP_DETECTOR, SH2_FRS_ID_META_PICKUP_DETECTOR },
|
|
|
|
|
{ SH2_STABILITY_DETECTOR, SH2_FRS_ID_META_STABILITY_DETECTOR },
|
|
|
|
|
{ SH2_PERSONAL_ACTIVITY_CLASSIFIER, SH2_FRS_ID_META_PERSONAL_ACTIVITY_CLASSIFIER },
|
|
|
|
|
{ SH2_SLEEP_DETECTOR, SH2_FRS_ID_META_SLEEP_DETECTOR },
|
|
|
|
|
{ SH2_TILT_DETECTOR, SH2_FRS_ID_META_TILT_DETECTOR },
|
|
|
|
|
{ SH2_POCKET_DETECTOR, SH2_FRS_ID_META_POCKET_DETECTOR },
|
|
|
|
|
{ SH2_CIRCLE_DETECTOR, SH2_FRS_ID_META_CIRCLE_DETECTOR },
|
2024-11-19 21:24:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void stuffMetadata(sh2_SensorMetadata_t *pData, uint32_t *frsData)
|
|
|
|
|
{
|
|
|
|
|
// Populate the sensorMetadata structure with results
|
|
|
|
|
pData->meVersion = (frsData[0] >> 0) & 0xFF;
|
|
|
|
|
pData->mhVersion = (frsData[0] >> 8) & 0xFF;
|
|
|
|
|
pData->shVersion = (frsData[0] >> 16) & 0xFF;
|
|
|
|
|
pData->range = frsData[1];
|
|
|
|
|
pData->resolution = frsData[2];
|
|
|
|
|
pData->power_mA = (frsData[3] >> 0) & 0xFFFF; // 16.10 format
|
|
|
|
|
pData->revision = (frsData[3] >> 16) & 0xFFFF;
|
|
|
|
|
pData->minPeriod_uS = frsData[4];
|
|
|
|
|
pData->maxPeriod_uS = 0; // ...unless reading format 4 metadata below
|
|
|
|
|
pData->fifoMax = (frsData[5] >> 0) & 0xFFFF;
|
|
|
|
|
pData->fifoReserved = (frsData[5] >> 16) & 0xFFFF;
|
|
|
|
|
pData->batchBufferBytes = (frsData[6] >> 0) & 0xFFFF;;
|
|
|
|
|
pData->vendorIdLen = (frsData[6] >> 16) & 0xFFFF;
|
|
|
|
|
|
|
|
|
|
// Init fields that may not be present, depending on metadata revision
|
|
|
|
|
pData->qPoint1 = 0;
|
|
|
|
|
pData->qPoint2 = 0;
|
|
|
|
|
pData->qPoint3 = 0;
|
|
|
|
|
pData->sensorSpecificLen = 0;
|
|
|
|
|
strcpy(pData->vendorId, ""); // init with empty string in case vendorIdLen == 0
|
|
|
|
|
|
|
|
|
|
int vendorIdOffset = 8;
|
|
|
|
|
// Get revision-specific fields
|
|
|
|
|
if (pData->revision == 0) {
|
|
|
|
|
// No fixed fields, vendor id copied after if-else block
|
|
|
|
|
}
|
|
|
|
|
else if (pData->revision == 1) {
|
|
|
|
|
pData->qPoint1 = (frsData[7] >> 0) & 0xFFFF;
|
|
|
|
|
pData->qPoint2 = (frsData[7] >> 16) & 0xFFFF;
|
|
|
|
|
}
|
|
|
|
|
else if (pData->revision == 2) {
|
|
|
|
|
pData->qPoint1 = (frsData[7] >> 0) & 0xFFFF;
|
|
|
|
|
pData->qPoint2 = (frsData[7] >> 16) & 0xFFFF;
|
|
|
|
|
pData->sensorSpecificLen = (frsData[8] >> 0) & 0xFFFF;
|
|
|
|
|
memcpy(pData->sensorSpecific, (uint8_t *)&frsData[9], pData->sensorSpecificLen);
|
|
|
|
|
vendorIdOffset = 9 + ((pData->sensorSpecificLen+3)/4); // 9 + one word for every 4 bytes of SS data
|
|
|
|
|
}
|
|
|
|
|
else if (pData->revision == 3) {
|
|
|
|
|
pData->qPoint1 = (frsData[7] >> 0) & 0xFFFF;
|
|
|
|
|
pData->qPoint2 = (frsData[7] >> 16) & 0xFFFF;
|
|
|
|
|
pData->sensorSpecificLen = (frsData[8] >> 0) & 0xFFFF;
|
|
|
|
|
pData->qPoint3 = (frsData[8] >> 16) & 0xFFFF;
|
|
|
|
|
memcpy(pData->sensorSpecific, (uint8_t *)&frsData[9], pData->sensorSpecificLen);
|
|
|
|
|
vendorIdOffset = 9 + ((pData->sensorSpecificLen+3)/4); // 9 + one word for every 4 bytes of SS data
|
|
|
|
|
}
|
|
|
|
|
else if (pData->revision == 4) {
|
|
|
|
|
pData->qPoint1 = (frsData[7] >> 0) & 0xFFFF;
|
|
|
|
|
pData->qPoint2 = (frsData[7] >> 16) & 0xFFFF;
|
|
|
|
|
pData->sensorSpecificLen = (frsData[8] >> 0) & 0xFFFF;
|
|
|
|
|
pData->qPoint3 = (frsData[8] >> 16) & 0xFFFF;
|
|
|
|
|
pData->maxPeriod_uS = frsData[9];
|
|
|
|
|
memcpy(pData->sensorSpecific, (uint8_t *)&frsData[10], pData->sensorSpecificLen);
|
|
|
|
|
vendorIdOffset = 10 + ((pData->sensorSpecificLen+3)/4); // 9 + one word for every 4 bytes of SS data
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Unrecognized revision!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy vendor id
|
|
|
|
|
memcpy(pData->vendorId, (uint8_t *)&frsData[vendorIdOffset],
|
|
|
|
|
pData->vendorIdLen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Set FRS.
|
|
|
|
|
|
|
|
|
|
static int setFrsStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
int rc = SH2_OK;
|
|
|
|
|
FrsWriteReq_t req;
|
|
|
|
|
|
|
|
|
|
pSh2->opData.setFrs.offset = 0;
|
|
|
|
|
|
|
|
|
|
// set up request to issue
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
req.reportId = SENSORHUB_FRS_WRITE_REQ;
|
|
|
|
|
req.reserved = 0;
|
|
|
|
|
req.length = pSh2->opData.setFrs.words;
|
|
|
|
|
req.frsType = pSh2->opData.getFrs.frsType;
|
|
|
|
|
|
|
|
|
|
rc = sendCtrl(pSh2, (uint8_t *)&req, sizeof(req));
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setFrsRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
FrsWriteResp_t *resp = (FrsWriteResp_t *)payload;
|
|
|
|
|
FrsWriteDataReq_t req;
|
|
|
|
|
uint8_t status;
|
|
|
|
|
bool sendMoreData = false;
|
|
|
|
|
bool completed = false;
|
|
|
|
|
int rc = SH2_OK;
|
|
|
|
|
|
|
|
|
|
// skip this if it isn't the response we're looking for.
|
|
|
|
|
if (resp->reportId != SENSORHUB_FRS_WRITE_RESP) return;
|
|
|
|
|
|
|
|
|
|
// Check for errors: Unrecognized FRS type, Busy, Out of range, Device error
|
|
|
|
|
status = resp->status;
|
|
|
|
|
switch(status) {
|
|
|
|
|
case FRS_WRITE_STATUS_RECEIVED:
|
|
|
|
|
case FRS_WRITE_STATUS_READY:
|
|
|
|
|
sendMoreData = true;
|
|
|
|
|
break;
|
|
|
|
|
case FRS_WRITE_STATUS_UNRECOGNIZED_FRS_TYPE:
|
|
|
|
|
case FRS_WRITE_STATUS_BUSY:
|
|
|
|
|
case FRS_WRITE_STATUS_FAILED:
|
|
|
|
|
case FRS_WRITE_STATUS_NOT_READY:
|
|
|
|
|
case FRS_WRITE_STATUS_INVALID_LENGTH:
|
|
|
|
|
case FRS_WRITE_STATUS_INVALID_RECORD:
|
|
|
|
|
case FRS_WRITE_STATUS_DEVICE_ERROR:
|
|
|
|
|
case FRS_WRITE_STATUS_READ_ONLY:
|
|
|
|
|
rc = SH2_ERR_HUB;
|
|
|
|
|
completed = true;
|
|
|
|
|
break;
|
|
|
|
|
case FRS_WRITE_STATUS_WRITE_COMPLETED:
|
|
|
|
|
// Successful completion
|
|
|
|
|
rc = SH2_OK;
|
|
|
|
|
completed = true;
|
|
|
|
|
break;
|
|
|
|
|
case FRS_WRITE_STATUS_RECORD_VALID:
|
|
|
|
|
// That's nice, keep waiting
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if we should send more data, do it.
|
|
|
|
|
if (sendMoreData &&
|
|
|
|
|
(pSh2->opData.setFrs.offset < pSh2->opData.setFrs.words)) {
|
|
|
|
|
uint16_t offset = pSh2->opData.setFrs.offset;
|
|
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
req.reportId = SENSORHUB_FRS_WRITE_DATA_REQ;
|
|
|
|
|
req.reserved = 0;
|
|
|
|
|
req.offset = offset;
|
|
|
|
|
req.data0 = pSh2->opData.setFrs.pData[offset++];
|
|
|
|
|
if (offset < pSh2->opData.setFrs.words) {
|
|
|
|
|
req.data1 = pSh2->opData.setFrs.pData[offset++];
|
|
|
|
|
} else {
|
|
|
|
|
req.data1 = 0;
|
|
|
|
|
}
|
|
|
|
|
pSh2->opData.setFrs.offset = offset;
|
|
|
|
|
|
|
|
|
|
rc = sendCtrl(pSh2, (uint8_t *)&req, sizeof(req));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if the operation is done or has to be aborted, complete it
|
|
|
|
|
if (completed) {
|
|
|
|
|
opCompleted(pSh2, rc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t setFrsOp = {
|
|
|
|
|
.start = setFrsStart,
|
|
|
|
|
.rx = setFrsRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Support for sending commands
|
|
|
|
|
|
|
|
|
|
static int sendCmd(sh2_t *pSh2, uint8_t cmd, uint8_t p[COMMAND_PARAMS])
|
|
|
|
|
{
|
|
|
|
|
int rc = SH2_OK;
|
|
|
|
|
CommandReq_t req;
|
|
|
|
|
|
|
|
|
|
// Clear request structure
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
|
|
|
|
|
// Create a command sequence number for this command
|
|
|
|
|
pSh2->lastCmdId = cmd;
|
|
|
|
|
pSh2->cmdSeq = pSh2->nextCmdSeq++;
|
|
|
|
|
|
|
|
|
|
// set up request to issue
|
|
|
|
|
req.reportId = SENSORHUB_COMMAND_REQ;
|
|
|
|
|
req.seq = pSh2->cmdSeq;
|
|
|
|
|
req.command = cmd;
|
|
|
|
|
for (int n = 0; n < COMMAND_PARAMS; n++) {
|
|
|
|
|
req.p[n] = p[n];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rc = sendCtrl(pSh2, (uint8_t *)&req, sizeof(req));
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send a command with 0 parameters
|
|
|
|
|
static int sendCmd0(sh2_t *pSh2, uint8_t cmd)
|
|
|
|
|
{
|
|
|
|
|
uint8_t p[COMMAND_PARAMS];
|
|
|
|
|
|
|
|
|
|
memset(p, 0, COMMAND_PARAMS);
|
|
|
|
|
|
|
|
|
|
return sendCmd(pSh2, cmd, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send a command with 1 parameter
|
|
|
|
|
static int sendCmd1(sh2_t *pSh2, uint8_t cmd, uint8_t p0)
|
|
|
|
|
{
|
|
|
|
|
uint8_t p[COMMAND_PARAMS];
|
|
|
|
|
|
|
|
|
|
memset(p, 0, COMMAND_PARAMS);
|
|
|
|
|
|
|
|
|
|
p[0] = p0;
|
|
|
|
|
return sendCmd(pSh2, cmd, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send a command with 2 parameters
|
|
|
|
|
static int sendCmd2(sh2_t *pSh2, uint8_t cmd, uint8_t p0, uint8_t p1)
|
|
|
|
|
{
|
|
|
|
|
uint8_t p[COMMAND_PARAMS];
|
|
|
|
|
|
|
|
|
|
memset(p, 0, COMMAND_PARAMS);
|
|
|
|
|
|
|
|
|
|
p[0] = p0;
|
|
|
|
|
p[1] = p1;
|
|
|
|
|
return sendCmd(pSh2, cmd, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool wrongResponse(sh2_t *pSh2, CommandResp_t *resp)
|
|
|
|
|
{
|
|
|
|
|
if (resp->reportId != SENSORHUB_COMMAND_RESP) return true;
|
|
|
|
|
if (resp->command != pSh2->lastCmdId) return true;
|
|
|
|
|
if (resp->commandSeq != pSh2->cmdSeq) return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Get Errors
|
|
|
|
|
|
|
|
|
|
static int getErrorsStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
// Initialize state
|
|
|
|
|
pSh2->opData.getErrors.errsRead = 0;
|
|
|
|
|
|
|
|
|
|
return sendCmd1(pSh2, SH2_CMD_ERRORS, pSh2->opData.getErrors.severity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void getErrorsRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
CommandResp_t *resp = (CommandResp_t *)payload;
|
|
|
|
|
|
|
|
|
|
// skip this if it isn't the right response
|
|
|
|
|
if (wrongResponse(pSh2, resp)) return;
|
|
|
|
|
|
|
|
|
|
if (resp->r[2] == 255) {
|
|
|
|
|
// No error to report, operation is complete
|
|
|
|
|
*(pSh2->opData.getErrors.pNumErrors) = pSh2->opData.getErrors.errsRead;
|
|
|
|
|
opCompleted(pSh2, SH2_OK);
|
|
|
|
|
} else {
|
|
|
|
|
// Copy data for invoker.
|
|
|
|
|
unsigned int index = pSh2->opData.getErrors.errsRead;
|
|
|
|
|
if (index < *(pSh2->opData.getErrors.pNumErrors)) {
|
|
|
|
|
// We have room for this one.
|
|
|
|
|
pSh2->opData.getErrors.pErrors[index].severity = resp->r[0];
|
|
|
|
|
pSh2->opData.getErrors.pErrors[index].sequence = resp->r[1];
|
|
|
|
|
pSh2->opData.getErrors.pErrors[index].source = resp->r[2];
|
|
|
|
|
pSh2->opData.getErrors.pErrors[index].error = resp->r[3];
|
|
|
|
|
pSh2->opData.getErrors.pErrors[index].module = resp->r[4];
|
|
|
|
|
pSh2->opData.getErrors.pErrors[index].code = resp->r[5];
|
|
|
|
|
|
|
|
|
|
pSh2->opData.getErrors.errsRead++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t getErrorsOp = {
|
|
|
|
|
.start = getErrorsStart,
|
|
|
|
|
.rx = getErrorsRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Get Counts
|
|
|
|
|
|
|
|
|
|
static int getCountsStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
return sendCmd2(pSh2, SH2_CMD_COUNTS, SH2_COUNTS_GET_COUNTS, pSh2->opData.getCounts.sensorId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void getCountsRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
CommandResp_t *resp = (CommandResp_t *)payload;
|
|
|
|
|
|
|
|
|
|
if (wrongResponse(pSh2, resp)) return;
|
|
|
|
|
|
|
|
|
|
// Store results
|
|
|
|
|
if (resp->respSeq == 0) {
|
|
|
|
|
pSh2->opData.getCounts.pCounts->offered = readu32(&resp->r[3]);
|
|
|
|
|
pSh2->opData.getCounts.pCounts->accepted = readu32(&resp->r[7]);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
pSh2->opData.getCounts.pCounts->on = readu32(&resp->r[3]);
|
|
|
|
|
pSh2->opData.getCounts.pCounts->attempted = readu32(&resp->r[7]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Complete this operation if we've received last response
|
|
|
|
|
if (resp->respSeq == 1) {
|
|
|
|
|
opCompleted(pSh2, SH2_OK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t getCountsOp = {
|
|
|
|
|
.start = getCountsStart,
|
|
|
|
|
.rx = getCountsRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Generic Send Command
|
|
|
|
|
|
|
|
|
|
static int sendCmdStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
int status = sendCmd(pSh2, pSh2->opData.sendCmd.req.command,
|
|
|
|
|
pSh2->opData.sendCmd.req.p);
|
|
|
|
|
|
|
|
|
|
opCompleted(pSh2, status);
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t sendCmdOp = {
|
|
|
|
|
.start = sendCmdStart,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Reinit
|
|
|
|
|
|
|
|
|
|
static int reinitStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
int status = sendCmd1(pSh2, SH2_CMD_INITIALIZE, SH2_INIT_SYSTEM);
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void reinitRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
CommandResp_t *resp = (CommandResp_t *)payload;
|
|
|
|
|
|
|
|
|
|
// Ignore message if it doesn't pertain to this operation
|
|
|
|
|
if (wrongResponse(pSh2, resp)) return;
|
|
|
|
|
|
|
|
|
|
// Get return status
|
|
|
|
|
int status = SH2_OK;
|
|
|
|
|
if (resp->r[0] != 0) {
|
|
|
|
|
status = SH2_ERR_HUB;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opCompleted(pSh2, status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t reinitOp = {
|
|
|
|
|
.start = reinitStart,
|
|
|
|
|
.rx = reinitRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Save DCD Now
|
|
|
|
|
|
|
|
|
|
static int saveDcdNowStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
int status = sendCmd0(pSh2, SH2_CMD_DCD);
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void saveDcdNowRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
CommandResp_t *resp = (CommandResp_t *)payload;
|
|
|
|
|
|
|
|
|
|
// Ignore message if it doesn't pertain to this operation
|
|
|
|
|
if (wrongResponse(pSh2, resp)) return;
|
|
|
|
|
|
|
|
|
|
// Get return status
|
|
|
|
|
int status = SH2_OK;
|
|
|
|
|
if (resp->r[0] != 0) {
|
|
|
|
|
status = SH2_ERR_HUB;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opCompleted(pSh2, status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t saveDcdNowOp = {
|
|
|
|
|
.start = saveDcdNowStart,
|
|
|
|
|
.rx = saveDcdNowRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Get Osc Type
|
|
|
|
|
|
|
|
|
|
static int getOscTypeStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
return sendCmd0(pSh2, SH2_CMD_GET_OSC_TYPE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void getOscTypeRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
CommandResp_t *resp = (CommandResp_t *)payload;
|
|
|
|
|
sh2_OscType_t *pOscType;
|
|
|
|
|
|
|
|
|
|
// Ignore message if it doesn't pertain to this operation
|
|
|
|
|
if (wrongResponse(pSh2, resp)) return;
|
|
|
|
|
|
|
|
|
|
// Read out data
|
|
|
|
|
pOscType = pSh2->opData.getOscType.pOscType;
|
|
|
|
|
*pOscType = (sh2_OscType_t)resp->r[0];
|
|
|
|
|
|
|
|
|
|
// Complete this operation
|
|
|
|
|
opCompleted(pSh2, SH2_OK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t getOscTypeOp = {
|
|
|
|
|
.start = getOscTypeStart,
|
|
|
|
|
.rx = getOscTypeRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Set Cal Config
|
|
|
|
|
|
|
|
|
|
static int setCalConfigStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
uint8_t p[COMMAND_PARAMS];
|
|
|
|
|
|
|
|
|
|
// Clear p. (Importantly, set subcommand in p[3] to 0, CONFIGURE)
|
|
|
|
|
memset(p, 0, COMMAND_PARAMS);
|
|
|
|
|
|
|
|
|
|
// Which cal processes to enable/disable
|
|
|
|
|
p[0] = (pSh2->opData.calConfig.sensors & SH2_CAL_ACCEL) ? 1 : 0; // accel cal
|
|
|
|
|
p[1] = (pSh2->opData.calConfig.sensors & SH2_CAL_GYRO) ? 1 : 0; // gyro cal
|
|
|
|
|
p[2] = (pSh2->opData.calConfig.sensors & SH2_CAL_MAG) ? 1 : 0; // mag cal
|
|
|
|
|
p[4] = (pSh2->opData.calConfig.sensors & SH2_CAL_PLANAR) ? 1 : 0; // planar cal
|
|
|
|
|
|
|
|
|
|
return sendCmd(pSh2, SH2_CMD_ME_CAL, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setCalConfigRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
CommandResp_t *resp = (CommandResp_t *)payload;
|
|
|
|
|
|
|
|
|
|
// Ignore message if it doesn't pertain to this operation
|
|
|
|
|
if (wrongResponse(pSh2, resp)) return;
|
|
|
|
|
|
|
|
|
|
// Read out data
|
|
|
|
|
int status = SH2_OK;
|
|
|
|
|
if (resp->r[0] != 0) {
|
|
|
|
|
status = SH2_ERR_HUB;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Complete this operation
|
|
|
|
|
opCompleted(pSh2, status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t setCalConfigOp = {
|
|
|
|
|
.start = setCalConfigStart,
|
|
|
|
|
.rx = setCalConfigRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Get Cal Config
|
|
|
|
|
|
|
|
|
|
static int getCalConfigStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
uint8_t p[COMMAND_PARAMS];
|
|
|
|
|
|
|
|
|
|
// Clear p. (Importantly, set subcommand in p[3] to 0, CONFIGURE)
|
|
|
|
|
memset(p, 0, COMMAND_PARAMS);
|
|
|
|
|
|
|
|
|
|
// Subcommand: Get ME Calibration
|
|
|
|
|
p[3] = 0x01;
|
|
|
|
|
|
|
|
|
|
return sendCmd(pSh2, SH2_CMD_ME_CAL, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void getCalConfigRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
CommandResp_t *resp = (CommandResp_t *)payload;
|
|
|
|
|
|
|
|
|
|
// Ignore message if it doesn't pertain to this operation
|
|
|
|
|
if (wrongResponse(pSh2, resp)) return;
|
|
|
|
|
|
|
|
|
|
// Read out data
|
|
|
|
|
int status = SH2_OK;
|
|
|
|
|
if (resp->r[0] != 0) {
|
|
|
|
|
status = SH2_ERR_HUB;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Unload results into pSensors
|
|
|
|
|
uint8_t sensors = 0;
|
|
|
|
|
if (resp->r[1]) sensors |= SH2_CAL_ACCEL;
|
|
|
|
|
if (resp->r[2]) sensors |= SH2_CAL_GYRO;
|
|
|
|
|
if (resp->r[3]) sensors |= SH2_CAL_MAG;
|
|
|
|
|
if (resp->r[4]) sensors |= SH2_CAL_PLANAR;
|
|
|
|
|
*(pSh2->opData.getCalConfig.pSensors) = sensors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Complete this operation
|
|
|
|
|
opCompleted(pSh2, status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t getCalConfigOp = {
|
|
|
|
|
.start = getCalConfigStart,
|
|
|
|
|
.rx = getCalConfigRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Force Flush
|
|
|
|
|
|
|
|
|
|
static int forceFlushStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
ForceFlushReq_t req;
|
|
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
req.reportId = SENSORHUB_FORCE_SENSOR_FLUSH;
|
|
|
|
|
req.sensorId = pSh2->opData.forceFlush.sensorId;
|
|
|
|
|
|
|
|
|
|
return sendCtrl(pSh2, (uint8_t *)&req, sizeof(req));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void forceFlushRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
ForceFlushResp_t *resp = (ForceFlushResp_t *)payload;
|
|
|
|
|
|
|
|
|
|
// Ignore message if it doesn't pertain to this operation
|
|
|
|
|
if (resp->reportId != SENSORHUB_FLUSH_COMPLETED) return;
|
|
|
|
|
if (resp->sensorId != pSh2->opData.forceFlush.sensorId) return;
|
|
|
|
|
|
|
|
|
|
// Complete this operation
|
|
|
|
|
opCompleted(pSh2, SH2_OK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t forceFlushOp = {
|
|
|
|
|
.start = forceFlushStart,
|
|
|
|
|
.rx = forceFlushRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Start Cal
|
|
|
|
|
|
|
|
|
|
static int startCalStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
uint8_t p[COMMAND_PARAMS];
|
|
|
|
|
|
|
|
|
|
// Clear p. (Importantly, set subcommand in p[3] to 0, CONFIGURE)
|
|
|
|
|
memset(p, 0, COMMAND_PARAMS);
|
|
|
|
|
|
|
|
|
|
// Subcommand: Get ME Calibration
|
|
|
|
|
p[0] = SH2_CAL_START;
|
|
|
|
|
p[1] = pSh2->opData.startCal.interval_us & 0xFF; // LSB
|
|
|
|
|
p[2] = (pSh2->opData.startCal.interval_us >> 8) & 0xFF;
|
|
|
|
|
p[3] = (pSh2->opData.startCal.interval_us >> 16) & 0xFF;
|
|
|
|
|
p[4] = (pSh2->opData.startCal.interval_us >> 24) & 0xFF; // MSB
|
|
|
|
|
|
|
|
|
|
return sendCmd(pSh2, SH2_CMD_CAL, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void startCalRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
CommandResp_t *resp = (CommandResp_t *)payload;
|
|
|
|
|
|
|
|
|
|
// Ignore message if it doesn't pertain to this operation
|
|
|
|
|
if (wrongResponse(pSh2, resp)) return;
|
|
|
|
|
|
|
|
|
|
// Complete this operation
|
|
|
|
|
opCompleted(pSh2, SH2_OK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t startCalOp = {
|
|
|
|
|
.start = startCalStart,
|
|
|
|
|
.rx = startCalRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Start Cal
|
|
|
|
|
|
|
|
|
|
static int finishCalStart(sh2_t *pSh2)
|
|
|
|
|
{
|
|
|
|
|
return sendCmd1(pSh2, SH2_CMD_CAL, SH2_CAL_FINISH);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void finishCalRx(sh2_t *pSh2, const uint8_t *payload, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
CommandResp_t *resp = (CommandResp_t *)payload;
|
|
|
|
|
|
|
|
|
|
// Ignore message if it doesn't pertain to this operation
|
|
|
|
|
if (wrongResponse(pSh2, resp)) return;
|
|
|
|
|
|
|
|
|
|
pSh2->opData.finishCal.status = (sh2_CalStatus_t)resp->r[1];
|
|
|
|
|
|
|
|
|
|
// Complete this operation
|
|
|
|
|
if (pSh2->opData.finishCal.status == SH2_CAL_SUCCESS) {
|
|
|
|
|
opCompleted(pSh2, SH2_OK);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
opCompleted(pSh2, SH2_ERR_HUB);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sh2_Op_t finishCalOp = {
|
|
|
|
|
.start = finishCalStart,
|
|
|
|
|
.rx = finishCalRx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// SHTP Event Callback
|
|
|
|
|
|
|
|
|
|
static void shtpEventCallback(void *cookie, shtp_Event_t shtpEvent) {
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
sh2AsyncEvent.eventId = SH2_SHTP_EVENT;
|
|
|
|
|
sh2AsyncEvent.shtpEvent = shtpEvent;
|
|
|
|
|
if (pSh2->eventCallback) {
|
|
|
|
|
pSh2->eventCallback(pSh2->eventCookie, &sh2AsyncEvent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// Public functions
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Open a session with a sensor hub.
|
|
|
|
|
*
|
|
|
|
|
* This function should be called before others in this API.
|
|
|
|
|
* An instance of an SH2 HAL should be passed in.
|
|
|
|
|
* This call will result in the open() function of the HAL being called.
|
|
|
|
|
*
|
|
|
|
|
* As part of the initialization process, a callback function is registered that will
|
|
|
|
|
* be invoked when the device generates certain events. (See sh2_AsyncEventId)
|
|
|
|
|
*
|
|
|
|
|
* @param pHal Pointer to an SH2 HAL instance, provided by the target system.
|
|
|
|
|
* @param eventCallback Will be called when events, such as reset complete, occur.
|
|
|
|
|
* @param eventCookie Will be passed to eventCallback.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_open(sh2_Hal_t *pHal,
|
|
|
|
|
sh2_EventCallback_t *eventCallback, void *eventCookie)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// Validate parameters
|
|
|
|
|
if (pHal == 0) return SH2_ERR_BAD_PARAM;
|
|
|
|
|
|
|
|
|
|
// Clear everything in sh2 structure.
|
|
|
|
|
memset(&_sh2, 0, sizeof(_sh2));
|
|
|
|
|
|
|
|
|
|
pSh2->resetComplete = false; // will go true after reset response from SH.
|
|
|
|
|
pSh2->controlChan = 0xFF; // An invalid value since we don't know yet.
|
|
|
|
|
|
|
|
|
|
// Store reference to HAL for future use.
|
|
|
|
|
pSh2->pHal = pHal;
|
|
|
|
|
pSh2->eventCallback = eventCallback;
|
|
|
|
|
pSh2->eventCookie = eventCookie;
|
|
|
|
|
pSh2->sensorCallback = 0;
|
|
|
|
|
pSh2->sensorCookie = 0;
|
|
|
|
|
|
|
|
|
|
// Open SHTP layer
|
|
|
|
|
pSh2->pShtp = shtp_open(pSh2->pHal);
|
|
|
|
|
if (pSh2->pShtp == 0) {
|
|
|
|
|
// Error opening SHTP
|
|
|
|
|
return SH2_ERR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Register SHTP event callback
|
|
|
|
|
shtp_setEventCallback(pSh2->pShtp, shtpEventCallback, &_sh2);
|
|
|
|
|
|
|
|
|
|
// Register with SHTP
|
|
|
|
|
// Register SH2 handlers
|
|
|
|
|
shtp_listenAdvert(pSh2->pShtp, GUID_SENSORHUB, sensorhubAdvertHdlr, &_sh2);
|
|
|
|
|
shtp_listenChan(pSh2->pShtp, GUID_SENSORHUB, "control", sensorhubControlHdlr, &_sh2);
|
|
|
|
|
shtp_listenChan(pSh2->pShtp, GUID_SENSORHUB, "inputNormal", sensorhubInputNormalHdlr, &_sh2);
|
|
|
|
|
shtp_listenChan(pSh2->pShtp, GUID_SENSORHUB, "inputWake", sensorhubInputWakeHdlr, &_sh2);
|
|
|
|
|
shtp_listenChan(pSh2->pShtp, GUID_SENSORHUB, "inputGyroRv", sensorhubInputGyroRvHdlr, &_sh2);
|
|
|
|
|
|
|
|
|
|
// Register EXECUTABLE handlers
|
|
|
|
|
shtp_listenAdvert(pSh2->pShtp, GUID_EXECUTABLE, executableAdvertHdlr, &_sh2);
|
|
|
|
|
shtp_listenChan(pSh2->pShtp, GUID_EXECUTABLE, "device", executableDeviceHdlr, &_sh2);
|
|
|
|
|
|
|
|
|
|
// Wait for reset notifications to arrive.
|
|
|
|
|
// The client can't talk to the sensor hub until that happens.
|
|
|
|
|
uint32_t start_us = pSh2->pHal->getTimeUs(pSh2->pHal);
|
|
|
|
|
uint32_t now_us = start_us;
|
|
|
|
|
while (((now_us - start_us) < ADVERT_TIMEOUT_US) &&
|
|
|
|
|
(!pSh2->resetComplete))
|
|
|
|
|
{
|
|
|
|
|
shtp_service(pSh2->pShtp);
|
|
|
|
|
now_us = pSh2->pHal->getTimeUs(pSh2->pHal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No errors.
|
|
|
|
|
return SH2_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Close a session with a sensor hub.
|
|
|
|
|
*
|
|
|
|
|
* This should be called at the end of a sensor hub session.
|
|
|
|
|
* The underlying SHTP and HAL instances will be closed.
|
|
|
|
|
*/
|
|
|
|
|
void sh2_close(void)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
shtp_close(pSh2->pShtp);
|
|
|
|
|
|
|
|
|
|
// Clear everything in sh2 structure.
|
|
|
|
|
memset(pSh2, 0, sizeof(sh2_t));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Service the SH2 device, reading any data that is available and dispatching callbacks.
|
|
|
|
|
*
|
|
|
|
|
* This function should be called periodically by the host system to service an open sensor hub.
|
|
|
|
|
*/
|
|
|
|
|
void sh2_service(void)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
shtp_service(pSh2->pShtp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Register a function to receive sensor events.
|
|
|
|
|
*
|
|
|
|
|
* @param callback A function that will be called each time a sensor event is received.
|
|
|
|
|
* @param cookie A value that will be passed to the sensor callback function.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_setSensorCallback(sh2_SensorCallback_t *callback, void *cookie)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
pSh2->sensorCallback = callback;
|
|
|
|
|
pSh2->sensorCookie = cookie;
|
|
|
|
|
|
|
|
|
|
return SH2_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Reset the sensor hub device by sending RESET (1) command on "device" channel.
|
|
|
|
|
*
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_devReset(void)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
return sendExecutable(pSh2, EXECUTABLE_DEVICE_CMD_RESET);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Turn sensor hub on by sending RESET (1) command on "device" channel.
|
|
|
|
|
*
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_devOn(void)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
return sendExecutable(pSh2, EXECUTABLE_DEVICE_CMD_ON);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Put sensor hub in sleep state by sending SLEEP (2) command on "device" channel.
|
|
|
|
|
*
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_devSleep(void)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
return sendExecutable(pSh2, EXECUTABLE_DEVICE_CMD_SLEEP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Get Product ID information from Sensorhub.
|
|
|
|
|
*
|
|
|
|
|
* @param prodIds Pointer to structure that will receive results.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_getProdIds(sh2_ProductIds_t *prodIds)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
pSh2->opData.getProdIds.pProdIds = prodIds;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &getProdIdOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Get sensor configuration.
|
|
|
|
|
*
|
|
|
|
|
* @param sensorId Which sensor to query.
|
|
|
|
|
* @param config SensorConfig structure to store results.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_getSensorConfig(sh2_SensorId_t sensorId, sh2_SensorConfig_t *pConfig)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
// Set up operation
|
|
|
|
|
pSh2->opData.getSensorConfig.sensorId = sensorId;
|
|
|
|
|
pSh2->opData.getSensorConfig.pConfig = pConfig;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &getSensorConfigOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Set sensor configuration. (e.g enable a sensor at a particular rate.)
|
|
|
|
|
*
|
|
|
|
|
* @param sensorId Which sensor to configure.
|
|
|
|
|
* @param pConfig Pointer to structure holding sensor configuration.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_setSensorConfig(sh2_SensorId_t sensorId, const sh2_SensorConfig_t *pConfig)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
// Set up operation
|
|
|
|
|
pSh2->opData.setSensorConfig.sensorId = sensorId;
|
|
|
|
|
pSh2->opData.setSensorConfig.pConfig = pConfig;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &setSensorConfigOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Get metadata related to a sensor.
|
|
|
|
|
*
|
|
|
|
|
* @param sensorId Which sensor to query.
|
|
|
|
|
* @param pData Pointer to structure to receive the results.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_getMetadata(sh2_SensorId_t sensorId, sh2_SensorMetadata_t *pData)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// pData must be non-null
|
|
|
|
|
if (pData == 0) return SH2_ERR_BAD_PARAM;
|
|
|
|
|
|
|
|
|
|
// Convert sensorId to metadata recordId
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < ARRAY_LEN(sensorToRecordMap); i++) {
|
|
|
|
|
if (sensorToRecordMap[i].sensorId == sensorId) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i >= ARRAY_LEN(sensorToRecordMap)) {
|
|
|
|
|
// no match was found
|
|
|
|
|
return SH2_ERR_BAD_PARAM;
|
|
|
|
|
}
|
|
|
|
|
uint16_t recordId = sensorToRecordMap[i].recordId;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
// Set up an FRS read operation
|
|
|
|
|
pSh2->opData.getFrs.frsType = recordId;
|
|
|
|
|
pSh2->opData.getFrs.pData = pSh2->frsData;
|
|
|
|
|
pSh2->frsDataLen = ARRAY_LEN(pSh2->frsData);
|
|
|
|
|
pSh2->opData.getFrs.pWords = &pSh2->frsDataLen;
|
|
|
|
|
|
|
|
|
|
// Read an FRS record
|
|
|
|
|
int status = opProcess(pSh2, &getFrsOp);
|
|
|
|
|
|
|
|
|
|
// Copy the results into pData
|
|
|
|
|
if (status == SH2_OK) {
|
|
|
|
|
stuffMetadata(pData, pSh2->frsData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Get an FRS record.
|
|
|
|
|
*
|
|
|
|
|
* @param recordId Which FRS Record to retrieve.
|
|
|
|
|
* @param pData pointer to buffer to receive the results
|
|
|
|
|
* @param[in] words Size of pData buffer, in 32-bit words.
|
|
|
|
|
* @param[out] words Number of 32-bit words retrieved.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_getFrs(uint16_t recordId, uint32_t *pData, uint16_t *words)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
if ((pData == 0) || (words == 0)) {
|
|
|
|
|
return SH2_ERR_BAD_PARAM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
// Store params for this op
|
|
|
|
|
pSh2->opData.getFrs.frsType = recordId;
|
|
|
|
|
pSh2->opData.getFrs.pData = pData;
|
|
|
|
|
pSh2->opData.getFrs.pWords = words;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &getFrsOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Set an FRS record
|
|
|
|
|
*
|
|
|
|
|
* @param recordId Which FRS Record to set.
|
|
|
|
|
* @param pData pointer to buffer containing the new data.
|
|
|
|
|
* @param words number of 32-bit words to write. (0 to delete record.)
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_setFrs(uint16_t recordId, uint32_t *pData, uint16_t words)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
if ((pData == 0) && (words != 0)) {
|
|
|
|
|
return SH2_ERR_BAD_PARAM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
pSh2->opData.setFrs.frsType = recordId;
|
|
|
|
|
pSh2->opData.setFrs.pData = pData;
|
|
|
|
|
pSh2->opData.setFrs.words = words;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &setFrsOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Get error counts.
|
|
|
|
|
*
|
|
|
|
|
* @param severity Only errors of this severity or greater are returned.
|
|
|
|
|
* @param pErrors Buffer to receive error codes.
|
|
|
|
|
* @param numErrors size of pErrors array
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_getErrors(uint8_t severity, sh2_ErrorRecord_t *pErrors, uint16_t *numErrors)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
pSh2->opData.getErrors.severity = severity;
|
|
|
|
|
pSh2->opData.getErrors.pErrors = pErrors;
|
|
|
|
|
pSh2->opData.getErrors.pNumErrors = numErrors;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &getErrorsOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Read counters related to a sensor.
|
|
|
|
|
*
|
|
|
|
|
* @param sensorId Which sensor to operate on.
|
|
|
|
|
* @param pCounts Pointer to Counts structure that will receive data.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_getCounts(sh2_SensorId_t sensorId, sh2_Counts_t *pCounts)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
pSh2->opData.getCounts.sensorId = sensorId;
|
|
|
|
|
pSh2->opData.getCounts.pCounts = pCounts;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &getCountsOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Clear counters related to a sensor.
|
|
|
|
|
*
|
|
|
|
|
* @param sensorId which sensor to operate on.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_clearCounts(sh2_SensorId_t sensorId)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pSh2->opData.sendCmd.req.command = SH2_CMD_COUNTS;
|
|
|
|
|
pSh2->opData.sendCmd.req.p[0] = SH2_COUNTS_CLEAR_COUNTS;
|
|
|
|
|
pSh2->opData.sendCmd.req.p[1] = sensorId;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &sendCmdOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Perform a tare operation on one or more axes.
|
|
|
|
|
*
|
|
|
|
|
* @param axes Bit mask specifying which axes should be tared.
|
|
|
|
|
* @param basis Which rotation vector to use as the basis for Tare adjustment.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_setTareNow(uint8_t axes, // SH2_TARE_X | SH2_TARE_Y | SH2_TARE_Z
|
|
|
|
|
sh2_TareBasis_t basis)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pSh2->opData.sendCmd.req.command = SH2_CMD_TARE;
|
|
|
|
|
pSh2->opData.sendCmd.req.p[0] = SH2_TARE_TARE_NOW;
|
|
|
|
|
pSh2->opData.sendCmd.req.p[1] = axes;
|
|
|
|
|
pSh2->opData.sendCmd.req.p[2] = basis;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &sendCmdOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Clears the previously applied tare operation.
|
|
|
|
|
*
|
|
|
|
|
* @return SH2_OK \n");
|
|
|
|
|
*/
|
|
|
|
|
int sh2_clearTare(void)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pSh2->opData.sendCmd.req.command = SH2_CMD_TARE;
|
|
|
|
|
pSh2->opData.sendCmd.req.p[0] = SH2_TARE_SET_REORIENTATION;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &sendCmdOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Persist the results of last tare operation to flash.
|
|
|
|
|
*
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_persistTare(void)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pSh2->opData.sendCmd.req.command = SH2_CMD_TARE;
|
|
|
|
|
pSh2->opData.sendCmd.req.p[0] = SH2_TARE_PERSIST_TARE;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &sendCmdOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Set the current run-time sensor reorientation. (Set to zero to clear tare.)
|
|
|
|
|
*
|
|
|
|
|
* @param orientation Quaternion rotation vector to apply as new tare.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_setReorientation(sh2_Quaternion_t *orientation)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pSh2->opData.sendCmd.req.command = SH2_CMD_TARE;
|
|
|
|
|
pSh2->opData.sendCmd.req.p[0] = SH2_TARE_SET_REORIENTATION;
|
|
|
|
|
|
|
|
|
|
// save me a lot of typing and you a lot of reading
|
|
|
|
|
uint8_t *p = pSh2->opData.sendCmd.req.p;
|
|
|
|
|
|
|
|
|
|
// Put new orientation in command parameters
|
|
|
|
|
writeu16(&p[1], toQ14(orientation->x));
|
|
|
|
|
writeu16(&p[3], toQ14(orientation->y));
|
|
|
|
|
writeu16(&p[5], toQ14(orientation->z));
|
|
|
|
|
writeu16(&p[7], toQ14(orientation->w));
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &sendCmdOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Command the sensorhub to reset.
|
|
|
|
|
*
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_reinitialize(void)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &reinitOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Save Dynamic Calibration Data to flash.
|
|
|
|
|
*
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_saveDcdNow(void)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &saveDcdNowOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Get Oscillator type.
|
|
|
|
|
*
|
|
|
|
|
* @param pOscType pointer to data structure to receive results.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_getOscType(sh2_OscType_t *pOscType)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
pSh2->opData.getOscType.pOscType = pOscType;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &getOscTypeOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Enable/Disable dynamic calibration for certain sensors
|
|
|
|
|
*
|
|
|
|
|
* @param sensors Bit mask to configure which sensors are affected.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_setCalConfig(uint8_t sensors)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
pSh2->opData.calConfig.sensors = sensors;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &setCalConfigOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Get dynamic calibration configuration settings.
|
|
|
|
|
*
|
|
|
|
|
* @param pSensors pointer to Bit mask, set on return.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_getCalConfig(uint8_t *pSensors)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
pSh2->opData.getCalConfig.pSensors = pSensors;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &getCalConfigOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Configure automatic saving of dynamic calibration data.
|
|
|
|
|
*
|
|
|
|
|
* @param enabled Enable or Disable DCD auto-save.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_setDcdAutoSave(bool enabled)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
pSh2->opData.sendCmd.req.command = SH2_CMD_DCD_SAVE;
|
|
|
|
|
pSh2->opData.sendCmd.req.p[0] = enabled ? 0 : 1;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &sendCmdOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Immediately issue all buffered sensor reports from a given sensor.
|
|
|
|
|
*
|
|
|
|
|
* @param sensorId Which sensor reports to flush.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_flush(sh2_SensorId_t sensorId)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
pSh2->opData.forceFlush.sensorId = sensorId;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &forceFlushOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Command clear DCD in RAM, then reset sensor hub.
|
|
|
|
|
*
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_clearDcdAndReset(void)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
pSh2->opData.sendCmd.req.command = SH2_CMD_CLEAR_DCD_AND_RESET;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &sendCmdOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Start simple self-calibration procedure.
|
|
|
|
|
*
|
|
|
|
|
* @parameter interval_us sensor report interval, uS.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_startCal(uint32_t interval_us)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
pSh2->opData.startCal.interval_us = interval_us;
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &startCalOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Finish simple self-calibration procedure.
|
|
|
|
|
*
|
|
|
|
|
* @parameter status contains calibration status code on return.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_finishCal(sh2_CalStatus_t *status)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
return opProcess(pSh2, &finishCalOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief send Interactive ZRO Request.
|
|
|
|
|
*
|
|
|
|
|
* @parameter intent Inform the sensor hub what sort of motion should be in progress.
|
|
|
|
|
* @return SH2_OK (0), on success. Negative value from sh2_err.h on error.
|
|
|
|
|
*/
|
|
|
|
|
int sh2_setIZro(sh2_IZroMotionIntent_t intent)
|
|
|
|
|
{
|
|
|
|
|
sh2_t *pSh2 = &_sh2;
|
|
|
|
|
|
|
|
|
|
// clear opData
|
|
|
|
|
memset(&pSh2->opData, 0, sizeof(sh2_OpData_t));
|
|
|
|
|
|
|
|
|
|
// set up opData for iZRO request
|
|
|
|
|
pSh2->opData.sendCmd.req.command = SH2_CMD_INTERACTIVE_ZRO;
|
|
|
|
|
pSh2->opData.sendCmd.req.p[0] = intent;
|
|
|
|
|
|
|
|
|
|
// Send command
|
|
|
|
|
return opProcess(pSh2, &sendCmdOp);
|
|
|
|
|
}
|