feat: implement cyborg joystick bulk write, global timings, and enhanced CLI mapping feedback
This commit is contained in:
@@ -407,6 +407,16 @@ int azeron_device_set_active_profile(struct azeron_device *device, uint8_t profi
|
||||
return azeron_protocol_set_active_profile(device, profile_id);
|
||||
}
|
||||
|
||||
/* Global settings */
|
||||
int azeron_device_set_global_timings(struct azeron_device *device, uint16_t long_press_delay, uint16_t double_click_delay)
|
||||
{
|
||||
if (!device) {
|
||||
return AZERON_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return azeron_protocol_set_global_timings(device, long_press_delay, double_click_delay);
|
||||
}
|
||||
|
||||
/* Save profile to device EEPROM */
|
||||
int azeron_device_save_profile(struct azeron_device *device, uint8_t profile_id)
|
||||
{
|
||||
|
||||
@@ -89,6 +89,7 @@ struct azeron_stick_config {
|
||||
bool invert_x;
|
||||
bool invert_y;
|
||||
uint8_t response_curve; /* 0=linear, 1=exponential, etc. */
|
||||
uint8_t angle; /* 0-360, but typically fits in 8 bits? wait. */
|
||||
};
|
||||
|
||||
/* Profile configuration */
|
||||
@@ -134,6 +135,9 @@ int azeron_device_set_stick_config(struct azeron_device *device,
|
||||
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);
|
||||
|
||||
/* Global settings */
|
||||
int azeron_device_set_global_timings(struct azeron_device *device, uint16_t long_press_delay, uint16_t double_click_delay);
|
||||
|
||||
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,
|
||||
|
||||
@@ -45,6 +45,7 @@ int azeron_protocol_get_stick_config(struct azeron_device *device, struct azeron
|
||||
int azeron_protocol_set_stick_config(struct azeron_device *device, const struct azeron_stick_config *config);
|
||||
int azeron_protocol_get_active_profile(struct azeron_device *device, uint8_t *profile_id);
|
||||
int azeron_protocol_set_active_profile(struct azeron_device *device, uint8_t profile_id);
|
||||
int azeron_protocol_set_global_timings(struct azeron_device *device, uint16_t long_press_delay, uint16_t double_click_delay);
|
||||
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, uint8_t profile_id);
|
||||
|
||||
@@ -229,8 +229,8 @@ int azeron_protocol_write_config(struct azeron_device *device, const uint8_t *da
|
||||
int azeron_protocol_get_button_mapping(struct azeron_device *device, uint8_t button_id,
|
||||
struct azeron_button_mapping *mapping)
|
||||
{
|
||||
uint8_t config[64];
|
||||
size_t size = sizeof(config);
|
||||
uint8_t response[64];
|
||||
size_t response_len;
|
||||
int ret;
|
||||
|
||||
if (!device || !mapping) {
|
||||
@@ -241,28 +241,25 @@ int azeron_protocol_get_button_mapping(struct azeron_device *device, uint8_t but
|
||||
return AZERON_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/* Calculate offset for this button's 4-byte mapping */
|
||||
uint32_t offset = AZERON_PROFILE_BASE_OFFSET + 8 + (button_id * AZERON_BUTTON_MAPPING_SIZE);
|
||||
|
||||
ret = azeron_protocol_read_config(device, offset, config, &size);
|
||||
/* Status command returns the full profile block in Cyborg */
|
||||
ret = send_config_command(device, AZERON_CMD_STATUS, AZERON_OP_READ_STATUS,
|
||||
NULL, 0, response, &response_len);
|
||||
if (ret != AZERON_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Payload structure in response after 6-byte header:
|
||||
* Bytes 0-3: Echoed offset?
|
||||
* Bytes 4-7: The 4-byte mapping
|
||||
* response[0] is status
|
||||
* response[1..58] is profile data.
|
||||
* Mappings start at profile index 8 (response[9]).
|
||||
* Each button has 3 actions, 4 bytes each = 12 bytes per button.
|
||||
*/
|
||||
mapping->button_id = button_id;
|
||||
uint32_t profile_idx = 8 + (button_id * 3 * 4) + (mapping->action * 4);
|
||||
|
||||
/*
|
||||
* Mapping format:
|
||||
* Byte 0: Type (0xf0 = keyboard, 0xf1 = mouse, etc)
|
||||
* Byte 1: Key code
|
||||
*/
|
||||
uint8_t type_byte = config[4];
|
||||
mapping->key_code = config[5];
|
||||
/* Mapping structure: [Type, Code, 0, 0] */
|
||||
uint8_t type_byte = response[profile_idx + 1];
|
||||
mapping->key_code = response[profile_idx + 2];
|
||||
mapping->button_id = button_id;
|
||||
|
||||
switch (type_byte) {
|
||||
case 0xf0: mapping->type = AZERON_BTN_KEYBOARD; break;
|
||||
@@ -342,28 +339,33 @@ int azeron_protocol_set_button_mapping(struct azeron_device *device,
|
||||
int azeron_protocol_get_stick_config(struct azeron_device *device,
|
||||
struct azeron_stick_config *config)
|
||||
{
|
||||
uint8_t data[64];
|
||||
size_t size = sizeof(data);
|
||||
uint8_t response[64];
|
||||
size_t response_len;
|
||||
int ret;
|
||||
|
||||
if (!device || !config) {
|
||||
return AZERON_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
uint32_t offset = AZERON_PROFILE_BASE_OFFSET + AZERON_STICK_CONFIG_OFFSET;
|
||||
|
||||
ret = azeron_protocol_read_config(device, offset, data, &size);
|
||||
/* Status command returns the full profile block in Cyborg */
|
||||
ret = send_config_command(device, AZERON_CMD_STATUS, AZERON_OP_READ_STATUS,
|
||||
NULL, 0, response, &response_len);
|
||||
if (ret != AZERON_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Payload: [Offset(4)] [Deadzone(1)] [Curve(1)] [Sensitivity(1)] [Invert(1)] */
|
||||
config->deadzone = data[4];
|
||||
config->response_curve = data[5];
|
||||
config->sensitivity = data[6];
|
||||
config->invert_x = data[7] & 0x01;
|
||||
config->invert_y = (data[7] >> 1) & 0x01;
|
||||
config->mode = AZERON_STICK_ANALOG;
|
||||
/*
|
||||
* response[0] is status (usually 0x01)
|
||||
* response[1..58] is the profile payload data.
|
||||
* Index 3 in payload is response[4].
|
||||
*/
|
||||
config->mode = (enum azeron_stick_mode)response[4];
|
||||
config->angle = response[9]; /* index 8 in payload */
|
||||
config->deadzone = response[11]; /* index 10 */
|
||||
config->response_curve = response[12]; /* index 11 */
|
||||
config->sensitivity = response[13]; /* index 12 */
|
||||
config->invert_x = response[14] & 0x01; /* index 13 */
|
||||
config->invert_y = (response[14] >> 1) & 0x01;
|
||||
|
||||
return AZERON_SUCCESS;
|
||||
}
|
||||
@@ -375,7 +377,6 @@ int azeron_protocol_set_stick_config(struct azeron_device *device,
|
||||
uint8_t response[64];
|
||||
size_t response_len;
|
||||
uint8_t data[58] = {0};
|
||||
uint32_t offset;
|
||||
uint16_t operation;
|
||||
uint8_t active_profile;
|
||||
int ret;
|
||||
@@ -390,6 +391,7 @@ int azeron_protocol_set_stick_config(struct azeron_device *device,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Operation selects which profile slot to write to */
|
||||
switch (active_profile) {
|
||||
case 0: operation = AZERON_OP_WRITE_PROFILE_0; break;
|
||||
case 1: operation = AZERON_OP_WRITE_PROFILE_1; break;
|
||||
@@ -397,22 +399,44 @@ int azeron_protocol_set_stick_config(struct azeron_device *device,
|
||||
default: return AZERON_ERROR_PROTOCOL;
|
||||
}
|
||||
|
||||
offset = AZERON_PROFILE_BASE_OFFSET + AZERON_STICK_CONFIG_OFFSET;
|
||||
|
||||
data[0] = offset & 0xFF;
|
||||
data[1] = (offset >> 8) & 0xFF;
|
||||
data[2] = (offset >> 16) & 0xFF;
|
||||
data[3] = (offset >> 24) & 0xFF;
|
||||
|
||||
data[4] = config->deadzone;
|
||||
data[5] = config->response_curve;
|
||||
data[6] = config->sensitivity;
|
||||
data[7] = 0;
|
||||
if (config->invert_x) data[7] |= 0x01;
|
||||
if (config->invert_y) data[7] |= 0x02;
|
||||
/* Cyborg Bulk Write Format for 0x26EC:
|
||||
* Index 0-1: Base offset (0x39 0x03)
|
||||
* Index 3: Mode
|
||||
* Index 8: Angle
|
||||
* Index 10: Deadzone
|
||||
*/
|
||||
data[0] = AZERON_PROFILE_BASE_OFFSET & 0xFF;
|
||||
data[1] = (AZERON_PROFILE_BASE_OFFSET >> 8) & 0xFF;
|
||||
data[3] = (uint8_t)config->mode;
|
||||
data[8] = config->angle;
|
||||
data[10] = config->deadzone;
|
||||
|
||||
ret = send_config_command(device, AZERON_CMD_WRITE_PROFILE, operation,
|
||||
data, 8, response, &response_len);
|
||||
data, 58, response, &response_len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set global timings */
|
||||
int azeron_protocol_set_global_timings(struct azeron_device *device, uint16_t long_press_delay, uint16_t double_click_delay)
|
||||
{
|
||||
uint8_t response[64];
|
||||
size_t response_len;
|
||||
uint8_t data[58] = {0};
|
||||
int ret;
|
||||
|
||||
if (!device) {
|
||||
return AZERON_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/* Delay values are LITTLE ENDIAN at the very end of the 58-byte payload */
|
||||
data[54] = long_press_delay & 0xFF;
|
||||
data[55] = (long_press_delay >> 8) & 0xFF;
|
||||
data[56] = double_click_delay & 0xFF;
|
||||
data[57] = (double_click_delay >> 8) & 0xFF;
|
||||
|
||||
ret = send_config_command(device, AZERON_CMD_SET_GLOBAL, 0x0101,
|
||||
data, 58, response, &response_len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user