can-utils/isobusfs/isobusfs_cli_cm.c

242 lines
6.2 KiB
C

// SPDX-License-Identifier: LGPL-2.0-only
// SPDX-FileCopyrightText: 2023 Oleksij Rempel <linux@rempel-privat.de>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "isobusfs_cli.h"
#include "isobusfs_cmn_cm.h"
int isobusfs_cli_volume_status_req(struct isobusfs_priv *priv,
uint8_t volume_mode,
uint16_t path_name_length,
const char *volume_name)
{
struct isobusfs_cm_vol_stat_req req;
size_t req_size;
int ret;
req.fs_function =
isobusfs_cg_function_to_buf(ISOBUSFS_CG_CONNECTION_MANAGMENT,
ISOBUSFS_CM_VOLUME_STATUS_REQ);
req.volume_mode = volume_mode;
req.name_len = htole16(path_name_length);
req_size = sizeof(req) - sizeof(req.name) + path_name_length;
memcpy(req.name, volume_name, path_name_length);
ret = isobusfs_send(priv->sock_main, &req, req_size, &priv->tx_buf_log);
if (ret < 0) {
ret = -errno;
pr_warn("failed to send volume status request: %d (%s)", ret, strerror(ret));
return ret;
}
priv->state = ISOBUSFS_CLI_STATE_WAIT_VOLUME_STATUS;
pr_debug("> tx: volume status request");
return 0;
}
int isobusfs_cli_property_req(struct isobusfs_priv *priv)
{
uint8_t buf[ISOBUSFS_MIN_TRANSFER_LENGH];
int ret;
/* not used space should be filled with 0xff */
memset(buf, 0xff, ARRAY_SIZE(buf));
buf[0] = isobusfs_cg_function_to_buf(ISOBUSFS_CG_CONNECTION_MANAGMENT,
ISOBUSFS_CM_GET_FS_PROPERTIES);
/* send property request */
ret = isobusfs_send(priv->sock_main, buf, sizeof(buf), &priv->tx_buf_log);
if (ret < 0)
return ret;
priv->state = ISOBUSFS_CLI_STATE_WAIT_FS_PROPERTIES;
pr_debug("> tx: FS property request");
return 0;
}
/* ccm section */
void isobusfs_cli_ccm_init(struct isobusfs_priv *priv)
{
struct isobusfs_cm_ccm *ccm = &priv->ccm;
ccm->fs_function =
isobusfs_cg_function_to_buf(ISOBUSFS_CG_CONNECTION_MANAGMENT,
ISOBUSFS_CM_F_FS_STATUS);
ccm->version = 2;
memset(ccm->reserved, 0xFF, sizeof(ccm->reserved));
}
/**
* isobusfs_cli_ccm_send - send periodic file server status messages
* @priv: pointer to the isobusfs_priv structure
*
* Returns 0 on success, -1 on errors.
*/
int isobusfs_cli_ccm_send(struct isobusfs_priv *priv)
{
int64_t time_diff;
int ret;
/* Test if it is proper time to send next status message. */
time_diff = timespec_diff_ms(&priv->cmn.next_send_time,
&priv->cmn.last_time);
if (time_diff > ISOBUSFS_CM_F_FS_STATUS_RATE_JITTER) {
/* too early to send next message */
return 0;
}
if (time_diff < -ISOBUSFS_CM_F_FS_STATUS_RATE_JITTER) {
pr_warn("too late to send next fs status message: %ld ms",
time_diff);
}
/* Make sure we send the message with the latest stats */
if (priv->stats.tskey_sch != priv->stats.tskey_ack)
pr_warn("previous message was not acked");
/* send periodic file servers status messages. */
ret = isobusfs_send(priv->sock_ccm, &priv->ccm, sizeof(priv->ccm),
&priv->tx_buf_log);
if (ret < 0) {
pr_err("sendto() failed: %d (%s)", ret, strerror(ret));
return ret;
}
pr_debug("> tx: ccm version: %d", priv->ccm.version);
priv->cmn.next_send_time = priv->cmn.last_time;
timespec_add_ms(&priv->cmn.next_send_time, 2000);
return 0;
}
/* detect if FS is timeout */
void isobusfs_cli_fs_detect_timeout(struct isobusfs_priv *priv)
{
int64_t time_diff;
if (!priv->fs_is_active)
return;
time_diff = timespec_diff_ms(&priv->cmn.last_time,
&priv->fs_last_seen);
if (time_diff > ISOBUSFS_FS_TIMEOUT) {
pr_debug("file server timeout");
priv->fs_is_active = false;
}
}
/* activate FS status if was not active till now */
static void isobusfs_cli_fs_activate(struct isobusfs_priv *priv)
{
if (priv->fs_is_active)
return;
pr_debug("file server detectet");
priv->fs_is_active = true;
}
static int isobusfs_cli_rx_fs_status(struct isobusfs_priv *priv,
struct isobusfs_msg *msg)
{
struct isobusfs_cm_fss *fs_status = (void *)msg->buf;
int ret = 0;
if (msg->len != sizeof(*fs_status)) {
pr_warn("wrong message length: %d", msg->len);
return -EINVAL;
}
isobusfs_cli_fs_activate(priv);
priv->fs_last_seen = priv->cmn.last_time;
pr_debug("< rx: fs status: %x, opened files: %d",
fs_status->status, fs_status->num_open_files);
return ret;
}
/* process FS properties response */
static int isobusfs_cli_rx_fs_property_res(struct isobusfs_priv *priv,
struct isobusfs_msg *msg)
{
struct isobusfs_cm_get_fs_props_resp *fs_prop = (void *)msg->buf;
int ret = 0;
if (priv->state != ISOBUSFS_CLI_STATE_WAIT_FS_PROPERTIES) {
pr_warn("unexpected fs properties response");
return -EINVAL;
}
if (msg->len != sizeof(*fs_prop)) {
pr_warn("wrong message length: %d", msg->len);
return -EINVAL;
}
priv->fs_version = fs_prop->version_number;
priv->fs_max_open_files = fs_prop->max_open_files;
priv->fs_caps = fs_prop->fs_capabilities;
pr_debug("< rx: fs properties: version: %d, max open files: %d, caps: %x",
priv->fs_version, priv->fs_max_open_files, priv->fs_caps);
priv->state = ISOBUSFS_CLI_STATE_GET_FS_PROPERTIES_DONE;
return ret;
}
/* function to handle ISOBUSFS_CM_VOLUME_STATUS_RES */
static int isobusfs_cli_rx_volume_status_res(struct isobusfs_priv *priv,
struct isobusfs_msg *msg)
{
struct isobusfs_cm_vol_stat_res *vol_status = (void *)msg->buf;
int ret = 0;
if (priv->state != ISOBUSFS_CLI_STATE_WAIT_VOLUME_STATUS) {
pr_warn("unexpected volume status response");
return -EINVAL;
}
pr_debug("< rx: volume status: %x, max time before remove %d, error code %d, path name length %d, name %s",
vol_status->volume_status,
vol_status->max_time_before_removal,
vol_status->error_code,
vol_status->name_len,
vol_status->name);
priv->state = ISOBUSFS_CLI_STATE_VOLUME_STATUS_DONE;
return ret;
}
/* Command group: connection management */
int isobusfs_cli_rx_cg_cm(struct isobusfs_priv *priv, struct isobusfs_msg *msg)
{
int func = isobusfs_buf_to_function(msg->buf);
int ret = 0;
switch (func) {
case ISOBUSFS_CM_F_FS_STATUS:
return isobusfs_cli_rx_fs_status(priv, msg);
case ISOBUSFS_CM_GET_FS_PROPERTIES_RES:
return isobusfs_cli_rx_fs_property_res(priv, msg);
case ISOBUSFS_CM_VOLUME_STATUS_RES:
return isobusfs_cli_rx_volume_status_res(priv, msg);
default:
pr_warn("unsupported function: %i", func);
return -EINVAL;
}
return ret;
}