1169 lines
26 KiB
C
1169 lines
26 KiB
C
/****************************************************************************
|
|
* apps/logging/nxscope/nxscope.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <logging/nxscope/nxscope.h>
|
|
|
|
#include "nxscope_internals.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* NOTE: channel name is always terminated with a null-character */
|
|
|
|
#define CHINFO_DATA_SIZE_MAX (sizeof(struct nxscope_chinfo_s) - \
|
|
sizeof(char *) + CHAN_NAMELEN_MAX + 1)
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_frame_send
|
|
*
|
|
* NOTE: This function assumes that we have exclusive access to the nxscope
|
|
* instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int nxscope_frame_send(FAR struct nxscope_s *s, uint8_t id,
|
|
FAR uint8_t *data, size_t dlen)
|
|
{
|
|
size_t tx_i = 0;
|
|
int ret = OK;
|
|
|
|
DEBUGASSERT(s);
|
|
DEBUGASSERT(data);
|
|
|
|
#ifdef CONFIG_DEBUG_FEATURES
|
|
/* Validate TX buffer space */
|
|
|
|
if (s->txbuf_len < dlen + s->proto_cmd->footlen + s->proto_cmd->hdrlen)
|
|
{
|
|
ret = -ENOBUFS;
|
|
_err("ERROR: no space in txbuf %d\n", ret);
|
|
goto errout;
|
|
}
|
|
#endif
|
|
|
|
/* Offset for hdr */
|
|
|
|
tx_i = s->proto_cmd->hdrlen;
|
|
|
|
/* Copy data */
|
|
|
|
memcpy(&s->txbuf[tx_i], data, dlen);
|
|
tx_i += dlen;
|
|
|
|
/* Finalize a new frame */
|
|
|
|
ret = PROTO_FRAME_FINAL(s, s->proto_cmd, id, s->txbuf, &tx_i);
|
|
if (ret < 0)
|
|
{
|
|
_err("ERROR: PROTO_FRAME_FINAL failed %d\n", ret);
|
|
goto errout;
|
|
}
|
|
|
|
/* Send frame */
|
|
|
|
ret = INTF_SEND(s, s->intf_cmd, s->txbuf, tx_i);
|
|
if (ret < 0)
|
|
{
|
|
_err("ERROR: INTF_SEND failed %d\n", ret);
|
|
}
|
|
|
|
errout:
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_cmninfo_send
|
|
*
|
|
* NOTE: This function assumes that we have exclusive access to the nxscope
|
|
* instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int nxscope_cmninfo_send(FAR struct nxscope_s *s)
|
|
{
|
|
DEBUGASSERT(s);
|
|
|
|
return nxscope_frame_send(s,
|
|
NXSCOPE_HDRID_CMNINFO,
|
|
(FAR uint8_t *)&s->cmninfo,
|
|
sizeof(struct nxscope_info_cmn_s));
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_chinfo_send
|
|
*
|
|
* NOTE: This function assumes that we have exclusive access to the nxscope
|
|
* instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int nxscope_chinfo_send(FAR struct nxscope_s *s, uint8_t ch)
|
|
{
|
|
uint8_t data[CHINFO_DATA_SIZE_MAX];
|
|
size_t namelen = 0;
|
|
size_t txlen = 0;
|
|
size_t tmp = 0;
|
|
|
|
DEBUGASSERT(s);
|
|
|
|
/* Copy channel info */
|
|
|
|
tmp = sizeof(struct nxscope_chinfo_s) - sizeof(char *);
|
|
memcpy(data, &s->chinfo[ch], tmp);
|
|
|
|
namelen = strnlen(s->chinfo[ch].name, CHAN_NAMELEN_MAX);
|
|
memcpy(&data[tmp], s->chinfo[ch].name, namelen);
|
|
|
|
/* Treminate name wit a null-character */
|
|
|
|
txlen = tmp + namelen + 1;
|
|
data[txlen - 1] = '\0';
|
|
|
|
/* Send frame */
|
|
|
|
return nxscope_frame_send(s, NXSCOPE_HDRID_CHINFO, data, txlen);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_enable_req
|
|
*
|
|
* NOTE: This function assumes that we have exclusive access to the nxscope
|
|
* instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int nxscope_enable_req(FAR struct nxscope_s *s,
|
|
FAR struct nxscope_set_frame_s *set,
|
|
uint16_t dlen)
|
|
{
|
|
FAR struct nxscope_enable_data_s *data = NULL;
|
|
int ret = OK;
|
|
int i = 0;
|
|
|
|
DEBUGASSERT(s);
|
|
DEBUGASSERT(set);
|
|
|
|
/* Get data */
|
|
|
|
data = (FAR struct nxscope_enable_data_s *) set->data;
|
|
|
|
/* Handle set request */
|
|
|
|
switch (set->req)
|
|
{
|
|
case NXSCOPE_SET_REQ_SINGLE:
|
|
{
|
|
/* Verify request length */
|
|
|
|
if (dlen != (sizeof(struct nxscope_set_frame_s) +
|
|
NXSCOPE_ENABLE_LEN - 1))
|
|
{
|
|
_err("ERROR: invalid enable single dlen = %d\n", dlen);
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
/* Validate data */
|
|
|
|
if (data->ch[0].en != 0 && data->ch[0].en != 1)
|
|
{
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
if (set->chan > s->cmninfo.chmax)
|
|
{
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
/* Write configuration */
|
|
|
|
s->chinfo[set->chan].enable = data->ch[0].en;
|
|
|
|
break;
|
|
}
|
|
|
|
case NXSCOPE_SET_REQ_BULK:
|
|
{
|
|
/* Verify request length */
|
|
|
|
if (dlen != (sizeof(struct nxscope_set_frame_s) +
|
|
NXSCOPE_ENABLE_LEN * s->cmninfo.chmax - 1))
|
|
{
|
|
_err("ERROR: invalid enable bulk dlen = %d\n", dlen);
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
/* Validate data */
|
|
|
|
for (i = 0; i < s->cmninfo.chmax; i++)
|
|
{
|
|
if (data->ch[i].en != 0 && data->ch[i].en != 1)
|
|
{
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
}
|
|
|
|
/* Write configuration */
|
|
|
|
for (i = 0; i < s->cmninfo.chmax; i++)
|
|
{
|
|
s->chinfo[i].enable = data->ch[i].en;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case NXSCOPE_SET_REQ_ALL:
|
|
{
|
|
/* Verify request length */
|
|
|
|
if (dlen != (sizeof(struct nxscope_set_frame_s) +
|
|
NXSCOPE_ENABLE_LEN - 1))
|
|
{
|
|
_err("ERROR: invalid enable all dlen = %d\n", dlen);
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
if (data->ch[0].en != 0 && data->ch[0].en != 1)
|
|
{
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
/* Write configuration */
|
|
|
|
for (i = 0; i < s->cmninfo.chmax; i++)
|
|
{
|
|
s->chinfo[i].enable = data->ch[0].en;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
_err("ERROR: invalid set->req=%d\n", set->req);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
errout:
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_DIVIDER
|
|
/****************************************************************************
|
|
* Name: nxscope_div_req
|
|
*
|
|
* NOTE: This function assumes that we have exclusive access to the nxscope
|
|
* instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int nxscope_div_req(FAR struct nxscope_s *s,
|
|
FAR struct nxscope_set_frame_s *set,
|
|
uint16_t dlen)
|
|
{
|
|
FAR struct nxscope_div_data_s *data = NULL;
|
|
int ret = OK;
|
|
int i = 0;
|
|
|
|
DEBUGASSERT(s);
|
|
DEBUGASSERT(set);
|
|
|
|
/* Get data */
|
|
|
|
data = (FAR struct nxscope_div_data_s *)set->data;
|
|
|
|
/* Handle set request */
|
|
|
|
switch (set->req)
|
|
{
|
|
case NXSCOPE_SET_REQ_SINGLE:
|
|
{
|
|
/* Verify request length */
|
|
|
|
if (dlen != (sizeof(struct nxscope_set_frame_s) +
|
|
NXSCOPE_DIV_LEN - 1))
|
|
{
|
|
_err("ERROR: invalid div single dlen = %d\n", dlen);
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
if (set->chan > s->cmninfo.chmax)
|
|
{
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
/* Write configuration */
|
|
|
|
s->chinfo[set->chan].div = data->ch[0].div;
|
|
|
|
break;
|
|
}
|
|
|
|
case NXSCOPE_SET_REQ_BULK:
|
|
{
|
|
/* Verify request length */
|
|
|
|
if (dlen != (sizeof(struct nxscope_set_frame_s) +
|
|
NXSCOPE_DIV_LEN * s->cmninfo.chmax - 1))
|
|
{
|
|
_err("ERROR: invalid div bulk dlen = %d\n", dlen);
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
/* Write configuration */
|
|
|
|
for (i = 0; i < s->cmninfo.chmax; i++)
|
|
{
|
|
s->chinfo[i].div = data->ch[i].div;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case NXSCOPE_SET_REQ_ALL:
|
|
{
|
|
/* Verify request length */
|
|
|
|
if (dlen != (sizeof(struct nxscope_set_frame_s) +
|
|
NXSCOPE_DIV_LEN - 1))
|
|
{
|
|
_err("ERROR: invalid div all dlen = %d\n", dlen);
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
/* Write configuration */
|
|
|
|
for (i = 0; i < s->cmninfo.chmax; i++)
|
|
{
|
|
s->chinfo[i].div = data->ch[0].div;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
_err("ERROR: invalid set->req=%d\n", set->req);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
errout:
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_start_set
|
|
*
|
|
* NOTE: This function assumes that we have exclusive access to the nxscope
|
|
* instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int nxscope_start_set(FAR struct nxscope_s *s, bool start)
|
|
{
|
|
int ret = OK;
|
|
|
|
s->start = start;
|
|
|
|
/* User specific callback */
|
|
|
|
if (s->callbacks != NULL && s->callbacks->start != NULL)
|
|
{
|
|
ret = s->callbacks->start(s->callbacks->start_priv, start);
|
|
if (ret < 0)
|
|
{
|
|
_err("ERROR: s->callbacks->start failed %d\n", ret);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_start_req
|
|
*
|
|
* NOTE: This function assumes that we have exclusive access to the nxscope
|
|
* instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int nxscope_start_req(FAR struct nxscope_s *s,
|
|
FAR struct nxscope_start_data_s *data)
|
|
{
|
|
int ret = -EINVAL;
|
|
|
|
DEBUGASSERT(s);
|
|
DEBUGASSERT(data);
|
|
|
|
if (data->start == 0 || data->start == 1)
|
|
{
|
|
_info("data->start=%d\n", data->start);
|
|
ret = nxscope_start_set(s, data->start);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_stream_reset
|
|
*
|
|
* NOTE: This function assumes that we have exclusive access to the nxscope
|
|
* instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void nxscope_stream_reset(FAR struct nxscope_s *s)
|
|
{
|
|
DEBUGASSERT(s);
|
|
|
|
/* Offset for hdr + 1 byte for flags */
|
|
|
|
s->stream_i = s->proto_stream->hdrlen + 1;
|
|
|
|
/* Reset flags */
|
|
|
|
s->streambuf[s->proto_stream->hdrlen] = 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_stream_empty
|
|
*
|
|
* NOTE: This function assumes that we have exclusive access to the nxscope
|
|
* instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
static bool nxscope_stream_empty(FAR struct nxscope_s *s)
|
|
{
|
|
DEBUGASSERT(s);
|
|
|
|
if (s->stream_i > s->proto_stream->hdrlen + 1)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_ACKFRAMES
|
|
/****************************************************************************
|
|
* Name: nxscope_ack
|
|
*
|
|
* NOTE: This function assumes that we have exclusive access to the nxscope
|
|
* instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void nxscope_ack(FAR struct nxscope_s *s, int ret)
|
|
{
|
|
DEBUGASSERT(s);
|
|
|
|
nxscope_frame_send(s, NXSCOPE_HDRID_ACK, (FAR uint8_t *)&ret, 4);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_recv_handle
|
|
****************************************************************************/
|
|
|
|
static int nxscope_recv_handle(FAR struct nxscope_s *s, uint8_t id,
|
|
uint16_t dlen, FAR uint8_t *buf)
|
|
{
|
|
int ret = OK;
|
|
|
|
DEBUGASSERT(s);
|
|
DEBUGASSERT(buf);
|
|
|
|
nxscope_lock(s);
|
|
|
|
switch (id)
|
|
{
|
|
case NXSCOPE_HDRID_CMNINFO:
|
|
{
|
|
_info("NXSCOPE_HDRID_CMNINFO\n");
|
|
|
|
/* Verify request length */
|
|
|
|
if (dlen != 0)
|
|
{
|
|
_err("ERROR: cmninfo request invalid dlen = %d\n", dlen);
|
|
goto errout;
|
|
}
|
|
|
|
/* Send cmninfo response */
|
|
|
|
ret = nxscope_cmninfo_send(s);
|
|
|
|
break;
|
|
}
|
|
|
|
case NXSCOPE_HDRID_CHINFO:
|
|
{
|
|
FAR uint8_t *ch = buf;
|
|
|
|
_info("NXSCOPE_HDRID_CHINFO %d\n", *ch);
|
|
|
|
/* Verify request length */
|
|
|
|
if (dlen != 1)
|
|
{
|
|
_err("ERROR: chinfo request invalid dlen = %d\n", dlen);
|
|
goto errout;
|
|
}
|
|
|
|
/* Send chinfo response */
|
|
|
|
ret = nxscope_chinfo_send(s, *ch);
|
|
|
|
break;
|
|
}
|
|
|
|
case NXSCOPE_HDRID_START:
|
|
{
|
|
FAR struct nxscope_start_data_s *data =
|
|
(FAR struct nxscope_start_data_s *)buf;
|
|
|
|
_info("NXSCOPE_HDRID_START\n");
|
|
|
|
/* Verify request length */
|
|
|
|
if (dlen != NXSCOPE_START_LEN)
|
|
{
|
|
_err("ERROR: start request invalid dlen = %d\n", dlen);
|
|
goto errout;
|
|
}
|
|
|
|
/* Start request */
|
|
|
|
ret = nxscope_start_req(s, data);
|
|
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_ACKFRAMES
|
|
/* Send ACK */
|
|
|
|
nxscope_ack(s, ret);
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
case NXSCOPE_HDRID_ENABLE:
|
|
{
|
|
FAR struct nxscope_set_frame_s *data =
|
|
(FAR struct nxscope_set_frame_s *)buf;
|
|
|
|
_info("NXSCOPE_HDRID_ENABLE\n");
|
|
|
|
/* Enable request */
|
|
|
|
ret = nxscope_enable_req(s, data, dlen);
|
|
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_ACKFRAMES
|
|
/* Send ACK */
|
|
|
|
nxscope_ack(s, ret);
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
case NXSCOPE_HDRID_DIV:
|
|
{
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_DIVIDER
|
|
FAR struct nxscope_set_frame_s *data =
|
|
(FAR struct nxscope_set_frame_s *)buf;
|
|
|
|
_info("NXSCOPE_HDRID_DIV\n");
|
|
|
|
/* Divider request */
|
|
|
|
ret = nxscope_div_req(s, data, dlen);
|
|
#else
|
|
ret = -EPERM;
|
|
#endif
|
|
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_ACKFRAMES
|
|
/* Send ACK */
|
|
|
|
nxscope_ack(s, ret);
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
/* User specific commands */
|
|
|
|
if (s->callbacks != NULL && s->callbacks->userid != NULL)
|
|
{
|
|
ret = s->callbacks->userid(s->callbacks->userid_priv, id, buf);
|
|
if (ret < 0)
|
|
{
|
|
_err("ERROR: s->callbacks->userid failed %d\n", ret);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_err("ERROR: unsupported id %d\n", id);
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
errout:
|
|
nxscope_unlock(s);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_init
|
|
*
|
|
* Description:
|
|
* Initialize a nxscope instance
|
|
*
|
|
* Input Parameters:
|
|
* s - a pointer to a nxscope instance
|
|
* cfg - a pointer to a nxscope configuration data
|
|
*
|
|
****************************************************************************/
|
|
|
|
int nxscope_init(FAR struct nxscope_s *s, FAR struct nxscope_cfg_s *cfg)
|
|
{
|
|
int ret = OK;
|
|
int i = 0;
|
|
|
|
DEBUGASSERT(s);
|
|
DEBUGASSERT(cfg);
|
|
|
|
/* Reset structure */
|
|
|
|
memset(s, 0, sizeof(struct nxscope_s));
|
|
|
|
/* Connect command interface */
|
|
|
|
DEBUGASSERT(cfg->intf_cmd);
|
|
DEBUGASSERT(cfg->intf_cmd->ops->send);
|
|
DEBUGASSERT(cfg->intf_cmd->ops->recv);
|
|
|
|
/* Must be initialized */
|
|
|
|
if (!cfg->intf_cmd->initialized)
|
|
{
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
s->intf_cmd = cfg->intf_cmd;
|
|
|
|
/* Connect command protocol handler */
|
|
|
|
DEBUGASSERT(cfg->proto_cmd);
|
|
DEBUGASSERT(cfg->proto_cmd->ops->frame_get);
|
|
DEBUGASSERT(cfg->proto_cmd->ops->frame_final);
|
|
|
|
/* Must be initialized */
|
|
|
|
if (!cfg->proto_cmd->initialized)
|
|
{
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
s->proto_cmd = cfg->proto_cmd;
|
|
|
|
/* Connect stream interface */
|
|
|
|
DEBUGASSERT(cfg->intf_stream);
|
|
DEBUGASSERT(cfg->intf_stream->ops->send);
|
|
DEBUGASSERT(cfg->intf_stream->ops->recv);
|
|
|
|
/* Must be initialized */
|
|
|
|
if (!cfg->intf_stream->initialized)
|
|
{
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
s->intf_stream = cfg->intf_stream;
|
|
|
|
/* Connect stream protocol handler */
|
|
|
|
DEBUGASSERT(cfg->proto_stream);
|
|
DEBUGASSERT(cfg->proto_stream->ops->frame_get);
|
|
DEBUGASSERT(cfg->proto_stream->ops->frame_final);
|
|
|
|
/* Must be initialized */
|
|
|
|
if (!cfg->proto_stream->initialized)
|
|
{
|
|
ret = -EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
s->proto_stream = cfg->proto_stream;
|
|
|
|
/* Connect callbacks (optional) */
|
|
|
|
s->callbacks = cfg->callbacks;
|
|
|
|
/* Allocate memory for stream buffer */
|
|
|
|
DEBUGASSERT(cfg->streambuf_len > 0);
|
|
|
|
s->streambuf = zalloc(cfg->streambuf_len);
|
|
if (s->streambuf == NULL)
|
|
{
|
|
ret = -errno;
|
|
_err("ERROR: streambuf zalloc failed %d\n", ret);
|
|
goto errout;
|
|
}
|
|
|
|
s->streambuf_len = cfg->streambuf_len;
|
|
|
|
/* Allocate memory for nxscope channels info */
|
|
|
|
DEBUGASSERT(cfg->channels > 0);
|
|
|
|
s->chinfo_size = cfg->channels * sizeof(struct nxscope_chinfo_s);
|
|
|
|
s->chinfo = zalloc(s->chinfo_size);
|
|
if (s->chinfo == NULL)
|
|
{
|
|
ret = -errno;
|
|
_err("ERROR: chinfo zalloc failed %d\n", ret);
|
|
goto errout;
|
|
}
|
|
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_DIVIDER
|
|
/* Allocate memory for divider counters */
|
|
|
|
s->cntr = zalloc(cfg->channels * sizeof(uint32_t));
|
|
if (s->cntr == NULL)
|
|
{
|
|
ret = -errno;
|
|
_err("ERROR: cntr zalloc failed %d\n", ret);
|
|
goto errout;
|
|
}
|
|
#endif
|
|
|
|
/* Allocate memory for RX buffer */
|
|
|
|
DEBUGASSERT(cfg->rxbuf_len > 0);
|
|
s->rxbuf_len = cfg->rxbuf_len;
|
|
|
|
s->rxbuf = zalloc(s->chinfo_size);
|
|
if (s->rxbuf == NULL)
|
|
{
|
|
ret = -errno;
|
|
_err("ERROR: rxbuf zalloc failed %d\n", ret);
|
|
goto errout;
|
|
}
|
|
|
|
/* Allocate memory for TX buffer.
|
|
* We must fit the longest possible CHINFO response.
|
|
*/
|
|
|
|
s->txbuf_len = (CHINFO_DATA_SIZE_MAX + s->proto_cmd->footlen +
|
|
s->proto_cmd->hdrlen);
|
|
s->txbuf = zalloc(s->txbuf_len);
|
|
if (s->txbuf == NULL)
|
|
{
|
|
ret = -errno;
|
|
_err("ERROR: txbuf zalloc failed %d\n", ret);
|
|
goto errout;
|
|
}
|
|
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_CRICHANNELS
|
|
/* Allocate memory for critical channels buffer */
|
|
|
|
s->cribuf_len = cfg->cribuf_len;
|
|
s->cribuf = zalloc(s->cribuf_len);
|
|
if (s->cribuf == NULL)
|
|
{
|
|
ret = -errno;
|
|
_err("ERROR: cribuf zalloc failed %d\n", ret);
|
|
goto errout;
|
|
}
|
|
#endif
|
|
|
|
/* Initialize lock */
|
|
|
|
ret = pthread_mutex_init(&s->lock, NULL);
|
|
if (ret != 0)
|
|
{
|
|
_err("ERROR: pthread_mutex_init failed %d\n", errno);
|
|
goto errout;
|
|
}
|
|
|
|
/* Reset stream buffer */
|
|
|
|
nxscope_stream_reset(s);
|
|
|
|
/* Initialize info data */
|
|
|
|
s->cmninfo.chmax = cfg->channels;
|
|
|
|
s->cmninfo.flags = 0;
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_DIVIDER
|
|
s->cmninfo.flags |= NXSCOPE_FLAGS_DIVIDER_SUPPORT;
|
|
#endif
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_ACKFRAMES
|
|
s->cmninfo.flags |= NXSCOPE_FLAGS_ACK_SUPPORT;
|
|
#endif
|
|
|
|
s->cmninfo.rx_padding = cfg->rx_padding;
|
|
|
|
/* Initialize channels */
|
|
|
|
for (i = 0; i < cfg->channels; i++)
|
|
{
|
|
s->chinfo[i].name = "\0";
|
|
}
|
|
|
|
return OK;
|
|
|
|
errout:
|
|
|
|
if (s->streambuf != NULL)
|
|
{
|
|
free(s->streambuf);
|
|
}
|
|
|
|
if (s->chinfo != NULL)
|
|
{
|
|
free(s->chinfo);
|
|
}
|
|
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_DIVIDER
|
|
if (s->cntr != NULL)
|
|
{
|
|
free(s->cntr);
|
|
}
|
|
#endif
|
|
|
|
if (s->rxbuf != NULL)
|
|
{
|
|
free(s->rxbuf);
|
|
}
|
|
|
|
if (s->txbuf != NULL)
|
|
{
|
|
free(s->txbuf);
|
|
}
|
|
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_CRICHANNELS
|
|
if (s->cribuf != NULL)
|
|
{
|
|
free(s->cribuf);
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_deinit
|
|
*
|
|
* Description:
|
|
* De-initialize a nxscope instance
|
|
*
|
|
* Input Parameters:
|
|
* s - a pointer to a nxscope instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
void nxscope_deinit(FAR struct nxscope_s *s)
|
|
{
|
|
DEBUGASSERT(s);
|
|
|
|
/* Free mutex */
|
|
|
|
pthread_mutex_destroy(&s->lock);
|
|
|
|
/* Free allocated memory */
|
|
|
|
if (s->streambuf != NULL)
|
|
{
|
|
free(s->streambuf);
|
|
}
|
|
|
|
if (s->chinfo != NULL)
|
|
{
|
|
free(s->chinfo);
|
|
}
|
|
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_DIVIDER
|
|
if (s->cntr != NULL)
|
|
{
|
|
free(s->cntr);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_LOGGING_NXSCOPE_CRICHANNELS
|
|
if (s->cribuf != NULL)
|
|
{
|
|
free(s->cribuf);
|
|
}
|
|
#endif
|
|
|
|
if (s->txbuf != NULL)
|
|
{
|
|
free(s->txbuf);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_lock
|
|
*
|
|
* Description:
|
|
* Lock a nxscope instance
|
|
*
|
|
* Input Parameters:
|
|
* s - a pointer to a nxscope instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
void nxscope_lock(FAR struct nxscope_s *s)
|
|
{
|
|
DEBUGASSERT(s);
|
|
|
|
pthread_mutex_lock(&s->lock);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_unlock
|
|
*
|
|
* Description:
|
|
* Unlock a nxscope instance
|
|
*
|
|
* Input Parameters:
|
|
* s - a pointer to a nxscope instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
void nxscope_unlock(FAR struct nxscope_s *s)
|
|
{
|
|
DEBUGASSERT(s);
|
|
|
|
pthread_mutex_unlock(&s->lock);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_stream
|
|
*
|
|
* Description:
|
|
* Send nxscope stream data.
|
|
*
|
|
* NOTE: It's the user's responsibility to periodically call this function.
|
|
*
|
|
* Input Parameters:
|
|
* s - a pointer to a nxscope instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
int nxscope_stream(FAR struct nxscope_s *s)
|
|
{
|
|
int ret = OK;
|
|
|
|
DEBUGASSERT(s);
|
|
|
|
nxscope_lock(s);
|
|
|
|
/* Do nothing if stream not started */
|
|
|
|
if (!s->start)
|
|
{
|
|
ret = OK;
|
|
goto errout;
|
|
}
|
|
|
|
/* Do nothing if no data */
|
|
|
|
if (nxscope_stream_empty(s))
|
|
{
|
|
goto errout;
|
|
}
|
|
|
|
/* Send stream data */
|
|
|
|
ret = nxscope_stream_send(s, s->streambuf, &s->stream_i);
|
|
if (ret < 0)
|
|
{
|
|
_err("ERROR: nxscope_stream_send failed %d\n", ret);
|
|
goto errout;
|
|
}
|
|
|
|
/* Reset stream buffer */
|
|
|
|
nxscope_stream_reset(s);
|
|
|
|
errout:
|
|
nxscope_unlock(s);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_recv
|
|
*
|
|
* Description:
|
|
* Receive and handle nxscope protocol data.
|
|
*
|
|
* NOTE: It's the user's responsibility to periodically call this function.
|
|
*
|
|
* Input Parameters:
|
|
* s - a pointer to a nxscope instance
|
|
*
|
|
****************************************************************************/
|
|
|
|
int nxscope_recv(FAR struct nxscope_s *s)
|
|
{
|
|
int ret = OK;
|
|
size_t size_left = 0;
|
|
size_t bytes_left = 0;
|
|
struct nxscope_frame_s frame;
|
|
|
|
DEBUGASSERT(s);
|
|
|
|
do
|
|
{
|
|
/* Accumulate data until buffer empty */
|
|
|
|
size_left = s->rxbuf_len - s->rxbuf_i;
|
|
ret = INTF_RECV(s, s->intf_cmd, &s->rxbuf[s->rxbuf_i], size_left);
|
|
if (ret <= 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
s->rxbuf_i += ret;
|
|
}
|
|
while (1);
|
|
|
|
/* Return if no data */
|
|
|
|
if (s->rxbuf_i == 0)
|
|
{
|
|
goto errout;
|
|
}
|
|
|
|
/* Get frame */
|
|
|
|
ret = PROTO_FRAME_GET(s, s->proto_cmd, s->rxbuf, s->rxbuf_i, &frame);
|
|
if (ret < 0)
|
|
{
|
|
/* Do not pass err further */
|
|
|
|
ret = OK;
|
|
goto errout;
|
|
}
|
|
|
|
/* Handle frame */
|
|
|
|
ret = nxscope_recv_handle(s, frame.id, frame.dlen, frame.data);
|
|
if (ret < 0)
|
|
{
|
|
_err("ERROR: nxscope_recv_handle failed %d\n", ret);
|
|
goto errout;
|
|
}
|
|
|
|
/* Keep the remaining data */
|
|
|
|
bytes_left = s->rxbuf_i - frame.drop;
|
|
if (bytes_left > 0)
|
|
{
|
|
memmove(s->rxbuf, &s->rxbuf[frame.drop], bytes_left);
|
|
}
|
|
|
|
errout:
|
|
s->rxbuf_i = bytes_left;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxscope_stream_start
|
|
*
|
|
* Description:
|
|
* Start/stop data stream
|
|
*
|
|
* Input Parameters:
|
|
* s - a pointer to a nxscope instance
|
|
* start - start/stop
|
|
*
|
|
****************************************************************************/
|
|
|
|
int nxscope_stream_start(FAR struct nxscope_s *s, bool start)
|
|
{
|
|
int ret = OK;
|
|
|
|
DEBUGASSERT(s);
|
|
|
|
nxscope_lock(s);
|
|
|
|
_info("force stream_start=%d\n", start);
|
|
ret = nxscope_start_set(s, start);
|
|
|
|
nxscope_unlock(s);
|
|
|
|
return ret;
|
|
}
|