root/sys/dev/raid/twa/tw_cl_misc.c
/*
 * Copyright (c) 2004-07 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: head/sys/dev/twa/tw_cl_misc.c 242827 2012-11-09 15:29:52Z rdivacky $
 */

/*
 * AMCC'S 3ware driver for 9000 series storage controllers.
 *
 * Author: Vinod Kashyap
 * Modifications by: Adam Radford
 * Modifications by: Manjunath Ranganathaiah
 */


/*
 * Common Layer miscellaneous 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"



/* AEN severity table. */
TW_INT8 *tw_cli_severity_string_table[] = {
        "None",
        TW_CL_SEVERITY_ERROR_STRING,
        TW_CL_SEVERITY_WARNING_STRING,
        TW_CL_SEVERITY_INFO_STRING,
        TW_CL_SEVERITY_DEBUG_STRING,
        ""
};



/*
 * Function name:       tw_cli_drain_complete_queue
 * Description:         This function gets called during a controller reset.
 *                      It errors back to the OS Layer, all those requests that
 *                      are in the complete queue, at the time of the reset.
 *                      Any CL internal requests will be simply freed.
 *
 * Input:               ctlr    -- ptr to CL internal ctlr context
 * Output:              None
 * Return value:        None
 */
TW_VOID
tw_cli_drain_complete_queue(struct tw_cli_ctlr_context *ctlr)
{
        struct tw_cli_req_context       *req;
        struct tw_cl_req_packet         *req_pkt;

        tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");

        /* Walk the busy queue. */
        while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_COMPLETE_Q)) !=
                TW_CL_NULL) {
                if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) {
                        /*
                         * It's an internal request.  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 = TW_CL_ERR_REQ_BUS_RESET;
                        if (req->tw_cli_callback)
                                req->tw_cli_callback(req);
                } else if (req->flags & TW_CLI_REQ_FLAGS_PASSTHRU) {
                        /* It's a passthru request.  Complete it. */
                        if ((req_pkt = req->orig_req) != TW_CL_NULL) {
                                req_pkt->status = TW_CL_ERR_REQ_BUS_RESET;

                                if (req_pkt->tw_osl_callback)
                                        req_pkt->tw_osl_callback(req->req_handle);
                        }
                        tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
                } else {
                        /* It's an external (SCSI) request.  Add it to the reset queue. */
                        tw_osl_untimeout(req->req_handle);
                        tw_cli_req_q_insert_tail(req, TW_CLI_RESET_Q);
                }
        } /* End of while loop */
}



/*
 * Function name:       tw_cli_drain_busy_queue
 * Description:         This function gets called during a controller reset.
 *                      It errors back to the OS Layer, all those requests that
 *                      were pending with the firmware, at the time of the
 *                      reset.
 *
 * Input:               ctlr    -- ptr to CL internal ctlr context
 * Output:              None
 * Return value:        None
 */
TW_VOID
tw_cli_drain_busy_queue(struct tw_cli_ctlr_context *ctlr)
{
        struct tw_cli_req_context       *req;
        struct tw_cl_req_packet         *req_pkt;

        tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");

        /* Walk the busy queue. */
        while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_BUSY_Q)) !=
                TW_CL_NULL) {
                if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) {
                        /*
                         * It's an internal request.  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 = TW_CL_ERR_REQ_BUS_RESET;
                        if (req->tw_cli_callback)
                                req->tw_cli_callback(req);
                } else if (req->flags & TW_CLI_REQ_FLAGS_PASSTHRU) {
                        /* It's a passthru request.  Complete it. */
                        if ((req_pkt = req->orig_req) != TW_CL_NULL) {
                                req_pkt->status = TW_CL_ERR_REQ_BUS_RESET;

                                if (req_pkt->tw_osl_callback)
                                        req_pkt->tw_osl_callback(req->req_handle);
                        }
                        tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
                } else {
                        /* It's an external (SCSI) request.  Add it to the reset queue. */
                        tw_osl_untimeout(req->req_handle);
                        tw_cli_req_q_insert_tail(req, TW_CLI_RESET_Q);
                }
        } /* End of while loop */
}



/*
 * Function name:       tw_cli_drain_pending_queue
 * Description:         This function gets called during a controller reset.
 *                      It errors back to the OS Layer, all those requests that
 *                      were in the pending queue, at the time of the reset.
 *
 * Input:               ctlr    -- ptr to CL internal ctlr context
 * Output:              None
 * Return value:        None
 */

