Initial commit

This commit is contained in:
Aodhan Collins
2026-02-22 12:51:32 +00:00
commit 0267543622
21 changed files with 2888 additions and 0 deletions

38
libazeron/CMakeLists.txt Normal file
View File

@@ -0,0 +1,38 @@
# libazeron - Core Azeron device library
set(LIBAZERON_SOURCES
azeron.c
protocol.c
device.c
utils.c
)
set(LIBAZERON_HEADERS
azeron.h
internal.h
)
# Create shared library
add_library(azeron SHARED ${LIBAZERON_SOURCES})
# Link libraries
target_link_libraries(azeron ${LIBUSB_LIBRARIES} ${JSON_LIBRARIES})
# Set properties
set_target_properties(azeron PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1
PUBLIC_HEADER "${LIBAZERON_HEADERS}"
)
# Install library
install(TARGETS azeron
LIBRARY DESTINATION lib
PUBLIC_HEADER DESTINATION include/azeron
)
# Install pkg-config file
configure_file(azeron.pc.in azeron.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/azeron.pc
DESTINATION lib/pkgconfig
)

420
libazeron/azeron.c Normal file
View File

@@ -0,0 +1,420 @@
/*
* Azeron Linux Configuration Library
* Copyright (C) 2024 Azeron Linux Project
*
* SPDX-License-Identifier: MIT
*/
#include "azeron.h"
#include "internal.h"
#include <libusb-1.0/libusb.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
static bool g_initialized = false;
static libusb_context *g_context = NULL;
/* Initialize the library */
int azeron_init(void)
{
int ret;
if (g_initialized) {
return AZERON_SUCCESS;
}
ret = libusb_init(&g_context);
if (ret < 0) {
AZERON_ERROR("Failed to initialize libusb: %s", azeron_usb_error_string(ret));
return AZERON_ERROR_INIT;
}
#ifdef AZERON_DEBUG
libusb_set_debug(g_context, LIBUSB_LOG_LEVEL_INFO);
#endif
g_initialized = true;
AZERON_LOG("Library initialized");
return AZERON_SUCCESS;
}
/* Cleanup the library */
void azeron_exit(void)
{
if (!g_initialized) {
return;
}
if (g_context) {
libusb_exit(g_context);
g_context = NULL;
}
g_initialized = false;
AZERON_LOG("Library exited");
}
/* Convert error code to string */
const char *azeron_error_string(int error)
{
switch (error) {
case AZERON_SUCCESS:
return "Success";
case AZERON_ERROR_INIT:
return "Initialization error";
case AZERON_ERROR_NOT_FOUND:
return "Device not found";
case AZERON_ERROR_ACCESS:
return "Access denied";
case AZERON_ERROR_IO:
return "I/O error";
case AZERON_ERROR_PROTOCOL:
return "Protocol error";
case AZERON_ERROR_INVALID_PARAM:
return "Invalid parameter";
case AZERON_ERROR_NO_MEM:
return "Out of memory";
case AZERON_ERROR_UNSUPPORTED:
return "Unsupported operation";
default:
return "Unknown error";
}
}
/* Convert libusb error to azeron error */
int azeron_libusb_to_azeron_error(int libusb_error)
{
switch (libusb_error) {
case LIBUSB_SUCCESS:
return AZERON_SUCCESS;
case LIBUSB_ERROR_IO:
return AZERON_ERROR_IO;
case LIBUSB_ERROR_INVALID_PARAM:
return AZERON_ERROR_INVALID_PARAM;
case LIBUSB_ERROR_ACCESS:
return AZERON_ERROR_ACCESS;
case LIBUSB_ERROR_NO_DEVICE:
return AZERON_ERROR_NOT_FOUND;
case LIBUSB_ERROR_NOT_FOUND:
return AZERON_ERROR_NOT_FOUND;
case LIBUSB_ERROR_BUSY:
return AZERON_ERROR_ACCESS;
case LIBUSB_ERROR_TIMEOUT:
return AZERON_ERROR_IO;
case LIBUSB_ERROR_OVERFLOW:
return AZERON_ERROR_IO;
case LIBUSB_ERROR_PIPE:
return AZERON_ERROR_PROTOCOL;
case LIBUSB_ERROR_INTERRUPTED:
return AZERON_ERROR_IO;
case LIBUSB_ERROR_NO_MEM:
return AZERON_ERROR_NO_MEM;
case LIBUSB_ERROR_NOT_SUPPORTED:
return AZERON_ERROR_UNSUPPORTED;
default:
return AZERON_ERROR_IO;
}
}
/* Get USB error string */
const char *azeron_usb_error_string(int error)
{
return libusb_error_name(error);
}
/* Extract device info from libusb device */
void azeron_device_info_from_libusb(struct azeron_device *device, libusb_device *libusb_dev)
{
struct libusb_device_descriptor desc;
int ret;
ret = libusb_get_device_descriptor(libusb_dev, &desc);
if (ret < 0) {
AZERON_ERROR("Failed to get device descriptor: %s", azeron_usb_error_string(ret));
return;
}
device->info.vendor_id = desc.idVendor;
device->info.product_id = desc.idProduct;
device->info.firmware_version = desc.bcdDevice;
/* Get string descriptors if possible */
if (device->handle) {
if (desc.iSerialNumber > 0) {
libusb_get_string_descriptor_ascii(device->handle, desc.iSerialNumber,
(unsigned char *)device->info.serial_number,
sizeof(device->info.serial_number));
}
if (desc.iManufacturer > 0) {
libusb_get_string_descriptor_ascii(device->handle, desc.iManufacturer,
(unsigned char *)device->info.manufacturer,
sizeof(device->info.manufacturer));
}
if (desc.iProduct > 0) {
libusb_get_string_descriptor_ascii(device->handle, desc.iProduct,
(unsigned char *)device->info.product,
sizeof(device->info.product));
}
}
}
/* List all Azeron devices */
int azeron_device_list(struct azeron_device_info **devices, size_t *count)
{
libusb_device **dev_list;
ssize_t num_devs;
size_t azeron_count = 0;
struct azeron_device_info *azeron_devices = NULL;
int i;
if (!devices || !count) {
return AZERON_ERROR_INVALID_PARAM;
}
if (!g_initialized) {
return AZERON_ERROR_INIT;
}
num_devs = libusb_get_device_list(g_context, &dev_list);
if (num_devs < 0) {
AZERON_ERROR("Failed to get device list: %s", azeron_usb_error_string(num_devs));
return azeron_libusb_to_azeron_error(num_devs);
}
/* First pass: count Azeron devices */
for (i = 0; i < num_devs; i++) {
struct libusb_device_descriptor desc;
int ret = libusb_get_device_descriptor(dev_list[i], &desc);
if (ret >= 0 && desc.idVendor == AZERON_VENDOR_ID && desc.idProduct == AZERON_PRODUCT_ID) {
azeron_count++;
}
}
if (azeron_count == 0) {
libusb_free_device_list(dev_list, 1);
*devices = NULL;
*count = 0;
return AZERON_SUCCESS;
}
/* Allocate memory for device info */
azeron_devices = calloc(azeron_count, sizeof(struct azeron_device_info));
if (!azeron_devices) {
libusb_free_device_list(dev_list, 1);
return AZERON_ERROR_NO_MEM;
}
/* Second pass: fill device info */
azeron_count = 0;
for (i = 0; i < num_devs; i++) {
struct libusb_device_descriptor desc;
int ret = libusb_get_device_descriptor(dev_list[i], &desc);
if (ret >= 0 && desc.idVendor == AZERON_VENDOR_ID && desc.idProduct == AZERON_PRODUCT_ID) {
struct azeron_device tmp_device = {0};
int open_ret;
tmp_device.context = g_context;
open_ret = libusb_open(dev_list[i], &tmp_device.handle);
if (open_ret >= 0) {
azeron_device_info_from_libusb(&tmp_device, dev_list[i]);
libusb_close(tmp_device.handle);
} else {
/* Still fill basic info even if we can't open */
tmp_device.info.vendor_id = desc.idVendor;
tmp_device.info.product_id = desc.idProduct;
tmp_device.info.firmware_version = desc.bcdDevice;
snprintf(tmp_device.info.product, sizeof(tmp_device.info.product), "Azeron Keypad");
}
memcpy(&azeron_devices[azeron_count], &tmp_device.info, sizeof(struct azeron_device_info));
azeron_count++;
}
}
libusb_free_device_list(dev_list, 1);
*devices = azeron_devices;
*count = azeron_count;
AZERON_LOG("Found %zu Azeron device(s)", azeron_count);
return AZERON_SUCCESS;
}
/* Free device list */
void azeron_device_list_free(struct azeron_device_info *devices, size_t count)
{
(void)count; /* Not used currently */
free(devices);
}
/* Open device by VID/PID */
int azeron_device_open(struct azeron_device **device, uint16_t vendor_id, uint16_t product_id)
{
struct azeron_device *dev;
int ret;
if (!device) {
return AZERON_ERROR_INVALID_PARAM;
}
if (!g_initialized) {
return AZERON_ERROR_INIT;
}
dev = calloc(1, sizeof(struct azeron_device));
if (!dev) {
return AZERON_ERROR_NO_MEM;
}
dev->context = g_context;
pthread_mutex_init(&dev->mutex, NULL);
ret = libusb_open_device_with_vid_pid(g_context, vendor_id, product_id);
if (!ret) {
AZERON_ERROR("Failed to open device %04x:%04x", vendor_id, product_id);
free(dev);
return AZERON_ERROR_NOT_FOUND;
}
dev->handle = ret;
azeron_device_info_from_libusb(dev, libusb_get_device(dev->handle));
*device = dev;
AZERON_LOG("Device opened: %s (%04x:%04x)", dev->info.product, vendor_id, product_id);
return AZERON_SUCCESS;
}
/* Open device by index */
int azeron_device_open_index(struct azeron_device **device, size_t index)
{
libusb_device **dev_list;
ssize_t num_devs;
int i;
size_t azeron_idx = 0;
int ret;
if (!device) {
return AZERON_ERROR_INVALID_PARAM;
}
if (!g_initialized) {
return AZERON_ERROR_INIT;
}
num_devs = libusb_get_device_list(g_context, &dev_list);
if (num_devs < 0) {
return azeron_libusb_to_azeron_error(num_devs);
}
for (i = 0; i < num_devs; i++) {
struct libusb_device_descriptor desc;
ret = libusb_get_device_descriptor(dev_list[i], &desc);
if (ret >= 0 && desc.idVendor == AZERON_VENDOR_ID && desc.idProduct == AZERON_PRODUCT_ID) {
if (azeron_idx == index) {
struct azeron_device *dev = calloc(1, sizeof(struct azeron_device));
if (!dev) {
libusb_free_device_list(dev_list, 1);
return AZERON_ERROR_NO_MEM;
}
dev->context = g_context;
pthread_mutex_init(&dev->mutex, NULL);
ret = libusb_open(dev_list[i], &dev->handle);
if (ret < 0) {
AZERON_ERROR("Failed to open device at index %zu: %s", index, azeron_usb_error_string(ret));
free(dev);
libusb_free_device_list(dev_list, 1);
return azeron_libusb_to_azeron_error(ret);
}
azeron_device_info_from_libusb(dev, dev_list[i]);
libusb_free_device_list(dev_list, 1);
*device = dev;
AZERON_LOG("Device opened at index %zu: %s", index, dev->info.product);
return AZERON_SUCCESS;
}
azeron_idx++;
}
}
libusb_free_device_list(dev_list, 1);
return AZERON_ERROR_NOT_FOUND;
}
/* Close device */
void azeron_device_close(struct azeron_device *device)
{
if (!device) {
return;
}
if (device->claimed) {
azeron_device_release(device);
}
if (device->handle) {
libusb_close(device->handle);
}
pthread_mutex_destroy(&device->mutex);
free(device);
AZERON_LOG("Device closed");
}
/* Get device information */
int azeron_device_get_info(struct azeron_device *device, struct azeron_device_info *info)
{
if (!device || !info) {
return AZERON_ERROR_INVALID_PARAM;
}
memcpy(info, &device->info, sizeof(struct azeron_device_info));
return AZERON_SUCCESS;
}
/* Button type to string */
const char *azeron_button_type_string(enum azeron_button_type type)
{
switch (type) {
case AZERON_BTN_KEYBOARD:
return "keyboard";
case AZERON_BTN_MOUSE:
return "mouse";
case AZERON_BTN_GAMEPAD:
return "gamepad";
case AZERON_BTN_MACRO:
return "macro";
case AZERON_BTN_LAYER_SWITCH:
return "layer_switch";
default:
return "unknown";
}
}
/* Stick mode to string */
const char *azeron_stick_mode_string(enum azeron_stick_mode mode)
{
switch (mode) {
case AZERON_STICK_ANALOG:
return "analog";
case AZERON_STICK_DIGITAL_4:
return "digital_4";
case AZERON_STICK_DIGITAL_8:
return "digital_8";
case AZERON_STICK_MOUSE:
return "mouse";
default:
return "unknown";
}
}

