• (2 Pages)
  • +
  • 1
  • 2

Introduction to using LibUSB-1.0 Rate Topic: ****- 2 Votes

#1 Anarion  Icon User is offline

  • The Persian Coder
  • member icon

Reputation: 305
  • View blog
  • Posts: 1,507
  • Joined: 16-May 09

Posted 07 January 2010 - 03:41 PM

*
POPULAR

Introduction: libusb is an open source library that allows you to communicate with USB devices from userspace. For more info, see Their MainPage.
In their documentation, they suggest you to read the usb2 specifications first: Here, it really helps you understand how things work here.

Installation of Libusb
You have to obtain the source code from Their Website and build it yourself. Or if your Distribution's repository contains the package, you can easily install it from there.
---
If you have setup the library, go on...

Communication
Communication between devices and client software is conceptualized as using pipes. Each pipe is a communication channel between software on the host and an endpoint on a device. Each endpoint represents a part of a device that fulfills one specific purpose for that device, such as to receive commands or transmit data. A full speed device can have up to 16 endpoints, though low speed devices can have only three.

All USB devices support endpoint 0 when powered up. This endpoint is the target of the default pipe. After the attachment of a device has been detected, the USBD software uses endpoint 0 to initialise the device, perform generic (i.e. non device-specific) configuration, and obtain information about the other endpoints provided by the device. Endpoints are characterised by their endpoint number (set at design time) and bus bandwidth, access frequency, latency and error handling behaviour requirements.

Once the endpoints of a device have been identified and configured, pipes come into existence allowing the client software to communicate with the device. A pipe has associated with it characteristics such as a claim on bus access and bandwidth, the type of transfer, the direction of transfer and the maximum data payload size.

USB defines four types of transfer: control transfers which are typically used for command or status operations, interrupt transfers which are initiated by a device to request some action from the host, isochronous transfers which are used to carry data the delivery of which is time critical (such as for video and speech), and bulk transfers which can use all available bandwidth but are not time critical. All transfers take the form of packets, which contain control information, data and error checking fields.

There are also two types of pipe: message pipes and stream pipes. Control transfers are made using message pipes. In a message pipe, the data portion of each packet has some meaning to the USB system software.

Stream pipes are used for interrupt, isochronous and bulk transfers. In a stream pipe, the data portion of the packet has no defined meaning to the USB: the data is merely conveyed between client software and device.

Synchronous Interface
The synchronous I/O interface allows you to perform a USB transfer with a single function call. When the function call returns, the transfer has completed and you can parse the results. The main advantage of this model is simplicity: you did everything with a single simple function call.
However, this interface has its limitations. Your application will sleep inside libusb_bulk_transfer()(when using bulk transfer) until the transaction has completed. If it takes 3 hours, your application will be sleeping for that long. Execution will be tied up inside the library; the entire thread will be useless for that duration.
Another issue is that by tieing up the thread with that single transaction there is no possibility of performing I/O with multiple endpoints and/or multiple devices simultaneously, unless you resort to creating one thread per transaction.
Additionally, there is no opportunity to cancel the transfer after the request has been submitted.

Devices and interfaces
Each usb device is manipulated with a libusb_device and libusb_device_handle objects in libusb. The libusb API ties an open device to a specific interface. This means that if you want to claim multiple interfaces on a device, you should open the device multiple times to receive one libusb_dev_handle for each interface you want to communicate with. Don't forget to call usb_claim_interface.
What does this mean? Means you have to claim the interface before you do any operation on the device, and also, you have to release the claimed interface after you are finished with the device.

OK, now what? yup, we have to find these, so called devices/interfaces so that we can claim them and assign to a handle and so on... here is the magic... :D

Each device has it's own configuration values, like vendor id, product id, etc. We use these settings to discover the desired device and work with. First we have to write a function to determine these options and print them, so that we can discover the right one; the basic operations we have to do are:
  • initialize the library by calling the function libusb_init and creating a session
  • Call the function libusb_get_device_list to get a list of connected devices. This creates an array of libusb_device containing all usb devices connected to the system.
  • Loop through all these devices and check their options
  • Discover the one and open the device either by libusb_open or libusb_open_device_with_vid_pid(when you know vendor and product id of the device) to open the device
  • Clear the list you got from libusb_get_device_list by using libusb_free_device_list
  • Claim the interface with libusb_claim_interface (requires you to know the interface numbers of device)
  • Do desired I/O
  • Release the device by using libusb_release_interface
  • Close the device you openedbefore, by using libusb_close
  • Close the session by using libusb_exit
