xref: /illumos-gate/usr/src/uts/common/io/1394/t1394.c (revision 2570281c)
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 		/* Unlock the Isoch CEC member list */
1848 		mutex_exit(&cec_curr->isoch_cec_mutex);
1849 		return (DDI_FAILURE);
1850 	}
1851 
1852 	/* Copy the events information into the struct */
1853 	member_new->isoch_cec_evts	= join_isoch_info->isoch_cec_evts;
1854 	member_new->isoch_cec_evts_arg	= join_isoch_info->isoch_cec_evts_arg;
1855 	member_new->cec_mem_options	= join_isoch_info->jii_options;
1856 	member_new->cec_mem_target	= (s1394_target_t *)t1394_hdl;
1857 
1858 	/* Insert new member into Isoch CEC's member list */
1859 	s1394_isoch_cec_member_list_insert(hal, cec_curr, member_new);
1860 
1861 	/* Update the channel mask filter */
1862 	cec_curr->filter_channel_mask	= check_mask;
1863 
1864 	/* Update the speed filter */
1865 	cec_curr->filter_current_speed	= curr_max_speed;
1866 
1867 	/* Update the talker pointer (if necessary) */
1868 	if (join_isoch_info->jii_options & T1394_TALKER)
1869 		cec_curr->cec_member_talker = cec_curr->cec_member_list_head;
1870 
1871 	/*
1872 	 * Now "leave" is a legal state transition
1873 	 * and "free" is an illegal state transition
1874 	 */
1875 	CEC_SET_LEGAL(cec_curr, ISOCH_CEC_LEAVE);
1876 	CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_FREE);
1877 
1878 	/* Unlock the Isoch CEC member list */
1879 	mutex_exit(&cec_curr->isoch_cec_mutex);
1880 
1881 	return (DDI_SUCCESS);
1882 }
1883 
1884 /*
1885  * Function:    t1394_leave_isoch_cec()
1886  * Input(s):    t1394_hdl		The target "handle" returned by
1887  *					    t1394_attach()
1888  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
1889  *					    t1394_alloc_isoch_cec()
1890  *		flags			The flags parameter is unused (for now)
1891  *
1892  * Output(s):	DDI_SUCCESS		Target successfully left the
1893  *					    Isoch CEC
1894  *		DDI_FAILURE		Target failed to leave the Isoch CEC
1895  *
1896  * Description:	t1394_leave_isoch_cec() is used by a target driver to remove
1897  *		itself from the Isoch CEC's member list.  It is possible
1898  *		for this call to fail because the target is not found in
1899  *		the current member list, or because it is not an appropriate
1900  *		time for a target to leave.
1901  */
1902 /* ARGSUSED */
1903 int
t1394_leave_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_handle_t t1394_isoch_cec_hdl,uint_t flags)1904 t1394_leave_isoch_cec(t1394_handle_t t1394_hdl,
1905     t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
1906 {
1907 	s1394_hal_t		 *hal;
1908 	s1394_isoch_cec_t	 *cec_curr;
1909 	s1394_isoch_cec_member_t *member_curr;
1910 	s1394_isoch_cec_member_t *member_temp;
1911 	boolean_t		 found;
1912 	uint64_t		 temp_channel_mask;
1913 	uint_t			 temp_max_speed;
1914 
1915 	ASSERT(t1394_hdl != NULL);
1916 	ASSERT(t1394_isoch_cec_hdl != NULL);
1917 
1918 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
1919 
1920 	/* Convert the handle to an Isoch CEC pointer */
1921 	cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
1922 
1923 	/* Lock the Isoch CEC member list */
1924 	mutex_enter(&cec_curr->isoch_cec_mutex);
1925 
1926 	/* Are we in any callbacks? (Wait for them to finish) */
1927 	while (CEC_IN_ANY_CALLBACKS(cec_curr)) {
1928 		cec_curr->cec_want_wakeup = B_TRUE;
1929 		cv_wait(&cec_curr->in_callbacks_cv,
1930 		    &cec_curr->isoch_cec_mutex);
1931 	}
1932 
1933 	/* Is "leave" a legal state transition? */
1934 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_LEAVE) == 0) {
1935 		/* Unlock the Isoch CEC member list */
1936 		mutex_exit(&cec_curr->isoch_cec_mutex);
1937 		return (DDI_FAILURE);
1938 	}
1939 
1940 	/* Find the Target on the CEC's member list */
1941 	found = B_FALSE;
1942 	temp_channel_mask = cec_curr->cec_alloc_props.cec_channel_mask;
1943 	temp_max_speed	  = cec_curr->cec_alloc_props.cec_max_speed;
1944 	member_curr	  = cec_curr->cec_member_list_head;
1945 	while (member_curr != NULL) {
1946 		if (member_curr->cec_mem_target ==
1947 		    (s1394_target_t *)t1394_hdl) {
1948 			member_temp = member_curr;
1949 			found	    = B_TRUE;
1950 		} else {
1951 			/* Keep track of channel mask and max speed info */
1952 			temp_channel_mask &= member_curr->req_channel_mask;
1953 			if (member_curr->req_max_speed < temp_max_speed)
1954 				temp_max_speed = member_curr->req_max_speed;
1955 		}
1956 		member_curr = member_curr->cec_mem_next;
1957 	}
1958 
1959 	/* Target not found on this Isoch CEC */
1960 	if (found == B_FALSE) {
1961 		/* Unlock the Isoch CEC member list */
1962 		mutex_exit(&cec_curr->isoch_cec_mutex);
1963 		return (DDI_FAILURE);
1964 	} else {
1965 		/* This member's departure may change filter constraints */
1966 		cec_curr->filter_current_speed  = temp_max_speed;
1967 		cec_curr->filter_channel_mask   = temp_channel_mask;
1968 	}
1969 
1970 	/* Remove member from Isoch CEC's member list */
1971 	s1394_isoch_cec_member_list_remove(hal, cec_curr, member_temp);
1972 
1973 	/* If we are removing the talker, then update the pointer */
1974 	if (cec_curr->cec_member_talker == member_temp)
1975 		cec_curr->cec_member_talker = NULL;
1976 
1977 	/* Is the Isoch CEC's member list empty? */
1978 	if ((cec_curr->cec_member_list_head == NULL) &&
1979 	    (cec_curr->cec_member_list_tail == NULL)) {
1980 		/*
1981 		 * Now "free" _might_ be a legal state transition
1982 		 * if we aren't in setup or start phases and "leave"
1983 		 * is definitely an illegal state transition
1984 		 */
1985 		if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_JOIN) != 0)
1986 			CEC_SET_LEGAL(cec_curr, ISOCH_CEC_FREE);
1987 		CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_LEAVE);
1988 	}
1989 
1990 	/* Unlock the Isoch CEC member list */
1991 	mutex_exit(&cec_curr->isoch_cec_mutex);
1992 
1993 	/* Free the Isoch CEC member structure */
1994 	kmem_free(member_temp, sizeof (s1394_isoch_cec_member_t));
1995 
1996 	return (DDI_SUCCESS);
1997 }
1998 
1999 /*
2000  * Function:    t1394_setup_isoch_cec()
2001  * Input(s):    t1394_hdl		The target "handle" returned by
2002  *					    t1394_attach()
2003  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
2004  *					    t1394_alloc_isoch_cec()
2005  *		flags			The flags parameter is unused (for now)
2006  *
2007  * Output(s):	result			Used to pass more specific info back
2008  *					    to target
2009  *
2010  * Description:	t1394_setup_isoch_cec() directs the 1394 Software Framework
2011  *		to allocate isochronous resources and invoke the setup_target()
2012  *		callback for each member of the Isoch CEC.  This call may
2013  *		fail because bandwidth was unavailable (T1394_ENO_BANDWIDTH),
2014  *		channels were unavailable (T1394_ENO_CHANNEL), or one of the
2015  *		member targets returned failure from its setup_target()
2016  *		callback.
2017  */
2018 /* ARGSUSED */
2019 int
t1394_setup_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_handle_t t1394_isoch_cec_hdl,uint_t flags,int * result)2020 t1394_setup_isoch_cec(t1394_handle_t t1394_hdl,
2021     t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags, int *result)
2022 {
2023 	s1394_hal_t			*hal;
2024 	s1394_isoch_cec_t		*cec_curr;
2025 	s1394_isoch_cec_member_t	*member_curr;
2026 	t1394_setup_target_args_t	target_args;
2027 	uint64_t			temp_chnl_mask;
2028 	uint32_t			old_chnl;
2029 	uint32_t			try_chnl;
2030 	uint_t				bw_alloc_units;
2031 	uint_t				generation;
2032 	int				chnl_num;
2033 	int				err;
2034 	int				ret;
2035 	int				j;
2036 	int	(*setup_callback)(t1394_isoch_cec_handle_t, opaque_t,
2037 			    t1394_setup_target_args_t *);
2038 
2039 	ASSERT(t1394_hdl != NULL);
2040 	ASSERT(t1394_isoch_cec_hdl != NULL);
2041 
2042 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2043 
2044 	/* Convert the handle to an Isoch CEC pointer */
2045 	cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
2046 
2047 	/* Lock the Isoch CEC member list */
2048 	mutex_enter(&cec_curr->isoch_cec_mutex);
2049 
2050 	/* Are we in any callbacks? */
2051 	if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
2052 		/* Unlock the Isoch CEC member list */
2053 		mutex_exit(&cec_curr->isoch_cec_mutex);
2054 		return (DDI_FAILURE);
2055 	}
2056 
2057 	/* Is "setup" a legal state transition? */
2058 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_SETUP) == 0) {
2059 		/* Unlock the Isoch CEC member list */
2060 		mutex_exit(&cec_curr->isoch_cec_mutex);
2061 		return (DDI_FAILURE);
2062 	}
2063 
2064 	/* If T1394_NO_IRM_ALLOC is set then don't allocate... do callbacks */
2065 	if (cec_curr->cec_options & T1394_NO_IRM_ALLOC) {
2066 		goto setup_do_callbacks;
2067 	}
2068 
2069 	/* Allocate bandwidth and channels */
2070 	for (j = 0; j < S1394_ISOCH_ALLOC_RETRIES; j++) {
2071 		/*
2072 		 * Get the current generation number - don't
2073 		 * need the lock because we are read only here
2074 		 */
2075 		generation = hal->generation_count;
2076 
2077 		/* Compute how much bandwidth is needed */
2078 		bw_alloc_units = s1394_compute_bw_alloc_units(hal,
2079 		    cec_curr->bandwidth, cec_curr->filter_current_speed);
2080 
2081 		/* Check that the generation has not changed - */
2082 		/* don't need the lock (read only) */
2083 		if (generation != hal->generation_count)
2084 			continue;
2085 
2086 		/* Unlock the Isoch CEC member list */
2087 		mutex_exit(&cec_curr->isoch_cec_mutex);
2088 
2089 		/* Try to allocate the bandwidth */
2090 		ret = s1394_bandwidth_alloc(hal, bw_alloc_units, generation,
2091 		    &err);
2092 
2093 		/* Lock the Isoch CEC member list */
2094 		mutex_enter(&cec_curr->isoch_cec_mutex);
2095 
2096 		/* If there was a bus reset, start over */
2097 		if (ret == DDI_FAILURE) {
2098 			if (err == CMD1394_EBUSRESET) {
2099 				continue; /* start over and try again */
2100 			} else {
2101 				*result = T1394_ENO_BANDWIDTH;
2102 				/* Unlock the Isoch CEC member list */
2103 				mutex_exit(&cec_curr->isoch_cec_mutex);
2104 				return (DDI_FAILURE);
2105 			}
2106 		}
2107 
2108 		/* Check that the generation has not changed - */
2109 		/* don't need the lock (read only) */
2110 		if (generation != hal->generation_count)
2111 			continue;
2112 
2113 		/*
2114 		 * Allocate a channel
2115 		 *    From IEEE 1394-1995, Section 8.3.2.3.8: "Bits
2116 		 *    allocated in the CHANNELS_AVAILABLE_HI field of
2117 		 *    this register shall start at bit zero (channel
2118 		 *    number zero), and additional channel numbers shall
2119 		 *    be represented in a monotonically increasing sequence
2120 		 *    of bit numbers up to a maximum of bit 31 (channel
2121 		 *    number 31).  Bits allocated in the CHANNELS_AVAILABLE_LO
2122 		 *    field of this register shall start at bit zero
2123 		 *    (channel number 32), and additional channel numbers
2124 		 *    shall be represented in a monotonically increasing
2125 		 *    sequence of bit numbers up to a maximum of bit 31
2126 		 *    (channel number 63).
2127 		 */
2128 		temp_chnl_mask = cec_curr->filter_channel_mask;
2129 		for (chnl_num = 63; chnl_num >= 0; chnl_num--) {
2130 			if ((temp_chnl_mask & 1) == 1) {
2131 				try_chnl = (1 << ((63 - chnl_num) % 32));
2132 
2133 				/* Unlock the Isoch CEC member list */
2134 				mutex_exit(&cec_curr->isoch_cec_mutex);
2135 				if (chnl_num < 32) {
2136 					ret = s1394_channel_alloc(hal,
2137 					    try_chnl, generation,
2138 					    S1394_CHANNEL_ALLOC_HI, &old_chnl,
2139 					    &err);
2140 				} else {
2141 					ret = s1394_channel_alloc(hal,
2142 					    try_chnl, generation,
2143 					    S1394_CHANNEL_ALLOC_LO, &old_chnl,
2144 					    &err);
2145 				}
2146 				/* Lock the Isoch CEC member list */
2147 				mutex_enter(&cec_curr->isoch_cec_mutex);
2148 
2149 				/* Did we get a channel? (or a bus reset) */
2150 				if ((ret == DDI_SUCCESS) ||
2151 				    (err == CMD1394_EBUSRESET))
2152 					break;
2153 			}
2154 			temp_chnl_mask = temp_chnl_mask >> 1;
2155 		}
2156 
2157 		/* If we've tried all the possible channels, then fail */
2158 		if (chnl_num == 0) {
2159 			*result = T1394_ENO_CHANNEL;
2160 			/*
2161 			 * If we successfully allocate bandwidth, and
2162 			 * then fail getting a channel, we need to
2163 			 * free up the bandwidth
2164 			 */
2165 
2166 			/* Check that the generation has not changed */
2167 			/* lock not needed here (read only) */
2168 			if (generation != hal->generation_count)
2169 				continue;
2170 
2171 			/* Unlock the Isoch CEC member list */
2172 			mutex_exit(&cec_curr->isoch_cec_mutex);
2173 
2174 			/* Try to free up the bandwidth */
2175 			ret = s1394_bandwidth_free(hal, bw_alloc_units,
2176 			    generation, &err);
2177 
2178 			/* Lock the Isoch CEC member list */
2179 			mutex_enter(&cec_curr->isoch_cec_mutex);
2180 
2181 			if (ret == DDI_FAILURE) {
2182 				if (err == CMD1394_EBUSRESET) {
2183 					continue;
2184 				}
2185 			}
2186 
2187 			/* Unlock the Isoch CEC member list */
2188 			mutex_exit(&cec_curr->isoch_cec_mutex);
2189 			return (DDI_FAILURE);
2190 		}
2191 
2192 		/* If we got a channel, we're done (else start over) */
2193 		if (ret == DDI_SUCCESS)
2194 			break;
2195 		else if (err == CMD1394_EBUSRESET)
2196 			continue;
2197 	}
2198 
2199 	/* Have we gotten too many bus resets? */
2200 	if (j == S1394_ISOCH_ALLOC_RETRIES) {
2201 		*result = T1394_ENO_BANDWIDTH;
2202 		/* Unlock the Isoch CEC member list */
2203 		mutex_exit(&cec_curr->isoch_cec_mutex);
2204 		return (DDI_FAILURE);
2205 	}
2206 
2207 	cec_curr->realloc_valid	    = B_TRUE;
2208 	cec_curr->realloc_chnl_num  = chnl_num;
2209 	cec_curr->realloc_bandwidth = cec_curr->bandwidth;
2210 	cec_curr->realloc_speed	    = cec_curr->filter_current_speed;
2211 
2212 setup_do_callbacks:
2213 	/* Call all of the setup_target() callbacks */
2214 	target_args.channel_num	    = chnl_num;
2215 	target_args.channel_speed   = cec_curr->filter_current_speed;
2216 
2217 	/* Now we are going into the callbacks */
2218 	cec_curr->in_callbacks	    = B_TRUE;
2219 
2220 	/* Unlock the Isoch CEC member list */
2221 	mutex_exit(&cec_curr->isoch_cec_mutex);
2222 
2223 	member_curr = cec_curr->cec_member_list_head;
2224 	*result = 0;
2225 	while (member_curr != NULL) {
2226 		if (member_curr->isoch_cec_evts.setup_target != NULL) {
2227 			setup_callback =
2228 			    member_curr->isoch_cec_evts.setup_target;
2229 			ret = setup_callback(t1394_isoch_cec_hdl,
2230 			    member_curr->isoch_cec_evts_arg, &target_args);
2231 			if (ret != DDI_SUCCESS)
2232 				*result = T1394_ETARGET;
2233 		}
2234 		member_curr = member_curr->cec_mem_next;
2235 	}
2236 
2237 	/* Lock the Isoch CEC member list */
2238 	mutex_enter(&cec_curr->isoch_cec_mutex);
2239 
2240 	/* We are finished with the callbacks */
2241 	cec_curr->in_callbacks = B_FALSE;
2242 	if (cec_curr->cec_want_wakeup == B_TRUE) {
2243 		cec_curr->cec_want_wakeup = B_FALSE;
2244 		cv_broadcast(&cec_curr->in_callbacks_cv);
2245 	}
2246 
2247 	/*
2248 	 * Now "start" and "teardown" are legal state transitions
2249 	 * and "join", "free", and "setup" are illegal state transitions
2250 	 */
2251 	CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
2252 	CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_JOIN | ISOCH_CEC_FREE |
2253 	    ISOCH_CEC_SETUP));
2254 
2255 	/* Unlock the Isoch CEC member list */
2256 	mutex_exit(&cec_curr->isoch_cec_mutex);
2257 
2258 	/* Return DDI_FAILURE if any targets failed setup */
2259 	if (*result != 0) {
2260 		return (DDI_FAILURE);
2261 	}
2262 
2263 	return (DDI_SUCCESS);
2264 }
2265 
2266 /*
2267  * Function:    t1394_start_isoch_cec()
2268  * Input(s):    t1394_hdl		The target "handle" returned by
2269  *					    t1394_attach()
2270  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
2271  *					    t1394_alloc_isoch_cec()
2272  *		flags			The flags parameter is unused (for now)
2273  *
2274  * Output(s):	DDI_SUCCESS		All start_target() callbacks returned
2275  *					    successfully
2276  *		DDI_FAILURE		One or more start_target() callbacks
2277  *					    returned failure
2278  *
2279  * Description:	t1394_start_isoch_cec() directs the 1394 Software Framework
2280  *		to invoke each of the start_target() callbacks, first for
2281  *		each listener, then for the talker.
2282  */
2283 /* ARGSUSED */
2284 int
t1394_start_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_handle_t t1394_isoch_cec_hdl,uint_t flags)2285 t1394_start_isoch_cec(t1394_handle_t t1394_hdl,
2286     t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
2287 {
2288 	s1394_isoch_cec_t	 *cec_curr;
2289 	s1394_isoch_cec_member_t *member_curr;
2290 	int			 ret;
2291 	boolean_t		 err;
2292 	int	(*start_callback)(t1394_isoch_cec_handle_t, opaque_t);
2293 
2294 	ASSERT(t1394_hdl != NULL);
2295 	ASSERT(t1394_isoch_cec_hdl != NULL);
2296 
2297 	/* Convert the handle to an Isoch CEC pointer */
2298 	cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
2299 
2300 	/* Lock the Isoch CEC member list */
2301 	mutex_enter(&cec_curr->isoch_cec_mutex);
2302 
2303 	/* Are we in any callbacks? */
2304 	if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
2305 		/* Unlock the Isoch CEC member list */
2306 		mutex_exit(&cec_curr->isoch_cec_mutex);
2307 		return (DDI_FAILURE);
2308 	}
2309 
2310 	/* Is "start" a legal state transition? */
2311 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_START) == 0) {
2312 		/* Unlock the Isoch CEC member list */
2313 		mutex_exit(&cec_curr->isoch_cec_mutex);
2314 		return (DDI_FAILURE);
2315 	}
2316 
2317 	/* Now we are going into the callbacks */
2318 	cec_curr->in_callbacks = B_TRUE;
2319 
2320 	/* Unlock the Isoch CEC member list */
2321 	mutex_exit(&cec_curr->isoch_cec_mutex);
2322 
2323 	/*
2324 	 * Call all of the start_target() callbacks
2325 	 * Start at the tail (listeners first) and
2326 	 * go toward the head (talker last)
2327 	 */
2328 	member_curr = cec_curr->cec_member_list_tail;
2329 	err = B_FALSE;
2330 	while (member_curr != NULL) {
2331 		if (member_curr->isoch_cec_evts.start_target != NULL) {
2332 			start_callback =
2333 			    member_curr->isoch_cec_evts.start_target;
2334 			ret = start_callback(t1394_isoch_cec_hdl,
2335 			    member_curr->isoch_cec_evts_arg);
2336 		if (ret != DDI_SUCCESS)
2337 			err = B_TRUE;
2338 		}
2339 		member_curr = member_curr->cec_mem_prev;
2340 	}
2341 
2342 	/* Lock the Isoch CEC member list */
2343 	mutex_enter(&cec_curr->isoch_cec_mutex);
2344 
2345 	/* We are finished with the callbacks */
2346 	cec_curr->in_callbacks = B_FALSE;
2347 	if (cec_curr->cec_want_wakeup == B_TRUE) {
2348 		cec_curr->cec_want_wakeup = B_FALSE;
2349 		cv_broadcast(&cec_curr->in_callbacks_cv);
2350 	}
2351 
2352 	/*
2353 	 * Now "stop" is a legal state transitions
2354 	 * and "start" and "teardown" are illegal state transitions
2355 	 */
2356 	CEC_SET_LEGAL(cec_curr, ISOCH_CEC_STOP);
2357 	CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
2358 
2359 	/* Unlock the Isoch CEC member list */
2360 	mutex_exit(&cec_curr->isoch_cec_mutex);
2361 
2362 	/* Return DDI_FAILURE if any targets failed start */
2363 	if (err == B_TRUE) {
2364 		return (DDI_FAILURE);
2365 	}
2366 
2367 	return (DDI_SUCCESS);
2368 }
2369 
2370 /*
2371  * Function:    t1394_stop_isoch_cec()
2372  * Input(s):    t1394_hdl		The target "handle" returned by
2373  *					    t1394_attach()
2374  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
2375  *					    t1394_alloc_isoch_cec()
2376  *		flags			The flags parameter is unused (for now)
2377  *
2378  * Output(s):	DDI_SUCCESS		Target successfully stopped the
2379  *					    Isoch CEC
2380  *		DDI_FAILURE		Target failed to stop the Isoch CEC
2381  *
2382  * Description:	t1394_stop_isoch_cec() directs the 1394 Software Framework
2383  *		to invoke each of the stop_target() callbacks, first for
2384  *		the talker, then for each listener.
2385  *		(This call will fail if it is called at an
2386  *		inappropriate time, i.e. before the t1394_start_isoch_cec()
2387  *		call, etc.)
2388  */
2389 /* ARGSUSED */
2390 int
t1394_stop_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_handle_t t1394_isoch_cec_hdl,uint_t flags)2391 t1394_stop_isoch_cec(t1394_handle_t t1394_hdl,
2392     t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
2393 {
2394 	s1394_isoch_cec_t	 *cec_curr;
2395 	s1394_isoch_cec_member_t *member_curr;
2396 	void	(*stop_callback)(t1394_isoch_cec_handle_t, opaque_t);
2397 
2398 	ASSERT(t1394_hdl != NULL);
2399 	ASSERT(t1394_isoch_cec_hdl != NULL);
2400 
2401 	/* Convert the handle to an Isoch CEC pointer */
2402 	cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
2403 
2404 	/* Lock the Isoch CEC member list */
2405 	mutex_enter(&cec_curr->isoch_cec_mutex);
2406 
2407 	/* Are we in any callbacks? */
2408 	if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
2409 		/* Unlock the Isoch CEC member list */
2410 		mutex_exit(&cec_curr->isoch_cec_mutex);
2411 		return (DDI_FAILURE);
2412 	}
2413 
2414 	/* Is "stop" a legal state transition? */
2415 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_STOP) == 0) {
2416 		/* Unlock the Isoch CEC member list */
2417 		mutex_exit(&cec_curr->isoch_cec_mutex);
2418 		return (DDI_FAILURE);
2419 	}
2420 
2421 	/* Now we are going into the callbacks */
2422 	cec_curr->in_callbacks = B_TRUE;
2423 
2424 	/* Unlock the Isoch CEC member list */
2425 	mutex_exit(&cec_curr->isoch_cec_mutex);
2426 
2427 	/*
2428 	 * Call all of the stop_target() callbacks
2429 	 * Start at the head (talker first) and
2430 	 * go toward the tail (listeners last)
2431 	 */
2432 	member_curr = cec_curr->cec_member_list_head;
2433 	while (member_curr != NULL) {
2434 		if (member_curr->isoch_cec_evts.stop_target != NULL) {
2435 			stop_callback =
2436 			    member_curr->isoch_cec_evts.stop_target;
2437 			stop_callback(t1394_isoch_cec_hdl,
2438 			    member_curr->isoch_cec_evts_arg);
2439 		}
2440 		member_curr = member_curr->cec_mem_next;
2441 	}
2442 
2443 	/* Lock the Isoch CEC member list */
2444 	mutex_enter(&cec_curr->isoch_cec_mutex);
2445 
2446 	/* We are finished with the callbacks */
2447 	cec_curr->in_callbacks = B_FALSE;
2448 	if (cec_curr->cec_want_wakeup == B_TRUE) {
2449 		cec_curr->cec_want_wakeup = B_FALSE;
2450 		cv_broadcast(&cec_curr->in_callbacks_cv);
2451 	}
2452 
2453 	/*
2454 	 * Now "start" and "teardown" are legal state transitions
2455 	 * and "stop" is an illegal state transitions
2456 	 */
2457 	CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
2458 	CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_STOP);
2459 
2460 	/* Unlock the Isoch CEC member list */
2461 	mutex_exit(&cec_curr->isoch_cec_mutex);
2462 
2463 	return (DDI_SUCCESS);
2464 }
2465 
2466 /*
2467  * Function:    t1394_teardown_isoch_cec()
2468  * Input(s):    t1394_hdl		The target "handle" returned by
2469  *					    t1394_attach()
2470  *		t1394_isoch_cec_hdl	The Isoch CEC "handle" returned by
2471  *					    t1394_alloc_isoch_cec()
2472  *		flags			The flags parameter is unused (for now)
2473  *
2474  * Output(s):	DDI_SUCCESS		Target successfully tore down the
2475  *					    Isoch CEC
2476  *		DDI_FAILURE		Target failed to tear down the
2477  *					    Isoch CEC
2478  *
2479  * Description:	t1394_teardown_isoch_cec() directs the 1394 Software Framework
2480  *		to free up any isochronous resources we might be holding and
2481  *		call all of the teardown_target() callbacks.
2482  *		(This call will fail if it is called at an
2483  *		inappropriate time, i.e. before the t1394_start_isoch_cec()
2484  *		call, before the t1394_stop_isoch_cec, etc.
2485  */
2486 /* ARGSUSED */
2487 int
t1394_teardown_isoch_cec(t1394_handle_t t1394_hdl,t1394_isoch_cec_handle_t t1394_isoch_cec_hdl,uint_t flags)2488 t1394_teardown_isoch_cec(t1394_handle_t t1394_hdl,
2489     t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
2490 {
2491 	s1394_hal_t		 *hal;
2492 	s1394_isoch_cec_t	 *cec_curr;
2493 	s1394_isoch_cec_member_t *member_curr;
2494 	uint32_t		 chnl_mask;
2495 	uint32_t		 old_chnl_mask;
2496 	uint_t			 bw_alloc_units;
2497 	uint_t			 generation;
2498 	int			 ret;
2499 	int			 err;
2500 	void	(*teardown_callback)(t1394_isoch_cec_handle_t, opaque_t);
2501 
2502 	ASSERT(t1394_hdl != NULL);
2503 	ASSERT(t1394_isoch_cec_hdl != NULL);
2504 
2505 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2506 
2507 	/* Convert the handle to an Isoch CEC pointer */
2508 	cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
2509 
2510 	/* Lock the Isoch CEC member list */
2511 	mutex_enter(&cec_curr->isoch_cec_mutex);
2512 
2513 	/* Are we in any callbacks? */
2514 	if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
2515 		/* Unlock the Isoch CEC member list */
2516 		mutex_exit(&cec_curr->isoch_cec_mutex);
2517 		return (DDI_FAILURE);
2518 	}
2519 
2520 	/* Is "teardown" a legal state transition? */
2521 	if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_TEARDOWN) == 0) {
2522 		/* Unlock the Isoch CEC member list */
2523 		mutex_exit(&cec_curr->isoch_cec_mutex);
2524 		return (DDI_FAILURE);
2525 	}
2526 
2527 	/* If T1394_NO_IRM_ALLOC is set then don't free... do callbacks */
2528 	if (cec_curr->cec_options & T1394_NO_IRM_ALLOC) {
2529 		goto teardown_do_callbacks;
2530 	}
2531 
2532 	/* If nothing has been allocated or we failed to */
2533 	/* reallocate, then we are done... call the callbacks */
2534 	if ((cec_curr->realloc_valid == B_FALSE) ||
2535 	    (cec_curr->realloc_failed == B_TRUE)) {
2536 		goto teardown_do_callbacks;
2537 	}
2538 
2539 	/*
2540 	 * Get the current generation number - don't need the
2541 	 * topology tree mutex here because it is read-only, and
2542 	 * there is a race condition with or without it.
2543 	 */
2544 	generation = hal->generation_count;
2545 
2546 	/* Compute the amount bandwidth to free */
2547 	bw_alloc_units = s1394_compute_bw_alloc_units(hal,
2548 	    cec_curr->bandwidth, cec_curr->realloc_speed);
2549 
2550 	/* Check that the generation has not changed - */
2551 	/* don't need the lock (read only) */
2552 	if (generation != hal->generation_count)
2553 		goto teardown_do_callbacks;
2554 
2555 	/* Unlock the Isoch CEC member list */
2556 	mutex_exit(&cec_curr->isoch_cec_mutex);
2557 
2558 	/* Try to free up the bandwidth */
2559 	ret = s1394_bandwidth_free(hal, bw_alloc_units, generation, &err);
2560 
2561 	/* Lock the Isoch CEC member list */
2562 	mutex_enter(&cec_curr->isoch_cec_mutex);
2563 
2564 	if (ret == DDI_FAILURE) {
2565 		if (err == CMD1394_EBUSRESET) {
2566 			goto teardown_do_callbacks;
2567 		}
2568 	}
2569 
2570 	/* Free the allocated channel */
2571 	chnl_mask = (1 << ((63 - cec_curr->realloc_chnl_num) % 32));
2572 
2573 	/* Unlock the Isoch CEC member list */
2574 	mutex_exit(&cec_curr->isoch_cec_mutex);
2575 	if (cec_curr->realloc_chnl_num < 32) {
2576 		ret = s1394_channel_free(hal, chnl_mask, generation,
2577 		    S1394_CHANNEL_ALLOC_HI, &old_chnl_mask, &err);
2578 	} else {
2579 		ret = s1394_channel_free(hal, chnl_mask, generation,
2580 		    S1394_CHANNEL_ALLOC_LO, &old_chnl_mask, &err);
2581 	}
2582 	/* Lock the Isoch CEC member list */
2583 	mutex_enter(&cec_curr->isoch_cec_mutex);
2584 
2585 teardown_do_callbacks:
2586 	/* From here on reallocation is unnecessary */
2587 	cec_curr->realloc_valid	    = B_FALSE;
2588 	cec_curr->realloc_chnl_num  = 0;
2589 	cec_curr->realloc_bandwidth = 0;
2590 
2591 	/* Now we are going into the callbacks */
2592 	cec_curr->in_callbacks	    = B_TRUE;
2593 
2594 	/* Unlock the Isoch CEC member list */
2595 	mutex_exit(&cec_curr->isoch_cec_mutex);
2596 
2597 	/* Call all of the teardown_target() callbacks */
2598 	member_curr = cec_curr->cec_member_list_head;
2599 	while (member_curr != NULL) {
2600 		if (member_curr->isoch_cec_evts.teardown_target != NULL) {
2601 			teardown_callback =
2602 			    member_curr->isoch_cec_evts.teardown_target;
2603 			teardown_callback(t1394_isoch_cec_hdl,
2604 			    member_curr->isoch_cec_evts_arg);
2605 		}
2606 		member_curr = member_curr->cec_mem_next;
2607 	}
2608 
2609 	/* Lock the Isoch CEC member list */
2610 	mutex_enter(&cec_curr->isoch_cec_mutex);
2611 
2612 	/* We are finished with the callbacks */
2613 	cec_curr->in_callbacks = B_FALSE;
2614 	if (cec_curr->cec_want_wakeup == B_TRUE) {
2615 		cec_curr->cec_want_wakeup = B_FALSE;
2616 		cv_broadcast(&cec_curr->in_callbacks_cv);
2617 	}
2618 
2619 	/*
2620 	 * Now "join" and "setup" are legal state transitions
2621 	 * and "start" and "teardown" are illegal state transitions
2622 	 */
2623 	CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_JOIN | ISOCH_CEC_SETUP));
2624 	CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
2625 
2626 	/* And if the member list is empty, then "free" is legal too */
2627 	if ((cec_curr->cec_member_list_head == NULL) &&
2628 	    (cec_curr->cec_member_list_tail == NULL)) {
2629 		CEC_SET_LEGAL(cec_curr, ISOCH_CEC_FREE);
2630 	}
2631 
2632 	/* Unlock the Isoch CEC member list */
2633 	mutex_exit(&cec_curr->isoch_cec_mutex);
2634 	return (DDI_SUCCESS);
2635 }
2636 
2637 /*
2638  * Function:    t1394_alloc_isoch_dma()
2639  * Input(s):    t1394_hdl		The target "handle" returned by
2640  *					    t1394_attach()
2641  *		idi			This structure contains information
2642  *					    for configuring the data flow for
2643  *					    isochronous DMA
2644  *		flags			The flags parameter is unused (for now)
2645  *
2646  * Output(s):	t1394_idma_hdl		The IDMA "handle" used in all
2647  *					    subsequent isoch_dma() calls
2648  *		result			Used to pass more specific info back
2649  *					    to target
2650  *
2651  * Description:	t1394_alloc_isoch_dma() allocates and initializes an
2652  *		isochronous DMA resource for transmitting or receiving
2653  *		isochronous data.  If it fails, result may hold
2654  *		T1394_EIDMA_NO_RESRCS, indicating that no isoch DMA resource
2655  *		are available.
2656  */
2657 /* ARGSUSED */
2658 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)2659 t1394_alloc_isoch_dma(t1394_handle_t t1394_hdl,
2660     id1394_isoch_dmainfo_t *idi, uint_t flags,
2661     t1394_isoch_dma_handle_t *t1394_idma_hdl, int *result)
2662 {
2663 	s1394_hal_t	*hal;
2664 	int		ret;
2665 
2666 	ASSERT(t1394_hdl != NULL);
2667 	ASSERT(idi != NULL);
2668 	ASSERT(t1394_idma_hdl != NULL);
2669 
2670 	/* Find the HAL this target resides on */
2671 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2672 
2673 	/* Sanity check dma options.  If talk enabled, listen should be off */
2674 	if ((idi->idma_options & ID1394_TALK) &&
2675 	    (idi->idma_options != ID1394_TALK)) {
2676 		*result = T1394_EIDMA_CONFLICT;
2677 		return (DDI_FAILURE);
2678 	}
2679 
2680 	/* Only one listen mode allowed */
2681 	if ((idi->idma_options & ID1394_LISTEN_PKT_MODE) &&
2682 	    (idi->idma_options & ID1394_LISTEN_BUF_MODE)) {
2683 		*result = T1394_EIDMA_CONFLICT;
2684 		return (DDI_FAILURE);
2685 	}
2686 
2687 	/* Have HAL alloc a resource and compile ixl */
2688 	ret = HAL_CALL(hal).alloc_isoch_dma(hal->halinfo.hal_private, idi,
2689 	    (void **)t1394_idma_hdl, result);
2690 
2691 	if (ret != DDI_SUCCESS) {
2692 		if (*result == IXL1394_ENO_DMA_RESRCS) {
2693 			*result = T1394_EIDMA_NO_RESRCS;
2694 		}
2695 	}
2696 
2697 	return (ret);
2698 }
2699 
2700 /*
2701  * Function:    t1394_free_isoch_dma()
2702  * Input(s):    t1394_hdl		The target "handle" returned by
2703  *					    t1394_attach()
2704  *		flags			The flags parameter is unused (for now)
2705  *		t1394_idma_hdl		The IDMA "handle" returned by
2706  *					    t1394_alloc_isoch_dma()
2707  *
2708  * Output(s):	None
2709  *
2710  * Description:	t1394_free_isoch_dma() is used to free all DMA resources
2711  *		allocated for the isoch stream associated with t1394_idma_hdl.
2712  */
2713 /* ARGSUSED */
2714 void
t1394_free_isoch_dma(t1394_handle_t t1394_hdl,uint_t flags,t1394_isoch_dma_handle_t * t1394_idma_hdl)2715 t1394_free_isoch_dma(t1394_handle_t t1394_hdl, uint_t flags,
2716     t1394_isoch_dma_handle_t *t1394_idma_hdl)
2717 {
2718 	s1394_hal_t	*hal;
2719 
2720 	ASSERT(t1394_hdl != NULL);
2721 	ASSERT(*t1394_idma_hdl != NULL);
2722 
2723 	/* Find the HAL this target resides on */
2724 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2725 
2726 	/* Tell HAL to release local isoch dma resources */
2727 	HAL_CALL(hal).free_isoch_dma(hal->halinfo.hal_private, *t1394_idma_hdl);
2728 
2729 	/* Null out isoch handle */
2730 	*t1394_idma_hdl = NULL;
2731 }
2732 
2733 /*
2734  * Function:    t1394_start_isoch_dma()
2735  * Input(s):    t1394_hdl		The target "handle" returned by
2736  *					    t1394_attach()
2737  *		t1394_idma_hdl		The IDMA "handle" returned by
2738  *					    t1394_alloc_isoch_dma()
2739  *		idma_ctrlinfo		This structure contains control args
2740  *					    used when starting isoch DMA for
2741  *					    the allocated resource
2742  *		flags			One flag defined - ID1394_START_ON_CYCLE
2743  *
2744  * Output(s):	result			Used to pass more specific info back
2745  *					    to target
2746  *
2747  * Description:	t1394_start_isoch_dma() is used to start DMA for the isoch
2748  *		stream associated with t1394_idma_hdl.
2749  */
2750 /* ARGSUSED */
2751 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)2752 t1394_start_isoch_dma(t1394_handle_t t1394_hdl,
2753     t1394_isoch_dma_handle_t t1394_idma_hdl,
2754     id1394_isoch_dma_ctrlinfo_t *idma_ctrlinfo, uint_t flags,
2755     int *result)
2756 {
2757 	s1394_hal_t	*hal;
2758 	int		ret;
2759 
2760 	ASSERT(t1394_hdl != NULL);
2761 	ASSERT(t1394_idma_hdl != NULL);
2762 	ASSERT(idma_ctrlinfo != NULL);
2763 
2764 	/* Find the HAL this target resides on */
2765 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2766 
2767 	ret = HAL_CALL(hal).start_isoch_dma(hal->halinfo.hal_private,
2768 	    (void *)t1394_idma_hdl, idma_ctrlinfo, flags, result);
2769 
2770 	return (ret);
2771 }
2772 
2773 /*
2774  * Function:    t1394_stop_isoch_dma()
2775  * Input(s):    t1394_hdl		The target "handle" returned by
2776  *					    t1394_attach()
2777  *		t1394_idma_hdl		The IDMA "handle" returned by
2778  *					    t1394_alloc_isoch_dma()
2779  *		flags			The flags parameter is unused (for now)
2780  *
2781  * Output(s):	None
2782  *
2783  * Description:	t1394_stop_isoch_dma() is used to stop DMA for the isoch
2784  *		stream associated with t1394_idma_hdl.
2785  */
2786 /* ARGSUSED */
2787 void
t1394_stop_isoch_dma(t1394_handle_t t1394_hdl,t1394_isoch_dma_handle_t t1394_idma_hdl,uint_t flags)2788 t1394_stop_isoch_dma(t1394_handle_t t1394_hdl,
2789     t1394_isoch_dma_handle_t t1394_idma_hdl, uint_t flags)
2790 {
2791 	s1394_hal_t	*hal;
2792 	int		result;
2793 
2794 	ASSERT(t1394_hdl != NULL);
2795 	ASSERT(t1394_idma_hdl != NULL);
2796 
2797 	/* Find the HAL this target resides on */
2798 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2799 
2800 	HAL_CALL(hal).stop_isoch_dma(hal->halinfo.hal_private,
2801 	    (void *)t1394_idma_hdl, &result);
2802 }
2803 
2804 /*
2805  * Function:    t1394_update_isoch_dma()
2806  * Input(s):    t1394_hdl		The target "handle" returned by
2807  *					    t1394_attach()
2808  *		t1394_idma_hdl		The IDMA "handle" returned by
2809  *					    t1394_alloc_isoch_dma()
2810  *		idma_updateinfo		This structure contains ixl command args
2811  *					    used when updating args in an
2812  *					    existing list of ixl commands with
2813  *					    args in a new list of ixl commands.
2814  *		flags			The flags parameter is unused (for now)
2815  *
2816  * Output(s):	result			Used to pass more specific info back
2817  *					    to target
2818  *
2819  * Description:	t1394_update_isoch_dma() is used to alter an IXL program that
2820  *		has already been built (compiled) by t1394_alloc_isoch_dma().
2821  */
2822 /* ARGSUSED */
2823 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)2824 t1394_update_isoch_dma(t1394_handle_t t1394_hdl,
2825     t1394_isoch_dma_handle_t t1394_idma_hdl,
2826     id1394_isoch_dma_updateinfo_t *idma_updateinfo, uint_t flags,
2827     int *result)
2828 {
2829 	s1394_hal_t	*hal;
2830 	int		ret;
2831 
2832 	ASSERT(t1394_hdl != NULL);
2833 	ASSERT(t1394_idma_hdl != NULL);
2834 	ASSERT(idma_updateinfo != NULL);
2835 
2836 	/* Find the HAL this target resides on */
2837 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2838 
2839 	ret = HAL_CALL(hal).update_isoch_dma(hal->halinfo.hal_private,
2840 	    (void *)t1394_idma_hdl, idma_updateinfo, flags, result);
2841 
2842 	return (ret);
2843 }
2844 
2845 /*
2846  * Function:    t1394_initiate_bus_reset()
2847  * Input(s):    t1394_hdl		The target "handle" returned by
2848  *					    t1394_attach()
2849  *		flags			The flags parameter is unused (for now)
2850  *
2851  * Output(s):	None
2852  *
2853  * Description:	t1394_initiate_bus_reset() determines whether the local
2854  *		device has a P1394A PHY and will support the arbitrated
2855  *		short bus reset. If not, it will initiate a normal bus reset.
2856  */
2857 /* ARGSUSED */
2858 void
t1394_initiate_bus_reset(t1394_handle_t t1394_hdl,uint_t flags)2859 t1394_initiate_bus_reset(t1394_handle_t t1394_hdl, uint_t flags)
2860 {
2861 	s1394_hal_t	*hal;
2862 
2863 	ASSERT(t1394_hdl != NULL);
2864 
2865 	/* Find the HAL this target resides on */
2866 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2867 
2868 	/* Reset the bus */
2869 	if (hal->halinfo.phy == H1394_PHY_1394A) {
2870 		(void) HAL_CALL(hal).short_bus_reset(hal->halinfo.hal_private);
2871 	} else {
2872 		(void) HAL_CALL(hal).bus_reset(hal->halinfo.hal_private);
2873 	}
2874 }
2875 
2876 /*
2877  * Function:    t1394_get_topology_map()
2878  * Input(s):    t1394_hdl		The target "handle" returned by
2879  *					    t1394_attach()
2880  *		bus_generation		The current generation
2881  *		tm_length		The size of the tm_buffer given
2882  *		flags			The flags parameter is unused (for now)
2883  *
2884  * Output(s):	tm_buffer		Filled in by the 1394 Software Framework
2885  *					    with the contents of the local
2886  *					    TOPOLOGY_MAP
2887  *
2888  * Description:	t1394_get_topology_map() returns the 1394 TOPLOGY_MAP.  See
2889  *		IEEE 1394-1995 Section 8.2.3.4.1 for format information.  This
2890  *		call can fail if there is a generation mismatch or the
2891  *		tm_buffer is too small to hold the TOPOLOGY_MAP.
2892  */
2893 /* ARGSUSED */
2894 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)2895 t1394_get_topology_map(t1394_handle_t t1394_hdl, uint_t bus_generation,
2896     size_t tm_length, uint_t flags, uint32_t *tm_buffer)
2897 {
2898 	s1394_hal_t	*hal;
2899 	uint32_t	*tm_ptr;
2900 	uint_t		length;
2901 
2902 	ASSERT(t1394_hdl != NULL);
2903 
2904 	/* Find the HAL this target resides on */
2905 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
2906 
2907 	/* Lock the topology tree */
2908 	mutex_enter(&hal->topology_tree_mutex);
2909 
2910 	/* Check the bus_generation for the Topology Map */
2911 	if (bus_generation != hal->generation_count) {
2912 		/* Unlock the topology tree */
2913 		mutex_exit(&hal->topology_tree_mutex);
2914 		return (DDI_FAILURE);
2915 	}
2916 
2917 	tm_ptr	= (uint32_t *)hal->CSR_topology_map;
2918 	length	= tm_ptr[0] >> 16;
2919 	length  = length * 4;	/* Bytes instead of quadlets   */
2920 	length  = length + 4;   /* don't forget the first quad */
2921 
2922 	/* Check that the buffer is big enough */
2923 	if (length > (uint_t)tm_length) {
2924 		/* Unlock the topology tree */
2925 		mutex_exit(&hal->topology_tree_mutex);
2926 		return (DDI_FAILURE);
2927 	}
2928 
2929 	/* Do the copy */
2930 	bcopy(tm_ptr, tm_buffer, length);
2931 
2932 	/* Unlock the topology tree */
2933 	mutex_exit(&hal->topology_tree_mutex);
2934 	return (DDI_SUCCESS);
2935 }
2936 
2937 /*
2938  * Function:    t1394_CRC16()
2939  * Input(s):    d			The data to compute the CRC-16 for
2940  *		crc_length		The length into the data to compute for
2941  *		flags			The flags parameter is unused (for now)
2942  *
2943  * Output(s):	CRC			The CRC-16 computed for the length
2944  *					    of data specified
2945  *
2946  * Description:	t1394_CRC16() implements ISO/IEC 13213:1994, ANSI/IEEE Std
2947  *		1212, 1994 - 8.1.5.
2948  */
2949 /* ARGSUSED */
2950 uint_t
t1394_CRC16(uint32_t * d,size_t crc_length,uint_t flags)2951 t1394_CRC16(uint32_t *d, size_t crc_length, uint_t flags)
2952 {
2953 	/* Implements ISO/IEC 13213:1994,	*/
2954 	/* ANSI/IEEE Std 1212, 1994 - 8.1.5	*/
2955 	uint_t	ret;
2956 
2957 	ret = s1394_CRC16((uint_t *)d, (uint_t)crc_length);
2958 
2959 	return (ret);
2960 }
2961 
2962 /*
2963  * Function:    t1394_add_cfgrom_entry()
2964  * Input(s):    t1394_hdl		The target "handle" returned by
2965  *					    t1394_attach()
2966  *		cfgrom_entryinfo	This structure holds the cfgrom key,
2967  *					    buffer, and size
2968  *		flags			The flags parameter is unused (for now)
2969  *
2970  * Output(s):	t1394_cfgrom_hdl	The ConfigROM "handle" used in
2971  *					    t1394_rem_cfgrom_entry()
2972  *		result			Used to pass more specific info back
2973  *					    to target
2974  *
2975  * Description:	t1394_add_cfgrom_entry() adds an entry to the local Config ROM,
2976  *		updating the directory entries as necessary.  This call could
2977  *		fail because there is no room for the new entry in Config ROM
2978  *		(T1394_ECFGROM_FULL), the key is invalid (T1394_EINVALID_PARAM),
2979  *		or it was called in interrupt context (T1394_EINVALID_CONTEXT).
2980  */
2981 /* ARGSUSED */
2982 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)2983 t1394_add_cfgrom_entry(t1394_handle_t t1394_hdl,
2984     t1394_cfgrom_entryinfo_t *cfgrom_entryinfo, uint_t flags,
2985     t1394_cfgrom_handle_t *t1394_cfgrom_hdl, int *result)
2986 {
2987 	s1394_hal_t	*hal;
2988 	s1394_target_t	*target;
2989 	int		ret;
2990 	uint_t		key;
2991 	uint_t		size;
2992 	uint32_t	*buffer;
2993 
2994 	ASSERT(t1394_hdl != NULL);
2995 
2996 	target = (s1394_target_t *)t1394_hdl;
2997 
2998 	key = cfgrom_entryinfo->ce_key;
2999 	buffer = cfgrom_entryinfo->ce_buffer;
3000 	size = (uint_t)cfgrom_entryinfo->ce_size;
3001 
3002 	/* Check for a valid size */
3003 	if (size == 0) {
3004 		*result = T1394_EINVALID_PARAM;
3005 		return (DDI_FAILURE);
3006 	}
3007 
3008 	/* Check for a valid key type */
3009 	if (((key << IEEE1212_KEY_VALUE_SHIFT) & IEEE1212_KEY_TYPE_MASK) == 0) {
3010 		*result = T1394_EINVALID_PARAM;
3011 		return (DDI_FAILURE);
3012 	}
3013 
3014 	/* Find the HAL this target resides on */
3015 	hal = target->on_hal;
3016 
3017 	/* Is this on the interrupt stack? */
3018 	if (servicing_interrupt()) {
3019 		*result = T1394_EINVALID_CONTEXT;
3020 		return (DDI_FAILURE);
3021 	}
3022 
3023 	/* Lock the Config ROM buffer */
3024 	mutex_enter(&hal->local_config_rom_mutex);
3025 
3026 	ret = s1394_add_config_rom_entry(hal, key, buffer, size,
3027 	    (void **)t1394_cfgrom_hdl, result);
3028 	if (ret != DDI_SUCCESS) {
3029 		if (*result == CMD1394_ERSRC_CONFLICT)
3030 			*result = T1394_ECFGROM_FULL;
3031 		mutex_exit(&hal->local_config_rom_mutex);
3032 
3033 		return (ret);
3034 	}
3035 
3036 	/* Setup the timeout function */
3037 	if (hal->config_rom_timer_set == B_FALSE) {
3038 		hal->config_rom_timer_set = B_TRUE;
3039 		mutex_exit(&hal->local_config_rom_mutex);
3040 		hal->config_rom_timer =
3041 		    timeout(s1394_update_config_rom_callback, hal,
3042 			drv_usectohz(CONFIG_ROM_UPDATE_DELAY * 1000));
3043 	} else {
3044 		mutex_exit(&hal->local_config_rom_mutex);
3045 	}
3046 
3047 	return (ret);
3048 }
3049 
3050 /*
3051  * Function:    t1394_rem_cfgrom_entry()
3052  * Input(s):    t1394_hdl		The target "handle" returned by
3053  *					    t1394_attach()
3054  *		flags			The flags parameter is unused (for now)
3055  *		t1394_cfgrom_hdl	The ConfigROM "handle" returned by
3056  *					    t1394_add_cfgrom_entry()
3057  *
3058  * Output(s):	result			Used to pass more specific info back
3059  *					    to target
3060  *
3061  * Description:	t1394_rem_cfgrom_entry() is used to remove a previously added
3062  *		Config ROM entry (indicated by t1394_cfgrom_hdl).
3063  */
3064 /* ARGSUSED */
3065 int
t1394_rem_cfgrom_entry(t1394_handle_t t1394_hdl,uint_t flags,t1394_cfgrom_handle_t * t1394_cfgrom_hdl,int * result)3066 t1394_rem_cfgrom_entry(t1394_handle_t t1394_hdl, uint_t flags,
3067     t1394_cfgrom_handle_t *t1394_cfgrom_hdl, int *result)
3068 {
3069 	s1394_hal_t	*hal;
3070 	s1394_target_t	*target;
3071 	int		ret;
3072 
3073 	ASSERT(t1394_hdl != NULL);
3074 
3075 	target = (s1394_target_t *)t1394_hdl;
3076 
3077 	/* Find the HAL this target resides on */
3078 	hal = target->on_hal;
3079 
3080 	/* Is this on the interrupt stack? */
3081 	if (servicing_interrupt()) {
3082 		*result = T1394_EINVALID_CONTEXT;
3083 		return (DDI_FAILURE);
3084 	}
3085 
3086 	/* Lock the Config ROM buffer */
3087 	mutex_enter(&hal->local_config_rom_mutex);
3088 
3089 	ret = s1394_remove_config_rom_entry(hal, (void **)t1394_cfgrom_hdl,
3090 	    result);
3091 	if (ret != DDI_SUCCESS) {
3092 		mutex_exit(&hal->local_config_rom_mutex);
3093 		return (ret);
3094 	}
3095 
3096 	/* Setup the timeout function */
3097 	if (hal->config_rom_timer_set == B_FALSE) {
3098 		hal->config_rom_timer_set = B_TRUE;
3099 		mutex_exit(&hal->local_config_rom_mutex);
3100 		hal->config_rom_timer =
3101 		    timeout(s1394_update_config_rom_callback, hal,
3102 			drv_usectohz(CONFIG_ROM_UPDATE_DELAY * 1000));
3103 	} else {
3104 		mutex_exit(&hal->local_config_rom_mutex);
3105 	}
3106 
3107 	return (ret);
3108 }
3109 
3110 /*
3111  * Function:    t1394_get_targetinfo()
3112  * Input(s):    t1394_hdl		The target "handle" returned by
3113  *					    t1394_attach()
3114  *		bus_generation		The current generation
3115  *		flags			The flags parameter is unused (for now)
3116  *
3117  * Output(s):	targetinfo		Structure containing max_payload,
3118  *					    max_speed, and target node ID.
3119  *
3120  * Description:	t1394_get_targetinfo() is used to retrieve information specific
3121  *		to a target device.  It will fail if the generation given
3122  *		does not match the current generation.
3123  */
3124 /* ARGSUSED */
3125 int
t1394_get_targetinfo(t1394_handle_t t1394_hdl,uint_t bus_generation,uint_t flags,t1394_targetinfo_t * targetinfo)3126 t1394_get_targetinfo(t1394_handle_t t1394_hdl, uint_t bus_generation,
3127     uint_t flags, t1394_targetinfo_t *targetinfo)
3128 {
3129 	s1394_hal_t	*hal;
3130 	s1394_target_t	*target;
3131 	uint_t		dev;
3132 	uint_t		curr;
3133 	uint_t		from_node;
3134 	uint_t		to_node;
3135 
3136 	ASSERT(t1394_hdl != NULL);
3137 
3138 	/* Find the HAL this target resides on */
3139 	hal = ((s1394_target_t *)t1394_hdl)->on_hal;
3140 
3141 	target = (s1394_target_t *)t1394_hdl;
3142 
3143 	/* Lock the topology tree */
3144 	mutex_enter(&hal->topology_tree_mutex);
3145 
3146 	/* Check the bus_generation */
3147 	if (bus_generation != hal->generation_count) {
3148 		/* Unlock the topology tree */
3149 		mutex_exit(&hal->topology_tree_mutex);
3150 		return (DDI_FAILURE);
3151 	}
3152 
3153 	rw_enter(&hal->target_list_rwlock, RW_READER);
3154 	/*
3155 	 * If there is no node, report T1394_INVALID_NODEID for target_nodeID;
3156 	 * current_max_speed and current_max_payload are undefined for this
3157 	 * case.
3158 	 */
3159 	if (((target->target_state & S1394_TARG_GONE) != 0) ||
3160 	    (target->on_node == NULL)) {
3161 		targetinfo->target_nodeID = T1394_INVALID_NODEID;
3162 	} else {
3163 		targetinfo->target_nodeID =
3164 		    (target->on_hal->node_id & IEEE1394_BUS_NUM_MASK) |
3165 		    target->on_node->node_num;
3166 
3167 		from_node = (target->on_hal->node_id) & IEEE1394_NODE_NUM_MASK;
3168 		to_node = target->on_node->node_num;
3169 
3170 		targetinfo->current_max_speed = (uint_t)s1394_speed_map_get(
3171 		    hal, from_node, to_node);
3172 
3173 		/* Get current_max_payload */
3174 		s1394_get_maxpayload(target, &dev, &curr);
3175 		targetinfo->current_max_payload	= curr;
3176 	}
3177 
3178 	rw_exit(&hal->target_list_rwlock);
3179 	/* Unlock the topology tree */
3180 	mutex_exit(&hal->topology_tree_mutex);
3181 	return (DDI_SUCCESS);
3182 }
3183