M7350/qcom-opensource/kernel/kernel-tests/bam_dmux_loopback/loopback_sps.c

878 lines
23 KiB
C
Raw Permalink Normal View History

2024-09-09 08:57:42 +00:00
/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
#include <linux/interrupt.h>
#include <linux/ipc_logging.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include "loopback_a2.h"
#include "loopback_sps.h"
#include "../../../drivers/soc/qcom/bam_dmux_private.h"
/* Client connection state */
#define SPS_STATE_DISCONNECT 0
#define SPS_STATE_CONNECT 1
#define SPS_EVENT_INDEX(e) ((e) - 1)
#define SPS_ERROR -1
#define SPSRM_CLEAR 0xcccccccc
#define DEFAULT_IOVEC_RETRYS 1
#define MOCK_SPS_IPC_PAGES 5
void *mock_sps_ipc_log_txt;
static struct mock_sps_pipe *a2_to_bam_pipe;
#define MOCK_SPS_LOG(fmt, args...) \
do { \
if (mock_sps_ipc_log_txt) { \
ipc_log_string(mock_sps_ipc_log_txt, fmt, args); \
} \
} while (0)
static struct workqueue_struct *tx_cmd_done_wq;
static struct workqueue_struct *reg_event_cb_wq;
static int reg_event_cb_wq_init(void);
struct reg_event_work_struct {
struct work_struct work;
void (*callback)(struct sps_event_notify *notify);
struct sps_event_notify event;
};
static void reg_event_cb_handler(struct work_struct *work);
static struct tasklet_struct fill_bam_rx_tasklet;
static void fill_bam_rx_handler(unsigned long data);
/**
* struct mock_sps_pipe - pipe state information
* @available_list: list of available buffers
* @ready_list: list of filled buffers
* @list_lock: lock that must be grabbed before modifying either lock
* @client_state: state of the client
* @connect: sps connection for this pipe
* @event_regs: registered events for this pipe
* @event_regs_inited: flag as to whether or not events are initialized
*/
struct mock_sps_pipe {
struct list_head available_list;
struct list_head ready_list;
spinlock_t list_lock;
u32 client_state;
struct sps_connect connect;
struct sps_register_event *event_regs[SPS_EVENT_MAX];
int event_regs_inited;
};
/**
* stuct mock_iov_info - IO vector
* @iov_info_list: used to add and remove from pipes
* @user: holds the packet passed in by the user parameter of
* transfer one
* @size: holds the size passed in from transfer one
* @flags: holds the flags passed in from transfer one
* @retrys: holds the number or retrys for transmitting the io
*/
struct mock_iov_info {
struct list_head iov_info_list;
void *user;
u32 addr;
u32 size;
u32 flags;
unsigned int retrys;
};
/**
* enum bam_pipe_irq - enum used to describe the bits in the pipe interrupt
* mask
* @BAM_PIPE_IRQ_DESC_INT: BAM finishes descriptor which has INT bit
* selected
* @BAM_PIPE_IRQ_TIMER: inactivity timer expires
* @BAM_PIPE_IRQ_WAKE: wakeup peripheral
* @BAM_PIPE_IRQ_OUT_OF_DESC: producer means no free space for descriptor or
* consumer means no descriptors to process
* @BAM_PIPE_IRQ_ERROR: pipe error
* @BAM_PIPE_IRQ_EOT: end of transfer
*/
enum bam_pipe_irq {
BAM_PIPE_IRQ_DESC_INT = 0x00000001,
BAM_PIPE_IRQ_TIMER = 0x00000002,
BAM_PIPE_IRQ_WAKE = 0x00000004,
BAM_PIPE_IRQ_OUT_OF_DESC = 0x00000008,
BAM_PIPE_IRQ_ERROR = 0x00000010,
BAM_PIPE_IRQ_EOT = 0x00000020,
};
/**
* struct sps_bam_opt_event_table - struct copied from the real sps to
* validate options for transfer one
* @event_id: the id of the event
* @option: the transferring option
* @pipe_irq: the irq of the bam pipe
*
* An array of this struct will be maintained to ensure that only valid
* combinations will tried to be transferred.
*/
struct sps_bam_opt_event_table {
enum sps_event event_id;
enum sps_option option;
enum bam_pipe_irq pipe_irq;
};
static const struct sps_bam_opt_event_table opt_event_table[] = {
{SPS_EVENT_EOT, SPS_O_EOT, BAM_PIPE_IRQ_EOT},
{SPS_EVENT_DESC_DONE, SPS_O_DESC_DONE, BAM_PIPE_IRQ_DESC_INT},
{SPS_EVENT_WAKEUP, SPS_O_WAKEUP, BAM_PIPE_IRQ_WAKE},
{SPS_EVENT_INACTIVE, SPS_O_INACTIVE, BAM_PIPE_IRQ_TIMER},
{SPS_EVENT_OUT_OF_DESC, SPS_O_OUT_OF_DESC,
BAM_PIPE_IRQ_OUT_OF_DESC},
{SPS_EVENT_ERROR, SPS_O_ERROR, BAM_PIPE_IRQ_ERROR}
};
static int mock_sps_client_init(struct mock_sps_pipe *client);
static void mock_sps_rm_config_init(struct sps_connect *connect);
/**
* mock_sps_connect() - mocks the call to sps_connect() to provide
* loopback support
* @h: handle for the sps_pipe
* @connect: connection to add to the pipe
*
* Return: 0 on success or error code
*/
int mock_sps_connect(struct sps_pipe *h, struct sps_connect *connect)
{
struct mock_sps_pipe *ctx = (struct mock_sps_pipe *)h;
if (ctx == NULL) {
MOCK_SPS_LOG("%s: pipe null\n", __func__);
return SPS_ERROR;
} else if (connect == NULL) {
MOCK_SPS_LOG("%s: connect null\n", __func__);
return SPS_ERROR;
}
if (ctx->client_state != SPS_STATE_DISCONNECT) {
MOCK_SPS_LOG("%s: client not disconnected\n", __func__);
return SPS_ERROR;
}
ctx->client_state = SPS_STATE_CONNECT;
ctx->connect = *connect;
return 0;
}
/**
* mock_sps_disconnect() - mocks the call to sps_disconnect() to provide
* loopback support
* @h: handle for the sps_pipe
*
* Frees IO vectors on pipe and moves the pipe to the disconnect state.
*
* Return: 0 on success, error code on failure
*/
int mock_sps_disconnect(struct sps_pipe *h)
{
unsigned long flags;
struct mock_sps_pipe *ctx = (struct mock_sps_pipe *)h;
struct mock_iov_info *info;
if (ctx == NULL) {
MOCK_SPS_LOG("%s: pipe null\n", __func__);
return SPS_ERROR;
}
if (ctx->client_state != SPS_STATE_CONNECT) {
MOCK_SPS_LOG("%s: client not connected\n", __func__);
return SPS_ERROR;
}
if (ctx->connect.mode == SPS_MODE_SRC) {
mock_a2_clear_lists();
spin_lock_irqsave(&ctx->list_lock, flags);
while (!list_empty(&ctx->available_list)) {
info = list_first_entry(&ctx->available_list,
struct mock_iov_info, iov_info_list);
list_del(ctx->available_list.next);
kfree(info);
}
while (!list_empty(&ctx->ready_list)) {
info = list_first_entry(&ctx->ready_list,
struct mock_iov_info, iov_info_list);
list_del(ctx->ready_list.next);
kfree(info);
}
spin_unlock_irqrestore(&ctx->list_lock, flags);
}
ctx->client_state = SPS_STATE_DISCONNECT;
mock_sps_rm_config_init(&(ctx->connect));
return 0;
}
/**
* mock_sps_register_bam_device() - mocks the call to sps_register_bam_device
* to provide loopback support
* @bam_props: just verified that non-null, just needed for interface
* purposes
* @dev_handle: output parameter to receive handle to the device
*
* Return: 0 if parameters are non-null, SPS_ERROR otherwise
*/
int mock_sps_register_bam_device(const struct sps_bam_props *bam_props,
unsigned long *dev_handle)
{
if (bam_props == NULL) {
MOCK_SPS_LOG("%s: bam props null\n", __func__);
return SPS_ERROR;
} else if (dev_handle == NULL) {
MOCK_SPS_LOG("%s: dev_handle null\n", __func__);
return SPS_ERROR;
}
*dev_handle = 1;
return 0;
}
/**
* mock_sps_deregister_bam_device() - mocks the call to
* sps_deregister_bam_device to provide loopback support
* @dev_handle: handle for the device
*
* Return: 0 if handle is non-zero, SPS_ERROR otherwise
*/
int mock_sps_deregister_bam_device(unsigned long dev_handle)
{
if (dev_handle == 0) {
MOCK_SPS_LOG("%s: invalid dev_handle\n", __func__);
return SPS_ERROR;
}
return 0;
}
/**
* mock_sps_alloc_endpoint() - mocks the call to sps_alloc_endpoint() to
* provide loopback support
*
* Return: an initialized mock_sps_pipe (cast as an sps_pipe for interface
* purposes)
*/
struct sps_pipe *mock_sps_alloc_endpoint(void)
{
struct mock_sps_pipe *ctx = NULL;
ctx = kzalloc(sizeof(struct mock_sps_pipe), GFP_KERNEL);
if (ctx == NULL) {
MOCK_SPS_LOG("%s: ctx allocation failed\n", __func__);
return NULL;
}
mock_sps_client_init(ctx);
return (struct sps_pipe *)ctx;
}
/**
* mock_sps_client_init() - helper function to help initialize the mock_sps_pipe
* @client: mock_sps_pipe to be initialized
*
* Return: 0 on success, error code otherwise
*/
static int mock_sps_client_init(struct mock_sps_pipe *client)
{
int i;
int is_valid = 1;
if (client == NULL) {
MOCK_SPS_LOG("%s: client is null\n", __func__);
return -EINVAL;
}
INIT_LIST_HEAD(&(client->available_list));
INIT_LIST_HEAD(&(client->ready_list));
spin_lock_init(&(client->list_lock));
mock_sps_rm_config_init(&(client->connect));
client->client_state = SPS_STATE_DISCONNECT;
for (i = 0; i < SPS_EVENT_MAX; i++) {
client->event_regs[i] = kzalloc(sizeof(
struct sps_register_event), GFP_KERNEL);
if (unlikely(client->event_regs[i] == NULL)) {
is_valid = 0;
MOCK_SPS_LOG("%s: event_reg allocing failed\n",
__func__);
}
}
if (likely(is_valid))
client->event_regs_inited = 1;
return 0;
}
/**
* mock_sps_rm_config_init() - helper to configure the initialization of the
* connect portion of the pipe
* @connect: connection to be initialized
*/
static void mock_sps_rm_config_init(struct sps_connect *connect)
{
if (connect == NULL) {
MOCK_SPS_LOG("%s: connect null\n", __func__);
return;
}
memset(connect, SPSRM_CLEAR, sizeof(*connect));
}
/**
* mock_sps_free_endpoint() - mocks the call to sps_free_endpoint() to provide
* loopback support
* @h: handle for pipe
*
* Return: 0 on success, error code otherwise
*/
int mock_sps_free_endpoint(struct sps_pipe *h)
{
struct mock_sps_pipe *ctx;
int i;
if (h == NULL) {
MOCK_SPS_LOG("%s: pipe is null\n", __func__);
return SPS_ERROR;
}
ctx = (struct mock_sps_pipe *)h;
for (i = 0; i < SPS_EVENT_MAX; i++)
kfree(ctx->event_regs[i]);
kfree(ctx);
return 0;
}
/**
* mock_sps_set_config() - mocks the call to sps_set_config() to provide
* loopback support
* @h: handle for pipe
* @config: connection configuration to add to pipe
*
* Return: 0 on success, error code otherwise
*/
int mock_sps_set_config(struct sps_pipe *h, struct sps_connect *config)
{
if (h == NULL) {
MOCK_SPS_LOG("%s: pipe is null\n", __func__);
return SPS_ERROR;
} else if (config == NULL) {
MOCK_SPS_LOG("%s: config null\n", __func__);
return SPS_ERROR;
}
((struct mock_sps_pipe *)h)->connect.options = config->options;
return 0;
}
/**
* mock_sps_get_config() - mocks the call to sps_get_config() to provide
* loopback support
* @h: handle for pipe
* @conig: configuration to store pipe connection in
*
* Return: 0 on success, error code otherwise
*/
int mock_sps_get_config(struct sps_pipe *h, struct sps_connect *config)
{
if (h == NULL) {
MOCK_SPS_LOG("%s: pipe is null\n", __func__);
return SPS_ERROR;
} else if (config == NULL) {
MOCK_SPS_LOG("%s: config is null\n", __func__);
return SPS_ERROR;
}
*config = ((struct mock_sps_pipe *)h)->connect;
return 0;
}
/**
* mock_sps_device_reset() - mocks the call to sps_device_reset() to provide
* loopback support
* @dev: handle of device to reset
*
* Return: SPS_ERROR if handle is 0, 0 otherwise
*/
int mock_sps_device_reset(unsigned long dev)
{
if (dev == 0) {
MOCK_SPS_LOG("%s: dev is null\n", __func__);
return SPS_ERROR;
}
return 0;
}
/**
* mock_sps_register_event() - mocks the call to sps_register_event() to
* provide loopback support
* @h: handle for pipe to register event on
* @reg: event to be registered
*
* Return: 0 on success, error code otherwise
*/
int mock_sps_register_event(struct sps_pipe *h, struct sps_register_event *reg)
{
struct mock_sps_pipe *ctx = (struct mock_sps_pipe *)h;
int n, index;
struct sps_register_event *event_reg;
if (ctx == NULL || !(ctx->event_regs_inited)) {
MOCK_SPS_LOG("%s: ctx null or event regs not inited, ctx=%p\n",
__func__, ctx);
return SPS_ERROR;
} else if (reg == NULL) {
MOCK_SPS_LOG("%s: reg is null\n", __func__);
return SPS_ERROR;
}
if (reg->xfer_done != NULL && reg->mode != SPS_TRIGGER_CALLBACK) {
MOCK_SPS_LOG("%s: invalid reg mode, xfer_done=%p, mode=%u\n",
__func__, reg->xfer_done, reg->mode);
return SPS_ERROR;
}
for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) {
if ((reg->options & opt_event_table[n].option) == 0)
continue;
index = SPS_EVENT_INDEX(opt_event_table[n].event_id);
if (index < 0) {
MOCK_SPS_LOG("%s: invalid index %d\n", __func__,
index);
return SPS_ERROR;
} else {
event_reg = ctx->event_regs[index];
event_reg->xfer_done = reg->xfer_done;
event_reg->callback = reg->callback;
event_reg->mode = reg->mode;
event_reg->user = reg->user;
}
}
return 0;
}
/**
* mock_sps_trigger_event() - mocks the call to sps_trigger_event to provide
* loopback support
* @data: pipe data to trigger event upon
* @o_option: options for triggered event
* @pkt: packet that triggered event
*/
void mock_sps_trigger_event(void *data, enum sps_option o_option, void *pkt)
{
int ret, index, n;
struct reg_event_work_struct *cb_work_struct;
struct sps_register_event *reg_event;
enum sps_event event_id;
struct sps_connect curr_connect;
struct mock_sps_pipe *pipe = (struct mock_sps_pipe *)data;
if (pipe == NULL) {
MOCK_SPS_LOG("%s: pipe null", __func__);
return;
}
ret = mock_sps_get_config((struct sps_pipe *)pipe, &curr_connect);
if (ret) {
MOCK_SPS_LOG("%s: get_config fail\n", __func__);
return;
}
if (curr_connect.options & SPS_O_POLL)
return;
for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) {
if (!(o_option & opt_event_table[n].option))
continue;
event_id = opt_event_table[n].event_id;
index = SPS_EVENT_INDEX(event_id);
if (index < 0) {
MOCK_SPS_LOG("%s: invalid event index\n", __func__);
return;
}
reg_event = pipe->event_regs[index];
if (!reg_event || !(reg_event->callback)) {
MOCK_SPS_LOG("%s: no callback\n", __func__);
return;
}
cb_work_struct = kmalloc(sizeof(*cb_work_struct),GFP_ATOMIC);
if (cb_work_struct) {
INIT_WORK(&(cb_work_struct->work),
reg_event_cb_handler);
cb_work_struct->event.event_id = event_id;
cb_work_struct->event.data.transfer.user = pkt;
cb_work_struct->callback = reg_event->callback;
queue_work(reg_event_cb_wq,
&(cb_work_struct->work));
return;
} else {
MOCK_SPS_LOG("%s: work_struct alloc failed\n",
__func__);
}
}
}
/**
* reg_event_cb_handler() - handler for function for register events
* @work: work struct contained in the event to be handled
*/
static void reg_event_cb_handler(struct work_struct *work)
{
struct reg_event_work_struct *cb_work_struct;
if (work == NULL) {
MOCK_SPS_LOG("%s: work null\n", __func__);
return;
}
cb_work_struct = container_of(work,
struct reg_event_work_struct, work);
if (cb_work_struct) {
(*(cb_work_struct->callback))(&(cb_work_struct->event));
kfree(cb_work_struct);
}
}
/**
* fill_bam_rx_handler() - handler used to defer fill of the bam receive
* buffers to a later time
* @data: not used
*
* Fills as many available bam receive buffers by popping of packets from the
* mock a2.
*/
static void fill_bam_rx_handler(unsigned long data)
{
struct mock_iov_info *info;
struct sk_buff *skb;
struct rx_pkt_info *rx_info;
struct bam_mux_hdr *old_hdr;
uint8_t *buff;
int ret;
struct bam_mux_hdr *new_hdr;
unsigned long lock_flags;
if (a2_to_bam_pipe == NULL) {
MOCK_SPS_LOG("%s: pipe is null\n", __func__);
return;
}
spin_lock_irqsave(&a2_to_bam_pipe->list_lock, lock_flags);
while (!list_empty(&a2_to_bam_pipe->available_list)) {
buff = kmalloc(DEFAULT_BUFFER_SIZE, GFP_ATOMIC);
if (!buff) {
MOCK_SPS_LOG("%s: buff alloc failed\n", __func__);
break;
}
new_hdr = kmalloc(sizeof(struct bam_mux_hdr), GFP_ATOMIC);
if (!new_hdr) {
MOCK_SPS_LOG("%s: bam hdr alloc failed\n", __func__);
break;
}
ret = mock_a2_tx_list_pop(new_hdr, buff);
if (!ret) {
info = list_first_entry(&a2_to_bam_pipe->available_list,
struct mock_iov_info, iov_info_list);
if (!info) {
MOCK_SPS_LOG("%s: null info", __func__);
kfree(buff);
goto exit;
}
list_del_init(&info->iov_info_list);
dma_sync_single_for_cpu(NULL,
info->addr, info->size,
DMA_BIDIRECTIONAL);
rx_info = ((struct rx_pkt_info *)(info->user));
if (!rx_info) {
MOCK_SPS_LOG("%s: invalid packet info\n",
__func__);
kfree(info);
kfree(buff);
kfree(new_hdr);
goto exit;
}
skb = rx_info->skb;
if (!skb) {
MOCK_SPS_LOG("%s: invalid skb\n", __func__);
kfree(info);
kfree(buff);
kfree(new_hdr);
goto exit;
}
old_hdr = (struct bam_mux_hdr *)(skb->data);
if (!old_hdr) {
MOCK_SPS_LOG("%s: invalid bam_hdr\n",
__func__);
kfree(info);
kfree(buff);
kfree(new_hdr);
goto exit;
}
*old_hdr = *new_hdr;
memcpy(old_hdr + 1, buff, new_hdr->pkt_len);
skb_set_network_header(skb, sizeof(struct bam_mux_hdr));
skb->ip_summed = CHECKSUM_NONE;
kfree(buff);
kfree(new_hdr);
dma_sync_single_for_device(
NULL,
info->addr, info->size,
DMA_BIDIRECTIONAL);
list_add_tail(&(info->iov_info_list),
&(a2_to_bam_pipe->ready_list));
spin_unlock_irqrestore(&(a2_to_bam_pipe->list_lock),
lock_flags);
mock_sps_trigger_event((void *)a2_to_bam_pipe,
SPS_O_EOT, NULL);
spin_lock_irqsave(&(a2_to_bam_pipe->list_lock),
lock_flags);
} else {
MOCK_SPS_LOG("%s: tx pop fail\n", __func__);
kfree(buff);
kfree(new_hdr);
break;
}
}
exit:
spin_unlock_irqrestore(&(a2_to_bam_pipe->list_lock), lock_flags);
}
/**
* mock_sps_transfer_one() - mocks the call to sps_transfer_one() to provide
* loopback support
* @pipe: pipe to transfer on
* @addr: address to be transferred
* @size: size to be transferred
* @user: pointer to user data
* @flags: flags used for transferring
*
* Used to both send data and queue up receive buffers, depending on which pipe
* is used.
*
* Return: 0 on success, error code otherwise
*/
int mock_sps_transfer_one(struct sps_pipe *h, phys_addr_t addr, u32 size,
void *user, u32 flags)
{
struct mock_sps_pipe *ctx = (struct mock_sps_pipe *)h;
unsigned long lock_flags;
struct mock_iov_info *info;
int ret;
if (ctx == NULL) {
MOCK_SPS_LOG("%s: pipe_null\n", __func__);
return SPS_ERROR;
}
if ((flags & SPS_IOVEC_FLAG_NWD) &&
!(flags & (SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_CMD))) {
MOCK_SPS_LOG("%s: invalid flags %x\n", __func__, flags);
return SPS_ERROR;
} else if ((flags & SPS_IOVEC_FLAG_EOT) &&
(flags & SPS_IOVEC_FLAG_CMD)) {
MOCK_SPS_LOG("%s: invalid flags %x\n", __func__, flags);
return SPS_ERROR;
} else if (!(flags & SPS_IOVEC_FLAG_CMD) &&
(flags & (SPS_IOVEC_FLAG_LOCK |
SPS_IOVEC_FLAG_UNLOCK))) {
MOCK_SPS_LOG("%s: invalid flags %x\n", __func__, flags);
return SPS_ERROR;
} else if ((flags & SPS_IOVEC_FLAG_LOCK) &&
(flags & SPS_IOVEC_FLAG_UNLOCK)) {
MOCK_SPS_LOG("%s: invalid flags %x\n", __func__, flags);
return SPS_ERROR;
} else if ((flags & SPS_IOVEC_FLAG_IMME) &&
(flags & SPS_IOVEC_FLAG_CMD)) {
MOCK_SPS_LOG("%s: invalid flags %x\n", __func__, flags);
return SPS_ERROR;
} else if ((flags & SPS_IOVEC_FLAG_IMME) &&
(flags & SPS_IOVEC_FLAG_NWD)) {
MOCK_SPS_LOG("%s: invalid flags %x\n", __func__, flags);
return SPS_ERROR;
}
if (ctx->connect.mode == SPS_MODE_DEST) {
ret = mock_a2_rx_list_push((void *)ctx,
(struct tx_pkt_info *)user);
if (ret)
MOCK_SPS_LOG("%s: a2 rx push failed\n", __func__);
return ret;
}
info = kmalloc(sizeof(struct mock_iov_info), GFP_ATOMIC);
if (info == NULL) {
MOCK_SPS_LOG("%s: info alloc failed\n", __func__);
return -ENOMEM;
}
info->addr = addr;
info->size = size;
info->flags = flags;
info->user = user;
info->retrys = DEFAULT_IOVEC_RETRYS;
INIT_LIST_HEAD(&(info->iov_info_list));
spin_lock_irqsave(&(ctx->list_lock), lock_flags);
list_add_tail(&(info->iov_info_list), &(ctx->available_list));
spin_unlock_irqrestore(&(ctx->list_lock), lock_flags);
a2_to_bam_pipe = ctx;
tasklet_schedule(&fill_bam_rx_tasklet);
return 0;
}
/**
* mock_sps_get_iovec() - mocks the call to sps_get_iovec() to provide
* loopback support
* @h: handle of pipe to get iovec from
* @iovec: where the io vector will be placed
*
* Return: 0 on success, error code otherwise
*/
int mock_sps_get_iovec(struct sps_pipe *h, struct sps_iovec *iovec)
{
struct mock_sps_pipe *ctx = (struct mock_sps_pipe *)h;
struct mock_iov_info *info;
unsigned long lock_flags;
if (iovec == NULL) {
MOCK_SPS_LOG("%s: iovec null\n", __func__);
return SPS_ERROR;
} else if (ctx == NULL) {
MOCK_SPS_LOG("%s: pipe null\n", __func__);
return SPS_ERROR;
}
MOCK_SPS_LOG("%s:\n", __func__);
spin_lock_irqsave(&(ctx->list_lock), lock_flags);
if (list_empty(&(ctx->ready_list))) {
spin_unlock_irqrestore(&(ctx->list_lock), lock_flags);
iovec->addr = 0;
return 0;
} else {
info = list_first_entry(&ctx->ready_list,
struct mock_iov_info, iov_info_list);
info->retrys--;
if (info->retrys == 0) {
list_del(&info->iov_info_list);
iovec->addr = info->addr;
iovec->size = info->size;
iovec->flags = info->flags;
} else
iovec->addr = 0;
spin_unlock_irqrestore(&(ctx->list_lock), lock_flags);
kfree(info);
}
return 0;
}
/**
* mock_sps_get_unused_desc_num() - mocks the call to sps_get_unused_desc_num
* to provide loopback support
* @h: handle for pipe
* @desc_num: where number of unused descriptors will be stored
*
* Return: 0 on success, error code otherwise
*/
int mock_sps_get_unused_desc_num(struct sps_pipe *h, u32 *desc_num)
{
if (!h || !desc_num) {
return -EINVAL;
} else {
*desc_num = 0;
return 0;
}
}
/**
* mock_sps_rewrite_notify() - this function schedules a tasklet which
* processes all available packets to be sent
*/
void mock_sps_rewrite_notify(void)
{
tasklet_schedule(&fill_bam_rx_tasklet);
}
/**
* reg_event_cb_wq_init() - initializes the workqueue that processes registered
* events
*/
static int reg_event_cb_wq_init(void)
{
int ret = 0;
reg_event_cb_wq = create_singlethread_workqueue(
"reg_event_cb_wq");
if (!reg_event_cb_wq) {
MOCK_SPS_LOG("%s: workqueue creation failed\n", __func__);
ret = -EFAULT;
}
return ret;
}
/**
* mock_sps_init() - initializaes all of the mock_sps functionality
*
* Initializes all of the mock_sps tasklets, workqueues and ipc logging
* context.
*/
int mock_sps_init(void)
{
int ret = 0;
mock_sps_ipc_log_txt = ipc_log_context_create(MOCK_SPS_IPC_PAGES,
"mock_sps", 0);
if (!mock_sps_ipc_log_txt)
pr_err("%s: failed to allocate log\n", __func__);
ret = reg_event_cb_wq_init();
if (ret) {
pr_err("%s: reg_event_cb_wq_init failed:%d\n", __func__, ret);
return ret;
}
tasklet_init(&fill_bam_rx_tasklet, fill_bam_rx_handler, 0);
tx_cmd_done_wq = create_workqueue(
"tx_cmd_done_wq");
if (!tx_cmd_done_wq) {
MOCK_SPS_LOG("%s: cmd_done_wq alloc fail\n", __func__);
ret = -EFAULT;
}
return ret;
}