/* * Function name: tw_cli_aen_callback * Description: Callback for requests to fetch AEN's. * * Input: req -- ptr to completed request pkt * Output: None * Return value: None */ TW_VOID tw_cli_aen_callback(struct tw_cli_req_context *req) { struct tw_cli_ctlr_context *ctlr = req->ctlr; struct tw_cl_command_header *cmd_hdr; struct tw_cl_command_9k *cmd = &(req->cmd_pkt->command.cmd_pkt_9k); TW_UINT16 aen_code = TWA_AEN_QUEUE_EMPTY; TW_INT32 error; tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "req_id = 0x%x, req error = %d, status = 0x%x", GET_REQ_ID(cmd->lun_l4__req_id), req->error_code, cmd->status); /* * If the request was never submitted to the controller, the function * that sets error is responsible for calling tw_cl_create_event. */ if (!(error = req->error_code)) if ((error = cmd->status)) { cmd_hdr = (struct tw_cl_command_header *) (&(req->cmd_pkt->cmd_hdr)); tw_cli_create_ctlr_event(ctlr, TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, cmd_hdr); tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1206, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Request Sense failed", "opcode = 0x%x, status = %d", GET_OPCODE(cmd->res__opcode), cmd->status); } if (error) { ctlr->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); } }
/* * Function name: tw_cli_param_callback * Description: Callback for get/set_param requests. * * Input: req -- ptr to completed request pkt * Output: None * Return value: None */ TW_VOID tw_cli_param_callback(struct tw_cli_req_context *req) { struct tw_cli_ctlr_context *ctlr = req->ctlr; union tw_cl_command_7k *cmd = &(req->cmd_pkt->command.cmd_pkt_7k); TW_INT32 error; tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); /* * If the request was never submitted to the controller, the function * that sets req->error is responsible for calling tw_cl_create_event. */ if (! req->error_code) if (cmd->param.status) { #if 0 tw_cli_create_ctlr_event(ctlr, TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, &(req->cmd_pkt->cmd_hdr)); #endif // 0 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); } }
/* * Function name: tw_cli_manage_aen * Description: Handles AEN's. * * Input: ctlr -- ptr to CL internal ctlr context * req -- ptr to CL internal request context * Output: None * Return value: None */ TW_UINT16 tw_cli_manage_aen(struct tw_cli_ctlr_context *ctlr, struct tw_cli_req_context *req) { struct tw_cl_command_header *cmd_hdr; TW_UINT16 aen_code; TW_TIME local_time; TW_TIME sync_time; TW_UINT32 error; tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); cmd_hdr = (struct tw_cl_command_header *)(req->data); aen_code = cmd_hdr->status_block.error; switch (aen_code) { case TWA_AEN_SYNC_TIME_WITH_HOST: tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "Received AEN_SYNC_TIME"); /* * Free the internal req pkt right here, since * tw_cli_set_param will need it. */ ctlr->internal_req_busy = TW_CL_FALSE; tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); /* * We will use a callback in tw_cli_set_param only when * interrupts are enabled and we can expect our callback * to get called. Setting the get_more_aens * flag will make the callback continue to try to retrieve * more AEN's. */ if (ctlr->interrupts_enabled) ctlr->get_more_aens = TW_CL_TRUE; /* Calculate time (in seconds) since last Sunday 12.00 AM. */ local_time = tw_osl_get_local_time(); sync_time = (local_time - (3 * 86400)) % 604800; if ((error = tw_cli_set_param(ctlr, TWA_PARAM_TIME_TABLE, TWA_PARAM_TIME_SCHED_TIME, 4, &sync_time, (ctlr->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: /* Queue the event. */ tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "Queueing AEN"); tw_cli_create_ctlr_event(ctlr, TW_CL_MESSAGE_SOURCE_CONTROLLER_EVENT, cmd_hdr); break; } /* switch */ return(aen_code); }
/* * Function name: tw_cli_scsi_complete * Description: Completion routine for SCSI requests. * * Input: req -- ptr to CL internal request context * Output: None * Return value: None */ TW_VOID tw_cli_scsi_complete(struct tw_cli_req_context *req) { struct tw_cl_req_packet *req_pkt = (struct tw_cl_req_packet *)(req->orig_req); struct tw_cl_scsi_req_packet *scsi_req = &(req_pkt->gen_req_pkt.scsi_req); struct tw_cl_command_9k *cmd = &(req->cmd_pkt->command.cmd_pkt_9k); struct tw_cl_command_header *cmd_hdr; TW_UINT16 error; TW_UINT8 *cdb; tw_cli_dbg_printf(8, req->ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); scsi_req->scsi_status = cmd->status; if (! cmd->status) return; tw_cli_dbg_printf(1, req->ctlr->ctlr_handle, tw_osl_cur_func(), "req_id = 0x%x, status = 0x%x", GET_REQ_ID(cmd->lun_l4__req_id), cmd->status); cmd_hdr = &(req->cmd_pkt->cmd_hdr); error = cmd_hdr->status_block.error; if ((error == TWA_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) || (error == TWA_ERROR_UNIT_OFFLINE)) { if (GET_LUN_L4(cmd->lun_l4__req_id)) req_pkt->status |= TW_CL_ERR_REQ_INVALID_LUN; else req_pkt->status |= TW_CL_ERR_REQ_INVALID_TARGET; } else { tw_cli_dbg_printf(2, req->ctlr->ctlr_handle, tw_osl_cur_func(), "cmd = %x %x %x %x %x %x %x", GET_OPCODE(cmd->res__opcode), GET_SGL_OFF(cmd->res__opcode), cmd->unit, cmd->lun_l4__req_id, cmd->status, cmd->sgl_offset, cmd->lun_h4__sgl_entries); cdb = (TW_UINT8 *)(cmd->cdb); tw_cli_dbg_printf(2, req->ctlr->ctlr_handle, tw_osl_cur_func(), "cdb = %x %x %x %x %x %x %x %x " "%x %x %x %x %x %x %x %x", cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11], cdb[12], cdb[13], cdb[14], cdb[15]); #if 0 /* * Print the error. Firmware doesn't yet support * the 'Mode Sense' cmd. Don't print if the cmd * is 'Mode Sense', and the error is 'Invalid field * in CDB'. */ if (! ((cdb[0] == 0x1A) && (error == 0x10D))) tw_cli_create_ctlr_event(req->ctlr, TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, cmd_hdr); #endif // 0 } if (scsi_req->sense_data) { tw_osl_memcpy(scsi_req->sense_data, cmd_hdr->sense_data, TWA_SENSE_DATA_LENGTH); scsi_req->sense_len = TWA_SENSE_DATA_LENGTH; req_pkt->status |= TW_CL_ERR_REQ_AUTO_SENSE_VALID; } req_pkt->status |= TW_CL_ERR_REQ_SCSI_ERROR; }
/* * Function name: tw_cli_init_connection * Description: Sends init_connection cmd to firmware * * Input: ctlr -- ptr to per ctlr structure * message_credits -- max # of requests that we might send * down simultaneously. This will be * typically set to 256 at init-time or * after a reset, and to 1 at shutdown-time * set_features -- indicates if we intend to use 64-bit * sg, also indicates if we want to do a * basic or an extended init_connection; * * Note: The following input/output parameters are valid, only in case of an * extended init_connection: * * current_fw_srl -- srl of fw we are bundled * with, if any; 0 otherwise * current_fw_arch_id -- arch_id of fw we are bundled * with, if any; 0 otherwise * current_fw_branch -- branch # of fw we are bundled * with, if any; 0 otherwise * current_fw_build -- build # of fw we are bundled * with, if any; 0 otherwise * Output: fw_on_ctlr_srl -- srl of fw on ctlr * fw_on_ctlr_arch_id -- arch_id of fw on ctlr * fw_on_ctlr_branch -- branch # of fw on ctlr * fw_on_ctlr_build -- build # of fw on ctlr * init_connect_result -- result bitmap of fw response * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_cli_init_connection(struct tw_cli_ctlr_context *ctlr, TW_UINT16 message_credits, TW_UINT32 set_features, TW_UINT16 current_fw_srl, TW_UINT16 current_fw_arch_id, TW_UINT16 current_fw_branch, TW_UINT16 current_fw_build, TW_UINT16 *fw_on_ctlr_srl, TW_UINT16 *fw_on_ctlr_arch_id, TW_UINT16 *fw_on_ctlr_branch, TW_UINT16 *fw_on_ctlr_build, TW_UINT32 *init_connect_result) { struct tw_cli_req_context *req; struct tw_cl_command_init_connect *init_connect; TW_INT32 error = TW_OSL_EBUSY; tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); /* Get a request packet. */ if ((req = tw_cli_get_request(ctlr )) == TW_CL_NULL) goto out; req->flags |= TW_CLI_REQ_FLAGS_INTERNAL; /* Build the cmd pkt. */ init_connect = &(req->cmd_pkt->command.cmd_pkt_7k.init_connect); req->cmd_pkt->cmd_hdr.header_desc.size_header = 128; init_connect->res1__opcode = BUILD_RES__OPCODE(0, TWA_FW_CMD_INIT_CONNECTION); init_connect->request_id = (TW_UINT8)(TW_CL_SWAP16(req->request_id)); init_connect->message_credits = TW_CL_SWAP16(message_credits); init_connect->features = TW_CL_SWAP32(set_features); if (ctlr->flags & TW_CL_64BIT_ADDRESSES) init_connect->features |= TW_CL_SWAP32(TWA_64BIT_SG_ADDRESSES); if (set_features & TWA_EXTENDED_INIT_CONNECT) { /* * Fill in the extra fields needed for an extended * init_connect. */ init_connect->size = 6; init_connect->fw_srl = TW_CL_SWAP16(current_fw_srl); init_connect->fw_arch_id = TW_CL_SWAP16(current_fw_arch_id); init_connect->fw_branch = TW_CL_SWAP16(current_fw_branch); init_connect->fw_build = TW_CL_SWAP16(current_fw_build); } else init_connect->size = 3; /* Submit the command, and wait for it to complete. */ error = tw_cli_submit_and_poll_request(req, TW_CLI_REQUEST_TIMEOUT_PERIOD); if (error == TW_OSL_ETIMEDOUT) /* Clean-up done by tw_cli_submit_and_poll_request. */ return(error); if (error) goto out; if ((error = init_connect->status)) { tw_cli_create_ctlr_event(ctlr, TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, &(req->cmd_pkt->cmd_hdr)); goto out; } if (set_features & TWA_EXTENDED_INIT_CONNECT) { *fw_on_ctlr_srl = TW_CL_SWAP16(init_connect->fw_srl); *fw_on_ctlr_arch_id = TW_CL_SWAP16(init_connect->fw_arch_id); *fw_on_ctlr_branch = TW_CL_SWAP16(init_connect->fw_branch); *fw_on_ctlr_build = TW_CL_SWAP16(init_connect->fw_build); *init_connect_result = TW_CL_SWAP32(init_connect->result); } tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); return(error); out: tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, 0x1016, 0x1, TW_CL_SEVERITY_ERROR_STRING, "init_connection failed", "error = %d", error); if (req) tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); return(error); }
/* * Function name: tw_cli_drain_aen_queue * Description: Fetches all un-retrieved AEN's posted by fw. * * Input: ctlr -- ptr to CL internal ctlr context * Output: None * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_cli_drain_aen_queue(struct tw_cli_ctlr_context *ctlr) { struct tw_cli_req_context *req; struct tw_cl_command_header *cmd_hdr; TW_TIME end_time; TW_UINT16 aen_code; TW_INT32 error; tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); for (;;) { if ((req = tw_cli_get_request(ctlr )) == TW_CL_NULL) { error = TW_OSL_EBUSY; break; } req->flags |= TW_CLI_REQ_FLAGS_INTERNAL; req->tw_cli_callback = TW_CL_NULL; if ((error = tw_cli_send_scsi_cmd(req, 0x03 /* REQUEST_SENSE */))) { tw_cli_dbg_printf(1, ctlr->ctlr_handle, tw_osl_cur_func(), "Cannot send command to fetch aen"); break; } end_time = tw_osl_get_local_time() + TW_CLI_REQUEST_TIMEOUT_PERIOD; do { if ((error = req->error_code)) /* * This will take care of completion due to * a reset, or a failure in * tw_cli_submit_pending_queue. */ goto out; tw_cli_process_resp_intr(req->ctlr); if ((req->state != TW_CLI_REQ_STATE_BUSY) && (req->state != TW_CLI_REQ_STATE_PENDING)) break; } while (tw_osl_get_local_time() <= end_time); if (req->state != TW_CLI_REQ_STATE_COMPLETE) { error = TW_OSL_ETIMEDOUT; break; } if ((error = req->cmd_pkt->command.cmd_pkt_9k.status)) { cmd_hdr = &req->cmd_pkt->cmd_hdr; #if 0 tw_cli_create_ctlr_event(ctlr, TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, cmd_hdr); #endif // 0 break; } aen_code = tw_cli_manage_aen(ctlr, req); if (aen_code == TWA_AEN_QUEUE_EMPTY) break; if (aen_code == TWA_AEN_SYNC_TIME_WITH_HOST) continue; ctlr->internal_req_busy = TW_CL_FALSE; tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); } out: if (req) { if (req->data) ctlr->internal_req_busy = TW_CL_FALSE; tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); } return(error); }