| Commit | Line | Data |
|---|---|---|
| df54c2f9 SW |
1 | /* |
| 2 | * Copyright (c) 2004-07 Applied Micro Circuits Corporation. | |
| 3 | * Copyright (c) 2004-05 Vinod Kashyap | |
| 4 | * All rights reserved. | |
| 5 | * | |
| 6 | * Redistribution and use in source and binary forms, with or without | |
| 7 | * modification, are permitted provided that the following conditions | |
| 8 | * are met: | |
| 9 | * 1. Redistributions of source code must retain the above copyright | |
| 10 | * notice, this list of conditions and the following disclaimer. | |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer in the | |
| 13 | * documentation and/or other materials provided with the distribution. | |
| 14 | * | |
| 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 25 | * SUCH DAMAGE. | |
| 26 | * | |
| 4fbf05f9 | 27 | * $FreeBSD: src/sys/dev/twa/tw_cl_intr.c,v 1.6 2010/08/30 19:15:04 delphij Exp $ |
| df54c2f9 SW |
28 | */ |
| 29 | ||
| 30 | /* | |
| 31 | * AMCC'S 3ware driver for 9000 series storage controllers. | |
| 32 | * | |
| 33 | * Author: Vinod Kashyap | |
| 34 | * Modifications by: Adam Radford | |
| 35 | * Modifications by: Manjunath Ranganathaiah | |
| 36 | */ | |
| 37 | ||
| 38 | ||
| 39 | /* | |
| 40 | * Common Layer interrupt handling functions. | |
| 41 | */ | |
| 42 | ||
| 43 | ||
| 44 | #include "tw_osl_share.h" | |
| 45 | #include "tw_cl_share.h" | |
| 46 | #include "tw_cl_fwif.h" | |
| 47 | #include "tw_cl_ioctl.h" | |
| 48 | #include "tw_cl.h" | |
| 49 | #include "tw_cl_externs.h" | |
| 50 | #include "tw_osl_ioctl.h" | |
| 51 | ||
| 52 | ||
| 53 | ||
| 54 | /* | |
| 55 | * Function name: twa_interrupt | |
| 56 | * Description: Interrupt handler. Determines the kind of interrupt, | |
| 57 | * and returns TW_CL_TRUE if it recognizes the interrupt. | |
| 58 | * | |
| 59 | * Input: ctlr_handle -- controller handle | |
| 60 | * Output: None | |
| 61 | * Return value: TW_CL_TRUE -- interrupt recognized | |
| 62 | * TW_CL_FALSE-- interrupt not recognized | |
| 63 | */ | |
| 64 | TW_INT32 | |
| 65 | tw_cl_interrupt(struct tw_cl_ctlr_handle *ctlr_handle) | |
| 66 | { | |
| 67 | struct tw_cli_ctlr_context *ctlr = | |
| 68 | (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt); | |
| 69 | TW_UINT32 status_reg; | |
| 70 | TW_INT32 rc = TW_CL_FALSE; | |
| 71 | ||
| 72 | tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 73 | ||
| 74 | /* If we don't have controller context, bail */ | |
| 75 | if (ctlr == NULL) | |
| 76 | goto out; | |
| 77 | ||
| 78 | /* | |
| 79 | * Bail If we get an interrupt while resetting, or shutting down. | |
| 80 | */ | |
| 81 | if (ctlr->reset_in_progress || !(ctlr->active)) | |
| 82 | goto out; | |
| 83 | ||
| 84 | /* Read the status register to determine the type of interrupt. */ | |
| 85 | status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle); | |
| 86 | if (tw_cli_check_ctlr_state(ctlr, status_reg)) | |
| 87 | goto out; | |
| 88 | ||
| 89 | /* Clear the interrupt. */ | |
| 90 | if (status_reg & TWA_STATUS_HOST_INTERRUPT) { | |
| 91 | tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(), | |
| 92 | "Host interrupt"); | |
| 93 | TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle, | |
| 94 | TWA_CONTROL_CLEAR_HOST_INTERRUPT); | |
| 95 | } | |
| 96 | if (status_reg & TWA_STATUS_ATTENTION_INTERRUPT) { | |
| 97 | tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(), | |
| 98 | "Attention interrupt"); | |
| 99 | rc |= TW_CL_TRUE; /* request for a deferred isr call */ | |
| 100 | tw_cli_process_attn_intr(ctlr); | |
| 101 | TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle, | |
| 102 | TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT); | |
| 103 | } | |
| 104 | if (status_reg & TWA_STATUS_COMMAND_INTERRUPT) { | |
| 105 | tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(), | |
| 106 | "Command interrupt"); | |
| 107 | rc |= TW_CL_TRUE; /* request for a deferred isr call */ | |
| 108 | tw_cli_process_cmd_intr(ctlr); | |
| 109 | if ((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) == TW_CL_NULL) | |
| 110 | TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle, | |
| 111 | TWA_CONTROL_MASK_COMMAND_INTERRUPT); | |
| 112 | } | |
| 113 | if (status_reg & TWA_STATUS_RESPONSE_INTERRUPT) { | |
| 114 | tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), | |
| 115 | "Response interrupt"); | |
| 116 | rc |= TW_CL_TRUE; /* request for a deferred isr call */ | |
| 117 | tw_cli_process_resp_intr(ctlr); | |
| 118 | } | |
| 119 | out: | |
| 120 | return(rc); | |
| 121 | } | |
| 122 | ||
| 123 | ||
| 124 | ||
| 125 | /* | |
| 126 | * Function name: tw_cli_process_host_intr | |
| 127 | * Description: This function gets called if we triggered an interrupt. | |
| 128 | * We don't use it as of now. | |
| 129 | * | |
| 130 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 131 | * Output: None | |
| 132 | * Return value: None | |
| 133 | */ | |
| 134 | TW_VOID | |
| 135 | tw_cli_process_host_intr(struct tw_cli_ctlr_context *ctlr) | |
| 136 | { | |
| 137 | tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 138 | } | |
| 139 | ||
| 140 | ||
| 141 | ||
| 142 | /* | |
| 143 | * Function name: tw_cli_process_attn_intr | |
| 144 | * Description: This function gets called if the fw posted an AEN | |
| 145 | * (Asynchronous Event Notification). It fetches | |
| 146 | * all the AEN's that the fw might have posted. | |
| 147 | * | |
| 148 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 149 | * Output: None | |
| 150 | * Return value: None | |
| 151 | */ | |
| 152 | TW_VOID | |
| 153 | tw_cli_process_attn_intr(struct tw_cli_ctlr_context *ctlr) | |
| 154 | { | |
| 155 | TW_INT32 error; | |
| 156 | ||
| 157 | tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 158 | ||
| 159 | if ((error = tw_cli_get_aen(ctlr))) { | |
| 160 | /* | |
| 161 | * If the driver is already in the process of retrieveing AEN's, | |
| 162 | * we will be returned TW_OSL_EBUSY. In this case, | |
| 163 | * tw_cli_param_callback or tw_cli_aen_callback will eventually | |
| 164 | * retrieve the AEN this attention interrupt is for. So, we | |
| 165 | * don't need to print the failure. | |
| 166 | */ | |
| 167 | if (error != TW_OSL_EBUSY) | |
| 168 | tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, | |
| 169 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, | |
| 170 | 0x1200, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 171 | "Failed to fetch AEN", | |
| 172 | "error = %d", error); | |
| 173 | } | |
| 174 | } | |
| 175 | ||
| 176 | ||
| 177 | ||
| 178 | /* | |
| 179 | * Function name: tw_cli_process_cmd_intr | |
| 180 | * Description: This function gets called if we hit a queue full | |
| 181 | * condition earlier, and the fw is now ready for | |
| 182 | * new cmds. Submits any pending requests. | |
| 183 | * | |
| 184 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 185 | * Output: None | |
| 186 | * Return value: None | |
| 187 | */ | |
| 188 | TW_VOID | |
| 189 | tw_cli_process_cmd_intr(struct tw_cli_ctlr_context *ctlr) | |
| 190 | { | |
| 191 | tw_cli_dbg_printf(6, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 192 | ||
| 193 | /* Start any requests that might be in the pending queue. */ | |
| 194 | tw_cli_submit_pending_queue(ctlr); | |
| 195 | ||
| 196 | /* | |
| 197 | * If tw_cli_submit_pending_queue was unsuccessful due to a "cmd queue | |
| 198 | * full" condition, cmd_intr will already have been unmasked by | |
| 199 | * tw_cli_submit_cmd. We don't need to do it again... simply return. | |
| 200 | */ | |
| 201 | } | |
| 202 | ||
| 203 | ||
| 204 | ||
| 205 | /* | |
| 206 | * Function name: tw_cli_process_resp_intr | |
| 207 | * Description: Looks for cmd completions from fw; queues cmds completed | |
| 208 | * by fw into complete queue. | |
| 209 | * | |
| 210 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 211 | * Output: None | |
| 212 | * Return value: 0 -- no ctlr error | |
| 213 | * non-zero-- ctlr error | |
| 214 | */ | |
| 215 | TW_INT32 | |
| 216 | tw_cli_process_resp_intr(struct tw_cli_ctlr_context *ctlr) | |
| 217 | { | |
| 218 | TW_UINT32 resp; | |
| 219 | struct tw_cli_req_context *req; | |
| 220 | TW_INT32 error; | |
| 221 | TW_UINT32 status_reg; | |
| 222 | ||
| 223 | tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 224 | ||
| 225 | for (;;) { | |
| 226 | status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle); | |
| 227 | if ((error = tw_cli_check_ctlr_state(ctlr, status_reg))) | |
| 228 | break; | |
| 229 | if (status_reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY) { | |
| 230 | tw_cli_dbg_printf(7, ctlr->ctlr_handle, | |
| 231 | tw_osl_cur_func(), "Response queue empty"); | |
| 232 | break; | |
| 233 | } | |
| 234 | ||
| 235 | /* Response queue is not empty. */ | |
| 236 | resp = TW_CLI_READ_RESPONSE_QUEUE(ctlr->ctlr_handle); | |
| 237 | { | |
| 238 | req = &(ctlr->req_ctxt_buf[GET_RESP_ID(resp)]); | |
| 239 | } | |
| 240 | ||
| 241 | if (req->state != TW_CLI_REQ_STATE_BUSY) { | |
| 242 | tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, | |
| 243 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, | |
| 244 | 0x1201, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 245 | "Unposted command completed!!", | |
| 246 | "request = %p, status = %d", | |
| 247 | req, req->state); | |
| 248 | #ifdef TW_OSL_DEBUG | |
| 249 | tw_cl_print_ctlr_stats(ctlr->ctlr_handle); | |
| 250 | #endif /* TW_OSL_DEBUG */ | |
| 4fbf05f9 | 251 | continue; |
| df54c2f9 SW |
252 | } |
| 253 | ||
| 254 | /* | |
| 255 | * Remove the request from the busy queue, mark it as complete, | |
| 256 | * and enqueue it in the complete queue. | |
| 257 | */ | |
| 258 | tw_cli_req_q_remove_item(req, TW_CLI_BUSY_Q); | |
| 259 | req->state = TW_CLI_REQ_STATE_COMPLETE; | |
| 260 | tw_cli_req_q_insert_tail(req, TW_CLI_COMPLETE_Q); | |
| 261 | ||
| 262 | } | |
| 263 | ||
| 264 | /* Complete this, and other requests in the complete queue. */ | |
| 265 | tw_cli_process_complete_queue(ctlr); | |
| 266 | ||
| 267 | return(error); | |
| 268 | } | |
| 269 | ||
| 270 | ||
| 271 | ||
| 272 | /* | |
| 273 | * Function name: tw_cli_submit_pending_queue | |
| 274 | * Description: Kick starts any requests in the pending queue. | |
| 275 | * | |
| 276 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 277 | * Output: None | |
| 278 | * Return value: 0 -- all pending requests submitted successfully | |
| 279 | * non-zero-- otherwise | |
| 280 | */ | |
| 281 | TW_INT32 | |
| 282 | tw_cli_submit_pending_queue(struct tw_cli_ctlr_context *ctlr) | |
| 283 | { | |
| 284 | struct tw_cli_req_context *req; | |
| 285 | TW_INT32 error = TW_OSL_ESUCCESS; | |
| 286 | ||
| 287 | tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 288 | ||
| 289 | /* | |
| 290 | * Pull requests off the pending queue, and submit them. | |
| 291 | */ | |
| 292 | while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_PENDING_Q)) != | |
| 293 | TW_CL_NULL) { | |
| 294 | if ((error = tw_cli_submit_cmd(req))) { | |
| 295 | if (error == TW_OSL_EBUSY) { | |
| 296 | tw_cli_dbg_printf(2, ctlr->ctlr_handle, | |
| 297 | tw_osl_cur_func(), | |
| 298 | "Requeueing pending request"); | |
| 299 | req->state = TW_CLI_REQ_STATE_PENDING; | |
| 300 | /* | |
| 301 | * Queue the request at the head of the pending | |
| 302 | * queue, and break away, so we don't try to | |
| 303 | * submit any more requests. | |
| 304 | */ | |
| 305 | tw_cli_req_q_insert_head(req, TW_CLI_PENDING_Q); | |
| 306 | break; | |
| 307 | } else { | |
| 308 | tw_cl_create_event(ctlr->ctlr_handle, | |
| 309 | TW_CL_FALSE, | |
| 310 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, | |
| 311 | 0x1202, 0x1, | |
| 312 | TW_CL_SEVERITY_ERROR_STRING, | |
| 313 | "Could not start request " | |
| 314 | "in pending queue", | |
| 315 | "request = %p, opcode = 0x%x, " | |
| 316 | "error = %d", req, | |
| 317 | GET_OPCODE(req->cmd_pkt-> | |
| 318 | command.cmd_pkt_9k.res__opcode), | |
| 319 | error); | |
| 320 | /* | |
| 321 | * Set the appropriate error and call the CL | |
| 322 | * internal callback if there's one. If the | |
| 323 | * request originator is polling for completion, | |
| 324 | * he should be checking req->error to | |
| 325 | * determine that the request did not go | |
| 326 | * through. The request originators are | |
| 327 | * responsible for the clean-up. | |
| 328 | */ | |
| 329 | req->error_code = error; | |
| 330 | req->state = TW_CLI_REQ_STATE_COMPLETE; | |
| 331 | if (req->tw_cli_callback) | |
| 332 | req->tw_cli_callback(req); | |
| 333 | error = TW_OSL_ESUCCESS; | |
| 334 | } | |
| 335 | } | |
| 336 | } | |
| 337 | return(error); | |
| 338 | } | |
| 339 | ||
| 340 | ||
| 341 | ||
| 342 | /* | |
| 343 | * Function name: tw_cli_process_complete_queue | |
| 344 | * Description: Calls the CL internal callback routine, if any, for | |
| 345 | * each request in the complete queue. | |
| 346 | * | |
| 347 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 348 | * Output: None | |
| 349 | * Return value: None | |
| 350 | */ | |
| 351 | TW_VOID | |
| 352 | tw_cli_process_complete_queue(struct tw_cli_ctlr_context *ctlr) | |
| 353 | { | |
| 354 | struct tw_cli_req_context *req; | |
| 355 | ||
| 356 | tw_cli_dbg_printf(10, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 357 | ||
| 358 | /* | |
| 359 | * Pull commands off the completed list, dispatch them appropriately. | |
| 360 | */ | |
| 361 | while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_COMPLETE_Q)) != | |
| 362 | TW_CL_NULL) { | |
| 363 | /* Call the CL internal callback, if there's one. */ | |
| 364 | if (req->tw_cli_callback) | |
| 365 | req->tw_cli_callback(req); | |
| 366 | } | |
| 367 | } | |
| 368 | ||
| 369 | ||
| 370 | ||
| 371 | /* | |
| 372 | * Function name: tw_cli_complete_io | |
| 373 | * Description: CL internal callback for SCSI/fw passthru requests. | |
| 374 | * | |
| 375 | * Input: req -- ptr to CL internal request context | |
| 376 | * Output: None | |
| 377 | * Return value: None | |
| 378 | */ | |
| 379 | TW_VOID | |
| 380 | tw_cli_complete_io(struct tw_cli_req_context *req) | |
| 381 | { | |
| 382 | struct tw_cli_ctlr_context *ctlr = req->ctlr; | |
| 383 | struct tw_cl_req_packet *req_pkt = | |
| 384 | (struct tw_cl_req_packet *)(req->orig_req); | |
| 385 | ||
| 386 | tw_cli_dbg_printf(8, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 387 | ||
| 388 | req_pkt->status = TW_CL_ERR_REQ_SUCCESS; | |
| 389 | if (req->error_code) { | |
| 390 | req_pkt->status = TW_CL_ERR_REQ_UNABLE_TO_SUBMIT_COMMAND; | |
| 391 | goto out; | |
| 392 | } | |
| 393 | ||
| 394 | if (req->state != TW_CLI_REQ_STATE_COMPLETE) { | |
| 395 | tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, | |
| 396 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, | |
| 397 | 0x1203, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 398 | "I/O completion on incomplete command!!", | |
| 399 | "request = %p, status = %d", | |
| 400 | req, req->state); | |
| 401 | #ifdef TW_OSL_DEBUG | |
| 402 | tw_cl_print_ctlr_stats(ctlr->ctlr_handle); | |
| 403 | #endif /* TW_OSL_DEBUG */ | |
| 4fbf05f9 | 404 | return; |
| df54c2f9 SW |
405 | } |
| 406 | ||
| 407 | if (req->flags & TW_CLI_REQ_FLAGS_PASSTHRU) { | |
| 408 | /* Copy the command packet back into OSL's space. */ | |
| 409 | tw_osl_memcpy(req_pkt->gen_req_pkt.pt_req.cmd_pkt, req->cmd_pkt, | |
| 410 | sizeof(struct tw_cl_command_packet)); | |
| 411 | } else | |
| 412 | tw_cli_scsi_complete(req); | |
| 413 | ||
| 414 | out: | |
| 415 | req_pkt->tw_osl_callback(req->req_handle); | |
| 416 | tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); | |
| 417 | } | |
| 418 | ||
| 419 | ||
| 420 | ||
| 421 | /* | |
| 422 | * Function name: tw_cli_scsi_complete | |
| 423 | * Description: Completion routine for SCSI requests. | |
| 424 | * | |
| 425 | * Input: req -- ptr to CL internal request context | |
| 426 | * Output: None | |
| 427 | * Return value: None | |
| 428 | */ | |
| 429 | TW_VOID | |
| 430 | tw_cli_scsi_complete(struct tw_cli_req_context *req) | |
| 431 | { | |
| 432 | struct tw_cl_req_packet *req_pkt = | |
| 433 | (struct tw_cl_req_packet *)(req->orig_req); | |
| 434 | struct tw_cl_scsi_req_packet *scsi_req = | |
| 435 | &(req_pkt->gen_req_pkt.scsi_req); | |
| 436 | struct tw_cl_command_9k *cmd = | |
| 437 | &(req->cmd_pkt->command.cmd_pkt_9k); | |
| 438 | struct tw_cl_command_header *cmd_hdr; | |
| 439 | TW_UINT16 error; | |
| 440 | TW_UINT8 *cdb; | |
| 441 | ||
| 442 | tw_cli_dbg_printf(8, req->ctlr->ctlr_handle, tw_osl_cur_func(), | |
| 443 | "entered"); | |
| 444 | ||
| 445 | scsi_req->scsi_status = cmd->status; | |
| 446 | if (! cmd->status) | |
| 447 | return; | |
| 448 | ||
| 449 | tw_cli_dbg_printf(1, req->ctlr->ctlr_handle, tw_osl_cur_func(), | |
| 450 | "req_id = 0x%x, status = 0x%x", | |
| 451 | GET_REQ_ID(cmd->lun_l4__req_id), cmd->status); | |
| 452 | ||
| 453 | cmd_hdr = &(req->cmd_pkt->cmd_hdr); | |
| 454 | error = cmd_hdr->status_block.error; | |
| 455 | if ((error == TWA_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) || | |
| 456 | (error == TWA_ERROR_UNIT_OFFLINE)) { | |
| 457 | if (GET_LUN_L4(cmd->lun_l4__req_id)) | |
| 458 | req_pkt->status |= TW_CL_ERR_REQ_INVALID_LUN; | |
| 459 | else | |
| 460 | req_pkt->status |= TW_CL_ERR_REQ_INVALID_TARGET; | |
| 461 | } else { | |
| 462 | tw_cli_dbg_printf(2, req->ctlr->ctlr_handle, | |
| 463 | tw_osl_cur_func(), | |
| 464 | "cmd = %x %x %x %x %x %x %x", | |
| 465 | GET_OPCODE(cmd->res__opcode), | |
| 466 | GET_SGL_OFF(cmd->res__opcode), | |
| 467 | cmd->unit, | |
| 468 | cmd->lun_l4__req_id, | |
| 469 | cmd->status, | |
| 470 | cmd->sgl_offset, | |
| 471 | cmd->lun_h4__sgl_entries); | |
| 472 | ||
| 473 | cdb = (TW_UINT8 *)(cmd->cdb); | |
| 474 | tw_cli_dbg_printf(2, req->ctlr->ctlr_handle, | |
| 475 | tw_osl_cur_func(), | |
| 476 | "cdb = %x %x %x %x %x %x %x %x " | |
| 477 | "%x %x %x %x %x %x %x %x", | |
| 478 | cdb[0], cdb[1], cdb[2], cdb[3], | |
| 479 | cdb[4], cdb[5], cdb[6], cdb[7], | |
| 480 | cdb[8], cdb[9], cdb[10], cdb[11], | |
| 481 | cdb[12], cdb[13], cdb[14], cdb[15]); | |
| 482 | ||
| 4fbf05f9 | 483 | #if 0 |
| df54c2f9 SW |
484 | /* |
| 485 | * Print the error. Firmware doesn't yet support | |
| 486 | * the 'Mode Sense' cmd. Don't print if the cmd | |
| 487 | * is 'Mode Sense', and the error is 'Invalid field | |
| 488 | * in CDB'. | |
| 489 | */ | |
| 490 | if (! ((cdb[0] == 0x1A) && (error == 0x10D))) | |
| 491 | tw_cli_create_ctlr_event(req->ctlr, | |
| 492 | TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, | |
| 493 | cmd_hdr); | |
| 4fbf05f9 | 494 | #endif // 0 |
| df54c2f9 SW |
495 | } |
| 496 | ||
| 497 | if (scsi_req->sense_data) { | |
| 498 | tw_osl_memcpy(scsi_req->sense_data, cmd_hdr->sense_data, | |
| 499 | TWA_SENSE_DATA_LENGTH); | |
| 500 | scsi_req->sense_len = TWA_SENSE_DATA_LENGTH; | |
| 501 | req_pkt->status |= TW_CL_ERR_REQ_AUTO_SENSE_VALID; | |
| 502 | } | |
| 503 | req_pkt->status |= TW_CL_ERR_REQ_SCSI_ERROR; | |
| 504 | } | |
| 505 | ||
| 506 | ||
| 507 | ||
| 508 | /* | |
| 509 | * Function name: tw_cli_param_callback | |
| 510 | * Description: Callback for get/set_param requests. | |
| 511 | * | |
| 512 | * Input: req -- ptr to completed request pkt | |
| 513 | * Output: None | |
| 514 | * Return value: None | |
| 515 | */ | |
| 516 | TW_VOID | |
| 517 | tw_cli_param_callback(struct tw_cli_req_context *req) | |
| 518 | { | |
| 519 | struct tw_cli_ctlr_context *ctlr = req->ctlr; | |
| 520 | union tw_cl_command_7k *cmd = | |
| 521 | &(req->cmd_pkt->command.cmd_pkt_7k); | |
| 522 | TW_INT32 error; | |
| 523 | ||
| 524 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 525 | ||
| 526 | /* | |
| 527 | * If the request was never submitted to the controller, the function | |
| 528 | * that sets req->error is responsible for calling tw_cl_create_event. | |
| 529 | */ | |
| 530 | if (! req->error_code) | |
| 531 | if (cmd->param.status) { | |
| 4fbf05f9 | 532 | #if 0 |
| df54c2f9 SW |
533 | tw_cli_create_ctlr_event(ctlr, |
| 534 | TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, | |
| 535 | &(req->cmd_pkt->cmd_hdr)); | |
| 4fbf05f9 | 536 | #endif // 0 |
| df54c2f9 SW |
537 | tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, |
| 538 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, | |
| 539 | 0x1204, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 540 | "get/set_param failed", | |
| 541 | "status = %d", cmd->param.status); | |
| 542 | } | |
| 543 | ||
| 544 | ctlr->internal_req_busy = TW_CL_FALSE; | |
| 545 | tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); | |
| 546 | ||
| 547 | if ((ctlr->get_more_aens) && (!(ctlr->reset_in_progress))) { | |
| 548 | ctlr->get_more_aens = TW_CL_FALSE; | |
| 549 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), | |
| 550 | "Fetching more AEN's"); | |
| 551 | if ((error = tw_cli_get_aen(ctlr))) | |
| 552 | tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, | |
| 553 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, | |
| 554 | 0x1205, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 555 | "Failed to fetch all AEN's from param_callback", | |
| 556 | "error = %d", error); | |
| 557 | } | |
| 558 | } | |
| 559 | ||
| 560 | ||
| 561 | ||
| 562 | /* | |
| 563 | * Function name: tw_cli_aen_callback | |
| 564 | * Description: Callback for requests to fetch AEN's. | |
| 565 | * | |
| 566 | * Input: req -- ptr to completed request pkt | |
| 567 | * Output: None | |
| 568 | * Return value: None | |
| 569 | */ | |
| 570 | TW_VOID | |
| 571 | tw_cli_aen_callback(struct tw_cli_req_context *req) | |
| 572 | { | |
| 573 | struct tw_cli_ctlr_context *ctlr = req->ctlr; | |
| 574 | struct tw_cl_command_header *cmd_hdr; | |
| 575 | struct tw_cl_command_9k *cmd = | |
| 576 | &(req->cmd_pkt->command.cmd_pkt_9k); | |
| 577 | TW_UINT16 aen_code = TWA_AEN_QUEUE_EMPTY; | |
| 578 | TW_INT32 error; | |
| 579 | ||
| 580 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 581 | ||
| 582 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), | |
| 583 | "req_id = 0x%x, req error = %d, status = 0x%x", | |
| 584 | GET_REQ_ID(cmd->lun_l4__req_id), req->error_code, cmd->status); | |
| 585 | ||
| 586 | /* | |
| 587 | * If the request was never submitted to the controller, the function | |
| 588 | * that sets error is responsible for calling tw_cl_create_event. | |
| 589 | */ | |
| 590 | if (!(error = req->error_code)) | |
| 591 | if ((error = cmd->status)) { | |
| 592 | cmd_hdr = (struct tw_cl_command_header *) | |
| 593 | (&(req->cmd_pkt->cmd_hdr)); | |
| 4fbf05f9 | 594 | #if 0 |
| df54c2f9 SW |
595 | tw_cli_create_ctlr_event(ctlr, |
| 596 | TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, | |
| 597 | cmd_hdr); | |
| 4fbf05f9 | 598 | #endif // 0 |
| df54c2f9 SW |
599 | tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, |
| 600 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, | |
| 601 | 0x1206, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 602 | "Request Sense failed", | |
| 603 | "opcode = 0x%x, status = %d", | |
| 604 | GET_OPCODE(cmd->res__opcode), cmd->status); | |
| 605 | } | |
| 606 | ||
| 607 | if (error) { | |
| 608 | ctlr->internal_req_busy = TW_CL_FALSE; | |
| 609 | tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); | |
| 610 | return; | |
| 611 | } | |
| 612 | ||
| 613 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), | |
| 614 | "Request Sense command succeeded"); | |
| 615 | ||
| 616 | aen_code = tw_cli_manage_aen(ctlr, req); | |
| 617 | ||
| 618 | if (aen_code != TWA_AEN_SYNC_TIME_WITH_HOST) { | |
| 619 | ctlr->internal_req_busy = TW_CL_FALSE; | |
| 620 | tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); | |
| 621 | if (aen_code != TWA_AEN_QUEUE_EMPTY) | |
| 622 | if ((error = tw_cli_get_aen(ctlr))) | |
| 623 | tw_cl_create_event(ctlr->ctlr_handle, | |
| 624 | TW_CL_FALSE, | |
| 625 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, | |
| 626 | 0x1207, 0x1, | |
| 627 | TW_CL_SEVERITY_ERROR_STRING, | |
| 628 | "Failed to fetch all AEN's", | |
| 629 | "error = %d", error); | |
| 630 | } | |
| 631 | } | |
| 632 | ||
| 633 | ||
| 634 | ||
| 635 | /* | |
| 636 | * Function name: tw_cli_manage_aen | |
| 637 | * Description: Handles AEN's. | |
| 638 | * | |
| 639 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 640 | * req -- ptr to CL internal request context | |
| 641 | * Output: None | |
| 642 | * Return value: None | |
| 643 | */ | |
| 644 | TW_UINT16 | |
| 645 | tw_cli_manage_aen(struct tw_cli_ctlr_context *ctlr, | |
| 646 | struct tw_cli_req_context *req) | |
| 647 | { | |
| 648 | struct tw_cl_command_header *cmd_hdr; | |
| 649 | TW_UINT16 aen_code; | |
| 650 | TW_TIME local_time; | |
| 651 | TW_TIME sync_time; | |
| 652 | TW_UINT32 error; | |
| 653 | ||
| 654 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 655 | ||
| 656 | cmd_hdr = (struct tw_cl_command_header *)(req->data); | |
| 657 | aen_code = cmd_hdr->status_block.error; | |
| 658 | ||
| 659 | switch (aen_code) { | |
| 660 | case TWA_AEN_SYNC_TIME_WITH_HOST: | |
| 661 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), | |
| 662 | "Received AEN_SYNC_TIME"); | |
| 663 | /* | |
| 664 | * Free the internal req pkt right here, since | |
| 665 | * tw_cli_set_param will need it. | |
| 666 | */ | |
| 667 | ctlr->internal_req_busy = TW_CL_FALSE; | |
| 668 | tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); | |
| 669 | ||
| 670 | /* | |
| 671 | * We will use a callback in tw_cli_set_param only when | |
| 672 | * interrupts are enabled and we can expect our callback | |
| 673 | * to get called. Setting the get_more_aens | |
| 674 | * flag will make the callback continue to try to retrieve | |
| 675 | * more AEN's. | |
| 676 | */ | |
| 677 | if (ctlr->interrupts_enabled) | |
| 678 | ctlr->get_more_aens = TW_CL_TRUE; | |
| 679 | /* Calculate time (in seconds) since last Sunday 12.00 AM. */ | |
| 680 | local_time = tw_osl_get_local_time(); | |
| 681 | sync_time = (local_time - (3 * 86400)) % 604800; | |
| 682 | if ((error = tw_cli_set_param(ctlr, TWA_PARAM_TIME_TABLE, | |
| 683 | TWA_PARAM_TIME_SCHED_TIME, 4, | |
| 684 | &sync_time, | |
| 685 | (ctlr->interrupts_enabled) | |
| 686 | ? tw_cli_param_callback : TW_CL_NULL))) | |
| 687 | tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, | |
| 688 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, | |
| 689 | 0x1208, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 690 | "Unable to sync time with ctlr", | |
| 691 | "error = %d", error); | |
| 692 | ||
| 693 | break; | |
| 694 | ||
| 695 | ||
| 696 | case TWA_AEN_QUEUE_EMPTY: | |
| 697 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), | |
| 698 | "AEN queue empty"); | |
| 699 | break; | |
| 700 | ||
| 701 | ||
| 702 | default: | |
| 703 | /* Queue the event. */ | |
| 704 | ||
| 705 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), | |
| 706 | "Queueing AEN"); | |
| 707 | tw_cli_create_ctlr_event(ctlr, | |
| 708 | TW_CL_MESSAGE_SOURCE_CONTROLLER_EVENT, | |
| 709 | cmd_hdr); | |
| 710 | break; | |
| 711 | } /* switch */ | |
| 712 | return(aen_code); | |
| 713 | } | |
| 714 | ||
| 715 | ||
| 716 | ||
| 717 | /* | |
| 718 | * Function name: tw_cli_enable_interrupts | |
| 719 | * Description: Enables interrupts on the controller | |
| 720 | * | |
| 721 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 722 | * Output: None | |
| 723 | * Return value: None | |
| 724 | */ | |
| 725 | TW_VOID | |
| 726 | tw_cli_enable_interrupts(struct tw_cli_ctlr_context *ctlr) | |
| 727 | { | |
| 728 | tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 729 | ||
| 730 | ctlr->interrupts_enabled = TW_CL_TRUE; | |
| 731 | TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle, | |
| 732 | TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT | | |
| 733 | TWA_CONTROL_UNMASK_RESPONSE_INTERRUPT | | |
| 734 | TWA_CONTROL_ENABLE_INTERRUPTS); | |
| 735 | } | |
| 736 | ||
| 737 | ||
| 738 | ||
| 739 | /* | |
| 740 | * Function name: twa_setup | |
| 741 | * Description: Disables interrupts on the controller | |
| 742 | * | |
| 743 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 744 | * Output: None | |
| 745 | * Return value: None | |
| 746 | */ | |
| 747 | TW_VOID | |
| 748 | tw_cli_disable_interrupts(struct tw_cli_ctlr_context *ctlr) | |
| 749 | { | |
| 750 | tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 751 | ||
| 752 | TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle, | |
| 753 | TWA_CONTROL_DISABLE_INTERRUPTS); | |
| 754 | ctlr->interrupts_enabled = TW_CL_FALSE; | |
| 755 | } |