151
libazeron/azeron.h Normal file
View File

@@ -0,0 +1,151 @@
/*
* Azeron Linux Configuration Library
* Copyright (C) 2024 Azeron Linux Project
*
* SPDX-License-Identifier: MIT
*/
#ifndef AZERON_H
#define AZERON_H
#include <stdint.h>
#include <stdbool.h>
#include <json-c/json.h>
#ifdef __cplusplus
extern "C" {
#endif
#define AZERON_VENDOR_ID 0x16d0
#define AZERON_PRODUCT_ID 0x113c
#define AZERON_MAX_BUTTONS 32
#define AZERON_MAX_PROFILES 3
/* Error codes */
enum azeron_error {
AZERON_SUCCESS = 0,
AZERON_ERROR_INIT = -1,
AZERON_ERROR_NOT_FOUND = -2,
AZERON_ERROR_ACCESS = -3,
AZERON_ERROR_IO = -4,
AZERON_ERROR_PROTOCOL = -5,
AZERON_ERROR_INVALID_PARAM = -6,
AZERON_ERROR_NO_MEM = -7,
AZERON_ERROR_UNSUPPORTED = -8,
};
/* Button types that can be mapped */
enum azeron_button_type {
AZERON_BTN_KEYBOARD = 0,
AZERON_BTN_MOUSE,
AZERON_BTN_GAMEPAD,
AZERON_BTN_MACRO,
AZERON_BTN_LAYER_SWITCH,
};
/* Analog stick modes */
enum azeron_stick_mode {
AZERON_STICK_ANALOG = 0,
AZERON_STICK_DIGITAL_4,
AZERON_STICK_DIGITAL_8,
AZERON_STICK_MOUSE,
};
/* Device information */
struct azeron_device_info {
uint16_t vendor_id;
uint16_t product_id;
char serial_number[64];
char manufacturer[128];
char product[128];
uint8_t firmware_version;
uint8_t num_profiles;
uint8_t active_profile;
};
/* Button mapping */
struct azeron_button_mapping {
uint8_t button_id;
enum azeron_button_type type;
uint16_t key_code; /* Linux input event code */
char *macro; /* For macro type */
uint8_t layer_target; /* For layer switch type */
};
/* Analog stick configuration */
struct azeron_stick_config {
enum azeron_stick_mode mode;
uint8_t deadzone; /* 0-100 */
uint8_t sensitivity; /* 0-100 */
bool invert_x;
bool invert_y;
uint8_t response_curve; /* 0=linear, 1=exponential, etc. */
};
/* Profile configuration */
struct azeron_profile {
uint8_t profile_id;
char name[64];
struct azeron_button_mapping buttons[AZERON_MAX_BUTTONS];
uint8_t num_buttons;
struct azeron_stick_config stick_config;
};
/* Opaque device handle */
struct azeron_device;
/* Library initialization */
int azeron_init(void);
void azeron_exit(void);
const char *azeron_error_string(int error);
/* Device management */
int azeron_device_list(struct azeron_device_info **devices, size_t *count);
void azeron_device_list_free(struct azeron_device_info *devices, size_t count);
int azeron_device_open(struct azeron_device **device, uint16_t vendor_id, uint16_t product_id);
int azeron_device_open_index(struct azeron_device **device, size_t index);
void azeron_device_close(struct azeron_device *device);
int azeron_device_get_info(struct azeron_device *device, struct azeron_device_info *info);
/* Button mapping */
int azeron_device_get_button_mapping(struct azeron_device *device, uint8_t button_id,
struct azeron_button_mapping *mapping);
int azeron_device_set_button_mapping(struct azeron_device *device,
const struct azeron_button_mapping *mapping);
/* Analog stick configuration */
int azeron_device_get_stick_config(struct azeron_device *device,
struct azeron_stick_config *config);
int azeron_device_set_stick_config(struct azeron_device *device,
const struct azeron_stick_config *config);
/* Profile management */
int azeron_device_get_active_profile(struct azeron_device *device, uint8_t *profile_id);
int azeron_device_set_active_profile(struct azeron_device *device, uint8_t profile_id);
int azeron_device_get_profile(struct azeron_device *device, uint8_t profile_id,
struct azeron_profile *profile);
int azeron_device_set_profile(struct azeron_device *device,
const struct azeron_profile *profile);
/* Configuration import/export */
int azeron_device_export_config(struct azeron_device *device, const char *filename);
int azeron_device_import_config(struct azeron_device *device, const char *filename);
int azeron_device_export_config_json(struct azeron_device *device, struct json_object **json);
int azeron_device_import_config_json(struct azeron_device *device, struct json_object *json);
/* Utility functions */
const char *azeron_button_type_string(enum azeron_button_type type);
const char *azeron_stick_mode_string(enum azeron_stick_mode mode);
int azeron_keycode_from_string(const char *key_name);
const char *azeron_keycode_to_string(int keycode);
#ifdef __cplusplus
}
#endif
#endif /* AZERON_H */

