root/sys/dev/raid/twa/tw_cl_io.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_io.c 212008 2010-08-30 19:15:04Z delphij $
 */

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


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

#include <bus/cam/cam.h>
#include <bus/cam/cam_ccb.h>
#include <bus/cam/cam_xpt_sim.h>



/*
 * Function name:       tw_cl_start_io
 * Description:         Interface to OS Layer for accepting SCSI requests.
 *
 * Input:               ctlr_handle     -- controller handle
 *                      req_pkt         -- OSL built request packet
 *                      req_handle      -- request handle
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cl_start_io(struct tw_cl_ctlr_handle *ctlr_handle,
        struct tw_cl_req_packet *req_pkt, struct tw_cl_req_handle *req_handle)
{
        struct tw_cli_ctlr_context              *ctlr;
        struct tw_cli_req_context               *req;
        struct tw_cl_command_9k                 *cmd;
        struct tw_cl_scsi_req_packet            *scsi_req;
        TW_INT32                                error = TW_CL_ERR_REQ_SUCCESS;

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

        ctlr = (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);

        /*
         * If working with a firmware version that does not support multiple
         * luns, and this request is directed at a non-zero lun, error it
         * back right away.
         */
        if ((req_pkt->gen_req_pkt.scsi_req.lun) &&
                (ctlr->working_srl < TWA_MULTI_LUN_FW_SRL)) {
                req_pkt->status |= (TW_CL_ERR_REQ_INVALID_LUN |
                        TW_CL_ERR_REQ_SCSI_ERROR);
                req_pkt->tw_osl_callback(req_handle);
                return(TW_CL_ERR_REQ_SUCCESS);
        }

        if ((req = tw_cli_get_request(ctlr
                )) == TW_CL_NULL) {
                tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
                        "Out of request context packets: returning busy");
                return(TW_OSL_EBUSY);
        }

        req_handle->cl_req_ctxt = req;
        req->req_handle = req_handle;
        req->orig_req = req_pkt;
        req->tw_cli_callback = tw_cli_complete_io;

        req->flags |= TW_CLI_REQ_FLAGS_EXTERNAL;
        req->flags |= TW_CLI_REQ_FLAGS_9K;

        scsi_req = &(req_pkt->gen_req_pkt.scsi_req);

        /* Build the cmd pkt. */
        cmd = &(req->cmd_pkt->command.cmd_pkt_9k);

        req->cmd_pkt->cmd_hdr.header_desc.size_header = 128;

        cmd->res__opcode = BUILD_RES__OPCODE(0, TWA_FW_CMD_EXECUTE_SCSI);
        cmd->unit = (TW_UINT8)(scsi_req->unit);
        cmd->lun_l4__req_id = TW_CL_SWAP16(
                BUILD_LUN_L4__REQ_ID(scsi_req->lun, req->request_id));
        cmd->status = 0;
        cmd->sgl_offset = 16; /* offset from end of hdr = max cdb len */
        tw_osl_memcpy(cmd->cdb, scsi_req->cdb, scsi_req->cdb_len);

        if (req_pkt->flags & TW_CL_REQ_CALLBACK_FOR_SGLIST) {
                TW_UINT32       num_sgl_entries;

                req_pkt->tw_osl_sgl_callback(req_handle, cmd->sg_list,
                        &num_sgl_entries);
                cmd->lun_h4__sgl_entries =
                        TW_CL_SWAP16(BUILD_LUN_H4__SGL_ENTRIES(scsi_req->lun,
                                num_sgl_entries));
        } else {
                cmd->lun_h4__sgl_entries =
                        TW_CL_SWAP16(BUILD_LUN_H4__SGL_ENTRIES(scsi_req->lun,
                                scsi_req->sgl_entries));
                tw_cli_fill_sg_list(ctlr, scsi_req->sg_list,
                        cmd->sg_list, scsi_req->sgl_entries);
        }

        if (((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) != TW_CL_NULL) ||
                (ctlr->reset_in_progress)) {
                tw_cli_req_q_insert_tail(req, TW_CLI_PENDING_Q);
                TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
                        TWA_CONTROL_UNMASK_COMMAND_INTERRUPT);
        } else if ((error = tw_cli_submit_cmd(req))) {
                tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
                        "Could not start request. request = %p, error = %d",
                        req, error);
                tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
        }
        return(error);
}



/*
 * Function name:       tw_cli_submit_cmd
 * Description:         Submits a cmd to firmware.
 *
 * Input:               req     -- ptr to CL internal request context
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_submit_cmd(struct tw_cli_req_context *req)
{
        struct tw_cli_ctlr_context      *ctlr = req->ctlr;
        struct tw_cl_ctlr_handle        *ctlr_handle = ctlr->ctlr_handle;
        TW_UINT32                       status_reg;
        TW_INT32                        error = 0;

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

        /* Serialize access to the controller cmd queue. */
        tw_osl_get_lock(ctlr_handle, ctlr->io_lock);

        /* For 9650SE first write low 4 bytes */
        if ((ctlr->device_id == TW_CL_DEVICE_ID_9K_E) ||
            (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA))
                tw_osl_write_reg(ctlr_handle,
                                 TWA_COMMAND_QUEUE_OFFSET_LOW,
                                 (TW_UINT32)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)), 4);

        status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle);
        if (status_reg & TWA_STATUS_COMMAND_QUEUE_FULL) {
                struct tw_cl_req_packet *req_pkt =
                        (struct tw_cl_req_packet *)(req->orig_req);

                tw_cli_dbg_printf(7, ctlr_handle, tw_osl_cur_func(),
                        "Cmd queue full");

                if ((req->flags & TW_CLI_REQ_FLAGS_INTERNAL)
                        || ((req_pkt) &&
                        (req_pkt->flags & TW_CL_REQ_RETRY_ON_BUSY))
                        ) {
                        if (req->state != TW_CLI_REQ_STATE_PENDING) {
                                tw_cli_dbg_printf(2, ctlr_handle,
                                        tw_osl_cur_func(),
                                        "pending internal/ioctl request");
                                req->state = TW_CLI_REQ_STATE_PENDING;
                                tw_cli_req_q_insert_tail(req, TW_CLI_PENDING_Q);
                                /* Unmask command interrupt. */
                                TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
                                        TWA_CONTROL_UNMASK_COMMAND_INTERRUPT);
                        } else
                                error = TW_OSL_EBUSY;
                } else {
                        error = TW_OSL_EBUSY;
                }
        } else {
                tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(),
                        "Submitting command");

                /* Insert command into busy queue */
                req->state = TW_CLI_REQ_STATE_BUSY;
                tw_cli_req_q_insert_tail(req, TW_CLI_BUSY_Q);

                if ((ctlr->device_id == TW_CL_DEVICE_ID_9K_E) ||
                    (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA)) {
                        /* Now write the high 4 bytes */
                        tw_osl_write_reg(ctlr_handle,
                                         TWA_COMMAND_QUEUE_OFFSET_HIGH,
                                         (TW_UINT32)(((TW_UINT64)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)))>>32), 4);
                } else {
                        if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
                                /* First write the low 4 bytes, then the high 4. */
                                tw_osl_write_reg(ctlr_handle,
                                                 TWA_COMMAND_QUEUE_OFFSET_LOW,
                                                 (TW_UINT32)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)), 4);
                                tw_osl_write_reg(ctlr_handle,
                                                 TWA_COMMAND_QUEUE_OFFSET_HIGH,
                                                 (TW_UINT32)(((TW_UINT64)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)))>>32), 4);
                        } else
                                tw_osl_write_reg(ctlr_handle,
                                                 TWA_COMMAND_QUEUE_OFFSET,
                                                 (TW_UINT32)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)), 4);
                }
        }

        tw_osl_free_lock(ctlr_handle, ctlr->io_lock);

        return(error);
}



