/* * The MIT License (MIT) * * Copyright (c) 2014-2015 Gary Rowe * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ package org.hid4java; import org.hid4java.jna.HidApi; import org.hid4java.jna.HidDeviceInfoStructure; import org.hid4java.jna.HidDeviceStructure; /** *
* High level wrapper to provide the following to API consumers: *
*Open this device and obtain a device structure
* @return True if the device was successfully opened */ public boolean open() { hidDeviceStructure = HidApi.open(path); return hidDeviceStructure != null; } /** * @return True if the device structure is present */ public boolean isOpen() { return hidDeviceStructure != null; } /** ** Close this device freeing the HidApi resources *
*/ public void close() { if (!isOpen()) { return; } HidApi.close(hidDeviceStructure); hidDeviceStructure = null; } /** ** Set the device handle to be non-blocking *
* ** In non-blocking mode calls to hid_read() will return immediately with a * value of 0 if there is no data to be read. In blocking mode, hid_read() * will wait (block) until there is data to read before returning *
* ** Non-blocking can be turned on and off at any time *
* * @param nonBlocking True if non-blocking mode is required */ public void setNonBlocking(boolean nonBlocking) { if (!isOpen()) { throw new IllegalStateException("Device has not been opened"); } HidApi.setNonBlocking(hidDeviceStructure, nonBlocking); } /** ** Read an Input report from a HID device *
** Input reports are returned to the host through the INTERRUPT IN endpoint. * The first byte will contain the Report number if the device uses numbered * reports *
* * @param data The buffer to read into * * @return The actual number of bytes read and -1 on error. If no packet was * available to be read and the handle is in non-blocking mode, this * function returns 0. */ public int read(byte[] data) { if (!isOpen()) { throw new IllegalStateException("Device has not been opened"); } return HidApi.read(hidDeviceStructure, data); } /** ** Read an Input report from a HID device with timeout *
* * @param bytes The buffer to read into * @param timeoutMillis The number of milliseconds to wait before giving up * * @return The actual number of bytes read and -1 on error. If no packet was * available to be read within the timeout period returns 0. */ public int read(byte[] bytes, int timeoutMillis) { if (!isOpen()) { throw new IllegalStateException("Device has not been opened"); } return HidApi.read(hidDeviceStructure, bytes, timeoutMillis); } /** ** Get a feature report from a HID device *
** Under the covers the HID library will set the first byte of data[] to the * Report ID of the report to be read. Upon return, the first byte will * still contain the Report ID, and the report data will start in data[1] *
** This method handles all the wide string and array manipulation for you *
* * @param data The buffer to contain the report * @param reportId The report ID (or (byte) 0x00) * * @return The number of bytes read plus one for the report ID (which has * been removed from the first byte), or -1 on error. */ public int getFeatureReport(byte[] data, byte reportId) { if (!isOpen()) { throw new IllegalStateException("Device has not been opened"); } return HidApi.getFeatureReport(hidDeviceStructure, data, reportId); } /** ** Send a Feature report to the device *
* ** Under the covers, feature reports are sent over the Control endpoint as a * Set_Report transfer. The first byte of data[] must contain the Report ID. * For devices which only support a single report, this must be set to 0x0. * The remaining bytes contain the report data *
** Since the Report ID is mandatory, calls to hid_send_feature_report() will * always contain one more byte than the report contains. For example, if a * hid report is 16 bytes long, 17 bytes must be passed to * hid_send_feature_report(): the Report ID (or 0x0, for devices which do * not use numbered reports), followed by the report data (16 bytes). In * this example, the length passed in would be 17 *
* ** This method handles all the array manipulation for you *
* * @param data The feature report data (will be widened and have the report * ID pre-pended) * @param reportId The report ID (or (byte) 0x00) * * @return This function returns the actual number of bytes written and -1 * on error. */ public int sendFeatureReport(byte[] data, byte reportId) { if (!isOpen()) { throw new IllegalStateException("Device has not been opened"); } return HidApi.sendFeatureReport(hidDeviceStructure, data, reportId); } /** ** Get a string from a HID device, based on its string index *
* * @param index The index * * @return The string */ public String getIndexedString(int index) { return HidApi.getIndexedString(hidDeviceStructure, index); } public int write(byte[] message, int packetLength, byte reportId) { if (!isOpen()) { throw new IllegalStateException("Device has not been opened"); } int result = HidApi.write(hidDeviceStructure, message, packetLength, reportId); // Update HID manager hidDeviceManager.afterDeviceWrite(); return result; } public String getLastErrorMessage() { return HidApi.getLastErrorMessage(hidDeviceStructure); } /** * @param vendorId The vendor ID * @param productId The product ID * @param serialNumber The serial number * * @return True if the device matches the given the combination */ public boolean isVidPidSerial(int vendorId, int productId, String serialNumber) { if(serialNumber == null) return (vendorId == 0 || this.vendorId == vendorId) && (productId == 0 || this.productId == productId); else return (vendorId == 0 || this.vendorId == vendorId) && (productId == 0 || this.productId == productId) && ( this.serialNumber.equals(serialNumber)); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HidDevice hidDevice = (HidDevice) o; return path.equals(hidDevice.path); } @Override public int hashCode() { return path.hashCode(); } @Override public String toString() { return "HidDevice [path=" + path + ", vendorId=0x" + Integer.toHexString(vendorId) + ", productId=0x" + Integer.toHexString(productId) + ", serialNumber=" + serialNumber + ", releaseNumber=0x" + Integer.toHexString(releaseNumber) + ", manufacturer=" + manufacturer + ", product=" + product + ", usagePage=0x" + Integer.toHexString(usagePage) + ", usage=0x" + Integer.toHexString(usage) + ", interfaceNumber=" + interfaceNumber + "]"; } }