191 lines
5.2 KiB
C
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;
|
|
}
|