简体   繁体   English

Android上的低级USB API,NDK级别,可在Qt Android上使用

[英]Low-level USB API on Android at NDK level to use on Qt Android

I need to interact with an HID device on Android. 我需要在Android上与HID设备进行交互。 The problem is that i'm using Qt Android, and i'm not using Java UsbManager classes. 问题是我正在使用Qt Android,而我没有使用Java UsbManager类。

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? 是否有任何C lib我可以链接以便与Android上的HID设备进行通信而无需使用Java API?

I have found this: 我发现了这个:

http://source.android.com/devices/reference/bt__hh_8h_source.html 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. 这似乎是定义HID通信的标头,但我找不到关联的lib。 Any ideas? 有任何想法吗?

Thanks in advance 提前致谢

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. 基本上我正在使用UsbManager来查找和获取设备。 I then open the device and call getFileDescriptor() method from UsbDeviceConnection. 然后我打开设备并从UsbDeviceConnection调用getFileDescriptor()方法。 I then pass this int to the native code side. 然后我将此int传递给本机代码端。 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. 从那里我可以使用任何类型的请求轻松地从设备获取数据,而无需将数据从本机代码传递到Java,反之亦然,通过JNI,这是缓慢而缓慢的死亡工作。

The most tricky part is actually doing the ioctl calls which need to have a special format. 最棘手的部分实际上是进行需要特殊格式的ioctl调用。

Part of this codes are already well exemplified on libusb source code since it's the way they implement libsub calls to linux kernel. 这些代码的一部分已经在libusb源代码中得到了很好的例证,因为它是它们实现对linux内核的libsub调用的方式。

If anyone knows an even better solution to this problem just let me know. 如果有人知道更好的解决方案,请告诉我。

Android activity code with USBManager hooking: USBManager挂钩的Android活动代码:

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. 当连接或断开具有所希望的VID和PID的设备时,调用本机调用notifyDeviceAttached(int fd)和notifyDeviceDetached(int fd)将设备的文件描述符发送到本机端。 In my case I instantiate a class of type Device. 在我的例子中,我实例化一个类型为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. 在Linux中,您可以像libusb一样进行ioctl调用。 You can see the code below for getFeature and setFeature. 您可以在下面看到getFeature和setFeature的代码。 If you need anything else than this you can look at libusb source code. 如果您还需要其他任何东西,可以查看libusb源代码。

C++ native side code: C ++本机端代码:

#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 努诺桑托斯

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM