/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma	ident	"%Z%%M%	%I%	%E% SMI"

/*
 * s1394_isoch.c
 *    1394 Services Layer Isochronous Communication Routines
 *    This file contains routines for managing isochronous bandwidth
 *    and channel needs for registered targets (through the target
 *    isoch interfaces).
 */

#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/types.h>
#include <sys/tnf_probe.h>

#include <sys/1394/t1394.h>
#include <sys/1394/s1394.h>
#include <sys/1394/h1394.h>
#include <sys/1394/ieee1394.h>

/*
 * s1394_isoch_rsrc_realloc()
 *    is called during bus reset processing to reallocate any isochronous
 *    resources that were previously allocated.
 */
void
s1394_isoch_rsrc_realloc(s1394_hal_t *hal)
{
	s1394_isoch_cec_t *cec_curr;
	uint32_t	  chnl_mask;
	uint32_t	  old_chnl_mask;
	uint_t		  bw_alloc_units;
	uint_t		  generation;
	uint_t		  chnl_num;
	int		  err;
	int		  ret;

	TNF_PROBE_0_DEBUG(s1394_isoch_rsrc_realloc_enter,
	    S1394_TNF_SL_ISOCH_STACK, "");

	/*
	 * Get the current generation number - don't need the
	 * topology tree mutex here because it is read-only, and
	 * there is a race condition with or without it.
	 */
	generation = hal->generation_count;

	/* Lock the Isoch CEC list */
	mutex_enter(&hal->isoch_cec_list_mutex);

	cec_curr = hal->isoch_cec_list_head;
	while (cec_curr != NULL) {
		/* Lock the Isoch CEC member list */
		mutex_enter(&cec_curr->isoch_cec_mutex);

		/* Are we supposed to reallocate resources? */
		if (!(cec_curr->cec_options & T1394_NO_IRM_ALLOC) &&
		    (cec_curr->realloc_valid == B_TRUE) &&
		    (cec_curr->realloc_failed == B_FALSE)) {

			/* Reallocate some bandwidth */
			bw_alloc_units = s1394_compute_bw_alloc_units(hal,
			    cec_curr->bandwidth, cec_curr->realloc_speed);

			/* Check that the generation has not changed */
			if (generation != hal->generation_count) {
				/* Try the next Isoch CEC */
				goto next_isoch_cec;
			}

			/* Unlock the Isoch CEC member list */
			mutex_exit(&cec_curr->isoch_cec_mutex);
			/*
			 * We can unlock the Isoch CEC list here
			 * because we know this Isoch CEC can not
			 * go away (we are trying to realloc its
			 * resources so it can't be in a state that
			 * will allow a free).
			 */
			mutex_exit(&hal->isoch_cec_list_mutex);

			/* Try to reallocate bandwidth */
			ret = s1394_bandwidth_alloc(hal, bw_alloc_units,
			    generation, &err);

			/* Lock the Isoch CEC list */
			mutex_enter(&hal->isoch_cec_list_mutex);
			/* Lock the Isoch CEC member list */
			mutex_enter(&cec_curr->isoch_cec_mutex);

			/* If we failed because we couldn't get bandwidth */
			if (ret == DDI_FAILURE) {
				cec_curr->realloc_failed = B_TRUE;
				cec_curr->realloc_fail_reason =
				    T1394_RSRC_BANDWIDTH;
			}
		}

		/* Are we supposed to reallocate resources? */
		if (!(cec_curr->cec_options & T1394_NO_IRM_ALLOC) &&
		    (cec_curr->realloc_valid == B_TRUE) &&
		    (cec_curr->realloc_failed == B_FALSE)) {

			/* Reallocate the channel */
			chnl_num  = cec_curr->realloc_chnl_num;
			chnl_mask = (1 << ((63 - chnl_num) % 32));

			/* Unlock the Isoch CEC member list */
			mutex_exit(&cec_curr->isoch_cec_mutex);
			/*
			 * We can unlock the Isoch CEC list here
			 * because we know this Isoch CEC can not
			 * go away (we are trying to realloc its
			 * resources so it can't be in a state that
			 * will allow a free).
			 */
			mutex_exit(&hal->isoch_cec_list_mutex);

			if (chnl_num < 32) {
				ret = s1394_channel_alloc(hal, chnl_mask,
				    generation, S1394_CHANNEL_ALLOC_HI,
				    &old_chnl_mask, &err);
			} else {
				ret = s1394_channel_alloc(hal, chnl_mask,
				    generation, S1394_CHANNEL_ALLOC_LO,
				    &old_chnl_mask, &err);
			}

			/* Lock the Isoch CEC list */
			mutex_enter(&hal->isoch_cec_list_mutex);
			/* Lock the Isoch CEC member list */
			mutex_enter(&cec_curr->isoch_cec_mutex);

			if (ret == DDI_FAILURE) {
				if (err != CMD1394_EBUSRESET) {
					/*
					 * If we successfully reallocate
					 * bandwidth, and then fail getting
					 * the channel, we need to free up
					 * the bandwidth
					 */

					/* Try to free up the bandwidth */
					ret = s1394_bandwidth_free(hal,
					    bw_alloc_units, generation, &err);
					if ((ret == DDI_FAILURE) &&
					    (err != CMD1394_EBUSRESET)) {
						TNF_PROBE_1(
						s1394_isoch_rsrc_realloc_error,
						    S1394_TNF_SL_ISOCH_ERROR,
						    "", tnf_string, msg,
						    "Unable to free bandwidth");
					}
					/* Try the next Isoch CEC */
					goto next_isoch_cec;
				}
				cec_curr->realloc_failed = B_TRUE;
				cec_curr->realloc_fail_reason =
				    T1394_RSRC_CHANNEL;
			}
		}
next_isoch_cec:
		/* Unlock the Isoch CEC member list */
		mutex_exit(&cec_curr->isoch_cec_mutex);
		cec_curr = cec_curr->cec_next;
	}

	/* Unlock the Isoch CEC list */
	mutex_exit(&hal->isoch_cec_list_mutex);
	TNF_PROBE_0_DEBUG(s1394_isoch_rsrc_realloc_exit,
	    S1394_TNF_SL_ISOCH_STACK, "");
}

