blob: 6e2a890daa0a732fe732c0f0ed3dc5fecb9573ab [file] [log] [blame]
Andrew Duggan052556f2014-04-16 11:32:30 -07001/*
2 * Copyright (C) 2014 Andrew Duggan
3 * Copyright (C) 2014 Synaptics Inc
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
Andrew Duggan4e811252014-04-03 15:17:57 -070018#include <stdio.h>
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <fcntl.h>
Andrew Dugganbef9c2d2015-05-06 17:45:48 -070022#include <dirent.h>
Andrew Duggan4e811252014-04-03 15:17:57 -070023#include <errno.h>
24#include <string.h>
25#include <unistd.h>
26#include <sys/ioctl.h>
27#include <sys/select.h>
28
29#include <linux/types.h>
30#include <linux/input.h>
31#include <linux/hidraw.h>
32#include <signal.h>
33#include <stdlib.h>
34
35#include "hiddevice.h"
36
37#define RMI_WRITE_REPORT_ID 0x9 // Output Report
38#define RMI_READ_ADDR_REPORT_ID 0xa // Output Report
39#define RMI_READ_DATA_REPORT_ID 0xb // Input Report
40#define RMI_ATTN_REPORT_ID 0xc // Input Report
41#define RMI_SET_RMI_MODE_REPORT_ID 0xf // Feature Report
42
43enum rmi_hid_mode_type {
44 HID_RMI4_MODE_MOUSE = 0,
45 HID_RMI4_MODE_ATTN_REPORTS = 1,
46 HID_RMI4_MODE_NO_PACKED_ATTN_REPORTS = 2,
47};
48
Andrew Duggan8b774392014-06-18 13:11:49 -070049enum hid_report_type {
50 HID_REPORT_TYPE_UNKNOWN = 0x0,
51 HID_REPORT_TYPE_INPUT = 0x81,
52 HID_REPORT_TYPE_OUTPUT = 0x91,
53 HID_REPORT_TYPE_FEATURE = 0xb1,
54};
55
Andrew Duggan4e811252014-04-03 15:17:57 -070056#define HID_RMI4_REPORT_ID 0
57#define HID_RMI4_READ_INPUT_COUNT 1
58#define HID_RMI4_READ_INPUT_DATA 2
59#define HID_RMI4_READ_OUTPUT_ADDR 2
60#define HID_RMI4_READ_OUTPUT_COUNT 4
61#define HID_RMI4_WRITE_OUTPUT_COUNT 1
62#define HID_RMI4_WRITE_OUTPUT_ADDR 2
63#define HID_RMI4_WRITE_OUTPUT_DATA 4
64#define HID_RMI4_FEATURE_MODE 1
65#define HID_RMI4_ATTN_INTERUPT_SOURCES 1
66#define HID_RMI4_ATTN_DATA 2
67
Andrew Dugganfa10fc82014-07-07 17:36:33 -070068#define SYNAPTICS_VENDOR_ID 0x06cb
69
Andrew Duggan4e811252014-04-03 15:17:57 -070070int HIDDevice::Open(const char * filename)
71{
72 int rc;
73 int desc_size;
74
Andrew Duggan72163582014-04-03 16:25:12 -070075 if (!filename)
76 return -EINVAL;
77
Andrew Duggan4e811252014-04-03 15:17:57 -070078 m_fd = open(filename, O_RDWR);
79 if (m_fd < 0)
80 return -1;
81
82 memset(&m_rptDesc, 0, sizeof(m_rptDesc));
83 memset(&m_info, 0, sizeof(m_info));
84
85 rc = ioctl(m_fd, HIDIOCGRDESCSIZE, &desc_size);
86 if (rc < 0)
87 return rc;
88
89 m_rptDesc.size = desc_size;
90 rc = ioctl(m_fd, HIDIOCGRDESC, &m_rptDesc);
91 if (rc < 0)
92 return rc;
93
94 rc = ioctl(m_fd, HIDIOCGRAWINFO, &m_info);
95 if (rc < 0)
96 return rc;
97
Andrew Dugganfa10fc82014-07-07 17:36:33 -070098 if (m_info.vendor != SYNAPTICS_VENDOR_ID) {
99 errno = -ENODEV;
100 return -1;
101 }
102
Andrew Duggan8b774392014-06-18 13:11:49 -0700103 ParseReportSizes();
Andrew Duggan4e811252014-04-03 15:17:57 -0700104
105 m_inputReport = new unsigned char[m_inputReportSize]();
Andrew Dugganfa10fc82014-07-07 17:36:33 -0700106 if (!m_inputReport) {
107 errno = -ENOMEM;
108 return -1;
109 }
Andrew Duggan4e811252014-04-03 15:17:57 -0700110
111 m_outputReport = new unsigned char[m_outputReportSize]();
Andrew Dugganfa10fc82014-07-07 17:36:33 -0700112 if (!m_outputReport) {
113 errno = -ENOMEM;
114 return -1;
115 }
Andrew Duggan4e811252014-04-03 15:17:57 -0700116
117 m_readData = new unsigned char[m_inputReportSize]();
Andrew Dugganfa10fc82014-07-07 17:36:33 -0700118 if (!m_readData) {
119 errno = -ENOMEM;
120 return -1;
121 }
Andrew Duggan4e811252014-04-03 15:17:57 -0700122
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800123 m_attnData = new unsigned char[m_inputReportSize]();
124 if (!m_attnData) {
Andrew Dugganfa10fc82014-07-07 17:36:33 -0700125 errno = -ENOMEM;
126 return -1;
127 }
Andrew Duggan4e811252014-04-03 15:17:57 -0700128
129 m_deviceOpen = true;
130
131 rc = SetMode(HID_RMI4_MODE_ATTN_REPORTS);
132 if (rc)
133 return -1;
134
135 return 0;
136}
137
Andrew Duggan8b774392014-06-18 13:11:49 -0700138void HIDDevice::ParseReportSizes()
139{
140 bool isVendorSpecific = false;
141 bool isReport = false;
142 int totalReportSize = 0;
143 int reportSize = 0;
144 int reportCount = 0;
145 enum hid_report_type hidReportType = HID_REPORT_TYPE_UNKNOWN;
146
147 for (unsigned int i = 0; i < m_rptDesc.size; ++i) {
148 if (isVendorSpecific) {
149 if (m_rptDesc.value[i] == 0x85 || m_rptDesc.value[i] == 0xc0) {
150 if (isReport) {
151 // finish up data on the previous report
152 totalReportSize = (reportSize * reportCount) >> 3;
153
154 switch (hidReportType) {
155 case HID_REPORT_TYPE_INPUT:
156 m_inputReportSize = totalReportSize + 1;
157 break;
158 case HID_REPORT_TYPE_OUTPUT:
159 m_outputReportSize = totalReportSize + 1;
160 break;
161 case HID_REPORT_TYPE_FEATURE:
162 m_featureReportSize = totalReportSize + 1;
163 break;
164 case HID_REPORT_TYPE_UNKNOWN:
165 default:
166 break;
167 }
168 }
169
170 // reset values for the new report
171 totalReportSize = 0;
172 reportSize = 0;
173 reportCount = 0;
174 hidReportType = HID_REPORT_TYPE_UNKNOWN;
175
176 if (m_rptDesc.value[i] == 0x85)
177 isReport = true;
178 else
179 isReport = false;
180
181 if (m_rptDesc.value[i] == 0xc0)
182 isVendorSpecific = false;
183 }
184
185 if (isReport) {
186 if (m_rptDesc.value[i] == 0x75) {
187 reportSize = m_rptDesc.value[++i];
188 continue;
189 }
190
191 if (m_rptDesc.value[i] == 0x95) {
192 reportCount = m_rptDesc.value[++i];
193 continue;
194 }
195
196 if (m_rptDesc.value[i] == HID_REPORT_TYPE_INPUT)
197 hidReportType = HID_REPORT_TYPE_INPUT;
198
199 if (m_rptDesc.value[i] == HID_REPORT_TYPE_OUTPUT)
200 hidReportType = HID_REPORT_TYPE_OUTPUT;
201
202 if (m_rptDesc.value[i] == HID_REPORT_TYPE_FEATURE) {
203 hidReportType = HID_REPORT_TYPE_FEATURE;
204 }
205 }
206 }
207
208 if (m_rptDesc.value[i] == 0x06 && m_rptDesc.value[i + 1] == 0x00
209 && m_rptDesc.value[i + 2] == 0xFF) {
210 isVendorSpecific = true;
211 i += 2;
212 }
213 }
214}
215
Andrew Duggan4e811252014-04-03 15:17:57 -0700216int HIDDevice::Read(unsigned short addr, unsigned char *buf, unsigned short len)
217{
Andrew Duggan64d6e592014-07-01 13:02:10 -0700218 ssize_t count;
Andrew Duggan4e811252014-04-03 15:17:57 -0700219 size_t bytesReadPerRequest;
220 size_t bytesInDataReport;
221 size_t totalBytesRead;
222 size_t bytesPerRequest;
223 size_t bytesWritten;
224 size_t bytesToRequest;
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800225 int reportId;
Andrew Duggan4e811252014-04-03 15:17:57 -0700226 int rc;
227
228 if (!m_deviceOpen)
229 return -1;
230
231 if (m_bytesPerReadRequest)
232 bytesPerRequest = m_bytesPerReadRequest;
233 else
234 bytesPerRequest = len;
235
236 for (totalBytesRead = 0; totalBytesRead < len; totalBytesRead += bytesReadPerRequest) {
237 count = 0;
238 if ((len - totalBytesRead) < bytesPerRequest)
239 bytesToRequest = len % bytesPerRequest;
240 else
241 bytesToRequest = bytesPerRequest;
242
Andrew de los Reyes242ea832015-09-04 14:40:06 -0700243 if (m_outputReportSize < HID_RMI4_READ_OUTPUT_COUNT + 2) {
244 return -1;
245 }
Andrew Duggan4e811252014-04-03 15:17:57 -0700246 m_outputReport[HID_RMI4_REPORT_ID] = RMI_READ_ADDR_REPORT_ID;
247 m_outputReport[1] = 0; /* old 1 byte read count */
248 m_outputReport[HID_RMI4_READ_OUTPUT_ADDR] = addr & 0xFF;
249 m_outputReport[HID_RMI4_READ_OUTPUT_ADDR + 1] = (addr >> 8) & 0xFF;
250 m_outputReport[HID_RMI4_READ_OUTPUT_COUNT] = bytesToRequest & 0xFF;
251 m_outputReport[HID_RMI4_READ_OUTPUT_COUNT + 1] = (bytesToRequest >> 8) & 0xFF;
252
253 m_dataBytesRead = 0;
254
255 for (bytesWritten = 0; bytesWritten < m_outputReportSize; bytesWritten += count) {
256 m_bCancel = false;
257 count = write(m_fd, m_outputReport + bytesWritten,
258 m_outputReportSize - bytesWritten);
259 if (count < 0) {
260 if (errno == EINTR && m_deviceOpen && !m_bCancel)
261 continue;
262 else
263 return count;
264 }
265 break;
266 }
267
268 bytesReadPerRequest = 0;
269 while (bytesReadPerRequest < bytesToRequest) {
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800270 rc = GetReport(&reportId);
271 if (rc > 0 && reportId == RMI_READ_DATA_REPORT_ID) {
Andrew de los Reyes242ea832015-09-04 14:40:06 -0700272 if (static_cast<ssize_t>(m_inputReportSize) <
273 std::max(HID_RMI4_READ_INPUT_COUNT,
274 HID_RMI4_READ_INPUT_DATA))
275 return -1;
Andrew Duggan4e811252014-04-03 15:17:57 -0700276 bytesInDataReport = m_readData[HID_RMI4_READ_INPUT_COUNT];
277 memcpy(buf + bytesReadPerRequest, &m_readData[HID_RMI4_READ_INPUT_DATA],
278 bytesInDataReport);
279 bytesReadPerRequest += bytesInDataReport;
280 m_dataBytesRead = 0;
281 }
282 }
283 addr += bytesPerRequest;
284 }
285
286 return totalBytesRead;
287}
288
289int HIDDevice::Write(unsigned short addr, const unsigned char *buf, unsigned short len)
290{
Andrew Duggan64d6e592014-07-01 13:02:10 -0700291 ssize_t count;
Andrew Duggan4e811252014-04-03 15:17:57 -0700292
293 if (!m_deviceOpen)
294 return -1;
295
Andrew de los Reyes242ea832015-09-04 14:40:06 -0700296 if (static_cast<ssize_t>(m_outputReportSize) <
297 HID_RMI4_WRITE_OUTPUT_DATA + len)
298 return -1;
Andrew Duggan4e811252014-04-03 15:17:57 -0700299 m_outputReport[HID_RMI4_REPORT_ID] = RMI_WRITE_REPORT_ID;
300 m_outputReport[HID_RMI4_WRITE_OUTPUT_COUNT] = len;
301 m_outputReport[HID_RMI4_WRITE_OUTPUT_ADDR] = addr & 0xFF;
302 m_outputReport[HID_RMI4_WRITE_OUTPUT_ADDR + 1] = (addr >> 8) & 0xFF;
303 memcpy(&m_outputReport[HID_RMI4_WRITE_OUTPUT_DATA], buf, len);
304
305 for (;;) {
306 m_bCancel = false;
307 count = write(m_fd, m_outputReport, m_outputReportSize);
308 if (count < 0) {
309 if (errno == EINTR && m_deviceOpen && !m_bCancel)
310 continue;
311 else
312 return count;
313 }
314 return count;
315 }
316}
317
318int HIDDevice::SetMode(int mode)
319{
320 int rc;
321 char buf[2];
322
323 if (!m_deviceOpen)
324 return -1;
325
326 buf[0] = 0xF;
327 buf[1] = mode;
328 rc = ioctl(m_fd, HIDIOCSFEATURE(2), buf);
329 if (rc < 0) {
330 perror("HIDIOCSFEATURE");
331 return rc;
332 }
333
334 return 0;
335}
336
337void HIDDevice::Close()
338{
339 if (!m_deviceOpen)
340 return;
341
342 SetMode(HID_RMI4_MODE_MOUSE);
343 m_deviceOpen = false;
344 close(m_fd);
345 m_fd = -1;
346
347 delete[] m_inputReport;
348 m_inputReport = NULL;
349 delete[] m_outputReport;
350 m_outputReport = NULL;
351 delete[] m_readData;
352 m_readData = NULL;
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800353 delete[] m_attnData;
354 m_attnData = NULL;
Andrew Duggan4e811252014-04-03 15:17:57 -0700355}
356
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800357int HIDDevice::WaitForAttention(struct timeval * timeout, unsigned int source_mask)
Andrew Duggan4e811252014-04-03 15:17:57 -0700358{
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800359 return GetAttentionReport(timeout, source_mask, NULL, NULL);
Andrew Duggan4e811252014-04-03 15:17:57 -0700360}
361
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800362int HIDDevice::GetAttentionReport(struct timeval * timeout, unsigned int source_mask,
363 unsigned char *buf, unsigned int *len)
Andrew Duggan4e811252014-04-03 15:17:57 -0700364{
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800365 int rc = 0;
Andrew de los Reyes242ea832015-09-04 14:40:06 -0700366 unsigned int bytes = m_inputReportSize;
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800367 int reportId;
Andrew Duggan4e811252014-04-03 15:17:57 -0700368
Andrew Duggane9a5cd02014-04-29 13:34:42 -0700369 if (len && m_inputReportSize < *len) {
Andrew Duggan4e811252014-04-03 15:17:57 -0700370 bytes = *len;
371 *len = m_inputReportSize;
372 }
373
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800374 // Assume the Linux implementation of select with timeout set to the
375 // time remaining.
376 while (!timeout || (timeout->tv_sec != 0 || timeout->tv_usec != 0)) {
377 rc = GetReport(&reportId, timeout);
378 if (rc > 0) {
379 if (reportId == RMI_ATTN_REPORT_ID) {
Andrew de los Reyes242ea832015-09-04 14:40:06 -0700380 if (buf) {
381 if (bytes > m_inputReportSize ||
382 m_inputReportSize < HID_RMI4_ATTN_INTERUPT_SOURCES + 1)
383 return -1;
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800384 memcpy(buf, m_attnData, bytes);
Andrew de los Reyes242ea832015-09-04 14:40:06 -0700385 }
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800386 if (source_mask & m_attnData[HID_RMI4_ATTN_INTERUPT_SOURCES])
387 return rc;
388 }
389 } else {
390 return rc;
391 }
Andrew Duggan4e811252014-04-03 15:17:57 -0700392 }
393
394 return rc;
395}
396
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800397int HIDDevice::GetReport(int *reportId, struct timeval * timeout)
Andrew Duggan4e811252014-04-03 15:17:57 -0700398{
Andrew Duggan64d6e592014-07-01 13:02:10 -0700399 ssize_t count = 0;
Andrew Duggan4e811252014-04-03 15:17:57 -0700400 fd_set fds;
401 int rc;
Andrew Duggan4e811252014-04-03 15:17:57 -0700402
403 if (!m_deviceOpen)
404 return -1;
405
Andrew de los Reyes242ea832015-09-04 14:40:06 -0700406 if (m_inputReportSize < HID_RMI4_REPORT_ID + 1)
407 return -1;
408
Andrew Duggan4e811252014-04-03 15:17:57 -0700409 for (;;) {
410 FD_ZERO(&fds);
411 FD_SET(m_fd, &fds);
412
413 rc = select(m_fd + 1, &fds, NULL, NULL, timeout);
414 if (rc == 0) {
415 return -ETIMEDOUT;
416 } else if (rc < 0) {
417 if (errno == EINTR && m_deviceOpen && !m_bCancel)
418 continue;
419 else
420 return rc;
421 } else if (rc > 0 && FD_ISSET(m_fd, &fds)) {
422 size_t offset = 0;
423 for (;;) {
424 m_bCancel = false;
425 count = read(m_fd, m_inputReport + offset, m_inputReportSize - offset);
426 if (count < 0) {
427 if (errno == EINTR && m_deviceOpen && !m_bCancel)
428 continue;
429 else
430 return count;
431 }
432 offset += count;
433 if (offset == m_inputReportSize)
434 break;
435 }
436 }
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800437 break;
Andrew Duggan4e811252014-04-03 15:17:57 -0700438 }
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800439
440 if (reportId)
441 *reportId = m_inputReport[HID_RMI4_REPORT_ID];
442
443 if (m_inputReport[HID_RMI4_REPORT_ID] == RMI_ATTN_REPORT_ID) {
Andrew de los Reyes242ea832015-09-04 14:40:06 -0700444 if (static_cast<ssize_t>(m_inputReportSize) < count)
445 return -1;
446 memcpy(m_attnData, m_inputReport, count /*offset?*/);
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800447 } else if (m_inputReport[HID_RMI4_REPORT_ID] == RMI_READ_DATA_REPORT_ID) {
Andrew de los Reyes242ea832015-09-04 14:40:06 -0700448 if (static_cast<ssize_t>(m_inputReportSize) < count)
449 return -1;
450 memcpy(m_readData, m_inputReport, count /*offset?*/);
451 m_dataBytesRead = count /*offset?*/;
Andrew Dugganf73fdc72014-11-09 11:02:22 -0800452 }
453 return 1;
Andrew Duggan4e811252014-04-03 15:17:57 -0700454}
455
456void HIDDevice::PrintReport(const unsigned char *report)
457{
458 int i;
459 int len = 0;
460 const unsigned char * data;
461 int addr = 0;
462
463 switch (report[HID_RMI4_REPORT_ID]) {
464 case RMI_WRITE_REPORT_ID:
465 len = report[HID_RMI4_WRITE_OUTPUT_COUNT];
466 data = &report[HID_RMI4_WRITE_OUTPUT_DATA];
467 addr = (report[HID_RMI4_WRITE_OUTPUT_ADDR] & 0xFF)
468 | ((report[HID_RMI4_WRITE_OUTPUT_ADDR + 1] & 0xFF) << 8);
469 fprintf(stdout, "Write Report:\n");
470 fprintf(stdout, "Address = 0x%02X\n", addr);
471 fprintf(stdout, "Length = 0x%02X\n", len);
472 break;
473 case RMI_READ_ADDR_REPORT_ID:
474 addr = (report[HID_RMI4_READ_OUTPUT_ADDR] & 0xFF)
475 | ((report[HID_RMI4_READ_OUTPUT_ADDR + 1] & 0xFF) << 8);
476 len = (report[HID_RMI4_READ_OUTPUT_COUNT] & 0xFF)
477 | ((report[HID_RMI4_READ_OUTPUT_COUNT + 1] & 0xFF) << 8);
478 fprintf(stdout, "Read Request (Output Report):\n");
479 fprintf(stdout, "Address = 0x%02X\n", addr);
480 fprintf(stdout, "Length = 0x%02X\n", len);
481 return;
482 break;
483 case RMI_READ_DATA_REPORT_ID:
484 len = report[HID_RMI4_READ_INPUT_COUNT];
485 data = &report[HID_RMI4_READ_INPUT_DATA];
486 fprintf(stdout, "Read Data Report:\n");
487 fprintf(stdout, "Length = 0x%02X\n", len);
488 break;
489 case RMI_ATTN_REPORT_ID:
490 fprintf(stdout, "Attention Report:\n");
491 len = 28;
492 data = &report[HID_RMI4_ATTN_DATA];
493 fprintf(stdout, "Interrupt Sources: 0x%02X\n",
494 report[HID_RMI4_ATTN_INTERUPT_SOURCES]);
495 break;
496 default:
497 fprintf(stderr, "Unknown Report: ID 0x%02x\n", report[HID_RMI4_REPORT_ID]);
498 return;
499 }
500
501 fprintf(stdout, "Data:\n");
502 for (i = 0; i < len; ++i) {
503 fprintf(stdout, "0x%02X ", data[i]);
504 if (i % 8 == 7) {
505 fprintf(stdout, "\n");
506 }
507 }
508 fprintf(stdout, "\n\n");
Andrew Duggan64d6e592014-07-01 13:02:10 -0700509}
Andrew Dugganbef9c2d2015-05-06 17:45:48 -0700510
Andrew Duggan2c24adb2015-05-06 18:18:06 -0700511// Print protocol specific device information
512void HIDDevice::PrintDeviceInfo()
513{
514 fprintf(stdout, "HID device info:\nBus: %s Vendor: 0x%04x Product: 0x%04x\n",
515 m_info.bustype == BUS_I2C ? "I2C" : "USB", m_info.vendor, m_info.product);
Andrew Dugganb6302c32015-05-07 11:07:22 -0700516 fprintf(stdout, "Report sizes: input: %ld output: %ld\n", (unsigned long)m_inputReportSize,
517 (unsigned long)m_outputReportSize);
Andrew Duggan2c24adb2015-05-06 18:18:06 -0700518}
519
Andrew Dugganbef9c2d2015-05-06 17:45:48 -0700520bool WriteDeviceNameToFile(const char * file, const char * str)
521{
522 int fd;
523 ssize_t size;
524
525 fd = open(file, O_WRONLY);
526 if (fd < 0)
527 return false;
528
529 for (;;) {
530 size = write(fd, str, 19);
531 if (size < 0) {
532 if (errno == EINTR)
533 continue;
534
535 return false;
536 }
537 break;
538 }
539 close(fd);
540
541 return true;
542}
543
544void HIDDevice::RebindDriver()
545{
546 int bus = m_info.bustype;
547 int vendor = m_info.vendor;
548 int product = m_info.product;
549 std::string hidDeviceName;
550 std::string transportDeviceName;
551 std::string driverPath;
552 std::string bindFile;
553 std::string unbindFile;
554 std::string hidrawFile;
555
556 Close();
557
558 if (!LookupHidDeviceName(bus, vendor, product, hidDeviceName)) {
559 fprintf(stderr, "Failed to find HID device name for the specified device: bus (0x%x) vendor: (0x%x) product: (0x%x)\n",
560 bus, vendor, product);
561 return;
562 }
563
Andrew Dugganb6302c32015-05-07 11:07:22 -0700564 if (!FindTransportDevice(bus, hidDeviceName, transportDeviceName, driverPath)) {
Andrew Dugganbef9c2d2015-05-06 17:45:48 -0700565 fprintf(stderr, "Failed to find the transport device / driver for %s\n", hidDeviceName.c_str());
566 return;
567 }
568
569 bindFile = driverPath + "bind";
570 unbindFile = driverPath + "unbind";
571
572 if (!WriteDeviceNameToFile(unbindFile.c_str(), transportDeviceName.c_str())) {
573 fprintf(stderr, "Failed to unbind HID device %s: %s\n",
574 transportDeviceName.c_str(), strerror(errno));
575 return;
576 }
577
578 if (!WriteDeviceNameToFile(bindFile.c_str(), transportDeviceName.c_str())) {
579 fprintf(stderr, "Failed to bind HID device %s: %s\n",
580 transportDeviceName.c_str(), strerror(errno));
581 return;
582 }
583
Andrew Dugganb6302c32015-05-07 11:07:22 -0700584 // The hid device id has changed since this is now a new hid device. Now we have to look up the new name.
Andrew Dugganbef9c2d2015-05-06 17:45:48 -0700585 if (!LookupHidDeviceName(bus, vendor, product, hidDeviceName)) {
586 fprintf(stderr, "Failed to find HID device name for the specified device: bus (0x%x) vendor: (0x%x) product: (0x%x)\n",
587 bus, vendor, product);
588 return;
589 }
590
591 if (!FindHidRawFile(hidDeviceName, hidrawFile)) {
592 fprintf(stderr, "Failed to find the hidraw device file for %s\n", hidDeviceName.c_str());
593 return;
594 }
595
596 Open(hidrawFile.c_str());
597}
598
Andrew Dugganb6302c32015-05-07 11:07:22 -0700599bool HIDDevice::FindTransportDevice(int bus, std::string & hidDeviceName,
Andrew Dugganbef9c2d2015-05-06 17:45:48 -0700600 std::string & transportDeviceName, std::string & driverPath)
601{
602 std::string devicePrefix = "/sys/bus/";
603 std::string devicePath;
604 struct dirent * devicesDirEntry;
605 DIR * devicesDir;
606 struct dirent * devDirEntry;
607 DIR * devDir;
608 bool deviceFound = false;
609 ssize_t sz;
610
611 if (bus == BUS_I2C) {
612 devicePrefix += "i2c/";
613 driverPath = devicePrefix + "drivers/i2c_hid/";
614 } else {
615 devicePrefix += "usb/";
616 driverPath = devicePrefix + "drivers/usbhid/";
617 }
618 devicePath = devicePrefix + "devices/";
619
620 devicesDir = opendir(devicePath.c_str());
621 if (!devicesDir)
622 return false;
623
624 while((devicesDirEntry = readdir(devicesDir)) != NULL) {
625 if (devicesDirEntry->d_type != DT_LNK)
626 continue;
627
628 char buf[PATH_MAX];
629
630 sz = readlinkat(dirfd(devicesDir), devicesDirEntry->d_name, buf, PATH_MAX);
631 if (sz < 0)
632 continue;
633
634 buf[sz] = 0;
635
636 std::string fullLinkPath = devicePath + buf;
637 devDir = opendir(fullLinkPath.c_str());
638 if (!devDir) {
639 fprintf(stdout, "opendir failed\n");
640 continue;
641 }
642
643 while ((devDirEntry = readdir(devDir)) != NULL) {
644 if (!strcmp(devDirEntry->d_name, hidDeviceName.c_str())) {
645 transportDeviceName = devicesDirEntry->d_name;
646 deviceFound = true;
647 break;
648 }
649 }
650 closedir(devDir);
651
652 if (deviceFound)
653 break;
654 }
655 closedir(devicesDir);
656
657 return deviceFound;
658}
659
660bool HIDDevice::LookupHidDeviceName(int bus, int vendorId, int productId, std::string & deviceName)
661{
662 bool ret = false;
663 struct dirent * devDirEntry;
664 DIR * devDir;
665 char devicePrefix[15];
666
667 snprintf(devicePrefix, 15, "%04X:%04X:%04X", bus, vendorId, productId);
668
669 devDir = opendir("/sys/bus/hid/devices");
670 if (!devDir)
671 return false;
672
673 while ((devDirEntry = readdir(devDir)) != NULL) {
674 if (!strncmp(devDirEntry->d_name, devicePrefix, 14)) {
675 deviceName = devDirEntry->d_name;
676 ret = true;
677 break;
678 }
679 }
680 closedir(devDir);
681
682 return ret;
683}
684
685bool HIDDevice::FindHidRawFile(std::string & deviceName, std::string & hidrawFile)
686{
687 bool ret = false;
688 char hidrawDir[PATH_MAX];
689 struct dirent * devDirEntry;
690 DIR * devDir;
691
692 snprintf(hidrawDir, PATH_MAX, "/sys/bus/hid/devices/%s/hidraw", deviceName.c_str());
693
694 devDir = opendir(hidrawDir);
695 if (!devDir)
696 return false;
697
698 while ((devDirEntry = readdir(devDir)) != NULL) {
699 if (!strncmp(devDirEntry->d_name, "hidraw", 6)) {
700 hidrawFile = std::string("/dev/") + devDirEntry->d_name;
701 ret = true;
702 break;
703 }
704 }
705 closedir(devDir);
706
707 return ret;
Andrew de los Reyes242ea832015-09-04 14:40:06 -0700708}