/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * t1394.c * 1394 Target Driver Interface * This file contains all of the 1394 Software Framework routines called * by target drivers */ #include #include #include #include #include #include #include #include #include #include #include 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); *t1394_hdl = NULL; if (version != T1394_VERSION_V1) { return (DDI_FAILURE); } hal = s1394_dip_to_hal(ddi_get_parent(dip)); if (hal == NULL) { 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)); 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); 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; 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); 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; 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; 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); /* 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); /* 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); 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; 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); 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); 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++; 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; 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) { 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; return (DDI_FAILURE); } /* Is this a blocking command on interrupt stack? */ if ((cmd->cmd_options & CMD1394_BLOCKING) && (servicing_interrupt())) { cmd->cmd_result = CMD1394_EINVALID_CONTEXT; 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); 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; 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; return (DDI_FAILURE); } } else { /* Block (if necessary) */ goto block_on_asynch_cmd; } block_on_asynch_cmd: s1394_block_on_asynch_cmd(cmd); 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; 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) { 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)) { 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); return (DDI_FAILURE); } /* Is this a blocking command on interrupt stack? */ if ((cmd->cmd_options & CMD1394_BLOCKING) && (servicing_interrupt())) { cmd->cmd_result = CMD1394_EINVALID_CONTEXT; s1394_fa_check_restore_cmd(to_hal, cmd); 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); 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); 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); 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); 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); return (DDI_FAILURE); } } else { /* Block (if necessary) */ s1394_block_on_asynch_cmd(cmd); 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; 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) { 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; 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) && (servicing_interrupt())) { cmd->cmd_result = CMD1394_EINVALID_CONTEXT; 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: cmd->cmd_result = CMD1394_EINVALID_COMMAND; ret = DDI_FAILURE; break; } 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; 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; /* 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; /* 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; /* 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; } 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); } } 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; 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) { 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++; 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; 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); 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) { /* * 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) { /* * 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); return (ret); default: 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; ASSERT(t1394_hdl != NULL); result = s1394_fcp_register_ctl((s1394_target_t *)t1394_hdl, evts); 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; ASSERT(t1394_hdl != NULL); result = s1394_fcp_unregister_ctl((s1394_target_t *)t1394_hdl); 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; ASSERT(t1394_hdl != NULL); result = s1394_fcp_register_tgt((s1394_target_t *)t1394_hdl, evts); 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; ASSERT(t1394_hdl != NULL); result = s1394_fcp_unregister_tgt((s1394_target_t *)t1394_hdl); 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; ASSERT(t1394_hdl != NULL); result = s1394_cmp_register((s1394_target_t *)t1394_hdl, evts); 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; ASSERT(t1394_hdl != NULL); result = s1394_cmp_unregister((s1394_target_t *)t1394_hdl); 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; ASSERT(t1394_hdl != NULL); result = s1394_cmp_read((s1394_target_t *)t1394_hdl, reg, valp); 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; ASSERT(t1394_hdl != NULL); result = s1394_cmp_cas((s1394_target_t *)t1394_hdl, reg, arg_val, new_val, old_valp); 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; 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) { return (DDI_FAILURE); } /* Check for invalid bandwidth */ if ((sii->si_bandwidth <= IEEE1394_BANDWIDTH_MIN) || (sii->si_bandwidth > IEEE1394_BANDWIDTH_MAX)) { return (DDI_FAILURE); } /* Verify that rsrc_fail_target() callback is non-NULL */ if (sii->rsrc_fail_target == NULL) { 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) { 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 */ ASSERT(0); } /* Handle is nulled out before returning */ *t1394_single_hdl = NULL; 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) { *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 */ 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 */ ASSERT(0); } /* Handle is nulled out before returning */ *t1394_single_hdl = NULL; 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; 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; 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 */ 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 */ 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 */ ASSERT(0); } /* Handle is nulled out before returning */ *t1394_single_hdl = NULL; } /* * 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; 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) { 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 (!ISP2(temp)) { return (DDI_FAILURE); } /* If T1394_NO_IRM_ALLOC, then speeds should be equal */ if (props->cec_min_speed != props->cec_max_speed) { return (DDI_FAILURE); } } /* Check for invalid bandwidth */ if ((props->cec_bandwidth <= IEEE1394_BANDWIDTH_MIN) || (props->cec_bandwidth > IEEE1394_BANDWIDTH_MAX)) { 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; 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; 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); 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); 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; 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; 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); 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); 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); 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); 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))) { kmem_free(member_new, sizeof (s1394_isoch_cec_member_t)); /* Unlock the Isoch CEC member list */ mutex_exit(&cec_curr->isoch_cec_mutex); 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); 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; 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); 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); 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)); 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 *); 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); 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); 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); 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; } } /* Unlock the Isoch CEC member list */ mutex_exit(&cec_curr->isoch_cec_mutex); 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); 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) { return (DDI_FAILURE); } 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); 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); 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); 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) { return (DDI_FAILURE); } 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); 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); 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); 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); 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); 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); 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); 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; } } /* 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); 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); 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; 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)) { *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)) { *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) { if (*result == IXL1394_ENO_DMA_RESRCS) { *result = T1394_EIDMA_NO_RESRCS; } } 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; 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; } /* * 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; 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); 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; 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); } /* * 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; 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); 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; 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) { (void) HAL_CALL(hal).short_bus_reset(hal->halinfo.hal_private); } else { (void) HAL_CALL(hal).bus_reset(hal->halinfo.hal_private); } } /* * 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; 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); 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); return (DDI_FAILURE); } /* Do the copy */ bcopy(tm_ptr, tm_buffer, length); /* Unlock the topology tree */ mutex_exit(&hal->topology_tree_mutex); 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; ret = s1394_CRC16((uint_t *)d, (uint_t)crc_length); 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; 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; return (DDI_FAILURE); } /* Check for a valid key type */ if (((key << IEEE1212_KEY_VALUE_SHIFT) & IEEE1212_KEY_TYPE_MASK) == 0) { *result = T1394_EINVALID_PARAM; return (DDI_FAILURE); } /* Find the HAL this target resides on */ hal = target->on_hal; /* Is this on the interrupt stack? */ if (servicing_interrupt()) { *result = T1394_EINVALID_CONTEXT; 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); 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); } 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; 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 (servicing_interrupt()) { *result = T1394_EINVALID_CONTEXT; 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); 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); } 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; 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); 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; } 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; } rw_exit(&hal->target_list_rwlock); /* Unlock the topology tree */ mutex_exit(&hal->topology_tree_mutex); return (DDI_SUCCESS); }