com.embeddedunveiled.serial.hid.SerialComRawHID Maven / Gradle / Ivy
Show all versions of scm Show documentation
/*
* Author : Rishi Gupta
*
* This file is part of 'serial communication manager' library.
* Copyright (C) <2014-2016>
*
* This 'serial communication manager' is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* The 'serial communication manager' is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with 'serial communication manager'. If not, see .
*/
package com.embeddedunveiled.serial.hid;
import java.util.HashMap;
import java.util.TreeMap;
import com.embeddedunveiled.serial.ISerialComUSBHotPlugListener;
import com.embeddedunveiled.serial.SerialComException;
import com.embeddedunveiled.serial.SerialComManager;
import com.embeddedunveiled.serial.internal.HIDdevHandleInfo;
import com.embeddedunveiled.serial.internal.SerialComHIDJNIBridge;
import com.embeddedunveiled.serial.usb.SerialComUSBHID;
import com.embeddedunveiled.serial.util.SerialComUtil;
/* Executes as a worker thread waiting for input reports, reading whenever available and delivering
* them to the registered listener. */
final class HIDInputReportReader implements Runnable {
private int ret;
private long handle;
private long context;
private IHIDInputReportListener listener;
private byte[] inputReportBuffer;
private SerialComRawHID scrh;
public HIDInputReportReader(long handle, final IHIDInputReportListener listener, byte[] inputReportBuffer, long context, SerialComRawHID scrh) {
this.handle = handle;
this.listener = listener;
this.inputReportBuffer = inputReportBuffer;
this.context = context;
this.scrh = scrh;
}
@Override
public void run() {
while(true) {
try {
ret = 0;
ret = scrh.readInputReportR(handle, inputReportBuffer, context);
// deliver input report
if(ret > 0) {
listener.onNewInputReportAvailable(ret, inputReportBuffer);
}
} catch (SerialComException e) {
if(SerialComHID.EXP_UNBLOCK_HIDIO.equals(e.getExceptionMsg())) {
// this thread should exit as other thread indicated it to return.
return;
}
} catch (Exception e1) {
// do nothing
}
}
}
}
/**
* Contains APIs to communicate with a HID class device in raw mode. The reports sent/received
* will not be parsed. The application must understand meaning and format of each field in report
* exchanged.
*
* This API for human interface device communication uses kernel mode drivers provided by operating system.
*
* Applications may develop user space drivers using raw HID methods in this class.
*
*
*
* 1: Device discovery
* listHIDdevicesWithInfoR
*
* 3 : Information
* getManufacturerStringR
* getProductStringR
* getSerialNumberStringR
* getIndexedStringR
* findDriverServingHIDDeviceR
*
* 2 : Data/Configuration exchange
* writeOutputReportR
* readInputReportR
* readInputReportWithTimeoutR
* sendFeatureReportR
* getFeatureReportR
* createBlockingHIDIOContextR
* unblockBlockingHIDIOOperationR
* destroyBlockingIOContextR
*
* 4 : Miscellaneous
* openHidDeviceR
* closeHidDeviceR
* flushInputReportQueueR
* getReportDescriptorR
* getPhysicalDescriptorR
* formatReportToHexR
*
*
*
* @author Rishi Gupta
*/
public final class SerialComRawHID extends SerialComHID {
// used to synchronize access to treemap if caller can modify treemap.
private final Object lock = new Object();
// This provides guaranteed log(n) time complexity for the containsKey, get, put and remove operations.
// It maps opened handle of HID device to its information object.
private final TreeMap devInfo;
// Instead of linearly traversing tree to find context corresponding to listener, we maintain
// a hashmap to decrease look up time.
private final HashMap listenerToHandleMap;
/**
* Construct and allocates a new SerialComRawHID object with given details.
*
* @param mHIDJNIBridge interface class to native library for calling platform specific routines.
* @param osType operating system this library is running on.
* @throws SerialComException if the object can not be constructed.
*/
public SerialComRawHID(SerialComHIDJNIBridge mHIDJNIBridge, int osType) {
super(mHIDJNIBridge, osType);
devInfo = new TreeMap();
listenerToHandleMap = new HashMap();
}
/**
* Find all the device instances claiming to be HID device.
*
* Returns an array of SerialComHIDdevice objects containing information about HID devices
* as found by this library. The HID devices found may be USB HID or Bluetooth HID device.
* Application can call various methods on returned SerialComHIDdevice object to get specific
* information like vendor id and product id etc.
*
* The information about HID device returned includes, transport, vendor ID, product ID, serial
* number, product, manufacturer, USB bus number, USB device number, location ID etc. In situations
* where two or more devices with exactly same vendor ID, product ID and serial number are present
* into system, information like location can be used to further categories them into unique devices.
* Application can also use some custom protocol to identify devices that are of interest to them.
*
* If you know that the HID device is USB device than consider using listUSBHIDdevicesWithInfo
* method in SerialComUSBHID class.
*
* @return list of the HID devices with information about them or empty array if no device
* matching given criteria found.
* @throws SerialComException if an I/O error occurs.
*/
public SerialComHIDdevice[] listHIDdevicesWithInfoR() throws SerialComException {
int i = 0;
int numOfDevices = 0;
SerialComHIDdevice[] hidDevicesFound = null;
String[] hidDevicesInfo = mHIDJNIBridge.listHIDdevicesWithInfoR();
if(hidDevicesInfo != null) {
if(hidDevicesInfo.length < 3) {
return new SerialComHIDdevice[] { };
}
numOfDevices = hidDevicesInfo.length / 8;
hidDevicesFound = new SerialComHIDdevice[numOfDevices];
for(int x=0; x < numOfDevices; x++) {
hidDevicesFound[x] = new SerialComHIDdevice(hidDevicesInfo[i], hidDevicesInfo[i+1], hidDevicesInfo[i+2],
hidDevicesInfo[i+3], hidDevicesInfo[i+4], hidDevicesInfo[i+5], hidDevicesInfo[i+6],
hidDevicesInfo[i+7]);
i = i + 8;
}
return hidDevicesFound;
}else {
throw new SerialComException("Could not find HID devices. Please retry !");
}
}
/**
* Converts report read from HID device to hexadecimal string. This may be
* useful when report is to be passed to next level as hex data or report is to be
* feed into external HID report parser tool.
*
* @param report report to be converted into hex string.
* @param separator separator string to be placed between two consecutive bytes (useful
* when printing values on console).
* @return constructed hex string if report.length > 0 otherwise empty string.
* @throws IllegalArgumentException if report is null.
*/
public String formatReportToHexR(byte[] report, String separator) throws SerialComException {
return SerialComUtil.byteArrayToHexString(report, separator);
}
/**
* Opens a HID device for communication using its path name. Applications should first list HID devices
* to get the path of the desired device using methods like listUSBHIDdevicesWithInfo etc.
*
* Applications can register USB hot plug listener to get notified when the desired USB device
* is plugged into system. Once the listener is invoked indicating device is added, application
* can find the device node representing this USB-HID device and proceed to open it.
*
* In Linux it may be required to add correct udev rules so as to grant permission to
* access to the USB-HID device. Refer this udev rule file for MCP2200 as an example :
* https://github.com/RishiGupta12/serial-communication-manager/blob/master/tools-and-utilities/99-scm-mcp2200-hid.rules
*
* In Windows, a unique physical device object (PDO) is created for each Top Level Collection
* described by the Report Descriptor and there will be device instance for each Top Level
* Collection. This means same USB HID interface may have many HID device instances associated
* with it.
*
* Windows supports many top level collection and some of them might be opened in shared mode while
* may be available for exclusive access only. Some of the HID devices may be reserved for system use
* only and operating system may provide a dedicated framework/driver and API for it. Some devices
* need to be switched from keyboard emulation mode to HID mode to make them accessible by application.
*
* @param pathName device node full path for Unix-like OS and device instance for Windows
* (as obtained by listing HID devices).
* @param shared set to true if the device is to be opened in shared mode otherwise false
* for exclusive access.
* @return handle of the opened HID device.
* @throws SerialComException if an IO error occurs.
* @throws IllegalArgumentException if pathName is null or empty string.
* @see com.embeddedunveiled.serial.SerialComManager#registerUSBHotPlugEventListener(ISerialComUSBHotPlugListener, int, int, String)
*/
public long openHidDeviceR(final String pathName, boolean shared) throws SerialComException {
if(pathName == null) {
throw new IllegalArgumentException("Argument pathName can not be null !");
}
String pathNameVal = pathName.trim();
if(pathNameVal.length() == 0) {
throw new IllegalArgumentException("Argument pathName can not be empty string !");
}
long handle = mHIDJNIBridge.openHidDeviceR(pathNameVal, shared, osType);
if(handle < 0) {
/* JNI should have already thrown exception, this is an extra check to increase reliability of program. */
throw new SerialComException("Could not open the HID device " + pathNameVal + ". Please retry !");
}
// save data info internally for later use
synchronized(lock) {
devInfo.put(handle, new HIDdevHandleInfo(null));
}
return handle;
}
/**
* Closes a HID device.
*
* @param handle handle of the device to be closed.
* @return true if device's handle closed successfully.
* @throws SerialComException if fails to close the device or an IO error occurs.
* @throws IllegalStateException if application tries to close handle when input report listener still exist.
*/
public boolean closeHidDeviceR(long handle) throws SerialComException {
HIDdevHandleInfo info = devInfo.get(handle);
if(info == null) {
throw new SerialComException("Given handle does not represent a HID device opened through SCM !");
}
if(info.getInputReportListener() != null) {
throw new IllegalStateException("Closing device handle without unregistering input report listener is not allowed to prevent inconsistency !");
}
int ret = mHIDJNIBridge.closeHidDeviceR(handle);
if(ret < 0) {
throw new SerialComException("Could not close the given HID device. Please retry !");
}
// remove local data info
synchronized(lock) {
devInfo.remove(handle);
}
return true;
}
/**
* Prepares a context that should be passed to readInputReportR, unblockBlockingHIDIOOperationR
* and destroyBlockingIOContextR methods.
*
* Application must catch exception thrown by this method. When this method returns and
* exception with message SerialComHID.EXP_UNBLOCK_HIDIO is thrown, it indicates that the
* blocked read method was explicitly unblocked by another thread (possibly because it is
* going to close the device).
*
* @return context that should be passed to readInputReportR, unblockBlockingHIDIOOperationR and
* destroyBlockingIOContextR methods.
* @throws SerialComException if an I/O error occurs.
*/
public long createBlockingHIDIOContextR() throws SerialComException {
long ret = mHIDJNIBridge.createBlockingHIDIOContextR();
if(ret < 0) {
throw new SerialComException("Could not create blocking HID I/O context. Please retry !");
}
return ret;
}
/**
* Unblocks any blocked operation if it exist. This causes closing of HID device possible
* gracefully and return the worker thread that called blocking read/write to return and proceed
* as per application design.
*
* @param context context obtained from call to createBlockingHIDIOContextR method for blocking
* HID I/O operations.
* @return true if blocked operation was unblocked successfully.
* @throws SerialComException if an I/O error occurs.
*/
public boolean unblockBlockingHIDIOOperationR(long context) throws SerialComException {
int ret = mHIDJNIBridge.unblockBlockingHIDIOOperationR(context);
if(ret < 0) {
throw new SerialComException("Could not unblock the blocked HID I/O operation. Please retry !");
}
return true;
}
/**
* Destroys the context that was created by a call to createBlockingHIDIOContextR method for
* blocking I/O operations uses.
*
* @param context context obtained from call to createBlockingIOContext method for blocking
* HID I/O operations.
* @return true if the context gets destroyed successfully.
* @throws SerialComException if an I/O error occurs.
*/
public boolean destroyBlockingIOContextR(long context) throws SerialComException {
int ret = mHIDJNIBridge.destroyBlockingIOContextR(context);
if(ret < 0) {
throw new SerialComException("Could not destroy blocking HID I/O context. Please retry !");
}
return true;
}
/**
* Sends the given output report to the HID device. Report ID is used to uniquely identify the
* report.
*
* Output report (controls) may be a sink for application data, for example, an LED that indicates
* the state of a device. It can represent a command sent from application running on host to USB HID
* device for example to toggle a GPIO pin or vibrate the motor mounted on gamepad.
*
* If the HID device uses numbered report, reportID should be set to report number. If the HID
* device does not uses numbered reports reportID must be set to -1. The report (report array) should
* should contain only report bytes (it should not contain report ID).
*
* @param handle handle of the HID device to which this report will be sent.
* @param reportId unique identifier for the report type or -1 if device does not use report IDs.
* @param report report to be sent to the HID device.
* @return number of bytes sent to the HID device.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if report is null or empty array.
*/
public int writeOutputReportR(long handle, byte reportId, final byte[] report) throws SerialComException {
if(report == null) {
throw new IllegalArgumentException("Argumenet report can not be null !");
}
if(report.length == 0) {
throw new IllegalArgumentException("Argumenet report can not be of zero length !");
}
int ret = mHIDJNIBridge.writeOutputReportR(handle, reportId, report, report.length);
if(ret < 0) {
throw new SerialComException("Could not write output report to the HID device. Please retry !");
}
return ret;
}
/**
* Reads input report from the given HID device.
*
* The size of {@code reportBuffer} passed must be large enough to hold the expected number of bytes
* in input report and one more extra byte if the HID device uses numbered reports. The 1st byte will be
* report ID if device uses numbered reports otherwise the report data will begin at the first byte.
*
* If input report is read from device, it returns number of bytes read and places data bytes in
* given buffer. If the device uses numbered reports, first byte in reportBuffer array will be report
* number. If the device does not uses numbered reports, first byte in reportBuffer will be beginning
* of data itself.
*
* HID devices with custom firmware provide valid HID report descriptor to comply with USB
* standards and to make sure that class driver of operating system recognizes device and serve it.
* However, the data carried in reports may have different meaning and interpretation than what was
* described in report descriptor. This is the case mainly when developing custom application HID device
* which can not be strictly categorized as a HID device, however, leverages HID specifications and
* API to communicate with the host system. Vendors provide a document describing how to interpret a
* particular byte in report received from device or how to construct an output report.
*
* @param handle handle of the HID device from whom input report is to be read.
* @param reportBuffer byte buffer in which input report will be saved.
* @param context context obtained by a call to createBlockingIOContext method.
* @return number of bytes read from HID device.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if reportBuffer is null or if length is negative.
*/
public int readInputReportR(long handle, byte[] reportBuffer, long context) throws SerialComException {
if(reportBuffer == null) {
throw new IllegalArgumentException("Argumenet reportBuffer can not be null !");
}
int ret = mHIDJNIBridge.readInputReportR(handle, reportBuffer, reportBuffer.length, context);
if(ret < 0) {
throw new SerialComException("Could not read input report from HID device. Please retry !");
}
return ret;
}
/**
* Try to read input report from HID device within the given timeout limit.
*
* The size of {@code reportBuffer} passed must be large enough to hold the expected number of bytes
* in input report and one more extra byte if the HID device uses numbered reports. The 1st byte will be
* report ID if device uses numbered reports otherwise the report data will begin at the first byte.
*
* If input report is read from HID device, it returns number of bytes read and places data bytes in
* given buffer. If there was no data to read it returns 0.
*
* Input report (controls) are sources of data for application running on host processor (USB Host side)
* for example X and Y coordinates obtained from touch screen or state of a GPIO pin. It can also be a
* response to a command sent previously as output report.
*
* @param handle handle of the HID device from whom input report is to be read.
* @param reportBuffer byte buffer in which input report will be saved.
* @param timeoutValue time in milliseconds after which read must return with whatever data is read
* till that time or no data read at all.
* @return number of bytes read from HID device.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if reportBuffer is null or if length is negative.
*/
public int readInputReportWithTimeoutR(long handle, byte[] reportBuffer, int timeoutValue)
throws SerialComException {
if(reportBuffer == null) {
throw new IllegalArgumentException("Argumenet reportBuffer can not be null !");
}
int ret = mHIDJNIBridge.readInputReportWithTimeoutR(handle, reportBuffer, reportBuffer.length, timeoutValue);
if(ret < 0) {
throw new SerialComException("Could not read input report from HID device. Please retry !");
}
return ret;
}
/**
* Send a feature report to the HID device. If the HID device uses numbered reports, set reportID
* to report number. If the HID device does not uses numbered reports set reportID to -1. The report
* byte array should contain only report data bytes.
*
* Typically, feature reports are sent/received for configuring USB device or USB host at application
* start-up, or for sending/receiving special event or state information, or for saving any data item that
* application wish to write in HID device and read it back may be some time later.
*
* @param handle handle of the HID device to which this feature report will be sent.
* @param reportId unique identifier for the report type or -1 if not applicable.
* @param report feature report to be sent to the HID device.
* @return number of bytes sent to HID device.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if report is null or empty array.
*/
public int sendFeatureReportR(long handle, byte reportId, final byte[] report) throws SerialComException {
if(report == null) {
throw new IllegalArgumentException("Argumenet report can not be null !");
}
if(report.length == 0) {
throw new IllegalArgumentException("Argumenet report can not be of zero length !");
}
int ret = mHIDJNIBridge.sendFeatureReportR(handle, reportId, report, report.length);
if(ret < 0) {
throw new SerialComException("Could not send feature report to HID device. Please retry !");
}
return ret;
}
/**
* Read a feature report to the HID device. If the HID device uses numbered reports, set reportID
* to report number. If the HID device does not uses numbered reports set reportID to -1. If the
* featured report is read from HID device, data read will be placed in report byte array. This
* array will contain feature report (excluding report ID).
*
* Typically, feature reports are sent/received for configuring USB device or USB host at application
* start-up, or for sending/receiving special event or state information, or for saving any data item that
* application wish to write in HID device and read it back may be some time later.
*
* @param handle handle of the HID device from whom feature report is to be read.
* @param reportId unique identifier for the report type or -1 if not applicable.
* @param report byte type buffer where feature report will be saved.
* @return number of bytes read from HID device.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if reportBuffer is null or its length is zero.
*/
public int getFeatureReportR(long handle, byte reportId, final byte[] report) throws SerialComException {
if(report == null) {
throw new IllegalArgumentException("Argumenet report can not be null !");
}
int ret = mHIDJNIBridge.getFeatureReportR(handle, reportId, report, report.length);
if(ret < 0) {
throw new SerialComException("Could not get feature report from HID device. Please retry !");
}
return ret;
}
/**
* Gives the manufacturer of the HID device.
*
* @param handle handle of the HID device whose manufacturer is to be found.
* @return manufacturer name string.
* @throws SerialComException if an I/O error occurs.
*/
public String getManufacturerStringR(long handle) throws SerialComException {
String ret = mHIDJNIBridge.getManufacturerStringR(handle);
if(ret == null) {
throw new SerialComException("Could not get the manufacturer string from the HID device. Please retry !");
}
return ret;
}
/**
* Gives the product name of the HID device.
*
* @param handle handle of the HID device whose product name is to be found.
* @return product name string of the HID device.
* @throws SerialComException if an I/O error occurs.
*/
public String getProductStringR(long handle) throws SerialComException {
String ret = mHIDJNIBridge.getProductStringR(handle);
if(ret == null) {
throw new SerialComException("Could not get the product string from the HID device. Please retry !");
}
return ret;
}
/**
* Gives the serial number of the HID device.
*
* @param handle handle of the HID device whose serial number is to be found.
* @return serial number string of the HID device.
* @throws SerialComException if an I/O error occurs.
*/
public String getSerialNumberStringR(long handle) throws SerialComException {
String ret = mHIDJNIBridge.getSerialNumberStringR(handle);
if(ret == null) {
throw new SerialComException("Could not get the serial number string from the HID device. Please retry !");
}
return ret;
}
/**
* Gives the string at the given index of string descriptor from HID device.
*
* Supported on Windows only as serial communication manager library does not use any
* user space drivers.
*
* @param handle handle of the HID device from whom indexed string is to be read.
* @return string at given index read from the HID device.
* @throws SerialComException if an I/O error occurs.
*/
public String getIndexedStringR(long handle, int index) throws SerialComException {
if(osType == SerialComManager.OS_WINDOWS) {
String ret = mHIDJNIBridge.getIndexedStringR(handle, index);
if(ret == null) {
throw new SerialComException("Could not get the string at given index from the HID device. Please retry !");
}
return ret;
}else {
throw new SerialComException("Not supported on this operating system !");
}
}
/**
* Gives the name of the driver who is driving the given HID device.
*
* @param hidDeviceNode device node (port name) for HID device whose driver is to be found.
* @return name of driver serving given HID device.
* @throws SerialComException if operation can not be completed successfully.
* @throws IllegalArgumentException if argument hidDeviceNode is null or is an empty string.
*/
public String findDriverServingHIDDeviceR(String hidDeviceNode) throws SerialComException {
if(hidDeviceNode == null) {
throw new IllegalArgumentException("Argument hidDeviceNode can not be null !");
}
if(hidDeviceNode.length() == 0) {
throw new IllegalArgumentException("Argument hidDeviceNode can not be empty string !");
}
if(hidDeviceNode.length() > 256) {
// Linux may have 256 as maximum length of file name.
throw new IllegalArgumentException("Argument hidDeviceNode string can not be greater than 256 in length !");
}
String driverName = mHIDJNIBridge.findDriverServingHIDDeviceR(hidDeviceNode);
if(driverName == null) {
throw new SerialComException("Failed to find driver serving the given HID device. Please retry !");
}
return driverName;
}
/**
* Gives the report descriptor as supplied by device itself.
*
* @param handle handle of the device whose report descriptor is to be obtained.
* @return HID report descriptor as array of bytes otherwise empty array.
* @throws SerialComException if operation can not be completed successfully.
*/
public byte[] getReportDescriptorR(long handle) throws SerialComException {
byte[] reportDescriptorRead = mHIDJNIBridge.getReportDescriptorR(handle);
if(reportDescriptorRead != null) {
return reportDescriptorRead;
}
return new byte[0];
}
/**
* Gives the physical descriptor for the given HID device.
*
* Physical Descriptors are entirely optional. They add complexity and offer very little in
* return for most devices. However, some devices, particularly those with a large number of
* identical controls (for example, buttons) will find that Physical Descriptors help different
* applications assign functionality to these controls in a more consistent manner.
*
* @param handle handle of the device whose physical descriptor is to be obtained.
* @return HID physical descriptor as array of bytes otherwise empty array.
* @throws SerialComException if operation can not be completed successfully.
*/
public byte[] getPhysicalDescriptorR(long handle) throws SerialComException {
byte[] physicalDescriptorRead = mHIDJNIBridge.getPhysicalDescriptorR(handle);
if(physicalDescriptorRead != null) {
return physicalDescriptorRead;
}
return new byte[0];
}
/**
* Deletes all the input reports from input report buffer maintained by operating system.
*
* @param handle handle of the device whose input report queue is to be flushed.
* @return true on success.
* @throws SerialComException if operation can not be completed successfully.
*/
public boolean flushInputReportQueueR(long handle) throws SerialComException {
int ret = mHIDJNIBridge.flushInputReportQueueR(handle);
if (ret < 0) {
throw new SerialComException("Could not flush the input report queue. Please retry !");
}
return true;
}
/**
* Registers a listener which will be invoked whenever an input report is available to read.
*
* @param handle of the HID device for which input reports are to be listened.
* @param listener instance of class which implements IHIDInputReportListener interface.
* @param inputReportBuffer byte buffer that will contain report read from HID device.
* @return true on success.
* @throws SerialComException if the registration fails due to some reason.
* @throws IllegalArgumentException if input report listener is null.
* @throws IllegalStateException if input report listener already exist for given handle.
*/
public boolean registerInputReportListener(long handle, final IHIDInputReportListener listener, byte[] inputReportBuffer) throws SerialComException {
HIDdevHandleInfo info = devInfo.get(handle);
if(info != null) {
if(info.getInputReportListener() != null) {
throw new IllegalStateException("Input report listener already exist for given handle !");
}
}
if(listener == null) {
throw new IllegalArgumentException("Argument listener can not be null !");
}
long context = createBlockingHIDIOContextR();
Thread dataReaderThread = new Thread(new HIDInputReportReader(handle, listener, inputReportBuffer, context, this));
synchronized(lock) {
info.setInputReportListener(listener);
info.setListenerContext(context);
listenerToHandleMap.put(listener, handle);
dataReaderThread.start();
}
return true;
}
/**
* This unregisters listener and terminates worker thread which was delivering input reports.
*
* @param listener reference to class which implemented IHIDInputReportListener interface to get input reports.
* @return true on success.
* @throws SerialComException if un-registration fails due to some reason.
* @throws IllegalArgumentException if listener is null or given listener is not registered.
*/
public boolean unregisterInputReportListener(final IHIDInputReportListener listener) throws SerialComException {
if(listener == null) {
throw new IllegalArgumentException("Argument listener can not be null !");
}
long handle = listenerToHandleMap.get(listener);
HIDdevHandleInfo info = devInfo.get(handle);
if(info == null) {
throw new IllegalArgumentException("Invalid listener passed for unregistration !");
}
long context = info.getListenerContext();
synchronized(lock) {
unblockBlockingHIDIOOperationR(context);
try {
Thread.sleep(2); // let worker thread's read method get unblocked and thread to exit itself.
} catch (InterruptedException e) {
}
destroyBlockingIOContextR(context);
info.setInputReportListener(null);
listenerToHandleMap.remove(listener);
}
return true;
}
/**
* Returns an instance of class SerialComUSBHID for HID over USB operations.
*
* @param transport one of the HID_USB or HID_BLUETOOTH constants.
* @return object of one of the subclasses of SerialComHIDTransport class.
* @throws SerialComException if operation can not be completed successfully.
*/
public SerialComHIDTransport getHIDTransportInstance(int transport) throws SerialComException {
if(transport == SerialComHID.HID_USB) {
return new SerialComUSBHID(mHIDJNIBridge, osType);
}else if(transport == SerialComHID.HID_BLUETOOTH) {
//TODO
}else {
throw new IllegalArgumentException("Argument transport must be one of the HID_XXX constants !");
}
return null;
}
/**
* Read input report from given HID device and handles inconsistencies in reports internally using
* facilities provided by operating system. For example some device send/receive reports which are inconsistent
* with what was described in their HID report descriptor. For some devices their firmware can not be modified
* and this is where this method can be used.
*
* For Windows operating system the methods readInputReportWithTimeoutR and readInputReportR uses ReadFile
* function, whereas readPlatformSpecificInputReportR uses HidD_GetInputReport function.
*
* The size of reportBuffer passed must be equal to the number of bytes(fields) in input report (excluding
* report ID whether devices uses numbered reports or not).
*
* @param handle handle of the HID device from whom input report is to be read.
* @param reportId unique identifier for the report type or -1 if device does not use report IDs.
* @param reportBuffer byte buffer in which input report will be saved.
* @return true on success.
* @throws SerialComException if an I/O error occurs.
*/
public boolean readPlatformSpecificInputReportR(long handle, byte reportId, byte[] reportBuffer) throws SerialComException {
if(reportBuffer == null) {
throw new IllegalArgumentException("Argumenet reportBuffer can not be null !");
}
int ret = mHIDJNIBridge.readPlatformSpecificInputReportR(handle, reportId, reportBuffer, reportBuffer.length);
if(ret < 0) {
throw new SerialComException("Could not read input report from HID device. Please retry !");
}
return true;
}
/**
* Sends an output report to given HID device and handles inconsistencies in reports internally using
* facilities provided by operating system. For example some device send/receive reports which are inconsistent
* with what was described in their HID report descriptor. For some devices their firmware can not be modified
* and this is where this method can be used.
*
* For Windows operating system the method writeOutputReportR uses WriteFile function, whereas
* writePlatformSpecificOutputReportR uses HidD_SetOutputReport function.
*
* @param handle handle of the HID device to whom this output report is to be sent.
* @param reportId unique identifier for the report type or -1 if device does not use report IDs.
* @param reportBuffer buffer containing output report to be sent to device.
* @return true on success.
* @throws SerialComException
*/
public boolean writePlatformSpecificOutputReportR(long handle, byte reportId, byte[] reportBuffer) throws SerialComException {
if(reportBuffer == null) {
throw new IllegalArgumentException("Argumenet reportBuffer can not be null !");
}
int ret = mHIDJNIBridge.writePlatformSpecificOutputReportR(handle, reportId, reportBuffer, reportBuffer.length);
if(ret < 0) {
throw new SerialComException("Could not read input report from HID device. Please retry !");
}
return true;
}
}