t1394.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* t1394.c
* 1394 Target Driver Interface
* This file contains all of the 1394 Software Framework routines called
* by target drivers
*/
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/types.h>
#include <sys/kmem.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>
static int s1394_allow_detach = 0;
/*
* Function: t1394_attach()
* Input(s): dip The dip given to the target driver
* in it's attach() routine
* version The version of the target driver -
* T1394_VERSION_V1
* flags The flags parameter is unused (for now)
*
* Output(s): attachinfo Used to pass info back to target,
* including bus generation, local
* node ID, dma attribute, etc.
* t1394_hdl The target "handle" to be used for
* all subsequent calls into the
* 1394 Software Framework
*
* Description: t1394_attach() registers the target (based on its dip) with
* the 1394 Software Framework. It returns the bus_generation,
* local_nodeID, iblock_cookie and other useful information to
* the target, as well as a handle (t1394_hdl) that will be used
* in all subsequent calls into this framework.
*/
/* ARGSUSED */
int
t1394_attach(dev_info_t *dip, int version, uint_t flags,
t1394_attachinfo_t *attachinfo, t1394_handle_t *t1394_hdl)
{
s1394_hal_t *hal;
s1394_target_t *target;
uint_t dev;
uint_t curr;
uint_t unit_dir;
int hp_node = 0;
ASSERT(t1394_hdl != NULL);
ASSERT(attachinfo != NULL);
TNF_PROBE_0_DEBUG(t1394_attach_enter, S1394_TNF_SL_HOTPLUG_STACK, "");
*t1394_hdl = NULL;
if (version != T1394_VERSION_V1) {
TNF_PROBE_1(t1394_attach_error, S1394_TNF_SL_HOTPLUG_ERROR, "",
tnf_string, msg, "Invalid version");
TNF_PROBE_0_DEBUG(t1394_attach_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
hal = s1394_dip_to_hal(ddi_get_parent(dip));
if (hal == NULL) {
TNF_PROBE_1(t1394_attach_error, S1394_TNF_SL_HOTPLUG_ERROR, "",
tnf_string, msg, "No parent dip found for target");
TNF_PROBE_0_DEBUG(t1394_attach_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
hp_node = ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"hp-node");
/* Allocate space for s1394_target_t */
target = kmem_zalloc(sizeof (s1394_target_t), KM_SLEEP);
mutex_enter(&hal->topology_tree_mutex);
target->target_version = version;
/* Copy in the params */
target->target_dip = dip;
target->on_hal = hal;
/* Place the target on the appropriate node */
target->on_node = NULL;
rw_enter(&target->on_hal->target_list_rwlock, RW_WRITER);
if (hp_node != 0) {
s1394_add_target_to_node(target);
/*
* on_node can be NULL if the node got unplugged
* while the target driver is in its attach routine.
*/
if (target->on_node == NULL) {
s1394_remove_target_from_node(target);
rw_exit(&target->on_hal->target_list_rwlock);
mutex_exit(&hal->topology_tree_mutex);
kmem_free(target, sizeof (s1394_target_t));
TNF_PROBE_1(t1394_attach_error,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"on_node == NULL");
TNF_PROBE_0_DEBUG(t1394_attach_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
target->target_state = S1394_TARG_HP_NODE;
if (S1394_NODE_BUS_PWR_CONSUMER(target->on_node) == B_TRUE)
target->target_state |= S1394_TARG_BUS_PWR_CONSUMER;
}
/* Return the current generation */
attachinfo->localinfo.bus_generation = target->on_hal->generation_count;
/* Fill in hal node id */
attachinfo->localinfo.local_nodeID = target->on_hal->node_id;
/* Give the target driver the iblock_cookie */
attachinfo->iblock_cookie = target->on_hal->halinfo.hw_interrupt;
/* Give the target driver the attributes */
attachinfo->acc_attr = target->on_hal->halinfo.acc_attr;
attachinfo->dma_attr = target->on_hal->halinfo.dma_attr;
unit_dir = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "unit-dir-offset", 0);
target->unit_dir = unit_dir;
/* By default, disable all physical AR requests */
target->physical_arreq_enabled = 0;
/* Get dev_max_payload & current_max_payload */
s1394_get_maxpayload(target, &dev, &curr);
target->dev_max_payload = dev;
target->current_max_payload = curr;
/* Add into linked list */
if ((target->on_hal->target_head == NULL) &&
(target->on_hal->target_tail == NULL)) {
target->on_hal->target_head = target;
target->on_hal->target_tail = target;
} else {
target->on_hal->target_tail->target_next = target;
target->target_prev = target->on_hal->target_tail;
target->on_hal->target_tail = target;
}
rw_exit(&target->on_hal->target_list_rwlock);
/* Fill in services layer private info */
*t1394_hdl = (t1394_handle_t)target;
mutex_exit(&hal->topology_tree_mutex);
TNF_PROBE_0_DEBUG(t1394_attach_exit, S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_detach()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* flags The flags parameter is unused (for now)
*
* Output(s): DDI_SUCCESS Target successfully detached
* DDI_FAILURE Target failed to detach
*
* Description: t1394_detach() unregisters the target from the 1394 Software
* Framework. t1394_detach() can fail if the target has any
* allocated commands that haven't been freed.
*/
/* ARGSUSED */
int
t1394_detach(t1394_handle_t *t1394_hdl, uint_t flags)
{
s1394_target_t *target;
uint_t num_cmds;
TNF_PROBE_0_DEBUG(t1394_detach_enter, S1394_TNF_SL_HOTPLUG_STACK, "");
ASSERT(t1394_hdl != NULL);
target = (s1394_target_t *)(*t1394_hdl);
ASSERT(target->on_hal);
mutex_enter(&target->on_hal->topology_tree_mutex);
rw_enter(&target->on_hal->target_list_rwlock, RW_WRITER);
/* How many cmds has this target allocated? */
num_cmds = target->target_num_cmds;
if (num_cmds != 0) {
rw_exit(&target->on_hal->target_list_rwlock);
mutex_exit(&target->on_hal->topology_tree_mutex);
TNF_PROBE_1(t1394_detach_error, S1394_TNF_SL_HOTPLUG_ERROR, "",
tnf_string, msg, "Must free all commands before detach()");
TNF_PROBE_0_DEBUG(t1394_detach_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
/*
* Remove from linked lists. Topology tree is already locked
* so that the node won't go away while we are looking at it.
*/
if ((target->on_hal->target_head == target) &&
(target->on_hal->target_tail == target)) {
target->on_hal->target_head = NULL;
target->on_hal->target_tail = NULL;
} else {
if (target->target_prev)
target->target_prev->target_next = target->target_next;
if (target->target_next)
target->target_next->target_prev = target->target_prev;
if (target->on_hal->target_head == target)
target->on_hal->target_head = target->target_next;
if (target->on_hal->target_tail == target)
target->on_hal->target_tail = target->target_prev;
}
s1394_remove_target_from_node(target);
rw_exit(&target->on_hal->target_list_rwlock);
mutex_exit(&target->on_hal->topology_tree_mutex);
/* Free memory */
kmem_free(target, sizeof (s1394_target_t));
*t1394_hdl = NULL;
TNF_PROBE_0_DEBUG(t1394_detach_exit, S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_alloc_cmd()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* flags The flags parameter is described below
*
* Output(s): cmdp Pointer to the newly allocated command
*
* Description: t1394_alloc_cmd() allocates a command for use with the
* t1394_read(), t1394_write(), or t1394_lock() interfaces
* of the 1394 Software Framework. By default, t1394_alloc_cmd()
* may sleep while allocating memory for the command structure.
* If this is undesirable, the target may set the
* T1394_ALLOC_CMD_NOSLEEP bit in the flags parameter. Also,
* this call may fail because a target driver has already
* allocated MAX_NUMBER_ALLOC_CMDS commands.
*/
int
t1394_alloc_cmd(t1394_handle_t t1394_hdl, uint_t flags, cmd1394_cmd_t **cmdp)
{
s1394_hal_t *hal;
s1394_target_t *target;
s1394_cmd_priv_t *s_priv;
uint_t num_cmds;
TNF_PROBE_0_DEBUG(t1394_alloc_cmd_enter, S1394_TNF_SL_ATREQ_STACK, "");
ASSERT(t1394_hdl != NULL);
target = (s1394_target_t *)t1394_hdl;
/* Find the HAL this target resides on */
hal = target->on_hal;
rw_enter(&hal->target_list_rwlock, RW_WRITER);
/* How many cmds has this target allocated? */
num_cmds = target->target_num_cmds;
if (num_cmds >= MAX_NUMBER_ALLOC_CMDS) {
rw_exit(&hal->target_list_rwlock);
TNF_PROBE_1(t1394_alloc_cmd_error, S1394_TNF_SL_ATREQ_ERROR,
"", tnf_string, msg, "Attempted to alloc > "
"MAX_NUMBER_ALLOC_CMDS");
TNF_PROBE_0_DEBUG(t1394_alloc_cmd_exit,
S1394_TNF_SL_ATREQ_STACK, "");
/* kstats - cmd alloc failures */
hal->hal_kstats->cmd_alloc_fail++;
return (DDI_FAILURE);
}
/* Increment the number of cmds this target has allocated? */
target->target_num_cmds = num_cmds + 1;
if (s1394_alloc_cmd(hal, flags, cmdp) != DDI_SUCCESS) {
target->target_num_cmds = num_cmds; /* Undo increment */
rw_exit(&hal->target_list_rwlock);
TNF_PROBE_1(t1394_alloc_cmd_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Failed to allocate command structure");
TNF_PROBE_0_DEBUG(t1394_alloc_cmd_exit,
S1394_TNF_SL_ATREQ_STACK, "");
/* kstats - cmd alloc failures */
hal->hal_kstats->cmd_alloc_fail++;
return (DDI_FAILURE);
}
rw_exit(&hal->target_list_rwlock);
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(*cmdp);
/* Initialize the command's blocking mutex */
mutex_init(&s_priv->blocking_mutex, NULL, MUTEX_DRIVER,
hal->halinfo.hw_interrupt);
/* Initialize the command's blocking condition variable */
cv_init(&s_priv->blocking_cv, NULL, CV_DRIVER, NULL);
TNF_PROBE_0_DEBUG(t1394_alloc_cmd_exit, S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_free_cmd()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* flags The flags parameter is unused (for now)
* cmdp Pointer to the command to be freed
*
* Output(s): DDI_SUCCESS Target successfully freed command
* DDI_FAILURE Target failed to free command
*
* Description: t1394_free_cmd() attempts to free a command that has previously
* been allocated by the target driver. It is possible for
* t1394_free_cmd() to fail because the command is currently
* in-use by the 1394 Software Framework.
*/
/* ARGSUSED */
int
t1394_free_cmd(t1394_handle_t t1394_hdl, uint_t flags, cmd1394_cmd_t **cmdp)
{
s1394_hal_t *hal;
s1394_target_t *target;
s1394_cmd_priv_t *s_priv;
uint_t num_cmds;
TNF_PROBE_0_DEBUG(t1394_free_cmd_enter, S1394_TNF_SL_ATREQ_STACK, "");
ASSERT(t1394_hdl != NULL);
target = (s1394_target_t *)t1394_hdl;
/* Find the HAL this target resides on */
hal = target->on_hal;
rw_enter(&hal->target_list_rwlock, RW_WRITER);
/* How many cmds has this target allocated? */
num_cmds = target->target_num_cmds;
if (num_cmds == 0) {
rw_exit(&hal->target_list_rwlock);
TNF_PROBE_2(t1394_free_cmd_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "No commands left to be freed "
"(num_cmds <= 0)", tnf_uint, num_cmds, num_cmds);
TNF_PROBE_0_DEBUG(t1394_free_cmd_exit,
S1394_TNF_SL_ATREQ_STACK, "");
ASSERT(num_cmds != 0);
return (DDI_FAILURE);
}
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(*cmdp);
/* Check that command isn't in use */
if (s_priv->cmd_in_use == B_TRUE) {
rw_exit(&hal->target_list_rwlock);
TNF_PROBE_1(t1394_free_cmd_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Attempted to free an in-use command");
TNF_PROBE_0_DEBUG(t1394_free_cmd_exit,
S1394_TNF_SL_ATREQ_STACK, "");
ASSERT(s_priv->cmd_in_use == B_FALSE);
return (DDI_FAILURE);
}
/* Decrement the number of cmds this target has allocated */
target->target_num_cmds--;
rw_exit(&hal->target_list_rwlock);
/* Destroy the command's blocking condition variable */
cv_destroy(&s_priv->blocking_cv);
/* Destroy the command's blocking mutex */
mutex_destroy(&s_priv->blocking_mutex);
kmem_cache_free(hal->hal_kmem_cachep, *cmdp);
/* Command pointer is set to NULL before returning */
*cmdp = NULL;
/* kstats - number of cmd frees */
hal->hal_kstats->cmd_free++;
TNF_PROBE_0_DEBUG(t1394_free_cmd_exit, S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_read()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* cmd Pointer to the command to send
*
* Output(s): DDI_SUCCESS Target successful sent the command
* DDI_FAILURE Target failed to send command
*
* Description: t1394_read() attempts to send an asynchronous read request
* onto the 1394 bus.
*/
int
t1394_read(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
{
s1394_hal_t *to_hal;
s1394_target_t *target;
s1394_cmd_priv_t *s_priv;
s1394_hal_state_t state;
int ret;
int err;
TNF_PROBE_0_DEBUG(t1394_read_enter, S1394_TNF_SL_ATREQ_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(cmd != NULL);
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Is this command currently in use? */
if (s_priv->cmd_in_use == B_TRUE) {
TNF_PROBE_1(t1394_read_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Attempted to resend an in-use command");
TNF_PROBE_0_DEBUG(t1394_read_exit, S1394_TNF_SL_ATREQ_STACK,
"");
ASSERT(s_priv->cmd_in_use == B_FALSE);
return (DDI_FAILURE);
}
target = (s1394_target_t *)t1394_hdl;
/* Set-up the destination of the command */
to_hal = target->on_hal;
/* No status (default) */
cmd->cmd_result = CMD1394_NOSTATUS;
/* Check for proper command type */
if ((cmd->cmd_type != CMD1394_ASYNCH_RD_QUAD) &&
(cmd->cmd_type != CMD1394_ASYNCH_RD_BLOCK)) {
cmd->cmd_result = CMD1394_EINVALID_COMMAND;
TNF_PROBE_1(t1394_read_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Invalid command type specified");
TNF_PROBE_0_DEBUG(t1394_read_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
/* Is this a blocking command on interrupt stack? */
if ((cmd->cmd_options & CMD1394_BLOCKING) &&
(curthread->t_flag == T_INTR_THREAD)) {
cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
TNF_PROBE_1(t1394_read_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Tried to use CMD1394_BLOCKING in "
"intr context");
TNF_PROBE_0_DEBUG(t1394_read_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
mutex_enter(&to_hal->topology_tree_mutex);
state = to_hal->hal_state;
if (state != S1394_HAL_NORMAL) {
ret = s1394_HAL_asynch_error(to_hal, cmd, state);
if (ret != CMD1394_CMDSUCCESS) {
cmd->cmd_result = ret;
mutex_exit(&to_hal->topology_tree_mutex);
return (DDI_FAILURE);
}
}
ret = s1394_setup_asynch_command(to_hal, target, cmd,
S1394_CMD_READ, &err);
/* Command has now been put onto the queue! */
if (ret != DDI_SUCCESS) {
/* Copy error code into result */
cmd->cmd_result = err;
mutex_exit(&to_hal->topology_tree_mutex);
TNF_PROBE_1(t1394_read_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Failed in s1394_setup_asynch_command()");
TNF_PROBE_0_DEBUG(t1394_read_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
/*
* If this command was sent during a bus reset,
* then put it onto the pending Q.
*/
if (state == S1394_HAL_RESET) {
/* Remove cmd from outstanding request Q */
s1394_remove_q_asynch_cmd(to_hal, cmd);
/* Are we on the bus reset event stack? */
if (s1394_on_br_thread(to_hal) == B_TRUE) {
/* Blocking commands are not allowed */
if (cmd->cmd_options & CMD1394_BLOCKING) {
mutex_exit(&to_hal->topology_tree_mutex);
s_priv->cmd_in_use = B_FALSE;
cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
TNF_PROBE_1(t1394_read_error,
S1394_TNF_SL_ATREQ_ERROR, "", tnf_string,
msg, "CMD1394_BLOCKING in bus reset "
"context");
TNF_PROBE_0_DEBUG(t1394_read_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
}
s1394_pending_q_insert(to_hal, cmd, S1394_PENDING_Q_FRONT);
mutex_exit(&to_hal->topology_tree_mutex);
/* Block (if necessary) */
goto block_on_asynch_cmd;
}
mutex_exit(&to_hal->topology_tree_mutex);
/* Send the command out */
ret = s1394_xfer_asynch_command(to_hal, cmd, &err);
if (ret != DDI_SUCCESS) {
if (err == CMD1394_ESTALE_GENERATION) {
/* Remove cmd from outstanding request Q */
s1394_remove_q_asynch_cmd(to_hal, cmd);
s1394_pending_q_insert(to_hal, cmd,
S1394_PENDING_Q_FRONT);
/* Block (if necessary) */
goto block_on_asynch_cmd;
} else {
/* Remove cmd from outstanding request Q */
s1394_remove_q_asynch_cmd(to_hal, cmd);
s_priv->cmd_in_use = B_FALSE;
/* Copy error code into result */
cmd->cmd_result = err;
TNF_PROBE_1(t1394_read_error, S1394_TNF_SL_ATREQ_ERROR,
"", tnf_string, msg, "Failed in "
"s1394_xfer_asynch_command()");
TNF_PROBE_0_DEBUG(t1394_read_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
} else {
/* Block (if necessary) */
goto block_on_asynch_cmd;
}
block_on_asynch_cmd:
s1394_block_on_asynch_cmd(cmd);
TNF_PROBE_0_DEBUG(t1394_read_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_write()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* cmd Pointer to the command to send
*
* Output(s): DDI_SUCCESS Target successful sent the command
* DDI_FAILURE Target failed to send command
*
* Description: t1394_write() attempts to send an asynchronous write request
* onto the 1394 bus.
*/
int
t1394_write(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
{
s1394_hal_t *to_hal;
s1394_target_t *target;
s1394_cmd_priv_t *s_priv;
s1394_hal_state_t state;
int ret;
int err;
TNF_PROBE_0_DEBUG(t1394_write_enter, S1394_TNF_SL_ATREQ_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(cmd != NULL);
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Is this command currently in use? */
if (s_priv->cmd_in_use == B_TRUE) {
TNF_PROBE_1(t1394_write_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Attempted to resend an in-use command");
TNF_PROBE_0_DEBUG(t1394_write_exit, S1394_TNF_SL_ATREQ_STACK,
"");
ASSERT(s_priv->cmd_in_use == B_FALSE);
return (DDI_FAILURE);
}
target = (s1394_target_t *)t1394_hdl;
/* Set-up the destination of the command */
to_hal = target->on_hal;
/* Is this an FA request? */
if (s_priv->cmd_ext_type == S1394_CMD_EXT_FA) {
if (S1394_IS_CMD_FCP(s_priv) &&
(s1394_fcp_write_check_cmd(cmd) != DDI_SUCCESS)) {
TNF_PROBE_0_DEBUG(t1394_write_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
s1394_fa_convert_cmd(to_hal, cmd);
}
/* No status (default) */
cmd->cmd_result = CMD1394_NOSTATUS;
/* Check for proper command type */
if ((cmd->cmd_type != CMD1394_ASYNCH_WR_QUAD) &&
(cmd->cmd_type != CMD1394_ASYNCH_WR_BLOCK)) {
cmd->cmd_result = CMD1394_EINVALID_COMMAND;
s1394_fa_check_restore_cmd(to_hal, cmd);
TNF_PROBE_1(t1394_write_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Invalid command type specified");
TNF_PROBE_0_DEBUG(t1394_write_exit, S1394_TNF_SL_ATREQ_STACK,
"");
return (DDI_FAILURE);
}
/* Is this a blocking command on interrupt stack? */
if ((cmd->cmd_options & CMD1394_BLOCKING) &&
(curthread->t_flag == T_INTR_THREAD)) {
cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
s1394_fa_check_restore_cmd(to_hal, cmd);
TNF_PROBE_1(t1394_write_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Tried to use CMD1394_BLOCKING in intr "
"context");
TNF_PROBE_0_DEBUG(t1394_write_exit, S1394_TNF_SL_ATREQ_STACK,
"");
return (DDI_FAILURE);
}
mutex_enter(&to_hal->topology_tree_mutex);
state = to_hal->hal_state;
if (state != S1394_HAL_NORMAL) {
ret = s1394_HAL_asynch_error(to_hal, cmd, state);
if (ret != CMD1394_CMDSUCCESS) {
cmd->cmd_result = ret;
mutex_exit(&to_hal->topology_tree_mutex);
s1394_fa_check_restore_cmd(to_hal, cmd);
return (DDI_FAILURE);
}
}
ret = s1394_setup_asynch_command(to_hal, target, cmd,
S1394_CMD_WRITE, &err);
/* Command has now been put onto the queue! */
if (ret != DDI_SUCCESS) {
/* Copy error code into result */
cmd->cmd_result = err;
mutex_exit(&to_hal->topology_tree_mutex);
s1394_fa_check_restore_cmd(to_hal, cmd);
TNF_PROBE_1(t1394_write_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Failed in s1394_setup_asynch_command()");
TNF_PROBE_0_DEBUG(t1394_write_exit, S1394_TNF_SL_ATREQ_STACK,
"");
return (DDI_FAILURE);
}
/*
* If this command was sent during a bus reset,
* then put it onto the pending Q.
*/
if (state == S1394_HAL_RESET) {
/* Remove cmd from outstanding request Q */
s1394_remove_q_asynch_cmd(to_hal, cmd);
/* Are we on the bus reset event stack? */
if (s1394_on_br_thread(to_hal) == B_TRUE) {
/* Blocking commands are not allowed */
if (cmd->cmd_options & CMD1394_BLOCKING) {
mutex_exit(&to_hal->topology_tree_mutex);
s_priv->cmd_in_use = B_FALSE;
cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
s1394_fa_check_restore_cmd(to_hal, cmd);
TNF_PROBE_1(t1394_write_error,
S1394_TNF_SL_ATREQ_ERROR, "", tnf_string,
msg, "CMD1394_BLOCKING in bus reset cntxt");
TNF_PROBE_0_DEBUG(t1394_write_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
}
s1394_pending_q_insert(to_hal, cmd, S1394_PENDING_Q_FRONT);
mutex_exit(&to_hal->topology_tree_mutex);
/* Block (if necessary) */
s1394_block_on_asynch_cmd(cmd);
TNF_PROBE_0_DEBUG(t1394_write_exit, S1394_TNF_SL_ATREQ_STACK,
"");
return (DDI_SUCCESS);
}
mutex_exit(&to_hal->topology_tree_mutex);
/* Send the command out */
ret = s1394_xfer_asynch_command(to_hal, cmd, &err);
if (ret != DDI_SUCCESS) {
if (err == CMD1394_ESTALE_GENERATION) {
/* Remove cmd from outstanding request Q */
s1394_remove_q_asynch_cmd(to_hal, cmd);
s1394_pending_q_insert(to_hal, cmd,
S1394_PENDING_Q_FRONT);
/* Block (if necessary) */
s1394_block_on_asynch_cmd(cmd);
TNF_PROBE_0_DEBUG(t1394_write_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
} else {
/* Remove cmd from outstanding request Q */
s1394_remove_q_asynch_cmd(to_hal, cmd);
s_priv->cmd_in_use = B_FALSE;
/* Copy error code into result */
cmd->cmd_result = err;
s1394_fa_check_restore_cmd(to_hal, cmd);
TNF_PROBE_1(t1394_write_error,
S1394_TNF_SL_ATREQ_ERROR, "", tnf_string, msg,
"Failed in s1394_xfer_asynch_command()");
TNF_PROBE_0_DEBUG(t1394_write_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
} else {
/* Block (if necessary) */
s1394_block_on_asynch_cmd(cmd);
TNF_PROBE_0_DEBUG(t1394_write_exit, S1394_TNF_SL_ATREQ_STACK,
"");
return (DDI_SUCCESS);
}
}
/*
* Function: t1394_lock()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* cmd Pointer to the command to send
*
* Output(s): DDI_SUCCESS Target successful sent the command
* DDI_FAILURE Target failed to send command
*
* Description: t1394_lock() attempts to send an asynchronous lock request
* onto the 1394 bus.
*/
int
t1394_lock(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
{
s1394_hal_t *to_hal;
s1394_target_t *target;
s1394_cmd_priv_t *s_priv;
s1394_hal_state_t state;
cmd1394_lock_type_t lock_type;
uint_t num_retries;
int ret;
TNF_PROBE_0_DEBUG(t1394_lock_enter, S1394_TNF_SL_ATREQ_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(cmd != NULL);
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Is this command currently in use? */
if (s_priv->cmd_in_use == B_TRUE) {
TNF_PROBE_1(t1394_lock_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Attempted to resend an in-use command");
TNF_PROBE_0_DEBUG(t1394_lock_exit, S1394_TNF_SL_ATREQ_STACK,
"");
ASSERT(s_priv->cmd_in_use == B_FALSE);
return (DDI_FAILURE);
}
target = (s1394_target_t *)t1394_hdl;
/* Set-up the destination of the command */
to_hal = target->on_hal;
mutex_enter(&to_hal->topology_tree_mutex);
state = to_hal->hal_state;
if (state != S1394_HAL_NORMAL) {
ret = s1394_HAL_asynch_error(to_hal, cmd, state);
if (ret != CMD1394_CMDSUCCESS) {
cmd->cmd_result = ret;
mutex_exit(&to_hal->topology_tree_mutex);
return (DDI_FAILURE);
}
}
mutex_exit(&to_hal->topology_tree_mutex);
/* Check for proper command type */
if ((cmd->cmd_type != CMD1394_ASYNCH_LOCK_32) &&
(cmd->cmd_type != CMD1394_ASYNCH_LOCK_64)) {
cmd->cmd_result = CMD1394_EINVALID_COMMAND;
TNF_PROBE_1(t1394_lock_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Invalid command type sent to "
"t1394_lock()");
TNF_PROBE_0_DEBUG(t1394_lock_exit, S1394_TNF_SL_ATREQ_STACK,
"");
return (DDI_FAILURE);
}
/* No status (default) */
cmd->cmd_result = CMD1394_NOSTATUS;
/* Is this a blocking command on interrupt stack? */
if ((cmd->cmd_options & CMD1394_BLOCKING) &&
(curthread->t_flag == T_INTR_THREAD)) {
cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
TNF_PROBE_1(t1394_lock_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Tried to use CMD1394_BLOCKING in intr "
"context");
TNF_PROBE_0_DEBUG(t1394_lock_exit, S1394_TNF_SL_ATREQ_STACK,
"");
return (DDI_FAILURE);
}
if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) {
lock_type = cmd->cmd_u.l32.lock_type;
num_retries = cmd->cmd_u.l32.num_retries;
} else { /* (cmd->cmd_type == CMD1394_ASYNCH_LOCK_64) */
lock_type = cmd->cmd_u.l64.lock_type;
num_retries = cmd->cmd_u.l64.num_retries;
}
/* Make sure num_retries is reasonable */
ASSERT(num_retries <= MAX_NUMBER_OF_LOCK_RETRIES);
switch (lock_type) {
case CMD1394_LOCK_MASK_SWAP:
case CMD1394_LOCK_FETCH_ADD:
case CMD1394_LOCK_LITTLE_ADD:
case CMD1394_LOCK_BOUNDED_ADD:
case CMD1394_LOCK_WRAP_ADD:
case CMD1394_LOCK_COMPARE_SWAP:
ret = s1394_compare_swap(to_hal, target, cmd);
break;
case CMD1394_LOCK_BIT_AND:
case CMD1394_LOCK_BIT_OR:
case CMD1394_LOCK_BIT_XOR:
case CMD1394_LOCK_INCREMENT:
case CMD1394_LOCK_DECREMENT:
case CMD1394_LOCK_ADD:
case CMD1394_LOCK_SUBTRACT:
case CMD1394_LOCK_THRESH_ADD:
case CMD1394_LOCK_THRESH_SUBTRACT:
case CMD1394_LOCK_CLIP_ADD:
case CMD1394_LOCK_CLIP_SUBTRACT:
ret = s1394_split_lock_req(to_hal, target, cmd);
break;
default:
TNF_PROBE_1(t1394_lock_error, S1394_TNF_SL_ATREQ_ERROR, "",
tnf_string, msg, "Invalid lock_type in command");
cmd->cmd_result = CMD1394_EINVALID_COMMAND;
ret = DDI_FAILURE;
break;
}
TNF_PROBE_0_DEBUG(t1394_lock_exit, S1394_TNF_SL_ATREQ_STACK, "");
return (ret);
}
/*
* Function: t1394_alloc_addr()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* addr_allocp The structure used to specify the type,
* size, permissions, and callbacks
* (if any) for the requested block
* of 1394 address space
* flags The flags parameter is unused (for now)
*
* Output(s): result Used to pass more specific info back
* to target
*
* Description: t1394_alloc_addr() requests that part of the 1394 Address Space
* on the local node be set aside for this target driver, and
* associated with this address space should be some permissions
* and callbacks. If the request is unable to be fulfilled,
* t1394_alloc_addr() will return DDI_FAILURE and result will
* indicate the reason. T1394_EINVALID_PARAM indicates that the
* combination of flags given is invalid, and T1394_EALLOC_ADDR
* indicates that the requested type of address space is
* unavailable.
*/
/* ARGSUSED */
int
t1394_alloc_addr(t1394_handle_t t1394_hdl, t1394_alloc_addr_t *addr_allocp,
uint_t flags, int *result)
{
s1394_hal_t *hal;
s1394_target_t *target;
uint64_t addr_lo;
uint64_t addr_hi;
int err;
TNF_PROBE_0_DEBUG(t1394_alloc_addr_enter, S1394_TNF_SL_ARREQ_STACK,
"");
ASSERT(t1394_hdl != NULL);
ASSERT(addr_allocp != NULL);
target = (s1394_target_t *)t1394_hdl;
/* Find the HAL this target resides on */
hal = target->on_hal;
/* Get the bounds of the request */
addr_lo = addr_allocp->aa_address;
addr_hi = addr_lo + addr_allocp->aa_length;
/* Check combination of flags */
if ((addr_allocp->aa_enable & T1394_ADDR_RDENBL) &&
(addr_allocp->aa_evts.recv_read_request == NULL) &&
(addr_allocp->aa_kmem_bufp == NULL)) {
if ((addr_allocp->aa_type != T1394_ADDR_FIXED) ||
(addr_lo < hal->physical_addr_lo) ||
(addr_hi > hal->physical_addr_hi)) {
/*
* Reads are enabled, but target doesn't want to
* be notified and hasn't given backing store
*/
*result = T1394_EINVALID_PARAM;
TNF_PROBE_1(t1394_alloc_addr_error,
S1394_TNF_SL_ARREQ_ERROR, "", tnf_string, msg,
"Invalid flags "
"(RDs on, notify off, no backing store)");
TNF_PROBE_0_DEBUG(t1394_alloc_addr_exit,
S1394_TNF_SL_ARREQ_STACK, "");
/* kstats - addr alloc failures */
hal->hal_kstats->addr_alloc_fail++;
return (DDI_FAILURE);
} else {
addr_allocp->aa_enable &= ~T1394_ADDR_RDENBL;
}
}
if ((addr_allocp->aa_enable & T1394_ADDR_WRENBL) &&
(addr_allocp->aa_evts.recv_write_request == NULL) &&
(addr_allocp->aa_kmem_bufp == NULL)) {
if ((addr_allocp->aa_type != T1394_ADDR_FIXED) ||
(addr_lo < hal->physical_addr_lo) ||
(addr_hi > hal->physical_addr_hi)) {
/*
* Writes are enabled, but target doesn't want to
* be notified and hasn't given backing store
*/
*result = T1394_EINVALID_PARAM;
TNF_PROBE_1(t1394_alloc_addr_error,
S1394_TNF_SL_ARREQ_ERROR, "", tnf_string, msg,
"Invalid flags "
"(WRs on, notify off, no backing store)");
TNF_PROBE_0_DEBUG(t1394_alloc_addr_exit,
S1394_TNF_SL_ARREQ_STACK, "");
/* kstats - addr alloc failures */
hal->hal_kstats->addr_alloc_fail++;
return (DDI_FAILURE);
} else {
addr_allocp->aa_enable &= ~T1394_ADDR_WRENBL;
}
}
if ((addr_allocp->aa_enable & T1394_ADDR_LKENBL) &&
(addr_allocp->aa_evts.recv_lock_request == NULL) &&
(addr_allocp->aa_kmem_bufp == NULL)) {
if ((addr_allocp->aa_type != T1394_ADDR_FIXED) ||
(addr_lo < hal->physical_addr_lo) ||
(addr_hi > hal->physical_addr_hi)) {
/*
* Locks are enabled, but target doesn't want to
* be notified and hasn't given backing store
*/
*result = T1394_EINVALID_PARAM;
TNF_PROBE_1(t1394_alloc_addr_error,
S1394_TNF_SL_ARREQ_ERROR, "", tnf_string, msg,
"Invalid flags "
"(LKs on, notify off, no backing store)");
TNF_PROBE_0_DEBUG(t1394_alloc_addr_exit,
S1394_TNF_SL_ARREQ_STACK, "");
/* kstats - addr alloc failures */
hal->hal_kstats->addr_alloc_fail++;
return (DDI_FAILURE);
} else {
addr_allocp->aa_enable &= ~T1394_ADDR_LKENBL;
}
}
/* If not T1394_ADDR_FIXED, then allocate a block */
if (addr_allocp->aa_type != T1394_ADDR_FIXED) {
err = s1394_request_addr_blk((s1394_hal_t *)target->on_hal,
addr_allocp);
if (err != DDI_SUCCESS) {
*result = T1394_EALLOC_ADDR;
/* kstats - addr alloc failures */
hal->hal_kstats->addr_alloc_fail++;
} else {
*result = T1394_NOERROR;
}
TNF_PROBE_0_DEBUG(t1394_alloc_addr_exit,
S1394_TNF_SL_ARREQ_STACK, "");
return (err);
} else {
err = s1394_claim_addr_blk((s1394_hal_t *)target->on_hal,
addr_allocp);
if (err != DDI_SUCCESS) {
*result = T1394_EALLOC_ADDR;
/* kstats - addr alloc failures */
hal->hal_kstats->addr_alloc_fail++;
} else {
*result = T1394_NOERROR;
/* If physical, update the AR request counter */
if ((addr_lo >= hal->physical_addr_lo) &&
(addr_hi <= hal->physical_addr_hi)) {
rw_enter(&hal->target_list_rwlock, RW_WRITER);
target->physical_arreq_enabled++;
rw_exit(&hal->target_list_rwlock);
s1394_physical_arreq_set_one(target);
}
}
TNF_PROBE_0_DEBUG(t1394_alloc_addr_exit,
S1394_TNF_SL_ARREQ_STACK, "");
return (err);
}
}
/*
* Function: t1394_free_addr()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* addr_hdl The address "handle" returned by the
* the t1394_alloc_addr() routine
* flags The flags parameter is unused (for now)
*
* Output(s): DDI_SUCCESS Target successfully freed memory
* DDI_FAILURE Target failed to free the memory block
*
* Description: t1394_free_addr() attempts to free up memory that has been
* allocated by the target using t1394_alloc_addr().
*/
/* ARGSUSED */
int
t1394_free_addr(t1394_handle_t t1394_hdl, t1394_addr_handle_t *addr_hdl,
uint_t flags)
{
s1394_addr_space_blk_t *curr_blk;
s1394_hal_t *hal;
s1394_target_t *target;
TNF_PROBE_0_DEBUG(t1394_free_addr_enter, S1394_TNF_SL_ARREQ_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(addr_hdl != NULL);
target = (s1394_target_t *)t1394_hdl;
/* Find the HAL this target resides on */
hal = target->on_hal;
curr_blk = (s1394_addr_space_blk_t *)(*addr_hdl);
if (s1394_free_addr_blk(hal, curr_blk) != DDI_SUCCESS) {
TNF_PROBE_0_DEBUG(t1394_free_addr_exit,
S1394_TNF_SL_ARREQ_STACK, "");
return (DDI_FAILURE);
}
/* If physical, update the AR request counter */
if (curr_blk->addr_type == T1394_ADDR_FIXED) {
target->physical_arreq_enabled--;
s1394_physical_arreq_clear_one(target);
}
*addr_hdl = NULL;
/* kstats - number of addr frees */
hal->hal_kstats->addr_space_free++;
TNF_PROBE_0_DEBUG(t1394_free_addr_exit, S1394_TNF_SL_ARREQ_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_recv_request_done()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* resp Pointer to the command which the
* target received in it's callback
* flags The flags parameter is unused (for now)
*
* Output(s): DDI_SUCCESS Target successfully returned command
* to the 1394 Software Framework,
* and, if necessary, sent response
* DDI_FAILURE Target failed to return the command to
* the 1394 Software Framework
*
* Description: t1394_recv_request_done() takes the command that is given and
* determines whether that command requires a response to be
* sent on the 1394 bus. If it is necessary and it's response
* code (cmd_result) has been set appropriately, then a response
* will be sent. If no response is necessary (broadcast or
* posted write), then the command resources are reclaimed.
*/
/* ARGSUSED */
int
t1394_recv_request_done(t1394_handle_t t1394_hdl, cmd1394_cmd_t *resp,
uint_t flags)
{
s1394_hal_t *hal;
s1394_cmd_priv_t *s_priv;
h1394_cmd_priv_t *h_priv;
mblk_t *curr_blk;
size_t msgb_len;
size_t size;
int ret;
boolean_t response = B_TRUE;
boolean_t posted_write = B_FALSE;
boolean_t write_cmd = B_FALSE;
boolean_t mblk_too_small;
TNF_PROBE_0_DEBUG(t1394_recv_request_done_enter,
S1394_TNF_SL_ARREQ_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(resp != NULL);
/* Find the HAL this target resides on */
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(resp);
/* Get a pointer to the HAL private struct */
h_priv = (h1394_cmd_priv_t *)&s_priv->hal_cmd_private;
/* Is this an FA request? */
if (s_priv->cmd_ext_type == S1394_CMD_EXT_FA) {
s1394_fa_convert_cmd(hal, resp);
}
/* Is this a write request? */
if ((resp->cmd_type == CMD1394_ASYNCH_WR_QUAD) ||
(resp->cmd_type == CMD1394_ASYNCH_WR_BLOCK)) {
write_cmd = B_TRUE;
/* Is this a posted write request? */
posted_write = s_priv->posted_write;
}
/* If broadcast or posted write cmd, don't send response */
if ((resp->broadcast == 1) ||
((write_cmd == B_TRUE) && (posted_write == B_TRUE)))
response = B_FALSE;
if (response == B_FALSE) {
if ((write_cmd == B_TRUE) && (posted_write == B_TRUE)) {
/* kstats - Posted Write error */
hal->hal_kstats->arreq_posted_write_error++;
}
/* Free the command - Pass it back to the HAL */
HAL_CALL(hal).response_complete(hal->halinfo.hal_private, resp,
h_priv);
TNF_PROBE_0_DEBUG(t1394_recv_request_done_exit,
S1394_TNF_SL_ARREQ_STACK, "");
return (DDI_SUCCESS);
}
ASSERT(response == B_TRUE);
/* Verify valid response code */
switch (resp->cmd_result) {
case IEEE1394_RESP_COMPLETE:
/* Is the mblk_t too small? */
if (resp->cmd_type == CMD1394_ASYNCH_RD_BLOCK) {
curr_blk = resp->cmd_u.b.data_block;
size = resp->cmd_u.b.blk_length;
msgb_len = 0;
mblk_too_small = B_TRUE;
if (curr_blk == NULL) {
TNF_PROBE_1(t1394_recv_request_done_error,
S1394_TNF_SL_ARREQ_ERROR, "", tnf_string,
msg, "mblk_t is NULL in response");
TNF_PROBE_0_DEBUG(t1394_recv_request_done_exit,
S1394_TNF_SL_ARREQ_STACK, "");
/*
* Free the command - Pass it back
* to the HAL
*/
HAL_CALL(hal).response_complete(
hal->halinfo.hal_private, resp, h_priv);
ASSERT(curr_blk != NULL);
return (DDI_FAILURE);
}
while (curr_blk != NULL) {
msgb_len +=
(curr_blk->b_wptr - curr_blk->b_rptr);
if (msgb_len >= size) {
mblk_too_small = B_FALSE;
break;
}
curr_blk = curr_blk->b_cont;
}
if (mblk_too_small == B_TRUE) {
TNF_PROBE_1(t1394_recv_request_done_error,
S1394_TNF_SL_ARREQ_ERROR, "", tnf_string,
msg, "mblk_t too small in response");
TNF_PROBE_0_DEBUG(t1394_recv_request_done_exit,
S1394_TNF_SL_ARREQ_STACK, "");
/*
* Free the command - Pass it back
* to the HAL
*/
HAL_CALL(hal).response_complete(
hal->halinfo.hal_private, resp, h_priv);
ASSERT(mblk_too_small != B_TRUE);
return (DDI_FAILURE);
}
}
/* FALLTHROUGH */
case IEEE1394_RESP_CONFLICT_ERROR:
case IEEE1394_RESP_DATA_ERROR:
case IEEE1394_RESP_TYPE_ERROR:
case IEEE1394_RESP_ADDRESS_ERROR:
ret = s1394_send_response(hal, resp);
TNF_PROBE_0_DEBUG(t1394_recv_request_done_exit,
S1394_TNF_SL_ARREQ_STACK, "");
return (ret);
default:
TNF_PROBE_1(t1394_recv_request_done_error,
S1394_TNF_SL_ARREQ_ERROR, "", tnf_string, msg,
"Invalid response code");
TNF_PROBE_0_DEBUG(t1394_recv_request_done_exit,
S1394_TNF_SL_ARREQ_STACK, "");
return (DDI_FAILURE);
}
}
/*
* Function: t1394_fcp_register_controller()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* evts The structure in which the target
* specifies its callback routines
*
* flags The flags parameter is unused (for now)
*
* Output(s): DDI_SUCCESS Successfully registered.
*
* DDI_FAILURE Not registered due to failure.
*
* Description: Used to register the target within the Framework as an FCP
* controller.
*/
/* ARGSUSED */
int
t1394_fcp_register_controller(t1394_handle_t t1394_hdl, t1394_fcp_evts_t *evts,
uint_t flags)
{
int result;
TNF_PROBE_0_DEBUG(t1394_fcp_register_controller_enter,
S1394_TNF_SL_FCP_STACK, "");
ASSERT(t1394_hdl != NULL);
result = s1394_fcp_register_ctl((s1394_target_t *)t1394_hdl, evts);
TNF_PROBE_0_DEBUG(t1394_fcp_register_controller_exit,
S1394_TNF_SL_FCP_STACK, "");
return (result);
}
/*
* Function: t1394_fcp_unregister_controller()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
*
* Output(s): DDI_SUCCESS Successfully unregistered.
*
* DDI_FAILURE Not unregistered due to failure.
*
* Description: Used to unregister the target within the Framework as an FCP
* controller.
*/
int
t1394_fcp_unregister_controller(t1394_handle_t t1394_hdl)
{
int result;
TNF_PROBE_0_DEBUG(t1394_fcp_unregister_controller_enter,
S1394_TNF_SL_FCP_STACK, "");
ASSERT(t1394_hdl != NULL);
result = s1394_fcp_unregister_ctl((s1394_target_t *)t1394_hdl);
TNF_PROBE_0_DEBUG(t1394_fcp_unregister_controller_exit,
S1394_TNF_SL_FCP_STACK, "");
return (result);
}
/*
* Function: t1394_fcp_register_target()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* evts The structure in which the target
* specifies its callback routines
*
* flags The flags parameter is unused (for now)
*
* Output(s): DDI_SUCCESS Successfully registered.
*
* DDI_FAILURE Not registered due to failure.
*
* Description: Used to register the target within the Framework as an FCP
* target.
*/
/* ARGSUSED */
int
t1394_fcp_register_target(t1394_handle_t t1394_hdl, t1394_fcp_evts_t *evts,
uint_t flags)
{
int result;
TNF_PROBE_0_DEBUG(t1394_fcp_register_target_enter,
S1394_TNF_SL_FCP_STACK, "");
ASSERT(t1394_hdl != NULL);
result = s1394_fcp_register_tgt((s1394_target_t *)t1394_hdl, evts);
TNF_PROBE_0_DEBUG(t1394_fcp_register_target_exit,
S1394_TNF_SL_FCP_STACK, "");
return (result);
}
/*
* Function: t1394_fcp_unregister_target()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
*
* Output(s): DDI_SUCCESS Successfully unregistered.
*
* DDI_FAILURE Not unregistered due to failure.
*
* Description: Used to unregister the target within the Framework as an FCP
* target.
*/
int
t1394_fcp_unregister_target(t1394_handle_t t1394_hdl)
{
int result;
TNF_PROBE_0_DEBUG(t1394_fcp_unregister_target_enter,
S1394_TNF_SL_FCP_STACK, "");
ASSERT(t1394_hdl != NULL);
result = s1394_fcp_unregister_tgt((s1394_target_t *)t1394_hdl);
TNF_PROBE_0_DEBUG(t1394_fcp_unregister_target_exit,
S1394_TNF_SL_FCP_STACK, "");
return (result);
}
/*
* Function: t1394_cmp_register()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* evts The structure in which the target
* specifies its callback routines
*
* Output(s): DDI_SUCCESS Successfully registered.
*
* DDI_FAILURE Not registered due to failure.
*
* Description: Used to register the target within the Framework as a CMP
* device.
*/
/* ARGSUSED */
int
t1394_cmp_register(t1394_handle_t t1394_hdl, t1394_cmp_evts_t *evts,
uint_t flags)
{
int result;
TNF_PROBE_0_DEBUG(t1394_cmp_register_enter, S1394_TNF_SL_CMP_STACK, "");
ASSERT(t1394_hdl != NULL);
result = s1394_cmp_register((s1394_target_t *)t1394_hdl, evts);
TNF_PROBE_0_DEBUG(t1394_cmp_register_exit, S1394_TNF_SL_CMP_STACK, "");
return (result);
}
/*
* Function: t1394_cmp_unregister()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* evts The structure in which the target
* specifies its callback routines
*
* Output(s): DDI_SUCCESS Successfully registered.
*
* DDI_FAILURE Not registered due to failure.
*
* Description: Used to unregister the target within the Framework as a CMP
* device.
*/
int
t1394_cmp_unregister(t1394_handle_t t1394_hdl)
{
int result;
TNF_PROBE_0_DEBUG(t1394_cmp_unregister_enter, S1394_TNF_SL_CMP_STACK,
"");
ASSERT(t1394_hdl != NULL);
result = s1394_cmp_unregister((s1394_target_t *)t1394_hdl);
TNF_PROBE_0_DEBUG(t1394_cmp_unregister_exit, S1394_TNF_SL_CMP_STACK,
"");
return (result);
}
/*
* Function: t1394_cmp_read()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* reg Register type.
* valp Returned register value.
*
* Output(s): DDI_SUCCESS Successfully registered.
*
* DDI_FAILURE Not registered due to failure.
*
* Description: Used to read a CMP register value.
*/
int
t1394_cmp_read(t1394_handle_t t1394_hdl, t1394_cmp_reg_t reg, uint32_t *valp)
{
int result;
TNF_PROBE_0_DEBUG(t1394_cmp_read_enter, S1394_TNF_SL_CMP_STACK, "");
ASSERT(t1394_hdl != NULL);
result = s1394_cmp_read((s1394_target_t *)t1394_hdl, reg, valp);
TNF_PROBE_0_DEBUG(t1394_cmp_read_exit, S1394_TNF_SL_CMP_STACK, "");
return (result);
}
/*
* Function: t1394_cmp_cas()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* reg Register type.
* arg_val Compare argument.
* new_val New register value.
* old_valp Returned original register value.
*
* Output(s): DDI_SUCCESS Successfully registered.
*
* DDI_FAILURE Not registered due to failure.
*
* Description: Used to compare-swap a CMP register value.
*/
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)
{
int result;
TNF_PROBE_0_DEBUG(t1394_cmp_read_enter, S1394_TNF_SL_CMP_STACK, "");
ASSERT(t1394_hdl != NULL);
result = s1394_cmp_cas((s1394_target_t *)t1394_hdl, reg, arg_val,
new_val, old_valp);
TNF_PROBE_0_DEBUG(t1394_cmp_read_exit, S1394_TNF_SL_CMP_STACK, "");
return (result);
}
/*
* Function: t1394_alloc_isoch_single()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* sii The structure used to set up the
* overall characteristics of the
* isochronous stream
* flags The flags parameter is unused (for now)
*
* Output(s): setup_args Contains the channel number that was
* allocated
* t1394_single_hdl This in the isoch "handle" used in
* t1394_free_isoch_single()
* result Used to pass more specific info back
* to target
*
* Description: t1394_alloc_isoch_single() is used to direct the 1394 Software
* Framework to allocate an isochronous channel and bandwidth
* from the Isochronous Resource Manager (IRM). If a bus reset
* occurs, the 1394 Software Framework attempts to reallocate the
* same resources, calling the rsrc_fail_target() callback if
* it is unsuccessful.
*/
/* ARGSUSED */
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)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_new;
t1394_join_isochinfo_t jii;
int ret;
int err;
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_single_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_single_hdl != NULL);
ASSERT(sii != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Check for invalid channel_mask */
if (sii->si_channel_mask == 0) {
TNF_PROBE_1(t1394_alloc_isoch_single_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Invalid channel mask");
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_single_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Check for invalid bandwidth */
if ((sii->si_bandwidth <= IEEE1394_BANDWIDTH_MIN) ||
(sii->si_bandwidth > IEEE1394_BANDWIDTH_MAX)) {
TNF_PROBE_1(t1394_alloc_isoch_single_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Invalid bandwidth requirements");
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_single_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Verify that rsrc_fail_target() callback is non-NULL */
if (sii->rsrc_fail_target == NULL) {
TNF_PROBE_1(t1394_alloc_isoch_single_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Invalid callback specified");
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_single_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/*
* Allocate an Isoch CEC of type S1394_SINGLE
*/
/* Allocate the Isoch CEC structure */
cec_new = kmem_zalloc(sizeof (s1394_isoch_cec_t), KM_SLEEP);
/* Initialize the structure type */
cec_new->cec_type = S1394_SINGLE;
/* Create the mutex and "in_callbacks" cv */
mutex_init(&cec_new->isoch_cec_mutex, NULL, MUTEX_DRIVER,
hal->halinfo.hw_interrupt);
cv_init(&cec_new->in_callbacks_cv, NULL, CV_DRIVER,
hal->halinfo.hw_interrupt);
/* Initialize the Isoch CEC's member list */
cec_new->cec_member_list_head = NULL;
cec_new->cec_member_list_tail = NULL;
/* Initialize the filters */
cec_new->filter_min_speed = sii->si_speed;
cec_new->filter_max_speed = sii->si_speed;
cec_new->filter_current_speed = cec_new->filter_max_speed;
cec_new->filter_channel_mask = sii->si_channel_mask;
cec_new->bandwidth = sii->si_bandwidth;
cec_new->state_transitions = ISOCH_CEC_FREE | ISOCH_CEC_JOIN |
ISOCH_CEC_SETUP;
mutex_enter(&hal->isoch_cec_list_mutex);
/* Insert Isoch CEC into the HAL's list */
s1394_isoch_cec_list_insert(hal, cec_new);
mutex_exit(&hal->isoch_cec_list_mutex);
/*
* Join the newly created Isoch CEC
*/
jii.req_channel_mask = sii->si_channel_mask;
jii.req_max_speed = sii->si_speed;
jii.jii_options = T1394_TALKER;
jii.isoch_cec_evts_arg = sii->single_evt_arg;
/* All events are NULL except rsrc_fail_target() */
jii.isoch_cec_evts.setup_target = NULL;
jii.isoch_cec_evts.start_target = NULL;
jii.isoch_cec_evts.stop_target = NULL;
jii.isoch_cec_evts.stop_target = NULL;
jii.isoch_cec_evts.teardown_target = NULL;
jii.isoch_cec_evts.rsrc_fail_target = sii->rsrc_fail_target;
ret = t1394_join_isoch_cec(t1394_hdl,
(t1394_isoch_cec_handle_t)cec_new, 0, &jii);
if (ret != DDI_SUCCESS) {
TNF_PROBE_1(t1394_alloc_isoch_single_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unexpected error from t1394_join_isoch_cec()");
ret = t1394_free_isoch_cec(t1394_hdl, flags,
(t1394_isoch_cec_handle_t *)&cec_new);
if (ret != DDI_SUCCESS) {
/* Unable to free the Isoch CEC */
TNF_PROBE_1(t1394_alloc_isoch_single_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unexpected error from t1394_free_isoch_cec()");
ASSERT(0);
}
/* Handle is nulled out before returning */
*t1394_single_hdl = NULL;
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_single_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/*
* Setup the isoch resources, etc.
*/
ret = t1394_setup_isoch_cec(t1394_hdl,
(t1394_isoch_cec_handle_t)cec_new, 0, &err);
if (ret != DDI_SUCCESS) {
TNF_PROBE_1(t1394_alloc_isoch_single_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unexpected error from t1394_setup_isoch_cec()");
*result = err;
/* Leave the Isoch CEC */
ret = t1394_leave_isoch_cec(t1394_hdl,
(t1394_isoch_cec_handle_t)cec_new, 0);
if (ret != DDI_SUCCESS) {
/* Unable to leave the Isoch CEC */
TNF_PROBE_1(t1394_alloc_isoch_single_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unexpected error from t1394_leave_isoch_cec()");
ASSERT(0);
}
/* Free up the Isoch CEC */
ret = t1394_free_isoch_cec(t1394_hdl, flags,
(t1394_isoch_cec_handle_t *)&cec_new);
if (ret != DDI_SUCCESS) {
/* Unable to free the Isoch CEC */
TNF_PROBE_1(t1394_alloc_isoch_single_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unexpected error from t1394_free_isoch_cec()");
ASSERT(0);
}
/* Handle is nulled out before returning */
*t1394_single_hdl = NULL;
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_single_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Return the setup_args - channel num and speed */
mutex_enter(&cec_new->isoch_cec_mutex);
output_args->channel_num = cec_new->realloc_chnl_num;
mutex_exit(&cec_new->isoch_cec_mutex);
/* Update the handle */
*t1394_single_hdl = (t1394_isoch_single_handle_t)cec_new;
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_single_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_free_isoch_single()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* t1394_single_hdl The isoch "handle" return by
* t1394_alloc_isoch_single()
* flags The flags parameter is unused (for now)
*
* Output(s): None
*
* Description: t1394_free_isoch_single() frees the isochronous resources
* and the handle that were allocated during the call to
* t1394_alloc_isoch_single().
*/
/* ARGSUSED */
void
t1394_free_isoch_single(t1394_handle_t t1394_hdl,
t1394_isoch_single_handle_t *t1394_single_hdl, uint_t flags)
{
s1394_isoch_cec_t *cec_curr;
int ret;
TNF_PROBE_0_DEBUG(t1394_free_isoch_single_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_single_hdl != NULL);
/* Convert the handle to an Isoch CEC pointer */
cec_curr = (s1394_isoch_cec_t *)(*t1394_single_hdl);
/*
* Teardown the isoch resources, etc.
*/
ret = t1394_teardown_isoch_cec(t1394_hdl,
(t1394_isoch_cec_handle_t)cec_curr, 0);
if (ret != DDI_SUCCESS) {
/* Unable to teardown the Isoch CEC */
TNF_PROBE_1(t1394_free_isoch_single_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unexpected error from t1394_teardown_isoch_cec()");
ASSERT(0);
}
/*
* Leave the Isoch CEC
*/
ret = t1394_leave_isoch_cec(t1394_hdl,
(t1394_isoch_cec_handle_t)cec_curr, 0);
if (ret != DDI_SUCCESS) {
/* Unable to leave the Isoch CEC */
TNF_PROBE_1(t1394_free_isoch_single_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unexpected error from t1394_leave_isoch_cec()");
ASSERT(0);
}
/*
* Free the Isoch CEC
*/
ret = t1394_free_isoch_cec(t1394_hdl, flags,
(t1394_isoch_cec_handle_t *)&cec_curr);
if (ret != DDI_SUCCESS) {
/* Unable to free the Isoch CEC */
TNF_PROBE_1(t1394_free_isoch_single_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unexpected error from t1394_free_isoch_cec()");
ASSERT(0);
}
/* Handle is nulled out before returning */
*t1394_single_hdl = NULL;
TNF_PROBE_0_DEBUG(t1394_free_isoch_single_exit,
S1394_TNF_SL_ISOCH_STACK, "");
}
/*
* Function: t1394_alloc_isoch_cec()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* props The structure used to set up the
* overall characteristics of for
* the Isoch CEC.
* flags The flags parameter is unused (for now)
*
* Output(s): t1394_isoch_cec_hdl The Isoch CEC "handle" used in all
* subsequent isoch_cec() calls
*
* Description: t1394_alloc_isoch_cec() allocates and initializes an
* isochronous channel event coordinator (Isoch CEC) for use
* in managing and coordinating activity for an isoch channel
*/
/* ARGSUSED */
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)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_new;
uint64_t temp;
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_cec_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
ASSERT(props != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Check for invalid channel_mask */
if (props->cec_channel_mask == 0) {
TNF_PROBE_1(t1394_alloc_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Invalid channel mask");
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Test conditions specific to T1394_NO_IRM_ALLOC */
temp = props->cec_channel_mask;
if (props->cec_options & T1394_NO_IRM_ALLOC) {
/* If T1394_NO_IRM_ALLOC, then only one bit should be set */
if ((temp & (temp - 1)) != 0) {
TNF_PROBE_1(t1394_alloc_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Invalid channel mask");
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* If T1394_NO_IRM_ALLOC, then speeds should be equal */
if (props->cec_min_speed != props->cec_max_speed) {
TNF_PROBE_1(t1394_alloc_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Invalid speeds (min != max)");
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
}
/* Check for invalid bandwidth */
if ((props->cec_bandwidth <= IEEE1394_BANDWIDTH_MIN) ||
(props->cec_bandwidth > IEEE1394_BANDWIDTH_MAX)) {
TNF_PROBE_1(t1394_alloc_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Invalid bandwidth requirements");
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Allocate the Isoch CEC structure */
cec_new = kmem_zalloc(sizeof (s1394_isoch_cec_t), KM_SLEEP);
/* Initialize the structure type */
cec_new->cec_type = S1394_PEER_TO_PEER;
/* Create the mutex and "in_callbacks" cv */
mutex_init(&cec_new->isoch_cec_mutex, NULL, MUTEX_DRIVER,
hal->halinfo.hw_interrupt);
cv_init(&cec_new->in_callbacks_cv, NULL, CV_DRIVER,
hal->halinfo.hw_interrupt);
/* Initialize the Isoch CEC's member list */
cec_new->cec_member_list_head = NULL;
cec_new->cec_member_list_tail = NULL;
/* Initialize the filters */
cec_new->filter_min_speed = props->cec_min_speed;
cec_new->filter_max_speed = props->cec_max_speed;
cec_new->filter_current_speed = cec_new->filter_max_speed;
cec_new->filter_channel_mask = props->cec_channel_mask;
cec_new->bandwidth = props->cec_bandwidth;
cec_new->cec_options = props->cec_options;
cec_new->state_transitions = ISOCH_CEC_FREE | ISOCH_CEC_JOIN |
ISOCH_CEC_SETUP;
mutex_enter(&hal->isoch_cec_list_mutex);
/* Insert Isoch CEC into the HAL's list */
s1394_isoch_cec_list_insert(hal, cec_new);
mutex_exit(&hal->isoch_cec_list_mutex);
/* Update the handle and return */
*t1394_isoch_cec_hdl = (t1394_isoch_cec_handle_t)cec_new;
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_free_isoch_cec()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* flags The flags parameter is unused (for now)
* t1394_isoch_cec_hdl The Isoch CEC "handle" returned by
* t1394_alloc_isoch_cec()
*
* Output(s): DDI_SUCCESS Target successfully freed the Isoch CEC
* DDI_FAILURE Target failed to free the Isoch CEC
*
* Description: t1394_free_isoch_cec() attempts to free the Isoch CEC
* structure. It will fail (DDI_FAILURE) if there are any
* remaining members who have not yet left.
*/
/* ARGSUSED */
int
t1394_free_isoch_cec(t1394_handle_t t1394_hdl, uint_t flags,
t1394_isoch_cec_handle_t *t1394_isoch_cec_hdl)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_curr;
TNF_PROBE_0_DEBUG(t1394_free_isoch_cec_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Convert the handle to an Isoch CEC pointer */
cec_curr = (s1394_isoch_cec_t *)(*t1394_isoch_cec_hdl);
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* Are we in any callbacks? */
if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_free_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to free Isoch CEC (in callbacks)");
TNF_PROBE_0_DEBUG(t1394_free_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Is "free" a legal state transition? */
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_FREE) == 0) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_free_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to free Isoch CEC");
TNF_PROBE_0_DEBUG(t1394_free_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
mutex_exit(&cec_curr->isoch_cec_mutex);
mutex_enter(&hal->isoch_cec_list_mutex);
/* Remove Isoch CEC from HAL's list */
s1394_isoch_cec_list_remove(hal, cec_curr);
mutex_exit(&hal->isoch_cec_list_mutex);
/* Destroy the Isoch CEC's mutex and cv */
cv_destroy(&cec_curr->in_callbacks_cv);
mutex_destroy(&cec_curr->isoch_cec_mutex);
/* Free up the memory for the Isoch CEC struct */
kmem_free(cec_curr, sizeof (s1394_isoch_cec_t));
/* Update the handle and return */
*t1394_isoch_cec_hdl = NULL;
TNF_PROBE_0_DEBUG(t1394_free_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_join_isoch_cec()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* t1394_isoch_cec_hdl The Isoch CEC "handle" returned by
* t1394_alloc_isoch_cec()
* flags The flags parameter is unused (for now)
* join_isoch_info This structure provides infomation
* about a target that wishes to join
* the given Isoch CEC. It gives
* max_speed, channel_mask, etc.
*
* Output(s): DDI_SUCCESS Target successfully joined the
* Isoch CEC
* DDI_FAILURE Target failed to join the Isoch CEC
*
* Description: t1394_join_isoch_cec() determines, based on the information
* given in the join_isoch_info structure, if the target may
* join the Isoch CEC. If it is determined that the target may
* join, the specified callback routines are stored away for
* later use in the coordination tasks.
*/
/* ARGSUSED */
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)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_new;
uint64_t check_mask;
uint_t curr_max_speed;
TNF_PROBE_0_DEBUG(t1394_join_isoch_cec_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Convert the handle to an Isoch CEC pointer */
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
/* Allocate a new Isoch CEC member structure */
member_new = kmem_zalloc(sizeof (s1394_isoch_cec_member_t), KM_SLEEP);
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* Are we in any callbacks? (Wait for them to finish) */
while (CEC_IN_ANY_CALLBACKS(cec_curr)) {
cec_curr->cec_want_wakeup = B_TRUE;
cv_wait(&cec_curr->in_callbacks_cv,
&cec_curr->isoch_cec_mutex);
}
/* Is "join" a legal state transition? */
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_JOIN) == 0) {
kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_join_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to join Isoch CEC");
TNF_PROBE_0_DEBUG(t1394_join_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Check the channel mask for consistency */
check_mask = join_isoch_info->req_channel_mask &
cec_curr->filter_channel_mask;
if (check_mask == 0) {
kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_join_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Inconsistent channel mask specified");
TNF_PROBE_0_DEBUG(t1394_join_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Check for consistent speeds */
if (join_isoch_info->req_max_speed < cec_curr->filter_min_speed) {
kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_join_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Inconsistent speed specified");
TNF_PROBE_0_DEBUG(t1394_join_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
} else if (join_isoch_info->req_max_speed <
cec_curr->filter_current_speed) {
curr_max_speed = join_isoch_info->req_max_speed;
} else {
curr_max_speed = cec_curr->filter_current_speed;
}
/* Check for no more than one talker */
if ((join_isoch_info->jii_options & T1394_TALKER) &&
(cec_curr->cec_member_talker != NULL)) {
kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_join_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Multiple talkers specified");
TNF_PROBE_0_DEBUG(t1394_join_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Verify that all callbacks are non-NULL (for PEER_TO_PEER) */
if ((cec_curr->cec_type == S1394_PEER_TO_PEER) &&
((join_isoch_info->isoch_cec_evts.setup_target == NULL) ||
(join_isoch_info->isoch_cec_evts.start_target == NULL) ||
(join_isoch_info->isoch_cec_evts.stop_target == NULL) ||
(join_isoch_info->isoch_cec_evts.rsrc_fail_target == NULL) ||
(join_isoch_info->isoch_cec_evts.teardown_target == NULL))) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_join_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Invalid callbacks specified");
TNF_PROBE_0_DEBUG(t1394_join_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Copy the events information into the struct */
member_new->isoch_cec_evts = join_isoch_info->isoch_cec_evts;
member_new->isoch_cec_evts_arg = join_isoch_info->isoch_cec_evts_arg;
member_new->cec_mem_options = join_isoch_info->jii_options;
member_new->cec_mem_target = (s1394_target_t *)t1394_hdl;
/* Insert new member into Isoch CEC's member list */
s1394_isoch_cec_member_list_insert(hal, cec_curr, member_new);
/* Update the channel mask filter */
cec_curr->filter_channel_mask = check_mask;
/* Update the speed filter */
cec_curr->filter_current_speed = curr_max_speed;
/* Update the talker pointer (if necessary) */
if (join_isoch_info->jii_options & T1394_TALKER)
cec_curr->cec_member_talker = cec_curr->cec_member_list_head;
/*
* Now "leave" is a legal state transition
* and "free" is an illegal state transition
*/
CEC_SET_LEGAL(cec_curr, ISOCH_CEC_LEAVE);
CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_FREE);
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_0_DEBUG(t1394_join_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_leave_isoch_cec()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* t1394_isoch_cec_hdl The Isoch CEC "handle" returned by
* t1394_alloc_isoch_cec()
* flags The flags parameter is unused (for now)
*
* Output(s): DDI_SUCCESS Target successfully left the
* Isoch CEC
* DDI_FAILURE Target failed to leave the Isoch CEC
*
* Description: t1394_leave_isoch_cec() is used by a target driver to remove
* itself from the Isoch CEC's member list. It is possible
* for this call to fail because the target is not found in
* the current member list, or because it is not an appropriate
* time for a target to leave.
*/
/* ARGSUSED */
int
t1394_leave_isoch_cec(t1394_handle_t t1394_hdl,
t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_curr;
s1394_isoch_cec_member_t *member_temp;
boolean_t found;
uint64_t temp_channel_mask;
uint_t temp_max_speed;
TNF_PROBE_0_DEBUG(t1394_leave_isoch_cec_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Convert the handle to an Isoch CEC pointer */
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* Are we in any callbacks? (Wait for them to finish) */
while (CEC_IN_ANY_CALLBACKS(cec_curr)) {
cec_curr->cec_want_wakeup = B_TRUE;
cv_wait(&cec_curr->in_callbacks_cv,
&cec_curr->isoch_cec_mutex);
}
/* Is "leave" a legal state transition? */
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_LEAVE) == 0) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_leave_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to leave Isoch CEC");
TNF_PROBE_0_DEBUG(t1394_leave_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Find the Target on the CEC's member list */
found = B_FALSE;
temp_channel_mask = cec_curr->cec_alloc_props.cec_channel_mask;
temp_max_speed = cec_curr->cec_alloc_props.cec_max_speed;
member_curr = cec_curr->cec_member_list_head;
while (member_curr != NULL) {
if (member_curr->cec_mem_target ==
(s1394_target_t *)t1394_hdl) {
member_temp = member_curr;
found = B_TRUE;
} else {
/* Keep track of channel mask and max speed info */
temp_channel_mask &= member_curr->req_channel_mask;
if (member_curr->req_max_speed < temp_max_speed)
temp_max_speed = member_curr->req_max_speed;
}
member_curr = member_curr->cec_mem_next;
}
/* Target not found on this Isoch CEC */
if (found == B_FALSE) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_leave_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Target not found in Isoch CEC member list");
TNF_PROBE_0_DEBUG(t1394_leave_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
} else {
/* This member's departure may change filter constraints */
cec_curr->filter_current_speed = temp_max_speed;
cec_curr->filter_channel_mask = temp_channel_mask;
}
/* Remove member from Isoch CEC's member list */
s1394_isoch_cec_member_list_remove(hal, cec_curr, member_temp);
/* If we are removing the talker, then update the pointer */
if (cec_curr->cec_member_talker == member_temp)
cec_curr->cec_member_talker = NULL;
/* Is the Isoch CEC's member list empty? */
if ((cec_curr->cec_member_list_head == NULL) &&
(cec_curr->cec_member_list_tail == NULL)) {
/*
* Now "free" _might_ be a legal state transition
* if we aren't in setup or start phases and "leave"
* is definitely an illegal state transition
*/
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_JOIN) != 0)
CEC_SET_LEGAL(cec_curr, ISOCH_CEC_FREE);
CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_LEAVE);
}
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
/* Free the Isoch CEC member structure */
kmem_free(member_temp, sizeof (s1394_isoch_cec_member_t));
TNF_PROBE_0_DEBUG(t1394_leave_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_setup_isoch_cec()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* t1394_isoch_cec_hdl The Isoch CEC "handle" returned by
* t1394_alloc_isoch_cec()
* flags The flags parameter is unused (for now)
*
* Output(s): result Used to pass more specific info back
* to target
*
* Description: t1394_setup_isoch_cec() directs the 1394 Software Framework
* to allocate isochronous resources and invoke the setup_target()
* callback for each member of the Isoch CEC. This call may
* fail because bandwidth was unavailable (T1394_ENO_BANDWIDTH),
* channels were unavailable (T1394_ENO_CHANNEL), or one of the
* member targets returned failure from its setup_target()
* callback.
*/
/* ARGSUSED */
int
t1394_setup_isoch_cec(t1394_handle_t t1394_hdl,
t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags, int *result)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_curr;
t1394_setup_target_args_t target_args;
uint64_t temp_chnl_mask;
uint32_t old_chnl;
uint32_t try_chnl;
uint_t bw_alloc_units;
uint_t generation;
int chnl_num;
int err;
int ret;
int j;
int (*setup_callback)(t1394_isoch_cec_handle_t, opaque_t,
t1394_setup_target_args_t *);
TNF_PROBE_0_DEBUG(t1394_setup_isoch_cec_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Convert the handle to an Isoch CEC pointer */
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* Are we in any callbacks? */
if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_setup_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to setup Isoch CEC (in callbacks)");
TNF_PROBE_0_DEBUG(t1394_setup_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Is "setup" a legal state transition? */
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_SETUP) == 0) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_setup_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to setup Isoch CEC");
TNF_PROBE_0_DEBUG(t1394_setup_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* If T1394_NO_IRM_ALLOC is set then don't allocate... do callbacks */
if (cec_curr->cec_options & T1394_NO_IRM_ALLOC) {
goto setup_do_callbacks;
}
/* Allocate bandwidth and channels */
for (j = 0; j < S1394_ISOCH_ALLOC_RETRIES; j++) {
/*
* Get the current generation number - don't
* need the lock because we are read only here
*/
generation = hal->generation_count;
/* Compute how much bandwidth is needed */
bw_alloc_units = s1394_compute_bw_alloc_units(hal,
cec_curr->bandwidth, cec_curr->filter_current_speed);
/* Check that the generation has not changed - */
/* don't need the lock (read only) */
if (generation != hal->generation_count)
continue;
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
/* Try to allocate the bandwidth */
ret = s1394_bandwidth_alloc(hal, bw_alloc_units, generation,
&err);
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* If there was a bus reset, start over */
if (ret == DDI_FAILURE) {
if (err == CMD1394_EBUSRESET) {
continue; /* start over and try again */
} else {
*result = T1394_ENO_BANDWIDTH;
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_setup_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string,
msg, "Unable to allocate isoch bandwidth");
TNF_PROBE_0_DEBUG(t1394_setup_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
}
/* Check that the generation has not changed - */
/* don't need the lock (read only) */
if (generation != hal->generation_count)
continue;
/*
* Allocate a channel
* From IEEE 1394-1995, Section 8.3.2.3.8: "Bits
* allocated in the CHANNELS_AVAILABLE_HI field of
* this register shall start at bit zero (channel
* number zero), and additional channel numbers shall
* be represented in a monotonically increasing sequence
* of bit numbers up to a maximum of bit 31 (channel
* number 31). Bits allocated in the CHANNELS_AVAILABLE_LO
* field of this register shall start at bit zero
* (channel number 32), and additional channel numbers
* shall be represented in a monotonically increasing
* sequence of bit numbers up to a maximum of bit 31
* (channel number 63).
*/
temp_chnl_mask = cec_curr->filter_channel_mask;
for (chnl_num = 63; chnl_num >= 0; chnl_num--) {
if ((temp_chnl_mask & 1) == 1) {
try_chnl = (1 << ((63 - chnl_num) % 32));
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
if (chnl_num < 32) {
ret = s1394_channel_alloc(hal,
try_chnl, generation,
S1394_CHANNEL_ALLOC_HI, &old_chnl,
&err);
} else {
ret = s1394_channel_alloc(hal,
try_chnl, generation,
S1394_CHANNEL_ALLOC_LO, &old_chnl,
&err);
}
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* Did we get a channel? (or a bus reset) */
if ((ret == DDI_SUCCESS) ||
(err == CMD1394_EBUSRESET))
break;
}
temp_chnl_mask = temp_chnl_mask >> 1;
}
/* If we've tried all the possible channels, then fail */
if (chnl_num == 0) {
*result = T1394_ENO_CHANNEL;
/*
* If we successfully allocate bandwidth, and
* then fail getting a channel, we need to
* free up the bandwidth
*/
/* Check that the generation has not changed */
/* lock not needed here (read only) */
if (generation != hal->generation_count)
continue;
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
/* Try to free up the bandwidth */
ret = s1394_bandwidth_free(hal, bw_alloc_units,
generation, &err);
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
if (ret == DDI_FAILURE) {
if (err == CMD1394_EBUSRESET) {
continue;
} else {
TNF_PROBE_1(t1394_setup_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "",
tnf_string, msg,
"Unable to free isoch bandwidth");
}
}
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_setup_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unable to allocate isoch channel");
TNF_PROBE_0_DEBUG(t1394_setup_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* If we got a channel, we're done (else start over) */
if (ret == DDI_SUCCESS)
break;
else if (err == CMD1394_EBUSRESET)
continue;
}
/* Have we gotten too many bus resets? */
if (j == S1394_ISOCH_ALLOC_RETRIES) {
*result = T1394_ENO_BANDWIDTH;
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_setup_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unable to allocate isoch channel");
TNF_PROBE_0_DEBUG(t1394_setup_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
cec_curr->realloc_valid = B_TRUE;
cec_curr->realloc_chnl_num = chnl_num;
cec_curr->realloc_bandwidth = cec_curr->bandwidth;
cec_curr->realloc_speed = cec_curr->filter_current_speed;
setup_do_callbacks:
/* Call all of the setup_target() callbacks */
target_args.channel_num = chnl_num;
target_args.channel_speed = cec_curr->filter_current_speed;
/* Now we are going into the callbacks */
cec_curr->in_callbacks = B_TRUE;
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
member_curr = cec_curr->cec_member_list_head;
*result = 0;
while (member_curr != NULL) {
if (member_curr->isoch_cec_evts.setup_target != NULL) {
setup_callback =
member_curr->isoch_cec_evts.setup_target;
ret = setup_callback(t1394_isoch_cec_hdl,
member_curr->isoch_cec_evts_arg, &target_args);
if (ret != DDI_SUCCESS)
*result = T1394_ETARGET;
}
member_curr = member_curr->cec_mem_next;
}
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* We are finished with the callbacks */
cec_curr->in_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);
}
/*
* Now "start" and "teardown" are legal state transitions
* and "join", "free", and "setup" are illegal state transitions
*/
CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_JOIN | ISOCH_CEC_FREE |
ISOCH_CEC_SETUP));
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
/* Return DDI_FAILURE if any targets failed setup */
if (*result != 0) {
TNF_PROBE_1(t1394_setup_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Target returned error in setup_target()");
TNF_PROBE_0_DEBUG(t1394_setup_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
TNF_PROBE_0_DEBUG(t1394_setup_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_start_isoch_cec()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* t1394_isoch_cec_hdl The Isoch CEC "handle" returned by
* t1394_alloc_isoch_cec()
* flags The flags parameter is unused (for now)
*
* Output(s): DDI_SUCCESS All start_target() callbacks returned
* successfully
* DDI_FAILURE One or more start_target() callbacks
* returned failure
*
* Description: t1394_start_isoch_cec() directs the 1394 Software Framework
* to invoke each of the start_target() callbacks, first for
* each listener, then for the talker.
*/
/* ARGSUSED */
int
t1394_start_isoch_cec(t1394_handle_t t1394_hdl,
t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_curr;
int ret;
boolean_t err;
int (*start_callback)(t1394_isoch_cec_handle_t, opaque_t);
TNF_PROBE_0_DEBUG(t1394_start_isoch_cec_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
/* Convert the handle to an Isoch CEC pointer */
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* Are we in any callbacks? */
if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_start_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to start Isoch CEC (in callbacks)");
TNF_PROBE_0_DEBUG(t1394_start_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Is "start" a legal state transition? */
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_START) == 0) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_start_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to start Isoch CEC");
TNF_PROBE_0_DEBUG(t1394_start_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Now we are going into the callbacks */
cec_curr->in_callbacks = B_TRUE;
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
/*
* Call all of the start_target() callbacks
* Start at the tail (listeners first) and
* go toward the head (talker last)
*/
member_curr = cec_curr->cec_member_list_tail;
err = B_FALSE;
while (member_curr != NULL) {
if (member_curr->isoch_cec_evts.start_target != NULL) {
start_callback =
member_curr->isoch_cec_evts.start_target;
ret = start_callback(t1394_isoch_cec_hdl,
member_curr->isoch_cec_evts_arg);
if (ret != DDI_SUCCESS)
err = B_TRUE;
}
member_curr = member_curr->cec_mem_prev;
}
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* We are finished with the callbacks */
cec_curr->in_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);
}
/*
* Now "stop" is a legal state transitions
* and "start" and "teardown" are illegal state transitions
*/
CEC_SET_LEGAL(cec_curr, ISOCH_CEC_STOP);
CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
/* Return DDI_FAILURE if any targets failed start */
if (err == B_TRUE) {
TNF_PROBE_1(t1394_start_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Target returned error in start_target()");
TNF_PROBE_0_DEBUG(t1394_start_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
TNF_PROBE_0_DEBUG(t1394_start_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_stop_isoch_cec()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* t1394_isoch_cec_hdl The Isoch CEC "handle" returned by
* t1394_alloc_isoch_cec()
* flags The flags parameter is unused (for now)
*
* Output(s): DDI_SUCCESS Target successfully stopped the
* Isoch CEC
* DDI_FAILURE Target failed to stop the Isoch CEC
*
* Description: t1394_stop_isoch_cec() directs the 1394 Software Framework
* to invoke each of the stop_target() callbacks, first for
* the talker, then for each listener.
* (This call will fail if it is called at an
* inappropriate time, i.e. before the t1394_start_isoch_cec()
* call, etc.)
*/
/* ARGSUSED */
int
t1394_stop_isoch_cec(t1394_handle_t t1394_hdl,
t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_curr;
void (*stop_callback)(t1394_isoch_cec_handle_t, opaque_t);
TNF_PROBE_0_DEBUG(t1394_stop_isoch_cec_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
/* Convert the handle to an Isoch CEC pointer */
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* Are we in any callbacks? */
if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_stop_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to stop Isoch CEC (in callbacks)");
TNF_PROBE_0_DEBUG(t1394_stop_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Is "stop" a legal state transition? */
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_STOP) == 0) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_stop_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to stop Isoch CEC");
TNF_PROBE_0_DEBUG(t1394_stop_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Now we are going into the callbacks */
cec_curr->in_callbacks = B_TRUE;
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
/*
* Call all of the stop_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) {
if (member_curr->isoch_cec_evts.stop_target != NULL) {
stop_callback =
member_curr->isoch_cec_evts.stop_target;
stop_callback(t1394_isoch_cec_hdl,
member_curr->isoch_cec_evts_arg);
}
member_curr = member_curr->cec_mem_next;
}
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* We are finished with the callbacks */
cec_curr->in_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);
}
/*
* Now "start" and "teardown" are legal state transitions
* and "stop" is an illegal state transitions
*/
CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_STOP);
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_0_DEBUG(t1394_stop_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_teardown_isoch_cec()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* t1394_isoch_cec_hdl The Isoch CEC "handle" returned by
* t1394_alloc_isoch_cec()
* flags The flags parameter is unused (for now)
*
* Output(s): DDI_SUCCESS Target successfully tore down the
* Isoch CEC
* DDI_FAILURE Target failed to tear down the
* Isoch CEC
*
* Description: t1394_teardown_isoch_cec() directs the 1394 Software Framework
* to free up any isochronous resources we might be holding and
* call all of the teardown_target() callbacks.
* (This call will fail if it is called at an
* inappropriate time, i.e. before the t1394_start_isoch_cec()
* call, before the t1394_stop_isoch_cec, etc.
*/
/* ARGSUSED */
int
t1394_teardown_isoch_cec(t1394_handle_t t1394_hdl,
t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
s1394_hal_t *hal;
s1394_isoch_cec_t *cec_curr;
s1394_isoch_cec_member_t *member_curr;
uint32_t chnl_mask;
uint32_t old_chnl_mask;
uint_t bw_alloc_units;
uint_t generation;
int ret;
int err;
void (*teardown_callback)(t1394_isoch_cec_handle_t, opaque_t);
TNF_PROBE_0_DEBUG(t1394_teardown_isoch_cec_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_isoch_cec_hdl != NULL);
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Convert the handle to an Isoch CEC pointer */
cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* Are we in any callbacks? */
if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_teardown_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to teardown Isoch CEC (in callbacks)");
TNF_PROBE_0_DEBUG(t1394_teardown_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* Is "teardown" a legal state transition? */
if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_TEARDOWN) == 0) {
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_1(t1394_teardown_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Not allowed to teardown Isoch CEC");
TNF_PROBE_0_DEBUG(t1394_teardown_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_FAILURE);
}
/* If T1394_NO_IRM_ALLOC is set then don't free... do callbacks */
if (cec_curr->cec_options & T1394_NO_IRM_ALLOC) {
goto teardown_do_callbacks;
}
/* If nothing has been allocated or we failed to */
/* reallocate, then we are done... call the callbacks */
if ((cec_curr->realloc_valid == B_FALSE) ||
(cec_curr->realloc_failed == B_TRUE)) {
goto teardown_do_callbacks;
}
/*
* 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;
/* Compute the amount bandwidth to free */
bw_alloc_units = s1394_compute_bw_alloc_units(hal,
cec_curr->bandwidth, cec_curr->realloc_speed);
/* Check that the generation has not changed - */
/* don't need the lock (read only) */
if (generation != hal->generation_count)
goto teardown_do_callbacks;
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
/* Try to free up the bandwidth */
ret = s1394_bandwidth_free(hal, bw_alloc_units, generation, &err);
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
if (ret == DDI_FAILURE) {
if (err == CMD1394_EBUSRESET) {
goto teardown_do_callbacks;
} else {
TNF_PROBE_1(t1394_teardown_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unable to free allocated bandwidth");
}
}
/* Free the allocated channel */
chnl_mask = (1 << ((63 - cec_curr->realloc_chnl_num) % 32));
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
if (cec_curr->realloc_chnl_num < 32) {
ret = s1394_channel_free(hal, chnl_mask, generation,
S1394_CHANNEL_ALLOC_HI, &old_chnl_mask, &err);
} else {
ret = s1394_channel_free(hal, chnl_mask, generation,
S1394_CHANNEL_ALLOC_LO, &old_chnl_mask, &err);
}
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
if (ret == DDI_FAILURE) {
if (err == CMD1394_EBUSRESET) {
goto teardown_do_callbacks;
} else {
TNF_PROBE_1(t1394_teardown_isoch_cec_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"Unable to free allocated bandwidth");
}
}
teardown_do_callbacks:
/* From here on reallocation is unnecessary */
cec_curr->realloc_valid = B_FALSE;
cec_curr->realloc_chnl_num = 0;
cec_curr->realloc_bandwidth = 0;
/* Now we are going into the callbacks */
cec_curr->in_callbacks = B_TRUE;
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
/* Call all of the teardown_target() callbacks */
member_curr = cec_curr->cec_member_list_head;
while (member_curr != NULL) {
if (member_curr->isoch_cec_evts.teardown_target != NULL) {
teardown_callback =
member_curr->isoch_cec_evts.teardown_target;
teardown_callback(t1394_isoch_cec_hdl,
member_curr->isoch_cec_evts_arg);
}
member_curr = member_curr->cec_mem_next;
}
/* Lock the Isoch CEC member list */
mutex_enter(&cec_curr->isoch_cec_mutex);
/* We are finished with the callbacks */
cec_curr->in_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);
}
/*
* Now "join" and "setup" are legal state transitions
* and "start" and "teardown" are illegal state transitions
*/
CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_JOIN | ISOCH_CEC_SETUP));
CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
/* And if the member list is empty, then "free" is legal too */
if ((cec_curr->cec_member_list_head == NULL) &&
(cec_curr->cec_member_list_tail == NULL)) {
CEC_SET_LEGAL(cec_curr, ISOCH_CEC_FREE);
}
/* Unlock the Isoch CEC member list */
mutex_exit(&cec_curr->isoch_cec_mutex);
TNF_PROBE_0_DEBUG(t1394_teardown_isoch_cec_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (DDI_SUCCESS);
}
/*
* Function: t1394_alloc_isoch_dma()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* idi This structure contains information
* for configuring the data flow for
* isochronous DMA
* flags The flags parameter is unused (for now)
*
* Output(s): t1394_idma_hdl The IDMA "handle" used in all
* subsequent isoch_dma() calls
* result Used to pass more specific info back
* to target
*
* Description: t1394_alloc_isoch_dma() allocates and initializes an
* isochronous DMA resource for transmitting or receiving
* isochronous data. If it fails, result may hold
* T1394_EIDMA_NO_RESRCS, indicating that no isoch DMA resource
* are available.
*/
/* ARGSUSED */
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)
{
s1394_hal_t *hal;
int ret;
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_dma_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(idi != NULL);
ASSERT(t1394_idma_hdl != NULL);
/* Find the HAL this target resides on */
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Sanity check dma options. If talk enabled, listen should be off */
if ((idi->idma_options & ID1394_TALK) &&
(idi->idma_options != ID1394_TALK)) {
TNF_PROBE_1(t1394_alloc_isoch_dma_talk_conflict_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"conflicting idma options; talker and listener");
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_dma_exit,
S1394_TNF_SL_ISOCH_STACK, "");
*result = T1394_EIDMA_CONFLICT;
return (DDI_FAILURE);
}
/* Only one listen mode allowed */
if ((idi->idma_options & ID1394_LISTEN_PKT_MODE) &&
(idi->idma_options & ID1394_LISTEN_BUF_MODE)) {
TNF_PROBE_1(t1394_alloc_isoch_dma_listen_conflict_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"conflicting idma options; both listener modes set");
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_dma_exit,
S1394_TNF_SL_ISOCH_STACK, "");
*result = T1394_EIDMA_CONFLICT;
return (DDI_FAILURE);
}
/* Have HAL alloc a resource and compile ixl */
ret = HAL_CALL(hal).alloc_isoch_dma(hal->halinfo.hal_private, idi,
(void **)t1394_idma_hdl, result);
if (ret != DDI_SUCCESS) {
TNF_PROBE_1(t1394_alloc_isoch_dma_hal_error,
S1394_TNF_SL_ISOCH_ERROR, "", tnf_string, msg,
"HAL alloc_isoch_dma error, maybe IXL compilation");
if (*result == IXL1394_ENO_DMA_RESRCS) {
*result = T1394_EIDMA_NO_RESRCS;
}
}
TNF_PROBE_0_DEBUG(t1394_alloc_isoch_dma_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (ret);
}
/*
* Function: t1394_free_isoch_dma()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* flags The flags parameter is unused (for now)
* t1394_idma_hdl The IDMA "handle" returned by
* t1394_alloc_isoch_dma()
*
* Output(s): None
*
* Description: t1394_free_isoch_dma() is used to free all DMA resources
* allocated for the isoch stream associated with t1394_idma_hdl.
*/
/* ARGSUSED */
void
t1394_free_isoch_dma(t1394_handle_t t1394_hdl, uint_t flags,
t1394_isoch_dma_handle_t *t1394_idma_hdl)
{
s1394_hal_t *hal;
TNF_PROBE_0_DEBUG(t1394_free_isoch_dma_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(*t1394_idma_hdl != NULL);
/* Find the HAL this target resides on */
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Tell HAL to release local isoch dma resources */
HAL_CALL(hal).free_isoch_dma(hal->halinfo.hal_private, *t1394_idma_hdl);
/* Null out isoch handle */
*t1394_idma_hdl = NULL;
TNF_PROBE_0_DEBUG(t1394_free_isoch_dma_exit,
S1394_TNF_SL_ISOCH_STACK, "");
}
/*
* Function: t1394_start_isoch_dma()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* t1394_idma_hdl The IDMA "handle" returned by
* t1394_alloc_isoch_dma()
* idma_ctrlinfo This structure contains control args
* used when starting isoch DMA for
* the allocated resource
* flags One flag defined - ID1394_START_ON_CYCLE
*
* Output(s): result Used to pass more specific info back
* to target
*
* Description: t1394_start_isoch_dma() is used to start DMA for the isoch
* stream associated with t1394_idma_hdl.
*/
/* ARGSUSED */
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)
{
s1394_hal_t *hal;
int ret;
TNF_PROBE_0_DEBUG(t1394_start_isoch_dma_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_idma_hdl != NULL);
ASSERT(idma_ctrlinfo != NULL);
/* Find the HAL this target resides on */
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
ret = HAL_CALL(hal).start_isoch_dma(hal->halinfo.hal_private,
(void *)t1394_idma_hdl, idma_ctrlinfo, flags, result);
TNF_PROBE_0_DEBUG(t1394_start_isoch_dma_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (ret);
}
/*
* Function: t1394_stop_isoch_dma()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* t1394_idma_hdl The IDMA "handle" returned by
* t1394_alloc_isoch_dma()
* flags The flags parameter is unused (for now)
*
* Output(s): None
*
* Description: t1394_stop_isoch_dma() is used to stop DMA for the isoch
* stream associated with t1394_idma_hdl.
*/
/* ARGSUSED */
void
t1394_stop_isoch_dma(t1394_handle_t t1394_hdl,
t1394_isoch_dma_handle_t t1394_idma_hdl, uint_t flags)
{
s1394_hal_t *hal;
int result;
TNF_PROBE_0_DEBUG(t1394_stop_isoch_dma_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_idma_hdl != NULL);
/* Find the HAL this target resides on */
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
HAL_CALL(hal).stop_isoch_dma(hal->halinfo.hal_private,
(void *)t1394_idma_hdl, &result);
TNF_PROBE_0_DEBUG(t1394_stop_isoch_dma_exit,
S1394_TNF_SL_ISOCH_STACK, "");
}
/*
* Function: t1394_update_isoch_dma()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* t1394_idma_hdl The IDMA "handle" returned by
* t1394_alloc_isoch_dma()
* idma_updateinfo This structure contains ixl command args
* used when updating args in an
* existing list of ixl commands with
* args in a new list of ixl commands.
* flags The flags parameter is unused (for now)
*
* Output(s): result Used to pass more specific info back
* to target
*
* Description: t1394_update_isoch_dma() is used to alter an IXL program that
* has already been built (compiled) by t1394_alloc_isoch_dma().
*/
/* ARGSUSED */
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)
{
s1394_hal_t *hal;
int ret;
TNF_PROBE_0_DEBUG(t1394_update_isoch_dma_enter,
S1394_TNF_SL_ISOCH_STACK, "");
ASSERT(t1394_hdl != NULL);
ASSERT(t1394_idma_hdl != NULL);
ASSERT(idma_updateinfo != NULL);
/* Find the HAL this target resides on */
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
ret = HAL_CALL(hal).update_isoch_dma(hal->halinfo.hal_private,
(void *)t1394_idma_hdl, idma_updateinfo, flags, result);
TNF_PROBE_0_DEBUG(t1394_update_isoch_dma_exit,
S1394_TNF_SL_ISOCH_STACK, "");
return (ret);
}
/*
* Function: t1394_initiate_bus_reset()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* flags The flags parameter is unused (for now)
*
* Output(s): None
*
* Description: t1394_initiate_bus_reset() determines whether the local
* device has a P1394A PHY and will support the arbitrated
* short bus reset. If not, it will initiate a normal bus reset.
*/
/* ARGSUSED */
void
t1394_initiate_bus_reset(t1394_handle_t t1394_hdl, uint_t flags)
{
s1394_hal_t *hal;
int ret;
TNF_PROBE_0_DEBUG(t1394_initiate_bus_reset_enter,
S1394_TNF_SL_BR_STACK, "");
ASSERT(t1394_hdl != NULL);
/* Find the HAL this target resides on */
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Reset the bus */
if (hal->halinfo.phy == H1394_PHY_1394A) {
ret = HAL_CALL(hal).short_bus_reset(hal->halinfo.hal_private);
if (ret != DDI_SUCCESS) {
TNF_PROBE_1(t1394_initiate_bus_reset_error,
S1394_TNF_SL_ERROR, "", tnf_string, msg,
"Error initiating short bus reset");
}
} else {
ret = HAL_CALL(hal).bus_reset(hal->halinfo.hal_private);
if (ret != DDI_SUCCESS) {
TNF_PROBE_1(t1394_initiate_bus_reset_error,
S1394_TNF_SL_ERROR, "", tnf_string, msg,
"Error initiating bus reset");
}
}
TNF_PROBE_0_DEBUG(t1394_initiate_bus_reset_exit,
S1394_TNF_SL_BR_STACK, "");
}
/*
* Function: t1394_get_topology_map()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* bus_generation The current generation
* tm_length The size of the tm_buffer given
* flags The flags parameter is unused (for now)
*
* Output(s): tm_buffer Filled in by the 1394 Software Framework
* with the contents of the local
* TOPOLOGY_MAP
*
* Description: t1394_get_topology_map() returns the 1394 TOPLOGY_MAP. See
* IEEE 1394-1995 Section 8.2.3.4.1 for format information. This
* call can fail if there is a generation mismatch or the
* tm_buffer is too small to hold the TOPOLOGY_MAP.
*/
/* ARGSUSED */
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)
{
s1394_hal_t *hal;
uint32_t *tm_ptr;
uint_t length;
TNF_PROBE_0_DEBUG(t1394_get_topology_map_enter, S1394_TNF_SL_CSR_STACK,
"");
ASSERT(t1394_hdl != NULL);
/* Find the HAL this target resides on */
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
/* Lock the topology tree */
mutex_enter(&hal->topology_tree_mutex);
/* Check the bus_generation for the Topology Map */
if (bus_generation != hal->generation_count) {
/* Unlock the topology tree */
mutex_exit(&hal->topology_tree_mutex);
TNF_PROBE_1(t1394_get_topology_map_error,
S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
"Generation mismatch");
TNF_PROBE_0_DEBUG(t1394_get_topology_map_exit,
S1394_TNF_SL_CSR_STACK, "");
return (DDI_FAILURE);
}
tm_ptr = (uint32_t *)hal->CSR_topology_map;
length = tm_ptr[0] >> 16;
length = length * 4; /* Bytes instead of quadlets */
length = length + 4; /* don't forget the first quad */
/* Check that the buffer is big enough */
if (length > (uint_t)tm_length) {
/* Unlock the topology tree */
mutex_exit(&hal->topology_tree_mutex);
TNF_PROBE_1(t1394_get_topology_map_error,
S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
"Buffer size too small");
TNF_PROBE_0_DEBUG(t1394_get_topology_map_exit,
S1394_TNF_SL_CSR_STACK, "");
return (DDI_FAILURE);
}
/* Do the copy */
bcopy(tm_ptr, tm_buffer, length);
/* Unlock the topology tree */
mutex_exit(&hal->topology_tree_mutex);
TNF_PROBE_0_DEBUG(t1394_get_topology_map_exit, S1394_TNF_SL_CSR_STACK,
"");
return (DDI_SUCCESS);
}
/*
* Function: t1394_CRC16()
* Input(s): d The data to compute the CRC-16 for
* crc_length The length into the data to compute for
* flags The flags parameter is unused (for now)
*
* Output(s): CRC The CRC-16 computed for the length
* of data specified
*
* Description: t1394_CRC16() implements ISO/IEC 13213:1994, ANSI/IEEE Std
* 1212, 1994 - 8.1.5.
*/
/* ARGSUSED */
uint_t
t1394_CRC16(uint32_t *d, size_t crc_length, uint_t flags)
{
/* Implements ISO/IEC 13213:1994, */
/* ANSI/IEEE Std 1212, 1994 - 8.1.5 */
uint_t ret;
TNF_PROBE_0_DEBUG(t1394_CRC16_enter, S1394_TNF_SL_STACK, "");
ret = s1394_CRC16((uint_t *)d, (uint_t)crc_length);
TNF_PROBE_0_DEBUG(t1394_CRC16_exit, S1394_TNF_SL_STACK, "");
return (ret);
}
/*
* Function: t1394_add_cfgrom_entry()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* cfgrom_entryinfo This structure holds the cfgrom key,
* buffer, and size
* flags The flags parameter is unused (for now)
*
* Output(s): t1394_cfgrom_hdl The ConfigROM "handle" used in
* t1394_rem_cfgrom_entry()
* result Used to pass more specific info back
* to target
*
* Description: t1394_add_cfgrom_entry() adds an entry to the local Config ROM,
* updating the directory entries as necessary. This call could
* fail because there is no room for the new entry in Config ROM
* (T1394_ECFGROM_FULL), the key is invalid (T1394_EINVALID_PARAM),
* or it was called in interrupt context (T1394_EINVALID_CONTEXT).
*/
/* ARGSUSED */
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)
{
s1394_hal_t *hal;
s1394_target_t *target;
int ret;
uint_t key;
uint_t size;
uint32_t *buffer;
TNF_PROBE_0_DEBUG(t1394_add_cfgrom_entry_enter,
S1394_TNF_SL_CFGROM_STACK, "");
ASSERT(t1394_hdl != NULL);
target = (s1394_target_t *)t1394_hdl;
key = cfgrom_entryinfo->ce_key;
buffer = cfgrom_entryinfo->ce_buffer;
size = (uint_t)cfgrom_entryinfo->ce_size;
/* Check for a valid size */
if (size == 0) {
*result = T1394_EINVALID_PARAM;
TNF_PROBE_1_DEBUG(t1394_add_cfgrom_entry_error,
S1394_TNF_SL_CFGROM_ERROR, "", tnf_string, msg,
"Invalid size of Config ROM buffer (== 0)");
TNF_PROBE_0_DEBUG(t1394_add_cfgrom_entry_exit,
S1394_TNF_SL_CFGROM_STACK, "");
return (DDI_FAILURE);
}
/* Check for a valid key type */
if (((key << IEEE1212_KEY_VALUE_SHIFT) & IEEE1212_KEY_TYPE_MASK) == 0) {
*result = T1394_EINVALID_PARAM;
TNF_PROBE_1_DEBUG(t1394_add_cfgrom_entry_error,
S1394_TNF_SL_CFGROM_ERROR, "", tnf_string, msg,
"Invalid key_type in Config ROM key");
TNF_PROBE_0_DEBUG(t1394_add_cfgrom_entry_exit,
S1394_TNF_SL_CFGROM_STACK, "");
return (DDI_FAILURE);
}
/* Find the HAL this target resides on */
hal = target->on_hal;
/* Is this on the interrupt stack? */
if (curthread->t_flag == T_INTR_THREAD) {
*result = T1394_EINVALID_CONTEXT;
TNF_PROBE_0_DEBUG(t1394_add_cfgrom_entry_exit,
S1394_TNF_SL_CFGROM_STACK, "");
return (DDI_FAILURE);
}
/* Lock the Config ROM buffer */
mutex_enter(&hal->local_config_rom_mutex);
ret = s1394_add_config_rom_entry(hal, key, buffer, size,
(void **)t1394_cfgrom_hdl, result);
if (ret != DDI_SUCCESS) {
if (*result == CMD1394_ERSRC_CONFLICT)
*result = T1394_ECFGROM_FULL;
mutex_exit(&hal->local_config_rom_mutex);
TNF_PROBE_1(t1394_add_cfgrom_entry_error,
S1394_TNF_SL_CFGROM_ERROR, "", tnf_string, msg,
"Failed in s1394_add_cfgrom_entry()");
TNF_PROBE_0_DEBUG(t1394_add_cfgrom_entry_exit,
"stacktrace 1394 s1394", "");
return (ret);
}
/* Setup the timeout function */
if (hal->config_rom_timer_set == B_FALSE) {
hal->config_rom_timer_set = B_TRUE;
mutex_exit(&hal->local_config_rom_mutex);
hal->config_rom_timer =
timeout(s1394_update_config_rom_callback, hal,
drv_usectohz(CONFIG_ROM_UPDATE_DELAY * 1000));
} else {
mutex_exit(&hal->local_config_rom_mutex);
}
TNF_PROBE_0_DEBUG(t1394_add_cfgrom_entry_exit,
S1394_TNF_SL_CFGROM_STACK, "");
return (ret);
}
/*
* Function: t1394_rem_cfgrom_entry()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* flags The flags parameter is unused (for now)
* t1394_cfgrom_hdl The ConfigROM "handle" returned by
* t1394_add_cfgrom_entry()
*
* Output(s): result Used to pass more specific info back
* to target
*
* Description: t1394_rem_cfgrom_entry() is used to remove a previously added
* Config ROM entry (indicated by t1394_cfgrom_hdl).
*/
/* ARGSUSED */
int
t1394_rem_cfgrom_entry(t1394_handle_t t1394_hdl, uint_t flags,
t1394_cfgrom_handle_t *t1394_cfgrom_hdl, int *result)
{
s1394_hal_t *hal;
s1394_target_t *target;
int ret;
TNF_PROBE_0_DEBUG(t1394_rem_cfgrom_entry_enter,
S1394_TNF_SL_CFGROM_STACK, "");
ASSERT(t1394_hdl != NULL);
target = (s1394_target_t *)t1394_hdl;
/* Find the HAL this target resides on */
hal = target->on_hal;
/* Is this on the interrupt stack? */
if (curthread->t_flag == T_INTR_THREAD) {
*result = T1394_EINVALID_CONTEXT;
TNF_PROBE_0_DEBUG(t1394_rem_cfgrom_entry_exit,
S1394_TNF_SL_CFGROM_STACK, "");
return (DDI_FAILURE);
}
/* Lock the Config ROM buffer */
mutex_enter(&hal->local_config_rom_mutex);
ret = s1394_remove_config_rom_entry(hal, (void **)t1394_cfgrom_hdl,
result);
if (ret != DDI_SUCCESS) {
mutex_exit(&hal->local_config_rom_mutex);
TNF_PROBE_1(t1394_rem_cfgrom_entry_error,
S1394_TNF_SL_CFGROM_ERROR, "", tnf_string, msg,
"Failed in s1394_remove_cfgrom_entry()");
TNF_PROBE_0_DEBUG(t1394_rem_cfgrom_entry_exit,
"stacktrace 1394 s1394", "");
return (ret);
}
/* Setup the timeout function */
if (hal->config_rom_timer_set == B_FALSE) {
hal->config_rom_timer_set = B_TRUE;
mutex_exit(&hal->local_config_rom_mutex);
hal->config_rom_timer =
timeout(s1394_update_config_rom_callback, hal,
drv_usectohz(CONFIG_ROM_UPDATE_DELAY * 1000));
} else {
mutex_exit(&hal->local_config_rom_mutex);
}
TNF_PROBE_0_DEBUG(t1394_rem_cfgrom_entry_exit,
S1394_TNF_SL_CFGROM_STACK, "");
return (ret);
}
/*
* Function: t1394_get_targetinfo()
* Input(s): t1394_hdl The target "handle" returned by
* t1394_attach()
* bus_generation The current generation
* flags The flags parameter is unused (for now)
*
* Output(s): targetinfo Structure containing max_payload,
* max_speed, and target node ID.
*
* Description: t1394_get_targetinfo() is used to retrieve information specific
* to a target device. It will fail if the generation given
* does not match the current generation.
*/
/* ARGSUSED */
int
t1394_get_targetinfo(t1394_handle_t t1394_hdl, uint_t bus_generation,
uint_t flags, t1394_targetinfo_t *targetinfo)
{
s1394_hal_t *hal;
s1394_target_t *target;
uint_t dev;
uint_t curr;
uint_t from_node;
uint_t to_node;
TNF_PROBE_0_DEBUG(t1394_get_targetinfo_enter, S1394_TNF_SL_STACK, "");
ASSERT(t1394_hdl != NULL);
/* Find the HAL this target resides on */
hal = ((s1394_target_t *)t1394_hdl)->on_hal;
target = (s1394_target_t *)t1394_hdl;
/* Lock the topology tree */
mutex_enter(&hal->topology_tree_mutex);
/* Check the bus_generation */
if (bus_generation != hal->generation_count) {
/* Unlock the topology tree */
mutex_exit(&hal->topology_tree_mutex);
TNF_PROBE_3(t1394_get_targetinfo_error, S1394_TNF_SL_STACK, "",
tnf_string, msg, "Generation mismatch",
tnf_uint, gen, bus_generation,
tnf_uint, current_gen, hal->generation_count);
return (DDI_FAILURE);
}
rw_enter(&hal->target_list_rwlock, RW_READER);
/*
* If there is no node, report T1394_INVALID_NODEID for target_nodeID;
* current_max_speed and current_max_payload are undefined for this
* case.
*/
if (((target->target_state & S1394_TARG_GONE) != 0) ||
(target->on_node == NULL)) {
targetinfo->target_nodeID = T1394_INVALID_NODEID;
TNF_PROBE_1_DEBUG(t1394_get_targetinfo_exit,
S1394_TNF_SL_STACK, "", tnf_string, msg, "No device");
} else {
targetinfo->target_nodeID =
(target->on_hal->node_id & IEEE1394_BUS_NUM_MASK) |
target->on_node->node_num;
from_node = (target->on_hal->node_id) & IEEE1394_NODE_NUM_MASK;
to_node = target->on_node->node_num;
targetinfo->current_max_speed = (uint_t)s1394_speed_map_get(
hal, from_node, to_node);
/* Get current_max_payload */
s1394_get_maxpayload(target, &dev, &curr);
targetinfo->current_max_payload = curr;
TNF_PROBE_3_DEBUG(t1394_get_targetinfo_exit,
S1394_TNF_SL_STACK, "",
tnf_uint, payload, targetinfo->current_max_payload,
tnf_uint, speed, targetinfo->current_max_speed,
tnf_uint, nodeid, targetinfo->target_nodeID);
}
rw_exit(&hal->target_list_rwlock);
/* Unlock the topology tree */
mutex_exit(&hal->topology_tree_mutex);
return (DDI_SUCCESS);
}