xref: /illumos-gate/usr/src/uts/common/io/1394/s1394_isoch.c (revision 2570281cf351044b6936651ce26dbe1f801dcbd8)
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