aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/twa/tw_cl_intr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/twa/tw_cl_intr.c')
-rw-r--r--sys/dev/twa/tw_cl_intr.c826
1 files changed, 826 insertions, 0 deletions
diff --git a/sys/dev/twa/tw_cl_intr.c b/sys/dev/twa/tw_cl_intr.c
new file mode 100644
index 000000000000..3271d59f7239
--- /dev/null
+++ b/sys/dev/twa/tw_cl_intr.c
@@ -0,0 +1,826 @@
+/*
+ * Copyright (c) 2004-05 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * AMCC'S 3ware driver for 9000 series storage controllers.
+ *
+ * Author: Vinod Kashyap
+ */
+
+
+/*
+ * Common Layer interrupt handling functions.
+ */
+
+
+#include "tw_osl_share.h"
+#include "tw_cl_share.h"
+#include "tw_cl_fwif.h"
+#include "tw_cl_ioctl.h"
+#include "tw_cl.h"
+#include "tw_cl_externs.h"
+#include "tw_osl_ioctl.h"
+
+
+
+/*
+ * Function name: twa_interrupt
+ * Description: Interrupt handler. Determines the kind of interrupt,
+ * and returns TW_CL_TRUE if it recognizes the interrupt.
+ *
+ * Input: ctlr_handle -- controller handle
+ * Output: None
+ * Return value: TW_CL_TRUE -- interrupt recognized
+ * TW_CL_FALSE-- interrupt not recognized
+ */
+TW_INT32
+tw_cl_interrupt(struct tw_cl_ctlr_handle *ctlr_handle)
+{
+ struct tw_cli_ctlr_context *ctlr =
+ (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+ TW_UINT32 status_reg;
+ TW_INT32 rc = TW_CL_FALSE;
+
+ tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /*
+ * Serialize access to this function so multiple threads don't try to
+ * do the same thing (such as clearing interrupt bits).
+ */
+ tw_osl_get_lock(ctlr_handle, ctlr->intr_lock);
+
+ /* Read the status register to determine the type of interrupt. */
+ status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle);
+ if (tw_cli_check_ctlr_state(ctlr, status_reg))
+ goto out;
+
+ /* Clear the interrupt. */
+ if (status_reg & TWA_STATUS_HOST_INTERRUPT) {
+ tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
+ "Host interrupt");
+ TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
+ TWA_CONTROL_CLEAR_HOST_INTERRUPT);
+ ctlr->host_intr_pending = 0; /* we don't use this */
+ rc |= TW_CL_FALSE; /* don't request for a deferred isr call */
+ }
+ if (status_reg & TWA_STATUS_ATTENTION_INTERRUPT) {
+ tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
+ "Attention interrupt");
+ TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
+ TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT);
+ ctlr->attn_intr_pending = 1;
+ rc |= TW_CL_TRUE; /* request for a deferred isr call */
+ }
+ if (status_reg & TWA_STATUS_COMMAND_INTERRUPT) {
+ tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
+ "Command interrupt");
+ TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
+ TWA_CONTROL_MASK_COMMAND_INTERRUPT);
+ ctlr->cmd_intr_pending = 1;
+ rc |= TW_CL_TRUE; /* request for a deferred isr call */
+ }
+ if (status_reg & TWA_STATUS_RESPONSE_INTERRUPT) {
+ tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(),
+ "Response interrupt");
+ TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
+ TWA_CONTROL_MASK_RESPONSE_INTERRUPT);
+ ctlr->resp_intr_pending = 1;
+ rc |= TW_CL_TRUE; /* request for a deferred isr call */
+ }
+out:
+ tw_osl_free_lock(ctlr_handle, ctlr->intr_lock);
+
+ return(rc);
+}
+
+
+
+/*
+ * Function name: tw_cl_deferred_interrupt
+ * Description: Deferred interrupt handler. Does most of the processing
+ * related to an interrupt.
+ *
+ * Input: ctlr_handle -- controller handle
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cl_deferred_interrupt(struct tw_cl_ctlr_handle *ctlr_handle)
+{
+ struct tw_cli_ctlr_context *ctlr =
+ (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
+
+ tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /* Dispatch based on the kind of interrupt. */
+ if (ctlr->host_intr_pending) {
+ tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
+ "Processing Host interrupt");
+ ctlr->host_intr_pending = 0;
+ tw_cli_process_host_intr(ctlr);
+ }
+ if (ctlr->attn_intr_pending) {
+ tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
+ "Processing Attention interrupt");
+ ctlr->attn_intr_pending = 0;
+ tw_cli_process_attn_intr(ctlr);
+ }
+ if (ctlr->cmd_intr_pending) {
+ tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
+ "Processing Command interrupt");
+ ctlr->cmd_intr_pending = 0;
+ tw_cli_process_cmd_intr(ctlr);
+ }
+ if (ctlr->resp_intr_pending) {
+ tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(),
+ "Processing Response interrupt");
+ ctlr->resp_intr_pending = 0;
+ tw_cli_process_resp_intr(ctlr);
+ }
+}
+
+
+
+/*
+ * Function name: tw_cli_process_host_intr
+ * Description: This function gets called if we triggered an interrupt.
+ * We don't use it as of now.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_process_host_intr(struct tw_cli_ctlr_context *ctlr)
+{
+ tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+}
+
+
+
+/*
+ * Function name: tw_cli_process_attn_intr
+ * Description: This function gets called if the fw posted an AEN
+ * (Asynchronous Event Notification). It fetches
+ * all the AEN's that the fw might have posted.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_process_attn_intr(struct tw_cli_ctlr_context *ctlr)
+{
+ TW_INT32 error;
+
+ tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ if ((error = tw_cli_get_aen(ctlr))) {
+ /*
+ * If the driver is already in the process of retrieveing AEN's,
+ * we will be returned TW_OSL_EBUSY. In this case,
+ * tw_cli_param_callback or tw_cli_aen_callback will eventually
+ * retrieve the AEN this attention interrupt is for. So, we
+ * don't need to print the failure.
+ */
+ if (error != TW_OSL_EBUSY)
+ tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+ 0x1200, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "Failed to fetch AEN",
+ "error = %d", error);
+ }
+}
+
+
+
+/*
+ * Function name: tw_cli_process_cmd_intr
+ * Description: This function gets called if we hit a queue full
+ * condition earlier, and the fw is now ready for
+ * new cmds. Submits any pending requests.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_process_cmd_intr(struct tw_cli_ctlr_context *ctlr)
+{
+ tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /*
+ * Let the OS Layer submit any requests in its pending queue,
+ * if it has one.
+ */
+ tw_osl_ctlr_ready(ctlr->ctlr_handle);
+
+ /* Start any requests that might be in the pending queue. */
+ tw_cli_submit_pending_queue(ctlr);
+
+ /*
+ * If tw_cli_submit_pending_queue was unsuccessful due to a "cmd queue
+ * full" condition, cmd_intr will already have been unmasked by
+ * tw_cli_submit_cmd. We don't need to do it again... simply return.
+ */
+}
+
+
+
+/*
+ * Function name: tw_cli_process_resp_intr
+ * Description: Looks for cmd completions from fw; queues cmds completed
+ * by fw into complete queue.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: 0 -- no ctlr error
+ * non-zero-- ctlr error
+ */
+TW_INT32
+tw_cli_process_resp_intr(struct tw_cli_ctlr_context *ctlr)
+{
+ TW_UINT32 resp;
+ struct tw_cli_req_context *req;
+ TW_INT32 error;
+ TW_UINT32 status_reg;
+
+ tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /* Serialize access to the controller response queue. */
+ tw_osl_get_lock(ctlr->ctlr_handle, ctlr->intr_lock);
+
+ for (;;) {
+ status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle);
+ if ((error = tw_cli_check_ctlr_state(ctlr, status_reg)))
+ break;
+ if (status_reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY) {
+ tw_cli_dbg_printf(7, ctlr->ctlr_handle,
+ tw_osl_cur_func(), "Response queue empty");
+ break;
+ }
+
+ /* Response queue is not empty. */
+ resp = TW_CLI_READ_RESPONSE_QUEUE(ctlr->ctlr_handle);
+#ifdef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST
+ if (GET_RESP_ID(resp) >= 1)
+ req = ctlr->busy_reqs[GET_RESP_ID(resp)];
+ else
+#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */
+ {
+ req = &(ctlr->req_ctxt_buf[GET_RESP_ID(resp)]);
+ }
+
+ if (req->state != TW_CLI_REQ_STATE_BUSY) {
+ tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+ 0x1201, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "Unposted command completed!!",
+ "request = %p, status = %d",
+ req, req->state);
+#ifdef TW_OSL_DEBUG
+ tw_cl_print_ctlr_stats(ctlr->ctlr_handle);
+#endif /* TW_OSL_DEBUG */
+ tw_osl_free_lock(ctlr->ctlr_handle, ctlr->intr_lock);
+ tw_cl_reset_ctlr(ctlr->ctlr_handle);
+ return(TW_OSL_EIO);
+ }
+
+ /*
+ * Remove the request from the busy queue, mark it as complete,
+ * and enqueue it in the complete queue.
+ */
+ tw_cli_req_q_remove_item(req, TW_CLI_BUSY_Q);
+ req->state = TW_CLI_REQ_STATE_COMPLETE;
+ tw_cli_req_q_insert_tail(req, TW_CLI_COMPLETE_Q);
+#ifdef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST
+ /* Call the CL internal callback, if there's one. */
+ if (req->tw_cli_callback)
+ req->tw_cli_callback(req);
+#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */
+ }
+
+ /* Unmask the response interrupt. */
+ TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+ TWA_CONTROL_UNMASK_RESPONSE_INTERRUPT);
+
+ tw_osl_free_lock(ctlr->ctlr_handle, ctlr->intr_lock);
+
+#ifndef TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST
+ /* Complete this, and other requests in the complete queue. */
+ tw_cli_process_complete_queue(ctlr);
+#endif /* TW_OSL_NON_DMA_MEM_ALLOC_PER_REQUEST */
+
+ return(error);
+}
+
+
+
+/*
+ * Function name: tw_cli_submit_pending_queue
+ * Description: Kick starts any requests in the pending queue.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: 0 -- all pending requests submitted successfully
+ * non-zero-- otherwise
+ */
+TW_INT32
+tw_cli_submit_pending_queue(struct tw_cli_ctlr_context *ctlr)
+{
+ struct tw_cli_req_context *req;
+ TW_INT32 error = TW_OSL_ESUCCESS;
+
+ tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /*
+ * Pull requests off the pending queue, and submit them.
+ */
+ while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_PENDING_Q)) !=
+ TW_CL_NULL) {
+ if ((error = tw_cli_submit_cmd(req))) {
+ if (error == TW_OSL_EBUSY) {
+ tw_cli_dbg_printf(2, ctlr->ctlr_handle,
+ tw_osl_cur_func(),
+ "Requeueing pending request");
+ req->state = TW_CLI_REQ_STATE_PENDING;
+ /*
+ * Queue the request at the head of the pending
+ * queue, and break away, so we don't try to
+ * submit any more requests.
+ */
+ tw_cli_req_q_insert_head(req, TW_CLI_PENDING_Q);
+ break;
+ } else {
+ tw_cl_create_event(ctlr->ctlr_handle,
+ TW_CL_FALSE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+ 0x1202, 0x1,
+ TW_CL_SEVERITY_ERROR_STRING,
+ "Could not start request "
+ "in pending queue",
+ "request = %p, opcode = 0x%x, "
+ "error = %d", req,
+ GET_OPCODE(req->cmd_pkt->
+ command.cmd_pkt_9k.res__opcode),
+ error);
+ /*
+ * Set the appropriate error and call the CL
+ * internal callback if there's one. If the
+ * request originator is polling for completion,
+ * he should be checking req->error to
+ * determine that the request did not go
+ * through. The request originators are
+ * responsible for the clean-up.
+ */
+ req->error_code = error;
+ req->state = TW_CLI_REQ_STATE_COMPLETE;
+ if (req->tw_cli_callback)
+ req->tw_cli_callback(req);
+ error = TW_OSL_ESUCCESS;
+ }
+ }
+ }
+ return(error);
+}
+
+
+
+/*
+ * Function name: tw_cli_process_complete_queue
+ * Description: Calls the CL internal callback routine, if any, for
+ * each request in the complete queue.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_process_complete_queue(struct tw_cli_ctlr_context *ctlr)
+{
+ struct tw_cli_req_context *req;
+
+ tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /*
+ * Pull commands off the completed list, dispatch them appropriately.
+ */
+ while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_COMPLETE_Q)) !=
+ TW_CL_NULL) {
+ /* Call the CL internal callback, if there's one. */
+ if (req->tw_cli_callback)
+ req->tw_cli_callback(req);
+ }
+}
+
+
+
+/*
+ * Function name: tw_cli_complete_io
+ * Description: CL internal callback for SCSI/fw passthru requests.
+ *
+ * Input: req -- ptr to CL internal request context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_complete_io(struct tw_cli_req_context *req)
+{
+ struct tw_cli_ctlr_context *ctlr = req->ctlr;
+ struct tw_cl_req_packet *req_pkt =
+ (struct tw_cl_req_packet *)(req->orig_req);
+
+ tw_cli_dbg_printf(8, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ req_pkt->status = TW_CL_ERR_REQ_SUCCESS;
+ if (req->error_code) {
+ req_pkt->status = TW_CL_ERR_REQ_UNABLE_TO_SUBMIT_COMMAND;
+ goto out;
+ }
+
+ if (req->state != TW_CLI_REQ_STATE_COMPLETE) {
+ tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+ 0x1203, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "I/O completion on incomplete command!!",
+ "request = %p, status = %d",
+ req, req->state);
+#ifdef TW_OSL_DEBUG
+ tw_cl_print_ctlr_stats(ctlr->ctlr_handle);
+#endif /* TW_OSL_DEBUG */
+ tw_cl_reset_ctlr(ctlr->ctlr_handle);
+ req_pkt->status = TW_CL_ERR_REQ_BUS_RESET;
+ goto out;
+ }
+
+ if (req->flags & TW_CLI_REQ_FLAGS_PASSTHRU) {
+ /* Copy the command packet back into OSL's space. */
+ tw_osl_memcpy(req_pkt->gen_req_pkt.pt_req.cmd_pkt, req->cmd_pkt,
+ sizeof(struct tw_cl_command_packet));
+ } else
+ tw_cli_scsi_complete(req);
+
+out:
+ req_pkt->tw_osl_callback(req->req_handle);
+ tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+}
+
+
+
+/*
+ * Function name: tw_cli_scsi_complete
+ * Description: Completion routine for SCSI requests.
+ *
+ * Input: req -- ptr to CL internal request context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_scsi_complete(struct tw_cli_req_context *req)
+{
+ struct tw_cl_req_packet *req_pkt =
+ (struct tw_cl_req_packet *)(req->orig_req);
+ struct tw_cl_scsi_req_packet *scsi_req =
+ &(req_pkt->gen_req_pkt.scsi_req);
+ struct tw_cl_command_9k *cmd =
+ &(req->cmd_pkt->command.cmd_pkt_9k);
+ struct tw_cl_command_header *cmd_hdr;
+ TW_UINT16 error;
+ TW_UINT8 *cdb;
+
+ tw_cli_dbg_printf(8, req->ctlr->ctlr_handle, tw_osl_cur_func(),
+ "entered");
+
+ scsi_req->scsi_status = cmd->status;
+ if (! cmd->status)
+ return;
+
+ tw_cli_dbg_printf(1, req->ctlr->ctlr_handle, tw_osl_cur_func(),
+ "req_id = 0x%x, status = 0x%x",
+ GET_REQ_ID(cmd->lun_l4__req_id), cmd->status);
+
+ cmd_hdr = &(req->cmd_pkt->cmd_hdr);
+ error = cmd_hdr->status_block.error;
+ if ((error == TWA_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) ||
+ (error == TWA_ERROR_UNIT_OFFLINE)) {
+ if (GET_LUN_L4(cmd->lun_l4__req_id))
+ req_pkt->status |= TW_CL_ERR_REQ_INVALID_LUN;
+ else
+ req_pkt->status |= TW_CL_ERR_REQ_INVALID_TARGET;
+ } else {
+ tw_cli_dbg_printf(2, req->ctlr->ctlr_handle,
+ tw_osl_cur_func(),
+ "cmd = %x %x %x %x %x %x %x",
+ GET_OPCODE(cmd->res__opcode),
+ GET_SGL_OFF(cmd->res__opcode),
+ cmd->unit,
+ cmd->lun_l4__req_id,
+ cmd->status,
+ cmd->sgl_offset,
+ cmd->lun_h4__sgl_entries);
+
+ cdb = (TW_UINT8 *)(cmd->cdb);
+ tw_cli_dbg_printf(2, req->ctlr->ctlr_handle,
+ tw_osl_cur_func(),
+ "cdb = %x %x %x %x %x %x %x %x "
+ "%x %x %x %x %x %x %x %x",
+ cdb[0], cdb[1], cdb[2], cdb[3],
+ cdb[4], cdb[5], cdb[6], cdb[7],
+ cdb[8], cdb[9], cdb[10], cdb[11],
+ cdb[12], cdb[13], cdb[14], cdb[15]);
+
+ /*
+ * Print the error. Firmware doesn't yet support
+ * the 'Mode Sense' cmd. Don't print if the cmd
+ * is 'Mode Sense', and the error is 'Invalid field
+ * in CDB'.
+ */
+ if (! ((cdb[0] == 0x1A) && (error == 0x10D)))
+ tw_cli_create_ctlr_event(req->ctlr,
+ TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
+ cmd_hdr);
+ }
+
+ if (scsi_req->sense_data) {
+ tw_osl_memcpy(scsi_req->sense_data, cmd_hdr->sense_data,
+ TWA_SENSE_DATA_LENGTH);
+ scsi_req->sense_len = TWA_SENSE_DATA_LENGTH;
+ req_pkt->status |= TW_CL_ERR_REQ_AUTO_SENSE_VALID;
+ }
+ req_pkt->status |= TW_CL_ERR_REQ_SCSI_ERROR;
+}
+
+
+
+/*
+ * Function name: tw_cli_param_callback
+ * Description: Callback for get/set_param requests.
+ *
+ * Input: req -- ptr to completed request pkt
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_param_callback(struct tw_cli_req_context *req)
+{
+ struct tw_cli_ctlr_context *ctlr = req->ctlr;
+ union tw_cl_command_7k *cmd =
+ &(req->cmd_pkt->command.cmd_pkt_7k);
+ TW_INT32 error;
+
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ /*
+ * If the request was never submitted to the controller, the function
+ * that sets req->error is responsible for calling tw_cl_create_event.
+ */
+ if (! req->error_code)
+ if (cmd->param.status) {
+ tw_cli_create_ctlr_event(ctlr,
+ TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
+ &(req->cmd_pkt->cmd_hdr));
+ tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+ 0x1204, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "get/set_param failed",
+ "status = %d", cmd->param.status);
+ }
+
+ ctlr->state &= ~TW_CLI_CTLR_STATE_INTERNAL_REQ_BUSY;
+ tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+
+ if ((ctlr->state & TW_CLI_CTLR_STATE_GET_MORE_AENS) &&
+ (!(ctlr->state & TW_CLI_CTLR_STATE_RESET_IN_PROGRESS))) {
+ ctlr->state &= ~TW_CLI_CTLR_STATE_GET_MORE_AENS;
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+ "Fetching more AEN's");
+ if ((error = tw_cli_get_aen(ctlr)))
+ tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+ 0x1205, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "Failed to fetch all AEN's from param_callback",
+ "error = %d", error);
+ }
+}
+
+
+
+/*
+ * Function name: tw_cli_aen_callback
+ * Description: Callback for requests to fetch AEN's.
+ *
+ * Input: req -- ptr to completed request pkt
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_aen_callback(struct tw_cli_req_context *req)
+{
+ struct tw_cli_ctlr_context *ctlr = req->ctlr;
+ struct tw_cl_command_header *cmd_hdr;
+ struct tw_cl_command_9k *cmd =
+ &(req->cmd_pkt->command.cmd_pkt_9k);
+ TW_UINT16 aen_code = TWA_AEN_QUEUE_EMPTY;
+ TW_INT32 error;
+
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+ "req_id = 0x%x, req error = %d, status = 0x%x",
+ GET_REQ_ID(cmd->lun_l4__req_id), req->error_code, cmd->status);
+
+ /*
+ * If the request was never submitted to the controller, the function
+ * that sets error is responsible for calling tw_cl_create_event.
+ */
+ if (!(error = req->error_code))
+ if ((error = cmd->status)) {
+ cmd_hdr = (struct tw_cl_command_header *)
+ (&(req->cmd_pkt->cmd_hdr));
+ tw_cli_create_ctlr_event(ctlr,
+ TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
+ cmd_hdr);
+ tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+ 0x1206, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "Request Sense failed",
+ "opcode = 0x%x, status = %d",
+ GET_OPCODE(cmd->res__opcode), cmd->status);
+ }
+
+ if (error) {
+ ctlr->state &= ~TW_CLI_CTLR_STATE_INTERNAL_REQ_BUSY;
+ tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+ return;
+ }
+
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+ "Request Sense command succeeded");
+
+ aen_code = tw_cli_manage_aen(ctlr, req);
+
+ if (aen_code != TWA_AEN_SYNC_TIME_WITH_HOST) {
+ ctlr->state &= ~TW_CLI_CTLR_STATE_INTERNAL_REQ_BUSY;
+ tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+ if (aen_code != TWA_AEN_QUEUE_EMPTY)
+ if ((error = tw_cli_get_aen(ctlr)))
+ tw_cl_create_event(ctlr->ctlr_handle,
+ TW_CL_FALSE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+ 0x1207, 0x1,
+ TW_CL_SEVERITY_ERROR_STRING,
+ "Failed to fetch all AEN's",
+ "error = %d", error);
+ }
+}
+
+
+
+/*
+ * Function name: tw_cli_manage_aen
+ * Description: Handles AEN's.
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * req -- ptr to CL internal request context
+ * Output: None
+ * Return value: None
+ */
+TW_UINT16
+tw_cli_manage_aen(struct tw_cli_ctlr_context *ctlr,
+ struct tw_cli_req_context *req)
+{
+ struct tw_cl_command_header *cmd_hdr;
+ TW_UINT16 aen_code;
+ TW_TIME local_time;
+ TW_TIME sync_time;
+ TW_UINT32 error;
+
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ cmd_hdr = (struct tw_cl_command_header *)(req->data);
+ aen_code = cmd_hdr->status_block.error;
+
+ switch (aen_code) {
+ case TWA_AEN_SYNC_TIME_WITH_HOST:
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+ "Received AEN_SYNC_TIME");
+ /*
+ * Free the internal req pkt right here, since
+ * tw_cli_set_param will need it.
+ */
+ ctlr->state &= ~TW_CLI_CTLR_STATE_INTERNAL_REQ_BUSY;
+ tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
+
+ /*
+ * We will use a callback in tw_cli_set_param only when
+ * interrupts are enabled and we can expect our callback
+ * to get called. Setting the TW_CLI_CTLR_STATE_GET_MORE_AENS
+ * flag will make the callback continue to try to retrieve
+ * more AEN's.
+ */
+ if (ctlr->state & TW_CLI_CTLR_STATE_INTR_ENABLED)
+ ctlr->state |= TW_CLI_CTLR_STATE_GET_MORE_AENS;
+ /* Calculate time (in seconds) since last Sunday 12.00 AM. */
+ local_time = tw_osl_get_local_time();
+ sync_time = (local_time - (3 * 86400)) % 604800;
+ if ((error = tw_cli_set_param(ctlr, TWA_PARAM_TIME_TABLE,
+ TWA_PARAM_TIME_SCHED_TIME, 4,
+ &sync_time,
+ (ctlr->state & TW_CLI_CTLR_STATE_INTR_ENABLED)
+ ? tw_cli_param_callback : TW_CL_NULL)))
+ tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
+ TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
+ 0x1208, 0x1, TW_CL_SEVERITY_ERROR_STRING,
+ "Unable to sync time with ctlr",
+ "error = %d", error);
+
+ break;
+
+
+ case TWA_AEN_QUEUE_EMPTY:
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+ "AEN queue empty");
+ break;
+
+
+ default:
+ /* Queue the event. */
+
+ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
+ "Queueing AEN");
+ tw_cli_create_ctlr_event(ctlr,
+ TW_CL_MESSAGE_SOURCE_CONTROLLER_EVENT,
+ cmd_hdr);
+ break;
+ } /* switch */
+ return(aen_code);
+}
+
+
+
+/*
+ * Function name: tw_cli_enable_interrupts
+ * Description: Enables interrupts on the controller
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_enable_interrupts(struct tw_cli_ctlr_context *ctlr)
+{
+ tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ ctlr->state |= TW_CLI_CTLR_STATE_INTR_ENABLED;
+ TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+ TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT |
+ TWA_CONTROL_UNMASK_RESPONSE_INTERRUPT |
+ TWA_CONTROL_ENABLE_INTERRUPTS);
+}
+
+
+
+/*
+ * Function name: twa_setup
+ * Description: Disables interrupts on the controller
+ *
+ * Input: ctlr -- ptr to CL internal ctlr context
+ * Output: None
+ * Return value: None
+ */
+TW_VOID
+tw_cli_disable_interrupts(struct tw_cli_ctlr_context *ctlr)
+{
+ tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
+
+ TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
+ TWA_CONTROL_DISABLE_INTERRUPTS);
+ ctlr->state &= ~TW_CLI_CTLR_STATE_INTR_ENABLED;
+}
+