/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "HidRawDevice.h" #include "HidLog.h" #include "Utils.h" #include #include #include #include // HID_STRING_SIZE #include #include #include namespace android { namespace SensorHalExt { using HidUtil::HidItem; HidRawDevice::HidRawDevice( const std::string &devName, const std::unordered_set &usageSet) : mDevFd(-1), mMultiIdDevice(false), mValid(false) { // open device mDevFd = ::open(devName.c_str(), O_RDWR); // read write? if (mDevFd < 0) { LOG_E << "Error in open device node: " << errno << " (" << ::strerror(errno) << ")" << LOG_ENDL; return; } // get device information, including hid descriptor if (!populateDeviceInfo()) { LOG_E << "Error obtaining HidRaw device information" << LOG_ENDL; return; } if (!generateDigest(usageSet)) { LOG_E << "Cannot parse hid descriptor" << LOG_ENDL; return; } // digest error checking std::unordered_set reportIdSet; for (auto const &digest : mDigestVector) { for (auto const &packet : digest.packets) { if (mReportTypeIdMap.emplace( std::make_pair(packet.type, packet.id), &packet).second == false) { LOG_E << "Same type - report id pair (" << packet.type << ", " << packet.id << ")" << "is used by more than one usage collection" << LOG_ENDL; return; } reportIdSet.insert(packet.id); } } if (mReportTypeIdMap.empty()) { return; } if (reportIdSet.size() > 1) { if (reportIdSet.find(0) != reportIdSet.end()) { LOG_E << "Default report id 0 is not expected when more than one report id is found." << LOG_ENDL; return; } mMultiIdDevice = true; } else { // reportIdSet.size() == 1 mMultiIdDevice = !(reportIdSet.find(0) != reportIdSet.end()); } mValid = true; } HidRawDevice::~HidRawDevice() { if (mDevFd > 0) { ::close(mDevFd); mDevFd = -1; } } bool HidRawDevice::populateDeviceInfo() { HidDeviceInfo info; char buffer[HID_STRING_SIZE + 1]; if (mDevFd < 0) { return false; } // name if (ioctl(mDevFd, HIDIOCGRAWNAME(sizeof(buffer) - 1), buffer) < 0) { return false; } buffer[sizeof(buffer) - 1] = '\0'; info.name = buffer; // physical path if (ioctl(mDevFd, HIDIOCGRAWPHYS(sizeof(buffer) - 1), buffer) < 0) { return false; } buffer[sizeof(buffer) - 1] = '\0'; info.physicalPath = buffer; // raw device info hidraw_devinfo devInfo; if (ioctl(mDevFd, HIDIOCGRAWINFO, &devInfo) < 0) { return false; } switch (devInfo.bustype) { case BUS_USB: info.busType = "USB"; break; case BUS_HIL: info.busType = "HIL"; break; case BUS_BLUETOOTH: info.busType = "Bluetooth"; break; case BUS_VIRTUAL: info.busType = "Virtual"; break; default: info.busType = "Other"; break; } info.vendorId = devInfo.vendor; info.productId = devInfo.vendor; uint32_t descriptorSize; /* Get Report Descriptor Size */ if (ioctl(mDevFd, HIDIOCGRDESCSIZE, &descriptorSize) < 0) { return false; } struct hidraw_report_descriptor reportDescriptor; memset(&reportDescriptor, 0, sizeof(reportDescriptor)); info.descriptor.resize(descriptorSize); reportDescriptor.size = descriptorSize; if (ioctl(mDevFd, HIDIOCGRDESC, &reportDescriptor) < 0) { return false; } std::copy(reportDescriptor.value, reportDescriptor.value + descriptorSize, info.descriptor.begin()); mDeviceInfo = info; return true; } bool HidRawDevice::generateDigest(const std::unordered_set &usage) { if (mDeviceInfo.descriptor.empty()) { return false; } std::vector tokens = HidItem::tokenize(mDeviceInfo.descriptor); HidParser parser; if (!parser.parse(tokens)) { return false; } parser.filterTree(); mDigestVector = parser.generateDigest(usage); return mDigestVector.size() > 0; } bool HidRawDevice::isValid() { return mValid; } bool HidRawDevice::getFeature(uint8_t id, std::vector *out) { if (mDevFd < 0) { return false; } if (out == nullptr) { return false; } const HidParser::ReportPacket *packet = getReportPacket(HidParser::REPORT_TYPE_FEATURE, id); if (packet == nullptr) { LOG_E << "HidRawDevice::getFeature: unknown feature " << id << LOG_ENDL; return false; } size_t size = packet->getByteSize() + 1; // report id size std::lock_guard l(mIoBufferLock); if (mIoBuffer.size() < size) { mIoBuffer.resize(size); } mIoBuffer[0] = id; int res = ::ioctl(mDevFd, HIDIOCGFEATURE(size), mIoBuffer.data()); if (res < 0) { LOG_E << "HidRawDevice::getFeature: feature " << static_cast(id) << " ioctl returns " << res << " (" << ::strerror(res) << ")" << LOG_ENDL; return false; } if (static_cast(res) != size) { LOG_E << "HidRawDevice::getFeature: get feature " << static_cast(id) << " returned " << res << " bytes, does not match expected " << size << LOG_ENDL; return false; } if (mIoBuffer.front() != id) { LOG_E << "HidRawDevice::getFeature: get feature " << static_cast(id) << " result has header " << mIoBuffer.front() << LOG_ENDL; } out->resize(size - 1); std::copy(mIoBuffer.begin() + 1, mIoBuffer.begin() + size, out->begin()); return true; } bool HidRawDevice::setFeature(uint8_t id, const std::vector &in) { if (mDevFd < 0) { return false; } const HidParser::ReportPacket *packet = getReportPacket(HidParser::REPORT_TYPE_FEATURE, id); if (packet == nullptr) { LOG_E << "HidRawDevice::setFeature: Unknown feature " << id << LOG_ENDL; return false; } size_t size = packet->getByteSize(); if (size != in.size()) { LOG_E << "HidRawDevice::setFeature: set feature " << id << " size mismatch, need " << size << " bytes, have " << in.size() << " bytes" << LOG_ENDL; return false; } ++size; // report id byte std::lock_guard l(mIoBufferLock); if (mIoBuffer.size() < size) { mIoBuffer.resize(size); } mIoBuffer[0] = id; std::copy(in.begin(), in.end(), &mIoBuffer[1]); int res = ::ioctl(mDevFd, HIDIOCSFEATURE(size), mIoBuffer.data()); if (res < 0) { LOG_E << "HidRawDevice::setFeature: feature " << id << " ioctl returns " << res << " (" << ::strerror(res) << ")" << LOG_ENDL; return false; } return true; } bool HidRawDevice::sendReport(uint8_t id, std::vector &data) { if (mDevFd < 0) { return false; } const HidParser::ReportPacket *packet = getReportPacket(HidParser::REPORT_TYPE_OUTPUT, id); if (packet == nullptr) { LOG_E << "HidRawDevice::sendReport: unknown output " << id << LOG_ENDL; return false; } size_t size = packet->getByteSize(); if (size != data.size()) { LOG_E << "HidRawDevice::sendReport: send report " << id << " size mismatch, need " << size << " bytes, have " << data.size() << " bytes" << LOG_ENDL; return false; } int res; if (mMultiIdDevice) { std::lock_guard l(mIoBufferLock); ++size; if (mIoBuffer.size() < size) { mIoBuffer.resize(size); } mIoBuffer[0] = id; std::copy(mIoBuffer.begin() + 1, mIoBuffer.end(), data.begin()); res = ::write(mDevFd, mIoBuffer.data(), size); } else { res = ::write(mDevFd, data.data(), size); } if (res < 0) { LOG_E << "HidRawDevice::sendReport: output " << id << " write returns " << res << " (" << ::strerror(res) << ")" << LOG_ENDL; return false; } return true; } bool HidRawDevice::receiveReport(uint8_t *id, std::vector *data) { if (mDevFd < 0) { return false; } uint8_t buffer[256]; int res = ::read(mDevFd, buffer, 256); if (res < 0) { LOG_E << "HidRawDevice::receiveReport: read returns " << res << " (" << ::strerror(res) << ")" << LOG_ENDL; return false; } if (mMultiIdDevice) { if (!(res > 1)) { LOG_E << "read hidraw returns data too short, len: " << res << LOG_ENDL; return false; } data->resize(static_cast(res - 1)); std::copy(buffer + 1, buffer + res, data->begin()); *id = buffer[0]; } else { data->resize(static_cast(res)); std::copy(buffer, buffer + res, data->begin()); *id = 0; } return true; } const HidParser::ReportPacket *HidRawDevice::getReportPacket(unsigned int type, unsigned int id) { auto i = mReportTypeIdMap.find(std::make_pair(type, id)); return (i == mReportTypeIdMap.end()) ? nullptr : i->second; } } // namespace SensorHalExt } // namespace android