Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile and link against libusb for android

Tags:

android

usb

I consider trying to compile a certain C-programm which allows the control of Gembird SilverShield power outlets via USB for android. On my android HDMI TV-stick this would be very useful. There is an open project for this. It works under Linux and depends on libusb. The target platform is android ICS. I want to develop on Ubuntu Linux. What are the chances I get it working? What are the required steps. Setup android SDK, NDK, crosscompiler ...
There is an older question here, related to libusb on android but no information how.
Is it maybe easier to port the application to androids own usb library?

like image 553
highsciguy Avatar asked Apr 11 '13 19:04

highsciguy


3 Answers

Libusb can work on a non-rooted android (provided the device supports USB host ... this is VERY important as not all devices do). You need to use the standard android USB stack. You can then get a device descriptor from the USBDevice and pass that over to libusb.

Unfortunately you also need to modify libusb. Fortunately other people have explained how you need to modify LibUSB.

LibUSB has been modified here.

Good luck!

Edit:

First you need to define a broadcast receiver:

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

Now you need to create a USBManager and enumerate the devices:

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

Edit 2:

Getting libusb to build is just a matter of putting the files somewhere handy (I put them in jni/libusb) and then adding the following lines to your 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)
like image 184
Goz Avatar answered Oct 23 '22 14:10

Goz


The solution I implemented is to open the USB device using the Java APIs and then using the file descriptor with libusb.I used libusb from openni project by primesense(https://github.com/OpenNI/OpenNI2)

Code bits:

Opening the device(Using 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);
}

Opening the device in JAVA code(Not running on main thread!):

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

Permission handling code:

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

};

Open function:

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) change:

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);
}
like image 4
Arik Halperin Avatar answered Oct 23 '22 14:10

Arik Halperin


Even if you get it compiled, Android is probably not going to let you access the USB device through libusb unless your device is rooted. If it is feasible to port your app to Android's native USB stack, that would almost certainly be a more stable solution.

like image 1
mlc Avatar answered Oct 23 '22 13:10

mlc