2021-11-13 16:01:24 +00:00
|
|
|
// Copyright 2021, Jan Schmidt
|
2021-11-14 11:04:03 +00:00
|
|
|
// Copyright 2021, Philipp Zabel
|
|
|
|
// Copyright 2021, Jakob Bornecrantz
|
2021-11-13 16:01:24 +00:00
|
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
|
|
/*!
|
|
|
|
* @file
|
|
|
|
* @brief WMR camera interface
|
|
|
|
* @author Jan Schmidt <jan@centricular.com>
|
2021-11-14 11:04:03 +00:00
|
|
|
* @author Philipp Zabel <philipp.zabel@gmail.com>
|
|
|
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
2021-11-13 16:01:24 +00:00
|
|
|
* @ingroup drv_wmr
|
|
|
|
*/
|
|
|
|
#include <asm/byteorder.h>
|
|
|
|
#include <libusb.h>
|
|
|
|
#include <stdlib.h>
|
2021-11-25 09:01:18 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <inttypes.h>
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
#include "os/os_threading.h"
|
2021-11-14 11:04:03 +00:00
|
|
|
#include "util/u_var.h"
|
|
|
|
#include "util/u_sink.h"
|
|
|
|
#include "util/u_frame.h"
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
#include "wmr_protocol.h"
|
|
|
|
#include "wmr_camera.h"
|
|
|
|
|
2021-11-20 14:58:21 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Defines and structs.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
#define WMR_CAM_TRACE(c, ...) U_LOG_IFL_T((c)->log_level, __VA_ARGS__)
|
|
|
|
#define WMR_CAM_DEBUG(c, ...) U_LOG_IFL_D((c)->log_level, __VA_ARGS__)
|
|
|
|
#define WMR_CAM_INFO(c, ...) U_LOG_IFL_I((c)->log_level, __VA_ARGS__)
|
|
|
|
#define WMR_CAM_WARN(c, ...) U_LOG_IFL_W((c)->log_level, __VA_ARGS__)
|
|
|
|
#define WMR_CAM_ERROR(c, ...) U_LOG_IFL_E((c)->log_level, __VA_ARGS__)
|
|
|
|
|
|
|
|
#define CAM_ENDPOINT 0x05
|
|
|
|
|
|
|
|
#define NUM_XFERS 2
|
|
|
|
|
2021-11-14 12:06:19 +00:00
|
|
|
#define WMR_CAMERA_CMD_GAIN 0x80
|
|
|
|
#define WMR_CAMERA_CMD_ON 0x81
|
|
|
|
#define WMR_CAMERA_CMD_OFF 0x82
|
|
|
|
|
2021-12-06 11:35:56 +00:00
|
|
|
#define DEFAULT_EXPOSURE 6000
|
|
|
|
#define DEFAULT_GAIN 127
|
|
|
|
|
|
|
|
#define UI_EXPOSURE_STEP_SIZE 200
|
2021-11-14 12:06:19 +00:00
|
|
|
|
|
|
|
struct wmr_camera_active_cmd
|
2021-11-13 16:01:24 +00:00
|
|
|
{
|
|
|
|
__le32 magic;
|
|
|
|
__le32 len;
|
|
|
|
__le32 cmd;
|
|
|
|
} __attribute__((packed));
|
|
|
|
|
2021-11-14 12:06:19 +00:00
|
|
|
struct wmr_camera_gain_cmd
|
|
|
|
{
|
|
|
|
__le32 magic;
|
|
|
|
__le32 len;
|
|
|
|
__le16 cmd;
|
|
|
|
__le16 camera_id;
|
2021-12-06 11:35:56 +00:00
|
|
|
__le16 exposure; //!< observed 60 to 6000 (but supports up to ~9000)
|
|
|
|
__le16 gain; //!< observed 16 to 255
|
|
|
|
__le16 camera_id2; //!< same as camera_id
|
2021-11-14 12:06:19 +00:00
|
|
|
} __attribute__((packed));
|
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
struct wmr_camera
|
|
|
|
{
|
|
|
|
libusb_context *ctx;
|
|
|
|
libusb_device_handle *dev;
|
|
|
|
|
|
|
|
bool running;
|
|
|
|
|
|
|
|
struct os_thread_helper usb_thread;
|
|
|
|
int usb_complete;
|
|
|
|
|
2021-11-20 13:50:54 +00:00
|
|
|
const struct wmr_camera_config *configs;
|
|
|
|
int config_count;
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
size_t xfer_size;
|
2021-11-14 11:04:03 +00:00
|
|
|
uint32_t frame_width, frame_height;
|
|
|
|
uint8_t last_seq;
|
2021-11-25 09:01:18 +00:00
|
|
|
uint64_t last_frame_ts;
|
|
|
|
|
|
|
|
/* Unwrapped frame sequence number */
|
|
|
|
uint64_t frame_sequence;
|
2021-11-14 11:04:03 +00:00
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
struct libusb_transfer *xfers[NUM_XFERS];
|
|
|
|
|
2021-11-25 09:00:27 +00:00
|
|
|
uint8_t last_gain, debug_gain;
|
2021-12-06 11:35:56 +00:00
|
|
|
uint8_t last_exposure, debug_exposure;
|
2021-11-25 09:00:27 +00:00
|
|
|
|
2021-11-14 11:04:03 +00:00
|
|
|
struct u_sink_debug debug_sinks[2];
|
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
enum u_logging_level log_level;
|
|
|
|
};
|
|
|
|
|
2021-11-20 14:58:21 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Helper functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
/* Some WMR headsets use 616538 byte transfers. HP G2 needs 1233018 (4 cameras)
|
|
|
|
* As a general formula, it seems we have:
|
|
|
|
* 0x6000 byte packets. Each has a 32 byte header.
|
|
|
|
* packet contains frame data for each camera in turn.
|
|
|
|
* Each frame has an extra (first) line with metadata
|
|
|
|
* Then, there's an extra 26 bytes on the end.
|
|
|
|
*
|
|
|
|
* F = camera frames X * (Y+1) + 26
|
|
|
|
* n_packets = F/(0x6000-32)
|
|
|
|
* leftover = F - n_packets*(0x6000-32)
|
|
|
|
* size = n_packets * 0x6000 + 32 + leftover,
|
|
|
|
*
|
|
|
|
* so for 2 x 640x480 cameras:
|
|
|
|
* F = 2 * 640 * 481 + 26 = 615706
|
|
|
|
* n_packets = 615706 / 24544 = 25
|
|
|
|
* leftover = 615706 - 25 * 24544 = 2106
|
|
|
|
* size = 25 * 0x6000 + 32 + 2106 = 616538
|
|
|
|
*
|
|
|
|
* For HP G2 = 4 x 640 * 480 cameras:
|
|
|
|
* F = 4 * 640 * 481 + 26 = 1231386
|
|
|
|
* n_packets = 1231386 / 24544 = 50
|
|
|
|
* leftover = 1231386 - 50 * 24544 = 4186
|
|
|
|
* size = 50 * 0x6000 + 32 + 4186 = 1233018
|
|
|
|
*
|
|
|
|
* It would be good to test these calculations on other headsets with
|
|
|
|
* different camera setups.
|
|
|
|
*/
|
2021-11-14 11:04:03 +00:00
|
|
|
static bool
|
|
|
|
compute_frame_size(struct wmr_camera *cam)
|
2021-11-13 16:01:24 +00:00
|
|
|
{
|
|
|
|
int i, cams_found = 0;
|
2021-11-14 11:04:03 +00:00
|
|
|
int width, height;
|
|
|
|
size_t F, n_packets, leftover;
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
F = 26;
|
|
|
|
|
2021-11-20 13:50:54 +00:00
|
|
|
for (i = 0; i < cam->config_count; i++) {
|
|
|
|
const struct wmr_camera_config *config = &cam->configs[i];
|
2021-11-20 14:59:52 +00:00
|
|
|
if (config->purpose != WMR_CAMERA_PURPOSE_HEAD_TRACKING) {
|
2021-11-13 16:01:24 +00:00
|
|
|
continue;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
|
2021-12-12 19:07:03 +00:00
|
|
|
WMR_CAM_DEBUG(cam, "Found head tracking camera index %d width %d height %d", i, config->roi.extent.w,
|
|
|
|
config->roi.extent.h);
|
2021-11-14 11:04:03 +00:00
|
|
|
|
|
|
|
if (cams_found == 0) {
|
2021-12-12 19:07:03 +00:00
|
|
|
width = config->roi.extent.w;
|
|
|
|
height = config->roi.extent.h;
|
|
|
|
} else if (height != config->roi.extent.h) {
|
2021-11-14 11:04:03 +00:00
|
|
|
WMR_CAM_ERROR(cam, "Head tracking sensors have mismatched heights - %u != %u. Please report",
|
2021-12-12 19:07:03 +00:00
|
|
|
height, config->roi.extent.h);
|
2021-11-14 11:04:03 +00:00
|
|
|
return false;
|
|
|
|
} else {
|
2021-12-12 19:07:03 +00:00
|
|
|
width += config->roi.extent.w;
|
2021-11-14 11:04:03 +00:00
|
|
|
}
|
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
cams_found++;
|
2021-12-12 19:07:03 +00:00
|
|
|
F += config->roi.extent.w * (config->roi.extent.h + 1);
|
2021-11-13 16:01:24 +00:00
|
|
|
}
|
|
|
|
|
2021-11-20 14:59:52 +00:00
|
|
|
if (cams_found == 0) {
|
2021-11-14 11:04:03 +00:00
|
|
|
return false;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-14 11:04:03 +00:00
|
|
|
|
2021-11-20 14:59:52 +00:00
|
|
|
if (width < 1280 || height < 480) {
|
2021-11-14 11:04:03 +00:00
|
|
|
return false;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
n_packets = F / (0x6000 - 32);
|
|
|
|
leftover = F - n_packets * (0x6000 - 32);
|
|
|
|
|
2021-11-14 11:04:03 +00:00
|
|
|
cam->xfer_size = n_packets * 0x6000 + 32 + leftover;
|
|
|
|
|
|
|
|
cam->frame_width = width;
|
|
|
|
cam->frame_height = height;
|
2021-11-13 16:01:24 +00:00
|
|
|
|
2021-11-14 11:04:03 +00:00
|
|
|
WMR_CAM_INFO(cam, "WMR camera framebuffer %u x %u - %zu transfer size", cam->frame_width, cam->frame_height,
|
|
|
|
cam->xfer_size);
|
|
|
|
|
|
|
|
return true;
|
2021-11-13 16:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *
|
|
|
|
wmr_cam_usb_thread(void *ptr)
|
|
|
|
{
|
|
|
|
struct wmr_camera *cam = ptr;
|
|
|
|
|
|
|
|
os_thread_helper_lock(&cam->usb_thread);
|
|
|
|
while (os_thread_helper_is_running_locked(&cam->usb_thread) && !cam->usb_complete) {
|
|
|
|
os_thread_helper_unlock(&cam->usb_thread);
|
|
|
|
|
|
|
|
libusb_handle_events_completed(cam->ctx, &cam->usb_complete);
|
|
|
|
|
|
|
|
os_thread_helper_lock(&cam->usb_thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
os_thread_helper_wait_locked(&cam->usb_thread);
|
|
|
|
os_thread_helper_unlock(&cam->usb_thread);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-11-20 14:53:03 +00:00
|
|
|
send_buffer_to_device(struct wmr_camera *cam, uint8_t *buf, uint8_t len)
|
2021-11-13 16:01:24 +00:00
|
|
|
{
|
|
|
|
struct libusb_transfer *xfer;
|
|
|
|
uint8_t *data;
|
|
|
|
|
|
|
|
xfer = libusb_alloc_transfer(0);
|
2021-11-20 14:59:52 +00:00
|
|
|
if (xfer == NULL) {
|
2021-11-13 16:01:24 +00:00
|
|
|
return LIBUSB_ERROR_NO_MEM;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
data = malloc(len);
|
|
|
|
if (data == NULL) {
|
|
|
|
libusb_free_transfer(xfer);
|
|
|
|
return LIBUSB_ERROR_NO_MEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(data, buf, len);
|
|
|
|
|
|
|
|
libusb_fill_bulk_transfer(xfer, cam->dev, CAM_ENDPOINT | LIBUSB_ENDPOINT_OUT, data, len, NULL, NULL, 0);
|
|
|
|
xfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER;
|
|
|
|
|
|
|
|
return libusb_submit_transfer(xfer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-11-20 14:53:03 +00:00
|
|
|
set_active(struct wmr_camera *cam, bool active)
|
2021-11-13 16:01:24 +00:00
|
|
|
{
|
2021-11-20 14:53:03 +00:00
|
|
|
struct wmr_camera_active_cmd cmd = {
|
|
|
|
.magic = __cpu_to_le32(WMR_MAGIC),
|
|
|
|
.len = __cpu_to_le32(sizeof(struct wmr_camera_active_cmd)),
|
|
|
|
.cmd = __cpu_to_le32(active ? WMR_CAMERA_CMD_ON : WMR_CAMERA_CMD_OFF),
|
|
|
|
};
|
2021-11-13 16:01:24 +00:00
|
|
|
|
2021-11-20 14:53:03 +00:00
|
|
|
return send_buffer_to_device(cam, (uint8_t *)&cmd, sizeof(cmd));
|
2021-11-13 16:01:24 +00:00
|
|
|
}
|
|
|
|
|
2021-11-20 14:58:21 +00:00
|
|
|
|
|
|
|
static void LIBUSB_CALL
|
|
|
|
img_xfer_cb(struct libusb_transfer *xfer)
|
|
|
|
{
|
|
|
|
struct wmr_camera *cam = xfer->user_data;
|
|
|
|
|
|
|
|
if (xfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
|
|
|
WMR_CAM_TRACE(cam, "Camera transfer completed with status %u", xfer->status);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xfer->actual_length < xfer->length) {
|
|
|
|
WMR_CAM_DEBUG(cam, "Camera transfer only delivered %d bytes", xfer->actual_length);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
WMR_CAM_TRACE(cam, "Camera transfer complete - %d bytes of %d", xfer->actual_length, xfer->length);
|
|
|
|
|
|
|
|
/* Convert the output into frames and send them off to debug / tracking */
|
|
|
|
struct xrt_frame *xf = NULL;
|
|
|
|
|
|
|
|
/* There's always one extra line of pixels with exposure info */
|
|
|
|
u_frame_create_one_off(XRT_FORMAT_L8, cam->frame_width, cam->frame_height + 1, &xf);
|
|
|
|
|
|
|
|
const uint8_t *src = xfer->buffer;
|
|
|
|
|
|
|
|
uint8_t *dst = xf->data;
|
|
|
|
size_t dst_remain = xf->size;
|
|
|
|
const size_t chunk_size = 0x6000 - 32;
|
|
|
|
|
|
|
|
while (dst_remain > 0) {
|
|
|
|
const size_t to_copy = dst_remain > chunk_size ? chunk_size : dst_remain;
|
|
|
|
|
2021-11-25 09:01:18 +00:00
|
|
|
/* 32 byte header seems to contain:
|
|
|
|
* __be32 magic = "Dlo+"
|
|
|
|
* __le32 frame_ctr;
|
|
|
|
* __le32 slice_ctr;
|
|
|
|
* __u8 unknown[20]; - binary block where all bytes are different each slice,
|
|
|
|
* but repeat every 8 slices. They're different each boot
|
|
|
|
* of the headset. Might just be uninitialised memory?
|
|
|
|
*/
|
2021-11-20 14:58:21 +00:00
|
|
|
src += 0x20;
|
|
|
|
|
|
|
|
memcpy(dst, src, to_copy);
|
|
|
|
src += to_copy;
|
|
|
|
dst += to_copy;
|
|
|
|
dst_remain -= to_copy;
|
|
|
|
}
|
|
|
|
|
2021-11-25 09:01:18 +00:00
|
|
|
/* There should be exactly a 26 byte footer left over */
|
|
|
|
assert(xfer->buffer + xfer->length - src == 26);
|
|
|
|
|
|
|
|
/* Footer contains:
|
|
|
|
* __le64 start_ts; - 100ns unit timestamp, from same clock as video_timestamps on the IMU feed
|
|
|
|
* __le64 end_ts; - 100ns unit timestamp, always about 111000 * 100ns later than start_ts ~= 90Hz
|
|
|
|
* __le16 ctr1; - Counter that increments by 88, but sometimes by 96, and wraps at 16384
|
|
|
|
* __le16 unknown0 - Unknown value, has only ever been 0
|
|
|
|
* __be32 magic - "Dlo+"
|
|
|
|
* __le16 frametype?- either 0x00 or 0x02. Every 3rd frame is 0x0, others are 0x2. Might be SLAM vs controllers?
|
|
|
|
*/
|
|
|
|
uint64_t frame_start_ts = read64(&src) * WMR_MS_HOLOLENS_NS_PER_TICK;
|
|
|
|
uint64_t frame_end_ts = read64(&src) * WMR_MS_HOLOLENS_NS_PER_TICK;
|
|
|
|
int64_t delta = frame_end_ts - frame_start_ts;
|
|
|
|
|
|
|
|
uint16_t unknown16 = read16(&src);
|
|
|
|
uint16_t unknown16_2 = read16(&src);
|
|
|
|
|
|
|
|
WMR_CAM_DEBUG(
|
|
|
|
cam, "Frame start TS %" PRIu64 " (%" PRIi64 " since last) end %" PRIu64 " dt %" PRIi64 " unknown %u %u",
|
|
|
|
frame_start_ts, frame_start_ts - cam->last_frame_ts, frame_end_ts, delta, unknown16, unknown16_2);
|
|
|
|
|
2021-11-20 14:58:21 +00:00
|
|
|
uint16_t exposure = xf->data[6] << 8 | xf->data[7];
|
|
|
|
uint8_t seq = xf->data[89];
|
2021-11-25 09:01:18 +00:00
|
|
|
uint8_t seq_delta = seq - cam->last_seq;
|
2021-11-20 14:58:21 +00:00
|
|
|
|
2021-11-25 09:01:18 +00:00
|
|
|
/* Extend the sequence number to 64-bits */
|
|
|
|
cam->frame_sequence += seq_delta;
|
|
|
|
|
|
|
|
WMR_CAM_TRACE(cam, "Camera frame seq %u (prev %u) -> frame %" PRIu64 " - exposure %u", seq, cam->last_seq,
|
|
|
|
cam->frame_sequence, exposure);
|
|
|
|
|
|
|
|
xf->source_sequence = cam->frame_sequence;
|
|
|
|
xf->timestamp = xf->source_timestamp = frame_start_ts;
|
|
|
|
|
|
|
|
cam->last_frame_ts = frame_start_ts;
|
|
|
|
cam->last_seq = seq;
|
2021-11-20 14:58:21 +00:00
|
|
|
|
|
|
|
/* Exposure of 0 is a dark frame for controller tracking */
|
|
|
|
int sink_index = (exposure == 0) ? 1 : 0;
|
|
|
|
|
|
|
|
if (u_sink_debug_is_active(&cam->debug_sinks[sink_index])) {
|
|
|
|
u_sink_debug_push_frame(&cam->debug_sinks[sink_index], xf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Push frame for tracking */
|
|
|
|
xrt_frame_reference(&xf, NULL);
|
|
|
|
|
2021-12-06 11:35:56 +00:00
|
|
|
if (cam->last_exposure != cam->debug_exposure || cam->last_gain != cam->debug_gain) {
|
2021-11-25 09:00:27 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < cam->config_count; i++) {
|
|
|
|
const struct wmr_camera_config *config = &cam->configs[i];
|
|
|
|
if (config->purpose != WMR_CAMERA_PURPOSE_HEAD_TRACKING) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-12-06 11:35:56 +00:00
|
|
|
wmr_camera_set_exposure_gain(cam, config->location, cam->debug_exposure * UI_EXPOSURE_STEP_SIZE,
|
|
|
|
cam->debug_gain);
|
2021-11-25 09:00:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-20 14:58:21 +00:00
|
|
|
out:
|
|
|
|
libusb_submit_transfer(xfer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* 'Exported' functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
struct wmr_camera *
|
2021-11-20 14:50:47 +00:00
|
|
|
wmr_camera_open(struct xrt_prober_device *dev_holo, enum u_logging_level log_level)
|
2021-11-13 16:01:24 +00:00
|
|
|
{
|
|
|
|
struct wmr_camera *cam = calloc(1, sizeof(struct wmr_camera));
|
|
|
|
int res, i;
|
|
|
|
|
2021-11-20 14:50:47 +00:00
|
|
|
cam->log_level = log_level;
|
2021-11-25 09:00:27 +00:00
|
|
|
cam->debug_gain = DEFAULT_GAIN;
|
2021-12-06 11:35:56 +00:00
|
|
|
cam->debug_exposure = DEFAULT_EXPOSURE / UI_EXPOSURE_STEP_SIZE;
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
if (os_thread_helper_init(&cam->usb_thread) != 0) {
|
|
|
|
WMR_CAM_ERROR(cam, "Failed to initialise threading");
|
|
|
|
wmr_camera_free(cam);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = libusb_init(&cam->ctx);
|
2021-11-20 14:59:52 +00:00
|
|
|
if (res < 0) {
|
2021-11-13 16:01:24 +00:00
|
|
|
goto fail;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
cam->dev = libusb_open_device_with_vid_pid(cam->ctx, dev_holo->vendor_id, dev_holo->product_id);
|
2021-11-20 14:59:52 +00:00
|
|
|
if (cam->dev == NULL) {
|
2021-11-13 16:01:24 +00:00
|
|
|
goto fail;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
res = libusb_claim_interface(cam->dev, 3);
|
2021-11-20 14:59:52 +00:00
|
|
|
if (res < 0) {
|
2021-11-13 16:01:24 +00:00
|
|
|
goto fail;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
cam->usb_complete = 0;
|
|
|
|
if (os_thread_helper_start(&cam->usb_thread, wmr_cam_usb_thread, cam) != 0) {
|
|
|
|
WMR_CAM_ERROR(cam, "Failed to start camera USB thread");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_XFERS; i++) {
|
|
|
|
cam->xfers[i] = libusb_alloc_transfer(0);
|
|
|
|
if (cam->xfers[i] == NULL) {
|
|
|
|
res = LIBUSB_ERROR_NO_MEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-14 11:04:03 +00:00
|
|
|
u_sink_debug_init(&cam->debug_sinks[0]);
|
|
|
|
u_sink_debug_init(&cam->debug_sinks[1]);
|
|
|
|
u_var_add_root(cam, "WMR Camera", true);
|
2021-11-20 13:54:46 +00:00
|
|
|
u_var_add_log_level(cam, &cam->log_level, "Log level");
|
2021-11-25 09:00:27 +00:00
|
|
|
u_var_add_u8(cam, &cam->debug_gain, "Gain");
|
2021-12-06 11:35:56 +00:00
|
|
|
u_var_add_u8(cam, &cam->debug_exposure, "Exposure * 200");
|
2021-11-14 11:04:03 +00:00
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
return cam;
|
|
|
|
|
2021-11-20 14:59:52 +00:00
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
fail:
|
|
|
|
WMR_CAM_ERROR(cam, "Failed to open camera: %s", libusb_error_name(res));
|
|
|
|
wmr_camera_free(cam);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
wmr_camera_free(struct wmr_camera *cam)
|
|
|
|
{
|
2021-11-20 13:29:24 +00:00
|
|
|
// Stop the camera.
|
2021-11-13 16:01:24 +00:00
|
|
|
wmr_camera_stop(cam);
|
|
|
|
|
2021-11-20 13:29:24 +00:00
|
|
|
// Tidy the variable tracking.
|
|
|
|
u_var_remove_root(cam);
|
|
|
|
u_sink_debug_destroy(&cam->debug_sinks[0]);
|
|
|
|
u_sink_debug_destroy(&cam->debug_sinks[1]);
|
|
|
|
|
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
if (cam->ctx != NULL) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
os_thread_helper_lock(&cam->usb_thread);
|
|
|
|
cam->usb_complete = 1;
|
|
|
|
os_thread_helper_unlock(&cam->usb_thread);
|
|
|
|
|
2021-11-20 14:59:52 +00:00
|
|
|
if (cam->dev != NULL) {
|
2021-11-13 16:01:24 +00:00
|
|
|
libusb_close(cam->dev);
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
os_thread_helper_destroy(&cam->usb_thread);
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_XFERS; i++) {
|
|
|
|
if (cam->xfers[i] != NULL)
|
|
|
|
libusb_free_transfer(cam->xfers[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
libusb_exit(cam->ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(cam);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2021-11-20 13:50:54 +00:00
|
|
|
wmr_camera_start(struct wmr_camera *cam, const struct wmr_camera_config *cam_configs, int config_count)
|
2021-11-13 16:01:24 +00:00
|
|
|
{
|
2021-12-06 22:56:22 +00:00
|
|
|
int res = 0;
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
cam->configs = cam_configs;
|
2021-11-20 13:50:54 +00:00
|
|
|
cam->config_count = config_count;
|
2021-11-14 11:04:03 +00:00
|
|
|
if (!compute_frame_size(cam)) {
|
|
|
|
WMR_CAM_WARN(cam, "Invalid config or no head tracking cameras found");
|
2021-11-13 16:01:24 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2021-11-20 14:53:03 +00:00
|
|
|
res = set_active(cam, false);
|
2021-11-20 14:59:52 +00:00
|
|
|
if (res < 0) {
|
2021-11-13 16:01:24 +00:00
|
|
|
goto fail;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
|
2021-11-20 14:53:03 +00:00
|
|
|
res = set_active(cam, true);
|
2021-11-20 14:59:52 +00:00
|
|
|
if (res < 0) {
|
2021-11-13 16:01:24 +00:00
|
|
|
goto fail;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
|
2021-12-06 22:56:22 +00:00
|
|
|
for (int i = 0; i < cam->config_count; i++) {
|
2021-11-20 13:50:54 +00:00
|
|
|
const struct wmr_camera_config *config = &cam->configs[i];
|
2021-11-20 14:59:52 +00:00
|
|
|
if (config->purpose != WMR_CAMERA_PURPOSE_HEAD_TRACKING) {
|
2021-11-14 12:06:19 +00:00
|
|
|
continue;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-14 12:06:19 +00:00
|
|
|
|
2021-12-06 11:35:56 +00:00
|
|
|
res = wmr_camera_set_exposure_gain(cam, config->location, cam->debug_exposure * UI_EXPOSURE_STEP_SIZE,
|
|
|
|
cam->debug_gain);
|
2021-11-14 12:06:19 +00:00
|
|
|
if (res < 0) {
|
|
|
|
WMR_CAM_ERROR(cam, "Failed to set initial gain for camera %d", i);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-06 22:56:22 +00:00
|
|
|
for (int i = 0; i < NUM_XFERS; i++) {
|
2021-11-13 16:01:24 +00:00
|
|
|
uint8_t *recv_buf = malloc(cam->xfer_size);
|
|
|
|
|
|
|
|
libusb_fill_bulk_transfer(cam->xfers[i], cam->dev, 0x85, recv_buf, cam->xfer_size, img_xfer_cb, cam, 0);
|
|
|
|
cam->xfers[i]->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
|
|
|
|
|
|
|
|
res = libusb_submit_transfer(cam->xfers[i]);
|
2021-11-20 14:59:52 +00:00
|
|
|
if (res < 0) {
|
2021-11-13 16:01:24 +00:00
|
|
|
goto fail;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WMR_CAM_INFO(cam, "WMR camera started");
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
2021-11-20 14:59:52 +00:00
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
fail:
|
2021-12-06 22:56:22 +00:00
|
|
|
if (res < 0) {
|
2021-11-13 16:01:24 +00:00
|
|
|
WMR_CAM_ERROR(cam, "Error starting camera input: %s", libusb_error_name(res));
|
2021-12-06 22:56:22 +00:00
|
|
|
}
|
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
wmr_camera_stop(cam);
|
2021-12-06 22:56:22 +00:00
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
wmr_camera_stop(struct wmr_camera *cam)
|
|
|
|
{
|
|
|
|
int res, i;
|
|
|
|
|
2021-11-20 14:59:52 +00:00
|
|
|
if (!cam->running) {
|
2021-11-13 16:01:24 +00:00
|
|
|
return true;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
cam->running = false;
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_XFERS; i++) {
|
2021-11-20 14:59:52 +00:00
|
|
|
if (cam->xfers[i] != NULL) {
|
2021-11-13 16:01:24 +00:00
|
|
|
libusb_cancel_transfer(cam->xfers[i]);
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
}
|
|
|
|
|
2021-11-20 14:53:03 +00:00
|
|
|
res = set_active(cam, false);
|
2021-11-20 14:59:52 +00:00
|
|
|
if (res < 0) {
|
2021-11-13 16:01:24 +00:00
|
|
|
goto fail;
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
2021-11-13 16:01:24 +00:00
|
|
|
|
|
|
|
WMR_CAM_INFO(cam, "WMR camera stopped");
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
2021-11-20 14:59:52 +00:00
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
fail:
|
2021-11-20 14:59:52 +00:00
|
|
|
if (res < 0) {
|
2021-11-13 16:01:24 +00:00
|
|
|
WMR_CAM_ERROR(cam, "Error stopping camera input: %s", libusb_error_name(res));
|
2021-11-20 14:59:52 +00:00
|
|
|
}
|
|
|
|
|
2021-11-13 16:01:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-11-14 12:06:19 +00:00
|
|
|
|
|
|
|
int
|
2021-12-06 11:35:56 +00:00
|
|
|
wmr_camera_set_exposure_gain(struct wmr_camera *cam, uint8_t camera_id, uint16_t exposure, uint8_t gain)
|
2021-11-14 12:06:19 +00:00
|
|
|
{
|
2021-11-20 14:53:03 +00:00
|
|
|
struct wmr_camera_gain_cmd cmd = {
|
|
|
|
.magic = __cpu_to_le32(WMR_MAGIC),
|
|
|
|
.len = __cpu_to_le32(sizeof(struct wmr_camera_gain_cmd)),
|
|
|
|
.cmd = __cpu_to_le16(WMR_CAMERA_CMD_GAIN),
|
|
|
|
.camera_id = __cpu_to_le16(camera_id),
|
2021-12-06 11:35:56 +00:00
|
|
|
.exposure = __cpu_to_le16(exposure),
|
2021-11-20 14:53:03 +00:00
|
|
|
.gain = __cpu_to_le16(gain),
|
|
|
|
.camera_id2 = __cpu_to_le16(camera_id),
|
|
|
|
};
|
|
|
|
|
2021-11-25 09:00:27 +00:00
|
|
|
cam->last_gain = gain;
|
|
|
|
|
2021-11-20 14:53:03 +00:00
|
|
|
return send_buffer_to_device(cam, (uint8_t *)&cmd, sizeof(cmd));
|
2021-11-14 12:06:19 +00:00
|
|
|
}
|