TW_VOID
tw_cli_drain_pending_queue(struct tw_cli_ctlr_context *ctlr)
{
        struct tw_cli_req_context       *req;
        struct tw_cl_req_packet         *req_pkt;

        tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");

        /*
         * Pull requests off the pending queue, and complete them.
         */
        while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_PENDING_Q)) !=
                TW_CL_NULL) {
                if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) {
                        /*
                         * It's an internal request.  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 = TW_CL_ERR_REQ_BUS_RESET;
                        if (req->tw_cli_callback)
                                req->tw_cli_callback(req);
                } else if (req->flags & TW_CLI_REQ_FLAGS_PASSTHRU) {
                        /* It's a passthru request.  Complete it. */
                        if ((req_pkt = req->orig_req) != TW_CL_NULL) {
                                req_pkt->status = TW_CL_ERR_REQ_BUS_RESET;

                                if (req_pkt->tw_osl_callback)
                                        req_pkt->tw_osl_callback(req->req_handle);
                        }
                        tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
                } else {
                        /* It's an external (SCSI) request.  Add it to the reset queue. */
                        tw_osl_untimeout(req->req_handle);
                        tw_cli_req_q_insert_tail(req, TW_CLI_RESET_Q);
                }
        } /* End of while loop */
}



/*
 * Function name:       tw_cli_drain_response_queue
 * Description:         Drain the controller response queue.
 *
 * Input:               ctlr    -- ptr to per ctlr structure
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_drain_response_queue(struct tw_cli_ctlr_context *ctlr)
{
        TW_UINT32       resp;
        TW_UINT32       status_reg;

        tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");

        for (;;) {
                status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle);

                if (status_reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY)
                        return(TW_OSL_ESUCCESS); /* no more response queue entries */

                resp = TW_CLI_READ_RESPONSE_QUEUE(ctlr->ctlr_handle);
        }
}



/*
 * Function name:       tw_cli_find_response
 * Description:         Find a particular response in the ctlr response queue.
 *
 * Input:               ctlr    -- ptr to per ctlr structure
 *                      req_id  -- request id of the response to look for
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_find_response(struct tw_cli_ctlr_context *ctlr, TW_INT32 req_id)
{
        TW_UINT32       resp;
        TW_INT32        resp_id;
        TW_UINT32       status_reg;

        tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");

        for (;;) {
                status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle);

                if (status_reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY)
                        return(TW_OSL_ENOTTY); /* no more response queue entries */

                if (ctlr->device_id == TW_CL_DEVICE_ID_9K) {
                        resp = TW_CLI_READ_RESPONSE_QUEUE(ctlr->ctlr_handle);
                        resp_id = GET_RESP_ID(resp);
                } else {
                        resp = TW_CLI_READ_LARGE_RESPONSE_QUEUE(
                                ctlr->ctlr_handle);
                        resp_id = GET_LARGE_RESP_ID(resp);
                }
                if (resp_id == req_id)
                        return(TW_OSL_ESUCCESS); /* found the req_id */
        }
}



