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
s1394_isoch_rsrc_realloc(s1394_hal_t * hal)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
s1394_isoch_rsrc_realloc_notify(s1394_hal_t * hal)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
s1394_channel_alloc(s1394_hal_t * hal,uint32_t channel_mask,uint_t generation,uint_t flags,uint32_t * old_channels,int * result)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
s1394_channel_free(s1394_hal_t * hal,uint32_t channel_mask,uint_t generation,uint_t flags,uint32_t * old_channels,int * result)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
s1394_bandwidth_alloc(s1394_hal_t * hal,uint32_t bw_alloc_units,uint_t generation,int * result)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
s1394_compute_bw_alloc_units(s1394_hal_t * hal,uint_t bandwidth,uint_t speed)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
s1394_bandwidth_free(s1394_hal_t * hal,uint32_t bw_alloc_units,uint_t generation,int * result)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
s1394_isoch_cec_list_insert(s1394_hal_t * hal,s1394_isoch_cec_t * cec)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
s1394_isoch_cec_list_remove(s1394_hal_t * hal,s1394_isoch_cec_t * cec)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
s1394_isoch_cec_member_list_insert(s1394_hal_t * hal,s1394_isoch_cec_t * cec,s1394_isoch_cec_member_t * member)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
s1394_isoch_cec_member_list_remove(s1394_hal_t * hal,s1394_isoch_cec_t * cec,s1394_isoch_cec_member_t * member)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