1 /*
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 
18 #include <stdio.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <dirent.h>
23 #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 
43 enum 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 
49 enum 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 
56 #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 
68 #define SYNAPTICS_VENDOR_ID			0x06cb
69 
Open(const char * filename)70 int HIDDevice::Open(const char * filename)
71 {
72 	int rc;
73 	int desc_size;
74 
75 	if (!filename)
76 		return -EINVAL;
77 
78 	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 
98 	if (m_info.vendor != SYNAPTICS_VENDOR_ID) {
99 		errno = -ENODEV;
100 		return -1;
101 	}
102 
103 	ParseReportSizes();
104 
105 	m_inputReport = new unsigned char[m_inputReportSize]();
106 	if (!m_inputReport) {
107 		errno = -ENOMEM;
108 		return -1;
109 	}
110 
111 	m_outputReport = new unsigned char[m_outputReportSize]();
112 	if (!m_outputReport) {
113 		errno = -ENOMEM;
114 		return -1;
115 	}
116 
117 	m_readData = new unsigned char[m_inputReportSize]();
118 	if (!m_readData) {
119 		errno = -ENOMEM;
120 		return -1;
121 	}
122 
123 	m_attnData = new unsigned char[m_inputReportSize]();
124 	if (!m_attnData) {
125 		errno = -ENOMEM;
126 		return -1;
127 	}
128 
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 
ParseReportSizes()138 void 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 					if (i + 1 >= m_rptDesc.size)
188 						return;
189 					reportSize = m_rptDesc.value[++i];
190 					continue;
191 				}
192 
193 				if (m_rptDesc.value[i] == 0x95) {
194 					if (i + 1 >= m_rptDesc.size)
195 						return;
196 					reportCount = m_rptDesc.value[++i];
197 					continue;
198 				}
199 
200 				if (m_rptDesc.value[i] == HID_REPORT_TYPE_INPUT)
201 					hidReportType = HID_REPORT_TYPE_INPUT;
202 
203 				if (m_rptDesc.value[i] == HID_REPORT_TYPE_OUTPUT)
204 					hidReportType = HID_REPORT_TYPE_OUTPUT;
205 
206 				if (m_rptDesc.value[i] == HID_REPORT_TYPE_FEATURE) {
207 					hidReportType = HID_REPORT_TYPE_FEATURE;
208 				}
209 			}
210 		}
211 
212 		if (i + 2 >= m_rptDesc.size)
213 			return;
214 		if (m_rptDesc.value[i] == 0x06 && m_rptDesc.value[i + 1] == 0x00
215 						&& m_rptDesc.value[i + 2] == 0xFF) {
216 			isVendorSpecific = true;
217 			i += 2;
218 		}
219 	}
220 }
221 
Read(unsigned short addr,unsigned char * buf,unsigned short len)222 int HIDDevice::Read(unsigned short addr, unsigned char *buf, unsigned short len)
223 {
224 	ssize_t count;
225 	size_t bytesReadPerRequest;
226 	size_t bytesInDataReport;
227 	size_t totalBytesRead;
228 	size_t bytesPerRequest;
229 	size_t bytesWritten;
230 	size_t bytesToRequest;
231 	int reportId;
232 	int rc;
233 
234 	if (!m_deviceOpen)
235 		return -1;
236 
237 	if (m_bytesPerReadRequest)
238 		bytesPerRequest = m_bytesPerReadRequest;
239 	else
240 		bytesPerRequest = len;
241 
242 	for (totalBytesRead = 0; totalBytesRead < len; totalBytesRead += bytesReadPerRequest) {
243 		count = 0;
244 		if ((len - totalBytesRead) < bytesPerRequest)
245 			bytesToRequest = len % bytesPerRequest;
246 		else
247 			bytesToRequest = bytesPerRequest;
248 
249 		if (m_outputReportSize < HID_RMI4_READ_OUTPUT_COUNT + 2) {
250 			return -1;
251 		}
252 		m_outputReport[HID_RMI4_REPORT_ID] = RMI_READ_ADDR_REPORT_ID;
253 		m_outputReport[1] = 0; /* old 1 byte read count */
254 		m_outputReport[HID_RMI4_READ_OUTPUT_ADDR] = addr & 0xFF;
255 		m_outputReport[HID_RMI4_READ_OUTPUT_ADDR + 1] = (addr >> 8) & 0xFF;
256 		m_outputReport[HID_RMI4_READ_OUTPUT_COUNT] = bytesToRequest  & 0xFF;
257 		m_outputReport[HID_RMI4_READ_OUTPUT_COUNT + 1] = (bytesToRequest >> 8) & 0xFF;
258 
259 		m_dataBytesRead = 0;
260 
261 		for (bytesWritten = 0; bytesWritten < m_outputReportSize; bytesWritten += count) {
262 			m_bCancel = false;
263 			count = write(m_fd, m_outputReport + bytesWritten,
264 					m_outputReportSize - bytesWritten);
265 			if (count < 0) {
266 				if (errno == EINTR && m_deviceOpen && !m_bCancel)
267 					continue;
268 				else
269 					return count;
270 			}
271 			break;
272 		}
273 
274 		bytesReadPerRequest = 0;
275 		while (bytesReadPerRequest < bytesToRequest) {
276 			rc = GetReport(&reportId);
277 			if (rc > 0 && reportId == RMI_READ_DATA_REPORT_ID) {
278 				if (static_cast<ssize_t>(m_inputReportSize) <
279 				    std::max(HID_RMI4_READ_INPUT_COUNT,
280 					     HID_RMI4_READ_INPUT_DATA))
281 					return -1;
282 				bytesInDataReport = m_readData[HID_RMI4_READ_INPUT_COUNT];
283 				if (bytesInDataReport > bytesToRequest
284 				    || bytesReadPerRequest + bytesInDataReport > len)
285 					return -1;
286 				memcpy(buf + bytesReadPerRequest, &m_readData[HID_RMI4_READ_INPUT_DATA],
287 					bytesInDataReport);
288 				bytesReadPerRequest += bytesInDataReport;
289 				m_dataBytesRead = 0;
290 			}
291 		}
292 		addr += bytesPerRequest;
293 	}
294 
295 	return totalBytesRead;
296 }
297 
Write(unsigned short addr,const unsigned char * buf,unsigned short len)298 int HIDDevice::Write(unsigned short addr, const unsigned char *buf, unsigned short len)
299 {
300 	ssize_t count;
301 
302 	if (!m_deviceOpen)
303 		return -1;
304 
305 	if (static_cast<ssize_t>(m_outputReportSize) <
306 	    HID_RMI4_WRITE_OUTPUT_DATA + len)
307 		return -1;
308 	m_outputReport[HID_RMI4_REPORT_ID] = RMI_WRITE_REPORT_ID;
309 	m_outputReport[HID_RMI4_WRITE_OUTPUT_COUNT] = len;
310 	m_outputReport[HID_RMI4_WRITE_OUTPUT_ADDR] = addr & 0xFF;
311 	m_outputReport[HID_RMI4_WRITE_OUTPUT_ADDR + 1] = (addr >> 8) & 0xFF;
312 	memcpy(&m_outputReport[HID_RMI4_WRITE_OUTPUT_DATA], buf, len);
313 
314 	for (;;) {
315 		m_bCancel = false;
316 		count = write(m_fd, m_outputReport, m_outputReportSize);
317 		if (count < 0) {
318 			if (errno == EINTR && m_deviceOpen && !m_bCancel)
319 				continue;
320 			else
321 				return count;
322 		}
323 		return len;
324 	}
325 }
326 
SetMode(int mode)327 int HIDDevice::SetMode(int mode)
328 {
329 	int rc;
330 	char buf[2];
331 
332 	if (!m_deviceOpen)
333 		return -1;
334 
335 	buf[0] = 0xF;
336 	buf[1] = mode;
337 	rc = ioctl(m_fd, HIDIOCSFEATURE(2), buf);
338 	if (rc < 0) {
339 		perror("HIDIOCSFEATURE");
340 		return rc;
341 	}
342 
343 	return 0;
344 }
345 
Close()346 void HIDDevice::Close()
347 {
348 	if (!m_deviceOpen)
349 		return;
350 
351 	SetMode(HID_RMI4_MODE_MOUSE);
352 	m_deviceOpen = false;
353 	close(m_fd);
354 	m_fd = -1;
355 
356 	delete[] m_inputReport;
357 	m_inputReport = NULL;
358 	delete[] m_outputReport;
359 	m_outputReport = NULL;
360 	delete[] m_readData;
361 	m_readData = NULL;
362 	delete[] m_attnData;
363 	m_attnData = NULL;
364 }
365 
WaitForAttention(struct timeval * timeout,unsigned int source_mask)366 int HIDDevice::WaitForAttention(struct timeval * timeout, unsigned int source_mask)
367 {
368 	return GetAttentionReport(timeout, source_mask, NULL, NULL);
369 }
370 
GetAttentionReport(struct timeval * timeout,unsigned int source_mask,unsigned char * buf,unsigned int * len)371 int HIDDevice::GetAttentionReport(struct timeval * timeout, unsigned int source_mask,
372 					unsigned char *buf, unsigned int *len)
373 {
374 	int rc = 0;
375 	int reportId;
376 
377 	// Assume the Linux implementation of select with timeout set to the
378 	// time remaining.
379 	while (!timeout || (timeout->tv_sec != 0 || timeout->tv_usec != 0)) {
380 		rc = GetReport(&reportId, timeout);
381 		if (rc > 0) {
382 			if (reportId == RMI_ATTN_REPORT_ID) {
383 				// If a valid buffer is passed in then copy the data from
384 				// the attention report into it. If the buffer is
385 				// too small simply set *len to 0 to indicate nothing
386 				// was copied. Some callers won't care about the contents
387 				// of the report so failing to copy the data should not return
388 				// an error.
389 				if (buf && len) {
390 					if (*len >= m_inputReportSize) {
391 						*len = m_inputReportSize;
392 						memcpy(buf, m_attnData, *len);
393 					} else {
394 						*len = 0;
395 					}
396 				}
397 
398 				if (m_inputReportSize < HID_RMI4_ATTN_INTERUPT_SOURCES + 1)
399 					return -1;
400 
401 				if (source_mask & m_attnData[HID_RMI4_ATTN_INTERUPT_SOURCES])
402 					return rc;
403 			}
404 		} else {
405 			return rc;
406 		}
407 	}
408 
409 	return rc;
410 }
411 
GetReport(int * reportId,struct timeval * timeout)412 int HIDDevice::GetReport(int *reportId, struct timeval * timeout)
413 {
414 	ssize_t count = 0;
415 	fd_set fds;
416 	int rc;
417 
418 	if (!m_deviceOpen)
419 		return -1;
420 
421 	if (m_inputReportSize < HID_RMI4_REPORT_ID + 1)
422 		return -1;
423 
424 	for (;;) {
425 		FD_ZERO(&fds);
426 		FD_SET(m_fd, &fds);
427 
428 		rc = select(m_fd + 1, &fds, NULL, NULL, timeout);
429 		if (rc == 0) {
430 			return -ETIMEDOUT;
431 		} else if (rc < 0) {
432 			if (errno == EINTR && m_deviceOpen && !m_bCancel)
433 				continue;
434 			else
435 				return rc;
436 		} else if (rc > 0 && FD_ISSET(m_fd, &fds)) {
437 			size_t offset = 0;
438 			for (;;) {
439 				m_bCancel = false;
440 				count = read(m_fd, m_inputReport + offset, m_inputReportSize - offset);
441 				if (count < 0) {
442 					if (errno == EINTR && m_deviceOpen && !m_bCancel)
443 						continue;
444 					else
445 						return count;
446 				}
447 				offset += count;
448 				if (offset == m_inputReportSize)
449 					break;
450 			}
451 			count = offset;
452 		}
453 		break;
454 	}
455 
456 	if (reportId)
457 		*reportId = m_inputReport[HID_RMI4_REPORT_ID];
458 
459 	if (m_inputReport[HID_RMI4_REPORT_ID] == RMI_ATTN_REPORT_ID) {
460 		if (static_cast<ssize_t>(m_inputReportSize) < count)
461 			return -1;
462 		memcpy(m_attnData, m_inputReport, count);
463 	} else if (m_inputReport[HID_RMI4_REPORT_ID] == RMI_READ_DATA_REPORT_ID) {
464 		if (static_cast<ssize_t>(m_inputReportSize) < count)
465 			return -1;
466 		memcpy(m_readData, m_inputReport, count);
467 		m_dataBytesRead = count;
468 	}
469 	return 1;
470 }
471 
PrintReport(const unsigned char * report)472 void HIDDevice::PrintReport(const unsigned char *report)
473 {
474 	int i;
475 	int len = 0;
476 	const unsigned char * data;
477 	int addr = 0;
478 
479 	switch (report[HID_RMI4_REPORT_ID]) {
480 		case RMI_WRITE_REPORT_ID:
481 			len = report[HID_RMI4_WRITE_OUTPUT_COUNT];
482 			data = &report[HID_RMI4_WRITE_OUTPUT_DATA];
483 			addr = (report[HID_RMI4_WRITE_OUTPUT_ADDR] & 0xFF)
484 				| ((report[HID_RMI4_WRITE_OUTPUT_ADDR + 1] & 0xFF) << 8);
485 			fprintf(stdout, "Write Report:\n");
486 			fprintf(stdout, "Address = 0x%02X\n", addr);
487 			fprintf(stdout, "Length = 0x%02X\n", len);
488 			break;
489 		case RMI_READ_ADDR_REPORT_ID:
490 			addr = (report[HID_RMI4_READ_OUTPUT_ADDR] & 0xFF)
491 				| ((report[HID_RMI4_READ_OUTPUT_ADDR + 1] & 0xFF) << 8);
492 			len = (report[HID_RMI4_READ_OUTPUT_COUNT] & 0xFF)
493 				| ((report[HID_RMI4_READ_OUTPUT_COUNT + 1] & 0xFF) << 8);
494 			fprintf(stdout, "Read Request (Output Report):\n");
495 			fprintf(stdout, "Address = 0x%02X\n", addr);
496 			fprintf(stdout, "Length = 0x%02X\n", len);
497 			return;
498 			break;
499 		case RMI_READ_DATA_REPORT_ID:
500 			len = report[HID_RMI4_READ_INPUT_COUNT];
501 			data = &report[HID_RMI4_READ_INPUT_DATA];
502 			fprintf(stdout, "Read Data Report:\n");
503 			fprintf(stdout, "Length = 0x%02X\n", len);
504 			break;
505 		case RMI_ATTN_REPORT_ID:
506 			fprintf(stdout, "Attention Report:\n");
507 			len = 28;
508 			data = &report[HID_RMI4_ATTN_DATA];
509 			fprintf(stdout, "Interrupt Sources: 0x%02X\n",
510 				report[HID_RMI4_ATTN_INTERUPT_SOURCES]);
511 			break;
512 		default:
513 			fprintf(stderr, "Unknown Report: ID 0x%02x\n", report[HID_RMI4_REPORT_ID]);
514 			return;
515 	}
516 
517 	fprintf(stdout, "Data:\n");
518 	for (i = 0; i < len; ++i) {
519 		fprintf(stdout, "0x%02X ", data[i]);
520 		if (i % 8 == 7) {
521 			fprintf(stdout, "\n");
522 		}
523 	}
524 	fprintf(stdout, "\n\n");
525 }
526 
527 // Print protocol specific device information
PrintDeviceInfo()528 void HIDDevice::PrintDeviceInfo()
529 {
530 	fprintf(stdout, "HID device info:\nBus: %s Vendor: 0x%04x Product: 0x%04x\n",
531 		m_info.bustype == BUS_I2C ? "I2C" : "USB", m_info.vendor, m_info.product);
532 	fprintf(stdout, "Report sizes: input: %ld output: %ld\n", (unsigned long)m_inputReportSize,
533 		(unsigned long)m_outputReportSize);
534 }
535 
WriteDeviceNameToFile(const char * file,const char * str)536 bool WriteDeviceNameToFile(const char * file, const char * str)
537 {
538 	int fd;
539 	ssize_t size;
540 
541 	fd = open(file, O_WRONLY);
542 	if (fd < 0)
543 		return false;
544 
545 	for (;;) {
546 		size = write(fd, str, strlen(str));
547 		if (size < 0) {
548 			if (errno == EINTR)
549 				continue;
550 
551 			return false;
552 		}
553 		break;
554 	}
555 
556 	return close(fd) == 0 && size == static_cast<ssize_t>(strlen(str));
557 }
558 
RebindDriver()559 void HIDDevice::RebindDriver()
560 {
561 	int bus = m_info.bustype;
562 	int vendor = m_info.vendor;
563 	int product = m_info.product;
564 	std::string hidDeviceName;
565 	std::string transportDeviceName;
566 	std::string driverPath;
567 	std::string bindFile;
568 	std::string unbindFile;
569 	std::string hidrawFile;
570 	struct stat stat_buf;
571 	int rc;
572 	int i;
573 
574 	Close();
575 
576 	if (!LookupHidDeviceName(bus, vendor, product, hidDeviceName)) {
577 		fprintf(stderr, "Failed to find HID device name for the specified device: bus (0x%x) vendor: (0x%x) product: (0x%x)\n",
578 			bus, vendor, product);
579 		return;
580 	}
581 
582 	if (!FindTransportDevice(bus, hidDeviceName, transportDeviceName, driverPath)) {
583 		fprintf(stderr, "Failed to find the transport device / driver for %s\n", hidDeviceName.c_str());
584 		return;
585 	}
586 
587 	bindFile = driverPath + "bind";
588 	unbindFile = driverPath + "unbind";
589 
590 	if (!WriteDeviceNameToFile(unbindFile.c_str(), transportDeviceName.c_str())) {
591 		fprintf(stderr, "Failed to unbind HID device %s: %s\n",
592 			transportDeviceName.c_str(), strerror(errno));
593 		return;
594 	}
595 
596 	if (!WriteDeviceNameToFile(bindFile.c_str(), transportDeviceName.c_str())) {
597 		fprintf(stderr, "Failed to bind HID device %s: %s\n",
598 			transportDeviceName.c_str(), strerror(errno));
599 		return;
600 	}
601 
602 	// The hid device id has changed since this is now a new hid device. Now we have to look up the new name.
603 	if (!LookupHidDeviceName(bus, vendor, product, hidDeviceName)) {
604 		fprintf(stderr, "Failed to find HID device name for the specified device: bus (0x%x) vendor: (0x%x) product: (0x%x)\n",
605 			bus, vendor, product);
606 		return;
607 	}
608 
609 	if (!FindHidRawFile(hidDeviceName, hidrawFile)) {
610 		fprintf(stderr, "Failed to find the hidraw device file for %s\n", hidDeviceName.c_str());
611 		return;
612 	}
613 
614 	for (i = 0; i < 200; i++) {
615 		rc = stat(hidrawFile.c_str(), &stat_buf);
616 		if (!rc)
617 			break;
618 		Sleep(5);
619 	}
620 
621 	rc = Open(hidrawFile.c_str());
622 	if (rc)
623 		fprintf(stderr, "Failed to open device (%s) during rebind: %d: errno: %s (%d)\n",
624 				hidrawFile.c_str(), rc, strerror(errno), errno);
625 }
626 
FindTransportDevice(int bus,std::string & hidDeviceName,std::string & transportDeviceName,std::string & driverPath)627 bool HIDDevice::FindTransportDevice(int bus, std::string & hidDeviceName,
628 			std::string & transportDeviceName, std::string & driverPath)
629 {
630 	std::string devicePrefix = "/sys/bus/";
631 	std::string devicePath;
632 	struct dirent * devicesDirEntry;
633 	DIR * devicesDir;
634 	struct dirent * devDirEntry;
635 	DIR * devDir;
636 	bool deviceFound = false;
637 	ssize_t sz;
638 
639 	if (bus == BUS_I2C) {
640 		devicePrefix += "i2c/";
641 		driverPath = devicePrefix + "drivers/i2c_hid/";
642 	} else {
643 		devicePrefix += "usb/";
644 		driverPath = devicePrefix + "drivers/usbhid/";
645 	}
646 	devicePath = devicePrefix + "devices/";
647 
648 	devicesDir = opendir(devicePath.c_str());
649 	if (!devicesDir)
650 		return false;
651 
652 	while((devicesDirEntry = readdir(devicesDir)) != NULL) {
653 		if (devicesDirEntry->d_type != DT_LNK)
654 			continue;
655 
656 		char buf[PATH_MAX];
657 
658 		sz = readlinkat(dirfd(devicesDir), devicesDirEntry->d_name, buf, PATH_MAX);
659 		if (sz < 0)
660 			continue;
661 
662 		buf[sz] = 0;
663 
664 		std::string fullLinkPath = devicePath + buf;
665 		devDir = opendir(fullLinkPath.c_str());
666 		if (!devDir) {
667 			fprintf(stdout, "opendir failed\n");
668 			continue;
669 		}
670 
671 		while ((devDirEntry = readdir(devDir)) != NULL) {
672 			if (!strcmp(devDirEntry->d_name, hidDeviceName.c_str())) {
673 				transportDeviceName = devicesDirEntry->d_name;
674 				deviceFound = true;
675 				break;
676 			}
677 		}
678 		closedir(devDir);
679 
680 		if (deviceFound)
681 			break;
682 	}
683 	closedir(devicesDir);
684 
685 	return deviceFound;
686 }
687 
LookupHidDeviceName(int bus,int vendorId,int productId,std::string & deviceName)688 bool HIDDevice::LookupHidDeviceName(int bus, int vendorId, int productId, std::string & deviceName)
689 {
690 	bool ret = false;
691 	struct dirent * devDirEntry;
692 	DIR * devDir;
693 	char devicePrefix[15];
694 
695 	snprintf(devicePrefix, 15, "%04X:%04X:%04X", bus, vendorId, productId);
696 
697 	devDir = opendir("/sys/bus/hid/devices");
698 	if (!devDir)
699 		return false;
700 
701 	while ((devDirEntry = readdir(devDir)) != NULL) {
702 		if (!strncmp(devDirEntry->d_name, devicePrefix, 14)) {
703 			deviceName = devDirEntry->d_name;
704 			ret = true;
705 			break;
706 		}
707 	}
708 	closedir(devDir);
709 
710 	return ret;
711 }
712 
FindHidRawFile(std::string & deviceName,std::string & hidrawFile)713 bool HIDDevice::FindHidRawFile(std::string & deviceName, std::string & hidrawFile)
714 {
715 	bool ret = false;
716 	char hidrawDir[PATH_MAX];
717 	struct dirent * devDirEntry;
718 	DIR * devDir;
719 
720 	snprintf(hidrawDir, PATH_MAX, "/sys/bus/hid/devices/%s/hidraw", deviceName.c_str());
721 
722 	devDir = opendir(hidrawDir);
723 	if (!devDir)
724 		return false;
725 
726 	while ((devDirEntry = readdir(devDir)) != NULL) {
727 		if (!strncmp(devDirEntry->d_name, "hidraw", 6)) {
728 			hidrawFile = std::string("/dev/") + devDirEntry->d_name;
729 			ret = true;
730 			break;
731 		}
732 	}
733 	closedir(devDir);
734 
735 	return ret;
736 }
737