-
Notifications
You must be signed in to change notification settings - Fork 325
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
local: Update to new DMABUF-based kernel API
The previous DMABUF-based API was refused upstream as it had several problems and was very complex. A lot of that complexity was lifted by making the IIO core a DMABUF importer instead of an exporter. This means that the IIO core is no more responsible for creating the DMABUF objects, and to map them in the DMA space. This task is now delegated to the "dma-heap" kernel driver, available since kernel 5.6, which allows userspace programs to create DMABUF objects from the system heap. The DMABUF interface of the IIO core then simply consists in three IOCTLs: one to attach a DMABUF to the interface, one to detach it, and one to request a data transfer. Signed-off-by: Paul Cercueil <[email protected]>
- Loading branch information
1 parent
3ba7170
commit dc59ac0
Showing
1 changed file
with
101 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,20 +6,35 @@ | |
* Author: Paul Cercueil <[email protected]> | ||
*/ | ||
|
||
#define _GNU_SOURCE | ||
#include "local.h" | ||
|
||
#include <errno.h> | ||
#include <fcntl.h> | ||
#include <iio/iio.h> | ||
#include <iio/iio-backend.h> | ||
#include <iio/iio-debug.h> | ||
#include <poll.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <sys/ioctl.h> | ||
#include <sys/mman.h> | ||
#include <sys/syscall.h> | ||
#include <time.h> | ||
#include <unistd.h> | ||
|
||
#define IIO_DMABUF_ALLOC_IOCTL _IOW('i', 0x92, struct iio_dmabuf_req) | ||
#define IIO_DMABUF_ENQUEUE_IOCTL _IOW('i', 0x93, struct iio_dmabuf) | ||
struct iio_dmabuf_heap_data { | ||
uint64_t len; | ||
uint32_t fd; | ||
uint32_t fd_flags; | ||
uint64_t heap_flags; | ||
}; | ||
|
||
#define IIO_DMA_HEAP_ALLOC _IOWR('H', 0x0, struct iio_dmabuf_heap_data) | ||
|
||
#define IIO_DMABUF_ATTACH_IOCTL _IOW('i', 0x92, int) | ||
#define IIO_DMABUF_DETACH_IOCTL _IOW('i', 0x93, int) | ||
#define IIO_DMABUF_ENQUEUE_IOCTL _IOW('i', 0x94, struct iio_dmabuf) | ||
#define IIO_DMABUF_SYNC_IOCTL _IOW('b', 0, struct dma_buf_sync) | ||
|
||
#define IIO_DMABUF_FLAG_CYCLIC (1 << 0) | ||
|
@@ -30,11 +45,6 @@ | |
#define DMA_BUF_SYNC_START (0 << 2) | ||
#define DMA_BUF_SYNC_END (1 << 2) | ||
|
||
struct iio_dmabuf_req { | ||
uint64_t size; | ||
uint64_t resv; | ||
}; | ||
|
||
struct iio_dmabuf { | ||
int32_t fd; | ||
uint32_t flags; | ||
|
@@ -45,30 +55,54 @@ struct dma_buf_sync { | |
uint64_t flags; | ||
}; | ||
|
||
static int enable_cpu_access(struct iio_block_pdata *pdata, bool enable) | ||
{ | ||
struct dma_buf_sync dbuf_sync = { 0 }; | ||
int fd = (int)(intptr_t) pdata->pdata; | ||
|
||
dbuf_sync.flags = DMA_BUF_SYNC_RW; | ||
|
||
if (enable) | ||
dbuf_sync.flags |= DMA_BUF_SYNC_START; | ||
else | ||
dbuf_sync.flags |= DMA_BUF_SYNC_END; | ||
|
||
return ioctl_nointr(fd, IIO_DMABUF_SYNC_IOCTL, &dbuf_sync); | ||
} | ||
|
||
struct iio_block_pdata * | ||
local_create_dmabuf(struct iio_buffer_pdata *pdata, size_t size, void **data) | ||
{ | ||
struct iio_dmabuf_heap_data req = { | ||
.len = size, | ||
.fd_flags = O_CLOEXEC | O_RDWR, | ||
}; | ||
struct iio_block_pdata *priv; | ||
struct iio_dmabuf_req req; | ||
int ret, fd; | ||
int ret, fd, devfd; | ||
|
||
priv = zalloc(sizeof(*priv)); | ||
if (!priv) | ||
return iio_ptr(-ENOMEM); | ||
|
||
req.size = size; | ||
req.resv = 0; | ||
devfd = open("/dev/dma_heap/system", O_RDONLY | O_CLOEXEC | O_NOFOLLOW); /* Flawfinder: ignore */ | ||
if (devfd < 0) { | ||
ret = -errno; | ||
|
||
ret = ioctl_nointr(pdata->fd, IIO_DMABUF_ALLOC_IOCTL, &req); | ||
/* If we're running on an old kernel, return -ENOSYS to mark | ||
* the DMABUF interface as unavailable */ | ||
if (ret == -ENOENT) | ||
ret = -ENOSYS; | ||
|
||
/* If we get -ENODEV or -EINVAL errors here, the ioctl is wrong and the | ||
* high-speed DMABUF interface is not supported. */ | ||
if (ret == -ENODEV || ret == -EINVAL || ret == -ENOTTY) | ||
ret = -ENOSYS; | ||
if (ret < 0) | ||
goto err_free_priv; | ||
} | ||
|
||
ret = ioctl(devfd, IIO_DMA_HEAP_ALLOC, &req); | ||
if (ret < 0) { | ||
ret = -errno; | ||
goto err_close_devfd; | ||
} | ||
|
||
fd = ret; | ||
fd = req.fd; | ||
|
||
*data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | ||
if (*data == MAP_FAILED) { | ||
|
@@ -83,18 +117,47 @@ local_create_dmabuf(struct iio_buffer_pdata *pdata, size_t size, void **data) | |
priv->dequeued = true; | ||
pdata->dmabuf_supported = true; | ||
|
||
/* The new block is dequeued by default, so enable CPU access */ | ||
ret = enable_cpu_access(priv, true); | ||
if (ret) | ||
goto err_data_unmap; | ||
|
||
/* Attach DMABUF to the buffer */ | ||
ret = ioctl(pdata->fd, IIO_DMABUF_ATTACH_IOCTL, &fd); | ||
if (ret) { | ||
ret = -errno; | ||
|
||
if (ret == -ENODEV) { | ||
/* If the ioctl is not available, return -ENOSYS to mark | ||
* the DMABUF interface as unavailable */ | ||
ret = -ENOSYS; | ||
} | ||
|
||
goto err_data_unmap; | ||
} | ||
|
||
close(devfd); | ||
|
||
return priv; | ||
|
||
err_data_unmap: | ||
munmap(priv->data, priv->size); | ||
err_close_fd: | ||
close(fd); | ||
err_close_devfd: | ||
close(devfd); | ||
err_free_priv: | ||
free(priv); | ||
return iio_ptr(ret); | ||
} | ||
|
||
void local_free_dmabuf(struct iio_block_pdata *pdata) | ||
{ | ||
int fd = (int)(intptr_t) pdata->pdata; | ||
int ret, fd = (int)(intptr_t) pdata->pdata; | ||
|
||
ret = ioctl(pdata->buf->fd, IIO_DMABUF_DETACH_IOCTL, &fd); | ||
if (ret) | ||
dev_perror(pdata->buf->dev, ret, "Unable to detach DMABUF"); | ||
|
||
munmap(pdata->data, pdata->size); | ||
close(fd); | ||
|
@@ -104,30 +167,34 @@ void local_free_dmabuf(struct iio_block_pdata *pdata) | |
int local_enqueue_dmabuf(struct iio_block_pdata *pdata, | ||
size_t bytes_used, bool cyclic) | ||
{ | ||
struct dma_buf_sync dbuf_sync; | ||
struct iio_dmabuf dmabuf; | ||
int ret, fd = (int)(intptr_t) pdata->pdata; | ||
|
||
if (!pdata->dequeued) | ||
return -EPERM; | ||
|
||
if (bytes_used > pdata->size || bytes_used == 0) | ||
return -EINVAL; | ||
|
||
dmabuf.fd = fd; | ||
dmabuf.flags = 0; | ||
dmabuf.bytes_used = bytes_used; | ||
|
||
if (cyclic) | ||
dmabuf.flags |= IIO_DMABUF_FLAG_CYCLIC; | ||
dmabuf.flags |= IIO_DMABUF_FLAG_CYCLIC; | ||
|
||
dbuf_sync.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW; | ||
|
||
/* Disable CPU access to last block */ | ||
ret = ioctl_nointr(fd, IIO_DMABUF_SYNC_IOCTL, &dbuf_sync); | ||
if (ret) | ||
return ret; | ||
if (!pdata->cpu_access_disabled) { | ||
/* Disable CPU access to last block */ | ||
ret = enable_cpu_access(pdata, false); | ||
if (ret) | ||
return ret; | ||
} | ||
|
||
ret = ioctl_nointr(pdata->buf->fd, IIO_DMABUF_ENQUEUE_IOCTL, &dmabuf); | ||
if (ret) | ||
if (ret) { | ||
dev_perror(pdata->buf->dev, ret, "Unable to enqueue DMABUF"); | ||
return ret; | ||
} | ||
|
||
pdata->dequeued = false; | ||
|
||
|
@@ -137,7 +204,6 @@ int local_enqueue_dmabuf(struct iio_block_pdata *pdata, | |
int local_dequeue_dmabuf(struct iio_block_pdata *pdata, bool nonblock) | ||
{ | ||
struct iio_buffer_pdata *buf_pdata = pdata->buf; | ||
struct dma_buf_sync dbuf_sync; | ||
struct timespec start, *time_ptr = NULL; | ||
int ret, fd = (int)(intptr_t) pdata->pdata; | ||
|
||
|
@@ -153,12 +219,12 @@ int local_dequeue_dmabuf(struct iio_block_pdata *pdata, bool nonblock) | |
if (ret < 0) | ||
return ret; | ||
|
||
dbuf_sync.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW; | ||
|
||
/* Enable CPU access to new block */ | ||
ret = ioctl_nointr(fd, IIO_DMABUF_SYNC_IOCTL, &dbuf_sync); | ||
if (ret < 0) | ||
return ret; | ||
if (!pdata->cpu_access_disabled) { | ||
/* Enable CPU access to new block */ | ||
ret = enable_cpu_access(pdata, true); | ||
if (ret < 0) | ||
return ret; | ||
} | ||
|
||
pdata->dequeued = true; | ||
|
||
|