36

When using Android, I'm losing data on an incoming USB data stream that I do not lose when reading the same device/stream in Windows. (I know that Android is not a real-time OS, but neither is Windows, and Windows is having no problem 'keeping up' with the data.)

I have data coming in at about 3.5MB/sec using an FTDI 2232H chip which has a built in 4K buffer. The bulk_transfer calls in libusb can ask for 16K at a time, so Android needs to reap the contents of the USB buffer every 4ms or so.

I have tried: writing in Java and in C, raising the thread (and/or process) priority to it's highest, sync and async routines, and I even pass a separate buffer for each USB read so I don't even have to copy data between successive reads. (There is no garbage collection going on during the transfer.) I only need to buffer 20MB of data, so it's all to RAM.

Still, Android is 'not getting around' to the USB data, sometimes waiting as long as 12ms between reads, causing a bunch of data to be lost.

Does anyone have any ideas? DMA? Some sort of 'real-time' request to the kernel?

DIF
  • 2,470
  • 6
  • 35
  • 49
Greg
  • 839
  • 8
  • 8
  • What type of phone do you have, what Android version, etc. – PearsonArtPhoto Feb 23 '12 at 14:58
  • Two tablets, a Toshiba Thrive and an ASUS Transformer, both running Android 3.2.1, both rooted. (Sorry I forgot to mention that in the original post.) – Greg Feb 23 '12 at 19:54
  • @Greg I have exactly the same problem (http://stackoverflow.com/questions/10889461/android-usb-host-api-bulk-transfer-buffer-size). Have you found any solution? – syntagma Jun 25 '12 at 07:54
  • 2
    @REACHUS - After weeks of trying, I finally determined that the Android platform simply could not keep up with a high-speed, 'fixed rate' USB data flow. I gave in and added a hardware FIFO buffer to my design to compensate, which allows me to bring the data out at Android's maximum rate (which, ironically, is faster than I wanted overall, but plagued with intermittent gaps where I assume the system is just 'out handling other events'.) – Greg Jun 25 '12 at 11:56
  • @Greg Thanks a lot. I could not add FIFO buffer in my solution because it is peripheral device provided by external company. Do you have any idea? Have you tried patching the kernel or changing the pipe size on your rooted devices? – syntagma Jun 25 '12 at 12:49
  • @REACHUS - I tired everything ~except~ patching the kernel. From what I understand, there is no 'real-time' capability in Android - and that is by design. There are some real-time kernels out there, but they are 'non-standard', and I wanted to deal with a standard. There seems to be no way to tell Android that 'I need you to ignore everything else for a few seconds, I need this data'. It can actually read data much faster than I need it to (3.5MB/sec), but only in bursts, with periods of 'inattentiveness' in between. If you figure anything out, let me know - I'd love to lose the FIFO. – Greg Jun 25 '12 at 15:27
  • @Greg Could you please name these real-time kernels? – syntagma Jun 25 '12 at 21:36
  • @REACHUS - It's been too long for me to remember off the top of my head. Search for real-time OS's that can 'replace' Android, or real-time 'alternatives' to Android. Good luck. – Greg Jun 26 '12 at 00:45
  • It seems that at least audio latency [improved](http://music.columbia.edu/pipermail/andraudio/2012-June/000645.html) in Android 4.1, could this mean that in general the real-time performance of the kernel improved? – gfour Jul 02 '12 at 13:56
  • @REACHUS - It's possible, seeing as the audio priority setting was one of the ways I ways trying to induce the kernel to 'pay attention' to my USB-grabbing thread. Unfortunately, my project needs a guarantee, not just an 'improved chance' of getting the data. Also, it's possible that enough Android end-users reported audio latency problems, so only ~that~ portion of the kernel was made more 'real-time'...in other words, not a 'general' improvement. – Greg Jul 02 '12 at 17:53
  • possible duplicate of [Android USB Host - bulkTransfer() is losing data](http://stackoverflow.com/questions/9108548/android-usb-host-bulktransfer-is-losing-data) – DocMax Jan 09 '13 at 01:18
  • Nevermind, you say you are calling bulk_transfer in the question. As undesirable as it is, if you can resort to polling, you will probably get the latency really short. Sorry, don't know the android API in depth enough to help there, it's where I would look. Hopefully the data transfer is important enough to the user for them to not mind you using a lot of CPU. If the transfer is continuous, perhaps this would be a bad enough approach to not try it. Perhaps you could automatically switch to polling mode when data loss happens n times (or offer an option and pass that info onto support people). – doug65536 Jan 17 '13 at 21:14
  • I speculate that, due to the nature of handhelds, they probably "defer" interrupts and handle them in batches, to save context switches and increase average sleep time, greatly improving battery life. Even Windows (on PC) does this with threadpool timers. Drivers are strongly encouraged to use DPCs instead of actually handling things in the ISR. The same idea can apply to anything though. – doug65536 Jan 17 '13 at 21:30
  • Unless running isochronous, the USB protocol is *based* on polling. To my knowledge, there is no way for an "interrupt" type USB device to asynchronously request service, it is polled by the host. – doug65536 Jan 25 '13 at 01:34
  • Do you pipeline your USB request blocks (URBs)? The idea is that several requests should exist at all times, so that once a single request is completed, another one is instantly ready for the kernel to satisfy. – dragonroot Jan 27 '13 at 23:50
  • 1
    I tried everything I could think of (while I was trying to solve this problem in software). I ended up solving it in hardware by adding an 8MB FIFO to the device, which allows me to pull the data in at 'Android speeds'. As I recall, I did not 'pipeline' anything, I just made another request as soon as one was fulfilled (the device at the time had a 4K buffer, so I was expecting/hoping that the Android would always be ready for the next packet before the buffer filled - which it did successfully 99% of the time). If I get a chance to try to pipeline, I'll post the results back here. – Greg Jan 28 '13 at 03:38

1 Answers1

6

I've encountered this kind of problem before. Forget using Java, in the background it's doing untold number of things that prevent realtime access, e.g. garbage collection, thread processing. Also forget using event-driven programming, even in high priority threads, it can take a long time before the event is processed and you can lose data.

The way I fixed it was to write "unfriendly" code! Used C or assembly, and wrote a polling function like this (in C-like pseudo-code):

#define PAUSE 2 /* Check twice as often as the packet rate */
#define TIMEOUT (500 / PAUSE) /* Abort if half a second of no data */

/* Provide handle, data buffer and size of buffer
   Returns TRUE if full buffer read, FALSE if not, data unread in size
*/ 
BOOL real_time_read(HANDLE handle, BYTE *data, size_t *size)
{
    BOOL result = FALSE;
    int timeout = TIMEOUT;

    set_thread_priority(REALTIME);

    while (is_handle_valid(handle))
    {
        if (is_data_pending(handle))
        {
            size_t count = get_data(handle, data, size);
            data += count;
            *size -= count;
            if (!*size)
            {
                result = TRUE;
                break;
            }
        }
        else if (!--timeout)
            break;

        /* Give a tiny time slice to other processes */
        usleep(PAUSE);
    }

    return result;
}

You mentioned you tried C, so it should be straightforward to convert this to real functions. Avoid the temptation to use convenience functions, you want as close to the metal as possible. E.g. if an O/S function Read() in turn calls read() which in turn calls _read(), you want to be using _read(). The device will be noticeably slower while this is going on, but that's the tradeoff of real-time access.

Avadhani Y
  • 7,566
  • 19
  • 63
  • 90
Yimin Rong
  • 1,890
  • 4
  • 31
  • 48
  • 1
    I can't believe I didn't try 'REALTIME' --- the documentation led me to believe that the top priority setting was 'URGENT_AUDIO' (-19 instead of -20). Having since solved this problem in hardware, I may not get a chance to try out 'REALTIME', but if I do, I'll post the results back here. (See above response to 'dragonroot'.) – Greg Jan 28 '13 at 03:41
  • As I am also going to try solving [my problem](http://stackoverflow.com/questions/10889461/android-usb-host-api-bulk-transfer-buffer-size) this way, could you elaborate a bit on this solution? How much of your complete solution was written in C (where there any other functions besides the one you quoted and the ones it uses)? Did you have any trouble later connecting it with you Java application using NDK? – syntagma Apr 10 '13 at 07:26
  • What device(s) have you been using for testing? – syntagma Apr 10 '13 at 07:32
  • Basically, you implement in C as much as you need, but no more - it depends on the hardware, O/S and what you're running in the background. For Java, I used `carray` to pass arrays. I was able to get by with just one C function accessing low level I/O. – Yimin Rong Apr 17 '13 at 14:10