/*
 * Function name:       tw_cl_fw_passthru
 * Description:         Interface to OS Layer for accepting firmware
 *                      passthru requests.
 * Input:               ctlr_handle     -- controller handle
 *                      req_pkt         -- OSL built request packet
 *                      req_handle      -- request handle
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cl_fw_passthru(struct tw_cl_ctlr_handle *ctlr_handle,
        struct tw_cl_req_packet *req_pkt, struct tw_cl_req_handle *req_handle)
{
        struct tw_cli_ctlr_context              *ctlr;
        struct tw_cli_req_context               *req;
        union tw_cl_command_7k                  *cmd_7k;
        struct tw_cl_command_9k                 *cmd_9k;
        struct tw_cl_passthru_req_packet        *pt_req;
        TW_UINT8                                opcode;
        TW_UINT8                                sgl_offset;
        TW_VOID                                 *sgl = TW_CL_NULL;
        TW_INT32                                error = TW_CL_ERR_REQ_SUCCESS;

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

        ctlr = (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);

        if ((req = tw_cli_get_request(ctlr
                )) == TW_CL_NULL) {
                tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
                        "Out of request context packets: returning busy");
                return(TW_OSL_EBUSY);
        }

        req_handle->cl_req_ctxt = req;
        req->req_handle = req_handle;
        req->orig_req = req_pkt;
        req->tw_cli_callback = tw_cli_complete_io;

        req->flags |= TW_CLI_REQ_FLAGS_PASSTHRU;

        pt_req = &(req_pkt->gen_req_pkt.pt_req);

        tw_osl_memcpy(req->cmd_pkt, pt_req->cmd_pkt,
                pt_req->cmd_pkt_length);
        /* Build the cmd pkt. */
        if ((opcode = GET_OPCODE(((TW_UINT8 *)
                (pt_req->cmd_pkt))[sizeof(struct tw_cl_command_header)]))
                        == TWA_FW_CMD_EXECUTE_SCSI) {
                TW_UINT16       lun_l4, lun_h4;

                tw_cli_dbg_printf(5, ctlr_handle, tw_osl_cur_func(),
                        "passthru: 9k cmd pkt");
                req->flags |= TW_CLI_REQ_FLAGS_9K;
                cmd_9k = &(req->cmd_pkt->command.cmd_pkt_9k);
                lun_l4 = GET_LUN_L4(cmd_9k->lun_l4__req_id);
                lun_h4 = GET_LUN_H4(cmd_9k->lun_h4__sgl_entries);
                cmd_9k->lun_l4__req_id = TW_CL_SWAP16(
                        BUILD_LUN_L4__REQ_ID(lun_l4, req->request_id));
                if (pt_req->sgl_entries) {
                        cmd_9k->lun_h4__sgl_entries =
                                TW_CL_SWAP16(BUILD_LUN_H4__SGL_ENTRIES(lun_h4,
                                        pt_req->sgl_entries));
                        sgl = (TW_VOID *)(cmd_9k->sg_list);
                }
        } else {
                tw_cli_dbg_printf(5, ctlr_handle, tw_osl_cur_func(),
                        "passthru: 7k cmd pkt");
                cmd_7k = &(req->cmd_pkt->command.cmd_pkt_7k);
                cmd_7k->generic.request_id =
                        (TW_UINT8)(TW_CL_SWAP16(req->request_id));
                if ((sgl_offset =
                        GET_SGL_OFF(cmd_7k->generic.sgl_off__opcode))) {
                        if (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA)
                                sgl = (((TW_UINT32 *)cmd_7k) + cmd_7k->generic.size);
                        else
                                sgl = (((TW_UINT32 *)cmd_7k) + sgl_offset);
                        cmd_7k->generic.size += pt_req->sgl_entries *
                                ((ctlr->flags & TW_CL_64BIT_ADDRESSES) ? 3 : 2);
                }
        }

        if (sgl)
                tw_cli_fill_sg_list(ctlr, pt_req->sg_list,
                        sgl, pt_req->sgl_entries);

        if (((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) != TW_CL_NULL) ||
                (ctlr->reset_in_progress)) {
                tw_cli_req_q_insert_tail(req, TW_CLI_PENDING_Q);
                TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
                        TWA_CONTROL_UNMASK_COMMAND_INTERRUPT);
        } else if ((error = tw_cli_submit_cmd(req))) {
                tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                        TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
                        0x1100, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                        "Failed to start passthru command",
                        "error = %d", error);
                tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
        }
        return(error);
}