/*
 * Function name:       tw_cli_drain_aen_queue
 * Description:         Fetches all un-retrieved AEN's posted by fw.
 *
 * Input:               ctlr    -- ptr to CL internal ctlr context
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_drain_aen_queue(struct tw_cli_ctlr_context *ctlr)
{
        struct tw_cli_req_context       *req;
        TW_TIME                         end_time;
        TW_UINT16                       aen_code;
        TW_INT32                        error;

        tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");

        for (;;) {
                if ((req = tw_cli_get_request(ctlr
                        )) == TW_CL_NULL) {
                        error = TW_OSL_EBUSY;
                        break;
                }

                req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
                req->tw_cli_callback = TW_CL_NULL;
                if ((error = tw_cli_send_scsi_cmd(req,
                                0x03 /* REQUEST_SENSE */))) {
                        tw_cli_dbg_printf(1, ctlr->ctlr_handle,
                                tw_osl_cur_func(),
                                "Cannot send command to fetch aen");
                        break;
                }

                end_time = tw_osl_get_local_time() +
                        TW_CLI_REQUEST_TIMEOUT_PERIOD;
                do {
                        if ((error = req->error_code))
                                /*
                                 * This will take care of completion due to
                                 * a reset, or a failure in
                                 * tw_cli_submit_pending_queue.
                                 */
                                goto out;

                        tw_cli_process_resp_intr(req->ctlr);

                        if ((req->state != TW_CLI_REQ_STATE_BUSY) &&
                                (req->state != TW_CLI_REQ_STATE_PENDING))
                                break;
                } while (tw_osl_get_local_time() <= end_time);

                if (req->state != TW_CLI_REQ_STATE_COMPLETE) {
                        error = TW_OSL_ETIMEDOUT;
                        break;
                }

                if ((error = req->cmd_pkt->command.cmd_pkt_9k.status)) {
#if       0
                        struct tw_cl_command_header     *cmd_hdr;
                        cmd_hdr = &req->cmd_pkt->cmd_hdr;
                        tw_cli_create_ctlr_event(ctlr,
                                TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
                                cmd_hdr);
#endif // 0
                        break;
                }

                aen_code = tw_cli_manage_aen(ctlr, req);
                if (aen_code == TWA_AEN_QUEUE_EMPTY)
                        break;
                if (aen_code == TWA_AEN_SYNC_TIME_WITH_HOST)
                        continue;

                ctlr->internal_req_busy = TW_CL_FALSE;
                tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
        }

out:
        if (req) {
                if (req->data)
                        ctlr->internal_req_busy = TW_CL_FALSE;
                tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
        }
        return(error);
}



/*
 * Function name:       tw_cli_find_aen
 * Description:         Reports whether a given AEN ever occurred.
 *
 * Input:               ctlr    -- ptr to CL internal ctlr context
 *                      aen_code-- AEN to look for
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_find_aen(struct tw_cli_ctlr_context *ctlr, TW_UINT16 aen_code)
{
        TW_UINT32       last_index;
        TW_INT32        i;

        tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");

        if (ctlr->aen_q_wrapped)
                last_index = ctlr->aen_head;
        else
                last_index = 0;

        i = ctlr->aen_head;
        do {
                i = (i + ctlr->max_aens_supported - 1) %
                        ctlr->max_aens_supported;
                if (ctlr->aen_queue[i].aen_code == aen_code)
                        return(TW_OSL_ESUCCESS);
        } while (i != last_index);

        return(TW_OSL_EGENFAILURE);
}



/*
 * Function name:       tw_cli_poll_status
 * Description:         Poll for a given status to show up in the firmware
 *                      status register.
 *
 * Input:               ctlr    -- ptr to CL internal ctlr context
 *                      status  -- status to look for
 *                      timeout -- max # of seconds to wait before giving up
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_poll_status(struct tw_cli_ctlr_context *ctlr, TW_UINT32 status,
        TW_UINT32 timeout)
{
        TW_TIME         end_time;
        TW_UINT32       status_reg;

        tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");

        end_time = tw_osl_get_local_time() + timeout;
        do {
                status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle);
                if ((status_reg & status) == status)
                        /* got the required bit(s) */
                        return(TW_OSL_ESUCCESS);

                tw_osl_delay(1000);
        } while (tw_osl_get_local_time() <= end_time);

        return(TW_OSL_ETIMEDOUT);
}



/*
 * Function name:       tw_cl_create_event
 * Description:         Creates and queues ctlr/CL/OSL AEN's to be
 *                      supplied to user-space tools on request.
 *                      Also notifies OS Layer.
 * Input:               ctlr    -- ptr to CL internal ctlr context
 *                      queue_event-- TW_CL_TRUE --> queue event;
 *                                    TW_CL_FALSE--> don't queue event
 *                                                      (simply notify OSL)
 *                      event_src  -- source of event
 *                      event_code -- AEN/error code
 *                      severity -- severity of event
 *                      severity_str--Text description of severity
 *                      event_desc -- standard string related to the event/error
 *                      event_specific_desc -- format string for additional
 *                                              info about the event
 *                      ... -- additional arguments conforming to the format
 *                              specified by event_specific_desc
 * Output:              None
 * Return value:        None
 */
