问题
I need to interact with an HID device on Android. The problem is that i'm using Qt Android, and i'm not using Java UsbManager classes.
Is there any C lib which I can link against in order to communicate with an HID device on Android without having to work with the Java API?
I have found this:
http://source.android.com/devices/reference/bt__hh_8h_source.html
Which seems to be an header that defines HID communication but I can't find the associated lib. Any ideas?
Thanks in advance
回答1:
I have found a way of doing this. In my case, i'm developing a control panel for a device and I need it to work on every device without having the device rooted.
Basically I'm using UsbManager to find and grab the device. I then open the device and call getFileDescriptor() method from UsbDeviceConnection. I then pass this int to the native code side. From there I can easily obtain data from the device using any kind of requests without having to pass data from native code to java and vice versa thru JNI which is slow and slow dying work.
The most tricky part is actually doing the ioctl calls which need to have a special format.
Part of this codes are already well exemplified on libusb source code since it's the way they implement libsub calls to linux kernel.
If anyone knows an even better solution to this problem just let me know.
Android activity code with USBManager hooking:
public class MyActivity extends QtActivity
{
private static MyActivity m_instance;
private UsbAccessory accessory;
private String TAG = "TAG";
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
private PendingIntent mPermissionIntent;
private UsbManager manager;
private UsbDeviceConnection connection;
private HashMap<Integer, Integer> connectedDevices;
public MyActivity()
{
m_instance = this;
connectedDevices = new HashMap<Integer, Integer>();
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
manager = (UsbManager) getSystemService(Context.USB_SERVICE);
registerReceiver(usbManagerBroadcastReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED));
registerReceiver(usbManagerBroadcastReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED));
registerReceiver(usbManagerBroadcastReceiver, new IntentFilter(ACTION_USB_PERMISSION));
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
final Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
@Override
public void run()
{
checkForDevices();
}
}, 1000);
}
@Override
public void onDestroy()
{
super.onDestroy();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
}
private static native void notifyDeviceAttached(int fd);
private static native void notifyDeviceDetached(int fd);
private final BroadcastReceiver usbManagerBroadcastReceiver = new BroadcastReceiver()
{
public void onReceive(Context context, Intent intent)
{
try
{
String action = intent.getAction();
Log.d(TAG, "INTENT ACTION: " + action);
if (ACTION_USB_PERMISSION.equals(action))
{
Log.d(TAG, "onUsbPermission");
synchronized (this)
{
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false))
{
if(device != null)
{
int fd = connectToDevice(device);
Log.d(TAG,"device file descriptor: " + fd);
notifyDeviceAttached(fd);
}
}
else
{
Log.d(TAG, "permission denied for device " + device);
}
}
}
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action))
{
Log.d(TAG, "onDeviceConnected");
synchronized(this)
{
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (device != null)
{
manager.requestPermission(device, mPermissionIntent);
}
}
}
if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action))
{
Log.d(TAG, "onDeviceDisconnected");
synchronized(this)
{
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
int fd = connectedDevices.get(device.getDeviceId());
Log.d(TAG, "device: " + device.getDeviceId() + " disconnected. fd: " + fd);
notifyDeviceDetached(fd);
connectedDevices.remove(device.getDeviceId());
}
}
}
catch(Exception e)
{
Log.d(TAG, "Exception: " + e);
}
}
};
private int connectToDevice(UsbDevice device)
{
connection = manager.openDevice(device);
// if we make this, kernel driver will be disconnected
connection.claimInterface(device.getInterface(0), true);
Log.d(TAG, "inserting device with id: " + device.getDeviceId() + " and file descriptor: " + connection.getFileDescriptor());
connectedDevices.put(device.getDeviceId(), connection.getFileDescriptor());
return connection.getFileDescriptor();
}
private void checkForDevices()
{
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext())
{
UsbDevice device = deviceIterator.next();
if (device.getVendorId()==VID && device.getProductId()==PID)
{
Log.d(TAG, "Found a device: " + device);
manager.requestPermission(device, mPermissionIntent);
}
}
}
}
When the device with the wished VID and PID is connected or disconnected the native calls notifyDeviceAttached(int fd) and notifyDeviceDetached(int fd) are called sending the file descriptor of the device to the native side. In my case I instantiate a class of type Device. At this point the device is already open and you can start doing calls to it. In Linux you make ioctl calls just like libusb does. You can see the code below for getFeature and setFeature. If you need anything else than this you can look at libusb source code.
C++ native side code:
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <QDebug>
#include <QElapsedTimer>
static inline uint16_t cpu_to_le16(const uint16_t x)
{
union
{
uint8_t b8[2];
uint16_t b16;
} _tmp;
_tmp.b8[1] = (uint8_t) (x >> 8);
_tmp.b8[0] = (uint8_t) (x & 0xff);
return _tmp.b16;
}
struct usbdevfs_ctrltransfer
{
unsigned char bRequestType;
unsigned char bRequest;
unsigned short wValue;
unsigned short wIndex;
unsigned short wLength;
unsigned int timeout;
void *data;
};
Device::Device(int fileDescriptor, QObject *parent) :
fd(fileDescriptor)
{
}
int Device::getFeature(unsigned char reportId, unsigned char *buffer, int length)
{
struct usbdevfs_ctrltransfer data;
data.bRequestType = (0x01 << 5)|0x01|0x80;
data.bRequest = 0x01;
data.wValue = cpu_to_le16((3 << 8) | reportId);
data.wIndex = cpu_to_le16(0);
data.wLength = cpu_to_le16(length);
data.data = buffer;
data.timeout = 1000;
int res = ioctl(fd, _IOWR('U', 0, struct usbdevfs_ctrltransfer), &data);
if (res<0)
{
qDebug() << "error: " << strerror(errno);
}
return res;
}
int Device::setFeature(unsigned char reportId, unsigned char *buffer, int length)
{
struct usbdevfs_ctrltransfer data;
data.bRequestType = (0x01 << 5)|0x01|0x00;
data.bRequest = 0x09;
data.wValue = cpu_to_le16((3 << 8) | reportId);
data.wIndex = cpu_to_le16(0);
data.wLength = cpu_to_le16(length);
data.data = buffer;
data.timeout = 1000;
int res = ioctl(fd, _IOWR('U', 0, struct usbdevfs_ctrltransfer), &data);
if (res<0)
{
qDebug() << "error: " << strerror(errno);
}
return res;
}
Regards,
Nuno Santos
来源:https://stackoverflow.com/questions/22197425/low-level-usb-api-on-android-at-ndk-level-to-use-on-qt-android