/* * Azeron Linux Configuration Library - Device Communication * Copyright (C) 2024 Azeron Linux Project * * SPDX-License-Identifier: MIT */ #include "azeron.h" #include "internal.h" #include #include #define AZERON_CONFIG_INTERFACE 4 /* 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); /* * The device has 5 interfaces (0-4). * Interfaces 1, 2, and 3 are handled by the kernel for input (HID). * Interface 4 is the configuration interface we need. */ /* Detach kernel driver if active on Interface 4 */ if (libusb_kernel_driver_active(device->handle, AZERON_CONFIG_INTERFACE) == 1) { ret = libusb_detach_kernel_driver(device->handle, AZERON_CONFIG_INTERFACE); if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) { AZERON_ERROR("Failed to detach kernel driver: %s", azeron_usb_error_string(ret)); pthread_mutex_unlock(&device->mutex); return azeron_libusb_to_azeron_error(ret); } } /* Claim configuration interface */ ret = libusb_claim_interface(device->handle, AZERON_CONFIG_INTERFACE); if (ret < 0) { AZERON_ERROR("Failed to claim interface %d: %s", AZERON_CONFIG_INTERFACE, azeron_usb_error_string(ret)); pthread_mutex_unlock(&device->mutex); return azeron_libusb_to_azeron_error(ret); } device->claimed = true; pthread_mutex_unlock(&device->mutex); AZERON_LOG("Device configuration interface claimed"); return AZERON_SUCCESS; } /* Release device interfaces */ int azeron_device_release(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); ret = libusb_release_interface(device->handle, AZERON_CONFIG_INTERFACE); if (ret < 0) { AZERON_ERROR("Failed to release interface %d: %s", AZERON_CONFIG_INTERFACE, azeron_usb_error_string(ret)); } /* Re-attach kernel driver */ libusb_attach_kernel_driver(device->handle, AZERON_CONFIG_INTERFACE); device->claimed = false; pthread_mutex_unlock(&device->mutex); AZERON_LOG("Device interface released"); return (ret < 0) ? azeron_libusb_to_azeron_error(ret) : AZERON_SUCCESS; } /* 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; }