i2s: expose i2s APIs to user mode
User mode may now access the read, write, and trigger APIs. Unlike supervisor mode, memory slabs are not dealt with directly, the data is always copied. A new driver API added to fetch the current channel configuration, used by the system call handlers. The i2s_sam_ssc driver updated for the new API. CAVS driver not modified as there is no user mode port to Xtensa yet. Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
parent
1a40990b2d
commit
06c23f157f
5 changed files with 225 additions and 3 deletions
|
@ -1,4 +1,6 @@
|
|||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(i2s_common.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2S_SAM_SSC i2s_sam_ssc.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_I2S_CAVS i2s_cavs.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE i2s_handlers.c)
|
||||
|
|
52
drivers/i2s/i2s_common.c
Normal file
52
drivers/i2s/i2s_common.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <string.h>
|
||||
#include <i2s.h>
|
||||
|
||||
int _impl_i2s_buf_read(struct device *dev, void *buf, size_t *size)
|
||||
{
|
||||
void *mem_block;
|
||||
int ret;
|
||||
|
||||
ret = i2s_read((struct device *)dev, &mem_block, size);
|
||||
|
||||
if (!ret) {
|
||||
struct i2s_config *rx_cfg =
|
||||
i2s_config_get((struct device *)dev, I2S_DIR_RX);
|
||||
|
||||
memcpy(buf, mem_block, *size);
|
||||
k_mem_slab_free(rx_cfg->mem_slab, mem_block);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int _impl_i2s_buf_write(struct device *dev, void *buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
struct i2s_config *tx_cfg;
|
||||
void *mem_block;
|
||||
|
||||
tx_cfg = i2s_config_get((struct device *)dev, I2S_DIR_TX);
|
||||
if (!tx_cfg) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (size > tx_cfg->block_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = k_mem_slab_alloc(tx_cfg->mem_slab, &mem_block, K_FOREVER);
|
||||
if (ret < 0) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(mem_block, (void *)buf, size);
|
||||
|
||||
return i2s_write((struct device *)dev, mem_block, size);
|
||||
}
|
77
drivers/i2s/i2s_handlers.c
Normal file
77
drivers/i2s/i2s_handlers.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <syscall_handler.h>
|
||||
#include <i2s.h>
|
||||
|
||||
Z_SYSCALL_HANDLER(i2s_buf_read, dev, buf, size)
|
||||
{
|
||||
void *mem_block;
|
||||
size_t data_size;
|
||||
int ret;
|
||||
|
||||
Z_OOPS(Z_SYSCALL_DRIVER_I2S(dev, read));
|
||||
|
||||
ret = i2s_read((struct device *)dev, &mem_block, &data_size);
|
||||
|
||||
if (!ret) {
|
||||
struct i2s_config *rx_cfg;
|
||||
int copy_success;
|
||||
|
||||
/* Presumed to be configured otherwise the i2s_read() call
|
||||
* would have failed.
|
||||
*/
|
||||
rx_cfg = i2s_config_get((struct device *)dev, I2S_DIR_RX);
|
||||
|
||||
copy_success = z_user_to_copy((void *)buf, mem_block,
|
||||
data_size);
|
||||
|
||||
k_mem_slab_free(rx_cfg->mem_slab, mem_block);
|
||||
Z_OOPS(copy_success);
|
||||
Z_OOPS(z_user_to_copy((void *)size, &data_size,
|
||||
sizeof(data_size)));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Z_SYSCALL_HANDLER(i2s_buf_write, dev, buf, size)
|
||||
{
|
||||
int ret;
|
||||
struct i2s_config *tx_cfg;
|
||||
void *mem_block;
|
||||
|
||||
Z_OOPS(Z_SYSCALL_DRIVER_I2S(dev, write));
|
||||
tx_cfg = i2s_config_get((struct device *)dev, I2S_DIR_TX);
|
||||
if (!tx_cfg) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (size > tx_cfg->block_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = k_mem_slab_alloc(tx_cfg->mem_slab, &mem_block, K_FOREVER);
|
||||
if (ret < 0) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = z_user_from_copy(mem_block, (void *)buf, size);
|
||||
if (ret) {
|
||||
k_mem_slab_free(tx_cfg->mem_slab, mem_block);
|
||||
Z_OOPS(ret);
|
||||
}
|
||||
|
||||
return i2s_write((struct device *)dev, mem_block, size);
|
||||
}
|
||||
|
||||
Z_SYSCALL_HANDLER(i2s_trigger, dev, dir, cmd)
|
||||
{
|
||||
Z_OOPS(Z_SYSCALL_DRIVER_I2S(dev, trigger));
|
||||
|
||||
return _impl_i2s_trigger((struct device *)dev, dir, cmd);
|
||||
}
|
|
@ -508,6 +508,25 @@ static int bit_clock_set(Ssc *const ssc, u32_t bit_clk_freq)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct i2s_config *i2s_sam_config_get(struct device *dev,
|
||||
enum i2s_dir dir)
|
||||
{
|
||||
struct i2s_sam_dev_data *const dev_data = DEV_DATA(dev);
|
||||
struct stream *stream;
|
||||
|
||||
if (dir == I2S_DIR_RX) {
|
||||
stream = &dev_data->rx;
|
||||
} else {
|
||||
stream = &dev_data->tx;
|
||||
}
|
||||
|
||||
if (stream->state == I2S_STATE_NOT_READY) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &stream->cfg;
|
||||
}
|
||||
|
||||
static int i2s_sam_configure(struct device *dev, enum i2s_dir dir,
|
||||
struct i2s_config *i2s_cfg)
|
||||
{
|
||||
|
@ -925,6 +944,7 @@ static int i2s_sam_initialize(struct device *dev)
|
|||
|
||||
static const struct i2s_driver_api i2s_sam_driver_api = {
|
||||
.configure = i2s_sam_configure,
|
||||
.config_get = i2s_sam_config_get,
|
||||
.read = i2s_sam_read,
|
||||
.write = i2s_sam_write,
|
||||
.trigger = i2s_sam_trigger,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue