Add support for persistent device memory.
Add a function to allocate memory belonging to a specific device, so that the operating system can DMA straight into it for zerocopy, and also avoid some clearing. Also, this allows up-front memory allocation in the kernel at program startup; memory allocation is otherwise done per-transfer, which can fail in a system where memory has become fragmented over time). This mirrors new functionality going into Linux' USB stack (recently reviewed and acked upstream); only Linux is supported as a backend currently. [Chris Dickens] Modified to fix doxygen documentation, correct parameter naming, reposition function declarations, and address a missing request during the patch review process. Signed-off-by: Chris Dickens <christopher.a.dickens@gmail.com>
This commit is contained in:
parent
0b947e5f9b
commit
a283c3b5a3
7 changed files with 110 additions and 2 deletions
0
.amend
Normal file
0
.amend
Normal file
|
@ -357,6 +357,8 @@ if (cfg != desired)
|
||||||
* - libusb_control_transfer_get_setup()
|
* - libusb_control_transfer_get_setup()
|
||||||
* - libusb_cpu_to_le16()
|
* - libusb_cpu_to_le16()
|
||||||
* - libusb_detach_kernel_driver()
|
* - libusb_detach_kernel_driver()
|
||||||
|
* - libusb_dev_mem_alloc()
|
||||||
|
* - libusb_dev_mem_free()
|
||||||
* - libusb_error_name()
|
* - libusb_error_name()
|
||||||
* - libusb_event_handler_active()
|
* - libusb_event_handler_active()
|
||||||
* - libusb_event_handling_ok()
|
* - libusb_event_handling_ok()
|
||||||
|
@ -1812,6 +1814,60 @@ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle,
|
||||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \ingroup libusb_asyncio
|
||||||
|
* Attempts to allocate a block of persistent DMA memory suitable for transfers
|
||||||
|
* against the given device. If successful, will return a block of memory
|
||||||
|
* that is suitable for use as "buffer" in \ref libusb_transfer against this
|
||||||
|
* device. Using this memory instead of regular memory means that the host
|
||||||
|
* controller can use DMA directly into the buffer to increase performance, and
|
||||||
|
* also that transfers can no longer fail due to kernel memory fragmentation.
|
||||||
|
*
|
||||||
|
* Note that this means you should not modify this memory (or even data on
|
||||||
|
* the same cache lines) when a transfer is in progress, although it is legal
|
||||||
|
* to have several transfers going on within the same memory block.
|
||||||
|
*
|
||||||
|
* Will return NULL on failure. Many systems do not support such zerocopy
|
||||||
|
* and will always return NULL. Memory allocated with this function must be
|
||||||
|
* freed with \ref libusb_dev_mem_free. Specifically, this means that the
|
||||||
|
* flag \ref LIBUSB_TRANSFER_FREE_BUFFER cannot be used to free memory allocated
|
||||||
|
* with this function.
|
||||||
|
*
|
||||||
|
* Since version 1.0.21, \ref LIBUSB_API_VERSION >= 0x01000105
|
||||||
|
*
|
||||||
|
* \param dev_handle a device handle
|
||||||
|
* \param length size of desired data buffer
|
||||||
|
* \returns a pointer to the newly allocated memory, or NULL on failure
|
||||||
|
*/
|
||||||
|
DEFAULT_VISIBILITY
|
||||||
|
unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle,
|
||||||
|
size_t length)
|
||||||
|
{
|
||||||
|
if (!dev_handle->dev->attached)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (usbi_backend->dev_mem_alloc)
|
||||||
|
return usbi_backend->dev_mem_alloc(dev_handle, length);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \ingroup libusb_asyncio
|
||||||
|
* Free device memory allocated with libusb_dev_mem_alloc().
|
||||||
|
*
|
||||||
|
* \param dev_handle a device handle
|
||||||
|
* \param buffer pointer to the previously allocated memory
|
||||||
|
* \param length size of previously allocated memory
|
||||||
|
* \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure
|
||||||
|
*/
|
||||||
|
int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle,
|
||||||
|
unsigned char *buffer, size_t length)
|
||||||
|
{
|
||||||
|
if (usbi_backend->dev_mem_free)
|
||||||
|
return usbi_backend->dev_mem_free(dev_handle, buffer, length);
|
||||||
|
else
|
||||||
|
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
/** \ingroup libusb_dev
|
/** \ingroup libusb_dev
|
||||||
* Determine if a kernel driver is active on an interface. If a kernel driver
|
* Determine if a kernel driver is active on an interface. If a kernel driver
|
||||||
* is active, you cannot claim the interface, and libusb will be unable to
|
* is active, you cannot claim the interface, and libusb will be unable to
|
||||||
|
|
|
@ -20,6 +20,10 @@ EXPORTS
|
||||||
libusb_control_transfer@32 = libusb_control_transfer
|
libusb_control_transfer@32 = libusb_control_transfer
|
||||||
libusb_detach_kernel_driver
|
libusb_detach_kernel_driver
|
||||||
libusb_detach_kernel_driver@8 = libusb_detach_kernel_driver
|
libusb_detach_kernel_driver@8 = libusb_detach_kernel_driver
|
||||||
|
libusb_dev_mem_alloc
|
||||||
|
libusb_dev_mem_alloc@8 = libusb_dev_mem_alloc
|
||||||
|
libusb_dev_mem_free
|
||||||
|
libusb_dev_mem_free@12 = libusb_dev_mem_free
|
||||||
libusb_error_name
|
libusb_error_name
|
||||||
libusb_error_name@4 = libusb_error_name
|
libusb_error_name@4 = libusb_error_name
|
||||||
libusb_event_handler_active
|
libusb_event_handler_active
|
||||||
|
|
|
@ -1137,7 +1137,10 @@ enum libusb_transfer_flags {
|
||||||
/** Report short frames as errors */
|
/** Report short frames as errors */
|
||||||
LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0,
|
LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0,
|
||||||
|
|
||||||
/** Automatically free() transfer buffer during libusb_free_transfer() */
|
/** Automatically free() transfer buffer during libusb_free_transfer().
|
||||||
|
* Note that buffers allocated with libusb_dev_mem_alloc() should not
|
||||||
|
* be attempted freed in this way, since free() is not an appropriate
|
||||||
|
* way to release such memory. */
|
||||||
LIBUSB_TRANSFER_FREE_BUFFER = 1<<1,
|
LIBUSB_TRANSFER_FREE_BUFFER = 1<<1,
|
||||||
|
|
||||||
/** Automatically call libusb_free_transfer() after callback returns.
|
/** Automatically call libusb_free_transfer() after callback returns.
|
||||||
|
@ -1392,6 +1395,11 @@ int LIBUSB_CALL libusb_alloc_streams(libusb_device_handle *dev_handle,
|
||||||
int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev_handle,
|
int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev_handle,
|
||||||
unsigned char *endpoints, int num_endpoints);
|
unsigned char *endpoints, int num_endpoints);
|
||||||
|
|
||||||
|
unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle,
|
||||||
|
size_t length);
|
||||||
|
int LIBUSB_CALL libusb_dev_mem_free(libusb_device_handle *dev_handle,
|
||||||
|
unsigned char *buffer, size_t length);
|
||||||
|
|
||||||
int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev_handle,
|
int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev_handle,
|
||||||
int interface_number);
|
int interface_number);
|
||||||
int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
|
int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
|
||||||
|
|
|
@ -933,6 +933,16 @@ struct usbi_os_backend {
|
||||||
int (*free_streams)(struct libusb_device_handle *dev_handle,
|
int (*free_streams)(struct libusb_device_handle *dev_handle,
|
||||||
unsigned char *endpoints, int num_endpoints);
|
unsigned char *endpoints, int num_endpoints);
|
||||||
|
|
||||||
|
/* Allocate persistent DMA memory for the given device, suitable for
|
||||||
|
* zerocopy. May return NULL on failure. Optional to implement.
|
||||||
|
*/
|
||||||
|
unsigned char *(*dev_mem_alloc)(struct libusb_device_handle *handle,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
|
/* Free memory allocated by dev_mem_alloc. */
|
||||||
|
int (*dev_mem_free)(struct libusb_device_handle *handle,
|
||||||
|
unsigned char *buffer, size_t len);
|
||||||
|
|
||||||
/* Determine if a kernel driver is active on an interface. Optional.
|
/* Determine if a kernel driver is active on an interface. Optional.
|
||||||
*
|
*
|
||||||
* The presence of a kernel driver on an interface indicates that any
|
* The presence of a kernel driver on an interface indicates that any
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
@ -1557,6 +1558,32 @@ static int op_free_streams(struct libusb_device_handle *handle,
|
||||||
endpoints, num_endpoints);
|
endpoints, num_endpoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned char *op_dev_mem_alloc(struct libusb_device_handle *handle,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
struct linux_device_handle_priv *hpriv = _device_handle_priv(handle);
|
||||||
|
unsigned char *buffer = (unsigned char *)mmap(NULL, len,
|
||||||
|
PROT_READ | PROT_WRITE, MAP_SHARED, hpriv->fd, 0);
|
||||||
|
if (buffer == MAP_FAILED) {
|
||||||
|
usbi_err(HANDLE_CTX(handle), "alloc dev mem failed errno %d",
|
||||||
|
errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int op_dev_mem_free(struct libusb_device_handle *handle,
|
||||||
|
unsigned char *buffer, size_t len)
|
||||||
|
{
|
||||||
|
if (munmap(buffer, len) != 0) {
|
||||||
|
usbi_err(HANDLE_CTX(handle), "free dev mem failed errno %d",
|
||||||
|
errno);
|
||||||
|
return LIBUSB_ERROR_OTHER;
|
||||||
|
} else {
|
||||||
|
return LIBUSB_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int op_kernel_driver_active(struct libusb_device_handle *handle,
|
static int op_kernel_driver_active(struct libusb_device_handle *handle,
|
||||||
int interface)
|
int interface)
|
||||||
{
|
{
|
||||||
|
@ -2678,6 +2705,9 @@ const struct usbi_os_backend linux_usbfs_backend = {
|
||||||
.alloc_streams = op_alloc_streams,
|
.alloc_streams = op_alloc_streams,
|
||||||
.free_streams = op_free_streams,
|
.free_streams = op_free_streams,
|
||||||
|
|
||||||
|
.dev_mem_alloc = op_dev_mem_alloc,
|
||||||
|
.dev_mem_free = op_dev_mem_free,
|
||||||
|
|
||||||
.kernel_driver_active = op_kernel_driver_active,
|
.kernel_driver_active = op_kernel_driver_active,
|
||||||
.detach_kernel_driver = op_detach_kernel_driver,
|
.detach_kernel_driver = op_detach_kernel_driver,
|
||||||
.attach_kernel_driver = op_attach_kernel_driver,
|
.attach_kernel_driver = op_attach_kernel_driver,
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
#define LIBUSB_NANO 11109
|
#define LIBUSB_NANO 11110
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue