/* * 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. */ /* * hci1394_async.c * These routines manipulate the 1394 asynchronous dma engines. This * includes incoming and outgoing reads, writes, and locks and their * associated responses. */ #include #include #include #include #include #include #include #include #include #include #include /* * ASYNC_ARRESP_ACK_ERROR is or'd into the error status when we get an ACK error * on an ARRESP. Since the 1394 response code overlaps with the OpenHCI ACK/EVT * errors, we use this to distinguish between the errors in process_arresp(). */ #define ASYNC_ARRESP_ACK_ERROR 0x8000 /* Macro's to help extract 48-bit 1394 address into a uint64_t */ #define HCI1394_TO_ADDR_HI(data) (((uint64_t)((data) & 0xFFFF)) << 32) #define HCI1394_TO_ADDR_LO(data) ((uint64_t)((data) & 0xFFFFFFFF)) /* * Macro to convert a byte stream into a big endian quadlet or octlet or back * the other way. 1394 arithmetic lock operations are done on big endian * quadlets or octlets. compare swaps and bit masks are done on a byte streams. * All data is treated as byte streams over the bus. These macros will convert * the data to a big endian "integer" on x86 plaforms if the operation is an * arithmetic lock operation. It will do nothing if it is not on x86 or is not * an arithmetic lock operation. */ #ifdef _LITTLE_ENDIAN #define HCI1394_ARITH_LOCK_SWAP32(tcode, data) \ (((tcode) == CMD1394_LOCK_FETCH_ADD) || \ ((tcode) == CMD1394_LOCK_BOUNDED_ADD) || \ ((tcode) == CMD1394_LOCK_WRAP_ADD)) ? \ (ddi_swap32(data)) : (data) #define HCI1394_ARITH_LOCK_SWAP64(tcode, data) \ (((tcode) == CMD1394_LOCK_FETCH_ADD) || \ ((tcode) == CMD1394_LOCK_BOUNDED_ADD) || \ ((tcode) == CMD1394_LOCK_WRAP_ADD)) ? \ (ddi_swap64(data)) : (data) #else #define HCI1394_ARITH_LOCK_SWAP32(tcode, data) (data) #define HCI1394_ARITH_LOCK_SWAP64(tcode, data) (data) #endif static int hci1394_async_arresp_read(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, uint_t *tcode, hci1394_async_cmd_t **hcicmd, uint_t *size); static int hci1394_async_arresp_size_get(uint_t tcode, hci1394_q_handle_t q, uint32_t *addr, uint_t *size); static int hci1394_async_arreq_read(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, uint_t *tcode, hci1394_async_cmd_t **hcicmd, uint_t *size); static int hci1394_async_arreq_read_qrd(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size); static int hci1394_async_arreq_read_qwr(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size); static int hci1394_async_arreq_read_brd(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size); static int hci1394_async_arreq_read_bwr(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size); static int hci1394_async_arreq_read_lck(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size); static int hci1394_async_arreq_read_phy(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size, boolean_t *bus_reset_token); static void hci1394_async_hcicmd_init(hci1394_async_handle_t async_handle, cmd1394_cmd_t *cmd, h1394_cmd_priv_t *cmd_priv, hci1394_async_cmd_t **hcicmd); static void hci1394_async_atreq_start(void *async, uint32_t command_ptr); static void hci1394_async_arresp_start(void *async, uint32_t command_ptr); static void hci1394_async_arreq_start(void *async, uint32_t command_ptr); static void hci1394_async_atresp_start(void *async, uint32_t command_ptr); static void hci1394_async_atreq_wake(void *async); static void hci1394_async_arresp_wake(void *async); static void hci1394_async_arreq_wake(void *async); static void hci1394_async_atresp_wake(void *async); static void hci1394_async_atreq_flush(hci1394_async_handle_t async_handle); static void hci1394_async_arresp_flush(hci1394_async_handle_t async_handle); static void hci1394_async_arreq_flush(hci1394_async_handle_t async_handle); static void hci1394_async_atresp_flush(hci1394_async_handle_t async_handle); static void hci1394_async_pending_list_flush(hci1394_async_handle_t async_handle); static void hci1394_async_pending_timeout(hci1394_tlist_node_t *node, void *arg); static uint_t hci1394_async_timeout_calc(hci1394_async_handle_t async_handle, uint_t current_time); _NOTE(SCHEME_PROTECTS_DATA("unique", msgb)) /* * hci1394_async_init() * Initialize the async DMA engines and state. We init the tlabels; ATREQ * pending Q; and ATREQ, ARRESP, ARREQ, and ATRESP Q's. init() returns a * handle to be used in rest of the functions. */ int hci1394_async_init(hci1394_drvinfo_t *drvinfo, hci1394_ohci_handle_t ohci_handle, hci1394_csr_handle_t csr_handle, hci1394_async_handle_t *async_handle) { hci1394_tlist_timer_t timer_info; hci1394_q_info_t qinfo; hci1394_async_t *async; int status; ASSERT(drvinfo != NULL); ASSERT(ohci_handle != NULL); ASSERT(csr_handle != NULL); ASSERT(async_handle != NULL); /* alloc the space to keep track of the list */ async = kmem_alloc(sizeof (hci1394_async_t), KM_SLEEP); /* copy in parms to our local state */ async->as_drvinfo = drvinfo; async->as_ohci = ohci_handle; async->as_csr = csr_handle; async->as_flushing_arreq = B_FALSE; async->as_phy_reset = 0xFFFFFFFF; mutex_init(&async->as_atomic_lookup, NULL, MUTEX_DRIVER, drvinfo->di_iblock_cookie); /* * Initialize the tlabels. Reclaim a bad tlabel after the split timeout * has gone by. This time is in reference to the point the transaction * has been marked as bad. Therefore the tlabel will be reclaimed at * twice the split_timeout. (i.e. if the split timeout was set to 100mS * and the transaction has timed out, 100mS has already gone by. We need * to wait for 100mS more before we can reuse the tlabel. Therefore, the * reclaim time is split_timeout and not split_timeout * 2. The split * timeout is stored as the number of bus cycles. We need to convert * this to nS since the reclaim time is passed as nS. */ hci1394_tlabel_init(drvinfo, OHCI_BUS_CYCLE_TO_nS( hci1394_csr_split_timeout_get(csr_handle)), &async->as_tlabel); /* * Initialize ATREQ pending list. A pended ATREQ will be timed out after * "split_timeout" has gone by. split timeout is in bus cycles so we * need to convert that to nS for the tlist timer info. We will set the * timer resolution to 1/2 of the timeout so that we will have a worst * case timeout of split timeout + (1/2 * split timeout). See * hci1394_tlist.h for more information about this. */ timer_info.tlt_timeout = OHCI_BUS_CYCLE_TO_nS(hci1394_csr_split_timeout_get(csr_handle)); timer_info.tlt_timer_resolution = timer_info.tlt_timeout / 2; timer_info.tlt_callback = hci1394_async_pending_timeout; timer_info.tlt_callback_arg = async; hci1394_tlist_init(drvinfo, &timer_info, &async->as_pending_list); /* Initialize ATREQ Q */ qinfo.qi_desc_size = ASYNC_ATREQ_DESC_SIZE; qinfo.qi_data_size = ASYNC_ATREQ_DATA_SIZE; qinfo.qi_mode = HCI1394_ATQ; qinfo.qi_start = hci1394_async_atreq_start; qinfo.qi_wake = hci1394_async_atreq_wake; qinfo.qi_callback_arg = async; status = hci1394_q_init(drvinfo, async->as_ohci, &qinfo, &async->as_atreq_q); if (status != DDI_SUCCESS) { mutex_destroy(&async->as_atomic_lookup); hci1394_tlist_fini(&async->as_pending_list); hci1394_tlabel_fini(&async->as_tlabel); kmem_free(async, sizeof (hci1394_async_t)); *async_handle = NULL; return (DDI_FAILURE); } /* Initialize ARRESP Q */ qinfo.qi_desc_size = ASYNC_ARRESP_DESC_SIZE; qinfo.qi_data_size = ASYNC_ARRESP_DATA_SIZE; qinfo.qi_mode = HCI1394_ARQ; qinfo.qi_start = hci1394_async_arresp_start; qinfo.qi_wake = hci1394_async_arresp_wake; qinfo.qi_callback_arg = async; status = hci1394_q_init(drvinfo, async->as_ohci, &qinfo, &async->as_arresp_q); if (status != DDI_SUCCESS) { mutex_destroy(&async->as_atomic_lookup); hci1394_tlist_fini(&async->as_pending_list); hci1394_tlabel_fini(&async->as_tlabel); hci1394_q_fini(&async->as_atreq_q); kmem_free(async, sizeof (hci1394_async_t)); *async_handle = NULL; return (DDI_FAILURE); } /* Initialize ARREQ Q */ qinfo.qi_desc_size = ASYNC_ARREQ_DESC_SIZE; qinfo.qi_data_size = ASYNC_ARREQ_DATA_SIZE; qinfo.qi_mode = HCI1394_ARQ; qinfo.qi_start = hci1394_async_arreq_start; qinfo.qi_wake = hci1394_async_arreq_wake; qinfo.qi_callback_arg = async; status = hci1394_q_init(drvinfo, async->as_ohci, &qinfo, &async->as_arreq_q); if (status != DDI_SUCCESS) { mutex_destroy(&async->as_atomic_lookup); hci1394_tlist_fini(&async->as_pending_list); hci1394_tlabel_fini(&async->as_tlabel); hci1394_q_fini(&async->as_atreq_q); hci1394_q_fini(&async->as_arresp_q); kmem_free(async, sizeof (hci1394_async_t)); *async_handle = NULL; return (DDI_FAILURE); } /* Initialize ATRESP Q */ qinfo.qi_desc_size = ASYNC_ATRESP_DESC_SIZE; qinfo.qi_data_size = ASYNC_ATRESP_DATA_SIZE; qinfo.qi_mode = HCI1394_ATQ; qinfo.qi_start = hci1394_async_atresp_start; qinfo.qi_wake = hci1394_async_atresp_wake; qinfo.qi_callback_arg = async; status = hci1394_q_init(drvinfo, async->as_ohci, &qinfo, &async->as_atresp_q); if (status != DDI_SUCCESS) { mutex_destroy(&async->as_atomic_lookup); hci1394_tlist_fini(&async->as_pending_list); hci1394_tlabel_fini(&async->as_tlabel); hci1394_q_fini(&async->as_atreq_q); hci1394_q_fini(&async->as_arresp_q); hci1394_q_fini(&async->as_arreq_q); kmem_free(async, sizeof (hci1394_async_t)); *async_handle = NULL; return (DDI_FAILURE); } *async_handle = async; return (DDI_SUCCESS); } /* * hci1394_async_fini() * Free's up the space allocated in init(). Notice that a pointer to the * handle is used for the parameter. fini() will set your handle to NULL * before returning. */ void hci1394_async_fini(hci1394_async_handle_t *async_handle) { hci1394_async_t *async; ASSERT(async_handle != NULL); async = (hci1394_async_t *)*async_handle; mutex_destroy(&async->as_atomic_lookup); hci1394_tlabel_fini(&async->as_tlabel); hci1394_tlist_fini(&async->as_pending_list); hci1394_q_fini(&async->as_atreq_q); hci1394_q_fini(&async->as_atresp_q); hci1394_q_fini(&async->as_arreq_q); hci1394_q_fini(&async->as_arresp_q); kmem_free(async, sizeof (hci1394_async_t)); /* set handle to null. This helps catch bugs. */ *async_handle = NULL; } /* * hci1394_async_suspend() * The system is getting ready to be suspended. Make sure that all of * the Q's are clean and that the there are no scheduled timeouts in the * pending Q. */ void hci1394_async_suspend(hci1394_async_handle_t async_handle) { ASSERT(async_handle != NULL); /* Flush out async DMA Q's */ hci1394_async_flush(async_handle); /* Cancel any scheduled pending timeouts */ hci1394_tlist_timeout_cancel(async_handle->as_pending_list); } /* * hci1394_async_resume() * Re-setup the DMA Q's during a resume after a successful suspend. The * tlabels will be re-initialized during the bus reset and the pending Q will * be flushed during the suspend. */ int hci1394_async_resume(hci1394_async_handle_t async_handle) { ASSERT(async_handle != NULL); hci1394_q_resume(async_handle->as_atreq_q); hci1394_q_resume(async_handle->as_atresp_q); hci1394_q_resume(async_handle->as_arreq_q); hci1394_q_resume(async_handle->as_arresp_q); return (DDI_SUCCESS); } /* * hci1394_async_cmd_overhead() * Return the size of the HAL private area to attach to every alloced 1394 * framework command. This allows us to track command state without having * to alloc memory every time a command comes down the pipe. */ uint_t hci1394_async_cmd_overhead() { return (sizeof (hci1394_async_cmd_t)); } /* * hci1394_async_flush() * Flush out the Async Q's and the ATREQ pending list. This is called every * bus reset so that we're sync'd up with the HW and when shutting down or * suspending to make sure we cleanup after all commands. */ void hci1394_async_flush(hci1394_async_handle_t async_handle) { ASSERT(async_handle != NULL); hci1394_async_atreq_flush(async_handle); hci1394_async_arresp_flush(async_handle); hci1394_async_pending_list_flush(async_handle); hci1394_async_arreq_flush(async_handle); hci1394_async_atresp_flush(async_handle); hci1394_tlabel_reset(async_handle->as_tlabel); } /* * hci1394_async_pending_timeout_update() * Update the timeout for the pending list. This updates both the pending * list timeout and time we wait to reclaim bad tlabels. timeout is the * time in nS so we do not have to do any conversions. This routine will be * called when the CSR split timeout registers are updated. */ void hci1394_async_pending_timeout_update(hci1394_async_handle_t async_handle, hrtime_t timeout) { ASSERT(async_handle != NULL); hci1394_tlist_timeout_update(async_handle->as_pending_list, timeout); hci1394_tlabel_set_reclaim_time(async_handle->as_tlabel, timeout); } /* * hci1394_async_atreq_process() * Process an atreq, if one has completed. This is called during interrupt * processing and will process a completed atreq. It returns status if an * atreq was processed so that the ISR knows that it needs to be called * again to see if another ATREQ has completed. flush_q set to B_TRUE tells * this routine to process all commands regardless of their completion * status. This is used during bus reset processing to remove all commands * from the Q. * * There are a few race conditions that we have to watch for in atreq/arresp. * They all have to do with pended responses so they are not applicable in * the ARREQ/ATRESP engine (since ATRESP's can't be pended). * * Since the race conditions only exist for pended responses, we will only * talk about that sequence here. We're also going to simplify the discussion * so what the code does, so it won't exactly match what we say (e.g. we * don't always setup a timeout for every single command, etc.) * * After Q'ing up an ATREQ, we will process the result of that command in * one of a couple different paths. A normal condition would be that we Q up * a command, we get an ATREQ complete interrupt and look at the ATREQ * result. In the case it has been pended, we setup a timeout to wait for the * response. If we receive the response before the timeout, the command is * done and we send the response up the chain, if we do not, the command is * done and we send a timeout notification up the chain. * * The first race condition is when we get the timeout at the same time as * the response. At first glance a mutex around the command state would * solve this problem. But on a multi-processor machine, we may have the * ARRESP interrupt handler(ISR) running on one processor and the timeout on * another. This means that the command state could change between two * reads while in the ISR. This means we need to have a little more complex * logic around changing the command state and have to be careful how and * when we do this. * * The second race condition is that we could see the ARRESP before we * process the ATREQ. We could be processing a few ARRESP from previous * ATREQ's when the ATREQ completes and then the ARRESP comes in. Since we * already are in the interrupt handler, the ATREQ complete will not preempt * us. * * We will never see a race condition between the ATREQ interrupt for a * command and the pending timeout since the command is not being timed until * this routine is run for that command. */ int hci1394_async_atreq_process(hci1394_async_handle_t async_handle, boolean_t flush_q, boolean_t *request_available) { hci1394_async_cmd_t *hcicmd; hci1394_q_cmd_t *qcmd; int cmd_status; ASSERT(async_handle != NULL); ASSERT(request_available != NULL); /* * Get the next ATREQ that has completed (if one has). Space is free'd * up in atreq_q and atreq_data_q as part of this function call. */ hci1394_q_at_next(async_handle->as_atreq_q, flush_q, &qcmd); /* * See if there were anymore requests on ATREQ Q. A NULL means there * were no completed commands left on the Q */ if (qcmd == NULL) { *request_available = B_FALSE; return (DDI_SUCCESS); } /* There is a completed ATREQ, setup the HAL command pointer */ *request_available = B_TRUE; hcicmd = (hci1394_async_cmd_t *)qcmd->qc_arg; /* save away the command completed timestamp for the services layer */ hcicmd->ac_priv->ack_tstamp = qcmd->qc_timestamp; /* * Make sure this command has not already been processed. This command * may have already received a response. If the ACK was not an ACK * pending, we have a HW error (i.e. The target HW sent a response to a * non-pended request). There is a race condition where the software * will see and complete a response before processing it's ACK Pending. * This can only happen for ACK pendings. We have seen this race * condition and response to a non-pended request during real-world * testing :-) */ if (hcicmd->ac_state != HCI1394_CMD_STATE_IN_PROGRESS) { /* * we already processed the ARRESP in arresp_process(), it * better have been ACK pended. Otherwise the target device * performed an illegal action. */ if (qcmd->qc_status == OHCI_ACK_PENDING) { /* * Tell source that their command has completed. We're * done with this command. * NOTE: We use ac_status which was set in * process_arresp() */ h1394_cmd_is_complete( async_handle->as_drvinfo->di_sl_private, hcicmd->ac_cmd, H1394_AT_REQ, hcicmd->ac_status); return (DDI_SUCCESS); /* * This is a HW error. Process the ACK like we never saw the * response. We will do this below. */ } } /* * if we got an ack pending, add it to the pending list and leave. We * will either get an ARRESP or the pending list will timeout the * response. */ if (qcmd->qc_status == OHCI_ACK_PENDING) { hcicmd->ac_state = HCI1394_CMD_STATE_PENDING; /* Add this command to the pending list */ hcicmd->ac_plist_node.tln_addr = hcicmd; hci1394_tlist_add(async_handle->as_pending_list, &hcicmd->ac_plist_node); return (DDI_SUCCESS); } /* * setup our return command status based on the ACK from the HW. See the * OpenHCI 1.0 spec (table 3.2 on pg. 18) for more information about * these ACK/EVT's. */ switch (qcmd->qc_status) { case OHCI_ACK_COMPLETE: cmd_status = H1394_CMD_SUCCESS; break; /* * we can get a nostatus during a bus reset (i.e. we shutdown the AT * engine before it flushed all the commands) */ case OHCI_EVT_FLUSHED: case OHCI_EVT_NO_STATUS: cmd_status = H1394_CMD_EBUSRESET; break; case OHCI_EVT_MISSING_ACK: case OHCI_EVT_TIMEOUT: cmd_status = H1394_CMD_ETIMEOUT; break; case OHCI_ACK_BUSY_X: case OHCI_ACK_BUSY_A: case OHCI_ACK_BUSY_B: cmd_status = H1394_CMD_EDEVICE_BUSY; break; case OHCI_ACK_TARDY: cmd_status = H1394_CMD_EDEVICE_POWERUP; break; case OHCI_ACK_DATA_ERROR: cmd_status = H1394_CMD_EDATA_ERROR; break; case OHCI_ACK_TYPE_ERROR: cmd_status = H1394_CMD_ETYPE_ERROR; break; case OHCI_ACK_CONFLICT_ERROR: cmd_status = H1394_CMD_ERSRC_CONFLICT; break; case OHCI_ACK_ADDRESS_ERROR: cmd_status = H1394_CMD_EADDR_ERROR; break; case OHCI_EVT_UNDERRUN: case OHCI_EVT_DATA_READ: case OHCI_EVT_TCODE_ERR: case OHCI_EVT_DESCRIPTOR_READ: case OHCI_EVT_UNKNOWN: default: cmd_status = H1394_CMD_EUNKNOWN_ERROR; break; } /* * Free the tlabel that was used for this transfer. We will not try and * free the tlabel in the case that we already received a response or if * we did not allocate one (PHY packet). If we already received a * response, the tlabel would have been free'd in * hci1394_async_arresp_process(). */ if ((hcicmd->ac_state == HCI1394_CMD_STATE_IN_PROGRESS) && (hcicmd->ac_tlabel_alloc == B_TRUE)) { hci1394_tlabel_free(async_handle->as_tlabel, &hcicmd->ac_tlabel); } /* * if we got anything other than and ACK pending, we are done w/ this * transaction. */ hcicmd->ac_state = HCI1394_CMD_STATE_COMPLETED; /* tell the services layer that the command has completed */ h1394_cmd_is_complete(async_handle->as_drvinfo->di_sl_private, hcicmd->ac_cmd, H1394_AT_REQ, cmd_status); return (DDI_SUCCESS); } /* * hci1394_async_arresp_process() * Process an arresp, if one has completed. This is called during interrupt * processing and will process a completed arresp. It returns status if an * arresp was processed so that the ISR knows that it needs to be called * again to see if another ARRESP has completed. */ int hci1394_async_arresp_process(hci1394_async_handle_t async_handle, boolean_t *response_available) { hci1394_async_cmd_t *hcicmd; uint32_t *addr; int cmd_status; uint_t tcode; uint_t size; int status; ASSERT(async_handle != NULL); ASSERT(response_available != NULL); /* * See if there were any responses on ARRESP Q. A NULL means there * were no responses on the Q. This call does NOT free up space. We * need to do that later after we figure out how much space the * response takes up. */ hci1394_q_ar_next(async_handle->as_arresp_q, &addr); if (addr == NULL) { *response_available = B_FALSE; return (DDI_SUCCESS); } /* * We got a response. Lock out pending timeout callback from marking * tlabel bad. */ *response_available = B_TRUE; mutex_enter(&async_handle->as_atomic_lookup); /* * Read in the response into the 1394 framework command. We could get a * NULL for a command if we got a response with an error (i.e. tlabel * that didn't match a request) This would be a successful read but with * a NULL hcicmd returned. If we ever get a DDI_FAILURE, we will * shutdown. */ status = hci1394_async_arresp_read(async_handle, (hci1394_basic_pkt_t *)addr, &tcode, &hcicmd, &size); if (status != DDI_SUCCESS) { mutex_exit(&async_handle->as_atomic_lookup); h1394_error_detected(async_handle->as_drvinfo->di_sl_private, H1394_SELF_INITIATED_SHUTDOWN, NULL); cmn_err(CE_WARN, "hci1394(%d): driver shutdown: " "unrecoverable error interrupt detected", async_handle->as_drvinfo->di_instance); hci1394_shutdown(async_handle->as_drvinfo->di_dip); return (DDI_FAILURE); } /* Free up the arresp Q space, we are done with the data */ hci1394_q_ar_free(async_handle->as_arresp_q, size); /* * if we did not get a valid command response (i.e. we got a bad tlabel * or something like that) we don't have anything else to do. We will * say that we processed a response and will return successfully. We * still may have other responses on the Q. */ if (hcicmd == NULL) { mutex_exit(&async_handle->as_atomic_lookup); return (DDI_SUCCESS); } /* * Make sure this is in the pending list. There is a small chance that * we will see the response before we see the ACK PENDING. If it is the * expected case, it is in the pending list. We will remove it since * we are done with the command. * * NOTE: there is a race condition here with the pending timeout. Look * at the comments before hci1394_async_atreq_process() for more info. */ if (hcicmd->ac_state == HCI1394_CMD_STATE_PENDING) { /* remove this transfer from our the pending list */ status = hci1394_tlist_delete(async_handle->as_pending_list, &hcicmd->ac_plist_node); if (status != DDI_SUCCESS) { mutex_exit(&async_handle->as_atomic_lookup); return (DDI_SUCCESS); } } /* allow pending timeout callback to mark tlabel as bad */ mutex_exit(&async_handle->as_atomic_lookup); /* * We got a valid response that we were able to read in. Free the tlabel * that was used for this transfer. */ hci1394_tlabel_free(async_handle->as_tlabel, &hcicmd->ac_tlabel); /* * Setup our return command status based on the RESP or ACK or SW error. * See the IEEE1394-1995 spec (6.2.4.10 on pg. 159) for more information * on response codes. See the OpenHCI 1.0 spec (table 3.2 on pg. 18) for * more information about ACK/EVT's. ac_status could have an IEEE1394 * response in it, a 1394 EVT/ACK, or a special cmd1394 error for a * device error caught in SW (e.g. for a block read request that got a * quadlet read response). We use a special mask to separate the * ACK/EVT's from the responses (ASYNC_ARRESP_ACK_ERROR). */ switch (hcicmd->ac_status) { case IEEE1394_RESP_COMPLETE: cmd_status = H1394_CMD_SUCCESS; break; case IEEE1394_RESP_DATA_ERROR: cmd_status = H1394_CMD_EDATA_ERROR; break; case IEEE1394_RESP_TYPE_ERROR: cmd_status = H1394_CMD_ETYPE_ERROR; break; case IEEE1394_RESP_CONFLICT_ERROR: cmd_status = H1394_CMD_ERSRC_CONFLICT; break; case IEEE1394_RESP_ADDRESS_ERROR: cmd_status = H1394_CMD_EADDR_ERROR; break; case H1394_CMD_EDEVICE_ERROR: cmd_status = H1394_CMD_EDEVICE_ERROR; break; case OHCI_ACK_DATA_ERROR | ASYNC_ARRESP_ACK_ERROR: cmd_status = H1394_CMD_EDATA_ERROR; break; case OHCI_ACK_TYPE_ERROR | ASYNC_ARRESP_ACK_ERROR: cmd_status = H1394_CMD_ETYPE_ERROR; break; case OHCI_EVT_UNDERRUN | ASYNC_ARRESP_ACK_ERROR: case OHCI_EVT_DATA_READ | ASYNC_ARRESP_ACK_ERROR: case OHCI_EVT_TCODE_ERR | ASYNC_ARRESP_ACK_ERROR: cmd_status = H1394_CMD_EUNKNOWN_ERROR; break; default: cmd_status = H1394_CMD_EUNKNOWN_ERROR; break; } /* * if we have already processed the atreq and put it on the pending Q * (normal case), tell the services layer it completed. */ if (hcicmd->ac_state == HCI1394_CMD_STATE_PENDING) { /* Set state indicating that we are done with this cmd */ hcicmd->ac_state = HCI1394_CMD_STATE_COMPLETED; /* tell the services lyaer the command has completed */ h1394_cmd_is_complete(async_handle->as_drvinfo->di_sl_private, hcicmd->ac_cmd, H1394_AT_REQ, cmd_status); /* * We have not seen the atreq status yet. We will call * h1394_command_is_complete() in atreq_process() in case we did not get * an ack pending (target HW error -> this is based on real world * experience :-)) */ } else { /* Set state indicating that we are done with this cmd */ hcicmd->ac_state = HCI1394_CMD_STATE_COMPLETED; /* save away the status for atreq_process() */ hcicmd->ac_status = cmd_status; } return (DDI_SUCCESS); } /* * hci1394_async_arreq_process() * Process an arreq, if one has arrived. This is called during interrupt * processing and will process an arreq that has arrived. It returns status * if an arreq was processed so that the ISR knows that it needs to be * called again to see if another ARREQ has arrived. */ int hci1394_async_arreq_process(hci1394_async_handle_t async_handle, boolean_t *request_available) { hci1394_async_cmd_t *hcicmd; uint32_t *addr; uint_t tcode; uint_t size; int status; ASSERT(async_handle != NULL); ASSERT(request_available != NULL); /* * See if there were any requests on ARREQ Q. A NULL means there * were no requests on the Q. This call does NOT free up space. We * need to do that later after we figure out how much space the * request takes up. */ hci1394_q_ar_next(async_handle->as_arreq_q, &addr); if (addr == NULL) { *request_available = B_FALSE; return (DDI_SUCCESS); } /* * We got a request. Read the request into a 1394 framework command. * We could get a NULL for a command if we got a request with an error * (i.e. ARREQ ACK was not ack pending or ack complete). This would be a * successful read but with a NULL hcicmd returned. If we ever get a * DDI_FAILURE, we will shutdown. */ *request_available = B_TRUE; status = hci1394_async_arreq_read(async_handle, (hci1394_basic_pkt_t *)addr, &tcode, &hcicmd, &size); if (status != DDI_SUCCESS) { h1394_error_detected(async_handle->as_drvinfo->di_sl_private, H1394_SELF_INITIATED_SHUTDOWN, NULL); cmn_err(CE_WARN, "hci1394(%d): driver shutdown: " "unrecoverable error interrupt detected", async_handle->as_drvinfo->di_instance); hci1394_shutdown(async_handle->as_drvinfo->di_dip); return (DDI_FAILURE); } /* Free up the arreq Q space, we are done with the data */ hci1394_q_ar_free(async_handle->as_arreq_q, size); /* * if we did not get a valid request (i.e. The ARREQ had a bad ACK * or something like that) we don't have anything else to do. We will * say that we processed a request and will return successfully. We * still may have other requests on the Q. */ if (hcicmd == NULL) { return (DDI_SUCCESS); } /* * If as_flushing_arreq is set, we do not want to send any requests up * to the Services Layer. We are flushing the ARREQ until we see a bus * reset token that matches the current bus generation. Free up the * alloc'd command and return success. */ if (async_handle->as_flushing_arreq == B_TRUE) { hci1394_async_response_complete(async_handle, hcicmd->ac_cmd, hcicmd->ac_priv); return (DDI_SUCCESS); } /* * We got a valid request that we were able to read in. Call into the * services layer based on the type of request. */ switch (tcode) { case IEEE1394_TCODE_READ_QUADLET: case IEEE1394_TCODE_READ_BLOCK: h1394_read_request(async_handle->as_drvinfo->di_sl_private, hcicmd->ac_cmd); break; case IEEE1394_TCODE_WRITE_QUADLET: case IEEE1394_TCODE_WRITE_BLOCK: h1394_write_request(async_handle->as_drvinfo->di_sl_private, hcicmd->ac_cmd); break; case IEEE1394_TCODE_LOCK: h1394_lock_request(async_handle->as_drvinfo->di_sl_private, hcicmd->ac_cmd); break; case IEEE1394_TCODE_PHY: /* * OpenHCI only handles 1 PHY quadlet at a time. If a selfid * packet was received with multiple quadlets, we will treat * each quadlet as a separate call. We do not notify the * services layer through the normal command interface, we will * treat it like a command internally and then free up the * command ourselves when we are done with it. */ h1394_phy_packet(async_handle->as_drvinfo->di_sl_private, &hcicmd->ac_cmd->cmd_u.q.quadlet_data, 1, hcicmd->ac_priv->recv_tstamp); /* free alloc'd command */ hci1394_async_response_complete(async_handle, hcicmd->ac_cmd, hcicmd->ac_priv); break; default: /* free alloc'd command */ hci1394_async_response_complete(async_handle, hcicmd->ac_cmd, hcicmd->ac_priv); break; } return (DDI_SUCCESS); } /* * hci1394_async_atresp_process() * Process an atresp, if one has completed. This is called during interrupt * processing and will process a completed atresp. It returns status if an * atresp was processed so that the ISR knows that it needs to be called * again to see if another ATRESP has completed. flush_q set to B_TRUE tells * this routine to process all commands regardless of their completion * status. This is used during bus reset processing to remove all commands * from the Q. */ int hci1394_async_atresp_process(hci1394_async_handle_t async_handle, boolean_t flush_q, boolean_t *response_available) { hci1394_async_cmd_t *hcicmd; hci1394_q_cmd_t *qcmd; int cmd_status; ASSERT(async_handle != NULL); ASSERT(response_available != NULL); /* * Get the next ATRESP that has completed (if one has). Space is free'd * up in atresp_q and atresp_data_q as part of this function call. */ hci1394_q_at_next(async_handle->as_atresp_q, flush_q, &qcmd); /* * See if there were anymore requests on ATRESP Q. A NULL means there * were no completed commands left on the Q. */ if (qcmd == NULL) { *response_available = B_FALSE; return (DDI_SUCCESS); } /* There is a completed ATRESP, setup the HAL command pointer */ *response_available = B_TRUE; hcicmd = (hci1394_async_cmd_t *)qcmd->qc_arg; /* save away the command completed timestamp for the services layer */ hcicmd->ac_priv->ack_tstamp = qcmd->qc_timestamp; /* * setup our return command status based on the ACK from the HW. See the * OpenHCI 1.0 spec (table 3.2 on pg. 18) for more information about * these ACK/EVT's. */ switch (qcmd->qc_status) { case OHCI_ACK_COMPLETE: cmd_status = H1394_CMD_SUCCESS; break; /* * we can get a nostatus during a bus reset (i.e. we shutdown the AT * engine before it flushed all the commands) */ case OHCI_EVT_FLUSHED: case OHCI_EVT_NO_STATUS: cmd_status = H1394_CMD_EBUSRESET; break; case OHCI_EVT_MISSING_ACK: case OHCI_EVT_TIMEOUT: cmd_status = H1394_CMD_ETIMEOUT; break; case OHCI_ACK_BUSY_X: case OHCI_ACK_BUSY_A: case OHCI_ACK_BUSY_B: cmd_status = H1394_CMD_EDEVICE_BUSY; break; case OHCI_ACK_TARDY: cmd_status = H1394_CMD_EDEVICE_POWERUP; break; case OHCI_ACK_DATA_ERROR: cmd_status = H1394_CMD_EDATA_ERROR; break; case OHCI_ACK_TYPE_ERROR: cmd_status = H1394_CMD_ETYPE_ERROR; break; case OHCI_ACK_CONFLICT_ERROR: cmd_status = H1394_CMD_ERSRC_CONFLICT; break; case OHCI_ACK_ADDRESS_ERROR: cmd_status = H1394_CMD_EADDR_ERROR; break; case OHCI_EVT_UNKNOWN: cmd_status = H1394_CMD_EUNKNOWN_ERROR; break; case OHCI_EVT_UNDERRUN: case OHCI_EVT_DATA_READ: case OHCI_EVT_TCODE_ERR: case OHCI_EVT_DESCRIPTOR_READ: default: cmd_status = H1394_CMD_EUNKNOWN_ERROR; break; } /* tell the services layer that the command has completed */ h1394_cmd_is_complete(async_handle->as_drvinfo->di_sl_private, hcicmd->ac_cmd, H1394_AT_RESP, cmd_status); return (DDI_SUCCESS); } /* * hci1394_async_arresp_read() * Read ARRESP in from memory into 1394 Framework command. We read the tcode * which tells us which kind of arresp the packet is, get the size of the * response, read in the sender, tlabel, and response code, and then * lookup the command based on the sender and tlabel. Once we get the command * (corresponding to the ATREQ), we will copy the rest of the response into * that command. * * The only time this routine should return DDI_FAILURE is if it was unable * to maintain a good state in the ARRESP Q (i.e. an unknown response was * received and we can not cleanup after it.) If we detect a recoverable * error, and it doesn't make sense to pass the response up to the Services * Layer, we should return DDI_SUCCESS with hcicmd = NULL. */ static int hci1394_async_arresp_read(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, uint_t *tcode, hci1394_async_cmd_t **hcicmd, uint_t *size) { hci1394_tlabel_info_t ac_tlabel; h1394_cmd_priv_t *cmd_priv; cmd1394_cmd_t *cmd; uint32_t *status_addr; uint_t data_length; uint32_t quadlet; void *command; uint_t rcode; uint_t ack; int status; ASSERT(async_handle != NULL); ASSERT(pkt != NULL); ASSERT(tcode != NULL); ASSERT(hcicmd != NULL); ASSERT(size != NULL); /* read in the arresp tcode */ quadlet = hci1394_q_ar_get32(async_handle->as_arresp_q, &pkt->q1); *tcode = HCI1394_DESC_TCODE_GET(quadlet); /* Get the size of the arresp */ status = hci1394_async_arresp_size_get(*tcode, async_handle->as_arresp_q, &pkt->q1, size); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } /* Read in the tlabel, destination, and rcode (response code) */ quadlet = hci1394_q_ar_get32(async_handle->as_arresp_q, &pkt->q1); ac_tlabel.tbi_tlabel = HCI1394_DESC_TLABEL_GET(quadlet); quadlet = hci1394_q_ar_get32(async_handle->as_arresp_q, &pkt->q2); ac_tlabel.tbi_destination = HCI1394_DESC_DESTID_GET(quadlet); rcode = HCI1394_DESC_RCODE_GET(quadlet); /* Lookup the ATREQ framework command this response goes with */ hci1394_tlabel_lookup(async_handle->as_tlabel, &ac_tlabel, &command); /* * If there is not a cooresponding ATREQ command, this is an error. We * will ignore this response but still return success so we cleanup * after it and go on with other arresp's. This could happend if a * response was sent after the command has timed out or if the target * device is misbehaving. (we have seen both cases) */ *hcicmd = (hci1394_async_cmd_t *)command; if ((*hcicmd) == NULL) { return (DDI_SUCCESS); } /* * copy the response code into the hal private command space. Setup * shortcuts to the 1394 framework command (cmd) and the HAL/SL private * area (cmd_priv). A command is made up of 4 parts. There is the public * part which is accessable to the target driver, there is the Services * Layer private part which is only accessible to the services layer, * there is the SL/HAL private area which is where the SL and HAL share * information about a particular command, and there is the HAL private * area where we keep track of our command specific state information. */ (*hcicmd)->ac_status = rcode; cmd = (*hcicmd)->ac_cmd; cmd_priv = (*hcicmd)->ac_priv; /* * Calculate the address where the status of the ARRESP and timestamp is * kept at. It is the last quadlet in the response. Save away the * timestamp. */ status_addr = (uint32_t *)((uintptr_t)pkt + (uintptr_t)*size - (uintptr_t)IEEE1394_QUADLET); quadlet = hci1394_q_ar_get32(async_handle->as_arresp_q, status_addr); cmd_priv->recv_tstamp = HCI1394_DESC_TIMESTAMP_GET(quadlet); /* * if we did not get an ACK_COMPLETE, we will use the ack error instead * of the response in the packet for our status. We use special mask to * separate the reponses from the ACKs (ASYNC_ARRESP_ACK_ERROR). We will * return success with hcicmd set to the command so that this error gets * sent up to the Services Layer. */ ack = HCI1394_DESC_EVT_GET(quadlet); if (ack != OHCI_ACK_COMPLETE) { /* use the ack error instead of rcode for the command status */ (*hcicmd)->ac_status = ack | ASYNC_ARRESP_ACK_ERROR; return (DDI_SUCCESS); } /* * If we get to this point we have gotten a valid ACK on the response * and have matched up the response with an ATREQ. Now we check the * response code. If it is not resp_complete, we do not have anything * left to look at in the response. Return successfully. */ if (rcode != IEEE1394_RESP_COMPLETE) { return (DDI_SUCCESS); } /* * Read the rest of the response (based on which kind of response it is) * into the 1394 framework command. In all of the different responses, * we check to make sure the response matches the original request. We * originally did not have this check but found a device or two which * did not behave very well and would cause us to corrupt our commands. * Now we check :-) We will return success when we get this error since * we can recover from it. */ switch (*tcode) { case IEEE1394_TCODE_WRITE_RESP: /* * make sure the ATREQ was a quadlet/block write. The same * response is sent back for those two type of ATREQs. */ if ((cmd->cmd_type != CMD1394_ASYNCH_WR_QUAD) && (cmd->cmd_type != CMD1394_ASYNCH_WR_BLOCK)) { (*hcicmd)->ac_status = H1394_CMD_EDEVICE_ERROR; return (DDI_SUCCESS); } break; case IEEE1394_TCODE_READ_QUADLET_RESP: /* make sure the ATREQ was a quadlet read */ if (cmd->cmd_type != CMD1394_ASYNCH_RD_QUAD) { (*hcicmd)->ac_status = H1394_CMD_EDEVICE_ERROR; return (DDI_SUCCESS); } /* * read the quadlet read response in. Data is treated as a byte * stream. */ hci1394_q_ar_rep_get8(async_handle->as_arresp_q, (uint8_t *)&cmd->cmd_u.q.quadlet_data, (uint8_t *)&pkt->q4, IEEE1394_QUADLET); break; case IEEE1394_TCODE_READ_BLOCK_RESP: /* make sure the ATREQ was a block read */ if (cmd->cmd_type != CMD1394_ASYNCH_RD_BLOCK) { (*hcicmd)->ac_status = H1394_CMD_EDEVICE_ERROR; return (DDI_SUCCESS); } /* * read in the data length. Make sure the data length is the * same size as the read block request size that went out. */ quadlet = hci1394_q_ar_get32(async_handle->as_arresp_q, &pkt->q4); data_length = HCI1394_DESC_DATALEN_GET(quadlet); if (data_length != cmd_priv->mblk.length) { (*hcicmd)->ac_status = H1394_CMD_EDEVICE_ERROR; return (DDI_SUCCESS); } /* Copy the read block data into the command mblk */ hci1394_q_ar_copy_to_mblk(async_handle->as_arresp_q, (uint8_t *)&pkt->q5, &cmd_priv->mblk); break; case IEEE1394_TCODE_LOCK_RESP: /* read in the data length */ quadlet = hci1394_q_ar_get32(async_handle->as_arresp_q, &pkt->q4); data_length = HCI1394_DESC_DATALEN_GET(quadlet); if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) { /* * read in the data length. Make sure the data length * is the valid for a lock32 response (1 quadlet) */ if (data_length != IEEE1394_QUADLET) { (*hcicmd)->ac_status = H1394_CMD_EDEVICE_ERROR; return (DDI_SUCCESS); } /* * read the lock32 response in. Data is treated as a * byte stream unless it is an arithmetic lock * operation. In that case we treat data like a 32-bit * word. */ hci1394_q_ar_rep_get8(async_handle->as_arresp_q, (uint8_t *)&cmd->cmd_u.l32.old_value, (uint8_t *)&pkt->q5, IEEE1394_QUADLET); cmd->cmd_u.l32.old_value = HCI1394_ARITH_LOCK_SWAP32( cmd->cmd_u.l32.lock_type, cmd->cmd_u.l32.old_value); } else if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_64) { /* * read in the data length. Make sure the data length * is the valid for a lock64 response (1 octlet) */ if (data_length != IEEE1394_OCTLET) { (*hcicmd)->ac_status = H1394_CMD_EDEVICE_ERROR; return (DDI_SUCCESS); } /* * read the lock64 response in. Data is treated as a * byte stream unless it is an arithmetic lock * operation. In that case we treat data like a 64-bit * word. */ hci1394_q_ar_rep_get8(async_handle->as_arresp_q, (uint8_t *)&cmd->cmd_u.l64.old_value, (uint8_t *)&pkt->q5, IEEE1394_OCTLET); cmd->cmd_u.l64.old_value = HCI1394_ARITH_LOCK_SWAP64( cmd->cmd_u.l64.lock_type, cmd->cmd_u.l64.old_value); /* * we sent out a request that was NOT a lock request and got * back a lock response. */ } else { (*hcicmd)->ac_status = H1394_CMD_EDEVICE_ERROR; return (DDI_SUCCESS); } break; default: /* we got a tcode that we don't know about. Return error */ return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * hci1394_async_arreq_read() * Read ARREQ in from memory into a 1394 Framework command. Allocate a 1394 * framework command, read in the ARREQ, and before passing it up to the * services layer, see if it was a valid broadcast request. * * The only time this routine should return DDI_FAILURE is if it was unable * to maintain a good state in the ARREQ Q (i.e. an unknown request was * received and we can not cleanup after it.) If we detect a recoverable * error we should return DDI_SUCCESS with hcicmd = NULL. */ static int hci1394_async_arreq_read(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, uint_t *tcode, hci1394_async_cmd_t **hcicmd, uint_t *size) { h1394_cmd_priv_t *cmd_priv; boolean_t is_reset_token; cmd1394_cmd_t *cmd; uint32_t quadlet; int status; ASSERT(async_handle != NULL); ASSERT(pkt != NULL); ASSERT(tcode != NULL); ASSERT(hcicmd != NULL); ASSERT(size != NULL); /* read in the arresp tcode */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q1); *tcode = HCI1394_DESC_TCODE_GET(quadlet); /* * Allocated 1394 framework command. The Services layer takes care of * cacheing commands. This is called during interrupt processing so we * do not want to sleep. */ status = h1394_alloc_cmd(async_handle->as_drvinfo->di_sl_private, H1394_ALLOC_CMD_NOSLEEP, &cmd, &cmd_priv); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } /* Initialize the HAL private command info */ hci1394_async_hcicmd_init(async_handle, cmd, cmd_priv, hcicmd); /* * There are two generations in the command structure, one in the public * space and one in the HAL/SL private shared space. We need to fill in * both. We only use the private one internally. */ cmd_priv->bus_generation = async_handle->as_drvinfo->di_gencnt; cmd->bus_generation = async_handle->as_drvinfo->di_gencnt; /* * Read the request (based on which kind of request it is) into the 1394 * framework command. */ switch (*tcode) { case IEEE1394_TCODE_READ_QUADLET: /* * We got a ARREQ quadlet read request. Read in the packet. * If there is a problem with the packet (i.e. we don't get * DDI_SUCCESS), we will free up the command and return NULL in * hcicmd to indicate that we did not get a valid ARREQ to * process. */ status = hci1394_async_arreq_read_qrd(async_handle, pkt, *hcicmd, size); if (status != DDI_SUCCESS) { hci1394_async_response_complete(async_handle, cmd, cmd_priv); *hcicmd = NULL; return (DDI_SUCCESS); } break; case IEEE1394_TCODE_WRITE_QUADLET: /* * We got a ARREQ quadlet write request. Read in the packet. * If there is a problem with the packet (i.e. we don't get * DDI_SUCCESS), we will free up the command and return NULL in * hcicmd to indicate that we did not get a valid ARREQ to * process. */ status = hci1394_async_arreq_read_qwr(async_handle, pkt, *hcicmd, size); if (status != DDI_SUCCESS) { hci1394_async_response_complete(async_handle, cmd, cmd_priv); *hcicmd = NULL; return (DDI_SUCCESS); } break; case IEEE1394_TCODE_READ_BLOCK: /* * We got a ARREQ block read request. Read in the packet. * If there is a problem with the packet (i.e. we don't get * DDI_SUCCESS), we will free up the command and return NULL in * hcicmd to indicate that we did not get a valid ARREQ to * process. */ status = hci1394_async_arreq_read_brd(async_handle, pkt, *hcicmd, size); if (status != DDI_SUCCESS) { hci1394_async_response_complete(async_handle, cmd, cmd_priv); *hcicmd = NULL; return (DDI_SUCCESS); } break; case IEEE1394_TCODE_WRITE_BLOCK: /* * We got a ARREQ block write request. Read in the packet. * If there is a problem with the packet (i.e. we don't get * DDI_SUCCESS), we will free up the command and return NULL in * hcicmd to indicate that we did not get a valid ARREQ to * process. */ status = hci1394_async_arreq_read_bwr(async_handle, pkt, *hcicmd, size); if (status != DDI_SUCCESS) { hci1394_async_response_complete(async_handle, cmd, cmd_priv); *hcicmd = NULL; return (DDI_SUCCESS); } break; case IEEE1394_TCODE_LOCK: /* * We got a ARREQ lock request. Read in the packet. * If there is a problem with the packet (i.e. we don't get * DDI_SUCCESS), we will free up the command and return NULL in * hcicmd to indicate that we did not get a valid ARREQ to * process. */ status = hci1394_async_arreq_read_lck(async_handle, pkt, *hcicmd, size); if (status != DDI_SUCCESS) { hci1394_async_response_complete(async_handle, cmd, cmd_priv); *hcicmd = NULL; return (DDI_SUCCESS); } break; case IEEE1394_TCODE_PHY: /* * We got a PHY packet in the ARREQ buffer. Read in the packet. * If there is a problem with the packet (i.e. we don't get * DDI_SUCCESS), we will free up the command and return NULL in * hcicmd to indicate that we did not get a valid ARREQ to * process. */ status = hci1394_async_arreq_read_phy(async_handle, pkt, *hcicmd, size, &is_reset_token); if (status != DDI_SUCCESS) { hci1394_async_response_complete(async_handle, cmd, cmd_priv); *hcicmd = NULL; return (DDI_SUCCESS); } /* * If we got a bus reset token, free up the command and return * NULL in hcicmd to indicate that we did not get a valid ARREQ * to process. */ if (is_reset_token == B_TRUE) { hci1394_async_response_complete(async_handle, cmd, cmd_priv); *hcicmd = NULL; return (DDI_SUCCESS); } break; default: /* we got a tcode that we don't know about. Return error */ return (DDI_FAILURE); } /* * If this command was broadcast and it was not a write, drop the * command since it's an invalid request. We will free up the command * and return NULL in hcicmd to indicate that we did not get a valid * ARREQ to process. */ if ((((*hcicmd)->ac_dest & IEEE1394_NODE_NUM_MASK) == IEEE1394_BROADCAST_NODEID) && ((*tcode != IEEE1394_TCODE_WRITE_QUADLET) && (*tcode != IEEE1394_TCODE_WRITE_BLOCK))) { hci1394_async_response_complete(async_handle, cmd, cmd_priv); *hcicmd = NULL; return (DDI_SUCCESS); /* * It is a valid broadcast command, set that field in the public * command structure. */ } else if ((((*hcicmd)->ac_dest & IEEE1394_NODE_NUM_MASK) == IEEE1394_BROADCAST_NODEID)) { cmd->broadcast = 1; } return (DDI_SUCCESS); } /* * hci1394_async_arreq_read_qrd() * Read ARREQ quadlet read into the 1394 Framework command. This routine will * return DDI_FAILURE if it was not able to read the request succesfully. */ static int hci1394_async_arreq_read_qrd(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size) { h1394_cmd_priv_t *cmd_priv; cmd1394_cmd_t *cmd; uint32_t quadlet; ASSERT(async_handle != NULL); ASSERT(pkt != NULL); ASSERT(hcicmd != NULL); ASSERT(size != NULL); /* Setup shortcuts, command type, and size of request */ cmd = hcicmd->ac_cmd; cmd_priv = hcicmd->ac_priv; cmd->cmd_type = CMD1394_ASYNCH_RD_QUAD; *size = DESC_SZ_AR_READQUAD_REQ; /* * read in the ARREQ ACK/EVT, the speed, the time we received it, and * calculate the ATRESP timeout for when we send it. */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q4); hcicmd->ac_status = HCI1394_DESC_EVT_GET(quadlet); cmd_priv->speed = HCI1394_DESC_AR_SPD_GET(quadlet); cmd_priv->recv_tstamp = HCI1394_DESC_TIMESTAMP_GET(quadlet); hcicmd->ac_qcmd.qc_timestamp = hci1394_async_timeout_calc(async_handle, cmd_priv->recv_tstamp); /* * if the ARREQ ACK was bad, we were unable to successfully read in this * request. Return failure. */ if ((hcicmd->ac_status != OHCI_ACK_COMPLETE) && (hcicmd->ac_status != OHCI_ACK_PENDING)) { return (DDI_FAILURE); } /* * Read in the tlabel and destination. We don't use an mblk for this * request. */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q1); hcicmd->ac_dest = HCI1394_DESC_DESTID_GET(quadlet); hcicmd->ac_tlabel.tbi_tlabel = HCI1394_DESC_TLABEL_GET(quadlet); hcicmd->ac_mblk_alloc = B_FALSE; /* * Read in the sender so we know who to send the ATRESP to and read in * the 1394 48-bit address for this request. */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q2); cmd->nodeID = HCI1394_DESC_SRCID_GET(quadlet); cmd->cmd_addr = HCI1394_TO_ADDR_HI(quadlet); quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q3); cmd->cmd_addr |= HCI1394_TO_ADDR_LO(quadlet); return (DDI_SUCCESS); } /* * hci1394_async_arreq_read_qwr() * Read ARREQ quadlet write into the 1394 Framework command. This routine * will return DDI_FAILURE if it was not able to read the request * succesfully. */ static int hci1394_async_arreq_read_qwr(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size) { h1394_cmd_priv_t *cmd_priv; cmd1394_cmd_t *cmd; uint32_t quadlet; ASSERT(async_handle != NULL); ASSERT(pkt != NULL); ASSERT(hcicmd != NULL); ASSERT(size != NULL); /* Setup shortcuts, command type, and size of request */ cmd = hcicmd->ac_cmd; cmd_priv = hcicmd->ac_priv; cmd->cmd_type = CMD1394_ASYNCH_WR_QUAD; *size = DESC_SZ_AR_WRITEQUAD_REQ; /* * read in the ARREQ ACK/EVT, the speed, the time we received it, and * calculate the ATRESP timeout for when we send it. */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q5); hcicmd->ac_status = HCI1394_DESC_EVT_GET(quadlet); cmd_priv->speed = HCI1394_DESC_AR_SPD_GET(quadlet); cmd_priv->recv_tstamp = HCI1394_DESC_TIMESTAMP_GET(quadlet); hcicmd->ac_qcmd.qc_timestamp = hci1394_async_timeout_calc(async_handle, cmd_priv->recv_tstamp); /* * if the ARREQ ACK was bad, we were unable to successfully read in this * request. Return failure. */ if ((hcicmd->ac_status != OHCI_ACK_COMPLETE) && (hcicmd->ac_status != OHCI_ACK_PENDING)) { return (DDI_FAILURE); } /* * Read in the tlabel and destination. We don't use an mblk for this * request. */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q1); hcicmd->ac_dest = HCI1394_DESC_DESTID_GET(quadlet); hcicmd->ac_tlabel.tbi_tlabel = HCI1394_DESC_TLABEL_GET(quadlet); hcicmd->ac_mblk_alloc = B_FALSE; /* * Read in the sender so we know who to send the ATRESP to. Read in * the 1394 48-bit address for this request. Copy the data quadlet into * the command. The data quadlet is treated like a byte stream. */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q2); cmd->nodeID = HCI1394_DESC_SRCID_GET(quadlet); cmd->cmd_addr = HCI1394_TO_ADDR_HI(quadlet); quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q3); cmd->cmd_addr |= HCI1394_TO_ADDR_LO(quadlet); hci1394_q_ar_rep_get8(async_handle->as_arreq_q, (uint8_t *)&cmd->cmd_u.q.quadlet_data, (uint8_t *)&pkt->q4, IEEE1394_QUADLET); return (DDI_SUCCESS); } /* * hci1394_async_arreq_read_brd() * Read ARREQ block read into the 1394 Framework command. This routine will * return DDI_FAILURE if it was not able to read the request succesfully. */ static int hci1394_async_arreq_read_brd(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size) { h1394_cmd_priv_t *cmd_priv; cmd1394_cmd_t *cmd; uint32_t quadlet; ASSERT(async_handle != NULL); ASSERT(pkt != NULL); ASSERT(hcicmd != NULL); ASSERT(size != NULL); /* Setup shortcuts, command type, and size of request */ cmd = hcicmd->ac_cmd; cmd_priv = hcicmd->ac_priv; cmd->cmd_type = CMD1394_ASYNCH_RD_BLOCK; *size = DESC_SZ_AR_READBLOCK_REQ; /* * read in the ARREQ ACK/EVT, the speed, the time we received it, and * calculate the ATRESP timeout for when we send it. */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q5); hcicmd->ac_status = HCI1394_DESC_EVT_GET(quadlet); cmd_priv->speed = HCI1394_DESC_AR_SPD_GET(quadlet); cmd_priv->recv_tstamp = HCI1394_DESC_TIMESTAMP_GET(quadlet); hcicmd->ac_qcmd.qc_timestamp = hci1394_async_timeout_calc(async_handle, cmd_priv->recv_tstamp); /* * if the ARREQ ACK was bad, we were unable to successfully read in this * request. Return failure. */ if ((hcicmd->ac_status != OHCI_ACK_COMPLETE) && (hcicmd->ac_status != OHCI_ACK_PENDING)) { return (DDI_FAILURE); } /* Read in the tlabel and destination */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q1); hcicmd->ac_dest = HCI1394_DESC_DESTID_GET(quadlet); hcicmd->ac_tlabel.tbi_tlabel = HCI1394_DESC_TLABEL_GET(quadlet); /* * Read in the sender so we know who to send the ATRESP to. Read in * the 1394 48-bit address for this request. Read in the block data size * and allocate an mblk of that size. */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q2); cmd->nodeID = HCI1394_DESC_SRCID_GET(quadlet); cmd->cmd_addr = HCI1394_TO_ADDR_HI(quadlet); quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q3); cmd->cmd_addr |= HCI1394_TO_ADDR_LO(quadlet); quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q4); cmd->cmd_u.b.blk_length = HCI1394_DESC_DATALEN_GET(quadlet); cmd->cmd_u.b.data_block = allocb(cmd->cmd_u.b.blk_length, 0); if (cmd->cmd_u.b.data_block == NULL) { return (DDI_FAILURE); } hcicmd->ac_mblk_alloc = B_TRUE; return (DDI_SUCCESS); } /* * hci1394_async_arreq_read_bwr() * Read ARREQ block write into the 1394 Framework command. This routine will * return DDI_FAILURE if it was not able to read the request succesfully. */ static int hci1394_async_arreq_read_bwr(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size) { h1394_cmd_priv_t *cmd_priv; uint32_t *local_addr; cmd1394_cmd_t *cmd; uint32_t quadlet; ASSERT(async_handle != NULL); ASSERT(pkt != NULL); ASSERT(hcicmd != NULL); ASSERT(size != NULL); /* * Setup shortcuts, command type, and size of request. The size of the * request is in quadlets, therefore we need to make sure we count in * the padding when figureing out the size (i.e. data may be in bytes * but the HW always pads to quadlets) */ cmd = hcicmd->ac_cmd; cmd_priv = hcicmd->ac_priv; cmd->cmd_type = CMD1394_ASYNCH_WR_BLOCK; quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q4); cmd->cmd_u.b.blk_length = HCI1394_DESC_DATALEN_GET(quadlet); *size = DESC_SZ_AR_WRITEBLOCK_REQ + HCI1394_ALIGN_QUAD(cmd->cmd_u.b.blk_length); /* * read in the ARREQ ACK/EVT, the speed, the time we received it, and * calculate the ATRESP timeout for when we send it. The status word is * the last quadlet in the packet. */ local_addr = (uint32_t *)(((uintptr_t)(&pkt->q5)) + ((uintptr_t)HCI1394_ALIGN_QUAD(cmd->cmd_u.b.blk_length))); quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, local_addr); hcicmd->ac_status = HCI1394_DESC_EVT_GET(quadlet); cmd_priv->speed = HCI1394_DESC_AR_SPD_GET(quadlet); cmd_priv->recv_tstamp = HCI1394_DESC_TIMESTAMP_GET(quadlet); hcicmd->ac_qcmd.qc_timestamp = hci1394_async_timeout_calc(async_handle, cmd_priv->recv_tstamp); /* * if the ARREQ ACK was bad, we were unable to successfully read in this * request. Return failure. */ if ((hcicmd->ac_status != OHCI_ACK_COMPLETE) && (hcicmd->ac_status != OHCI_ACK_PENDING)) { return (DDI_FAILURE); } /* Read in the tlabel and destination */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q1); hcicmd->ac_dest = HCI1394_DESC_DESTID_GET(quadlet); hcicmd->ac_tlabel.tbi_tlabel = HCI1394_DESC_TLABEL_GET(quadlet); /* * Read in the sender so we know who to send the ATRESP to. Read in * the 1394 48-bit address for this request. Read in the block data size * and allocate an mblk of that size. */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q2); cmd->nodeID = HCI1394_DESC_SRCID_GET(quadlet); cmd->cmd_addr = HCI1394_TO_ADDR_HI(quadlet); quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q3); cmd->cmd_addr |= HCI1394_TO_ADDR_LO(quadlet); cmd->cmd_u.b.data_block = allocb(cmd->cmd_u.b.blk_length, 0); if (cmd->cmd_u.b.data_block == NULL) { return (DDI_FAILURE); } hcicmd->ac_mblk_alloc = B_TRUE; /* Copy ARREQ write data into mblk_t */ hci1394_q_ar_rep_get8(async_handle->as_arreq_q, (uint8_t *)cmd->cmd_u.b.data_block->b_wptr, (uint8_t *)&pkt->q5, cmd->cmd_u.b.blk_length); /* Update mblk_t wptr */ cmd->cmd_u.b.data_block->b_wptr += cmd->cmd_u.b.blk_length; return (DDI_SUCCESS); } /* * hci1394_async_arreq_read_lck() * Read ARREQ lock request into the 1394 Framework command. This routine will * return DDI_FAILURE if it was not able to read the request succesfully. */ static int hci1394_async_arreq_read_lck(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size) { h1394_cmd_priv_t *cmd_priv; uint32_t *local_addr; cmd1394_cmd_t *cmd; uint8_t *data_addr; uint32_t quadlet; uint32_t length; ASSERT(async_handle != NULL); ASSERT(pkt != NULL); ASSERT(hcicmd != NULL); ASSERT(size != NULL); /* * Setup shortcuts, command type, and size of request. The size of the * request is in quadlets, therefore we need to make sure we count in * the padding when figuring out the size (i.e. data may be in bytes * but the HW always pads to quadlets) */ cmd = hcicmd->ac_cmd; cmd_priv = hcicmd->ac_priv; quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q4); length = HCI1394_DESC_DATALEN_GET(quadlet); *size = DESC_SZ_AR_LOCK_REQ + HCI1394_ALIGN_QUAD(length); /* make sure the length is a valid lock request length */ if (length == DESC_TWO_QUADS) { cmd->cmd_type = CMD1394_ASYNCH_LOCK_32; cmd->cmd_u.l32.lock_type = HCI1394_DESC_EXTTCODE_GET(quadlet); } else if (length == DESC_TWO_OCTLETS) { cmd->cmd_type = CMD1394_ASYNCH_LOCK_64; cmd->cmd_u.l64.lock_type = HCI1394_DESC_EXTTCODE_GET(quadlet); } else { return (DDI_FAILURE); } /* * read in the ARREQ ACK/EVT, the speed, the time we received it, and * calculate the ATRESP timeout for when we send it. The status word is * the last quadlet in the packet. */ local_addr = (uint32_t *)(((uintptr_t)(&pkt->q5)) + ((uintptr_t)HCI1394_ALIGN_QUAD(length))); quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, local_addr); hcicmd->ac_status = HCI1394_DESC_EVT_GET(quadlet); cmd_priv->speed = HCI1394_DESC_AR_SPD_GET(quadlet); cmd_priv->recv_tstamp = HCI1394_DESC_TIMESTAMP_GET(quadlet); hcicmd->ac_qcmd.qc_timestamp = hci1394_async_timeout_calc(async_handle, cmd_priv->recv_tstamp); /* * if the ARREQ ACK was bad, we were unable to successfully read in this * request. Return failure. */ if ((hcicmd->ac_status != OHCI_ACK_COMPLETE) && (hcicmd->ac_status != OHCI_ACK_PENDING)) { return (DDI_FAILURE); } /* Read in the tlabel and destination */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q1); hcicmd->ac_dest = HCI1394_DESC_DESTID_GET(quadlet); hcicmd->ac_tlabel.tbi_tlabel = HCI1394_DESC_TLABEL_GET(quadlet); hcicmd->ac_mblk_alloc = B_FALSE; /* * Read in the sender so we know who to send the ATRESP to. Read in * the 1394 48-bit address for this request. */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q2); cmd->nodeID = HCI1394_DESC_SRCID_GET(quadlet); cmd->cmd_addr = HCI1394_TO_ADDR_HI(quadlet); quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q3); cmd->cmd_addr |= HCI1394_TO_ADDR_LO(quadlet); /* Copy ARREQ lock data into 1394 framework command */ if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) { data_addr = (uint8_t *)&pkt->q5; hci1394_q_ar_rep_get8(async_handle->as_arreq_q, (uint8_t *)&cmd->cmd_u.l32.arg_value, data_addr, IEEE1394_QUADLET); data_addr = (uint8_t *)((uintptr_t)data_addr + (uintptr_t)IEEE1394_QUADLET); hci1394_q_ar_rep_get8(async_handle->as_arreq_q, (uint8_t *)&cmd->cmd_u.l32.data_value, data_addr, IEEE1394_QUADLET); /* * swap these for our correct architecture if we are doing * arithmetic lock operations */ cmd->cmd_u.l32.arg_value = HCI1394_ARITH_LOCK_SWAP32( cmd->cmd_u.l32.lock_type, cmd->cmd_u.l32.arg_value); cmd->cmd_u.l32.data_value = HCI1394_ARITH_LOCK_SWAP32( cmd->cmd_u.l32.lock_type, cmd->cmd_u.l32.data_value); } else if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_64) { data_addr = (uint8_t *)&pkt->q5; hci1394_q_ar_rep_get8(async_handle->as_arreq_q, (uint8_t *)&cmd->cmd_u.l64.arg_value, data_addr, IEEE1394_OCTLET); data_addr = (uint8_t *)((uintptr_t)data_addr + (uintptr_t)IEEE1394_OCTLET); hci1394_q_ar_rep_get8(async_handle->as_arreq_q, (uint8_t *)&cmd->cmd_u.l64.data_value, data_addr, IEEE1394_OCTLET); /* * swap these for our correct architecture if we are doing * arithmetic lock operations */ cmd->cmd_u.l64.arg_value = HCI1394_ARITH_LOCK_SWAP64( cmd->cmd_u.l64.lock_type, cmd->cmd_u.l64.arg_value); cmd->cmd_u.l64.data_value = HCI1394_ARITH_LOCK_SWAP64( cmd->cmd_u.l64.lock_type, cmd->cmd_u.l64.data_value); } return (DDI_SUCCESS); } /* * hci1394_async_arreq_read_phy() * Read ARREQ PHY quadlet into the 1394 Framework command. This routine will * return DDI_FAILURE if it was not able to read the request succesfully. */ static int hci1394_async_arreq_read_phy(hci1394_async_handle_t async_handle, hci1394_basic_pkt_t *pkt, hci1394_async_cmd_t *hcicmd, uint_t *size, boolean_t *bus_reset_token) { cmd1394_cmd_t *cmd; uint32_t quadlet; uint32_t data1; uint32_t data2; ASSERT(async_handle != NULL); ASSERT(pkt != NULL); ASSERT(hcicmd != NULL); ASSERT(size != NULL); /* Setup shortcuts, command type, and size of request */ cmd = hcicmd->ac_cmd; cmd->cmd_type = CMD1394_ASYNCH_WR_QUAD; *size = DESC_SZ_AR_PHY; /* * read in the ARREQ ACK/EVT, the speed, the time we received it, and * set state that we do not use an mblk for this request. */ quadlet = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q4); hcicmd->ac_status = HCI1394_DESC_EVT_GET(quadlet); hcicmd->ac_priv->speed = HCI1394_DESC_AR_SPD_GET(quadlet); hcicmd->ac_priv->recv_tstamp = HCI1394_DESC_TIMESTAMP_GET(quadlet); hcicmd->ac_mblk_alloc = B_FALSE; /* Read in the PHY packet quadlet and its check quadlet */ data1 = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q2); data2 = hci1394_q_ar_get32(async_handle->as_arreq_q, &pkt->q3); /* * if this is a bus reset token, save away the generation. If the bus * reset token is for the current generation, we do not need to flush * the ARREQ Q anymore. */ if (hcicmd->ac_status == OHCI_EVT_BUS_RESET) { *bus_reset_token = B_TRUE; async_handle->as_phy_reset = HCI1394_DESC_PHYGEN_GET(data2); if (async_handle->as_phy_reset == hci1394_ohci_current_busgen( async_handle->as_ohci)) { async_handle->as_flushing_arreq = B_FALSE; } return (DDI_SUCCESS); } *bus_reset_token = B_FALSE; /* if there is a data error in the PHY packet, return failure */ if (data1 != ~data2) { return (DDI_FAILURE); } /* Copy the PHY quadlet to the command */ cmd->cmd_u.q.quadlet_data = data1; return (DDI_SUCCESS); } /* * hci1394_async_phy() * Queue up ATREQ phy packet. */ int hci1394_async_phy(hci1394_async_handle_t async_handle, cmd1394_cmd_t *cmd, h1394_cmd_priv_t *cmd_priv, int *result) { hci1394_basic_pkt_t header; hci1394_async_cmd_t *hcicmd; int status; ASSERT(async_handle != NULL); ASSERT(cmd != NULL); ASSERT(cmd_priv != NULL); ASSERT(result != NULL); /* * make sure this call is during the current bus generation (i.e. no * bus resets have occured since this request was made. */ if (cmd_priv->bus_generation != hci1394_ohci_current_busgen( async_handle->as_ohci)) { *result = H1394_STATUS_INVALID_BUSGEN; return (DDI_FAILURE); } /* Initialize the private HAL command structure */ hci1394_async_hcicmd_init(async_handle, cmd, cmd_priv, &hcicmd); /* We do not allocate a tlabel for a PHY packet */ hcicmd->ac_tlabel_alloc = B_FALSE; /* * Setup the packet header information for a ATREQ PHY packet Add in * the tcode, phy quadlet, and it's 1's complement. */ header.q1 = DESC_ATREQ_Q1_PHY; header.q2 = cmd->cmd_u.q.quadlet_data; header.q3 = ~header.q2; /* Write request into the ATREQ Q. If we fail, we're out of space */ status = hci1394_q_at(async_handle->as_atreq_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_PHY, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * hci1394_async_write() * Queue up ATREQ write. This could be either a block write or a quadlet * write. */ int hci1394_async_write(hci1394_async_handle_t async_handle, cmd1394_cmd_t *cmd, h1394_cmd_priv_t *cmd_priv, int *result) { hci1394_async_cmd_t *hcicmd; hci1394_basic_pkt_t header; int status; ASSERT(async_handle != NULL); ASSERT(cmd != NULL); ASSERT(cmd_priv != NULL); ASSERT(result != NULL); /* * make sure this call is during the current bus generation (i.e. no * bus resets have occured since this request was made. */ if (cmd_priv->bus_generation != hci1394_ohci_current_busgen( async_handle->as_ohci)) { *result = H1394_STATUS_INVALID_BUSGEN; return (DDI_FAILURE); } /* Initialize the private HAL command structure */ hci1394_async_hcicmd_init(async_handle, cmd, cmd_priv, &hcicmd); hcicmd->ac_dest = (uint_t)(cmd->cmd_addr >> IEEE1394_ADDR_PHY_ID_SHIFT); /* allocate a tlabel for this request */ status = hci1394_tlabel_alloc(async_handle->as_tlabel, hcicmd->ac_dest, &hcicmd->ac_tlabel); if (status != DDI_SUCCESS) { *result = H1394_STATUS_EMPTY_TLABEL; return (DDI_FAILURE); } /* * Setup the packet header information for a ATREQ write packet. We * will set the tcode later on since this could be a block write or * a quadlet write. Set SRCBusId if this write is not a local bus * access. Copy in the speed, tlabel, and destination address. */ header.q1 = 0; if ((hcicmd->ac_dest & IEEE1394_BUS_NUM_MASK) != IEEE1394_BUS_NUM_MASK) { header.q1 |= DESC_AT_SRCBUSID; } header.q1 |= HCI1394_DESC_AT_SPD_SET(cmd_priv->speed) | HCI1394_DESC_TLABEL_SET(hcicmd->ac_tlabel.tbi_tlabel); header.q2 = (uint32_t)(cmd->cmd_addr >> 32); header.q3 = (uint32_t)(cmd->cmd_addr & DESC_PKT_DESTOFFLO_MASK); /* Register this command w/ its tlabel */ hci1394_tlabel_register(async_handle->as_tlabel, &hcicmd->ac_tlabel, hcicmd); /* If this is a quadlet write ATREQ */ if (cmd->cmd_type == CMD1394_ASYNCH_WR_QUAD) { /* * setup the tcode for a quadlet write request and copy in * the quadlet data. Endian issues will be taken care of in * hci1394_q_at(). */ header.q1 |= DESC_ATREQ_Q1_QWR; header.q4 = cmd->cmd_u.q.quadlet_data; /* * Write the request into the ATREQ Q. If we fail, we are out * of space. */ status = hci1394_q_at(async_handle->as_atreq_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_WRITEQUAD, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } /* This is a block write ATREQ */ } else { /* setup the tcode and the length of the block write */ header.q1 |= DESC_ATREQ_Q1_BWR; header.q4 = HCI1394_DESC_DATALEN_SET(cmd_priv->mblk.length); /* * Write the request into the ATREQ Q. If we fail, we are out * of space. The data is in a mblk(s). We use a special * interface in the HAL/SL private command block to handle * partial transfers out of the mblk due to packet size * restrictions. */ status = hci1394_q_at_with_mblk(async_handle->as_atreq_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_WRITEBLOCK, &cmd_priv->mblk, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } } return (DDI_SUCCESS); } /* * hci1394_async_read() * Queue up ATREQ read. This could be either a block read or a quadlet * read. */ int hci1394_async_read(hci1394_async_handle_t async_handle, cmd1394_cmd_t *cmd, h1394_cmd_priv_t *cmd_priv, int *result) { hci1394_basic_pkt_t header; int status; hci1394_async_cmd_t *hcicmd; ASSERT(async_handle != NULL); ASSERT(cmd != NULL); ASSERT(cmd_priv != NULL); ASSERT(result != NULL); /* * make sure this call is during the current bus generation (i.e. no * bus resets have occured since this request was made. */ if (cmd_priv->bus_generation != hci1394_ohci_current_busgen( async_handle->as_ohci)) { *result = H1394_STATUS_INVALID_BUSGEN; return (DDI_FAILURE); } /* Initialize the private HAL command structure */ hci1394_async_hcicmd_init(async_handle, cmd, cmd_priv, &hcicmd); hcicmd->ac_dest = (uint_t)(cmd->cmd_addr >> IEEE1394_ADDR_PHY_ID_SHIFT); /* allocate a tlabel for this request */ status = hci1394_tlabel_alloc(async_handle->as_tlabel, hcicmd->ac_dest, &hcicmd->ac_tlabel); if (status != DDI_SUCCESS) { *result = H1394_STATUS_EMPTY_TLABEL; return (DDI_FAILURE); } /* * Setup the packet header information for a ATREQ read packet. We * will set the tcode later on since this could be a block read or * a quadlet read. Set SRCBusId if this read is not a local bus * access. Copy in the speed, tlabel, and destination address. */ header.q1 = 0; if ((hcicmd->ac_dest & IEEE1394_BUS_NUM_MASK) != IEEE1394_BUS_NUM_MASK) { header.q1 |= DESC_AT_SRCBUSID; } header.q1 |= HCI1394_DESC_AT_SPD_SET(cmd_priv->speed) | HCI1394_DESC_TLABEL_SET(hcicmd->ac_tlabel.tbi_tlabel); header.q2 = (uint32_t)(cmd->cmd_addr >> 32); header.q3 = (uint32_t)(cmd->cmd_addr & DESC_PKT_DESTOFFLO_MASK); /* Register this command w/ its tlabel */ hci1394_tlabel_register(async_handle->as_tlabel, &hcicmd->ac_tlabel, hcicmd); /* If this is a quadlet read ATREQ */ if (cmd->cmd_type == CMD1394_ASYNCH_RD_QUAD) { /* setup the tcode for a quadlet read request */ header.q1 |= DESC_ATREQ_Q1_QRD; header.q4 = 0; /* * Write the request into the ATREQ Q. If we fail, we are out * of space. */ status = hci1394_q_at(async_handle->as_atreq_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_READQUAD, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } } else { /* setup the tcode and the length of the block read */ header.q1 |= DESC_ATREQ_Q1_BRD; header.q4 = HCI1394_DESC_DATALEN_SET(cmd_priv->mblk.length); /* * Write the request into the ATREQ Q. If we fail, we are out * of space. */ status = hci1394_q_at(async_handle->as_atreq_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_READBLOCK, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } } return (DDI_SUCCESS); } /* * hci1394_async_lock() * Queue up ATREQ lock. This could be either a 32-bit or 64-bit lock * request. */ int hci1394_async_lock(hci1394_async_handle_t async_handle, cmd1394_cmd_t *cmd, h1394_cmd_priv_t *cmd_priv, int *result) { hci1394_basic_pkt_t header; hci1394_async_cmd_t *hcicmd; uint32_t data32[2]; uint64_t data64[2]; uint8_t *datap; uint_t size; int status; ASSERT(async_handle != NULL); ASSERT(cmd != NULL); ASSERT(cmd_priv != NULL); ASSERT(result != NULL); /* * make sure this call is during the current bus generation (i.e. no * bus resets have occured since this request was made. */ if (cmd_priv->bus_generation != hci1394_ohci_current_busgen( async_handle->as_ohci)) { *result = H1394_STATUS_INVALID_BUSGEN; return (DDI_FAILURE); } /* Initialize the private HAL command structure */ hci1394_async_hcicmd_init(async_handle, cmd, cmd_priv, &hcicmd); hcicmd->ac_dest = (uint_t)(cmd->cmd_addr >> IEEE1394_ADDR_PHY_ID_SHIFT); /* allocate a tlabel for this request */ status = hci1394_tlabel_alloc(async_handle->as_tlabel, hcicmd->ac_dest, &hcicmd->ac_tlabel); if (status != DDI_SUCCESS) { *result = H1394_STATUS_EMPTY_TLABEL; return (DDI_FAILURE); } /* Register this command w/ its tlabel */ hci1394_tlabel_register(async_handle->as_tlabel, &hcicmd->ac_tlabel, hcicmd); /* * Setup the packet header information for a ATREQ lock packet. Set * the tcode up as a lock request. Set SRCBusId if this lock is not a * local bus access. Copy in the speed, tlabel, and destination * address. */ header.q1 = DESC_ATREQ_Q1_LCK; if ((hcicmd->ac_dest & IEEE1394_BUS_NUM_MASK) != IEEE1394_BUS_NUM_MASK) { header.q1 |= DESC_AT_SRCBUSID; } header.q1 |= HCI1394_DESC_AT_SPD_SET(cmd_priv->speed) | HCI1394_DESC_TLABEL_SET(hcicmd->ac_tlabel.tbi_tlabel); header.q2 = (uint32_t)(cmd->cmd_addr >> 32); header.q3 = (uint32_t)(cmd->cmd_addr & DESC_PKT_DESTOFFLO_MASK); /* * Setup the lock length based on what size lock operation we are * performing. If it isn't a lock32 or lock64, we have encountered an * internal error. Copy the lock data into a local data buffer. Perform * a byte swap if it is an arithmetic lock operation and we are on a * little endian machine. */ if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) { size = DESC_TWO_QUADS; header.q4 = HCI1394_DESC_DATALEN_SET(size) | HCI1394_DESC_EXTTCODE_SET(cmd->cmd_u.l32.lock_type); data32[0] = HCI1394_ARITH_LOCK_SWAP32( cmd->cmd_u.l32.lock_type, cmd->cmd_u.l32.arg_value); data32[1] = HCI1394_ARITH_LOCK_SWAP32( cmd->cmd_u.l32.lock_type, cmd->cmd_u.l32.data_value); datap = (uint8_t *)data32; } else if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_64) { size = DESC_TWO_OCTLETS; header.q4 = HCI1394_DESC_DATALEN_SET(size) | HCI1394_DESC_EXTTCODE_SET(cmd->cmd_u.l64.lock_type); data64[0] = HCI1394_ARITH_LOCK_SWAP64( cmd->cmd_u.l64.lock_type, cmd->cmd_u.l64.arg_value); data64[1] = HCI1394_ARITH_LOCK_SWAP64( cmd->cmd_u.l64.lock_type, cmd->cmd_u.l64.data_value); datap = (uint8_t *)data64; } else { *result = H1394_STATUS_INTERNAL_ERROR; return (DDI_FAILURE); } /* Write request into the ATREQ Q. If we fail, we're out of space */ status = hci1394_q_at_with_data(async_handle->as_atreq_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_LOCK, datap, size, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * hci1394_async_write_response() * Send a write ATRESP. This routine should be called from the Services * layer to send a response to a received write request (ARREQ). The same * response is sent to a quadlet and block write request. */ int hci1394_async_write_response(hci1394_async_handle_t async_handle, cmd1394_cmd_t *cmd, h1394_cmd_priv_t *cmd_priv, int *result) { hci1394_basic_pkt_t header; int status; hci1394_async_cmd_t *hcicmd; ASSERT(async_handle != NULL); ASSERT(cmd != NULL); ASSERT(cmd_priv != NULL); ASSERT(result != NULL); /* * make sure this call is during the current bus generation (i.e. no * bus resets have occured since this request was made. */ if (cmd_priv->bus_generation != hci1394_ohci_current_busgen( async_handle->as_ohci)) { *result = H1394_STATUS_INVALID_BUSGEN; return (DDI_FAILURE); } /* * setup a shortcut to the hal private command area. Copy the generation * to the Q area so that we can check the generation when the AT Q is * locked. This prevents us from loosing commands due to race * conditions. */ hcicmd = (hci1394_async_cmd_t *)cmd_priv->hal_overhead; hcicmd->ac_qcmd.qc_generation = cmd_priv->bus_generation; /* * Setup the packet header information for a ATRESP write packet. Set * the tcode for a write response. Set SRCBusId if the addr is not a * local bus address. Copy in the speed, tlabel, and response code. */ header.q1 = DESC_ATRESP_Q1_WR; if ((cmd->nodeID & IEEE1394_BUS_NUM_MASK) != IEEE1394_BUS_NUM_MASK) { header.q1 |= DESC_AT_SRCBUSID; } header.q1 |= HCI1394_DESC_AT_SPD_SET(cmd_priv->speed) | HCI1394_DESC_TLABEL_SET(hcicmd->ac_tlabel.tbi_tlabel); header.q2 = (HCI1394_DESC_DESTID_SET(cmd->nodeID) | HCI1394_DESC_RCODE_SET(cmd->cmd_result)); header.q3 = 0; /* Write response into the ATRESP Q. If we fail, we're out of space */ status = hci1394_q_at(async_handle->as_atresp_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_WRITE_RESP, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * hci1394_async_read_response() * Send a read ATRESP. This routine should be called from the Services * layer to send a response to a received read request (ARREQ). The * response will differ between quadlet/block read requests. */ int hci1394_async_read_response(hci1394_async_handle_t async_handle, cmd1394_cmd_t *cmd, h1394_cmd_priv_t *cmd_priv, int *result) { hci1394_basic_pkt_t header; int status; hci1394_async_cmd_t *hcicmd; ASSERT(async_handle != NULL); ASSERT(cmd != NULL); ASSERT(cmd_priv != NULL); ASSERT(result != NULL); /* * make sure this call is during the current bus generation (i.e. no * bus resets have occured since this request was made. */ if (cmd_priv->bus_generation != hci1394_ohci_current_busgen( async_handle->as_ohci)) { *result = H1394_STATUS_INVALID_BUSGEN; return (DDI_FAILURE); } /* * setup a shortcut to the hal private command area. Copy the generation * to the Q area so that we can check the generation when the AT Q is * locked. This prevents us from loosing commands due to race * conditions. */ hcicmd = (hci1394_async_cmd_t *)cmd_priv->hal_overhead; hcicmd->ac_qcmd.qc_generation = cmd_priv->bus_generation; /* * Setup the packet header information for a ATRESP read packet. we * will set the tcode later based on type of read response. Set * SRCBusId if the addr is not a local bus address. Copy in the * speed, tlabel, and response code. */ header.q1 = 0; if ((cmd->nodeID & IEEE1394_BUS_NUM_MASK) != IEEE1394_BUS_NUM_MASK) { header.q1 |= DESC_AT_SRCBUSID; } header.q1 |= HCI1394_DESC_AT_SPD_SET(cmd_priv->speed) | HCI1394_DESC_TLABEL_SET(hcicmd->ac_tlabel.tbi_tlabel); header.q2 = (uint32_t)(HCI1394_DESC_DESTID_SET(cmd->nodeID) | HCI1394_DESC_RCODE_SET(cmd->cmd_result)); header.q3 = 0; /* if the response is a read quadlet response */ if (cmd->cmd_type == CMD1394_ASYNCH_RD_QUAD) { /* * setup the tcode for a quadlet read response, If the * response code is not resp complete. */ header.q1 |= DESC_ATRESP_Q1_QRD; if (cmd->cmd_result == IEEE1394_RESP_COMPLETE) { header.q4 = cmd->cmd_u.q.quadlet_data; } else { header.q4 = 0x0; } /* * Write response into the ATRESP Q. If we fail, we're out of * space. */ status = hci1394_q_at(async_handle->as_atresp_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_READQUAD_RESP, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } /* * the response is a block read response. If the result is not a * resp complete, we are not going to send any data back. */ } else if ((cmd->cmd_type == CMD1394_ASYNCH_RD_BLOCK) && (cmd->cmd_result != IEEE1394_RESP_COMPLETE)) { /* * Setup the tcode for a block read response, set the data * length to zero since we had an error. */ header.q1 |= DESC_ATRESP_Q1_BRD; header.q4 = 0x0; /* * Write response into the ATRESP Q. If we fail, we're out of * space. */ status = hci1394_q_at(async_handle->as_atresp_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_READBLOCK_RESP, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } /* * the response is a block read response with a resp complete for the * response code. Send back the read data. */ } else { /* * Setup the tcode for a block read response, setup the data * length. */ header.q1 |= DESC_ATRESP_Q1_BRD; header.q4 = HCI1394_DESC_DATALEN_SET(cmd->cmd_u.b.blk_length); /* * Write response into the ATRESP Q. If we fail, we're out of * space. Use the data in the mblk. */ status = hci1394_q_at_with_mblk(async_handle->as_atresp_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_READBLOCK_RESP, &cmd_priv->mblk, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } } return (DDI_SUCCESS); } /* * hci1394_async_lock_response() * Send a lock ATRESP. This routine should be called from the Services * layer to send a response to a received lock request (ARREQ). The * response will differ between 32-bit/64-bit lock requests. */ int hci1394_async_lock_response(hci1394_async_handle_t async_handle, cmd1394_cmd_t *cmd, h1394_cmd_priv_t *cmd_priv, int *result) { hci1394_basic_pkt_t header; hci1394_async_cmd_t *hcicmd; uint32_t data32; uint64_t data64; uint8_t *datap; uint_t size; int status; ASSERT(async_handle != NULL); ASSERT(cmd != NULL); ASSERT(cmd_priv != NULL); ASSERT(result != NULL); /* * make sure this call is during the current bus generation (i.e. no * bus resets have occured since this request was made. */ if (cmd_priv->bus_generation != hci1394_ohci_current_busgen( async_handle->as_ohci)) { *result = H1394_STATUS_INVALID_BUSGEN; return (DDI_FAILURE); } /* * setup a shortcut to the hal private command area. Copy the generation * to the Q area so that we can check the generation when the AT Q is * locked. This prevents us from loosing commands due to race * conditions. */ hcicmd = (hci1394_async_cmd_t *)cmd_priv->hal_overhead; hcicmd->ac_qcmd.qc_generation = cmd_priv->bus_generation; /* * Setup the packet header information for a ATRESP lock packet. Set * the tcode for a lock response. Set SRCBusId if the addr is not a * local bus address. Copy in the speed, tlabel, and response code. */ header.q1 = DESC_ATRESP_Q1_LCK; if ((cmd->nodeID & IEEE1394_BUS_NUM_MASK) != IEEE1394_BUS_NUM_MASK) { header.q1 |= DESC_AT_SRCBUSID; } header.q1 |= HCI1394_DESC_AT_SPD_SET(cmd_priv->speed) | HCI1394_DESC_TLABEL_SET(hcicmd->ac_tlabel.tbi_tlabel); header.q2 = (uint32_t)(HCI1394_DESC_DESTID_SET(cmd->nodeID) | HCI1394_DESC_RCODE_SET(cmd->cmd_result)); header.q3 = 0; /* * If the lock result is not a resp complete, we are not going to send * any data back.with the response. */ if (cmd->cmd_result != IEEE1394_RESP_COMPLETE) { /* set response size to 0 for error. Set the extended tcode */ size = 0; if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) { header.q4 = HCI1394_DESC_DATALEN_SET(size) | HCI1394_DESC_EXTTCODE_SET(cmd->cmd_u.l32.lock_type); } else { header.q4 = HCI1394_DESC_DATALEN_SET(size) | HCI1394_DESC_EXTTCODE_SET(cmd->cmd_u.l64.lock_type); } /* * Write response into the ATRESP Q. If we fail, we're out of * space. */ status = hci1394_q_at(async_handle->as_atresp_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_LOCK_RESP, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * if the lock result is resp complete, setup the size of the response * depending on the lock size and copy the lock response data into a * local buffer. If the lock response is an arithmetic operation, swap * the data on little endian machines. If we don't know what type of * lock operation it is, someone has corrupted the command since we * had received the ARREQ. */ if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) { size = IEEE1394_QUADLET; header.q4 = HCI1394_DESC_DATALEN_SET(size) | HCI1394_DESC_EXTTCODE_SET(cmd->cmd_u.l32.lock_type); data32 = HCI1394_ARITH_LOCK_SWAP32( cmd->cmd_u.l32.lock_type, cmd->cmd_u.l32.old_value); datap = (uint8_t *)&data32; } else if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_64) { size = IEEE1394_OCTLET; header.q4 = HCI1394_DESC_DATALEN_SET(size) | HCI1394_DESC_EXTTCODE_SET(cmd->cmd_u.l64.lock_type); data64 = HCI1394_ARITH_LOCK_SWAP64( cmd->cmd_u.l64.lock_type, cmd->cmd_u.l64.old_value); datap = (uint8_t *)&data64; } else { *result = H1394_STATUS_INTERNAL_ERROR; return (DDI_FAILURE); } /* * Write response into the ATRESP Q. If we fail, we're out of space. * Use the local data buffer that we copied the data to above. */ status = hci1394_q_at_with_data(async_handle->as_atresp_q, &hcicmd->ac_qcmd, &header, DESC_PKT_HDRLEN_AT_LOCK_RESP, datap, size, result); if (status != DDI_SUCCESS) { return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * hci1394_async_response_complete() * Free up space allocted during an ARREQ. This is called when the target * driver and Services Layer are done with a command which was by the HAL * during ARREQ processing. This routine will also free up any allocated * mblks. * * NOTE: a target driver can hold on to a block write ARREQ mblk by setting * the mblk pointer to NULL. This ONLY applies to block write ARREQs. The * HAL will no longer track the mblk for this case. */ void hci1394_async_response_complete(hci1394_async_handle_t async_handle, cmd1394_cmd_t *cmd, h1394_cmd_priv_t *cmd_priv) { hci1394_async_cmd_t *hcicmd; ASSERT(async_handle != NULL); ASSERT(cmd != NULL); ASSERT(cmd_priv != NULL); hcicmd = (hci1394_async_cmd_t *)cmd_priv->hal_overhead; /* If we allocated an mblk for this command */ if (hcicmd->ac_mblk_alloc == B_TRUE) { /* * Don't free mblk if it is set to NULL. This allows a target * driver to hold on to it in the case of a block write ARREQ. */ if (cmd->cmd_u.b.data_block != NULL) { freeb(cmd->cmd_u.b.data_block); } } /* free up the 1394 framework command */ (void) h1394_free_cmd((void *)async_handle->as_drvinfo->di_sl_private, &cmd); } /* * hci1394_async_pending_timeout() * This is the ARREQ Pending timeout callback routine. It is called from * the tlist code. There is a race condition with the ARRESP interrupt * handler (hci1394_async_arresp_process) which requires a mutex to * lock around the mark of the bad tlabel. * * Once we enter this routine, the command has timed out. If the command is * in both the ARRESP handler and here, we will consider it to have timed * out. That code path handles the race condition more easily. */ static void hci1394_async_pending_timeout(hci1394_tlist_node_t *node, void *arg) { hci1394_async_handle_t async_handle; hci1394_async_cmd_t *hcicmd; async_handle = (hci1394_async_handle_t)arg; ASSERT(async_handle != NULL); ASSERT(node != NULL); hcicmd = (hci1394_async_cmd_t *)node->tln_addr; /* * We do NOT want to set the command state here. That should only be * done in the ISR. The state does nothing for us here. */ /* * We want a lock around tlabel_lookup/reading data into the cmd in the * ARRESP ISR processing and a lock around the tlabel_bad in this * routine. This ensures that we will not be touching the command * structure after we pass it up to the Services Layer. If we mark it as * bad first, the lookup will fail. If we get to the lookup first, the * pending list delete will fail in arresp_process() which will tell * that guy that we are in the middle of doing the timeout processing * for this command. The ARRESP logic will just drop the response and * continue on. */ mutex_enter(&hcicmd->ac_async->as_atomic_lookup); hci1394_tlabel_bad(async_handle->as_tlabel, &hcicmd->ac_tlabel); mutex_exit(&hcicmd->ac_async->as_atomic_lookup); /* Tell the Services Layer that the command has timed out */ h1394_cmd_is_complete(async_handle->as_drvinfo->di_sl_private, hcicmd->ac_cmd, H1394_AT_REQ, H1394_CMD_ETIMEOUT); } /* * hci1394_async_timeout_calc() * Calculate the timeout for an ATRESP. When an ARREQ is received, this * routine is called with the time the ARREQ was received. It returns the * time when the ATRESP is considered to have timed out. We timeout after * split_timeout has gone by. Split timeout and the returned value are in bus * cycles. */ static uint_t hci1394_async_timeout_calc(hci1394_async_handle_t async_handle, uint_t current_time) { uint_t split_timeout; uint_t temp; uint_t carry; uint_t z; /* Get the current split timeout */ split_timeout = hci1394_csr_split_timeout_get(async_handle->as_csr); /* * The cycle count is broken up into two sections, the 3-bit seconds * field and the 13-bit cycle count. The cycle count is in 125uS * increments. The maximum value of cycle count is 7999 (8000 is one * second). With 13-bits, we could store up to 8191. Therefore, we don't * have a simple 16-bit addition. Hence, the code we see below. */ /* * calculate the new cycle count based on the cycle count from current * time and the split timeout. If this new value is not greater than the * maximum cycle count, we don't have a carry. Go to the next step. */ temp = (current_time & OHCI_CYCLE_CNT_MASK) + (split_timeout & OHCI_CYCLE_CNT_MASK); if (temp < OHCI_MAX_CYCLE_CNT) { carry = 0; /* * the new cycle count adds up to more than the maximum cycle count, * set the carry state and adjust the total accordingly. */ } else { temp = temp - OHCI_MAX_CYCLE_CNT; carry = 1; } /* * The timeout time equals the seconds added with the carry (1 or 0 * seconds), added with the adjusted (if necessary) cycle count. * Mask the final value to get rid of any second rollovers. */ z = (current_time & OHCI_CYCLE_SEC_MASK) + (split_timeout & OHCI_CYCLE_SEC_MASK) + (carry << OHCI_CYCLE_SEC_SHIFT) + temp; z = z & OHCI_TIMESTAMP_MASK; return (z); } /* * hci1394_async_arresp_size_get() * Return the size of the arresp that was received in q_handle at addr. */ static int hci1394_async_arresp_size_get(uint_t tcode, hci1394_q_handle_t q_handle, uint32_t *addr, uint_t *size) { uint_t data_length; uint32_t quadlet; ASSERT(q_handle != NULL); ASSERT(addr != NULL); ASSERT(size != NULL); if (tcode == IEEE1394_TCODE_WRITE_RESP) { *size = DESC_PKT_HDRLEN_AT_WRITE_RESP + IEEE1394_QUADLET; } else if (tcode == IEEE1394_TCODE_READ_QUADLET_RESP) { *size = DESC_PKT_HDRLEN_AT_READQUAD_RESP + IEEE1394_QUADLET; } else if (tcode == IEEE1394_TCODE_READ_BLOCK_RESP) { quadlet = hci1394_q_ar_get32(q_handle, &addr[3]); data_length = HCI1394_DESC_DATALEN_GET(quadlet); /* * response size is in quadlets, therefore we need to * make sure we count in the padding when figuring out * the size used up for this response */ *size = DESC_PKT_HDRLEN_AT_READBLOCK_RESP + HCI1394_ALIGN_QUAD(data_length) + IEEE1394_QUADLET; } else if (tcode == IEEE1394_TCODE_LOCK_RESP) { quadlet = hci1394_q_ar_get32(q_handle, &addr[3]); data_length = HCI1394_DESC_DATALEN_GET(quadlet); /* * response size is in quadlets, therefore we need to * make sure we count in the padding when figuring out * the size used up for this response */ *size = DESC_PKT_HDRLEN_AT_LOCK_RESP + HCI1394_ALIGN_QUAD(data_length) + IEEE1394_QUADLET; } else { return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * hci1394_async_pending_list_flush() * Flush out the ATREQ pending list. All commands still on the ATREQ pending * list are considered to be completed due to a bus reset. The ATREQ and * ARRESP Q's should be flushed before the pending Q is flushed. The ATREQ * could have more ACK pendings and the ARRESP could have valid responses to * pended requests. */ void hci1394_async_pending_list_flush(hci1394_async_handle_t async_handle) { hci1394_tlist_node_t *node; hci1394_async_cmd_t *hcicmd; ASSERT(async_handle != NULL); do { /* * get the first node on the pending list. This routine also * removes the node from the list. */ hci1394_tlist_get(async_handle->as_pending_list, &node); if (node != NULL) { /* set the command state to completed */ hcicmd = (hci1394_async_cmd_t *)node->tln_addr; hcicmd->ac_state = HCI1394_CMD_STATE_COMPLETED; /* * Send the command up to the Services Layer with * completed due to the bus reset for status. */ h1394_cmd_is_complete( async_handle->as_drvinfo->di_sl_private, hcicmd->ac_cmd, H1394_AT_REQ, H1394_CMD_EBUSRESET); } } while (node != NULL); } /* * hci1394_async_atreq_start() * Setup the command pointer for the first descriptor to be fetched and * then set the run bit. This routine will be called the first time * a descriptor is added to the Q. */ static void hci1394_async_atreq_start(void *async, uint32_t command_ptr) { hci1394_async_handle_t async_handle; ASSERT(async != NULL); async_handle = (hci1394_async_handle_t)async; hci1394_ohci_atreq_start(async_handle->as_ohci, command_ptr); } /* * hci1394_async_atreq_wake() * Set the wake bit for the ATREQ DMA engine. This routine will be called * from the Q logic after placing a descriptor on the Q. */ static void hci1394_async_atreq_wake(void *async) { hci1394_async_handle_t async_handle; ASSERT(async != NULL); async_handle = (hci1394_async_handle_t)async; hci1394_ohci_atreq_wake(async_handle->as_ohci); } /* * hci1394_async_atreq_reset() * Reset the atreq Q. The AT DMA engines must be stopped every bus reset. * They will restart when the next descriptor is added to the Q. We will stop * the DMA engine and then notify the Q logic that it has been stopped so it * knows to do a start next time it puts a descriptor on the Q. */ void hci1394_async_atreq_reset(hci1394_async_handle_t async_handle) { ASSERT(async_handle != NULL); hci1394_ohci_atreq_stop(async_handle->as_ohci); hci1394_q_stop(async_handle->as_atreq_q); } /* * hci1394_async_atreq_flush() * Flush out the atreq Q. This routine is called during bus reset processing. * it should be called before arresp_flush() and pending_list_flush(). */ static void hci1394_async_atreq_flush(hci1394_async_handle_t async_handle) { boolean_t request_available; ASSERT(async_handle != NULL); /* Clear reqTxComplete interrupt */ hci1394_ohci_intr_clear(async_handle->as_ohci, OHCI_INTR_REQ_TX_CMPLT); /* * Processes all Q'd AT requests. If the request is pended, it is * considered complete relative the the atreq engine. * flush_pending_list() will finish up the required processing for * pended requests. */ do { /* Flush the atreq Q. Process all Q'd commands */ (void) hci1394_async_atreq_process(async_handle, B_TRUE, &request_available); } while (request_available == B_TRUE); } /* * hci1394_async_arresp_start() * Setup the command pointer for the first descriptor to be fetched and * then set the run bit. This routine will be called the first time * a descriptor is added to the Q. */ static void hci1394_async_arresp_start(void *async, uint32_t command_ptr) { hci1394_async_handle_t async_handle; ASSERT(async != NULL); async_handle = (hci1394_async_handle_t)async; hci1394_ohci_arresp_start(async_handle->as_ohci, command_ptr); } /* * hci1394_async_arresp_wake() * Set the wake bit for the ARRESP DMA engine. This routine will be called * from the Q logic after placing a descriptor on the Q. */ static void hci1394_async_arresp_wake(void *async) { hci1394_async_handle_t async_handle; ASSERT(async != NULL); async_handle = (hci1394_async_handle_t)async; hci1394_ohci_arresp_wake(async_handle->as_ohci); } /* * hci1394_async_arresp_flush() * Flush out the arresp Q. This routine is called during bus reset * processing. This should be called before pending_list_flush(). All * receive responses will be processed normally. The tlabels should * not be reset until after the ARRESP Q has been flushed. Otherwise * we would reject valid responses. */ static void hci1394_async_arresp_flush(hci1394_async_handle_t async_handle) { boolean_t response_available; ASSERT(async_handle != NULL); /* Clear reqTxComplete interrupt */ hci1394_ohci_intr_clear(async_handle->as_ohci, OHCI_INTR_RSPKT); do { /* Flush the arresp Q. Process all received commands */ (void) hci1394_async_arresp_process(async_handle, &response_available); } while (response_available == B_TRUE); } /* * hci1394_async_arreq_start() * Setup the command pointer for the first descriptor to be fetched and * then set the run bit. This routine will be called the first time * a descriptor is added to the Q. */ static void hci1394_async_arreq_start(void *async, uint32_t command_ptr) { hci1394_async_handle_t async_handle; ASSERT(async != NULL); async_handle = (hci1394_async_handle_t)async; hci1394_ohci_arreq_start(async_handle->as_ohci, command_ptr); } /* * hci1394_async_arreq_wake() * Set the wake bit for the ARREQ DMA engine. This routine will be called * from the Q logic after placing a descriptor on the Q. */ static void hci1394_async_arreq_wake(void *async) { hci1394_async_handle_t async_handle; ASSERT(async != NULL); async_handle = (hci1394_async_handle_t)async; hci1394_ohci_arreq_wake(async_handle->as_ohci); } /* * hci1394_async_arreq_flush() * Flush the ARREQ Q. This will flush up to the bus reset token in the * ARREQ. There is no order dependency for when routine should get called * (relative to the other Q flushing routines) */ static void hci1394_async_arreq_flush(hci1394_async_handle_t async_handle) { boolean_t request_available; ASSERT(async_handle != NULL); /* * If the last bus reset token we have seen in * hci1394_async_arreq_read_phy() matches the current generation, the * ARREQ is already flushed. We have nothing further to do here so * return. This can happen if we are processing ARREQ's and a bus reset * occurs. Since we are already in the ISR, we will see the token before * the bus reset handler gets to run. */ if (async_handle->as_phy_reset == hci1394_ohci_current_busgen( async_handle->as_ohci)) { return; } /* * set flag to tell hci1394_async_arreq_process() that we should not * pass ARREQ's up to the Services Layer. This will be set to B_FALSE * in hci1394_async_arreq_read_phy() when a bus reset token matching * the current generation is found. */ async_handle->as_flushing_arreq = B_TRUE; /* * Process all requests that have been received or until we find the * correct bus reset token. */ do { (void) hci1394_async_arreq_process(async_handle, &request_available); } while ((request_available == B_TRUE) && (async_handle->as_flushing_arreq == B_TRUE)); /* * Clear the asserted interrupt if there are no more ARREQ's to process. * We could have ARREQ's in the Q after the bus reset token since we * will set as_flushing_arreq to FALSE when we see the correct bus reset * token in hci1394_async_arreq_read_phy(). If there are more ARREQ's, * we will process them later after finishing the reset of bus reset * processing. That is why we will leave the interrupt asserted. */ if (request_available == B_FALSE) { hci1394_ohci_intr_clear(async_handle->as_ohci, OHCI_INTR_RQPKT); } } /* * hci1394_async_atresp_start() * Setup the command pointer for the first descriptor to be fetched and * then set the run bit. This routine will be called the first time * a descriptor is added to the Q. */ static void hci1394_async_atresp_start(void *async, uint32_t command_ptr) { hci1394_async_handle_t async_handle; ASSERT(async != NULL); async_handle = (hci1394_async_handle_t)async; hci1394_ohci_atresp_start(async_handle->as_ohci, command_ptr); } /* * hci1394_async_atresp_wake() * Set the wake bit for the ATRESP DMA engine. This routine will be called * from the Q logic after placing a descriptor on the Q. */ static void hci1394_async_atresp_wake(void *async) { hci1394_async_handle_t async_handle; ASSERT(async != NULL); async_handle = (hci1394_async_handle_t)async; hci1394_ohci_atresp_wake(async_handle->as_ohci); } /* * hci1394_async_atresp_reset() * Reset the atresp Q. The AT DMA engines must be stopped every bus reset. * They will restart when the next descriptor is added to the Q. We will stop * the DMA engine and then notify the Q logic that it has been stopped so it * knows to do a start next time it puts a descriptor on the Q. */ void hci1394_async_atresp_reset(hci1394_async_handle_t async_handle) { ASSERT(async_handle != NULL); hci1394_ohci_atresp_stop(async_handle->as_ohci); hci1394_q_stop(async_handle->as_atresp_q); } /* * hci1394_async_atresp_flush() * Flush all commands out of the atresp Q. This routine will be called * during bus reset processing. There is no order dependency for when * routine should get called (relative to the other Q flushing routines) */ static void hci1394_async_atresp_flush(hci1394_async_handle_t async_handle) { boolean_t response_available; ASSERT(async_handle != NULL); /* Clear respTxComplete interrupt */ hci1394_ohci_intr_clear(async_handle->as_ohci, OHCI_INTR_RESP_TX_CMPLT); /* Processes all AT responses */ do { /* Flush the atresp Q. Process all Q'd commands */ (void) hci1394_async_atresp_process(async_handle, B_TRUE, &response_available); } while (response_available == B_TRUE); } /* * hci1394_async_hcicmd_init() * Initialize the private HAL command structure. This should be called from * ATREQ and ARREQ routines. */ static void hci1394_async_hcicmd_init(hci1394_async_handle_t async_handle, cmd1394_cmd_t *cmd, h1394_cmd_priv_t *cmd_priv, hci1394_async_cmd_t **hcicmd) { *hcicmd = (hci1394_async_cmd_t *)cmd_priv->hal_overhead; (*hcicmd)->ac_cmd = cmd; (*hcicmd)->ac_priv = cmd_priv; (*hcicmd)->ac_async = async_handle; (*hcicmd)->ac_state = HCI1394_CMD_STATE_IN_PROGRESS; (*hcicmd)->ac_dest = 0; (*hcicmd)->ac_tlabel_alloc = B_TRUE; (*hcicmd)->ac_tlabel.tbi_tlabel = 0; (*hcicmd)->ac_tlabel.tbi_destination = 0; (*hcicmd)->ac_status = 0; (*hcicmd)->ac_qcmd.qc_timestamp = 0; (*hcicmd)->ac_qcmd.qc_arg = *hcicmd; (*hcicmd)->ac_qcmd.qc_generation = cmd_priv->bus_generation; (*hcicmd)->ac_mblk_alloc = B_FALSE; }