/*
 * s1394_isoch_rsrc_realloc_notify()
 *    is called during bus reset processing to notify all targets for
 *    which isochronous resources were not able to be reallocated.
 */
void
s1394_isoch_rsrc_realloc_notify(s1394_hal_t *hal)
{
	s1394_isoch_cec_t	 *cec_curr;
	s1394_isoch_cec_member_t *member_curr;
	t1394_isoch_rsrc_error_t fail_arg;
	opaque_t		 evts_arg;
	s1394_isoch_cec_type_t	 type;
	void (*rsrc_fail_callback)(t1394_isoch_cec_handle_t, opaque_t,
				t1394_isoch_rsrc_error_t);

	TNF_PROBE_0_DEBUG(s1394_isoch_rsrc_realloc_notify_enter,
	    S1394_TNF_SL_ISOCH_STACK, "");

	/* Lock the Isoch CEC list */
	mutex_enter(&hal->isoch_cec_list_mutex);

	/* Notify all targets that failed realloc */
	cec_curr = hal->isoch_cec_list_head;
	while (cec_curr != NULL) {
		/* Lock the Isoch CEC member list */
		mutex_enter(&cec_curr->isoch_cec_mutex);

		/* Do we notify of realloc failure? */
		if (!(cec_curr->cec_options & T1394_NO_IRM_ALLOC) &&
		    (cec_curr->realloc_valid == B_TRUE) &&
		    (cec_curr->realloc_failed == B_TRUE)) {

			/* Reason for realloc failure */
			fail_arg = cec_curr->realloc_fail_reason;

			/* Now we are going into the callbacks */
			cec_curr->in_fail_callbacks = B_TRUE;

			type = cec_curr->cec_type;

			/* Unlock the Isoch CEC member list */
			mutex_exit(&cec_curr->isoch_cec_mutex);
			/*
			 * We can unlock the Isoch CEC list here
			 * because we have the in_fail_callbacks
			 * field set to B_TRUE.  And free will fail
			 * if we are in fail callbacks.
			 */
			mutex_exit(&hal->isoch_cec_list_mutex);

			/* Call all of the rsrc_fail_target() callbacks */
			/* Start at the head (talker first) and */
			/* go toward the tail (listeners last) */
			member_curr = cec_curr->cec_member_list_head;
			while (member_curr != NULL) {
				rsrc_fail_callback = member_curr->
				    isoch_cec_evts.rsrc_fail_target;
				evts_arg = member_curr->isoch_cec_evts_arg;
				if (rsrc_fail_callback != NULL) {

					if (type == S1394_PEER_TO_PEER) {
						rsrc_fail_callback(
						    (t1394_isoch_cec_handle_t)
						    cec_curr, evts_arg,
						    fail_arg);
					} else {
						rsrc_fail_callback(
						    (t1394_isoch_cec_handle_t)
						    cec_curr, evts_arg,
						    fail_arg);
					}
				}
				member_curr = member_curr->cec_mem_next;
			}

			/* Lock the Isoch CEC list */
			mutex_enter(&hal->isoch_cec_list_mutex);
			/* Lock the Isoch CEC member list */
			mutex_enter(&cec_curr->isoch_cec_mutex);

			/* We are finished with the callbacks */
			cec_curr->in_fail_callbacks = B_FALSE;
			if (cec_curr->cec_want_wakeup == B_TRUE) {
				cec_curr->cec_want_wakeup = B_FALSE;
				cv_broadcast(&cec_curr->in_callbacks_cv);
			}

			/* Set flags back to original state */
			cec_curr->realloc_valid	 = B_FALSE;
			cec_curr->realloc_failed = B_FALSE;
		}
		/* Unlock the Isoch CEC member list */
		mutex_exit(&cec_curr->isoch_cec_mutex);
		cec_curr = cec_curr->cec_next;
	}

	/* Unlock the Isoch CEC list */
	mutex_exit(&hal->isoch_cec_list_mutex);
	TNF_PROBE_0_DEBUG(s1394_isoch_rsrc_realloc_notify_exit,
	    S1394_TNF_SL_ISOCH_STACK, "");
}

/*
 * s1394_channel_alloc()
 *    is used to allocate an isochronous channel.  A channel mask and
 *    generation are passed.  A request is sent to whichever node is the
 *    IRM for the appropriate channels.  If it fails because of a bus
 *    reset it can be retried.  If it fails for another reason the
 *    channel(s) may not be availble or there may be no IRM.
 */
