| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License, Version 1.0 only |
| * (the "License"). You may not use this file except in compliance |
| * with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2004 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| /* |
| * s1394_misc.c |
| * 1394 Services Layer Miscellaneous Routines |
| * This file contains miscellaneous routines used as "helper" functions |
| * by various other files in the Services Layer. |
| */ |
| |
| #include <sys/conf.h> |
| #include <sys/ddi.h> |
| #include <sys/sunddi.h> |
| #include <sys/cmn_err.h> |
| #include <sys/types.h> |
| #include <sys/kmem.h> |
| #include <sys/kstat.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> |
| |
| int s1394_print_guids = 0; /* patch to print GUIDs */ |
| |
| extern void nx1394_undefine_events(s1394_hal_t *hal); |
| static void s1394_cleanup_node_cfgrom(s1394_hal_t *hal); |
| |
| /* |
| * s1394_cleanup_for_detach() |
| * is used to do all of the necessary cleanup to handle a detach or a |
| * failure in h1394_attach(). The cleanup_level specifies how far we |
| * got in h1394_attach() before failure. |
| */ |
| void |
| s1394_cleanup_for_detach(s1394_hal_t *hal, uint_t cleanup_level) |
| { |
| |
| TNF_PROBE_0_DEBUG(s1394_cleanup_for_detach_enter, S1394_TNF_SL_STACK, |
| ""); |
| |
| switch (cleanup_level) { |
| case H1394_CLEANUP_LEVEL7: |
| /* remove HAL from the global HAL list */ |
| mutex_enter(&s1394_statep->hal_list_mutex); |
| if ((s1394_statep->hal_head == hal) && |
| (s1394_statep->hal_tail == hal)) { |
| s1394_statep->hal_head = NULL; |
| s1394_statep->hal_tail = NULL; |
| } else { |
| if (hal->hal_prev) |
| hal->hal_prev->hal_next = hal->hal_next; |
| if (hal->hal_next) |
| hal->hal_next->hal_prev = hal->hal_prev; |
| if (s1394_statep->hal_head == hal) |
| s1394_statep->hal_head = hal->hal_next; |
| if (s1394_statep->hal_tail == hal) |
| s1394_statep->hal_tail = hal->hal_prev; |
| } |
| mutex_exit(&s1394_statep->hal_list_mutex); |
| /* |
| * No FCP cleanup needed at this time -- the following call |
| * to s1394_destroy_addr_space() takes care of everything. |
| */ |
| /* FALLTHROUGH */ |
| |
| case H1394_CLEANUP_LEVEL6: |
| s1394_destroy_addr_space(hal); |
| /* FALLTHROUGH */ |
| |
| case H1394_CLEANUP_LEVEL5: |
| s1394_destroy_local_config_rom(hal); |
| /* FALLTHROUGH */ |
| |
| case H1394_CLEANUP_LEVEL4: |
| /* Undo all the kstat stuff */ |
| (void) s1394_kstat_delete(hal); |
| /* FALLTHROUGH */ |
| |
| case H1394_CLEANUP_LEVEL3: |
| /* Free up the memory for selfID buffer #1 */ |
| kmem_free(hal->selfid_buf1, S1394_SELFID_BUF_SIZE); |
| /* Free up the memory for selfID buffer #0 */ |
| kmem_free(hal->selfid_buf0, S1394_SELFID_BUF_SIZE); |
| /* Turn off any timers that might be set */ |
| s1394_destroy_timers(hal); |
| /* Destroy the bus_reset thread */ |
| s1394_destroy_br_thread(hal); |
| /* Cleanup the Config ROM buffers in the topology_tree */ |
| s1394_cleanup_node_cfgrom(hal); |
| /* FALLTHROUGH */ |
| |
| case H1394_CLEANUP_LEVEL2: |
| /* Destroy the br_cmplq_cv and br_cmplq_mutex */ |
| cv_destroy(&hal->br_cmplq_cv); |
| mutex_destroy(&hal->br_cmplq_mutex); |
| /* Destroy the br_thread_cv and br_thread_mutex */ |
| cv_destroy(&hal->br_thread_cv); |
| mutex_destroy(&hal->br_thread_mutex); |
| /* FALLTHROUGH */ |
| |
| case H1394_CLEANUP_LEVEL1: |
| (void) ddi_prop_remove_all(hal->halinfo.dip); |
| nx1394_undefine_events(hal); |
| /* FALLTHROUGH */ |
| |
| case H1394_CLEANUP_LEVEL0: |
| kmem_cache_destroy(hal->hal_kmem_cachep); |
| /* Destroy pending_q_mutex and outstanding_q_mutex */ |
| mutex_destroy(&hal->pending_q_mutex); |
| mutex_destroy(&hal->outstanding_q_mutex); |
| /* Destroy target_list_rwlock */ |
| rw_destroy(&hal->target_list_rwlock); |
| /* Destroy bus_mgr_node_mutex and bus_mgr_node_cv */ |
| cv_destroy(&hal->bus_mgr_node_cv); |
| mutex_destroy(&hal->bus_mgr_node_mutex); |
| /* Destroy isoch_cec_list_mutex */ |
| mutex_destroy(&hal->isoch_cec_list_mutex); |
| /* Destroy the Cycle Master timer mutex */ |
| mutex_destroy(&hal->cm_timer_mutex); |
| /* Destroy topology_tree_mutex */ |
| mutex_destroy(&hal->topology_tree_mutex); |
| /* Free the hal structure */ |
| kmem_free(hal, sizeof (s1394_hal_t)); |
| break; |
| |
| default: |
| /* Error */ |
| TNF_PROBE_1(s1394_cleanup_for_detach_error, |
| S1394_TNF_SL_ERROR, "", tnf_string, msg, |
| "Invalid cleanup_level"); |
| break; |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_cleanup_for_detach_exit, S1394_TNF_SL_STACK, |
| ""); |
| } |
| |
| /* |
| * s1394_hal_shutdown() |
| * is used to shutdown the HAL. If the HAL indicates that an error |
| * condition (hardware or software) has occurred, it is shutdown. This |
| * routine is also called when HAL informs the services layer of a shutdown |
| * (due an internal shutdown, for eg). disable_hal indicates whether the |
| * caller intends to inform the hal of the (services layer) shutdown or not. |
| */ |
| void |
| s1394_hal_shutdown(s1394_hal_t *hal, boolean_t disable_hal) |
| { |
| ddi_eventcookie_t cookie; |
| t1394_localinfo_t localinfo; |
| |
| TNF_PROBE_0_DEBUG(s1394_hal_shutdown_enter, S1394_TNF_SL_STACK, ""); |
| |
| mutex_enter(&hal->topology_tree_mutex); |
| |
| if (hal->hal_state == S1394_HAL_SHUTDOWN) { |
| mutex_exit(&hal->topology_tree_mutex); |
| if (disable_hal == B_TRUE) |
| HAL_CALL(hal).shutdown(hal->halinfo.hal_private); |
| |
| TNF_PROBE_0_DEBUG(s1394_hal_shutdown_exit_already, |
| S1394_TNF_SL_STACK, ""); |
| return; |
| } |
| |
| hal->hal_state = S1394_HAL_SHUTDOWN; |
| mutex_exit(&hal->topology_tree_mutex); |
| /* Disable the HAL */ |
| if (disable_hal == B_TRUE) |
| HAL_CALL(hal).shutdown(hal->halinfo.hal_private); |
| |
| /* |
| * Send a remove event to all interested parties |
| */ |
| mutex_enter(&hal->topology_tree_mutex); |
| localinfo.bus_generation = hal->generation_count; |
| localinfo.local_nodeID = hal->node_id; |
| mutex_exit(&hal->topology_tree_mutex); |
| |
| if (ndi_event_retrieve_cookie(hal->hal_ndi_event_hdl, NULL, |
| DDI_DEVI_REMOVE_EVENT, &cookie, NDI_EVENT_NOPASS) == |
| NDI_SUCCESS) |
| (void) ndi_event_run_callbacks(hal->hal_ndi_event_hdl, NULL, |
| cookie, &localinfo); |
| |
| TNF_PROBE_0_DEBUG(s1394_hal_shutdown_exit, S1394_TNF_SL_STACK, ""); |
| } |
| |
| /* |
| * s1394_initiate_hal_reset() |
| * sets up the HAL structure to indicate a self-initiated bus reset and |
| * calls the appropriate HAL entry point. If too many bus resets have |
| * happened, a message is printed out and the call is ignored. |
| */ |
| void |
| s1394_initiate_hal_reset(s1394_hal_t *hal, int reason) |
| { |
| int ret; |
| |
| TNF_PROBE_0_DEBUG(s1394_initiate_hal_reset_enter, S1394_TNF_SL_BR_STACK, |
| ""); |
| |
| if (hal->num_bus_reset_till_fail > 0) { |
| hal->initiated_bus_reset = B_TRUE; |
| hal->initiated_br_reason = reason; |
| |
| /* Reset the bus */ |
| ret = HAL_CALL(hal).bus_reset(hal->halinfo.hal_private); |
| if (ret != DDI_SUCCESS) { |
| TNF_PROBE_1(s1394_initiate_hal_reset_error, |
| S1394_TNF_SL_ERROR, "", tnf_string, msg, |
| "Error initiating bus reset"); |
| } |
| } else { |
| cmn_err(CE_NOTE, "Unable to reenumerate the 1394 bus - If new" |
| " devices have recently been added, remove them."); |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_initiate_hal_reset_exit, S1394_TNF_SL_BR_STACK, |
| ""); |
| } |
| |
| /* |
| * s1394_on_br_thread() |
| * is used to determine if the current thread of execution is the same |
| * as the bus reset thread. This is useful during bus reset callbacks |
| * to determine whether or not a target may block. |
| */ |
| boolean_t |
| s1394_on_br_thread(s1394_hal_t *hal) |
| { |
| if (hal->br_thread == curthread) |
| return (B_TRUE); |
| else |
| return (B_FALSE); |
| } |
| |
| /* |
| * s1394_destroy_br_thread() |
| * is used in h1394_detach() to signal the bus reset thread to go away. |
| */ |
| void |
| s1394_destroy_br_thread(s1394_hal_t *hal) |
| { |
| TNF_PROBE_0_DEBUG(s1394_destroy_br_thread_enter, S1394_TNF_SL_STACK, |
| ""); |
| |
| /* Send the signal to the reset thread to go away */ |
| mutex_enter(&hal->br_thread_mutex); |
| hal->br_thread_ev_type |= BR_THR_GO_AWAY; |
| cv_signal(&hal->br_thread_cv); |
| mutex_exit(&hal->br_thread_mutex); |
| |
| /* Wakeup the bus_reset thread if waiting for bus_mgr timer */ |
| mutex_enter(&hal->bus_mgr_node_mutex); |
| hal->bus_mgr_node = S1394_INVALID_NODE_NUM; |
| cv_signal(&hal->bus_mgr_node_cv); |
| mutex_exit(&hal->bus_mgr_node_mutex); |
| |
| mutex_enter(&hal->br_cmplq_mutex); |
| cv_signal(&hal->br_cmplq_cv); |
| mutex_exit(&hal->br_cmplq_mutex); |
| |
| /* Wait for the br_thread to be done */ |
| while (hal->br_thread_ev_type & BR_THR_GO_AWAY) |
| delay(drv_usectohz(10)); |
| |
| TNF_PROBE_0_DEBUG(s1394_destroy_br_thread_exit, S1394_TNF_SL_STACK, |
| ""); |
| } |
| |
| /* |
| * s1394_tickle_bus_reset_thread() |
| * is used to wakeup the bus reset thread after the interrupt routine |
| * has completed its bus reset processing. |
| */ |
| void |
| s1394_tickle_bus_reset_thread(s1394_hal_t *hal) |
| { |
| if (hal->topology_tree_processed != B_TRUE) { |
| /* Send the signal to the reset thread */ |
| mutex_enter(&hal->br_thread_mutex); |
| hal->br_thread_ev_type |= BR_THR_CFGROM_SCAN; |
| cv_signal(&hal->br_thread_cv); |
| mutex_exit(&hal->br_thread_mutex); |
| |
| /* Signal the msgq wait, too (just in case) */ |
| mutex_enter(&hal->br_cmplq_mutex); |
| cv_signal(&hal->br_cmplq_cv); |
| mutex_exit(&hal->br_cmplq_mutex); |
| |
| /* Signal the bus_mgr wait, too (just in case) */ |
| mutex_enter(&hal->bus_mgr_node_mutex); |
| cv_signal(&hal->bus_mgr_node_cv); |
| mutex_exit(&hal->bus_mgr_node_mutex); |
| } |
| } |
| |
| /* |
| * s1394_block_on_asynch_cmd() |
| * is used by many of the asynch routines to block (if necessary) |
| * while waiting for command completion. |
| */ |
| void |
| s1394_block_on_asynch_cmd(cmd1394_cmd_t *cmd) |
| { |
| s1394_cmd_priv_t *s_priv; |
| |
| TNF_PROBE_0_DEBUG(s1394_block_on_asynch_cmd_enter, |
| S1394_TNF_SL_ATREQ_STACK, ""); |
| |
| /* Get the Services Layer private area */ |
| s_priv = S1394_GET_CMD_PRIV(cmd); |
| |
| /* Is this a blocking command? */ |
| if (cmd->cmd_options & CMD1394_BLOCKING) { |
| /* Block until command completes */ |
| mutex_enter(&s_priv->blocking_mutex); |
| while (s_priv->blocking_flag != B_TRUE) |
| cv_wait(&s_priv->blocking_cv, &s_priv->blocking_mutex); |
| s_priv->blocking_flag = B_FALSE; |
| mutex_exit(&s_priv->blocking_mutex); |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_block_on_asynch_cmd_exit, |
| S1394_TNF_SL_ATREQ_STACK, ""); |
| } |
| |
| /* |
| * s1394_HAL_asynch_error() |
| * is used by many of the asynch routines to determine what error |
| * code is expected in a given situation (based on HAL state). |
| */ |
| /* ARGSUSED */ |
| int |
| s1394_HAL_asynch_error(s1394_hal_t *hal, cmd1394_cmd_t *cmd, |
| s1394_hal_state_t state) |
| { |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| switch (state) { |
| case S1394_HAL_RESET: |
| /* "dreq" bit is set (CSR) */ |
| if (hal->disable_requests_bit == 1) |
| return (CMD1394_ENO_ATREQ); |
| else |
| return (CMD1394_CMDSUCCESS); |
| |
| case S1394_HAL_DREQ: |
| /* "dreq" bit is set (CSR) */ |
| return (CMD1394_ENO_ATREQ); |
| |
| case S1394_HAL_SHUTDOWN: |
| return (CMD1394_EFATAL_ERROR); |
| |
| default: |
| return (CMD1394_CMDSUCCESS); |
| } |
| } |
| |
| /* |
| * s1394_mblk_too_small() |
| * is used to determine if the mlbk_t structure(s) given in an asynch |
| * block request are sufficient to hold the amount of data requested. |
| */ |
| boolean_t |
| s1394_mblk_too_small(cmd1394_cmd_t *cmd) |
| { |
| mblk_t *curr_blk; |
| boolean_t flag; |
| size_t msgb_len; |
| size_t size; |
| |
| TNF_PROBE_0_DEBUG(s1394_mblk_too_small_enter, S1394_TNF_SL_ATREQ_STACK, |
| ""); |
| |
| curr_blk = cmd->cmd_u.b.data_block; |
| msgb_len = 0; |
| flag = B_TRUE; |
| size = cmd->cmd_u.b.blk_length; |
| |
| while (curr_blk != NULL) { |
| if (cmd->cmd_type == CMD1394_ASYNCH_WR_BLOCK) { |
| msgb_len += (curr_blk->b_wptr - curr_blk->b_rptr); |
| } else { |
| msgb_len += |
| (curr_blk->b_datap->db_lim - curr_blk->b_wptr); |
| } |
| |
| if (msgb_len >= size) { |
| flag = B_FALSE; |
| break; |
| } |
| |
| curr_blk = curr_blk->b_cont; |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_mblk_too_small_exit, S1394_TNF_SL_ATREQ_STACK, |
| ""); |
| return (flag); |
| } |
| |
| /* |
| * s1394_address_rollover() |
| * is used to determine if the address given will rollover the 48-bit |
| * address space. |
| */ |
| boolean_t |
| s1394_address_rollover(cmd1394_cmd_t *cmd) |
| { |
| uint64_t addr_before; |
| uint64_t addr_after; |
| size_t length; |
| |
| TNF_PROBE_0_DEBUG(s1394_address_rollover_enter, |
| S1394_TNF_SL_ATREQ_STACK, ""); |
| |
| switch (cmd->cmd_type) { |
| case CMD1394_ASYNCH_RD_QUAD: |
| case CMD1394_ASYNCH_WR_QUAD: |
| case CMD1394_ASYNCH_LOCK_32: |
| length = IEEE1394_QUADLET; |
| break; |
| |
| case CMD1394_ASYNCH_LOCK_64: |
| length = IEEE1394_OCTLET; |
| break; |
| |
| case CMD1394_ASYNCH_RD_BLOCK: |
| case CMD1394_ASYNCH_WR_BLOCK: |
| length = cmd->cmd_u.b.blk_length; |
| break; |
| } |
| |
| addr_before = cmd->cmd_addr & IEEE1394_ADDR_OFFSET_MASK; |
| addr_after = (addr_before + length) & IEEE1394_ADDR_OFFSET_MASK; |
| |
| if (addr_after < addr_before) { |
| TNF_PROBE_0_DEBUG(s1394_address_rollover_exit, |
| S1394_TNF_SL_ATREQ_STACK, ""); |
| return (B_TRUE); |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_address_rollover_exit, |
| S1394_TNF_SL_ATREQ_STACK, ""); |
| return (B_FALSE); |
| } |
| |
| /* |
| * s1394_stoi() |
| * returns the integer value of the string of hex/dec/oct numeric characters |
| * beginning at *p. Does no overflow checking. |
| */ |
| uint_t |
| s1394_stoi(char *p, int len, int base) |
| { |
| int n; |
| int c; |
| |
| if (len == 0) |
| return (0); |
| |
| for (n = 0; len && (c = *p); p++, len--) { |
| if (c >= '0' && c <= '9') |
| c = c - '0'; |
| else if (c >= 'a' && c <= 'f') |
| c = c - 'a' + 10; |
| else if (c >= 'A' && c <= 'F') |
| c = c - 'F' + 10; |
| n = (n * base) + c; |
| } |
| |
| return (n); |
| } |
| |
| /* |
| * s1394_CRC16() |
| * implements ISO/IEC 13213:1994, ANSI/IEEE Std 1212, 1994 - 8.1.5 |
| */ |
| uint_t |
| s1394_CRC16(uint_t *d, uint_t crc_length) |
| { |
| uint_t CRC = 0; |
| uint_t data; |
| uint_t next; |
| uint_t sum; |
| int shift; |
| int i; |
| |
| TNF_PROBE_0_DEBUG(s1394_CRC16_enter, S1394_TNF_SL_STACK, ""); |
| |
| for (i = 0; i < crc_length; i++) { |
| data = d[i]; |
| |
| /* Another check should be made with "shift > 0" in */ |
| /* order to support any devices that coded it wrong. */ |
| for (next = CRC, shift = 28; shift >= 0; shift -= 4) { |
| sum = ((next >> 12) ^ (data >> shift)) & 0xF; |
| next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); |
| } |
| CRC = next & IEEE1394_CRC16_MASK; |
| } |
| |
| TNF_PROBE_1_DEBUG(s1394_CRC16_exit, S1394_TNF_SL_STACK, "", |
| tnf_uint, crc, CRC); |
| return (CRC); |
| } |
| |
| /* |
| * s1394_CRC16_old() |
| * implements a slightly modified version of ISO/IEC 13213:1994, |
| * ANSI/IEEE Std 1212, 1994 - 8.1.5. In the original IEEE 1212-1994 |
| * specification the C code example was incorrect and some devices |
| * were manufactured using this incorrect CRC. On CRC16 failures |
| * this CRC is tried in case it is a legacy device. |
| */ |
| uint_t |
| s1394_CRC16_old(uint_t *d, uint_t crc_length) |
| { |
| uint_t CRC = 0; |
| uint_t data; |
| uint_t next; |
| uint_t sum; |
| int shift; |
| int i; |
| |
| TNF_PROBE_0_DEBUG(s1394_CRC16_old_enter, S1394_TNF_SL_STACK, ""); |
| |
| for (i = 0; i < crc_length; i++) { |
| data = d[i]; |
| for (next = CRC, shift = 28; shift > 0; shift -= 4) { |
| sum = ((next >> 12) ^ (data >> shift)) & 0xF; |
| next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); |
| } |
| CRC = next & IEEE1394_CRC16_MASK; |
| } |
| |
| TNF_PROBE_1_DEBUG(s1394_CRC16_old_exit, S1394_TNF_SL_STACK, "", |
| tnf_uint, crc, CRC); |
| return (CRC); |
| } |
| |
| /* |
| * s1394_ioctl() |
| * implements generic ioctls (eg. devctl support) and any non-HAL ioctls. |
| * Only ioctls required for devctl support are implemented at present. |
| */ |
| /* ARGSUSED */ |
| int |
| s1394_ioctl(s1394_hal_t *hal, int cmd, intptr_t arg, int mode, cred_t *cred_p, |
| int *rval_p) |
| { |
| struct devctl_iocdata *dcp; |
| dev_info_t *self; |
| int rv = 0; |
| int ret; |
| |
| TNF_PROBE_0_DEBUG(s1394_ioctl_enter, S1394_TNF_SL_IOCTL_STACK, ""); |
| |
| self = hal->halinfo.dip; |
| |
| /* |
| * We can use the generic implementation for these ioctls |
| */ |
| switch (cmd) { |
| case DEVCTL_DEVICE_GETSTATE: |
| case DEVCTL_DEVICE_ONLINE: |
| case DEVCTL_DEVICE_OFFLINE: |
| case DEVCTL_DEVICE_REMOVE: |
| case DEVCTL_BUS_GETSTATE: |
| return (ndi_devctl_ioctl(self, cmd, arg, mode, 0)); |
| } |
| |
| /* Read devctl ioctl data */ |
| if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) { |
| TNF_PROBE_0_DEBUG(s1394_ioctl_exit, S1394_TNF_SL_IOCTL_STACK, |
| ""); |
| return (EFAULT); |
| } |
| |
| switch (cmd) { |
| |
| case DEVCTL_DEVICE_RESET: |
| case DEVCTL_DEVICE_REMOVE: |
| rv = ENOTSUP; |
| break; |
| |
| case DEVCTL_BUS_CONFIGURE: |
| case DEVCTL_BUS_UNCONFIGURE: |
| rv = ENOTSUP; |
| break; |
| |
| case DEVCTL_BUS_QUIESCE: |
| case DEVCTL_BUS_UNQUIESCE: |
| rv = ENOTSUP; /* Or call up the tree? */ |
| break; |
| |
| case DEVCTL_BUS_RESET: |
| case DEVCTL_BUS_RESETALL: |
| if (hal->halinfo.phy == H1394_PHY_1394A) { |
| ret = HAL_CALL(hal).short_bus_reset( |
| hal->halinfo.hal_private); |
| if (ret != DDI_SUCCESS) { |
| TNF_PROBE_1(s1394_ioctl_error, |
| S1394_TNF_SL_ERROR, "", tnf_string, msg, |
| "Error initiating short bus reset"); |
| } |
| } else { |
| ret = HAL_CALL(hal).bus_reset(hal->halinfo.hal_private); |
| if (ret != DDI_SUCCESS) { |
| TNF_PROBE_1(t1394_initiate_bus_reset_error, |
| S1394_TNF_SL_ERROR, "", tnf_string, msg, |
| "Error initiating bus reset"); |
| } |
| } |
| break; |
| |
| default: |
| rv = ENOTTY; |
| } |
| |
| ndi_dc_freehdl(dcp); |
| |
| TNF_PROBE_0_DEBUG(s1394_ioctl_exit, S1394_TNF_SL_IOCTL_STACK, ""); |
| return (rv); |
| } |
| |
| /* |
| * s1394_kstat_init() |
| * is used to initialize and the Services Layer's kernel statistics. |
| */ |
| int |
| s1394_kstat_init(s1394_hal_t *hal) |
| { |
| int instance; |
| |
| TNF_PROBE_0_DEBUG(s1394_kstat_init_enter, S1394_TNF_SL_STACK, ""); |
| |
| hal->hal_kstats = (s1394_kstat_t *)kmem_zalloc(sizeof (s1394_kstat_t), |
| KM_SLEEP); |
| |
| instance = ddi_get_instance(hal->halinfo.dip); |
| |
| hal->hal_ksp = kstat_create("s1394", instance, "stats", "misc", |
| KSTAT_TYPE_RAW, sizeof (s1394_kstat_t), KSTAT_FLAG_VIRTUAL); |
| if (hal->hal_ksp != NULL) { |
| hal->hal_ksp->ks_private = (void *)hal; |
| hal->hal_ksp->ks_update = s1394_kstat_update; |
| kstat_install(hal->hal_ksp); |
| |
| TNF_PROBE_0_DEBUG(s1394_kstat_init_exit, S1394_TNF_SL_STACK, |
| ""); |
| return (DDI_SUCCESS); |
| } else { |
| kmem_free((void *)hal->hal_kstats, sizeof (s1394_kstat_t)); |
| TNF_PROBE_0_DEBUG(s1394_kstat_init_exit, S1394_TNF_SL_STACK, |
| ""); |
| return (DDI_FAILURE); |
| } |
| } |
| |
| /* |
| * s1394_kstat_delete() |
| * is used (in h1394_detach()) to cleanup/free and the Services Layer's |
| * kernel statistics. |
| */ |
| int |
| s1394_kstat_delete(s1394_hal_t *hal) |
| { |
| TNF_PROBE_0_DEBUG(s1394_kstat_delete_enter, S1394_TNF_SL_STACK, ""); |
| |
| kstat_delete(hal->hal_ksp); |
| kmem_free((void *)hal->hal_kstats, sizeof (s1394_kstat_t)); |
| |
| TNF_PROBE_0_DEBUG(s1394_kstat_delete_exit, S1394_TNF_SL_STACK, ""); |
| return (DDI_SUCCESS); |
| } |
| |
| /* |
| * s1394_kstat_update() |
| * is a callback that is called whenever a request to read the kernel |
| * statistics is made. |
| */ |
| int |
| s1394_kstat_update(kstat_t *ksp, int rw) |
| { |
| s1394_hal_t *hal; |
| |
| TNF_PROBE_0_DEBUG(s1394_kstat_update_enter, S1394_TNF_SL_STACK, ""); |
| |
| hal = ksp->ks_private; |
| |
| if (rw == KSTAT_WRITE) { |
| TNF_PROBE_0_DEBUG(s1394_kstat_update_exit, S1394_TNF_SL_STACK, |
| ""); |
| return (EACCES); |
| } else { |
| ksp->ks_data = hal->hal_kstats; |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_kstat_update_exit, S1394_TNF_SL_STACK, ""); |
| return (0); |
| } |
| |
| /* |
| * s1394_addr_alloc_kstat() |
| * is used by the kernel statistics to update the count for each type of |
| * address allocation. |
| */ |
| void |
| s1394_addr_alloc_kstat(s1394_hal_t *hal, uint64_t addr) |
| { |
| /* kstats - number of addr allocs */ |
| if (s1394_is_posted_write(hal, addr) == B_TRUE) |
| hal->hal_kstats->addr_posted_alloc++; |
| else if (s1394_is_normal_addr(hal, addr) == B_TRUE) |
| hal->hal_kstats->addr_normal_alloc++; |
| else if (s1394_is_csr_addr(hal, addr) == B_TRUE) |
| hal->hal_kstats->addr_csr_alloc++; |
| else if (s1394_is_physical_addr(hal, addr) == B_TRUE) |
| hal->hal_kstats->addr_phys_alloc++; |
| } |
| |
| /* |
| * s1394_print_node_info() |
| * is used to print speed map and GUID information on the console. |
| */ |
| void |
| s1394_print_node_info(s1394_hal_t *hal) |
| { |
| int i, j; |
| uint_t hal_node_num; |
| char str[200], tmp[200]; |
| |
| /* These are in common/os/logsubr.c */ |
| extern void log_enter(void); |
| extern void log_exit(void); |
| |
| if (s1394_print_guids == 0) |
| return; |
| |
| hal_node_num = IEEE1394_NODE_NUM(hal->node_id); |
| |
| log_enter(); |
| |
| cmn_err(CE_CONT, "Speed Map (%d):\n", |
| ddi_get_instance(hal->halinfo.dip)); |
| |
| (void) strcpy(str, " |"); |
| for (i = 0; i < hal->number_of_nodes; i++) { |
| (void) sprintf(tmp, " %2d ", i); |
| (void) strcat(str, tmp); |
| } |
| (void) strcat(str, " | GUID\n"); |
| cmn_err(CE_CONT, str); |
| |
| (void) strcpy(str, "----|"); |
| for (i = 0; i < hal->number_of_nodes; i++) { |
| (void) sprintf(tmp, "----"); |
| (void) strcat(str, tmp); |
| } |
| (void) strcat(str, "--|------------------\n"); |
| cmn_err(CE_CONT, str); |
| |
| for (i = 0; i < hal->number_of_nodes; i++) { |
| |
| (void) sprintf(str, " %2d |", i); |
| |
| for (j = 0; j < hal->number_of_nodes; j++) { |
| (void) sprintf(tmp, " %3d", hal->speed_map[i][j]); |
| (void) strcat(str, tmp); |
| } |
| |
| if (i == hal_node_num) { |
| |
| (void) strcat(str, " | Local OHCI Card\n"); |
| |
| } else if (CFGROM_BIB_READ(&hal->topology_tree[i])) { |
| |
| (void) sprintf(tmp, " | %08x%08x\n", |
| hal->topology_tree[i].node_guid_hi, |
| hal->topology_tree[i].node_guid_lo); |
| (void) strcat(str, tmp); |
| |
| } else if (hal->topology_tree[i].link_active == 0) { |
| |
| (void) strcat(str, " | Link off\n"); |
| |
| } else { |
| |
| (void) strcat(str, " | ????????????????\n"); |
| } |
| cmn_err(CE_CONT, str); |
| } |
| cmn_err(CE_CONT, "\n"); |
| |
| log_exit(); |
| } |
| |
| /* |
| * s1394_dip_to_hal() |
| * is used to lookup a HAL's structure pointer by its dip. |
| */ |
| s1394_hal_t * |
| s1394_dip_to_hal(dev_info_t *hal_dip) |
| { |
| s1394_hal_t *current_hal = NULL; |
| |
| TNF_PROBE_0_DEBUG(s1394_dip_to_hal_enter, |
| S1394_TNF_SL_HOTPLUG_STACK, ""); |
| |
| mutex_enter(&s1394_statep->hal_list_mutex); |
| |
| /* Search the HAL list for this dip */ |
| current_hal = s1394_statep->hal_head; |
| while (current_hal != NULL) { |
| if (current_hal->halinfo.dip == hal_dip) { |
| break; |
| } |
| current_hal = current_hal->hal_next; |
| } |
| |
| mutex_exit(&s1394_statep->hal_list_mutex); |
| |
| TNF_PROBE_0_DEBUG(s1394_dip_to_hal_exit, |
| S1394_TNF_SL_HOTPLUG_STACK, ""); |
| return (current_hal); |
| } |
| |
| /* |
| * s1394_target_from_dip_locked() |
| * searches target_list on the HAL for target corresponding to tdip; |
| * if found, target is returned, else returns NULL. This routine assumes |
| * target_list_rwlock is locked. |
| * NOTE: the callers may have the list locked in either write mode or read |
| * mode. Currently, there is no ddi-compliant way we can assert on the lock |
| * being held in write mode. |
| */ |
| s1394_target_t * |
| s1394_target_from_dip_locked(s1394_hal_t *hal, dev_info_t *tdip) |
| { |
| s1394_target_t *temp; |
| |
| TNF_PROBE_0_DEBUG(s1394_target_from_dip_locked_enter, |
| S1394_TNF_SL_STACK, ""); |
| |
| temp = hal->target_head; |
| while (temp != NULL) { |
| if (temp->target_dip == tdip) { |
| return (temp); |
| } |
| temp = temp->target_next; |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_target_from_dip_locked_exit, |
| S1394_TNF_SL_STACK, ""); |
| return (NULL); |
| } |
| /* |
| * s1394_target_from_dip() |
| * searches target_list on the HAL for target corresponding to tdip; |
| * if found, target is returned locked. |
| */ |
| s1394_target_t * |
| s1394_target_from_dip(s1394_hal_t *hal, dev_info_t *tdip) |
| { |
| s1394_target_t *target; |
| |
| TNF_PROBE_0_DEBUG(s1394_target_from_dip_enter, S1394_TNF_SL_STACK, ""); |
| |
| rw_enter(&hal->target_list_rwlock, RW_READER); |
| target = s1394_target_from_dip_locked(hal, tdip); |
| rw_exit(&hal->target_list_rwlock); |
| |
| TNF_PROBE_0_DEBUG(s1394_target_from_dip_exit, S1394_TNF_SL_STACK, ""); |
| return (target); |
| } |
| |
| /* |
| * s1394_destroy_timers() |
| * turns off any outstanding timers in preparation for detach or suspend. |
| */ |
| void |
| s1394_destroy_timers(s1394_hal_t *hal) |
| { |
| /* Destroy both of the Bus Mgr timers */ |
| (void) untimeout(hal->bus_mgr_timeout_id); |
| (void) untimeout(hal->bus_mgr_query_timeout_id); |
| |
| /* Destroy the Cycle Master timer */ |
| (void) untimeout(hal->cm_timer); |
| |
| /* Wait for the Config ROM timer (if necessary) */ |
| while (hal->config_rom_timer_set == B_TRUE) { |
| delay(drv_usectohz(10)); |
| } |
| } |
| |
| |
| /* |
| * s1394_cleanup_node_cfgrom() |
| * frees up all of the Config ROM in use by nodes in the topology_tree |
| */ |
| static void |
| s1394_cleanup_node_cfgrom(s1394_hal_t *hal) |
| { |
| uint32_t *cfgrom; |
| int i; |
| |
| for (i = 0; i < IEEE1394_MAX_NODES; i++) { |
| if ((cfgrom = hal->topology_tree[i].cfgrom) != NULL) |
| kmem_free(cfgrom, IEEE1394_CONFIG_ROM_SZ); |
| } |
| } |
| |
| /* |
| * s1394_cycle_too_long_callback() |
| * turns on the cycle master bit of the root node (current Cycle Master) |
| */ |
| void |
| s1394_cycle_too_long_callback(void *arg) |
| { |
| s1394_hal_t *hal; |
| ushort_t root_node_num; |
| ushort_t hal_node_num; |
| uint32_t data; |
| uint_t offset; |
| |
| TNF_PROBE_0_DEBUG(s1394_cycle_too_long_callback_enter, |
| S1394_TNF_SL_STACK, ""); |
| |
| hal = (s1394_hal_t *)arg; |
| |
| /* Clear the cm_timer_cet bit */ |
| mutex_enter(&hal->topology_tree_mutex); |
| mutex_enter(&hal->cm_timer_mutex); |
| hal->cm_timer_set = B_FALSE; |
| mutex_exit(&hal->cm_timer_mutex); |
| |
| /* Get the root node and host node numbers */ |
| root_node_num = hal->number_of_nodes - 1; |
| hal_node_num = IEEE1394_NODE_NUM(hal->node_id); |
| mutex_exit(&hal->topology_tree_mutex); |
| |
| /* If we are the root node, set the cycle master bit */ |
| if (hal_node_num == root_node_num) { |
| data = IEEE1394_CSR_STATE_CMSTR; |
| offset = (IEEE1394_CSR_STATE_SET & IEEE1394_CSR_OFFSET_MASK); |
| (void) HAL_CALL(hal).csr_write(hal->halinfo.hal_private, |
| offset, data); |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_cycle_too_long_callback_exit, |
| S1394_TNF_SL_STACK, ""); |
| } |