/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1999-2000 by Sun Microsystems, Inc. * All rights reserved. */ /* * s1394_csr.c * 1394 Services Layer CSR and Config ROM Routines * Contains all of the CSR callback routines for various required * CSR registers. Also contains routines for their initialization * and destruction, as well as routines to handle the processing * of Config ROM update requests. */ #include #include #include #include #include #include #include #include #include #include static void s1394_CSR_state_clear(cmd1394_cmd_t *req); static void s1394_CSR_state_set(cmd1394_cmd_t *req); static void s1394_CSR_node_ids(cmd1394_cmd_t *req); static void s1394_CSR_reset_start(cmd1394_cmd_t *req); static void s1394_CSR_split_timeout(cmd1394_cmd_t *req); static void s1394_CSR_argument_regs(cmd1394_cmd_t *req); static void s1394_CSR_test_regs(cmd1394_cmd_t *req); static void s1394_CSR_interrupt_regs(cmd1394_cmd_t *req); static void s1394_CSR_clock_regs(cmd1394_cmd_t *req); static void s1394_CSR_message_regs(cmd1394_cmd_t *req); static void s1394_CSR_cycle_time(cmd1394_cmd_t *req); static void s1394_CSR_bus_time(cmd1394_cmd_t *req); static void s1394_CSR_busy_timeout(cmd1394_cmd_t *req); static void s1394_CSR_IRM_regs(cmd1394_cmd_t *req); static void s1394_CSR_topology_map(cmd1394_cmd_t *req); static void s1394_common_CSR_routine(s1394_hal_t *hal, cmd1394_cmd_t *req); static int s1394_init_config_rom_structures(s1394_hal_t *hal); static int s1394_destroy_config_rom_structures(s1394_hal_t *hal); /* * s1394_setup_CSR_space() * setups up the local host's CSR registers and callback routines. */ int s1394_setup_CSR_space(s1394_hal_t *hal) { s1394_addr_space_blk_t *curr_blk; t1394_alloc_addr_t addr; t1394_addr_enable_t rw_flags; int result; /* * Although they are not freed up in this routine, if * one of the s1394_claim_addr_blk() routines fails, * all of the previously successful claims will be * freed up in s1394_destroy_addr_space() upon returning * DDI_FAILURE from this routine. */ rw_flags = T1394_ADDR_RDENBL | T1394_ADDR_WRENBL; /* * STATE_CLEAR * see IEEE 1394-1995, Section 8.3.2.2.1 or * IEEE 1212-1994, Section 7.4.1 */ addr.aa_address = IEEE1394_CSR_STATE_CLEAR; addr.aa_length = IEEE1394_QUADLET; addr.aa_enable = rw_flags; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_state_clear; addr.aa_evts.recv_write_request = s1394_CSR_state_clear; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * STATE_SET * see IEEE 1394-1995, Section 8.3.2.2.2 or * IEEE 1212-1994, Section 7.4.2 */ addr.aa_address = IEEE1394_CSR_STATE_SET; addr.aa_length = IEEE1394_QUADLET; addr.aa_enable = T1394_ADDR_WRENBL; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = NULL; addr.aa_evts.recv_write_request = s1394_CSR_state_set; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * NODE_IDS * see IEEE 1394-1995, Section 8.3.2.2.3 or * IEEE 1212-1994, Section 7.4.3 */ addr.aa_address = IEEE1394_CSR_NODE_IDS; addr.aa_length = IEEE1394_QUADLET; addr.aa_enable = rw_flags; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_node_ids; addr.aa_evts.recv_write_request = s1394_CSR_node_ids; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * RESET_START * see IEEE 1394-1995, Section 8.3.2.2.4 or * IEEE 1212-1994, Section 7.4.4 */ addr.aa_address = IEEE1394_CSR_RESET_START; addr.aa_length = IEEE1394_QUADLET; addr.aa_enable = T1394_ADDR_WRENBL; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = NULL; addr.aa_evts.recv_write_request = s1394_CSR_reset_start; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * SPLIT_TIMEOUT * see IEEE 1394-1995, Section 8.3.2.2.6 or * IEEE 1212-1994, Section 7.4.7 */ addr.aa_address = IEEE1394_CSR_SPLIT_TIMEOUT_HI; addr.aa_length = IEEE1394_OCTLET; addr.aa_enable = rw_flags; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_split_timeout; addr.aa_evts.recv_write_request = s1394_CSR_split_timeout; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * ARGUMENT_HI and ARGUMENT_LO * see IEEE 1394-1995, Section 8.3.2.2.7 or * IEEE 1212-1994, Section 7.4.8 */ addr.aa_address = IEEE1394_CSR_ARG_HI; addr.aa_length = 2 * (IEEE1394_QUADLET); addr.aa_enable = rw_flags; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_argument_regs; addr.aa_evts.recv_write_request = s1394_CSR_argument_regs; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * TEST_START and TEST_STATUS * see IEEE 1394-1995, Section 8.3.2.2.7 or * IEEE 1212-1994, Section 7.4.9 - 7.4.10 */ addr.aa_address = IEEE1394_CSR_TEST_START; addr.aa_length = 2 * (IEEE1394_QUADLET); addr.aa_enable = rw_flags; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_test_regs; addr.aa_evts.recv_write_request = s1394_CSR_test_regs; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * INTERRUPT_TARGET and INTERRUPT_MASK * see IEEE 1394-1995, Section 8.3.2.2.9 or * IEEE 1212-1994, Section 7.4.15 - 7.4.16 */ addr.aa_address = IEEE1394_CSR_INTERRUPT_TARGET; addr.aa_length = 2 * (IEEE1394_QUADLET); addr.aa_enable = rw_flags; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_interrupt_regs; addr.aa_evts.recv_write_request = s1394_CSR_interrupt_regs; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * CLOCK_VALUE, CLOCK_TICK_PERIOD, CLOCK_INFO, etc. * see IEEE 1394-1995, Section 8.3.2.2.10 or * IEEE 1212-1994, Section 7.4.17 - 7.4.20 */ addr.aa_address = IEEE1394_CSR_CLOCK_VALUE; addr.aa_length = IEEE1394_CSR_CLOCK_VALUE_SZ; addr.aa_enable = rw_flags; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_clock_regs; addr.aa_evts.recv_write_request = s1394_CSR_clock_regs; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * MESSAGE_REQUEST and MESSAGE_RESPONSE * see IEEE 1394-1995, Section 8.3.2.2.11 or * IEEE 1212-1994, Section 7.4.21 */ addr.aa_address = IEEE1394_CSR_MESSAGE_REQUEST; addr.aa_length = IEEE1394_CSR_MESSAGE_REQUEST_SZ; addr.aa_enable = rw_flags; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_message_regs; addr.aa_evts.recv_write_request = s1394_CSR_message_regs; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * CYCLE_TIME * see IEEE 1394-1995, Section 8.3.2.3.1 */ addr.aa_address = IEEE1394_SCSR_CYCLE_TIME; addr.aa_length = IEEE1394_QUADLET; addr.aa_enable = rw_flags; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_cycle_time; addr.aa_evts.recv_write_request = s1394_CSR_cycle_time; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * BUS_TIME * see IEEE 1394-1995, Section 8.3.2.3.2 */ addr.aa_address = IEEE1394_SCSR_BUS_TIME; addr.aa_length = IEEE1394_QUADLET; addr.aa_enable = rw_flags; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_bus_time; addr.aa_evts.recv_write_request = s1394_CSR_bus_time; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * BUSY_TIMEOUT * see IEEE 1394-1995, Section 8.3.2.3.5 */ addr.aa_address = IEEE1394_SCSR_BUSY_TIMEOUT; addr.aa_length = IEEE1394_QUADLET; addr.aa_enable = rw_flags; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_busy_timeout; addr.aa_evts.recv_write_request = s1394_CSR_busy_timeout; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * BUS_MANAGER_ID * BANDWIDTH_AVAILABLE * CHANNELS_AVAILABLE * see IEEE 1394-1995, Section 8.3.2.3.6 - 8.3.2.3.8 */ addr.aa_address = IEEE1394_SCSR_BUSMGR_ID; addr.aa_length = 3 * (IEEE1394_QUADLET); addr.aa_enable = T1394_ADDR_RDENBL | T1394_ADDR_LKENBL; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_IRM_regs; addr.aa_evts.recv_write_request = NULL; addr.aa_evts.recv_lock_request = s1394_CSR_IRM_regs; addr.aa_kmem_bufp = NULL; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * Reserved for Configuration ROM * see IEEE 1394-1995, Section 8.3.2.5.3 */ addr.aa_address = IEEE1394_CONFIG_ROM_ADDR; addr.aa_length = IEEE1394_CONFIG_ROM_SZ; result = s1394_reserve_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * TOPOLOGY_MAP * see IEEE 1394-1995, Section 8.3.2.4.1 */ hal->CSR_topology_map = kmem_zalloc(IEEE1394_UCSR_TOPOLOGY_MAP_SZ, KM_SLEEP); addr.aa_address = IEEE1394_UCSR_TOPOLOGY_MAP; addr.aa_length = IEEE1394_UCSR_TOPOLOGY_MAP_SZ; addr.aa_enable = T1394_ADDR_RDENBL; addr.aa_type = T1394_ADDR_FIXED; addr.aa_evts.recv_read_request = s1394_CSR_topology_map; addr.aa_evts.recv_write_request = NULL; addr.aa_evts.recv_lock_request = NULL; addr.aa_kmem_bufp = (caddr_t)hal->CSR_topology_map; addr.aa_arg = hal; result = s1394_claim_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { kmem_free((void *)hal->CSR_topology_map, IEEE1394_UCSR_TOPOLOGY_MAP_SZ); return (DDI_FAILURE); } curr_blk = (s1394_addr_space_blk_t *)(addr.aa_hdl); /* Set up the block so that we free kmem_bufp at detach */ curr_blk->free_kmem_bufp = B_TRUE; /* * Reserve the SPEED_MAP * see IEEE 1394-1995, Section 8.3.2.4.1 * (obsoleted in P1394A) */ addr.aa_address = IEEE1394_UCSR_SPEED_MAP; addr.aa_length = IEEE1394_UCSR_SPEED_MAP_SZ; result = s1394_reserve_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } /* * Reserved - Boundary between reserved Serial Bus * dependent registers and other CSR register space. * See IEEE 1394-1995, Table 8-4 for this address. * * This quadlet is reserved as a way of preventing * the inadvertant allocation of a part of CSR space * that will likely be used by future specifications */ addr.aa_address = IEEE1394_UCSR_RESERVED_BOUNDARY; addr.aa_length = IEEE1394_QUADLET; result = s1394_reserve_addr_blk(hal, &addr); if (result != DDI_SUCCESS) { return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * s1394_CSR_state_clear() * handles all requests to the STATE_CLEAR CSR register. It enforces * that certain bits that can be twiddled only by a given node (IRM or * Bus Manager). */ static void s1394_CSR_state_clear(cmd1394_cmd_t *req) { s1394_hal_t *hal; uint32_t data; uint_t offset; uint_t is_from; uint_t should_be_from; int result; hal = (s1394_hal_t *)req->cmd_callback_arg; /* Register offset */ offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK; /* Verify that request is quadlet aligned */ if ((offset & 0x3) != 0) { req->cmd_result = IEEE1394_RESP_TYPE_ERROR; (void) s1394_send_response(hal, req); return; } /* Only writes from IRM or Bus Mgr allowed (in some cases) */ mutex_enter(&hal->topology_tree_mutex); is_from = IEEE1394_NODE_NUM(req->nodeID); if (hal->bus_mgr_node != -1) should_be_from = IEEE1394_NODE_NUM(hal->bus_mgr_node); else if (hal->IRM_node != -1) should_be_from = IEEE1394_NODE_NUM(hal->IRM_node); else should_be_from = S1394_INVALID_NODE_NUM; mutex_exit(&hal->topology_tree_mutex); switch (req->cmd_type) { case CMD1394_ASYNCH_RD_QUAD: /* * The csr_read() call can return DDI_FAILURE if the HAL * is shutdown or if the register at "offset" is * unimplemented. But although the STATE_CLEAR register * is required to be implemented and readable, we will * return IEEE1394_RESP_ADDRESS_ERROR in the response if * we ever see this error. */ result = HAL_CALL(hal).csr_read(hal->halinfo.hal_private, offset, &data); if (result == DDI_SUCCESS) { req->cmd_u.q.quadlet_data = data; req->cmd_result = IEEE1394_RESP_COMPLETE; } else { req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; } break; case CMD1394_ASYNCH_WR_QUAD: data = req->cmd_u.q.quadlet_data; /* CMSTR bit - request must be from bus_mgr/IRM */ if (is_from != should_be_from) { data = data & ~IEEE1394_CSR_STATE_CMSTR; } mutex_enter(&hal->topology_tree_mutex); /* DREQ bit - disabling DREQ can come from anyone */ if (data & IEEE1394_CSR_STATE_DREQ) { hal->disable_requests_bit = 0; if (hal->hal_state == S1394_HAL_DREQ) hal->hal_state = S1394_HAL_NORMAL; } /* ABDICATE bit */ if (data & IEEE1394_CSR_STATE_ABDICATE) { hal->abdicate_bus_mgr_bit = 0; } mutex_exit(&hal->topology_tree_mutex); /* * The csr_write() call can return DDI_FAILURE if the HAL * is shutdown or if the register at "offset" is * unimplemented. But although the STATE_CLEAR register * is required to be implemented and writeable, we will * return IEEE1394_RESP_ADDRESS_ERROR in the response if * we ever see this error. */ result = HAL_CALL(hal).csr_write(hal->halinfo.hal_private, offset, data); if (result == DDI_SUCCESS) { req->cmd_result = IEEE1394_RESP_COMPLETE; } else { req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; } break; default: req->cmd_result = IEEE1394_RESP_TYPE_ERROR; } (void) s1394_send_response(hal, req); } /* * s1394_CSR_state_set() * handles all requests to the STATE_SET CSR register. It enforces that * certain bits that can be twiddled only by a given node (IRM or Bus * Manager). */ static void s1394_CSR_state_set(cmd1394_cmd_t *req) { s1394_hal_t *hal; uint32_t data; uint_t offset; uint_t is_from; uint_t should_be_from; uint_t hal_node_num; uint_t hal_number_of_nodes; int result; hal = (s1394_hal_t *)req->cmd_callback_arg; /* Register offset */ offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK; /* Verify that request is quadlet aligned */ if ((offset & 0x3) != 0) { req->cmd_result = IEEE1394_RESP_TYPE_ERROR; (void) s1394_send_response(hal, req); return; } /* Only writes from IRM or Bus Mgr allowed (in some cases) */ mutex_enter(&hal->topology_tree_mutex); is_from = IEEE1394_NODE_NUM(req->nodeID); if (hal->bus_mgr_node != -1) should_be_from = IEEE1394_NODE_NUM(hal->bus_mgr_node); else if (hal->IRM_node != -1) should_be_from = IEEE1394_NODE_NUM(hal->IRM_node); else should_be_from = S1394_INVALID_NODE_NUM; hal_node_num = IEEE1394_NODE_NUM(hal->node_id); hal_number_of_nodes = hal->number_of_nodes; mutex_exit(&hal->topology_tree_mutex); switch (req->cmd_type) { case CMD1394_ASYNCH_WR_QUAD: data = req->cmd_u.q.quadlet_data; /* CMSTR bit - request must be from bus_mgr/IRM */ /* & must be root to have bit set */ if ((is_from != should_be_from) || (hal_node_num != (hal_number_of_nodes - 1))) { data = data & ~IEEE1394_CSR_STATE_CMSTR; } mutex_enter(&hal->topology_tree_mutex); /* DREQ bit - only bus_mgr/IRM can set this bit */ if (is_from != should_be_from) { data = data & ~IEEE1394_CSR_STATE_DREQ; } else if (data & IEEE1394_CSR_STATE_DREQ) { hal->disable_requests_bit = 1; if (hal->hal_state == S1394_HAL_NORMAL) hal->hal_state = S1394_HAL_DREQ; } /* ABDICATE bit */ if (data & IEEE1394_CSR_STATE_ABDICATE) { hal->abdicate_bus_mgr_bit = 1; } mutex_exit(&hal->topology_tree_mutex); /* * The csr_write() call can return DDI_FAILURE if the HAL * is shutdown or if the register at "offset" is * unimplemented. But although the STATE_SET register * is required to be implemented and writeable, we will * return IEEE1394_RESP_ADDRESS_ERROR in the response if * we ever see this error. */ result = HAL_CALL(hal).csr_write(hal->halinfo.hal_private, offset, data); if (result == DDI_SUCCESS) { req->cmd_result = IEEE1394_RESP_COMPLETE; } else { req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; } break; default: req->cmd_result = IEEE1394_RESP_TYPE_ERROR; } (void) s1394_send_response(hal, req); } /* * s1394_CSR_node_ids() * handles all requests to the NODE_IDS CSR register. It passes all * requests to the common routine - s1394_common_CSR_routine(). */ static void s1394_CSR_node_ids(cmd1394_cmd_t *req) { s1394_hal_t *hal; hal = (s1394_hal_t *)req->cmd_callback_arg; s1394_common_CSR_routine(hal, req); } /* * s1394_CSR_reset_start() * handles all requests to the RESET_START CSR register. Only write * requests are legal, everything else gets a type_error response. */ static void s1394_CSR_reset_start(cmd1394_cmd_t *req) { s1394_hal_t *hal; uint32_t data; uint_t offset; hal = (s1394_hal_t *)req->cmd_callback_arg; /* RESET_START register offset */ offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK; /* Verify that request is quadlet aligned */ if ((offset & 0x3) != 0) { req->cmd_result = IEEE1394_RESP_TYPE_ERROR; (void) s1394_send_response(hal, req); return; } switch (req->cmd_type) { case CMD1394_ASYNCH_WR_QUAD: data = req->cmd_u.q.quadlet_data; /* * The csr_write() call can return DDI_FAILURE if the HAL * is shutdown or if the register at "offset" is * unimplemented. Because we don't do any thing with * the RESET_START register we will ignore failures and * return IEEE1394_RESP_COMPLETE regardless. */ (void) HAL_CALL(hal).csr_write(hal->halinfo.hal_private, offset, data); req->cmd_result = IEEE1394_RESP_COMPLETE; break; default: req->cmd_result = IEEE1394_RESP_TYPE_ERROR; } (void) s1394_send_response(hal, req); } /* * s1394_CSR_split_timeout() * handles all requests to the SPLIT_TIMEOUT CSR register. It passes all * requests to the common routine - s1394_common_CSR_routine(). */ static void s1394_CSR_split_timeout(cmd1394_cmd_t *req) { s1394_hal_t *hal; hal = (s1394_hal_t *)req->cmd_callback_arg; s1394_common_CSR_routine(hal, req); } /* * s1394_CSR_argument_regs() * handles all requests to the ARGUMENT CSR registers. It passes all * requests to the common routine - s1394_common_CSR_routine(). */ static void s1394_CSR_argument_regs(cmd1394_cmd_t *req) { s1394_hal_t *hal; hal = (s1394_hal_t *)req->cmd_callback_arg; s1394_common_CSR_routine(hal, req); } /* * s1394_CSR_test_regs() * handles all requests to the TEST CSR registers. It passes all requests * to the common routine - s1394_common_CSR_routine(). */ static void s1394_CSR_test_regs(cmd1394_cmd_t *req) { s1394_hal_t *hal; uint_t offset; hal = (s1394_hal_t *)req->cmd_callback_arg; /* TEST register offset */ offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK; /* TEST_STATUS is Read-Only */ if ((offset == (IEEE1394_CSR_TEST_STATUS & IEEE1394_CSR_OFFSET_MASK)) && (req->cmd_type == CMD1394_ASYNCH_WR_QUAD)) { req->cmd_result = IEEE1394_RESP_TYPE_ERROR; (void) s1394_send_response(hal, req); } else { s1394_common_CSR_routine(hal, req); } } /* * s1394_CSR_interrupt_regs() * handles all requests to the INTERRUPT CSR registers. It passes all * requests to the common routine - s1394_common_CSR_routine(). */ static void s1394_CSR_interrupt_regs(cmd1394_cmd_t *req) { s1394_hal_t *hal; hal = (s1394_hal_t *)req->cmd_callback_arg; s1394_common_CSR_routine(hal, req); } /* * s1394_CSR_clock_regs() * handles all requests to the CLOCK CSR registers. It passes all * requests to the common routine - s1394_common_CSR_routine(). */ static void s1394_CSR_clock_regs(cmd1394_cmd_t *req) { s1394_hal_t *hal; hal = (s1394_hal_t *)req->cmd_callback_arg; s1394_common_CSR_routine(hal, req); } /* * s1394_CSR_message_regs() * handles all requests to the MESSAGE CSR registers. It passes all * requests to the common routine - s1394_common_CSR_routine(). */ static void s1394_CSR_message_regs(cmd1394_cmd_t *req) { s1394_hal_t *hal; hal = (s1394_hal_t *)req->cmd_callback_arg; s1394_common_CSR_routine(hal, req); } /* * s1394_CSR_cycle_time() * handles all requests to the CYCLE_TIME CSR register. */ static void s1394_CSR_cycle_time(cmd1394_cmd_t *req) { s1394_hal_t *hal; uint32_t data; uint_t offset; int result; hal = (s1394_hal_t *)req->cmd_callback_arg; /* CYCLE_TIME register offset */ offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK; /* Verify that request is quadlet aligned */ if ((offset & 0x3) != 0) { req->cmd_result = IEEE1394_RESP_TYPE_ERROR; (void) s1394_send_response(hal, req); return; } switch (req->cmd_type) { case CMD1394_ASYNCH_RD_QUAD: /* * The csr_read() call can return DDI_FAILURE if the HAL * is shutdown or if the register at "offset" is * unimplemented. But although the CYCLE_TIME register * is required to be implemented on devices capable of * providing isochronous services (like us), we will * return IEEE1394_RESP_ADDRESS_ERROR in the response * if we ever see this error. */ result = HAL_CALL(hal).csr_read(hal->halinfo.hal_private, offset, &data); if (result == DDI_SUCCESS) { req->cmd_u.q.quadlet_data = data; req->cmd_result = IEEE1394_RESP_COMPLETE; } else { req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; } break; case CMD1394_ASYNCH_WR_QUAD: data = req->cmd_u.q.quadlet_data; /* * The csr_write() call can return DDI_FAILURE if the HAL * is shutdown or if the register at "offset" is * unimplemented. But although the CYCLE_TIME register * is required to be implemented on devices capable of * providing isochronous services (like us), the effects * of a write are "node-dependent" so we will return * IEEE1394_RESP_ADDRESS_ERROR in the response if we * ever see this error. */ result = HAL_CALL(hal).csr_write(hal->halinfo.hal_private, offset, data); if (result == DDI_SUCCESS) { req->cmd_result = IEEE1394_RESP_COMPLETE; } else { req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; } break; default: req->cmd_result = IEEE1394_RESP_TYPE_ERROR; } (void) s1394_send_response(hal, req); } /* * s1394_CSR_bus_time() * handles all requests to the BUS_TIME CSR register. It enforces that * only a broadcast write request from the IRM or Bus Manager can change * its value. */ static void s1394_CSR_bus_time(cmd1394_cmd_t *req) { s1394_hal_t *hal; uint32_t data; uint_t offset; uint_t is_from; uint_t should_be_from; int result; hal = (s1394_hal_t *)req->cmd_callback_arg; /* BUS_TIME register offset */ offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK; /* Verify that request is quadlet aligned */ if ((offset & 0x3) != 0) { req->cmd_result = IEEE1394_RESP_TYPE_ERROR; (void) s1394_send_response(hal, req); return; } switch (req->cmd_type) { case CMD1394_ASYNCH_RD_QUAD: /* * The csr_read() call can return DDI_FAILURE if the HAL * is shutdown or if the register at "offset" is * unimplemented. But although the BUS_TIME register * is required to be implemented by devices capable of * being cycle master (like us), we will return * IEEE1394_RESP_ADDRESS_ERROR in the response if we * ever see this error. */ result = HAL_CALL(hal).csr_read(hal->halinfo.hal_private, offset, &data); if (result == DDI_SUCCESS) { req->cmd_u.q.quadlet_data = data; req->cmd_result = IEEE1394_RESP_COMPLETE; } else { req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; } break; case CMD1394_ASYNCH_WR_QUAD: /* Only broadcast writes from IRM or Bus Mgr allowed */ mutex_enter(&hal->topology_tree_mutex); is_from = IEEE1394_NODE_NUM(req->nodeID); if (hal->bus_mgr_node != -1) should_be_from = IEEE1394_NODE_NUM(hal->bus_mgr_node); else if (hal->IRM_node != -1) should_be_from = IEEE1394_NODE_NUM(hal->IRM_node); else should_be_from = S1394_INVALID_NODE_NUM; mutex_exit(&hal->topology_tree_mutex); if ((req->broadcast != 1) || (is_from != should_be_from)) { req->cmd_result = IEEE1394_RESP_TYPE_ERROR; break; } data = req->cmd_u.q.quadlet_data; /* * The csr_write() call can return DDI_FAILURE if the HAL * is shutdown or if the register at "offset" is * unimplemented. But although the BUS_TIME register * is required to be implemented on devices capable of * being cycle master (like us), we will return * IEEE1394_RESP_ADDRESS_ERROR in the response if we * ever see this error. */ result = HAL_CALL(hal).csr_write(hal->halinfo.hal_private, offset, data); if (result == DDI_SUCCESS) { req->cmd_result = IEEE1394_RESP_COMPLETE; } else { req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; } break; default: req->cmd_result = IEEE1394_RESP_TYPE_ERROR; } (void) s1394_send_response(hal, req); } /* * s1394_CSR_busy_timeout() * handles all requests to the BUSY_TIMEOUT CSR register. It passes all * requests to the common routine - s1394_common_CSR_routine(). */ static void s1394_CSR_busy_timeout(cmd1394_cmd_t *req) { s1394_hal_t *hal; hal = (s1394_hal_t *)req->cmd_callback_arg; s1394_common_CSR_routine(hal, req); } /* * s1394_CSR_IRM_regs() * handles all requests to the IRM registers, including BANDWIDTH_AVAILABLE, * CHANNELS_AVAILABLE, and the BUS_MANAGER_ID. Only quadlet read and lock * requests are allowed. */ static void s1394_CSR_IRM_regs(cmd1394_cmd_t *req) { s1394_hal_t *hal; uint32_t generation; uint32_t data; uint32_t compare; uint32_t swap; uint32_t old; uint_t offset; int result; hal = (s1394_hal_t *)req->cmd_callback_arg; /* IRM register offset */ offset = (req->cmd_addr & IEEE1394_CSR_OFFSET_MASK); /* Verify that request is quadlet aligned */ if ((offset & 0x3) != 0) { req->cmd_result = IEEE1394_RESP_TYPE_ERROR; (void) s1394_send_response(hal, req); return; } switch (req->cmd_type) { case CMD1394_ASYNCH_RD_QUAD: /* * The csr_read() call can return DDI_FAILURE if the HAL * is shutdown or if the register at "offset" is * unimplemented. In many cases these registers will * have been implemented in HW. We are not likely to ever * receive this callback. If we do, though, we will * return IEEE1394_RESP_ADDRESS_ERROR when we get an error * and IEEE1394_RESP_COMPLETE for success. */ result = HAL_CALL(hal).csr_read(hal->halinfo.hal_private, offset, &data); if (result == DDI_SUCCESS) { req->cmd_u.q.quadlet_data = data; req->cmd_result = IEEE1394_RESP_COMPLETE; } else { req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; } break; case CMD1394_ASYNCH_LOCK_32: mutex_enter(&hal->topology_tree_mutex); generation = hal->generation_count; mutex_exit(&hal->topology_tree_mutex); if (req->cmd_u.l32.lock_type == CMD1394_LOCK_COMPARE_SWAP) { compare = req->cmd_u.l32.arg_value; swap = req->cmd_u.l32.data_value; /* * The csr_cswap32() call can return DDI_FAILURE if * the HAL is shutdown, if the register at "offset" * is unimplemented, or if the generation has changed. * In the last case, it shouldn't matter because the * call to s1394_send_response will fail on a bad * generation and the command will be freed. */ result = HAL_CALL(hal).csr_cswap32( hal->halinfo.hal_private, generation, offset, compare, swap, &old); if (result == DDI_SUCCESS) { req->cmd_u.l32.old_value = old; req->cmd_result = IEEE1394_RESP_COMPLETE; } else { req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; } break; } else { req->cmd_result = IEEE1394_RESP_TYPE_ERROR; } break; default: req->cmd_result = IEEE1394_RESP_TYPE_ERROR; } (void) s1394_send_response(hal, req); } /* * s1394_CSR_topology_map() * handles all request for the TOPOLOGY_MAP[]. Since it is implemented * with backing store, there isn't much to do besides return success or * failure. */ static void s1394_CSR_topology_map(cmd1394_cmd_t *req) { s1394_hal_t *hal; hal = (s1394_hal_t *)req->cmd_callback_arg; /* Make sure it's a quadlet read request */ if (req->cmd_type == CMD1394_ASYNCH_RD_QUAD) req->cmd_result = IEEE1394_RESP_COMPLETE; else req->cmd_result = IEEE1394_RESP_TYPE_ERROR; (void) s1394_send_response(hal, req); } /* * s1394_CSR_topology_map_update() * is used to update the local host's TOPOLOGY_MAP[] buffer. It copies in * the SelfID packets, updates the generation and other fields, and * computes the necessary CRC values before returning. * Callers must be holding the topology_tree_mutex. */ void s1394_CSR_topology_map_update(s1394_hal_t *hal) { s1394_selfid_pkt_t *selfid_packet; uint32_t *tm_ptr; uint32_t *data_ptr; uint32_t node_count; uint32_t self_id_count; uint_t CRC; uint32_t length; int i, j, c; ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); tm_ptr = (uint32_t *)hal->CSR_topology_map; data_ptr = (uint32_t *)&(tm_ptr[3]); c = 0; for (i = 0; i < hal->number_of_nodes; i++) { j = -1; selfid_packet = hal->selfid_ptrs[i]; do { j++; data_ptr[c++] = selfid_packet[j].spkt_data; } while (IEEE1394_SELFID_ISMORE(&selfid_packet[j])); } /* Update Topology Map Generation */ tm_ptr[1] = tm_ptr[1] + 1; /* Update Node_Count and Self_Id_Count */ node_count = (i & IEEE1394_TOP_MAP_LEN_MASK); self_id_count = (c & IEEE1394_TOP_MAP_LEN_MASK); tm_ptr[2] = (node_count << IEEE1394_TOP_MAP_LEN_SHIFT) | (self_id_count); /* Calculate CRC-16 */ length = self_id_count + 2; CRC = s1394_CRC16(&(tm_ptr[1]), length); tm_ptr[0] = (length << IEEE1394_TOP_MAP_LEN_SHIFT) | CRC; } /* * s1394_CSR_topology_map_disable() * is used to disable the local host's TOPOLOGY_MAP[] buffer (during bus * reset processing). It sets the topology map's length to zero to * indicate that it is invalid. */ void s1394_CSR_topology_map_disable(s1394_hal_t *hal) { uint32_t *tm_ptr; ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); tm_ptr = (uint32_t *)hal->CSR_topology_map; /* Set length = 0 */ tm_ptr[0] = tm_ptr[0] & IEEE1394_TOP_MAP_LEN_MASK; } /* * s1394_common_CSR_routine() * is used to handle most of the CSR register requests. They are passed * to the appropriate HAL entry point for further processing. Then they * are filled in with an appropriate response code, and the response is sent. */ static void s1394_common_CSR_routine(s1394_hal_t *hal, cmd1394_cmd_t *req) { uint32_t data; uint_t offset; int result; /* Register offset */ offset = (req->cmd_addr & IEEE1394_CSR_OFFSET_MASK); /* Verify that request is quadlet aligned */ if ((offset & 0x3) != 0) { req->cmd_result = IEEE1394_RESP_TYPE_ERROR; (void) s1394_send_response(hal, req); } switch (req->cmd_type) { case CMD1394_ASYNCH_RD_QUAD: /* * The csr_read() call can return DDI_FAILURE if the HAL * is shutdown or if the register at "offset" is * unimplemented. We will return IEEE1394_RESP_ADDRESS_ERROR * in the response if we see this error. */ result = HAL_CALL(hal).csr_read(hal->halinfo.hal_private, offset, &data); if (result == DDI_SUCCESS) { req->cmd_u.q.quadlet_data = data; req->cmd_result = IEEE1394_RESP_COMPLETE; } else { req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; } break; case CMD1394_ASYNCH_WR_QUAD: data = req->cmd_u.q.quadlet_data; /* * The csr_read() call can return DDI_FAILURE if the HAL * is shutdown or if the register at "offset" is * unimplemented. We will return IEEE1394_RESP_ADDRESS_ERROR * in the response if we see this error. */ result = HAL_CALL(hal).csr_write(hal->halinfo.hal_private, offset, data); if (result == DDI_SUCCESS) { req->cmd_result = IEEE1394_RESP_COMPLETE; } else { req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; } break; default: req->cmd_result = IEEE1394_RESP_TYPE_ERROR; } (void) s1394_send_response(hal, req); } /* * s1394_init_local_config_rom() * is called in the HAL attach routine - h1394_attach() - to setup the * initial Config ROM entries on the local host, including the * bus_info_block and the root and unit directories. */ int s1394_init_local_config_rom(s1394_hal_t *hal) { uint32_t *config_rom; uint32_t *node_unique_id_leaf; uint32_t *unit_dir; uint32_t *text_leaf; void *n_handle; uint64_t guid; uint32_t guid_hi, guid_lo; uint32_t bus_capabilities; uint32_t irmc, g; uint32_t module_vendor_id; uint32_t node_capabilities; uint32_t root_dir_len; uint32_t CRC; int status, i, ret; /* Setup Config ROM mutex */ mutex_init(&hal->local_config_rom_mutex, NULL, MUTEX_DRIVER, hal->halinfo.hw_interrupt); /* Allocate 1K for the Config ROM buffer */ hal->local_config_rom = (uint32_t *)kmem_zalloc(IEEE1394_CONFIG_ROM_SZ, KM_SLEEP); /* Allocate 1K for the temporary buffer */ hal->temp_config_rom_buf = (uint32_t *)kmem_zalloc( IEEE1394_CONFIG_ROM_SZ, KM_SLEEP); config_rom = hal->local_config_rom; /* Lock the Config ROM buffer */ mutex_enter(&hal->local_config_rom_mutex); /* Build the config ROM structures */ ret = s1394_init_config_rom_structures(hal); if (ret != DDI_SUCCESS) { /* Unlock the Config ROM buffer */ mutex_exit(&hal->local_config_rom_mutex); kmem_free((void *)hal->temp_config_rom_buf, IEEE1394_CONFIG_ROM_SZ); kmem_free((void *)hal->local_config_rom, IEEE1394_CONFIG_ROM_SZ); mutex_destroy(&hal->local_config_rom_mutex); return (DDI_FAILURE); } /* Build the Bus_Info_Block - see IEEE 1394-1995, Section 8.3.2.5.4 */ bus_capabilities = hal->halinfo.bus_capabilities; /* * If we are Isoch Resource Manager capable then we are * Bus Manager capable too. */ irmc = (bus_capabilities & IEEE1394_BIB_IRMC_MASK) >> IEEE1394_BIB_IRMC_SHIFT; if (irmc) bus_capabilities = bus_capabilities | IEEE1394_BIB_BMC_MASK; /* * Set generation to P1394a valid (but changeable) * Even if we have a 1995 PHY, we will still provide * certain P1394A functionality (especially with respect * to Config ROM updates). So we must publish this * information. */ g = 2 << IEEE1394_BIB_GEN_SHIFT; bus_capabilities = bus_capabilities | g; /* Get the GUID */ guid = hal->halinfo.guid; guid_hi = (uint32_t)(guid >> 32); guid_lo = (uint32_t)(guid & 0x00000000FFFFFFFF); config_rom[1] = 0x31333934; /* "1394" */ config_rom[2] = bus_capabilities; config_rom[3] = guid_hi; config_rom[4] = guid_lo; /* The CRC covers only our Bus_Info_Block */ CRC = s1394_CRC16(&config_rom[1], 4); config_rom[0] = (0x04040000) | CRC; /* Do byte-swapping if necessary (x86) */ for (i = 0; i < IEEE1394_BIB_QUAD_SZ; i++) config_rom[i] = T1394_DATA32(config_rom[i]); /* Build the Root_Directory - see IEEE 1394-1995, Section 8.3.2.5.5 */ /* MODULE_VENDOR_ID - see IEEE 1394-1995, Section 8.3.2.5.5.1 */ module_vendor_id = S1394_SUNW_OUI; /* NODE_CAPABILITIES - see IEEE 1394-1995, Section 8.3.2.5.5.2 */ node_capabilities = hal->halinfo.node_capabilities & IEEE1212_NODE_CAPABILITIES_MASK; root_dir_len = 2; config_rom[6] = (IEEE1212_MODULE_VENDOR_ID << IEEE1212_KEY_VALUE_SHIFT) | module_vendor_id; config_rom[7] = (IEEE1212_NODE_CAPABILITIES << IEEE1212_KEY_VALUE_SHIFT) | node_capabilities; CRC = s1394_CRC16(&config_rom[6], root_dir_len); config_rom[IEEE1394_BIB_QUAD_SZ] = (root_dir_len << IEEE1394_CFG_ROM_LEN_SHIFT) | CRC; /* Do byte-swapping if necessary (x86) */ for (i = IEEE1394_BIB_QUAD_SZ; i < 8; i++) config_rom[i] = T1394_DATA32(config_rom[i]); /* Build the Root Text leaf - see IEEE 1394-1995, Section 8.3.2.5.7 */ text_leaf = (uint32_t *)kmem_zalloc(S1394_ROOT_TEXT_LEAF_SZ, KM_SLEEP); text_leaf[1] = 0x00000000; text_leaf[2] = 0x00000000; text_leaf[3] = 0x53756e20; /* "Sun " */ text_leaf[4] = 0x4d696372; /* "Micr" */ text_leaf[5] = 0x6f737973; /* "osys" */ text_leaf[6] = 0x74656d73; /* "tems" */ text_leaf[7] = 0x2c20496e; /* ", In" */ text_leaf[8] = 0x632e0000; /* "c." */ CRC = s1394_CRC16(&text_leaf[1], S1394_ROOT_TEXT_LEAF_QUAD_SZ - 1); text_leaf[0] = (0x00080000) | CRC; /* Do byte-swapping if necessary (x86) */ for (i = 0; i < 9; i++) text_leaf[i] = T1394_DATA32(text_leaf[i]); ret = s1394_add_config_rom_entry(hal, S1394_ROOT_TEXT_KEY, text_leaf, S1394_ROOT_TEXT_LEAF_QUAD_SZ, &n_handle, &status); if (ret != DDI_SUCCESS) { kmem_free((void *)text_leaf, S1394_ROOT_TEXT_LEAF_SZ); /* Destroy the config_rom structures */ (void) s1394_destroy_config_rom_structures(hal); /* Unlock the Config ROM buffer */ mutex_exit(&hal->local_config_rom_mutex); kmem_free((void *)hal->temp_config_rom_buf, IEEE1394_CONFIG_ROM_SZ); kmem_free((void *)hal->local_config_rom, IEEE1394_CONFIG_ROM_SZ); mutex_destroy(&hal->local_config_rom_mutex); return (DDI_FAILURE); } kmem_free((void *)text_leaf, S1394_ROOT_TEXT_LEAF_SZ); /* Build the Node_Unique_Id leaf - IEEE 1394-1995, Sect. 8.3.2.5.7.1 */ node_unique_id_leaf = (uint32_t *)kmem_zalloc(S1394_NODE_UNIQUE_ID_SZ, KM_SLEEP); node_unique_id_leaf[1] = guid_hi; node_unique_id_leaf[2] = guid_lo; CRC = s1394_CRC16(&node_unique_id_leaf[1], S1394_NODE_UNIQUE_ID_QUAD_SZ - 1); node_unique_id_leaf[0] = (0x00020000) | CRC; /* Do byte-swapping if necessary (x86) */ for (i = 0; i < S1394_NODE_UNIQUE_ID_QUAD_SZ; i++) node_unique_id_leaf[i] = T1394_DATA32(node_unique_id_leaf[i]); ret = s1394_add_config_rom_entry(hal, S1394_NODE_UNIQUE_ID_KEY, node_unique_id_leaf, S1394_NODE_UNIQUE_ID_QUAD_SZ, &n_handle, &status); if (ret != DDI_SUCCESS) { kmem_free((void *)node_unique_id_leaf, S1394_NODE_UNIQUE_ID_SZ); /* Destroy the config_rom structures */ (void) s1394_destroy_config_rom_structures(hal); /* Unlock the Config ROM buffer */ mutex_exit(&hal->local_config_rom_mutex); kmem_free((void *)hal->temp_config_rom_buf, IEEE1394_CONFIG_ROM_SZ); kmem_free((void *)hal->local_config_rom, IEEE1394_CONFIG_ROM_SZ); mutex_destroy(&hal->local_config_rom_mutex); return (DDI_FAILURE); } kmem_free((void *)node_unique_id_leaf, S1394_NODE_UNIQUE_ID_SZ); /* Build the Unit_Directory for 1394 Framework */ unit_dir = (uint32_t *)kmem_zalloc(S1394_UNIT_DIR_SZ, KM_SLEEP); unit_dir[1] = 0x12080020; /* Sun Microsystems */ unit_dir[2] = 0x13000001; /* Version 1 */ unit_dir[3] = 0x81000001; /* offset to the text leaf */ CRC = s1394_CRC16(&unit_dir[1], 3); unit_dir[0] = (0x00030000) | CRC; /* Do byte-swapping if necessary (x86) */ for (i = 0; i < 4; i++) unit_dir[i] = T1394_DATA32(unit_dir[i]); /* Build the Unit Directory text leaf */ unit_dir[5] = 0x00000000; unit_dir[6] = 0x00000000; unit_dir[7] = 0x536f6c61; /* "Sola" */ unit_dir[8] = 0x72697320; /* "ris " */ unit_dir[9] = 0x31333934; /* "1394" */ unit_dir[10] = 0x20535720; /* " SW " */ unit_dir[11] = 0x4672616d; /* "Fram" */ unit_dir[12] = 0x65576f72; /* "ewor" */ unit_dir[13] = 0x6b000000; /* "k" */ CRC = s1394_CRC16(&unit_dir[5], 9); unit_dir[4] = (0x00090000) | CRC; /* Do byte-swapping if necessary (x86) */ for (i = 4; i < S1394_UNIT_DIR_QUAD_SZ; i++) unit_dir[i] = T1394_DATA32(unit_dir[i]); ret = s1394_add_config_rom_entry(hal, S1394_UNIT_DIR_KEY, unit_dir, S1394_UNIT_DIR_QUAD_SZ, &n_handle, &status); if (ret != DDI_SUCCESS) { kmem_free((void *)unit_dir, S1394_UNIT_DIR_SZ); /* Destroy the config_rom structures */ (void) s1394_destroy_config_rom_structures(hal); /* Unlock the Config ROM buffer */ mutex_exit(&hal->local_config_rom_mutex); kmem_free((void *)hal->temp_config_rom_buf, IEEE1394_CONFIG_ROM_SZ); /* Free the 1K for the Config ROM buffer */ kmem_free((void *)hal->local_config_rom, IEEE1394_CONFIG_ROM_SZ); mutex_destroy(&hal->local_config_rom_mutex); return (DDI_FAILURE); } kmem_free((void *)unit_dir, S1394_UNIT_DIR_SZ); hal->config_rom_update_amount = (IEEE1394_CONFIG_ROM_QUAD_SZ - hal->free_space); /* Unlock the Config ROM buffer */ mutex_exit(&hal->local_config_rom_mutex); /* * The update_config_rom() call can return DDI_FAILURE if the * HAL is shutdown. */ (void) HAL_CALL(hal).update_config_rom(hal->halinfo.hal_private, config_rom, IEEE1394_CONFIG_ROM_QUAD_SZ); return (DDI_SUCCESS); } /* * s1394_destroy_local_config_rom() * is necessary for h1394_detach(). It undoes all the work that * s1394_init_local_config_rom() had setup and more. By pulling * everything out of the conig rom structures and freeing them and their * associated mutexes, the Config ROM is completely cleaned up. */ void s1394_destroy_local_config_rom(s1394_hal_t *hal) { /* Lock the Config ROM buffer */ mutex_enter(&hal->local_config_rom_mutex); /* Destroy the config_rom structures */ (void) s1394_destroy_config_rom_structures(hal); /* Unlock the Config ROM buffer */ mutex_exit(&hal->local_config_rom_mutex); /* Free the 1K for the temporary buffer */ kmem_free((void *)hal->temp_config_rom_buf, IEEE1394_CONFIG_ROM_SZ); /* Free the 1K for the Config ROM buffer */ kmem_free((void *)hal->local_config_rom, IEEE1394_CONFIG_ROM_SZ); /* Setup Config ROM mutex */ mutex_destroy(&hal->local_config_rom_mutex); } /* * s1394_init_config_rom_structures() * initializes the structures that are used to maintain the local Config ROM. * Callers must be holding the local_config_rom_mutex. */ static int s1394_init_config_rom_structures(s1394_hal_t *hal) { s1394_config_rom_t *root_directory; s1394_config_rom_t *rest_of_config_rom; ASSERT(MUTEX_HELD(&hal->local_config_rom_mutex)); root_directory = (s1394_config_rom_t *)kmem_zalloc( sizeof (s1394_config_rom_t), KM_SLEEP); root_directory->cfgrom_used = B_TRUE; root_directory->cfgrom_addr_lo = IEEE1394_BIB_QUAD_SZ; root_directory->cfgrom_addr_hi = IEEE1394_BIB_QUAD_SZ + 2; rest_of_config_rom = (s1394_config_rom_t *)kmem_zalloc( sizeof (s1394_config_rom_t), KM_SLEEP); rest_of_config_rom->cfgrom_used = B_FALSE; rest_of_config_rom->cfgrom_addr_lo = root_directory->cfgrom_addr_hi + 1; rest_of_config_rom->cfgrom_addr_hi = IEEE1394_CONFIG_ROM_QUAD_SZ - 1; root_directory->cfgrom_next = rest_of_config_rom; root_directory->cfgrom_prev = NULL; rest_of_config_rom->cfgrom_next = NULL; rest_of_config_rom->cfgrom_prev = root_directory; hal->root_directory = root_directory; hal->free_space = IEEE1394_CONFIG_ROM_QUAD_SZ - (rest_of_config_rom->cfgrom_addr_lo); return (DDI_SUCCESS); } /* * s1394_destroy_config_rom_structures() * is used to destroy the structures that maintain the local Config ROM. * Callers must be holding the local_config_rom_mutex. */ static int s1394_destroy_config_rom_structures(s1394_hal_t *hal) { s1394_config_rom_t *curr_blk; s1394_config_rom_t *next_blk; ASSERT(MUTEX_HELD(&hal->local_config_rom_mutex)); curr_blk = hal->root_directory; while (curr_blk != NULL) { next_blk = curr_blk->cfgrom_next; kmem_free(curr_blk, sizeof (s1394_config_rom_t)); curr_blk = next_blk; } return (DDI_SUCCESS); } /* * s1394_add_config_rom_entry() * is used to add a new entry to the local host's config ROM. By * specifying a key and a buffer, it is possible to update the Root * Directory to point to the new entry (in buffer). Additionally, all * of the relevant CRCs, lengths, and generations are updated as well. * By returning a Config ROM "handle", we can allow targets to remove * the corresponding entry. * Callers must be holding the local_config_rom_mutex. */ int s1394_add_config_rom_entry(s1394_hal_t *hal, uint8_t key, uint32_t *buffer, uint_t size, void **handle, int *status) { s1394_config_rom_t *curr_blk; s1394_config_rom_t *new_blk; uint32_t *config_rom; uint32_t *temp_buf; uint32_t CRC; uint_t tmp_offset; uint_t tmp_size, temp; uint_t last_entry_offset; int i; ASSERT(MUTEX_HELD(&hal->local_config_rom_mutex)); if (size > hal->free_space) { /* Out of space */ *status = CMD1394_ERSRC_CONFLICT; return (DDI_FAILURE); } config_rom = hal->local_config_rom; temp_buf = hal->temp_config_rom_buf; /* Copy the Bus_Info_Block */ bcopy(&config_rom[0], &temp_buf[0], IEEE1394_BIB_SZ); /* Copy and add to the Root_Directory */ tmp_offset = hal->root_directory->cfgrom_addr_lo; tmp_size = (hal->root_directory->cfgrom_addr_hi - tmp_offset) + 1; tmp_size = tmp_size + 1; /* For the new entry */ bcopy(&config_rom[tmp_offset], &temp_buf[tmp_offset], tmp_size << 2); last_entry_offset = hal->root_directory->cfgrom_addr_hi + 1; curr_blk = hal->root_directory; curr_blk->cfgrom_addr_hi = curr_blk->cfgrom_addr_hi + 1; while (curr_blk->cfgrom_next != NULL) { if (curr_blk->cfgrom_next->cfgrom_used == B_TRUE) { tmp_offset = curr_blk->cfgrom_next->cfgrom_addr_lo; tmp_size = (curr_blk->cfgrom_next->cfgrom_addr_hi - tmp_offset) + 1; bcopy(&config_rom[tmp_offset], &temp_buf[tmp_offset + 1], tmp_size << 2); curr_blk->cfgrom_next->cfgrom_addr_lo++; curr_blk->cfgrom_next->cfgrom_addr_hi++; last_entry_offset = curr_blk->cfgrom_next->cfgrom_addr_hi; tmp_offset = curr_blk->cfgrom_next->root_dir_offset; /* Swap... add one... then unswap */ temp = T1394_DATA32(temp_buf[tmp_offset]); temp++; temp_buf[tmp_offset] = T1394_DATA32(temp); } else { curr_blk->cfgrom_next->cfgrom_addr_lo++; hal->free_space--; break; } curr_blk = curr_blk->cfgrom_next; } /* Get the pointer to the "free" space */ curr_blk = curr_blk->cfgrom_next; /* Is it an exact fit? */ if (hal->free_space == size) { curr_blk->cfgrom_used = B_TRUE; } else { /* Must break this piece */ new_blk = (s1394_config_rom_t *)kmem_zalloc( sizeof (s1394_config_rom_t), KM_SLEEP); if (new_blk == NULL) { return (DDI_FAILURE); } new_blk->cfgrom_addr_hi = curr_blk->cfgrom_addr_hi; new_blk->cfgrom_addr_lo = curr_blk->cfgrom_addr_lo + size; curr_blk->cfgrom_addr_hi = new_blk->cfgrom_addr_lo - 1; new_blk->cfgrom_next = curr_blk->cfgrom_next; curr_blk->cfgrom_next = new_blk; new_blk->cfgrom_prev = curr_blk; curr_blk->cfgrom_used = B_TRUE; last_entry_offset = curr_blk->cfgrom_addr_hi; } hal->free_space = hal->free_space - size; /* Copy in the new entry */ tmp_offset = curr_blk->cfgrom_addr_lo; bcopy(buffer, &temp_buf[tmp_offset], size << 2); /* Update root directory */ tmp_offset = hal->root_directory->cfgrom_addr_hi; tmp_size = tmp_offset - hal->root_directory->cfgrom_addr_lo; curr_blk->root_dir_offset = tmp_offset; tmp_offset = curr_blk->cfgrom_addr_lo - tmp_offset; temp_buf[hal->root_directory->cfgrom_addr_hi] = T1394_DATA32((((uint32_t)key) << IEEE1212_KEY_VALUE_SHIFT) | tmp_offset); tmp_offset = hal->root_directory->cfgrom_addr_lo; /* Do byte-swapping if necessary (x86) */ for (i = (tmp_offset + 1); i <= hal->root_directory->cfgrom_addr_hi; i++) temp_buf[i] = T1394_DATA32(temp_buf[i]); CRC = s1394_CRC16(&temp_buf[tmp_offset + 1], tmp_size); temp_buf[tmp_offset] = (tmp_size << IEEE1394_CFG_ROM_LEN_SHIFT) | CRC; /* Redo byte-swapping if necessary (x86) */ for (i = tmp_offset; i <= hal->root_directory->cfgrom_addr_hi; i++) temp_buf[i] = T1394_DATA32(temp_buf[i]); /* Copy it back to config_rom buffer */ last_entry_offset++; bcopy(&temp_buf[0], &config_rom[0], last_entry_offset << 2); /* Return a handle to this block */ *handle = curr_blk; *status = T1394_NOERROR; return (DDI_SUCCESS); } /* * s1394_remove_config_rom_entry() * is used to remove an entry from the local host's config ROM. By * specifying the Config ROM "handle" that was given in the allocation, * it is possible to remove the entry. Subsequently, the Config ROM is * updated again. * Callers must be holding the local_config_rom_mutex. */ int s1394_remove_config_rom_entry(s1394_hal_t *hal, void **handle, int *status) { s1394_config_rom_t *del_blk; s1394_config_rom_t *curr_blk; s1394_config_rom_t *last_blk; s1394_config_rom_t *free_blk; uint32_t *config_rom; uint32_t *temp_buf; uint32_t entry; uint_t CRC; uint_t root_offset; uint_t del_offset; uint_t tmp_offset; uint_t tmp_size; int i; ASSERT(MUTEX_HELD(&hal->local_config_rom_mutex)); del_blk = (s1394_config_rom_t *)(*handle); config_rom = hal->local_config_rom; temp_buf = hal->temp_config_rom_buf; /* Copy the Bus_Info_Block */ bcopy(&config_rom[0], &temp_buf[0], IEEE1394_BIB_SZ); root_offset = hal->root_directory->cfgrom_addr_lo; del_offset = del_blk->root_dir_offset; /* Update Root_Directory entries before the deleted one */ for (i = root_offset; i < del_offset; i++) { entry = T1394_DATA32(config_rom[i]); /* If entry is an offset address - update it */ if (entry & 0x80000000) temp_buf[i] = T1394_DATA32(entry - 1); else temp_buf[i] = T1394_DATA32(entry); } /* Move all Unit_Directories prior to the deleted one */ curr_blk = hal->root_directory->cfgrom_next; while (curr_blk != del_blk) { tmp_offset = curr_blk->cfgrom_addr_lo; tmp_size = (curr_blk->cfgrom_addr_hi - tmp_offset) + 1; bcopy(&config_rom[tmp_offset], &temp_buf[tmp_offset - 1], tmp_size << 2); curr_blk->cfgrom_addr_lo--; curr_blk->cfgrom_addr_hi--; curr_blk = curr_blk->cfgrom_next; } /* Move all Unit_Directories after the deleted one */ curr_blk = del_blk->cfgrom_next; last_blk = del_blk->cfgrom_prev; del_offset = (del_blk->cfgrom_addr_hi - del_blk->cfgrom_addr_lo) + 1; while ((curr_blk != NULL) && (curr_blk->cfgrom_used == B_TRUE)) { tmp_offset = curr_blk->cfgrom_addr_lo; tmp_size = (curr_blk->cfgrom_addr_hi - tmp_offset) + 1; bcopy(&config_rom[tmp_offset], &temp_buf[tmp_offset - (del_offset + 1)], tmp_size << 2); root_offset = curr_blk->root_dir_offset; temp_buf[root_offset - 1] = config_rom[root_offset] - del_offset; curr_blk->root_dir_offset--; curr_blk->cfgrom_addr_lo = curr_blk->cfgrom_addr_lo - (del_offset + 1); curr_blk->cfgrom_addr_hi = curr_blk->cfgrom_addr_hi - (del_offset + 1); last_blk = curr_blk; curr_blk = curr_blk->cfgrom_next; } /* Remove del_blk from the list */ if (del_blk->cfgrom_prev != NULL) del_blk->cfgrom_prev->cfgrom_next = del_blk->cfgrom_next; if (del_blk->cfgrom_next != NULL) del_blk->cfgrom_next->cfgrom_prev = del_blk->cfgrom_prev; del_blk->cfgrom_prev = NULL; del_blk->cfgrom_next = NULL; kmem_free((void *)del_blk, sizeof (s1394_config_rom_t)); /* Update and zero out the "free" block */ if (curr_blk != NULL) { curr_blk->cfgrom_addr_lo = curr_blk->cfgrom_addr_lo - (del_offset + 1); } else { free_blk = (s1394_config_rom_t *)kmem_zalloc( sizeof (s1394_config_rom_t), KM_SLEEP); if (free_blk == NULL) { return (DDI_FAILURE); } free_blk->cfgrom_used = B_FALSE; free_blk->cfgrom_addr_lo = (IEEE1394_CONFIG_ROM_QUAD_SZ - 1) - (del_offset + 1); free_blk->cfgrom_addr_hi = (IEEE1394_CONFIG_ROM_QUAD_SZ - 1); free_blk->cfgrom_prev = last_blk; free_blk->cfgrom_next = NULL; curr_blk = free_blk; } hal->free_space = hal->free_space + (del_offset + 1); tmp_offset = curr_blk->cfgrom_addr_lo; tmp_size = (curr_blk->cfgrom_addr_hi - tmp_offset) + 1; bzero(&temp_buf[tmp_offset], tmp_size << 2); /* Update root directory */ hal->root_directory->cfgrom_addr_hi--; tmp_offset = hal->root_directory->cfgrom_addr_lo; tmp_size = hal->root_directory->cfgrom_addr_hi - tmp_offset; /* Do byte-swapping if necessary (x86) */ for (i = (tmp_offset + 1); i <= hal->root_directory->cfgrom_addr_hi; i++) temp_buf[i] = T1394_DATA32(temp_buf[i]); CRC = s1394_CRC16(&temp_buf[tmp_offset + 1], tmp_size); temp_buf[tmp_offset] = (tmp_size << IEEE1394_CFG_ROM_LEN_SHIFT) | CRC; /* Do byte-swapping if necessary (x86) */ for (i = (tmp_offset + 1); i <= hal->root_directory->cfgrom_addr_hi; i++) temp_buf[i] = T1394_DATA32(temp_buf[i]); /* Copy it back to config_rom buffer */ tmp_size = IEEE1394_CONFIG_ROM_SZ - (hal->free_space << 2); bcopy(&temp_buf[0], &config_rom[0], tmp_size); /* Return a handle to this block */ *handle = NULL; *status = T1394_NOERROR; return (DDI_SUCCESS); } /* * s1394_update_config_rom_callback() * is the callback used by t1394_add_cfgrom_entry() and * t1394_rem_cfgrom_entry(). After a target updates the Config ROM, a * timer is set with this as its callback function. This is to reduce * the number of bus resets that would be necessary if many targets * wished to update the Config ROM simultaneously. */ void s1394_update_config_rom_callback(void *arg) { s1394_hal_t *hal; uint32_t *config_rom; uint32_t bus_capabilities; uint32_t g; uint_t CRC; uint_t last_entry_offset; int i; hal = (s1394_hal_t *)arg; /* Lock the Config ROM buffer */ mutex_enter(&hal->local_config_rom_mutex); config_rom = hal->local_config_rom; /* Update Generation and CRC for Bus_Info_Block */ /* Do byte-swapping if necessary (x86) */ for (i = 0; i < IEEE1394_BIB_QUAD_SZ; i++) config_rom[i] = T1394_DATA32(config_rom[i]); bus_capabilities = config_rom[IEEE1212_NODE_CAP_QUAD]; g = ((bus_capabilities & IEEE1394_BIB_GEN_MASK) >> IEEE1394_BIB_GEN_SHIFT) + 1; if (g > 15) g = 2; g = g << IEEE1394_BIB_GEN_SHIFT; bus_capabilities = (bus_capabilities & (~IEEE1394_BIB_GEN_MASK)) | g; config_rom[IEEE1212_NODE_CAP_QUAD] = bus_capabilities; CRC = s1394_CRC16(&config_rom[1], IEEE1394_BIB_QUAD_SZ - 1); config_rom[0] = (0x04040000) | CRC; /* Do byte-swapping if necessary (x86) */ for (i = 0; i < IEEE1394_BIB_QUAD_SZ; i++) config_rom[i] = T1394_DATA32(config_rom[i]); /* Make sure we update only what is necessary */ last_entry_offset = (IEEE1394_CONFIG_ROM_QUAD_SZ - hal->free_space); if (last_entry_offset < hal->config_rom_update_amount) last_entry_offset = hal->config_rom_update_amount; hal->config_rom_update_amount = (IEEE1394_CONFIG_ROM_QUAD_SZ - hal->free_space); /* Clear the timer flag */ hal->config_rom_timer_set = B_FALSE; /* Unlock the Config ROM buffer */ mutex_exit(&hal->local_config_rom_mutex); /* * The update_config_rom() call can return DDI_FAILURE if the * HAL is shutdown. */ (void) HAL_CALL(hal).update_config_rom(hal->halinfo.hal_private,\ config_rom, last_entry_offset); /* Initiate a bus reset */ (void) HAL_CALL(hal).bus_reset(hal->halinfo.hal_private); }