int
s1394_channel_alloc(s1394_hal_t *hal, uint32_t channel_mask, uint_t generation,
    uint_t flags, uint32_t *old_channels, int *result)
{
	cmd1394_cmd_t	*cmd;
	uint64_t	IRM_ID_addr;
	uint32_t	compare;
	uint32_t	swap;
	uint32_t	old_value;
	uint_t		hal_node_num;
	uint_t		IRM_node;
	uint_t		offset;
	int		ret;
	int		i;
	int		num_retries = S1394_ISOCH_ALLOC_RETRIES;

	TNF_PROBE_0_DEBUG(s1394_channel_alloc_enter,
	    S1394_TNF_SL_ISOCH_STACK, "");

	/* Lock the topology tree */
	mutex_enter(&hal->topology_tree_mutex);

	hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
	IRM_node = hal->IRM_node;

	/* Unlock the topology tree */
	mutex_exit(&hal->topology_tree_mutex);

	/* Make sure there is a valid IRM on the bus */
	if (IRM_node == -1) {
		*result = CMD1394_ERETRIES_EXCEEDED;
		TNF_PROBE_1(s1394_channel_alloc_error,
		    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
		    "No IRM on the 1394 bus");
		TNF_PROBE_0_DEBUG(s1394_channel_alloc_exit,
		    S1394_TNF_SL_ISOCH_STACK, "");
		return (DDI_FAILURE);
	}

	if (flags & S1394_CHANNEL_ALLOC_HI) {
		offset =
		    (IEEE1394_SCSR_CHANS_AVAIL_HI & IEEE1394_CSR_OFFSET_MASK);
	} else {
		offset =
		    (IEEE1394_SCSR_CHANS_AVAIL_LO & IEEE1394_CSR_OFFSET_MASK);
	}

	/* Send compare-swap to CHANNELS_AVAILABLE */
	/* register on the Isoch Rsrc Mgr */
	if (IRM_node == hal_node_num) {
		/* Local */
		i = num_retries;
		do {
			(void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
			    offset, &old_value);

			/* Check that the generation has not changed */
			if (generation != hal->generation_count) {
				*result = CMD1394_EBUSRESET;
				TNF_PROBE_0_DEBUG(s1394_channel_alloc_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}

			compare = old_value;
			swap	= old_value & (~channel_mask);

			ret = HAL_CALL(hal).csr_cswap32(
			    hal->halinfo.hal_private, generation,
			    offset, compare, swap, &old_value);
			if (ret != DDI_SUCCESS) {
				*result = CMD1394_EBUSRESET;
				TNF_PROBE_1(s1394_channel_alloc_error,
				    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
				    msg, "Error in cswap32");
				TNF_PROBE_0_DEBUG(s1394_channel_alloc_exit,
				    "stacktrace 1394 s1394", "");
				return (DDI_FAILURE);
			}

			if ((~old_value & channel_mask) != 0) {
				*result = CMD1394_ERETRIES_EXCEEDED;
				TNF_PROBE_1(s1394_channel_alloc_error,
				    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
				    msg, "Channels already taken");
				TNF_PROBE_0_DEBUG(s1394_channel_alloc_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}

			if (old_value == compare) {
				*result = CMD1394_CMDSUCCESS;
				*old_channels = old_value;

				TNF_PROBE_0_DEBUG(s1394_channel_alloc_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_SUCCESS);
			}
		} while (i--);

		*result = CMD1394_ERETRIES_EXCEEDED;
		TNF_PROBE_1(s1394_channel_alloc_error,
		    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
		    "Retries exceeded");
		TNF_PROBE_0_DEBUG(s1394_channel_alloc_exit,
		    S1394_TNF_SL_ISOCH_STACK, "");
		return (DDI_FAILURE);

	} else {
		/* Remote */
		if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
			*result = CMD1394_EUNKNOWN_ERROR;
			TNF_PROBE_1(s1394_channel_alloc_error,
			    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
			    "Unable to allocate command");
			TNF_PROBE_0_DEBUG(s1394_channel_alloc_exit,
			    S1394_TNF_SL_ISOCH_STACK, "");
			return (DDI_FAILURE);
		}

		cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET |
		    CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING);
		cmd->cmd_type = CMD1394_ASYNCH_LOCK_32;

		if (flags & S1394_CHANNEL_ALLOC_HI) {
			IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
			    IEEE1394_SCSR_CHANS_AVAIL_HI) |
			    (((uint64_t)IRM_node) <<
			    IEEE1394_ADDR_PHY_ID_SHIFT);
		} else {
			IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
			    IEEE1394_SCSR_CHANS_AVAIL_LO) |
			    (((uint64_t)IRM_node) <<
			    IEEE1394_ADDR_PHY_ID_SHIFT);
		}

		cmd->cmd_addr		   = IRM_ID_addr;
		cmd->bus_generation	   = generation;
		cmd->cmd_u.l32.data_value  = T1394_DATA32(~channel_mask);
		cmd->cmd_u.l32.num_retries = num_retries;
		cmd->cmd_u.l32.lock_type   = CMD1394_LOCK_BIT_AND;

		ret = s1394_split_lock_req(hal, NULL, cmd);

		if (ret == DDI_SUCCESS) {
			if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
				*old_channels = T1394_DATA32(
				    cmd->cmd_u.l32.old_value);

				if ((~(*old_channels) & channel_mask) != 0) {
					*result = CMD1394_ERETRIES_EXCEEDED;
					TNF_PROBE_1(s1394_channel_alloc_error,
					    S1394_TNF_SL_ISOCH_ERROR, "",
					    tnf_string, msg,
					    "Channels already taken");
					TNF_PROBE_0_DEBUG(
					    s1394_channel_alloc_exit,
					    S1394_TNF_SL_ISOCH_STACK, "");
					ret = DDI_FAILURE;
				} else {
					*result = cmd->cmd_result;
				}

				/* Need to free the command */
				(void) s1394_free_cmd(hal, &cmd);

				TNF_PROBE_0_DEBUG(s1394_channel_alloc_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (ret);

			} else {
				*result = cmd->cmd_result;
				/* Need to free the command */
				(void) s1394_free_cmd(hal, &cmd);

				TNF_PROBE_1(s1394_channel_alloc_error,
				    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
				    msg, "Error allocating isoch channel");
				TNF_PROBE_0_DEBUG(s1394_channel_alloc_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}
		} else {
			*result = cmd->cmd_result;

			/* Need to free the command */
			(void) s1394_free_cmd(hal, &cmd);

			TNF_PROBE_1(s1394_channel_alloc_error,
			    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
			    "Error allocating isoch channel");
			TNF_PROBE_0_DEBUG(s1394_channel_alloc_exit,
			    S1394_TNF_SL_ISOCH_STACK, "");
			return (DDI_FAILURE);
		}
	}
}