TW_VOID
tw_cl_create_event(struct tw_cl_ctlr_handle *ctlr_handle,
        TW_UINT8 queue_event, TW_UINT8 event_src, TW_UINT16 event_code,
        TW_UINT8 severity, TW_UINT8 *severity_str, TW_UINT8 *event_desc,
        TW_UINT8 *event_specific_desc, ...)
{
        struct tw_cli_ctlr_context      *ctlr = ctlr_handle->cl_ctlr_ctxt;
        struct tw_cl_event_packet       event_pkt;
        struct tw_cl_event_packet       *event;
        TW_UINT32                       aen_head;
        __va_list                       ap;

        tw_cli_dbg_printf(8, ctlr_handle, tw_osl_cur_func(), "entered");

        if ((ctlr) && (queue_event)) {
                /* Protect access to ctlr->aen_head. */
                tw_osl_get_lock(ctlr_handle, ctlr->gen_lock);

                aen_head = ctlr->aen_head;
                ctlr->aen_head = (aen_head + 1) % ctlr->max_aens_supported;

                /* Queue the event. */
                event = &(ctlr->aen_queue[aen_head]);
                tw_osl_memzero(event->parameter_data,
                        sizeof(event->parameter_data));

                if (event->retrieved == TW_CL_AEN_NOT_RETRIEVED)
                        ctlr->aen_q_overflow = TW_CL_TRUE;
                event->sequence_id = ++(ctlr->aen_cur_seq_id);
                if ((aen_head + 1) == ctlr->max_aens_supported) {
                        tw_cli_dbg_printf(4, ctlr->ctlr_handle,
                                tw_osl_cur_func(), "AEN queue wrapped");
                        ctlr->aen_q_wrapped = TW_CL_TRUE;
                }

                /* Free access to ctlr->aen_head. */
                tw_osl_free_lock(ctlr_handle, ctlr->gen_lock);
        } else {
                event = &event_pkt;
                tw_osl_memzero(event, sizeof(struct tw_cl_event_packet));
        }

        event->event_src = event_src;
        event->time_stamp_sec = (TW_UINT32)tw_osl_get_local_time();
        event->aen_code = event_code;
        event->severity = severity;
        tw_osl_strcpy(event->severity_str, severity_str);
        event->retrieved = TW_CL_AEN_NOT_RETRIEVED;

        __va_start(ap, event_specific_desc);
        tw_osl_vsprintf(event->parameter_data, event_specific_desc, ap);
        __va_end(ap);

        event->parameter_len =
                (TW_UINT8)(tw_osl_strlen(event->parameter_data));
        tw_osl_strcpy(event->parameter_data + event->parameter_len + 1,
                event_desc);
        event->parameter_len += (1 + tw_osl_strlen(event_desc));

        tw_cli_dbg_printf(4, ctlr_handle, tw_osl_cur_func(),
                "event = %x %x %x %x %x %x %x\n %s",
                event->sequence_id,
                event->time_stamp_sec,
                event->aen_code,
                event->severity,
                event->retrieved,
                event->repeat_count,
                event->parameter_len,
                event->parameter_data);

        tw_osl_notify_event(ctlr_handle, event);
}



/*
 * Function name:       tw_cli_get_request
 * Description:         Gets a request pkt from the free queue.
 *
 * Input:               ctlr    -- ptr to CL internal ctlr context
 *                      req_pkt -- ptr to OSL built req_pkt, if there's one
 * Output:              None
 * Return value:        ptr to request pkt      -- success
 *                      TW_CL_NULL              -- failure
 */
struct tw_cli_req_context *
tw_cli_get_request(struct tw_cli_ctlr_context *ctlr
        )
{
        struct tw_cli_req_context       *req;

        tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");

        {
                /* Get a free request packet. */
                req = tw_cli_req_q_remove_head(ctlr, TW_CLI_FREE_Q);
        }

        /* Initialize some fields to their defaults. */
        if (req) {
                req->req_handle = TW_CL_NULL;
                req->data = TW_CL_NULL;
                req->length = 0;
                req->data_phys = 0;
                req->state = TW_CLI_REQ_STATE_INIT; /* req being initialized */
                req->flags = 0;
                req->error_code = 0;
                req->orig_req = TW_CL_NULL;
                req->tw_cli_callback = TW_CL_NULL;

                /*
                 * Look at the status field in the command packet to see how
                 * it completed the last time it was used, and zero out only
                 * the portions that might have changed.  Note that we don't
                 * care to zero out the sglist.
                 */
                if (req->cmd_pkt->command.cmd_pkt_9k.status)
                        tw_osl_memzero(req->cmd_pkt,
                                sizeof(struct tw_cl_command_header) +
                                28 /* max bytes before sglist */);
                else
                        tw_osl_memzero(&(req->cmd_pkt->command),
                                28 /* max bytes before sglist */);

        }
        return(req);
}



