1114 lines
35 KiB
C
1114 lines
35 KiB
C
|
/* Copyright (c) 2014-2015, 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/debugfs.h>
|
||
|
#include <linux/ipc_logging.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <soc/qcom/tracer_pkt.h>
|
||
|
#include "glink_loopback_client.h"
|
||
|
#include "glink_private.h"
|
||
|
#include "glink_test_common.h"
|
||
|
|
||
|
static atomic_t request_id = ATOMIC_INIT(0);
|
||
|
struct ut_notify_data data_cb_data;
|
||
|
#define WAIT_TIMEOUT HZ
|
||
|
|
||
|
struct workqueue_struct *glink_lbp_client_wq;
|
||
|
|
||
|
/**
|
||
|
* struct clnt_rx_intent_req_work - Work structure for client RX intent requests
|
||
|
* @req_intent_size: Size of the intent being requested
|
||
|
* @lpb_ch: The loopback channel associated with this intent request
|
||
|
* @work: Work structure
|
||
|
*/
|
||
|
struct clnt_rx_intent_req_work {
|
||
|
size_t req_intent_size;
|
||
|
struct loopback_channel *lpb_ch;
|
||
|
struct work_struct work;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* rx_done_completion_init() - Initialize rx_done_completion structure
|
||
|
* @rx_done_comp: The rx_done_completion structure to be initialized
|
||
|
*/
|
||
|
void rx_done_completion_init(struct rx_done_completion *rx_done_comp)
|
||
|
{
|
||
|
init_completion(&rx_done_comp->completion);
|
||
|
spin_lock_init(&rx_done_comp->rx_done_lock_lha0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* cb_data_init() - Initialize notification callback data structure
|
||
|
* @cb_data: The notification callback data structure to be initialized
|
||
|
*/
|
||
|
void cb_data_init(struct ut_notify_data *cb_data)
|
||
|
{
|
||
|
init_completion(&cb_data->cb_completion);
|
||
|
cb_data_reset(cb_data);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* cb_data_reset() - Reset the notification callback data structure
|
||
|
* @cb_data: The notification callback data structure to be reset
|
||
|
*/
|
||
|
void cb_data_reset(struct ut_notify_data *cb_data)
|
||
|
{
|
||
|
cb_data->rx_notify = false;
|
||
|
cb_data->size = 0;
|
||
|
cb_data->tx_done = false;
|
||
|
cb_data->send_intent = false;
|
||
|
cb_data->intent_cb_ntfy = false;
|
||
|
cb_data->tx_data = NULL;
|
||
|
cb_data->tx_vbuf_provider = NULL;
|
||
|
cb_data->tx_pbuf_provider = NULL;
|
||
|
cb_data->rx_data = NULL;
|
||
|
cb_data->rx_vbuf_provider = NULL;
|
||
|
cb_data->rx_pbuf_provider = NULL;
|
||
|
|
||
|
reinit_completion(&cb_data->cb_completion);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* get_data() - Retrieve vector data using the physical or virtual buffer
|
||
|
* provider functions
|
||
|
* @iovec: Pointer to the vector
|
||
|
* @iovec_size: Size of the data/vector
|
||
|
* @offset: Offset from the beginning of the vector
|
||
|
* @vbuf_provider: Helper function to iterate the vector in virtual
|
||
|
* address space
|
||
|
* @pbuf_provider: Helper function to iterate the vector in physical
|
||
|
* address space
|
||
|
* @data_size: Size of the retrieved data
|
||
|
*/
|
||
|
static void *get_data(void *iovec, size_t iovec_size, size_t offset,
|
||
|
void * (*vbuf_provider)(void *iovec, size_t offset, size_t *size),
|
||
|
void * (*pbuf_provider)(void *iovec, size_t offset, size_t *size),
|
||
|
size_t *data_size)
|
||
|
{
|
||
|
void *pdata;
|
||
|
|
||
|
if (vbuf_provider) {
|
||
|
return vbuf_provider(iovec, offset, data_size);
|
||
|
} else if (pbuf_provider) {
|
||
|
pdata = pbuf_provider(iovec, offset, data_size);
|
||
|
return phys_to_virt((unsigned long)pdata);
|
||
|
} else {
|
||
|
*data_size = iovec_size - offset;
|
||
|
return (void *)iovec + offset;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_vector_cmp() - Compare two vectors
|
||
|
* @cb_data: The callback notification structure containing the vectors and
|
||
|
* the size of the data to compare
|
||
|
*
|
||
|
* Return: 0 on success, standard error codes otherwise
|
||
|
*/
|
||
|
static int glink_loopback_vector_cmp(struct ut_notify_data *cb_data)
|
||
|
{
|
||
|
size_t offset = 0;
|
||
|
void *buf1;
|
||
|
size_t buf1_size;
|
||
|
void *buf2;
|
||
|
size_t buf2_size;
|
||
|
size_t cmp_size;
|
||
|
char *p;
|
||
|
int i;
|
||
|
|
||
|
do {
|
||
|
buf1 = get_data(cb_data->tx_data, cb_data->size, offset,
|
||
|
cb_data->tx_vbuf_provider, cb_data->tx_pbuf_provider,
|
||
|
&buf1_size);
|
||
|
buf2 = get_data(cb_data->rx_data, cb_data->size, offset,
|
||
|
cb_data->rx_vbuf_provider, cb_data->rx_pbuf_provider,
|
||
|
&buf2_size);
|
||
|
cmp_size = min(buf1_size, buf2_size);
|
||
|
if (memcmp(buf1, buf2, cmp_size))
|
||
|
break;
|
||
|
offset += cmp_size;
|
||
|
} while (offset != cb_data->size);
|
||
|
|
||
|
if (offset != cb_data->size) {
|
||
|
GLINK_LL_CLNT_ERR("%s: DATA Mismatch-off:sz %zu:%zu\n",
|
||
|
__func__, offset, cmp_size);
|
||
|
p = (char *)buf1;
|
||
|
for (i = 0; i < cmp_size; i = i + 10)
|
||
|
GLINK_LL_CLNT_INFO(
|
||
|
"[tx line idx:%d] 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
|
||
|
i, p[i], p[i+1], p[i+2], p[i+3], p[i+4],
|
||
|
p[i+5], p[i+6], p[i+7], p[i+8], p[i+9]);
|
||
|
p = (char *)buf2;
|
||
|
for (i = 0; i < cmp_size; i = i + 10)
|
||
|
GLINK_LL_CLNT_INFO(
|
||
|
"[rx line idx:%d] 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
|
||
|
i, p[i], p[i+1], p[i+2], p[i+3], p[i+4],
|
||
|
p[i+5], p[i+6], p[i+7], p[i+8], p[i+9]);
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_make_request_pkt_hdr() - Makes the loopback request packet header
|
||
|
* @pkt: The loopback request packet structure
|
||
|
* @type: The request type
|
||
|
*
|
||
|
* This function fills the loopback request header with the request type,
|
||
|
* request size, and a request ID, which is unique for each request.
|
||
|
*/
|
||
|
static void glink_make_request_pkt_hdr(struct req *pkt, int type)
|
||
|
{
|
||
|
GLINK_LL_CLNT_INFO("%s: type[%d]\n", __func__, type);
|
||
|
pkt->hdr.req_id = atomic_inc_return(&request_id);
|
||
|
pkt->hdr.req_type = type;
|
||
|
switch (type) {
|
||
|
case OPEN:
|
||
|
pkt->hdr.req_size = sizeof(struct open_req);
|
||
|
break;
|
||
|
case CLOSE:
|
||
|
pkt->hdr.req_size = sizeof(struct close_req);
|
||
|
break;
|
||
|
case QUEUE_RX_INTENT_CONFIG:
|
||
|
pkt->hdr.req_size = sizeof(struct queue_rx_intent_config_req);
|
||
|
break;
|
||
|
case TX_CONFIG:
|
||
|
pkt->hdr.req_size = sizeof(struct tx_config_req);
|
||
|
break;
|
||
|
case RX_DONE_CONFIG:
|
||
|
pkt->hdr.req_size = sizeof(struct rx_done_config_req);
|
||
|
break;
|
||
|
default:
|
||
|
GLINK_LL_CLNT_ERR("%s: unknown request type[%d]\n", __func__,
|
||
|
type);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_send_request() - Sends the loopback request to the loopback
|
||
|
* server
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @pkt: Loopback request packet structure
|
||
|
* @type: Type of the loopback request
|
||
|
* @req_intent: Specifies whether or not the tx() call should block waiting
|
||
|
* for an RX intent
|
||
|
*
|
||
|
* This function creates and sends a loopback request to the loopback server
|
||
|
* and queues an RX intent to receive the response.
|
||
|
*
|
||
|
* Return: 0 on success, standard error codes otherwise
|
||
|
*/
|
||
|
int glink_loopback_send_request(void *handle, struct req *pkt, int type,
|
||
|
bool req_intent)
|
||
|
{
|
||
|
int ret;
|
||
|
unsigned long flags;
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)handle;
|
||
|
const char *transport = lpb_ch->open_cfg.transport;
|
||
|
const char *edge = lpb_ch->open_cfg.edge;
|
||
|
const char *name = lpb_ch->open_cfg.name;
|
||
|
struct rx_done_completion *rx_done_comp;
|
||
|
uint32_t tx_flags = req_intent ? GLINK_TX_REQ_INTENT : 0;
|
||
|
|
||
|
rx_done_comp = kzalloc(sizeof(struct rx_done_completion), GFP_KERNEL);
|
||
|
if (!rx_done_comp) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s: %s: %s\n",
|
||
|
transport, edge, name, __func__,
|
||
|
"Could not allocate rx_done_completion");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
rx_done_completion_init(rx_done_comp);
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: orphaned flag just after init: %d\n",
|
||
|
transport, edge, name, __func__,
|
||
|
rx_done_comp->orphaned);
|
||
|
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s: %s: Queueing rx_intent packet\n",
|
||
|
transport, edge, name, __func__);
|
||
|
ret = glink_queue_rx_intent(lpb_ch->handle, (void *)rx_done_comp,
|
||
|
sizeof(struct resp));
|
||
|
if (ret) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s ret[%d]\n",
|
||
|
transport, edge, name, __func__,
|
||
|
"glink_queue_rx_intent_failed", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
glink_make_request_pkt_hdr(pkt, type);
|
||
|
|
||
|
cb_data_reset(&lpb_ch->cb_data);
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: Sending request packet Type[%d]\n",
|
||
|
transport, edge, name, __func__, type);
|
||
|
ret = glink_tx(lpb_ch->handle, (void *)pkt, (void *)pkt,
|
||
|
sizeof(struct req), tx_flags);
|
||
|
if (ret) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: glink_tx failed ret[%d]\n",
|
||
|
transport, edge, name, __func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
ret = wait_for_completion_timeout(&rx_done_comp->completion,
|
||
|
WAIT_TIMEOUT);
|
||
|
|
||
|
spin_lock_irqsave(&rx_done_comp->rx_done_lock_lha0, flags);
|
||
|
if (ret == 0) {
|
||
|
ret = completion_done(&rx_done_comp->completion);
|
||
|
if (!ret) {
|
||
|
rx_done_comp->orphaned = true;
|
||
|
GLINK_LL_CLNT_DBG("%s:%s:%s %s: %s\n",
|
||
|
transport, edge, name, __func__,
|
||
|
"Set orphaned flag to true");
|
||
|
}
|
||
|
}
|
||
|
spin_unlock_irqrestore(&rx_done_comp->rx_done_lock_lha0, flags);
|
||
|
|
||
|
if (ret == 0 || lpb_ch->cb_data.rx_notify != true
|
||
|
|| lpb_ch->cb_data.size != sizeof(struct resp)) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s ret[%d] %s[%d] size[%zu]\n",
|
||
|
transport, edge, name, __func__, "Wrong Response", ret,
|
||
|
"rx_notify", lpb_ch->cb_data.rx_notify,
|
||
|
lpb_ch->cb_data.size);
|
||
|
return -ETIMEDOUT;
|
||
|
}
|
||
|
|
||
|
glink_rx_done(lpb_ch->handle, lpb_ch->cb_data.rx_data, false);
|
||
|
|
||
|
kfree(rx_done_comp);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_rmt_rx_intent_req_worker() - Remote RX intent request worker
|
||
|
* function
|
||
|
* @work: Work structure
|
||
|
*
|
||
|
* This worker function queues an RX intent of the requested size on the given
|
||
|
* loopback channel. The channel handle and intent size are provided in the
|
||
|
* container of @work.
|
||
|
*/
|
||
|
static void glink_loopback_rmt_rx_intent_req_worker(struct work_struct *work)
|
||
|
{
|
||
|
int ret;
|
||
|
struct clnt_rx_intent_req_work *work_item =
|
||
|
container_of(work,
|
||
|
struct clnt_rx_intent_req_work, work);
|
||
|
struct loopback_channel *lpb_ch = work_item->lpb_ch;
|
||
|
|
||
|
ret = glink_queue_rx_intent(lpb_ch->handle, lpb_ch,
|
||
|
work_item->req_intent_size);
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: Triggered with size[%zu] ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, work_item->req_intent_size, ret);
|
||
|
if (ret)
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s queue_rx_intent failed\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__);
|
||
|
kfree(work_item);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_notify_rx_cb() - Receive notification callback
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @priv: The loopback channel
|
||
|
* @pkt_priv: An rx_done_completion structure
|
||
|
* @ptr: Pointer to the receive data
|
||
|
* @size: Size of the receive data
|
||
|
*/
|
||
|
void glink_loopback_notify_rx_cb(void *handle, const void *priv,
|
||
|
const void *pkt_priv, const void *ptr, size_t size)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)priv;
|
||
|
struct rx_done_completion *rx_done_comp =
|
||
|
(struct rx_done_completion *)pkt_priv;
|
||
|
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: priv[%p] data[%p] size[%zu]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, pkt_priv, (char *)ptr, size);
|
||
|
lpb_ch->cb_data.size = size;
|
||
|
lpb_ch->cb_data.rx_notify = true;
|
||
|
lpb_ch->cb_data.rx_data = (void *)ptr;
|
||
|
|
||
|
if (lpb_ch->ch_type == CH_CNTL_TYPE) {
|
||
|
spin_lock_irqsave(&rx_done_comp->rx_done_lock_lha0, flags);
|
||
|
if (rx_done_comp->orphaned) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s: %s: %s\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, "Receive data orphaned");
|
||
|
kfree(rx_done_comp);
|
||
|
} else {
|
||
|
complete(&rx_done_comp->completion);
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: Called complete()%s\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__,
|
||
|
" for rx_done completion");
|
||
|
}
|
||
|
spin_unlock_irqrestore(&rx_done_comp->rx_done_lock_lha0, flags);
|
||
|
} else {
|
||
|
complete(&lpb_ch->cb_data.cb_completion);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_notify_rx_nowait_cb() - Receive notification callback for the
|
||
|
* no-wait receive mode
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @priv: The loopback channel
|
||
|
* @pkt_priv: Notification callback data structure
|
||
|
* @ptr: Pointer to the receive data
|
||
|
* @size: Size of the receive data
|
||
|
*/
|
||
|
void glink_loopback_notify_rx_nowait_cb(void *handle, const void *priv,
|
||
|
const void *pkt_priv, const void *ptr, size_t size)
|
||
|
{
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)priv;
|
||
|
struct mt_cb_data *cb_data = (struct mt_cb_data *)pkt_priv;
|
||
|
|
||
|
atomic_inc(&cb_data->num_rx_pkts);
|
||
|
GLINK_LL_CLNT_INFO_PERF(
|
||
|
"%s:%s:%s %s: end (Success) RX priv[%p] data[%p] size[%zu]\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__, pkt_priv, (char *)ptr,
|
||
|
size);
|
||
|
if (atomic_read(&cb_data->num_rx_pkts) ==
|
||
|
atomic_read(&cb_data->num_tx_pkts))
|
||
|
wake_up(&cb_data->test_comp);
|
||
|
glink_rx_done(lpb_ch->handle, ptr, false);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_notify_rxv_cb() - Receive notification callback for packets
|
||
|
* transmitted in vector form
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @priv: The loopback channel
|
||
|
* @pkt_priv: A completion used to indicate data receipt in control
|
||
|
* channels
|
||
|
* @iovec: Pointer to the vector
|
||
|
* @size: Size of data/vector
|
||
|
* @vbuf_provider: Helper function to iterate the vector in virtual address
|
||
|
* space
|
||
|
* @pbuf_provider: Helper function to iterate the vector in physical
|
||
|
* address space
|
||
|
*/
|
||
|
void glink_loopback_notify_rxv_cb(void *handle, const void *priv,
|
||
|
const void *pkt_priv, void *iovec, size_t size,
|
||
|
void * (*vbuf_provider)(void *iovec, size_t offset, size_t *size),
|
||
|
void * (*pbuf_provider)(void *iovec, size_t offset, size_t *size))
|
||
|
{
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)priv;
|
||
|
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: priv[%p] data[%p] size[%zu]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, pkt_priv, (char *)iovec, size);
|
||
|
lpb_ch->cb_data.size = size;
|
||
|
lpb_ch->cb_data.rx_notify = true;
|
||
|
lpb_ch->cb_data.rx_data = (void *)iovec;
|
||
|
lpb_ch->cb_data.rx_vbuf_provider = vbuf_provider;
|
||
|
lpb_ch->cb_data.rx_pbuf_provider = pbuf_provider;
|
||
|
if (lpb_ch->ch_type == CH_CNTL_TYPE)
|
||
|
complete((struct completion *)pkt_priv);
|
||
|
else
|
||
|
complete(&lpb_ch->cb_data.cb_completion);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_notify_rx_tp_cb() - Receive tracer packet callback
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @priv: The loopback channel
|
||
|
* @pkt_priv: An rx_done_completion structure
|
||
|
* @ptr: Pointer to the tracer packet
|
||
|
* @size: Size of the tracer packet
|
||
|
*/
|
||
|
void glink_loopback_notify_rx_tp_cb(void *handle, const void *priv,
|
||
|
const void *pkt_priv, const void *ptr, size_t size)
|
||
|
{
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)priv;
|
||
|
|
||
|
tracer_pkt_log_event((void *)ptr, LOOPBACK_CLNT_RX);
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: priv[%p] data[%p] size[%zu]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, pkt_priv, (char *)ptr, size);
|
||
|
lpb_ch->cb_data.size = size;
|
||
|
lpb_ch->cb_data.rx_notify = true;
|
||
|
lpb_ch->cb_data.rx_data = (void *)ptr;
|
||
|
complete(&lpb_ch->cb_data.cb_completion);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_notify_tx_done_cb() - Callback used to notify the client when
|
||
|
* TX data has finished transmitting
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @priv: The loopback channel
|
||
|
* @pkt_priv: Private packet data
|
||
|
* @ptr: Pointer to the data that was sent
|
||
|
*/
|
||
|
void glink_loopback_notify_tx_done_cb(void *handle, const void *priv,
|
||
|
const void *pkt_priv, const void *ptr)
|
||
|
{
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)priv;
|
||
|
GLINK_LL_CLNT_INFO_PERF(
|
||
|
"%s:%s:%s %s: ptr[%p]\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__, ptr);
|
||
|
lpb_ch->cb_data.tx_done = true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_notify_state_cb() - Callback used to notify the client when
|
||
|
* the channel state has changed
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @priv: The loopback channel
|
||
|
* @event: The new state of the loopback channel
|
||
|
*/
|
||
|
void glink_loopback_notify_state_cb(void *handle, const void *priv,
|
||
|
unsigned event)
|
||
|
{
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)priv;
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: event[%d], Thread: %d\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, event, current->pid);
|
||
|
if (event == GLINK_CONNECTED
|
||
|
|| event == GLINK_LOCAL_DISCONNECTED) {
|
||
|
lpb_ch->cb_data.event = event;
|
||
|
complete(&lpb_ch->cb_data.cb_completion);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_rmt_rx_intent_req_cb() - Callback used to notify the client
|
||
|
* when the remote side has requested that
|
||
|
* an RX intent be queued
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @priv: The loopback channel
|
||
|
* @sz: The size of the requested intent
|
||
|
*
|
||
|
* This function initiates a work item that queues the requested intent.
|
||
|
*/
|
||
|
bool glink_loopback_rmt_rx_intent_req_cb(void *handle, const void *priv,
|
||
|
size_t sz)
|
||
|
{
|
||
|
struct clnt_rx_intent_req_work *work_item;
|
||
|
|
||
|
work_item = kmalloc(sizeof(struct clnt_rx_intent_req_work), GFP_ATOMIC);
|
||
|
if (!work_item) {
|
||
|
GLINK_LL_CLNT_ERR("%s failed allocate work_item\n", __func__);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
work_item->req_intent_size = sz;
|
||
|
work_item->lpb_ch = (struct loopback_channel *)priv;
|
||
|
INIT_WORK(&work_item->work, glink_loopback_rmt_rx_intent_req_worker);
|
||
|
queue_work(glink_lbp_client_wq, &work_item->work);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_notify_rx_sigs_cb - Handle the event of TIOCM signal changes
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @priv: The loopback channel
|
||
|
* @old_sigs: The old TIOCM signals
|
||
|
* @new_sigs: The new TIOCM signals
|
||
|
*/
|
||
|
void glink_loopback_notify_rx_sigs_cb(void *handle, const void *priv,
|
||
|
uint32_t old_sigs, uint32_t new_sigs)
|
||
|
{
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)priv;
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: sigs [0x%x]->[0x%X], Thread: %d\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, old_sigs, new_sigs, current->pid);
|
||
|
lpb_ch->cb_data.old_sigs = old_sigs;
|
||
|
lpb_ch->cb_data.new_sigs = new_sigs;
|
||
|
complete(&lpb_ch->cb_data.cb_completion);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_open() - Open the loopback channel
|
||
|
* @transport: Transport of the control or data channel
|
||
|
* @edge: Edge of the control or data channel
|
||
|
* @name: Name of the control or data channel
|
||
|
* @ch_type: Type of control or data channel
|
||
|
* @rx_reuse: Reuse RX intents
|
||
|
*
|
||
|
* This function is used to open the loopback channel on @transport & @edge with
|
||
|
* @name and waits until the GLINK_CONNECTED event is received or a timeout is
|
||
|
* reached.
|
||
|
*
|
||
|
* Return: Handle to loopback channel or NULL in error case
|
||
|
*/
|
||
|
void *glink_loopback_open(const char *transport, const char *edge,
|
||
|
const char *name, int ch_type, int rx_type,
|
||
|
bool rx_reuse)
|
||
|
{
|
||
|
int ret;
|
||
|
struct loopback_channel *lpb_ch;
|
||
|
|
||
|
if (!transport || !edge || !name) {
|
||
|
GLINK_LL_CLNT_ERR("%s: Incorrect open configurations\n",
|
||
|
__func__);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
lpb_ch = kzalloc(sizeof(struct loopback_channel), GFP_KERNEL);
|
||
|
if (!lpb_ch) {
|
||
|
GLINK_LL_CLNT_ERR("%s: No memory for allocation\n", __func__);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
lpb_ch->ch_type = ch_type;
|
||
|
|
||
|
memset(&lpb_ch->open_cfg, 0, sizeof(struct glink_open_config));
|
||
|
lpb_ch->open_cfg.transport = transport;
|
||
|
lpb_ch->open_cfg.edge = edge;
|
||
|
lpb_ch->open_cfg.name = name;
|
||
|
lpb_ch->rx_reuse = rx_reuse;
|
||
|
|
||
|
if (rx_type == LINEAR_RX || rx_type == LINEAR_VECTOR_RX)
|
||
|
lpb_ch->open_cfg.notify_rx = glink_loopback_notify_rx_cb;
|
||
|
if (rx_type == VECTOR_RX || rx_type == LINEAR_VECTOR_RX)
|
||
|
lpb_ch->open_cfg.notify_rxv = glink_loopback_notify_rxv_cb;
|
||
|
if (rx_type == LINEAR_RX_NOWAIT)
|
||
|
lpb_ch->open_cfg.notify_rx = glink_loopback_notify_rx_nowait_cb;
|
||
|
lpb_ch->open_cfg.notify_rx_tracer_pkt = glink_loopback_notify_rx_tp_cb;
|
||
|
lpb_ch->open_cfg.notify_tx_done = glink_loopback_notify_tx_done_cb;
|
||
|
lpb_ch->open_cfg.notify_state = glink_loopback_notify_state_cb;
|
||
|
lpb_ch->open_cfg.notify_rx_intent_req =
|
||
|
glink_loopback_rmt_rx_intent_req_cb;
|
||
|
lpb_ch->open_cfg.notify_rx_sigs = glink_loopback_notify_rx_sigs_cb;
|
||
|
lpb_ch->open_cfg.notify_rx_abort = NULL;
|
||
|
lpb_ch->open_cfg.notify_tx_abort = NULL;
|
||
|
lpb_ch->open_cfg.priv = lpb_ch;
|
||
|
|
||
|
cb_data_init(&lpb_ch->cb_data);
|
||
|
cb_data_reset(&lpb_ch->cb_data);
|
||
|
|
||
|
lpb_ch->handle = glink_open(&lpb_ch->open_cfg);
|
||
|
if (IS_ERR_OR_NULL(lpb_ch->handle)) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: unable to open channel\n",
|
||
|
transport, edge, name, __func__);
|
||
|
kfree(lpb_ch);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
ret = wait_for_completion_timeout(&lpb_ch->cb_data.cb_completion,
|
||
|
WAIT_TIMEOUT);
|
||
|
|
||
|
if (ret == 0 || lpb_ch->cb_data.event != GLINK_CONNECTED) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s Thread: %d\n",
|
||
|
transport, edge, name, __func__,
|
||
|
"Connected event TIMED OUT.", current->pid);
|
||
|
glink_loopback_close(lpb_ch);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return lpb_ch;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_close() - Close the loopback channel
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
*
|
||
|
* This function is used to close the loopback channel and waits until the
|
||
|
* GLINK_DISCONNECTED event is received or a timeout is reached.
|
||
|
*
|
||
|
* Return: 0 on success, standard error codes otherwise
|
||
|
*/
|
||
|
int glink_loopback_close(void *handle)
|
||
|
{
|
||
|
int ret;
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)handle;
|
||
|
|
||
|
if (!lpb_ch) {
|
||
|
GLINK_LL_CLNT_ERR("%s: NULL Pointer.\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
cb_data_reset(&lpb_ch->cb_data);
|
||
|
ret = glink_close(lpb_ch->handle);
|
||
|
if (ret) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__,
|
||
|
"unable to close channel.", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = wait_for_completion_timeout(&lpb_ch->cb_data.cb_completion,
|
||
|
WAIT_TIMEOUT);
|
||
|
|
||
|
if (ret == 0 || lpb_ch->cb_data.event != GLINK_LOCAL_DISCONNECTED) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s ret[%d] event[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
"Local Disconnected event TIMED OUT",
|
||
|
__func__, ret, lpb_ch->cb_data.event);
|
||
|
return -ETIMEDOUT;
|
||
|
}
|
||
|
|
||
|
kfree(lpb_ch);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_tx() - Send the data on loopback channel
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @data: Data to send on the loopback channel
|
||
|
* @size: Size of the data to send on the loopback channel
|
||
|
* @req_intent: Remote intent request flag
|
||
|
* @first_tx: First tx since channel was opened
|
||
|
* @rx_reuse: Reuse the RX intents
|
||
|
*
|
||
|
* This function sends data on the loopback channel, queues an RX intent to
|
||
|
* receive the loopback data, and waits until the loopback data is received
|
||
|
* or a timeout is reached.
|
||
|
*
|
||
|
* Return: 0 on success, standard error codes otherwise
|
||
|
*/
|
||
|
int glink_loopback_tx(void *handle, void *data, size_t size, bool req_intent,
|
||
|
bool first_tx, bool rx_reuse)
|
||
|
{
|
||
|
int ret;
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)handle;
|
||
|
int i;
|
||
|
char *p;
|
||
|
uint32_t tx_flags = req_intent ? GLINK_TX_REQ_INTENT : 0;
|
||
|
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: %s\n", lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge, lpb_ch->open_cfg.name,
|
||
|
__func__, "Queueing rx_intent for loopback data");
|
||
|
if (!rx_reuse || (rx_reuse && first_tx)) {
|
||
|
ret = glink_queue_rx_intent(lpb_ch->handle, (void *)lpb_ch,
|
||
|
size);
|
||
|
if (ret) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge, lpb_ch->open_cfg.name,
|
||
|
__func__, "glink_queue_rx_intent failed", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cb_data_reset(&lpb_ch->cb_data);
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: Sending data[%p] size[%zu]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, data, size);
|
||
|
ret = glink_tx(lpb_ch->handle, (void *)data, (void *)data, size,
|
||
|
tx_flags);
|
||
|
if (ret) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: glink_tx failed ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = wait_for_completion_timeout(&lpb_ch->cb_data.cb_completion,
|
||
|
WAIT_TIMEOUT);
|
||
|
if (ret == 0 || lpb_ch->cb_data.size != size) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__,
|
||
|
"data Rx TIMED out or Not exact data");
|
||
|
return -ETIMEDOUT;
|
||
|
}
|
||
|
|
||
|
ret = 0;
|
||
|
if (lpb_ch->ch_type == CH_DATA_TYPE) {
|
||
|
if (memcmp(lpb_ch->cb_data.rx_data, data, size)) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: DATA Mismatch\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__);
|
||
|
p = (char *)data;
|
||
|
for (i = 0; i < size; i = i + 10) {
|
||
|
GLINK_LL_CLNT_INFO(
|
||
|
"[tx line idx:%d] 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
|
||
|
i, p[i], p[i+1], p[i+2], p[i+3], p[i+4],
|
||
|
p[i+5], p[i+6], p[i+7], p[i+8], p[i+9]);
|
||
|
}
|
||
|
p = (char *)lpb_ch->cb_data.rx_data;
|
||
|
for (i = 0; i < size; i = i + 10) {
|
||
|
GLINK_LL_CLNT_INFO(
|
||
|
"[rx line idx:%d] 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
|
||
|
i, p[i], p[i+1], p[i+2], p[i+3], p[i+4],
|
||
|
p[i+5], p[i+6], p[i+7], p[i+8], p[i+9]);
|
||
|
}
|
||
|
ret = -EFAULT;
|
||
|
} else {
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: DATA Matched\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__);
|
||
|
}
|
||
|
}
|
||
|
glink_rx_done(lpb_ch->handle, lpb_ch->cb_data.rx_data, rx_reuse);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_tx_tp() - Send the tracer packet on loopback channel
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @data: Buffer for the trace packet
|
||
|
* @size: Size of the tracer packet buffer
|
||
|
* @req_intent: Remote intent request flag
|
||
|
* @first_tx: First tx since channel was opened
|
||
|
* @rx_reuse: Reuse the RX intents
|
||
|
*
|
||
|
* This function sends tracer packet on the loopback channel, queues an RX
|
||
|
* intent to receive the loopback data, and waits until the loopback data is
|
||
|
* received or a timeout is reached.
|
||
|
*
|
||
|
* Return: 0 on success, standard error codes otherwise
|
||
|
*/
|
||
|
int glink_loopback_tx_tp(void *handle, void *data, size_t size, bool req_intent,
|
||
|
bool first_tx, bool rx_reuse)
|
||
|
{
|
||
|
int ret;
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)handle;
|
||
|
uint32_t tx_flags = req_intent ? GLINK_TX_REQ_INTENT : 0;
|
||
|
|
||
|
tx_flags |= GLINK_TX_TRACER_PKT;
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: %s\n", lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge, lpb_ch->open_cfg.name,
|
||
|
__func__, "Queueing rx_intent for loopback data");
|
||
|
tracer_pkt_log_event(data, LOOPBACK_CLNT_TX);
|
||
|
if (!rx_reuse || (rx_reuse && first_tx)) {
|
||
|
ret = glink_queue_rx_intent(lpb_ch->handle, (void *)lpb_ch,
|
||
|
size);
|
||
|
if (ret) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge, lpb_ch->open_cfg.name,
|
||
|
__func__, "glink_queue_rx_intent failed", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cb_data_reset(&lpb_ch->cb_data);
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: Sending data[%p] size[%zu]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, data, size);
|
||
|
ret = glink_tx(lpb_ch->handle, (void *)data, (void *)data, size,
|
||
|
tx_flags);
|
||
|
if (ret) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: glink_tx failed ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = wait_for_completion_timeout(&lpb_ch->cb_data.cb_completion,
|
||
|
WAIT_TIMEOUT);
|
||
|
if (ret == 0 || lpb_ch->cb_data.size != size) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__,
|
||
|
"data Rx TIMED out or Not exact data");
|
||
|
return -ETIMEDOUT;
|
||
|
}
|
||
|
ret = 0;
|
||
|
memcpy(data, lpb_ch->cb_data.rx_data, size);
|
||
|
glink_rx_done(lpb_ch->handle, lpb_ch->cb_data.rx_data, rx_reuse);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_tx_nowait() - Send the data on loopback channel
|
||
|
* without waiting for the echo of the data.
|
||
|
* @handle: Handle returned by glink_open()
|
||
|
* @data: Data to send on the loopback channel
|
||
|
* @size: Size of the data to send on the loopback channel
|
||
|
* @req_intent: Remote intent request flag
|
||
|
* @cb_data: Callback data to be used when the data arrives back
|
||
|
*
|
||
|
* This function is used to send the data on the loopback channel
|
||
|
* and queue the RX intent to receive the loopback data.
|
||
|
*
|
||
|
* Return: 0 on success, standard error codes otherwise
|
||
|
*/
|
||
|
int glink_loopback_tx_nowait(void *handle, void *data, size_t size,
|
||
|
bool req_intent, struct mt_cb_data *cb_data)
|
||
|
{
|
||
|
int ret;
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)handle;
|
||
|
uint32_t tx_flags = req_intent ? GLINK_TX_REQ_INTENT : 0;
|
||
|
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: %s\n", lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge, lpb_ch->open_cfg.name,
|
||
|
__func__, "Queueing rx_intent for loopback data");
|
||
|
ret = glink_queue_rx_intent(lpb_ch->handle, (void *)cb_data, size);
|
||
|
if (ret) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge, lpb_ch->open_cfg.name,
|
||
|
__func__, "glink_queue_rx_intent failed", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
atomic_inc(&cb_data->num_tx_pkts);
|
||
|
GLINK_LL_CLNT_INFO_PERF("%s:%s:%s %s: start TX data[%p] size[%zu]\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__, data, size);
|
||
|
ret = glink_tx(lpb_ch->handle, (void *)data, (void *)data, size,
|
||
|
tx_flags);
|
||
|
if (ret)
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: glink_tx failed ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_txv() - Send data on the loopback channel in vector form
|
||
|
* @handle: Handle returned by glink_loopback_open()
|
||
|
* @iovec: Pointer to the vector
|
||
|
* @size: Size of data/vector
|
||
|
* @vbuf_provider: Client provided helper function to iterate the vector
|
||
|
* in virtual address space
|
||
|
* @pbuf_provider: Client provided helper function to iterate the vector
|
||
|
* in physical address space
|
||
|
* @req_intent: Flag indicating whether or not to request an intent
|
||
|
* from the remote side
|
||
|
*
|
||
|
* Return: 0 on success, standard error codes otherwise
|
||
|
*/
|
||
|
int glink_loopback_txv(void *handle, void *iovec, size_t size,
|
||
|
void * (*vbuf_provider)(void *iovec, size_t offset, size_t *size),
|
||
|
void * (*pbuf_provider)(void *iovec, size_t offset, size_t *size),
|
||
|
bool req_intent)
|
||
|
{
|
||
|
int ret;
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)handle;
|
||
|
uint32_t tx_flags = req_intent ? GLINK_TX_REQ_INTENT : 0;
|
||
|
|
||
|
if (!vbuf_provider && !pbuf_provider)
|
||
|
return -EINVAL;
|
||
|
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: %s\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__,
|
||
|
"Queueing rx_intent for loopback data");
|
||
|
|
||
|
ret = glink_queue_rx_intent(lpb_ch->handle, (void *)lpb_ch, size);
|
||
|
if (ret) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__,
|
||
|
"glink_queue_rx_intent failed", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
cb_data_reset(&lpb_ch->cb_data);
|
||
|
lpb_ch->cb_data.tx_data = iovec;
|
||
|
lpb_ch->cb_data.tx_vbuf_provider = vbuf_provider;
|
||
|
lpb_ch->cb_data.tx_pbuf_provider = pbuf_provider;
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: Sending data[%p] size[%zu]\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__, iovec, size);
|
||
|
ret = glink_txv(lpb_ch->handle, (void *)iovec, (void *)iovec, size,
|
||
|
vbuf_provider, pbuf_provider, tx_flags);
|
||
|
if (ret) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: glink_tx failed ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = wait_for_completion_timeout(&lpb_ch->cb_data.cb_completion,
|
||
|
WAIT_TIMEOUT);
|
||
|
if (ret == 0 || lpb_ch->cb_data.size != size) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__,
|
||
|
"data Rx TIMED out or Not exact data");
|
||
|
return -ETIMEDOUT;
|
||
|
}
|
||
|
|
||
|
ret = glink_loopback_vector_cmp(&lpb_ch->cb_data);
|
||
|
glink_rx_done(lpb_ch->handle, lpb_ch->cb_data.rx_data, false);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_sigs_set() - Set the signals on a loopback channel
|
||
|
* @handle: Handle returned by glink_open()
|
||
|
* @sigs: New signals to be set to the channel
|
||
|
*
|
||
|
* This function is used to set the signals for a loopback channel. It waits for
|
||
|
* the remote loopback channel to be set with the same signals and returns the
|
||
|
* callback.
|
||
|
*
|
||
|
* Return: 0 on success, standard error codes otherwise
|
||
|
*/
|
||
|
int glink_loopback_sigs_set(void *handle, uint32_t sigs)
|
||
|
{
|
||
|
int ret;
|
||
|
uint32_t newsigs;
|
||
|
struct loopback_channel *lpb_ch = (struct loopback_channel *)handle;
|
||
|
|
||
|
cb_data_reset(&lpb_ch->cb_data);
|
||
|
GLINK_LL_CLNT_INFO("%s:%s:%s %s: Sending sigs[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, sigs);
|
||
|
ret = glink_sigs_set(lpb_ch->handle, sigs);
|
||
|
if (ret) {
|
||
|
GLINK_LL_CLNT_ERR(
|
||
|
"%s:%s:%s %s: glink_sigs_set failed ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = wait_for_completion_timeout(&lpb_ch->cb_data.cb_completion,
|
||
|
WAIT_TIMEOUT);
|
||
|
if (ret == 0 || lpb_ch->cb_data.new_sigs != sigs) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__,
|
||
|
"set Sigs Timeout or Mis match");
|
||
|
return -ETIMEDOUT;
|
||
|
}
|
||
|
|
||
|
ret = glink_sigs_local_get(lpb_ch->handle, &newsigs);
|
||
|
if (ret < 0) {
|
||
|
GLINK_LL_CLNT_ERR(
|
||
|
"%s:%s:%s %s: glink_sigs_local_get failed ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
if (newsigs != sigs) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__,
|
||
|
"local Sigs Not set correctly");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
ret = glink_sigs_remote_get(lpb_ch->handle, &newsigs);
|
||
|
if (ret < 0) {
|
||
|
GLINK_LL_CLNT_ERR(
|
||
|
"%s:%s:%s %s: glink_sigs_remote_get failed ret[%d]\n",
|
||
|
lpb_ch->open_cfg.transport,
|
||
|
lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name,
|
||
|
__func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
if (newsigs != sigs) {
|
||
|
GLINK_LL_CLNT_ERR("%s:%s:%s %s: %s\n",
|
||
|
lpb_ch->open_cfg.transport, lpb_ch->open_cfg.edge,
|
||
|
lpb_ch->open_cfg.name, __func__,
|
||
|
"Remote Sigs Not set correctly");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_client_init() - Basic loopback client init
|
||
|
*
|
||
|
* This function performs the basic initialization for the loopback
|
||
|
* client.
|
||
|
*/
|
||
|
void glink_loopback_client_init(void)
|
||
|
{
|
||
|
glink_lbp_client_wq = create_singlethread_workqueue("glink_lbp_client");
|
||
|
if (!glink_lbp_client_wq) {
|
||
|
GLINK_LL_CLNT_ERR("%s: Error creating glink_lbp_client_wq\n",
|
||
|
__func__);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_hex_dump_tp() - Hex dump of the tracer packet into debugfs
|
||
|
* @s: Sequential file handle to debugfs
|
||
|
* @data: Pointer to the tracer packet
|
||
|
* @data_len: Length of the tracer packet
|
||
|
*
|
||
|
* This function dumps the tracer packet into the debugfs file handle associated
|
||
|
* with the test.
|
||
|
*/
|
||
|
void glink_loopback_hex_dump_tp(struct seq_file *s, void *data,
|
||
|
size_t data_len)
|
||
|
{
|
||
|
char *hex_dump_buf;
|
||
|
size_t hex_dump_buf_size;
|
||
|
int ret;
|
||
|
|
||
|
hex_dump_buf_size = tracer_pkt_calc_hex_dump_size(data, data_len);
|
||
|
if (hex_dump_buf_size <= 0)
|
||
|
return;
|
||
|
|
||
|
hex_dump_buf = kzalloc(hex_dump_buf_size, GFP_KERNEL);
|
||
|
if (!hex_dump_buf)
|
||
|
return;
|
||
|
|
||
|
ret = tracer_pkt_hex_dump(hex_dump_buf, hex_dump_buf_size,
|
||
|
data, data_len);
|
||
|
if (ret == 0)
|
||
|
seq_printf(s, "%s\n", hex_dump_buf);
|
||
|
kfree(hex_dump_buf);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* glink_loopback_client_exit() - loopback client deinitialization
|
||
|
*
|
||
|
* This function performs the clean up when loopback client is deleted.
|
||
|
*/
|
||
|
void glink_loopback_client_exit(void)
|
||
|
{
|
||
|
if (glink_lbp_client_wq)
|
||
|
destroy_workqueue(glink_lbp_client_wq);
|
||
|
}
|