/*
 * s1394_channel_free()
 *    is used to free up an isochronous channel.  A channel mask and
 *    generation are passed.  A request is sent to whichever node is the
 *    IRM for the appropriate channels.  If it fails because of a bus
 *    reset it can be retried.  If it fails for another reason the
 *    channel(s) may already be free or there may be no IRM.
 */
int
s1394_channel_free(s1394_hal_t *hal, uint32_t channel_mask, uint_t generation,
    uint_t flags, uint32_t *old_channels, int *result)
{
	cmd1394_cmd_t	*cmd;
	uint64_t	IRM_ID_addr;
	uint32_t	compare;
	uint32_t	swap;
	uint32_t	old_value;
	uint_t		hal_node_num;
	uint_t		IRM_node;
	uint_t		offset;
	int		ret;
	int		i;
	int		num_retries = S1394_ISOCH_ALLOC_RETRIES;

	TNF_PROBE_0_DEBUG(s1394_channel_free_enter,
	    S1394_TNF_SL_ISOCH_STACK, "");

	/* Lock the topology tree */
	mutex_enter(&hal->topology_tree_mutex);

	hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
	IRM_node = hal->IRM_node;

	/* Unlock the topology tree */
	mutex_exit(&hal->topology_tree_mutex);

	/* Make sure there is a valid IRM on the bus */
	if (IRM_node == -1) {
		*result = CMD1394_ERETRIES_EXCEEDED;
		TNF_PROBE_1(s1394_channel_free_error,
		    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
		    "No IRM on the 1394 bus");
		TNF_PROBE_0_DEBUG(s1394_channel_free_exit,
		    S1394_TNF_SL_ISOCH_STACK, "");
		return (DDI_FAILURE);
	}

	if (flags & S1394_CHANNEL_ALLOC_HI) {
		offset =
		    (IEEE1394_SCSR_CHANS_AVAIL_HI & IEEE1394_CSR_OFFSET_MASK);
	} else {
		offset =
		    (IEEE1394_SCSR_CHANS_AVAIL_LO & IEEE1394_CSR_OFFSET_MASK);
	}

	/* Send compare-swap to CHANNELS_AVAILABLE */
	/* register on the Isoch Rsrc Mgr */
	if (hal->IRM_node == hal_node_num) {
		/* Local */
		i = num_retries;
		do {
			(void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
			    offset, &old_value);

			/* Check that the generation has not changed */
			if (generation != hal->generation_count) {
				*result = CMD1394_EBUSRESET;
				TNF_PROBE_0_DEBUG(s1394_channel_free_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}

			compare = old_value;
			swap	= old_value | channel_mask;

			ret = HAL_CALL(hal).csr_cswap32(
			    hal->halinfo.hal_private, hal->generation_count,
			    offset, compare, swap, &old_value);
			if (ret != DDI_SUCCESS) {
				*result = CMD1394_EBUSRESET;
				TNF_PROBE_1(s1394_channel_free_error,
				    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
				    msg, "Error in cswap32");
				TNF_PROBE_0_DEBUG(s1394_channel_free_exit,
				    "stacktrace 1394 s1394", "");
				return (DDI_FAILURE);
			}

			if (old_value == compare) {
				*result = CMD1394_CMDSUCCESS;
				*old_channels = old_value;
				TNF_PROBE_0_DEBUG(s1394_channel_free_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_SUCCESS);
			}
		} while (i--);

		*result = CMD1394_ERETRIES_EXCEEDED;
		TNF_PROBE_1(s1394_channel_free_error,
		    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
		    "Retries exceeded");
		TNF_PROBE_0_DEBUG(s1394_channel_free_exit,
		    S1394_TNF_SL_ISOCH_STACK, "");
		return (DDI_FAILURE);

	} else {
		/* Remote */
		if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
			*result = CMD1394_EUNKNOWN_ERROR;
			TNF_PROBE_1(s1394_channel_free_error,
			    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
			    "Unable to allocate command");
			TNF_PROBE_0_DEBUG(s1394_channel_free_exit,
			    S1394_TNF_SL_ISOCH_STACK, "");
			return (DDI_FAILURE);
		}

		cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET |
		    CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING);
		cmd->cmd_type = CMD1394_ASYNCH_LOCK_32;

		if (flags & S1394_CHANNEL_ALLOC_HI) {
			IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
			    IEEE1394_SCSR_CHANS_AVAIL_HI) |
			    (((uint64_t)IRM_node) <<
			    IEEE1394_ADDR_PHY_ID_SHIFT);
		} else {
			IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
			    IEEE1394_SCSR_CHANS_AVAIL_LO) |
			    (((uint64_t)IRM_node) <<
			    IEEE1394_ADDR_PHY_ID_SHIFT);
		}

		cmd->cmd_addr		   = IRM_ID_addr;
		cmd->bus_generation	   = generation;
		cmd->cmd_u.l32.data_value  = T1394_DATA32(channel_mask);
		cmd->cmd_u.l32.num_retries = num_retries;
		cmd->cmd_u.l32.lock_type   = CMD1394_LOCK_BIT_OR;

		ret = s1394_split_lock_req(hal, NULL, cmd);

		if (ret == DDI_SUCCESS) {
			if (cmd->cmd_result == CMD1394_CMDSUCCESS) {

				*old_channels = T1394_DATA32(
				    cmd->cmd_u.l32.old_value);
				*result = cmd->cmd_result;

				/* Need to free the command */
				(void) s1394_free_cmd(hal, &cmd);

				TNF_PROBE_0_DEBUG(s1394_channel_free_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_SUCCESS);

			} else {
				*result = cmd->cmd_result;

				/* Need to free the command */
				(void) s1394_free_cmd(hal, &cmd);

				TNF_PROBE_1(s1394_channel_free_error,
				    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
				    msg, "Error freeing isoch channel");
				TNF_PROBE_0_DEBUG(s1394_channel_free_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}
		} else {
			*result = cmd->cmd_result;
			/* Need to free the command */
			(void) s1394_free_cmd(hal, &cmd);

			TNF_PROBE_1(s1394_channel_free_error,
			    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
			    "Error freeing isoch channel");
			TNF_PROBE_0_DEBUG(s1394_channel_free_exit,
			    S1394_TNF_SL_ISOCH_STACK, "");
			return (DDI_FAILURE);
		}
	}
}