/*
 * Function name:       tw_cli_dbg_printf
 * Description:         Calls OSL print function if dbg_level is appropriate
 *
 * Input:               dbg_level -- Determines whether or not to print
 *                      ctlr_handle -- controller handle
 *                      cur_func -- text name of calling function
 *                      fmt -- format string for the arguments to follow
 *                      ... -- variable number of arguments, to be printed
 *                              based on the fmt string
 * Output:              None
 * Return value:        None
 */
TW_VOID
tw_cli_dbg_printf(TW_UINT8 dbg_level,
        struct tw_cl_ctlr_handle *ctlr_handle, const TW_INT8 *cur_func,
        TW_INT8 *fmt, ...)
{
#ifdef TW_OSL_DEBUG
        TW_INT8 print_str[256];
        __va_list       ap;

        tw_osl_memzero(print_str, 256);
        if (dbg_level <= TW_OSL_DEBUG_LEVEL_FOR_CL) {
                tw_osl_sprintf(print_str, "%s: ", cur_func);

                __va_start(ap, fmt);
                tw_osl_vsprintf(print_str + tw_osl_strlen(print_str), fmt, ap);
                __va_end(ap);

                tw_osl_strcpy(print_str + tw_osl_strlen(print_str), "\n");
                tw_osl_dbg_printf(ctlr_handle, "%s", print_str);
        }
#endif /* TW_OSL_DEBUG */
}



/*
 * Function name:       tw_cli_notify_ctlr_info
 * Description:         Notify OSL of controller info (fw/BIOS versions, etc.).
 *
 * Input:               ctlr    -- ptr to CL internal ctlr context
 * Output:              None
 * Return value:        None
 */
TW_VOID
tw_cli_notify_ctlr_info(struct tw_cli_ctlr_context *ctlr)
{
        TW_INT8         fw_ver[16];
        TW_INT8         bios_ver[16];
        TW_INT8         ctlr_model[16];
        TW_INT32        error[3];
        TW_UINT8        num_ports = 0;

        tw_cli_dbg_printf(5, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");

        /* Get the port count. */
        error[0] = tw_cli_get_param(ctlr, TWA_PARAM_CONTROLLER_TABLE,
                        TWA_PARAM_CONTROLLER_PORT_COUNT, &num_ports,
                        1, TW_CL_NULL);

        /* Get the firmware and BIOS versions. */
        error[0] = tw_cli_get_param(ctlr, TWA_PARAM_VERSION_TABLE,
                        TWA_PARAM_VERSION_FW, fw_ver, 16, TW_CL_NULL);
        error[1] = tw_cli_get_param(ctlr, TWA_PARAM_VERSION_TABLE,
                        TWA_PARAM_VERSION_BIOS, bios_ver, 16, TW_CL_NULL);
        error[2] = tw_cli_get_param(ctlr, TWA_PARAM_VERSION_TABLE,
                        TWA_PARAM_CTLR_MODEL, ctlr_model, 16, TW_CL_NULL);

        tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
                TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
                0x1300, 0x3, TW_CL_SEVERITY_INFO_STRING,
                "Controller details",
                "Model %.16s, %d ports, Firmware %.16s, BIOS %.16s",
                error[2]?(TW_INT8 *)TW_CL_NULL:ctlr_model,
                num_ports,
                error[0]?(TW_INT8 *)TW_CL_NULL:fw_ver,
                error[1]?(TW_INT8 *)TW_CL_NULL:bios_ver);
}



/*
 * Function name:       tw_cli_check_ctlr_state
 * Description:         Makes sure that the fw status register reports a
 *                      proper status.
 *
 * Input:               ctlr    -- ptr to CL internal ctlr context
 *                      status_reg-- value in the status register
 * Output:              None
 * Return value:        0       -- no errors
 *                      non-zero-- errors
 */
TW_INT32
tw_cli_check_ctlr_state(struct tw_cli_ctlr_context *ctlr, TW_UINT32 status_reg)
{
        struct tw_cl_ctlr_handle        *ctlr_handle = ctlr->ctlr_handle;
        TW_INT32                        error = TW_OSL_ESUCCESS;

        tw_cli_dbg_printf(8, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");

        /* Check if the 'micro-controller ready' bit is not set. */
        if (!(status_reg & TWA_STATUS_MICROCONTROLLER_READY)) {
                TW_INT8 desc[200];

                tw_osl_memzero(desc, 200);
                if (!(ctlr->reset_phase1_in_progress)) {
                        tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                                TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                                0x1301, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                                "Missing expected status bit(s)",
                                "status reg = 0x%x; Missing bits: %s",
                                status_reg,
                                tw_cli_describe_bits(
                                        TWA_STATUS_MICROCONTROLLER_READY,
                                        desc));
                        error = TW_OSL_EGENFAILURE;
                }
        }

        /* Check if any error bits are set. */
        if ((status_reg & TWA_STATUS_UNEXPECTED_BITS) != 0) {
                TW_INT8 desc[200];

                tw_osl_memzero(desc, 200);

                /* Skip queue error msgs during 9650SE/9690SA reset */
                if (((ctlr->device_id != TW_CL_DEVICE_ID_9K_E) &&
                     (ctlr->device_id != TW_CL_DEVICE_ID_9K_SA)) ||
                    (!(ctlr->reset_in_progress)) ||
                    ((status_reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT) == 0))
                {
                        tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                            TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                            0x1302, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                            "Unexpected status bit(s)",
                            "status reg = 0x%x Unexpected bits: %s",
                            status_reg & TWA_STATUS_UNEXPECTED_BITS,
                            tw_cli_describe_bits(status_reg &
                                    TWA_STATUS_UNEXPECTED_BITS, desc));
                }

                if (status_reg & TWA_STATUS_PCI_PARITY_ERROR_INTERRUPT) {
                        tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                                TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                                0x1303, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                                "PCI parity error: clearing... "
                                "Re-seat/move/replace card",
                                "status reg = 0x%x %s",
                                status_reg,
                                tw_cli_describe_bits(status_reg, desc));
                        TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
                                TWA_CONTROL_CLEAR_PARITY_ERROR);

#ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE
                        tw_osl_write_pci_config(ctlr->ctlr_handle,
                                TW_CLI_PCI_CONFIG_STATUS_OFFSET,
                                TWA_PCI_CONFIG_CLEAR_PARITY_ERROR, 2);
#endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */

                }

                if (status_reg & TWA_STATUS_PCI_ABORT_INTERRUPT) {
                        tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                                TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                                0x1304, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                                "PCI abort: clearing... ",
                                "status reg = 0x%x %s",
                                status_reg,
                                tw_cli_describe_bits(status_reg, desc));
                        TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
                                TWA_CONTROL_CLEAR_PCI_ABORT);

#ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE
                        tw_osl_write_pci_config(ctlr->ctlr_handle,
                                TW_CLI_PCI_CONFIG_STATUS_OFFSET,
                                TWA_PCI_CONFIG_CLEAR_PCI_ABORT, 2);
#endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */

                }

                if (status_reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT) {
                        /* Skip queue error msgs during 9650SE/9690SA reset */
                        if (((ctlr->device_id != TW_CL_DEVICE_ID_9K_E) &&
                             (ctlr->device_id != TW_CL_DEVICE_ID_9K_SA)) ||
                            (!(ctlr->reset_in_progress)))
                                tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                                                   TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                                                   0x1305, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                                                   "Controller queue error: clearing... ",
                                                   "status reg = 0x%x %s",
                                                   status_reg,
                                                   tw_cli_describe_bits(status_reg, desc));
                        TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
                                TWA_CONTROL_CLEAR_QUEUE_ERROR);
                }
        }
        return(error);
}



/*
 * Function name:       tw_cli_describe_bits
 * Description:         Given the value of the status register, returns a
 *                      string describing the meaning of each set bit.
 *
 * Input:               reg -- status register value
 * Output:              Pointer to a string describing each set bit
 * Return value:        Pointer to the string describing each set bit
 */
