root/usr/src/lib/fm/topo/modules/common/shared/topo_port.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright (c) 2019, Joyent, Inc.
 */

#include <sys/fm/protocol.h>
#include <fm/topo_mod.h>
#include <fm/topo_list.h>
#include <fm/topo_method.h>

#include <topo_port.h>

/*
 * Common routines to create port entries in the topology tree.
 */

static const topo_pgroup_info_t port_pgroup = {
        TOPO_PGROUP_PORT,
        TOPO_STABILITY_PRIVATE,
        TOPO_STABILITY_PRIVATE,
        1
};

static const topo_method_t port_methods[] = {
        { TOPO_METH_OCCUPIED, TOPO_METH_OCCUPIED_DESC,
            TOPO_METH_OCCUPIED_VERSION, TOPO_STABILITY_INTERNAL,
            topo_mod_hc_occupied },
        { NULL }
};

int
port_range_create(topo_mod_t *mod, tnode_t *pnode, topo_instance_t min,
    topo_instance_t max)
{
        return (topo_node_range_create(mod, pnode, PORT, min, max));
}

/*
 * Create a port node, specifying the type of port it is. This will create the
 * common port property group and populate it. The caller will need to populate
 * the port-specific property group as needed.
 */
static tnode_t *
port_create_common(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst,
    const char *type)
{
        int err;
        tnode_t *tn = NULL;
        nvlist_t *fmri = NULL, *auth = NULL, *presource = NULL;

        if (type == NULL) {
                topo_mod_dprintf(mod, "port_create_common missing type "
                    "argument\n");
                goto error;
        }

        if ((auth = topo_mod_auth(mod, pnode)) == NULL) {
                topo_mod_dprintf(mod, "topo_mod_auth() failed: %s\n",
                    topo_mod_errmsg(mod));
                goto error;
        }

        if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, PORT,
            inst, NULL, auth, NULL, NULL, NULL)) == NULL) {
                topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s\n",
                    topo_mod_errmsg(mod));
                goto error;
        }

        if ((tn = topo_node_bind(mod, pnode, PORT, inst, fmri)) == NULL) {
                topo_mod_dprintf(mod, "topo_node_bind() failed: %s\n",
                    topo_mod_errmsg(mod));
                goto error;
        }

        /*
         * The FRU is always set to the FMRI of the parent device for a port.
         */
        if (topo_node_resource(pnode, &presource, &err) != 0) {
                topo_mod_dprintf(mod, "topo_node_resource() failed: %s\n",
                    topo_strerror(err));
                goto error;
        }

        if (topo_node_fru_set(tn, presource, 0, &err) != 0) {
                topo_mod_dprintf(mod, "topo_node_fru_set() failed: %s\n",
                    topo_strerror(err));
                goto error;
        }

        if (topo_pgroup_create(tn, &port_pgroup, &err) != 0) {
                topo_mod_dprintf(mod, "failed to create property group %s: "
                    "%s\n", TOPO_PGROUP_PORT, topo_strerror(err));
                goto error;
        }

        if (topo_prop_set_string(tn, TOPO_PGROUP_PORT, TOPO_PROP_PORT_TYPE,
            TOPO_PROP_IMMUTABLE, type, &err) != 0) {
                topo_mod_dprintf(mod, "failed to set %s property: %s\n",
                    TOPO_PROP_PORT_TYPE, topo_strerror(err));
                goto error;
        }

        if (topo_method_register(mod, tn, port_methods) != 0) {
                topo_mod_dprintf(mod, "topo_method_register() failed on "
                    "%s=%" PRIu64 ": %s", PORT, inst, topo_mod_errmsg(mod));
                /* errno set */
                goto error;
        }

        nvlist_free(fmri);
        nvlist_free(auth);
        nvlist_free(presource);
        return (tn);
error:
        topo_node_unbind(tn);
        nvlist_free(fmri);
        nvlist_free(auth);
        nvlist_free(presource);
        return (NULL);
}

int
port_create_sff(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst,
    tnode_t **nodep)
{
        tnode_t *tn;

        tn = port_create_common(mod, pnode, inst, TOPO_PROP_PORT_TYPE_SFF);
        if (tn == NULL)
                return (-1);
        *nodep = tn;
        return (0);
}

int
port_create_usb(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst,
    tnode_t **nodep)
{
        tnode_t *tn;

        tn = port_create_common(mod, pnode, inst, TOPO_PROP_PORT_TYPE_USB);
        if (tn == NULL)
                return (-1);
        *nodep = tn;
        return (0);
}

int
port_create_unknown(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst,
    tnode_t **nodep)
{
        tnode_t *tn;

        tn = port_create_common(mod, pnode, inst, TOPO_PROP_PORT_TYPE_UNKNOWN);
        if (tn == NULL)
                return (-1);
        *nodep = tn;
        return (0);
}