/*
 * s1394_bandwidth_alloc()
 *    is used to allocate isochronous bandwidth.  A number of bandwidth
 *    allocation units and a generation are passed.  The request is sent
 *    to whichever node is the IRM for this amount of bandwidth.  If it
 *    fails because of a bus reset it can be retried.  If it fails for
 *    another reason the bandwidth may not be available or there may be
 *    no IRM.
 */
int
s1394_bandwidth_alloc(s1394_hal_t *hal, uint32_t bw_alloc_units,
    uint_t generation, int *result)
{
	cmd1394_cmd_t	*cmd;
	uint64_t	IRM_ID_addr;
	uint32_t	compare;
	uint32_t	swap;
	uint32_t	old_value;
	uint_t		hal_node_num;
	uint_t		IRM_node;
	int		temp_value;
	int		ret;
	int		i;
	int		num_retries = S1394_ISOCH_ALLOC_RETRIES;

	TNF_PROBE_0_DEBUG(s1394_bandwidth_alloc_enter,
	    S1394_TNF_SL_ISOCH_STACK, "");

	/* Lock the topology tree */
	mutex_enter(&hal->topology_tree_mutex);

	hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
	IRM_node = hal->IRM_node;

	/* Unlock the topology tree */
	mutex_exit(&hal->topology_tree_mutex);

	/* Make sure there is a valid IRM on the bus */
	if (IRM_node == -1) {
		*result = CMD1394_ERETRIES_EXCEEDED;
		TNF_PROBE_1(s1394_bandwidth_alloc_error,
		    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
		    "No IRM on the 1394 bus");
		TNF_PROBE_0_DEBUG(s1394_bandwidth_alloc_exit,
		    S1394_TNF_SL_ISOCH_STACK, "");
		return (DDI_FAILURE);
	}

	/* Send compare-swap to BANDWIDTH_AVAILABLE */
	/* register on the Isoch Rsrc Mgr */
	if (IRM_node == hal_node_num) {
		/* Local */
		i = num_retries;
		do {
			(void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
			    (IEEE1394_SCSR_BANDWIDTH_AVAIL &
			    IEEE1394_CSR_OFFSET_MASK), &old_value);
			/*
			 * Check that the generation has not changed -
			 * don't need the lock (read-only)
			 */
			if (generation != hal->generation_count) {
				*result = CMD1394_EBUSRESET;
				TNF_PROBE_0_DEBUG(s1394_bandwidth_alloc_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}

			temp_value = (old_value - bw_alloc_units);
			if ((old_value >= bw_alloc_units) &&
			    (temp_value >= IEEE1394_BANDWIDTH_MIN)) {
				compare = old_value;
				swap	= (uint32_t)temp_value;
			} else {
				*result = CMD1394_ERETRIES_EXCEEDED;
				TNF_PROBE_1(s1394_bandwidth_alloc_error,
				    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
				    msg, "Retries exceeded");
				TNF_PROBE_0_DEBUG(s1394_bandwidth_alloc_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}

			ret = HAL_CALL(hal).csr_cswap32(
			    hal->halinfo.hal_private, generation,
			    (IEEE1394_SCSR_BANDWIDTH_AVAIL &
			    IEEE1394_CSR_OFFSET_MASK), compare, swap,
			    &old_value);
			if (ret != DDI_SUCCESS) {
				*result = CMD1394_EBUSRESET;
				TNF_PROBE_1(s1394_bandwidth_alloc_error,
				    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
				    msg, "Error in cswap32");
				TNF_PROBE_0_DEBUG(s1394_bandwidth_alloc_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}

			if (old_value == compare) {
				*result = CMD1394_CMDSUCCESS;
				TNF_PROBE_0_DEBUG(s1394_bandwidth_alloc_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_SUCCESS);
			}
		} while (i--);

		*result = CMD1394_ERETRIES_EXCEEDED;
		TNF_PROBE_1(s1394_bandwidth_alloc_error,
		    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
		    "Too many retries");
		TNF_PROBE_0_DEBUG(s1394_bandwidth_alloc_exit,
		    S1394_TNF_SL_ISOCH_STACK, "");
		return (DDI_FAILURE);

	} else {
		/* Remote */
		if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
			*result = CMD1394_EUNKNOWN_ERROR;
			TNF_PROBE_1(s1394_bandwidth_alloc_error,
			    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
			    "Unable to allocate command");
			TNF_PROBE_0_DEBUG(s1394_bandwidth_alloc_exit,
			    S1394_TNF_SL_ISOCH_STACK, "");
			return (DDI_FAILURE);
		}

		cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET |
		    CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING);
		cmd->cmd_type = CMD1394_ASYNCH_LOCK_32;
		IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
		    IEEE1394_SCSR_BANDWIDTH_AVAIL) | (((uint64_t)IRM_node) <<
		    IEEE1394_ADDR_PHY_ID_SHIFT);
		cmd->cmd_addr		   = IRM_ID_addr;
		cmd->bus_generation	   = generation;
		cmd->cmd_u.l32.arg_value   = 0;
		cmd->cmd_u.l32.data_value  = bw_alloc_units;
		cmd->cmd_u.l32.num_retries = num_retries;
		cmd->cmd_u.l32.lock_type   = CMD1394_LOCK_THRESH_SUBTRACT;

		ret = s1394_split_lock_req(hal, NULL, cmd);

		if (ret == DDI_SUCCESS) {
			if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
				*result = cmd->cmd_result;
				/* Need to free the command */
				(void) s1394_free_cmd(hal, &cmd);

				TNF_PROBE_0_DEBUG(s1394_bandwidth_alloc_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_SUCCESS);

			} else {
				*result = cmd->cmd_result;
				/* Need to free the command */
				(void) s1394_free_cmd(hal, &cmd);

				TNF_PROBE_1(s1394_bandwidth_alloc_error,
				    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
				    msg, "Error allocating isoch bandwidth");
				TNF_PROBE_0_DEBUG(s1394_bandwidth_alloc_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}
		} else {
			*result = cmd->cmd_result;
			/* Need to free the command */
			(void) s1394_free_cmd(hal, &cmd);

			TNF_PROBE_1(s1394_bandwidth_alloc_error,
			    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
			    "Error allocating isoch bandwidth");
			TNF_PROBE_0_DEBUG(s1394_bandwidth_alloc_exit,
			    S1394_TNF_SL_ISOCH_STACK, "");
			return (DDI_FAILURE);
		}
	}
}

