【问题标题】:Compile and link against libusb for android针对 android 的 libusb 编译和链接
【发布时间】:2013-04-04 03:55:04
【问题描述】:

我考虑尝试编译一个允许通过 USB 控制 Gembird SilverShield 电源插座的 C 程序。在我的安卓 HDMI 电视棒上,这将非常有用。 this 有一个开放项目。它可以在 Linux 下运行,并且依赖于 libusb。目标平台是android ICS。我想在 Ubuntu Linux 上开发。我让它工作的机会有多大?需要哪些步骤。设置 android SDK、NDK、交叉编译器 ...
有一个较旧的问题 here,与 android 上的 libusb 相关,但没有关于如何操作的信息。
将应用程序移植到 android 自己的 USB 库是否更容易?

【问题讨论】:

标签: android usb


【解决方案1】:

即使你编译了它,Android 也可能不会让你通过 libusb 访问 USB 设备,除非你的设备是 root 的。如果将您的应用移植到Android's native USB stack 是可行的,那几乎肯定是一个更稳定的解决方案。

【讨论】:

  • 设备已root。这也适用于所有其他具有最新固件的 Rikomagic III HDMI 棒。如果这是不使用 libusb 的唯一原因,我会试一试。但如果您对此有所了解,了解 Android 的 USB 堆栈和 libusb 的相似程度也会很有趣……另外:我如何在 C 中使用它。
【解决方案2】:

Libusb 可以在非 root 的 android 上运行(前提是设备支持 USB 主机......这非常重要,因为并非所有设备都这样做)。您需要使用标准的 android USB 堆栈。然后,您可以从 USBDevice 获取设备描述符并将其传递给 libusb。

不幸的是,您还需要修改 libusb。幸运的是,其他人已经解释了您需要如何修改 LibUSB。

LibUSB已修改here

祝你好运!

编辑

首先你需要定义一个广播接收器:

private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() 
{
    public void onReceive(Context context, Intent intent)
    {
        String action = intent.getAction();
        if (ACTION_USB_PERMISSION.equals(action)) 
        {
            synchronized (this) 
            {
                UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) 
                {
                    if(device != null)
                    {
                        UsbDeviceConnection deviceConnection    = mUsbManager.openDevice( device );
                        Log.d( "USB",  deviceConnection.getSerial() );
                    }
                } 
                else 
                {
                    Log.d( "USB", "permission denied for device " + device);
                }
            }
        }
    }
}

现在您需要创建一个 USBManager 并枚举设备:

    mUsbManager         = (UsbManager) getSystemService( Context.USB_SERVICE );
    HashMap< String, UsbDevice > stringDeviceMap    =       mUsbManager.getDeviceList();
    Collection< UsbDevice > usbDevices              = stringDeviceMap.values();

    mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent( ACTION_USB_PERMISSION ), 0 );
    IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
    registerReceiver( mUsbReceiver, filter );

    Iterator< UsbDevice > usbDeviceIter             = usbDevices.iterator();
    while( usbDeviceIter.hasNext() )
    {
        if ( USBDeviceValid( usbDevice ) )
        {
            // Request permission to access the device.
            mUsbManager.requestPermission( usbDevice, mPermissionIntent );

            // Open the device.
            UsbDeviceConnection connection = mUsbManager.openDevice( usbDevice );
            int fd = connection.getFileDescriptor();

            // Now pass the file descriptor to libusb through a native call.
        }
    }

编辑 2

构建 libusb 只需将文件放在方便的地方(我将它们放在 jni/libusb 中),然后将以下行添加到您的 Android.mk:

include $(CLEAR_VARS)
LOCAL_MODULE    := libusb
LOCAL_SRC_FILES := libusb/core.c libusb/descriptor.c libusb/io.c libusb/sync.c libusb/os/linux_usbfs.c

LOCAL_LDLIBS    := -llog
include $(BUILD_SHARED_LIBRARY)