TW_INT8 *
tw_cli_describe_bits(TW_UINT32 reg, TW_INT8 *str)
{
        tw_osl_strcpy(str, "[");

        if (reg & TWA_STATUS_COMMAND_QUEUE_EMPTY)
                tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_Q_EMPTY,");
        if (reg & TWA_STATUS_MICROCONTROLLER_READY)
                tw_osl_strcpy(&str[tw_osl_strlen(str)], "MC_RDY,");
        if (reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY)
                tw_osl_strcpy(&str[tw_osl_strlen(str)], "RESP_Q_EMPTY,");
        if (reg & TWA_STATUS_COMMAND_QUEUE_FULL)
                tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_Q_FULL,");
        if (reg & TWA_STATUS_RESPONSE_INTERRUPT)
                tw_osl_strcpy(&str[tw_osl_strlen(str)], "RESP_INTR,");
        if (reg & TWA_STATUS_COMMAND_INTERRUPT)
                tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_INTR,");
        if (reg & TWA_STATUS_ATTENTION_INTERRUPT)
                tw_osl_strcpy(&str[tw_osl_strlen(str)], "ATTN_INTR,");
        if (reg & TWA_STATUS_HOST_INTERRUPT)
                tw_osl_strcpy(&str[tw_osl_strlen(str)], "HOST_INTR,");
        if (reg & TWA_STATUS_PCI_ABORT_INTERRUPT)
                tw_osl_strcpy(&str[tw_osl_strlen(str)], "PCI_ABRT,");
        if (reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT)
                tw_osl_strcpy(&str[tw_osl_strlen(str)], "Q_ERR,");
        if (reg & TWA_STATUS_PCI_PARITY_ERROR_INTERRUPT)
                tw_osl_strcpy(&str[tw_osl_strlen(str)], "PCI_PERR");

        tw_osl_strcpy(&str[tw_osl_strlen(str)], "]");
        return(str);
}



#ifdef TW_OSL_DEBUG

/*
 * Function name:       tw_cl_print_ctlr_stats
 * Description:         Prints the current status of the controller.
 *
 * Input:               ctlr_handle-- controller handle
 * Output:              None
 * Return value:        None
 */
TW_VOID
tw_cl_print_ctlr_stats(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_INT8                         desc[200];

        tw_cli_dbg_printf(7, ctlr->ctlr_handle, "", "entered");

        /* Print current controller details. */
        tw_cli_dbg_printf(0, ctlr_handle, "", "cl_ctlr_ctxt = %p", ctlr);

        tw_osl_memzero(desc, 200);
        status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle);
        tw_cli_dbg_printf(0, ctlr_handle, "", "status reg = 0x%x %s",
                status_reg, tw_cli_describe_bits(status_reg, desc));

        tw_cli_dbg_printf(0, ctlr_handle, "", "CLq type  current  max");
        tw_cli_dbg_printf(0, ctlr_handle, "", "free      %04d     %04d",
                ctlr->q_stats[TW_CLI_FREE_Q].cur_len,
                ctlr->q_stats[TW_CLI_FREE_Q].max_len);
        tw_cli_dbg_printf(0, ctlr_handle, "", "busy      %04d     %04d",
                ctlr->q_stats[TW_CLI_BUSY_Q].cur_len,
                ctlr->q_stats[TW_CLI_BUSY_Q].max_len);
        tw_cli_dbg_printf(0, ctlr_handle, "", "pending   %04d     %04d",
                ctlr->q_stats[TW_CLI_PENDING_Q].cur_len,
                ctlr->q_stats[TW_CLI_PENDING_Q].max_len);
        tw_cli_dbg_printf(0, ctlr_handle, "", "complete  %04d     %04d",
                ctlr->q_stats[TW_CLI_COMPLETE_Q].cur_len,
                ctlr->q_stats[TW_CLI_COMPLETE_Q].max_len);
        tw_cli_dbg_printf(0, ctlr_handle, "", "AEN queue head %d  tail %d",
                        ctlr->aen_head, ctlr->aen_tail);
}



/*
 * Function name:       tw_cl_reset_stats
 * Description:         Resets CL maintained statistics for the controller.
 *
 * Input:               ctlr_handle-- controller handle
 * Output:              None
 * Return value:        None
 */
TW_VOID
tw_cl_reset_stats(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(7, ctlr_handle, tw_osl_cur_func(), "entered");
        ctlr->q_stats[TW_CLI_FREE_Q].max_len = 0;
        ctlr->q_stats[TW_CLI_BUSY_Q].max_len = 0;
        ctlr->q_stats[TW_CLI_PENDING_Q].max_len = 0;
        ctlr->q_stats[TW_CLI_COMPLETE_Q].max_len = 0;
}