OK, now you have the basic idea on how to obtain some information about your devices... Here's a basic program to do this:
#include <iostream>
#include <libusb.h>
using namespace std;

void printdev(libusb_device *dev); //prototype of the function

int main() {
	libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
	libusb_context *ctx = NULL; //a libusb session
	int r; //for return values
	ssize_t cnt; //holding number of devices in list
	r = libusb_init(&ctx); //initialize a library session
	if(r < 0) {
		cout<<"Init Error "<<r<<endl; //there was an error
				return 1;
	}
	libusb_set_debug(ctx, 3); //set verbosity level to 3, as suggested in the documentation
	cnt = libusb_get_device_list(ctx, &devs); //get the list of devices
	if(cnt < 0) {
		cout<<"Get Device Error"<<endl; //there was an error
	}
	cout<<cnt<<" Devices in list."<<endl; //print total number of usb devices
		ssize_t i; //for iterating through the list
	for(i = 0; i < cnt; i++) {
				printdev(devs[i]); //print specs of this device
		}
		libusb_free_device_list(devs, 1); //free the list, unref the devices in it
		libusb_exit(ctx); //close the session
		return 0;
}

void printdev(libusb_device *dev) {
	libusb_device_descriptor desc;
	int r = libusb_get_device_descriptor(dev, &desc);
	if (r < 0) {
		cout<<"failed to get device descriptor"<<endl;
		return;
	}
	cout<<"Number of possible configurations: "<<(int)desc.bNumConfigurations<<"  ";
	cout<<"Device Class: "<<(int)desc.bDeviceClass<<"  ";
	cout<<"VendorID: "<<desc.idVendor<<"  ";
	cout<<"ProductID: "<<desc.idProduct<<endl;
	libusb_config_descriptor *config;
	libusb_get_config_descriptor(dev, 0, &config);
	cout<<"Interfaces: "<<(int)config->bNumInterfaces<<" ||| ";
	const libusb_interface *inter;
	const libusb_interface_descriptor *interdesc;
	const libusb_endpoint_descriptor *epdesc;
	for(int i=0; i<(int)config->bNumInterfaces; i++) {
		inter = &config->interface[i];
		cout<<"Number of alternate settings: "<<inter->num_altsetting<<" | ";
		for(int j=0; j<inter->num_altsetting; j++) {
			interdesc = &inter->altsetting[j];
			cout<<"Interface Number: "<<(int)interdesc->bInterfaceNumber<<" | ";
			cout<<"Number of endpoints: "<<(int)interdesc->bNumEndpoints<<" | ";
			for(int k=0; k<(int)interdesc->bNumEndpoints; k++) {
				epdesc = &interdesc->endpoint[k];
				cout<<"Descriptor Type: "<<(int)epdesc->bDescriptorType<<" | ";
				cout<<"EP Address: "<<(int)epdesc->bEndpointAddress<<" | ";
			}
		}
	}
	cout<<endl<<endl<<endl;
	libusb_free_config_descriptor(config);
}

OK, now compile it and try to see things... I ran this program and checked the devices, then connected my pendrive and ran the program again... there was a new entry up there, this way I could find the vendor id and product id of that pendrive so that I can open it.

**Note** Device discovery (calling libusb_get_device_list()) returns a freshly-allocated list of devices. The list itself must be freed when you are done with it. libusb also needs to know when it is OK to free the contents of the list; the devices themselves.
To handle these issues, libusb provides you with two separate items:
  • A function to free the list itself
  • A reference counting system for the devices inside
New devices presented by the libusb_get_device_list() function all have a reference count of 1. You can increase and decrease reference count using libusb_ref_device() and libusb_unref_device(). A device is destroyed when its reference count reaches 0.
With the above information in mind, the process of opening a device can be viewed as follows:
  • Discover devices using libusb_get_device_list().
  • Choose the device that you want to operate, and call libusb_open().
  • Unref all devices in the discovered device list.
  • Free the discovered device list.
