Skip to content
zarthcode edited this page Sep 9, 2012 · 11 revisions

Device Creation & Usage

Creating a libusb device is easy simply call the appropriate LibUSB::FindDevice(...) to filter by vendorID, productID, and (optionally) serial descriptor. Or simply call LibUSB::FindAllDevices() to get every USB device attached to the system.

All of these functions accept a LibUSB::DeviceFactory_t function pointer to a factory method, to be used if you're using a class that inherits from class LibUSB::Device. Leaving it as null (default) will cause the method to simply return a LibUSB::Device object.

Note: Don't let your device shared_ptr go out of scope until you're done using it.

To use your device's basic descriptors, simply call its methods! Note, that to retrieve string descriptors, call Device::Open() first!

Device Configuration

Many times, a configuration will already be active. If so, Device::getActiveConfiguration() will return it. If this returns NULL, use Device::getConfiguration(config value) to obtain a LibUSB::Configuration object.

Note Configuration objects are recalled by config value, as defined by your device descriptors. They are NOT automatically zero indexed. Device::NumConfigurations() does return the number of configurations reported, however.)

Once you've obtained a Configuration object, you can use it's methods to determine if it's suitable. To set it as the active configuration, simply call Configuration::SetAsActive(). (You do not have to keep your own copy of the configuration, your Device will always hold a shared_ptr to its active configuration object)

Choosing an interface

You can examine the interfaces on any Configuration object, even ones that aren't currently selected as active. Simply use Configuration::NumInterfaces(), Configuration::getInterfaceByIndex(index), and Configuration::getInterface(interfaceNumber) Once you've obtained your desired LibUSB::Interface, simply call it's Claim() method.

If you wish to browse the interfaces alternate settings, use Interface::NumAlternateSettings() to get the number of alt settings, and Interface::SetAlternate(setting) to choose an alternate setting. This can be done before or after calling Interface::Claim() on the interface.

Obtaining an endpoint

Endpoints are required in order to obtain a LibUSB::Transfer object.

Endpoint Zero

The required control endpoint doesn't require most of the steps above. You can simply call Device::Open(), followed by Device::getControlEndpoint(). No other objects or steps are required in-between.

If you have an any Interface object already, calling Interface::getEndpoint(0) will also return the default USB control endpoint.

Otherwise you can call Interface::NumEndpoints() to get the number of endpoints in the current interface/altsetting, and Interface::getEndpoint() to get the actual endpoint object.

Transfers

Obtaining a LibUSB::Transfer object

Once you have an endpoint, you're almost ready to transfer data! But first, you need to get the appropriate LibUSB::Transfer object.

std::shared_ptr<LibUSB::**TRANSFERCLASSHERE**> pTransfer = std::static_pointer_cast<LibUSB::**TRANSFERCLASSHERE**>(pEndpoint->CreateTransfer());

Where TRANSFERCLASSHERE is one of the intuitive four transfer types:

  1. Control Transfers - LibUSB::ControlTransfer
  2. Interrupt Transfers - LibUSB::InterruptTransfer
  3. Bulk Transfers - LibUSB::BulkTransfer
  4. Isochronous Transfers - LibUSB::IsochronousTransfer

You can use Endpoint::TransferType() to determine the proper type pragmatically. (Note: If you have suggestions on implementing a helper to make this a bit smoother, syntactically, Feel free to suggest/add-on.)

Performing Transfers

Congrats! You're now ready to perform a transfer! Essentially, every transfer is either going to send or receive data from a buffer that you provide. (Be sure to check out the doxygen/html documentation before starting, or examine the LibusbTest.cpp file to see an example.) But here's an overview:

Initializing the transfer

Common to all transfers is the Transfer::setTransferBuffer(pBuffer, transfersize) method.

If performing an OUT (to the device transfer, You simply allocate a block of memory containing your data structure that you want to transfer OUT, and cast it to a std::shared_ptr<unsigned char>. If transfersize is 0, no data will be transferred!

If performing an IN transfer, provide a buffer that you wish the data to be placed in. If transfersize is 0, no data will be transferred!

Note: For control transfers with data, you must provide 8 additional bytes at the beginning of your data block for the setup packet!! When providing the transfersize, do NOT include those bytes, they are assumed!

To set your timeout, simply call Transfer::SetTimeout(chrono)

Control Transfer Setup packets

If performing a control transfer, you must call ControlTransfer::SetupPacket(...) to create your setup packet data.

Starting the transfer

To begin your transfer, simply call Transfer::Start()

If you have not set a transfer buffer, a default for your transfer type one will be created at this time, or an exception will be thrown.

To start your transfer in a background thread, call Transfer::AsyncStart() instead. Check the transfer's status using Transfer::isComplete(), block on completion by calling Transfer::WaitForCompletion().

Obtaining the results of a transfer.

Failed transfers tend to throw exceptions. All are derived from the standard exception classes. Use Transfer::isSuccessful() to obtain the exception for asynchronous transfers, and Transfer::Result() for actual information, and Transfer::BytesTransferred() and to determine exactly how much data was transferred.

Transfer::getTransferBuffer() will return your original shared_ptr object to the data transferred.

To reuse the same transfer object again, you can call Transfer::Reset()