xref: /illumos-gate/usr/src/uts/common/io/1394/t1394.c (revision da59b86634f0fe3ed09807da97d108e580ece778)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * t1394.c
28  *    1394 Target Driver Interface
29  *    This file contains all of the 1394 Software Framework routines called
30  *    by target drivers
31  */
32 
33 #include <sys/sysmacros.h>
34 #include <sys/conf.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/types.h>
38 #include <sys/kmem.h>
39 #include <sys/disp.h>
40 #include <sys/1394/t1394.h>
41 #include <sys/1394/s1394.h>
42 #include <sys/1394/h1394.h>
43 #include <sys/1394/ieee1394.h>
44 
45 static int s1394_allow_detach = 0;
46 
47 /*
48  * Function:    t1394_attach()
49  * Input(s):    dip			The dip given to the target driver
50  *					    in it's attach() routine
51  *		version			The version of the target driver -
52  *					    T1394_VERSION_V1
53  *		flags			The flags parameter is unused (for now)
54  *
55  * Output(s):	attachinfo		Used to pass info back to target,
56  *					    including bus generation, local
57  *					    node ID, dma attribute, etc.
58  *		t1394_hdl		The target "handle" to be used for
59  *					    all subsequent calls into the
60  *					    1394 Software Framework
61  *
62  * Description:	t1394_attach() registers the target (based on its dip) with
63  *		the 1394 Software Framework.  It returns the bus_generation,
64  *		local_nodeID, iblock_cookie and other useful information to
65  *		the target, as well as a handle (t1394_hdl) that will be used
66  *		in all subsequent calls into this framework.
67  */
68 /* ARGSUSED */
69 int
t1394_attach(dev_info_t * dip,int version,uint_t flags,t1394_attachinfo_t * attachinfo,t1394_handle_t * t1394_hdl)70 t1394_attach(dev_info_t *dip, int version, uint_t flags,
71     t1394_attachinfo_t *attachinfo, t1394_handle_t *t1394_hdl)
72 {
73 	s1394_hal_t	*hal;
74 	s1394_target_t	*target;
75 	uint_t		dev;
76 	uint_t		curr;
77 	uint_t		unit_dir;
78 	int		hp_node = 0;
79 
80 	ASSERT(t1394_hdl != NULL);
81 	ASSERT(attachinfo != NULL);
82 
83 	*t1394_hdl = NULL;
84 
85 	if (version != T1394_VERSION_V1) {
86 		return (DDI_FAILURE);
87 	}
88 
89 	hal = s1394_dip_to_hal(ddi_get_parent(dip));
90 	if (hal == NULL) {
91 		return (DDI_FAILURE);
92 	}
93 
94 	ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
95 
96 	hp_node = ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
97 	    "hp-node");
98 
99 	/* Allocate space for s1394_target_t */
100 	target = kmem_zalloc(sizeof (s1394_target_t), KM_SLEEP);
101 
102 	mutex_enter(&hal->topology_tree_mutex);
103 
104 	target->target_version = version;
105 
106 	/* Copy in the params */
107 	target->target_dip = dip;
108 	target->on_hal	   = hal;
109 
110 	/* Place the target on the appropriate node */
111 	target->on_node	= NULL;
112 
113 	rw_enter(&target->on_hal->target_list_rwlock, RW_WRITER);
114 	if (hp_node != 0) {
115 		s1394_add_target_to_node(target);
116 		/*
117 		 * on_node can be NULL if the node got unplugged
118 		 * while the target driver is in its attach routine.
119 		 */
120 		if (target->on_node == NULL) {
121 			s1394_remove_target_from_node(target);
122 			rw_exit(&target->on_hal->target_list_rwlock);
123 			mutex_exit(&hal->topology_tree_mutex);
124 			kmem_free(target, sizeof (s1394_target_t));
125 			return (DDI_FAILURE);
126 		}
127 
128 		target->target_state = S1394_TARG_HP_NODE;
129 		if (S1394_NODE_BUS_PWR_CONSUMER(target->on_node) == B_TRUE)
130 			target->target_state |= S1394_TARG_BUS_PWR_CONSUMER;
131 	}
132 
133 	/* Return the current generation */
134 	attachinfo->localinfo.bus_generation = target->on_hal->generation_count;
135 
136 	/* Fill in hal node id */
137 	attachinfo->localinfo.local_nodeID = target->on_hal->node_id;
138 
139 	/* Give the target driver the iblock_cookie */
140 	attachinfo->iblock_cookie = target->on_hal->halinfo.hw_interrupt;
141 
142 	/* Give the target driver the attributes */
143 	attachinfo->acc_attr	= target->on_hal->halinfo.acc_attr;
144 	attachinfo->dma_attr	= target->on_hal->halinfo.dma_attr;
145 
146 	unit_dir = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
147 	    DDI_PROP_DONTPASS, "unit-dir-offset", 0);
148 	target->unit_dir = unit_dir;
149 
150 	/* By default, disable all physical AR requests */
151 	target->physical_arreq_enabled = 0;
152 
153 
154 	/* Get dev_max_payload & current_max_payload */
155 	s1394_get_maxpayload(target, &dev, &curr);
156 	target->dev_max_payload		= dev;
157 	target->current_max_payload	= curr;
158 
159 	/* Add into linked list */
160 	if ((target->on_hal->target_head == NULL) &&
161 	    (target->on_hal->target_tail == NULL)) {
162 		target->on_hal->target_head = target;
163 		target->on_hal->target_tail = target;
164 	} else {
165 		target->on_hal->target_tail->target_next = target;
166 		target->target_prev = target->on_hal->target_tail;
167 		target->on_hal->target_tail = target;
168 	}
169 	rw_exit(&target->on_hal->target_list_rwlock);
170 
171 	/* Fill in services layer private info */
172 	*t1394_hdl = (t1394_handle_t)target;
173 
174 	mutex_exit(&hal->topology_tree_mutex);
175 
176 	return (DDI_SUCCESS);
177 }
178 
179 /*
180  * Function:    t1394_detach()
181  * Input(s):    t1394_hdl		The target "handle" returned by
182  *					    t1394_attach()
183  *		flags			The flags parameter is unused (for now)
184  *
185  * Output(s):	DDI_SUCCESS		Target successfully detached
186  *		DDI_FAILURE		Target failed to detach
187  *
188  * Description:	t1394_detach() unregisters the target from the 1394 Software
189  *		Framework.  t1394_detach() can fail if the target has any
190  *		allocated commands that haven't been freed.
191  */
192 /* ARGSUSED */
193 int
t1394_detach(t1394_handle_t * t1394_hdl,uint_t flags)194 t1394_detach(t1394_handle_t *t1394_hdl, uint_t flags)
195 {
196 	s1394_target_t	*target;
197 	uint_t		num_cmds;
198 
199 	ASSERT(t1394_hdl != NULL);
200 
201 	target = (s1394_target_t *)(*t1394_hdl);
202 
203 	ASSERT(target->on_hal);
204 
205 	mutex_enter(&target->on_hal->topology_tree_mutex);
206 	rw_enter(&target->on_hal->target_list_rwlock, RW_WRITER);
207 
208 	/* How many cmds has this target allocated? */
209 	num_cmds = target->target_num_cmds;
210 
211 	if (num_cmds != 0) {
212 		rw_exit(&target->on_hal->target_list_rwlock);
213 		mutex_exit(&target->on_hal->topology_tree_mutex);
214 		return (DDI_FAILURE);
215 	}
216 
217 	/*
218 	 * Remove from linked lists. Topology tree is already locked
219 	 * so that the node won't go away while we are looking at it.
220 	 */
221 	if ((target->on_hal->target_head == target) &&
222 	    (target->on_hal->target_tail == target)) {
223 		target->on_hal->target_head = NULL;
224 		target->on_hal->target_tail = NULL;
225 	} else {
226 		if (target->target_prev)
227 			target->target_prev->target_next = target->target_next;
228 		if (target->target_next)
229 			target->target_next->target_prev = target->target_prev;
230 		if (target->on_hal->target_head == target)
231 			target->on_hal->target_head = target->target_next;
232 		if (target->on_hal->target_tail == target)
233 			target->on_hal->target_tail = target->target_prev;
234 	}
235 
236 	s1394_remove_target_from_node(target);
237 	rw_exit(&target->on_hal->target_list_rwlock);
238 
239 	mutex_exit(&target->on_hal->topology_tree_mutex);
240 
241 	/* Free memory */
242 	kmem_free(target, sizeof (s1394_target_t));
243 
244 	*t1394_hdl = NULL;
245 
246 	return (DDI_SUCCESS);
247 }
248 
249 /*
250  * Function:    t1394_alloc_cmd()
251  * Input(s):    t1394_hdl		The target "handle" returned by
252  *					    t1394_attach()
253  *		flags			The flags parameter is described below
254  *
255  * Output(s):	cmdp			Pointer to the newly allocated command
256  *
257  * Description:	t1394_alloc_cmd() allocates a command for use with the
258  *		t1394_read(), t1394_write(), or t1394_lock() interfaces
259  *		of the 1394 Software Framework.  By default, t1394_alloc_cmd()
260  *		may sleep while allocating memory for the command structure.
261  *		If this is undesirable, the target may set the
262  *		T1394_ALLOC_CMD_NOSLEEP bit in the flags parameter.  Also,
263  *		this call may fail because a target driver has already
264  *		allocated MAX_NUMBER_ALLOC_CMDS commands.
265  */
266 int
t1394_alloc_cmd(t1394_handle_t t1394_hdl,uint_t flags,cmd1394_cmd_t ** cmdp)267 t1394_alloc_cmd(t1394_handle_t t1394_hdl, uint_t flags, cmd1394_cmd_t **cmdp)
268 {
269 	s1394_hal_t	 *hal;
270 	s1394_target_t	 *target;
271 	s1394_cmd_priv_t *s_priv;
272 	uint_t		 num_cmds;
273 
274 	ASSERT(t1394_hdl != NULL);
275 
276 	target = (s1394_target_t *)t1394_hdl;
277 
278 	/* Find the HAL this target resides on */
279 	hal = target->on_hal;
280 
281 	rw_enter(&hal->target_list_rwlock, RW_WRITER);
282 
283 	/* How many cmds has this target allocated? */
284 	num_cmds = target->target_num_cmds;
285 
286 	if (num_cmds >= MAX_NUMBER_ALLOC_CMDS) {
287 		rw_exit(&hal->target_list_rwlock);
288 		/* kstats - cmd alloc failures */
289 		hal->hal_kstats->cmd_alloc_fail++;
290 		return (DDI_FAILURE);
291 	}
292 
293 	/* Increment the number of cmds this target has allocated? */
294 	target->target_num_cmds = num_cmds + 1;
295 
296 	if (s1394_alloc_cmd(hal, flags, cmdp) != DDI_SUCCESS) {
297 		target->target_num_cmds = num_cmds;	/* Undo increment */
298 		rw_exit(&hal->target_list_rwlock);
299 		/* kstats - cmd alloc failures */
300 		hal->hal_kstats->cmd_alloc_fail++;
301 		return (DDI_FAILURE);
302 	}
303 
304 	rw_exit(&hal->target_list_rwlock);
305 
306 	/* Get the Services Layer private area */
307 	s_priv = S1394_GET_CMD_PRIV(*cmdp);
308 
309 	/* Initialize the command's blocking mutex */
310 	mutex_init(&s_priv->blocking_mutex, NULL, MUTEX_DRIVER,
311 	    hal->halinfo.hw_interrupt);
312 
313 	/* Initialize the command's blocking condition variable */
314 	cv_init(&s_priv->blocking_cv, NULL, CV_DRIVER, NULL);
315 
316 	return (DDI_SUCCESS);
317 }
318 
319 /*
320  * Function:    t1394_free_cmd()
321  * Input(s):    t1394_hdl		The target "handle" returned by
322  *					    t1394_attach()
323  *		flags			The flags parameter is unused (for now)
324  *		cmdp			Pointer to the command to be freed
325  *
326  * Output(s):	DDI_SUCCESS		Target successfully freed command
327  *		DDI_FAILURE		Target failed to free command
328  *
329  * Description:	t1394_free_cmd() attempts to free a command that has previously
330  *		been allocated by the target driver.  It is possible for
331  *		t1394_free_cmd() to fail because the command is currently
332  *		in-use by the 1394 Software Framework.
333  */
334 /* ARGSUSED */
335 int
t1394_free_cmd(t1394_handle_t t1394_hdl,uint_t flags,cmd1394_cmd_t ** cmdp)336 t1394_free_cmd(t1394_handle_t t1394_hdl, uint_t flags, cmd1394_cmd_t **cmdp)
337 {
338 	s1394_hal_t	 *hal;
339 	s1394_target_t	 *target;
340 	s1394_cmd_priv_t *s_priv;
341 	uint_t		 num_cmds;
342 
343 	ASSERT(t1394_hdl != NULL);
344 
345 	target = (s1394_target_t *)t1394_hdl;
346 
347 	/* Find the HAL this target resides on */
348 	hal = target->on_hal;
349 
350 	rw_enter(&hal->target_list_rwlock, RW_WRITER);
351 
352 	/* How many cmds has this target allocated? */
353 	num_cmds = target->target_num_cmds;
354 
355 	if (num_cmds == 0) {
356 		rw_exit(&hal->target_list_rwlock);
357 		ASSERT(num_cmds != 0);
358 		return (DDI_FAILURE);
359 	}
360 
361 	/* Get the Services Layer private area */
362 	s_priv = S1394_GET_CMD_PRIV(*cmdp);
363 
364 	/* Check that command isn't in use */
365 	if (s_priv->cmd_in_use == B_TRUE) {
366 		rw_exit(&hal->target_list_rwlock);
367 		ASSERT(s_priv->cmd_in_use == B_FALSE);
368 		return (DDI_FAILURE);
369 	}
370 
371 	/* Decrement the number of cmds this target has allocated */
372 	target->target_num_cmds--;
373 
374 	rw_exit(&hal->target_list_rwlock);
375 
376 	/* Destroy the command's blocking condition variable */
377 	cv_destroy(&s_priv->blocking_cv);
378 
379 	/* Destroy the command's blocking mutex */
380 	mutex_destroy(&s_priv->blocking_mutex);
381 
382 	kmem_cache_free(hal->hal_kmem_cachep, *cmdp);
383 
384 	/* Command pointer is set to NULL before returning */
385 	*cmdp = NULL;
386 
387 	/* kstats - number of cmd frees */
388 	hal->hal_kstats->cmd_free++;
389 
390 	return (DDI_SUCCESS);
391 }
392 
393 /*
394  * Function:    t1394_read()
395  * Input(s):    t1394_hdl		The target "handle" returned by
396  *					    t1394_attach()
397  *		cmd			Pointer to the command to send
398  *
399  * Output(s):	DDI_SUCCESS		Target successful sent the command
400  *		DDI_FAILURE		Target failed to send command
401  *
402  * Description:	t1394_read() attempts to send an asynchronous read request
403  *		onto the 1394 bus.
404  */
405 int
t1394_read(t1394_handle_t t1394_hdl,cmd1394_cmd_t * cmd)406 t1394_read(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
407 {
408 	s1394_hal_t	  *to_hal;
409 	s1394_target_t	  *target;
410 	s1394_cmd_priv_t  *s_priv;
411 	s1394_hal_state_t state;
412 	int		  ret;
413 	int		  err;
414 
415 	ASSERT(t1394_hdl != NULL);
416 	ASSERT(cmd != NULL);
417 
418 	/* Get the Services Layer private area */
419 	s_priv = S1394_GET_CMD_PRIV(cmd);
420 
421 	/* Is this command currently in use? */
422 	if (s_priv->cmd_in_use == B_TRUE) {
423 		ASSERT(s_priv->cmd_in_use == B_FALSE);
424 		return (DDI_FAILURE);
425 	}
426 
427 	target = (s1394_target_t *)t1394_hdl;
428 
429 	/* Set-up the destination of the command */
430 	to_hal = target->on_hal;
431 
432 	/* No status (default) */
433 	cmd->cmd_result = CMD1394_NOSTATUS;
434 
435 	/* Check for proper command type */
436 	if ((cmd->cmd_type != CMD1394_ASYNCH_RD_QUAD) &&
437 	    (cmd->cmd_type != CMD1394_ASYNCH_RD_BLOCK)) {
438 		cmd->cmd_result = CMD1394_EINVALID_COMMAND;
439 		return (DDI_FAILURE);
440 	}
441 
442 	/* Is this a blocking command on interrupt stack? */
443 	if ((cmd->cmd_options & CMD1394_BLOCKING) &&
444 	    (servicing_interrupt())) {
445 		cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
446 		return (DDI_FAILURE);
447 	}
448 
449 	mutex_enter(&to_hal->topology_tree_mutex);
450 	state = to_hal->hal_state;
451 	if (state != S1394_HAL_NORMAL) {
452 		ret = s1394_HAL_asynch_error(to_hal, cmd, state);
453 		if (ret != CMD1394_CMDSUCCESS) {
454 			cmd->cmd_result = ret;
455 			mutex_exit(&to_hal->topology_tree_mutex);
456 			return (DDI_FAILURE);
457 		}
458 	}
459 
460 	ret = s1394_setup_asynch_command(to_hal, target, cmd,
461 	    S1394_CMD_READ, &err);
462 
463 	/* Command has now been put onto the queue! */
464 	if (ret != DDI_SUCCESS) {
465 		/* Copy error code into result */
466 		cmd->cmd_result = err;
467 		mutex_exit(&to_hal->topology_tree_mutex);
468 		return (DDI_FAILURE);
469 	}
470 
471 	/*
472 	 * If this command was sent during a bus reset,
473 	 * then put it onto the pending Q.
474 	 */
475 	if (state == S1394_HAL_RESET) {
476 		/* Remove cmd from outstanding request Q */
477 		s1394_remove_q_asynch_cmd(to_hal, cmd);
478 		/* Are we on the bus reset event stack? */
479 		if (s1394_on_br_thread(to_hal) == B_TRUE) {
480 			/* Blocking commands are not allowed */
481 			if (cmd->cmd_options & CMD1394_BLOCKING) {
482 				mutex_exit(&to_hal->topology_tree_mutex);
483 				s_priv->cmd_in_use = B_FALSE;
484 				cmd->cmd_result	   = CMD1394_EINVALID_CONTEXT;
485 				return (DDI_FAILURE);
486 			}
487 		}
488 
489 		s1394_pending_q_insert(to_hal, cmd, S1394_PENDING_Q_FRONT);
490 		mutex_exit(&to_hal->topology_tree_mutex);
491 
492 		/* Block (if necessary) */
493 		goto block_on_asynch_cmd;
494 	}
495 	mutex_exit(&to_hal->topology_tree_mutex);
496 
497 	/* Send the command out */
498 	ret = s1394_xfer_asynch_command(to_hal, cmd, &err);
499 
500 	if (ret != DDI_SUCCESS) {
501 		if (err == CMD1394_ESTALE_GENERATION) {
502 			/* Remove cmd from outstanding request Q */
503 			s1394_remove_q_asynch_cmd(to_hal, cmd);
504 			s1394_pending_q_insert(to_hal, cmd,
505 			    S1394_PENDING_Q_FRONT);
506 
507 			/* Block (if necessary) */
508 			goto block_on_asynch_cmd;
509 
510 		} else {
511 			/* Remove cmd from outstanding request Q */
512 			s1394_remove_q_asynch_cmd(to_hal, cmd);
513 
514 			s_priv->cmd_in_use = B_FALSE;
515 
516 			/* Copy error code into result */
517 			cmd->cmd_result    = err;
518 
519 			return (DDI_FAILURE);
520 		}
521 	} else {
522 		/* Block (if necessary) */
523 		goto block_on_asynch_cmd;
524 	}
525 
526 block_on_asynch_cmd:
527 	s1394_block_on_asynch_cmd(cmd);
528 
529 	return (DDI_SUCCESS);
530 }
531 
532 /*
533  * Function:    t1394_write()
534  * Input(s):    t1394_hdl		The target "handle" returned by
535  *					    t1394_attach()
536  *		cmd			Pointer to the command to send
537  *
538  * Output(s):	DDI_SUCCESS		Target successful sent the command
539  *		DDI_FAILURE		Target failed to send command
540  *
541  * Description:	t1394_write() attempts to send an asynchronous write request
542  *		onto the 1394 bus.
543  */
544 int
t1394_write(t1394_handle_t t1394_hdl,cmd1394_cmd_t * cmd)545 t1394_write(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
546 {
547 	s1394_hal_t	  *to_hal;
548 	s1394_target_t	  *target;
549 	s1394_cmd_priv_t  *s_priv;
550 	s1394_hal_state_t state;
551 	int		  ret;
552 	int		  err;
553 
554 	ASSERT(t1394_hdl != NULL);
555 	ASSERT(cmd != NULL);
556 
557 	/* Get the Services Layer private area */
558 	s_priv = S1394_GET_CMD_PRIV(cmd);
559 
560 	/* Is this command currently in use? */
561 	if (s_priv->cmd_in_use == B_TRUE) {
562 		ASSERT(s_priv->cmd_in_use == B_FALSE);
563 		return (DDI_FAILURE);
564 	}
565 
566 	target = (s1394_target_t *)t1394_hdl;
567 
568 	/* Set-up the destination of the command */
569 	to_hal = target->on_hal;
570 
571 	/* Is this an FA request? */
572 	if (s_priv->cmd_ext_type == S1394_CMD_EXT_FA) {
573 		if (S1394_IS_CMD_FCP(s_priv) &&
574 		    (s1394_fcp_write_check_cmd(cmd) != DDI_SUCCESS)) {
575 			return (DDI_FAILURE);
576 		}
577 		s1394_fa_convert_cmd(to_hal, cmd);
578 	}
579 
580 	/* No status (default) */
581 	cmd->cmd_result = CMD1394_NOSTATUS;
582 
583 	/* Check for proper command type */
584 	if ((cmd->cmd_type != CMD1394_ASYNCH_WR_QUAD) &&
585 	    (cmd->cmd_type != CMD1394_ASYNCH_WR_BLOCK)) {
586 		cmd->cmd_result = CMD1394_EINVALID_COMMAND;
587 		s1394_fa_check_restore_cmd(to_hal, cmd);
588 		return (DDI_FAILURE);
589 	}
590 
591 	/* Is this a blocking command on interrupt stack? */
592 	if ((cmd->cmd_options & CMD1394_BLOCKING) &&
593 	    (servicing_interrupt())) {
594 		cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
595 		s1394_fa_check_restore_cmd(to_hal, cmd);
596 		return (DDI_FAILURE);
597 	}
598 
599 	mutex_enter(&to_hal->topology_tree_mutex);
600 	state = to_hal->hal_state;
601 	if (state != S1394_HAL_NORMAL) {
602 		ret = s1394_HAL_asynch_error(to_hal, cmd, state);
603 		if (ret != CMD1394_CMDSUCCESS) {
604 			cmd->cmd_result = ret;
605 			mutex_exit(&to_hal->topology_tree_mutex);
606 			s1394_fa_check_restore_cmd(to_hal, cmd);
607 			return (DDI_FAILURE);
608 		}
609 	}
610 
611 	ret = s1394_setup_asynch_command(to_hal, target, cmd,
612 	    S1394_CMD_WRITE, &err);
613 
614 	/* Command has now been put onto the queue! */
615 	if (ret != DDI_SUCCESS) {
616 		/* Copy error code into result */
617 		cmd->cmd_result = err;
618 		mutex_exit(&to_hal->topology_tree_mutex);
619 		s1394_fa_check_restore_cmd(to_hal, cmd);
620 		return (DDI_FAILURE);
621 	}
622 
623 	/*
624 	 * If this command was sent during a bus reset,
625 	 * then put it onto the pending Q.
626 	 */
627 	if (state == S1394_HAL_RESET) {
628 		/* Remove cmd from outstanding request Q */
629 		s1394_remove_q_asynch_cmd(to_hal, cmd);
630 		/* Are we on the bus reset event stack? */
631 		if (s1394_on_br_thread(to_hal) == B_TRUE) {
632 			/* Blocking commands are not allowed */
633 			if (cmd->cmd_options & CMD1394_BLOCKING) {
634 				mutex_exit(&to_hal->topology_tree_mutex);
635 				s_priv->cmd_in_use = B_FALSE;
636 				cmd->cmd_result    = CMD1394_EINVALID_CONTEXT;
637 				s1394_fa_check_restore_cmd(to_hal, cmd);
638 				return (DDI_FAILURE);
639 			}
640 		}
641 
642 		s1394_pending_q_insert(to_hal, cmd, S1394_PENDING_Q_FRONT);
643 		mutex_exit(&to_hal->topology_tree_mutex);
644 
645 		/* Block (if necessary) */
646 		s1394_block_on_asynch_cmd(cmd);
647 
648 		return (DDI_SUCCESS);
649 	}
650 	mutex_exit(&to_hal->topology_tree_mutex);
651 
652 	/* Send the command out */
653 	ret = s1394_xfer_asynch_command(to_hal, cmd, &err);
654 
655 	if (ret != DDI_SUCCESS) {
656 		if (err == CMD1394_ESTALE_GENERATION) {
657 			/* Remove cmd from outstanding request Q */
658 			s1394_remove_q_asynch_cmd(to_hal, cmd);
659 			s1394_pending_q_insert(to_hal, cmd,
660 			    S1394_PENDING_Q_FRONT);
661 
662 			/* Block (if necessary) */
663 			s1394_block_on_asynch_cmd(cmd);
664 
665 			return (DDI_SUCCESS);
666 		} else {
667 			/* Remove cmd from outstanding request Q */
668 			s1394_remove_q_asynch_cmd(to_hal, cmd);
669 
670 			s_priv->cmd_in_use = B_FALSE;
671 
672 			/* Copy error code into result */
673 			cmd->cmd_result = err;
674 
675 			s1394_fa_check_restore_cmd(to_hal, cmd);
676 			return (DDI_FAILURE);
677 		}
678 	} else {
679 		/* Block (if necessary) */
680 		s1394_block_on_asynch_cmd(cmd);
681 
682 		return (DDI_SUCCESS);
683 	}
684 }
685 
686 /*
687  * Function:    t1394_lock()
688  * Input(s):    t1394_hdl		The target "handle" returned by
689  *					    t1394_attach()
690  *		cmd			Pointer to the command to send
691  *
692  * Output(s):	DDI_SUCCESS		Target successful sent the command
693  *		DDI_FAILURE		Target failed to send command
694  *
695  * Description:	t1394_lock() attempts to send an asynchronous lock request
696  *		onto the 1394 bus.
697  */
698 int
t1394_lock(t1394_handle_t t1394_hdl,cmd1394_cmd_t * cmd)699 t1394_lock(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
700 {
701 	s1394_hal_t	    *to_hal;
702 	s1394_target_t	    *target;
703 	s1394_cmd_priv_t    *s_priv;
704 	s1394_hal_state_t   state;
705 	cmd1394_lock_type_t lock_type;
706 	uint_t		    num_retries;
707 	int		    ret;
708 
709 	ASSERT(t1394_hdl != NULL);
710 	ASSERT(cmd != NULL);
711 
712 	/* Get the Services Layer private area */
713 	s_priv = S1394_GET_CMD_PRIV(cmd);
714 
715 	/* Is this command currently in use? */
716 	if (s_priv->cmd_in_use == B_TRUE) {
717 		ASSERT(s_priv->cmd_in_use == B_FALSE);
718 		return (DDI_FAILURE);
719 	}
720 
721 	target = (s1394_target_t *)t1394_hdl;
722 
723 	/* Set-up the destination of the command */
724 	to_hal = target->on_hal;
725 
726 	mutex_enter(&to_hal->topology_tree_mutex);
727 	state = to_hal->hal_state;
728 	if (state != S1394_HAL_NORMAL) {
729 		ret = s1394_HAL_asynch_error(to_hal, cmd, state);
730 		if (ret != CMD1394_CMDSUCCESS) {
731 			cmd->cmd_result = ret;
732 			mutex_exit(&to_hal->topology_tree_mutex);
733 			return (DDI_FAILURE);
734 		}
735 	}
736 	mutex_exit(&to_hal->topology_tree_mutex);
737 
738 	/* Check for proper command type */
739 	if ((cmd->cmd_type != CMD1394_ASYNCH_LOCK_32) &&
740 	    (cmd->cmd_type != CMD1394_ASYNCH_LOCK_64)) {
741 		cmd->cmd_result = CMD1394_EINVALID_COMMAND;
742 		return (DDI_FAILURE);
743 	}
744 
745 	/* No status (default) */
746 	cmd->cmd_result = CMD1394_NOSTATUS;
747 
748 	/* Is this a blocking command on interrupt stack? */
749 	if ((cmd->cmd_options & CMD1394_BLOCKING) &&
750 	    (servicing_interrupt())) {
751 		cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
752 		return (DDI_FAILURE);
753 	}
754 
755 	if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) {
756 		lock_type	= cmd->cmd_u.l32.lock_type;
757 		num_retries	= cmd->cmd_u.l32.num_retries;
758 	} else {	/* (cmd->cmd_type == CMD1394_ASYNCH_LOCK_64) */
759 		lock_type	= cmd->cmd_u.l64.lock_type;
760 		num_retries	= cmd->cmd_u.l64.num_retries;
761 	}
762 
763 	/* Make sure num_retries is reasonable */
764 	ASSERT(num_retries <= MAX_NUMBER_OF_LOCK_RETRIES);
765 
766 	switch (lock_type) {
767 	case CMD1394_LOCK_MASK_SWAP:
768 	case CMD1394_LOCK_FETCH_ADD:
769 	case CMD1394_LOCK_LITTLE_ADD:
770 	case CMD1394_LOCK_BOUNDED_ADD:
771 	case CMD1394_LOCK_WRAP_ADD:
772 	case CMD1394_LOCK_COMPARE_SWAP:
773 		ret = s1394_compare_swap(to_hal, target, cmd);
774 		break;
775 
776 	case CMD1394_LOCK_BIT_AND:
777 	case CMD1394_LOCK_BIT_OR:
778 	case CMD1394_LOCK_BIT_XOR:
779 	case CMD1394_LOCK_INCREMENT:
780 	case CMD1394_LOCK_DECREMENT:
781 	case CMD1394_LOCK_ADD:
782 	case CMD1394_LOCK_SUBTRACT:
783 	case CMD1394_LOCK_THRESH_ADD:
784 	case CMD1394_LOCK_THRESH_SUBTRACT:
785 	case CMD1394_LOCK_CLIP_ADD:
786 	case CMD1394_LOCK_CLIP_SUBTRACT:
787 		ret = s1394_split_lock_req(to_hal, target, cmd);
788 		break;
789 
790 	default:
791 		cmd->cmd_result = CMD1394_EINVALID_COMMAND;
792 		ret = DDI_FAILURE;
793 		break;
794 	}
795 
796 	return (ret);
797 }
798 
799 /*
800  * Function:    t1394_alloc_addr()
801  * Input(s):    t1394_hdl		The target "handle" returned by
802  *					    t1394_attach()
803  *		addr_allocp		The structure used to specify the type,
804  *					    size, permissions, and callbacks
805  *					    (if any) for the requested block
806  *					    of 1394 address space
807  *		flags			The flags parameter is unused (for now)
808  *
809  * Output(s):	result			Used to pass more specific info back
810  *					    to target
811  *
812  * Description:	t1394_alloc_addr() requests that part of the 1394 Address Space
813  *		on the local node be set aside for this target driver, and
814  *		associated with this address space should be some permissions
815  *		and callbacks.  If the request is unable to be fulfilled,
816  *		t1394_alloc_addr() will return DDI_FAILURE and result will
817  *		indicate the reason.  T1394_EINVALID_PARAM indicates that the
818  *		combination of flags given is invalid, and T1394_EALLOC_ADDR
819  *		indicates that the requested type of address space is
820  *		unavailable.
821  */
822 /* ARGSUSED */
823 int
t1394_alloc_addr(t1394_handle_t t1394_hdl,t1394_alloc_addr_t * addr_allocp,uint_t flags,int * result)824 t1394_alloc_addr(t1394_handle_t t1394_hdl, t1394_alloc_addr_t *addr_allocp,
825     uint_t flags, int *result)
826 {
827 	s1394_hal_t	*hal;
828 	s1394_target_t	*target;
829 	uint64_t	addr_lo;
830 	uint64_t	addr_hi;
831 	int		err;
832 
833 	ASSERT(t1394_hdl != NULL);
834 	ASSERT(addr_allocp != NULL);
835 
836 	target = (s1394_target_t *)t1394_hdl;
837 
838 	/* Find the HAL this target resides on */
839 	hal = target->on_hal;
840 
841 	/* Get the bounds of the request */
842 	addr_lo = addr_allocp->aa_address;
843 	addr_hi = addr_lo + addr_allocp->aa_length;
844 
845 	/* Check combination of flags */
846 	if ((addr_allocp->aa_enable & T1394_ADDR_RDENBL) &&
847 	    (addr_allocp->aa_evts.recv_read_request == NULL) &&
848 	    (addr_allocp->aa_kmem_bufp == NULL)) {
849 		if ((addr_allocp->aa_type != T1394_ADDR_FIXED)	||
850 		    (addr_lo < hal->physical_addr_lo)		||
851 		    (addr_hi > hal->physical_addr_hi)) {
852 
853 			/*
854 			 * Reads are enabled, but target doesn't want to
855 			 * be notified and hasn't given backing store
856 			 */
857 			*result = T1394_EINVALID_PARAM;
858 
859 			/* kstats - addr alloc failures */
860 			hal->hal_kstats->addr_alloc_fail++;
861 			return (DDI_FAILURE);
862 		} else {
863 			addr_allocp->aa_enable &= ~T1394_ADDR_RDENBL;
864 		}
865 	}
866 
867 	if ((addr_allocp->aa_enable & T1394_ADDR_WRENBL) &&
868 	    (addr_allocp->aa_evts.recv_write_request == NULL) &&
869 	    (addr_allocp->aa_kmem_bufp == NULL)) {
870 		if ((addr_allocp->aa_type != T1394_ADDR_FIXED)	||
871 		    (addr_lo < hal->physical_addr_lo)		||
872 		    (addr_hi > hal->physical_addr_hi)) {
873 
874 			/*
875 			 * Writes are enabled, but target doesn't want to
876 			 * be notified and hasn't given backing store
877 			 */
878 			*result = T1394_EINVALID_PARAM;
879 
880 			/* kstats - addr alloc failures */
881 			hal->hal_kstats->addr_alloc_fail++;
882 			return (DDI_FAILURE);
883 		} else {
884 			addr_allocp->aa_enable &= ~T1394_ADDR_WRENBL;
885 		}
886 	}
887 
888 	if ((addr_allocp->aa_enable & T1394_ADDR_LKENBL) &&
889 	    (addr_allocp->aa_evts.recv_lock_request == NULL) &&
890 	    (addr_allocp->aa_kmem_bufp == NULL)) {
891 		if ((addr_allocp->aa_type != T1394_ADDR_FIXED)	||
892 		    (addr_lo < hal->physical_addr_lo)		||
893 		    (addr_hi > hal->physical_addr_hi)) {
894 
895 			/*
896 			 * Locks are enabled, but target doesn't want to
897 			 * be notified and hasn't given backing store
898 			 */
899 			*result = T1394_EINVALID_PARAM;
900 
901 			/* kstats - addr alloc failures */
902 			hal->hal_kstats->addr_alloc_fail++;
903 			return (DDI_FAILURE);
904 		} else {
905 			addr_allocp->aa_enable &= ~T1394_ADDR_LKENBL;
906 		}
907 	}
908 
909 	/* If not T1394_ADDR_FIXED, then allocate a block */
910 	if (addr_allocp->aa_type != T1394_ADDR_FIXED) {
911 		err = s1394_request_addr_blk((s1394_hal_t *)target->on_hal,
912 		    addr_allocp);
913 		if (err != DDI_SUCCESS) {
914 			*result = T1394_EALLOC_ADDR;
915 			/* kstats - addr alloc failures */
916 			hal->hal_kstats->addr_alloc_fail++;
917 		} else {
918 			*result = T1394_NOERROR;
919 		}
920 		return (err);
921 	} else {
922 		err = s1394_claim_addr_blk((s1394_hal_t *)target->on_hal,
923 		    addr_allocp);
924 		if (err != DDI_SUCCESS) {
925 			*result = T1394_EALLOC_ADDR;
926 			/* kstats - addr alloc failures */
927 			hal->hal_kstats->addr_alloc_fail++;
928 		} else {
929 			*result = T1394_NOERROR;
930 			/* If physical, update the AR request counter */
931 			if ((addr_lo >= hal->physical_addr_lo) &&
932 			    (addr_hi <= hal->physical_addr_hi)) {
933 				rw_enter(&hal->target_list_rwlock, RW_WRITER);
934 				target->physical_arreq_enabled++;
935 				rw_exit(&hal->target_list_rwlock);
936 
937 				s1394_physical_arreq_set_one(target);
938 			}
939 		}
940 		return (err);
941 	}
942 }
943 
944 /*
945  * Function:    t1394_free_addr()
946  * Input(s):    t1394_hdl		The target "handle" returned by
947  *					    t1394_attach()
948  *		addr_hdl		The address "handle" returned by the
949  *					   the t1394_alloc_addr() routine
950  *		flags			The flags parameter is unused (for now)
951  *
952  * Output(s):	DDI_SUCCESS		Target successfully freed memory
953  *		DDI_FAILURE		Target failed to free the memory block
954  *
955  * Description:	t1394_free_addr() attempts to free up memory that has been
956  *		allocated by the target using t1394_alloc_addr().
957  */
958 /* ARGSUSED */
959 int
t1394_free_addr(t1394_handle_t t1394_hdl,t1394_addr_handle_t * addr_hdl,uint_t flags)960 t1394_free_addr(t1394_handle_t t1394_hdl, t1394_addr_handle_t *addr_hdl,
961     uint_t flags)
962 {
963 	s1394_addr_space_blk_t	*curr_blk;
964 	s1394_hal_t		*hal;
965 	s1394_target_t		*target;
966 
967 	ASSERT(t1394_hdl != NULL);
968 	ASSERT(addr_hdl != NULL);
969 
970 	target = (s1394_target_t *)t1394_hdl;
971 
972 	/* Find the HAL this target resides on */
973 	hal = target->on_hal;
974 
975 	curr_blk = (s1394_addr_space_blk_t *)(*addr_hdl);
976 
977 	if (s1394_free_addr_blk(hal, curr_blk) != DDI_SUCCESS) {
978 		return (DDI_FAILURE);
979 	}
980 
981 	/* If physical, update the AR request counter */
982 	if (curr_blk->addr_type == T1394_ADDR_FIXED) {
983 		target->physical_arreq_enabled--;
984 		s1394_physical_arreq_clear_one(target);
985 	}
986 
987 	*addr_hdl = NULL;
988 
989 	/* kstats - number of addr frees */
990 	hal->hal_kstats->addr_space_free++;
991 
992 	return (DDI_SUCCESS);
993 }
994 
995 /*
996  * Function:    t1394_recv_request_done()
997  * Input(s):    t1394_hdl		The target "handle" returned by
998  *					    t1394_attach()
999  *		resp			Pointer to the command which the
1000  *					    target received in it's callback
1001  *		flags			The flags parameter is unused (for now)
1002  *
1003  * Output(s):	DDI_SUCCESS		Target successfully returned command
1004  *					    to the 1394 Software Framework,
1005  *					    and, if necessary, sent response
1006  *		DDI_FAILURE		Target failed to return the command to
1007  *					    the 1394 Software Framework
1008  *
1009  * Description:	t1394_recv_request_done() takes the command that is given and
1010  *		determines whether that command requires a response to be
1011  *		sent on the 1394 bus.  If it is necessary and it's response
1012  *		code (cmd_result) has been set appropriately, then a response
1013  *		will be sent.  If no response is necessary (broadcast or
1014  *		posted write), then the command resources are reclaimed.
1015  */
1016 /* ARGSUSED */
1017 int
t1394_recv_request_done(t1394_handle_t t1394_hdl,cmd1394_cmd_t * resp,uint_t flags)1018 t1394_recv_request_done(t1394_handle_t t1394_hdl, cmd1394_cmd_t *resp,
1019     uint_t flags)
1020 {
1021 	s1394_hal_t	 *hal;
1022 	s1394_cmd_priv_t *s_priv;
1023 	h1394_cmd_priv_t *h_priv;
1024 	mblk_t		 *curr_blk;
1025 	size_t		 msgb_len;
1026 	size_t		 size;
1027 	int		 ret;
1028 	boolean_t	 response = B_TRUE;
1029 	boolean_t	 posted_write = B_FALSE;
1030 	boolean_t	 write_cmd = B_FALSE;
1031 	boolean_t	 mblk_too_small;
1032 
1033 	ASSERT(t1394_hdl != NULL);
1034 	ASSERT(resp != NULL);
1035 
1036 	/* Find the HAL this target resides on */
1037 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
1038 
1039 	/* Get the Services Layer private area */
1040 	s_priv = S1394_GET_CMD_PRIV(resp);
1041 
1042 	/* Get a pointer to the HAL private struct */
1043 	h_priv = (h1394_cmd_priv_t *)&s_priv->hal_cmd_private;
1044 
1045 	/* Is this an FA request? */
1046 	if (s_priv->cmd_ext_type == S1394_CMD_EXT_FA) {
1047 		s1394_fa_convert_cmd(hal, resp);
1048 	}
1049 
1050 	/* Is this a write request? */
1051 	if ((resp->cmd_type == CMD1394_ASYNCH_WR_QUAD) ||
1052 	    (resp->cmd_type == CMD1394_ASYNCH_WR_BLOCK)) {
1053 		write_cmd = B_TRUE;
1054 		/* Is this a posted write request? */
1055 		posted_write = s_priv->posted_write;
1056 	}
1057 
1058 	/* If broadcast or posted write cmd, don't send response */
1059 	if ((resp->broadcast == 1) ||
1060 	    ((write_cmd == B_TRUE) && (posted_write == B_TRUE)))
1061 		response = B_FALSE;
1062 
1063 	if (response == B_FALSE) {
1064 		if ((write_cmd == B_TRUE) && (posted_write == B_TRUE)) {
1065 			/* kstats - Posted Write error */
1066 			hal->hal_kstats->arreq_posted_write_error++;
1067 		}
1068 
1069 		/* Free the command - Pass it back to the HAL */
1070 		HAL_CALL(hal).response_complete(hal->halinfo.hal_private, resp,
1071 		    h_priv);
1072 		return (DDI_SUCCESS);
1073 	}
1074 
1075 	ASSERT(response == B_TRUE);
1076 
1077 	/* Verify valid response code */
1078 	switch (resp->cmd_result) {
1079 	case IEEE1394_RESP_COMPLETE:
1080 		/* Is the mblk_t too small? */
1081 		if (resp->cmd_type == CMD1394_ASYNCH_RD_BLOCK) {
1082 			curr_blk = resp->cmd_u.b.data_block;
1083 			size	 = resp->cmd_u.b.blk_length;
1084 			msgb_len = 0;
1085 			mblk_too_small = B_TRUE;
1086 
1087 			if (curr_blk == NULL) {
1088 				/*
1089 				 * Free the command - Pass it back
1090 				 * to the HAL
1091 				 */
1092 				HAL_CALL(hal).response_complete(
1093 				    hal->halinfo.hal_private, resp, h_priv);
1094 				ASSERT(curr_blk != NULL);
1095 				return (DDI_FAILURE);
1096 			}
1097 
1098 			while (curr_blk != NULL) {
1099 				msgb_len +=
1100 				    (curr_blk->b_wptr - curr_blk->b_rptr);
1101 
1102 				if (msgb_len >= size) {
1103 					mblk_too_small = B_FALSE;
1104 					break;
1105 				}
1106 				curr_blk = curr_blk->b_cont;
1107 			}
1108 
1109 			if (mblk_too_small == B_TRUE) {
1110 				/*
1111 				 * Free the command - Pass it back
1112 				 * to the HAL
1113 				 */
1114 				HAL_CALL(hal).response_complete(
1115 				    hal->halinfo.hal_private, resp, h_priv);
1116 				ASSERT(mblk_too_small != B_TRUE);
1117 				return (DDI_FAILURE);
1118 			}
1119 		}
1120 		/* FALLTHROUGH */
1121 	case IEEE1394_RESP_CONFLICT_ERROR:
1122 	case IEEE1394_RESP_DATA_ERROR:
1123 	case IEEE1394_RESP_TYPE_ERROR:
1124 	case IEEE1394_RESP_ADDRESS_ERROR:
1125 		ret = s1394_send_response(hal, resp);
1126 		return (ret);
1127 
1128 	default:
1129 		return (DDI_FAILURE);
1130 	}
1131 }
1132 
1133 
1134 /*
1135  * Function:    t1394_fcp_register_controller()
1136  * Input(s):    t1394_hdl		The target "handle" returned by
1137  *					    t1394_attach()
1138  *		evts			The structure in which the target
1139  *					    specifies its callback routines
1140  *
1141  *		flags			The flags parameter is unused (for now)
1142  *
1143  * Output(s):	DDI_SUCCESS		Successfully registered.
1144  *
1145  *		DDI_FAILURE		Not registered due to failure.
1146  *
1147  * Description:	Used to register the target within the Framework as an FCP
1148  *		controller.
1149  */
1150 /* ARGSUSED */
1151 int
t1394_fcp_register_controller(t1394_handle_t t1394_hdl,t1394_fcp_evts_t * evts,uint_t flags)1152 t1394_fcp_register_controller(t1394_handle_t t1394_hdl, t1394_fcp_evts_t *evts,
1153     uint_t flags)
1154 {
1155 	int		result;
1156 
1157 	ASSERT(t1394_hdl != NULL);
1158 
1159 	result = s1394_fcp_register_ctl((s1394_target_t *)t1394_hdl, evts);
1160 
1161 	return (result);
1162 }
1163 
1164 /*
1165  * Function:    t1394_fcp_unregister_controller()
1166  * Input(s):    t1394_hdl		The target "handle" returned by
1167  *					    t1394_attach()
1168  *
1169  * Output(s):	DDI_SUCCESS		Successfully unregistered.
1170  *
1171  *		DDI_FAILURE		Not unregistered due to failure.
1172  *
1173  * Description:	Used to unregister the target within the Framework as an FCP
1174  *		controller.
1175  */
1176 int
t1394_fcp_unregister_controller(t1394_handle_t t1394_hdl)1177 t1394_fcp_unregister_controller(t1394_handle_t t1394_hdl)
1178 {
1179 	int		result;
1180 
1181 	ASSERT(t1394_hdl != NULL);
1182 
1183 	result = s1394_fcp_unregister_ctl((s1394_target_t *)t1394_hdl);
1184 
1185 	return (result);
1186 }
1187 
1188 /*
1189  * Function:    t1394_fcp_register_target()
1190  * Input(s):    t1394_hdl		The target "handle" returned by
1191  *					    t1394_attach()
1192  *		evts			The structure in which the target
1193  *					    specifies its callback routines
1194  *
1195  *		flags			The flags parameter is unused (for now)
1196  *
1197  * Output(s):	DDI_SUCCESS		Successfully registered.
1198  *
1199  *		DDI_FAILURE		Not registered due to failure.
1200  *
1201  * Description:	Used to register the target within the Framework as an FCP
1202  *		target.
1203  */
1204 /* ARGSUSED */
1205 int
t1394_fcp_register_target(t1394_handle_t t1394_hdl,t1394_fcp_evts_t * evts,uint_t flags)1206 t1394_fcp_register_target(t1394_handle_t t1394_hdl, t1394_fcp_evts_t *evts,
1207     uint_t flags)
1208 {
1209 	int		result;
1210 
1211 	ASSERT(t1394_hdl != NULL);
1212 
1213 	result = s1394_fcp_register_tgt((s1394_target_t *)t1394_hdl, evts);
1214 
1215 	return (result);
1216 }
1217 
1218 /*
1219  * Function:    t1394_fcp_unregister_target()
1220  * Input(s):    t1394_hdl		The target "handle" returned by
1221  *					    t1394_attach()
1222  *
1223  * Output(s):	DDI_SUCCESS		Successfully unregistered.
1224  *
1225  *		DDI_FAILURE		Not unregistered due to failure.
1226  *
1227  * Description:	Used to unregister the target within the Framework as an FCP
1228  *		target.
1229  */
1230 int
t1394_fcp_unregister_target(t1394_handle_t t1394_hdl)1231 t1394_fcp_unregister_target(t1394_handle_t t1394_hdl)
1232 {
1233 	int		result;
1234 
1235 	ASSERT(t1394_hdl != NULL);
1236 
1237 	result = s1394_fcp_unregister_tgt((s1394_target_t *)t1394_hdl);
1238 
1239 	return (result);
1240 }
1241 
1242 /*
1243  * Function:    t1394_cmp_register()
1244  * Input(s):    t1394_hdl		The target "handle" returned by
1245  *					    t1394_attach()
1246  *		evts			The structure in which the target
1247  *					    specifies its callback routines
1248  *
1249  * Output(s):	DDI_SUCCESS		Successfully registered.
1250  *
1251  *		DDI_FAILURE		Not registered due to failure.
1252  *
1253  * Description:	Used to register the target within the Framework as a CMP
1254  *		device.
1255  */
1256 /* ARGSUSED */
1257 int
t1394_cmp_register(t1394_handle_t t1394_hdl,t1394_cmp_evts_t * evts,uint_t flags)1258 t1394_cmp_register(t1394_handle_t t1394_hdl, t1394_cmp_evts_t *evts,
1259     uint_t flags)
1260 {
1261 	int		result;
1262 
1263 	ASSERT(t1394_hdl != NULL);
1264 
1265 	result = s1394_cmp_register((s1394_target_t *)t1394_hdl, evts);
1266 
1267 	return (result);
1268 }
1269 
1270 /*
1271  * Function:    t1394_cmp_unregister()
1272  * Input(s):    t1394_hdl		The target "handle" returned by
1273  *					    t1394_attach()
1274  *		evts			The structure in which the target
1275  *					    specifies its callback routines
1276  *
1277  * Output(s):	DDI_SUCCESS		Successfully registered.
1278  *
1279  *		DDI_FAILURE		Not registered due to failure.
1280  *
1281  * Description:	Used to unregister the target within the Framework as a CMP
1282  *		device.
1283  */
1284 int
t1394_cmp_unregister(t1394_handle_t t1394_hdl)1285 t1394_cmp_unregister(t1394_handle_t t1394_hdl)
1286 {
1287 	int		result;
1288 
1289 	ASSERT(t1394_hdl != NULL);
1290 
1291 	result = s1394_cmp_unregister((s1394_target_t *)t1394_hdl);
1292 
1293 	return (result);
1294 }
1295 
1296 /*
1297  * Function:    t1394_cmp_read()
1298  * Input(s):    t1394_hdl		The target "handle" returned by
1299  *					    t1394_attach()
1300  *		reg			Register type.
1301  *		valp			Returned register value.
1302  *
1303  * Output(s):	DDI_SUCCESS		Successfully registered.
1304  *
1305  *		DDI_FAILURE		Not registered due to failure.
1306  *
1307  * Description:	Used to read a CMP register value.
1308  */
1309 int
t1394_cmp_read(t1394_handle_t t1394_hdl,t1394_cmp_reg_t reg,uint32_t * valp)1310 t1394_cmp_read(t1394_handle_t t1394_hdl, t1394_cmp_reg_t reg, uint32_t *valp)
1311 {
1312 	int		result;
1313 
1314 	ASSERT(t1394_hdl != NULL);
1315 
1316 	result = s1394_cmp_read((s1394_target_t *)t1394_hdl, reg, valp);
1317 
1318 	return (result);
1319 }
1320 
1321 /*
1322  * Function:    t1394_cmp_cas()
1323  * Input(s):    t1394_hdl		The target "handle" returned by
1324  *					    t1394_attach()
1325  *		reg			Register type.
1326  *		arg_val			Compare argument.
1327  *		new_val			New register value.
1328  *		old_valp		Returned original register value.
1329  *
1330  * Output(s):	DDI_SUCCESS		Successfully registered.
1331  *
1332  *		DDI_FAILURE		Not registered due to failure.
1333  *
1334  * Description:	Used to compare-swap a CMP register value.
1335  */
1336 int
t1394_cmp_cas(t1394_handle_t t1394_hdl,t1394_cmp_reg_t reg,uint32_t arg_val,uint32_t new_val,uint32_t * old_valp)1337 t1394_cmp_cas(t1394_handle_t t1394_hdl, t1394_cmp_reg_t reg, uint32_t arg_val,
1338     uint32_t new_val, uint32_t *old_valp)
1339 {
1340 	int		result;
1341 
1342 	ASSERT(t1394_hdl != NULL);
1343 
1344 	result = s1394_cmp_cas((s1394_target_t *)t1394_hdl, reg, arg_val,
1345 	    new_val, old_valp);
1346 
1347 	return (result);
1348 }
1349 
1350 /*
1351  * Function:    t1394_alloc_isoch_single()
1352  * Input(s):    t1394_hdl		The target "handle" returned by
1353  *					    t1394_attach()
1354  *		sii			The structure used to set up the
1355  *					    overall characteristics of the
1356  *					    isochronous stream
1357  *		flags			The flags parameter is unused (for now)
1358  *
1359  * Output(s):	setup_args		Contains the channel number that was
1360  *					    allocated
1361  *		t1394_single_hdl	This in the isoch "handle" used in
1362  *					    t1394_free_isoch_single()
1363  *		result			Used to pass more specific info back
1364  *					    to target
1365  *
1366  * Description:	t1394_alloc_isoch_single() is used to direct the 1394 Software
1367  *		Framework to allocate an isochronous channel and bandwidth
1368  *		from the Isochronous Resource Manager (IRM).  If a bus reset
1369  *		occurs, the 1394 Software Framework attempts to reallocate the
1370  *		same resources, calling the rsrc_fail_target() callback if
1371  *		it is unsuccessful.
1372  */
1373 /* ARGSUSED */
1374 int
t1394_alloc_isoch_single(t1394_handle_t t1394_hdl,t1394_isoch_singleinfo_t * sii,uint_t flags,t1394_isoch_single_out_t * output_args,t1394_isoch_single_handle_t * t1394_single_hdl,int * result)1375 t1394_alloc_isoch_single(t1394_handle_t t1394_hdl,
1376     t1394_isoch_singleinfo_t *sii, uint_t flags,
1377     t1394_isoch_single_out_t *output_args,
1378     t1394_isoch_single_handle_t	*t1394_single_hdl, int *result)
1379 {
1380 	s1394_hal_t		*hal;
1381 	s1394_isoch_cec_t	*cec_new;
1382 	t1394_join_isochinfo_t	jii;
1383 	int			ret;
1384 	int			err;
1385 
1386 	ASSERT(t1394_hdl != NULL);
1387 	ASSERT(t1394_single_hdl != NULL);
1388 	ASSERT(sii != NULL);
1389 
1390 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
1391 
1392 	/* Check for invalid channel_mask */
1393 	if (sii->si_channel_mask == 0) {
1394 		return (DDI_FAILURE);
1395 	}
1396 
1397 	/* Check for invalid bandwidth */
1398 	if ((sii->si_bandwidth <= IEEE1394_BANDWIDTH_MIN) ||
1399 	    (sii->si_bandwidth > IEEE1394_BANDWIDTH_MAX)) {
1400 		return (DDI_FAILURE);
1401 	}
1402 
1403 	/* Verify that rsrc_fail_target() callback is non-NULL */
1404 	if (sii->rsrc_fail_target == NULL) {
1405 		return (DDI_FAILURE);
1406 	}
1407 
1408 	/*
1409 	 * Allocate an Isoch CEC of type S1394_SINGLE
1410 	 */
1411 
1412 	/* Allocate the Isoch CEC structure */
1413 	cec_new = kmem_zalloc(sizeof (s1394_isoch_cec_t), KM_SLEEP);
1414 
1415 	/* Initialize the structure type */
1416 	cec_new->cec_type = S1394_SINGLE;
1417 
1418 	/* Create the mutex and "in_callbacks" cv */
1419 	mutex_init(&cec_new->isoch_cec_mutex, NULL, MUTEX_DRIVER,
1420 	    hal->halinfo.hw_interrupt);
1421 	cv_init(&cec_new->in_callbacks_cv, NULL, CV_DRIVER,
1422 	    hal->halinfo.hw_interrupt);
1423 
1424 	/* Initialize the Isoch CEC's member list */
1425 	cec_new->cec_member_list_head = NULL;
1426 	cec_new->cec_member_list_tail = NULL;
1427 
1428 	/* Initialize the filters */
1429 	cec_new->filter_min_speed	= sii->si_speed;
1430 	cec_new->filter_max_speed	= sii->si_speed;
1431 	cec_new->filter_current_speed	= cec_new->filter_max_speed;
1432 	cec_new->filter_channel_mask	= sii->si_channel_mask;
1433 	cec_new->bandwidth		= sii->si_bandwidth;
1434 	cec_new->state_transitions	= ISOCH_CEC_FREE | ISOCH_CEC_JOIN |
1435 	    ISOCH_CEC_SETUP;
1436 
1437 	mutex_enter(&hal->isoch_cec_list_mutex);
1438 
1439 	/* Insert Isoch CEC into the HAL's list */
1440 	s1394_isoch_cec_list_insert(hal, cec_new);
1441 
1442 	mutex_exit(&hal->isoch_cec_list_mutex);
1443 
1444 	/*
1445 	 * Join the newly created Isoch CEC
1446 	 */
1447 	jii.req_channel_mask	= sii->si_channel_mask;
1448 	jii.req_max_speed	= sii->si_speed;
1449 	jii.jii_options		= T1394_TALKER;
1450 	jii.isoch_cec_evts_arg	= sii->single_evt_arg;
1451 
1452 	/* All events are NULL except rsrc_fail_target() */
1453 	jii.isoch_cec_evts.setup_target	    = NULL;
1454 	jii.isoch_cec_evts.start_target	    = NULL;
1455 	jii.isoch_cec_evts.stop_target	    = NULL;
1456 	jii.isoch_cec_evts.stop_target	    = NULL;
1457 	jii.isoch_cec_evts.teardown_target  = NULL;
1458 	jii.isoch_cec_evts.rsrc_fail_target = sii->rsrc_fail_target;
1459 
1460 	ret = t1394_join_isoch_cec(t1394_hdl,
1461 	    (t1394_isoch_cec_handle_t)cec_new, 0, &jii);
1462 
1463 	if (ret != DDI_SUCCESS) {
1464 		ret = t1394_free_isoch_cec(t1394_hdl, flags,
1465 		    (t1394_isoch_cec_handle_t *)&cec_new);
1466 		if (ret != DDI_SUCCESS) {
1467 			/* Unable to free the Isoch CEC */
1468 			ASSERT(0);
1469 		}
1470 
1471 		/* Handle is nulled out before returning */
1472 		*t1394_single_hdl = NULL;
1473 
1474 		return (DDI_FAILURE);
1475 	}
1476 
1477 	/*
1478 	 * Setup the isoch resources, etc.
1479 	 */
1480 	ret = t1394_setup_isoch_cec(t1394_hdl,
1481 	    (t1394_isoch_cec_handle_t)cec_new, 0, &err);
1482 
1483 	if (ret != DDI_SUCCESS) {
1484 		*result = err;
1485 
1486 		/* Leave the Isoch CEC */
1487 		ret = t1394_leave_isoch_cec(t1394_hdl,
1488 		    (t1394_isoch_cec_handle_t)cec_new, 0);
1489 		if (ret != DDI_SUCCESS) {
1490 			/* Unable to leave the Isoch CEC */
1491 			ASSERT(0);
1492 		}
1493 
1494 		/* Free up the Isoch CEC */
1495 		ret = t1394_free_isoch_cec(t1394_hdl, flags,
1496 		    (t1394_isoch_cec_handle_t *)&cec_new);
1497 		if (ret != DDI_SUCCESS) {
1498 			/* Unable to free the Isoch CEC */
1499 			ASSERT(0);
1500 		}
1501 
1502 		/* Handle is nulled out before returning */
1503 		*t1394_single_hdl = NULL;
1504 
1505 		return (DDI_FAILURE);
1506 	}
1507 
1508 	/* Return the setup_args - channel num and speed */
1509 	mutex_enter(&cec_new->isoch_cec_mutex);
1510 	output_args->channel_num  = cec_new->realloc_chnl_num;
1511 	mutex_exit(&cec_new->isoch_cec_mutex);
1512 
1513 	/* Update the handle */
1514 	*t1394_single_hdl = (t1394_isoch_single_handle_t)cec_new;
1515 
1516 	return (DDI_SUCCESS);
1517 }
1518 
1519 /*
1520  * Function:    t1394_free_isoch_single()
1521  * Input(s):    t1394_hdl		The target "handle" returned by
1522  *					    t1394_attach()
1523  *		t1394_single_hdl	The isoch "handle" return by
1524  *					    t1394_alloc_isoch_single()
1525  *		flags			The flags parameter is unused (for now)
1526  *
1527  * Output(s):	None
1528  *
1529  * Description:	t1394_free_isoch_single() frees the isochronous resources
1530  *		and the handle that were allocated during the call to
1531  *		t1394_alloc_isoch_single().
1532  */
1533 /* ARGSUSED */
1534 void
t1394_free_isoch_single(t1394_handle_t t1394_hdl,t1394_isoch_single_handle_t * t1394_single_hdl,uint_t flags)1535 t1394_free_isoch_single(t1394_handle_t t1394_hdl,
1536     t1394_isoch_single_handle_t *t1394_single_hdl, uint_t flags)
1537 {
1538 	s1394_isoch_cec_t *cec_curr;
1539 	int		  ret;
1540 
1541 	ASSERT(t1394_hdl != NULL);
1542 	ASSERT(t1394_single_hdl != NULL);
1543 
1544 	/* Convert the handle to an Isoch CEC pointer */
1545 	cec_curr = (s1394_isoch_cec_t *)(*t1394_single_hdl);
1546 
1547 	/*
1548 	 * Teardown the isoch resources, etc.
1549 	 */
1550 	ret = t1394_teardown_isoch_cec(t1394_hdl,
1551 	    (t1394_isoch_cec_handle_t)cec_curr, 0);
1552 	if (ret != DDI_SUCCESS) {
1553 		/* Unable to teardown the Isoch CEC */
1554 		ASSERT(0);
1555 	}
1556 
1557 	/*
1558 	 * Leave the Isoch CEC
1559 	 */
1560 	ret = t1394_leave_isoch_cec(t1394_hdl,
1561 	    (t1394_isoch_cec_handle_t)cec_curr, 0);
1562 	if (ret != DDI_SUCCESS) {
1563 		/* Unable to leave the Isoch CEC */
1564 		ASSERT(0);
1565 	}
1566 
1567 	/*
1568 	 * Free the Isoch CEC
1569 	 */
1570 	ret = t1394_free_isoch_cec(t1394_hdl, flags,
1571 	    (t1394_isoch_cec_handle_t *)&cec_curr);
1572 	if (ret != DDI_SUCCESS) {
1573 		/* Unable to free the Isoch CEC */
1574 		ASSERT(0);
1575 	}
1576 
1577 	/* Handle is nulled out before returning */
1578 	*t1394_single_hdl = NULL;
1579 }
1580 
1581 /*
1582  * Function:    t1394_alloc_isoch_cec()
1583  * Input(s):    t1394_hdl		The target "handle" returned by
1584  *					    t1394_attach()
1585  *		props			The structure used to set up the
1586  *					    overall characteristics of for
1587  *					    the Isoch CEC.
1588  *		flags			The flags parameter is unused (for now)
1589  *
1590  * Output(s):	t1394_isoch_cec_hdl	The Isoch CEC "handle" used in all
1591  *					    subsequent isoch_cec() calls
1592  *
1593  * Description:	t1394_alloc_isoch_cec() allocates and initializes an
1594  *		isochronous channel event coordinator (Isoch CEC) for use
1595  *		in managing and coordinating activity for an isoch channel
1596  */
1597 /* ARGSUSED */
1598 int
t1394_alloc_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_props_t * props,uint_t flags,t1394_isoch_cec_handle_t * t1394_isoch_cec_hdl)1599 t1394_alloc_isoch_cec(t1394_handle_t t1394_hdl, t1394_isoch_cec_props_t *props,
1600     uint_t flags, t1394_isoch_cec_handle_t *t1394_isoch_cec_hdl)
1601 {
1602 	s1394_hal_t	  *hal;
1603 	s1394_isoch_cec_t *cec_new;
1604 	uint64_t	  temp;
1605 
1606 	ASSERT(t1394_hdl != NULL);
1607 	ASSERT(t1394_isoch_cec_hdl != NULL);
1608 	ASSERT(props != NULL);
1609 
1610 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
1611 
1612 	/* Check for invalid channel_mask */
1613 	if (props->cec_channel_mask == 0) {
1614 		return (DDI_FAILURE);
1615 	}
1616 
1617 	/* Test conditions specific to T1394_NO_IRM_ALLOC */
1618 	temp = props->cec_channel_mask;
1619 	if (props->cec_options & T1394_NO_IRM_ALLOC) {
1620 		/* If T1394_NO_IRM_ALLOC, then only one bit should be set */
1621 		if (!ISP2(temp)) {
1622 			return (DDI_FAILURE);
1623 		}
1624 
1625 		/* If T1394_NO_IRM_ALLOC, then speeds should be equal */
1626 		if (props->cec_min_speed != props->cec_max_speed) {
1627 			return (DDI_FAILURE);
1628 		}
1629 	}
1630 
1631 	/* Check for invalid bandwidth */
1632 	if ((props->cec_bandwidth <= IEEE1394_BANDWIDTH_MIN) ||
1633 	    (props->cec_bandwidth > IEEE1394_BANDWIDTH_MAX)) {
1634 		return (DDI_FAILURE);
1635 	}
1636 
1637 	/* Allocate the Isoch CEC structure */
1638 	cec_new = kmem_zalloc(sizeof (s1394_isoch_cec_t), KM_SLEEP);
1639 
1640 	/* Initialize the structure type */
1641 	cec_new->cec_type = S1394_PEER_TO_PEER;
1642 
1643 	/* Create the mutex and "in_callbacks" cv */
1644 	mutex_init(&cec_new->isoch_cec_mutex, NULL, MUTEX_DRIVER,
1645 	    hal->halinfo.hw_interrupt);
1646 	cv_init(&cec_new->in_callbacks_cv, NULL, CV_DRIVER,
1647 	    hal->halinfo.hw_interrupt);
1648 
1649 	/* Initialize the Isoch CEC's member list */
1650 	cec_new->cec_member_list_head	= NULL;
1651 	cec_new->cec_member_list_tail	= NULL;
1652 
1653 	/* Initialize the filters */
1654 	cec_new->filter_min_speed	= props->cec_min_speed;
1655 	cec_new->filter_max_speed	= props->cec_max_speed;
1656 	cec_new->filter_current_speed	= cec_new->filter_max_speed;
1657 	cec_new->filter_channel_mask	= props->cec_channel_mask;
1658 	cec_new->bandwidth		= props->cec_bandwidth;
1659 	cec_new->cec_options		= props->cec_options;
1660 	cec_new->state_transitions	= ISOCH_CEC_FREE | ISOCH_CEC_JOIN |
1661 	    ISOCH_CEC_SETUP;
1662 
1663 	mutex_enter(&hal->isoch_cec_list_mutex);
1664 
1665 	/* Insert Isoch CEC into the HAL's list */
1666 	s1394_isoch_cec_list_insert(hal, cec_new);
1667 
1668 	mutex_exit(&hal->isoch_cec_list_mutex);
1669 
1670 	/* Update the handle and return */
1671 	*t1394_isoch_cec_hdl = (t1394_isoch_cec_handle_t)cec_new;
1672 
1673 	return (DDI_SUCCESS);
1674 }
1675 
1676 /*
1677  * Function:    t1394_free_isoch_cec()
1678  * Input(s):    t1394_hdl		The target "handle" returned by
1679  *					    t1394_attach()
1680  *		flags			The flags parameter is unused (for now)
1681  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
1682  *					    t1394_alloc_isoch_cec()
1683  *
1684  * Output(s):	DDI_SUCCESS		Target successfully freed the Isoch CEC
1685  *		DDI_FAILURE		Target failed to free the Isoch CEC
1686  *
1687  * Description:	t1394_free_isoch_cec() attempts to free the Isoch CEC
1688  *		structure.  It will fail (DDI_FAILURE) if there are any
1689  *		remaining members who have not yet left.
1690  */
1691 /* ARGSUSED */
1692 int
t1394_free_isoch_cec(t1394_handle_t t1394_hdl,uint_t flags,t1394_isoch_cec_handle_t * t1394_isoch_cec_hdl)1693 t1394_free_isoch_cec(t1394_handle_t t1394_hdl, uint_t flags,
1694     t1394_isoch_cec_handle_t *t1394_isoch_cec_hdl)
1695 {
1696 	s1394_hal_t	  *hal;
1697 	s1394_isoch_cec_t *cec_curr;
1698 
1699 	ASSERT(t1394_hdl != NULL);
1700 	ASSERT(t1394_isoch_cec_hdl != NULL);
1701 
1702 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
1703 
1704 	/* Convert the handle to an Isoch CEC pointer */
1705 	cec_curr = (s1394_isoch_cec_t *)(*t1394_isoch_cec_hdl);
1706 
1707 	/* Lock the Isoch CEC member list */
1708 	mutex_enter(&cec_curr->isoch_cec_mutex);
1709 
1710 	/* Are we in any callbacks? */
1711 	if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
1712 		/* Unlock the Isoch CEC member list */
1713 		mutex_exit(&cec_curr->isoch_cec_mutex);
1714 		return (DDI_FAILURE);
1715 	}
1716 
1717 	/* Is "free" a legal state transition? */
1718 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_FREE) == 0) {
1719 		/* Unlock the Isoch CEC member list */
1720 		mutex_exit(&cec_curr->isoch_cec_mutex);
1721 		return (DDI_FAILURE);
1722 	}
1723 	mutex_exit(&cec_curr->isoch_cec_mutex);
1724 
1725 	mutex_enter(&hal->isoch_cec_list_mutex);
1726 
1727 	/* Remove Isoch CEC from HAL's list */
1728 	s1394_isoch_cec_list_remove(hal, cec_curr);
1729 
1730 	mutex_exit(&hal->isoch_cec_list_mutex);
1731 
1732 	/* Destroy the Isoch CEC's mutex and cv */
1733 	cv_destroy(&cec_curr->in_callbacks_cv);
1734 	mutex_destroy(&cec_curr->isoch_cec_mutex);
1735 
1736 	/* Free up the memory for the Isoch CEC struct */
1737 	kmem_free(cec_curr, sizeof (s1394_isoch_cec_t));
1738 
1739 	/* Update the handle and return */
1740 	*t1394_isoch_cec_hdl = NULL;
1741 
1742 	return (DDI_SUCCESS);
1743 }
1744 
1745 /*
1746  * Function:    t1394_join_isoch_cec()
1747  * Input(s):    t1394_hdl		The target "handle" returned by
1748  *					    t1394_attach()
1749  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
1750  *					    t1394_alloc_isoch_cec()
1751  *		flags			The flags parameter is unused (for now)
1752  *		join_isoch_info		This structure provides infomation
1753  *					    about a target that wishes to join
1754  *					    the given Isoch CEC.  It gives
1755  *					    max_speed, channel_mask, etc.
1756  *
1757  * Output(s):	DDI_SUCCESS		Target successfully joined the
1758  *					    Isoch CEC
1759  *		DDI_FAILURE		Target failed to join the Isoch CEC
1760  *
1761  * Description:	t1394_join_isoch_cec() determines, based on the information
1762  *		given in the join_isoch_info structure, if the target may
1763  *		join the Isoch CEC.  If it is determined that the target may
1764  *		join, the specified callback routines are stored away for
1765  *		later use in the coordination tasks.
1766  */
1767 /* ARGSUSED */
1768 int
t1394_join_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_handle_t t1394_isoch_cec_hdl,uint_t flags,t1394_join_isochinfo_t * join_isoch_info)1769 t1394_join_isoch_cec(t1394_handle_t t1394_hdl,
1770     t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags,
1771     t1394_join_isochinfo_t *join_isoch_info)
1772 {
1773 	s1394_hal_t		 *hal;
1774 	s1394_isoch_cec_t	 *cec_curr;
1775 	s1394_isoch_cec_member_t *member_new;
1776 	uint64_t		 check_mask;
1777 	uint_t			 curr_max_speed;
1778 
1779 	ASSERT(t1394_hdl != NULL);
1780 	ASSERT(t1394_isoch_cec_hdl != NULL);
1781 
1782 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
1783 
1784 	/* Convert the handle to an Isoch CEC pointer */
1785 	cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
1786 
1787 	/* Allocate a new Isoch CEC member structure */
1788 	member_new = kmem_zalloc(sizeof (s1394_isoch_cec_member_t), KM_SLEEP);
1789 
1790 	/* Lock the Isoch CEC member list */
1791 	mutex_enter(&cec_curr->isoch_cec_mutex);
1792 
1793 	/* Are we in any callbacks? (Wait for them to finish) */
1794 	while (CEC_IN_ANY_CALLBACKS(cec_curr)) {
1795 		cec_curr->cec_want_wakeup = B_TRUE;
1796 		cv_wait(&cec_curr->in_callbacks_cv,
1797 		    &cec_curr->isoch_cec_mutex);
1798 	}
1799 
1800 	/* Is "join" a legal state transition? */
1801 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_JOIN) == 0) {
1802 		kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
1803 		/* Unlock the Isoch CEC member list */
1804 		mutex_exit(&cec_curr->isoch_cec_mutex);
1805 		return (DDI_FAILURE);
1806 	}
1807 
1808 	/* Check the channel mask for consistency */
1809 	check_mask = join_isoch_info->req_channel_mask &
1810 	    cec_curr->filter_channel_mask;
1811 	if (check_mask == 0) {
1812 		kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
1813 		/* Unlock the Isoch CEC member list */
1814 		mutex_exit(&cec_curr->isoch_cec_mutex);
1815 		return (DDI_FAILURE);
1816 	}
1817 
1818 	/* Check for consistent speeds */
1819 	if (join_isoch_info->req_max_speed < cec_curr->filter_min_speed) {
1820 		kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
1821 		/* Unlock the Isoch CEC member list */
1822 		mutex_exit(&cec_curr->isoch_cec_mutex);
1823 		return (DDI_FAILURE);
1824 	} else if (join_isoch_info->req_max_speed <
1825 	    cec_curr->filter_current_speed) {
1826 		curr_max_speed = join_isoch_info->req_max_speed;
1827 	} else {
1828 		curr_max_speed = cec_curr->filter_current_speed;
1829 	}
1830 
1831 	/* Check for no more than one talker */
1832 	if ((join_isoch_info->jii_options & T1394_TALKER) &&
1833 	    (cec_curr->cec_member_talker != NULL)) {
1834 		kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
1835 		/* Unlock the Isoch CEC member list */
1836 		mutex_exit(&cec_curr->isoch_cec_mutex);
1837 		return (DDI_FAILURE);
1838 	}
1839 
1840 	/* Verify that all callbacks are non-NULL (for PEER_TO_PEER) */
1841 	if ((cec_curr->cec_type == S1394_PEER_TO_PEER) &&
1842 	    ((join_isoch_info->isoch_cec_evts.setup_target	== NULL) ||
1843 	    (join_isoch_info->isoch_cec_evts.start_target	== NULL) ||
1844 	    (join_isoch_info->isoch_cec_evts.stop_target	== NULL) ||
1845 	    (join_isoch_info->isoch_cec_evts.rsrc_fail_target	== NULL) ||
1846 	    (join_isoch_info->isoch_cec_evts.teardown_target	== NULL))) {
1847 		kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
1848 		/* Unlock the Isoch CEC member list */
1849 		mutex_exit(&cec_curr->isoch_cec_mutex);
1850 		return (DDI_FAILURE);
1851 	}
1852 
1853 	/* Copy the events information into the struct */
1854 	member_new->isoch_cec_evts	= join_isoch_info->isoch_cec_evts;
1855 	member_new->isoch_cec_evts_arg	= join_isoch_info->isoch_cec_evts_arg;
1856 	member_new->cec_mem_options	= join_isoch_info->jii_options;
1857 	member_new->cec_mem_target	= (s1394_target_t *)t1394_hdl;
1858 
1859 	/* Insert new member into Isoch CEC's member list */
1860 	s1394_isoch_cec_member_list_insert(hal, cec_curr, member_new);
1861 
1862 	/* Update the channel mask filter */
1863 	cec_curr->filter_channel_mask	= check_mask;
1864 
1865 	/* Update the speed filter */
1866 	cec_curr->filter_current_speed	= curr_max_speed;
1867 
1868 	/* Update the talker pointer (if necessary) */
1869 	if (join_isoch_info->jii_options & T1394_TALKER)
1870 		cec_curr->cec_member_talker = cec_curr->cec_member_list_head;
1871 
1872 	/*
1873 	 * Now "leave" is a legal state transition
1874 	 * and "free" is an illegal state transition
1875 	 */
1876 	CEC_SET_LEGAL(cec_curr, ISOCH_CEC_LEAVE);
1877 	CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_FREE);
1878 
1879 	/* Unlock the Isoch CEC member list */
1880 	mutex_exit(&cec_curr->isoch_cec_mutex);
1881 
1882 	return (DDI_SUCCESS);
1883 }
1884 
1885 /*
1886  * Function:    t1394_leave_isoch_cec()
1887  * Input(s):    t1394_hdl		The target "handle" returned by
1888  *					    t1394_attach()
1889  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
1890  *					    t1394_alloc_isoch_cec()
1891  *		flags			The flags parameter is unused (for now)
1892  *
1893  * Output(s):	DDI_SUCCESS		Target successfully left the
1894  *					    Isoch CEC
1895  *		DDI_FAILURE		Target failed to leave the Isoch CEC
1896  *
1897  * Description:	t1394_leave_isoch_cec() is used by a target driver to remove
1898  *		itself from the Isoch CEC's member list.  It is possible
1899  *		for this call to fail because the target is not found in
1900  *		the current member list, or because it is not an appropriate
1901  *		time for a target to leave.
1902  */
1903 /* ARGSUSED */
1904 int
t1394_leave_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_handle_t t1394_isoch_cec_hdl,uint_t flags)1905 t1394_leave_isoch_cec(t1394_handle_t t1394_hdl,
1906     t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
1907 {
1908 	s1394_hal_t		 *hal;
1909 	s1394_isoch_cec_t	 *cec_curr;
1910 	s1394_isoch_cec_member_t *member_curr;
1911 	s1394_isoch_cec_member_t *member_temp;
1912 	boolean_t		 found;
1913 	uint64_t		 temp_channel_mask;
1914 	uint_t			 temp_max_speed;
1915 
1916 	ASSERT(t1394_hdl != NULL);
1917 	ASSERT(t1394_isoch_cec_hdl != NULL);
1918 
1919 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
1920 
1921 	/* Convert the handle to an Isoch CEC pointer */
1922 	cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
1923 
1924 	/* Lock the Isoch CEC member list */
1925 	mutex_enter(&cec_curr->isoch_cec_mutex);
1926 
1927 	/* Are we in any callbacks? (Wait for them to finish) */
1928 	while (CEC_IN_ANY_CALLBACKS(cec_curr)) {
1929 		cec_curr->cec_want_wakeup = B_TRUE;
1930 		cv_wait(&cec_curr->in_callbacks_cv,
1931 		    &cec_curr->isoch_cec_mutex);
1932 	}
1933 
1934 	/* Is "leave" a legal state transition? */
1935 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_LEAVE) == 0) {
1936 		/* Unlock the Isoch CEC member list */
1937 		mutex_exit(&cec_curr->isoch_cec_mutex);
1938 		return (DDI_FAILURE);
1939 	}
1940 
1941 	/* Find the Target on the CEC's member list */
1942 	found = B_FALSE;
1943 	temp_channel_mask = cec_curr->cec_alloc_props.cec_channel_mask;
1944 	temp_max_speed	  = cec_curr->cec_alloc_props.cec_max_speed;
1945 	member_curr	  = cec_curr->cec_member_list_head;
1946 	while (member_curr != NULL) {
1947 		if (member_curr->cec_mem_target ==
1948 		    (s1394_target_t *)t1394_hdl) {
1949 			member_temp = member_curr;
1950 			found	    = B_TRUE;
1951 		} else {
1952 			/* Keep track of channel mask and max speed info */
1953 			temp_channel_mask &= member_curr->req_channel_mask;
1954 			if (member_curr->req_max_speed < temp_max_speed)
1955 				temp_max_speed = member_curr->req_max_speed;
1956 		}
1957 		member_curr = member_curr->cec_mem_next;
1958 	}
1959 
1960 	/* Target not found on this Isoch CEC */
1961 	if (found == B_FALSE) {
1962 		/* Unlock the Isoch CEC member list */
1963 		mutex_exit(&cec_curr->isoch_cec_mutex);
1964 		return (DDI_FAILURE);
1965 	} else {
1966 		/* This member's departure may change filter constraints */
1967 		cec_curr->filter_current_speed  = temp_max_speed;
1968 		cec_curr->filter_channel_mask   = temp_channel_mask;
1969 	}
1970 
1971 	/* Remove member from Isoch CEC's member list */
1972 	s1394_isoch_cec_member_list_remove(hal, cec_curr, member_temp);
1973 
1974 	/* If we are removing the talker, then update the pointer */
1975 	if (cec_curr->cec_member_talker == member_temp)
1976 		cec_curr->cec_member_talker = NULL;
1977 
1978 	/* Is the Isoch CEC's member list empty? */
1979 	if ((cec_curr->cec_member_list_head == NULL) &&
1980 	    (cec_curr->cec_member_list_tail == NULL)) {
1981 		/*
1982 		 * Now "free" _might_ be a legal state transition
1983 		 * if we aren't in setup or start phases and "leave"
1984 		 * is definitely an illegal state transition
1985 		 */
1986 		if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_JOIN) != 0)
1987 			CEC_SET_LEGAL(cec_curr, ISOCH_CEC_FREE);
1988 		CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_LEAVE);
1989 	}
1990 
1991 	/* Unlock the Isoch CEC member list */
1992 	mutex_exit(&cec_curr->isoch_cec_mutex);
1993 
1994 	/* Free the Isoch CEC member structure */
1995 	kmem_free(member_temp, sizeof (s1394_isoch_cec_member_t));
1996 
1997 	return (DDI_SUCCESS);
1998 }
1999 
2000 /*
2001  * Function:    t1394_setup_isoch_cec()
2002  * Input(s):    t1394_hdl		The target "handle" returned by
2003  *					    t1394_attach()
2004  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
2005  *					    t1394_alloc_isoch_cec()
2006  *		flags			The flags parameter is unused (for now)
2007  *
2008  * Output(s):	result			Used to pass more specific info back
2009  *					    to target
2010  *
2011  * Description:	t1394_setup_isoch_cec() directs the 1394 Software Framework
2012  *		to allocate isochronous resources and invoke the setup_target()
2013  *		callback for each member of the Isoch CEC.  This call may
2014  *		fail because bandwidth was unavailable (T1394_ENO_BANDWIDTH),
2015  *		channels were unavailable (T1394_ENO_CHANNEL), or one of the
2016  *		member targets returned failure from its setup_target()
2017  *		callback.
2018  */
2019 /* ARGSUSED */
2020 int
t1394_setup_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_handle_t t1394_isoch_cec_hdl,uint_t flags,int * result)2021 t1394_setup_isoch_cec(t1394_handle_t t1394_hdl,
2022     t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags, int *result)
2023 {
2024 	s1394_hal_t			*hal;
2025 	s1394_isoch_cec_t		*cec_curr;
2026 	s1394_isoch_cec_member_t	*member_curr;
2027 	t1394_setup_target_args_t	target_args;
2028 	uint64_t			temp_chnl_mask;
2029 	uint32_t			old_chnl;
2030 	uint32_t			try_chnl;
2031 	uint_t				bw_alloc_units;
2032 	uint_t				generation;
2033 	int				chnl_num;
2034 	int				err;
2035 	int				ret;
2036 	int				j;
2037 	int	(*setup_callback)(t1394_isoch_cec_handle_t, opaque_t,
2038 	    t1394_setup_target_args_t *);
2039 
2040 	ASSERT(t1394_hdl != NULL);
2041 	ASSERT(t1394_isoch_cec_hdl != NULL);
2042 
2043 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2044 
2045 	/* Convert the handle to an Isoch CEC pointer */
2046 	cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
2047 
2048 	/* Lock the Isoch CEC member list */
2049 	mutex_enter(&cec_curr->isoch_cec_mutex);
2050 
2051 	/* Are we in any callbacks? */
2052 	if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
2053 		/* Unlock the Isoch CEC member list */
2054 		mutex_exit(&cec_curr->isoch_cec_mutex);
2055 		return (DDI_FAILURE);
2056 	}
2057 
2058 	/* Is "setup" a legal state transition? */
2059 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_SETUP) == 0) {
2060 		/* Unlock the Isoch CEC member list */
2061 		mutex_exit(&cec_curr->isoch_cec_mutex);
2062 		return (DDI_FAILURE);
2063 	}
2064 
2065 	/* If T1394_NO_IRM_ALLOC is set then don't allocate... do callbacks */
2066 	if (cec_curr->cec_options & T1394_NO_IRM_ALLOC) {
2067 		goto setup_do_callbacks;
2068 	}
2069 
2070 	/* Allocate bandwidth and channels */
2071 	for (j = 0; j < S1394_ISOCH_ALLOC_RETRIES; j++) {
2072 		/*
2073 		 * Get the current generation number - don't
2074 		 * need the lock because we are read only here
2075 		 */
2076 		generation = hal->generation_count;
2077 
2078 		/* Compute how much bandwidth is needed */
2079 		bw_alloc_units = s1394_compute_bw_alloc_units(hal,
2080 		    cec_curr->bandwidth, cec_curr->filter_current_speed);
2081 
2082 		/* Check that the generation has not changed - */
2083 		/* don't need the lock (read only) */
2084 		if (generation != hal->generation_count)
2085 			continue;
2086 
2087 		/* Unlock the Isoch CEC member list */
2088 		mutex_exit(&cec_curr->isoch_cec_mutex);
2089 
2090 		/* Try to allocate the bandwidth */
2091 		ret = s1394_bandwidth_alloc(hal, bw_alloc_units, generation,
2092 		    &err);
2093 
2094 		/* Lock the Isoch CEC member list */
2095 		mutex_enter(&cec_curr->isoch_cec_mutex);
2096 
2097 		/* If there was a bus reset, start over */
2098 		if (ret == DDI_FAILURE) {
2099 			if (err == CMD1394_EBUSRESET) {
2100 				continue; /* start over and try again */
2101 			} else {
2102 				*result = T1394_ENO_BANDWIDTH;
2103 				/* Unlock the Isoch CEC member list */
2104 				mutex_exit(&cec_curr->isoch_cec_mutex);
2105 				return (DDI_FAILURE);
2106 			}
2107 		}
2108 
2109 		/* Check that the generation has not changed - */
2110 		/* don't need the lock (read only) */
2111 		if (generation != hal->generation_count)
2112 			continue;
2113 
2114 		/*
2115 		 * Allocate a channel
2116 		 *    From IEEE 1394-1995, Section 8.3.2.3.8: "Bits
2117 		 *    allocated in the CHANNELS_AVAILABLE_HI field of
2118 		 *    this register shall start at bit zero (channel
2119 		 *    number zero), and additional channel numbers shall
2120 		 *    be represented in a monotonically increasing sequence
2121 		 *    of bit numbers up to a maximum of bit 31 (channel
2122 		 *    number 31).  Bits allocated in the CHANNELS_AVAILABLE_LO
2123 		 *    field of this register shall start at bit zero
2124 		 *    (channel number 32), and additional channel numbers
2125 		 *    shall be represented in a monotonically increasing
2126 		 *    sequence of bit numbers up to a maximum of bit 31
2127 		 *    (channel number 63).
2128 		 */
2129 		temp_chnl_mask = cec_curr->filter_channel_mask;
2130 		for (chnl_num = 63; chnl_num >= 0; chnl_num--) {
2131 			if ((temp_chnl_mask & 1) == 1) {
2132 				try_chnl = (1 << ((63 - chnl_num) % 32));
2133 
2134 				/* Unlock the Isoch CEC member list */
2135 				mutex_exit(&cec_curr->isoch_cec_mutex);
2136 				if (chnl_num < 32) {
2137 					ret = s1394_channel_alloc(hal,
2138 					    try_chnl, generation,
2139 					    S1394_CHANNEL_ALLOC_HI, &old_chnl,
2140 					    &err);
2141 				} else {
2142 					ret = s1394_channel_alloc(hal,
2143 					    try_chnl, generation,
2144 					    S1394_CHANNEL_ALLOC_LO, &old_chnl,
2145 					    &err);
2146 				}
2147 				/* Lock the Isoch CEC member list */
2148 				mutex_enter(&cec_curr->isoch_cec_mutex);
2149 
2150 				/* Did we get a channel? (or a bus reset) */
2151 				if ((ret == DDI_SUCCESS) ||
2152 				    (err == CMD1394_EBUSRESET))
2153 					break;
2154 			}
2155 			temp_chnl_mask = temp_chnl_mask >> 1;
2156 		}
2157 
2158 		/* If we've tried all the possible channels, then fail */
2159 		if (chnl_num == 0) {
2160 			*result = T1394_ENO_CHANNEL;
2161 			/*
2162 			 * If we successfully allocate bandwidth, and
2163 			 * then fail getting a channel, we need to
2164 			 * free up the bandwidth
2165 			 */
2166 
2167 			/* Check that the generation has not changed */
2168 			/* lock not needed here (read only) */
2169 			if (generation != hal->generation_count)
2170 				continue;
2171 
2172 			/* Unlock the Isoch CEC member list */
2173 			mutex_exit(&cec_curr->isoch_cec_mutex);
2174 
2175 			/* Try to free up the bandwidth */
2176 			ret = s1394_bandwidth_free(hal, bw_alloc_units,
2177 			    generation, &err);
2178 
2179 			/* Lock the Isoch CEC member list */
2180 			mutex_enter(&cec_curr->isoch_cec_mutex);
2181 
2182 			if (ret == DDI_FAILURE) {
2183 				if (err == CMD1394_EBUSRESET) {
2184 					continue;
2185 				}
2186 			}
2187 
2188 			/* Unlock the Isoch CEC member list */
2189 			mutex_exit(&cec_curr->isoch_cec_mutex);
2190 			return (DDI_FAILURE);
2191 		}
2192 
2193 		/* If we got a channel, we're done (else start over) */
2194 		if (ret == DDI_SUCCESS)
2195 			break;
2196 		else if (err == CMD1394_EBUSRESET)
2197 			continue;
2198 	}
2199 
2200 	/* Have we gotten too many bus resets? */
2201 	if (j == S1394_ISOCH_ALLOC_RETRIES) {
2202 		*result = T1394_ENO_BANDWIDTH;
2203 		/* Unlock the Isoch CEC member list */
2204 		mutex_exit(&cec_curr->isoch_cec_mutex);
2205 		return (DDI_FAILURE);
2206 	}
2207 
2208 	cec_curr->realloc_valid	    = B_TRUE;
2209 	cec_curr->realloc_chnl_num  = chnl_num;
2210 	cec_curr->realloc_bandwidth = cec_curr->bandwidth;
2211 	cec_curr->realloc_speed	    = cec_curr->filter_current_speed;
2212 
2213 setup_do_callbacks:
2214 	/* Call all of the setup_target() callbacks */
2215 	target_args.channel_num	    = chnl_num;
2216 	target_args.channel_speed   = cec_curr->filter_current_speed;
2217 
2218 	/* Now we are going into the callbacks */
2219 	cec_curr->in_callbacks	    = B_TRUE;
2220 
2221 	/* Unlock the Isoch CEC member list */
2222 	mutex_exit(&cec_curr->isoch_cec_mutex);
2223 
2224 	member_curr = cec_curr->cec_member_list_head;
2225 	*result = 0;
2226 	while (member_curr != NULL) {
2227 		if (member_curr->isoch_cec_evts.setup_target != NULL) {
2228 			setup_callback =
2229 			    member_curr->isoch_cec_evts.setup_target;
2230 			ret = setup_callback(t1394_isoch_cec_hdl,
2231 			    member_curr->isoch_cec_evts_arg, &target_args);
2232 			if (ret != DDI_SUCCESS)
2233 				*result = T1394_ETARGET;
2234 		}
2235 		member_curr = member_curr->cec_mem_next;
2236 	}
2237 
2238 	/* Lock the Isoch CEC member list */
2239 	mutex_enter(&cec_curr->isoch_cec_mutex);
2240 
2241 	/* We are finished with the callbacks */
2242 	cec_curr->in_callbacks = B_FALSE;
2243 	if (cec_curr->cec_want_wakeup == B_TRUE) {
2244 		cec_curr->cec_want_wakeup = B_FALSE;
2245 		cv_broadcast(&cec_curr->in_callbacks_cv);
2246 	}
2247 
2248 	/*
2249 	 * Now "start" and "teardown" are legal state transitions
2250 	 * and "join", "free", and "setup" are illegal state transitions
2251 	 */
2252 	CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
2253 	CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_JOIN | ISOCH_CEC_FREE |
2254 	    ISOCH_CEC_SETUP));
2255 
2256 	/* Unlock the Isoch CEC member list */
2257 	mutex_exit(&cec_curr->isoch_cec_mutex);
2258 
2259 	/* Return DDI_FAILURE if any targets failed setup */
2260 	if (*result != 0) {
2261 		return (DDI_FAILURE);
2262 	}
2263 
2264 	return (DDI_SUCCESS);
2265 }
2266 
2267 /*
2268  * Function:    t1394_start_isoch_cec()
2269  * Input(s):    t1394_hdl		The target "handle" returned by
2270  *					    t1394_attach()
2271  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
2272  *					    t1394_alloc_isoch_cec()
2273  *		flags			The flags parameter is unused (for now)
2274  *
2275  * Output(s):	DDI_SUCCESS		All start_target() callbacks returned
2276  *					    successfully
2277  *		DDI_FAILURE		One or more start_target() callbacks
2278  *					    returned failure
2279  *
2280  * Description:	t1394_start_isoch_cec() directs the 1394 Software Framework
2281  *		to invoke each of the start_target() callbacks, first for
2282  *		each listener, then for the talker.
2283  */
2284 /* ARGSUSED */
2285 int
t1394_start_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_handle_t t1394_isoch_cec_hdl,uint_t flags)2286 t1394_start_isoch_cec(t1394_handle_t t1394_hdl,
2287     t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
2288 {
2289 	s1394_isoch_cec_t	 *cec_curr;
2290 	s1394_isoch_cec_member_t *member_curr;
2291 	int			 ret;
2292 	boolean_t		 err;
2293 	int	(*start_callback)(t1394_isoch_cec_handle_t, opaque_t);
2294 
2295 	ASSERT(t1394_hdl != NULL);
2296 	ASSERT(t1394_isoch_cec_hdl != NULL);
2297 
2298 	/* Convert the handle to an Isoch CEC pointer */
2299 	cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
2300 
2301 	/* Lock the Isoch CEC member list */
2302 	mutex_enter(&cec_curr->isoch_cec_mutex);
2303 
2304 	/* Are we in any callbacks? */
2305 	if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
2306 		/* Unlock the Isoch CEC member list */
2307 		mutex_exit(&cec_curr->isoch_cec_mutex);
2308 		return (DDI_FAILURE);
2309 	}
2310 
2311 	/* Is "start" a legal state transition? */
2312 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_START) == 0) {
2313 		/* Unlock the Isoch CEC member list */
2314 		mutex_exit(&cec_curr->isoch_cec_mutex);
2315 		return (DDI_FAILURE);
2316 	}
2317 
2318 	/* Now we are going into the callbacks */
2319 	cec_curr->in_callbacks = B_TRUE;
2320 
2321 	/* Unlock the Isoch CEC member list */
2322 	mutex_exit(&cec_curr->isoch_cec_mutex);
2323 
2324 	/*
2325 	 * Call all of the start_target() callbacks
2326 	 * Start at the tail (listeners first) and
2327 	 * go toward the head (talker last)
2328 	 */
2329 	member_curr = cec_curr->cec_member_list_tail;
2330 	err = B_FALSE;
2331 	while (member_curr != NULL) {
2332 		if (member_curr->isoch_cec_evts.start_target != NULL) {
2333 			start_callback =
2334 			    member_curr->isoch_cec_evts.start_target;
2335 			ret = start_callback(t1394_isoch_cec_hdl,
2336 			    member_curr->isoch_cec_evts_arg);
2337 		if (ret != DDI_SUCCESS)
2338 			err = B_TRUE;
2339 		}
2340 		member_curr = member_curr->cec_mem_prev;
2341 	}
2342 
2343 	/* Lock the Isoch CEC member list */
2344 	mutex_enter(&cec_curr->isoch_cec_mutex);
2345 
2346 	/* We are finished with the callbacks */
2347 	cec_curr->in_callbacks = B_FALSE;
2348 	if (cec_curr->cec_want_wakeup == B_TRUE) {
2349 		cec_curr->cec_want_wakeup = B_FALSE;
2350 		cv_broadcast(&cec_curr->in_callbacks_cv);
2351 	}
2352 
2353 	/*
2354 	 * Now "stop" is a legal state transitions
2355 	 * and "start" and "teardown" are illegal state transitions
2356 	 */
2357 	CEC_SET_LEGAL(cec_curr, ISOCH_CEC_STOP);
2358 	CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
2359 
2360 	/* Unlock the Isoch CEC member list */
2361 	mutex_exit(&cec_curr->isoch_cec_mutex);
2362 
2363 	/* Return DDI_FAILURE if any targets failed start */
2364 	if (err == B_TRUE) {
2365 		return (DDI_FAILURE);
2366 	}
2367 
2368 	return (DDI_SUCCESS);
2369 }
2370 
2371 /*
2372  * Function:    t1394_stop_isoch_cec()
2373  * Input(s):    t1394_hdl		The target "handle" returned by
2374  *					    t1394_attach()
2375  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
2376  *					    t1394_alloc_isoch_cec()
2377  *		flags			The flags parameter is unused (for now)
2378  *
2379  * Output(s):	DDI_SUCCESS		Target successfully stopped the
2380  *					    Isoch CEC
2381  *		DDI_FAILURE		Target failed to stop the Isoch CEC
2382  *
2383  * Description:	t1394_stop_isoch_cec() directs the 1394 Software Framework
2384  *		to invoke each of the stop_target() callbacks, first for
2385  *		the talker, then for each listener.
2386  *		(This call will fail if it is called at an
2387  *		inappropriate time, i.e. before the t1394_start_isoch_cec()
2388  *		call, etc.)
2389  */
2390 /* ARGSUSED */
2391 int
t1394_stop_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_handle_t t1394_isoch_cec_hdl,uint_t flags)2392 t1394_stop_isoch_cec(t1394_handle_t t1394_hdl,
2393     t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
2394 {
2395 	s1394_isoch_cec_t	 *cec_curr;
2396 	s1394_isoch_cec_member_t *member_curr;
2397 	void	(*stop_callback)(t1394_isoch_cec_handle_t, opaque_t);
2398 
2399 	ASSERT(t1394_hdl != NULL);
2400 	ASSERT(t1394_isoch_cec_hdl != NULL);
2401 
2402 	/* Convert the handle to an Isoch CEC pointer */
2403 	cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
2404 
2405 	/* Lock the Isoch CEC member list */
2406 	mutex_enter(&cec_curr->isoch_cec_mutex);
2407 
2408 	/* Are we in any callbacks? */
2409 	if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
2410 		/* Unlock the Isoch CEC member list */
2411 		mutex_exit(&cec_curr->isoch_cec_mutex);
2412 		return (DDI_FAILURE);
2413 	}
2414 
2415 	/* Is "stop" a legal state transition? */
2416 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_STOP) == 0) {
2417 		/* Unlock the Isoch CEC member list */
2418 		mutex_exit(&cec_curr->isoch_cec_mutex);
2419 		return (DDI_FAILURE);
2420 	}
2421 
2422 	/* Now we are going into the callbacks */
2423 	cec_curr->in_callbacks = B_TRUE;
2424 
2425 	/* Unlock the Isoch CEC member list */
2426 	mutex_exit(&cec_curr->isoch_cec_mutex);
2427 
2428 	/*
2429 	 * Call all of the stop_target() callbacks
2430 	 * Start at the head (talker first) and
2431 	 * go toward the tail (listeners last)
2432 	 */
2433 	member_curr = cec_curr->cec_member_list_head;
2434 	while (member_curr != NULL) {
2435 		if (member_curr->isoch_cec_evts.stop_target != NULL) {
2436 			stop_callback =
2437 			    member_curr->isoch_cec_evts.stop_target;
2438 			stop_callback(t1394_isoch_cec_hdl,
2439 			    member_curr->isoch_cec_evts_arg);
2440 		}
2441 		member_curr = member_curr->cec_mem_next;
2442 	}
2443 
2444 	/* Lock the Isoch CEC member list */
2445 	mutex_enter(&cec_curr->isoch_cec_mutex);
2446 
2447 	/* We are finished with the callbacks */
2448 	cec_curr->in_callbacks = B_FALSE;
2449 	if (cec_curr->cec_want_wakeup == B_TRUE) {
2450 		cec_curr->cec_want_wakeup = B_FALSE;
2451 		cv_broadcast(&cec_curr->in_callbacks_cv);
2452 	}
2453 
2454 	/*
2455 	 * Now "start" and "teardown" are legal state transitions
2456 	 * and "stop" is an illegal state transitions
2457 	 */
2458 	CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
2459 	CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_STOP);
2460 
2461 	/* Unlock the Isoch CEC member list */
2462 	mutex_exit(&cec_curr->isoch_cec_mutex);
2463 
2464 	return (DDI_SUCCESS);
2465 }
2466 
2467 /*
2468  * Function:    t1394_teardown_isoch_cec()
2469  * Input(s):    t1394_hdl		The target "handle" returned by
2470  *					    t1394_attach()
2471  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
2472  *					    t1394_alloc_isoch_cec()
2473  *		flags			The flags parameter is unused (for now)
2474  *
2475  * Output(s):	DDI_SUCCESS		Target successfully tore down the
2476  *					    Isoch CEC
2477  *		DDI_FAILURE		Target failed to tear down the
2478  *					    Isoch CEC
2479  *
2480  * Description:	t1394_teardown_isoch_cec() directs the 1394 Software Framework
2481  *		to free up any isochronous resources we might be holding and
2482  *		call all of the teardown_target() callbacks.
2483  *		(This call will fail if it is called at an
2484  *		inappropriate time, i.e. before the t1394_start_isoch_cec()
2485  *		call, before the t1394_stop_isoch_cec, etc.
2486  */
2487 /* ARGSUSED */
2488 int
t1394_teardown_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_handle_t t1394_isoch_cec_hdl,uint_t flags)2489 t1394_teardown_isoch_cec(t1394_handle_t t1394_hdl,
2490     t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
2491 {
2492 	s1394_hal_t		 *hal;
2493 	s1394_isoch_cec_t	 *cec_curr;
2494 	s1394_isoch_cec_member_t *member_curr;
2495 	uint32_t		 chnl_mask;
2496 	uint32_t		 old_chnl_mask;
2497 	uint_t			 bw_alloc_units;
2498 	uint_t			 generation;
2499 	int			 ret;
2500 	int			 err;
2501 	void	(*teardown_callback)(t1394_isoch_cec_handle_t, opaque_t);
2502 
2503 	ASSERT(t1394_hdl != NULL);
2504 	ASSERT(t1394_isoch_cec_hdl != NULL);
2505 
2506 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2507 
2508 	/* Convert the handle to an Isoch CEC pointer */
2509 	cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
2510 
2511 	/* Lock the Isoch CEC member list */
2512 	mutex_enter(&cec_curr->isoch_cec_mutex);
2513 
2514 	/* Are we in any callbacks? */
2515 	if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
2516 		/* Unlock the Isoch CEC member list */
2517 		mutex_exit(&cec_curr->isoch_cec_mutex);
2518 		return (DDI_FAILURE);
2519 	}
2520 
2521 	/* Is "teardown" a legal state transition? */
2522 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_TEARDOWN) == 0) {
2523 		/* Unlock the Isoch CEC member list */
2524 		mutex_exit(&cec_curr->isoch_cec_mutex);
2525 		return (DDI_FAILURE);
2526 	}
2527 
2528 	/* If T1394_NO_IRM_ALLOC is set then don't free... do callbacks */
2529 	if (cec_curr->cec_options & T1394_NO_IRM_ALLOC) {
2530 		goto teardown_do_callbacks;
2531 	}
2532 
2533 	/* If nothing has been allocated or we failed to */
2534 	/* reallocate, then we are done... call the callbacks */
2535 	if ((cec_curr->realloc_valid == B_FALSE) ||
2536 	    (cec_curr->realloc_failed == B_TRUE)) {
2537 		goto teardown_do_callbacks;
2538 	}
2539 
2540 	/*
2541 	 * Get the current generation number - don't need the
2542 	 * topology tree mutex here because it is read-only, and
2543 	 * there is a race condition with or without it.
2544 	 */
2545 	generation = hal->generation_count;
2546 
2547 	/* Compute the amount bandwidth to free */
2548 	bw_alloc_units = s1394_compute_bw_alloc_units(hal,
2549 	    cec_curr->bandwidth, cec_curr->realloc_speed);
2550 
2551 	/* Check that the generation has not changed - */
2552 	/* don't need the lock (read only) */
2553 	if (generation != hal->generation_count)
2554 		goto teardown_do_callbacks;
2555 
2556 	/* Unlock the Isoch CEC member list */
2557 	mutex_exit(&cec_curr->isoch_cec_mutex);
2558 
2559 	/* Try to free up the bandwidth */
2560 	ret = s1394_bandwidth_free(hal, bw_alloc_units, generation, &err);
2561 
2562 	/* Lock the Isoch CEC member list */
2563 	mutex_enter(&cec_curr->isoch_cec_mutex);
2564 
2565 	if (ret == DDI_FAILURE) {
2566 		if (err == CMD1394_EBUSRESET) {
2567 			goto teardown_do_callbacks;
2568 		}
2569 	}
2570 
2571 	/* Free the allocated channel */
2572 	chnl_mask = (1 << ((63 - cec_curr->realloc_chnl_num) % 32));
2573 
2574 	/* Unlock the Isoch CEC member list */
2575 	mutex_exit(&cec_curr->isoch_cec_mutex);
2576 	if (cec_curr->realloc_chnl_num < 32) {
2577 		ret = s1394_channel_free(hal, chnl_mask, generation,
2578 		    S1394_CHANNEL_ALLOC_HI, &old_chnl_mask, &err);
2579 	} else {
2580 		ret = s1394_channel_free(hal, chnl_mask, generation,
2581 		    S1394_CHANNEL_ALLOC_LO, &old_chnl_mask, &err);
2582 	}
2583 	/* Lock the Isoch CEC member list */
2584 	mutex_enter(&cec_curr->isoch_cec_mutex);
2585 
2586 teardown_do_callbacks:
2587 	/* From here on reallocation is unnecessary */
2588 	cec_curr->realloc_valid	    = B_FALSE;
2589 	cec_curr->realloc_chnl_num  = 0;
2590 	cec_curr->realloc_bandwidth = 0;
2591 
2592 	/* Now we are going into the callbacks */
2593 	cec_curr->in_callbacks	    = B_TRUE;
2594 
2595 	/* Unlock the Isoch CEC member list */
2596 	mutex_exit(&cec_curr->isoch_cec_mutex);
2597 
2598 	/* Call all of the teardown_target() callbacks */
2599 	member_curr = cec_curr->cec_member_list_head;
2600 	while (member_curr != NULL) {
2601 		if (member_curr->isoch_cec_evts.teardown_target != NULL) {
2602 			teardown_callback =
2603 			    member_curr->isoch_cec_evts.teardown_target;
2604 			teardown_callback(t1394_isoch_cec_hdl,
2605 			    member_curr->isoch_cec_evts_arg);
2606 		}
2607 		member_curr = member_curr->cec_mem_next;
2608 	}
2609 
2610 	/* Lock the Isoch CEC member list */
2611 	mutex_enter(&cec_curr->isoch_cec_mutex);
2612 
2613 	/* We are finished with the callbacks */
2614 	cec_curr->in_callbacks = B_FALSE;
2615 	if (cec_curr->cec_want_wakeup == B_TRUE) {
2616 		cec_curr->cec_want_wakeup = B_FALSE;
2617 		cv_broadcast(&cec_curr->in_callbacks_cv);
2618 	}
2619 
2620 	/*
2621 	 * Now "join" and "setup" are legal state transitions
2622 	 * and "start" and "teardown" are illegal state transitions
2623 	 */
2624 	CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_JOIN | ISOCH_CEC_SETUP));
2625 	CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
2626 
2627 	/* And if the member list is empty, then "free" is legal too */
2628 	if ((cec_curr->cec_member_list_head == NULL) &&
2629 	    (cec_curr->cec_member_list_tail == NULL)) {
2630 		CEC_SET_LEGAL(cec_curr, ISOCH_CEC_FREE);
2631 	}
2632 
2633 	/* Unlock the Isoch CEC member list */
2634 	mutex_exit(&cec_curr->isoch_cec_mutex);
2635 	return (DDI_SUCCESS);
2636 }
2637 
2638 /*
2639  * Function:    t1394_alloc_isoch_dma()
2640  * Input(s):    t1394_hdl		The target "handle" returned by
2641  *					    t1394_attach()
2642  *		idi			This structure contains information
2643  *					    for configuring the data flow for
2644  *					    isochronous DMA
2645  *		flags			The flags parameter is unused (for now)
2646  *
2647  * Output(s):	t1394_idma_hdl		The IDMA "handle" used in all
2648  *					    subsequent isoch_dma() calls
2649  *		result			Used to pass more specific info back
2650  *					    to target
2651  *
2652  * Description:	t1394_alloc_isoch_dma() allocates and initializes an
2653  *		isochronous DMA resource for transmitting or receiving
2654  *		isochronous data.  If it fails, result may hold
2655  *		T1394_EIDMA_NO_RESRCS, indicating that no isoch DMA resource
2656  *		are available.
2657  */
2658 /* ARGSUSED */
2659 int
t1394_alloc_isoch_dma(t1394_handle_t t1394_hdl,id1394_isoch_dmainfo_t * idi,uint_t flags,t1394_isoch_dma_handle_t * t1394_idma_hdl,int * result)2660 t1394_alloc_isoch_dma(t1394_handle_t t1394_hdl,
2661     id1394_isoch_dmainfo_t *idi, uint_t flags,
2662     t1394_isoch_dma_handle_t *t1394_idma_hdl, int *result)
2663 {
2664 	s1394_hal_t	*hal;
2665 	int		ret;
2666 
2667 	ASSERT(t1394_hdl != NULL);
2668 	ASSERT(idi != NULL);
2669 	ASSERT(t1394_idma_hdl != NULL);
2670 
2671 	/* Find the HAL this target resides on */
2672 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2673 
2674 	/* Sanity check dma options.  If talk enabled, listen should be off */
2675 	if ((idi->idma_options & ID1394_TALK) &&
2676 	    (idi->idma_options != ID1394_TALK)) {
2677 		*result = T1394_EIDMA_CONFLICT;
2678 		return (DDI_FAILURE);
2679 	}
2680 
2681 	/* Only one listen mode allowed */
2682 	if ((idi->idma_options & ID1394_LISTEN_PKT_MODE) &&
2683 	    (idi->idma_options & ID1394_LISTEN_BUF_MODE)) {
2684 		*result = T1394_EIDMA_CONFLICT;
2685 		return (DDI_FAILURE);
2686 	}
2687 
2688 	/* Have HAL alloc a resource and compile ixl */
2689 	ret = HAL_CALL(hal).alloc_isoch_dma(hal->halinfo.hal_private, idi,
2690 	    (void **)t1394_idma_hdl, result);
2691 
2692 	if (ret != DDI_SUCCESS) {
2693 		if (*result == IXL1394_ENO_DMA_RESRCS) {
2694 			*result = T1394_EIDMA_NO_RESRCS;
2695 		}
2696 	}
2697 
2698 	return (ret);
2699 }
2700 
2701 /*
2702  * Function:    t1394_free_isoch_dma()
2703  * Input(s):    t1394_hdl		The target "handle" returned by
2704  *					    t1394_attach()
2705  *		flags			The flags parameter is unused (for now)
2706  *		t1394_idma_hdl		The IDMA "handle" returned by
2707  *					    t1394_alloc_isoch_dma()
2708  *
2709  * Output(s):	None
2710  *
2711  * Description:	t1394_free_isoch_dma() is used to free all DMA resources
2712  *		allocated for the isoch stream associated with t1394_idma_hdl.
2713  */
2714 /* ARGSUSED */
2715 void
t1394_free_isoch_dma(t1394_handle_t t1394_hdl,uint_t flags,t1394_isoch_dma_handle_t * t1394_idma_hdl)2716 t1394_free_isoch_dma(t1394_handle_t t1394_hdl, uint_t flags,
2717     t1394_isoch_dma_handle_t *t1394_idma_hdl)
2718 {
2719 	s1394_hal_t	*hal;
2720 
2721 	ASSERT(t1394_hdl != NULL);
2722 	ASSERT(*t1394_idma_hdl != NULL);
2723 
2724 	/* Find the HAL this target resides on */
2725 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2726 
2727 	/* Tell HAL to release local isoch dma resources */
2728 	HAL_CALL(hal).free_isoch_dma(hal->halinfo.hal_private, *t1394_idma_hdl);
2729 
2730 	/* Null out isoch handle */
2731 	*t1394_idma_hdl = NULL;
2732 }
2733 
2734 /*
2735  * Function:    t1394_start_isoch_dma()
2736  * Input(s):    t1394_hdl		The target "handle" returned by
2737  *					    t1394_attach()
2738  *		t1394_idma_hdl		The IDMA "handle" returned by
2739  *					    t1394_alloc_isoch_dma()
2740  *		idma_ctrlinfo		This structure contains control args
2741  *					    used when starting isoch DMA for
2742  *					    the allocated resource
2743  *		flags			One flag defined - ID1394_START_ON_CYCLE
2744  *
2745  * Output(s):	result			Used to pass more specific info back
2746  *					    to target
2747  *
2748  * Description:	t1394_start_isoch_dma() is used to start DMA for the isoch
2749  *		stream associated with t1394_idma_hdl.
2750  */
2751 /* ARGSUSED */
2752 int
t1394_start_isoch_dma(t1394_handle_t t1394_hdl,t1394_isoch_dma_handle_t t1394_idma_hdl,id1394_isoch_dma_ctrlinfo_t * idma_ctrlinfo,uint_t flags,int * result)2753 t1394_start_isoch_dma(t1394_handle_t t1394_hdl,
2754     t1394_isoch_dma_handle_t t1394_idma_hdl,
2755     id1394_isoch_dma_ctrlinfo_t *idma_ctrlinfo, uint_t flags,
2756     int *result)
2757 {
2758 	s1394_hal_t	*hal;
2759 	int		ret;
2760 
2761 	ASSERT(t1394_hdl != NULL);
2762 	ASSERT(t1394_idma_hdl != NULL);
2763 	ASSERT(idma_ctrlinfo != NULL);
2764 
2765 	/* Find the HAL this target resides on */
2766 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2767 
2768 	ret = HAL_CALL(hal).start_isoch_dma(hal->halinfo.hal_private,
2769 	    (void *)t1394_idma_hdl, idma_ctrlinfo, flags, result);
2770 
2771 	return (ret);
2772 }
2773 
2774 /*
2775  * Function:    t1394_stop_isoch_dma()
2776  * Input(s):    t1394_hdl		The target "handle" returned by
2777  *					    t1394_attach()
2778  *		t1394_idma_hdl		The IDMA "handle" returned by
2779  *					    t1394_alloc_isoch_dma()
2780  *		flags			The flags parameter is unused (for now)
2781  *
2782  * Output(s):	None
2783  *
2784  * Description:	t1394_stop_isoch_dma() is used to stop DMA for the isoch
2785  *		stream associated with t1394_idma_hdl.
2786  */
2787 /* ARGSUSED */
2788 void
t1394_stop_isoch_dma(t1394_handle_t t1394_hdl,t1394_isoch_dma_handle_t t1394_idma_hdl,uint_t flags)2789 t1394_stop_isoch_dma(t1394_handle_t t1394_hdl,
2790     t1394_isoch_dma_handle_t t1394_idma_hdl, uint_t flags)
2791 {
2792 	s1394_hal_t	*hal;
2793 	int		result;
2794 
2795 	ASSERT(t1394_hdl != NULL);
2796 	ASSERT(t1394_idma_hdl != NULL);
2797 
2798 	/* Find the HAL this target resides on */
2799 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2800 
2801 	HAL_CALL(hal).stop_isoch_dma(hal->halinfo.hal_private,
2802 	    (void *)t1394_idma_hdl, &result);
2803 }
2804 
2805 /*
2806  * Function:    t1394_update_isoch_dma()
2807  * Input(s):    t1394_hdl		The target "handle" returned by
2808  *					    t1394_attach()
2809  *		t1394_idma_hdl		The IDMA "handle" returned by
2810  *					    t1394_alloc_isoch_dma()
2811  *		idma_updateinfo		This structure contains ixl command args
2812  *					    used when updating args in an
2813  *					    existing list of ixl commands with
2814  *					    args in a new list of ixl commands.
2815  *		flags			The flags parameter is unused (for now)
2816  *
2817  * Output(s):	result			Used to pass more specific info back
2818  *					    to target
2819  *
2820  * Description:	t1394_update_isoch_dma() is used to alter an IXL program that
2821  *		has already been built (compiled) by t1394_alloc_isoch_dma().
2822  */
2823 /* ARGSUSED */
2824 int
t1394_update_isoch_dma(t1394_handle_t t1394_hdl,t1394_isoch_dma_handle_t t1394_idma_hdl,id1394_isoch_dma_updateinfo_t * idma_updateinfo,uint_t flags,int * result)2825 t1394_update_isoch_dma(t1394_handle_t t1394_hdl,
2826     t1394_isoch_dma_handle_t t1394_idma_hdl,
2827     id1394_isoch_dma_updateinfo_t *idma_updateinfo, uint_t flags,
2828     int *result)
2829 {
2830 	s1394_hal_t	*hal;
2831 	int		ret;
2832 
2833 	ASSERT(t1394_hdl != NULL);
2834 	ASSERT(t1394_idma_hdl != NULL);
2835 	ASSERT(idma_updateinfo != NULL);
2836 
2837 	/* Find the HAL this target resides on */
2838 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2839 
2840 	ret = HAL_CALL(hal).update_isoch_dma(hal->halinfo.hal_private,
2841 	    (void *)t1394_idma_hdl, idma_updateinfo, flags, result);
2842 
2843 	return (ret);
2844 }
2845 
2846 /*
2847  * Function:    t1394_initiate_bus_reset()
2848  * Input(s):    t1394_hdl		The target "handle" returned by
2849  *					    t1394_attach()
2850  *		flags			The flags parameter is unused (for now)
2851  *
2852  * Output(s):	None
2853  *
2854  * Description:	t1394_initiate_bus_reset() determines whether the local
2855  *		device has a P1394A PHY and will support the arbitrated
2856  *		short bus reset. If not, it will initiate a normal bus reset.
2857  */
2858 /* ARGSUSED */
2859 void
t1394_initiate_bus_reset(t1394_handle_t t1394_hdl,uint_t flags)2860 t1394_initiate_bus_reset(t1394_handle_t t1394_hdl, uint_t flags)
2861 {
2862 	s1394_hal_t	*hal;
2863 
2864 	ASSERT(t1394_hdl != NULL);
2865 
2866 	/* Find the HAL this target resides on */
2867 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2868 
2869 	/* Reset the bus */
2870 	if (hal->halinfo.phy == H1394_PHY_1394A) {
2871 		(void) HAL_CALL(hal).short_bus_reset(hal->halinfo.hal_private);
2872 	} else {
2873 		(void) HAL_CALL(hal).bus_reset(hal->halinfo.hal_private);
2874 	}
2875 }
2876 
2877 /*
2878  * Function:    t1394_get_topology_map()
2879  * Input(s):    t1394_hdl		The target "handle" returned by
2880  *					    t1394_attach()
2881  *		bus_generation		The current generation
2882  *		tm_length		The size of the tm_buffer given
2883  *		flags			The flags parameter is unused (for now)
2884  *
2885  * Output(s):	tm_buffer		Filled in by the 1394 Software Framework
2886  *					    with the contents of the local
2887  *					    TOPOLOGY_MAP
2888  *
2889  * Description:	t1394_get_topology_map() returns the 1394 TOPLOGY_MAP.  See
2890  *		IEEE 1394-1995 Section 8.2.3.4.1 for format information.  This
2891  *		call can fail if there is a generation mismatch or the
2892  *		tm_buffer is too small to hold the TOPOLOGY_MAP.
2893  */
2894 /* ARGSUSED */
2895 int
t1394_get_topology_map(t1394_handle_t t1394_hdl,uint_t bus_generation,size_t tm_length,uint_t flags,uint32_t * tm_buffer)2896 t1394_get_topology_map(t1394_handle_t t1394_hdl, uint_t bus_generation,
2897     size_t tm_length, uint_t flags, uint32_t *tm_buffer)
2898 {
2899 	s1394_hal_t	*hal;
2900 	uint32_t	*tm_ptr;
2901 	uint_t		length;
2902 
2903 	ASSERT(t1394_hdl != NULL);
2904 
2905 	/* Find the HAL this target resides on */
2906 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2907 
2908 	/* Lock the topology tree */
2909 	mutex_enter(&hal->topology_tree_mutex);
2910 
2911 	/* Check the bus_generation for the Topology Map */
2912 	if (bus_generation != hal->generation_count) {
2913 		/* Unlock the topology tree */
2914 		mutex_exit(&hal->topology_tree_mutex);
2915 		return (DDI_FAILURE);
2916 	}
2917 
2918 	tm_ptr	= (uint32_t *)hal->CSR_topology_map;
2919 	length	= tm_ptr[0] >> 16;
2920 	length  = length * 4;	/* Bytes instead of quadlets   */
2921 	length  = length + 4;   /* don't forget the first quad */
2922 
2923 	/* Check that the buffer is big enough */
2924 	if (length > (uint_t)tm_length) {
2925 		/* Unlock the topology tree */
2926 		mutex_exit(&hal->topology_tree_mutex);
2927 		return (DDI_FAILURE);
2928 	}
2929 
2930 	/* Do the copy */
2931 	bcopy(tm_ptr, tm_buffer, length);
2932 
2933 	/* Unlock the topology tree */
2934 	mutex_exit(&hal->topology_tree_mutex);
2935 	return (DDI_SUCCESS);
2936 }
2937 
2938 /*
2939  * Function:    t1394_CRC16()
2940  * Input(s):    d			The data to compute the CRC-16 for
2941  *		crc_length		The length into the data to compute for
2942  *		flags			The flags parameter is unused (for now)
2943  *
2944  * Output(s):	CRC			The CRC-16 computed for the length
2945  *					    of data specified
2946  *
2947  * Description:	t1394_CRC16() implements ISO/IEC 13213:1994, ANSI/IEEE Std
2948  *		1212, 1994 - 8.1.5.
2949  */
2950 /* ARGSUSED */
2951 uint_t
t1394_CRC16(uint32_t * d,size_t crc_length,uint_t flags)2952 t1394_CRC16(uint32_t *d, size_t crc_length, uint_t flags)
2953 {
2954 	/* Implements ISO/IEC 13213:1994,	*/
2955 	/* ANSI/IEEE Std 1212, 1994 - 8.1.5	*/
2956 	uint_t	ret;
2957 
2958 	ret = s1394_CRC16((uint_t *)d, (uint_t)crc_length);
2959 
2960 	return (ret);
2961 }
2962 
2963 /*
2964  * Function:    t1394_add_cfgrom_entry()
2965  * Input(s):    t1394_hdl		The target "handle" returned by
2966  *					    t1394_attach()
2967  *		cfgrom_entryinfo	This structure holds the cfgrom key,
2968  *					    buffer, and size
2969  *		flags			The flags parameter is unused (for now)
2970  *
2971  * Output(s):	t1394_cfgrom_hdl	The ConfigROM "handle" used in
2972  *					    t1394_rem_cfgrom_entry()
2973  *		result			Used to pass more specific info back
2974  *					    to target
2975  *
2976  * Description:	t1394_add_cfgrom_entry() adds an entry to the local Config ROM,
2977  *		updating the directory entries as necessary.  This call could
2978  *		fail because there is no room for the new entry in Config ROM
2979  *		(T1394_ECFGROM_FULL), the key is invalid (T1394_EINVALID_PARAM),
2980  *		or it was called in interrupt context (T1394_EINVALID_CONTEXT).
2981  */
2982 /* ARGSUSED */
2983 int
t1394_add_cfgrom_entry(t1394_handle_t t1394_hdl,t1394_cfgrom_entryinfo_t * cfgrom_entryinfo,uint_t flags,t1394_cfgrom_handle_t * t1394_cfgrom_hdl,int * result)2984 t1394_add_cfgrom_entry(t1394_handle_t t1394_hdl,
2985     t1394_cfgrom_entryinfo_t *cfgrom_entryinfo, uint_t flags,
2986     t1394_cfgrom_handle_t *t1394_cfgrom_hdl, int *result)
2987 {
2988 	s1394_hal_t	*hal;
2989 	s1394_target_t	*target;
2990 	int		ret;
2991 	uint_t		key;
2992 	uint_t		size;
2993 	uint32_t	*buffer;
2994 
2995 	ASSERT(t1394_hdl != NULL);
2996 
2997 	target = (s1394_target_t *)t1394_hdl;
2998 
2999 	key = cfgrom_entryinfo->ce_key;
3000 	buffer = cfgrom_entryinfo->ce_buffer;
3001 	size = (uint_t)cfgrom_entryinfo->ce_size;
3002 
3003 	/* Check for a valid size */
3004 	if (size == 0) {
3005 		*result = T1394_EINVALID_PARAM;
3006 		return (DDI_FAILURE);
3007 	}
3008 
3009 	/* Check for a valid key type */
3010 	if (((key << IEEE1212_KEY_VALUE_SHIFT) & IEEE1212_KEY_TYPE_MASK) == 0) {
3011 		*result = T1394_EINVALID_PARAM;
3012 		return (DDI_FAILURE);
3013 	}
3014 
3015 	/* Find the HAL this target resides on */
3016 	hal = target->on_hal;
3017 
3018 	/* Is this on the interrupt stack? */
3019 	if (servicing_interrupt()) {
3020 		*result = T1394_EINVALID_CONTEXT;
3021 		return (DDI_FAILURE);
3022 	}
3023 
3024 	/* Lock the Config ROM buffer */
3025 	mutex_enter(&hal->local_config_rom_mutex);
3026 
3027 	ret = s1394_add_config_rom_entry(hal, key, buffer, size,
3028 	    (void **)t1394_cfgrom_hdl, result);
3029 	if (ret != DDI_SUCCESS) {
3030 		if (*result == CMD1394_ERSRC_CONFLICT)
3031 			*result = T1394_ECFGROM_FULL;
3032 		mutex_exit(&hal->local_config_rom_mutex);
3033 
3034 		return (ret);
3035 	}
3036 
3037 	/* Setup the timeout function */
3038 	if (hal->config_rom_timer_set == B_FALSE) {
3039 		hal->config_rom_timer_set = B_TRUE;
3040 		mutex_exit(&hal->local_config_rom_mutex);
3041 		hal->config_rom_timer =
3042 		    timeout(s1394_update_config_rom_callback, hal,
3043 		    drv_usectohz(CONFIG_ROM_UPDATE_DELAY * 1000));
3044 	} else {
3045 		mutex_exit(&hal->local_config_rom_mutex);
3046 	}
3047 
3048 	return (ret);
3049 }
3050 
3051 /*
3052  * Function:    t1394_rem_cfgrom_entry()
3053  * Input(s):    t1394_hdl		The target "handle" returned by
3054  *					    t1394_attach()
3055  *		flags			The flags parameter is unused (for now)
3056  *		t1394_cfgrom_hdl	The ConfigROM "handle" returned by
3057  *					    t1394_add_cfgrom_entry()
3058  *
3059  * Output(s):	result			Used to pass more specific info back
3060  *					    to target
3061  *
3062  * Description:	t1394_rem_cfgrom_entry() is used to remove a previously added
3063  *		Config ROM entry (indicated by t1394_cfgrom_hdl).
3064  */
3065 /* ARGSUSED */
3066 int
t1394_rem_cfgrom_entry(t1394_handle_t t1394_hdl,uint_t flags,t1394_cfgrom_handle_t * t1394_cfgrom_hdl,int * result)3067 t1394_rem_cfgrom_entry(t1394_handle_t t1394_hdl, uint_t flags,
3068     t1394_cfgrom_handle_t *t1394_cfgrom_hdl, int *result)
3069 {
3070 	s1394_hal_t	*hal;
3071 	s1394_target_t	*target;
3072 	int		ret;
3073 
3074 	ASSERT(t1394_hdl != NULL);
3075 
3076 	target = (s1394_target_t *)t1394_hdl;
3077 
3078 	/* Find the HAL this target resides on */
3079 	hal = target->on_hal;
3080 
3081 	/* Is this on the interrupt stack? */
3082 	if (servicing_interrupt()) {
3083 		*result = T1394_EINVALID_CONTEXT;
3084 		return (DDI_FAILURE);
3085 	}
3086 
3087 	/* Lock the Config ROM buffer */
3088 	mutex_enter(&hal->local_config_rom_mutex);
3089 
3090 	ret = s1394_remove_config_rom_entry(hal, (void **)t1394_cfgrom_hdl,
3091 	    result);
3092 	if (ret != DDI_SUCCESS) {
3093 		mutex_exit(&hal->local_config_rom_mutex);
3094 		return (ret);
3095 	}
3096 
3097 	/* Setup the timeout function */
3098 	if (hal->config_rom_timer_set == B_FALSE) {
3099 		hal->config_rom_timer_set = B_TRUE;
3100 		mutex_exit(&hal->local_config_rom_mutex);
3101 		hal->config_rom_timer =
3102 		    timeout(s1394_update_config_rom_callback, hal,
3103 		    drv_usectohz(CONFIG_ROM_UPDATE_DELAY * 1000));
3104 	} else {
3105 		mutex_exit(&hal->local_config_rom_mutex);
3106 	}
3107 
3108 	return (ret);
3109 }
3110 
3111 /*
3112  * Function:    t1394_get_targetinfo()
3113  * Input(s):    t1394_hdl		The target "handle" returned by
3114  *					    t1394_attach()
3115  *		bus_generation		The current generation
3116  *		flags			The flags parameter is unused (for now)
3117  *
3118  * Output(s):	targetinfo		Structure containing max_payload,
3119  *					    max_speed, and target node ID.
3120  *
3121  * Description:	t1394_get_targetinfo() is used to retrieve information specific
3122  *		to a target device.  It will fail if the generation given
3123  *		does not match the current generation.
3124  */
3125 /* ARGSUSED */
3126 int
t1394_get_targetinfo(t1394_handle_t t1394_hdl,uint_t bus_generation,uint_t flags,t1394_targetinfo_t * targetinfo)3127 t1394_get_targetinfo(t1394_handle_t t1394_hdl, uint_t bus_generation,
3128     uint_t flags, t1394_targetinfo_t *targetinfo)
3129 {
3130 	s1394_hal_t	*hal;
3131 	s1394_target_t	*target;
3132 	uint_t		dev;
3133 	uint_t		curr;
3134 	uint_t		from_node;
3135 	uint_t		to_node;
3136 
3137 	ASSERT(t1394_hdl != NULL);
3138 
3139 	/* Find the HAL this target resides on */
3140 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
3141 
3142 	target = (s1394_target_t *)t1394_hdl;
3143 
3144 	/* Lock the topology tree */
3145 	mutex_enter(&hal->topology_tree_mutex);
3146 
3147 	/* Check the bus_generation */
3148 	if (bus_generation != hal->generation_count) {
3149 		/* Unlock the topology tree */
3150 		mutex_exit(&hal->topology_tree_mutex);
3151 		return (DDI_FAILURE);
3152 	}
3153 
3154 	rw_enter(&hal->target_list_rwlock, RW_READER);
3155 	/*
3156 	 * If there is no node, report T1394_INVALID_NODEID for target_nodeID;
3157 	 * current_max_speed and current_max_payload are undefined for this
3158 	 * case.
3159 	 */
3160 	if (((target->target_state & S1394_TARG_GONE) != 0) ||
3161 	    (target->on_node == NULL)) {
3162 		targetinfo->target_nodeID = T1394_INVALID_NODEID;
3163 	} else {
3164 		targetinfo->target_nodeID =
3165 		    (target->on_hal->node_id & IEEE1394_BUS_NUM_MASK) |
3166 		    target->on_node->node_num;
3167 
3168 		from_node = (target->on_hal->node_id) & IEEE1394_NODE_NUM_MASK;
3169 		to_node = target->on_node->node_num;
3170 
3171 		targetinfo->current_max_speed = (uint_t)s1394_speed_map_get(
3172 		    hal, from_node, to_node);
3173 
3174 		/* Get current_max_payload */
3175 		s1394_get_maxpayload(target, &dev, &curr);
3176 		targetinfo->current_max_payload	= curr;
3177 	}
3178 
3179 	rw_exit(&hal->target_list_rwlock);
3180 	/* Unlock the topology tree */
3181 	mutex_exit(&hal->topology_tree_mutex);
3182 	return (DDI_SUCCESS);
3183 }
3184