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 (c) 1999-2000 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 /* 28 * s1394_isoch.c 29 * 1394 Services Layer Isochronous Communication Routines 30 * This file contains routines for managing isochronous bandwidth 31 * and channel needs for registered targets (through the target 32 * isoch interfaces). 33 */ 34 35 #include <sys/conf.h> 36 #include <sys/ddi.h> 37 #include <sys/sunddi.h> 38 #include <sys/types.h> 39 #include <sys/1394/t1394.h> 40 #include <sys/1394/s1394.h> 41 #include <sys/1394/h1394.h> 42 #include <sys/1394/ieee1394.h> 43 44 /* 45 * s1394_isoch_rsrc_realloc() 46 * is called during bus reset processing to reallocate any isochronous 47 * resources that were previously allocated. 48 */ 49 void 50 s1394_isoch_rsrc_realloc(s1394_hal_t *hal) 51 { 52 s1394_isoch_cec_t *cec_curr; 53 uint32_t chnl_mask; 54 uint32_t old_chnl_mask; 55 uint_t bw_alloc_units; 56 uint_t generation; 57 uint_t chnl_num; 58 int err; 59 int ret; 60 61 /* 62 * Get the current generation number - don't need the 63 * topology tree mutex here because it is read-only, and 64 * there is a race condition with or without it. 65 */ 66 generation = hal->generation_count; 67 68 /* Lock the Isoch CEC list */ 69 mutex_enter(&hal->isoch_cec_list_mutex); 70 71 cec_curr = hal->isoch_cec_list_head; 72 while (cec_curr != NULL) { 73 /* Lock the Isoch CEC member list */ 74 mutex_enter(&cec_curr->isoch_cec_mutex); 75 76 /* Are we supposed to reallocate resources? */ 77 if (!(cec_curr->cec_options & T1394_NO_IRM_ALLOC) && 78 (cec_curr->realloc_valid == B_TRUE) && 79 (cec_curr->realloc_failed == B_FALSE)) { 80 81 /* Reallocate some bandwidth */ 82 bw_alloc_units = s1394_compute_bw_alloc_units(hal, 83 cec_curr->bandwidth, cec_curr->realloc_speed); 84 85 /* Check that the generation has not changed */ 86 if (generation != hal->generation_count) { 87 /* Try the next Isoch CEC */ 88 goto next_isoch_cec; 89 } 90 91 /* Unlock the Isoch CEC member list */ 92 mutex_exit(&cec_curr->isoch_cec_mutex); 93 /* 94 * We can unlock the Isoch CEC list here 95 * because we know this Isoch CEC can not 96 * go away (we are trying to realloc its 97 * resources so it can't be in a state that 98 * will allow a free). 99 */ 100 mutex_exit(&hal->isoch_cec_list_mutex); 101 102 /* Try to reallocate bandwidth */ 103 ret = s1394_bandwidth_alloc(hal, bw_alloc_units, 104 generation, &err); 105 106 /* Lock the Isoch CEC list */ 107 mutex_enter(&hal->isoch_cec_list_mutex); 108 /* Lock the Isoch CEC member list */ 109 mutex_enter(&cec_curr->isoch_cec_mutex); 110 111 /* If we failed because we couldn't get bandwidth */ 112 if (ret == DDI_FAILURE) { 113 cec_curr->realloc_failed = B_TRUE; 114 cec_curr->realloc_fail_reason = 115 T1394_RSRC_BANDWIDTH; 116 } 117 } 118 119 /* Are we supposed to reallocate resources? */ 120 if (!(cec_curr->cec_options & T1394_NO_IRM_ALLOC) && 121 (cec_curr->realloc_valid == B_TRUE) && 122 (cec_curr->realloc_failed == B_FALSE)) { 123 124 /* Reallocate the channel */ 125 chnl_num = cec_curr->realloc_chnl_num; 126 chnl_mask = (1 << ((63 - chnl_num) % 32)); 127 128 /* Unlock the Isoch CEC member list */ 129 mutex_exit(&cec_curr->isoch_cec_mutex); 130 /* 131 * We can unlock the Isoch CEC list here 132 * because we know this Isoch CEC can not 133 * go away (we are trying to realloc its 134 * resources so it can't be in a state that 135 * will allow a free). 136 */ 137 mutex_exit(&hal->isoch_cec_list_mutex); 138 139 if (chnl_num < 32) { 140 ret = s1394_channel_alloc(hal, chnl_mask, 141 generation, S1394_CHANNEL_ALLOC_HI, 142 &old_chnl_mask, &err); 143 } else { 144 ret = s1394_channel_alloc(hal, chnl_mask, 145 generation, S1394_CHANNEL_ALLOC_LO, 146 &old_chnl_mask, &err); 147 } 148 149 /* Lock the Isoch CEC list */ 150 mutex_enter(&hal->isoch_cec_list_mutex); 151 /* Lock the Isoch CEC member list */ 152 mutex_enter(&cec_curr->isoch_cec_mutex); 153 154 if (ret == DDI_FAILURE) { 155 if (err != CMD1394_EBUSRESET) { 156 /* 157 * If we successfully reallocate 158 * bandwidth, and then fail getting 159 * the channel, we need to free up 160 * the bandwidth 161 */ 162 163 /* Try to free up the bandwidth */ 164 ret = s1394_bandwidth_free(hal, 165 bw_alloc_units, generation, &err); 166 /* Try the next Isoch CEC */ 167 goto next_isoch_cec; 168 } 169 cec_curr->realloc_failed = B_TRUE; 170 cec_curr->realloc_fail_reason = 171 T1394_RSRC_CHANNEL; 172 } 173 } 174 next_isoch_cec: 175 /* Unlock the Isoch CEC member list */ 176 mutex_exit(&cec_curr->isoch_cec_mutex); 177 cec_curr = cec_curr->cec_next; 178 } 179 180 /* Unlock the Isoch CEC list */ 181 mutex_exit(&hal->isoch_cec_list_mutex); 182 } 183 184 /* 185 * s1394_isoch_rsrc_realloc_notify() 186 * is called during bus reset processing to notify all targets for 187 * which isochronous resources were not able to be reallocated. 188 */ 189 void 190 s1394_isoch_rsrc_realloc_notify(s1394_hal_t *hal) 191 { 192 s1394_isoch_cec_t *cec_curr; 193 s1394_isoch_cec_member_t *member_curr; 194 t1394_isoch_rsrc_error_t fail_arg; 195 opaque_t evts_arg; 196 s1394_isoch_cec_type_t type; 197 void (*rsrc_fail_callback)(t1394_isoch_cec_handle_t, opaque_t, 198 t1394_isoch_rsrc_error_t); 199 200 /* Lock the Isoch CEC list */ 201 mutex_enter(&hal->isoch_cec_list_mutex); 202 203 /* Notify all targets that failed realloc */ 204 cec_curr = hal->isoch_cec_list_head; 205 while (cec_curr != NULL) { 206 /* Lock the Isoch CEC member list */ 207 mutex_enter(&cec_curr->isoch_cec_mutex); 208 209 /* Do we notify of realloc failure? */ 210 if (!(cec_curr->cec_options & T1394_NO_IRM_ALLOC) && 211 (cec_curr->realloc_valid == B_TRUE) && 212 (cec_curr->realloc_failed == B_TRUE)) { 213 214 /* Reason for realloc failure */ 215 fail_arg = cec_curr->realloc_fail_reason; 216 217 /* Now we are going into the callbacks */ 218 cec_curr->in_fail_callbacks = B_TRUE; 219 220 type = cec_curr->cec_type; 221 222 /* Unlock the Isoch CEC member list */ 223 mutex_exit(&cec_curr->isoch_cec_mutex); 224 /* 225 * We can unlock the Isoch CEC list here 226 * because we have the in_fail_callbacks 227 * field set to B_TRUE. And free will fail 228 * if we are in fail callbacks. 229 */ 230 mutex_exit(&hal->isoch_cec_list_mutex); 231 232 /* Call all of the rsrc_fail_target() callbacks */ 233 /* Start at the head (talker first) and */ 234 /* go toward the tail (listeners last) */ 235 member_curr = cec_curr->cec_member_list_head; 236 while (member_curr != NULL) { 237 rsrc_fail_callback = member_curr-> 238 isoch_cec_evts.rsrc_fail_target; 239 evts_arg = member_curr->isoch_cec_evts_arg; 240 if (rsrc_fail_callback != NULL) { 241 242 if (type == S1394_PEER_TO_PEER) { 243 rsrc_fail_callback( 244 (t1394_isoch_cec_handle_t) 245 cec_curr, evts_arg, 246 fail_arg); 247 } else { 248 rsrc_fail_callback( 249 (t1394_isoch_cec_handle_t) 250 cec_curr, evts_arg, 251 fail_arg); 252 } 253 } 254 member_curr = member_curr->cec_mem_next; 255 } 256 257 /* Lock the Isoch CEC list */ 258 mutex_enter(&hal->isoch_cec_list_mutex); 259 /* Lock the Isoch CEC member list */ 260 mutex_enter(&cec_curr->isoch_cec_mutex); 261 262 /* We are finished with the callbacks */ 263 cec_curr->in_fail_callbacks = B_FALSE; 264 if (cec_curr->cec_want_wakeup == B_TRUE) { 265 cec_curr->cec_want_wakeup = B_FALSE; 266 cv_broadcast(&cec_curr->in_callbacks_cv); 267 } 268 269 /* Set flags back to original state */ 270 cec_curr->realloc_valid = B_FALSE; 271 cec_curr->realloc_failed = B_FALSE; 272 } 273 /* Unlock the Isoch CEC member list */ 274 mutex_exit(&cec_curr->isoch_cec_mutex); 275 cec_curr = cec_curr->cec_next; 276 } 277 278 /* Unlock the Isoch CEC list */ 279 mutex_exit(&hal->isoch_cec_list_mutex); 280 } 281 282 /* 283 * s1394_channel_alloc() 284 * is used to allocate an isochronous channel. A channel mask and 285 * generation are passed. A request is sent to whichever node is the 286 * IRM for the appropriate channels. If it fails because of a bus 287 * reset it can be retried. If it fails for another reason the 288 * channel(s) may not be availble or there may be no IRM. 289 */ 290 int 291 s1394_channel_alloc(s1394_hal_t *hal, uint32_t channel_mask, uint_t generation, 292 uint_t flags, uint32_t *old_channels, int *result) 293 { 294 cmd1394_cmd_t *cmd; 295 uint64_t IRM_ID_addr; 296 uint32_t compare; 297 uint32_t swap; 298 uint32_t old_value; 299 uint_t hal_node_num; 300 uint_t IRM_node; 301 uint_t offset; 302 int ret; 303 int i; 304 int num_retries = S1394_ISOCH_ALLOC_RETRIES; 305 306 /* Lock the topology tree */ 307 mutex_enter(&hal->topology_tree_mutex); 308 309 hal_node_num = IEEE1394_NODE_NUM(hal->node_id); 310 IRM_node = hal->IRM_node; 311 312 /* Unlock the topology tree */ 313 mutex_exit(&hal->topology_tree_mutex); 314 315 /* Make sure there is a valid IRM on the bus */ 316 if (IRM_node == -1) { 317 *result = CMD1394_ERETRIES_EXCEEDED; 318 return (DDI_FAILURE); 319 } 320 321 if (flags & S1394_CHANNEL_ALLOC_HI) { 322 offset = 323 (IEEE1394_SCSR_CHANS_AVAIL_HI & IEEE1394_CSR_OFFSET_MASK); 324 } else { 325 offset = 326 (IEEE1394_SCSR_CHANS_AVAIL_LO & IEEE1394_CSR_OFFSET_MASK); 327 } 328 329 /* Send compare-swap to CHANNELS_AVAILABLE */ 330 /* register on the Isoch Rsrc Mgr */ 331 if (IRM_node == hal_node_num) { 332 /* Local */ 333 i = num_retries; 334 do { 335 (void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private, 336 offset, &old_value); 337 338 /* Check that the generation has not changed */ 339 if (generation != hal->generation_count) { 340 *result = CMD1394_EBUSRESET; 341 return (DDI_FAILURE); 342 } 343 344 compare = old_value; 345 swap = old_value & (~channel_mask); 346 347 ret = HAL_CALL(hal).csr_cswap32( 348 hal->halinfo.hal_private, generation, 349 offset, compare, swap, &old_value); 350 if (ret != DDI_SUCCESS) { 351 *result = CMD1394_EBUSRESET; 352 return (DDI_FAILURE); 353 } 354 355 if ((~old_value & channel_mask) != 0) { 356 *result = CMD1394_ERETRIES_EXCEEDED; 357 return (DDI_FAILURE); 358 } 359 360 if (old_value == compare) { 361 *result = CMD1394_CMDSUCCESS; 362 *old_channels = old_value; 363 364 return (DDI_SUCCESS); 365 } 366 } while (i--); 367 368 *result = CMD1394_ERETRIES_EXCEEDED; 369 return (DDI_FAILURE); 370 371 } else { 372 /* Remote */ 373 if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) { 374 *result = CMD1394_EUNKNOWN_ERROR; 375 return (DDI_FAILURE); 376 } 377 378 cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET | 379 CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING); 380 cmd->cmd_type = CMD1394_ASYNCH_LOCK_32; 381 382 if (flags & S1394_CHANNEL_ALLOC_HI) { 383 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK | 384 IEEE1394_SCSR_CHANS_AVAIL_HI) | 385 (((uint64_t)IRM_node) << 386 IEEE1394_ADDR_PHY_ID_SHIFT); 387 } else { 388 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK | 389 IEEE1394_SCSR_CHANS_AVAIL_LO) | 390 (((uint64_t)IRM_node) << 391 IEEE1394_ADDR_PHY_ID_SHIFT); 392 } 393 394 cmd->cmd_addr = IRM_ID_addr; 395 cmd->bus_generation = generation; 396 cmd->cmd_u.l32.data_value = T1394_DATA32(~channel_mask); 397 cmd->cmd_u.l32.num_retries = num_retries; 398 cmd->cmd_u.l32.lock_type = CMD1394_LOCK_BIT_AND; 399 400 ret = s1394_split_lock_req(hal, NULL, cmd); 401 402 if (ret == DDI_SUCCESS) { 403 if (cmd->cmd_result == CMD1394_CMDSUCCESS) { 404 *old_channels = T1394_DATA32( 405 cmd->cmd_u.l32.old_value); 406 407 if ((~(*old_channels) & channel_mask) != 0) { 408 *result = CMD1394_ERETRIES_EXCEEDED; 409 ret = DDI_FAILURE; 410 } else { 411 *result = cmd->cmd_result; 412 } 413 414 /* Need to free the command */ 415 (void) s1394_free_cmd(hal, &cmd); 416 417 return (ret); 418 419 } else { 420 *result = cmd->cmd_result; 421 /* Need to free the command */ 422 (void) s1394_free_cmd(hal, &cmd); 423 424 return (DDI_FAILURE); 425 } 426 } else { 427 *result = cmd->cmd_result; 428 429 /* Need to free the command */ 430 (void) s1394_free_cmd(hal, &cmd); 431 432 return (DDI_FAILURE); 433 } 434 } 435 } 436 437 /* 438 * s1394_channel_free() 439 * is used to free up an isochronous channel. A channel mask and 440 * generation are passed. A request is sent to whichever node is the 441 * IRM for the appropriate channels. If it fails because of a bus 442 * reset it can be retried. If it fails for another reason the 443 * channel(s) may already be free or there may be no IRM. 444 */ 445 int 446 s1394_channel_free(s1394_hal_t *hal, uint32_t channel_mask, uint_t generation, 447 uint_t flags, uint32_t *old_channels, int *result) 448 { 449 cmd1394_cmd_t *cmd; 450 uint64_t IRM_ID_addr; 451 uint32_t compare; 452 uint32_t swap; 453 uint32_t old_value; 454 uint_t hal_node_num; 455 uint_t IRM_node; 456 uint_t offset; 457 int ret; 458 int i; 459 int num_retries = S1394_ISOCH_ALLOC_RETRIES; 460 461 /* Lock the topology tree */ 462 mutex_enter(&hal->topology_tree_mutex); 463 464 hal_node_num = IEEE1394_NODE_NUM(hal->node_id); 465 IRM_node = hal->IRM_node; 466 467 /* Unlock the topology tree */ 468 mutex_exit(&hal->topology_tree_mutex); 469 470 /* Make sure there is a valid IRM on the bus */ 471 if (IRM_node == -1) { 472 *result = CMD1394_ERETRIES_EXCEEDED; 473 return (DDI_FAILURE); 474 } 475 476 if (flags & S1394_CHANNEL_ALLOC_HI) { 477 offset = 478 (IEEE1394_SCSR_CHANS_AVAIL_HI & IEEE1394_CSR_OFFSET_MASK); 479 } else { 480 offset = 481 (IEEE1394_SCSR_CHANS_AVAIL_LO & IEEE1394_CSR_OFFSET_MASK); 482 } 483 484 /* Send compare-swap to CHANNELS_AVAILABLE */ 485 /* register on the Isoch Rsrc Mgr */ 486 if (hal->IRM_node == hal_node_num) { 487 /* Local */ 488 i = num_retries; 489 do { 490 (void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private, 491 offset, &old_value); 492 493 /* Check that the generation has not changed */ 494 if (generation != hal->generation_count) { 495 *result = CMD1394_EBUSRESET; 496 return (DDI_FAILURE); 497 } 498 499 compare = old_value; 500 swap = old_value | channel_mask; 501 502 ret = HAL_CALL(hal).csr_cswap32( 503 hal->halinfo.hal_private, hal->generation_count, 504 offset, compare, swap, &old_value); 505 if (ret != DDI_SUCCESS) { 506 *result = CMD1394_EBUSRESET; 507 return (DDI_FAILURE); 508 } 509 510 if (old_value == compare) { 511 *result = CMD1394_CMDSUCCESS; 512 *old_channels = old_value; 513 return (DDI_SUCCESS); 514 } 515 } while (i--); 516 517 *result = CMD1394_ERETRIES_EXCEEDED; 518 return (DDI_FAILURE); 519 520 } else { 521 /* Remote */ 522 if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) { 523 *result = CMD1394_EUNKNOWN_ERROR; 524 return (DDI_FAILURE); 525 } 526 527 cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET | 528 CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING); 529 cmd->cmd_type = CMD1394_ASYNCH_LOCK_32; 530 531 if (flags & S1394_CHANNEL_ALLOC_HI) { 532 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK | 533 IEEE1394_SCSR_CHANS_AVAIL_HI) | 534 (((uint64_t)IRM_node) << 535 IEEE1394_ADDR_PHY_ID_SHIFT); 536 } else { 537 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK | 538 IEEE1394_SCSR_CHANS_AVAIL_LO) | 539 (((uint64_t)IRM_node) << 540 IEEE1394_ADDR_PHY_ID_SHIFT); 541 } 542 543 cmd->cmd_addr = IRM_ID_addr; 544 cmd->bus_generation = generation; 545 cmd->cmd_u.l32.data_value = T1394_DATA32(channel_mask); 546 cmd->cmd_u.l32.num_retries = num_retries; 547 cmd->cmd_u.l32.lock_type = CMD1394_LOCK_BIT_OR; 548 549 ret = s1394_split_lock_req(hal, NULL, cmd); 550 551 if (ret == DDI_SUCCESS) { 552 if (cmd->cmd_result == CMD1394_CMDSUCCESS) { 553 554 *old_channels = T1394_DATA32( 555 cmd->cmd_u.l32.old_value); 556 *result = cmd->cmd_result; 557 558 /* Need to free the command */ 559 (void) s1394_free_cmd(hal, &cmd); 560 561 return (DDI_SUCCESS); 562 563 } else { 564 *result = cmd->cmd_result; 565 566 /* Need to free the command */ 567 (void) s1394_free_cmd(hal, &cmd); 568 569 return (DDI_FAILURE); 570 } 571 } else { 572 *result = cmd->cmd_result; 573 /* Need to free the command */ 574 (void) s1394_free_cmd(hal, &cmd); 575 576 return (DDI_FAILURE); 577 } 578 } 579 } 580 581 /* 582 * s1394_bandwidth_alloc() 583 * is used to allocate isochronous bandwidth. A number of bandwidth 584 * allocation units and a generation are passed. The request is sent 585 * to whichever node is the IRM for this amount of bandwidth. If it 586 * fails because of a bus reset it can be retried. If it fails for 587 * another reason the bandwidth may not be available or there may be 588 * no IRM. 589 */ 590 int 591 s1394_bandwidth_alloc(s1394_hal_t *hal, uint32_t bw_alloc_units, 592 uint_t generation, int *result) 593 { 594 cmd1394_cmd_t *cmd; 595 uint64_t IRM_ID_addr; 596 uint32_t compare; 597 uint32_t swap; 598 uint32_t old_value; 599 uint_t hal_node_num; 600 uint_t IRM_node; 601 int temp_value; 602 int ret; 603 int i; 604 int num_retries = S1394_ISOCH_ALLOC_RETRIES; 605 606 /* Lock the topology tree */ 607 mutex_enter(&hal->topology_tree_mutex); 608 609 hal_node_num = IEEE1394_NODE_NUM(hal->node_id); 610 IRM_node = hal->IRM_node; 611 612 /* Unlock the topology tree */ 613 mutex_exit(&hal->topology_tree_mutex); 614 615 /* Make sure there is a valid IRM on the bus */ 616 if (IRM_node == -1) { 617 *result = CMD1394_ERETRIES_EXCEEDED; 618 return (DDI_FAILURE); 619 } 620 621 /* Send compare-swap to BANDWIDTH_AVAILABLE */ 622 /* register on the Isoch Rsrc Mgr */ 623 if (IRM_node == hal_node_num) { 624 /* Local */ 625 i = num_retries; 626 do { 627 (void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private, 628 (IEEE1394_SCSR_BANDWIDTH_AVAIL & 629 IEEE1394_CSR_OFFSET_MASK), &old_value); 630 /* 631 * Check that the generation has not changed - 632 * don't need the lock (read-only) 633 */ 634 if (generation != hal->generation_count) { 635 *result = CMD1394_EBUSRESET; 636 return (DDI_FAILURE); 637 } 638 639 temp_value = (old_value - bw_alloc_units); 640 if ((old_value >= bw_alloc_units) && 641 (temp_value >= IEEE1394_BANDWIDTH_MIN)) { 642 compare = old_value; 643 swap = (uint32_t)temp_value; 644 } else { 645 *result = CMD1394_ERETRIES_EXCEEDED; 646 return (DDI_FAILURE); 647 } 648 649 ret = HAL_CALL(hal).csr_cswap32( 650 hal->halinfo.hal_private, generation, 651 (IEEE1394_SCSR_BANDWIDTH_AVAIL & 652 IEEE1394_CSR_OFFSET_MASK), compare, swap, 653 &old_value); 654 if (ret != DDI_SUCCESS) { 655 *result = CMD1394_EBUSRESET; 656 return (DDI_FAILURE); 657 } 658 659 if (old_value == compare) { 660 *result = CMD1394_CMDSUCCESS; 661 return (DDI_SUCCESS); 662 } 663 } while (i--); 664 665 *result = CMD1394_ERETRIES_EXCEEDED; 666 return (DDI_FAILURE); 667 668 } else { 669 /* Remote */ 670 if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) { 671 *result = CMD1394_EUNKNOWN_ERROR; 672 return (DDI_FAILURE); 673 } 674 675 cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET | 676 CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING); 677 cmd->cmd_type = CMD1394_ASYNCH_LOCK_32; 678 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK | 679 IEEE1394_SCSR_BANDWIDTH_AVAIL) | (((uint64_t)IRM_node) << 680 IEEE1394_ADDR_PHY_ID_SHIFT); 681 cmd->cmd_addr = IRM_ID_addr; 682 cmd->bus_generation = generation; 683 cmd->cmd_u.l32.arg_value = 0; 684 cmd->cmd_u.l32.data_value = bw_alloc_units; 685 cmd->cmd_u.l32.num_retries = num_retries; 686 cmd->cmd_u.l32.lock_type = CMD1394_LOCK_THRESH_SUBTRACT; 687 688 ret = s1394_split_lock_req(hal, NULL, cmd); 689 690 if (ret == DDI_SUCCESS) { 691 if (cmd->cmd_result == CMD1394_CMDSUCCESS) { 692 *result = cmd->cmd_result; 693 /* Need to free the command */ 694 (void) s1394_free_cmd(hal, &cmd); 695 696 return (DDI_SUCCESS); 697 698 } else { 699 *result = cmd->cmd_result; 700 /* Need to free the command */ 701 (void) s1394_free_cmd(hal, &cmd); 702 703 return (DDI_FAILURE); 704 } 705 } else { 706 *result = cmd->cmd_result; 707 /* Need to free the command */ 708 (void) s1394_free_cmd(hal, &cmd); 709 710 return (DDI_FAILURE); 711 } 712 } 713 } 714 715 /* 716 * s1394_compute_bw_alloc_units() 717 * is used to compute the number of "bandwidth allocation units" that 718 * are necessary for a given bit rate. It calculates the overhead 719 * necessary for isoch packet headers, bus arbitration, etc. (See 720 * IEEE 1394-1995 Section 8.3.2.3.7 for an explanation of what a 721 * "bandwidth allocation unit" is. 722 */ 723 uint_t 724 s1394_compute_bw_alloc_units(s1394_hal_t *hal, uint_t bandwidth, uint_t speed) 725 { 726 uint_t total_quads; 727 uint_t speed_factor; 728 uint_t bau; 729 int max_hops; 730 731 /* Lock the topology tree */ 732 mutex_enter(&hal->topology_tree_mutex); 733 734 /* Calculate the 1394 bus diameter */ 735 max_hops = s1394_topology_tree_calculate_diameter(hal); 736 737 /* Unlock the topology tree */ 738 mutex_exit(&hal->topology_tree_mutex); 739 740 /* Calculate the total bandwidth (including overhead) */ 741 total_quads = (bandwidth >> 2) + IEEE1394_ISOCH_HDR_QUAD_SZ; 742 switch (speed) { 743 case IEEE1394_S400: 744 speed_factor = ISOCH_SPEED_FACTOR_S400; 745 break; 746 case IEEE1394_S200: 747 speed_factor = ISOCH_SPEED_FACTOR_S200; 748 break; 749 case IEEE1394_S100: 750 speed_factor = ISOCH_SPEED_FACTOR_S100; 751 break; 752 } 753 /* See IEC 61883-1 pp. 26-29 for this formula */ 754 bau = (32 * max_hops) + (total_quads * speed_factor); 755 756 return (bau); 757 } 758 759 /* 760 * s1394_bandwidth_free() 761 * is used to free up isochronous bandwidth. A number of bandwidth 762 * allocation units and a generation are passed. The request is sent 763 * to whichever node is the IRM for this amount of bandwidth. If it 764 * fails because of a bus reset it can be retried. If it fails for 765 * another reason the bandwidth may already be freed or there may 766 * be no IRM. 767 */ 768 int 769 s1394_bandwidth_free(s1394_hal_t *hal, uint32_t bw_alloc_units, 770 uint_t generation, int *result) 771 { 772 cmd1394_cmd_t *cmd; 773 uint64_t IRM_ID_addr; 774 uint32_t compare; 775 uint32_t swap; 776 uint32_t old_value; 777 uint32_t temp_value; 778 uint_t hal_node_num; 779 uint_t IRM_node; 780 int ret; 781 int i; 782 int num_retries = S1394_ISOCH_ALLOC_RETRIES; 783 784 /* Lock the topology tree */ 785 mutex_enter(&hal->topology_tree_mutex); 786 787 hal_node_num = IEEE1394_NODE_NUM(hal->node_id); 788 IRM_node = hal->IRM_node; 789 790 /* Unlock the topology tree */ 791 mutex_exit(&hal->topology_tree_mutex); 792 793 /* Make sure there is a valid IRM on the bus */ 794 if (IRM_node == -1) { 795 *result = CMD1394_ERETRIES_EXCEEDED; 796 return (DDI_FAILURE); 797 } 798 799 /* Send compare-swap to BANDWIDTH_AVAILABLE */ 800 /* register on the Isoch Rsrc Mgr */ 801 if (IRM_node == hal_node_num) { 802 i = num_retries; 803 do { 804 (void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private, 805 (IEEE1394_SCSR_BANDWIDTH_AVAIL & 806 IEEE1394_CSR_OFFSET_MASK), &old_value); 807 808 /* Check that the generation has not changed */ 809 if (generation != hal->generation_count) { 810 *result = CMD1394_EBUSRESET; 811 return (DDI_FAILURE); 812 } 813 814 temp_value = (old_value + bw_alloc_units); 815 if ((temp_value >= old_value) && 816 (temp_value <= IEEE1394_BANDWIDTH_MAX)) { 817 compare = old_value; 818 swap = temp_value; 819 } else { 820 *result = CMD1394_ERETRIES_EXCEEDED; 821 return (DDI_FAILURE); 822 } 823 824 ret = HAL_CALL(hal).csr_cswap32( 825 hal->halinfo.hal_private, generation, 826 (IEEE1394_SCSR_BANDWIDTH_AVAIL & 827 IEEE1394_CSR_OFFSET_MASK), compare, swap, 828 &old_value); 829 if (ret != DDI_SUCCESS) { 830 *result = CMD1394_EBUSRESET; 831 return (DDI_FAILURE); 832 } 833 834 if (old_value == compare) { 835 *result = CMD1394_CMDSUCCESS; 836 return (DDI_SUCCESS); 837 } 838 } while (i--); 839 840 *result = CMD1394_ERETRIES_EXCEEDED; 841 return (DDI_FAILURE); 842 843 } else { 844 /* Remote */ 845 if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) { 846 *result = CMD1394_EUNKNOWN_ERROR; 847 return (DDI_FAILURE); 848 } 849 850 cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET | 851 CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING); 852 cmd->cmd_type = CMD1394_ASYNCH_LOCK_32; 853 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK | 854 IEEE1394_SCSR_BANDWIDTH_AVAIL) | 855 (((uint64_t)hal->IRM_node) << IEEE1394_ADDR_PHY_ID_SHIFT); 856 cmd->cmd_addr = IRM_ID_addr; 857 cmd->bus_generation = generation; 858 cmd->cmd_u.l32.arg_value = IEEE1394_BANDWIDTH_MAX; 859 cmd->cmd_u.l32.data_value = bw_alloc_units; 860 cmd->cmd_u.l32.num_retries = num_retries; 861 cmd->cmd_u.l32.lock_type = CMD1394_LOCK_THRESH_ADD; 862 863 ret = s1394_split_lock_req(hal, NULL, cmd); 864 865 if (ret == DDI_SUCCESS) { 866 if (cmd->cmd_result == CMD1394_CMDSUCCESS) { 867 *result = cmd->cmd_result; 868 869 /* Need to free the command */ 870 (void) s1394_free_cmd(hal, &cmd); 871 872 return (DDI_SUCCESS); 873 874 } else { 875 *result = cmd->cmd_result; 876 /* Need to free the command */ 877 (void) s1394_free_cmd(hal, &cmd); 878 879 return (DDI_FAILURE); 880 } 881 } else { 882 *result = cmd->cmd_result; 883 /* Need to free the command */ 884 (void) s1394_free_cmd(hal, &cmd); 885 886 return (DDI_FAILURE); 887 } 888 } 889 } 890 891 /* 892 * s1394_isoch_cec_list_insert() 893 * is used to insert an Isoch CEC into a given HAL's list of Isoch CECs. 894 */ 895 void 896 s1394_isoch_cec_list_insert(s1394_hal_t *hal, s1394_isoch_cec_t *cec) 897 { 898 s1394_isoch_cec_t *cec_temp; 899 900 ASSERT(MUTEX_HELD(&hal->isoch_cec_list_mutex)); 901 902 /* Is the Isoch CEC list empty? */ 903 if ((hal->isoch_cec_list_head == NULL) && 904 (hal->isoch_cec_list_tail == NULL)) { 905 906 hal->isoch_cec_list_head = cec; 907 hal->isoch_cec_list_tail = cec; 908 909 cec->cec_next = NULL; 910 cec->cec_prev = NULL; 911 912 } else { 913 cec->cec_next = hal->isoch_cec_list_head; 914 cec->cec_prev = NULL; 915 cec_temp = hal->isoch_cec_list_head; 916 cec_temp->cec_prev = cec; 917 918 hal->isoch_cec_list_head = cec; 919 } 920 } 921 922 /* 923 * s1394_isoch_cec_list_remove() 924 * is used to remove an Isoch CEC from a given HAL's list of Isoch CECs. 925 */ 926 void 927 s1394_isoch_cec_list_remove(s1394_hal_t *hal, s1394_isoch_cec_t *cec) 928 { 929 s1394_isoch_cec_t *prev_cec; 930 s1394_isoch_cec_t *next_cec; 931 932 ASSERT(MUTEX_HELD(&hal->isoch_cec_list_mutex)); 933 934 prev_cec = cec->cec_prev; 935 next_cec = cec->cec_next; 936 cec->cec_prev = NULL; 937 cec->cec_next = NULL; 938 939 if (prev_cec != NULL) { 940 prev_cec->cec_next = next_cec; 941 942 } else { 943 if (hal->isoch_cec_list_head == cec) 944 hal->isoch_cec_list_head = next_cec; 945 } 946 947 if (next_cec != NULL) { 948 next_cec->cec_prev = prev_cec; 949 950 } else { 951 if (hal->isoch_cec_list_tail == cec) 952 hal->isoch_cec_list_tail = prev_cec; 953 } 954 } 955 956 /* 957 * s1394_isoch_cec_member_list_insert() 958 * is used to insert a new member (target) into the list of members for 959 * a given Isoch CEC. 960 */ 961 /* ARGSUSED */ 962 void 963 s1394_isoch_cec_member_list_insert(s1394_hal_t *hal, s1394_isoch_cec_t *cec, 964 s1394_isoch_cec_member_t *member) 965 { 966 s1394_isoch_cec_member_t *member_temp; 967 968 ASSERT(MUTEX_HELD(&cec->isoch_cec_mutex)); 969 970 /* Is the Isoch CEC member list empty? */ 971 if ((cec->cec_member_list_head == NULL) && 972 (cec->cec_member_list_tail == NULL)) { 973 974 cec->cec_member_list_head = member; 975 cec->cec_member_list_tail = member; 976 member->cec_mem_next = NULL; 977 member->cec_mem_prev = NULL; 978 979 } else if (member->cec_mem_options & T1394_TALKER) { 980 /* Put talker at the head of the list */ 981 member->cec_mem_next = cec->cec_member_list_head; 982 member->cec_mem_prev = NULL; 983 member_temp = cec->cec_member_list_head; 984 member_temp->cec_mem_prev = member; 985 cec->cec_member_list_head = member; 986 987 } else { 988 /* Put listeners at the tail of the list */ 989 member->cec_mem_prev = cec->cec_member_list_tail; 990 member->cec_mem_next = NULL; 991 member_temp = cec->cec_member_list_tail; 992 member_temp->cec_mem_next = member; 993 cec->cec_member_list_tail = member; 994 } 995 } 996 997 /* 998 * s1394_isoch_cec_member_list_remove() 999 * is used to remove a member (target) from the list of members for 1000 * a given Isoch CEC. 1001 */ 1002 /* ARGSUSED */ 1003 void 1004 s1394_isoch_cec_member_list_remove(s1394_hal_t *hal, s1394_isoch_cec_t *cec, 1005 s1394_isoch_cec_member_t *member) 1006 { 1007 s1394_isoch_cec_member_t *prev_member; 1008 s1394_isoch_cec_member_t *next_member; 1009 1010 ASSERT(MUTEX_HELD(&cec->isoch_cec_mutex)); 1011 1012 prev_member = member->cec_mem_prev; 1013 next_member = member->cec_mem_next; 1014 1015 member->cec_mem_prev = NULL; 1016 member->cec_mem_next = NULL; 1017 1018 if (prev_member != NULL) { 1019 prev_member->cec_mem_next = next_member; 1020 1021 } else { 1022 if (cec->cec_member_list_head == member) 1023 cec->cec_member_list_head = next_member; 1024 } 1025 1026 if (next_member != NULL) { 1027 next_member->cec_mem_prev = prev_member; 1028 1029 } else { 1030 if (cec->cec_member_list_tail == member) 1031 cec->cec_member_list_tail = prev_member; 1032 } 1033 } 1034