11
libazeron/azeron.pc.in Normal file
View File

@@ -0,0 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=${prefix}/lib
includedir=${prefix}/include
Name: azeron
Description: Azeron device configuration library
Version: @PROJECT_VERSION@
Libs: -L${libdir} -lazeron
Cflags: -I${includedir}
Requires: libusb-1.0 json-c

218
libazeron/device.c Normal file
View File

@@ -0,0 +1,218 @@
/*
* Azeron Linux Configuration Library - Device Communication
* Copyright (C) 2024 Azeron Linux Project
*
* SPDX-License-Identifier: MIT
*/
#include "azeron.h"
#include "internal.h"
#include <libusb-1.0/libusb.h>
#include <string.h>
/* Claim device interfaces */
int azeron_device_claim(struct azeron_device *device)
{
int ret;
if (!device || !device->handle) {
return AZERON_ERROR_INVALID_PARAM;
}
if (device->claimed) {
return AZERON_SUCCESS;
}
pthread_mutex_lock(&device->mutex);
/* Claim all interfaces - the device has 5 interfaces (0-4) */
for (int i = 0; i <= 4; i++) {
ret = libusb_claim_interface(device->handle, i);
if (ret < 0) {
AZERON_ERROR("Failed to claim interface %d: %s", i, azeron_usb_error_string(ret));
/* Release already claimed interfaces */
for (int j = 0; j < i; j++) {
libusb_release_interface(device->handle, j);
}
pthread_mutex_unlock(&device->mutex);
return azeron_libusb_to_azeron_error(ret);
}
}
device->claimed = true;
pthread_mutex_unlock(&device->mutex);
AZERON_LOG("Device interfaces claimed");
return AZERON_SUCCESS;
}
/* Release device interfaces */
int azeron_device_release(struct azeron_device *device)
{
int ret;
int overall_ret = AZERON_SUCCESS;
if (!device || !device->handle) {
return AZERON_ERROR_INVALID_PARAM;
}
if (!device->claimed) {
return AZERON_SUCCESS;
}
pthread_mutex_lock(&device->mutex);
/* Release all interfaces */
for (int i = 0; i <= 4; i++) {
ret = libusb_release_interface(device->handle, i);
if (ret < 0) {
AZERON_ERROR("Failed to release interface %d: %s", i, azeron_usb_error_string(ret));
overall_ret = azeron_libusb_to_azeron_error(ret);
}
}
device->claimed = false;
pthread_mutex_unlock(&device->mutex);
AZERON_LOG("Device interfaces released");
return overall_ret;
}
/* Read data from endpoint */
int azeron_device_read(struct azeron_device *device, uint8_t endpoint, uint8_t *data, size_t size, int timeout)
{
int transferred;
int ret;
if (!device || !device->handle || !data) {
return AZERON_ERROR_INVALID_PARAM;
}
if (!device->claimed) {
ret = azeron_device_claim(device);
if (ret != AZERON_SUCCESS) {
return ret;
}
}
pthread_mutex_lock(&device->mutex);
ret = libusb_interrupt_transfer(device->handle, endpoint, data, size, &transferred, timeout);
pthread_mutex_unlock(&device->mutex);
if (ret < 0) {
AZERON_ERROR("Failed to read from endpoint 0x%02x: %s", endpoint, azeron_usb_error_string(ret));
return azeron_libusb_to_azeron_error(ret);
}
AZERON_LOG("Read %d bytes from endpoint 0x%02x", transferred, endpoint);
return transferred;
}
/* Write data to endpoint */
int azeron_device_write(struct azeron_device *device, uint8_t endpoint, const uint8_t *data, size_t size, int timeout)
{
int transferred;
int ret;
if (!device || !device->handle || !data) {
return AZERON_ERROR_INVALID_PARAM;
}
if (!device->claimed) {
ret = azeron_device_claim(device);
if (ret != AZERON_SUCCESS) {
return ret;
}
}
pthread_mutex_lock(&device->mutex);
ret = libusb_interrupt_transfer(device->handle, endpoint, (uint8_t *)data, size, &transferred, timeout);
pthread_mutex_unlock(&device->mutex);
if (ret < 0) {
AZERON_ERROR("Failed to write to endpoint 0x%02x: %s", endpoint, azeron_usb_error_string(ret));
return azeron_libusb_to_azeron_error(ret);
}
AZERON_LOG("Wrote %d bytes to endpoint 0x%02x", transferred, endpoint);
return transferred;
}
/* Perform control transfer */
int azeron_device_control_transfer(struct azeron_device *device, uint8_t request_type, uint8_t request,
uint16_t value, uint16_t index, uint8_t *data, size_t size, int timeout)
{
int ret;
if (!device || !device->handle) {
return AZERON_ERROR_INVALID_PARAM;
}
if (!device->claimed) {
ret = azeron_device_claim(device);
if (ret != AZERON_SUCCESS) {
return ret;
}
}
pthread_mutex_lock(&device->mutex);
ret = libusb_control_transfer(device->handle, request_type, request, value, index, data, size, timeout);
pthread_mutex_unlock(&device->mutex);
if (ret < 0) {
AZERON_ERROR("Control transfer failed: %s", azeron_usb_error_string(ret));
return azeron_libusb_to_azeron_error(ret);
}
AZERON_LOG("Control transfer: %d bytes transferred", ret);
return ret;
}
/* Get button type string */
const char *azeron_button_type_string(enum azeron_button_type type)
{
switch (type) {
case AZERON_BTN_KEYBOARD:
return "keyboard";
case AZERON_BTN_MOUSE:
return "mouse";
case AZERON_BTN_GAMEPAD:
return "gamepad";
case AZERON_BTN_MACRO:
return "macro";
case AZERON_BTN_LAYER_SWITCH:
return "layer_switch";
default:
return "unknown";
}
}
/* Get stick mode string */
const char *azeron_stick_mode_string(enum azeron_stick_mode mode)
{
switch (mode) {
case AZERON_STICK_ANALOG:
return "analog";
case AZERON_STICK_DIGITAL_4:
return "digital_4";
case AZERON_STICK_DIGITAL_8:
return "digital_8";
case AZERON_STICK_MOUSE:
return "mouse";
default:
return "unknown";
}
}

