static void hidpp20drv_read_button_1b04(struct ratbag_button *button) { struct ratbag_device *device = button->profile->device; struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct hidpp20_control_id *control; const struct ratbag_button_action *action; uint16_t mapping; if (!(drv_data->capabilities & HIDPP_CAP_BUTTON_KEY_1b04)) return; control = &drv_data->controls[button->index]; mapping = control->control_id; if (control->reporting.divert || control->reporting.persist) mapping = control->reporting.remapped; log_raw(device->ratbag, " - button%d: %s (%02x) %s%s:%d\n", button->index, hidpp20_1b04_get_logical_mapping_name(mapping), mapping, control->reporting.divert || control->reporting.persist ? "(redirected) " : "", __FILE__, __LINE__); button->type = hidpp20_1b04_get_physical_mapping(control->task_id); action = hidpp20_1b04_get_logical_mapping(mapping); if (action) button->action = *action; ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON); ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY); ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL); }
static int hidpp20drv_20_probe(struct ratbag_device *device) { struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct hidpp20_device *dev = drv_data->dev; struct hidpp20_feature *feature_list = dev->feature_list; unsigned int i; int rc; log_raw(device->ratbag, "'%s' has %d features\n", ratbag_device_get_name(device), dev->feature_count); for (i = 0; i < dev->feature_count; i++) { log_raw(device->ratbag, "Init feature %s (0x%04x) \n", hidpp20_feature_get_name(feature_list[i].feature), feature_list[i].feature); rc = hidpp20drv_init_feature(device, feature_list[i].feature); if (rc < 0) return rc; } return 0; }
static int hidpp20drv_read_resolution_dpi_2201(struct ratbag_device *device) { struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct ratbag *ratbag = device->ratbag; int rc; free(drv_data->sensors); drv_data->sensors = NULL; drv_data->num_sensors = 0; rc = hidpp20_adjustable_dpi_get_sensors(drv_data->dev, &drv_data->sensors); if (rc < 0) { log_error(ratbag, "Error while requesting resolution: %s (%d)\n", strerror(-rc), rc); return rc; } else if (rc == 0) { log_error(ratbag, "Error, no compatible sensors found.\n"); return -ENODEV; } log_debug(ratbag, "device is at %d dpi (variable between %d and %d).\n", drv_data->sensors[0].dpi, drv_data->sensors[0].dpi_min, drv_data->sensors[0].dpi_max); drv_data->num_sensors = rc; drv_data->num_resolutions = drv_data->num_sensors; return 0; }
static void test_read_profile(struct ratbag_profile *profile, unsigned int index) { struct ratbag_test_device *d = ratbag_get_drv_data(profile->device); struct ratbag_test_profile *p; struct ratbag_test_resolution *r; unsigned int i; assert(index < d->num_profiles); p = &d->profiles[index]; profile->resolution.num_modes = p->num_resolutions; for (i = 0; i < p->num_resolutions; i++) { struct ratbag_resolution *res; r = &p->resolutions[i]; res = ratbag_resolution_init(profile, i, r->xres, r->yres, r->hz); res->is_active = r->active; res->is_default = r->dflt; res->capabilities = r->caps; res->hz = r->hz; assert(res); } profile->is_active = p->active; profile->is_default = p->dflt; }
static int hidpp20drv_write_button_1b04(struct ratbag_button *button, const struct ratbag_button_action *action) { struct ratbag_device *device = button->profile->device; struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct hidpp20_control_id *control; uint16_t mapping; int rc; if (!(drv_data->capabilities & HIDPP_CAP_BUTTON_KEY_1b04)) return -ENOTSUP; control = &drv_data->controls[button->index]; mapping = hidpp20_1b04_get_logical_control_id(action); if (!mapping) return -EINVAL; control->reporting.divert = 1; control->reporting.remapped = mapping; control->reporting.updated = 1; rc = hidpp20_special_key_mouse_set_control(drv_data->dev, control); if (rc == ERR_INVALID_ADDRESS) return -EINVAL; if (rc) log_error(device->ratbag, "Error while writing profile: '%s' (%d)\n", strerror(-rc), rc); return rc; }
static int test_write_profile(struct ratbag_profile *profile) { /* check if the device is still valid */ assert(ratbag_get_drv_data(profile->device) != NULL); return 0; }
static void test_remove(struct ratbag_device *device) { struct ratbag_test_device *d = ratbag_get_drv_data(device); /* remove must be called only once */ assert(d != NULL); if (d->destroyed) d->destroyed(device, d->destroyed_data); ratbag_set_drv_data(device, NULL); }
static int hidpp20drv_write_button_8100(struct ratbag_button *button, const struct ratbag_button_action *action) { struct ratbag_device *device = button->profile->device; struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct hidpp20_profile *profile; uint8_t code, type, subtype; if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)) return -ENOTSUP; profile = &drv_data->profiles->profiles[button->profile->index]; switch (action->type) { case RATBAG_BUTTON_ACTION_TYPE_BUTTON: profile->buttons[button->index].button.type = HIDPP20_BUTTON_HID_TYPE; profile->buttons[button->index].button.subtype = HIDPP20_BUTTON_HID_TYPE_MOUSE; profile->buttons[button->index].button.buttons = action->action.button; break; case RATBAG_BUTTON_ACTION_TYPE_KEY: type = HIDPP20_BUTTON_HID_TYPE; subtype = HIDPP20_BUTTON_HID_TYPE_KEYBOARD; code = ratbag_hidraw_get_keyboard_usage_from_keycode(device, action->action.key.key); if (code == 0) { subtype = HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL; code = ratbag_hidraw_get_consumer_usage_from_keycode(device, action->action.key.key); if (code == 0) return -EINVAL; } profile->buttons[button->index].subany.type = type; profile->buttons[button->index].subany.subtype = subtype; if (subtype == HIDPP20_BUTTON_HID_TYPE_KEYBOARD) profile->buttons[button->index].keyboard_keys.key = code; else profile->buttons[button->index].consumer_control.consumer_control = code; break; case RATBAG_BUTTON_ACTION_TYPE_SPECIAL: code = hidpp20_onboard_profiles_get_code_from_special(action->action.special); if (code == 0) return -EINVAL; profile->buttons[button->index].special.type = HIDPP20_BUTTON_SPECIAL; profile->buttons[button->index].special.special = code; break; case RATBAG_BUTTON_ACTION_TYPE_MACRO: default: return -ENOTSUP; } return hidpp20_onboard_profiles_write(drv_data->dev, button->profile->index, drv_data->profiles); }
static void hidpp20drv_read_button_8100(struct ratbag_button *button) { struct ratbag_device *device = button->profile->device; struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct hidpp20_profile *profile; int rc; if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)) return; profile = &drv_data->profiles->profiles[button->profile->index]; switch (profile->buttons[button->index].any.type) { case HIDPP20_BUTTON_HID_TYPE: switch (profile->buttons[button->index].subany.subtype) { case HIDPP20_BUTTON_HID_TYPE_MOUSE: button->action.type = RATBAG_BUTTON_ACTION_TYPE_BUTTON; button->action.action.button = profile->buttons[button->index].button.buttons; break; case HIDPP20_BUTTON_HID_TYPE_KEYBOARD: button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY; button->action.action.key.key = ratbag_hidraw_get_keycode_from_keyboard_usage(device, profile->buttons[button->index].keyboard_keys.key); break; case HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL: button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY; button->action.action.key.key = ratbag_hidraw_get_keycode_from_consumer_usage(device, profile->buttons[button->index].consumer_control.consumer_control); break; } break; case HIDPP20_BUTTON_SPECIAL: 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: rc = hidpp20drv_read_macro_8100(button, profile, &profile->buttons[button->index]); if (rc) button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE; break; default: button->action.type = RATBAG_BUTTON_ACTION_TYPE_UNKNOWN; } ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON); ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY); ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL); ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO); }
static void hidpp20drv_read_profile(struct ratbag_profile *profile, unsigned int index) { struct ratbag_device *device = profile->device; struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) return hidpp20drv_read_profile_8100(profile, index); hidpp20drv_read_resolution_dpi(profile); hidpp20drv_read_special_key_mouse(device); profile->is_active = (index == 0); }
static int hidpp20drv_write_button(struct ratbag_button *button, const struct ratbag_button_action *action) { struct ratbag_device *device = button->profile->device; struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) return hidpp20drv_write_button_8100(button, action); if (drv_data->capabilities & HIDPP_CAP_BUTTON_KEY_1b04) return hidpp20drv_write_button_1b04(button, action); return -ENOTSUP; }
static int hidpp20drv_current_profile(struct ratbag_device *device) { struct hidpp20drv_data *drv_data = ratbag_get_drv_data((struct ratbag_device *)device); int rc; if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)) return 0; rc = hidpp20_onboard_profiles_get_current_profile(drv_data->dev); if (rc < 0) return rc; return rc - 1; }
static int hidpp20drv_read_onboard_profile(struct ratbag_device *device, unsigned index) { struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); int rc; if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)) return 0; rc = hidpp20_onboard_profiles_read(drv_data->dev, index, drv_data->profiles); if (rc < 0) return rc; return 0; }
static int hidpp20drv_write_resolution_dpi(struct ratbag_resolution *resolution, int dpi_x, int dpi_y) { struct ratbag_profile *profile = resolution->profile; struct ratbag_device *device = profile->device; struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct hidpp20_sensor *sensor; int rc, i; int dpi = dpi_x; /* dpi_x == dpi_y if we don't have the individual resolution cap */ if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) return hidpp20drv_write_resolution_dpi_8100(resolution, dpi_x, dpi_y); if (!(drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201)) return -ENOTSUP; if (!drv_data->num_sensors) return -ENOTSUP; /* just for clarity, we use the first available sensor only */ sensor = &drv_data->sensors[0]; /* validate that the sensor accepts the given DPI */ rc = -EINVAL; if (dpi < sensor->dpi_min || dpi > sensor->dpi_max) goto out; if (sensor->dpi_steps) { for (i = sensor->dpi_min; i < dpi; i += sensor->dpi_steps) { } if (i != dpi) goto out; } else { i = 0; while (sensor->dpi_list[i]) { if (sensor->dpi_list[i] == dpi) break; } if (sensor->dpi_list[i] != dpi) goto out; } rc = hidpp20_adjustable_dpi_set_sensor_dpi(drv_data->dev, sensor, dpi); out: return rc; }
static int hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile) { struct ratbag_device *device = profile->device; struct ratbag *ratbag = device->ratbag; struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct ratbag_resolution *res; int rc; unsigned int i; if (drv_data->capabilities & HIDPP_CAP_RESOLUTION_2200) { uint16_t resolution; uint8_t flags; rc = hidpp20_mousepointer_get_mousepointer_info(drv_data->dev, &resolution, &flags); if (rc) { log_error(ratbag, "Error while requesting resolution: %s (%d)\n", strerror(-rc), rc); return rc; } return 0; } if (drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201) { rc = hidpp20drv_read_resolution_dpi_2201(device); if (rc < 0) return rc; for (i = 0; i < profile->resolution.num_modes; i++) { int dpi = drv_data->sensors[i].dpi; /* FIXME: retrieve the refresh rate */ res = ratbag_resolution_init(profile, i, dpi, dpi, 0); /* FIXME: we mark all resolutions as active because * they are from different sensors */ res->is_active = true; } return 0; } return 0; }
static void hidpp20drv_remove(struct ratbag_device *device) { struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct hidpp20_device *dev = drv_data->dev; if (!device) return; ratbag_close_hidraw(device); if (drv_data->profiles) hidpp20_onboard_profiles_destroy(dev, drv_data->profiles); free(drv_data->controls); free(drv_data->sensors); if (drv_data->dev) hidpp20_device_destroy(drv_data->dev); free(drv_data); }
static int hidpp20drv_write_resolution_dpi_8100(struct ratbag_resolution *resolution, int dpi_x, int dpi_y) { struct ratbag_profile *profile = resolution->profile; struct ratbag_device *device = profile->device; struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct hidpp20_profile *h_profile; unsigned int index; int dpi = dpi_x; /* dpi_x == dpi_y if we don't have the individual resolution cap */ /* retrieve which resolution is asked to be changed */ index = resolution - profile->resolution.modes; h_profile = &drv_data->profiles->profiles[profile->index]; h_profile->dpi[index] = dpi; return hidpp20_onboard_profiles_write(drv_data->dev, profile->index, drv_data->profiles); }
static int hidpp20drv_read_kbd_reprogrammable_key(struct ratbag_device *device) { struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); int rc; if (!(drv_data->capabilities & HIDPP_CAP_KBD_REPROGRAMMABLE_KEYS_1b00)) return 0; free(drv_data->controls); drv_data->controls = NULL; drv_data->num_controls = 0; rc = hidpp20_kbd_reprogrammable_keys_get_controls(drv_data->dev, &drv_data->controls); if (rc > 0) { drv_data->num_controls = rc; rc = 0; } return rc; }
static int hidpp20drv_read_special_key_mouse(struct ratbag_device *device) { struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); int rc; if (!(drv_data->capabilities & HIDPP_CAP_BUTTON_KEY_1b04)) return 0; free(drv_data->controls); drv_data->controls = NULL; drv_data->num_controls = 0; rc = hidpp20_special_key_mouse_get_controls(drv_data->dev, &drv_data->controls); if (rc > 0) { drv_data->num_controls = rc; rc = 0; } return rc; }
static int hidpp20drv_set_current_profile(struct ratbag_device *device, unsigned int index) { struct hidpp20drv_data *drv_data = ratbag_get_drv_data((struct ratbag_device *)device); struct hidpp20_profile *h_profile; int rc; if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)) return 0; if (index >= drv_data->num_profiles) return -EINVAL; h_profile = &drv_data->profiles->profiles[index]; if (!h_profile->enabled) { rc = hidpp20_onboard_profiles_write(drv_data->dev, index, drv_data->profiles); if (rc) return rc; } return hidpp20_onboard_profiles_set_current_profile(drv_data->dev, index); }
static void hidpp20drv_read_profile_8100(struct ratbag_profile *profile, unsigned int index) { struct ratbag_device *device = profile->device; struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct ratbag_resolution *res; struct hidpp20_profile *p; unsigned i, dpi = 0; hidpp20drv_read_onboard_profile(device, profile->index); profile->is_active = false; if ((int)index == hidpp20drv_current_profile(device)) profile->is_active = true; /* retrieve the resolution through 220X as the profile doesn't has it */ if (profile->is_active) hidpp20drv_read_resolution_dpi(profile); dpi = ratbag_resolution_get_dpi(profile->resolution.modes); p = &drv_data->profiles->profiles[index]; for (i = 0; i < profile->resolution.num_modes; i++) { res = ratbag_resolution_init(profile, i, p->dpi[i], p->dpi[i], p->report_rate); if (profile->is_active && res->dpi_x == dpi) res->is_active = true; if (i == p->default_dpi) res->is_default = true; } }
static int hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature) { struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device); struct ratbag *ratbag = device->ratbag; int rc; uint8_t feature_index, feature_type, feature_version; rc = hidpp_root_get_feature(drv_data->dev, feature, &feature_index, &feature_type, &feature_version); switch (feature) { case HIDPP_PAGE_ROOT: case HIDPP_PAGE_FEATURE_SET: /* these features are mandatory and already handled */ break; case HIDPP_PAGE_MOUSE_POINTER_BASIC: { drv_data->capabilities |= HIDPP_CAP_RESOLUTION_2200; break; } case HIDPP_PAGE_ADJUSTABLE_DPI: { log_debug(ratbag, "device has adjustable dpi\n"); /* we read the profile once to get the correct number of * supported resolutions. */ rc = hidpp20drv_read_resolution_dpi_2201(device); if (rc < 0) return 0; /* this is not a hard failure */ ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION); drv_data->capabilities |= HIDPP_CAP_SWITCHABLE_RESOLUTION_2201; break; } case HIDPP_PAGE_SPECIAL_KEYS_BUTTONS: { log_debug(ratbag, "device has programmable keys/buttons\n"); drv_data->capabilities |= HIDPP_CAP_BUTTON_KEY_1b04; ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_KEY); /* we read the profile once to get the correct number of * supported buttons. */ if (!hidpp20drv_read_special_key_mouse(device)) device->num_buttons = drv_data->num_controls; break; } case HIDPP_PAGE_BATTERY_LEVEL_STATUS: { uint16_t level, next_level; enum hidpp20_battery_status status; rc = hidpp20_batterylevel_get_battery_level(drv_data->dev, &level, &next_level); if (rc < 0) return rc; status = rc; log_debug(ratbag, "device battery level is %d%% (next %d%%), status %d \n", level, next_level, status); drv_data->capabilities |= HIDPP_CAP_BATTERY_LEVEL_1000; break; } case HIDPP_PAGE_KBD_REPROGRAMMABLE_KEYS: { log_debug(ratbag, "device has programmable keys/buttons\n"); drv_data->capabilities |= HIDPP_CAP_KBD_REPROGRAMMABLE_KEYS_1b00; /* we read the profile once to get the correct number of * supported buttons. */ if (!hidpp20drv_read_kbd_reprogrammable_key(device)) device->num_buttons = drv_data->num_controls; break; } case HIDPP_PAGE_ADJUSTABLE_REPORT_RATE: { log_debug(ratbag, "device has adjustable report rate\n"); break; } case HIDPP_PAGE_COLOR_LED_EFFECTS: { log_debug(ratbag, "device has color effects\n"); break; } case HIDPP_PAGE_ONBOARD_PROFILES: { log_debug(ratbag, "device has onboard profiles\n"); drv_data->capabilities |= HIDPP_CAP_ONBOARD_PROFILES_8100; rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles); if (rc < 0) return rc; drv_data->num_profiles = drv_data->profiles->num_profiles; drv_data->num_resolutions = drv_data->profiles->num_modes; drv_data->num_buttons = drv_data->profiles->num_buttons; break; } case HIDPP_PAGE_MOUSE_BUTTON_SPY: { log_debug(ratbag, "device has configurable mouse button spy\n"); break; } default: log_raw(device->ratbag, "unknown feature 0x%04x\n", feature); } return 0; }