/*
 * s1394_compute_bw_alloc_units()
 *    is used to compute the number of "bandwidth allocation units" that
 *    are necessary for a given bit rate.  It calculates the overhead
 *    necessary for isoch packet headers, bus arbitration, etc.  (See
 *    IEEE 1394-1995 Section 8.3.2.3.7 for an explanation of what a
 *    "bandwidth allocation unit" is.
 */
uint_t
s1394_compute_bw_alloc_units(s1394_hal_t *hal, uint_t bandwidth, uint_t speed)
{
	uint_t	total_quads;
	uint_t	speed_factor;
	uint_t	bau;
	int	max_hops;

	/* Lock the topology tree */
	mutex_enter(&hal->topology_tree_mutex);

	/* Calculate the 1394 bus diameter */
	max_hops = s1394_topology_tree_calculate_diameter(hal);

	/* Unlock the topology tree */
	mutex_exit(&hal->topology_tree_mutex);

	/* Calculate the total bandwidth (including overhead) */
	total_quads = (bandwidth >> 2) + IEEE1394_ISOCH_HDR_QUAD_SZ;
	switch (speed) {
	case IEEE1394_S400:
		speed_factor = ISOCH_SPEED_FACTOR_S400;
		break;
	case IEEE1394_S200:
		speed_factor = ISOCH_SPEED_FACTOR_S200;
		break;
	case IEEE1394_S100:
		speed_factor = ISOCH_SPEED_FACTOR_S100;
		break;
	}
	/* See IEC 61883-1 pp. 26-29 for this formula */
	bau = (32 * max_hops) + (total_quads * speed_factor);

	return (bau);
}

/*
 * s1394_bandwidth_free()
 *    is used to free up isochronous bandwidth.  A number of bandwidth
 *    allocation units and a generation are passed. The request is sent
 *    to whichever node is the IRM for this amount of bandwidth.  If it
 *    fails because of a bus reset it can be retried. If it fails for
 *    another reason the bandwidth may already be freed or there may
 *    be no IRM.
 */
