I am writing software to communicate between tablet (Motorola Xoom with Android version 4.0.3 and Kernel version 2.6.39.4) and a peripheral device using USB Host API provided by Android. I use only two types of communication:
control: controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout)
bulk: bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout)
Control transfer works fine, but I have a problem with bulk transfer. I can use only 32768 as a size of the buffer for bulkTransfer function. It is not possible to use less or more. I know that I cannot use more because of the limit of the buffer pipe (size: 32769 bytes).
This peripheral device streams data which is not correctly read by bulkTranfer function. I suppose that some data is lost.
I find this: In Linux If a process attempts to read from an empty pipe (buffer), then read(2) will block until data is available. If a process attempts to write to a full pipe , then write(2) blocks until sufficient data has been read from the pipe to allow the write to complete.
And based on that, my explanation of the problem is that some data is not written to pipe (buffer) because of blocking flag made by write(2) function. Am I correct? If this is true I could change pipe buffer.
My first solution for this problem is greater buffer. For
kernel >= 2.6.35, you can change the size of a pipe with fcntl(fd, F_SETPIPE_SZ, size) but how can I find fd (file descriptor) for
USB pipes?
Second option is to use ulimit -p SIZE but parameter p for my kernel is not for pipe but process.
Has anyone faced the same problem, any solutions?
You should get a USB data analyzer, Im using this one: http://www.ellisys.com/products/usbex200/index.php
Using something like this really helped me when I was doing the same type of thing, what I found was that you had to do some type of while loop.
For my device I had 64 bytes of data coming in packets to me, the packet would be two control bytes and 62 for data, so for my transfer I had to do something like
StringBuilder sb = new StringBuilder();
while(bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout) > 2){
for(int i = 2; i < buffer.length(); i++){
sb.append((char) buffer[i]);
}
}
something a long these lines worked good for me, i had exactly the same issue and this is how I fixed it. I have more information if you need it. Just comment :). I know this was really frustrating for me to. Im using an Acer Iconia A500 with Android 4.0.3 btw
Transferring larger amounts of data USB
Transferring data USB
According to the same .pdf gfour posted, I found this paragraph in there:
"The size of the packet will affect the performance and is dependent on the data rate. For very high speed, the largest packet size is needed. For 'real-time' applications that are transferring audio data at 115200 Baud for example, the smallest packet possible is desirable, otherwise the device will be holding up 4k of data at a time. This can give the effect of 'jerky' data transfer if the USB request size is too large and the data rate too low (relatively)."
I'm running into a situation similar to SmartLemon with a FTDI Serial device, so I've been recently looking up ways to alleviate it. This will require me to pretty much write my own functions from scratch while previously I was using a library.
However, it seems that in your case, you could try using bulkTransfer's lowest buffer size instead of shooting for the biggest. You may have tried this already, but maybe not. I see that you say 32768 is the only size, but maybe you only meant the max. It seems weird that it'd only allow one specific size.
The Android SDK's UsbEndpoint object provides a getMaxPacketSize() method, allowing you to check what is appropriate for your device. Generally, the maximum allowable packet size is 64 bytes for USB 'Full-speed' devices and 512 for 'High-speed' devices - far less than 32,768 you are attempting. Are you perhaps confusing the underlying USB packet size with that of some higher-level protocol?
According to AN232B-04_DataLatencyFlow.pdf, flow control is needed for high data rates:
It is strongly encouraged that flow control is used because it is
impossible to ensure that the FTDI driver will always be scheduled.
Have you tried using one of the flow control options (RTS/CTS, DTR/DSR, XON/XOFF) to synchronize your data?
You can try this for Bulk transfer issue
byte[] buffer = new byte[4096];
StringBuilder strIN = new StringBuilder();
if (conn.bulkTransfer(epIN, buffer, 4096, 500) >= 0) {
for (int i = 2; i < 4096; i++) {
if (buffer[i] != 0) {
strIN.append((char) buffer[i]);
textReceiveDataInfo.append(strIN + "\n");
} else {
l(strIN);
break;
}
}
}
Related
I'm trying to use UsbDeviceConnection.controlTransfer to get the HID report descriptor for USB devices, so I can see what buttons the USB HID device has.
I've been able to get the actual input data from the HID devices by using a bulkTransfer.
I've looked it up and can only find tutorials for creating and parsing a HID report descriptor. I've tried entering in different perimeters into controlTransfer, but I've been unable to figure it out.
What values should I pass into controlTransfer to get the USB HID report descriptor, so I can start parsing what buttons the devices have and what bytes they are assigned to? Or are you not supposed to use controlTransfer to get the HID report descriptor? I'm new to working with USB.
This is really late, but in case you haven't come up with a solution or someone else is looking to do this, this was my solution. I should mention, I am not very experienced with any of this, so some details may be off, but the general picture is there.
UsbDeviceConnection.controlTransfer requires 7 variables:
Request type
Request
Request value
Request index
Output buffer
Size of buffer
Timeout
Request type describes the direction, type, and recipient of the transfer. In this case, we want to read (1) using a standard (00) transmission and we want to query an interface (00001). We want to query an interface because HID is an interface of the device. So, 0b10000001 or 0x81.
Request describes our specific request. We are looking for the HID Report Descriptor and GET_DESCRIPTOR is defined as 0x06 by the spec.
Request value consists of the descriptor type as the high byte and the index of the interface as the low byte. Descriptor type in our case is HID Report or 0x22 (this is from the HID spec, specifically class descriptors). Index of the interface is the same as request index below. In my case it was 0x00, but yours may be different. Combining the high and low bit, we get 0x2200 for our request value.
Request index specifies which interface you are querying. If HID is the only interface of the device, then this will be 0x00. Otherwise, you will have to check the configuration descriptor for the types of interfaces available and their indices.
Output buffer is the space allocated for the return of the transaction and should be of the size specified by size of buffer, explained below.
Size of buffer describes the number of bytes in the report descriptor. This value is specified in the configuration descriptor, in the HID class-specific descriptor (bDescriptorType = 0x21) and is the value of the 8th byte (wDescriptorLength) out of the 9 total. In my case this value was 104.
Timeout is the number of milliseconds after which to give up. I used 2000.
Putting this together, UsbDeviceConnection.controlTranfer(0x81, 0x06, 0x2200, 0x00, byte[] buffer, 104, 2000).
https://www.beyondlogic.org/usbnutshell/usb6.shtml gives an excellent overview of the bit fields used in controlTransfer, check it out if you want more context.
https://eleccelerator.com/usbdescreqparser/ is an extremely useful tool for understanding USB and HID descriptors, and may be more insightful than reading the spec sheets and the API alone.
I started my adventure with AoT (I have basic toolkit).
1rst Idea that I try to implement is to communicate via USB with Card Reader (USB Card Reader HID Prox v3).
But I don't get it correctly.
This is "sample" code:
usbRequest = new UsbRequest();
usbConnection = mUsbManager.openDevice(device);
usbRequest.initialize(usbConnection, device.getInterface(0).getEndpoint(0));//communication from card scanner
//In handler:
byte[] byteArray = new byte[16];
int transfer = usbConnection.bulkTransfer(device.getInterface(0).getEndpoint(0), byteArray, 16, 300);
transfer result is = -1, did anyone has the same issue?
Assumption: Your USB device conforms to the USB CCID Device Class. If this is not true, you will need to provide the datasheet for your specific card reader.
Before you can execute any communications with USB endpoints, your app has to claim the interface first. In other words, there should be a call to claimInterface() in your code before any requests are sent.
Next, you are mixing two different forms of communication. Your app should EITHER use UsbRequest for asynchronous use OR bulkTransfer() for synchronous use. You don't need to initialize a UsbRequest if you are using bulkTransfer() instead.
You should verify what the max packet size of your UsbEndpoint is. Bulk CCID endpoints can support up to 512 byte packets, so a fixed length of 16 bytes may not be enough to read a full packet response (assuming this transaction was on the Bulk IN endpoint). Match your array size with your endpoint's packet size.
I need to programmatically write data of say 1 to 100 MB in chunks of 1024 bytes to the remote Bluetooth device. Both are android devices. Here is a sample code snippet in my client program to transfer data –
bTSocket.connect(); //connect to remote BT device
DataOutputStream outStream = new DataOutputStream(bTSocket.getOutputStream());
byte[] buffer = new byte[1024];
int bytesToTransfer = 1000000;
while (bytesToTransfer > 0) {
outStream.write(buffer);
outStream.flush();
bytesToTransfer -= 1024;
}
outStream.close();
While running this piece of code on Android 2.2(Froyo), it works fine. However in case of Android 2.3.4 and 4.0.4, outStream.write(buffer) blocks infinitely after transfer of some data (say of 100 KB). Worth mentioning, the remote device is not configured for listening data. Is there any limitation on the amount of data that can be written?
The Bluetooth socket operates in blocking mode for both reads and writes.
If you fill up the send buffer, then the only thing that .write() can do to stop you trying to send any more data is to block. The alternative to it blocking would be to return an "operation would block!" error code, just like TCP sockets can do when placed in non-blocking mode. But the Bluetooth socket doesn't provide any such non-blocking mode.
You state that the remote Bluetooth device is not reading from its socket. With this being the case, the local sending buffer and remote receive buffer, with each only being of a certain finite size, will eventually fill up. At this point, your .write() operation is going to block until the remote end reads something from its socket. You can't just keep pumping in megabytes of data and expect it to just buffer it all somewhere.
The differences you experience between different Android platforms are probably down to the different amounts of buffer space available in the related Bluetooth stacks.
I'm using sockets for data transfer from one android phone and PC, I am using DataInputStream.
But it takes a long time in data transfer, about 10 minutes to transfer a 4 MB file.
Could anyone suggest any better way to do that?
I did some changes in my code and now it is taking 15 seconds to read about 1 Mb of data. I want to improve its performance.
My Code is:
InputStream is= socket.getInputStream();
DataInputStream inChannel= new DataInputStream(new BufferedInputStream(in));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int oneByte;
while ((oneByte = inChannel.read()) != -1) {
if (oneByte == 0) {
break;
}
baos.write(oneByte);
byteCount++;
}
byte[] inData = baos.toByteArray();
baos.close();
Are you sure you are not sending empty packets in between there or packets doing something else? If you are using TCP if a packet doesn't reach its destination (a buffer is overfilled with other packets somewhere in between your connection on the router or on one of the devices) the packet will have to be resent. This might be because of your setup. Given the little information you have given us, I can give the the following advice:
Look into more advanced typologies, I am going to assume you are not using any of these:
A send a receive thread; the send thread pushes out packets which you put into a queue:
|P| - packet held inside a Que part of your program
send out thread <-| P | P | P | <-App/program
reveive -> | P | P | P| P -> App/Program deques and analyses data
because your send and receive queuing system works along side each other they are constantly buffering up the received and sent packets. What seems to be happening with your setup (as far as I assume) is that you have a loop which grabs the latest packets and analyses them. This means that the hardware (part of your network hardware) buffer is overfilling with packets which your program is doing other things and by the time your program comes around to collect them, some of them are missing. This causes one side to go "I didn't get those packets, send them again", in other words, your resending the packets you should of sucked up into a Que which are waiting to be dequeued and analysed. The queue can grow regardless of how quickly the program is back to grab that data and do stuff with it (of course you are confined to the RAM). That way you ensure that you have possession of packets that you are supposed to receive rather than rely on your network card/receiver to hold on to them for you, possibly overflowing its buffer.
Another approach is to do a handshake system were one of the programs waits until the last packet is trasmitted, recieved and the other side goes "Cool, send me the next one". This slows down your download/upload speed but is a little bit quicker than packets falling off the end of the end of one of the buffers (each node, such as your router buffers your packets in case more come in than it can handle at a cycle) in the network.
You should utilize a state machine if you can on one or both ends. When your app is downloading the file, lock it into a receive state so its not trying to send/receive other stuff at the same time. Once the download is complete, switch to any other state (say, open file state). If you don't know much about state machines, I recommend you look at the wikipedia article:
http://en.wikipedia.org/wiki/Finite-state_machine
I need some help finding the documentation of .read(). I know this is easy, but I can't find it. I've search and searched, and this page on the android side is the closest I could find - http://developer.android.com/guide/topics/usb/accessory.html.
Here is the arduino code splice I am trying to interpret. I need to know how to modify the read() part for my needs. Thanks
AndroidAccessory acc("Manufacturer",
"Model",
"Description",
"1.0",
"hey.now.what",
"0000000012345678");
acc.read(msgIn, sizeof(msgIn), 1)
I was looking the same thing as you did. After googling around and trying it by myself, I managed to build up something like this:
Declaration:
int AndroidAccessory::read(void *buff, int len, unsigned int nakLimit);
Reads data from the Android device into the array pointed to by buff. It reads len number of bytes. Reading is stopped when len bytes is read or nakLimit number of NAKs is received from the USB controller. In case of Full Speed USB a NAK will be generated every 1ms. (according to the second source link).
The return value is the number of bytes available, not the number of bytes read. If you read less bytes than there are available, those extra bytes seem to be ignored.
In my experiments I found out that NAK interval is much smaller. With my Arduino Mega ADK, I found out that one second wait equals roughly nakLimit of 14000.
Sources:
http://developer.android.com/guide/topics/usb/adk.html (end of the page)
What is Nak Limit?