/*
 * Function name:       tw_cl_ioctl
 * Description:         Handler of CL supported ioctl cmds.
 *
 * Input:               ctlr    -- ptr to per ctlr structure
 *                      cmd     -- ioctl cmd
 *                      buf     -- ptr to buffer in kernel memory, which is
 *                                 a copy of the input buffer in user-space
 * Output:              buf     -- ptr to buffer in kernel memory, which will
 *                                 need to be copied to the output buffer in
 *                                 user-space
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cl_ioctl(struct tw_cl_ctlr_handle *ctlr_handle, u_long cmd, TW_VOID *buf)
{
        struct tw_cli_ctlr_context      *ctlr =
                (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
        struct tw_cl_ioctl_packet       *user_buf =
                (struct tw_cl_ioctl_packet *)buf;
        struct tw_cl_event_packet       event_buf;
        TW_INT32                        event_index;
        TW_INT32                        start_index;
        TW_INT32                        error = TW_OSL_ESUCCESS;

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

        /* Serialize access to the AEN queue and the ioctl lock. */
        tw_osl_get_lock(ctlr_handle, ctlr->gen_lock);

        switch (cmd) {
        case TW_CL_IOCTL_GET_FIRST_EVENT:
                tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                        "Get First Event");

                if (ctlr->aen_q_wrapped) {
                        if (ctlr->aen_q_overflow) {
                                /*
                                 * The aen queue has wrapped, even before some
                                 * events have been retrieved.  Let the caller
                                 * know that he missed out on some AEN's.
                                 */
                                user_buf->driver_pkt.status =
                                        TW_CL_ERROR_AEN_OVERFLOW;
                                ctlr->aen_q_overflow = TW_CL_FALSE;
                        } else
                                user_buf->driver_pkt.status = 0;
                        event_index = ctlr->aen_head;
                } else {
                        if (ctlr->aen_head == ctlr->aen_tail) {
                                user_buf->driver_pkt.status =
                                        TW_CL_ERROR_AEN_NO_EVENTS;
                                break;
                        }
                        user_buf->driver_pkt.status = 0;
                        event_index = ctlr->aen_tail;   /* = 0 */
                }
                tw_osl_memcpy(user_buf->data_buf,
                        &(ctlr->aen_queue[event_index]),
                        sizeof(struct tw_cl_event_packet));

                ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;

                break;


        case TW_CL_IOCTL_GET_LAST_EVENT:
                tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                        "Get Last Event");

                if (ctlr->aen_q_wrapped) {
                        if (ctlr->aen_q_overflow) {
                                /*
                                 * The aen queue has wrapped, even before some
                                 * events have been retrieved.  Let the caller
                                 * know that he missed out on some AEN's.
                                 */
                                user_buf->driver_pkt.status =
                                        TW_CL_ERROR_AEN_OVERFLOW;
                                ctlr->aen_q_overflow = TW_CL_FALSE;
                        } else
                                user_buf->driver_pkt.status = 0;
                } else {
                        if (ctlr->aen_head == ctlr->aen_tail) {
                                user_buf->driver_pkt.status =
                                        TW_CL_ERROR_AEN_NO_EVENTS;
                                break;
                        }
                        user_buf->driver_pkt.status = 0;
                }
                event_index = (ctlr->aen_head - 1 + ctlr->max_aens_supported) %
                        ctlr->max_aens_supported;

                tw_osl_memcpy(user_buf->data_buf,
                        &(ctlr->aen_queue[event_index]),
                        sizeof(struct tw_cl_event_packet));

                ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;

                break;


        case TW_CL_IOCTL_GET_NEXT_EVENT:
                tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                        "Get Next Event");

                user_buf->driver_pkt.status = 0;
                if (ctlr->aen_q_wrapped) {
                        tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                                "Get Next Event: wrapped");
                        if (ctlr->aen_q_overflow) {
                                /*
                                 * The aen queue has wrapped, even before some
                                 * events have been retrieved.  Let the caller
                                 * know that he missed out on some AEN's.
                                 */
                                tw_cli_dbg_printf(2, ctlr_handle,
                                        tw_osl_cur_func(),
                                        "Get Next Event: overflow");
                                user_buf->driver_pkt.status =
                                        TW_CL_ERROR_AEN_OVERFLOW;
                                ctlr->aen_q_overflow = TW_CL_FALSE;
                        }
                        start_index = ctlr->aen_head;
                } else {
                        if (ctlr->aen_head == ctlr->aen_tail) {
                                tw_cli_dbg_printf(3, ctlr_handle,
                                        tw_osl_cur_func(),
                                        "Get Next Event: empty queue");
                                user_buf->driver_pkt.status =
                                        TW_CL_ERROR_AEN_NO_EVENTS;
                                break;
                        }
                        start_index = ctlr->aen_tail;   /* = 0 */
                }
                tw_osl_memcpy(&event_buf, user_buf->data_buf,
                        sizeof(struct tw_cl_event_packet));

                event_index = (start_index + event_buf.sequence_id -
                        ctlr->aen_queue[start_index].sequence_id + 1) %
                        ctlr->max_aens_supported;

                tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                        "Get Next Event: si = %x, ei = %x, ebsi = %x, "
                        "sisi = %x, eisi = %x",
                        start_index, event_index, event_buf.sequence_id,
                        ctlr->aen_queue[start_index].sequence_id,
                        ctlr->aen_queue[event_index].sequence_id);

                if (! (ctlr->aen_queue[event_index].sequence_id >
                        event_buf.sequence_id)) {
                        /*
                         * We don't have any event matching the criterion.  So,
                         * we have to report TW_CL_ERROR_NO_EVENTS.  If we also
                         * encountered an overflow condition above, we cannot
                         * report both conditions during this call.  We choose
                         * to report NO_EVENTS this time, and an overflow the
                         * next time we are called.
                         */
                        if (user_buf->driver_pkt.status ==
                                TW_CL_ERROR_AEN_OVERFLOW) {
                                /*
                                 * Make a note so we report the overflow
                                 * next time.
                                 */
                                ctlr->aen_q_overflow = TW_CL_TRUE;
                        }
                        user_buf->driver_pkt.status = TW_CL_ERROR_AEN_NO_EVENTS;
                        break;
                }
                /* Copy the event -- even if there has been an overflow. */
                tw_osl_memcpy(user_buf->data_buf,
                        &(ctlr->aen_queue[event_index]),
                        sizeof(struct tw_cl_event_packet));

                ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;

                break;


        case TW_CL_IOCTL_GET_PREVIOUS_EVENT:
                tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                        "Get Previous Event");

                user_buf->driver_pkt.status = 0;
                if (ctlr->aen_q_wrapped) {
                        if (ctlr->aen_q_overflow) {
                                /*
                                 * The aen queue has wrapped, even before some
                                 * events have been retrieved.  Let the caller
                                 * know that he missed out on some AEN's.
                                 */
                                user_buf->driver_pkt.status =
                                        TW_CL_ERROR_AEN_OVERFLOW;
                                ctlr->aen_q_overflow = TW_CL_FALSE;
                        }
                        start_index = ctlr->aen_head;
                } else {
                        if (ctlr->aen_head == ctlr->aen_tail) {
                                user_buf->driver_pkt.status =
                                        TW_CL_ERROR_AEN_NO_EVENTS;
                                break;
                        }
                        start_index = ctlr->aen_tail;   /* = 0 */
                }
                tw_osl_memcpy(&event_buf, user_buf->data_buf,
                        sizeof(struct tw_cl_event_packet));

                event_index = (start_index + event_buf.sequence_id -
                        ctlr->aen_queue[start_index].sequence_id - 1) %
                        ctlr->max_aens_supported;

                if (! (ctlr->aen_queue[event_index].sequence_id <
                        event_buf.sequence_id)) {
                        /*
                         * We don't have any event matching the criterion.  So,
                         * we have to report TW_CL_ERROR_NO_EVENTS.  If we also
                         * encountered an overflow condition above, we cannot
                         * report both conditions during this call.  We choose
                         * to report NO_EVENTS this time, and an overflow the
                         * next time we are called.
                         */
                        if (user_buf->driver_pkt.status ==
                                TW_CL_ERROR_AEN_OVERFLOW) {
                                /*
                                 * Make a note so we report the overflow
                                 * next time.
                                 */
                                ctlr->aen_q_overflow = TW_CL_TRUE;
                        }
                        user_buf->driver_pkt.status = TW_CL_ERROR_AEN_NO_EVENTS;
                        break;
                }
                /* Copy the event -- even if there has been an overflow. */
                tw_osl_memcpy(user_buf->data_buf,
                        &(ctlr->aen_queue[event_index]),
                        sizeof(struct tw_cl_event_packet));

                ctlr->aen_queue[event_index].retrieved = TW_CL_AEN_RETRIEVED;

                break;


        case TW_CL_IOCTL_GET_LOCK:
        {
                struct tw_cl_lock_packet        lock_pkt;
                TW_TIME                         cur_time;

                tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                        "Get ioctl lock");

                cur_time = tw_osl_get_local_time();
                tw_osl_memcpy(&lock_pkt, user_buf->data_buf,
                        sizeof(struct tw_cl_lock_packet));

                if ((ctlr->ioctl_lock.lock == TW_CLI_LOCK_FREE) ||
                        (lock_pkt.force_flag) ||
                        (cur_time >= ctlr->ioctl_lock.timeout)) {
                        tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                                "GET_LOCK: Getting lock!");
                        ctlr->ioctl_lock.lock = TW_CLI_LOCK_HELD;
                        ctlr->ioctl_lock.timeout =
                                cur_time + (lock_pkt.timeout_msec / 1000);
                        lock_pkt.time_remaining_msec = lock_pkt.timeout_msec;
                        user_buf->driver_pkt.status = 0;
                } else {
                        tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
                                "GET_LOCK: Lock already held!");
                        lock_pkt.time_remaining_msec = (TW_UINT32)(
                                (ctlr->ioctl_lock.timeout - cur_time) * 1000);
                        user_buf->driver_pkt.status =
                                TW_CL_ERROR_IOCTL_LOCK_ALREADY_HELD;
                }
                tw_osl_memcpy(user_buf->data_buf, &lock_pkt,
                        sizeof(struct tw_cl_lock_packet));
                break;
        }


        case TW_CL_IOCTL_RELEASE_LOCK:
                tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                        "Release ioctl lock");

                if (ctlr->ioctl_lock.lock == TW_CLI_LOCK_FREE) {
                        tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(),
                                "twa_ioctl: RELEASE_LOCK: Lock not held!");
                        user_buf->driver_pkt.status =
                                TW_CL_ERROR_IOCTL_LOCK_NOT_HELD;
                } else {
                        tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                                "RELEASE_LOCK: Releasing lock!");
                        ctlr->ioctl_lock.lock = TW_CLI_LOCK_FREE;
                        user_buf->driver_pkt.status = 0;
                }
                break;


        case TW_CL_IOCTL_GET_COMPATIBILITY_INFO:
        {
                struct tw_cl_compatibility_packet       comp_pkt;

                tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                        "Get compatibility info");

                tw_osl_memcpy(comp_pkt.driver_version,
                        TW_OSL_DRIVER_VERSION_STRING,
                        sizeof(TW_OSL_DRIVER_VERSION_STRING));
                comp_pkt.working_srl = ctlr->working_srl;
                comp_pkt.working_branch = ctlr->working_branch;
                comp_pkt.working_build = ctlr->working_build;
                comp_pkt.driver_srl_high = TWA_CURRENT_FW_SRL;
                comp_pkt.driver_branch_high =
                        TWA_CURRENT_FW_BRANCH(ctlr->arch_id);
                comp_pkt.driver_build_high =
                        TWA_CURRENT_FW_BUILD(ctlr->arch_id);
                comp_pkt.driver_srl_low = TWA_BASE_FW_SRL;
                comp_pkt.driver_branch_low = TWA_BASE_FW_BRANCH;
                comp_pkt.driver_build_low = TWA_BASE_FW_BUILD;
                comp_pkt.fw_on_ctlr_srl = ctlr->fw_on_ctlr_srl;
                comp_pkt.fw_on_ctlr_branch = ctlr->fw_on_ctlr_branch;
                comp_pkt.fw_on_ctlr_build = ctlr->fw_on_ctlr_build;
                user_buf->driver_pkt.status = 0;

                /* Copy compatibility information to user space. */
                tw_osl_memcpy(user_buf->data_buf, &comp_pkt,
                        (sizeof(struct tw_cl_compatibility_packet) <
                        user_buf->driver_pkt.buffer_length) ?
                        sizeof(struct tw_cl_compatibility_packet) :
                        user_buf->driver_pkt.buffer_length);
                break;
        }

        default:
                /* Unknown opcode. */
                tw_cli_dbg_printf(3, ctlr_handle, tw_osl_cur_func(),
                        "Unknown ioctl cmd 0x%lx", cmd);
                error = TW_OSL_ENOTTY;
        }

        tw_osl_free_lock(ctlr_handle, ctlr->gen_lock);
        return(error);
}



