Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / 1394 / s1394_isoch.c
blob152320d9b739180e0aaf5679156b53d36e7c146f
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
24 * All rights reserved.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * s1394_isoch.c
31 * 1394 Services Layer Isochronous Communication Routines
32 * This file contains routines for managing isochronous bandwidth
33 * and channel needs for registered targets (through the target
34 * isoch interfaces).
37 #include <sys/conf.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/types.h>
41 #include <sys/tnf_probe.h>
43 #include <sys/1394/t1394.h>
44 #include <sys/1394/s1394.h>
45 #include <sys/1394/h1394.h>
46 #include <sys/1394/ieee1394.h>
49 * s1394_isoch_rsrc_realloc()
50 * is called during bus reset processing to reallocate any isochronous
51 * resources that were previously allocated.
53 void
54 s1394_isoch_rsrc_realloc(s1394_hal_t *hal)
56 s1394_isoch_cec_t *cec_curr;
57 uint32_t chnl_mask;
58 uint32_t old_chnl_mask;
59 uint_t bw_alloc_units;
60 uint_t generation;
61 uint_t chnl_num;
62 int err;
63 int ret;
66 * Get the current generation number - don't need the
67 * topology tree mutex here because it is read-only, and
68 * there is a race condition with or without it.
70 generation = hal->generation_count;
72 /* Lock the Isoch CEC list */
73 mutex_enter(&hal->isoch_cec_list_mutex);
75 cec_curr = hal->isoch_cec_list_head;
76 while (cec_curr != NULL) {
77 /* Lock the Isoch CEC member list */
78 mutex_enter(&cec_curr->isoch_cec_mutex);
80 /* Are we supposed to reallocate resources? */
81 if (!(cec_curr->cec_options & T1394_NO_IRM_ALLOC) &&
82 (cec_curr->realloc_valid == B_TRUE) &&
83 (cec_curr->realloc_failed == B_FALSE)) {
85 /* Reallocate some bandwidth */
86 bw_alloc_units = s1394_compute_bw_alloc_units(hal,
87 cec_curr->bandwidth, cec_curr->realloc_speed);
89 /* Check that the generation has not changed */
90 if (generation != hal->generation_count) {
91 /* Try the next Isoch CEC */
92 goto next_isoch_cec;
95 /* Unlock the Isoch CEC member list */
96 mutex_exit(&cec_curr->isoch_cec_mutex);
98 * We can unlock the Isoch CEC list here
99 * because we know this Isoch CEC can not
100 * go away (we are trying to realloc its
101 * resources so it can't be in a state that
102 * will allow a free).
104 mutex_exit(&hal->isoch_cec_list_mutex);
106 /* Try to reallocate bandwidth */
107 ret = s1394_bandwidth_alloc(hal, bw_alloc_units,
108 generation, &err);
110 /* Lock the Isoch CEC list */
111 mutex_enter(&hal->isoch_cec_list_mutex);
112 /* Lock the Isoch CEC member list */
113 mutex_enter(&cec_curr->isoch_cec_mutex);
115 /* If we failed because we couldn't get bandwidth */
116 if (ret == DDI_FAILURE) {
117 cec_curr->realloc_failed = B_TRUE;
118 cec_curr->realloc_fail_reason =
119 T1394_RSRC_BANDWIDTH;
123 /* Are we supposed to reallocate resources? */
124 if (!(cec_curr->cec_options & T1394_NO_IRM_ALLOC) &&
125 (cec_curr->realloc_valid == B_TRUE) &&
126 (cec_curr->realloc_failed == B_FALSE)) {
128 /* Reallocate the channel */
129 chnl_num = cec_curr->realloc_chnl_num;
130 chnl_mask = (1 << ((63 - chnl_num) % 32));
132 /* Unlock the Isoch CEC member list */
133 mutex_exit(&cec_curr->isoch_cec_mutex);
135 * We can unlock the Isoch CEC list here
136 * because we know this Isoch CEC can not
137 * go away (we are trying to realloc its
138 * resources so it can't be in a state that
139 * will allow a free).
141 mutex_exit(&hal->isoch_cec_list_mutex);
143 if (chnl_num < 32) {
144 ret = s1394_channel_alloc(hal, chnl_mask,
145 generation, S1394_CHANNEL_ALLOC_HI,
146 &old_chnl_mask, &err);
147 } else {
148 ret = s1394_channel_alloc(hal, chnl_mask,
149 generation, S1394_CHANNEL_ALLOC_LO,
150 &old_chnl_mask, &err);
153 /* Lock the Isoch CEC list */
154 mutex_enter(&hal->isoch_cec_list_mutex);
155 /* Lock the Isoch CEC member list */
156 mutex_enter(&cec_curr->isoch_cec_mutex);
158 if (ret == DDI_FAILURE) {
159 if (err != CMD1394_EBUSRESET) {
161 * If we successfully reallocate
162 * bandwidth, and then fail getting
163 * the channel, we need to free up
164 * the bandwidth
167 /* Try to free up the bandwidth */
168 ret = s1394_bandwidth_free(hal,
169 bw_alloc_units, generation, &err);
170 if ((ret == DDI_FAILURE) &&
171 (err != CMD1394_EBUSRESET)) {
173 /* Try the next Isoch CEC */
174 goto next_isoch_cec;
176 cec_curr->realloc_failed = B_TRUE;
177 cec_curr->realloc_fail_reason =
178 T1394_RSRC_CHANNEL;
181 next_isoch_cec:
182 /* Unlock the Isoch CEC member list */
183 mutex_exit(&cec_curr->isoch_cec_mutex);
184 cec_curr = cec_curr->cec_next;
187 /* Unlock the Isoch CEC list */
188 mutex_exit(&hal->isoch_cec_list_mutex);
192 * s1394_isoch_rsrc_realloc_notify()
193 * is called during bus reset processing to notify all targets for
194 * which isochronous resources were not able to be reallocated.
196 void
197 s1394_isoch_rsrc_realloc_notify(s1394_hal_t *hal)
199 s1394_isoch_cec_t *cec_curr;
200 s1394_isoch_cec_member_t *member_curr;
201 t1394_isoch_rsrc_error_t fail_arg;
202 opaque_t evts_arg;
203 s1394_isoch_cec_type_t type;
204 void (*rsrc_fail_callback)(t1394_isoch_cec_handle_t, opaque_t,
205 t1394_isoch_rsrc_error_t);
207 /* Lock the Isoch CEC list */
208 mutex_enter(&hal->isoch_cec_list_mutex);
210 /* Notify all targets that failed realloc */
211 cec_curr = hal->isoch_cec_list_head;
212 while (cec_curr != NULL) {
213 /* Lock the Isoch CEC member list */
214 mutex_enter(&cec_curr->isoch_cec_mutex);
216 /* Do we notify of realloc failure? */
217 if (!(cec_curr->cec_options & T1394_NO_IRM_ALLOC) &&
218 (cec_curr->realloc_valid == B_TRUE) &&
219 (cec_curr->realloc_failed == B_TRUE)) {
221 /* Reason for realloc failure */
222 fail_arg = cec_curr->realloc_fail_reason;
224 /* Now we are going into the callbacks */
225 cec_curr->in_fail_callbacks = B_TRUE;
227 type = cec_curr->cec_type;
229 /* Unlock the Isoch CEC member list */
230 mutex_exit(&cec_curr->isoch_cec_mutex);
232 * We can unlock the Isoch CEC list here
233 * because we have the in_fail_callbacks
234 * field set to B_TRUE. And free will fail
235 * if we are in fail callbacks.
237 mutex_exit(&hal->isoch_cec_list_mutex);
239 /* Call all of the rsrc_fail_target() callbacks */
240 /* Start at the head (talker first) and */
241 /* go toward the tail (listeners last) */
242 member_curr = cec_curr->cec_member_list_head;
243 while (member_curr != NULL) {
244 rsrc_fail_callback = member_curr->
245 isoch_cec_evts.rsrc_fail_target;
246 evts_arg = member_curr->isoch_cec_evts_arg;
247 if (rsrc_fail_callback != NULL) {
249 if (type == S1394_PEER_TO_PEER) {
250 rsrc_fail_callback(
251 (t1394_isoch_cec_handle_t)
252 cec_curr, evts_arg,
253 fail_arg);
254 } else {
255 rsrc_fail_callback(
256 (t1394_isoch_cec_handle_t)
257 cec_curr, evts_arg,
258 fail_arg);
261 member_curr = member_curr->cec_mem_next;
264 /* Lock the Isoch CEC list */
265 mutex_enter(&hal->isoch_cec_list_mutex);
266 /* Lock the Isoch CEC member list */
267 mutex_enter(&cec_curr->isoch_cec_mutex);
269 /* We are finished with the callbacks */
270 cec_curr->in_fail_callbacks = B_FALSE;
271 if (cec_curr->cec_want_wakeup == B_TRUE) {
272 cec_curr->cec_want_wakeup = B_FALSE;
273 cv_broadcast(&cec_curr->in_callbacks_cv);
276 /* Set flags back to original state */
277 cec_curr->realloc_valid = B_FALSE;
278 cec_curr->realloc_failed = B_FALSE;
280 /* Unlock the Isoch CEC member list */
281 mutex_exit(&cec_curr->isoch_cec_mutex);
282 cec_curr = cec_curr->cec_next;
285 /* Unlock the Isoch CEC list */
286 mutex_exit(&hal->isoch_cec_list_mutex);
290 * s1394_channel_alloc()
291 * is used to allocate an isochronous channel. A channel mask and
292 * generation are passed. A request is sent to whichever node is the
293 * IRM for the appropriate channels. If it fails because of a bus
294 * reset it can be retried. If it fails for another reason the
295 * channel(s) may not be availble or there may be no IRM.
298 s1394_channel_alloc(s1394_hal_t *hal, uint32_t channel_mask, uint_t generation,
299 uint_t flags, uint32_t *old_channels, int *result)
301 cmd1394_cmd_t *cmd;
302 uint64_t IRM_ID_addr;
303 uint32_t compare;
304 uint32_t swap;
305 uint32_t old_value;
306 uint_t hal_node_num;
307 uint_t IRM_node;
308 uint_t offset;
309 int ret;
310 int i;
311 int num_retries = S1394_ISOCH_ALLOC_RETRIES;
313 /* Lock the topology tree */
314 mutex_enter(&hal->topology_tree_mutex);
316 hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
317 IRM_node = hal->IRM_node;
319 /* Unlock the topology tree */
320 mutex_exit(&hal->topology_tree_mutex);
322 /* Make sure there is a valid IRM on the bus */
323 if (IRM_node == -1) {
324 *result = CMD1394_ERETRIES_EXCEEDED;
325 return (DDI_FAILURE);
328 if (flags & S1394_CHANNEL_ALLOC_HI) {
329 offset =
330 (IEEE1394_SCSR_CHANS_AVAIL_HI & IEEE1394_CSR_OFFSET_MASK);
331 } else {
332 offset =
333 (IEEE1394_SCSR_CHANS_AVAIL_LO & IEEE1394_CSR_OFFSET_MASK);
336 /* Send compare-swap to CHANNELS_AVAILABLE */
337 /* register on the Isoch Rsrc Mgr */
338 if (IRM_node == hal_node_num) {
339 /* Local */
340 i = num_retries;
341 do {
342 (void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
343 offset, &old_value);
345 /* Check that the generation has not changed */
346 if (generation != hal->generation_count) {
347 *result = CMD1394_EBUSRESET;
348 return (DDI_FAILURE);
351 compare = old_value;
352 swap = old_value & (~channel_mask);
354 ret = HAL_CALL(hal).csr_cswap32(
355 hal->halinfo.hal_private, generation,
356 offset, compare, swap, &old_value);
357 if (ret != DDI_SUCCESS) {
358 *result = CMD1394_EBUSRESET;
359 return (DDI_FAILURE);
362 if ((~old_value & channel_mask) != 0) {
363 *result = CMD1394_ERETRIES_EXCEEDED;
364 return (DDI_FAILURE);
367 if (old_value == compare) {
368 *result = CMD1394_CMDSUCCESS;
369 *old_channels = old_value;
371 return (DDI_SUCCESS);
373 } while (i--);
375 *result = CMD1394_ERETRIES_EXCEEDED;
376 return (DDI_FAILURE);
378 } else {
379 /* Remote */
380 if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
381 *result = CMD1394_EUNKNOWN_ERROR;
382 return (DDI_FAILURE);
385 cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET |
386 CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING);
387 cmd->cmd_type = CMD1394_ASYNCH_LOCK_32;
389 if (flags & S1394_CHANNEL_ALLOC_HI) {
390 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
391 IEEE1394_SCSR_CHANS_AVAIL_HI) |
392 (((uint64_t)IRM_node) <<
393 IEEE1394_ADDR_PHY_ID_SHIFT);
394 } else {
395 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
396 IEEE1394_SCSR_CHANS_AVAIL_LO) |
397 (((uint64_t)IRM_node) <<
398 IEEE1394_ADDR_PHY_ID_SHIFT);
401 cmd->cmd_addr = IRM_ID_addr;
402 cmd->bus_generation = generation;
403 cmd->cmd_u.l32.data_value = T1394_DATA32(~channel_mask);
404 cmd->cmd_u.l32.num_retries = num_retries;
405 cmd->cmd_u.l32.lock_type = CMD1394_LOCK_BIT_AND;
407 ret = s1394_split_lock_req(hal, NULL, cmd);
409 if (ret == DDI_SUCCESS) {
410 if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
411 *old_channels = T1394_DATA32(
412 cmd->cmd_u.l32.old_value);
414 if ((~(*old_channels) & channel_mask) != 0) {
415 *result = CMD1394_ERETRIES_EXCEEDED;
416 ret = DDI_FAILURE;
417 } else {
418 *result = cmd->cmd_result;
421 /* Need to free the command */
422 (void) s1394_free_cmd(hal, &cmd);
424 return (ret);
426 } else {
427 *result = cmd->cmd_result;
428 /* Need to free the command */
429 (void) s1394_free_cmd(hal, &cmd);
431 return (DDI_FAILURE);
433 } else {
434 *result = cmd->cmd_result;
436 /* Need to free the command */
437 (void) s1394_free_cmd(hal, &cmd);
439 return (DDI_FAILURE);
445 * s1394_channel_free()
446 * is used to free up an isochronous channel. A channel mask and
447 * generation are passed. A request is sent to whichever node is the
448 * IRM for the appropriate channels. If it fails because of a bus
449 * reset it can be retried. If it fails for another reason the
450 * channel(s) may already be free or there may be no IRM.
453 s1394_channel_free(s1394_hal_t *hal, uint32_t channel_mask, uint_t generation,
454 uint_t flags, uint32_t *old_channels, int *result)
456 cmd1394_cmd_t *cmd;
457 uint64_t IRM_ID_addr;
458 uint32_t compare;
459 uint32_t swap;
460 uint32_t old_value;
461 uint_t hal_node_num;
462 uint_t IRM_node;
463 uint_t offset;
464 int ret;
465 int i;
466 int num_retries = S1394_ISOCH_ALLOC_RETRIES;
468 /* Lock the topology tree */
469 mutex_enter(&hal->topology_tree_mutex);
471 hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
472 IRM_node = hal->IRM_node;
474 /* Unlock the topology tree */
475 mutex_exit(&hal->topology_tree_mutex);
477 /* Make sure there is a valid IRM on the bus */
478 if (IRM_node == -1) {
479 *result = CMD1394_ERETRIES_EXCEEDED;
480 return (DDI_FAILURE);
483 if (flags & S1394_CHANNEL_ALLOC_HI) {
484 offset =
485 (IEEE1394_SCSR_CHANS_AVAIL_HI & IEEE1394_CSR_OFFSET_MASK);
486 } else {
487 offset =
488 (IEEE1394_SCSR_CHANS_AVAIL_LO & IEEE1394_CSR_OFFSET_MASK);
491 /* Send compare-swap to CHANNELS_AVAILABLE */
492 /* register on the Isoch Rsrc Mgr */
493 if (hal->IRM_node == hal_node_num) {
494 /* Local */
495 i = num_retries;
496 do {
497 (void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
498 offset, &old_value);
500 /* Check that the generation has not changed */
501 if (generation != hal->generation_count) {
502 *result = CMD1394_EBUSRESET;
503 return (DDI_FAILURE);
506 compare = old_value;
507 swap = old_value | channel_mask;
509 ret = HAL_CALL(hal).csr_cswap32(
510 hal->halinfo.hal_private, hal->generation_count,
511 offset, compare, swap, &old_value);
512 if (ret != DDI_SUCCESS) {
513 *result = CMD1394_EBUSRESET;
514 return (DDI_FAILURE);
517 if (old_value == compare) {
518 *result = CMD1394_CMDSUCCESS;
519 *old_channels = old_value;
520 return (DDI_SUCCESS);
522 } while (i--);
524 *result = CMD1394_ERETRIES_EXCEEDED;
525 return (DDI_FAILURE);
527 } else {
528 /* Remote */
529 if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
530 *result = CMD1394_EUNKNOWN_ERROR;
531 return (DDI_FAILURE);
534 cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET |
535 CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING);
536 cmd->cmd_type = CMD1394_ASYNCH_LOCK_32;
538 if (flags & S1394_CHANNEL_ALLOC_HI) {
539 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
540 IEEE1394_SCSR_CHANS_AVAIL_HI) |
541 (((uint64_t)IRM_node) <<
542 IEEE1394_ADDR_PHY_ID_SHIFT);
543 } else {
544 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
545 IEEE1394_SCSR_CHANS_AVAIL_LO) |
546 (((uint64_t)IRM_node) <<
547 IEEE1394_ADDR_PHY_ID_SHIFT);
550 cmd->cmd_addr = IRM_ID_addr;
551 cmd->bus_generation = generation;
552 cmd->cmd_u.l32.data_value = T1394_DATA32(channel_mask);
553 cmd->cmd_u.l32.num_retries = num_retries;
554 cmd->cmd_u.l32.lock_type = CMD1394_LOCK_BIT_OR;
556 ret = s1394_split_lock_req(hal, NULL, cmd);
558 if (ret == DDI_SUCCESS) {
559 if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
561 *old_channels = T1394_DATA32(
562 cmd->cmd_u.l32.old_value);
563 *result = cmd->cmd_result;
565 /* Need to free the command */
566 (void) s1394_free_cmd(hal, &cmd);
568 return (DDI_SUCCESS);
570 } else {
571 *result = cmd->cmd_result;
573 /* Need to free the command */
574 (void) s1394_free_cmd(hal, &cmd);
576 return (DDI_FAILURE);
578 } else {
579 *result = cmd->cmd_result;
580 /* Need to free the command */
581 (void) s1394_free_cmd(hal, &cmd);
583 return (DDI_FAILURE);
589 * s1394_bandwidth_alloc()
590 * is used to allocate isochronous bandwidth. A number of bandwidth
591 * allocation units and a generation are passed. The request is sent
592 * to whichever node is the IRM for this amount of bandwidth. If it
593 * fails because of a bus reset it can be retried. If it fails for
594 * another reason the bandwidth may not be available or there may be
595 * no IRM.
598 s1394_bandwidth_alloc(s1394_hal_t *hal, uint32_t bw_alloc_units,
599 uint_t generation, int *result)
601 cmd1394_cmd_t *cmd;
602 uint64_t IRM_ID_addr;
603 uint32_t compare;
604 uint32_t swap;
605 uint32_t old_value;
606 uint_t hal_node_num;
607 uint_t IRM_node;
608 int temp_value;
609 int ret;
610 int i;
611 int num_retries = S1394_ISOCH_ALLOC_RETRIES;
613 /* Lock the topology tree */
614 mutex_enter(&hal->topology_tree_mutex);
616 hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
617 IRM_node = hal->IRM_node;
619 /* Unlock the topology tree */
620 mutex_exit(&hal->topology_tree_mutex);
622 /* Make sure there is a valid IRM on the bus */
623 if (IRM_node == -1) {
624 *result = CMD1394_ERETRIES_EXCEEDED;
625 return (DDI_FAILURE);
628 /* Send compare-swap to BANDWIDTH_AVAILABLE */
629 /* register on the Isoch Rsrc Mgr */
630 if (IRM_node == hal_node_num) {
631 /* Local */
632 i = num_retries;
633 do {
634 (void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
635 (IEEE1394_SCSR_BANDWIDTH_AVAIL &
636 IEEE1394_CSR_OFFSET_MASK), &old_value);
638 * Check that the generation has not changed -
639 * don't need the lock (read-only)
641 if (generation != hal->generation_count) {
642 *result = CMD1394_EBUSRESET;
643 return (DDI_FAILURE);
646 temp_value = (old_value - bw_alloc_units);
647 if ((old_value >= bw_alloc_units) &&
648 (temp_value >= IEEE1394_BANDWIDTH_MIN)) {
649 compare = old_value;
650 swap = (uint32_t)temp_value;
651 } else {
652 *result = CMD1394_ERETRIES_EXCEEDED;
653 return (DDI_FAILURE);
656 ret = HAL_CALL(hal).csr_cswap32(
657 hal->halinfo.hal_private, generation,
658 (IEEE1394_SCSR_BANDWIDTH_AVAIL &
659 IEEE1394_CSR_OFFSET_MASK), compare, swap,
660 &old_value);
661 if (ret != DDI_SUCCESS) {
662 *result = CMD1394_EBUSRESET;
663 return (DDI_FAILURE);
666 if (old_value == compare) {
667 *result = CMD1394_CMDSUCCESS;
668 return (DDI_SUCCESS);
670 } while (i--);
672 *result = CMD1394_ERETRIES_EXCEEDED;
673 return (DDI_FAILURE);
675 } else {
676 /* Remote */
677 if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
678 *result = CMD1394_EUNKNOWN_ERROR;
679 return (DDI_FAILURE);
682 cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET |
683 CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING);
684 cmd->cmd_type = CMD1394_ASYNCH_LOCK_32;
685 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
686 IEEE1394_SCSR_BANDWIDTH_AVAIL) | (((uint64_t)IRM_node) <<
687 IEEE1394_ADDR_PHY_ID_SHIFT);
688 cmd->cmd_addr = IRM_ID_addr;
689 cmd->bus_generation = generation;
690 cmd->cmd_u.l32.arg_value = 0;
691 cmd->cmd_u.l32.data_value = bw_alloc_units;
692 cmd->cmd_u.l32.num_retries = num_retries;
693 cmd->cmd_u.l32.lock_type = CMD1394_LOCK_THRESH_SUBTRACT;
695 ret = s1394_split_lock_req(hal, NULL, cmd);
697 if (ret == DDI_SUCCESS) {
698 if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
699 *result = cmd->cmd_result;
700 /* Need to free the command */
701 (void) s1394_free_cmd(hal, &cmd);
703 return (DDI_SUCCESS);
705 } else {
706 *result = cmd->cmd_result;
707 /* Need to free the command */
708 (void) s1394_free_cmd(hal, &cmd);
710 return (DDI_FAILURE);
712 } else {
713 *result = cmd->cmd_result;
714 /* Need to free the command */
715 (void) s1394_free_cmd(hal, &cmd);
717 return (DDI_FAILURE);
723 * s1394_compute_bw_alloc_units()
724 * is used to compute the number of "bandwidth allocation units" that
725 * are necessary for a given bit rate. It calculates the overhead
726 * necessary for isoch packet headers, bus arbitration, etc. (See
727 * IEEE 1394-1995 Section 8.3.2.3.7 for an explanation of what a
728 * "bandwidth allocation unit" is.
730 uint_t
731 s1394_compute_bw_alloc_units(s1394_hal_t *hal, uint_t bandwidth, uint_t speed)
733 uint_t total_quads;
734 uint_t speed_factor;
735 uint_t bau;
736 int max_hops;
738 /* Lock the topology tree */
739 mutex_enter(&hal->topology_tree_mutex);
741 /* Calculate the 1394 bus diameter */
742 max_hops = s1394_topology_tree_calculate_diameter(hal);
744 /* Unlock the topology tree */
745 mutex_exit(&hal->topology_tree_mutex);
747 /* Calculate the total bandwidth (including overhead) */
748 total_quads = (bandwidth >> 2) + IEEE1394_ISOCH_HDR_QUAD_SZ;
749 switch (speed) {
750 case IEEE1394_S400:
751 speed_factor = ISOCH_SPEED_FACTOR_S400;
752 break;
753 case IEEE1394_S200:
754 speed_factor = ISOCH_SPEED_FACTOR_S200;
755 break;
756 case IEEE1394_S100:
757 speed_factor = ISOCH_SPEED_FACTOR_S100;
758 break;
760 /* See IEC 61883-1 pp. 26-29 for this formula */
761 bau = (32 * max_hops) + (total_quads * speed_factor);
763 return (bau);
767 * s1394_bandwidth_free()
768 * is used to free up isochronous bandwidth. A number of bandwidth
769 * allocation units and a generation are passed. The request is sent
770 * to whichever node is the IRM for this amount of bandwidth. If it
771 * fails because of a bus reset it can be retried. If it fails for
772 * another reason the bandwidth may already be freed or there may
773 * be no IRM.
776 s1394_bandwidth_free(s1394_hal_t *hal, uint32_t bw_alloc_units,
777 uint_t generation, int *result)
779 cmd1394_cmd_t *cmd;
780 uint64_t IRM_ID_addr;
781 uint32_t compare;
782 uint32_t swap;
783 uint32_t old_value;
784 uint32_t temp_value;
785 uint_t hal_node_num;
786 uint_t IRM_node;
787 int ret;
788 int i;
789 int num_retries = S1394_ISOCH_ALLOC_RETRIES;
791 /* Lock the topology tree */
792 mutex_enter(&hal->topology_tree_mutex);
794 hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
795 IRM_node = hal->IRM_node;
797 /* Unlock the topology tree */
798 mutex_exit(&hal->topology_tree_mutex);
800 /* Make sure there is a valid IRM on the bus */
801 if (IRM_node == -1) {
802 *result = CMD1394_ERETRIES_EXCEEDED;
803 return (DDI_FAILURE);
806 /* Send compare-swap to BANDWIDTH_AVAILABLE */
807 /* register on the Isoch Rsrc Mgr */
808 if (IRM_node == hal_node_num) {
809 i = num_retries;
810 do {
811 (void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
812 (IEEE1394_SCSR_BANDWIDTH_AVAIL &
813 IEEE1394_CSR_OFFSET_MASK), &old_value);
815 /* Check that the generation has not changed */
816 if (generation != hal->generation_count) {
817 *result = CMD1394_EBUSRESET;
818 return (DDI_FAILURE);
821 temp_value = (old_value + bw_alloc_units);
822 if ((temp_value >= old_value) &&
823 (temp_value <= IEEE1394_BANDWIDTH_MAX)) {
824 compare = old_value;
825 swap = temp_value;
826 } else {
827 *result = CMD1394_ERETRIES_EXCEEDED;
828 return (DDI_FAILURE);
831 ret = HAL_CALL(hal).csr_cswap32(
832 hal->halinfo.hal_private, generation,
833 (IEEE1394_SCSR_BANDWIDTH_AVAIL &
834 IEEE1394_CSR_OFFSET_MASK), compare, swap,
835 &old_value);
836 if (ret != DDI_SUCCESS) {
837 *result = CMD1394_EBUSRESET;
838 return (DDI_FAILURE);
841 if (old_value == compare) {
842 *result = CMD1394_CMDSUCCESS;
843 return (DDI_SUCCESS);
845 } while (i--);
847 *result = CMD1394_ERETRIES_EXCEEDED;
848 return (DDI_FAILURE);
850 } else {
851 /* Remote */
852 if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
853 *result = CMD1394_EUNKNOWN_ERROR;
854 return (DDI_FAILURE);
857 cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET |
858 CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING);
859 cmd->cmd_type = CMD1394_ASYNCH_LOCK_32;
860 IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
861 IEEE1394_SCSR_BANDWIDTH_AVAIL) |
862 (((uint64_t)hal->IRM_node) << IEEE1394_ADDR_PHY_ID_SHIFT);
863 cmd->cmd_addr = IRM_ID_addr;
864 cmd->bus_generation = generation;
865 cmd->cmd_u.l32.arg_value = IEEE1394_BANDWIDTH_MAX;
866 cmd->cmd_u.l32.data_value = bw_alloc_units;
867 cmd->cmd_u.l32.num_retries = num_retries;
868 cmd->cmd_u.l32.lock_type = CMD1394_LOCK_THRESH_ADD;
870 ret = s1394_split_lock_req(hal, NULL, cmd);
872 if (ret == DDI_SUCCESS) {
873 if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
874 *result = cmd->cmd_result;
876 /* Need to free the command */
877 (void) s1394_free_cmd(hal, &cmd);
879 return (DDI_SUCCESS);
881 } else {
882 *result = cmd->cmd_result;
883 /* Need to free the command */
884 (void) s1394_free_cmd(hal, &cmd);
886 return (DDI_FAILURE);
888 } else {
889 *result = cmd->cmd_result;
890 /* Need to free the command */
891 (void) s1394_free_cmd(hal, &cmd);
893 return (DDI_FAILURE);
899 * s1394_isoch_cec_list_insert()
900 * is used to insert an Isoch CEC into a given HAL's list of Isoch CECs.
902 void
903 s1394_isoch_cec_list_insert(s1394_hal_t *hal, s1394_isoch_cec_t *cec)
905 s1394_isoch_cec_t *cec_temp;
907 ASSERT(MUTEX_HELD(&hal->isoch_cec_list_mutex));
909 /* Is the Isoch CEC list empty? */
910 if ((hal->isoch_cec_list_head == NULL) &&
911 (hal->isoch_cec_list_tail == NULL)) {
913 hal->isoch_cec_list_head = cec;
914 hal->isoch_cec_list_tail = cec;
916 cec->cec_next = NULL;
917 cec->cec_prev = NULL;
919 } else {
920 cec->cec_next = hal->isoch_cec_list_head;
921 cec->cec_prev = NULL;
922 cec_temp = hal->isoch_cec_list_head;
923 cec_temp->cec_prev = cec;
925 hal->isoch_cec_list_head = cec;
930 * s1394_isoch_cec_list_remove()
931 * is used to remove an Isoch CEC from a given HAL's list of Isoch CECs.
933 void
934 s1394_isoch_cec_list_remove(s1394_hal_t *hal, s1394_isoch_cec_t *cec)
936 s1394_isoch_cec_t *prev_cec;
937 s1394_isoch_cec_t *next_cec;
939 ASSERT(MUTEX_HELD(&hal->isoch_cec_list_mutex));
941 prev_cec = cec->cec_prev;
942 next_cec = cec->cec_next;
943 cec->cec_prev = NULL;
944 cec->cec_next = NULL;
946 if (prev_cec != NULL) {
947 prev_cec->cec_next = next_cec;
949 } else {
950 if (hal->isoch_cec_list_head == cec)
951 hal->isoch_cec_list_head = next_cec;
954 if (next_cec != NULL) {
955 next_cec->cec_prev = prev_cec;
957 } else {
958 if (hal->isoch_cec_list_tail == cec)
959 hal->isoch_cec_list_tail = prev_cec;
964 * s1394_isoch_cec_member_list_insert()
965 * is used to insert a new member (target) into the list of members for
966 * a given Isoch CEC.
968 /* ARGSUSED */
969 void
970 s1394_isoch_cec_member_list_insert(s1394_hal_t *hal, s1394_isoch_cec_t *cec,
971 s1394_isoch_cec_member_t *member)
973 s1394_isoch_cec_member_t *member_temp;
975 ASSERT(MUTEX_HELD(&cec->isoch_cec_mutex));
977 /* Is the Isoch CEC member list empty? */
978 if ((cec->cec_member_list_head == NULL) &&
979 (cec->cec_member_list_tail == NULL)) {
981 cec->cec_member_list_head = member;
982 cec->cec_member_list_tail = member;
983 member->cec_mem_next = NULL;
984 member->cec_mem_prev = NULL;
986 } else if (member->cec_mem_options & T1394_TALKER) {
987 /* Put talker at the head of the list */
988 member->cec_mem_next = cec->cec_member_list_head;
989 member->cec_mem_prev = NULL;
990 member_temp = cec->cec_member_list_head;
991 member_temp->cec_mem_prev = member;
992 cec->cec_member_list_head = member;
994 } else {
995 /* Put listeners at the tail of the list */
996 member->cec_mem_prev = cec->cec_member_list_tail;
997 member->cec_mem_next = NULL;
998 member_temp = cec->cec_member_list_tail;
999 member_temp->cec_mem_next = member;
1000 cec->cec_member_list_tail = member;
1005 * s1394_isoch_cec_member_list_remove()
1006 * is used to remove a member (target) from the list of members for
1007 * a given Isoch CEC.
1009 /* ARGSUSED */
1010 void
1011 s1394_isoch_cec_member_list_remove(s1394_hal_t *hal, s1394_isoch_cec_t *cec,
1012 s1394_isoch_cec_member_t *member)
1014 s1394_isoch_cec_member_t *prev_member;
1015 s1394_isoch_cec_member_t *next_member;
1017 ASSERT(MUTEX_HELD(&cec->isoch_cec_mutex));
1019 prev_member = member->cec_mem_prev;
1020 next_member = member->cec_mem_next;
1022 member->cec_mem_prev = NULL;
1023 member->cec_mem_next = NULL;
1025 if (prev_member != NULL) {
1026 prev_member->cec_mem_next = next_member;
1028 } else {
1029 if (cec->cec_member_list_head == member)
1030 cec->cec_member_list_head = next_member;
1033 if (next_member != NULL) {
1034 next_member->cec_mem_prev = prev_member;
1036 } else {
1037 if (cec->cec_member_list_tail == member)
1038 cec->cec_member_list_tail = prev_member;