1/*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source.  A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12/*
13 * Copyright (c) 2019, Joyent, Inc.
14 */
15
16#include <sys/fm/protocol.h>
17#include <fm/topo_mod.h>
18#include <fm/topo_list.h>
19#include <fm/topo_method.h>
20
21#include <topo_port.h>
22
23/*
24 * Common routines to create port entries in the topology tree.
25 */
26
27static const topo_pgroup_info_t port_pgroup = {
28	TOPO_PGROUP_PORT,
29	TOPO_STABILITY_PRIVATE,
30	TOPO_STABILITY_PRIVATE,
31	1
32};
33
34static const topo_method_t port_methods[] = {
35	{ TOPO_METH_OCCUPIED, TOPO_METH_OCCUPIED_DESC,
36	    TOPO_METH_OCCUPIED_VERSION, TOPO_STABILITY_INTERNAL,
37	    topo_mod_hc_occupied },
38	{ NULL }
39};
40
41int
42port_range_create(topo_mod_t *mod, tnode_t *pnode, topo_instance_t min,
43    topo_instance_t max)
44{
45	return (topo_node_range_create(mod, pnode, PORT, min, max));
46}
47
48/*
49 * Create a port node, specifying the type of port it is. This will create the
50 * common port property group and populate it. The caller will need to populate
51 * the port-specific property group as needed.
52 */
53static tnode_t *
54port_create_common(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst,
55    const char *type)
56{
57	int err;
58	tnode_t *tn = NULL;
59	nvlist_t *fmri = NULL, *auth = NULL, *presource = NULL;
60
61	if (type == NULL) {
62		topo_mod_dprintf(mod, "port_create_common missing type "
63		    "argument\n");
64		goto error;
65	}
66
67	if ((auth = topo_mod_auth(mod, pnode)) == NULL) {
68		topo_mod_dprintf(mod, "topo_mod_auth() failed: %s\n",
69		    topo_mod_errmsg(mod));
70		goto error;
71	}
72
73	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, PORT,
74	    inst, NULL, auth, NULL, NULL, NULL)) == NULL) {
75		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s\n",
76		    topo_mod_errmsg(mod));
77		goto error;
78	}
79
80	if ((tn = topo_node_bind(mod, pnode, PORT, inst, fmri)) == NULL) {
81		topo_mod_dprintf(mod, "topo_node_bind() failed: %s\n",
82		    topo_mod_errmsg(mod));
83		goto error;
84	}
85
86	/*
87	 * The FRU is always set to the FMRI of the parent device for a port.
88	 */
89	if (topo_node_resource(pnode, &presource, &err) != 0) {
90		topo_mod_dprintf(mod, "topo_node_resource() failed: %s\n",
91		    topo_strerror(err));
92		goto error;
93	}
94
95	if (topo_node_fru_set(tn, presource, 0, &err) != 0) {
96		topo_mod_dprintf(mod, "topo_node_fru_set() failed: %s\n",
97		    topo_strerror(err));
98		goto error;
99	}
100
101	if (topo_pgroup_create(tn, &port_pgroup, &err) != 0) {
102		topo_mod_dprintf(mod, "failed to create property group %s: "
103		    "%s\n", TOPO_PGROUP_PORT, topo_strerror(err));
104		goto error;
105	}
106
107	if (topo_prop_set_string(tn, TOPO_PGROUP_PORT, TOPO_PROP_PORT_TYPE,
108	    TOPO_PROP_IMMUTABLE, type, &err) != 0) {
109		topo_mod_dprintf(mod, "failed to set %s property: %s\n",
110		    TOPO_PROP_PORT_TYPE, topo_strerror(err));
111		goto error;
112	}
113
114	if (topo_method_register(mod, tn, port_methods) != 0) {
115		topo_mod_dprintf(mod, "topo_method_register() failed on "
116		    "%s=%d: %s", PORT, inst, topo_mod_errmsg(mod));
117		/* errno set */
118		goto error;
119	}
120
121	nvlist_free(fmri);
122	nvlist_free(auth);
123	nvlist_free(presource);
124	return (tn);
125error:
126	topo_node_unbind(tn);
127	nvlist_free(fmri);
128	nvlist_free(auth);
129	nvlist_free(presource);
130	return (NULL);
131}
132
133int
134port_create_sff(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst,
135    tnode_t **nodep)
136{
137	tnode_t *tn;
138
139	tn = port_create_common(mod, pnode, inst, TOPO_PROP_PORT_TYPE_SFF);
140	if (tn == NULL)
141		return (-1);
142	*nodep = tn;
143	return (0);
144}
145
146int
147port_create_usb(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst,
148    tnode_t **nodep)
149{
150	tnode_t *tn;
151
152	tn = port_create_common(mod, pnode, inst, TOPO_PROP_PORT_TYPE_USB);
153	if (tn == NULL)
154		return (-1);
155	*nodep = tn;
156	return (0);
157}
158