编译和链接libusb for android

时间:2013-04-11 19:44:31

标签: android usb

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

4 个答案:

答案 0 :(得分:23)

Libusb可以在非root用户上工作(假设设备支持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)

答案 1 :(得分:4)

我实现的解决方案是使用Java API打开USB设备,然后使用libusb的文件描述符。我使用来自openni项目的libusb by primesense(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;
    }

权限处理代码:

private 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 lib change(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);
}

答案 2 :(得分:1)

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

答案 3 :(得分:1)

您也可以尝试使用android serial port api

以下是串口init的示例。

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主机上使用设备也是如此。