Files

191 lines
5.2 KiB
C

/*
* 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>
#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;
}