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
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]
23 * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
24 * All rights reserved.
27 #pragma ident "%Z%%M% %I% %E% SMI"
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
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.
54 s1394_isoch_rsrc_realloc(s1394_hal_t
*hal
)
56 s1394_isoch_cec_t
*cec_curr
;
58 uint32_t old_chnl_mask
;
59 uint_t bw_alloc_units
;
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 */
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
,
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
);
144 ret
= s1394_channel_alloc(hal
, chnl_mask
,
145 generation
, S1394_CHANNEL_ALLOC_HI
,
146 &old_chnl_mask
, &err
);
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
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 */
176 cec_curr
->realloc_failed
= B_TRUE
;
177 cec_curr
->realloc_fail_reason
=
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.
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
;
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
) {
251 (t1394_isoch_cec_handle_t
)
256 (t1394_isoch_cec_handle_t
)
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
)
302 uint64_t IRM_ID_addr
;
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
) {
330 (IEEE1394_SCSR_CHANS_AVAIL_HI
& IEEE1394_CSR_OFFSET_MASK
);
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
) {
342 (void) HAL_CALL(hal
).csr_read(hal
->halinfo
.hal_private
,
345 /* Check that the generation has not changed */
346 if (generation
!= hal
->generation_count
) {
347 *result
= CMD1394_EBUSRESET
;
348 return (DDI_FAILURE
);
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
);
375 *result
= CMD1394_ERETRIES_EXCEEDED
;
376 return (DDI_FAILURE
);
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
);
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
;
418 *result
= cmd
->cmd_result
;
421 /* Need to free the command */
422 (void) s1394_free_cmd(hal
, &cmd
);
427 *result
= cmd
->cmd_result
;
428 /* Need to free the command */
429 (void) s1394_free_cmd(hal
, &cmd
);
431 return (DDI_FAILURE
);
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
)
457 uint64_t IRM_ID_addr
;
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
) {
485 (IEEE1394_SCSR_CHANS_AVAIL_HI
& IEEE1394_CSR_OFFSET_MASK
);
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
) {
497 (void) HAL_CALL(hal
).csr_read(hal
->halinfo
.hal_private
,
500 /* Check that the generation has not changed */
501 if (generation
!= hal
->generation_count
) {
502 *result
= CMD1394_EBUSRESET
;
503 return (DDI_FAILURE
);
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
);
524 *result
= CMD1394_ERETRIES_EXCEEDED
;
525 return (DDI_FAILURE
);
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
);
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
);
571 *result
= cmd
->cmd_result
;
573 /* Need to free the command */
574 (void) s1394_free_cmd(hal
, &cmd
);
576 return (DDI_FAILURE
);
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
598 s1394_bandwidth_alloc(s1394_hal_t
*hal
, uint32_t bw_alloc_units
,
599 uint_t generation
, int *result
)
602 uint64_t IRM_ID_addr
;
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
) {
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
)) {
650 swap
= (uint32_t)temp_value
;
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
,
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
);
672 *result
= CMD1394_ERETRIES_EXCEEDED
;
673 return (DDI_FAILURE
);
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
);
706 *result
= cmd
->cmd_result
;
707 /* Need to free the command */
708 (void) s1394_free_cmd(hal
, &cmd
);
710 return (DDI_FAILURE
);
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.
731 s1394_compute_bw_alloc_units(s1394_hal_t
*hal
, uint_t bandwidth
, uint_t speed
)
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
;
751 speed_factor
= ISOCH_SPEED_FACTOR_S400
;
754 speed_factor
= ISOCH_SPEED_FACTOR_S200
;
757 speed_factor
= ISOCH_SPEED_FACTOR_S100
;
760 /* See IEC 61883-1 pp. 26-29 for this formula */
761 bau
= (32 * max_hops
) + (total_quads
* speed_factor
);
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
776 s1394_bandwidth_free(s1394_hal_t
*hal
, uint32_t bw_alloc_units
,
777 uint_t generation
, int *result
)
780 uint64_t IRM_ID_addr
;
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
) {
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
)) {
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
,
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
);
847 *result
= CMD1394_ERETRIES_EXCEEDED
;
848 return (DDI_FAILURE
);
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
);
882 *result
= cmd
->cmd_result
;
883 /* Need to free the command */
884 (void) s1394_free_cmd(hal
, &cmd
);
886 return (DDI_FAILURE
);
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.
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
;
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.
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
;
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
;
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
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
;
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.
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
;
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
;
1037 if (cec
->cec_member_list_tail
== member
)
1038 cec
->cec_member_list_tail
= prev_member
;