#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>
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 ((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);
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;
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);
}
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");
tw_osl_get_lock(ctlr_handle, ctlr->io_lock);
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);
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");
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)) {
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) {
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);
}
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);
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);
}
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");
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) {
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;
}
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) {
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) {
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;
}
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)) {
if (user_buf->driver_pkt.status ==
TW_CL_ERROR_AEN_OVERFLOW) {
ctlr->aen_q_overflow = TW_CL_TRUE;
}
user_buf->driver_pkt.status = TW_CL_ERROR_AEN_NO_EVENTS;
break;
}
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) {
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;
}
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)) {
if (user_buf->driver_pkt.status ==
TW_CL_ERROR_AEN_OVERFLOW) {
ctlr->aen_q_overflow = TW_CL_TRUE;
}
user_buf->driver_pkt.status = TW_CL_ERROR_AEN_NO_EVENTS;
break;
}
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;
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:
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);
}
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");
if ((req = tw_cli_get_request(ctlr
)) == TW_CL_NULL)
goto out;
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;
param = (struct tw_cl_param_9k *)(req->data);
tw_osl_memzero(param, sizeof(struct tw_cl_param_9k) - 1 + param_size);
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;
}
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);
if (callback == TW_CL_NULL) {
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
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 {
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);
}
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");
if ((req = tw_cli_get_request(ctlr
)) == TW_CL_NULL)
goto out;
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;
param = (struct tw_cl_param_9k *)(req->data);
tw_osl_memzero(param, sizeof(struct tw_cl_param_9k) - 1 + param_size);
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;
}
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);
if (callback == TW_CL_NULL) {
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
goto out;
}
ctlr->internal_req_busy = TW_CL_FALSE;
tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q);
} else {
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);
}
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 ((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);
}
end_time = tw_osl_get_local_time() + timeout;
do {
if ((error = req->error_code))
return(error);
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);
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);
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");
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);
}
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);
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;
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;
}
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
break;
}
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;
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);
}
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...",
" ");
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)) {
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));
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);
}
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");
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;
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;
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);
}
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 ))) {
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);
}
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);
}
}
}