diff --git a/.gitignore b/.gitignore index 1e4fc308..1ef09442 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ test-context test-device test-*.log ratbag-command +hidpp10-dump-page diff --git a/src/Makefile.am b/src/Makefile.am index 19ae3e61..a2ed81c6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,5 @@ lib_LTLIBRARIES = libratbag.la +noinst_LTLIBRARIES = libhidpp10.la include_HEADERS = \ libratbag.h @@ -43,3 +44,11 @@ AM_CFLAGS = $(GCC_CFLAGS) DISTCLEANFILES = libratbag-version.h EXTRA_DIST = libratbag-version.h.in libratbag.sym + +# The hidpp libraries are static libraries only for in-tree tools. No API +# stability is guaranteed here. +libhidpp10_la_SOURCES = \ + hidpp-generic.h \ + hidpp-generic.c \ + hidpp10.h \ + hidpp10.c diff --git a/src/driver-hidpp10.c b/src/driver-hidpp10.c index b7f5d4c4..954cc631 100644 --- a/src/driver-hidpp10.c +++ b/src/driver-hidpp10.c @@ -197,12 +197,21 @@ hidpp10drv_test_hidraw(struct ratbag_device *device) return ratbag_hidraw_has_report(device, REPORT_ID_SHORT); } +static void +hidpp10_log(void *userdata, enum hidpp_log_priority priority, const char *format, va_list args) +{ + struct ratbag_device *device = userdata; + + log_msg_va(device->ratbag, priority, format, args); +} + static int hidpp10drv_probe(struct ratbag_device *device) { int rc; struct hidpp10drv_data *drv_data = NULL; struct hidpp10_device *dev = NULL; + struct hidpp_device base; rc = ratbag_find_hidraw(device, hidpp10drv_test_hidraw); if (rc == -ENODEV) { @@ -217,13 +226,15 @@ hidpp10drv_probe(struct ratbag_device *device) } drv_data = zalloc(sizeof(*drv_data)); + hidpp_device_init(&base, device->hidraw.fd); + hidpp_device_set_log_handler(&base, hidpp10_log, HIDPP_LOG_PRIORITY_RAW, device); /* We can treat all devices as wired devices here. If we talk to the * correct hidraw device the kernel adjusts the device index for us, * so even for unifying receiver devices we can just 0x00 as device * index. */ - dev = hidpp10_device_new_from_idx(device, WIRED_DEVICE_IDX); + dev = hidpp10_device_new_from_idx(&base, WIRED_DEVICE_IDX); if (!dev) { log_error(device->ratbag, diff --git a/src/hidpp-generic.c b/src/hidpp-generic.c index 15ee135c..f7c5f985 100644 --- a/src/hidpp-generic.c +++ b/src/hidpp-generic.c @@ -30,6 +30,8 @@ #include "hidpp-generic.h" +#include +#include #include #include "libratbag-private.h" @@ -201,3 +203,117 @@ hidpp20_1b04_get_physical_mapping_name(uint16_t value) return "UNKNOWN"; } +int +hidpp_write_command(struct hidpp_device *dev, uint8_t *cmd, int size) +{ + int fd = dev->hidraw_fd; + int res; + + if (size < 1 || !cmd || fd < 0) + return -EINVAL; + + hidpp_log_buf_raw(dev, "hidpp write: ", cmd, size); + res = write(fd, cmd, size); + if (res < 0) { + res = -errno; + hidpp_log_error(dev, "Error: %s (%d)\n", strerror(-res), -res); + } + + return res < 0 ? res : 0; +} + +int +hidpp_read_response(struct hidpp_device *dev, uint8_t *buf, size_t size) +{ + int fd = dev->hidraw_fd; + struct pollfd fds; + int rc; + + if (size < 1 || !buf || fd < 0) + return -EINVAL; + + fds.fd = fd; + fds.events = POLLIN; + + rc = poll(&fds, 1, 1000); + if (rc == -1) + return -errno; + + if (rc == 0) + return -ETIMEDOUT; + + rc = read(fd, buf, size); + + if (rc > 0) + hidpp_log_buf_raw(dev, "hidpp read: ", buf, rc); + + return rc >= 0 ? rc : -errno; +} + +void +hidpp_log(struct hidpp_device *dev, + enum hidpp_log_priority priority, + const char *format, + ...) +{ + va_list args; + + if (dev->log_priority > priority) + return; + + va_start(args, format); + dev->log_handler(dev->userdata, priority, format, args); + va_end(args); +} + +void +hidpp_log_buffer(struct hidpp_device *dev, + enum hidpp_log_priority priority, + const char *header, + uint8_t *buf, size_t len) +{ + _cleanup_free_ char *output_buf = NULL; + char *sep = ""; + unsigned int i, n; + unsigned int buf_len; + + buf_len = header ? strlen(header) : 0; + buf_len += len * 3; + buf_len += 1; /* terminating '\0' */ + + output_buf = zalloc(buf_len); + n = 0; + if (header) + n += snprintf_safe(output_buf, buf_len - n, "%s", header); + + for (i = 0; i < len; ++i) { + n += snprintf_safe(&output_buf[n], buf_len - n, "%s%02x", sep, buf[i] & 0xFF); + sep = " "; + } + + hidpp_log(dev, priority, "%s\n", output_buf); +} + +static void +simple_log(void *userdata, enum hidpp_log_priority priority, const char *format, va_list args) +{ + vprintf(format, args); +} + +void +hidpp_device_init(struct hidpp_device *dev, int fd) +{ + dev->hidraw_fd = fd; + hidpp_device_set_log_handler(dev, simple_log, HIDPP_LOG_PRIORITY_INFO, NULL); +} + +void +hidpp_device_set_log_handler(struct hidpp_device *dev, + hidpp_log_handler log_handler, + enum hidpp_log_priority priority, + void *userdata) +{ + dev->log_handler = log_handler; + dev->log_priority = priority; + dev->userdata = userdata; +} diff --git a/src/hidpp-generic.h b/src/hidpp-generic.h index 9d3908d7..4a321b63 100644 --- a/src/hidpp-generic.h +++ b/src/hidpp-generic.h @@ -27,6 +27,8 @@ */ #include +#include +#include #ifndef HIDPP_GENERIC_H #define HIDPP_GENERIC_H @@ -64,6 +66,38 @@ #define ERR_INVALID_PARAM_VALUE 0x0B #define ERR_WRONG_PIN_CODE 0x0C +/* Keep this in sync with ratbag_log_priority */ +enum hidpp_log_priority { + /** + * Raw protocol messages. Using this log level results in *a lot* of + * output. + */ + HIDPP_LOG_PRIORITY_RAW = 10, + HIDPP_LOG_PRIORITY_DEBUG = 20, + HIDPP_LOG_PRIORITY_INFO = 30, + HIDPP_LOG_PRIORITY_ERROR = 40, +}; + +typedef void (*hidpp_log_handler)(void *userdata, + enum hidpp_log_priority priority, + const char *format, va_list args) + __attribute__ ((format (printf, 3, 0))); + +struct hidpp_device { + int hidraw_fd; + void *userdata; + hidpp_log_handler log_handler; + enum hidpp_log_priority log_priority; +}; + +void +hidpp_device_init(struct hidpp_device *dev, int fd); +void +hidpp_device_set_log_handler(struct hidpp_device *dev, + hidpp_log_handler log_handler, + enum hidpp_log_priority priority, + void *userdata); + extern const char *hidpp_errors[0xFF]; const char * @@ -81,4 +115,33 @@ hidpp20_1b04_get_logical_mapping(uint16_t value); uint16_t hidpp20_1b04_get_logical_control_id(const struct ratbag_button_action *action); +int +hidpp_write_command(struct hidpp_device *dev, uint8_t *cmd, int size); + +int +hidpp_read_response(struct hidpp_device *dev, uint8_t *buf, size_t size); + +void +hidpp_log(struct hidpp_device *dev, + enum hidpp_log_priority priority, + const char *format, + ...) + __attribute__ ((format (printf, 3, 4))); + +void +hidpp_log_buffer(struct hidpp_device *dev, + enum hidpp_log_priority priority, + const char *header, + uint8_t *buf, size_t len); + +#define hidpp_log_raw(li_, ...) hidpp_log((li_), HIDPP_LOG_PRIORITY_RAW, __VA_ARGS__) +#define hidpp_log_debug(li_, ...) hidpp_log((li_), HIDPP_LOG_PRIORITY_DEBUG, __VA_ARGS__) +#define hidpp_log_info(li_, ...) hidpp_log((li_), HIDPP_LOG_PRIORITY_INFO, __VA_ARGS__) +#define hidpp_log_error(li_, ...) hidpp_log((li_), HIDPP_LOG_PRIORITY_ERROR, __VA_ARGS__) +#define hidpp_log_bug_kernel(li_, ...) hidpp((li_), HIDPP_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__) +#define hidpp_log_buf_raw(li_, h_, buf_, len_) hidpp_log_buffer(li_, HIDPP_LOG_PRIORITY_RAW, h_, buf_, len_) +#define hidpp_log_buf_debug(li_, h_, buf_, len_) hidpp_log_buffer(li_, HIDPP_LOG_PRIORITY_DEBUG, h_, buf_, len_) +#define hidpp_log_buf_info(li_, h_, buf_, len_) hidpp_log_buffer(li_, HIDPP_LOG_PRIORITY_INFO, h_, buf_, len_) +#define hidpp_log_buf_error(li_, h_, buf_, len_) hidpp_log_buffer(li_, HIDPP_LOG_PRIORITY_ERROR, h_, buf_, len_) + #endif /* HIDPP_GENERIC_H */ diff --git a/src/hidpp10.c b/src/hidpp10.c index 612aed49..0b93830e 100644 --- a/src/hidpp10.c +++ b/src/hidpp10.c @@ -35,13 +35,29 @@ #include #include #include +#include #include #include "hidpp10.h" #include "libratbag-util.h" #include "libratbag-hidraw.h" -#include "libratbag-private.h" + +struct _hidpp10_message { + uint8_t report_id; + uint8_t device_idx; + uint8_t sub_id; + uint8_t address; + union { + uint8_t parameters[3]; + uint8_t string[16]; + }; +} __attribute__((packed)); + +union hidpp10_message { + struct _hidpp10_message msg; + uint8_t data[LONG_MESSAGE_LENGTH]; +}; #define ERROR_MSG(__hidpp_msg, idx) { \ .msg = { \ @@ -87,25 +103,8 @@ const char *device_types[0xFF] = { }; static int -hidpp10_write_command(struct hidpp10_device *dev, uint8_t *cmd, int size) -{ - struct ratbag_device *device = dev->ratbag_device; - int res = ratbag_hidraw_output_report(device, cmd, size); - - if (res == 0) - return 0; - - if (res < 0) - log_error(device->ratbag, "Error: %s (%d)\n", strerror(-res), -res); - - return res; -} - -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); @@ -128,11 +127,11 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg) expected_header = *msg; /* response message length doesn't depend on request length */ - log_buf_raw(ratbag, " expected_header: ?? ", &expected_header.data[1], 3); - log_buf_raw(ratbag, " expected_error_dev: ", expected_error_dev.data, SHORT_MESSAGE_LENGTH); + hidpp_log_buf_raw(&dev->base, " expected_header: ?? ", &expected_header.data[1], 3); + hidpp_log_buf_raw(&dev->base, " expected_error_dev: ", expected_error_dev.data, SHORT_MESSAGE_LENGTH); /* Send the message to the Device */ - ret = hidpp10_write_command(dev, msg->data, command_size); + ret = hidpp_write_command(&dev->base, msg->data, command_size); if (ret) goto out_err; @@ -141,12 +140,12 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg) * 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); + ret = hidpp_read_response(&dev->base, 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); + ret = hidpp_read_response(&dev->base, read_buffer.data, LONG_MESSAGE_LENGTH); } /* Overwrite the return device index with ours. The kernel @@ -163,7 +162,7 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg) /* error */ if (!memcmp(read_buffer.data, expected_error_dev.data, 5)) { hidpp_err = read_buffer.msg.parameters[1]; - log_raw(ratbag, + hidpp_log_raw(&dev->base, " HID++ error from the %s (%d): %s (%02x)\n", read_buffer.msg.device_idx == RECEIVER_IDX ? "receiver" : "device", read_buffer.msg.device_idx, @@ -174,13 +173,13 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg) } while (ret > 0); if (ret < 0) { - log_error(ratbag, " USB error: %s (%d)\n", strerror(-ret), -ret); + hidpp_log_error(&dev->base, " 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); + hidpp_log_buf_raw(&dev->base, " received: ", read_buffer.data, ret); /* copy the answer for the caller */ *msg = read_buffer; } @@ -194,9 +193,6 @@ out_err: /* HID++ 1.0 commands 10 */ /* -------------------------------------------------------------------------- */ -static inline void -hidpp10_dump_all_pages(struct hidpp10_device *dev); - /* -------------------------------------------------------------------------- */ /* 0x00: Enable HID++ Notifications */ /* -------------------------------------------------------------------------- */ @@ -221,7 +217,7 @@ hidpp10_get_hidpp_notifications(struct hidpp10_device *dev, union hidpp10_message notifications = CMD_HIDPP_NOTIFICATIONS(idx, GET_REGISTER_REQ); int res; - log_raw(dev->ratbag_device->ratbag, "Fetching HID++ notifications\n"); + hidpp_log_raw(&dev->base, "Fetching HID++ notifications\n"); res = hidpp10_request_command(dev, ¬ifications); if (res) @@ -242,7 +238,7 @@ hidpp10_set_hidpp_notifications(struct hidpp10_device *dev, union hidpp10_message notifications = CMD_HIDPP_NOTIFICATIONS(idx, SET_REGISTER_REQ); int res; - log_raw(dev->ratbag_device->ratbag, "Setting HID++ notifications\n"); + hidpp_log_raw(&dev->base, "Setting HID++ notifications\n"); notifications.msg.parameters[0] = reporting_flags & 0xFF; notifications.msg.parameters[1] = (reporting_flags >> 8) & 0x1F; @@ -277,7 +273,7 @@ hidpp10_get_individual_features(struct hidpp10_device *dev, union hidpp10_message features = CMD_ENABLE_INDIVIDUAL_FEATURES(idx, GET_REGISTER_REQ); int res; - log_raw(dev->ratbag_device->ratbag, "Fetching individual features\n"); + hidpp_log_raw(&dev->base, "Fetching individual features\n"); res = hidpp10_request_command(dev, &features); if (res) @@ -300,7 +296,7 @@ hidpp10_set_individual_features(struct hidpp10_device *dev, union hidpp10_message mode = CMD_ENABLE_INDIVIDUAL_FEATURES(idx, SET_REGISTER_REQ); int res; - log_raw(dev->ratbag_device->ratbag, "Setting individual features\n"); + hidpp_log_raw(&dev->base, "Setting individual features\n"); mode.msg.device_idx = dev->index; @@ -510,7 +506,7 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, int8_t *current_profile) int res; int8_t page; - log_raw(dev->ratbag_device->ratbag, "Fetching current profile\n"); + hidpp_log_raw(&dev->base, "Fetching current profile\n"); res = hidpp10_request_command(dev, &profile); if (res) @@ -529,7 +525,6 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, int8_t *current_profile) 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; @@ -542,7 +537,7 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr * -> profiles are stored in the Flash */ number += 2; - log_raw(dev->ratbag_device->ratbag, "Fetching profile %d\n", number); + hidpp_log_raw(&dev->base, "Fetching profile %d\n", number); for (i = 0; i < sizeof(data); i += 16) { /* each sector contains 16 bytes of data */ @@ -603,56 +598,56 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr } - log_buf_raw(ratbag, + hidpp_log_buf_raw(&dev->base, "+++++++++++++++++++ Profile data: +++++++++++++++++ \n", data.data, 78); - log_raw(ratbag, "Profile %d:\n", number); + hidpp_log_raw(&dev->base, "Profile %d:\n", number); for (i = 0; i < 5; i++) { - log_raw(ratbag, + hidpp_log_raw(&dev->base, "DPI mode: %dx%d dpi\n", profile.dpi_modes[i].xres, profile.dpi_modes[i].yres); - log_raw(ratbag, + hidpp_log_raw(&dev->base, "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); + hidpp_log_raw(&dev->base, "Angle correction: %d\n", profile.angle_correction); + hidpp_log_raw(&dev->base, "Default DPI mode: %d\n", profile.default_dpi_mode); + hidpp_log_raw(&dev->base, "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 PROFILE_BUTTON_TYPE_BUTTON: - log_raw(ratbag, + hidpp_log_raw(&dev->base, "Button %zd: button %d\n", i, ffs(hidpp10_get_unaligned_u16le(&button->button.button_flags_lsb))); break; case PROFILE_BUTTON_TYPE_KEYS: - log_raw(ratbag, + hidpp_log_raw(&dev->base, "Button %zd: key %d modifier %x\n", i, button->keyboard_keys.key, button->keyboard_keys.modifier_flags); break; case PROFILE_BUTTON_TYPE_SPECIAL: - log_raw(ratbag, + hidpp_log_raw(&dev->base, "Button %zd: special %x\n", i, ffs(hidpp10_get_unaligned_u16le(&button->special.flags1))); break; case PROFILE_BUTTON_TYPE_CONSUMER_CONTROL: - log_raw(ratbag, + hidpp_log_raw(&dev->base, "Button %zd: consumer: %x\n", i, hidpp10_get_unaligned_u16(&button->consumer_control.consumer_control1)); break; case PROFILE_BUTTON_TYPE_DISABLED: - log_raw(ratbag, "Button %zd: disabled\n", i); + hidpp_log_raw(&dev->base, "Button %zd: disabled\n", i); break; default: /* FIXME: this is the page number for the macro, @@ -691,7 +686,7 @@ hidpp10_get_led_status(struct hidpp10_device *dev, uint8_t *status = led_status.msg.parameters; int res; - log_raw(dev->ratbag_device->ratbag, "Fetching LED status\n"); + hidpp_log_raw(&dev->base, "Fetching LED status\n"); res = hidpp10_request_command(dev, &led_status); if (res) @@ -717,7 +712,7 @@ hidpp10_set_led_status(struct hidpp10_device *dev, int res; int i; - log_raw(dev->ratbag_device->ratbag, "Setting LED status\n"); + hidpp_log_raw(&dev->base, "Setting LED status\n"); for (i = 0; i < 6; i++) { switch (led[i]) { @@ -768,7 +763,7 @@ hidpp10_get_led_intensity(struct hidpp10_device *dev, uint8_t *intensity = led_intensity.msg.parameters; int res; - log_raw(dev->ratbag_device->ratbag, "Fetching LED intensity\n"); + hidpp_log_raw(&dev->base, "Fetching LED intensity\n"); res = hidpp10_request_command(dev, &led_intensity); if (res) @@ -793,7 +788,7 @@ hidpp10_set_led_intensity(struct hidpp10_device *dev, uint8_t *intensity = led_intensity.msg.parameters; int res; - log_raw(dev->ratbag_device->ratbag, "Setting LED intensity\n"); + hidpp_log_raw(&dev->base, "Setting LED intensity\n"); intensity[0] = led_intensity_in_percent[0]/10 & 0xF; intensity[0] |= (led_intensity_in_percent[1]/10 & 0xF) << 4; @@ -832,7 +827,7 @@ hidpp10_get_led_color(struct hidpp10_device *dev, union hidpp10_message led_color = CMD_LED_COLOR(idx, GET_REGISTER_REQ); int res; - log_raw(dev->ratbag_device->ratbag, "Fetching LED color\n"); + hidpp_log_raw(&dev->base, "Fetching LED color\n"); res = hidpp10_request_command(dev, &led_color); if (res) @@ -855,7 +850,7 @@ hidpp10_set_led_color(struct hidpp10_device *dev, union hidpp10_message led_color = CMD_LED_COLOR(idx, SET_REGISTER_REQ); int res; - log_raw(dev->ratbag_device->ratbag, "Setting LED color\n"); + hidpp_log_raw(&dev->base, "Setting LED color\n"); led_color.msg.parameters[0] = red; led_color.msg.parameters[1] = green; @@ -888,7 +883,7 @@ hidpp10_get_optical_sensor_settings(struct hidpp10_device *dev, union hidpp10_message sensor = CMD_OPTICAL_SENSOR_SETTINGS(idx, GET_REGISTER_REQ); int res; - log_raw(dev->ratbag_device->ratbag, "Fetching optical sensor settings\n"); + hidpp_log_raw(&dev->base, "Fetching optical sensor settings\n"); res = hidpp10_request_command(dev, &sensor); if (res) @@ -925,7 +920,7 @@ hidpp10_get_current_resolution(struct hidpp10_device *dev, union hidpp10_message resolution = CMD_CURRENT_RESOLUTION(REPORT_ID_SHORT, idx, GET_LONG_REGISTER_REQ); int res; - log_raw(dev->ratbag_device->ratbag, "Fetching current resolution\n"); + hidpp_log_raw(&dev->base, "Fetching current resolution\n"); res = hidpp10_request_command(dev, &resolution); if (res) @@ -975,7 +970,7 @@ hidpp10_get_usb_refresh_rate(struct hidpp10_device *dev, union hidpp10_message refresh = CMD_USB_REFRESH_RATE(idx, GET_REGISTER_REQ); int res; - log_raw(dev->ratbag_device->ratbag, "Fetching USB refresh rate\n"); + hidpp_log_raw(&dev->base, "Fetching USB refresh rate\n"); res = hidpp10_request_command(dev, &refresh); if (res) @@ -993,7 +988,7 @@ hidpp10_set_usb_refresh_rate(struct hidpp10_device *dev, unsigned idx = dev->index; union hidpp10_message refresh = CMD_USB_REFRESH_RATE(idx, GET_REGISTER_REQ); - log_raw(dev->ratbag_device->ratbag, "Setting USB refresh rate\n"); + hidpp_log_raw(&dev->base, "Setting USB refresh rate\n"); refresh.msg.parameters[0] = 1000/rate; @@ -1026,9 +1021,7 @@ hidpp10_read_memory(struct hidpp10_device *dev, uint8_t page, uint8_t offset, if (page > 31) return -EINVAL; - log_raw(dev->ratbag_device->ratbag, - "Reading memory page %d, offset %#x\n", - page, offset); + hidpp_log_raw(&dev->base, "Reading memory page %d, offset %#x\n", page, offset); res = hidpp10_request_command(dev, &readmem); if (res) @@ -1039,30 +1032,6 @@ hidpp10_read_memory(struct hidpp10_device *dev, uint8_t page, uint8_t offset, return 0; } -static inline void -hidpp10_dump_all_pages(struct hidpp10_device *dev) -{ - uint16_t page, offset; - uint8_t bytes[16]; - int res; - - log_info(dev->ratbag_device->ratbag, "::::::: Dumping memory :::::\n"); - - for (page = 0; page < 512; page++) { - log_info(dev->ratbag_device->ratbag, ":: page %d\n", page); - for (offset = 0; offset < 256; offset += 16) { - res = hidpp10_read_memory(dev, page, offset, bytes); - if (res != 0) - goto out; - - log_buffer(dev->ratbag_device->ratbag, RATBAG_LOG_PRIORITY_INFO, " ", bytes, ARRAY_LENGTH(bytes)); - } - } - -out: - log_info(dev->ratbag_device->ratbag, "::::::: Dumping memory complete :::::\n"); -} - /* -------------------------------------------------------------------------- */ /* 0xB2: Device Connection and Disconnection (Pairing) */ /* -------------------------------------------------------------------------- */ @@ -1125,7 +1094,7 @@ hidpp10_get_pairing_information(struct hidpp10_device *dev, union hidpp10_message pairing_information = CMD_PAIRING_INFORMATION(idx, DEVICE_PAIRING_INFORMATION); int res; - log_raw(dev->ratbag_device->ratbag, "Fetching pairing information\n"); + hidpp_log_raw(&dev->base, "Fetching pairing information\n"); res = hidpp10_request_command(dev, &pairing_information); if (res) @@ -1147,7 +1116,7 @@ hidpp10_get_pairing_information_device_name(struct hidpp10_device *dev, union hidpp10_message device_name = CMD_PAIRING_INFORMATION(idx, DEVICE_NAME); int res; - log_raw(dev->ratbag_device->ratbag, "Fetching device name\n"); + hidpp_log_raw(&dev->base, "Fetching device name\n"); res = hidpp10_request_command(dev, &device_name); if (res) @@ -1190,7 +1159,7 @@ hidpp10_get_firmare_information(struct hidpp10_device *dev, int res; uint8_t maj, min, build; - log_raw(dev->ratbag_device->ratbag, "Fetching firmware information\n"); + hidpp_log_raw(&dev->base, "Fetching firmware information\n"); /* * This may fail on some devices @@ -1214,24 +1183,6 @@ hidpp10_get_firmare_information(struct hidpp10_device *dev, return 0; } -void hidpp10_list_devices(struct ratbag_device *device) { - struct hidpp10_device *dev; - int i; - - for (i = 0; i < 6; ++i) { - dev = hidpp10_device_new_from_idx(device, i); - if (!dev) - continue; - - log_info(device->ratbag, "[%d] %s %s (Wireless PID: %04x)\n", - i, - device_types[dev->device_type] ? device_types[dev->device_type] : "", - dev->name, dev->wpid); - hidpp10_device_destroy(dev); - } - -} - /* -------------------------------------------------------------------------- */ /* general device handling */ /* -------------------------------------------------------------------------- */ @@ -1241,26 +1192,32 @@ hidpp10_get_device_info(struct hidpp10_device *dev) uint32_t feature_mask, notifications; uint8_t reflect; int i; + uint16_t xres, yres; + uint16_t refresh_rate; + enum hidpp10_led_status led[6]; + int8_t current_profile; + struct hidpp10_profile profiles[HIDPP10_NUM_PROFILES]; hidpp10_get_individual_features(dev, &feature_mask); hidpp10_get_hidpp_notifications(dev, ¬ifications); - hidpp10_get_current_resolution(dev, &dev->xres, &dev->yres); - hidpp10_get_led_status(dev, dev->led); - hidpp10_get_usb_refresh_rate(dev, &dev->refresh_rate); + hidpp10_get_current_resolution(dev, &xres, &yres); + hidpp10_get_led_status(dev, led); + hidpp10_get_usb_refresh_rate(dev, &refresh_rate); hidpp10_get_optical_sensor_settings(dev, &reflect); - hidpp10_get_current_profile(dev, &dev->current_profile); + hidpp10_get_current_profile(dev, ¤t_profile); for (i = 0; i < HIDPP10_NUM_PROFILES; i++) - hidpp10_get_profile(dev, i, &dev->profiles[i]); + hidpp10_get_profile(dev, i, &profiles[i]); return 0; } static struct hidpp10_device* -hidpp10_device_new(struct ratbag_device *device, int index) +hidpp10_device_new(const struct hidpp_device *base, + int index) { struct hidpp10_device *dev; @@ -1269,19 +1226,19 @@ hidpp10_device_new(struct ratbag_device *device, int index) return NULL; dev->index = index; - dev->ratbag_device = device; + dev->base = *base; return dev; } struct hidpp10_device* -hidpp10_device_new_from_wpid(struct ratbag_device* device, uint16_t wpid) +hidpp10_device_new_from_wpid(const struct hidpp_device *base, uint16_t wpid) { struct hidpp10_device *dev; int i; for (i = 1; i < 7; i++) { - dev = hidpp10_device_new_from_idx(device, i); + dev = hidpp10_device_new_from_idx(base, i); if (!dev) continue; @@ -1295,11 +1252,11 @@ hidpp10_device_new_from_wpid(struct ratbag_device* device, uint16_t wpid) } struct hidpp10_device* -hidpp10_device_new_from_idx(struct ratbag_device *device, int idx) +hidpp10_device_new_from_idx(const struct hidpp_device *base, int idx) { struct hidpp10_device *dev; - dev = hidpp10_device_new(device, idx); + dev = hidpp10_device_new(base, idx); if (!dev) return NULL; diff --git a/src/hidpp10.h b/src/hidpp10.h index 697189c4..a7f3379a 100644 --- a/src/hidpp10.h +++ b/src/hidpp10.h @@ -33,33 +33,26 @@ #include #include "hidpp-generic.h" -#include "libratbag-private.h" -#include "libratbag.h" -struct hidpp10_device; +/* FIXME: that's what my G500s supports, but only pages 3-5 are valid. + * 0 is zeroed, 1 and 2 are garbage, all above 6 is garbage */ +#define HIDPP10_NUM_PROFILES 3 -struct _hidpp10_message { - uint8_t report_id; - uint8_t device_idx; - uint8_t sub_id; - uint8_t address; - union { - uint8_t parameters[3]; - uint8_t string[16]; - }; -} __attribute__((packed)); - -union hidpp10_message { - struct _hidpp10_message msg; - uint8_t data[LONG_MESSAGE_LENGTH]; +struct hidpp10_device { + struct hidpp_device base; + unsigned index; + uint16_t wpid; }; -void hidpp10_device_destroy(struct hidpp10_device *dev); +struct hidpp10_device* +hidpp10_device_new_from_wpid(const struct hidpp_device *base, + uint16_t wpid); +struct hidpp10_device* +hidpp10_device_new_from_idx(const struct hidpp_device *base, + int idx); -int hidpp10_request_command(struct hidpp10_device *device, union hidpp10_message *msg); -void hidpp10_list_devices(struct ratbag_device *device); -struct hidpp10_device *hidpp10_device_new_from_wpid(struct ratbag_device *device, uint16_t wpid); -struct hidpp10_device *hidpp10_device_new_from_idx(struct ratbag_device *device, int idx); +void +hidpp10_device_destroy(struct hidpp10_device *dev); /* -------------------------------------------------------------------------- */ /* 0x00: Enable HID++ Notifications */ @@ -503,24 +496,4 @@ hidpp10_get_firmare_information(struct hidpp10_device *dev, uint8_t *minor, uint8_t *build_number); - -/* FIXME: that's what my G500s supports, but only pages 3-5 are valid. - * 0 is zeroed, 1 and 2 are garbage, all above 6 is garbage */ -#define HIDPP10_NUM_PROFILES 3 -struct hidpp10_device { - struct ratbag_device *ratbag_device; - unsigned index; - char name[15]; - uint16_t wpid; - uint8_t report_interval; - uint8_t device_type; - uint8_t fw_major; - uint8_t fw_minor; - uint8_t build; - uint16_t xres, yres; - uint16_t refresh_rate; - enum hidpp10_led_status led[6]; - int8_t current_profile; - struct hidpp10_profile profiles[HIDPP10_NUM_PROFILES]; -}; #endif /* HIDPP_10_H */ diff --git a/src/libratbag-util.h b/src/libratbag-util.h index 186dcccc..b2cb59bf 100644 --- a/src/libratbag-util.h +++ b/src/libratbag-util.h @@ -85,6 +85,10 @@ cleanup_free(void *p) { free(*(void**)p); } +static inline void +cleanup_close(int *p) { + close(*p); +} static inline void cleanup_udev_unref(struct udev **udev) { if (*udev) @@ -99,6 +103,7 @@ cleanup_udev_device_unref(struct udev_device **udev_device) { #define _cleanup_(x) __attribute__((cleanup(x))) #define _cleanup_free_ _cleanup_(cleanup_free) +#define _cleanup_close_ _cleanup_(cleanup_close) #define _cleanup_udev_unref_ _cleanup_(cleanup_udev_unref) #define _cleanup_udev_device_unref_ _cleanup_(cleanup_udev_device_unref) diff --git a/tools/Makefile.am b/tools/Makefile.am index 3ee4dddd..cfd901db 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,4 +1,4 @@ -noinst_PROGRAMS = +noinst_PROGRAMS = hidpp10-dump-page bin_PROGRAMS = ratbag-command noinst_LTLIBRARIES = libshared.la @@ -16,3 +16,8 @@ ratbag_command_SOURCES = ratbag-command.c ratbag_command_LDADD = ../src/libratbag.la libshared.la $(LIBUDEV_LIBS) $(LIBEVDEV_LIBS) ratbag_command_LDFLAGS = -no-install ratbag_command_CFLAGS = $(LIBUDEV_CFLAGS) $(LIBEVDEV_CFLAGS) + +hidpp10_dump_page_SOURCES = hidpp10-dump-page.c +hidpp10_dump_page_LDADD = ../src/libhidpp10.la +hidpp10_dump_page_LDFLAGS = -no-install +hidpp10_dump_page_CFLAGS = -I$(top_srcdir)/src diff --git a/tools/hidpp10-dump-page.c b/tools/hidpp10-dump-page.c new file mode 100644 index 00000000..9997fad3 --- /dev/null +++ b/tools/hidpp10-dump-page.c @@ -0,0 +1,115 @@ +/* + * Copyright © 2015 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include + +static inline int +dump_page(struct hidpp10_device *dev, size_t page, size_t offset) +{ + int rc = 0; + uint8_t bytes[16]; + + while (offset < 256) { + hidpp_log_info(&dev->base, "page 0x%02zx off 0x%02zx: ", page, offset); + rc = hidpp10_read_memory(dev, page, offset, bytes); + if (rc != 0) + break; + + hidpp_log_buffer(&dev->base, HIDPP_LOG_PRIORITY_INFO, " ", bytes, ARRAY_LENGTH(bytes)); + offset += 16; + } + + return rc; +} + +static inline int +dump_all_pages(struct hidpp10_device *dev) +{ + uint8_t page; + int rc = 0; + + for (page = 0; page < 31; page++) { + rc = dump_page(dev, page, 0); + if (rc != 0) + break; + } + + /* We dumped at least one page successfully and get EAGAIN, so we're + * on the last page. Overwrite the last line with a blank one so it + * doesn't look like an error */ + if (page > 0 && rc == EAGAIN) { + hidpp_log_info(&dev->base, "\r \n"); + rc = 0; + } + + return rc; +} + +void +usage(void) +{ + printf("Usage: %s [page] [offset] /dev/hidraw0\n", program_invocation_short_name); +} + +int +main(int argc, char **argv) +{ + _cleanup_close_ int fd; + const char *path; + size_t page = 0, offset = 0; + struct hidpp10_device *dev = NULL; + struct hidpp_device base; + int rc; + + if (argc < 2 || argc > 4) { + usage(); + return 1; + } + + path = argv[argc - 1]; + fd = open(path, O_RDWR); + if (fd < 0) + error(1, errno, "Failed to open path %s", path); + + hidpp_device_init(&base, fd); + dev = hidpp10_device_new_from_idx(&base, WIRED_DEVICE_IDX); + + if (argc == 2) + rc = dump_all_pages(dev); + else { + page = atoi(argv[1]); + if (argc > 3) + offset = atoi(argv[2]); + rc = dump_page(dev, page, offset); + } + + hidpp10_device_destroy(dev); + + return rc; +}