From 3944c349965bfe322ad7b8afe6d1d7859318c068 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Mon, 7 Apr 2014 23:34:59 +0200 Subject: lib: mostly implement generic hidpp defs and helpers hidpp_open still needs to be done. --- lib/hidpp.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 lib/hidpp.c diff --git a/lib/hidpp.c b/lib/hidpp.c new file mode 100644 index 0000000..a07d689 --- /dev/null +++ b/lib/hidpp.c @@ -0,0 +1,139 @@ +/* + * Generic HID++ definitions and helpers. + * + * Copyright (C) 2014 Peter Wu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include /* needs -lrt, for clock_gettime as timeout helper */ +#include +#include "hidpp.h" +#include "debug.h" + +#define VID_LOGITECH 0x046d +#define PID_NANO_RECEIVER 0xc52f +#define RECEIVER_NAME "logitech-djreceiver" + +static inline bool is_valid_device_index(uint8_t ix) +{ + return ix >= 1 && ix <= MAX_DEVICES; +} + +static long long unsigned get_timestamp_ms(void) +{ + struct timespec tp; + + clock_gettime(CLOCK_MONOTONIC, &tp); + return tp.tv_sec * 1000 + tp.tv_nsec / 1000000; +} + +static ssize_t poll_read(int fd, int timeout, void *buf, size_t count) +{ + int r; + struct pollfd pollfd = { + .fd = fd, + .events = POLLIN + }; + + r = poll(&pollfd, 1, timeout); + if (r < 0) { + perror("poll"); + return 0; + } else if (r == 0) { + trace_log("poll timeout reached!\n"); + return 0; + } + + r = read(fd, buf, count); + if (r < 0) { + perror("read"); + return 0; + } + + return r; +} + +static void hidpp_discard_messages(int fd) +{ + char c; + + /* kernel discards remainder of message */ + while (poll_read(fd, 1, &c, 1)) ; +} + +bool hidpp_write_report(int fd, struct HidppMessage *msg) +{ + ssize_t r, payload_size = HIDPP_SHORT_LEN; + + if (msg->report_id == HIDPP_LONG) { + payload_size = HIDPP_LONG_LEN; + } + + /* start with an empty queue to avoid interference */ + hidpp_discard_messages(fd); + + trace_dump_data("wr", (uint8_t *) msg, payload_size); + + r = write(fd, msg, payload_size); + if (r < 0) { + perror("write"); + } + + return payload_size == r; +} + +bool hidpp_read_msg(int fd, int timeout, HidppMessage *msg, + hidpp_msg_cb_t *cb, void *userdata) +{ + int r; + long long unsigned begin_ms = 0, end_ms; + HidppMessage response; + + do { + end_ms = get_timestamp_ms(); + if (begin_ms > 0) + timeout -= end_ms - begin_ms; + begin_ms = end_ms; + if (timeout <= 0) + break; + + memset(&response, 0, sizeof(response)); + r = poll_read(fd, timeout, &response, sizeof(response)); + if (r > 0) { + trace_dump_data("rd", (uint8_t *) msg, r); + + if (r < HIDPP_SHORT_LEN) { + trace_log("Impossible short read: %i\n", r); + } else if (response.report_id == HIDPP_SHORT || + response.report_id == HIDPP_LONG) { + uint8_t ix = response.device_index; + + if (!is_valid_device_index(ix) && ix != 0xFF) { + trace_log("Invalid device index: %i\n", ix); + } else if (cb(&response, userdata)) { + /* response is accepted, return message */ + memcpy(msg, &response, sizeof(response)); + return true; + } + } + } + } while (1); + + /* timeout expired, no report found unfortunately */ + return false; +} -- cgit v1.2.1