/*
 * Function name:       tw_cli_get_param
 * Description:         Get a firmware parameter.
 *
 * Input:               ctlr            -- ptr to per ctlr structure
 *                      table_id        -- parameter table #
 *                      param_id        -- index of the parameter in the table
 *                      param_size      -- size of the parameter in bytes
 *                      callback        -- ptr to function, if any, to be called
 *                                      back on completion; TW_CL_NULL if no callback.
 * Output:              param_data      -- param value
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_get_param(struct tw_cli_ctlr_context *ctlr, TW_INT32 table_id,
        TW_INT32 param_id, TW_VOID *param_data, TW_INT32 param_size,
        TW_VOID (* callback)(struct tw_cli_req_context *req))
{
        struct tw_cli_req_context       *req;
        union tw_cl_command_7k          *cmd;
        struct tw_cl_param_9k           *param = TW_CL_NULL;
        TW_INT32                        error = TW_OSL_EBUSY;

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

        /* Get a request packet. */
        if ((req = tw_cli_get_request(ctlr
                )) == TW_CL_NULL)
                goto out;

        /* Make sure this is the only CL internal request at this time. */
        if (ctlr->internal_req_busy) {
                error = TW_OSL_EBUSY;
                goto out;
        }
        ctlr->internal_req_busy = TW_CL_TRUE;
        req->data = ctlr->internal_req_data;
        req->data_phys = ctlr->internal_req_data_phys;
        req->length = TW_CLI_SECTOR_SIZE;
        req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;

        /* Initialize memory to read data into. */
        param = (struct tw_cl_param_9k *)(req->data);
        tw_osl_memzero(param, sizeof(struct tw_cl_param_9k) - 1 + param_size);

        /* Build the cmd pkt. */
        cmd = &(req->cmd_pkt->command.cmd_pkt_7k);

        req->cmd_pkt->cmd_hdr.header_desc.size_header = 128;

        cmd->param.sgl_off__opcode =
                BUILD_SGL_OFF__OPCODE(2, TWA_FW_CMD_GET_PARAM);
        cmd->param.request_id = (TW_UINT8)(TW_CL_SWAP16(req->request_id));
        cmd->param.host_id__unit = BUILD_HOST_ID__UNIT(0, 0);
        cmd->param.param_count = TW_CL_SWAP16(1);

        if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
                ((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].address =
                        TW_CL_SWAP64(req->data_phys);
                ((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].length =
                        TW_CL_SWAP32(req->length);
                cmd->param.size = 2 + 3;
        } else {
                ((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].address =
                        TW_CL_SWAP32(req->data_phys);
                ((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].length =
                        TW_CL_SWAP32(req->length);
                cmd->param.size = 2 + 2;
        }

        /* Specify which parameter we need. */
        param->table_id = TW_CL_SWAP16(table_id | TWA_9K_PARAM_DESCRIPTOR);
        param->parameter_id = (TW_UINT8)(param_id);
        param->parameter_size_bytes = TW_CL_SWAP16(param_size);

        /* Submit the command. */
        if (callback == TW_CL_NULL) {
                /* There's no call back; wait till the command completes. */
                error = tw_cli_submit_and_poll_request(req,
                                TW_CLI_REQUEST_TIMEOUT_PERIOD);
                if (error)
                        goto out;
                if ((error = cmd->param.status)) {
#if       0
                        tw_cli_create_ctlr_event(ctlr,
                                TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
                                &(req->cmd_pkt->cmd_hdr));
#endif // 0
                        goto out;
                }
                tw_osl_memcpy(param_data, param->data, param_size);
                ctlr->internal_req_busy = TW_CL_FALSE;
                tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
        } else {
                /* There's a call back.  Simply submit the command. */
                req->tw_cli_callback = callback;
                if ((error = tw_cli_submit_cmd(req)))
                        goto out;
        }
        return(0);

out:
        tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
                TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
                0x1101, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                "get_param failed",
                "error = %d", error);
        if (param)
                ctlr->internal_req_busy = TW_CL_FALSE;
        if (req)
                tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
        return(1);
}



/*
 * Function name:       tw_cli_set_param
 * Description:         Set a firmware parameter.
 *
 * Input:               ctlr            -- ptr to per ctlr structure
 *                      table_id        -- parameter table #
 *                      param_id        -- index of the parameter in the table
 *                      param_size      -- size of the parameter in bytes
 *                      callback        -- ptr to function, if any, to be called
 *                                      back on completion; TW_CL_NULL if no callback.
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_set_param(struct tw_cli_ctlr_context *ctlr, TW_INT32 table_id,
        TW_INT32 param_id, TW_INT32 param_size, TW_VOID *data,
        TW_VOID (* callback)(struct tw_cli_req_context *req))
{
        struct tw_cli_req_context       *req;
        union tw_cl_command_7k          *cmd;
        struct tw_cl_param_9k           *param = TW_CL_NULL;
        TW_INT32                        error = TW_OSL_EBUSY;

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

        /* Get a request packet. */
        if ((req = tw_cli_get_request(ctlr
                )) == TW_CL_NULL)
                goto out;

        /* Make sure this is the only CL internal request at this time. */
        if (ctlr->internal_req_busy) {
                error = TW_OSL_EBUSY;
                goto out;
        }
        ctlr->internal_req_busy = TW_CL_TRUE;
        req->data = ctlr->internal_req_data;
        req->data_phys = ctlr->internal_req_data_phys;
        req->length = TW_CLI_SECTOR_SIZE;
        req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;

        /* Initialize memory to send data using. */
        param = (struct tw_cl_param_9k *)(req->data);
        tw_osl_memzero(param, sizeof(struct tw_cl_param_9k) - 1 + param_size);

        /* Build the cmd pkt. */
        cmd = &(req->cmd_pkt->command.cmd_pkt_7k);

        req->cmd_pkt->cmd_hdr.header_desc.size_header = 128;

        cmd->param.sgl_off__opcode =
                BUILD_SGL_OFF__OPCODE(2, TWA_FW_CMD_SET_PARAM);
        cmd->param.request_id = (TW_UINT8)(TW_CL_SWAP16(req->request_id));
        cmd->param.host_id__unit = BUILD_HOST_ID__UNIT(0, 0);
        cmd->param.param_count = TW_CL_SWAP16(1);

        if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
                ((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].address =
                        TW_CL_SWAP64(req->data_phys);
                ((struct tw_cl_sg_desc64 *)(cmd->param.sgl))[0].length =
                        TW_CL_SWAP32(req->length);
                cmd->param.size = 2 + 3;
        } else {
                ((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].address =
                        TW_CL_SWAP32(req->data_phys);
                ((struct tw_cl_sg_desc32 *)(cmd->param.sgl))[0].length =
                        TW_CL_SWAP32(req->length);
                cmd->param.size = 2 + 2;
        }

        /* Specify which parameter we want to set. */
        param->table_id = TW_CL_SWAP16(table_id | TWA_9K_PARAM_DESCRIPTOR);
        param->parameter_id = (TW_UINT8)(param_id);
        param->parameter_size_bytes = TW_CL_SWAP16(param_size);
        tw_osl_memcpy(param->data, data, param_size);

        /* Submit the command. */
        if (callback == TW_CL_NULL) {
                /* There's no call back; wait till the command completes. */
                error = tw_cli_submit_and_poll_request(req,
                                TW_CLI_REQUEST_TIMEOUT_PERIOD);
                if (error)
                        goto out;
                if ((error = cmd->param.status)) {
#if       0
                        tw_cli_create_ctlr_event(ctlr,
                                TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
                                &(req->cmd_pkt->cmd_hdr));
#endif // 0
                        goto out;
                }
                ctlr->internal_req_busy = TW_CL_FALSE;
                tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
        } else {
                /* There's a call back.  Simply submit the command. */
                req->tw_cli_callback = callback;
                if ((error = tw_cli_submit_cmd(req)))
                        goto out;
        }
        return(error);

out:
        tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
                TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
                0x1102, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                "set_param failed",
                "error = %d", error);
        if (param)
                ctlr->internal_req_busy = TW_CL_FALSE;
        if (req)
                tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
        return(error);
}



