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 int 57 av1394_cfgrom_init(av1394_inst_t *avp) 58 { 59 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 60 ddi_iblock_cookie_t ibc = avp->av_attachinfo.iblock_cookie; 61 62 rw_init(&crp->cr_rwlock, NULL, RW_DRIVER, ibc); 63 64 return (DDI_SUCCESS); 65 } 66 67 void 68 av1394_cfgrom_fini(av1394_inst_t *avp) 69 { 70 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 71 72 rw_destroy(&crp->cr_rwlock); 73 } 74 75 void 76 av1394_cfgrom_close(av1394_inst_t *avp) 77 { 78 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 79 80 rw_enter(&crp->cr_rwlock, RW_WRITER); 81 if (crp->cr_parsed) { 82 av1394_cfgrom_unparse_rom(avp); 83 } 84 rw_exit(&crp->cr_rwlock); 85 } 86 87 int 88 av1394_ioctl_node_get_bus_name(av1394_inst_t *avp, void *arg, int mode) 89 { 90 cmd1394_cmd_t *cmd; 91 uint32_t val; 92 int err; 93 int ret = 0; 94 95 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd); 96 if (err != DDI_SUCCESS) { 97 return (ENOMEM); 98 } 99 100 ret = av1394_cfgrom_rq(avp, cmd, AV1394_CFGROM_BUS_NAME_ADDR, &val); 101 if (ret == 0) { 102 if (ddi_copyout(&val, arg, sizeof (uint32_t), mode) != 0) { 103 ret = EFAULT; 104 } 105 } 106 107 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd); 108 ASSERT(err == DDI_SUCCESS); 109 110 return (ret); 111 } 112 113 int 114 av1394_ioctl_node_get_uid(av1394_inst_t *avp, void *arg, int mode) 115 { 116 cmd1394_cmd_t *cmd; 117 uint64_t eui64; 118 uint32_t hi, lo; 119 int err; 120 int ret = 0; 121 122 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd); 123 if (err != DDI_SUCCESS) { 124 return (ENOMEM); 125 } 126 127 AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_EUI64_HI_ADDR, &hi); 128 AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_EUI64_LO_ADDR, &lo); 129 130 eui64 = ((uint64_t)hi << 32) | lo; 131 if (ddi_copyout(&eui64, arg, sizeof (uint64_t), mode) != 0) { 132 ret = EFAULT; 133 } 134 135 catch: 136 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd); 137 ASSERT(err == DDI_SUCCESS); 138 139 return (ret); 140 } 141 142 int 143 av1394_ioctl_node_get_text_leaf(av1394_inst_t *avp, void *arg, int mode) 144 { 145 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 146 iec61883_node_text_leaf_t tl; 147 #ifdef _MULTI_DATAMODEL 148 iec61883_node_text_leaf32_t tl32; 149 #endif 150 int n; /* text leaf number requested */ 151 int parent; /* leaf parent */ 152 mblk_t *bp = NULL; 153 av1394_cfgrom_parsed_dir_t *pd; 154 int leaf_len; 155 uint32_t spec, lang_id, desc_entry; 156 int ret = 0; 157 158 /* copyin arguments */ 159 #ifdef _MULTI_DATAMODEL 160 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { 161 if (ddi_copyin(arg, &tl32, sizeof (tl32), mode) != 0) { 162 return (EFAULT); 163 } 164 n = tl32.tl_num; 165 parent = tl32.tl_parent; 166 } else { 167 #endif 168 if (ddi_copyin(arg, &tl, sizeof (tl), mode) != 0) { 169 return (EFAULT); 170 } 171 n = tl.tl_num; 172 parent = tl.tl_parent; 173 #ifdef _MULTI_DATAMODEL 174 } 175 #endif 176 /* verify arguments */ 177 if (((parent != IEC61883_ROM_ROOT) && (parent != IEC61883_ROM_UNIT)) || 178 (n < 0)) { 179 return (EINVAL); 180 } 181 182 /* parse ConfigROM if not already */ 183 rw_enter(&crp->cr_rwlock, RW_WRITER); 184 if (!crp->cr_parsed) { 185 ret = av1394_cfgrom_parse_rom(avp); 186 if (ret != 0) { 187 rw_exit(&crp->cr_rwlock); 188 return (ret); 189 } 190 } 191 rw_downgrade(&crp->cr_rwlock); 192 193 /* get parsed leaf info */ 194 if (parent == IEC61883_ROM_ROOT) { 195 pd = &crp->cr_root_dir; 196 } else { 197 pd = &crp->cr_unit_dir; 198 } 199 200 if (n < pd->pd_tl_next) { 201 /* read the leaf */ 202 ret = av1394_cfgrom_read_leaf(avp, pd->pd_tl[n].tl_addr, &bp); 203 if (ret != 0) { 204 rw_exit(&crp->cr_rwlock); 205 return (ret); 206 } 207 leaf_len = MBLKL(bp) / 4 - 2; 208 ASSERT(leaf_len > 0); 209 spec = *(uint32_t *)bp->b_rptr; 210 bp->b_rptr += 4; 211 lang_id = *(uint32_t *)bp->b_rptr; 212 bp->b_rptr += 4; 213 desc_entry = pd->pd_tl[n].tl_desc_entry; 214 } else { 215 /* return success anyway, but with tl_cnt < tl_num */ 216 spec = lang_id = desc_entry = 0; 217 leaf_len = 0; 218 } 219 220 /* copyout the results */ 221 #ifdef _MULTI_DATAMODEL 222 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { 223 tl32.tl_cnt = pd->pd_tl_next; 224 tl32.tl_desc_entry = desc_entry; 225 tl32.tl_rlen = leaf_len; 226 tl32.tl_spec = spec; 227 tl32.tl_lang_id = lang_id; 228 if (ddi_copyout(&tl32, arg, sizeof (tl32), mode) != 0) { 229 ret = EFAULT; 230 } else if (bp && ddi_copyout(bp->b_rptr, 231 (void *)(uintptr_t)tl32.tl_data, 232 4 * min(tl32.tl_len, tl32.tl_rlen), mode) != 0) { 233 ret = EFAULT; 234 } 235 } else { 236 #endif 237 tl.tl_cnt = pd->pd_tl_next; 238 tl.tl_desc_entry = desc_entry; 239 tl.tl_rlen = leaf_len; 240 tl.tl_spec = spec; 241 tl.tl_lang_id = lang_id; 242 if (ddi_copyout(&tl, arg, sizeof (tl), mode) != 0) { 243 ret = EFAULT; 244 } else if (bp && ddi_copyout(bp->b_rptr, tl.tl_data, 245 4 * min(tl.tl_len, tl.tl_rlen), mode) != 0) { 246 ret = EFAULT; 247 } 248 #ifdef _MULTI_DATAMODEL 249 } 250 #endif 251 rw_exit(&crp->cr_rwlock); 252 253 freemsg(bp); 254 255 return (ret); 256 } 257 258 259 /* 260 * 261 * --- configROM parsing 262 * 263 * Parse entire configROM. Only extract information that interests us. 264 * ConfigROM integrity checks are only made to ensure correct parsing. 265 */ 266 static int 267 av1394_cfgrom_parse_rom(av1394_inst_t *avp) 268 { 269 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 270 cmd1394_cmd_t *cmd; 271 uint32_t val; 272 uint64_t root_addr; /* root dir address */ 273 uint16_t root_len; /* root dir length */ 274 av1394_cfgrom_parse_arg_t pa; 275 int err; 276 int ret; 277 278 ASSERT(crp->cr_parsed == B_FALSE); 279 280 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd); 281 if (err != DDI_SUCCESS) { 282 return (ENOMEM); 283 } 284 285 /* skip info_len quadlets to get root dir address and length */ 286 AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_INFO_LEN_ADDR, &val); 287 val = AV_SWAP32(val); 288 root_addr = IEEE1394_CONFIG_ROM_ADDR + 4 + (val >> 24) * 4; 289 AV1394_CFGROM_RQ(avp, cmd, root_addr, &val); 290 val = AV_SWAP32(val); 291 root_len = IEEE1212_DIR_LEN(val); 292 293 /* parse root dir and everything underneath */ 294 pa.pa_depth = 0; 295 pa.pa_desc_entry = 0; 296 pa.pa_parent_k = 0; 297 pa.pa_addr = root_addr + 4; 298 pa.pa_len = root_len; 299 pa.pa_dir = &crp->cr_root_dir; 300 301 ret = av1394_cfgrom_parse_dir(avp, cmd, &pa); 302 303 catch: 304 if (ret == 0) { 305 crp->cr_parsed = B_TRUE; 306 } else { 307 av1394_cfgrom_unparse_rom(avp); 308 } 309 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd); 310 ASSERT(err == DDI_SUCCESS); 311 312 return (ret); 313 } 314 315 /* 316 * parse a directory 317 */ 318 static int 319 av1394_cfgrom_parse_dir(av1394_inst_t *avp, cmd1394_cmd_t *cmd, 320 av1394_cfgrom_parse_arg_t *pa) 321 { 322 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 323 int i; 324 uint64_t entry_addr; 325 uint32_t entry; 326 uint64_t leaf_addr; 327 uint64_t dir_addr; 328 uint16_t dir_len; 329 uint8_t t, k; 330 uint16_t v; 331 uint32_t val; 332 av1394_cfgrom_parse_arg_t this_pa; 333 int ret = 0; 334 335 /* safeguard against deep recursion */ 336 if (pa->pa_depth > AV1394_CFGROM_PARSE_MAX_DEPTH) { 337 return (ENOMEM); 338 } 339 340 /* initialize parse arguments */ 341 this_pa.pa_depth = pa->pa_depth + 1; 342 this_pa.pa_desc_entry = pa->pa_desc_entry; 343 344 /* walk dir entries */ 345 entry_addr = pa->pa_addr; 346 for (i = 0; i < pa->pa_len; i++) { 347 AV1394_CFGROM_RQ(avp, cmd, entry_addr, &entry); 348 entry = AV_SWAP32(entry); 349 350 CFGROM_TYPE_KEY_VALUE(entry, t, k, v); 351 if ((t == IEEE1212_LEAF_TYPE) && 352 (k == IEEE1212_TEXTUAL_DESCRIPTOR)) { 353 /* save this leaf */ 354 leaf_addr = entry_addr + 4 * v; 355 av1394_cfgrom_add_text_leaf(avp, pa->pa_dir, 356 leaf_addr, this_pa.pa_desc_entry); 357 } else if (t == IEEE1212_DIRECTORY_TYPE) { 358 dir_addr = entry_addr + 4 * v; 359 AV1394_CFGROM_RQ(avp, cmd, dir_addr, &val); 360 val = AV_SWAP32(val); 361 dir_len = IEEE1212_DIR_LEN(val); 362 363 /* parse this dir */ 364 this_pa.pa_parent_k = k; 365 this_pa.pa_addr = dir_addr + 4; 366 this_pa.pa_len = dir_len; 367 /* leaves will be added to either root or unit array */ 368 if (k == IEEE1212_UNIT_DIRECTORY) { 369 this_pa.pa_dir = &crp->cr_unit_dir; 370 } else { 371 this_pa.pa_dir = pa->pa_dir; 372 } 373 374 ret = av1394_cfgrom_parse_dir(avp, cmd, &this_pa); 375 if (ret != 0) { 376 goto catch; 377 } 378 } 379 380 /* 381 * if we're walking Textual_Descriptor directory, 382 * the described entry is the one preceding directory's entry, 383 * so we need to preserve what was passed in pa->pa_desc_entry 384 */ 385 if (pa->pa_parent_k != IEEE1212_TEXTUAL_DESCRIPTOR) { 386 this_pa.pa_desc_entry = entry; 387 } 388 entry_addr += 4; 389 } 390 391 catch: 392 return (ret); 393 } 394 395 /*ARGSUSED*/ 396 static void 397 av1394_cfgrom_add_text_leaf(av1394_inst_t *avp, av1394_cfgrom_parsed_dir_t *pd, 398 uint64_t addr, uint32_t desc_entry) 399 { 400 /* grow array of needed */ 401 if (pd->pd_tl_next >= pd->pd_tl_size) { 402 av1394_cfgrom_grow_parsed_dir(pd, 2); 403 } 404 pd->pd_tl[pd->pd_tl_next].tl_addr = addr; 405 pd->pd_tl[pd->pd_tl_next].tl_desc_entry = desc_entry; 406 pd->pd_tl_next++; 407 } 408 409 /* 410 * this routine cleans up after av1394_cfgrom_parse() 411 */ 412 static void 413 av1394_cfgrom_unparse_rom(av1394_inst_t *avp) 414 { 415 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom; 416 av1394_cfgrom_parsed_dir_t *pd; 417 418 pd = &crp->cr_root_dir; 419 if (pd->pd_tl) { 420 kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl)); 421 bzero(pd, sizeof (*pd)); 422 } 423 pd = &crp->cr_unit_dir; 424 if (pd->pd_tl) { 425 kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl)); 426 bzero(pd, sizeof (*pd)); 427 } 428 crp->cr_parsed = B_FALSE; 429 } 430 431 /* 432 * grow parsed dir leaf array by 'cnt' entries 433 */ 434 static void 435 av1394_cfgrom_grow_parsed_dir(av1394_cfgrom_parsed_dir_t *pd, int cnt) 436 { 437 int new_size; 438 void *new_tl; 439 440 ASSERT(cnt > 0); 441 442 new_size = (pd->pd_tl_size + cnt) * sizeof (av1394_cfgrom_text_leaf_t); 443 new_tl = kmem_zalloc(new_size, KM_SLEEP); 444 if (pd->pd_tl_size > 0) { 445 bcopy(pd->pd_tl, new_tl, pd->pd_tl_size * sizeof (*pd->pd_tl)); 446 kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl)); 447 } 448 pd->pd_tl = new_tl; 449 pd->pd_tl_size += cnt; 450 } 451 452 static int 453 av1394_cfgrom_read_leaf(av1394_inst_t *avp, uint64_t leaf_addr, mblk_t **bpp) 454 { 455 cmd1394_cmd_t *cmd; 456 uint64_t addr; 457 uint32_t val; 458 int leaf_len; /* leaf length in quadlets */ 459 mblk_t *bp = NULL; 460 int i; 461 int err; 462 int ret = 0; 463 464 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd); 465 if (err != DDI_SUCCESS) { 466 return (ENOMEM); 467 } 468 469 /* read leaf length */ 470 AV1394_CFGROM_RQ(avp, cmd, leaf_addr, &val); 471 val = AV_SWAP32(val); 472 leaf_len = IEEE1212_DIR_LEN(val); 473 474 if (leaf_len < 3) { 475 ret = EIO; 476 goto catch; 477 } 478 479 if ((bp = allocb(leaf_len * 4, BPRI_HI)) == NULL) { 480 return (ENOMEM); 481 } 482 483 /* read leaf value */ 484 addr = leaf_addr + 4; 485 for (i = 0; i < leaf_len; i++) { 486 AV1394_CFGROM_RQ(avp, cmd, addr, (uint32_t *)bp->b_wptr); 487 bp->b_wptr += 4; 488 addr += 4; 489 } 490 491 catch: 492 if (ret == 0) { 493 *bpp = bp; 494 } else { 495 freemsg(bp); 496 } 497 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd); 498 ASSERT(err == DDI_SUCCESS); 499 500 return (ret); 501 } 502 503 /* 504 * 505 * --- routines involving bus transactions 506 * 507 */ 508 static int 509 av1394_cfgrom_rq(av1394_inst_t *avp, cmd1394_cmd_t *cmd, uint64_t addr, 510 uint32_t *rval) 511 { 512 int err; 513 514 cmd->cmd_type = CMD1394_ASYNCH_RD_QUAD; 515 cmd->cmd_options = CMD1394_BLOCKING; 516 cmd->cmd_addr = addr; 517 518 err = t1394_read(avp->av_t1394_hdl, cmd); 519 if ((err == DDI_SUCCESS) && (cmd->cmd_result == CMD1394_CMDSUCCESS)) { 520 *rval = cmd->cmd_u.q.quadlet_data; 521 return (0); 522 } else { 523 return (EIO); 524 } 525 } 526