| /* |
| * 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 <sys/sysmacros.h> |
| #include <sys/conf.h> |
| #include <sys/ddi.h> |
| #include <sys/sunddi.h> |
| #include <sys/types.h> |
| #include <sys/kmem.h> |
| #include <sys/disp.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) && |
| (servicing_interrupt())) { |
| 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) && |
| (servicing_interrupt())) { |
| 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) && |
| (servicing_interrupt())) { |
| 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 (!ISP2(temp)) { |
| 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 */ |
|