// SPDX-License-Identifier: LGPL-2.0-only // SPDX-FileCopyrightText: 2023 Oleksij Rempel #include #include #include #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; }