aboutsummaryrefslogtreecommitdiff
path: root/src/device/device_io_hid.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/device/device_io_hid.cpp')
-rw-r--r--src/device/device_io_hid.cpp288
1 files changed, 288 insertions, 0 deletions
diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp
new file mode 100644
index 000000000..562aca8b8
--- /dev/null
+++ b/src/device/device_io_hid.cpp
@@ -0,0 +1,288 @@
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+#if defined(HAVE_HIDAPI)
+
+#include "log.hpp"
+#include "device_io_hid.hpp"
+
+namespace hw {
+ namespace io {
+
+ #undef MONERO_DEFAULT_LOG_CATEGORY
+ #define MONERO_DEFAULT_LOG_CATEGORY "device.io"
+
+ #define ASSERT_X(exp,msg) CHECK_AND_ASSERT_THROW_MES(exp, msg);
+
+ #define MAX_BLOCK 64
+
+ static std::string safe_hid_error(hid_device *hwdev) {
+ if (hwdev) {
+ return std::string((char*)hid_error(hwdev));
+ }
+ return std::string("NULL device");
+ }
+
+ static std::string safe_hid_path(const hid_device_info *hwdev_info) {
+ if (hwdev_info && hwdev_info->path) {
+ return std::string(hwdev_info->path);
+ }
+ return std::string("NULL path");
+ }
+
+ device_io_hid::device_io_hid(unsigned short c, unsigned char t, unsigned int ps, unsigned int to) :
+ channel(c),
+ tag(t),
+ packet_size(ps),
+ timeout(to),
+ usb_vid(0),
+ usb_pid(0),
+ usb_device(NULL) {
+ }
+
+ device_io_hid::device_io_hid() : device_io_hid(DEFAULT_CHANNEL, DEFAULT_TAG, DEFAULT_PACKET_SIZE, DEFAULT_TIMEOUT) {
+ }
+
+ void device_io_hid::io_hid_log(int read, unsigned char* buffer, int block_len) {
+ if (hid_verbose) {
+ char strbuffer[1024];
+ hw::buffer_to_str(strbuffer, sizeof(strbuffer), (char*)buffer, block_len);
+ MDEBUG( "HID " << (read?"<":">") <<" : "<<strbuffer);
+ }
+ }
+
+ void device_io_hid::init() {
+ int r;
+ r = hid_init();
+ ASSERT_X(r>=0, "Unable to init hidapi library. Error "+std::to_string(r)+": "+safe_hid_error(this->usb_device));
+ }
+
+ void device_io_hid::connect(void *params) {
+ hid_conn_params *p = (struct hid_conn_params*)params;
+ this->connect(p->vid, p->pid, p->interface_number, p->usage_page, p->interface_OR_page);
+ }
+
+ void device_io_hid::connect(unsigned int vid, unsigned int pid, unsigned int interface_number, unsigned int usage_page, bool interface_OR_page ) {
+ hid_device_info *hwdev_info, *hwdev_info_list;
+ hid_device *hwdev;
+
+ this->disconnect();
+
+ hwdev_info_list = hid_enumerate(vid, pid);
+ ASSERT_X(hwdev_info_list, "Unable to enumerate device "+std::to_string(vid)+":"+std::to_string(vid)+ ": "+ safe_hid_error(this->usb_device));
+ hwdev = NULL;
+ hwdev_info = hwdev_info_list;
+ while (hwdev_info) {
+ if ((interface_OR_page && ((usage_page == 0xffa0) || (interface_number == 0))) ||
+ ((usage_page == 0xffa0) && (interface_number == 0)) ) {
+ MDEBUG("HID Device found: " << safe_hid_path(hwdev_info));
+ hwdev = hid_open_path(hwdev_info->path);
+ break;
+ } else {
+ MDEBUG("HID Device discard: " << safe_hid_path(hwdev_info) << "("+std::to_string(hwdev_info->usage_page) << "," << std::to_string(hwdev_info->interface_number) << ")");
+ }
+ hwdev_info = hwdev_info->next;
+ }
+ hid_free_enumeration(hwdev_info_list);
+ ASSERT_X(hwdev, "Unable to open device "+std::to_string(pid)+":"+std::to_string(vid));
+ this->usb_vid = vid;
+ this->usb_pid = pid;
+ this->usb_device = hwdev;
+ }
+
+
+ bool device_io_hid::connected() const {
+ return this->usb_device != NULL;
+ }
+
+ int device_io_hid::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len) {
+ unsigned char buffer[400];
+ unsigned char padding_buffer[MAX_BLOCK+1];
+ unsigned int result;
+ int hid_ret;
+ unsigned int sw_offset;
+ unsigned int remaining;
+ unsigned int offset = 0;
+
+ ASSERT_X(this->usb_device,"No device opened");
+
+ //Split command in several HID packet
+ memset(buffer, 0, sizeof(buffer));
+ result = this->wrapCommand(command, cmd_len, buffer, sizeof(buffer));
+ remaining = result;
+
+ while (remaining > 0) {
+ int block_size = (remaining > MAX_BLOCK ? MAX_BLOCK : remaining);
+ memset(padding_buffer, 0, sizeof(padding_buffer));
+ memcpy(padding_buffer+1, buffer + offset, block_size);
+ io_hid_log(0, padding_buffer, block_size+1);
+ hid_ret = hid_write(this->usb_device, padding_buffer, block_size+1);
+ ASSERT_X(hid_ret>=0, "Unable to send hidapi command. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device));
+ offset += block_size;
+ remaining -= block_size;
+ }
+
+ //get first response
+ memset(buffer, 0, sizeof(buffer));
+ hid_ret = hid_read_timeout(this->usb_device, buffer, MAX_BLOCK, this->timeout);
+ ASSERT_X(hid_ret>=0, "Unable to read hidapi response. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device));
+ result = (unsigned int)hid_ret;
+ io_hid_log(1, buffer, result);
+ offset = MAX_BLOCK;
+ //parse first response and get others if any
+ for (;;) {
+ result = this->unwrapReponse(buffer, offset, response, max_resp_len);
+ if (result != 0) {
+ break;
+ }
+ hid_ret = hid_read_timeout(this->usb_device, buffer + offset, MAX_BLOCK, this->timeout);
+ ASSERT_X(hid_ret>=0, "Unable to receive hidapi response. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device));
+ result = (unsigned int)hid_ret;
+ io_hid_log(1, buffer + offset, result);
+ offset += MAX_BLOCK;
+ }
+ return result;
+ }
+
+ void device_io_hid::disconnect(void) {
+ if (this->usb_device) {
+ hid_close(this->usb_device);
+ }
+ this->usb_vid = 0;
+ this->usb_pid = 0;
+ this->usb_device = NULL;
+ }
+
+ void device_io_hid::release() {
+ /* Do not exit, as the lib context is global*/
+ //hid_exit();
+ }
+
+ unsigned int device_io_hid::wrapCommand(const unsigned char *command, size_t command_len, unsigned char *out, size_t out_len) {
+ unsigned int sequence_idx = 0;
+ unsigned int offset = 0;
+ unsigned int offset_out = 0;
+ unsigned int block_size;
+
+ ASSERT_X(this->packet_size >= 3, "Invalid Packet size: "+std::to_string(this->packet_size)) ;
+ ASSERT_X(out_len >= 7, "out_len too short: "+std::to_string(out_len));
+
+ out_len -= 7;
+ out[offset_out++] = ((this->channel >> 8) & 0xff);
+ out[offset_out++] = (this->channel & 0xff);
+ out[offset_out++] = this->tag;
+ out[offset_out++] = ((sequence_idx >> 8) & 0xff);
+ out[offset_out++] = (sequence_idx & 0xff);
+ sequence_idx++;
+ out[offset_out++] = ((command_len >> 8) & 0xff);
+ out[offset_out++] = (command_len & 0xff);
+ block_size = (command_len > this->packet_size - 7 ? this->packet_size - 7 : command_len);
+ ASSERT_X(out_len >= block_size, "out_len too short: "+std::to_string(out_len));
+ out_len -= block_size;
+ memcpy(out + offset_out, command + offset, block_size);
+ offset_out += block_size;
+ offset += block_size;
+ while (offset != command_len) {
+ ASSERT_X(out_len >= 5, "out_len too short: "+std::to_string(out_len));
+ out_len -= 5;
+ out[offset_out++] = ((this->channel >> 8) & 0xff);
+ out[offset_out++] = (this->channel & 0xff);
+ out[offset_out++] = this->tag;
+ out[offset_out++] = ((sequence_idx >> 8) & 0xff);
+ out[offset_out++] = (sequence_idx & 0xff);
+ sequence_idx++;
+ block_size = ((command_len - offset) > this->packet_size - 5 ? this->packet_size - 5 : command_len - offset);
+ ASSERT_X(out_len >= block_size, "out_len too short: "+std::to_string(out_len));
+ out_len -= block_size;
+ memcpy(out + offset_out, command + offset, block_size);
+ offset_out += block_size;
+ offset += block_size;
+ }
+ while ((offset_out % this->packet_size) != 0) {
+ ASSERT_X(out_len >= 1, "out_len too short: "+std::to_string(out_len));
+ out_len--;
+ out[offset_out++] = 0;
+ }
+ return offset_out;
+ }
+
+ /*
+ * return 0 if more data are needed
+ * >0 if response is fully available
+ */
+ unsigned int device_io_hid::unwrapReponse(const unsigned char *data, size_t data_len, unsigned char *out, size_t out_len) {
+ unsigned int sequence_idx = 0;
+ unsigned int offset = 0;
+ unsigned int offset_out = 0;
+ unsigned int response_len;
+ unsigned int block_size;
+ unsigned int val;
+
+ //end?
+ if ((data == NULL) || (data_len < 7 + 5)) {
+ return 0;
+ }
+
+ //check hid header
+ val = (data[offset]<<8) + data[offset+1];
+ offset += 2;
+ ASSERT_X(val == this->channel, "Wrong Channel");
+ val = data[offset];
+ offset++;
+ ASSERT_X(val == this->tag, "Wrong TAG");
+ val = (data[offset]<<8) + data[offset+1];
+ offset += 2;
+ ASSERT_X(val == sequence_idx, "Wrong sequence_idx");
+
+ //fetch
+ response_len = (data[offset++] << 8);
+ response_len |= data[offset++];
+ ASSERT_X(out_len >= response_len, "Out Buffer too short");
+ if (data_len < (7 + response_len)) {
+ return 0;
+ }
+ block_size = (response_len > (this->packet_size - 7) ? this->packet_size - 7 : response_len);
+ memcpy(out + offset_out, data + offset, block_size);
+ offset += block_size;
+ offset_out += block_size;
+ while (offset_out != response_len) {
+ sequence_idx++;
+ if (offset == data_len) {
+ return 0;
+ }
+ val = (data[offset]<<8) + data[offset+1];
+ offset += 2;
+ ASSERT_X(val == this->channel, "Wrong Channel");
+ val = data[offset];
+ offset++;
+ ASSERT_X(val == this->tag, "Wrong TAG");
+ val = (data[offset]<<8) + data[offset+1];
+ offset += 2;
+ ASSERT_X(val == sequence_idx, "Wrong sequence_idx");
+
+ block_size = ((response_len - offset_out) > this->packet_size - 5 ? this->packet_size - 5 : response_len - offset_out);
+ if (block_size > (data_len - offset)) {
+ return 0;
+ }
+ memcpy(out + offset_out, data + offset, block_size);
+ offset += block_size;
+ offset_out += block_size;
+ }
+ return offset_out;
+ }
+
+
+ }
+}
+
+#endif //#if defined(HAVE_HIDAPI)