【讨论】:

  • 我能在某处找到一个创建和传递设备描述符的例子吗?
  • @highsciguy:这有帮助吗?
  • 是的,它对我有帮助。应该支持 USB 主机(我成功连接了其他 USB 设备)。我还需要弄清楚如何编译和链接这些库。到目前为止,我只编写了 Java Android 应用程序。
  • @highsciguy:我用一些关于构建 libusb 的方便信息编辑了我的帖子。如果您喜欢这个答案,请随意点击已回答勾号下方的蓝色小 +50 框;)
  • ... jni目录在android ndk路径下,还是你指的是/usr/lib/*-linux-gnu/jni?我以为一旦我点击检查你就会得到积分......
【解决方案3】:

你也可以试试android serial port api

这里是串口初始化的例子。

private FileDescriptor mFd;
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;

public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {

    /* Check access permission */
    if (!device.canRead() || !device.canWrite()) {
        try {
            /* Missing read/write permission, trying to chmod the file */
            Process su;
            su = Runtime.getRuntime().exec("/system/bin/su");
            String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
                    + "exit\n";
            su.getOutputStream().write(cmd.getBytes());
            if ((su.waitFor() != 0) || !device.canRead()
                    || !device.canWrite()) {
                throw new SecurityException();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new SecurityException();
        }
    }

    mFd = open("/dev/ttyACM0", 9600, 0);
    if (mFd == null) {
        Log.e(TAG, "native open returns null");
        throw new IOException();
    }
    mFileInputStream = new FileInputStream(mFd);
    mFileOutputStream = new FileOutputStream(mFd);
}

使用 USB 主机上的设备也是如此。

