int hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg) { struct ratbag_device *device = dev->ratbag_device; struct ratbag *ratbag = device->ratbag; union hidpp10_message read_buffer; union hidpp10_message expected_header; union hidpp10_message expected_error_dev = ERROR_MSG(msg, msg->msg.device_idx); int ret; uint8_t hidpp_err = 0; /* create the expected header */ expected_header = *msg; switch (msg->msg.sub_id) { case SET_REGISTER_REQ: expected_header.msg.report_id = REPORT_ID_SHORT; break; case GET_REGISTER_REQ: expected_header.msg.report_id = REPORT_ID_SHORT; break; case SET_LONG_REGISTER_REQ: expected_header.msg.report_id = REPORT_ID_LONG; break; case GET_LONG_REGISTER_REQ: expected_header.msg.report_id = REPORT_ID_LONG; break; } log_buf_raw(ratbag, "sending: ", msg->data, SHORT_MESSAGE_LENGTH); log_buf_raw(ratbag, " expected_header: ", expected_header.data, SHORT_MESSAGE_LENGTH); log_buf_raw(ratbag, " expected_error_dev: ", expected_error_dev.data, SHORT_MESSAGE_LENGTH); /* Send the message to the Device */ ret = hidpp10_write_command(dev, msg->data, SHORT_MESSAGE_LENGTH); if (ret) goto out_err; /* * Now read the answers from the device: * loop until we get the actual answer or an error code. */ do { ret = ratbag_hidraw_read_input_report(device, read_buffer.data, LONG_MESSAGE_LENGTH); /* Overwrite the return device index with ours. The kernel * sets our device index on write, but gives us the real * device index on reply. Overwrite it with our index so the * messages are easier to check and compare. */ read_buffer.msg.device_idx = msg->msg.device_idx; log_buf_raw(ratbag, " *** received: ", read_buffer.data, ret); /* actual answer */ if (!memcmp(read_buffer.data, expected_header.data, 4)) break; /* error */ if (!memcmp(read_buffer.data, expected_error_dev.data, 5)) { hidpp_err = read_buffer.msg.parameters[1]; log_raw(ratbag, " HID++ error from the %s (%d): %s (%02x)\n", read_buffer.msg.device_idx == RECEIVER_IDX ? "receiver" : "device", read_buffer.msg.device_idx, hidpp_errors[hidpp_err] ? hidpp_errors[hidpp_err] : "Undocumented error code", hidpp_err); break; } } while (ret > 0); if (ret < 0) { log_error(ratbag, " USB error: %s (%d)\n", strerror(-ret), -ret); perror("write"); goto out_err; } if (!hidpp_err) { log_buf_raw(ratbag, " received: ", read_buffer.data, ret); /* copy the answer for the caller */ *msg = read_buffer; } ret = hidpp_err; out_err: return ret; }
static int hidpp20_request_command_allow_error(struct ratbag_device *device, union hidpp20_message *msg, bool allow_error) { struct ratbag *ratbag = device->ratbag; union hidpp20_message read_buffer; int ret; uint8_t hidpp_err = 0; size_t msg_len; /* msg->address is 4 MSB: subcommand, 4 LSB: 4-bit SW identifier so * the device knows who to respond to. kernel uses 0x1 */ const int DEVICE_SW_ID = 0x8; if (msg->msg.address & 0xf) { log_bug_libratbag(ratbag, "hidpp20 error: sw address is already set\n"); return -EINVAL; } msg->msg.address |= DEVICE_SW_ID; msg_len = msg->msg.report_id == REPORT_ID_SHORT ? SHORT_MESSAGE_LENGTH : LONG_MESSAGE_LENGTH; log_buf_raw(ratbag, "sending: ", msg->data, msg_len); /* Send the message to the Device */ ret = hidpp20_write_command(device, msg->data, msg_len); if (ret) goto out_err; /* * Now read the answers from the device: * loop until we get the actual answer or an error code. */ do { ret = ratbag_hidraw_read_input_report(device, read_buffer.data, LONG_MESSAGE_LENGTH); /* Wait and retry if the USB timed out */ if (ret == -ETIMEDOUT) { msleep(10); ret = ratbag_hidraw_read_input_report(device, read_buffer.data, LONG_MESSAGE_LENGTH); } log_buf_raw(ratbag, " *** received: ", read_buffer.data, ret > 0 ? ret : 0); if (read_buffer.msg.report_id != REPORT_ID_SHORT && read_buffer.msg.report_id != REPORT_ID_LONG) continue; /* actual answer */ if (read_buffer.msg.sub_id == msg->msg.sub_id && read_buffer.msg.address == msg->msg.address) break; /* error */ if ((read_buffer.msg.sub_id == __ERROR_MSG || read_buffer.msg.sub_id == 0xff) && read_buffer.msg.address == msg->msg.sub_id && read_buffer.msg.parameters[0] == msg->msg.address) { hidpp_err = read_buffer.msg.parameters[1]; if (allow_error) log_debug(ratbag, " HID++ error from the device (%d): %s (%02x)\n", read_buffer.msg.device_idx, hidpp_errors[hidpp_err] ? hidpp_errors[hidpp_err] : "Undocumented error code", hidpp_err); else log_error(ratbag, " HID++ error from the device (%d): %s (%02x)\n", read_buffer.msg.device_idx, hidpp_errors[hidpp_err] ? hidpp_errors[hidpp_err] : "Undocumented error code", hidpp_err); break; } } while (ret > 0); if (ret < 0) { log_error(ratbag, " USB error: %s (%d)\n", strerror(-ret), -ret); perror("write"); goto out_err; } if (!hidpp_err) { /* copy the answer for the caller */ *msg = read_buffer; } ret = hidpp_err; out_err: return ret; }
int hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_profile *profile_return) { struct ratbag *ratbag = dev->ratbag_device->ratbag; union _hidpp10_profile_data data; struct _hidpp10_profile *p = &data.profile; size_t i; int res; struct hidpp10_profile profile; /* FIXME: profile offset appears to be 3, 1 and 2 are garbage */ number += 3; log_raw(dev->ratbag_device->ratbag, "Fetching profile %d\n", number); for (i = 0; i < sizeof(data); i += 16) { res = hidpp10_read_memory(dev, number, i, &data.data[i]); if (res) return res; } profile.angle_correction = p->angle_correction; profile.default_dpi_mode = p->default_dpi_mode; profile.refresh_rate = p->usb_refresh_rate ? 1000/p->usb_refresh_rate : 0; profile.num_dpi_modes = PROFILE_NUM_DPI_MODES; for (i = 0; i < PROFILE_NUM_DPI_MODES; i++) { uint8_t *be; /* in big endian */ struct _hidpp10_dpi_mode *dpi = &p->dpi_modes[i]; be = (uint8_t*)&dpi->xres; profile.dpi_modes[i].xres = hidpp10_get_unaligned_u16(be) * 50; be = (uint8_t*)&dpi->yres; profile.dpi_modes[i].yres = hidpp10_get_unaligned_u16(be) * 50; profile.dpi_modes[i].led[0] = dpi->led1 == 0x2; profile.dpi_modes[i].led[1] = dpi->led2 == 0x2; profile.dpi_modes[i].led[2] = dpi->led3 == 0x2; profile.dpi_modes[i].led[3] = dpi->led4 == 0x2; } profile.num_buttons = PROFILE_NUM_BUTTONS; for (i = 0; i < PROFILE_NUM_BUTTONS; i++) { union _hidpp10_button_binding *b = &p->buttons[i]; union hidpp10_button *button = &profile.buttons[i]; button->any.type = b->any.type; switch (b->any.type) { case PROFILE_BUTTON_TYPE_BUTTON: button->button.button = ffs(hidpp10_get_unaligned_u16le(&b->button.button_flags_lsb)); break; case PROFILE_BUTTON_TYPE_KEYS: button->keys.modifier_flags = b->keyboard_keys.modifier_flags; button->keys.key = b->keyboard_keys.key; break; case PROFILE_BUTTON_TYPE_SPECIAL: button->special.special = ffs(hidpp10_get_unaligned_u16le(&b->special.flags1)); break; case PROFILE_BUTTON_TYPE_CONSUMER_CONTROL: button->consumer_control.consumer_control = hidpp10_get_unaligned_u16(&b->consumer_control.consumer_control1); break; case PROFILE_BUTTON_TYPE_DISABLED: break; } } log_buf_raw(ratbag, "+++++++++++++++++++ Profile data: +++++++++++++++++ \n", data.data, 78); log_raw(ratbag, "Profile %d:\n", number); for (i = 0; i < 5; i++) { log_raw(ratbag, "DPI mode: %dx%d dpi\n", profile.dpi_modes[i].xres, profile.dpi_modes[i].yres); log_raw(ratbag, "LED status: 1:%s 2:%s 3:%s 4:%s\n", (p->dpi_modes[i].led1 & 0x2) ? "on" : "off", (p->dpi_modes[i].led2 & 0x2) ? "on" : "off", (p->dpi_modes[i].led3 & 0x2) ? "on" : "off", (p->dpi_modes[i].led4 & 0x2) ? "on" : "off"); } log_raw(ratbag, "Angle correction: %d\n", profile.angle_correction); log_raw(ratbag, "Default DPI mode: %d\n", profile.default_dpi_mode); log_raw(ratbag, "Refresh rate: %d\n", profile.refresh_rate); for (i = 0; i < 13; i++) { union _hidpp10_button_binding *button = &p->buttons[i]; switch (button->any.type) { case 0x81: log_raw(ratbag, "Button %d: button %d\n", i, ffs(hidpp10_get_unaligned_u16le(&button->button.button_flags_lsb))); break; case 0x82: log_raw(ratbag, "Button %d: key %d modifier %x\n", i, button->keyboard_keys.key, button->keyboard_keys.modifier_flags); break; case 0x83: log_raw(ratbag, "Button %d: special %x\n", i, ffs(hidpp10_get_unaligned_u16le(&button->special.flags1))); break; case 0x84: log_raw(ratbag, "Button %d: consumer: %x\n", i, hidpp10_get_unaligned_u16(&button->consumer_control.consumer_control1)); break; case 0x8F: log_raw(ratbag, "Button %d: disabled\n", i); break; default: /* FIXME: this is the page number for the macro, * followed by a 1-byte offset */ break ; } } *profile_return = profile; return 0; }