61
libazeron/internal.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* Azeron Linux Configuration Library - Internal Header
* Copyright (C) 2024 Azeron Linux Project
*
* SPDX-License-Identifier: MIT
*/
#ifndef AZERON_INTERNAL_H
#define AZERON_INTERNAL_H
#include "azeron.h"
#include <libusb-1.0/libusb.h>
#include <pthread.h>
#define AZERON_USB_TIMEOUT 1000
#define AZERON_MAX_STRING_LENGTH 256
/* Debug logging */
#ifdef AZERON_DEBUG
#define AZERON_LOG(fmt, ...) fprintf(stderr, "[AZERON] " fmt "\n", ##__VA_ARGS__)
#else
#define AZERON_LOG(fmt, ...) do {} while (0)
#endif
/* Error logging */
#define AZERON_ERROR(fmt, ...) fprintf(stderr, "[AZERON ERROR] " fmt "\n", ##__VA_ARGS__)
/* Device structure */
struct azeron_device {
libusb_device_handle *handle;
libusb_context *context;
struct azeron_device_info info;
pthread_mutex_t mutex;
bool claimed;
};
/* Protocol functions */
int azeron_protocol_init(struct azeron_device *device);
int azeron_protocol_read_config(struct azeron_device *device, uint8_t *data, size_t *size);
int azeron_protocol_write_config(struct azeron_device *device, const uint8_t *data, size_t size);
int azeron_protocol_get_button_mapping(struct azeron_device *device, uint8_t button_id, struct azeron_button_mapping *mapping);
int azeron_protocol_set_button_mapping(struct azeron_device *device, const struct azeron_button_mapping *mapping);
int azeron_protocol_get_stick_config(struct azeron_device *device, struct azeron_stick_config *config);
int azeron_protocol_set_stick_config(struct azeron_device *device, const struct azeron_stick_config *config);
int azeron_protocol_get_profile(struct azeron_device *device, uint8_t profile_id, struct azeron_profile *profile);
int azeron_protocol_set_profile(struct azeron_device *device, const struct azeron_profile *profile);
int azeron_protocol_save_to_device(struct azeron_device *device);
/* Device functions */
int azeron_device_claim(struct azeron_device *device);
int azeron_device_release(struct azeron_device *device);
int azeron_device_read(struct azeron_device *device, uint8_t endpoint, uint8_t *data, size_t size, int timeout);
int azeron_device_write(struct azeron_device *device, uint8_t endpoint, const uint8_t *data, size_t size, int timeout);
int azeron_device_control_transfer(struct azeron_device *device, uint8_t request_type, uint8_t request, uint16_t value, uint16_t index, uint8_t *data, size_t size, int timeout);
/* Utility functions */
const char *azeron_usb_error_string(int error);
int azeron_libusb_to_azeron_error(int libusb_error);
void azeron_device_info_from_libusb(struct azeron_device *device, libusb_device *libusb_dev);
#endif /* AZERON_INTERNAL_H */

