xref: /onnv-gate/usr/src/lib/fm/topo/libtopo/common/dev.c (revision 4845:357e8e7542af)
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 /*
234198Seschrock  * 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>
354198Seschrock #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 **);
534198Seschrock static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
544198Seschrock     nvlist_t *, nvlist_t **);
554198Seschrock static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
564198Seschrock     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 },
654198Seschrock 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
664198Seschrock 	    TOPO_STABILITY_INTERNAL, dev_fmri_present },
674198Seschrock 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
684198Seschrock 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
694198Seschrock 	    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 
3034198Seschrock /*
3044198Seschrock  * callback routine for di_walk_minor()
3054198Seschrock  */
3064198Seschrock struct walkinfo {
3074198Seschrock 	int matched;
3084198Seschrock 	const char *path;
3094198Seschrock 	const char *devid;
3104198Seschrock 	int len;
3114198Seschrock };
3124198Seschrock 
3134198Seschrock static int
3144198Seschrock dev_match(di_node_t node, void *arg)
3154198Seschrock {
3164198Seschrock 	struct walkinfo *wip = (struct walkinfo *)arg;
3174198Seschrock 	char *path = di_devfs_path(node);
3184198Seschrock 	ddi_devid_t devid;
3194198Seschrock 	char *devidstr = NULL;
3204198Seschrock 
3214198Seschrock 	if (path != NULL && strncmp(path, wip->path, wip->len) == 0) {
3224198Seschrock 		/*
3234198Seschrock 		 * If we found the match we were looking for, check to see if a
3244198Seschrock 		 * devid was specified.  If so, then this must also match.
3254198Seschrock 		 */
3264198Seschrock 		if (wip->devid) {
3274198Seschrock 			if ((devid = di_devid(node)) == NULL ||
3284198Seschrock 			    (devidstr = devid_str_encode(devid, NULL)) == NULL)
3294198Seschrock 				goto out;
3304198Seschrock 
3314198Seschrock 			if (strcmp(devidstr, wip->devid) != 0)
3324198Seschrock 				goto out;
3334198Seschrock 		}
3344198Seschrock 
3354198Seschrock 		/*
3364198Seschrock 		 * Either the devid wasn't specified, or it correctly matched.
3374198Seschrock 		 * In this case, indicate a successful match and terminate the
3384198Seschrock 		 * walk.
3394198Seschrock 		 */
3404198Seschrock 		wip->matched = 1;
3414198Seschrock 		di_devfs_path_free(path);
3424198Seschrock 		devid_str_free(devidstr);
3434198Seschrock 		return (DI_WALK_TERMINATE);
3444198Seschrock 	}
3454198Seschrock 
3464198Seschrock out:
3474198Seschrock 	if (path != NULL)
3484198Seschrock 		di_devfs_path_free(path);
3494198Seschrock 	devid_str_free(devidstr);
3504198Seschrock 	return (DI_WALK_CONTINUE);
3514198Seschrock }
3524198Seschrock 
3534198Seschrock /*ARGSUSED*/
3544198Seschrock static int
3554198Seschrock dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
3564198Seschrock     nvlist_t *in, nvlist_t **out)
3574198Seschrock {
3584198Seschrock 	di_node_t parent;
3594198Seschrock 	uint8_t fmversion;
3604198Seschrock 	char *devpath = NULL;
3614198Seschrock 	char *parentpath;
3624198Seschrock 	char *cp;
3634198Seschrock 	struct walkinfo walkinfo;
3644198Seschrock 	uint32_t present;
3654198Seschrock 	char *devid = NULL;
3664198Seschrock 
3674198Seschrock 	if (version > TOPO_METH_PRESENT_VERSION)
3684198Seschrock 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
3694198Seschrock 
3704198Seschrock 	if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
3714198Seschrock 	    fmversion > FM_DEV_SCHEME_VERSION ||
3724198Seschrock 	    nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
3734198Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
3744198Seschrock 
3754198Seschrock 	(void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
3764198Seschrock 
3774198Seschrock 	if (devpath == NULL || (walkinfo.len = strlen(devpath)) == 0)
3784198Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
3794198Seschrock 
3804198Seschrock 	/* strip off last component of path */
3814198Seschrock 	parentpath = alloca(walkinfo.len + 1);
3824198Seschrock 	(void) strcpy(parentpath, devpath);
3834198Seschrock 	if ((cp = strrchr(parentpath, '/')) == NULL)
3844198Seschrock 		parentpath = "/";
3854198Seschrock 	else
3864198Seschrock 		*cp = '\0';
3874198Seschrock 
3884198Seschrock 	/* if the result is an empty path, start walk at "/" */
3894198Seschrock 	if (*parentpath == '\0')
3904198Seschrock 		parentpath = "/";
3914198Seschrock 
3924198Seschrock 	/*
3934198Seschrock 	 * DINFOFORCE is required for the devid to be present.
3944198Seschrock 	 */
3954198Seschrock 	if ((parent = di_init(parentpath,
3964198Seschrock 	    DINFOSUBTREE | DINFOFORCE)) == DI_NODE_NIL) {
3974198Seschrock 		if (errno != ENXIO)
3984198Seschrock 			return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
3994198Seschrock 		present = 0;
4004198Seschrock 	} else {
4014198Seschrock 		walkinfo.matched = 0;
4024198Seschrock 		walkinfo.path = devpath;
4034198Seschrock 		walkinfo.devid = devid;
4044198Seschrock 		(void) di_walk_node(parent,
4054198Seschrock 		    DI_WALK_SIBFIRST, (void *)&walkinfo, dev_match);
4064198Seschrock 		di_fini(parent);
4074198Seschrock 
4084198Seschrock 		present = walkinfo.matched;
4094198Seschrock 	}
4104198Seschrock 
4114198Seschrock 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
4124198Seschrock 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
4134198Seschrock 	if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) {
4144198Seschrock 		nvlist_free(*out);
4154198Seschrock 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
4164198Seschrock 	}
4174198Seschrock 
4184198Seschrock 	return (0);
4194198Seschrock }
4204198Seschrock 
4214198Seschrock /*ARGSUSED*/
4224198Seschrock static int
4234198Seschrock dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
4244198Seschrock     nvlist_t *in, nvlist_t **out)
4254198Seschrock {
4264198Seschrock 	di_node_t dnode;
4274198Seschrock 	uint8_t fmversion;
4284198Seschrock 	char *devpath = NULL;
4294198Seschrock 	uint32_t unusable;
4304198Seschrock 	uint_t state;
4314198Seschrock 
4324198Seschrock 	if (version > TOPO_METH_PRESENT_VERSION)
4334198Seschrock 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
4344198Seschrock 
4354198Seschrock 	if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
4364198Seschrock 	    fmversion > FM_DEV_SCHEME_VERSION ||
4374198Seschrock 	    nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
4384198Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
4394198Seschrock 
4404198Seschrock 	if (devpath == NULL)
4414198Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
4424198Seschrock 
4434198Seschrock 	if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
4444198Seschrock 		if (errno != ENXIO)
4454198Seschrock 			return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
4464198Seschrock 		unusable = 1;
4474198Seschrock 	} else {
448*4845Svikram 		uint_t retired = di_retired(dnode);
4494198Seschrock 		state = di_state(dnode);
450*4845Svikram 		if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
451*4845Svikram 		    DI_BUS_QUIESCED | DI_BUS_DOWN)))
4524198Seschrock 			unusable = 1;
4534198Seschrock 		else
4544198Seschrock 			unusable = 0;
4554198Seschrock 		di_fini(dnode);
4564198Seschrock 	}
4574198Seschrock 
4584198Seschrock 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
4594198Seschrock 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
4604444Svikram 	if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) {
4614198Seschrock 		nvlist_free(*out);
4624198Seschrock 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
4634198Seschrock 	}
4644198Seschrock 
4654198Seschrock 	return (0);
4664198Seschrock }
4674198Seschrock 
4681541Stimh static nvlist_t *
4691541Stimh dev_fmri_create(topo_mod_t *mp, const char *id, const char *path)
4701541Stimh {
4711541Stimh 	nvlist_t *out = NULL;
4721541Stimh 	int e;
4731541Stimh 
4741541Stimh 	if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) {
4751541Stimh 		(void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
4761541Stimh 		return (NULL);
4771541Stimh 	}
4781541Stimh 	e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
4791541Stimh 	e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION);
4801541Stimh 	e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path);
4811541Stimh 
4821541Stimh 	if (id != NULL)
4831541Stimh 		e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id);
4841541Stimh 
4851541Stimh 	if (e == 0)
4861541Stimh 		return (out);
4871541Stimh 
4881541Stimh 	topo_mod_dprintf(mp, "construction of dev nvl failed");
4891541Stimh 	(void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
4901541Stimh 	nvlist_free(out);
4911541Stimh 	return (NULL);
4921541Stimh }
4931541Stimh 
4941541Stimh /*ARGSUSED*/
4951541Stimh static int
4961541Stimh dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version,
4971541Stimh     nvlist_t *in, nvlist_t **out)
4981541Stimh {
4991541Stimh 	nvlist_t *args = NULL;
5001541Stimh 	char *path, *id = NULL;
5011541Stimh 
5021541Stimh 	if (version > TOPO_METH_FMRI_VERSION)
5031541Stimh 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
5041541Stimh 
5051541Stimh 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 ||
5061541Stimh 	    nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) {
5071541Stimh 		topo_mod_dprintf(mp, "no path string in method argument\n");
5081541Stimh 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
5091541Stimh 	}
5101541Stimh 
5111541Stimh 	(void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id);
5121541Stimh 
5131541Stimh 	if ((*out = dev_fmri_create(mp, id, path)) == NULL)
5141541Stimh 		return (-1);
5151541Stimh 	return (0);
5161541Stimh }
517