int hidpp20_adjustable_dpi_set_sensor_dpi(struct ratbag_device *device, struct hidpp20_sensor *sensor, uint16_t dpi) { uint8_t feature_index, feature_type, feature_version; int rc; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.address = CMD_ADJUSTABLE_DPI_SET_SENSOR_DPI, .msg.parameters[0] = sensor->index, .msg.parameters[1] = dpi >> 8, .msg.parameters[2] = dpi & 0xff, }; rc = hidpp_root_get_feature(device, HIDPP_PAGE_ADJUSTABLE_DPI, &feature_index, &feature_type, &feature_version); if (rc) return rc; msg.msg.sub_id = feature_index; rc = hidpp20_request_command(device, &msg); if (rc) return rc; if (hidpp20_get_unaligned_u16(&msg.msg.parameters[1]) != dpi) return -EIO; return 0; }
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; }
int hidpp20_batterylevel_get_battery_level(struct ratbag_device *device, uint16_t *level, uint16_t *next_level) { uint8_t feature_index, feature_type, feature_version; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.address = CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS, }; int rc; rc = hidpp_root_get_feature(device, HIDPP_PAGE_BATTERY_LEVEL_STATUS, &feature_index, &feature_type, &feature_version); if (rc) return rc; msg.msg.sub_id = feature_index; rc = hidpp20_request_command(device, &msg); if (rc) return rc; *level = msg.msg.parameters[0]; *next_level = msg.msg.parameters[1]; return msg.msg.parameters[2]; } /* -------------------------------------------------------------------------- */ /* 0x1b00: KBD reprogrammable keys and mouse buttons */ /* -------------------------------------------------------------------------- */ #define CMD_KBD_REPROGRAMMABLE_KEYS_GET_COUNT 0x00 #define CMD_KBD_REPROGRAMMABLE_KEYS_GET_CTRL_ID_INFO 0x10 static int hidpp20_kbd_reprogrammable_keys_get_count(struct ratbag_device *device, uint8_t reg) { union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = reg, .msg.address = CMD_KBD_REPROGRAMMABLE_KEYS_GET_COUNT, }; int rc; rc = hidpp20_request_command(device, &msg); if (rc) return rc; return msg.msg.parameters[0]; } static int hidpp20_kbd_reprogrammable_keys_get_info(struct ratbag_device *device, uint8_t reg, struct hidpp20_control_id *control) { int rc; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = reg, .msg.address = CMD_KBD_REPROGRAMMABLE_KEYS_GET_CTRL_ID_INFO, .msg.parameters[0] = control->index, }; rc = hidpp20_request_command(device, &msg); if (rc) return rc; control->control_id = hidpp20_get_unaligned_u16(&msg.msg.parameters[0]); control->task_id = hidpp20_get_unaligned_u16(&msg.msg.parameters[2]); control->flags = msg.msg.parameters[4]; return 0; } int hidpp20_kbd_reprogrammable_keys_get_controls(struct ratbag_device *device, struct hidpp20_control_id **controls_list) { uint8_t feature_index, feature_type, feature_version; struct hidpp20_control_id *c_list, *control; uint8_t num_controls; unsigned i; int rc; rc = hidpp_root_get_feature(device, HIDPP_PAGE_KBD_REPROGRAMMABLE_KEYS, &feature_index, &feature_type, &feature_version); if (rc) return rc; rc = hidpp20_kbd_reprogrammable_keys_get_count(device, feature_index); if (rc < 0) return rc; num_controls = rc; if (num_controls == 0) { *controls_list = NULL; return 0; } c_list = zalloc(num_controls * sizeof(struct hidpp20_control_id)); for (i = 0; i < num_controls; i++) { control = &c_list[i]; control->index = i; rc = hidpp20_kbd_reprogrammable_keys_get_info(device, feature_index, control); if (rc) goto err; /* 0x1b00 and 0x1b04 have the same control/task id mappings. * I hope */ log_raw(device->ratbag, "control %d: cid: '%s' (%d) tid: '%s' (%d) flags: 0x%02x\n", control->index, hidpp20_1b04_get_logical_mapping_name(control->control_id), control->control_id, hidpp20_1b04_get_physical_mapping_name(control->task_id), control->task_id, control->flags); } *controls_list = c_list; return num_controls; err: free(c_list); return rc; } /* -------------------------------------------------------------------------- */ /* 0x1b04: Special keys and mouse buttons */ /* -------------------------------------------------------------------------- */ #define CMD_SPECIAL_KEYS_BUTTONS_GET_COUNT 0x00 #define CMD_SPECIAL_KEYS_BUTTONS_GET_INFO 0x10 #define CMD_SPECIAL_KEYS_BUTTONS_GET_REPORTING 0x20 #define CMD_SPECIAL_KEYS_BUTTONS_SET_REPORTING 0x30 static int hidpp20_special_keys_buttons_get_count(struct ratbag_device *device, uint8_t reg) { int rc; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = reg, .msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_COUNT, }; rc = hidpp20_request_command(device, &msg); if (rc) return rc; return msg.msg.parameters[0]; } static int hidpp20_special_keys_buttons_get_info(struct ratbag_device *device, uint8_t reg, struct hidpp20_control_id *control) { int rc; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = reg, .msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_INFO, .msg.parameters[0] = control->index, }; rc = hidpp20_request_command(device, &msg); if (rc) return rc; control->control_id = hidpp20_get_unaligned_u16(&msg.msg.parameters[0]); control->task_id = hidpp20_get_unaligned_u16(&msg.msg.parameters[2]); control->flags = msg.msg.parameters[4]; control->position = msg.msg.parameters[5]; control->group = msg.msg.parameters[6]; control->group_mask = msg.msg.parameters[7]; control->raw_XY = msg.msg.parameters[8] & 0x01; return 0; } static int hidpp20_special_keys_buttons_get_reporting(struct ratbag_device *device, uint8_t reg, struct hidpp20_control_id *control) { int rc; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = reg, .msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_REPORTING, .msg.parameters[0] = control->control_id >> 8, .msg.parameters[1] = control->control_id & 0xff, }; rc = hidpp20_request_command(device, &msg); if (rc) return rc; control->reporting.remapped = hidpp20_get_unaligned_u16(&msg.msg.parameters[3]); control->reporting.raw_XY = !!(msg.msg.parameters[2] & 0x10); control->reporting.persist = !!(msg.msg.parameters[2] & 0x04); control->reporting.divert = !!(msg.msg.parameters[2] & 0x01); return 0; } int hidpp20_special_key_mouse_get_controls(struct ratbag_device *device, struct hidpp20_control_id **controls_list) { uint8_t feature_index, feature_type, feature_version; struct hidpp20_control_id *c_list, *control; uint8_t num_controls; unsigned i; int rc; rc = hidpp_root_get_feature(device, HIDPP_PAGE_SPECIAL_KEYS_BUTTONS, &feature_index, &feature_type, &feature_version); if (rc) return rc; rc = hidpp20_special_keys_buttons_get_count(device, feature_index); if (rc < 0) return rc; num_controls = rc; if (num_controls == 0) { *controls_list = NULL; return 0; } c_list = zalloc(num_controls * sizeof(struct hidpp20_control_id)); for (i = 0; i < num_controls; i++) { control = &c_list[i]; control->index = i; rc = hidpp20_special_keys_buttons_get_info(device, feature_index, control); if (rc) goto err; rc = hidpp20_special_keys_buttons_get_reporting(device, feature_index, control); if (rc) goto err; log_raw(device->ratbag, "control %d: cid: '%s' (%d) tid: '%s' (%d) flags: 0x%02x pos: %d group: %d gmask: 0x%02x raw_XY: %s\n" " reporting: raw_xy: %s persist: %s divert: %s remapped: '%s' (%d)\n", control->index, hidpp20_1b04_get_logical_mapping_name(control->control_id), control->control_id, hidpp20_1b04_get_physical_mapping_name(control->task_id), control->task_id, control->flags, control->position, control->group, control->group_mask, control->raw_XY ? "yes" : "no", control->reporting.raw_XY ? "yes" : "no", control->reporting.persist ? "yes" : "no", control->reporting.divert ? "yes" : "no", hidpp20_1b04_get_logical_mapping_name(control->reporting.remapped), control->reporting.remapped); } *controls_list = c_list; return num_controls; err: free(c_list); return rc; }
int hidpp20_special_key_mouse_set_control(struct ratbag_device *device, struct hidpp20_control_id *control) { uint8_t feature_index, feature_type, feature_version; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.address = CMD_SPECIAL_KEYS_BUTTONS_SET_REPORTING, .msg.parameters[0] = control->control_id >> 8, .msg.parameters[1] = control->control_id & 0xff, .msg.parameters[2] = 0x00, .msg.parameters[3] = control->reporting.remapped >> 8, .msg.parameters[4] = control->reporting.remapped & 0xff, }; int rc; rc = hidpp_root_get_feature(device, HIDPP_PAGE_SPECIAL_KEYS_BUTTONS, &feature_index, &feature_type, &feature_version); if (rc) return rc; msg.msg.sub_id = feature_index; if (control->reporting.divert) msg.msg.parameters[2] |= 0x03; if (control->reporting.persist) msg.msg.parameters[2] |= 0x0c; if (control->reporting.raw_XY) msg.msg.parameters[2] |= 0x20; return hidpp20_request_command(device, &msg); } /* -------------------------------------------------------------------------- */ /* 0x2200: Mouse Pointer Basic Optical Sensors */ /* -------------------------------------------------------------------------- */ #define CMD_MOUSE_POINTER_BASIC_GET_INFO 0x00 int hidpp20_mousepointer_get_mousepointer_info(struct ratbag_device *device, uint16_t *resolution, uint8_t *flags) { uint8_t feature_index, feature_type, feature_version; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.address = CMD_MOUSE_POINTER_BASIC_GET_INFO, .msg.parameters[0] = feature_index, }; int rc; rc = hidpp_root_get_feature(device, HIDPP_PAGE_MOUSE_POINTER_BASIC, &feature_index, &feature_type, &feature_version); if (rc) return rc; msg.msg.sub_id = feature_index; rc = hidpp20_request_command(device, &msg); if (rc) return rc; *resolution = hidpp20_get_unaligned_u16(msg.msg.parameters); *flags = msg.msg.parameters[2]; return 0; } /* -------------------------------------------------------------------------- */ /* 0x2201: Adjustable DPI */ /* -------------------------------------------------------------------------- */ #define CMD_ADJUSTABLE_DPI_GET_SENSOR_COUNT 0x00 #define CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI_LIST 0x10 #define CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI 0x20 #define CMD_ADJUSTABLE_DPI_SET_SENSOR_DPI 0x30 static int hidpp20_adjustable_dpi_get_count(struct ratbag_device *device, uint8_t reg) { int rc; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = reg, .msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_COUNT, }; rc = hidpp20_request_command(device, &msg); if (rc) return rc; return msg.msg.parameters[0]; } static int hidpp20_adjustable_dpi_get_dpi_list(struct ratbag_device *device, uint8_t reg, struct hidpp20_sensor *sensor) { int rc; unsigned i = 1, dpi_index = 0; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = reg, .msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI_LIST, .msg.parameters[0] = sensor->index, }; rc = hidpp20_request_command(device, &msg); if (rc) return rc; sensor->dpi_min = 0xffff; sensor->index = msg.msg.parameters[0]; while (i < LONG_MESSAGE_LENGTH - 4U && hidpp20_get_unaligned_u16(&msg.msg.parameters[i]) != 0) { uint16_t value = hidpp20_get_unaligned_u16(&msg.msg.parameters[i]); if (value > 0xe000) { sensor->dpi_steps = value - 0xe000; } else { sensor->dpi_min = value < sensor->dpi_min ? value : sensor->dpi_min; sensor->dpi_max = value > sensor->dpi_max ? value : sensor->dpi_max; sensor->dpi_list[dpi_index++] = value; } assert(sensor->dpi_list[dpi_index] == 0x0000); i += 2; } return 0; } static int hidpp20_adjustable_dpi_get_dpi(struct ratbag_device *device, uint8_t reg, struct hidpp20_sensor *sensor) { int rc; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = reg, .msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI, .msg.parameters[0] = sensor->index, }; rc = hidpp20_request_command(device, &msg); if (rc) return rc; sensor->dpi = hidpp20_get_unaligned_u16(&msg.msg.parameters[1]); sensor->default_dpi = hidpp20_get_unaligned_u16(&msg.msg.parameters[3]); return 0; } int hidpp20_adjustable_dpi_get_sensors(struct ratbag_device *device, struct hidpp20_sensor **sensors_list) { uint8_t feature_index, feature_type, feature_version; struct hidpp20_sensor *s_list, *sensor; uint8_t num_sensors; unsigned i; int rc; rc = hidpp_root_get_feature(device, HIDPP_PAGE_ADJUSTABLE_DPI, &feature_index, &feature_type, &feature_version); if (rc) return rc; rc = hidpp20_adjustable_dpi_get_count(device, feature_index); if (rc < 0) return rc; num_sensors = rc; if (num_sensors == 0) { *sensors_list = NULL; return 0; } s_list = zalloc(num_sensors * sizeof(struct hidpp20_sensor)); for (i = 0; i < num_sensors; i++) { sensor = &s_list[i]; sensor->index = i; rc = hidpp20_adjustable_dpi_get_dpi_list(device, feature_index, sensor); if (rc) goto err; rc = hidpp20_adjustable_dpi_get_dpi(device, feature_index, sensor); if (rc) goto err; log_raw(device->ratbag, "sensor %d: current dpi: %d (default: %d) min: %d max: %d steps: %d\n", sensor->index, sensor->dpi, sensor->default_dpi, sensor->dpi_min, sensor->dpi_max, sensor->dpi_steps); } *sensors_list = s_list; return num_sensors; err: free(s_list); return rc; }
int hidpp_root_get_feature(struct ratbag_device *device, uint16_t feature, uint8_t *feature_index, uint8_t *feature_type, uint8_t *feature_version) { int rc; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = HIDPP_PAGE_ROOT_IDX, .msg.address = CMD_ROOT_GET_FEATURE, .msg.parameters[0] = feature >> 8, .msg.parameters[1] = feature & 0xff, }; rc = hidpp20_request_command(device, &msg); if (rc) return rc; *feature_index = msg.msg.parameters[0]; *feature_type = msg.msg.parameters[1]; *feature_version = msg.msg.parameters[2]; log_raw(device->ratbag, "feature 0x%04x is at 0x%02x\n", feature, *feature_index); return 0; } int hidpp20_root_get_protocol_version(struct ratbag_device *device, unsigned *major, unsigned *minor) { int rc; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = HIDPP_PAGE_ROOT_IDX, .msg.address = CMD_ROOT_GET_PROTOCOL_VERSION, }; rc = hidpp20_request_command_allow_error(device, &msg, true); if (rc == ERR_INVALID_SUBID) { *major = 1; *minor = 0; return 0; } if (rc == 0) { *major = msg.msg.parameters[0]; *minor = msg.msg.parameters[1]; } return rc; } /* -------------------------------------------------------------------------- */ /* 0x0001: Feature Set */ /* -------------------------------------------------------------------------- */ #define CMD_FEATURE_SET_GET_COUNT 0x00 #define CMD_FEATURE_SET_GET_FEATURE_ID 0x10 static int hidpp20_feature_set_get_count(struct ratbag_device *device, uint8_t reg) { int rc; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = reg, .msg.address = CMD_FEATURE_SET_GET_COUNT, }; rc = hidpp20_request_command(device, &msg); if (rc) return rc; return msg.msg.parameters[0]; } static int hidpp20_feature_set_get_feature_id(struct ratbag_device *device, uint8_t reg, uint8_t feature_index, uint16_t *feature, uint8_t *type) { int rc; union hidpp20_message msg = { .msg.report_id = REPORT_ID_LONG, .msg.device_idx = 0xff, .msg.sub_id = reg, .msg.address = CMD_FEATURE_SET_GET_FEATURE_ID, .msg.parameters[0] = feature_index, }; rc = hidpp20_request_command(device, &msg); if (rc) return rc; *feature = hidpp20_get_unaligned_u16(msg.msg.parameters); *type = msg.msg.parameters[2]; return 0; } int hidpp20_feature_set_get(struct ratbag_device *device, struct hidpp20_feature **feature_list) { uint8_t feature_index, feature_type, feature_version; struct hidpp20_feature *flist; int rc; uint8_t feature_count; unsigned int i; rc = hidpp_root_get_feature(device, HIDPP_PAGE_FEATURE_SET, &feature_index, &feature_type, &feature_version); if (rc) return rc; rc = hidpp20_feature_set_get_count(device, feature_index); if (rc < 0) return rc; feature_count = (uint8_t)rc; if (!feature_count) { *feature_list = NULL; return 0; } flist = zalloc(feature_count * sizeof(struct hidpp20_feature)); for (i = 0; i < feature_count; i++) { rc = hidpp20_feature_set_get_feature_id(device, feature_index, i, &flist[i].feature, &flist[i].type); if (rc) goto err; } *feature_list = flist; return feature_count; err: free(flist); return rc; }