The order is important - you must not unreference the device before attempting to open it, because unreferencing it may destroy the device.
For convenience, the libusb_free_device_list() function includes a parameter to optionally unreference all the devices in the list before freeing the list itself. This combines steps 3 and 4 above.

OK, here is the libusb1's API documentationreference on functions if you need :)

OK, now you can find your desired devices... it's the time to open this device, claim it and perform a very simple I/O.
If you have the vendor and product id, use libusb_open_device_with_vid_pid.

Also note that, if kernel (your OS) connects to the device, you can't claim it. In this case you have to call libusb_detach_kernel_driver to detach kernel from your device. If you want to know whether the kernel did this or not, use libusb_kernel_driver_active, if the return value is 1, then kernel attached a driver to your device.

Bulk Transfer
To do a bulk transfer to your device, you have to have a device handler for your usb device, and you have to know which endpoint to use (get from device specs above).
Refer Here for information on the syntax.

Now, here's an example to combine all the things I mentioned above:
#include <iostream>
#include <libusb.h>

using namespace std;

int main() {
	libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
	libusb_device_handle *dev_handle; //a device handle
	libusb_context *ctx = NULL; //a libusb session
	int r; //for return values
	ssize_t cnt; //holding number of devices in list
	r = libusb_init(&ctx); //initialize the library for the session we just declared
	if(r < 0) {
		cout<<"Init Error "<<r<<endl; //there was an error
		return 1;
	}
	libusb_set_debug(ctx, 3); //set verbosity level to 3, as suggested in the documentation

	cnt = libusb_get_device_list(ctx, &devs); //get the list of devices
	if(cnt < 0) {
		cout<<"Get Device Error"<<endl; //there was an error
		return 1;
	}
	cout<<cnt<<" Devices in list."<<endl;

	dev_handle = libusb_open_device_with_vid_pid(ctx, 5118, 7424); //these are vendorID and productID I found for my usb device
	if(dev_handle == NULL)
		cout<<"Cannot open device"<<endl;
	else
		cout<<"Device Opened"<<endl;
	libusb_free_device_list(devs, 1); //free the list, unref the devices in it

	unsigned char *data = new unsigned char[4]; //data to write
	data[0]='a';data[1]='b';data[2]='c';data[3]='d'; //some dummy values

	int actual; //used to find out how many bytes were written
	if(libusb_kernel_driver_active(dev_handle, 0) == 1) { //find out if kernel driver is attached
		cout<<"Kernel Driver Active"<<endl;
		if(libusb_detach_kernel_driver(dev_handle, 0) == 0) //detach it
			cout<<"Kernel Driver Detached!"<<endl;
	}
	r = libusb_claim_interface(dev_handle, 0); //claim interface 0 (the first) of device (mine had jsut 1)
	if(r < 0) {
		cout<<"Cannot Claim Interface"<<endl;
		return 1;
	}
	cout<<"Claimed Interface"<<endl;
	
	cout<<"Data->"<<data<<"<-"<<endl; //just to see the data we want to write : abcd
	cout<<"Writing Data..."<<endl;
	r = libusb_bulk_transfer(dev_handle, (2 | LIBUSB_ENDPOINT_OUT), data, 4, &actual, 0); //my device's out endpoint was 2, found with trial- the device had 2 endpoints: 2 and 129
	if(r == 0 && actual == 4) //we wrote the 4 bytes successfully
		cout<<"Writing Successful!"<<endl;
	else
		cout<<"Write Error"<<endl;
	
	r = libusb_release_interface(dev_handle, 0); //release the claimed interface
	if(r!=0) {
		cout<<"Cannot Release Interface"<<endl;
		return 1;
	}
	cout<<"Released Interface"<<endl;

	libusb_close(dev_handle); //close the device we opened
	libusb_exit(ctx); //needs to be called to end the

	delete[] data; //delete the allocated memory for data
	return 0;
}