/*
 * Function name:       tw_cli_print_req_info
 * Description:         Prints CL internal details of a given request.
 *
 * Input:               req     -- ptr to CL internal request context
 * Output:              None
 * Return value:        None
 */
TW_VOID
tw_cl_print_req_info(struct tw_cl_req_handle *req_handle)
{
        struct tw_cli_req_context       *req = req_handle->cl_req_ctxt;
        struct tw_cli_ctlr_context      *ctlr = req->ctlr;
        struct tw_cl_ctlr_handle        *ctlr_handle = ctlr->ctlr_handle;
        struct tw_cl_command_packet     *cmd_pkt = req->cmd_pkt;
        struct tw_cl_command_9k         *cmd9k;
        union tw_cl_command_7k          *cmd7k;
        TW_UINT8                        *cdb;
        TW_VOID                         *sgl;
        TW_UINT32                       sgl_entries;
        TW_UINT32                       i;

        tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
                "CL details for request:");
        tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
                "req_handle = %p, ctlr = %p,\n"
                "cmd_pkt = %p, cmd_pkt_phys = 0x%llx,\n"
                "data = %p, length = 0x%x, data_phys = 0x%llx,\n"
                "state = 0x%x, flags = 0x%x, error = 0x%x,\n"
                "orig_req = %p, callback = %p, req_id = 0x%x,\n"
                "next_req = %p, prev_req = %p",
                req_handle, ctlr,
                cmd_pkt, req->cmd_pkt_phys,
                req->data, req->length, req->data_phys,
                req->state, req->flags, req->error_code,
                req->orig_req, req->tw_cli_callback, req->request_id,
                req->link.next, req->link.prev);

        if (req->flags & TW_CLI_REQ_FLAGS_9K) {
                cmd9k = &(cmd_pkt->command.cmd_pkt_9k);
                sgl = cmd9k->sg_list;
                sgl_entries = TW_CL_SWAP16(
                        GET_SGL_ENTRIES(cmd9k->lun_h4__sgl_entries));
                tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
                        "9K cmd: opcode = 0x%x, unit = 0x%x, req_id = 0x%x,\n"
                        "status = 0x%x, sgl_offset = 0x%x, sgl_entries = 0x%x",
                        GET_OPCODE(cmd9k->res__opcode),
                        cmd9k->unit,
                        TW_CL_SWAP16(GET_REQ_ID(cmd9k->lun_l4__req_id)),
                        cmd9k->status,
                        cmd9k->sgl_offset,
                        sgl_entries);

                cdb = (TW_UINT8 *)(cmd9k->cdb);
                tw_cli_dbg_printf(0, 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]);
        } else {
                cmd7k = &(cmd_pkt->command.cmd_pkt_7k);
                sgl = cmd7k->param.sgl;
                sgl_entries = (cmd7k->generic.size -
                        GET_SGL_OFF(cmd7k->generic.sgl_off__opcode)) /
                        ((ctlr->flags & TW_CL_64BIT_ADDRESSES) ? 3 : 2);
                tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
                        "7K cmd: opcode = 0x%x, sgl_offset = 0x%x,\n"
                        "size = 0x%x, req_id = 0x%x, unit = 0x%x,\n"
                        "status = 0x%x, flags = 0x%x, count = 0x%x",
                        GET_OPCODE(cmd7k->generic.sgl_off__opcode),
                        GET_SGL_OFF(cmd7k->generic.sgl_off__opcode),
                        cmd7k->generic.size,
                        TW_CL_SWAP16(cmd7k->generic.request_id),
                        GET_UNIT(cmd7k->generic.host_id__unit),
                        cmd7k->generic.status,
                        cmd7k->generic.flags,
                        TW_CL_SWAP16(cmd7k->generic.count));
        }

        tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(), "SG entries:");

        if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
                struct tw_cl_sg_desc64 *sgl64 = (struct tw_cl_sg_desc64 *)sgl;

                for (i = 0; i < sgl_entries; i++) {
                        tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
                                "0x%llx  0x%x",
                                sgl64[i].address, sgl64[i].length);
                }
        } else {
                struct tw_cl_sg_desc32 *sgl32 = (struct tw_cl_sg_desc32 *)sgl;

                for (i = 0; i < sgl_entries; i++) {
                        tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(),
                                "0x%x  0x%x",
                                sgl32[i].address, sgl32[i].length);
                }
        }
}

#endif /* TW_OSL_DEBUG */