hidpp20: parse macros

I am pretty sure we could do a better job, but heh.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
This commit is contained in:
Benjamin Tissoires
2015-12-17 11:39:34 +01:00
parent ff7a59f46a
commit a9a8e23d37
3 changed files with 301 additions and 2 deletions

View File

@@ -97,6 +97,78 @@ hidpp20drv_read_button_1b04(struct ratbag_button *button)
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
}
static unsigned int
hidpp20drv_read_macro_key_8100(struct ratbag_device *device, union hidpp20_macro_data *macro)
{
switch (macro->key.modifier) {
case 0x01: return KEY_LEFTCTRL;
case 0x02: return KEY_LEFTSHIFT;
case 0x04: return KEY_LEFTALT;
case 0x08: return KEY_LEFTMETA;
case 0x10: return KEY_RIGHTCTRL;
case 0x20: return KEY_RIGHTSHIFT;
case 0x40: return KEY_RIGHTALT;
case 0x80: return KEY_RIGHTMETA;
}
return ratbag_hidraw_get_keycode_from_keyboard_usage(device, macro->key.key);
}
static void
hidpp20drv_read_macro_8100(struct ratbag_button *button,
struct hidpp20_profile *profile,
union hidpp20_button_binding *binding)
{
struct ratbag_device *device = button->profile->device;
union hidpp20_macro_data *macro;
unsigned int i, keycode;
bool delay = true;
macro = profile->macros[binding->macro.page];
ratbag_button_set_macro(button, "macro");
i = 0;
while (macro && macro->any.type != HIDPP20_MACRO_END && i < MAX_MACRO_EVENTS) {
switch (macro->any.type) {
case HIDPP20_MACRO_DELAY:
ratbag_button_set_macro_event(button,
i++,
RATBAG_MACRO_EVENT_WAIT,
macro->delay.time);
delay = true;
break;
case HIDPP20_MACRO_KEY_PRESS:
keycode = hidpp20drv_read_macro_key_8100(device, macro);
if (!delay)
ratbag_button_set_macro_event(button,
i++,
RATBAG_MACRO_EVENT_WAIT,
1);
ratbag_button_set_macro_event(button,
i++,
RATBAG_MACRO_EVENT_KEY_PRESSED,
keycode);
delay = false;
break;
case HIDPP20_MACRO_KEY_RELEASE:
keycode = hidpp20drv_read_macro_key_8100(device, macro);
if (!delay)
ratbag_button_set_macro_event(button,
i++,
RATBAG_MACRO_EVENT_WAIT,
1);
ratbag_button_set_macro_event(button,
i++,
RATBAG_MACRO_EVENT_KEY_RELEASED,
keycode);
delay = false;
break;
}
macro++;
}
}
static void
hidpp20drv_read_button_8100(struct ratbag_button *button)
{
@@ -132,6 +204,9 @@ hidpp20drv_read_button_8100(struct ratbag_button *button)
button->action.type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL;
button->action.action.special = hidpp20_onboard_profiles_get_special(profile->buttons[button->index].special.special);
break;
case HIDPP20_BUTTON_MACRO:
hidpp20drv_read_macro_8100(button, profile, &profile->buttons[button->index]);
break;
default:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
}

View File