Conclusion: this tutorial is just a simple introduction to the subject... practice with other synchronous transfers, then move on to the asynchronous... See? there is much to learn :D

Hope this was helpful to get you started :)

Is This A Good Question/Topic? 5
  • +

Replies To: Introduction to using LibUSB-1.0

#2 laserbeak43  Icon User is offline

  • D.I.C Head

Reputation: 4
  • View blog
  • Posts: 134
  • Joined: 07-November 09

Posted 26 June 2010 - 08:17 AM

I'm really interested in this tutorial, but last time I've installed libUSB for windows(an older build) my computer went nuts. Has this issue been fixed? I'm on win7 btw
Was This Post Helpful? 1
  • +
  • -

#3 Anarion  Icon User is offline

  • The Persian Coder
  • member icon

Reputation: 305
  • View blog
  • Posts: 1,507
  • Joined: 16-May 09

Posted 26 June 2010 - 11:41 AM

View Postlaserbeak43, on 26 June 2010 - 05:47 PM, said:

I'm really interested in this tutorial, but last time I've installed libUSB for windows(an older build) my computer went nuts. Has this issue been fixed? I'm on win7 btw

I guess you should use WinUSB instead. Haven't worked with it myself yet, so I don't know how much it is similar to Libusb.
Was This Post Helpful? 1
  • +
  • -

#4 laserbeak43  Icon User is offline

  • D.I.C Head

Reputation: 4
  • View blog
  • Posts: 134
  • Joined: 07-November 09

Posted 26 June 2010 - 01:28 PM

View PostAnarion, on 26 June 2010 - 10:41 AM, said:

View Postlaserbeak43, on 26 June 2010 - 05:47 PM, said:

I'm really interested in this tutorial, but last time I've installed libUSB for windows(an older build) my computer went nuts. Has this issue been fixed? I'm on win7 btw

I guess you should use WinUSB instead. Haven't worked with it myself yet, so I don't know how much it is similar to Libusb.

hmm, i hear the microsoft library is REALLY hard. I will try the latest windows build of libusb and let you know. That was my main interest, just wanted to know if you've heard anything about the issues.
Was This Post Helpful? 0
  • +
  • -

#5 Guest_Jhon*


Reputation:

Posted 26 July 2010 - 04:21 AM

How do you buld the bulk example? (or the first for that matter...)
Was This Post Helpful? 0

#6 Anarion  Icon User is offline

  • The Persian Coder
  • member icon

Reputation: 305
  • View blog
  • Posts: 1,507
  • Joined: 16-May 09

Posted 28 July 2010 - 08:58 AM

View PostJhon, on 26 July 2010 - 01:51 PM, said:

How do you buld the bulk example? (or the first for that matter...)

You have to link with the libusb library to be able to link the project successfully. Use the options in your IDE(if you are using Code::Blocks or Visual Studio etc.). Or if you use makefiles and on linux, do like this:
executable: main.cpp /path/to/library/include/libusb.h
    g++ -o executable main.cpp -L /path/to/library/object/ -lusb-1.0

This post has been edited by Anarion: 01 August 2010 - 10:39 AM

Was This Post Helpful? 0
  • +
  • -

#7 Nev MX  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 4
  • Joined: 31-July 10

Posted 02 August 2010 - 04:44 AM

I used your tutorial and successfully detected my devices.

But how do you make the program listen what the device is sending through USB?
For example, when I send the command MODEL to my specific device, it should answer with its model number.

I've looked through the libusb API documentation, but I don't see any functions to receive data.

Thanks
Was This Post Helpful? 0
  • +
  • -

#8 Anarion  Icon User is offline

  • The Persian Coder
  • member icon

Reputation: 305
  • View blog
  • Posts: 1,507
  • Joined: 16-May 09

Posted 02 August 2010 - 10:22 AM

View PostNev MX, on 02 August 2010 - 02:14 PM, said:

I used your tutorial and successfully detected my devices.

But how do you make the program listen what the device is sending through USB?
For example, when I send the command MODEL to my specific device, it should answer with its model number.

I've looked through the libusb API documentation, but I don't see any functions to receive data.

Thanks

