monado/src/xrt/drivers/rift_s/rift_s_radio.c

302 lines
9 KiB
C
Raw Normal View History

/*
* Copyright 2020 Jan Schmidt
* SPDX-License-Identifier: BSL-1.0
*
* OpenHMD - Free and Open Source API and drivers for immersive technology.
*/
/*!
* @file
* @brief Oculus Rift S HMD Radio management
*
* Functions for serialising requests to communicate with
* Touch controllers over the HMDs wireless radio link,
* collecting responses and delivering them back via callbacks.
*
* Ported from OpenHMD
*
* @author Jan Schmidt <jan@centricular.com>
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "util/u_misc.h"
#include "rift_s.h"
#include "rift_s_radio.h"
#include "rift_s_protocol.h"
/* Struct that forms a double linked queue of pending commands,
* with the head being the currently active command */
struct rift_s_radio_command
{
rift_s_radio_command *prev;
rift_s_radio_command *next;
/* Request packet data */
rift_s_hmd_radio_command_t read_command;
/* Completion callback */
rift_s_radio_completion_fn cb;
void *cb_data;
};
static int
get_radio_response_report(struct os_hid_device *hid, rift_s_hmd_radio_response_t *radio_response)
{
int ret;
radio_response->cmd = 0xb;
ret = os_hid_get_feature(hid, radio_response->cmd, (uint8_t *)(radio_response),
sizeof(rift_s_hmd_radio_response_t));
return ret;
}
void
rift_s_radio_update(rift_s_radio_state *state, struct os_hid_device *hid)
{
bool read_another = false;
do {
/* Send a radio command if there is none active and some pending */
if (state->command_result_pending == false && state->pending_commands) {
rift_s_radio_command *cmd = state->pending_commands;
rift_s_hmd_radio_command_t *pkt = &cmd->read_command;
pkt->cmd = 0x12;
os_hid_set_feature(hid, (uint8_t *)pkt, sizeof(*pkt));
// rift_s_hexdump_buffer ("ControllerFWSend", (unsigned char *)(pkt), sizeof(*pkt));
state->command_result_pending = true;
}
if (!state->command_result_pending)
break; /* Nothing to do right now */
/* There's a command result pending, poll the radio response until it's complete */
rift_s_hmd_radio_response_t radio_response;
/* The radio response is ready when the busy flag has cleared, and the seqnum
* has incremented */
int ret = get_radio_response_report(hid, &radio_response);
if (ret < 2) {
break; /* HID read failed - bail */
}
if (radio_response.busy_flag != 0x00 || radio_response.seqnum == state->last_radio_seqnum) {
if (radio_response.busy_flag)
state->last_radio_seqnum = radio_response.seqnum;
return;
}
state->last_radio_seqnum = radio_response.seqnum;
/* We have the controller response! */
assert(ret > 3 && ret <= (int)sizeof(radio_response));
state->command_result_pending = false;
// rift_s_hexdump_buffer ("ControllerFWReply", (unsigned char *)(&radio_response), ret);
assert(state->pending_commands != NULL);
rift_s_radio_command *cmd = state->pending_commands;
/* Pop the head off the cmds queue, because it's complete now */
state->pending_commands = cmd->next;
if (state->pending_commands == NULL)
state->pending_commands_tail = NULL;
else
state->pending_commands->prev = NULL;
/* Call the completion callback */
if (cmd->cb)
cmd->cb(true, radio_response.response_bytes, ret - 3, cmd->cb_data);
free(cmd);
read_another = true;
} while (read_another);
}
void
rift_s_radio_state_init(rift_s_radio_state *state)
{
state->command_result_pending = false;
state->pending_commands = NULL;
state->pending_commands_tail = NULL;
state->last_radio_seqnum = -1;
}
void
rift_s_radio_state_clear(rift_s_radio_state *state)
{
/* Clear and free any pending commands. */
rift_s_radio_command *head = state->pending_commands;
while (head != NULL) {
rift_s_radio_command *prev = head;
head = prev->next;
if (prev->cb)
prev->cb(false, NULL, 0, prev->cb_data);
free(prev);
}
state->pending_commands = state->pending_commands_tail = NULL;
}
void
rift_s_radio_queue_command(rift_s_radio_state *state,
const uint64_t device_id,
const uint8_t *cmd_bytes,
const int cmd_bytes_len,
rift_s_radio_completion_fn cb,
void *cb_data)
{
rift_s_radio_command *cmd = U_TYPED_CALLOC(rift_s_radio_command);
assert(cmd_bytes_len <= (int)sizeof(cmd->read_command.cmd_bytes));
cmd->read_command.device_id = device_id;
memcpy(cmd->read_command.cmd_bytes, cmd_bytes, cmd_bytes_len);
cmd->cb = cb;
cmd->cb_data = cb_data;
/* Append to the pending commands queue. The command itself will be sent by the update() function
* when possible */
if (state->pending_commands_tail == NULL) {
assert(state->pending_commands == NULL);
state->pending_commands = state->pending_commands_tail = cmd;
} else {
state->pending_commands_tail->next = cmd;
cmd->prev = state->pending_commands_tail;
state->pending_commands_tail = cmd;
}
}
/* The current blocks are ~2-3KB, so this should be enough to read
* the JSON config: */
#define MAX_JSON_LEN 4096
typedef struct rift_s_radio_json_read_state
{
rift_s_radio_state *state;
uint64_t device_id;
rift_s_radio_completion_fn cb;
void *cb_data;
uint32_t cur_offset;
uint16_t block_len; /* Expected length, from the header */
uint8_t data[MAX_JSON_LEN + 1];
uint16_t data_len;
} rift_s_radio_json_read_state;
static void
read_json_cb(bool success, uint8_t *response_bytes, int response_bytes_len, rift_s_radio_json_read_state *json_read)
{
if (!success) {
/* Failed, report to the caller */
goto fail;
}
if (response_bytes_len < 5) {
RIFT_S_WARN("Not enough bytes in radio response - needed 5, got %d\n", response_bytes_len);
goto fail;
}
uint8_t reply_len = response_bytes[4];
response_bytes += 5;
if (json_read->cur_offset == 0) {
/* Read the header 0u32 0x20 01 00 62 09 7b 22 67 79 | .{..... ..b.{"gy
72 6f 5f 6d 22 3a 5b 2d 30 2e 30 31 34 33 35 36 | ro_m":[-0.014356
38 38 36 36 2c 2d 30 2e | 8866,-0.
= len 0x20, 0001 (file type, or sequence number?), 0x0962 = file length */
if (reply_len < 4) {
RIFT_S_WARN("Not enough bytes in remote configuration header - needed 4, got %d\n", reply_len);
goto fail; /* Not enough bytes in header */
}
uint16_t file_type = (response_bytes[1] << 8) | response_bytes[0];
uint16_t block_len = (response_bytes[3] << 8) | response_bytes[2];
if (file_type != 1) {
RIFT_S_WARN("Unknown file type in remote configuration header - expected 1, got %d\n",
file_type);
goto fail;
}
/* Assert if the MAX_JSON_LEN ever needs expanding */
assert(block_len <= MAX_JSON_LEN);
if (block_len > MAX_JSON_LEN) {
RIFT_S_WARN(
"Remote configuration block too long. Please expand the read buffer (needed %u bytes)\n",
block_len);
goto fail;
}
json_read->block_len = block_len;
json_read->cur_offset = 0x4;
} else {
uint16_t data_remain = json_read->block_len - json_read->data_len;
if (reply_len > data_remain)
reply_len = data_remain; /* Truncate any over-read */
/* Append these bytes to the buffer */
memcpy(json_read->data + json_read->data_len, response_bytes, reply_len);
json_read->data_len += reply_len;
}
/* If there is no more to read, then report success to the caller and return */
if (json_read->data_len >= json_read->block_len) {
json_read->data[json_read->data_len] = 0;
if (json_read->cb)
json_read->cb(true, json_read->data, json_read->data_len, json_read->cb_data);
free(json_read);
return;
}
/* Otherwise request more data */
uint8_t read_len = MIN(0x20, json_read->block_len - json_read->data_len);
uint8_t read_cmd[] = {0x2b, 0x20, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00};
read_cmd[4] = json_read->cur_offset;
read_cmd[5] = json_read->cur_offset >> 8;
read_cmd[6] = json_read->cur_offset >> 16;
read_cmd[7] = json_read->cur_offset >> 24;
read_cmd[8] = read_len;
rift_s_radio_queue_command(json_read->state, json_read->device_id, read_cmd, sizeof(read_cmd),
(rift_s_radio_completion_fn)read_json_cb, json_read);
json_read->cur_offset += read_len;
return;
fail:
if (json_read->cb)
json_read->cb(success, json_read->data, json_read->data_len, json_read->cb_data);
free(json_read);
return;
}
void
rift_s_radio_get_json_block(rift_s_radio_state *state,
const uint64_t device_id,
rift_s_radio_completion_fn cb,
void *cb_data)
{
/* Configuration JSON block reading */
rift_s_radio_json_read_state *json_read = U_TYPED_CALLOC(rift_s_radio_json_read_state);
/* cmd = 0x2b reply_buffer_len = 0x20 timeout(?) = 0x3e8 (=1000) offset = 0u32 len = 0x20 */
const uint8_t read_cmd[] = {0x2b, 0x20, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00};
json_read->state = state;
json_read->device_id = device_id;
json_read->cb = cb;
json_read->cb_data = cb_data;
rift_s_radio_queue_command(state, device_id, read_cmd, sizeof(read_cmd),
(rift_s_radio_completion_fn)read_json_cb, json_read);
}