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