Home | History | Annotate | Download | only in av1394
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 
     27 /*
     28  * av1394 configuration ROM
     29  */
     30 #include <sys/file.h>
     31 #include <sys/stream.h>
     32 #include <sys/strsun.h>
     33 #include <sys/1394/targets/av1394/av1394_impl.h>
     34 
     35 /* configROM parsing */
     36 static int	av1394_cfgrom_parse_rom(av1394_inst_t *);
     37 static void	av1394_cfgrom_unparse_rom(av1394_inst_t *);
     38 static int	av1394_cfgrom_parse_dir(av1394_inst_t *, cmd1394_cmd_t *,
     39 		av1394_cfgrom_parse_arg_t *);
     40 static void	av1394_cfgrom_add_text_leaf(av1394_inst_t *,
     41 		av1394_cfgrom_parsed_dir_t *, uint64_t, uint32_t);
     42 static int	av1394_cfgrom_read_leaf(av1394_inst_t *, uint64_t, mblk_t **);
     43 static void	av1394_cfgrom_grow_parsed_dir(av1394_cfgrom_parsed_dir_t *,
     44 		int);
     45 
     46 /* routines involving bus transactions */
     47 static int	av1394_cfgrom_rq(av1394_inst_t *, cmd1394_cmd_t *,
     48 		uint64_t, uint32_t *);
     49 
     50 /* the following macros emulate throwing an exception when read fails */
     51 #define	AV1394_CFGROM_RQ(avp, cmd, addr, valp) \
     52 	if ((ret = av1394_cfgrom_rq(avp, cmd, addr, valp)) != 0) { \
     53 		goto catch; \
     54 	}
     55 
     56 #define	AV1394_TNF_ENTER(func)	\
     57 	TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_ASYNC_STACK, "");
     58 
     59 #define	AV1394_TNF_EXIT(func)	\
     60 	TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_ASYNC_STACK, "");
     61 
     62 int
     63 av1394_cfgrom_init(av1394_inst_t *avp)
     64 {
     65 	av1394_cfgrom_t		*crp = &avp->av_a.a_cfgrom;
     66 	ddi_iblock_cookie_t	ibc = avp->av_attachinfo.iblock_cookie;
     67 
     68 	AV1394_TNF_ENTER(av1394_cfgrom_init);
     69 
     70 	rw_init(&crp->cr_rwlock, NULL, RW_DRIVER, ibc);
     71 
     72 	AV1394_TNF_EXIT(av1394_cfgrom_init);
     73 	return (DDI_SUCCESS);
     74 }
     75 
     76 void
     77 av1394_cfgrom_fini(av1394_inst_t *avp)
     78 {
     79 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
     80 
     81 	AV1394_TNF_ENTER(av1394_cfgrom_fini);
     82 
     83 	rw_destroy(&crp->cr_rwlock);
     84 
     85 	AV1394_TNF_EXIT(av1394_cfgrom_fini);
     86 }
     87 
     88 void
     89 av1394_cfgrom_close(av1394_inst_t *avp)
     90 {
     91 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
     92 
     93 	AV1394_TNF_ENTER(av1394_cfgrom_close);
     94 
     95 	rw_enter(&crp->cr_rwlock, RW_WRITER);
     96 	if (crp->cr_parsed) {
     97 		av1394_cfgrom_unparse_rom(avp);
     98 	}
     99 	rw_exit(&crp->cr_rwlock);
    100 
    101 	AV1394_TNF_EXIT(av1394_cfgrom_close);
    102 }
    103 
    104 int
    105 av1394_ioctl_node_get_bus_name(av1394_inst_t *avp, void *arg, int mode)
    106 {
    107 	cmd1394_cmd_t	*cmd;
    108 	uint32_t	val;
    109 	int		err;
    110 	int		ret = 0;
    111 
    112 	AV1394_TNF_ENTER(av1394_ioctl_node_get_bus_name);
    113 
    114 	err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
    115 	if (err != DDI_SUCCESS) {
    116 		AV1394_TNF_EXIT(av1394_ioctl_node_get_bus_name);
    117 		return (ENOMEM);
    118 	}
    119 
    120 	ret = av1394_cfgrom_rq(avp, cmd, AV1394_CFGROM_BUS_NAME_ADDR, &val);
    121 	if (ret == 0) {
    122 		if (ddi_copyout(&val, arg, sizeof (uint32_t), mode) != 0) {
    123 			ret = EFAULT;
    124 		}
    125 	}
    126 
    127 	err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
    128 	ASSERT(err == DDI_SUCCESS);
    129 
    130 	AV1394_TNF_EXIT(av1394_ioctl_node_get_bus_name);
    131 	return (ret);
    132 }
    133 
    134 int
    135 av1394_ioctl_node_get_uid(av1394_inst_t *avp, void *arg, int mode)
    136 {
    137 	cmd1394_cmd_t	*cmd;
    138 	uint64_t	eui64;
    139 	uint32_t	hi, lo;
    140 	int		err;
    141 	int		ret = 0;
    142 
    143 	AV1394_TNF_ENTER(av1394_ioctl_node_get_uid);
    144 
    145 	err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
    146 	if (err != DDI_SUCCESS) {
    147 		AV1394_TNF_EXIT(av1394_ioctl_node_get_uid);
    148 		return (ENOMEM);
    149 	}
    150 
    151 	AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_EUI64_HI_ADDR, &hi);
    152 	AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_EUI64_LO_ADDR, &lo);
    153 
    154 	eui64 = ((uint64_t)hi << 32) | lo;
    155 	if (ddi_copyout(&eui64, arg, sizeof (uint64_t), mode) != 0) {
    156 		ret = EFAULT;
    157 	}
    158 
    159 catch:
    160 	err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
    161 	ASSERT(err == DDI_SUCCESS);
    162 
    163 	AV1394_TNF_EXIT(av1394_ioctl_node_get_uid);
    164 	return (ret);
    165 }
    166 
    167 int
    168 av1394_ioctl_node_get_text_leaf(av1394_inst_t *avp, void *arg, int mode)
    169 {
    170 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
    171 	iec61883_node_text_leaf_t tl;
    172 #ifdef _MULTI_DATAMODEL
    173 	iec61883_node_text_leaf32_t tl32;
    174 #endif
    175 	int		n;		/* text leaf number requested */
    176 	int		parent;		/* leaf parent */
    177 	mblk_t		*bp = NULL;
    178 	av1394_cfgrom_parsed_dir_t *pd;
    179 	int		leaf_len;
    180 	uint32_t	spec, lang_id, desc_entry;
    181 	int		ret = 0;
    182 
    183 	AV1394_TNF_ENTER(av1394_ioctl_node_get_text_leaf);
    184 
    185 	/* copyin arguments */
    186 #ifdef _MULTI_DATAMODEL
    187 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
    188 		if (ddi_copyin(arg, &tl32, sizeof (tl32), mode) != 0) {
    189 			AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
    190 			return (EFAULT);
    191 		}
    192 		n = tl32.tl_num;
    193 		parent = tl32.tl_parent;
    194 	} else {
    195 #endif
    196 	if (ddi_copyin(arg, &tl, sizeof (tl), mode) != 0) {
    197 		AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
    198 		return (EFAULT);
    199 	}
    200 	n = tl.tl_num;
    201 	parent = tl.tl_parent;
    202 #ifdef _MULTI_DATAMODEL
    203 	}
    204 #endif
    205 	/* verify arguments */
    206 	if (((parent != IEC61883_ROM_ROOT) && (parent != IEC61883_ROM_UNIT)) ||
    207 	    (n < 0)) {
    208 		return (EINVAL);
    209 	}
    210 
    211 	/* parse ConfigROM if not already */
    212 	rw_enter(&crp->cr_rwlock, RW_WRITER);
    213 	if (!crp->cr_parsed) {
    214 		ret = av1394_cfgrom_parse_rom(avp);
    215 		if (ret != 0) {
    216 			rw_exit(&crp->cr_rwlock);
    217 			AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
    218 			return (ret);
    219 		}
    220 	}
    221 	rw_downgrade(&crp->cr_rwlock);
    222 
    223 	/* get parsed leaf info */
    224 	if (parent == IEC61883_ROM_ROOT) {
    225 		pd = &crp->cr_root_dir;
    226 	} else {
    227 		pd = &crp->cr_unit_dir;
    228 	}
    229 
    230 	if (n < pd->pd_tl_next) {
    231 		/* read the leaf */
    232 		ret = av1394_cfgrom_read_leaf(avp, pd->pd_tl[n].tl_addr, &bp);
    233 		if (ret != 0) {
    234 			rw_exit(&crp->cr_rwlock);
    235 			AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
    236 			return (ret);
    237 		}
    238 		leaf_len = MBLKL(bp) / 4 - 2;
    239 		ASSERT(leaf_len > 0);
    240 		spec = *(uint32_t *)bp->b_rptr;
    241 		bp->b_rptr += 4;
    242 		lang_id = *(uint32_t *)bp->b_rptr;
    243 		bp->b_rptr += 4;
    244 		desc_entry = pd->pd_tl[n].tl_desc_entry;
    245 	} else {
    246 		/* return success anyway, but with tl_cnt < tl_num */
    247 		spec = lang_id = desc_entry = 0;
    248 		leaf_len = 0;
    249 	}
    250 
    251 	/* copyout the results */
    252 #ifdef _MULTI_DATAMODEL
    253 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
    254 		tl32.tl_cnt = pd->pd_tl_next;
    255 		tl32.tl_desc_entry = desc_entry;
    256 		tl32.tl_rlen = leaf_len;
    257 		tl32.tl_spec = spec;
    258 		tl32.tl_lang_id = lang_id;
    259 		if (ddi_copyout(&tl32, arg, sizeof (tl32), mode) != 0) {
    260 			ret = EFAULT;
    261 		} else if (bp && ddi_copyout(bp->b_rptr,
    262 		    (void *)(uintptr_t)tl32.tl_data,
    263 		    4 * min(tl32.tl_len, tl32.tl_rlen), mode) != 0) {
    264 			ret = EFAULT;
    265 		}
    266 	} else {
    267 #endif
    268 	tl.tl_cnt = pd->pd_tl_next;
    269 	tl.tl_desc_entry = desc_entry;
    270 	tl.tl_rlen = leaf_len;
    271 	tl.tl_spec = spec;
    272 	tl.tl_lang_id = lang_id;
    273 	if (ddi_copyout(&tl, arg, sizeof (tl), mode) != 0) {
    274 		ret = EFAULT;
    275 	} else if (bp && ddi_copyout(bp->b_rptr, tl.tl_data,
    276 	    4 * min(tl.tl_len, tl.tl_rlen), mode) != 0) {
    277 		ret = EFAULT;
    278 	}
    279 #ifdef _MULTI_DATAMODEL
    280 	}
    281 #endif
    282 	rw_exit(&crp->cr_rwlock);
    283 
    284 	freemsg(bp);
    285 
    286 	AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
    287 	return (ret);
    288 }
    289 
    290 
    291 /*
    292  *
    293  * --- configROM parsing
    294  *
    295  * Parse entire configROM. Only extract information that interests us.
    296  * ConfigROM integrity checks are only made to ensure correct parsing.
    297  */
    298 static int
    299 av1394_cfgrom_parse_rom(av1394_inst_t *avp)
    300 {
    301 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
    302 	cmd1394_cmd_t	*cmd;
    303 	uint32_t	val;
    304 	uint64_t	root_addr;	/* root dir address */
    305 	uint16_t	root_len;	/* root dir length */
    306 	av1394_cfgrom_parse_arg_t pa;
    307 	int		err;
    308 	int		ret;
    309 
    310 	ASSERT(crp->cr_parsed == B_FALSE);
    311 
    312 	err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
    313 	if (err != DDI_SUCCESS) {
    314 		return (ENOMEM);
    315 	}
    316 
    317 	/* skip info_len quadlets to get root dir address and length */
    318 	AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_INFO_LEN_ADDR, &val);
    319 	val = AV_SWAP32(val);
    320 	root_addr = IEEE1394_CONFIG_ROM_ADDR + 4 + (val >> 24) * 4;
    321 	AV1394_CFGROM_RQ(avp, cmd, root_addr, &val);
    322 	val = AV_SWAP32(val);
    323 	root_len = IEEE1212_DIR_LEN(val);
    324 
    325 	/* parse root dir and everything underneath */
    326 	pa.pa_depth = 0;
    327 	pa.pa_desc_entry = 0;
    328 	pa.pa_parent_k = 0;
    329 	pa.pa_addr = root_addr + 4;
    330 	pa.pa_len = root_len;
    331 	pa.pa_dir = &crp->cr_root_dir;
    332 
    333 	ret = av1394_cfgrom_parse_dir(avp, cmd, &pa);
    334 
    335 catch:
    336 	if (ret == 0) {
    337 		crp->cr_parsed = B_TRUE;
    338 	} else {
    339 		av1394_cfgrom_unparse_rom(avp);
    340 	}
    341 	err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
    342 	ASSERT(err == DDI_SUCCESS);
    343 
    344 	return (ret);
    345 }
    346 
    347 /*
    348  * parse a directory
    349  */
    350 static int
    351 av1394_cfgrom_parse_dir(av1394_inst_t *avp, cmd1394_cmd_t *cmd,
    352 		av1394_cfgrom_parse_arg_t *pa)
    353 {
    354 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
    355 	int		i;
    356 	uint64_t	entry_addr;
    357 	uint32_t	entry;
    358 	uint64_t	leaf_addr;
    359 	uint64_t	dir_addr;
    360 	uint16_t	dir_len;
    361 	uint8_t		t, k;
    362 	uint16_t	v;
    363 	uint32_t	val;
    364 	av1394_cfgrom_parse_arg_t this_pa;
    365 	int		ret = 0;
    366 
    367 	/* safeguard against deep recursion */
    368 	if (pa->pa_depth > AV1394_CFGROM_PARSE_MAX_DEPTH) {
    369 		return (ENOMEM);
    370 	}
    371 
    372 	/* initialize parse arguments */
    373 	this_pa.pa_depth = pa->pa_depth + 1;
    374 	this_pa.pa_desc_entry = pa->pa_desc_entry;
    375 
    376 	/* walk dir entries */
    377 	entry_addr = pa->pa_addr;
    378 	for (i = 0; i < pa->pa_len; i++) {
    379 		AV1394_CFGROM_RQ(avp, cmd, entry_addr, &entry);
    380 		entry = AV_SWAP32(entry);
    381 
    382 		CFGROM_TYPE_KEY_VALUE(entry, t, k, v);
    383 		if ((t == IEEE1212_LEAF_TYPE) &&
    384 		    (k == IEEE1212_TEXTUAL_DESCRIPTOR)) {
    385 			/* save this leaf */
    386 			leaf_addr = entry_addr + 4 * v;
    387 			av1394_cfgrom_add_text_leaf(avp, pa->pa_dir,
    388 			    leaf_addr, this_pa.pa_desc_entry);
    389 		} else if (t == IEEE1212_DIRECTORY_TYPE) {
    390 			dir_addr = entry_addr + 4 * v;
    391 			AV1394_CFGROM_RQ(avp, cmd, dir_addr, &val);
    392 			val = AV_SWAP32(val);
    393 			dir_len = IEEE1212_DIR_LEN(val);
    394 
    395 			/* parse this dir */
    396 			this_pa.pa_parent_k = k;
    397 			this_pa.pa_addr = dir_addr + 4;
    398 			this_pa.pa_len = dir_len;
    399 			/* leaves will be added to either root or unit array */
    400 			if (k == IEEE1212_UNIT_DIRECTORY) {
    401 				this_pa.pa_dir = &crp->cr_unit_dir;
    402 			} else {
    403 				this_pa.pa_dir = pa->pa_dir;
    404 			}
    405 
    406 			ret = av1394_cfgrom_parse_dir(avp, cmd, &this_pa);
    407 			if (ret != 0) {
    408 				goto catch;
    409 			}
    410 		}
    411 
    412 		/*
    413 		 * if we're walking Textual_Descriptor directory,
    414 		 * the described entry is the one preceding directory's entry,
    415 		 * so we need to preserve what was passed in pa->pa_desc_entry
    416 		 */
    417 		if (pa->pa_parent_k != IEEE1212_TEXTUAL_DESCRIPTOR) {
    418 			this_pa.pa_desc_entry = entry;
    419 		}
    420 		entry_addr += 4;
    421 	}
    422 
    423 catch:
    424 	return (ret);
    425 }
    426 
    427 /*ARGSUSED*/
    428 static void
    429 av1394_cfgrom_add_text_leaf(av1394_inst_t *avp, av1394_cfgrom_parsed_dir_t *pd,
    430 		uint64_t addr, uint32_t desc_entry)
    431 {
    432 	/* grow array of needed */
    433 	if (pd->pd_tl_next >= pd->pd_tl_size) {
    434 		av1394_cfgrom_grow_parsed_dir(pd, 2);
    435 	}
    436 	pd->pd_tl[pd->pd_tl_next].tl_addr = addr;
    437 	pd->pd_tl[pd->pd_tl_next].tl_desc_entry = desc_entry;
    438 	pd->pd_tl_next++;
    439 }
    440 
    441 /*
    442  * this routine cleans up after av1394_cfgrom_parse()
    443  */
    444 static void
    445 av1394_cfgrom_unparse_rom(av1394_inst_t *avp)
    446 {
    447 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
    448 	av1394_cfgrom_parsed_dir_t *pd;
    449 
    450 	pd = &crp->cr_root_dir;
    451 	if (pd->pd_tl) {
    452 		kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
    453 		bzero(pd, sizeof (*pd));
    454 	}
    455 	pd = &crp->cr_unit_dir;
    456 	if (pd->pd_tl) {
    457 		kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
    458 		bzero(pd, sizeof (*pd));
    459 	}
    460 	crp->cr_parsed = B_FALSE;
    461 }
    462 
    463 /*
    464  * grow parsed dir leaf array by 'cnt' entries
    465  */
    466 static void
    467 av1394_cfgrom_grow_parsed_dir(av1394_cfgrom_parsed_dir_t *pd, int cnt)
    468 {
    469 	int	new_size;
    470 	void	*new_tl;
    471 
    472 	ASSERT(cnt > 0);
    473 
    474 	new_size = (pd->pd_tl_size + cnt) * sizeof (av1394_cfgrom_text_leaf_t);
    475 	new_tl = kmem_zalloc(new_size, KM_SLEEP);
    476 	if (pd->pd_tl_size > 0) {
    477 		bcopy(pd->pd_tl, new_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
    478 		kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
    479 	}
    480 	pd->pd_tl = new_tl;
    481 	pd->pd_tl_size += cnt;
    482 }
    483 
    484 static int
    485 av1394_cfgrom_read_leaf(av1394_inst_t *avp, uint64_t leaf_addr, mblk_t **bpp)
    486 {
    487 	cmd1394_cmd_t	*cmd;
    488 	uint64_t	addr;
    489 	uint32_t	val;
    490 	int		leaf_len;	/* leaf length in quadlets */
    491 	mblk_t		*bp = NULL;
    492 	int		i;
    493 	int		err;
    494 	int		ret = 0;
    495 
    496 	err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
    497 	if (err != DDI_SUCCESS) {
    498 		AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
    499 		return (ENOMEM);
    500 	}
    501 
    502 	/* read leaf length */
    503 	AV1394_CFGROM_RQ(avp, cmd, leaf_addr, &val);
    504 	val = AV_SWAP32(val);
    505 	leaf_len = IEEE1212_DIR_LEN(val);
    506 
    507 	if (leaf_len < 3) {
    508 		ret = EIO;
    509 		goto catch;
    510 	}
    511 
    512 	if ((bp = allocb(leaf_len * 4, BPRI_HI)) == NULL) {
    513 		TNF_PROBE_0(aav1394_cfgrom_read_leaf_error_allocb,
    514 		    AV1394_TNF_ASYNC_ERROR, "");
    515 		return (ENOMEM);
    516 	}
    517 
    518 	/* read leaf value */
    519 	addr = leaf_addr + 4;
    520 	for (i = 0; i < leaf_len; i++) {
    521 		AV1394_CFGROM_RQ(avp, cmd, addr, (uint32_t *)bp->b_wptr);
    522 		bp->b_wptr += 4;
    523 		addr += 4;
    524 	}
    525 
    526 catch:
    527 	if (ret == 0) {
    528 		*bpp = bp;
    529 	} else {
    530 		freemsg(bp);
    531 	}
    532 	err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
    533 	ASSERT(err == DDI_SUCCESS);
    534 
    535 	return (ret);
    536 }
    537 
    538 /*
    539  *
    540  * --- routines involving bus transactions
    541  *
    542  */
    543 static int
    544 av1394_cfgrom_rq(av1394_inst_t *avp, cmd1394_cmd_t *cmd, uint64_t addr,
    545 		uint32_t *rval)
    546 {
    547 	int	err;
    548 
    549 	cmd->cmd_type = CMD1394_ASYNCH_RD_QUAD;
    550 	cmd->cmd_options = CMD1394_BLOCKING;
    551 	cmd->cmd_addr = addr;
    552 
    553 	err = t1394_read(avp->av_t1394_hdl, cmd);
    554 	if ((err == DDI_SUCCESS) && (cmd->cmd_result == CMD1394_CMDSUCCESS)) {
    555 		*rval = cmd->cmd_u.q.quadlet_data;
    556 		return (0);
    557 	} else {
    558 		TNF_PROBE_2(av1394_cfgrom_rq_error,
    559 		    AV1394_TNF_ASYNC_ERROR, "", tnf_int, err, err,
    560 		    tnf_int, result, cmd->cmd_result);
    561 		return (EIO);
    562 	}
    563 }
    564