@@ -1133,6 +1133,166 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
return 0;
}
static int
hidpp20_onboard_profiles_macro_next(struct hidpp20_device *device,
uint8_t memory[32],
uint16_t *index,
union hidpp20_macro_data *macro)
{
int rc = 0;
unsigned int step = 1;
if (*index >= 32 - sizeof(union hidpp20_macro_data)) {
hidpp_log_error(&device->base, "error while parsing macro.\n");
return -EFAULT;
}
memcpy(macro, &memory[*index], sizeof(union hidpp20_macro_data));
switch (macro->any.type) {
case HIDPP20_MACRO_DELAY:
/* fallthrough */
case HIDPP20_MACRO_KEY_PRESS:
/* fallthrough */
case HIDPP20_MACRO_KEY_RELEASE:
/* fallthrough */
case HIDPP20_MACRO_JUMP:
step = 3;
rc = -EAGAIN;
break;
case HIDPP20_MACRO_NOOP:
step = 1;
rc = -EAGAIN;
break;
case HIDPP20_MACRO_END:
step = 1;
return 0;
default:
hidpp_log_error(&device->base, "unknown tag: 0x%02x\n", macro->any.type);
rc = -EFAULT;
}
if ((*index + step) & 0xF0)
/* the next item will be on the following chunk */
return -ENOMEM;
*index += step;
return rc;
}
static int
hidpp20_onboard_profiles_read_macro(struct hidpp20_device *device,
uint8_t page, uint8_t offset,
union hidpp20_macro_data **return_macro)
{
uint8_t memory[HIDPP20_PAGE_SIZE];
union hidpp20_macro_data *macro = NULL;
unsigned count = 0;
unsigned index = 0;
uint16_t mem_index = 0;
int rc = -ENOMEM;
do {
if (count == index) {
union hidpp20_macro_data *tmp;
count += 32;
/* manual realloc to have the memory zero-initialized */
tmp = zalloc(count * sizeof(union hidpp20_macro_data));
if (macro) {
memcpy(tmp, macro, (count - 32) * sizeof(union hidpp20_macro_data));
free(macro);
}
macro = tmp;
}
if (rc == -ENOMEM) {
offset += mem_index;
rc = hidpp20_onboard_profiles_read_memory(device,
0,
page,
offset,
memory);
if (rc)
goto out_err;
mem_index = 0;
}
rc = hidpp20_onboard_profiles_macro_next(device,
memory,
&mem_index,
&macro[index]);
if (rc == -EFAULT)
goto out_err;
if (rc != -ENOMEM) {
if (macro[index].any.type == HIDPP20_MACRO_JUMP) {
page = macro[index].jump.page;
offset = macro[index].jump.offset;
mem_index = 0;
/* no need to store the jump in memory */
index--;
/* force memory fetching */
rc = -ENOMEM;
}
index++;
}
} while (rc);
*return_macro = macro;
return index;
out_err:
if (macro)
free(macro);
return rc;
}
static int
hidpp20_onboard_profiles_parse_macro(struct hidpp20_device *device,
uint8_t page, uint8_t offset,
union hidpp20_macro_data **return_macro)
{
union hidpp20_macro_data *m, *macro = NULL;
unsigned i, count = 0;
int rc;
rc = hidpp20_onboard_profiles_read_macro(device, page, offset, &macro);
if (rc < 0)
return rc;
count = rc;
for (i = 0; i < count; i++) {
m = &macro[i];
switch (m->any.type) {
case HIDPP20_MACRO_DELAY:
m->delay.time = hidpp_be_u16_to_cpu(m->delay.time);
break;
case HIDPP20_MACRO_KEY_PRESS:
break;
case HIDPP20_MACRO_KEY_RELEASE:
break;
case HIDPP20_MACRO_JUMP:
break;
case HIDPP20_MACRO_END:
break;
case HIDPP20_MACRO_NOOP:
break;
default:
hidpp_log_error(&device->base, "unknown tag: 0x%02x\n", m->any.type);
}
}
*return_macro = macro;
return 0;
}
int
hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
struct hidpp20_profiles **profiles_list)
@@ -1177,9 +1337,22 @@ void
hidpp20_onboard_profiles_destroy(struct hidpp20_device *device,
struct hidpp20_profiles *profiles_list)
{
struct hidpp20_profile *profile;
union hidpp20_macro_data **macro;
unsigned i;
if (!profiles_list)
return;
for (i = 0; i < profiles_list->num_profiles; i++) {
profile = &profiles_list->profiles[i];
ARRAY_FOR_EACH(profile->macros, macro) {
if (*macro)
free(*macro);
}
}
free(profiles_list);
}
@@ -1288,7 +1461,8 @@ union hidpp20_internal_profile {
_Static_assert(sizeof(union hidpp20_internal_profile) == HIDPP20_PROFILE_SIZE, "Invalid size");
static void
hidpp20_buttons_to_cpu(struct hidpp20_profile *profile,
hidpp20_buttons_to_cpu(struct hidpp20_device *device,
struct hidpp20_profile *profile,
union hidpp20_button_binding *buttons,
unsigned int count)
{
@@ -1320,6 +1494,17 @@ hidpp20_buttons_to_cpu(struct hidpp20_profile *profile,
case HIDPP20_BUTTON_SPECIAL:
button->special.special = b->special.special;
break;
case HIDPP20_BUTTON_MACRO:
if (profile->macros[i]) {
free(profile->macros[i]);
profile->macros[i] = NULL;
}
hidpp20_onboard_profiles_parse_macro(device,
b->macro.page,
b->macro.offset,
&profile->macros[i]);
button->macro.page = i;
break;
case HIDPP20_BUTTON_DISABLED:
break;
default:
@@ -1396,7 +1581,7 @@ int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
profile->dpi[i] = hidpp_get_unaligned_le_u16(&data[2 * i + 3]);
}
hidpp20_buttons_to_cpu(profile, pdata.profile.buttons, profiles_list->num_buttons);
hidpp20_buttons_to_cpu(device, profile, pdata.profile.buttons, profiles_list->num_buttons);
memcpy(profile->name, pdata.profile.name.txt, sizeof(profile->name));
/* force terminating '\0' */

View File

@@ -269,6 +269,7 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
#define HIDPP20_BUTTON_HID_TYPE_KEYBOARD 0x02
#define HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL 0x03
#define HIDPP20_BUTTON_SPECIAL 0x90
#define HIDPP20_BUTTON_MACRO 0x00
#define HIDPP20_BUTTON_DISABLED 0xFF
#define HIDPP20_MODIFIER_KEY_CTRL 0x01
@@ -302,12 +303,49 @@ union hidpp20_button_binding {
uint8_t type; /* HIDPP20_BUTTON_SPECIAL */
uint8_t special;
} __attribute__((packed)) special;
struct {
uint8_t type; /* HIDPP20_BUTTON_MACRO */
uint8_t page;
uint8_t zero;
uint8_t offset;
} __attribute__((packed)) macro;
struct {
uint8_t type; /* PROFILE_BUTTON_TYPE_DISABLED */
} disabled;
} __attribute__((packed));
_Static_assert(sizeof(union hidpp20_button_binding) == 4, "Invalid size");
#define HIDPP20_MACRO_NOOP 0x01
#define HIDPP20_MACRO_DELAY 0x40
#define HIDPP20_MACRO_KEY_PRESS 0x43
#define HIDPP20_MACRO_KEY_RELEASE 0x44
#define HIDPP20_MACRO_JUMP 0x60
#define HIDPP20_MACRO_END 0xff
union hidpp20_macro_data {
struct {
uint8_t type;
} __attribute__((packed)) any;
struct {
uint8_t type; /* HIDPP20_MACRO_DELAY */
uint16_t time;
} __attribute__((packed)) delay;
struct {
uint8_t type; /* HIDPP20_MACRO_KEY_PRESS or HIDPP20_MACRO_KEY_RELEASE */
uint8_t modifier;
uint8_t key;
} __attribute__((packed)) key;
struct {
uint8_t type; /* HIDPP20_MACRO_JUMP */
uint8_t offset;
uint8_t page;
} __attribute__((packed)) jump;
struct {
uint8_t type; /* HIDPP20_MACRO_END */
} __attribute__((packed)) end;
} __attribute__((packed));
_Static_assert(sizeof(union hidpp20_macro_data) == 3, "Invalid size");
struct hidpp20_profile {
uint8_t index;
uint8_t enabled;
@@ -317,6 +355,7 @@ struct hidpp20_profile {
unsigned switched_dpi;
uint16_t dpi[5];
union hidpp20_button_binding buttons[32];
union hidpp20_macro_data *macros[32];
};
struct hidpp20_profiles {