【讨论】:

    【解决方案4】:

    我实现的解决方案是使用 Java API 打开 USB 设备,然后使用带有 libusb 的文件描述符。我使用了 primesense 的 openni 项目中的 libusb(https://github.com/OpenNI/OpenNI2)

    代码位:

    打开设备(使用 swig):

    int LibUsbAndroid::android_open(libusb_device *device, libusb_device_handle **devHandle)
    {
        int fd = USBJNICallbacks::getCallback()->getDeviceFd(device->bus_number, device->device_address);
    
        __android_log_print(ANDROID_LOG_VERBOSE,"USB","Got  FD:%d",fd);
        if(fd==-1)
        {
            __android_log_print(ANDROID_LOG_ERROR,"USB","android_open, bad fd");
            return -1;
        }
    
        return libusb_open(device, devHandle, fd);
    }
    

    用JAVA代码打开设备(不在主线程上运行!):

    public int getDeviceFd(int busNumber, int deviceAddress) {
            UsbDevice device = findDevice(busNumber, deviceAddress);
    
            if(device!=null)
            {
                mReceivedPermission = false;
                PermissionRequester pr = new PermissionRequester(device);
                pr.run();
    
                if(!mUsbManager.hasPermission(device))
                {
                    Log.v("USB", "Requesting permissiom to device");
                    mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
                    IntentFilter filterPermission = new IntentFilter(ACTION_USB_PERMISSION);
                    mContext.registerReceiver(mUsbPermissionReceiver, filterPermission);
                    mUsbManager.requestPermission(device, mPermissionIntent);
                }
                else
                {
                    Log.v("USB", "Already has permission");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
    
                    mReceivedPermission = true;
    
                    Log.v("USB", "Opening device");
                    OpenDevice od = openDevice(device);
                    Log.v("USB", "Adding to open devices");
                    mOpenDevices.put(""+busNumber+"/"+deviceAddress, od);
    
    
                }
    
                Log.v("USB", "Waiting for permission");
                waitForPermissionResult();
                OpenDevice od = mOpenDevices.get(""+busNumber+"/"+deviceAddress);
                if(od!=null)
                {
                    Log.v("USB", "Getting FD");
                    int result = od.mConnection.getFileDescriptor();
    
                    Log.i("USB","USB File desc:"+result);
                    return result;
                }
                else
                {
                    Log.v("USB", "Error getting FD");
                    return -1;
                }
            }
    
            return -1;
        }
    

    权限处理代码:

    私有 BroadcastReceiver mUsbPermissionReceiver=new BroadcastReceiver(){

    @Override
    public void onReceive(Context context, Intent intent) {
    
        Log.v("USB", "Received permission result");
        String action = intent.getAction();
        if (ACTION_USB_PERMISSION.equals(action)) {
            synchronized (this) {
                UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    Log.v("USB", "Received permission result OK");
                    if(device != null){
                        Log.v("USB", "Device OK");
                        mContext.unregisterReceiver(this);      
                        Log.v("USB", "Openning device");
                        OpenDevice od = openDevice(device);
                        Log.v("USB", "Adding to open device list");
                        mOpenDevices.put(""+od.mBus+"/"+od.mAddress,od);
    
                        Log.v("USB", "Received permission is true");
                        mReceivedPermission = true;
                    } 
                    else {
                        Log.d(TAG, "permission denied for device " + device);
                    }
                }
            }
        }
    }
    

    };

    开启功能:

    public OpenDevice openDevice(UsbDevice device) {
            UsbDeviceConnection connection = mUsbManager.openDevice(device);
    
            Log.i("USB","Device name="+device.getDeviceName());
    
            int bus = getBusNumber(device.getDeviceName());
            int address = getAddress(device.getDeviceName());
    
            return new OpenDevice(device, connection, bus, address);
    
        }
    

    USB 库更改(core.c):

    int API_EXPORTED libusb_open(libusb_device *dev,
        libusb_device_handle **handle, int fd)
    {
        struct libusb_context *ctx = DEVICE_CTX(dev);
        struct libusb_device_handle *_handle;
        size_t priv_size = usbi_backend->device_handle_priv_size;
        int r;
        usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
    
        _handle = malloc(sizeof(*_handle) + priv_size);
        if (!_handle)
            return LIBUSB_ERROR_NO_MEM;
    
        r = usbi_mutex_init(&_handle->lock, NULL);
        if (r) {
            free(_handle);
            return LIBUSB_ERROR_OTHER;
        }
    
        _handle->dev = libusb_ref_device(dev);
        _handle->claimed_interfaces = 0;
        memset(&_handle->os_priv, 0, priv_size);
    
        r = usbi_backend->open(_handle,fd);
        if (r < 0) {
            usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r);
            libusb_unref_device(dev);
            usbi_mutex_destroy(&_handle->lock);
            free(_handle);
            return r;
        }
    
        usbi_mutex_lock(&ctx->open_devs_lock);
        list_add(&_handle->list, &ctx->open_devs);
        usbi_mutex_unlock(&ctx->open_devs_lock);
        *handle = _handle;
        /* At this point, we want to interrupt any existing event handlers so
         * that they realise the addition of the new device's poll fd. One
         * example when this is desirable is if the user is running a separate
         * dedicated libusb events handling thread, which is running with a long
         * or infinite timeout. We want to interrupt that iteration of the loop,
         * so that it picks up the new fd, and then continues. */
        usbi_fd_notification(ctx);
    
        return 0;
    }
    

    op_open(libusb_fs.c) 更改:

    static int op_open(struct libusb_device_handle *handle, int fd)
    {
        struct linux_device_handle_priv *hpriv = _device_handle_priv(handle);
        char filename[PATH_MAX];
    
        _get_usbfs_path(handle->dev, filename);
        usbi_dbg("opening %s", filename);
    
        hpriv->fd = fd;
    
        if (hpriv->fd < 0) {
            if (errno == EACCES) {
                usbi_err(HANDLE_CTX(handle), "libusb couldn't open USB device %s: "
                    "Permission denied.", filename);
                usbi_err(HANDLE_CTX(handle),
                    "libusb requires write access to USB device nodes.");
                return LIBUSB_ERROR_ACCESS;
            } else if (errno == ENOENT) {
                usbi_err(HANDLE_CTX(handle), "libusb couldn't open USB device %s: "
                    "No such file or directory.", filename);
                return LIBUSB_ERROR_NO_DEVICE;
            } else {
                usbi_err(HANDLE_CTX(handle),
                    "open failed, code %d errno %d", hpriv->fd, errno);
                return LIBUSB_ERROR_IO;
            }
        }
    
        return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT);
    }
    

    【讨论】:

    • 谢谢,这很有帮助。您还可以分享一下您是如何创建调用 libusb_open() 所需的 libusb_device* 的吗?
    猜你喜欢
    • 2011-12-06
    • 1970-01-01
    • 2018-08-19
    • 1970-01-01
    • 2023-04-09
    • 2020-02-04
    • 2012-04-15
    • 1970-01-01
    相关资源
    最近更新 更多