6cc7bd9464
fix the problem with device discovery seen by some people. 2. Change to make 3ware CLI/3DM work on amd64. 3. Fix a potential problem that could cause the driver to do strlen(NULL) when using older firmware. Reviewed by:scottl
793 lines
20 KiB
C
793 lines
20 KiB
C
/*-
|
|
* Copyright (c) 2003-04 3ware, Inc.
|
|
* 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$
|
|
*/
|
|
|
|
/*
|
|
* 3ware driver for 9000 series storage controllers.
|
|
*
|
|
* Author: Vinod Kashyap
|
|
*/
|
|
|
|
|
|
#include <dev/twa/twa_includes.h>
|
|
|
|
#include <cam/cam.h>
|
|
#include <cam/cam_ccb.h>
|
|
#include <cam/cam_sim.h>
|
|
#include <cam/cam_xpt_sim.h>
|
|
#include <cam/cam_xpt_periph.h>
|
|
#include <cam/cam_debug.h>
|
|
#include <cam/cam_periph.h>
|
|
|
|
#include <cam/scsi/scsi_all.h>
|
|
#include <cam/scsi/scsi_message.h>
|
|
|
|
static int twa_execute_scsi(struct twa_request *tr, union ccb *ccb);
|
|
static void twa_action(struct cam_sim *sim, union ccb *ccb);
|
|
static void twa_poll(struct cam_sim *sim);
|
|
static void twa_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg);
|
|
static void twa_timeout(void *arg);
|
|
static void twa_bus_scan_cb(struct cam_periph *periph, union ccb *ccb);
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_cam_setup
|
|
* Description: Attaches the driver to CAM.
|
|
*
|
|
* Input: sc -- ptr to per ctlr structure
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
int
|
|
twa_cam_setup(struct twa_softc *sc)
|
|
{
|
|
struct cam_devq *devq;
|
|
struct ccb_setasync csa;
|
|
|
|
twa_dbg_dprint(3, sc, "sc = %p", sc);
|
|
/*
|
|
* Create the device queue for our SIM.
|
|
*/
|
|
devq = cam_simq_alloc(TWA_Q_LENGTH);
|
|
if (devq == NULL)
|
|
return(ENOMEM);
|
|
|
|
/*
|
|
* Create a SIM entry. Though we can support TWA_Q_LENGTH simultaneous
|
|
* requests, we claim to be able to handle only (TWA_Q_LENGTH - 1), so
|
|
* that we always have a request packet available to service attention
|
|
* interrupts.
|
|
*/
|
|
twa_dbg_dprint(3, sc, "Calling cam_sim_alloc");
|
|
sc->twa_sim = cam_sim_alloc(twa_action, twa_poll, "twa", sc,
|
|
device_get_unit(sc->twa_bus_dev),
|
|
TWA_Q_LENGTH - 1, 1, devq);
|
|
if (sc->twa_sim == NULL) {
|
|
cam_simq_free(devq);
|
|
return(ENOMEM);
|
|
}
|
|
|
|
/*
|
|
* Register the bus.
|
|
*/
|
|
twa_dbg_dprint(3, sc, "Calling xpt_bus_register");
|
|
if (xpt_bus_register(sc->twa_sim, 0) != CAM_SUCCESS) {
|
|
cam_sim_free(sc->twa_sim, TRUE);
|
|
sc->twa_sim = NULL; /* so twa_cam_detach will not try to free it */
|
|
return(ENXIO);
|
|
}
|
|
|
|
twa_dbg_dprint(3, sc, "Calling xpt_create_path");
|
|
if (xpt_create_path(&sc->twa_path, NULL,
|
|
cam_sim_path(sc->twa_sim),
|
|
CAM_TARGET_WILDCARD,
|
|
CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
|
|
xpt_bus_deregister(cam_sim_path (sc->twa_sim));
|
|
cam_sim_free(sc->twa_sim, TRUE); /* passing TRUE will free the devq as well */
|
|
return(ENXIO);
|
|
}
|
|
|
|
twa_dbg_dprint(3, sc, "Calling xpt_setup_ccb");
|
|
xpt_setup_ccb(&csa.ccb_h, sc->twa_path, 5);
|
|
csa.ccb_h.func_code = XPT_SASYNC_CB;
|
|
csa.event_enable = AC_FOUND_DEVICE | AC_LOST_DEVICE;
|
|
csa.callback = twa_async;
|
|
csa.callback_arg = sc;
|
|
xpt_action((union ccb *)&csa);
|
|
|
|
twa_dbg_dprint(3, sc, "Calling twa_request_bus_scan");
|
|
/*
|
|
* Request a bus scan, so that CAM gets to know of
|
|
* the logical units that we control.
|
|
*/
|
|
twa_request_bus_scan(sc);
|
|
twa_dbg_dprint(3, sc, "Exiting");
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_cam_detach
|
|
* Description: Detaches the driver from CAM.
|
|
*
|
|
* Input: sc -- ptr to per ctlr structure
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
void
|
|
twa_cam_detach(struct twa_softc *sc)
|
|
{
|
|
if (sc->twa_path)
|
|
xpt_free_path(sc->twa_path);
|
|
if (sc->twa_sim) {
|
|
xpt_bus_deregister(cam_sim_path(sc->twa_sim));
|
|
cam_sim_free(sc->twa_sim, TRUE); /* passing TRUE will free the devq as well */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_send_scsi_cmd
|
|
* Description: Sends down a scsi cmd to fw.
|
|
*
|
|
* Input: tr -- ptr to request pkt
|
|
* cmd -- opcode of scsi cmd to send
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
int
|
|
twa_send_scsi_cmd(struct twa_request *tr, int cmd)
|
|
{
|
|
union ccb ccb;
|
|
|
|
bzero(&ccb, sizeof(union ccb));
|
|
ccb.csio.cdb_io.cdb_bytes[0] = (u_int8_t)cmd;
|
|
ccb.csio.cdb_io.cdb_bytes[4] = 128;
|
|
ccb.csio.cdb_len = 16;
|
|
if ((ccb.csio.data_ptr = malloc(TWA_SECTOR_SIZE, M_DEVBUF, M_NOWAIT))
|
|
== NULL)
|
|
return(ENOMEM);
|
|
bzero(ccb.csio.data_ptr, TWA_SECTOR_SIZE);
|
|
ccb.csio.dxfer_len = TWA_SECTOR_SIZE;
|
|
|
|
ccb.ccb_h.target_id = 0;
|
|
ccb.ccb_h.flags |= CAM_DIR_IN;
|
|
|
|
if (twa_execute_scsi(tr, &ccb))
|
|
return(EIO);
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_execute_scsi
|
|
* Description: Build a fw cmd, based on a CAM style ccb, and
|
|
* send it down.
|
|
*
|
|
* Input: tr -- ptr to request pkt
|
|
* ccb -- ptr to CAM style ccb
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
int
|
|
twa_execute_scsi(struct twa_request *tr, union ccb *ccb)
|
|
{
|
|
struct twa_softc *sc = tr->tr_sc;
|
|
struct twa_command_packet *cmdpkt;
|
|
struct twa_command_9k *cmd9k;
|
|
struct ccb_hdr *ccb_h = &(ccb->ccb_h);
|
|
struct ccb_scsiio *csio = &(ccb->csio);
|
|
int error;
|
|
|
|
twa_dbg_dprint(3, sc, "SCSI I/O request 0x%x",
|
|
csio->cdb_io.cdb_bytes[0]);
|
|
|
|
if (ccb_h->target_id >= TWA_MAX_UNITS) {
|
|
twa_dbg_dprint(3, sc, "Invalid target. PTL = %x %x %x",
|
|
ccb_h->path_id, ccb_h->target_id, ccb_h->target_lun);
|
|
ccb_h->status |= CAM_TID_INVALID;
|
|
if (tr->tr_cmd_pkt_type & TWA_CMD_PKT_TYPE_EXTERNAL)
|
|
xpt_done(ccb);
|
|
return(1);
|
|
}
|
|
if (ccb_h->target_lun != 0) {
|
|
twa_dbg_dprint(3, sc, "Invalid lun. PTL = %x %x %x",
|
|
ccb_h->path_id, ccb_h->target_id, ccb_h->target_lun);
|
|
ccb_h->status |= CAM_LUN_INVALID;
|
|
if (tr->tr_cmd_pkt_type & TWA_CMD_PKT_TYPE_EXTERNAL)
|
|
xpt_done(ccb);
|
|
return(1);
|
|
}
|
|
|
|
if(ccb_h->flags & CAM_CDB_PHYS) {
|
|
twa_printf(sc, "Physical CDB address!\n");
|
|
ccb_h->status = CAM_REQ_CMP_ERR;
|
|
if (tr->tr_cmd_pkt_type & TWA_CMD_PKT_TYPE_EXTERNAL)
|
|
xpt_done(ccb);
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* We are going to work on this request. Mark it as enqueued (though
|
|
* we don't actually queue it...)
|
|
*/
|
|
ccb_h->status |= CAM_SIM_QUEUED;
|
|
|
|
if((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
|
|
if(ccb_h->flags & CAM_DIR_IN)
|
|
tr->tr_flags |= TWA_CMD_DATA_IN;
|
|
else
|
|
tr->tr_flags |= TWA_CMD_DATA_OUT;
|
|
}
|
|
|
|
cmdpkt = tr->tr_command;
|
|
|
|
cmdpkt->cmd_hdr.header_desc.size_header = 128;
|
|
|
|
cmd9k = &(cmdpkt->command.cmd_pkt_9k);
|
|
cmd9k->command.opcode = TWA_OP_EXECUTE_SCSI_COMMAND;
|
|
cmd9k->unit = ccb_h->target_id;
|
|
cmd9k->request_id = tr->tr_request_id;
|
|
cmd9k->status = 0;
|
|
cmd9k->sgl_offset = 16; /* offset from end of hdr = max cdb len */
|
|
|
|
if(ccb_h->flags & CAM_CDB_POINTER)
|
|
bcopy(csio->cdb_io.cdb_ptr, cmd9k->cdb, csio->cdb_len);
|
|
else
|
|
bcopy(csio->cdb_io.cdb_bytes, cmd9k->cdb, csio->cdb_len);
|
|
|
|
if (!(ccb_h->flags & CAM_DATA_PHYS)) {
|
|
/* Virtual data addresses. Need to convert them... */
|
|
twa_dbg_dprint(3, sc, "XPT_SCSI_IO: Single virtual address!");
|
|
if (!(ccb_h->flags & CAM_SCATTER_VALID)) {
|
|
if (csio->dxfer_len > TWA_MAX_IO_SIZE) {
|
|
twa_printf(sc, "I/O size %d too big.\n",
|
|
csio->dxfer_len);
|
|
ccb_h->status = CAM_REQ_TOO_BIG;
|
|
if (tr->tr_cmd_pkt_type & TWA_CMD_PKT_TYPE_EXTERNAL)
|
|
xpt_done(ccb);
|
|
return(1);
|
|
}
|
|
|
|
if ((tr->tr_length = csio->dxfer_len)) {
|
|
tr->tr_data = csio->data_ptr;
|
|
cmd9k->sgl_entries = 1;
|
|
}
|
|
} else {
|
|
twa_printf(sc, "twa_execute_scsi: XPT_SCSI_IO: Got SGList!\n");
|
|
ccb_h->status = CAM_REQ_CMP_ERR;
|
|
if (tr->tr_cmd_pkt_type & TWA_CMD_PKT_TYPE_EXTERNAL) {
|
|
xpt_done(ccb);
|
|
}
|
|
return(1);
|
|
}
|
|
} else {
|
|
/* Data addresses are physical. */
|
|
twa_printf(sc, "twa_execute_scsi: XPT_SCSI_IO: Physical data addresses!\n");
|
|
ccb_h->status = CAM_REQ_CMP_ERR;
|
|
if (tr->tr_cmd_pkt_type & TWA_CMD_PKT_TYPE_EXTERNAL) {
|
|
ccb_h->status |= CAM_RELEASE_SIMQ;
|
|
ccb_h->status &= ~CAM_SIM_QUEUED;
|
|
xpt_done(ccb);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
tr->tr_cmd_pkt_type |= TWA_CMD_PKT_TYPE_9K;
|
|
|
|
twa_set_timer(tr);
|
|
|
|
/* twa_setup_data_dmamap will fill in the SGL, and submit the I/O. */
|
|
error = twa_map_request(tr);
|
|
return(error);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_action
|
|
* Description: Driver entry point for CAM's use.
|
|
*
|
|
* Input: sim -- sim corresponding to the ctlr
|
|
* ccb -- ptr to CAM request
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
void
|
|
twa_action(struct cam_sim *sim, union ccb *ccb)
|
|
{
|
|
struct twa_softc *sc = (struct twa_softc *)cam_sim_softc(sim);
|
|
struct ccb_hdr *ccb_h = &(ccb->ccb_h);
|
|
|
|
switch (ccb_h->func_code) {
|
|
case XPT_SCSI_IO: /* SCSI I/O */
|
|
{
|
|
struct twa_request *tr;
|
|
|
|
if ((sc->twa_state & TWA_STATE_SIMQ_FROZEN) ||
|
|
((tr = twa_get_request(sc)) == NULL)) {
|
|
twa_dbg_dprint(2, sc, "simq frozen/Cannot get request pkt.");
|
|
/*
|
|
* Freeze the simq to maintain ccb ordering. The next
|
|
* ccb that gets completed will unfreeze the simq.
|
|
*/
|
|
twa_disallow_new_requests(sc);
|
|
ccb_h->status |= CAM_REQUEUE_REQ;
|
|
xpt_done(ccb);
|
|
break;
|
|
}
|
|
tr->tr_cmd_pkt_type |= TWA_CMD_PKT_TYPE_EXTERNAL;
|
|
tr->tr_private = ccb;
|
|
tr->tr_callback = twa_complete_io;
|
|
if (twa_execute_scsi(tr, ccb))
|
|
twa_release_request(tr);
|
|
break;
|
|
}
|
|
|
|
case XPT_ABORT:
|
|
twa_dbg_dprint(2, sc, "Abort request");
|
|
ccb_h->status = CAM_UA_ABORT;
|
|
xpt_done(ccb);
|
|
break;
|
|
|
|
case XPT_RESET_BUS:
|
|
twa_printf(sc, "Reset Bus request from CAM...\n");
|
|
if (twa_reset(sc)) {
|
|
twa_printf(sc, "Reset Bus failed!\n");
|
|
ccb_h->status = CAM_REQ_CMP_ERR;
|
|
}
|
|
else
|
|
ccb_h->status = CAM_REQ_CMP;
|
|
|
|
xpt_done(ccb);
|
|
break;
|
|
|
|
case XPT_SET_TRAN_SETTINGS:
|
|
twa_dbg_dprint(3, sc, "XPT_SET_TRAN_SETTINGS");
|
|
|
|
/*
|
|
* This command is not supported, since it's very specific
|
|
* to SCSI, and we are doing ATA.
|
|
*/
|
|
ccb_h->status = CAM_FUNC_NOTAVAIL;
|
|
xpt_done(ccb);
|
|
break;
|
|
|
|
case XPT_GET_TRAN_SETTINGS:
|
|
{
|
|
struct ccb_trans_settings *cts = &ccb->cts;
|
|
|
|
twa_dbg_dprint(3, sc, "XPT_GET_TRAN_SETTINGS");
|
|
cts->valid = (CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID);
|
|
cts->flags &= ~(CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB);
|
|
ccb_h->status = CAM_REQ_CMP;
|
|
xpt_done(ccb);
|
|
break;
|
|
}
|
|
|
|
case XPT_CALC_GEOMETRY:
|
|
twa_dbg_dprint(3, sc, "XPT_CALC_GEOMETRY request");
|
|
cam_calc_geometry(&ccb->ccg, 1/* extended */);
|
|
xpt_done(ccb);
|
|
break;
|
|
|
|
case XPT_PATH_INQ: /* Path inquiry -- get twa properties */
|
|
{
|
|
struct ccb_pathinq *path_inq = &ccb->cpi;
|
|
|
|
twa_dbg_dprint(3, sc, "XPT_PATH_INQ request");
|
|
|
|
path_inq->version_num = 1;
|
|
path_inq->hba_inquiry = 0;
|
|
path_inq->target_sprt = 0;
|
|
path_inq->hba_misc = 0;
|
|
path_inq->hba_eng_cnt = 0;
|
|
path_inq->max_target = TWA_MAX_UNITS;
|
|
path_inq->max_lun = 0;
|
|
path_inq->unit_number = cam_sim_unit(sim);
|
|
path_inq->bus_id = cam_sim_bus(sim);
|
|
path_inq->initiator_id = 12;
|
|
path_inq->base_transfer_speed = 100000;
|
|
strncpy(path_inq->sim_vid, "FreeBSD", SIM_IDLEN);
|
|
strncpy(path_inq->hba_vid, "3ware", HBA_IDLEN);
|
|
strncpy(path_inq->dev_name, cam_sim_name(sim), DEV_IDLEN);
|
|
ccb_h->status = CAM_REQ_CMP;
|
|
xpt_done(ccb);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
twa_dbg_dprint(3, sc, "func_code = %x", ccb_h->func_code);
|
|
ccb_h->status = CAM_REQ_INVALID;
|
|
xpt_done(ccb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_poll
|
|
* Description: Driver entry point called when interrupts are not available.
|
|
*
|
|
* Input: sim -- sim corresponding to the controller
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
void
|
|
twa_poll(struct cam_sim *sim)
|
|
{
|
|
#ifdef TWA_DEBUG
|
|
struct twa_softc *sc = (struct twa_softc *)cam_sim_softc(sim);
|
|
#endif /* TWA_DEBUG */
|
|
|
|
twa_dbg_dprint(3, sc, "Entering sc = %p", sc);
|
|
twa_interrupt(cam_sim_softc(sim));
|
|
twa_dbg_dprint(3, sc, "Exiting sc = %p", sc);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_async
|
|
* Description: Driver entry point for CAM to notify driver of special
|
|
* events. We don't use this for now.
|
|
*
|
|
* Input: callback_arg -- ptr to per ctlr structure
|
|
* code -- code associated with the event
|
|
* path -- cam path
|
|
* arg --
|
|
* Output: None
|
|
* Return value: 0 -- success
|
|
* non-zero-- failure
|
|
*/
|
|
void
|
|
twa_async(void *callback_arg, u_int32_t code,
|
|
struct cam_path *path, void *arg)
|
|
{
|
|
#ifdef TWA_DEBUG
|
|
struct twa_softc *sc = (struct twa_softc *)callback_arg;
|
|
#endif /* TWA_DEBUG */
|
|
|
|
twa_dbg_dprint(3, sc, "sc = %p, code = %x, path = %p, arg = %p",
|
|
sc, code, path, arg);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_timeout
|
|
* Description: Driver entry point for being alerted on a request
|
|
* timing out.
|
|
*
|
|
* Input: arg -- ptr to timed out request
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
static void
|
|
twa_timeout(void *arg)
|
|
{
|
|
struct twa_request *tr = (struct twa_request *)arg;
|
|
struct twa_softc *sc = (struct twa_softc *)(tr->tr_sc);
|
|
|
|
twa_printf(sc, "Request timed out! tr = %p\n", tr);
|
|
|
|
twa_reset(sc);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_request_bus_scan
|
|
* Description: Requests CAM for a scan of the bus.
|
|
*
|
|
* Input: sc -- ptr to per ctlr structure
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
void
|
|
twa_request_bus_scan(struct twa_softc *sc)
|
|
{
|
|
struct cam_path *path;
|
|
union ccb *ccb;
|
|
|
|
if ((ccb = malloc(sizeof(union ccb), M_TEMP, M_WAITOK)) == NULL)
|
|
return;
|
|
bzero(ccb, sizeof(union ccb));
|
|
if (xpt_create_path(&path, xpt_periph, cam_sim_path(sc->twa_sim),
|
|
CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP)
|
|
return;
|
|
|
|
xpt_setup_ccb(&ccb->ccb_h, path, 5);
|
|
ccb->ccb_h.func_code = XPT_SCAN_BUS;
|
|
ccb->ccb_h.cbfcnp = twa_bus_scan_cb;
|
|
ccb->crcn.flags = CAM_FLAG_NONE;
|
|
xpt_action(ccb);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_bus_scan_cb
|
|
* Description: Callback from CAM on a bus scan request.
|
|
*
|
|
* Input: periph -- we don't use this
|
|
* ccb -- bus scan request ccb that we sent to CAM
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
static void
|
|
twa_bus_scan_cb(struct cam_periph *periph, union ccb *ccb)
|
|
{
|
|
twa_dbg_print(3, "ccb = %p\n", ccb);
|
|
if (ccb->ccb_h.status != CAM_REQ_CMP)
|
|
printf("cam_scan_callback: failure status = %x\n",
|
|
ccb->ccb_h.status);
|
|
else
|
|
twa_dbg_print(3, "success");
|
|
|
|
xpt_free_path(ccb->ccb_h.path);
|
|
free(ccb, M_TEMP);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_scsi_complete
|
|
* Description: Called to complete CAM scsi requests.
|
|
*
|
|
* Input: tr -- ptr to request pkt to be completed
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
void
|
|
twa_scsi_complete(struct twa_request *tr)
|
|
{
|
|
struct twa_softc *sc = tr->tr_sc;
|
|
struct twa_command_header *cmd_hdr = &(tr->tr_command->cmd_hdr);
|
|
struct twa_command_9k *cmd = &(tr->tr_command->command.cmd_pkt_9k);
|
|
union ccb *ccb = (union ccb *)(tr->tr_private);
|
|
u_int16_t error;
|
|
u_int8_t *cdb;
|
|
|
|
twa_unset_timer(tr);
|
|
|
|
if (tr->tr_error) {
|
|
if (tr->tr_error == EBUSY)
|
|
ccb->ccb_h.status |= CAM_REQUEUE_REQ;
|
|
else if (tr->tr_error == EFBIG)
|
|
ccb->ccb_h.status = CAM_REQ_TOO_BIG;
|
|
else
|
|
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
|
|
} else {
|
|
if (cmd->status) {
|
|
twa_dbg_dprint(1, sc, "req_id = 0x%x, status = 0x%x",
|
|
cmd->request_id,
|
|
cmd->status);
|
|
|
|
error = cmd_hdr->status_block.error;
|
|
if ((error == TWA_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) ||
|
|
(error == TWA_ERROR_UNIT_OFFLINE)) {
|
|
twa_dbg_dprint(3, sc, "Unsupported unit. PTL = %x %x %x",
|
|
ccb->ccb_h.path_id,
|
|
ccb->ccb_h.target_id,
|
|
ccb->ccb_h.target_lun);
|
|
ccb->ccb_h.status |= CAM_TID_INVALID;
|
|
} else {
|
|
twa_dbg_dprint(2, sc, "cmd = %x %x %x %x %x %x %x",
|
|
cmd->command.opcode,
|
|
cmd->command.reserved,
|
|
cmd->unit,
|
|
cmd->request_id,
|
|
cmd->status,
|
|
cmd->sgl_offset,
|
|
cmd->sgl_entries);
|
|
|
|
cdb = (u_int8_t *)(cmd->cdb);
|
|
twa_dbg_dprint(2, sc, "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]);
|
|
|
|
/*
|
|
* 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))) {
|
|
u_int8_t *error_str =
|
|
&(cmd_hdr->err_desc[strlen(cmd_hdr->err_desc) + 1]);
|
|
|
|
if (error_str[0] == '\0')
|
|
error_str =
|
|
twa_find_msg_string(twa_error_table, error);
|
|
|
|
twa_printf(sc, "SCSI cmd = 0x%x: ERROR: (0x%02X: 0x%04X): %s: %s\n",
|
|
cdb[0],
|
|
TWA_MESSAGE_SOURCE_CONTROLLER_ERROR,
|
|
error,
|
|
error_str,
|
|
cmd_hdr->err_desc);
|
|
}
|
|
}
|
|
|
|
bcopy(cmd_hdr->sense_data, &(ccb->csio.sense_data),
|
|
TWA_SENSE_DATA_LENGTH);
|
|
ccb->csio.sense_len = TWA_SENSE_DATA_LENGTH;
|
|
ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
|
|
} else
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
|
|
ccb->csio.scsi_status = cmd->status;
|
|
/* If simq is frozen, unfreeze it. */
|
|
if (sc->twa_state & TWA_STATE_SIMQ_FROZEN)
|
|
twa_allow_new_requests(sc, (void *)ccb);
|
|
}
|
|
|
|
ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
|
|
xpt_done(ccb);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_drain_busy_queue
|
|
* Description: This function gets called after a controller reset.
|
|
* It errors back to CAM, all those requests that were
|
|
* pending with the firmware, at the time of the reset.
|
|
*
|
|
* Input: sc -- ptr to per ctlr structure
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
void
|
|
twa_drain_busy_queue(struct twa_softc *sc)
|
|
{
|
|
struct twa_request *tr;
|
|
union ccb *ccb;
|
|
|
|
/* Walk the busy queue. */
|
|
while ((tr = twa_dequeue_busy(sc))) {
|
|
twa_unmap_request(tr);
|
|
if ((tr->tr_cmd_pkt_type & TWA_CMD_PKT_TYPE_INTERNAL) ||
|
|
(tr->tr_cmd_pkt_type & TWA_CMD_PKT_TYPE_IOCTL)) {
|
|
/*
|
|
* It's an internal/ioctl request. The callback/
|
|
* originator should do the clean-up (except unmapping).
|
|
*/
|
|
tr->tr_error = EIO;
|
|
if (tr->tr_flags & TWA_CMD_SLEEP_ON_REQUEST) {
|
|
tr->tr_flags &= ~TWA_CMD_SLEEP_ON_REQUEST;
|
|
wakeup_one(tr);/* let the caller know */
|
|
}
|
|
else
|
|
if (tr->tr_callback)
|
|
tr->tr_callback(tr);
|
|
} else {
|
|
if ((ccb = tr->tr_private)) {
|
|
twa_unset_timer(tr);
|
|
/* It's a SCSI request. Complete it. */
|
|
ccb->ccb_h.status = CAM_SCSI_BUS_RESET;
|
|
twa_allow_new_requests(sc, ccb);
|
|
ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
|
|
xpt_done(ccb);
|
|
}
|
|
twa_release_request(tr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_allow_new_requests
|
|
* Description: Sets the appropriate status bits in a ccb such that,
|
|
* when the ccb is completed by a call to xpt_done,
|
|
* CAM knows that it's ok to unfreeze the flow of new
|
|
* requests to this controller, if the flow is frozen.
|
|
*
|
|
* Input: sc -- ptr to per ctlr structure
|
|
* ccb -- ptr to CAM request
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
void
|
|
twa_allow_new_requests(struct twa_softc *sc, void *ccb)
|
|
{
|
|
((union ccb *)(ccb))->ccb_h.status |= CAM_RELEASE_SIMQ;
|
|
sc->twa_state &= ~TWA_STATE_SIMQ_FROZEN;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_disallow_new_requests
|
|
* Description: Calls the appropriate CAM function, so as to freeze
|
|
* the flow of new requests from CAM to this controller.
|
|
*
|
|
* Input: sc -- ptr to per ctlr structure
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
void
|
|
twa_disallow_new_requests(struct twa_softc *sc)
|
|
{
|
|
xpt_freeze_simq(sc->twa_sim, 1);
|
|
sc->twa_state |= TWA_STATE_SIMQ_FROZEN;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_set_timer
|
|
* Description: Set a timer to time a given request.
|
|
*
|
|
* Input: tr -- ptr to request pkt
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
void
|
|
twa_set_timer(struct twa_request *tr)
|
|
{
|
|
union ccb *ccb = (union ccb *)(tr->tr_private);
|
|
|
|
/* Set the timer only if external (CAM) request. */
|
|
if (ccb) {
|
|
tr->tr_flags |= TWA_CMD_TIMER_SET;
|
|
ccb->ccb_h.timeout_ch = timeout(twa_timeout, tr,
|
|
(ccb->ccb_h.timeout * hz) / 1000);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function name: twa_unset_timer
|
|
* Description: Unset a previously set timer.
|
|
*
|
|
* Input: tr -- ptr to request pkt
|
|
* Output: None
|
|
* Return value: None
|
|
*/
|
|
void
|
|
twa_unset_timer(struct twa_request *tr)
|
|
{
|
|
union ccb *ccb = (union ccb *)(tr->tr_private);
|
|
|
|
if (tr->tr_flags & TWA_CMD_TIMER_SET) {
|
|
untimeout(twa_timeout, tr, ccb->ccb_h.timeout_ch);
|
|
tr->tr_flags &= ~TWA_CMD_TIMER_SET;
|
|
}
|
|
}
|