Home | History | Annotate | Download | only in rmidevice
      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 
     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 
    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 
    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 
    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 
    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 
    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 
    366 int HIDDevice::WaitForAttention(struct timeval * timeout, unsigned int source_mask)
    367 {
    368 	return GetAttentionReport(timeout, source_mask, NULL, NULL);
    369 }
    370 
    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 
    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 
    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
    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 
    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 
    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 
    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 
    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 
    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