/*
 * Function name:       tw_cli_submit_and_poll_request
 * Description:         Sends down a firmware cmd, and waits for the completion
 *                      in a tight loop.
 *
 * Input:               req     -- ptr to request pkt
 *                      timeout -- max # of seconds to wait before giving up
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_submit_and_poll_request(struct tw_cli_req_context *req,
        TW_UINT32 timeout)
{
        struct tw_cli_ctlr_context      *ctlr = req->ctlr;
        TW_TIME                         end_time;
        TW_INT32                        error;

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

        /*
         * If the cmd queue is full, tw_cli_submit_cmd will queue this
         * request in the pending queue, since this is an internal request.
         */
        if ((error = tw_cli_submit_cmd(req))) {
                tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
                        TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
                        0x1103, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                        "Failed to start internal request",
                        "error = %d", error);
                return(error);
        }

        /*
         * Poll for the response until the command gets completed, or there's
         * a timeout.
         */
        end_time = tw_osl_get_local_time() + timeout;
        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.
                         * The caller should do the clean-up.
                         */
                        return(error);

                /* See if the command completed. */
                tw_cli_process_resp_intr(ctlr);

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

        /* Time out! */
        tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
                TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
                0x1104, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                "Internal request timed out",
                "request = %p", req);

        /*
         * We will reset the controller only if the request has already been
         * submitted, so as to not lose the request packet.  If a busy request
         * timed out, the reset will take care of freeing resources.  If a
         * pending request timed out, we will free resources for that request,
         * right here, thereby avoiding a reset.  So, the caller is expected
         * to NOT cleanup when TW_OSL_ETIMEDOUT is returned.
         */

        /*
         * We have to make sure that this timed out request, if it were in the
         * pending queue, doesn't get submitted while we are here, from
         * tw_cli_submit_pending_queue.  There could be a race in that case.
         * Need to revisit.
         */
        if (req->state == TW_CLI_REQ_STATE_PENDING) {
                tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(),
                        "Removing request from pending queue");
                /*
                 * Request was never submitted.  Clean up.  Note that we did
                 * not do a reset.  So, we have to remove the request ourselves
                 * from the pending queue (as against tw_cli_drain_pendinq_queue
                 * taking care of it).
                 */
                tw_cli_req_q_remove_item(req, TW_CLI_PENDING_Q);
                if ((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) == TW_CL_NULL)
                        TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
                                TWA_CONTROL_MASK_COMMAND_INTERRUPT);
                if (req->data)
                        ctlr->internal_req_busy = TW_CL_FALSE;
                tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
        }

        return(TW_OSL_ETIMEDOUT);
}



/*
 * Function name:       tw_cl_reset_ctlr
 * Description:         Soft resets and then initializes the controller;
 *                      drains any incomplete requests.
 *
 * Input:               ctlr    -- ptr to per ctlr structure
 *                      req_handle      -- ptr to request handle
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cl_reset_ctlr(struct tw_cl_ctlr_handle *ctlr_handle)
{
        struct tw_cli_ctlr_context      *ctlr =
                (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
        struct twa_softc                *sc = ctlr_handle->osl_ctlr_ctxt;
        struct tw_cli_req_context       *req;
        TW_INT32                        reset_attempt = 1;
        TW_INT32                        error = 0;

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

        ctlr->reset_in_progress = TW_CL_TRUE;
        twa_teardown_intr(sc);


        /*
         * Error back all requests in the complete, busy, and pending queues.
         * If any request is already on its way to getting submitted, it's in
         * none of these queues and so, will not be completed.  That request
         * will continue its course and get submitted to the controller after
         * the reset is done (and io_lock is released).
         */
        tw_cli_drain_complete_queue(ctlr);
        tw_cli_drain_busy_queue(ctlr);
        tw_cli_drain_pending_queue(ctlr);
        ctlr->internal_req_busy = TW_CL_FALSE;
        ctlr->get_more_aens     = TW_CL_FALSE;

        /* Soft reset the controller. */
        while (reset_attempt <= TW_CLI_MAX_RESET_ATTEMPTS) {
                if ((error = tw_cli_soft_reset(ctlr))) {
                        tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                                TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                                0x1105, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                                "Controller reset failed",
                                "error = %d; attempt %d", error, reset_attempt++);
                        reset_attempt++;
                        continue;
                }

                /* Re-establish logical connection with the controller. */
                if ((error = tw_cli_init_connection(ctlr,
                                (TW_UINT16)(ctlr->max_simult_reqs),
                                0, 0, 0, 0, 0, TW_CL_NULL, TW_CL_NULL, TW_CL_NULL,
                                TW_CL_NULL, TW_CL_NULL))) {
                        tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                                TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                                0x1106, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                                "Can't initialize connection after reset",
                                "error = %d", error);
                        reset_attempt++;
                        continue;
                }

#ifdef    TW_OSL_DEBUG
                tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                        TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                        0x1107, 0x3, TW_CL_SEVERITY_INFO_STRING,
                        "Controller reset done!", " ");
#endif /* TW_OSL_DEBUG */
                break;
        } /* End of while */

        /* Move commands from the reset queue to the pending queue. */
        while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_RESET_Q)) != TW_CL_NULL) {
                tw_osl_timeout(req->req_handle);
                tw_cli_req_q_insert_tail(req, TW_CLI_PENDING_Q);
        }

        twa_setup_intr(sc);
        tw_cli_enable_interrupts(ctlr);
        if ((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) != TW_CL_NULL)
                TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
                        TWA_CONTROL_UNMASK_COMMAND_INTERRUPT);
        ctlr->reset_in_progress = TW_CL_FALSE;
        ctlr->reset_needed = TW_CL_FALSE;

        /* Request for a bus re-scan. */
        tw_osl_scan_bus(ctlr_handle);

        return(error);
}

TW_VOID
tw_cl_set_reset_needed(struct tw_cl_ctlr_handle *ctlr_handle)
{
        struct tw_cli_ctlr_context      *ctlr =
                (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);

        ctlr->reset_needed = TW_CL_TRUE;
}

TW_INT32
tw_cl_is_reset_needed(struct tw_cl_ctlr_handle *ctlr_handle)
{
        struct tw_cli_ctlr_context      *ctlr =
                (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);

        return(ctlr->reset_needed);
}

TW_INT32
tw_cl_is_active(struct tw_cl_ctlr_handle *ctlr_handle)
{
        struct tw_cli_ctlr_context      *ctlr =
                (struct tw_cli_ctlr_context *)
                (ctlr_handle->cl_ctlr_ctxt);

                return(ctlr->active);
}



/*
 * Function name:       tw_cli_soft_reset
 * Description:         Does the actual soft reset.
 *
 * Input:               ctlr    -- ptr to per ctlr structure
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_soft_reset(struct tw_cli_ctlr_context *ctlr)
{
        struct tw_cl_ctlr_handle        *ctlr_handle = ctlr->ctlr_handle;
        int                             found;
        int                             loop_count;
        TW_UINT32                       error;

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

        tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                0x1108, 0x3, TW_CL_SEVERITY_INFO_STRING,
                "Resetting controller...",
                " ");

        /* Don't let any new commands get submitted to the controller. */
        tw_osl_get_lock(ctlr_handle, ctlr->io_lock);

        TW_CLI_SOFT_RESET(ctlr_handle);

        if ((ctlr->device_id == TW_CL_DEVICE_ID_9K_X) ||
            (ctlr->device_id == TW_CL_DEVICE_ID_9K_E) ||
            (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA)) {
                /*
                 * There's a hardware bug in the G133 ASIC, which can lead to
                 * PCI parity errors and hangs, if the host accesses any
                 * registers when the firmware is resetting the hardware, as
                 * part of a hard/soft reset.  The window of time when the
                 * problem can occur is about 10 ms.  Here, we will handshake
                 * with the firmware to find out when the firmware is pulling
                 * down the hardware reset pin, and wait for about 500 ms to
                 * make sure we don't access any hardware registers (for
                 * polling) during that window.
                 */
                ctlr->reset_phase1_in_progress = TW_CL_TRUE;
                loop_count = 0;
                do {
                        found = (tw_cli_find_response(ctlr, TWA_RESET_PHASE1_NOTIFICATION_RESPONSE) == TW_OSL_ESUCCESS);
                        tw_osl_delay(10);
                        loop_count++;
                        error = 0x7888;
                } while (!found && (loop_count < 6000000)); /* Loop for no more than 60 seconds */

                if (!found) {
                        tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                                TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                                0x1109, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                                "Missed firmware handshake after soft-reset",
                                "error = %d", error);
                        tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
                        return(error);
                }

                tw_osl_delay(TWA_RESET_PHASE1_WAIT_TIME_MS * 1000);
                ctlr->reset_phase1_in_progress = TW_CL_FALSE;
        }

        if ((error = tw_cli_poll_status(ctlr,
                        TWA_STATUS_MICROCONTROLLER_READY |
                        TWA_STATUS_ATTENTION_INTERRUPT,
                        TW_CLI_RESET_TIMEOUT_PERIOD))) {
                tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                        TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                        0x1109, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                        "Micro-ctlr not ready/No attn intr after reset",
                        "error = %d", error);
                tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
                return(error);
        }

        TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
                TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT);

        if ((error = tw_cli_drain_response_queue(ctlr))) {
                tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                        TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
                        0x110A, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                        "Can't drain response queue after reset",
                        "error = %d", error);
                tw_osl_free_lock(ctlr_handle, ctlr->io_lock);
                return(error);
        }

        tw_osl_free_lock(ctlr_handle, ctlr->io_lock);

        if ((error = tw_cli_drain_aen_queue(ctlr))) {
                tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                        TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
                        0x110B, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                        "Can't drain AEN queue after reset",
                        "error = %d", error);
                return(error);
        }

        if ((error = tw_cli_find_aen(ctlr, TWA_AEN_SOFT_RESET))) {
                tw_cl_create_event(ctlr_handle, TW_CL_FALSE,
                        TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT,
                        0x110C, 0x1, TW_CL_SEVERITY_ERROR_STRING,
                        "Reset not reported by controller",
                        "error = %d", error);
                return(error);
        }

        return(TW_OSL_ESUCCESS);
}



/*
 * Function name:       tw_cli_send_scsi_cmd
 * Description:         Sends down a scsi cmd to fw.
 *
 * Input:               req     -- ptr to request pkt
 *                      cmd     -- opcode of scsi cmd to send
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_send_scsi_cmd(struct tw_cli_req_context *req, TW_INT32 cmd)
{
        struct tw_cl_command_packet     *cmdpkt;
        struct tw_cl_command_9k         *cmd9k;
        struct tw_cli_ctlr_context      *ctlr;
        TW_INT32                        error;

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

        /* Make sure this is the only CL internal request at this time. */
        if (ctlr->internal_req_busy)
                return(TW_OSL_EBUSY);
        ctlr->internal_req_busy = TW_CL_TRUE;
        req->data = ctlr->internal_req_data;
        req->data_phys = ctlr->internal_req_data_phys;
        tw_osl_memzero(req->data, TW_CLI_SECTOR_SIZE);
        req->length = TW_CLI_SECTOR_SIZE;

        /* Build the cmd pkt. */
        cmdpkt = req->cmd_pkt;

        cmdpkt->cmd_hdr.header_desc.size_header = 128;

        cmd9k = &(cmdpkt->command.cmd_pkt_9k);

        cmd9k->res__opcode =
                BUILD_RES__OPCODE(0, TWA_FW_CMD_EXECUTE_SCSI);
        cmd9k->unit = 0;
        cmd9k->lun_l4__req_id = TW_CL_SWAP16(req->request_id);
        cmd9k->status = 0;
        cmd9k->sgl_offset = 16; /* offset from end of hdr = max cdb len */
        cmd9k->lun_h4__sgl_entries = TW_CL_SWAP16(1);

        if (req->ctlr->flags & TW_CL_64BIT_ADDRESSES) {
                ((struct tw_cl_sg_desc64 *)(cmd9k->sg_list))[0].address =
                        TW_CL_SWAP64(req->data_phys);
                ((struct tw_cl_sg_desc64 *)(cmd9k->sg_list))[0].length =
                        TW_CL_SWAP32(req->length);
        } else {
                ((struct tw_cl_sg_desc32 *)(cmd9k->sg_list))[0].address =
                        TW_CL_SWAP32(req->data_phys);
                ((struct tw_cl_sg_desc32 *)(cmd9k->sg_list))[0].length =
                        TW_CL_SWAP32(req->length);
        }

        cmd9k->cdb[0] = (TW_UINT8)cmd;
        cmd9k->cdb[4] = 128;

        if ((error = tw_cli_submit_cmd(req)))
                if (error != TW_OSL_EBUSY) {
                        tw_cli_dbg_printf(1, ctlr->ctlr_handle,
                                tw_osl_cur_func(),
                                "Failed to start SCSI command "
                                "(request = %p, error = %d)", req, error);
                        return(TW_OSL_EIO);
                }
        return(TW_OSL_ESUCCESS);
}



/*
 * Function name:       tw_cli_get_aen
 * Description:         Sends down a Request Sense cmd to fw to fetch an AEN.
 *
 * Input:               ctlr    -- ptr to per ctlr structure
 * Output:              None
 * Return value:        0       -- success
 *                      non-zero-- failure
 */
TW_INT32
tw_cli_get_aen(struct tw_cli_ctlr_context *ctlr)
{
        struct tw_cli_req_context       *req;
        TW_INT32                        error;

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

        if ((req = tw_cli_get_request(ctlr
                )) == TW_CL_NULL)
                return(TW_OSL_EBUSY);

        req->flags |= TW_CLI_REQ_FLAGS_INTERNAL;
        req->flags |= TW_CLI_REQ_FLAGS_9K;
        req->tw_cli_callback = tw_cli_aen_callback;
        if ((error = tw_cli_send_scsi_cmd(req, 0x03 /* REQUEST_SENSE */))) {
                tw_cli_dbg_printf(1, ctlr->ctlr_handle, tw_osl_cur_func(),
                        "Could not send SCSI command "
                        "(request = %p, error = %d)", req, error);
                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_fill_sg_list
 * Description:         Fills in the scatter/gather list.
 *
 * Input:               ctlr    -- ptr to per ctlr structure
 *                      sgl_src -- ptr to fill the sg list from
 *                      sgl_dest-- ptr to sg list
 *                      nsegments--# of segments
 * Output:              None
 * Return value:        None
 */
TW_VOID
tw_cli_fill_sg_list(struct tw_cli_ctlr_context *ctlr, TW_VOID *sgl_src,
        TW_VOID *sgl_dest, TW_INT32 num_sgl_entries)
{
        TW_INT32        i;

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

        if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
                struct tw_cl_sg_desc64 *sgl_s =
                        (struct tw_cl_sg_desc64 *)sgl_src;
                struct tw_cl_sg_desc64 *sgl_d =
                        (struct tw_cl_sg_desc64 *)sgl_dest;

                tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(),
                        "64 bit addresses");
                for (i = 0; i < num_sgl_entries; i++) {
                        sgl_d[i].address = TW_CL_SWAP64(sgl_s->address);
                        sgl_d[i].length = TW_CL_SWAP32(sgl_s->length);
                        sgl_s++;
                        if (ctlr->flags & TW_CL_64BIT_SG_LENGTH)
                                sgl_s = (struct tw_cl_sg_desc64 *)
                                        (((TW_INT8 *)(sgl_s)) + 4);
                }
        } else {
                struct tw_cl_sg_desc32 *sgl_s =
                        (struct tw_cl_sg_desc32 *)sgl_src;
                struct tw_cl_sg_desc32 *sgl_d =
                        (struct tw_cl_sg_desc32 *)sgl_dest;

                tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(),
                        "32 bit addresses");
                for (i = 0; i < num_sgl_entries; i++) {
                        sgl_d[i].address = TW_CL_SWAP32(sgl_s[i].address);
                        sgl_d[i].length = TW_CL_SWAP32(sgl_s[i].length);
                }
        }
}