int
s1394_bandwidth_free(s1394_hal_t *hal, uint32_t bw_alloc_units,
    uint_t generation, int *result)
{
	cmd1394_cmd_t	*cmd;
	uint64_t	IRM_ID_addr;
	uint32_t	compare;
	uint32_t	swap;
	uint32_t	old_value;
	uint32_t	temp_value;
	uint_t		hal_node_num;
	uint_t		IRM_node;
	int		ret;
	int		i;
	int		num_retries = S1394_ISOCH_ALLOC_RETRIES;

	TNF_PROBE_0_DEBUG(s1394_bandwidth_free_enter,
	    S1394_TNF_SL_ISOCH_STACK, "");

	/* Lock the topology tree */
	mutex_enter(&hal->topology_tree_mutex);

	hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
	IRM_node = hal->IRM_node;

	/* Unlock the topology tree */
	mutex_exit(&hal->topology_tree_mutex);

	/* Make sure there is a valid IRM on the bus */
	if (IRM_node == -1) {
		*result = CMD1394_ERETRIES_EXCEEDED;
		TNF_PROBE_1(s1394_bandwidth_free_error,
		    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
		    "No IRM on the 1394 bus");
		TNF_PROBE_0_DEBUG(s1394_bandwidth_free_exit,
		    S1394_TNF_SL_ISOCH_STACK, "");
		return (DDI_FAILURE);
	}

	/* Send compare-swap to BANDWIDTH_AVAILABLE */
	/* register on the Isoch Rsrc Mgr */
	if (IRM_node == hal_node_num) {
		i = num_retries;
		do {
			(void) HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
			    (IEEE1394_SCSR_BANDWIDTH_AVAIL &
			    IEEE1394_CSR_OFFSET_MASK), &old_value);

			/* Check that the generation has not changed */
			if (generation != hal->generation_count) {
				*result = CMD1394_EBUSRESET;
				TNF_PROBE_0_DEBUG(s1394_bandwidth_free_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}

			temp_value = (old_value + bw_alloc_units);
			if ((temp_value >= old_value) &&
			    (temp_value <= IEEE1394_BANDWIDTH_MAX)) {
				compare = old_value;
				swap	= temp_value;
			} else {
				*result = CMD1394_ERETRIES_EXCEEDED;
				TNF_PROBE_1(s1394_bandwidth_free_error,
				    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
				    msg, "Too many retries");
				TNF_PROBE_0_DEBUG(s1394_bandwidth_free_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}

			ret = HAL_CALL(hal).csr_cswap32(
			    hal->halinfo.hal_private, generation,
			    (IEEE1394_SCSR_BANDWIDTH_AVAIL &
			    IEEE1394_CSR_OFFSET_MASK), compare, swap,
			    &old_value);
			if (ret != DDI_SUCCESS) {
				*result = CMD1394_EBUSRESET;
				TNF_PROBE_1(s1394_bandwidth_free_error,
				    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
				    msg, "Error in cswap32");
				TNF_PROBE_0_DEBUG(s1394_bandwidth_free_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}

			if (old_value == compare) {
				*result = CMD1394_CMDSUCCESS;
				TNF_PROBE_0_DEBUG(s1394_bandwidth_free_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_SUCCESS);
			}
		} while (i--);

		*result = CMD1394_ERETRIES_EXCEEDED;
		TNF_PROBE_1(s1394_bandwidth_free_error,
		    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
		    "Retries exceeded");
		TNF_PROBE_0_DEBUG(s1394_bandwidth_free_exit,
		    S1394_TNF_SL_ISOCH_STACK, "");
		return (DDI_FAILURE);

	} else {
		/* Remote */
		if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
			*result = CMD1394_EUNKNOWN_ERROR;
			TNF_PROBE_1(s1394_bandwidth_free_error,
			    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
			    "Unable to allocate command");
			TNF_PROBE_0_DEBUG(s1394_bandwidth_free_exit,
			    S1394_TNF_SL_ISOCH_STACK, "");
			return (DDI_FAILURE);
		}

		cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET |
		    CMD1394_OVERRIDE_ADDR | CMD1394_BLOCKING);
		cmd->cmd_type = CMD1394_ASYNCH_LOCK_32;
		IRM_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
		    IEEE1394_SCSR_BANDWIDTH_AVAIL) |
		    (((uint64_t)hal->IRM_node) << IEEE1394_ADDR_PHY_ID_SHIFT);
		cmd->cmd_addr		   = IRM_ID_addr;
		cmd->bus_generation	   = generation;
		cmd->cmd_u.l32.arg_value   = IEEE1394_BANDWIDTH_MAX;
		cmd->cmd_u.l32.data_value  = bw_alloc_units;
		cmd->cmd_u.l32.num_retries = num_retries;
		cmd->cmd_u.l32.lock_type   = CMD1394_LOCK_THRESH_ADD;

		ret = s1394_split_lock_req(hal, NULL, cmd);

		if (ret == DDI_SUCCESS) {
			if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
				*result = cmd->cmd_result;

				/* Need to free the command */
				(void) s1394_free_cmd(hal, &cmd);

				TNF_PROBE_0_DEBUG(s1394_bandwidth_free_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_SUCCESS);

			} else {
				*result = cmd->cmd_result;
				/* Need to free the command */
				(void) s1394_free_cmd(hal, &cmd);

				TNF_PROBE_1(s1394_bandwidth_free_error,
				    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
				    msg, "Error freeing isoch bandwidth");
				TNF_PROBE_0_DEBUG(s1394_bandwidth_free_exit,
				    S1394_TNF_SL_ISOCH_STACK, "");
				return (DDI_FAILURE);
			}
		} else {
			*result = cmd->cmd_result;
			/* Need to free the command */
			(void) s1394_free_cmd(hal, &cmd);

			TNF_PROBE_1(s1394_bandwidth_free_error,
			    S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
			    "Error freeing isoch bandwidth");
			TNF_PROBE_0_DEBUG(s1394_bandwidth_free_exit,
			    S1394_TNF_SL_ISOCH_STACK, "");
			return (DDI_FAILURE);
		}
	}
}

/*
 * s1394_isoch_cec_list_insert()
 *    is used to insert an Isoch CEC into a given HAL's list of Isoch CECs.
 */