It's the same function as sending, for example, you can use a bulk transfer both for reading and writing. As written [ Here ], the direction of the transfer is inferred from direction bits of endpoint address.

Something like this:
libusb_bulk_transfer(dev_handle, (2 | LIBUSB_ENDPOINT_IN), data, 4, &actual, 0);

It's important that you use the right endpoint address and direction.

This post has been edited by Anarion: 02 August 2010 - 10:23 AM

Was This Post Helpful? 0
  • +
  • -

#9 laserbeak43  Icon User is offline

  • D.I.C Head

Reputation: 4
  • View blog
  • Posts: 134
  • Joined: 07-November 09

Posted 07 August 2010 - 01:53 PM

bah last i tried libusb for windows, it crashed my PC. I need to try again.
Was This Post Helpful? 0
  • +
  • -

#10 Guest_Dan*


Reputation:

Posted 12 September 2010 - 01:44 PM

As I understand it, the OS will attempt to load a kernel driver after enumerating a new USB device. But a libusb driver will not be loaded since it's a user-space driver. If I have a USB device with no kernel driver (just the user-space libusb driver), Linux will throw an error immediately after enumeration. Is that typical behavior for homemade devices with libusb drivers?
Was This Post Helpful? 0

#11 Anarion  Icon User is offline

  • The Persian Coder
  • member icon

Reputation: 305
  • View blog
  • Posts: 1,507
  • Joined: 16-May 09

Posted 11 October 2010 - 08:34 AM

View PostDan, on 12 September 2010 - 11:14 PM, said:

As I understand it, the OS will attempt to load a kernel driver after enumerating a new USB device. But a libusb driver will not be loaded since it's a user-space driver. If I have a USB device with no kernel driver (just the user-space libusb driver), Linux will throw an error immediately after enumeration. Is that typical behavior for homemade devices with libusb drivers?

Yes, I think. The error is shown up because there is no driver available by kernel itself to attach to the device.
Was This Post Helpful? 0
  • +
  • -

#12 LeoricKing  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 23-November 10

Posted 23 November 2010 - 06:40 PM

O,O this is what i need;
can it work perfectly with c++?
Was This Post Helpful? 0
  • +
  • -

#13 Anarion  Icon User is offline

  • The Persian Coder
  • member icon

Reputation: 305
  • View blog
  • Posts: 1,507
  • Joined: 16-May 09

Posted 02 December 2010 - 03:09 AM

View PostLeoricKing, on 24 November 2010 - 04:10 AM, said:

O,O this is what i need;
can it work perfectly with c++?

As you can see, the form of API is C, which means it's not very nice compared to native C++ APIs. But there is no problem for using it within C++ :)

*Quick Note: I guess there is a C++ wrapper for this library, but might be abandoned by now...
Was This Post Helpful? 0
  • +
  • -

#14 youbob1212  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 16
  • View blog
  • Posts: 132
  • Joined: 31-October 10

Posted 17 December 2010 - 04:20 PM

My big question when working with the USB's, is that you can daisy chain jump drives, or just connect them to whatever open connection that you have on your computer; however, is there a way to link all of those different jump drives, under one drive letter?
With that being true, the idea is that all of those jump drives would act like one big drive. Much like a hard drive has several disk, and that data is written starting at the beginning of one disk, and that data would spill over the next disk if the first disk is full.

So again what I want to know is that with this programming library could someone write an program that would make linking several jump drives under one drive letter; having them to work like a hard drive's disk array.. or something like that?
Was This Post Helpful? 0
  • +
  • -

#15 Anarion  Icon User is offline

  • The Persian Coder
  • member icon

Reputation: 305
  • View blog
  • Posts: 1,507
  • Joined: 16-May 09

Posted 31 December 2010 - 02:54 AM

View Postyoubob1212, on 18 December 2010 - 01:50 AM, said:

is there a way to link all of those different jump drives, under one drive letter?

Because each of the USB devices have a Mass Storage Controller of their own, you need to separate the data first, and then send the parts to the right device yourself. Given that, you have to do all the preparations of data for reading/writing yourself.

So, in order to do the task, you have to write a driver yourself, and use that instead of the driver your OS provides you with.
Was This Post Helpful? 0
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2