I'm try to get usb stick's urb info. And I write like follows:
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <usb.h>
/* #include <stropts.h> */
/* #include <inttypes.h> */
#include <linux/usbdevice_fs.h>
/* #include <asm/byteorder.h> */
/* #include <linux/usb/ch9.h> */
#define USBDEVFS_REAPURB _IOW('U', 12, void *)
int main(int argc, char *argv[])
{
int fd;
int result;
int i;
struct usbdevfs_urb * receive;
receive = malloc(sizeof (struct usbdevfs_urb));
bzero(receive, sizeof (struct usbdevfs_urb));
char file[30];
strncpy (file, argv[1], 30);
if ((fd = open(file, O_RDWR)) < 0)
{
printf("failed to open device: %s, \n error: %s \n", file, strerror(errno));
}
else
{
printf ("Open successed: Device: %s\n", file);
for (i = 0; i < 1000; ++i)
{
printf ("polling...\n");
result = ioctl(fd, USBDEVFS_REAPURB, receive);
if (result < 0)
{
printf ("Error! : ioctl returned : %d\n errno =%s\n", result, strerror(errno));
break;
}
printf ("The %d th time ok.\n", i + 1);
usleep(2000);
}
}
close(fd);
return 0;
}
I can compile it. But when I run it it said that Invalid argument.
So, what's wrong with me?
I ended up with a similar problem - so here are my notes... Besides the problems pointed out so far in comments, I think the main issues with the OP code are:
- You cannot just "reap" a URB; you first have to "submit" a (read or write) URB, then you "reap" it
- You cannot just
open
a file
string that you got as path from command line arguments (argv[1]
), and obtain a file descriptor valid for URB ioctl
As for 1, see maybe c - User mode USB isochronous transfer from device-to-host
Also, once I have filled this structure am I right in thinking I need to call the following:
int retSubmit = ioctl( fd, USBDEVFS_SUBMITURB, &usbRequest );
and then once submitted I can wait for the request to complete using
USBDEVFS_REAPURBNDELAY
or '[Linux-usb-users] usbfs urb reap problem.' - MARC
The routine, and the call to it, whidch I use to submit the URB are as follows:
...
Submitting the URB works fine. This is how I try to reap it:
...
linux.usb.devel - Re: usbdevfs questions (and others... ) - msg#00167
Yes, you submit a BULK type usbdevfs_urb to the interrupt endpoint.
It gets one packet of data. You can submit multiple ones that will
queue up. and I'm guessing 'reap' is REAd Pointer, not like the grim
reaper (although maybe it is...:)
I think (but I'm not sure 100%) that "submit" and "reap" are equivalent to "submit" and "completion" as noted in Virtual USB Analyzer - Tutorial.
As far as the 2. goes - it depends on the kind of the device. For instance, if you connect an Arduino Duemillanove, which has an FT232 USB serial chip, to a Linux PC - that will automatically load the ftdi_sio
kernel driver, which in turn loads usbserial
driver, which then creates the file (device node) /dev/ttyUSB0
. Thus, Linux would see this file, at best, as a plain serial port - not something necessarily related to a URB request (I guess something similar goes for USB flash thumbdrives); and I was getting the same error as OP, when trying to call the OP code with an argument of /dev/ttyUSB0
.
Obtaining this file descriptor is somewhat difficult, as it's kinda hard to track down examples:
'Re: Accessing usb devices with USBDEVFS' - MARC
On Fri, May 05, 2006 at 04:45:30PM -0400, Danny Budik wrote:
> How do I use the "raw" usbfs? I tried reading from the /dev/bus/usb file
> and got a bind - address already in use error.
Look at how libusb is implemented for a good example of how to do this.
The source code to 'lsusb' will also help you out here.
Linux Kernel Documentation/usb/proc_usb_info.txt
NOTE: The filesystem has been renamed from "usbdevfs" to
"usbfs", to reduce confusion with "devfs". You may
still see references to the older "usbdevfs" name.
I basically started from the code in [Discuss-gnuradio] New implementation for fusb_linux without allocs/frees, and tried to modify the OP code so it works with an Arduino Duemillanove. The problem is that it uses the older libusb-0.1
code, which has some different names of headers and functions; e.g. libusb-0.1
has libusb/usbi.h, while the newer libusb-1.0
has libusb/libusbi.h. In essence, libusb
functions can be used to obtain the proper file descriptor.
The modified OP code is below, let's say we call it testu.c
. I've tested it on Ubuntu Natty - first, connect the Arduino Duemillanove, which hooks the ftdi_sio
driver (this can be checked in terminal with tail -f /var/log/syslog
). So, to begin with, remove the auto-hooked driver (libusb
doesn't need it to talk to the device, and it's possible it could interfere; note after removal, /dev/ttyUSB0
file doesn't exist anymore):
sudo modprobe -r ftdi_sio # this also removes usbserial
lsmod | grep ftdi # make sure ftdi_sio isn't listed by lsmod
Then, we will use the USB vendor/product ID to connect to the device via libusb
; to find it, use lsusb
:
$ lsusb | grep FT
Bus 002 Device 005: ID 0403:6001 Future Technology Devices International, Ltd FT232 USB-Serial (UART) IC
The VID:PID 0403:6001
is hardcoded in testu.c
; then we can build and run it. Note that the testu
program has to be ran as root (via sudo
) - else libusb
won't be able to communicate with the device:
$ gcc -o testu -Wall -g testu.c `pkg-config --libs --cflags libusb-1.0`
testu.c:23:1: warning: ‘fd_from_usb_dev_handle’ defined but not used
$ sudo ./testu
First
Second 0x8B4B4F0
Open successed: Device: 0403:6001 6
polling...
The 1 th time ok.
polling...
The 2 th time ok.
polling...
The 3 th time ok.
polling...
The 4 th time ok.
polling...
The 5 th time ok.
polling...
The 6 th time ok.
polling...
The 7 th time ok.
polling...
The 8 th time ok.
polling...
The 9 th time ok.
polling...
The 10 th time ok.
The code submits a write request (on endpoint 0x02), then reaps it - and I can see the RX led blink on the Arduino - meaning some data does get to it, as expected. However, not much more than that happens - so I'm not sure if the code answers the "get usb's urb info" part :)
However, it does show how the file descriptor can be obtained, so the raw ioctl
s can work - albeit via libusb
(you'd probably have to dig deeper through the libusb
source, in order to do the same without libusb).
Here is testu.c
(note, install the libusb-dev
package so it can compile):
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <usb.h>
/* #include <stropts.h> */
/* #include <inttypes.h> */
#include <linux/usbdevice_fs.h>
/* #include <asm/byteorder.h> */
/* #include <linux/usb/ch9.h> */
//~ #define USBDEVFS_REAPURB _IOW('U', 12, void *)
#include <libusb.h>
struct libusb_device_handle *d_udh = NULL;
// [http://www.mail-archive.com/discuss-gnuradio@gnu.org/msg17549.html [Discuss-gnuradio] New implementation for fusb_linux without allocs/frees]
static int
fd_from_usb_dev_handle (libusb_device_handle *udh) //(usb_dev_handle *udh)
{
return *((int *) udh);
}
//~ ./libusb/os/linux_usbfs.c
struct linux_device_handle_priv {
int fd;
};
//~ ./libusb/libusbi.h
#define usbi_mutex_t pthread_mutex_t
struct list_head {
struct list_head *prev, *next;
};
struct libusb_device_handle {
/* lock protects claimed_interfaces */
usbi_mutex_t lock;
unsigned long claimed_interfaces;
struct list_head list;
struct libusb_device *dev;
unsigned char os_priv[0];
};
//~ ./libusb/os/linux_usbfs.c
struct linux_device_handle_priv* _device_handle_priv(
struct libusb_device_handle *handle)
{
struct linux_device_handle_priv* out;
out = (struct linux_device_handle_priv *) handle->os_priv;
return out;
};
int main(int argc, char *argv[])
{
int fd;
int result;
int i;
struct usbdevfs_urb * receive;
struct usbdevfs_urb * send;
struct linux_device_handle_priv *hpriv;
send = malloc(sizeof (struct usbdevfs_urb));
memset(send, 0, sizeof (*send));
send->buffer_length = 10;
send->buffer = malloc(sizeof(unsigned char)*10);
send->type = USBDEVFS_URB_TYPE_BULK;
send->endpoint = 0x02;
send->signr = 0;
receive = malloc(sizeof (struct usbdevfs_urb));
bzero(receive, sizeof (struct usbdevfs_urb));
char file[30];
if (argv[1]) strncpy (file, argv[1], 30);
printf ("First\n");
result = libusb_init(NULL);
if (result < 0)
{
printf("failed to initialise libusb:\n");
}
d_udh = libusb_open_device_with_vid_pid(NULL, 0x0403, 0x6001);
hpriv = _device_handle_priv(d_udh);
printf ("Second 0x%X\n", (unsigned int)d_udh);
result = d_udh ? 0 : -EIO;
//~ if ((fd = open(file, O_RDWR)) < 0)
if (result < 0)
{
printf("failed to open device: %s, \n error: %s \n", "0403:6001", strerror(errno)); //file
}
else
{
//~ fd = fd_from_usb_dev_handle(d_udh); // doesn't work
fd = _device_handle_priv(d_udh)->fd;
printf ("Open successed: Device: %s %d\n", "0403:6001", fd ); // file);
for (i = 0; i < 10; ++i)
{
result = ioctl (fd, USBDEVFS_SUBMITURB, send);
if (result < 0)
{
printf ("Error! USBDEVFS_SUBMITURB : ioctl returned : %d\n errno =%s\n", result, strerror(errno));
break;
}
printf ("polling...\n");
result = ioctl(fd, USBDEVFS_REAPURB, receive);
if (result < 0)
{
printf ("Error! USBDEVFS_REAPURB : ioctl returned : %d\n errno =%s\n", result, strerror(errno));
break;
}
printf ("The %d th time ok.\n", i + 1);
usleep(2000);
}
}
close(fd);
return 0;
}
Well, hope this helps someone,
Cheers!
By carefully checking your USB requests won't interfere with the kernel driver, you can perform such requests without detaching the kernel driver. But the libusb workflow imposes claiming the device and thus detaching its non-usb sub kernel driver. So you need to perform the usb ioctls yourself.
Here is an example of sending a USB control message to a FTDI USB device in order to flush its RX and TX FIFOs, without having to detach the ftdi_sio kernel driver.