xref: /onnv-gate/usr/src/lib/fm/topo/libtopo/common/dev.c (revision 4198:6bdfb19526db)
11541Stimh /*
21541Stimh  * CDDL HEADER START
31541Stimh  *
41541Stimh  * The contents of this file are subject to the terms of the
51541Stimh  * Common Development and Distribution License (the "License").
61541Stimh  * You may not use this file except in compliance with the License.
71541Stimh  *
81541Stimh  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91541Stimh  * or http://www.opensolaris.org/os/licensing.
101541Stimh  * See the License for the specific language governing permissions
111541Stimh  * and limitations under the License.
121541Stimh  *
131541Stimh  * When distributing Covered Code, include this CDDL HEADER in each
141541Stimh  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151541Stimh  * If applicable, add the following below this CDDL HEADER, with the
161541Stimh  * fields enclosed by brackets "[]" replaced with your own identifying
171541Stimh  * information: Portions Copyright [yyyy] [name of copyright owner]
181541Stimh  *
191541Stimh  * CDDL HEADER END
201541Stimh  */
211541Stimh 
221541Stimh /*
23*4198Seschrock  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
241541Stimh  * Use is subject to license terms.
251541Stimh  */
261541Stimh 
271541Stimh #pragma ident	"%Z%%M%	%I%	%E% SMI"
281541Stimh 
291541Stimh #include <limits.h>
301541Stimh #include <strings.h>
311541Stimh #include <string.h>
321541Stimh #include <unistd.h>
331541Stimh #include <stdio.h>
341541Stimh #include <alloca.h>
35*4198Seschrock #include <devid.h>
361541Stimh #include <libnvpair.h>
371541Stimh #include <fm/topo_mod.h>
381541Stimh #include <sys/fm/protocol.h>
391541Stimh 
403062Scindi #include <topo_method.h>
411541Stimh #include <topo_subr.h>
423062Scindi #include <dev.h>
431541Stimh 
441541Stimh static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
453062Scindi     topo_instance_t, void *, void *);
461541Stimh static void dev_release(topo_mod_t *, tnode_t *);
471541Stimh static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
481541Stimh     nvlist_t *, nvlist_t **);
491541Stimh static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
501541Stimh     nvlist_t *, nvlist_t **);
511541Stimh static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
521541Stimh     nvlist_t *, nvlist_t **);
53*4198Seschrock static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
54*4198Seschrock     nvlist_t *, nvlist_t **);
55*4198Seschrock static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
56*4198Seschrock     nvlist_t *, nvlist_t **);
571541Stimh 
581541Stimh static const topo_method_t dev_methods[] = {
591541Stimh 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
601541Stimh 	    TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str },
611541Stimh 	{ TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
621541Stimh 	    TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl },
631541Stimh 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
641541Stimh 	    TOPO_STABILITY_INTERNAL, dev_fmri_create_meth },
65*4198Seschrock 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
66*4198Seschrock 	    TOPO_STABILITY_INTERNAL, dev_fmri_present },
67*4198Seschrock 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
68*4198Seschrock 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
69*4198Seschrock 	    dev_fmri_unusable },
701541Stimh 	{ NULL }
711541Stimh };
721541Stimh 
733062Scindi static const topo_modops_t dev_ops =
743062Scindi 	{ dev_enum, dev_release };
751541Stimh static const topo_modinfo_t dev_info =
763062Scindi 	{ "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops };
771541Stimh 
783062Scindi int
793062Scindi dev_init(topo_mod_t *mod, topo_version_t version)
801541Stimh {
813062Scindi 	if (getenv("TOPOHCDEBUG"))
823062Scindi 		topo_mod_setdebug(mod);
831541Stimh 	topo_mod_dprintf(mod, "initializing dev builtin\n");
841541Stimh 
853062Scindi 	if (version != DEV_VERSION)
863062Scindi 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
873062Scindi 
883062Scindi 	if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) {
891541Stimh 		topo_mod_dprintf(mod, "failed to register dev_info: "
901541Stimh 		    "%s\n", topo_mod_errmsg(mod));
913062Scindi 		return (-1);
921541Stimh 	}
933062Scindi 
943062Scindi 	return (0);
951541Stimh }
961541Stimh 
971541Stimh void
981541Stimh dev_fini(topo_mod_t *mod)
991541Stimh {
1001541Stimh 	topo_mod_unregister(mod);
1011541Stimh }
1021541Stimh 
1031541Stimh /*ARGSUSED*/
1041541Stimh static int
1051541Stimh dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
1063062Scindi     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
1071541Stimh {
1081541Stimh 	(void) topo_method_register(mod, pnode, dev_methods);
1091541Stimh 	return (0);
1101541Stimh }
1111541Stimh 
1121541Stimh static void
1131541Stimh dev_release(topo_mod_t *mod, tnode_t *node)
1141541Stimh {
1151541Stimh 	topo_method_unregister_all(mod, node);
1161541Stimh }
1171541Stimh 
1181541Stimh static ssize_t
1191541Stimh fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
1201541Stimh {
1211541Stimh 	nvlist_t *anvl = NULL;
1221541Stimh 	uint8_t version;
1231541Stimh 	ssize_t size = 0;
1241541Stimh 	char *devid = NULL;
1251541Stimh 	char *devpath = NULL;
1261541Stimh 	char *achas = NULL;
1271541Stimh 	char *adom = NULL;
1281541Stimh 	char *aprod = NULL;
1291541Stimh 	char *asrvr = NULL;
1301541Stimh 	char *ahost = NULL;
1311541Stimh 	int more_auth = 0;
1321541Stimh 	int err;
1331541Stimh 
1341541Stimh 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
1351541Stimh 	    version > FM_DEV_SCHEME_VERSION)
1361541Stimh 		return (-1);
1371541Stimh 
1381541Stimh 	/* Get authority, if present */
1391541Stimh 	err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl);
1401541Stimh 	if (err != 0 && err != ENOENT)
1411541Stimh 		return (-1);
1421541Stimh 
1431541Stimh 	/* Get devid, if present */
1441541Stimh 	err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid);
1451541Stimh 	if (err != 0 && err != ENOENT)
1461541Stimh 		return (-1);
1471541Stimh 
1481541Stimh 	/* There must be a device path present */
1491541Stimh 	err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath);
1501541Stimh 	if (err != 0 || devpath == NULL)
1511541Stimh 		return (-1);
1521541Stimh 
1531541Stimh 	if (anvl != NULL) {
1541541Stimh 		(void) nvlist_lookup_string(anvl,
1551541Stimh 		    FM_FMRI_AUTH_PRODUCT, &aprod);
1561541Stimh 		(void) nvlist_lookup_string(anvl,
1571541Stimh 		    FM_FMRI_AUTH_CHASSIS, &achas);
1581541Stimh 		(void) nvlist_lookup_string(anvl,
1591541Stimh 		    FM_FMRI_AUTH_DOMAIN, &adom);
1601541Stimh 		(void) nvlist_lookup_string(anvl,
1611541Stimh 		    FM_FMRI_AUTH_SERVER, &asrvr);
1621541Stimh 		(void) nvlist_lookup_string(anvl,
1631541Stimh 		    FM_FMRI_AUTH_HOST, &ahost);
1641541Stimh 		if (aprod != NULL)
1651541Stimh 			more_auth++;
1661541Stimh 		if (achas != NULL)
1671541Stimh 			more_auth++;
1681541Stimh 		if (adom != NULL)
1691541Stimh 			more_auth++;
1701541Stimh 		if (asrvr != NULL)
1711541Stimh 			more_auth++;
1721541Stimh 		if (ahost != NULL)
1731541Stimh 			more_auth++;
1741541Stimh 	}
1751541Stimh 
1761541Stimh 	/* dev:// */
1771541Stimh 	topo_fmristr_build(&size,
1781541Stimh 	    buf, buflen, FM_FMRI_SCHEME_DEV, NULL, "://");
1791541Stimh 
1801541Stimh 	/* authority, if any */
1811541Stimh 	if (aprod != NULL)
1821541Stimh 		topo_fmristr_build(&size,
1831541Stimh 		    buf, buflen, aprod, FM_FMRI_AUTH_PRODUCT "=",
1841541Stimh 		    --more_auth > 0 ? "," : NULL);
1851541Stimh 	if (achas != NULL)
1861541Stimh 		topo_fmristr_build(&size,
1871541Stimh 		    buf, buflen, achas, FM_FMRI_AUTH_CHASSIS "=",
1881541Stimh 		    --more_auth > 0 ? "," : NULL);
1891541Stimh 	if (adom != NULL)
1901541Stimh 		topo_fmristr_build(&size,
1911541Stimh 		    buf, buflen, adom, FM_FMRI_AUTH_DOMAIN "=",
1921541Stimh 		    --more_auth > 0 ? "," : NULL);
1931541Stimh 	if (asrvr != NULL)
1941541Stimh 		topo_fmristr_build(&size,
1951541Stimh 		    buf, buflen, asrvr, FM_FMRI_AUTH_SERVER "=",
1961541Stimh 		    --more_auth > 0 ? "," : NULL);
1971541Stimh 	if (ahost != NULL)
1981541Stimh 		topo_fmristr_build(&size,
1991541Stimh 		    buf, buflen, ahost, FM_FMRI_AUTH_HOST "=",
2001541Stimh 		    NULL);
2011541Stimh 
2021541Stimh 	/* device-id part, topo_fmristr_build does nothing if devid is NULL */
2031541Stimh 	topo_fmristr_build(&size,
2041541Stimh 	    buf, buflen, devid, "/:" FM_FMRI_DEV_ID "=", NULL);
2051541Stimh 
2061541Stimh 	/* device-path part */
2071541Stimh 	topo_fmristr_build(&size, buf, buflen, devpath, "/", NULL);
2081541Stimh 
2091541Stimh 	return (size);
2101541Stimh }
2111541Stimh 
2121541Stimh /*ARGSUSED*/
2131541Stimh static int
2141541Stimh dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
2151541Stimh     nvlist_t *nvl, nvlist_t **out)
2161541Stimh {
2171541Stimh 	ssize_t len;
2181541Stimh 	char *name = NULL;
2191541Stimh 	nvlist_t *fmristr;
2201541Stimh 
2211541Stimh 	if (version > TOPO_METH_NVL2STR_VERSION)
2221541Stimh 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
2231541Stimh 
2241541Stimh 	if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 ||
2251541Stimh 	    (name = topo_mod_alloc(mod, len + 1)) == NULL ||
2261541Stimh 	    fmri_nvl2str(nvl, name, len + 1) == 0) {
2271541Stimh 		if (name != NULL)
2281541Stimh 			topo_mod_free(mod, name, len + 1);
2291541Stimh 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2301541Stimh 	}
2311541Stimh 
2321541Stimh 	if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0)
2331541Stimh 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2341541Stimh 	if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
2351541Stimh 		topo_mod_free(mod, name, len + 1);
2361541Stimh 		nvlist_free(fmristr);
2371541Stimh 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2381541Stimh 	}
2391541Stimh 	topo_mod_free(mod, name, len + 1);
2401541Stimh 	*out = fmristr;
2411541Stimh 
2421541Stimh 	return (0);
2431541Stimh }
2441541Stimh 
2451541Stimh /*ARGSUSED*/
2461541Stimh static int
2471541Stimh dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
2481541Stimh     nvlist_t *in, nvlist_t **out)
2491541Stimh {
2501541Stimh 	nvlist_t *fmri;
2511541Stimh 	char *devpath;
2521541Stimh 	char *devid = NULL;
2531541Stimh 	char *str;
2541541Stimh 	int err;
2551541Stimh 
2561541Stimh 	if (version > TOPO_METH_STR2NVL_VERSION)
2571541Stimh 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
2581541Stimh 
2591541Stimh 	if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
2601541Stimh 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2611541Stimh 
2621541Stimh 	/* We're expecting a string version of a dev scheme FMRI */
2631541Stimh 	if (strncmp(str, "dev:///", 7) != 0)
2641541Stimh 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
2651541Stimh 
2661541Stimh 	devpath = str + 7;
2671541Stimh 	if (strncmp(devpath, ":" FM_FMRI_DEV_ID "=", 7) == 0) {
2681541Stimh 		char *n;
2691541Stimh 		int len;
2701541Stimh 
2711541Stimh 		n = strchr(devpath + 7, '/');
2721541Stimh 		if (n == NULL)
2731541Stimh 			return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
2741541Stimh 		len = n - (devpath + 7);
2751541Stimh 		devid = alloca(len + 1);
2761541Stimh 		(void) memcpy(devid, devpath + 7, len);
2771541Stimh 		devid[len] = 0;
2781541Stimh 		devpath = n + 1;
2791541Stimh 	}
2801541Stimh 
2811541Stimh 	/* the device-path should start with a slash */
2821541Stimh 	if (*devpath != '/')
2831541Stimh 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
2841541Stimh 
2851541Stimh 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
2861541Stimh 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2871541Stimh 
2881541Stimh 	err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION);
2891541Stimh 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
2901541Stimh 	err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath);
2911541Stimh 	if (devid != NULL)
2921541Stimh 		err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid);
2931541Stimh 
2941541Stimh 	if (err != 0) {
2951541Stimh 		nvlist_free(fmri);
2961541Stimh 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2971541Stimh 	}
2981541Stimh 	*out = fmri;
2991541Stimh 
3001541Stimh 	return (0);
3011541Stimh }
3021541Stimh 
303*4198Seschrock /*
304*4198Seschrock  * callback routine for di_walk_minor()
305*4198Seschrock  */
306*4198Seschrock struct walkinfo {
307*4198Seschrock 	int matched;
308*4198Seschrock 	const char *path;
309*4198Seschrock 	const char *devid;
310*4198Seschrock 	int len;
311*4198Seschrock };
312*4198Seschrock 
313*4198Seschrock static int
314*4198Seschrock dev_match(di_node_t node, void *arg)
315*4198Seschrock {
316*4198Seschrock 	struct walkinfo *wip = (struct walkinfo *)arg;
317*4198Seschrock 	char *path = di_devfs_path(node);
318*4198Seschrock 	ddi_devid_t devid;
319*4198Seschrock 	char *devidstr = NULL;
320*4198Seschrock 
321*4198Seschrock 	if (path != NULL && strncmp(path, wip->path, wip->len) == 0) {
322*4198Seschrock 		/*
323*4198Seschrock 		 * If we found the match we were looking for, check to see if a
324*4198Seschrock 		 * devid was specified.  If so, then this must also match.
325*4198Seschrock 		 */
326*4198Seschrock 		if (wip->devid) {
327*4198Seschrock 			if ((devid = di_devid(node)) == NULL ||
328*4198Seschrock 			    (devidstr = devid_str_encode(devid, NULL)) == NULL)
329*4198Seschrock 				goto out;
330*4198Seschrock 
331*4198Seschrock 			if (strcmp(devidstr, wip->devid) != 0)
332*4198Seschrock 				goto out;
333*4198Seschrock 		}
334*4198Seschrock 
335*4198Seschrock 		/*
336*4198Seschrock 		 * Either the devid wasn't specified, or it correctly matched.
337*4198Seschrock 		 * In this case, indicate a successful match and terminate the
338*4198Seschrock 		 * walk.
339*4198Seschrock 		 */
340*4198Seschrock 		wip->matched = 1;
341*4198Seschrock 		di_devfs_path_free(path);
342*4198Seschrock 		devid_str_free(devidstr);
343*4198Seschrock 		return (DI_WALK_TERMINATE);
344*4198Seschrock 	}
345*4198Seschrock 
346*4198Seschrock out:
347*4198Seschrock 	if (path != NULL)
348*4198Seschrock 		di_devfs_path_free(path);
349*4198Seschrock 	devid_str_free(devidstr);
350*4198Seschrock 	return (DI_WALK_CONTINUE);
351*4198Seschrock }
352*4198Seschrock 
353*4198Seschrock /*ARGSUSED*/
354*4198Seschrock static int
355*4198Seschrock dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
356*4198Seschrock     nvlist_t *in, nvlist_t **out)
357*4198Seschrock {
358*4198Seschrock 	di_node_t parent;
359*4198Seschrock 	uint8_t fmversion;
360*4198Seschrock 	char *devpath = NULL;
361*4198Seschrock 	char *parentpath;
362*4198Seschrock 	char *cp;
363*4198Seschrock 	struct walkinfo walkinfo;
364*4198Seschrock 	uint32_t present;
365*4198Seschrock 	char *devid = NULL;
366*4198Seschrock 
367*4198Seschrock 	if (version > TOPO_METH_PRESENT_VERSION)
368*4198Seschrock 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
369*4198Seschrock 
370*4198Seschrock 	if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
371*4198Seschrock 	    fmversion > FM_DEV_SCHEME_VERSION ||
372*4198Seschrock 	    nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
373*4198Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
374*4198Seschrock 
375*4198Seschrock 	(void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
376*4198Seschrock 
377*4198Seschrock 	if (devpath == NULL || (walkinfo.len = strlen(devpath)) == 0)
378*4198Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
379*4198Seschrock 
380*4198Seschrock 	/* strip off last component of path */
381*4198Seschrock 	parentpath = alloca(walkinfo.len + 1);
382*4198Seschrock 	(void) strcpy(parentpath, devpath);
383*4198Seschrock 	if ((cp = strrchr(parentpath, '/')) == NULL)
384*4198Seschrock 		parentpath = "/";
385*4198Seschrock 	else
386*4198Seschrock 		*cp = '\0';
387*4198Seschrock 
388*4198Seschrock 	/* if the result is an empty path, start walk at "/" */
389*4198Seschrock 	if (*parentpath == '\0')
390*4198Seschrock 		parentpath = "/";
391*4198Seschrock 
392*4198Seschrock 	/*
393*4198Seschrock 	 * DINFOFORCE is required for the devid to be present.
394*4198Seschrock 	 */
395*4198Seschrock 	if ((parent = di_init(parentpath,
396*4198Seschrock 	    DINFOSUBTREE | DINFOFORCE)) == DI_NODE_NIL) {
397*4198Seschrock 		if (errno != ENXIO)
398*4198Seschrock 			return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
399*4198Seschrock 		present = 0;
400*4198Seschrock 	} else {
401*4198Seschrock 		walkinfo.matched = 0;
402*4198Seschrock 		walkinfo.path = devpath;
403*4198Seschrock 		walkinfo.devid = devid;
404*4198Seschrock 		(void) di_walk_node(parent,
405*4198Seschrock 		    DI_WALK_SIBFIRST, (void *)&walkinfo, dev_match);
406*4198Seschrock 		di_fini(parent);
407*4198Seschrock 
408*4198Seschrock 		present = walkinfo.matched;
409*4198Seschrock 	}
410*4198Seschrock 
411*4198Seschrock 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
412*4198Seschrock 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
413*4198Seschrock 	if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) {
414*4198Seschrock 		nvlist_free(*out);
415*4198Seschrock 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
416*4198Seschrock 	}
417*4198Seschrock 
418*4198Seschrock 	return (0);
419*4198Seschrock }
420*4198Seschrock 
421*4198Seschrock /*ARGSUSED*/
422*4198Seschrock static int
423*4198Seschrock dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
424*4198Seschrock     nvlist_t *in, nvlist_t **out)
425*4198Seschrock {
426*4198Seschrock 	di_node_t dnode;
427*4198Seschrock 	uint8_t fmversion;
428*4198Seschrock 	char *devpath = NULL;
429*4198Seschrock 	uint32_t unusable;
430*4198Seschrock 	uint_t state;
431*4198Seschrock 
432*4198Seschrock 	if (version > TOPO_METH_PRESENT_VERSION)
433*4198Seschrock 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
434*4198Seschrock 
435*4198Seschrock 	if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
436*4198Seschrock 	    fmversion > FM_DEV_SCHEME_VERSION ||
437*4198Seschrock 	    nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
438*4198Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
439*4198Seschrock 
440*4198Seschrock 	if (devpath == NULL)
441*4198Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
442*4198Seschrock 
443*4198Seschrock 	if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
444*4198Seschrock 		if (errno != ENXIO)
445*4198Seschrock 			return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
446*4198Seschrock 		unusable = 1;
447*4198Seschrock 	} else {
448*4198Seschrock 		state = di_state(dnode);
449*4198Seschrock 		if (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
450*4198Seschrock 		    DI_BUS_QUIESCED | DI_BUS_DOWN))
451*4198Seschrock 			unusable = 1;
452*4198Seschrock 		else
453*4198Seschrock 			unusable = 0;
454*4198Seschrock 		di_fini(dnode);
455*4198Seschrock 	}
456*4198Seschrock 
457*4198Seschrock 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
458*4198Seschrock 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
459*4198Seschrock 	if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, unusable) != 0) {
460*4198Seschrock 		nvlist_free(*out);
461*4198Seschrock 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
462*4198Seschrock 	}
463*4198Seschrock 
464*4198Seschrock 	return (0);
465*4198Seschrock }
466*4198Seschrock 
4671541Stimh static nvlist_t *
4681541Stimh dev_fmri_create(topo_mod_t *mp, const char *id, const char *path)
4691541Stimh {
4701541Stimh 	nvlist_t *out = NULL;
4711541Stimh 	int e;
4721541Stimh 
4731541Stimh 	if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) {
4741541Stimh 		(void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
4751541Stimh 		return (NULL);
4761541Stimh 	}
4771541Stimh 	e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
4781541Stimh 	e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION);
4791541Stimh 	e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path);
4801541Stimh 
4811541Stimh 	if (id != NULL)
4821541Stimh 		e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id);
4831541Stimh 
4841541Stimh 	if (e == 0)
4851541Stimh 		return (out);
4861541Stimh 
4871541Stimh 	topo_mod_dprintf(mp, "construction of dev nvl failed");
4881541Stimh 	(void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
4891541Stimh 	nvlist_free(out);
4901541Stimh 	return (NULL);
4911541Stimh }
4921541Stimh 
4931541Stimh /*ARGSUSED*/
4941541Stimh static int
4951541Stimh dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version,
4961541Stimh     nvlist_t *in, nvlist_t **out)
4971541Stimh {
4981541Stimh 	nvlist_t *args = NULL;
4991541Stimh 	char *path, *id = NULL;
5001541Stimh 
5011541Stimh 	if (version > TOPO_METH_FMRI_VERSION)
5021541Stimh 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
5031541Stimh 
5041541Stimh 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 ||
5051541Stimh 	    nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) {
5061541Stimh 		topo_mod_dprintf(mp, "no path string in method argument\n");
5071541Stimh 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
5081541Stimh 	}
5091541Stimh 
5101541Stimh 	(void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id);
5111541Stimh 
5121541Stimh 	if ((*out = dev_fmri_create(mp, id, path)) == NULL)
5131541Stimh 		return (-1);
5141541Stimh 	return (0);
5151541Stimh }
516