void
s1394_isoch_cec_list_insert(s1394_hal_t *hal, s1394_isoch_cec_t *cec)
{
	s1394_isoch_cec_t *cec_temp;

	TNF_PROBE_0_DEBUG(s1394_isoch_cec_list_insert_enter,
	    S1394_TNF_SL_ISOCH_STACK, "");

	ASSERT(MUTEX_HELD(&hal->isoch_cec_list_mutex));

	/* Is the Isoch CEC list empty? */
	if ((hal->isoch_cec_list_head == NULL) &&
	    (hal->isoch_cec_list_tail == NULL)) {

		hal->isoch_cec_list_head = cec;
		hal->isoch_cec_list_tail = cec;

		cec->cec_next = NULL;
		cec->cec_prev = NULL;

	} else {
		cec->cec_next = hal->isoch_cec_list_head;
		cec->cec_prev = NULL;
		cec_temp = hal->isoch_cec_list_head;
		cec_temp->cec_prev = cec;

		hal->isoch_cec_list_head = cec;
	}

	TNF_PROBE_0_DEBUG(s1394_isoch_cec_list_insert_exit,
	    S1394_TNF_SL_ISOCH_STACK, "");
}

/*
 * s1394_isoch_cec_list_remove()
 *    is used to remove an Isoch CEC from a given HAL's list of Isoch CECs.
 */
void
s1394_isoch_cec_list_remove(s1394_hal_t *hal, s1394_isoch_cec_t *cec)
{
	s1394_isoch_cec_t *prev_cec;
	s1394_isoch_cec_t *next_cec;

	TNF_PROBE_0_DEBUG(s1394_isoch_cec_list_remove_enter,
	    S1394_TNF_SL_ISOCH_STACK, "");

	ASSERT(MUTEX_HELD(&hal->isoch_cec_list_mutex));

	prev_cec = cec->cec_prev;
	next_cec = cec->cec_next;
	cec->cec_prev = NULL;
	cec->cec_next = NULL;

	if (prev_cec != NULL) {
		prev_cec->cec_next = next_cec;

	} else {
		if (hal->isoch_cec_list_head == cec)
			hal->isoch_cec_list_head = next_cec;
	}

	if (next_cec != NULL) {
		next_cec->cec_prev = prev_cec;

	} else {
		if (hal->isoch_cec_list_tail == cec)
			hal->isoch_cec_list_tail = prev_cec;
	}

	TNF_PROBE_0_DEBUG(s1394_isoch_cec_list_remove_exit,
	    S1394_TNF_SL_ISOCH_STACK, "");
}

/*
 * s1394_isoch_cec_member_list_insert()
 *    is used to insert a new member (target) into the list of members for
 *    a given Isoch CEC.
 */
/* ARGSUSED */
void
s1394_isoch_cec_member_list_insert(s1394_hal_t *hal, s1394_isoch_cec_t *cec,
    s1394_isoch_cec_member_t *member)
{
	s1394_isoch_cec_member_t *member_temp;

	TNF_PROBE_0_DEBUG(s1394_isoch_cec_member_list_insert_enter,
	    S1394_TNF_SL_ISOCH_STACK, "");

	ASSERT(MUTEX_HELD(&cec->isoch_cec_mutex));

	/* Is the Isoch CEC member list empty? */
	if ((cec->cec_member_list_head == NULL) &&
	    (cec->cec_member_list_tail == NULL)) {

		cec->cec_member_list_head = member;
		cec->cec_member_list_tail = member;
		member->cec_mem_next = NULL;
		member->cec_mem_prev = NULL;

	} else if (member->cec_mem_options & T1394_TALKER) {
		/* Put talker at the head of the list */
		member->cec_mem_next = cec->cec_member_list_head;
		member->cec_mem_prev = NULL;
		member_temp = cec->cec_member_list_head;
		member_temp->cec_mem_prev = member;
		cec->cec_member_list_head = member;

	} else {
		/* Put listeners at the tail of the list */
		member->cec_mem_prev = cec->cec_member_list_tail;
		member->cec_mem_next = NULL;
		member_temp = cec->cec_member_list_tail;
		member_temp->cec_mem_next = member;
		cec->cec_member_list_tail = member;
	}

	TNF_PROBE_0_DEBUG(s1394_isoch_cec_member_list_insert_exit,
	    S1394_TNF_SL_ISOCH_STACK, "");
}

/*
 * s1394_isoch_cec_member_list_remove()
 *    is used to remove a member (target) from the list of members for
 *    a given Isoch CEC.
 */
/* ARGSUSED */
void
s1394_isoch_cec_member_list_remove(s1394_hal_t *hal, s1394_isoch_cec_t *cec,
    s1394_isoch_cec_member_t *member)
{
	s1394_isoch_cec_member_t *prev_member;
	s1394_isoch_cec_member_t *next_member;

	TNF_PROBE_0_DEBUG(s1394_isoch_cec_member_list_remove_enter,
	    S1394_TNF_SL_ISOCH_STACK, "");

	ASSERT(MUTEX_HELD(&cec->isoch_cec_mutex));

	prev_member = member->cec_mem_prev;
	next_member = member->cec_mem_next;

	member->cec_mem_prev = NULL;
	member->cec_mem_next = NULL;

	if (prev_member != NULL) {
		prev_member->cec_mem_next = next_member;

	} else {
		if (cec->cec_member_list_head == member)
			cec->cec_member_list_head = next_member;
	}

	if (next_member != NULL) {
		next_member->cec_mem_prev = prev_member;

	} else {
		if (cec->cec_member_list_tail == member)
			cec->cec_member_list_tail = prev_member;
	}

	TNF_PROBE_0_DEBUG(s1394_isoch_cec_member_list_remove_exit,
	    S1394_TNF_SL_ISOCH_STACK, "");
}