+#include <string.h>
+
+#include <rte_common.h>
+
+#include "tf_session.h"
+#include "tf_common.h"
+#include "tf_msg.h"
+#include "tfp.h"
+
+struct tf_session_client_create_parms {
+ /**
+ * [in] Pointer to the control channel name string
+ */
+ char *ctrl_chan_name;
+
+ /**
+ * [out] Firmware Session Client ID
+ */
+ union tf_session_client_id *session_client_id;
+};
+
+struct tf_session_client_destroy_parms {
+ /**
+ * FW Session Client Identifier
+ */
+ union tf_session_client_id session_client_id;
+};
+
+/**
+ * Creates a Session and the associated client.
+ *
+ * [in] tfp
+ * Pointer to TF handle
+ *
+ * [in] parms
+ * Pointer to session client create parameters
+ *
+ * Returns
+ * - (0) if successful.
+ * - (-EINVAL) on failure.
+ * - (-ENOMEM) if max session clients has been reached.
+ */
+static int
+tf_session_create(struct tf *tfp,
+ struct tf_session_open_session_parms *parms)
+{
+ int rc;
+ struct tf_session *session = NULL;
+ struct tf_session_client *client;
+ struct tfp_calloc_parms cparms;
+ uint8_t fw_session_id;
+ uint8_t fw_session_client_id;
+ union tf_session_id *session_id;
+
+ TF_CHECK_PARMS2(tfp, parms);
+
+ /* Open FW session and get a new session_id */
+ rc = tf_msg_session_open(tfp,
+ parms->open_cfg->ctrl_chan_name,
+ &fw_session_id,
+ &fw_session_client_id);
+ if (rc) {
+ /* Log error */
+ if (rc == -EEXIST)
+ TFP_DRV_LOG(ERR,
+ "Session is already open, rc:%s\n",
+ strerror(-rc));
+ else
+ TFP_DRV_LOG(ERR,
+ "Open message send failed, rc:%s\n",
+ strerror(-rc));
+
+ parms->open_cfg->session_id.id = TF_FW_SESSION_ID_INVALID;
+ return rc;
+ }
+
+ /* Allocate session */
+ cparms.nitems = 1;
+ cparms.size = sizeof(struct tf_session_info);
+ cparms.alignment = 0;
+ rc = tfp_calloc(&cparms);
+ if (rc) {
+ /* Log error */
+ TFP_DRV_LOG(ERR,
+ "Failed to allocate session info, rc:%s\n",
+ strerror(-rc));
+ goto cleanup;
+ }
+ tfp->session = (struct tf_session_info *)cparms.mem_va;
+
+ /* Allocate core data for the session */
+ cparms.nitems = 1;
+ cparms.size = sizeof(struct tf_session);
+ cparms.alignment = 0;
+ rc = tfp_calloc(&cparms);
+ if (rc) {
+ /* Log error */
+ TFP_DRV_LOG(ERR,
+ "Failed to allocate session data, rc:%s\n",
+ strerror(-rc));
+ goto cleanup;
+ }
+ tfp->session->core_data = cparms.mem_va;
+ session_id = &parms->open_cfg->session_id;
+
+ /* Update Session Info, which is what is visible to the caller */
+ tfp->session->ver.major = 0;
+ tfp->session->ver.minor = 0;
+ tfp->session->ver.update = 0;
+
+ tfp->session->session_id.internal.domain = session_id->internal.domain;
+ tfp->session->session_id.internal.bus = session_id->internal.bus;
+ tfp->session->session_id.internal.device = session_id->internal.device;
+ tfp->session->session_id.internal.fw_session_id = fw_session_id;
+
+ /* Initialize Session and Device, which is private */
+ session = (struct tf_session *)tfp->session->core_data;
+ session->ver.major = 0;
+ session->ver.minor = 0;
+ session->ver.update = 0;
+
+ session->session_id.internal.domain = session_id->internal.domain;
+ session->session_id.internal.bus = session_id->internal.bus;
+ session->session_id.internal.device = session_id->internal.device;
+ session->session_id.internal.fw_session_id = fw_session_id;
+ /* Return the allocated session id */
+ session_id->id = session->session_id.id;
+
+ session->shadow_copy = parms->open_cfg->shadow_copy;
+
+ /* Init session client list */
+ ll_init(&session->client_ll);
+
+ /* Create the local session client, initialize and attach to
+ * the session
+ */
+ cparms.nitems = 1;
+ cparms.size = sizeof(struct tf_session_client);
+ cparms.alignment = 0;
+ rc = tfp_calloc(&cparms);
+ if (rc) {
+ /* Log error */
+ TFP_DRV_LOG(ERR,
+ "Failed to allocate session client, rc:%s\n",
+ strerror(-rc));
+ goto cleanup;
+ }
+ client = cparms.mem_va;
+
+ /* Register FID with the client */
+ rc = tfp_get_fid(tfp, &client->fw_fid);
+ if (rc)
+ return rc;
+
+ client->session_client_id.internal.fw_session_id = fw_session_id;
+ client->session_client_id.internal.fw_session_client_id =
+ fw_session_client_id;
+
+ tfp_memcpy(client->ctrl_chan_name,
+ parms->open_cfg->ctrl_chan_name,
+ TF_SESSION_NAME_MAX);
+
+ ll_insert(&session->client_ll, &client->ll_entry);
+ session->ref_count++;
+
+ rc = tf_dev_bind(tfp,
+ parms->open_cfg->device_type,
+ session->shadow_copy,
+ &parms->open_cfg->resources,
+ &session->dev);
+ /* Logging handled by dev_bind */
+ if (rc)
+ return rc;
+
+ session->dev_init = true;
+
+ return 0;
+
+ cleanup:
+ tfp_free(tfp->session->core_data);
+ tfp_free(tfp->session);
+ tfp->session = NULL;
+ return rc;
+}
+
+/**
+ * Creates a Session Client on an existing Session.
+ *
+ * [in] tfp
+ * Pointer to TF handle
+ *
+ * [in] parms
+ * Pointer to session client create parameters
+ *
+ * Returns
+ * - (0) if successful.
+ * - (-EINVAL) on failure.
+ * - (-ENOMEM) if max session clients has been reached.
+ */
+static int
+tf_session_client_create(struct tf *tfp,
+ struct tf_session_client_create_parms *parms)
+{
+ int rc;
+ struct tf_session *session = NULL;
+ struct tf_session_client *client;
+ struct tfp_calloc_parms cparms;
+ union tf_session_client_id session_client_id;
+
+ TF_CHECK_PARMS2(tfp, parms);
+
+ /* Using internal version as session client may not exist yet */
+ rc = tf_session_get_session_internal(tfp, &session);
+ if (rc) {
+ TFP_DRV_LOG(ERR,
+ "Failed to lookup session, rc:%s\n",
+ strerror(-rc));
+ return rc;
+ }
+
+ client = tf_session_find_session_client_by_name(session,
+ parms->ctrl_chan_name);
+ if (client) {
+ TFP_DRV_LOG(ERR,
+ "Client %s, already registered with this session\n",
+ parms->ctrl_chan_name);
+ return -EOPNOTSUPP;
+ }
+
+ rc = tf_msg_session_client_register
+ (tfp,
+ parms->ctrl_chan_name,
+ &session_client_id.internal.fw_session_client_id);
+ if (rc) {
+ TFP_DRV_LOG(ERR,
+ "Failed to create client on session, rc:%s\n",
+ strerror(-rc));
+ return rc;
+ }
+
+ /* Create the local session client, initialize and attach to
+ * the session
+ */
+ cparms.nitems = 1;
+ cparms.size = sizeof(struct tf_session_client);
+ cparms.alignment = 0;
+ rc = tfp_calloc(&cparms);
+ if (rc) {
+ TFP_DRV_LOG(ERR,
+ "Failed to allocate session client, rc:%s\n",
+ strerror(-rc));
+ goto cleanup;
+ }
+ client = cparms.mem_va;
+
+ /* Register FID with the client */
+ rc = tfp_get_fid(tfp, &client->fw_fid);
+ if (rc)
+ return rc;
+
+ /* Build the Session Client ID by adding the fw_session_id */
+ rc = tf_session_get_fw_session_id
+ (tfp,
+ &session_client_id.internal.fw_session_id);
+ if (rc) {
+ TFP_DRV_LOG(ERR,
+ "Session Firmware id lookup failed, rc:%s\n",
+ strerror(-rc));
+ return rc;
+ }
+
+ tfp_memcpy(client->ctrl_chan_name,
+ parms->ctrl_chan_name,
+ TF_SESSION_NAME_MAX);
+
+ client->session_client_id.id = session_client_id.id;
+
+ ll_insert(&session->client_ll, &client->ll_entry);
+
+ session->ref_count++;
+
+ /* Build the return value */
+ parms->session_client_id->id = session_client_id.id;
+
+ cleanup:
+ /* TBD - Add code to unregister newly create client from fw */
+
+ return rc;
+}
+
+
+/**
+ * Destroys a Session Client on an existing Session.
+ *
+ * [in] tfp
+ * Pointer to TF handle
+ *
+ * [in] parms
+ * Pointer to the session client destroy parameters
+ *
+ * Returns
+ * - (0) if successful.
+ * - (-EINVAL) on failure.
+ * - (-ENOTFOUND) error, client not owned by the session.
+ * - (-ENOTSUPP) error, unable to destroy client as its the last
+ * client. Please use the tf_session_close().
+ */
+static int
+tf_session_client_destroy(struct tf *tfp,
+ struct tf_session_client_destroy_parms *parms)
+{
+ int rc;
+ struct tf_session *tfs;
+ struct tf_session_client *client;
+
+ TF_CHECK_PARMS2(tfp, parms);
+
+ rc = tf_session_get_session(tfp, &tfs);
+ if (rc) {
+ TFP_DRV_LOG(ERR,
+ "Failed to lookup session, rc:%s\n",
+ strerror(-rc));
+ return rc;
+ }
+
+ /* Check session owns this client and that we're not the last client */
+ client = tf_session_get_session_client(tfs,
+ parms->session_client_id);
+ if (client == NULL) {
+ TFP_DRV_LOG(ERR,
+ "Client %d, not found within this session\n",
+ parms->session_client_id.id);
+ return -EINVAL;
+ }
+
+ /* If last client the request is rejected and cleanup should
+ * be done by session close.
+ */
+ if (tfs->ref_count == 1)
+ return -EOPNOTSUPP;
+
+ rc = tf_msg_session_client_unregister
+ (tfp,
+ parms->session_client_id.internal.fw_session_client_id);
+
+ /* Log error, but continue. If FW fails we do not really have
+ * a way to fix this but the client would no longer be valid
+ * thus we remove from the session.
+ */
+ if (rc) {
+ TFP_DRV_LOG(ERR,
+ "Client destroy on FW Failed, rc:%s\n",
+ strerror(-rc));
+ }
+
+ ll_delete(&tfs->client_ll, &client->ll_entry);
+
+ /* Decrement the session ref_count */
+ tfs->ref_count--;
+
+ tfp_free(client);
+
+ return rc;
+}
+