#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"
TW_INT32
tw_cl_interrupt(struct tw_cl_ctlr_handle *ctlr_handle)
{
struct tw_cli_ctlr_context *ctlr =
(struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt);
TW_UINT32 status_reg;
TW_INT32 rc = TW_CL_FALSE;
tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered");
if (ctlr == NULL)
goto out;
if (ctlr->reset_in_progress || !(ctlr->active))
goto out;
status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle);
if (tw_cli_check_ctlr_state(ctlr, status_reg))
goto out;
if (status_reg & TWA_STATUS_HOST_INTERRUPT) {
tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
"Host interrupt");
TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
TWA_CONTROL_CLEAR_HOST_INTERRUPT);
}
if (status_reg & TWA_STATUS_ATTENTION_INTERRUPT) {
tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
"Attention interrupt");
rc |= TW_CL_TRUE;
tw_cli_process_attn_intr(ctlr);
TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle,
TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT);
}
if (status_reg & TWA_STATUS_COMMAND_INTERRUPT) {
tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(),
"Command interrupt");
rc |= TW_CL_TRUE;
tw_cli_process_cmd_intr(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_MASK_COMMAND_INTERRUPT);
}
if (status_reg & TWA_STATUS_RESPONSE_INTERRUPT) {
tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(),
"Response interrupt");
rc |= TW_CL_TRUE;
tw_cli_process_resp_intr(ctlr);
}
out:
return(rc);
}
TW_VOID
tw_cli_process_host_intr(struct tw_cli_ctlr_context *ctlr)
{
tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
}
TW_VOID
tw_cli_process_attn_intr(struct tw_cli_ctlr_context *ctlr)
{
TW_INT32 error;
tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
if ((error = tw_cli_get_aen(ctlr))) {
if (error != TW_OSL_EBUSY)
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
0x1200, 0x1, TW_CL_SEVERITY_ERROR_STRING,
"Failed to fetch AEN",
"error = %d", error);
}
}
TW_VOID
tw_cli_process_cmd_intr(struct tw_cli_ctlr_context *ctlr)
{
tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
tw_cli_submit_pending_queue(ctlr);
}
TW_INT32
tw_cli_process_resp_intr(struct tw_cli_ctlr_context *ctlr)
{
TW_UINT32 resp;
struct tw_cli_req_context *req;
TW_INT32 error;
TW_UINT32 status_reg;
tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
for (;;) {
status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle);
if ((error = tw_cli_check_ctlr_state(ctlr, status_reg)))
break;
if (status_reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY) {
tw_cli_dbg_printf(7, ctlr->ctlr_handle,
tw_osl_cur_func(), "Response queue empty");
break;
}
resp = TW_CLI_READ_RESPONSE_QUEUE(ctlr->ctlr_handle);
{
req = &(ctlr->req_ctxt_buf[GET_RESP_ID(resp)]);
}
if (req->state != TW_CLI_REQ_STATE_BUSY) {
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
0x1201, 0x1, TW_CL_SEVERITY_ERROR_STRING,
"Unposted command completed!!",
"request = %p, status = %d",
req, req->state);
#ifdef TW_OSL_DEBUG
tw_cl_print_ctlr_stats(ctlr->ctlr_handle);
#endif
continue;
}
tw_cli_req_q_remove_item(req, TW_CLI_BUSY_Q);
req->state = TW_CLI_REQ_STATE_COMPLETE;
tw_cli_req_q_insert_tail(req, TW_CLI_COMPLETE_Q);
}
tw_cli_process_complete_queue(ctlr);
return(error);
}
TW_INT32
tw_cli_submit_pending_queue(struct tw_cli_ctlr_context *ctlr)
{
struct tw_cli_req_context *req;
TW_INT32 error = TW_OSL_ESUCCESS;
tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_PENDING_Q)) !=
TW_CL_NULL) {
if ((error = tw_cli_submit_cmd(req))) {
if (error == TW_OSL_EBUSY) {
tw_cli_dbg_printf(2, ctlr->ctlr_handle,
tw_osl_cur_func(),
"Requeueing pending request");
req->state = TW_CLI_REQ_STATE_PENDING;
tw_cli_req_q_insert_head(req, TW_CLI_PENDING_Q);
break;
} else {
tw_cl_create_event(ctlr->ctlr_handle,
TW_CL_FALSE,
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
0x1202, 0x1,
TW_CL_SEVERITY_ERROR_STRING,
"Could not start request "
"in pending queue",
"request = %p, opcode = 0x%x, "
"error = %d", req,
GET_OPCODE(req->cmd_pkt->
command.cmd_pkt_9k.res__opcode),
error);
req->error_code = error;
req->state = TW_CLI_REQ_STATE_COMPLETE;
if (req->tw_cli_callback)
req->tw_cli_callback(req);
error = TW_OSL_ESUCCESS;
}
}
}
return(error);
}
TW_VOID
tw_cli_process_complete_queue(struct tw_cli_ctlr_context *ctlr)
{
struct tw_cli_req_context *req;
tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_COMPLETE_Q)) !=
TW_CL_NULL) {
if (req->tw_cli_callback)
req->tw_cli_callback(req);
}
}
TW_VOID
tw_cli_complete_io(struct tw_cli_req_context *req)
{
struct tw_cli_ctlr_context *ctlr = req->ctlr;
struct tw_cl_req_packet *req_pkt =
(struct tw_cl_req_packet *)(req->orig_req);
tw_cli_dbg_printf(8, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
req_pkt->status = TW_CL_ERR_REQ_SUCCESS;
if (req->error_code) {
req_pkt->status = TW_CL_ERR_REQ_UNABLE_TO_SUBMIT_COMMAND;
goto out;
}
if (req->state != TW_CLI_REQ_STATE_COMPLETE) {
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
0x1203, 0x1, TW_CL_SEVERITY_ERROR_STRING,
"I/O completion on incomplete command!!",
"request = %p, status = %d",
req, req->state);
#ifdef TW_OSL_DEBUG
tw_cl_print_ctlr_stats(ctlr->ctlr_handle);
#endif
return;
}
if (req->flags & TW_CLI_REQ_FLAGS_PASSTHRU) {
tw_osl_memcpy(req_pkt->gen_req_pkt.pt_req.cmd_pkt, req->cmd_pkt,
sizeof(struct tw_cl_command_packet));
} else
tw_cli_scsi_complete(req);
out:
req_pkt->tw_osl_callback(req->req_handle);
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
}
TW_VOID
tw_cli_scsi_complete(struct tw_cli_req_context *req)
{
struct tw_cl_req_packet *req_pkt =
(struct tw_cl_req_packet *)(req->orig_req);
struct tw_cl_scsi_req_packet *scsi_req =
&(req_pkt->gen_req_pkt.scsi_req);
struct tw_cl_command_9k *cmd =
&(req->cmd_pkt->command.cmd_pkt_9k);
struct tw_cl_command_header *cmd_hdr;
TW_UINT16 error;
TW_UINT8 *cdb;
tw_cli_dbg_printf(8, req->ctlr->ctlr_handle, tw_osl_cur_func(),
"entered");
scsi_req->scsi_status = cmd->status;
if (! cmd->status)
return;
tw_cli_dbg_printf(1, req->ctlr->ctlr_handle, tw_osl_cur_func(),
"req_id = 0x%x, status = 0x%x",
GET_REQ_ID(cmd->lun_l4__req_id), cmd->status);
cmd_hdr = &(req->cmd_pkt->cmd_hdr);
error = cmd_hdr->status_block.error;
if ((error == TWA_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) ||
(error == TWA_ERROR_UNIT_OFFLINE)) {
if (GET_LUN_L4(cmd->lun_l4__req_id))
req_pkt->status |= TW_CL_ERR_REQ_INVALID_LUN;
else
req_pkt->status |= TW_CL_ERR_REQ_INVALID_TARGET;
} else {
tw_cli_dbg_printf(2, req->ctlr->ctlr_handle,
tw_osl_cur_func(),
"cmd = %x %x %x %x %x %x %x",
GET_OPCODE(cmd->res__opcode),
GET_SGL_OFF(cmd->res__opcode),
cmd->unit,
cmd->lun_l4__req_id,
cmd->status,
cmd->sgl_offset,
cmd->lun_h4__sgl_entries);
cdb = (TW_UINT8 *)(cmd->cdb);
tw_cli_dbg_printf(2, req->ctlr->ctlr_handle,
tw_osl_cur_func(),
"cdb = %x %x %x %x %x %x %x %x "
"%x %x %x %x %x %x %x %x",
cdb[0], cdb[1], cdb[2], cdb[3],
cdb[4], cdb[5], cdb[6], cdb[7],
cdb[8], cdb[9], cdb[10], cdb[11],
cdb[12], cdb[13], cdb[14], cdb[15]);
#if 0
if (! ((cdb[0] == 0x1A) && (error == 0x10D)))
tw_cli_create_ctlr_event(req->ctlr,
TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
cmd_hdr);
#endif
}
if (scsi_req->sense_data) {
tw_osl_memcpy(scsi_req->sense_data, cmd_hdr->sense_data,
TWA_SENSE_DATA_LENGTH);
scsi_req->sense_len = TWA_SENSE_DATA_LENGTH;
req_pkt->status |= TW_CL_ERR_REQ_AUTO_SENSE_VALID;
}
req_pkt->status |= TW_CL_ERR_REQ_SCSI_ERROR;
}
TW_VOID
tw_cli_param_callback(struct tw_cli_req_context *req)
{
struct tw_cli_ctlr_context *ctlr = req->ctlr;
union tw_cl_command_7k *cmd =
&(req->cmd_pkt->command.cmd_pkt_7k);
TW_INT32 error;
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
if (! req->error_code)
if (cmd->param.status) {
#if 0
tw_cli_create_ctlr_event(ctlr,
TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
&(req->cmd_pkt->cmd_hdr));
#endif
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
0x1204, 0x1, TW_CL_SEVERITY_ERROR_STRING,
"get/set_param failed",
"status = %d", cmd->param.status);
}
ctlr->internal_req_busy = TW_CL_FALSE;
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
if ((ctlr->get_more_aens) && (!(ctlr->reset_in_progress))) {
ctlr->get_more_aens = TW_CL_FALSE;
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
"Fetching more AEN's");
if ((error = tw_cli_get_aen(ctlr)))
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
0x1205, 0x1, TW_CL_SEVERITY_ERROR_STRING,
"Failed to fetch all AEN's from param_callback",
"error = %d", error);
}
}
TW_VOID
tw_cli_aen_callback(struct tw_cli_req_context *req)
{
struct tw_cli_ctlr_context *ctlr = req->ctlr;
struct tw_cl_command_9k *cmd =
&(req->cmd_pkt->command.cmd_pkt_9k);
TW_UINT16 aen_code = TWA_AEN_QUEUE_EMPTY;
TW_INT32 error;
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
"req_id = 0x%x, req error = %d, status = 0x%x",
GET_REQ_ID(cmd->lun_l4__req_id), req->error_code, cmd->status);
if (!(error = req->error_code))
if ((error = cmd->status)) {
#if 0
struct tw_cl_command_header *cmd_hdr;
cmd_hdr = (struct tw_cl_command_header *)
(&(req->cmd_pkt->cmd_hdr));
tw_cli_create_ctlr_event(ctlr,
TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR,
cmd_hdr);
#endif
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
0x1206, 0x1, TW_CL_SEVERITY_ERROR_STRING,
"Request Sense failed",
"opcode = 0x%x, status = %d",
GET_OPCODE(cmd->res__opcode), cmd->status);
}
if (error) {
ctlr->internal_req_busy = TW_CL_FALSE;
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
return;
}
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
"Request Sense command succeeded");
aen_code = tw_cli_manage_aen(ctlr, req);
if (aen_code != TWA_AEN_SYNC_TIME_WITH_HOST) {
ctlr->internal_req_busy = TW_CL_FALSE;
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
if (aen_code != TWA_AEN_QUEUE_EMPTY)
if ((error = tw_cli_get_aen(ctlr)))
tw_cl_create_event(ctlr->ctlr_handle,
TW_CL_FALSE,
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
0x1207, 0x1,
TW_CL_SEVERITY_ERROR_STRING,
"Failed to fetch all AEN's",
"error = %d", error);
}
}
TW_UINT16
tw_cli_manage_aen(struct tw_cli_ctlr_context *ctlr,
struct tw_cli_req_context *req)
{
struct tw_cl_command_header *cmd_hdr;
TW_UINT16 aen_code;
TW_TIME local_time;
TW_TIME sync_time;
TW_UINT32 error;
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
cmd_hdr = (struct tw_cl_command_header *)(req->data);
aen_code = cmd_hdr->status_block.error;
switch (aen_code) {
case TWA_AEN_SYNC_TIME_WITH_HOST:
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
"Received AEN_SYNC_TIME");
ctlr->internal_req_busy = TW_CL_FALSE;
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
if (ctlr->interrupts_enabled)
ctlr->get_more_aens = TW_CL_TRUE;
local_time = tw_osl_get_local_time();
sync_time = (local_time - (3 * 86400)) % 604800;
if ((error = tw_cli_set_param(ctlr, TWA_PARAM_TIME_TABLE,
TWA_PARAM_TIME_SCHED_TIME, 4,
&sync_time,
(ctlr->interrupts_enabled)
? tw_cli_param_callback : TW_CL_NULL)))
tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE,
TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR,
0x1208, 0x1, TW_CL_SEVERITY_ERROR_STRING,
"Unable to sync time with ctlr",
"error = %d", error);
break;
case TWA_AEN_QUEUE_EMPTY:
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
"AEN queue empty");
break;
default:
tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(),
"Queueing AEN");
tw_cli_create_ctlr_event(ctlr,
TW_CL_MESSAGE_SOURCE_CONTROLLER_EVENT,
cmd_hdr);
break;
}
return(aen_code);
}
TW_VOID
tw_cli_enable_interrupts(struct tw_cli_ctlr_context *ctlr)
{
tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
ctlr->interrupts_enabled = TW_CL_TRUE;
TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT |
TWA_CONTROL_UNMASK_RESPONSE_INTERRUPT |
TWA_CONTROL_ENABLE_INTERRUPTS);
}
TW_VOID
tw_cli_disable_interrupts(struct tw_cli_ctlr_context *ctlr)
{
tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered");
TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle,
TWA_CONTROL_DISABLE_INTERRUPTS);
ctlr->interrupts_enabled = TW_CL_FALSE;
}