Initial commit
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
This commit is contained in:
commit
b694a15cb1
11 changed files with 1115 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
cmake-build-debug/
|
||||||
|
cmake-build-release/
|
||||||
|
|
||||||
|
build/
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "modules/Fusion"]
|
||||||
|
path = modules/Fusion
|
||||||
|
url = https://github.com/xioTechnologies/Fusion.git
|
23
CMakeLists.txt
Normal file
23
CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(nrealAirLinuxDriver)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 17)
|
||||||
|
|
||||||
|
add_subdirectory(modules/Fusion/Fusion)
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_search_module(LIBUSB1 REQUIRED libusb-1.0)
|
||||||
|
|
||||||
|
add_executable(nrealAirLinuxDriver
|
||||||
|
src/device3.c
|
||||||
|
src/device4.c
|
||||||
|
src/driver.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(nrealAirLinuxDriver
|
||||||
|
SYSTEM BEFORE PRIVATE modules/Fusion ${LIBUSB1_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(nrealAirLinuxDriver
|
||||||
|
${LIBUSB1_LIBRARIES} Fusion m
|
||||||
|
)
|
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 thejackimonster. All rights reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
62
README.md
Normal file
62
README.md
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
# Custom user-space driver for the nreal Air to use it on Linux
|
||||||
|
|
||||||
|
## Information before use
|
||||||
|
|
||||||
|
The code is provided as is and it's free to use. However the contributors can neither guarantee that
|
||||||
|
it will work or that it won't damage your device since all of it is based on reverse-engineering
|
||||||
|
instead of public documentation. The contributors are not responsible for proper or even official
|
||||||
|
support. So use it at your own risk!
|
||||||
|
|
||||||
|
## Inspiration and motivation
|
||||||
|
|
||||||
|
Because I've written a user-space driver before for a [graphics tablet](https://gitlab.com/TheJackiMonster/HuionGT191LinuxDriver),
|
||||||
|
I thought I might look into it. To my surprise video output, audio output (stereo) and audio input (microphone) already
|
||||||
|
worked using drivers from the [Linux kernel](https://linux-hardware.org/?id=usb:3318-0424). So the only piece missing
|
||||||
|
for full use was the IMU sensor data in theory.
|
||||||
|
|
||||||
|
A big help for implementing this piece of software was the source code and feedback from a custom
|
||||||
|
driver for Windows [here](https://github.com/MSmithDev/AirAPI_Windows/). Without that I would have
|
||||||
|
needed to find out payloads my own. So big thanks to such projects being open-source!
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
The driver will read, parse and interpret sensor data from two HID interfaces to feed custom event
|
||||||
|
callbacks with data which can be used in user-space applications (for example whether buttons have
|
||||||
|
been pressed, the current brightness level and the orientation quaternion/matrix/euler angles).
|
||||||
|
|
||||||
|
It's still a work-in-progress project since the goal would be to wire such events up into a
|
||||||
|
compositor to render whole screens virtually depending on your 6-DoF orientation (without position).
|
||||||
|
|
||||||
|
Also keep in mind that this software will only run on Linux including devices like the Steam Deck,
|
||||||
|
Pinephone or other mobile Linux devices.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
You can build the binary using `cmake` and there are only two dependencies for now:
|
||||||
|
- [libusb](https://libusb.info/)
|
||||||
|
- [Fusion](https://github.com/xioTechnologies/Fusion)
|
||||||
|
|
||||||
|
Fusion is a sensor fusion library which is integrated as git submodule. So when you checkout the
|
||||||
|
repository, just update the submodules to get it. The library `libusb` should be pretty common in
|
||||||
|
most Linux distributions repositories.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
The build process should be straight forward:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
It's easiest to run the software with root privileges. But theoretically it should be possible to
|
||||||
|
adjust rules of your device to not need them for read/write access vis USB. It's definitely not
|
||||||
|
planned to require that in the future (but for now):
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo nrealAirLinuxDriver
|
||||||
|
```
|
1
modules/Fusion
Submodule
1
modules/Fusion
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6c0de47eb6da355985c86030e78e552ab588e049
|
345
src/device3.c
Normal file
345
src/device3.c
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
//
|
||||||
|
// Created by thejackimonster on 30.03.23.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2023 thejackimonster. All rights reserved.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "device3.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
device3_type* device3_open(device3_event_callback callback) {
|
||||||
|
device3_type* device = (device3_type*) malloc(sizeof(device3_type));
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
perror("Not allocated!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(device, 0, sizeof(device3_type));
|
||||||
|
device->vendor_id = 0x3318;
|
||||||
|
device->product_id = 0x0424;
|
||||||
|
device->callback = callback;
|
||||||
|
|
||||||
|
if (0 != libusb_init(&(device->context))) {
|
||||||
|
perror("No context!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->handle = libusb_open_device_with_vid_pid(
|
||||||
|
device->context,
|
||||||
|
device->vendor_id,
|
||||||
|
device->product_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!device->handle) {
|
||||||
|
perror("No handle!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_device* dev = libusb_get_device(device->handle);
|
||||||
|
if (!dev) {
|
||||||
|
perror("No dev!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct libusb_device_descriptor desc;
|
||||||
|
if (0 != libusb_get_device_descriptor(dev, &desc)) {
|
||||||
|
perror("No desc!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct libusb_config_descriptor* config;
|
||||||
|
for (uint8_t i = 0; i < desc.bNumConfigurations; i++) {
|
||||||
|
if (0 != libusb_get_config_descriptor(dev, i, &config)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct libusb_interface* interface;
|
||||||
|
for (uint8_t j = 0; j < config->bNumInterfaces; j++) {
|
||||||
|
interface = &(config->interface[j]);
|
||||||
|
|
||||||
|
const struct libusb_interface_descriptor* setting;
|
||||||
|
for (int k = 0; k < interface->num_altsetting; k++) {
|
||||||
|
setting = &(interface->altsetting[k]);
|
||||||
|
|
||||||
|
if (LIBUSB_CLASS_HID != setting->bInterfaceClass) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (3 != setting->bInterfaceNumber) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->interface_number = setting->bInterfaceNumber;
|
||||||
|
|
||||||
|
if (2 != setting->bNumEndpoints) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->endpoint_address_in = setting->endpoint[0].bEndpointAddress;
|
||||||
|
device->max_packet_size_in = setting->endpoint[0].wMaxPacketSize;
|
||||||
|
|
||||||
|
device->endpoint_address_out = setting->endpoint[1].bEndpointAddress;
|
||||||
|
device->max_packet_size_out = setting->endpoint[1].wMaxPacketSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (3 != device->interface_number) {
|
||||||
|
perror("No interface!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 == libusb_kernel_driver_active(device->handle, device->interface_number)) {
|
||||||
|
if (0 == libusb_detach_kernel_driver(device->handle, device->interface_number)) {
|
||||||
|
device->detached = true;
|
||||||
|
} else {
|
||||||
|
perror("Not detached!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == libusb_claim_interface(device->handle, device->interface_number)) {
|
||||||
|
device->claimed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->claimed) {
|
||||||
|
uint8_t initial_imu_payload [9] = {
|
||||||
|
0xaa, 0xc5, 0xd1, 0x21,
|
||||||
|
0x42, 0x04, 0x00, 0x19,
|
||||||
|
0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
int size = device->max_packet_size_out;
|
||||||
|
if (sizeof(initial_imu_payload) < size) {
|
||||||
|
size = sizeof(initial_imu_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int transferred = 0;
|
||||||
|
int error = libusb_bulk_transfer(
|
||||||
|
device->handle,
|
||||||
|
device->endpoint_address_out,
|
||||||
|
initial_imu_payload,
|
||||||
|
size,
|
||||||
|
&transferred,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
if ((0 != error) || (transferred != sizeof(initial_imu_payload))) {
|
||||||
|
perror("ERROR\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t SAMPLE_RATE = 1000;
|
||||||
|
|
||||||
|
FusionOffsetInitialise(&(device->offset), SAMPLE_RATE);
|
||||||
|
FusionAhrsInitialise(&(device->ahrs));
|
||||||
|
|
||||||
|
const FusionAhrsSettings settings = {
|
||||||
|
.gain = 0.5f,
|
||||||
|
.accelerationRejection = 10.0f,
|
||||||
|
.magneticRejection = 20.0f,
|
||||||
|
.rejectionTimeout = 5 * SAMPLE_RATE, /* 5 seconds */
|
||||||
|
};
|
||||||
|
|
||||||
|
FusionAhrsSetSettings(&(device->ahrs), &settings);
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void device3_callback(device3_type* device,
|
||||||
|
uint64_t timestamp,
|
||||||
|
device3_event_type event,
|
||||||
|
const FusionAhrs* ahrs) {
|
||||||
|
if (!device->callback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->callback(timestamp, event, ahrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t pack24bit_signed(const uint8_t* data) {
|
||||||
|
uint32_t unsigned_value = (data[0]) | (data[1] << 8) | (data[2] << 16);
|
||||||
|
if ((data[2] & 0x80) != 0) unsigned_value |= (0xFF << 24);
|
||||||
|
return (int32_t) unsigned_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// based on 24bit signed int w/ FSR = +/-2000 dps, datasheet option
|
||||||
|
#define GYRO_SCALAR (1.0f / 8388608.0f * 2000.0f)
|
||||||
|
|
||||||
|
// based on 24bit signed int w/ FSR = +/-16 g, datasheet option
|
||||||
|
#define ACCEL_SCALAR (1.0f / 8388608.0f * 16.0f)
|
||||||
|
|
||||||
|
// based on 16bit signed int w/ FSR = +/-16 gauss, datasheet option
|
||||||
|
#define MAGNO_SCALAR (1.0f / 8388608.0f * 16.0f)
|
||||||
|
|
||||||
|
int device3_read(device3_type* device, int timeout) {
|
||||||
|
if (!device) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!device->claimed) {
|
||||||
|
perror("Not claimed!\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->max_packet_size_in != sizeof(device3_packet_type)) {
|
||||||
|
perror("Not proper size!\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
device3_packet_type packet;
|
||||||
|
memset(&packet, 0, sizeof(device3_packet_type));
|
||||||
|
|
||||||
|
int transferred = 0;
|
||||||
|
int error = libusb_bulk_transfer(
|
||||||
|
device->handle,
|
||||||
|
device->endpoint_address_in,
|
||||||
|
(uint8_t*) &packet,
|
||||||
|
device->max_packet_size_in,
|
||||||
|
&transferred,
|
||||||
|
timeout
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error == LIBUSB_ERROR_TIMEOUT) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((0 != error) || (device->max_packet_size_in != transferred)) {
|
||||||
|
perror("Not expected issue!\n");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t timestamp = packet.timestamp;
|
||||||
|
|
||||||
|
if ((packet.signature[0] == 0xaa) && (packet.signature[1] == 0x53)) {
|
||||||
|
device3_callback(device, timestamp, DEVICE3_EVENT_INIT, &(device->ahrs));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((packet.signature[0] != 0x01) || (packet.signature[1] != 0x02)) {
|
||||||
|
perror("Not matching signature!\n");
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t delta = timestamp - device->last_timestamp;
|
||||||
|
const float deltaTime = (float) ((double) delta / 1e9);
|
||||||
|
|
||||||
|
device->last_timestamp = timestamp;
|
||||||
|
|
||||||
|
int32_t vel_x = pack24bit_signed(packet.angular_velocity_x);
|
||||||
|
int32_t vel_y = pack24bit_signed(packet.angular_velocity_y);
|
||||||
|
int32_t vel_z = pack24bit_signed(packet.angular_velocity_z);
|
||||||
|
|
||||||
|
FusionVector gyroscope;
|
||||||
|
gyroscope.axis.x = vel_x * GYRO_SCALAR;
|
||||||
|
gyroscope.axis.y = vel_y * GYRO_SCALAR;
|
||||||
|
gyroscope.axis.z = vel_z * GYRO_SCALAR;
|
||||||
|
|
||||||
|
int32_t accel_x = pack24bit_signed(packet.acceleration_x);
|
||||||
|
int32_t accel_y = pack24bit_signed(packet.acceleration_y);
|
||||||
|
int32_t accel_z = pack24bit_signed(packet.acceleration_z);
|
||||||
|
|
||||||
|
FusionVector accelerometer;
|
||||||
|
accelerometer.axis.x = accel_x * ACCEL_SCALAR;
|
||||||
|
accelerometer.axis.y = accel_y * ACCEL_SCALAR;
|
||||||
|
accelerometer.axis.z = accel_z * ACCEL_SCALAR;
|
||||||
|
|
||||||
|
int16_t magnet_x = packet.magnetic_x;
|
||||||
|
int16_t magnet_y = packet.magnetic_y;
|
||||||
|
int16_t magnet_z = packet.magnetic_z;
|
||||||
|
|
||||||
|
FusionVector magnetometer;
|
||||||
|
magnetometer.axis.x = magnet_x * MAGNO_SCALAR;
|
||||||
|
magnetometer.axis.y = magnet_y * MAGNO_SCALAR;
|
||||||
|
magnetometer.axis.z = magnet_z * MAGNO_SCALAR;
|
||||||
|
|
||||||
|
const FusionMatrix gyroscopeMisalignment = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
|
||||||
|
const FusionVector gyroscopeSensitivity = { 1.0f, 1.0f, 1.0f };
|
||||||
|
const FusionVector gyroscopeOffset = { 0.0f, 0.0f, 0.0f };
|
||||||
|
const FusionMatrix accelerometerMisalignment = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
|
||||||
|
const FusionVector accelerometerSensitivity = { 1.0f, 1.0f, 1.0f };
|
||||||
|
const FusionVector accelerometerOffset = { 0.0f, 0.0f, 0.0f };
|
||||||
|
const FusionMatrix magnetometerMisalignment = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
|
||||||
|
const FusionVector magnetometerSensitivity = { 1.0f, 1.0f, 1.0f };
|
||||||
|
const FusionVector magnetometerOffset = { 0.0f, 0.0f, 0.0f };
|
||||||
|
const FusionMatrix softIronMatrix = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
|
||||||
|
const FusionVector hardIronOffset = { 0.0f, 0.0f, 0.0f };
|
||||||
|
|
||||||
|
gyroscope = FusionCalibrationInertial(
|
||||||
|
gyroscope,
|
||||||
|
gyroscopeMisalignment,
|
||||||
|
gyroscopeSensitivity,
|
||||||
|
gyroscopeOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
accelerometer = FusionCalibrationInertial(
|
||||||
|
accelerometer,
|
||||||
|
accelerometerMisalignment,
|
||||||
|
accelerometerSensitivity,
|
||||||
|
accelerometerOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
magnetometer = FusionCalibrationInertial(
|
||||||
|
magnetometer,
|
||||||
|
magnetometerMisalignment,
|
||||||
|
magnetometerSensitivity,
|
||||||
|
magnetometerOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
gyroscope = FusionOffsetUpdate(&(device->offset), gyroscope);
|
||||||
|
|
||||||
|
FusionAhrsUpdate(&(device->ahrs), gyroscope, accelerometer, magnetometer, deltaTime);
|
||||||
|
|
||||||
|
device3_callback(device, timestamp, DEVICE3_EVENT_UPDATE, &(device->ahrs));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void device3_close(device3_type* device) {
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((device->claimed) &&
|
||||||
|
(0 == libusb_release_interface(device->handle, device->interface_number))) {
|
||||||
|
device->claimed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((device->detached) &&
|
||||||
|
(0 == libusb_attach_kernel_driver(device->handle, device->interface_number))) {
|
||||||
|
device->detached = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->handle) {
|
||||||
|
libusb_close(device->handle);
|
||||||
|
device->handle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->context) {
|
||||||
|
libusb_exit(device->context);
|
||||||
|
device->context = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(device);
|
||||||
|
}
|
98
src/device3.h
Normal file
98
src/device3.h
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#pragma once
|
||||||
|
//
|
||||||
|
// Created by thejackimonster on 30.03.23.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2023 thejackimonster. All rights reserved.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <Fusion/Fusion.h>
|
||||||
|
#include <libusb-1.0/libusb.h>
|
||||||
|
|
||||||
|
struct __attribute__((__packed__)) device3_packet_t {
|
||||||
|
uint8_t signature [2];
|
||||||
|
uint8_t _padding0 [2];
|
||||||
|
uint64_t timestamp;
|
||||||
|
uint8_t _padding1 [6];
|
||||||
|
uint8_t angular_velocity_x [3];
|
||||||
|
uint8_t angular_velocity_y [3];
|
||||||
|
uint8_t angular_velocity_z [3];
|
||||||
|
uint8_t _padding2 [6];
|
||||||
|
uint8_t acceleration_x [3];
|
||||||
|
uint8_t acceleration_y [3];
|
||||||
|
uint8_t acceleration_z [3];
|
||||||
|
uint8_t _padding3 [6];
|
||||||
|
int16_t magnetic_x;
|
||||||
|
int16_t magnetic_y;
|
||||||
|
int16_t magnetic_z;
|
||||||
|
uint32_t checksum;
|
||||||
|
uint8_t _padding4 [6];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum device3_event_t {
|
||||||
|
DEVICE3_EVENT_UNKNOWN = 0,
|
||||||
|
DEVICE3_EVENT_INIT = 1,
|
||||||
|
DEVICE3_EVENT_UPDATE = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct device3_packet_t device3_packet_type;
|
||||||
|
typedef enum device3_event_t device3_event_type;
|
||||||
|
typedef void (*device3_event_callback)(
|
||||||
|
uint64_t timestamp,
|
||||||
|
device3_event_type event,
|
||||||
|
const FusionAhrs* ahrs
|
||||||
|
);
|
||||||
|
|
||||||
|
struct device3_t {
|
||||||
|
uint16_t vendor_id;
|
||||||
|
uint16_t product_id;
|
||||||
|
|
||||||
|
libusb_context* context;
|
||||||
|
libusb_device_handle* handle;
|
||||||
|
|
||||||
|
uint8_t interface_number;
|
||||||
|
|
||||||
|
uint8_t endpoint_address_in;
|
||||||
|
uint8_t max_packet_size_in;
|
||||||
|
|
||||||
|
uint8_t endpoint_address_out;
|
||||||
|
uint8_t max_packet_size_out;
|
||||||
|
|
||||||
|
bool detached;
|
||||||
|
bool claimed;
|
||||||
|
|
||||||
|
uint64_t last_timestamp;
|
||||||
|
|
||||||
|
FusionOffset offset;
|
||||||
|
FusionAhrs ahrs;
|
||||||
|
|
||||||
|
device3_event_callback callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct device3_t device3_type;
|
||||||
|
|
||||||
|
device3_type* device3_open(device3_event_callback callback);
|
||||||
|
|
||||||
|
int device3_read(device3_type* device, int timeout);
|
||||||
|
|
||||||
|
void device3_close(device3_type* device);
|
346
src/device4.c
Normal file
346
src/device4.c
Normal file
|
@ -0,0 +1,346 @@
|
||||||
|
//
|
||||||
|
// Created by thejackimonster on 29.03.23.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2023 thejackimonster. All rights reserved.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "device4.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
device4_type* device4_open(device4_event_callback callback) {
|
||||||
|
device4_type* device = (device4_type*) malloc(sizeof(device4_type));
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
perror("Not allocated!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(device, 0, sizeof(device4_type));
|
||||||
|
device->vendor_id = 0x3318;
|
||||||
|
device->product_id = 0x0424;
|
||||||
|
device->callback = callback;
|
||||||
|
|
||||||
|
if (0 != libusb_init(&(device->context))) {
|
||||||
|
perror("No context!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->handle = libusb_open_device_with_vid_pid(
|
||||||
|
device->context,
|
||||||
|
device->vendor_id,
|
||||||
|
device->product_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!device->handle) {
|
||||||
|
perror("No handle!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_device* dev = libusb_get_device(device->handle);
|
||||||
|
if (!dev) {
|
||||||
|
perror("No dev!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct libusb_device_descriptor desc;
|
||||||
|
if (0 != libusb_get_device_descriptor(dev, &desc)) {
|
||||||
|
perror("No desc!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct libusb_config_descriptor* config;
|
||||||
|
for (uint8_t i = 0; i < desc.bNumConfigurations; i++) {
|
||||||
|
if (0 != libusb_get_config_descriptor(dev, i, &config)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct libusb_interface* interface;
|
||||||
|
for (uint8_t j = 0; j < config->bNumInterfaces; j++) {
|
||||||
|
interface = &(config->interface[j]);
|
||||||
|
|
||||||
|
const struct libusb_interface_descriptor* setting;
|
||||||
|
for (int k = 0; k < interface->num_altsetting; k++) {
|
||||||
|
setting = &(interface->altsetting[k]);
|
||||||
|
|
||||||
|
if (LIBUSB_CLASS_HID != setting->bInterfaceClass) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (4 != setting->bInterfaceNumber) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->interface_number = setting->bInterfaceNumber;
|
||||||
|
|
||||||
|
if (2 != setting->bNumEndpoints) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->endpoint_address_in = setting->endpoint[0].bEndpointAddress;
|
||||||
|
device->max_packet_size_in = setting->endpoint[0].wMaxPacketSize;
|
||||||
|
|
||||||
|
device->endpoint_address_out = setting->endpoint[1].bEndpointAddress;
|
||||||
|
device->max_packet_size_out = setting->endpoint[1].wMaxPacketSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (4 != device->interface_number) {
|
||||||
|
perror("No interface!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 == libusb_kernel_driver_active(device->handle, device->interface_number)) {
|
||||||
|
if (0 == libusb_detach_kernel_driver(device->handle, device->interface_number)) {
|
||||||
|
device->detached = true;
|
||||||
|
} else {
|
||||||
|
perror("Not detached!\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == libusb_claim_interface(device->handle, device->interface_number)) {
|
||||||
|
device->claimed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->claimed) {
|
||||||
|
uint8_t initial_brightness_payload [16] = {
|
||||||
|
0xfd, 0x1e, 0xb9, 0xf0,
|
||||||
|
0x68, 0x11, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x03
|
||||||
|
};
|
||||||
|
|
||||||
|
int size = device->max_packet_size_out;
|
||||||
|
if (sizeof(initial_brightness_payload) < size) {
|
||||||
|
size = sizeof(initial_brightness_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int transferred = 0;
|
||||||
|
int error = libusb_bulk_transfer(
|
||||||
|
device->handle,
|
||||||
|
device->endpoint_address_out,
|
||||||
|
initial_brightness_payload,
|
||||||
|
size,
|
||||||
|
&transferred,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
if ((0 != error) || (transferred != sizeof(initial_brightness_payload))) {
|
||||||
|
perror("ERROR\n");
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void device4_callback(device4_type* device,
|
||||||
|
uint32_t timestamp,
|
||||||
|
device4_event_type event,
|
||||||
|
uint8_t brightness,
|
||||||
|
const char* msg) {
|
||||||
|
if (!device->callback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->callback(timestamp, event, brightness, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int device4_read(device4_type* device, int timeout) {
|
||||||
|
if (!device) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!device->claimed) {
|
||||||
|
perror("Not claimed!\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->max_packet_size_in != sizeof(device4_packet_type)) {
|
||||||
|
perror("Not proper size!\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
device4_packet_type packet;
|
||||||
|
memset(&packet, 0, sizeof(device4_packet_type));
|
||||||
|
|
||||||
|
int transferred = 0;
|
||||||
|
int error = libusb_bulk_transfer(
|
||||||
|
device->handle,
|
||||||
|
device->endpoint_address_in,
|
||||||
|
(uint8_t*) &packet,
|
||||||
|
device->max_packet_size_in,
|
||||||
|
&transferred,
|
||||||
|
timeout
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error == LIBUSB_ERROR_TIMEOUT) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((0 != error) || (device->max_packet_size_in != transferred)) {
|
||||||
|
perror("Not expected issue!\n");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t timestamp = packet.timestamp;
|
||||||
|
const size_t data_len = (size_t) &(packet.data) - (size_t) &(packet.length);
|
||||||
|
|
||||||
|
switch (packet.action) {
|
||||||
|
case DEVICE4_ACTION_PASSIVE_POLL_START: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DEVICE4_ACTION_BRIGHTNESS_COMMAND: {
|
||||||
|
const uint8_t brightness = packet.data[1];
|
||||||
|
|
||||||
|
device->brightness = brightness;
|
||||||
|
|
||||||
|
device4_callback(
|
||||||
|
device,
|
||||||
|
timestamp,
|
||||||
|
DEVICE4_EVENT_BRIGHTNESS_SET,
|
||||||
|
device->brightness,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DEVICE4_ACTION_MANUAL_POLL_CLICK: {
|
||||||
|
const uint8_t button = packet.data[0];
|
||||||
|
const uint8_t brightness = packet.data[8];
|
||||||
|
|
||||||
|
switch (button) {
|
||||||
|
case DEVICE4_BUTTON_DISPLAY_TOGGLE:
|
||||||
|
device->active = !device->active;
|
||||||
|
|
||||||
|
if (device->active) {
|
||||||
|
device4_callback(
|
||||||
|
device,
|
||||||
|
timestamp,
|
||||||
|
DEVICE4_EVENT_SCREEN_ON,
|
||||||
|
device->brightness,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
device4_callback(
|
||||||
|
device,
|
||||||
|
timestamp,
|
||||||
|
DEVICE4_EVENT_SCREEN_OFF,
|
||||||
|
device->brightness,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DEVICE4_BUTTON_BRIGHTNESS_UP:
|
||||||
|
device->brightness = brightness;
|
||||||
|
|
||||||
|
device4_callback(
|
||||||
|
device,
|
||||||
|
timestamp,
|
||||||
|
DEVICE4_EVENT_BRIGHTNESS_UP,
|
||||||
|
device->brightness,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case DEVICE4_BUTTON_BRIGHTNESS_DOWN:
|
||||||
|
device->brightness = brightness;
|
||||||
|
|
||||||
|
device4_callback(
|
||||||
|
device,
|
||||||
|
timestamp,
|
||||||
|
DEVICE4_EVENT_BRIGHTNESS_DOWN,
|
||||||
|
device->brightness,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DEVICE4_ACTION_ACTIVE_POLL: {
|
||||||
|
const char* text = packet.text;
|
||||||
|
const size_t text_len = strlen(text);
|
||||||
|
|
||||||
|
device->active = true;
|
||||||
|
|
||||||
|
if (data_len + text_len != packet.length) {
|
||||||
|
perror("Not matching length!\n");
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
device4_callback(
|
||||||
|
device,
|
||||||
|
timestamp,
|
||||||
|
DEVICE4_EVENT_MESSAGE,
|
||||||
|
device->brightness,
|
||||||
|
text
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DEVICE4_ACTION_PASSIVE_POLL_END: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
device4_callback(
|
||||||
|
device,
|
||||||
|
timestamp,
|
||||||
|
DEVICE4_EVENT_UNKNOWN,
|
||||||
|
device->brightness,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void device4_close(device4_type* device) {
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((device->claimed) &&
|
||||||
|
(0 == libusb_release_interface(device->handle, device->interface_number))) {
|
||||||
|
device->claimed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((device->detached) &&
|
||||||
|
(0 == libusb_attach_kernel_driver(device->handle, device->interface_number))) {
|
||||||
|
device->detached = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->handle) {
|
||||||
|
libusb_close(device->handle);
|
||||||
|
device->handle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->context) {
|
||||||
|
libusb_exit(device->context);
|
||||||
|
device->context = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(device);
|
||||||
|
}
|
104
src/device4.h
Normal file
104
src/device4.h
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
#pragma once
|
||||||
|
//
|
||||||
|
// Created by thejackimonster on 29.03.23.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2023 thejackimonster. All rights reserved.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <libusb-1.0/libusb.h>
|
||||||
|
|
||||||
|
#define DEVICE4_ACTION_PASSIVE_POLL_START 0b00010
|
||||||
|
#define DEVICE4_ACTION_BRIGHTNESS_COMMAND 0b00011
|
||||||
|
#define DEVICE4_ACTION_MANUAL_POLL_CLICK 0b00101
|
||||||
|
#define DEVICE4_ACTION_ACTIVE_POLL 0b01001
|
||||||
|
#define DEVICE4_ACTION_PASSIVE_POLL_END 0b10010
|
||||||
|
|
||||||
|
#define DEVICE4_BUTTON_DISPLAY_TOGGLE 0x1
|
||||||
|
#define DEVICE4_BUTTON_BRIGHTNESS_UP 0x2
|
||||||
|
#define DEVICE4_BUTTON_BRIGHTNESS_DOWN 0x3
|
||||||
|
|
||||||
|
struct __attribute__((__packed__)) device4_packet_t {
|
||||||
|
uint8_t signature;
|
||||||
|
uint32_t checksum;
|
||||||
|
uint16_t length;
|
||||||
|
uint8_t _padding0 [4];
|
||||||
|
uint32_t timestamp;
|
||||||
|
uint8_t action;
|
||||||
|
uint8_t _padding1 [6];
|
||||||
|
union {
|
||||||
|
char text [42];
|
||||||
|
uint8_t data [42];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
enum device4_event_t {
|
||||||
|
DEVICE4_EVENT_UNKNOWN = 0,
|
||||||
|
DEVICE4_EVENT_SCREEN_ON = 1,
|
||||||
|
DEVICE4_EVENT_SCREEN_OFF = 2,
|
||||||
|
DEVICE4_EVENT_BRIGHTNESS_SET = 3,
|
||||||
|
DEVICE4_EVENT_BRIGHTNESS_UP = 4,
|
||||||
|
DEVICE4_EVENT_BRIGHTNESS_DOWN = 5,
|
||||||
|
DEVICE4_EVENT_MESSAGE = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct device4_packet_t device4_packet_type;
|
||||||
|
typedef enum device4_event_t device4_event_type;
|
||||||
|
typedef void (*device4_event_callback)(
|
||||||
|
uint32_t timestamp,
|
||||||
|
device4_event_type event,
|
||||||
|
uint8_t brightness,
|
||||||
|
const char* msg
|
||||||
|
);
|
||||||
|
|
||||||
|
struct device4_t {
|
||||||
|
uint16_t vendor_id;
|
||||||
|
uint16_t product_id;
|
||||||
|
|
||||||
|
libusb_context* context;
|
||||||
|
libusb_device_handle* handle;
|
||||||
|
|
||||||
|
uint8_t interface_number;
|
||||||
|
|
||||||
|
uint8_t endpoint_address_in;
|
||||||
|
uint8_t max_packet_size_in;
|
||||||
|
|
||||||
|
uint8_t endpoint_address_out;
|
||||||
|
uint8_t max_packet_size_out;
|
||||||
|
|
||||||
|
bool detached;
|
||||||
|
bool claimed;
|
||||||
|
bool active;
|
||||||
|
|
||||||
|
uint8_t brightness;
|
||||||
|
|
||||||
|
device4_event_callback callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct device4_t device4_type;
|
||||||
|
|
||||||
|
device4_type* device4_open(device4_event_callback callback);
|
||||||
|
|
||||||
|
int device4_read(device4_type* device, int timeout);
|
||||||
|
|
||||||
|
void device4_close(device4_type* device);
|
106
src/driver.c
Normal file
106
src/driver.c
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
//
|
||||||
|
// Created by thejackimonster on 29.03.23.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2023 thejackimonster. All rights reserved.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "device3.h"
|
||||||
|
#include "device4.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <Fusion/Fusion.h>
|
||||||
|
|
||||||
|
void test3(uint64_t timestamp,
|
||||||
|
device3_event_type event,
|
||||||
|
const FusionAhrs* ahrs) {
|
||||||
|
if (event != DEVICE3_EVENT_UPDATE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FusionQuaternion q = FusionAhrsGetQuaternion(ahrs);
|
||||||
|
FusionEuler e = FusionQuaternionToEuler(q);
|
||||||
|
|
||||||
|
printf("Pitch: %f; Roll: %f; Yaw: %f\n", e.angle.pitch, e.angle.roll, e.angle.yaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test4(uint32_t timestamp,
|
||||||
|
device4_event_type event,
|
||||||
|
uint8_t brightness,
|
||||||
|
const char* msg) {
|
||||||
|
switch (event) {
|
||||||
|
case DEVICE4_EVENT_MESSAGE:
|
||||||
|
printf("Message: `%s`\n", msg);
|
||||||
|
break;
|
||||||
|
case DEVICE4_EVENT_BRIGHTNESS_SET:
|
||||||
|
printf("Set Brightness: %u\n", brightness);
|
||||||
|
break;
|
||||||
|
case DEVICE4_EVENT_BRIGHTNESS_UP:
|
||||||
|
printf("Increase Brightness: %u\n", brightness);
|
||||||
|
break;
|
||||||
|
case DEVICE4_EVENT_BRIGHTNESS_DOWN:
|
||||||
|
printf("Decrease Brightness: %u\n", brightness);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char** argv) {
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if (pid == -1) {
|
||||||
|
perror("Could not fork!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
device3_type* dev3 = device3_open(test3);
|
||||||
|
|
||||||
|
while (dev3) {
|
||||||
|
if (device3_read(dev3, 0) < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device3_close(dev3);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
device4_type* dev4 = device4_open(test4);
|
||||||
|
|
||||||
|
while (dev4) {
|
||||||
|
if (device4_read(dev4, 0) < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device4_close(dev4);
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
if (pid != waitpid(pid, &status, 0)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue