【问题标题】:Low-level USB API on Android at NDK level to use on Qt AndroidNDK 级别的 Android 上的低级 USB API,可在 Qt Android 上使用
【发布时间】:2014-04-07 11:47:55
【问题描述】:

我需要与 Android 上的 HID 设备进行交互。问题是我使用的是 Qt Android,而我没有使用 Java UsbManager 类。

是否有任何 C 库可供我链接,以便与 Android 上的 HID 设备进行通信,而无需使用 Java API?

我找到了这个:

http://source.android.com/devices/reference/bt__hh_8h_source.html

这似乎是定义 HID 通信的标头,但我找不到关联的库。有什么想法吗?

提前致谢

【问题讨论】:

  • 这是不可能的,除非您希望必须根设备。

标签: android qt android-ndk usb hid


【解决方案1】:

我找到了一种方法。就我而言,我正在为设备开发一个控制面板,我需要它在没有设备根的情况下在每台设备上工作。

基本上我使用 UsbManager 来查找和抓取设备。然后我打开设备并从 UsbDeviceConnection 调用 getFileDescriptor() 方法。然后我将此 int 传递给本机代码端。从那里我可以使用任何类型的请求轻松地从设备获取数据,而无需将数据从本机代码传递到 java,反之亦然,通过 JNI,这是缓慢而缓慢的工作。

最棘手的部分实际上是执行需要具有特殊格式的 ioctl 调用。

这些代码的一部分已经在 libusb 源代码中得到了很好的例证,因为这是他们实现对 linux 内核的 libsub 调用的方式。

如果有人知道这个问题的更好解决方案,请告诉我。

带有 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);
            }
        }
    }
}

当具有希望的 VID 和 PID 的设备连接或断开连接时,本机调用 notifyDeviceAttached(int fd) 和 notifyDeviceDetached(int fd) 被调用,将设备的文件描述符发送到本机端。在我的例子中,我实例化了一个设备类型的类。此时设备已经打开,您可以开始对其进行调用。在 Linux 中,您可以像 libusb 一样进行 ioctl 调用。您可以查看下面的 getFeature 和 setFeature 代码。如果您需要除此之外的任何其他内容,可以查看 libusb 源代码。

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;
}

问候,

努诺桑托斯

【讨论】:

  • 发送!你能分享一些关于你是如何做到的代码吗?谢谢!
  • @LennartRolland,当然。我已将代码添加到我的答案中。希望能帮助到你。问候
  • 你好。我正在为我的草图做同样的事情(但在 Xamarin 下)。我从 UsbManager 中获取文件描述符并将其传递给我的 C 库(使用 swig 限制)。但是当使用读/写调用时,它们会返回错误(无效参数)。我检查 FD 是否有效 > 0。我会尝试 ioctl 调用。
  • 您好!就一个问题。既然你有一个文件描述符,为什么读/写操作不能工作?
  • +1,如何使用读/写C函数?进行 ioctl() 调用不起作用。 @SelsoLiberado,你成功了吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-18
  • 2016-12-22
  • 2018-03-14
  • 1970-01-01
  • 1970-01-01
  • 2014-01-01
  • 2017-09-04
相关资源
最近更新 更多