108
libazeron/protocol.c Normal file
View File

@@ -0,0 +1,108 @@
/*
* Azeron Linux Configuration Library - Protocol Implementation
* Copyright (C) 2024 Azeron Linux Project
*
* SPDX-License-Identifier: MIT
*/
#include "azeron.h"
#include "internal.h"
#include <string.h>
/* Protocol initialization */
int azeron_protocol_init(struct azeron_device *device)
{
(void)device;
AZERON_LOG("Protocol initialization - not yet implemented");
return AZERON_ERROR_UNSUPPORTED;
}
/* Read configuration from device */
int azeron_protocol_read_config(struct azeron_device *device, uint8_t *data, size_t *size)
{
(void)device;
(void)data;
(void)size;
AZERON_LOG("Read config - not yet implemented");
return AZERON_ERROR_UNSUPPORTED;
}
/* Write configuration to device */
int azeron_protocol_write_config(struct azeron_device *device, const uint8_t *data, size_t size)
{
(void)device;
(void)data;
(void)size;
AZERON_LOG("Write config - not yet implemented");
return AZERON_ERROR_UNSUPPORTED;
}
/* Get button mapping */
int azeron_protocol_get_button_mapping(struct azeron_device *device, uint8_t button_id,
struct azeron_button_mapping *mapping)
{
(void)device;
(void)button_id;
(void)mapping;
AZERON_LOG("Get button mapping - not yet implemented");
return AZERON_ERROR_UNSUPPORTED;
}
/* Set button mapping */
int azeron_protocol_set_button_mapping(struct azeron_device *device,
const struct azeron_button_mapping *mapping)
{
(void)device;
(void)mapping;
AZERON_LOG("Set button mapping - not yet implemented");
return AZERON_ERROR_UNSUPPORTED;
}
/* Get stick configuration */
int azeron_protocol_get_stick_config(struct azeron_device *device,
struct azeron_stick_config *config)
{
(void)device;
(void)config;
AZERON_LOG("Get stick config - not yet implemented");
return AZERON_ERROR_UNSUPPORTED;
}
/* Set stick configuration */
int azeron_protocol_set_stick_config(struct azeron_device *device,
const struct azeron_stick_config *config)
{
(void)device;
(void)config;
AZERON_LOG("Set stick config - not yet implemented");
return AZERON_ERROR_UNSUPPORTED;
}
/* Get profile */
int azeron_protocol_get_profile(struct azeron_device *device, uint8_t profile_id,
struct azeron_profile *profile)
{
(void)device;
(void)profile_id;
(void)profile;
AZERON_LOG("Get profile - not yet implemented");
return AZERON_ERROR_UNSUPPORTED;
}
/* Set profile */
int azeron_protocol_set_profile(struct azeron_device *device,
const struct azeron_profile *profile)
{
(void)device;
(void)profile;
AZERON_LOG("Set profile - not yet implemented");
return AZERON_ERROR_UNSUPPORTED;
}
/* Save configuration to device */
int azeron_protocol_save_to_device(struct azeron_device *device)
{
(void)device;
AZERON_LOG("Save to device - not yet implemented");
return AZERON_ERROR_UNSUPPORTED;
}

229
libazeron/utils.c Normal file
View File

@@ -0,0 +1,229 @@
/*
* Azeron Linux Configuration Library - Utility Functions
* Copyright (C) 2024 Azeron Linux Project
*
* SPDX-License-Identifier: MIT
*/
#include "azeron.h"
#include <linux/input-event-codes.h>
#include <string.h>
/* Key name to keycode mapping table */
static struct {
const char *name;
int keycode;
} keymap[] = {
/* Letters */
{"KEY_A", KEY_A}, {"KEY_B", KEY_B}, {"KEY_C", KEY_C}, {"KEY_D", KEY_D},
{"KEY_E", KEY_E}, {"KEY_F", KEY_F}, {"KEY_G", KEY_G}, {"KEY_H", KEY_H},
{"KEY_I", KEY_I}, {"KEY_J", KEY_J}, {"KEY_K", KEY_K}, {"KEY_L", KEY_L},
{"KEY_M", KEY_M}, {"KEY_N", KEY_N}, {"KEY_O", KEY_O}, {"KEY_P", KEY_P},
{"KEY_Q", KEY_Q}, {"KEY_R", KEY_R}, {"KEY_S", KEY_S}, {"KEY_T", KEY_T},
{"KEY_U", KEY_U}, {"KEY_V", KEY_V}, {"KEY_W", KEY_W}, {"KEY_X", KEY_X},
{"KEY_Y", KEY_Y}, {"KEY_Z", KEY_Z},
/* Numbers */
{"KEY_1", KEY_1}, {"KEY_2", KEY_2}, {"KEY_3", KEY_3}, {"KEY_4", KEY_4},
{"KEY_5", KEY_5}, {"KEY_6", KEY_6}, {"KEY_7", KEY_7}, {"KEY_8", KEY_8},
{"KEY_9", KEY_9}, {"KEY_0", KEY_0},
/* Function keys */
{"KEY_F1", KEY_F1}, {"KEY_F2", KEY_F2}, {"KEY_F3", KEY_F3}, {"KEY_F4", KEY_F4},
{"KEY_F5", KEY_F5}, {"KEY_F6", KEY_F6}, {"KEY_F7", KEY_F7}, {"KEY_F8", KEY_F8},
{"KEY_F9", KEY_F9}, {"KEY_F10", KEY_F10}, {"KEY_F11", KEY_F11}, {"KEY_F12", KEY_F12},
/* Special keys */
{"KEY_ESC", KEY_ESC},
{"KEY_TAB", KEY_TAB},
{"KEY_CAPSLOCK", KEY_CAPSLOCK},
{"KEY_LEFTSHIFT", KEY_LEFTSHIFT}, {"KEY_RIGHTSHIFT", KEY_RIGHTSHIFT},
{"KEY_LEFTCTRL", KEY_LEFTCTRL}, {"KEY_RIGHTCTRL", KEY_RIGHTCTRL},
{"KEY_LEFTALT", KEY_LEFTALT}, {"KEY_RIGHTALT", KEY_RIGHTALT},
{"KEY_SPACE", KEY_SPACE},
{"KEY_ENTER", KEY_ENTER},
{"KEY_BACKSPACE", KEY_BACKSPACE},
{"KEY_DELETE", KEY_DELETE},
{"KEY_INSERT", KEY_INSERT},
{"KEY_HOME", KEY_HOME},
{"KEY_END", KEY_END},
{"KEY_PAGEUP", KEY_PAGEUP},
{"KEY_PAGEDOWN", KEY_PAGEDOWN},
/* Arrow keys */
{"KEY_UP", KEY_UP},
{"KEY_DOWN", KEY_DOWN},
{"KEY_LEFT", KEY_LEFT},
{"KEY_RIGHT", KEY_RIGHT},
/* Numpad */
{"KEY_NUMLOCK", KEY_NUMLOCK},
{"KEY_KP0", KEY_KP0}, {"KEY_KP1", KEY_KP1}, {"KEY_KP2", KEY_KP2}, {"KEY_KP3", KEY_KP3},
{"KEY_KP4", KEY_KP4}, {"KEY_KP5", KEY_KP5}, {"KEY_KP6", KEY_KP6}, {"KEY_KP7", KEY_KP7},
{"KEY_KP8", KEY_KP8}, {"KEY_KP9", KEY_KP9},
{"KEY_KPSLASH", KEY_KPSLASH}, {"KEY_KPASTERISK", KEY_KPASTERISK},
{"KEY_KPMINUS", KEY_KPMINUS}, {"KEY_KPPLUS", KEY_KPPLUS},
{"KEY_KPENTER", KEY_KPENTER}, {"KEY_KPDOT", KEY_KPDOT},
/* Mouse buttons */
{"BTN_LEFT", BTN_LEFT},
{"BTN_RIGHT", BTN_RIGHT},
{"BTN_MIDDLE", BTN_MIDDLE},
{"BTN_SIDE", BTN_SIDE},
{"BTN_EXTRA", BTN_EXTRA},
{NULL, 0}
};
/* Convert key name to keycode */
int azeron_keycode_from_string(const char *key_name)
{
int i;
if (!key_name) {
return -1;
}
/* First try exact match */
for (i = 0; keymap[i].name != NULL; i++) {
if (strcmp(key_name, keymap[i].name) == 0) {
return keymap[i].keycode;
}
}
/* Try without KEY_ prefix */
if (strncmp(key_name, "KEY_", 4) != 0) {
char prefixed[64];
snprintf(prefixed, sizeof(prefixed), "KEY_%s", key_name);
for (i = 0; keymap[i].name != NULL; i++) {
if (strcmp(prefixed, keymap[i].name) == 0) {
return keymap[i].keycode;
}
}
}
return -1;
}
/* Convert keycode to string */
const char *azeron_keycode_to_string(int keycode)
{
int i;
for (i = 0; keymap[i].name != NULL; i++) {
if (keymap[i].keycode == keycode) {
return keymap[i].name;
}
}
return "UNKNOWN";
}
/* Export configuration to JSON */
int azeron_device_export_config_json(struct azeron_device *device, struct json_object **json)
{
struct json_object *root;
struct json_object *profiles_array;
struct azeron_device_info info;
int ret;
int i;
if (!device || !json) {
return AZERON_ERROR_INVALID_PARAM;
}
root = json_object_new_object();
if (!root) {
return AZERON_ERROR_NO_MEM;
}
/* Add device info */
ret = azeron_device_get_info(device, &info);
if (ret == AZERON_SUCCESS) {
json_object_object_add(root, "vendor_id", json_object_new_int(info.vendor_id));
json_object_object_add(root, "product_id", json_object_new_int(info.product_id));
json_object_object_add(root, "serial_number", json_object_new_string(info.serial_number));
json_object_object_add(root, "firmware_version", json_object_new_int(info.firmware_version));
}
/* Create profiles array */
profiles_array = json_object_new_array();
if (!profiles_array) {
json_object_put(root);
return AZERON_ERROR_NO_MEM;
}
/* Add placeholder profiles - actual implementation would read from device */
for (i = 0; i < 3; i++) {
struct json_object *profile = json_object_new_object();
char name[32];
snprintf(name, sizeof(name), "Profile %d", i + 1);
json_object_object_add(profile, "id", json_object_new_int(i));
json_object_object_add(profile, "name", json_object_new_string(name));
json_object_object_add(profile, "active", json_object_new_boolean(i == 0));
json_object_array_add(profiles_array, profile);
}
json_object_object_add(root, "profiles", profiles_array);
*json = root;
return AZERON_SUCCESS;
}
/* Import configuration from JSON */
int azeron_device_import_config_json(struct azeron_device *device, struct json_object *json)
{
(void)device;
(void)json;
AZERON_LOG("Import config from JSON - not yet implemented");
return AZERON_ERROR_UNSUPPORTED;
}
/* Export configuration to file */
int azeron_device_export_config(struct azeron_device *device, const char *filename)
{
struct json_object *json;
int ret;
if (!device || !filename) {
return AZERON_ERROR_INVALID_PARAM;
}
ret = azeron_device_export_config_json(device, &json);
if (ret != AZERON_SUCCESS) {
return ret;
}
ret = json_object_to_file_ext(filename, json, JSON_C_TO_STRING_PRETTY);
json_object_put(json);
if (ret != 0) {
return AZERON_ERROR_IO;
}
return AZERON_SUCCESS;
}
/* Import configuration from file */
int azeron_device_import_config(struct azeron_device *device, const char *filename)
{
struct json_object *json;
int ret;
if (!device || !filename) {
return AZERON_ERROR_INVALID_PARAM;
}
json = json_object_from_file(filename);
if (!json) {
return AZERON_ERROR_IO;
}
ret = azeron_device_import_config_json(device, json);
json_object_put(json);
return ret;
}