hidpp10: parse macros

Macros are somewhat properly parsed in hidpp10.
The driver, however, supports only key press/release and delays.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
This commit is contained in:
Benjamin Tissoires
2015-12-17 13:19:19 +01:00
parent a9a8e23d37
commit 06d0c03ea1
3 changed files with 466 additions and 5 deletions

View File

@@ -53,6 +53,106 @@ struct hidpp10drv_data {
struct hidpp10_device *dev;
};
static unsigned int
hidpp10drv_read_macro_modifier(struct ratbag_device *device, union hidpp10_macro_data *macro)
{
switch (macro->key.key) {
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 KEY_RESERVED;
}
static void
hidpp10drv_read_macro(struct ratbag_button *button,
struct hidpp10_profile *profile,
union hidpp10_button *binding)
{
struct ratbag_device *device = button->profile->device;
const char *name;
union hidpp10_macro_data *macro;
unsigned int i, keycode;
bool delay = true;
macro = profile->macros[binding->macro.address];
name = binding->macro.address > 1 ? (const char *)profile->macro_names[binding->macro.address - 2] : "";
ratbag_button_set_macro(button, name);
i = 0;
while (macro && macro->any.type != HIDPP10_MACRO_END && i < MAX_MACRO_EVENTS) {
switch (macro->any.type) {
case HIDPP10_MACRO_DELAY:
ratbag_button_set_macro_event(button,
i++,
RATBAG_MACRO_EVENT_WAIT,
macro->delay.time);
delay = true;
break;
case HIDPP10_MACRO_KEY_PRESS:
keycode = ratbag_hidraw_get_keycode_from_keyboard_usage(device, macro->key.key);
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 HIDPP10_MACRO_KEY_RELEASE:
keycode = ratbag_hidraw_get_keycode_from_keyboard_usage(device, macro->key.key);
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;
case HIDPP10_MACRO_MOD_PRESS:
keycode = hidpp10drv_read_macro_modifier(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 HIDPP10_MACRO_MOD_RELEASE:
keycode = hidpp10drv_read_macro_modifier(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
hidpp10drv_map_button(struct ratbag_device *device,
struct hidpp10_device *hidpp10,
@@ -91,8 +191,7 @@ hidpp10drv_map_button(struct ratbag_device *device,
if (profile.buttons[button->index].any.type & 0x80) {
button->action.type = RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
} else {
/* FIXME: MACRO */
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
hidpp10drv_read_macro(button, &profile, &profile.buttons[button->index]);
}
}

View File

@@ -568,6 +568,11 @@ union _hidpp10_button_binding {
uint8_t zero0;
uint8_t zero1;
} disabled;
struct {
uint8_t page;
uint8_t offset;
uint8_t zero;
} __attribute__((packed)) macro;
} __attribute__((packed));
_Static_assert(sizeof(union _hidpp10_button_binding) == 3, "Invalid size");
@@ -920,6 +925,266 @@ hidpp10_write_dpi_modes_16(struct hidpp10_device *dev,
}
}
static int
hidpp10_onboard_profiles_macro_next(struct hidpp10_device *device,
uint8_t memory[32],
uint16_t *index,
union hidpp10_macro_data *macro)
{
int rc = 0;
unsigned int step = 0;
if (*index >= 32 - sizeof(union hidpp10_macro_data)) {
hidpp_log_error(&device->base, "error while parsing macro.\n");
return -EFAULT;
}
memcpy(macro, &memory[*index], sizeof(union hidpp10_macro_data));
switch (macro->any.type) {
case HIDPP10_MACRO_NOOP:
/* fallthrough */
case HIDPP10_MACRO_WAIT_FOR_BUTTON_RELEASE:
/* fallthrough */
case HIDPP10_MACRO_REPEAT_UNTIL_BUTTON_RELEASE:
/* fallthrough */
case HIDPP10_MACRO_REPEAT:
step = 1;
rc = -EAGAIN;
break;
case HIDPP10_MACRO_KEY_PRESS:
/* fallthrough */
case HIDPP10_MACRO_KEY_RELEASE:
/* fallthrough */
case HIDPP10_MACRO_MOD_PRESS:
/* fallthrough */
case HIDPP10_MACRO_MOD_RELEASE:
/* fallthrough */
case HIDPP10_MACRO_MOUSE_WHEEL:
step = 2;
rc = -EAGAIN;
break;
case HIDPP10_MACRO_MOUSE_BUTTON_PRESS:
/* fallthrough */
case HIDPP10_MACRO_MOUSE_BUTTON_RELEASE:
/* fallthrough */
case HIDPP10_MACRO_KEY_CONSUMER_CONTROL:
/* fallthrough */
case HIDPP10_MACRO_DELAY:
/* fallthrough */
case HIDPP10_MACRO_JUMP:
/* fallthrough */
case HIDPP10_MACRO_JUMP_IF_PRESSED:
step = 3;
rc = -EAGAIN;
break;
case HIDPP10_MACRO_MOUSE_POINTER_MOVE:
/* fallthrough */
case HIDPP10_MACRO_JUMP_IF_RELEASED_TIMEOUT:
step = 5;
rc = -EAGAIN;
break;
case HIDPP10_MACRO_END:
step = 1;
return 0;
default:
if (macro->any.type >= 0x80 && macro->any.type <= 0xFE) {
step = 1;
rc = -EAGAIN;
} else {
hidpp_log_error(&device->base, "unknown tag: 0x%02x\n", macro->any.type);
return -EFAULT;
}
}
if ((*index + step) & 0xF0)
/* the next item will be on the following chunk */
return -ENOMEM;
*index += step;
return rc;
}
static int
hidpp10_onboard_profiles_read_macro(struct hidpp10_device *device,
uint8_t page, uint8_t offset,
union hidpp10_macro_data **return_macro)
{
uint8_t memory[HIDPP10_PAGE_SIZE];
union hidpp10_macro_data *macro = NULL;
unsigned count = 0;
unsigned index = 0;
uint16_t mem_index = 0;
int rc = -ENOMEM;
do {
if (count == index) {
union hidpp10_macro_data *tmp;
count += 32;
/* manual realloc to have the memory zero-initialized */
tmp = zalloc(count * sizeof(union hidpp10_macro_data));
if (macro) {
memcpy(tmp, macro, (count - 32) * sizeof(union hidpp10_macro_data));
free(macro);
}
macro = tmp;
}
if (rc == -ENOMEM) {
offset += mem_index;
if (offset & 0x01)
offset--;
rc = hidpp10_read_memory(device, page, offset, memory);
if (rc)
goto out_err;
mem_index &= 0x01;
hidpp_log_buf_raw(&device->base, "-> ", memory + mem_index, 16 - mem_index);
}
rc = hidpp10_onboard_profiles_macro_next(device,
memory,
&mem_index,
&macro[index]);
if (rc == -EFAULT)
goto out_err;
if (rc != -ENOMEM) {
if (macro[index].any.type == HIDPP10_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
hidpp10_onboard_profiles_parse_macro(struct hidpp10_device *device,
uint8_t page, uint8_t offset,
union hidpp10_macro_data **return_macro)
{
union hidpp10_macro_data *m, *macro = NULL;
unsigned i, count = 0;
int rc;
hidpp_log_raw(&device->base, "*** macro starts at (0x%02x, 0x%04x) ***\n", page, offset);
rc = hidpp10_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 HIDPP10_MACRO_NOOP:
hidpp_log_raw(&device->base, "noop\n");
break;
case HIDPP10_MACRO_WAIT_FOR_BUTTON_RELEASE:
hidpp_log_raw(&device->base, "wait for button release\n");
break;
case HIDPP10_MACRO_REPEAT_UNTIL_BUTTON_RELEASE:
hidpp_log_raw(&device->base, "repeat from beginning until button release\n");
break;
case HIDPP10_MACRO_REPEAT:
hidpp_log_raw(&device->base, "repeat from beginning\n");
break;
case HIDPP10_MACRO_KEY_PRESS:
hidpp_log_raw(&device->base, "key press: %02x\n", m->key.key);
break;
case HIDPP10_MACRO_KEY_RELEASE:
hidpp_log_raw(&device->base, "key release: %02x\n", m->key.key);
break;
case HIDPP10_MACRO_MOD_PRESS:
hidpp_log_raw(&device->base, "modifier press: %02x\n", m->modifier.key);
break;
case HIDPP10_MACRO_MOD_RELEASE:
hidpp_log_raw(&device->base, "modifier release: %02x\n", m->modifier.key);
break;
case HIDPP10_MACRO_MOUSE_WHEEL:
hidpp_log_raw(&device->base, "mouse wheel: %+d\n", m->wheel.value);
break;
case HIDPP10_MACRO_MOUSE_BUTTON_PRESS:
m->button.flags = ffs(hidpp_le_u16_to_cpu(m->button.flags));
hidpp_log_raw(&device->base, "mouse button press: %d\n", m->button.flags);
break;
case HIDPP10_MACRO_MOUSE_BUTTON_RELEASE:
m->button.flags = ffs(hidpp_le_u16_to_cpu(m->button.flags));
hidpp_log_raw(&device->base, "mouse button release: %d\n", m->button.flags);
break;
case HIDPP10_MACRO_KEY_CONSUMER_CONTROL:
m->consumer_control.key = hidpp_be_u16_to_cpu(m->consumer_control.key);
hidpp_log_raw(&device->base, "switched to consumer control: 0x%04x\n", m->consumer_control.key);
break;
case HIDPP10_MACRO_DELAY:
m->delay.time = hidpp_be_u16_to_cpu(m->delay.time);
hidpp_log_raw(&device->base, "delay: %0.03f\n", m->delay.time/1000.0);
break;
case HIDPP10_MACRO_JUMP:
/* should be skipped by hidpp10_onboard_profiles_read_macro */
hidpp_log_raw(&device->base, "jump to: (0x%02x, 0x%02x)\n", m->jump.page, m->jump.offset);
break;
case HIDPP10_MACRO_JUMP_IF_PRESSED:
hidpp_log_raw(&device->base, "conditional jump to: (0x%02x, 0x%02x)\n", m->jump.page, m->jump.offset);
break;
case HIDPP10_MACRO_MOUSE_POINTER_MOVE:
break;
case HIDPP10_MACRO_JUMP_IF_RELEASED_TIMEOUT:
m->jump_timeout.timeout = hidpp_be_u16_to_cpu(m->jump_timeout.timeout);
hidpp_log_raw(&device->base, "conditional jump to: (0x%02x, 0x%02x) if released whithin %0.03f msecs.\n", m->jump_timeout.page, m->jump_timeout.offset, m->jump_timeout.timeout / 1000.0);
break;
case HIDPP10_MACRO_END:
break;
default:
if (m->any.type >= 0x80 && m->any.type <= 0x9F) {
m->delay.time = 8 + (m->any.type - 0x80) * 4;
m->any.type = HIDPP10_MACRO_DELAY;
hidpp_log_raw(&device->base, "short delay: %0.03f\n", m->delay.time/1000.0);
} else if (m->any.type >= 0xA0 && m->any.type <= 0xBF) {
m->delay.time = 132 + (m->any.type - 0x9F) * 8;
m->any.type = HIDPP10_MACRO_DELAY;
hidpp_log_raw(&device->base, "short delay: %0.03f\n", m->delay.time/1000.0);
} else if (m->any.type >= 0xC0 && m->any.type <= 0xDF) {
m->delay.time = 388 + (m->any.type - 0xBF) * 16;
m->any.type = HIDPP10_MACRO_DELAY;
hidpp_log_raw(&device->base, "short delay: %0.03f\n", m->delay.time/1000.0);
} else if (m->any.type >= 0xE0 && m->any.type <= 0xFE) {
m->delay.time = 900 + (m->any.type - 0xDF) * 32;
m->any.type = HIDPP10_MACRO_DELAY;
hidpp_log_raw(&device->base, "short delay: %0.03f\n", m->delay.time/1000.0);
} else {
hidpp_log_error(&device->base, "unknown tag: 0x%02x\n", m->any.type);
}
}
}
hidpp_log_raw(&device->base, "*** end of macro ***\n");
*return_macro = macro;
return 0;
}
static void
hidpp10_fill_buttons(struct hidpp10_device *dev,
struct hidpp10_profile *profile,
@@ -952,6 +1217,17 @@ hidpp10_fill_buttons(struct hidpp10_device *dev,
break;
case PROFILE_BUTTON_TYPE_DISABLED:
break;
default:
/* macros */
button->macro.address = i;
if (profile->macros[i]) {
free(profile->macros[i]);
profile->macros[i] = NULL;
}
hidpp10_onboard_profiles_parse_macro(dev,
b->macro.page,
b->macro.offset * 2,
&profile->macros[i]);
}
}
}
@@ -1133,16 +1409,16 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
profile->refresh_rate = p500->usb_refresh_rate ? 1000/p500->usb_refresh_rate : 0;
hidpp10_fill_dpi_modes_16(dev, profile, p500->dpi_modes, PROFILE_NUM_DPI_MODES);
hidpp10_fill_buttons(dev, profile, buttons, PROFILE_NUM_BUTTONS);
hidpp10_profile_parse_names(dev, profile, number, &p500->metadata);
hidpp10_fill_buttons(dev, profile, buttons, PROFILE_NUM_BUTTONS);
break;
case HIDPP10_PROFILE_G700:
profile->default_dpi_mode = p700->default_dpi_mode;
profile->refresh_rate = p700->usb_refresh_rate ? 1000/p700->usb_refresh_rate : 0;
hidpp10_fill_dpi_modes_8(dev, profile, p700->dpi_modes, PROFILE_NUM_DPI_MODES);
hidpp10_fill_buttons(dev, profile, buttons, PROFILE_NUM_BUTTONS);
hidpp10_profile_parse_names(dev, profile, number, &p700->metadata);
hidpp10_fill_buttons(dev, profile, buttons, PROFILE_NUM_BUTTONS);
break;
default:
hidpp_log_error(&dev->base, "This should never happen, complain to your maintainer.\n");
@@ -2287,11 +2563,24 @@ hidpp10_device_new(const struct hidpp_device *base, int idx,
void
hidpp10_device_destroy(struct hidpp10_device *dev)
{
union hidpp10_macro_data **macro;
unsigned i;
if (dev->dpi_table)
free(dev->dpi_table);
if (dev->profile_directory)
free(dev->profile_directory);
if (dev->profiles)
if (dev->profiles) {
for (i = 0; i < dev->profile_count; i++) {
ARRAY_FOR_EACH(dev->profiles[i].macros, macro) {
if (*macro) {
free(*macro);
*macro = NULL;
}
}
}
free(dev->profiles);
}
free(dev);
}

View File

@@ -376,6 +376,75 @@ hidpp10_get_battery_mileage(struct hidpp10_device *dev,
#define PROFILE_BUTTON_SPECIAL_DPI_NEXT 0x4
#define PROFILE_BUTTON_SPECIAL_DPI_PREV 0x8
#define HIDPP10_MACRO_NOOP 0x00
#define HIDPP10_MACRO_WAIT_FOR_BUTTON_RELEASE 0x01
#define HIDPP10_MACRO_REPEAT_UNTIL_BUTTON_RELEASE 0x02
#define HIDPP10_MACRO_REPEAT 0x03
#define HIDPP10_MACRO_KEY_PRESS 0x20
#define HIDPP10_MACRO_KEY_RELEASE 0x21
#define HIDPP10_MACRO_MOD_PRESS 0x22
#define HIDPP10_MACRO_MOD_RELEASE 0x23
#define HIDPP10_MACRO_MOUSE_WHEEL 0x24
#define HIDPP10_MACRO_MOUSE_BUTTON_PRESS 0x40
#define HIDPP10_MACRO_MOUSE_BUTTON_RELEASE 0x41
#define HIDPP10_MACRO_KEY_CONSUMER_CONTROL 0x42
#define HIDPP10_MACRO_DELAY 0x43
#define HIDPP10_MACRO_JUMP 0x44
#define HIDPP10_MACRO_JUMP_IF_PRESSED 0x45
#define HIDPP10_MACRO_MOUSE_POINTER_MOVE 0x60
#define HIDPP10_MACRO_JUMP_IF_RELEASED_TIMEOUT 0x61
#define HIDPP10_MACRO_END 0xff
union hidpp10_macro_data {
struct {
uint8_t type;
} __attribute__((packed)) any;
struct {
uint8_t type; /* HIDPP10_MACRO_KEY_PRESS or HIDPP10_MACRO_KEY_RELEASE */
uint8_t key;
} __attribute__((packed)) key;
struct {
uint8_t type; /* HIDPP10_MACRO_MOD_PRESS or HIDPP10_MACRO_MOD_RELEASE */
uint8_t key;
} __attribute__((packed)) modifier;
struct {
uint8_t type; /* HIDPP10_MACRO_MOUSE_WHEEL */
int8_t value;
} __attribute__((packed)) wheel;
struct {
uint8_t type; /* HIDPP10_MACRO_MOUSE_BUTTON_PRESS or HIDPP10_MACRO_MOUSE_BUTTON_RELEASE */
uint16_t flags;
} __attribute__((packed)) button;
struct {
uint8_t type; /* HIDPP10_MACRO_KEY_CONSUMER_CONTROL */
uint16_t key;
} __attribute__((packed)) consumer_control;
struct {
uint8_t type; /* HIDPP10_MACRO_DELAY */
uint16_t time;
} __attribute__((packed)) delay;
struct {
uint8_t type; /* HIDPP10_MACRO_JUMP or HIDPP10_MACRO_JUMP_IF_PRESSED */
uint8_t page;
uint8_t offset;
} __attribute__((packed)) jump;
struct {
uint8_t type; /* HIDPP10_MACRO_MOUSE_POINTER_MOVE */
int16_t x_rel;
uint16_t y_rel;
} __attribute__((packed)) pointer;
struct {
uint8_t type; /* HIDPP10_MACRO_JUMP_IF_RELEASED_TIMEOUT */
int16_t timeout;
uint8_t page;
uint8_t offset;
} __attribute__((packed)) jump_timeout;
struct {
uint8_t type; /* HIDPP10_MACRO_END */
} __attribute__((packed)) end;
} __attribute__((packed));
_Static_assert(sizeof(union hidpp10_macro_data) == 5, "Invalid size");
struct hidpp10_profile {
struct {
uint16_t xres;
@@ -415,7 +484,11 @@ struct hidpp10_profile {
struct {
uint8_t type;
} disabled;
struct {
uint8_t address;
} macro;
} buttons[PROFILE_NUM_BUTTONS];
union hidpp10_macro_data *macros[PROFILE_NUM_BUTTONS];
size_t num_buttons;
unsigned int initialized;